1516 lines
38 KiB
C
1516 lines
38 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
ea.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains various support routines for extended attributes.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David Treadwell (davidtr) 5-Apr-1990
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#include "ea.tmh"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#define BugCheckFileId SRV_FILE_EA
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, SrvAreEasNeeded )
|
|||
|
#pragma alloc_text( PAGE, SrvGetOs2FeaOffsetOfError )
|
|||
|
#pragma alloc_text( PAGE, SrvGetOs2GeaOffsetOfError )
|
|||
|
#pragma alloc_text( PAGE, SrvOs2FeaListToNt )
|
|||
|
#pragma alloc_text( PAGE, SrvOs2FeaListSizeToNt )
|
|||
|
#pragma alloc_text( PAGE, SrvOs2FeaToNt )
|
|||
|
#pragma alloc_text( PAGE, SrvOs2GeaListToNt )
|
|||
|
#pragma alloc_text( PAGE, SrvOs2GeaListSizeToNt )
|
|||
|
#pragma alloc_text( PAGE, SrvOs2GeaToNt )
|
|||
|
#pragma alloc_text( PAGE, SrvNtFullEaToOs2 )
|
|||
|
#pragma alloc_text( PAGE, SrvGetNumberOfEasInList )
|
|||
|
#pragma alloc_text( PAGE, SrvQueryOs2FeaList )
|
|||
|
#pragma alloc_text( PAGE, SrvSetOs2FeaList )
|
|||
|
#pragma alloc_text( PAGE, SrvConstructNullOs2FeaList )
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SrvAreEasNeeded (
|
|||
|
IN PFILE_FULL_EA_INFORMATION NtFullEa
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine checks whether any of the full EAs in the list have the
|
|||
|
FILE_NEED_EA bit set in the flags field.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NtFullEa - a pointer to a pointer to where the NT-style full EA list
|
|||
|
is stored.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if any EA has FILE_NEED_EA set, FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFILE_FULL_EA_INFORMATION lastEa;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
if ( NtFullEa->Flags & FILE_NEED_EA ) {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
lastEa = NtFullEa;
|
|||
|
NtFullEa = (PFILE_FULL_EA_INFORMATION)(
|
|||
|
(PCHAR)NtFullEa + NtFullEa->NextEntryOffset );
|
|||
|
|
|||
|
} while ( lastEa->NextEntryOffset != 0 );
|
|||
|
|
|||
|
return FALSE;
|
|||
|
|
|||
|
} // SrvAreEasNeeded
|
|||
|
|
|||
|
|
|||
|
USHORT
|
|||
|
SrvGetOs2FeaOffsetOfError (
|
|||
|
IN ULONG NtErrorOffset,
|
|||
|
IN PFILE_FULL_EA_INFORMATION NtFullEa,
|
|||
|
IN PFEALIST FeaList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Finds the offset in a FEALIST that corresponds to an offset into
|
|||
|
a list of FILE_FULL_EA_INFORMATION structures. This is used when
|
|||
|
NtSetEaFile returns an offset to an EA that caused an error, and we
|
|||
|
need to return the offset into the list of EAs given to us by the
|
|||
|
client.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NtErrorOffset - offset of the EA in the NT full EA list that caused
|
|||
|
the error.
|
|||
|
|
|||
|
NtFullEa - a pointer to a pointer to where the NT-style full EA list
|
|||
|
is stored. A buffer is allocated and pointer to by *NtFullEa.
|
|||
|
|
|||
|
FeaList - pointer to the OS/2 1.2 FEALIST.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
USHORT - offset into the FEALIST.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFEA fea = FeaList->list;
|
|||
|
PFEA lastFeaStartLocation;
|
|||
|
PFILE_FULL_EA_INFORMATION offsetLocation;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// If the NT error offset is zero, return 0 for the FEA error offset.
|
|||
|
//
|
|||
|
// !!! this shouldn't be necessary if the loop below is written
|
|||
|
// correctly.
|
|||
|
|
|||
|
//if ( NtErrorOffset == 0 ) {
|
|||
|
// return 0;
|
|||
|
//}
|
|||
|
|
|||
|
//
|
|||
|
// Find where in the NT full EA list the error occurred and the
|
|||
|
// last possible start location for an FEA in the FEALIST.
|
|||
|
//
|
|||
|
|
|||
|
offsetLocation = (PFILE_FULL_EA_INFORMATION)(
|
|||
|
(PCHAR)NtFullEa + NtErrorOffset);
|
|||
|
lastFeaStartLocation = (PFEA)( (PCHAR)FeaList +
|
|||
|
SmbGetUlong( &FeaList->cbList ) -
|
|||
|
sizeof(FEA) - 1 );
|
|||
|
|
|||
|
//
|
|||
|
// Walk through both lists simultaneously until one of three conditions
|
|||
|
// is true:
|
|||
|
// - we reach or pass the offset in the NT full EA list
|
|||
|
// - we reach the end of the NT full EA list
|
|||
|
// - we reach the end of the FEALIST.
|
|||
|
//
|
|||
|
|
|||
|
while ( NtFullEa < offsetLocation &&
|
|||
|
NtFullEa->NextEntryOffset != 0 &&
|
|||
|
fea <= lastFeaStartLocation ) {
|
|||
|
|
|||
|
NtFullEa = (PFILE_FULL_EA_INFORMATION)(
|
|||
|
(PCHAR)NtFullEa + NtFullEa->NextEntryOffset );
|
|||
|
fea = (PFEA)( (PCHAR)fea + sizeof(FEA) + fea->cbName + 1 +
|
|||
|
SmbGetUshort( &fea->cbValue ) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If NtFullEa is not equal to the offset location we calculated,
|
|||
|
// somebody messed up.
|
|||
|
//
|
|||
|
|
|||
|
//ASSERT( NtFullEa == offsetLocation );
|
|||
|
|
|||
|
return PTR_DIFF_SHORT(fea, FeaList );
|
|||
|
|
|||
|
} // SrvGetOs2FeaOffsetOfError
|
|||
|
|
|||
|
|
|||
|
USHORT
|
|||
|
SrvGetOs2GeaOffsetOfError (
|
|||
|
IN ULONG NtErrorOffset,
|
|||
|
IN PFILE_GET_EA_INFORMATION NtGetEa,
|
|||
|
IN PGEALIST GeaList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Finds the offset in a GEALIST that corresponds to an offset into
|
|||
|
a list of FILE_GET_EA_INFORMATION structures. This is used when
|
|||
|
NtQueryEaFile returns an offset to an EA that caused an error, and we
|
|||
|
need to return the offset into the list of EAs given to us by the
|
|||
|
client.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NtErrorOffset - offset of the EA in the NT get EA list that caused
|
|||
|
the error.
|
|||
|
|
|||
|
NtGetEa - a pointer to a pointer to where the NT-style get EA list
|
|||
|
is stored. A buffer is allocated and pointer to by *NtGetEa.
|
|||
|
|
|||
|
GeaList - pointer to the OS/2 1.2 GEALIST.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
USHORT - offset into the GEALIST.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PGEA gea = GeaList->list;
|
|||
|
PGEA lastGeaStartLocation;
|
|||
|
PFILE_GET_EA_INFORMATION offsetLocation;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Find where in the NT get EA list the error occurred and the
|
|||
|
// last possible start location for an GEA in the GEALIST.
|
|||
|
//
|
|||
|
|
|||
|
offsetLocation = (PFILE_GET_EA_INFORMATION)((PCHAR)NtGetEa + NtErrorOffset);
|
|||
|
lastGeaStartLocation = (PGEA)( (PCHAR)GeaList +
|
|||
|
SmbGetUlong( &GeaList->cbList ) - sizeof(GEA) );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Walk through both lists simultaneously until one of three conditions
|
|||
|
// is true:
|
|||
|
// - we reach or pass the offset in the NT full EA list
|
|||
|
// - we reach the end of the NT get EA list
|
|||
|
// - we reach the end of the GEALIST.
|
|||
|
//
|
|||
|
|
|||
|
while ( NtGetEa < offsetLocation &&
|
|||
|
NtGetEa->NextEntryOffset != 0 &&
|
|||
|
gea <= lastGeaStartLocation ) {
|
|||
|
|
|||
|
NtGetEa = (PFILE_GET_EA_INFORMATION)(
|
|||
|
(PCHAR)NtGetEa + NtGetEa->NextEntryOffset );
|
|||
|
gea = (PGEA)( (PCHAR)gea + sizeof(GEA) + gea->cbName );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If NtGetEa is not equal to the offset location we calculated,
|
|||
|
// somebody messed up.
|
|||
|
//
|
|||
|
|
|||
|
// ASSERT( NtGetEa == offsetLocation );
|
|||
|
|
|||
|
return PTR_DIFF_SHORT(gea, GeaList);
|
|||
|
|
|||
|
} // SrvGetOs2GeaOffsetOfError
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvOs2FeaListToNt (
|
|||
|
IN PFEALIST FeaList,
|
|||
|
OUT PFILE_FULL_EA_INFORMATION *NtFullEa,
|
|||
|
OUT PULONG BufferLength,
|
|||
|
OUT PUSHORT EaErrorOffset
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a list of OS/2 1.2 FEAs to NT style. Memory is allocated from
|
|||
|
non-paged pool to hold the NT full EA list. The calling routine is
|
|||
|
responsible for deallocating this memory when it is done with it.
|
|||
|
|
|||
|
WARNING! It is the responsibility of the calling routine to ensure
|
|||
|
that the value in FeaList->cbList will fit within the buffer allocated
|
|||
|
to FeaList. This prevents malicious redirectors from causing an
|
|||
|
access violation in the server.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FeaList - pointer to the OS/2 1.2 FEALIST to convert.
|
|||
|
|
|||
|
NtFullEa - a pointer to a pointer to where the NT-style full EA list
|
|||
|
is stored. A buffer is allocated and pointer to by *NtFullEa.
|
|||
|
|
|||
|
BufferLength - length of the allocated buffer.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_SUCCESS or STATUS_INSUFF_SERVER_RESOURCES.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFEA lastFeaStartLocation;
|
|||
|
PFEA fea = NULL;
|
|||
|
PFEA lastFea = NULL;
|
|||
|
PFILE_FULL_EA_INFORMATION ntFullEa = NULL;
|
|||
|
PFILE_FULL_EA_INFORMATION lastNtFullEa = NULL;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Find out how large the OS/2 1.2 FEALIST will be after it is
|
|||
|
// converted to NT format. This is necessary in order to
|
|||
|
// determine how large a buffer to allocate to receive the NT
|
|||
|
// EAs.
|
|||
|
//
|
|||
|
|
|||
|
*BufferLength = SrvOs2FeaListSizeToNt( FeaList );
|
|||
|
|
|||
|
//
|
|||
|
// It is possible for SrvOs2FeaListSizeToNt to return 0 in the event
|
|||
|
// that the very first FEA in the list is corrupt. Return an error
|
|||
|
// if this is the case with an appropriate EaErrorOffset value.
|
|||
|
//
|
|||
|
|
|||
|
if (*BufferLength == 0) {
|
|||
|
*EaErrorOffset = 0;
|
|||
|
return STATUS_OS2_EA_LIST_INCONSISTENT;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer to hold the NT list. This is allocated from
|
|||
|
// non-paged pool so that it may be used in IRP-based IO requests.
|
|||
|
//
|
|||
|
|
|||
|
*NtFullEa = ALLOCATE_NONPAGED_POOL( *BufferLength, BlockTypeDataBuffer );
|
|||
|
|
|||
|
if ( *NtFullEa == NULL ) {
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"SrvOs2FeaListToNt: Unable to allocate %d bytes from nonpaged "
|
|||
|
"pool.",
|
|||
|
*BufferLength,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find the last location at which an FEA can start. The -1 is to
|
|||
|
// account for the zero terminator on the name field of the FEA.
|
|||
|
//
|
|||
|
|
|||
|
lastFeaStartLocation = (PFEA)( (PCHAR)FeaList +
|
|||
|
SmbGetUlong( &FeaList->cbList ) -
|
|||
|
sizeof(FEA) - 1 );
|
|||
|
|
|||
|
//
|
|||
|
// Go through the FEA list, converting from OS/2 1.2 format to NT
|
|||
|
// until we pass the last possible location in which an FEA can start.
|
|||
|
//
|
|||
|
|
|||
|
for ( fea = FeaList->list, ntFullEa = *NtFullEa, lastNtFullEa = ntFullEa;
|
|||
|
fea <= lastFeaStartLocation;
|
|||
|
fea = (PFEA)( (PCHAR)fea + sizeof(FEA) +
|
|||
|
fea->cbName + 1 + SmbGetUshort( &fea->cbValue ) ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Check for an invalid flag bit. If set, return an error.
|
|||
|
//
|
|||
|
|
|||
|
if ( (fea->fEA & ~FEA_NEEDEA) != 0 ) {
|
|||
|
*EaErrorOffset = PTR_DIFF_SHORT(fea, FeaList);
|
|||
|
DEALLOCATE_NONPAGED_POOL( *NtFullEa );
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
lastNtFullEa = ntFullEa;
|
|||
|
lastFea = fea;
|
|||
|
ntFullEa = SrvOs2FeaToNt( ntFullEa, fea );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the FEALIST size parameter was correct. If we ended
|
|||
|
// on an EA that was not the first location after the end of the
|
|||
|
// last FEA, then the size parameter was wrong. Return an offset to
|
|||
|
// the EA that caused the error.
|
|||
|
//
|
|||
|
|
|||
|
if ( (PCHAR)fea != (PCHAR)FeaList + SmbGetUlong( &FeaList->cbList ) ) {
|
|||
|
*EaErrorOffset = PTR_DIFF_SHORT(lastFea, FeaList);
|
|||
|
DEALLOCATE_NONPAGED_POOL( *NtFullEa );
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the NextEntryOffset field of the last full EA to 0 to indicate
|
|||
|
// the end of the list.
|
|||
|
//
|
|||
|
|
|||
|
lastNtFullEa->NextEntryOffset = 0;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // SrvOs2FeaListToNt
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
SrvOs2FeaListSizeToNt (
|
|||
|
IN PFEALIST FeaList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the number of bytes that would be required to represent the
|
|||
|
FEALIST in NT format.
|
|||
|
|
|||
|
WARNING: This routine makes no checks on the size of the FEALIST
|
|||
|
buffer. It is assumed that FeaList->cbList is a legitimate value.
|
|||
|
|
|||
|
WARNING: The value returned by this routine may be as much as three
|
|||
|
higher than the actual size needed to hold the FEAs in NT format.
|
|||
|
See comments below.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Fea - a pointer to the list of FEAs.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ULONG - number of bytes required to hold the EAs in NT format.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG size = 0;
|
|||
|
|
|||
|
PCHAR lastValidLocation;
|
|||
|
PCHAR variableBuffer;
|
|||
|
PFEA fea;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Find the last valid location in the FEA buffer.
|
|||
|
//
|
|||
|
|
|||
|
lastValidLocation = (PCHAR)FeaList + SmbGetUlong( &FeaList->cbList );
|
|||
|
|
|||
|
//
|
|||
|
// Go through the FEA list until we pass the last location
|
|||
|
// indicated in the buffer.
|
|||
|
//
|
|||
|
|
|||
|
for ( fea = FeaList->list;
|
|||
|
fea < (PFEA)lastValidLocation;
|
|||
|
fea = (PFEA)( (PCHAR)fea + sizeof(FEA) +
|
|||
|
fea->cbName + 1 + SmbGetUshort( &fea->cbValue ) ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Be very careful accessing the embedded FEA since there are so many possibly
|
|||
|
// conflicting sizes running around here. The first part of the conditional
|
|||
|
// below makes sure that the cbName and cbValue fields of the FEA buffer can
|
|||
|
// actually be dereferenced. The second part verifies that they contain
|
|||
|
// reasonable values.
|
|||
|
//
|
|||
|
|
|||
|
variableBuffer = (PCHAR)fea + sizeof(FEA);
|
|||
|
|
|||
|
if (variableBuffer >= lastValidLocation ||
|
|||
|
(variableBuffer + fea->cbName + 1 + SmbGetUshort(&fea->cbValue)) > lastValidLocation) {
|
|||
|
|
|||
|
//
|
|||
|
// The values in this part of the buffer indicate a range outside the
|
|||
|
// buffer. Don't calculate a size, and shrink the cbList value to
|
|||
|
// include only the prior FEA.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort( &FeaList->cbList, PTR_DIFF_SHORT(fea, FeaList) );
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// SmbGetNtSizeOfFea returns the number of bytes needed to hold
|
|||
|
// a single FEA in NT format, including any padding needed for
|
|||
|
// longword-alignment. Since the size of the buffer needed to
|
|||
|
// hold the NT EA list does not include any padding after the
|
|||
|
// last EA, the value returned by this routine may be as much as
|
|||
|
// three higher than the size actually needed.
|
|||
|
//
|
|||
|
|
|||
|
size += SmbGetNtSizeOfFea( fea );
|
|||
|
}
|
|||
|
|
|||
|
return size;
|
|||
|
|
|||
|
} // SrvOs2FeaListSizeToNt
|
|||
|
|
|||
|
|
|||
|
PVOID
|
|||
|
SrvOs2FeaToNt (
|
|||
|
OUT PFILE_FULL_EA_INFORMATION NtFullEa,
|
|||
|
IN PFEA Fea
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a single OS/2 FEA to NT full EA style. The FEA need not have
|
|||
|
any particular alignment. This routine makes no checks on buffer
|
|||
|
overrunning--this is the responsibility of the calling routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NtFullEa - a pointer to where the NT full EA is to be written.
|
|||
|
|
|||
|
Fea - pointer to the OS/2 1.2 FEA to convert.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A pointer to the location after the last byte written.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCHAR ptr;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
NtFullEa->Flags = Fea->fEA;
|
|||
|
NtFullEa->EaNameLength = Fea->cbName;
|
|||
|
NtFullEa->EaValueLength = SmbGetUshort( &Fea->cbValue );
|
|||
|
|
|||
|
ptr = NtFullEa->EaName;
|
|||
|
RtlMoveMemory( ptr, (PVOID)(Fea+1), Fea->cbName );
|
|||
|
|
|||
|
ptr += NtFullEa->EaNameLength;
|
|||
|
*ptr++ = '\0';
|
|||
|
|
|||
|
//
|
|||
|
// Copy the EA value to the NT full EA.
|
|||
|
//
|
|||
|
|
|||
|
RtlMoveMemory(
|
|||
|
ptr,
|
|||
|
(PCHAR)(Fea+1) + NtFullEa->EaNameLength + 1,
|
|||
|
NtFullEa->EaValueLength
|
|||
|
);
|
|||
|
|
|||
|
ptr += NtFullEa->EaValueLength;
|
|||
|
|
|||
|
//
|
|||
|
// Longword-align ptr to determine the offset to the next location
|
|||
|
// for an NT full EA.
|
|||
|
//
|
|||
|
|
|||
|
ptr = (PCHAR)( ((ULONG_PTR)ptr + 3) & ~3 );
|
|||
|
NtFullEa->NextEntryOffset = (ULONG)( ptr - (PCHAR)NtFullEa );
|
|||
|
|
|||
|
return ptr;
|
|||
|
|
|||
|
} // SrvOs2FeaToNt
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvOs2GeaListToNt (
|
|||
|
IN PGEALIST GeaList,
|
|||
|
OUT PFILE_GET_EA_INFORMATION *NtGetEa,
|
|||
|
OUT PULONG BufferLength,
|
|||
|
OUT PUSHORT EaErrorOffset
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a list of OS/2 1.2 GEAs to NT style. Memory is allocated from
|
|||
|
non-paged pool to hold the NT get EA list. The calling routine is
|
|||
|
responsible for deallocating this memory when it is done with it.
|
|||
|
|
|||
|
WARNING! It is the responsibility of the calling routine to ensure
|
|||
|
that the value in GeaList->cbList will fit within the buffer allocated
|
|||
|
to GeaList. This prevents malicious redirectors from causing an
|
|||
|
access violation in the server.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GeaList - pointer to the OS/2 1.2 GEALIST to convert.
|
|||
|
|
|||
|
NtGetEa - a pointer to a pointer to where the NT-style get EA list
|
|||
|
is stored. A buffer is allocated and pointer to by *NtGetEa.
|
|||
|
|
|||
|
BufferLength - length of the allocated buffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_SUCCESS or STATUS_INSUFF_SERVER_RESOURCES.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PGEA lastGeaStartLocation;
|
|||
|
PGEA gea = NULL;
|
|||
|
PGEA lastGea = NULL;
|
|||
|
PFILE_GET_EA_INFORMATION ntGetEa = NULL;
|
|||
|
PFILE_GET_EA_INFORMATION lastNtGetEa = NULL;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Find out how large the OS/2 1.2 GEALIST will be after it is
|
|||
|
// converted to NT format. This is necessary in order to
|
|||
|
// determine how large a buffer to allocate to receive the NT
|
|||
|
// EAs.
|
|||
|
//
|
|||
|
|
|||
|
*BufferLength = SrvOs2GeaListSizeToNt( GeaList );
|
|||
|
|
|||
|
if ( *BufferLength == 0 ) {
|
|||
|
*EaErrorOffset = 0;
|
|||
|
return STATUS_OS2_EA_LIST_INCONSISTENT;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer to hold the NT list. This is allocated from
|
|||
|
// non-paged pool so that it may be used in IRP-based IO requests.
|
|||
|
//
|
|||
|
|
|||
|
*NtGetEa = ALLOCATE_NONPAGED_POOL( *BufferLength, BlockTypeDataBuffer );
|
|||
|
|
|||
|
if ( *NtGetEa == NULL ) {
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"SrvOs2GeaListToNt: Unable to allocate %d bytes from nonpaged "
|
|||
|
"pool.",
|
|||
|
*BufferLength,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
return STATUS_INSUFF_SERVER_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Find the last location at which a GEA can start. The zero
|
|||
|
// terminator on the name field of the GEA is accounted for by the
|
|||
|
// szName[0] field in the GEA structure.
|
|||
|
//
|
|||
|
|
|||
|
lastGeaStartLocation = (PGEA)( (PCHAR)GeaList +
|
|||
|
SmbGetUlong( &GeaList->cbList ) - sizeof(GEA) );
|
|||
|
|
|||
|
//
|
|||
|
// Go through the GEA list, converting from OS/2 1.2 format to NT
|
|||
|
// until we pass the last possible location in which an GEA can start.
|
|||
|
//
|
|||
|
|
|||
|
for ( gea = GeaList->list, ntGetEa = *NtGetEa, lastNtGetEa = ntGetEa;
|
|||
|
gea <= lastGeaStartLocation;
|
|||
|
gea = (PGEA)( (PCHAR)gea + sizeof(GEA) + gea->cbName ) ) {
|
|||
|
|
|||
|
lastNtGetEa = ntGetEa;
|
|||
|
lastGea = gea;
|
|||
|
ntGetEa = SrvOs2GeaToNt( ntGetEa, gea );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the GEALIST size parameter was correct. If we ended
|
|||
|
// on an EA that was not the first location after the end of the
|
|||
|
// last GEA, then the size parameter was wrong. Return an offset to
|
|||
|
// the EA that caused the error.
|
|||
|
//
|
|||
|
|
|||
|
if ( (PCHAR)gea != (PCHAR)GeaList + SmbGetUlong( &GeaList->cbList ) ) {
|
|||
|
DEALLOCATE_NONPAGED_POOL(*NtGetEa);
|
|||
|
*EaErrorOffset = PTR_DIFF_SHORT(lastGea, GeaList);
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the NextEntryOffset field of the last get EA to 0 to indicate
|
|||
|
// the end of the list.
|
|||
|
//
|
|||
|
|
|||
|
lastNtGetEa->NextEntryOffset = 0;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // SrvOs2GeaListToNt
|
|||
|
|
|||
|
|
|||
|
ULONG
|
|||
|
SrvOs2GeaListSizeToNt (
|
|||
|
IN PGEALIST GeaList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get the number of bytes that would be required to represent the
|
|||
|
GEALIST in NT format.
|
|||
|
|
|||
|
WARNING: This routine makes no checks on the size of the GEALIST
|
|||
|
buffer. It is assumed that GeaList->cbList is a legitimate value.
|
|||
|
|
|||
|
WARNING: The value returned by this routine may be as much as three
|
|||
|
higher than the actual size needed to hold the GEAs in NT format.
|
|||
|
See comments below.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Gea - a pointer to the list of GEAs.
|
|||
|
|
|||
|
Size - number of bytes required to hold the EAs in NT format.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_SUCCESS or STATUS_BUFFER_OVERFLOW if the GEA list was
|
|||
|
larger than it's buffer.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG size = 0;
|
|||
|
|
|||
|
PCHAR lastValidLocation;
|
|||
|
PCHAR variableBuffer;
|
|||
|
PGEA gea;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Find the last location in the GEA buffer.
|
|||
|
//
|
|||
|
|
|||
|
lastValidLocation = (PCHAR)GeaList + SmbGetUlong( &GeaList->cbList );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Go through the GEA list until we pass the last possible location
|
|||
|
// in which an GEA can start.
|
|||
|
//
|
|||
|
|
|||
|
for ( gea = GeaList->list;
|
|||
|
gea < (PGEA)lastValidLocation;
|
|||
|
gea = (PGEA)( (PCHAR)gea + sizeof(GEA) + gea->cbName ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Be very careful accessing the embedded GEA. The first part of the conditional
|
|||
|
// below makes sure that the cbName field of the GEA buffer can
|
|||
|
// actually be dereferenced. The second part verifies that it contains
|
|||
|
// reasonable values.
|
|||
|
//
|
|||
|
|
|||
|
variableBuffer = (PCHAR)gea + sizeof(GEA);
|
|||
|
|
|||
|
if ( variableBuffer >= lastValidLocation ||
|
|||
|
variableBuffer + gea->cbName > lastValidLocation ) {
|
|||
|
|
|||
|
//
|
|||
|
// If there's a bogus value in the buffer stop processing the size
|
|||
|
// and reset the cbList value to only enclose the previous GEA.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUshort(&GeaList->cbList, PTR_DIFF_SHORT(gea, GeaList));
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// SmbGetNtSizeOfGea returns the number of bytes needed to hold
|
|||
|
// a single GEA in NT format. This includes any padding needed
|
|||
|
// for longword-alignment. Since the size of the buffer needed
|
|||
|
// to hold the NT EA list does not include any padding after the
|
|||
|
// last EA, the value returned by this routine may be as much as
|
|||
|
// three higher than the size actually needed.
|
|||
|
//
|
|||
|
|
|||
|
size += SmbGetNtSizeOfGea( gea );
|
|||
|
}
|
|||
|
|
|||
|
return size;
|
|||
|
|
|||
|
} // SrvOs2GeaListSizeToNt
|
|||
|
|
|||
|
|
|||
|
PVOID
|
|||
|
SrvOs2GeaToNt (
|
|||
|
OUT PFILE_GET_EA_INFORMATION NtGetEa,
|
|||
|
IN PGEA Gea
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a single OS/2 GEA to NT get EA style. The GEA need not have
|
|||
|
any particular alignment. This routine makes no checks on buffer
|
|||
|
overrunning--this is the responsibility of the calling routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NtGetEa - a pointer to where the NT get EA is to be written.
|
|||
|
|
|||
|
Gea - pointer to the OS/2 1.2 GEA to convert.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A pointer to the location after the last byte written.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCHAR ptr;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
NtGetEa->EaNameLength = Gea->cbName;
|
|||
|
|
|||
|
ptr = NtGetEa->EaName;
|
|||
|
RtlMoveMemory( ptr, Gea->szName, Gea->cbName );
|
|||
|
|
|||
|
ptr += NtGetEa->EaNameLength;
|
|||
|
*ptr++ = '\0';
|
|||
|
|
|||
|
//
|
|||
|
// Longword-align ptr to determine the offset to the next location
|
|||
|
// for an NT full EA.
|
|||
|
//
|
|||
|
|
|||
|
ptr = (PCHAR)( ((ULONG_PTR)ptr + 3) & ~3 );
|
|||
|
NtGetEa->NextEntryOffset = (ULONG)( ptr - (PCHAR)NtGetEa );
|
|||
|
|
|||
|
return ptr;
|
|||
|
|
|||
|
} // SrvOs2GeaToNt
|
|||
|
|
|||
|
|
|||
|
PVOID
|
|||
|
SrvNtFullEaToOs2 (
|
|||
|
OUT PFEA Fea,
|
|||
|
IN PFILE_FULL_EA_INFORMATION NtFullEa
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a single NT full EA to OS/2 FEA style. The FEA need not have
|
|||
|
any particular alignment. This routine makes no checks on buffer
|
|||
|
overrunning--this is the responsibility of the calling routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Fea - a pointer to the location where the OS/2 FEA is to be written.
|
|||
|
|
|||
|
NtFullEa - a pointer to the NT full EA.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A pointer to the location after the last byte written.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCHAR ptr;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
Fea->fEA = (UCHAR)NtFullEa->Flags;
|
|||
|
Fea->cbName = NtFullEa->EaNameLength;
|
|||
|
SmbPutUshort( &Fea->cbValue, NtFullEa->EaValueLength );
|
|||
|
|
|||
|
//
|
|||
|
// Copy the attribute name.
|
|||
|
//
|
|||
|
|
|||
|
for ( i = 0, ptr = (PCHAR) (Fea + 1);
|
|||
|
i < (ULONG) NtFullEa->EaNameLength;
|
|||
|
i++, ptr++) {
|
|||
|
|
|||
|
*ptr = RtlUpperChar( NtFullEa->EaName[i] );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
*ptr++ = '\0';
|
|||
|
|
|||
|
RtlMoveMemory(
|
|||
|
ptr,
|
|||
|
NtFullEa->EaName + NtFullEa->EaNameLength + 1,
|
|||
|
NtFullEa->EaValueLength
|
|||
|
);
|
|||
|
|
|||
|
return (ptr + NtFullEa->EaValueLength);
|
|||
|
|
|||
|
} // SrvNtFullEaToOs2
|
|||
|
|
|||
|
|
|||
|
CLONG
|
|||
|
SrvGetNumberOfEasInList (
|
|||
|
IN PVOID List
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Finds the number of EAs in an NT get or full EA list. The list
|
|||
|
should have already been verified to be legitimate to prevent access
|
|||
|
violations.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
List - a pointer to the NT get or full EA list.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
CLONG - the number of EAs in the list.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CLONG count = 1;
|
|||
|
PULONG ea;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Walk through the list. The first longword of each EA is the offset
|
|||
|
// to the next EA.
|
|||
|
//
|
|||
|
|
|||
|
for ( ea = List; *ea != 0; ea = (PULONG)( (PCHAR)ea + *ea ) ) {
|
|||
|
count++;
|
|||
|
}
|
|||
|
|
|||
|
return count;
|
|||
|
|
|||
|
} // SrvGetNumberOfEasInList
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvQueryOs2FeaList (
|
|||
|
IN HANDLE FileHandle,
|
|||
|
IN PGEALIST GeaList OPTIONAL,
|
|||
|
IN PFILE_GET_EA_INFORMATION NtGetEaList OPTIONAL,
|
|||
|
IN ULONG GeaListLength OPTIONAL,
|
|||
|
IN PFEALIST FeaList,
|
|||
|
IN ULONG BufferLength,
|
|||
|
OUT PUSHORT EaErrorOffset
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a single NT full EA list to OS/2 FEALIST style. The FEALIST
|
|||
|
need not have any particular alignment.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FileHandle - handle to a file open with FILE_READ_EA access.
|
|||
|
|
|||
|
GeaList - if non-NULL, an OS/2 1.2 style GEALIST used to get only
|
|||
|
a subset of the files EAs rather than all the EAs. Only the
|
|||
|
EAs listed in GEALIST are returned.
|
|||
|
|
|||
|
NtGetEaList - if non-NULL, an NT style get EA list used to get only
|
|||
|
a subset of the files EAs rather than all the EAs. Only the
|
|||
|
EAs listed in GEALIST are returned.
|
|||
|
|
|||
|
GeaListLength - the maximum possible length of the GeaList (used to
|
|||
|
prevent access violations) or the actual size of the NtGetEaList.
|
|||
|
|
|||
|
FeaList - where to write the OS/2 1.2 style FEALIST for the file.
|
|||
|
|
|||
|
BufferLength - length of Buffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_SUCCESS, STATUS_BUFFER_OVERFLOW if the EAs wouldn't
|
|||
|
fit in Buffer, or a value returned by NtQuery{Information,Ea}File.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
PFEA fea = FeaList->list;
|
|||
|
PFILE_FULL_EA_INFORMATION ntFullEa;
|
|||
|
|
|||
|
PFILE_GET_EA_INFORMATION ntGetEa = NULL;
|
|||
|
ULONG ntGetEaBufferLength = 0;
|
|||
|
|
|||
|
FILE_EA_INFORMATION eaInfo;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
PSRV_EA_INFORMATION eaInformation = NULL;
|
|||
|
ULONG eaBufferSize;
|
|||
|
ULONG errorOffset;
|
|||
|
|
|||
|
BOOLEAN isFirstCall = TRUE;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
*EaErrorOffset = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Find out how big a buffer we need to get the EAs.
|
|||
|
//
|
|||
|
|
|||
|
status = NtQueryInformationFile(
|
|||
|
FileHandle,
|
|||
|
&ioStatusBlock,
|
|||
|
&eaInfo,
|
|||
|
sizeof(FILE_EA_INFORMATION),
|
|||
|
FileEaInformation
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_UNEXPECTED,
|
|||
|
"SrvQueryOs2FeaList: NtQueryInformationFile(ea information) "
|
|||
|
"returned %X",
|
|||
|
status,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the file has no EAs, return an FEA size = 4 (that's what OS/2
|
|||
|
// does--it accounts for the size of the cbList field of an
|
|||
|
// FEALIST). Also, store the NT EA size in case there is a buffer
|
|||
|
// overflow and we need to return the total EA size to the client.
|
|||
|
//
|
|||
|
|
|||
|
if ( eaInfo.EaSize == 0 ) {
|
|||
|
SmbPutUlong( &FeaList->cbList, 4 );
|
|||
|
} else {
|
|||
|
SmbPutUlong( &FeaList->cbList, eaInfo.EaSize );
|
|||
|
}
|
|||
|
|
|||
|
if ( eaInfo.EaSize == 0 && GeaList == NULL && NtGetEaList == NULL ) {
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a GEALIST was specified, convert it to NT style.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(GeaList) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the value in GeaList->cbList is legitimate
|
|||
|
// (allows the GEALIST to fit within its buffer).
|
|||
|
//
|
|||
|
|
|||
|
if ( GeaListLength < sizeof(GEALIST) ||
|
|||
|
SmbGetUlong( &GeaList->cbList ) < sizeof(GEALIST) ||
|
|||
|
SmbGetUlong( &GeaList->cbList ) > GeaListLength ) {
|
|||
|
status = STATUS_OS2_EA_LIST_INCONSISTENT;
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the GEALIST to NT style. SrvOs2GeaListToNt allocates
|
|||
|
// space to hold the NT get EA list, so remember to deallocate
|
|||
|
// this before exiting this routine.
|
|||
|
//
|
|||
|
|
|||
|
status = SrvOs2GeaListToNt(
|
|||
|
GeaList,
|
|||
|
&ntGetEa,
|
|||
|
&ntGetEaBufferLength,
|
|||
|
EaErrorOffset
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If an NT-style get EA list was specified, use it.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(NtGetEaList) ) {
|
|||
|
ntGetEa = NtGetEaList;
|
|||
|
ntGetEaBufferLength = GeaListLength;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// HACKHACK: eaInfo.EaSize is the size needed by OS/2. For NT,
|
|||
|
// the system has no way of telling us how big a buffer we need.
|
|||
|
// According to BrianAn, this should not be bigger than twice
|
|||
|
// what OS/2 needs.
|
|||
|
//
|
|||
|
|
|||
|
eaBufferSize = eaInfo.EaSize * EA_SIZE_FUDGE_FACTOR;
|
|||
|
|
|||
|
//
|
|||
|
// If a get EA list was specified, a larger buffer is needed to hold
|
|||
|
// all the EAs. This is because some or all of the specified EAs
|
|||
|
// may not exist, yet they will still be returned by the file system
|
|||
|
// with value length = 0. Add to the EA size the amount of space the
|
|||
|
// get EA list would use if it were converted to a full EA list.
|
|||
|
//
|
|||
|
|
|||
|
if ( ntGetEa != NULL ) {
|
|||
|
eaBufferSize += ntGetEaBufferLength +
|
|||
|
( SrvGetNumberOfEasInList( ntGetEa ) *
|
|||
|
( sizeof(FILE_FULL_EA_INFORMATION) -
|
|||
|
sizeof(FILE_GET_EA_INFORMATION) ));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer to receive the EAs. If the total EA size is
|
|||
|
// small enough to get it all in one call to NtQueryEaFile, allocate
|
|||
|
// a buffer that large. If The EAs are large, use a buffer size that
|
|||
|
// is the smallest size guaranteed to hold at least one EA.
|
|||
|
//
|
|||
|
// The buffer must be allocated from non-paged pool for the IRP
|
|||
|
// request built below.
|
|||
|
//
|
|||
|
|
|||
|
eaBufferSize = MIN( MAX_SIZE_OF_SINGLE_EA, eaBufferSize ) +
|
|||
|
sizeof(SRV_EA_INFORMATION);
|
|||
|
|
|||
|
eaInformation = ALLOCATE_NONPAGED_POOL( eaBufferSize, BlockTypeDataBuffer );
|
|||
|
|
|||
|
if ( eaInformation == NULL ) {
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"SrvQueryOs2FeaList: Unable to allocate %d bytes from nonpaged "
|
|||
|
"pool.",
|
|||
|
eaBufferSize,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the EAs.
|
|||
|
//
|
|||
|
|
|||
|
while(1) {
|
|||
|
|
|||
|
ULONG feaSize;
|
|||
|
|
|||
|
status = SrvQueryEaFile(
|
|||
|
isFirstCall,
|
|||
|
FileHandle,
|
|||
|
ntGetEa,
|
|||
|
ntGetEaBufferLength,
|
|||
|
eaInformation,
|
|||
|
eaBufferSize,
|
|||
|
&errorOffset
|
|||
|
);
|
|||
|
|
|||
|
if ( status == STATUS_NO_MORE_EAS ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(GeaList) ) {
|
|||
|
*EaErrorOffset = SrvGetOs2GeaOffsetOfError(
|
|||
|
errorOffset,
|
|||
|
ntGetEa,
|
|||
|
GeaList
|
|||
|
);
|
|||
|
//
|
|||
|
// SrvQueryEaFile has already logged the error. Do not
|
|||
|
// create another log entry here.
|
|||
|
//
|
|||
|
|
|||
|
IF_DEBUG(SMB_ERRORS) {
|
|||
|
PGEA errorGea = (PGEA)( (PCHAR)GeaList + *EaErrorOffset );
|
|||
|
SrvPrint1( "EA error offset in GEALIST: 0x%lx\n", *EaErrorOffset );
|
|||
|
SrvPrint1( "name: %s\n", errorGea->szName );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
isFirstCall = FALSE;
|
|||
|
ntFullEa = eaInformation->CurrentEntry;
|
|||
|
|
|||
|
//
|
|||
|
// See if there is enough room to hold the EA in the user buffer.
|
|||
|
//
|
|||
|
// *** STATUS_BUFFER_OVERFLOW is a special status code for the
|
|||
|
// find2 logic. See that code before making changes here.
|
|||
|
|
|||
|
feaSize = SmbGetOs2SizeOfNtFullEa( ntFullEa );
|
|||
|
if ( feaSize > (ULONG)( (PCHAR)FeaList + BufferLength - (PCHAR)fea ) ) {
|
|||
|
status = STATUS_BUFFER_OVERFLOW;
|
|||
|
goto exit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the NT format EA to OS/2 1.2 format and set the fea
|
|||
|
// pointer for the next iteration.
|
|||
|
//
|
|||
|
|
|||
|
fea = SrvNtFullEaToOs2( fea, ntFullEa );
|
|||
|
|
|||
|
ASSERT( (ULONG_PTR)fea <= (ULONG_PTR)FeaList + BufferLength );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the number of bytes in the FEALIST.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUlong(
|
|||
|
&FeaList->cbList,
|
|||
|
PTR_DIFF(fea, FeaList)
|
|||
|
);
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
exit:
|
|||
|
//
|
|||
|
// Deallocate the buffers used to hold the NT get and full EA lists.
|
|||
|
//
|
|||
|
|
|||
|
if ( ntGetEa != NULL && ARGUMENT_PRESENT(GeaList) ) {
|
|||
|
DEALLOCATE_NONPAGED_POOL( ntGetEa );
|
|||
|
}
|
|||
|
|
|||
|
if ( eaInformation != NULL ) {
|
|||
|
DEALLOCATE_NONPAGED_POOL( eaInformation );
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // SrvQueryOs2FeaList
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvSetOs2FeaList (
|
|||
|
IN HANDLE FileHandle,
|
|||
|
IN PFEALIST FeaList,
|
|||
|
IN ULONG BufferLength,
|
|||
|
OUT PUSHORT EaErrorOffset
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Sets the EAs on a file given an OS/2 1.2 representation of the EAs.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FileHandle - handle to a file open with FILE_WRITE_EA access whose
|
|||
|
EAs are to be set.
|
|||
|
|
|||
|
FeaList - a pointer to the location where the OS/2 FEALIST is stored.
|
|||
|
|
|||
|
BufferLength - maximum size of the buffer the FEALIST structure can
|
|||
|
have. This is used to prevent a malicious redirector from causing
|
|||
|
an access violation in the server.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - what happened
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
PFILE_FULL_EA_INFORMATION ntFullEa;
|
|||
|
ULONG ntFullEaBufferLength;
|
|||
|
ULONG errorOffset;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
*EaErrorOffset = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Special case for too-small FEALIST: don't set anything.
|
|||
|
// sizeof(FEALIST) will cover the buffer size and one FEA structure.
|
|||
|
// Without at least this much info there isn't anything to do here.
|
|||
|
//
|
|||
|
// NOTE: If there is space for at least one FEA in the list, but the
|
|||
|
// FEA is corrupt, we'll return an error below, i.e. insufficient
|
|||
|
// information is not an error condition while corrupt info is.
|
|||
|
//
|
|||
|
|
|||
|
if ( BufferLength <= sizeof(FEALIST) ||
|
|||
|
SmbGetUlong( &FeaList->cbList ) <= sizeof(FEALIST)) {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the value in Fealist->cbList is legitimate.
|
|||
|
//
|
|||
|
|
|||
|
if ( SmbGetUlong( &FeaList->cbList ) > BufferLength ) {
|
|||
|
DEBUG SrvPrint2(
|
|||
|
"SrvSetOs2FeaList: EA list size is inconsistent. Actual size"
|
|||
|
"is %d, expected maximum size is %d",
|
|||
|
SmbGetUlong( &FeaList->cbList ),
|
|||
|
BufferLength
|
|||
|
);
|
|||
|
return STATUS_OS2_EA_LIST_INCONSISTENT;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the FEALIST to NT style.
|
|||
|
//
|
|||
|
|
|||
|
status = SrvOs2FeaListToNt(
|
|||
|
FeaList,
|
|||
|
&ntFullEa,
|
|||
|
&ntFullEaBufferLength,
|
|||
|
EaErrorOffset
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
//
|
|||
|
// SrvOs2FeaListToNt has already logged the error. Do not
|
|||
|
// create another log entry here.
|
|||
|
//
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the file's EAs with a directly-built IRP. Doing this rather
|
|||
|
// than calling the NtSetEaFile system service prevents a copy of
|
|||
|
// the input data from occurring.
|
|||
|
//
|
|||
|
// *** The operation is performed synchronously.
|
|||
|
//
|
|||
|
|
|||
|
status = SrvIssueSetEaRequest(
|
|||
|
FileHandle,
|
|||
|
ntFullEa,
|
|||
|
ntFullEaBufferLength,
|
|||
|
&errorOffset
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
|
|||
|
//
|
|||
|
// An error occurred. Find the offset into the EA list of the
|
|||
|
// error.
|
|||
|
//
|
|||
|
|
|||
|
*EaErrorOffset = SrvGetOs2FeaOffsetOfError(
|
|||
|
errorOffset,
|
|||
|
ntFullEa,
|
|||
|
FeaList
|
|||
|
);
|
|||
|
|
|||
|
INTERNAL_ERROR(
|
|||
|
ERROR_LEVEL_EXPECTED,
|
|||
|
"SrvSetOs2FeaList: SrvIssueSetEaRequest returned %X",
|
|||
|
status,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
/*
|
|||
|
This logging can potentially crash the server on a bad packet. Removed.
|
|||
|
IF_DEBUG(ERRORS) {
|
|||
|
PFEA errorFea = (PFEA)( (PCHAR)FeaList + *EaErrorOffset );
|
|||
|
SrvPrint1( "EA error offset in FEALIST: 0x%lx\n", *EaErrorOffset );
|
|||
|
SrvPrint3( "name: %s, value len: %ld, value: %s", (PCHAR)(errorFea+1),
|
|||
|
SmbGetUshort( &errorFea->cbValue ),
|
|||
|
(PCHAR)(errorFea+1) + errorFea->cbName + 1 );
|
|||
|
}
|
|||
|
*/
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Deallocate the buffer used to hold the NT full EA list.
|
|||
|
//
|
|||
|
|
|||
|
DEALLOCATE_NONPAGED_POOL( ntFullEa );
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // SrvSetOs2FeaList
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SrvConstructNullOs2FeaList (
|
|||
|
IN PFILE_GET_EA_INFORMATION NtGeaList,
|
|||
|
OUT PFEALIST FeaList,
|
|||
|
IN ULONG BufferLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Converts a single NT full EA list to OS/2 FEALIST style for files
|
|||
|
with no eas. When a file has no eas but a GEAlist was supplied,
|
|||
|
we need to return an ealist that has all the attributes specified
|
|||
|
in the GEAlist present but with CbValues of 0. This routine was
|
|||
|
specifically written for the files . and .. but can be used to get
|
|||
|
the FEAlist of a no ea file given the NT Gea list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NtGetEaList - if non-NULL, an NT style get EA list used to get only
|
|||
|
a subset of the files EAs rather than all the EAs. Only the
|
|||
|
EAs listed in GEALIST are returned.
|
|||
|
|
|||
|
FeaList - where to write the OS/2 1.2 style FEALIST for the file.
|
|||
|
|
|||
|
BufferLength - length of Buffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - STATUS_SUCCESS, STATUS_BUFFER_OVERFLOW if the EAs wouldn't
|
|||
|
fit in Buffer, or a value returned by NtQuery{Information,Ea}File.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PCHAR ptr;
|
|||
|
PFEA fea = FeaList->list;
|
|||
|
PFILE_GET_EA_INFORMATION currentGea = NtGeaList;
|
|||
|
LONG remainingBytes = BufferLength;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
PAGED_CODE( );
|
|||
|
|
|||
|
//
|
|||
|
// Get the EAs.
|
|||
|
//
|
|||
|
|
|||
|
for ( ; ; ) {
|
|||
|
|
|||
|
//
|
|||
|
// Is our buffer big enough?
|
|||
|
//
|
|||
|
|
|||
|
remainingBytes -= ( sizeof( FEA ) + currentGea->EaNameLength + 1 );
|
|||
|
|
|||
|
if ( remainingBytes < 0 ) {
|
|||
|
|
|||
|
return STATUS_BUFFER_OVERFLOW;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We know what these are.
|
|||
|
//
|
|||
|
|
|||
|
fea->fEA = 0;
|
|||
|
fea->cbName = currentGea->EaNameLength;
|
|||
|
SmbPutUshort( &fea->cbValue, 0);
|
|||
|
|
|||
|
//
|
|||
|
// Copy the attribute name.
|
|||
|
//
|
|||
|
|
|||
|
for ( i = 0, ptr = (PCHAR) (fea + 1);
|
|||
|
i < (ULONG) currentGea->EaNameLength;
|
|||
|
i++, ptr++) {
|
|||
|
|
|||
|
*ptr = RtlUpperChar( currentGea->EaName[i] );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
*ptr++ = '\0';
|
|||
|
|
|||
|
fea = (PFEA) ptr;
|
|||
|
|
|||
|
//
|
|||
|
// Is this the last one?
|
|||
|
//
|
|||
|
|
|||
|
if ( currentGea->NextEntryOffset == 0 ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Move to the next attribute
|
|||
|
//
|
|||
|
|
|||
|
currentGea = (PFILE_GET_EA_INFORMATION)
|
|||
|
((PCHAR) currentGea + currentGea->NextEntryOffset);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the number of bytes in the FEALIST.
|
|||
|
//
|
|||
|
|
|||
|
SmbPutUlong(
|
|||
|
&FeaList->cbList,
|
|||
|
(ULONG)((ULONG_PTR)fea - (ULONG_PTR)FeaList)
|
|||
|
);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // SrvConstructNullOs2FeaList
|