Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1974 lines
51 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dllea.c
Abstract:
This module implements the OS/2 V2.0 extended attributes manipulation
API calls
Author:
Therese Stowell (thereses) 03-Nov-1989
Revision History:
Yaron Shamir (yarons) 20-May-1991
Fixed bugs found by filio test suite.
--*/
#define INCL_OS2V20_ERRORS
#define INCL_OS2V20_FILESYS
#include "os2dll.h"
BOOLEAN
NtTimeToFatTimeAndDate (
OUT PFTIME FatTime,
OUT PFDATE FatDate,
IN LARGE_INTEGER NtTime
)
/*++
Routine Description:
This routine converts a 64-bit NT time value to its corresponding
Fat Date/Time structure
Arguments:
FatTime - Receives the corresponding Fat Time
FatDate - Receives the corresponding Fat Date
NtTime - Supplies the input time to convert
Return Value:
BOOLEAN - TRUE if the time is within the range of Fat and FALSE
otherwise
adapted from NtTimeToFatTimeandDate
--*/
{
TIME_FIELDS TimeFields;
LARGE_INTEGER LocalTime;
if (NtTime.LowPart == 0 && NtTime.HighPart == 0) {
FatTime->twosecs = 0;
FatTime->minutes = 0;
FatTime->hours = 0;
FatDate->year = 0;
FatDate->month = 0;
FatDate->day = 0;
return FALSE;
}
//
// Convert UTC to Local time
//
if (!(NT_SUCCESS(RtlSystemTimeToLocalTime ( &NtTime, &LocalTime))))
{
return FALSE;
}
//
// Convert the input to the a time field record
//
RtlTimeToTimeFields( &LocalTime, &TimeFields );
//
// Check the range of the date found in the time field record
//
if ((TimeFields.Year < 1980) || (TimeFields.Year > (1980 + 128))) {
return FALSE;
}
//
// The year will fit in Fat so simply copy over the information
//
FatTime->twosecs = (USHORT) (TimeFields.Second / 2);
FatTime->minutes = TimeFields.Minute;
FatTime->hours = TimeFields.Hour;
FatDate->year = (USHORT) (TimeFields.Year - 1980);
FatDate->month = TimeFields.Month;
FatDate->day = TimeFields.Day;
return TRUE;
}
BOOLEAN
FatTimeAndDateToNtTime(
OUT PLARGE_INTEGER NtTime,
IN FTIME FatTime,
IN FDATE FatDate
)
/*++
Routine Description:
This routine converts a Fat time/date value to an NtTime
Arguments:
NtTime - Receives the corresponding date/time
FatTime - Supplies the input time
FatDate - Supplies the input date
Return Value:
BOOLEAN - Return TRUE if the input time/date is well formed and
FALSE otherwise
--*/
{
TIME_FIELDS TimeFields;
LARGE_INTEGER LocalTime;
//
// Pack the input time/date into a time field record
//
TimeFields.Year = (CSHORT) (FatDate.year + 1980);
TimeFields.Month = FatDate.month;
TimeFields.Day = FatDate.day;
TimeFields.Hour = FatTime.hours;
TimeFields.Minute = FatTime.minutes;
TimeFields.Second = (CSHORT) (FatTime.twosecs * 2);
TimeFields.Milliseconds = 0;
//
// Convert the time field record to Nt LARGE_INTEGER
//
if (!RtlTimeFieldsToTime( &TimeFields, &LocalTime )) {
NtTime->LowPart = 0;
NtTime->HighPart = 0;
return FALSE;
}
//
// Convert Local time to UTC
//
if (!(NT_SUCCESS(RtlLocalTimeToSystemTime ( &LocalTime, NtTime))))
{
NtTime->LowPart = 0;
NtTime->HighPart = 0;
return FALSE;
}
return TRUE;
}
APIRET
GetEaListLength(
IN HANDLE NtHandle,
OUT PULONG EaListSize
)
/*++
Routine Description:
This routine returns the OS/2 format size of an EA list, given an NT
handle to the open file.
Arguments:
NtHandle - handle to file to return EA size of
EaListSize - where to store EA size
Return Value:
ERROR_INVALID_ACCESS - handle not open in a mode that allows ea size
retrieval.
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_EA_INFORMATION EaInfo;
do {
Status = NtQueryInformationFile(NtHandle,
&IoStatus,
&EaInfo,
sizeof (EaInfo),
FileEaInformation);
} while (RetryIO(Status, NtHandle));
if (!(NT_SUCCESS(Status))) {
return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_ACCESS));
}
*EaListSize = MAX(MINFEALISTSIZE, EaInfo.EaSize); // FEA list size
return NO_ERROR;
}
APIRET
SetEAList(
IN HANDLE NtHandle,
IN OUT PBYTE UserBuffer,
IN ULONG Length
)
/*++
Routine Description:
This routine sets a list of EAs for a particular file.
Arguments:
NtHandle - handle to file to store EAs for
UserBuffer - OS/2 format list of EAs
Length - length of data in UserBuffer
Return Value:
ERROR_EA_LIST_TOO_LONG - EA list exceeds maximum length.
ERROR_INSUFFICIENT_BUFFER - buffer passed in is not long enough to contain
an EAOP.
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
PFEA2 FeaPtr;
ULONG FeaListLength;
if (Length < (sizeof (EAOP2))) {
return ERROR_INSUFFICIENT_BUFFER;
}
try {
FeaListLength = ((PEAOP2)UserBuffer)->fpFEA2List->cbList;
FeaListLength -= MINFEALISTSIZE; // subtract size of cbList
FeaPtr = ((PEAOP2)UserBuffer)->fpFEA2List->list;
Od2ProbeForRead(FeaPtr,FeaListLength,1);
} except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
if (FeaListLength != 0) {
do {
Status = NtSetEaFile(NtHandle,
&IoStatus,
FeaPtr,
FeaListLength
);
} while (RetryIO(Status, NtHandle));
if (!NT_SUCCESS(Status))
{
try
{
((PEAOP2)UserBuffer)->oError = IoStatus.Information; // BUGBUG check this value
} except( EXCEPTION_EXECUTE_HANDLER )
{
Od2ExitGP();
}
#if DBG
IF_OD2_DEBUG( TEMP )
{
DbgPrint("SetEAList: NtSetEaFile failed, Status=%x\n",
Status);
}
#endif
return (Or2MapNtStatusToOs2Error(Status, ERROR_EA_LIST_INCONSISTENT));
}
}
return NO_ERROR;
}
APIRET
GetGEAList(
IN HANDLE NtHandle,
OUT PBYTE UserBuffer,
IN ULONG Length
)
/*++
Routine Description:
This routine retrieves a list of EAs given a list of EA names.
Arguments:
NtHandle - handle to file to retrieve EAs for
UserBuffer - EAOP containing list of names and buffer to store them in
Length - length of data in UserBuffer
Return Value:
ERROR_BUFFER_OVERFLOW - buffer passed in is not long enough to contain
an EAOP or destination buffer is not large enough to contain all the
requested EAs.
ERROR_NOT_ENOUGH_MEMORY - memory to read the EAs into could not be
allocated.
ERROR_EA_LIST_INCONSISTENT - the length of the EA name doesn't correspond
to the length of the buffer
--*/
{
NTSTATUS Status;
PGEA2 GeaPtr;
PFEA2 FeaPtr;
ULONG GeaListLength;
ULONG FeaListLength;
FEA2LIST *fpFEA2List;
IO_STATUS_BLOCK IoStatus;
if (Length < (sizeof (EAOP2)))
{
return ERROR_BUFFER_OVERFLOW;
}
try
{
fpFEA2List = ((PEAOP2)UserBuffer)->fpFEA2List;
FeaListLength = fpFEA2List->cbList;
if (FeaListLength < MINFEALISTSIZE)
{
return ERROR_BUFFER_OVERFLOW;
}
FeaListLength -= MINFEALISTSIZE; // subtract size of cbList
FeaPtr = fpFEA2List->list;
Od2ProbeForWrite(FeaPtr,FeaListLength,1);
GeaListLength = ((PEAOP2)UserBuffer)->fpGEA2List->cbList - MINFEALISTSIZE;
GeaPtr = ((PEAOP2)UserBuffer)->fpGEA2List->list;
Od2ProbeForRead(GeaPtr,GeaListLength,1);
} except( EXCEPTION_EXECUTE_HANDLER )
{
Od2ExitGP();
}
if (GeaListLength != 0)
{
do {
Status = NtQueryEaFile(NtHandle,
&IoStatus,
FeaPtr,
FeaListLength,
FALSE,
GeaPtr,
GeaListLength,
NULL,
FALSE
);
} while (RetryIO(Status, NtHandle));
if (!NT_SUCCESS(Status))
{
try
{
// BUGBUG check this value
((PEAOP2)UserBuffer)->oError = IoStatus.Information;
} except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("NtQueryEaFile failed, Status=%X\n", Status);
}
#endif
if (Status == STATUS_NO_EAS_ON_FILE)
{
/* No EAs on file. Just set the .cbList field to 4 to indicate to
the OS/2 1.x emulation that fact. We still need to build an FEA
list with empty data fields */
fpFEA2List->cbList=sizeof(ULONG);
return NO_ERROR;
}
else
return Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
}
/* Fix the FEA2 list's .cbList created by
GetGEAList:
- unlike Cruiser, NT leaves the .cbList
untouched (rather than reflecting the total FEA2
list size)
- the .fEA field of each FEA2 entry is set incorrectly (i.e. not
FEA_NEEDEA)
*/
/* BUGBUG - Try to remove the workaround below after NT Beta */
Od2FixFEA2List(fpFEA2List);
}
else
fpFEA2List->cbList = sizeof(ULONG); /* To signify an empty FEA2 list */
return NO_ERROR;
}
VOID
MapAttributesToNt(
IN USHORT Os2Attributes,
OUT OPTIONAL PBOOLEAN Directory,
OUT PULONG NtAttributes
)
/*++
Routine Description:
This routine converts attributes from OS/2 format to NT format.
Arguments:
Os2Attributes - the attributes in OS/2 format.
Directory - the Directory field associated with the NT directory
entry.
NtAttributes - where the converted attributes are returned.
Return Value:
none.
--*/
{
*NtAttributes = (ULONG) Os2Attributes;
if (Os2Attributes == 0) {
*NtAttributes = FILE_ATTRIBUTE_NORMAL;
}
else if (Os2Attributes & FILE_DIRECTORY) {
if (Directory != NULL) {
*Directory = TRUE;
}
*NtAttributes &= ~FILE_DIRECTORY;
}
}
APIRET
SetFileInfo(
IN HANDLE NtHandle,
IN PBYTE Buffer,
IN ULONG Length
)
/*++
Routine Description:
This routine sets attributes for a given file
Arguments:
NtHandle - handle to file to set attributes for
UserBuffer - buffer containing attributes
Length - length of data in UserBuffer
Return Value:
ERROR_INVALID_ACCESS - user doesn't have access to set the attributes
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_BASIC_INFORMATION BasicInfo;
FILE_STANDARD_INFORMATION StandardInfo;
PFILESTATUS InfoBufPtr;
USHORT attrFileVal;
//
// Length has to be at least the date/time fields i.e. 12
//
if (Length < 12) {
return ERROR_INSUFFICIENT_BUFFER;
}
InfoBufPtr = (PFILESTATUS) Buffer;
// if zero is passed in as OS/2 time and date, pass zero as NT time.
// otherwise, call conversion routine.
try {
if (( *(PUSHORT)( &(InfoBufPtr->ftimeCreation) ) == 0) &&
( *(PUSHORT)( &(InfoBufPtr->fdateCreation) ) == 0)) {
BasicInfo.CreationTime.LowPart = 0;
BasicInfo.CreationTime.HighPart = 0;
}
else {
if (!FatTimeAndDateToNtTime(&BasicInfo.CreationTime,
InfoBufPtr->ftimeCreation,
InfoBufPtr->fdateCreation))
return ERROR_INVALID_PARAMETER;
}
if (( *(PUSHORT)( &(InfoBufPtr->ftimeLastAccess) ) == 0) &&
( *(PUSHORT)( &(InfoBufPtr->fdateLastAccess) ) == 0)) {
BasicInfo.LastAccessTime.LowPart = 0;
BasicInfo.LastAccessTime.HighPart = 0;
}
else {
if (!FatTimeAndDateToNtTime(&BasicInfo.LastAccessTime,
InfoBufPtr->ftimeLastAccess,
InfoBufPtr->fdateLastAccess))
return ERROR_INVALID_PARAMETER;
}
if (( *(PUSHORT)( &(InfoBufPtr->ftimeLastWrite) ) == 0) &&
( *(PUSHORT)( &(InfoBufPtr->fdateLastWrite) ) == 0)) {
BasicInfo.LastWriteTime.LowPart = 0;
BasicInfo.LastWriteTime.HighPart = 0;
}
else {
if (!FatTimeAndDateToNtTime(&BasicInfo.LastWriteTime,
InfoBufPtr->ftimeLastWrite,
InfoBufPtr->fdateLastWrite))
return ERROR_INVALID_PARAMETER;
}
//
// If the current attribute of the file/directory is FILE_ATTRIBUTE_DIRECTORY
// leave it like that.
//
do {
Status = NtQueryInformationFile(NtHandle,
&IoStatus,
&StandardInfo,
sizeof (StandardInfo),
FileStandardInformation
);
} while (RetryIO(Status, NtHandle));
if (!(NT_SUCCESS(Status))) {
return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_ACCESS));
}
if (StandardInfo.Directory) {
BasicInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
} else {
attrFileVal = *((PUSHORT) (&(InfoBufPtr->attrFile)));
if (attrFileVal & ~ATTR_CHANGEABLE) {
return ERROR_ACCESS_DENIED;
}
if (attrFileVal == 0) {
BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
} else {
BasicInfo.FileAttributes = attrFileVal;
}
}
} except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
// don't want to change these fields, so set them to zero.
BasicInfo.ChangeTime.LowPart = 0;
BasicInfo.ChangeTime.HighPart = 0;
do {
Status = NtSetInformationFile(NtHandle,
&IoStatus,
&BasicInfo,
sizeof (BasicInfo),
FileBasicInformation);
} while (RetryIO(Status, NtHandle));
if (!(NT_SUCCESS(Status))) {
return (Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED));
}
return NO_ERROR;
}
APIRET
GetFileInfo(
IN HANDLE NtHandle,
IN ULONG FileInformationLevel,
OUT PBYTE Buffer,
IN ULONG Length
)
/*++
Routine Description:
This routine retrieves attributes and EA size for a given file
Arguments:
NtHandle - handle to file to retrieve attributes for
FileInformationLevel - which type of data to return
Buffer - buffer to store attributes in (unprobed)
Length - length of UserBuffer
Return Value:
ERROR_INVALID_ACCESS - user doesn't have access to retrieve the attributes
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_BASIC_INFORMATION BasicInfo;
FILE_STANDARD_INFORMATION StandardInfo;
PFILEFINDBUF4 InfoBufPtr;
APIRET RetCode;
USHORT Attributes;
if (Length < sizeof(FILESTATUS))
return ERROR_BUFFER_OVERFLOW;
// get end of file information
do {
Status = NtQueryInformationFile(NtHandle,
&IoStatus,
&StandardInfo,
sizeof (StandardInfo),
FileStandardInformation);
} while (RetryIO(Status, NtHandle));
if (!(NT_SUCCESS(Status))) {
return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE));
}
// get date/time information
do {
Status = NtQueryInformationFile(NtHandle,
&IoStatus,
&BasicInfo,
sizeof (BasicInfo),
FileBasicInformation);
} while (RetryIO(Status, NtHandle));
if (!(NT_SUCCESS(Status))) {
return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_HANDLE));
}
InfoBufPtr = (PFILEFINDBUF4) Buffer;
// the data returned doesn't have a oNextEntryOffset field.
InfoBufPtr = (PFILEFINDBUF4) ((ULONG)InfoBufPtr - sizeof(InfoBufPtr->oNextEntryOffset));
try {
// convert creation time
NtTimeToFatTimeAndDate(&InfoBufPtr->ftimeCreation,
&InfoBufPtr->fdateCreation,
BasicInfo.CreationTime);
// convert last access time
NtTimeToFatTimeAndDate(&InfoBufPtr->ftimeLastAccess,
&InfoBufPtr->fdateLastAccess,
BasicInfo.LastAccessTime);
// convert last write time
NtTimeToFatTimeAndDate(&InfoBufPtr->ftimeLastWrite,
&InfoBufPtr->fdateLastWrite,
BasicInfo.LastWriteTime);
// BUGBUG - Therese, what should we do here if .HighPart is non-zero
InfoBufPtr->cbFile = StandardInfo.EndOfFile.LowPart;
InfoBufPtr->cbFileAlloc = StandardInfo.AllocationSize.LowPart;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("cbFile is %ld\n",StandardInfo.EndOfFile.LowPart);
DbgPrint("cbFileAlloc is %ld\n",StandardInfo.AllocationSize.LowPart);
}
#endif
MapAttributesToOs2(BasicInfo.FileAttributes,&Attributes);
InfoBufPtr->attrFile = Attributes & ATTR_ALL;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("attrFile is %lx\n",Attributes & ATTR_ALL);
}
#endif
if (FileInformationLevel == FIL_STANDARD)
return NO_ERROR;
if (Length < (sizeof(FILESTATUS) + sizeof(ULONG)))
return ERROR_BUFFER_OVERFLOW;
RetCode = GetEaListLength(NtHandle,&InfoBufPtr->cbList);
} except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("GetEaListLength returned %ld\n",RetCode);
DbgPrint("EaListLength is %ld\n",InfoBufPtr->cbList);
}
#endif
return RetCode;
}
APIRET
DosQueryFileInfo(
IN HFILE FileHandle,
IN ULONG FileInformationLevel,
OUT PBYTE Buffer,
IN ULONG Length
)
/*++
Routine Description:
This routine retrieves attributes, EA size, and EAs for a given file
Arguments:
FileHandle - OS/2 handle to file to retrieve information for
FileInformationLevel - type of data to return
UserBuffer - buffer to store information in
Length - length of UserBuffer
Return Value:
ERROR_INVALID_LEVEL - infolevel is invalid
ERROR_INVALID_HANDLE - file handle is not allocated
ERROR_INVALID_ACCESS - user doesn't have access to retrieve the attributes or EAs
--*/
{
APIRET RetCode;
HANDLE NtHandle;
PFILE_HANDLE hFileRecord;
#if DBG
PSZ RoutineName;
RoutineName = "DosQueryFileInfo";
#endif
if ((FileInformationLevel < FIL_STANDARD) || (FileInformationLevel > MAXQFILEINFOLEVEL))
return ERROR_INVALID_LEVEL;
AcquireFileLockShared(
#if DBG
RoutineName
#endif
);
RetCode = DereferenceFileHandle(FileHandle,&hFileRecord);
if (RetCode != NO_ERROR) {
ReleaseFileLockExclusive(
#if DBG
RoutineName
#endif
);
return RetCode;
}
//
// DosQueryFileInfo is valid for pipes and devices
//
NtHandle = hFileRecord->NtHandle;
ReleaseFileLockShared(
#if DBG
RoutineName
#endif
);
if (FileInformationLevel <= FIL_QUERYEASIZE) {
RetCode = GetFileInfo(NtHandle,
FileInformationLevel,
Buffer,
Length);
}
else if (FileInformationLevel == FIL_QUERYEASFROMLIST)
{
RetCode = GetGEAList(NtHandle,
Buffer,
Length);
}
return RetCode;
}
APIRET
DosSetFileInfo(
IN HFILE FileHandle,
IN ULONG FileInformationLevel,
IN OUT PBYTE Buffer,
IN ULONG Length
)
/*++
Routine Description:
This routine sets attributes and EAs for a given file
Arguments:
FileHandle - OS/2 handle to file to set information for
FileInformationLevel - type of data to set
UserBuffer - buffer containing information to set
Length - length of data in UserBuffer
Return Value:
ERROR_INVALID_LEVEL - infolevel is invalid
ERROR_INVALID_HANDLE - file handle is not allocated
ERROR_INVALID_ACCESS - user doesn't have access to set the attributes or EAs
--*/
{
APIRET RetCode;
HANDLE NtHandle;
PFILE_HANDLE hFileRecord;
IO_STATUS_BLOCK IoStatusBlock;
FILE_ACCESS_INFORMATION FileAccessInfo;
#if DBG
PSZ RoutineName;
RoutineName = "DosSetFileInfo";
#endif
if ((FileInformationLevel < FIL_STANDARD) ||
(FileInformationLevel > MAXSETFILEINFOLEVEL))
return ERROR_INVALID_LEVEL;
AcquireFileLockShared(
#if DBG
RoutineName
#endif
);
RetCode = DereferenceFileHandle(FileHandle,&hFileRecord);
if (RetCode != NO_ERROR) {
ReleaseFileLockExclusive(
#if DBG
RoutineName
#endif
);
return RetCode;
}
if (hFileRecord->FileType &
(FILE_TYPE_DEV | FILE_TYPE_PIPE | FILE_TYPE_NMPIPE | FILE_TYPE_PSDEV)) {
ReleaseFileLockShared(
#if DBG
RoutineName
#endif
);
return ERROR_INVALID_HANDLE;
}
NtHandle = hFileRecord->NtHandle;
ReleaseFileLockShared(
#if DBG
RoutineName
#endif
);
//
// We need to check whether we have READONLY access to this handle.
// If true, we should return with ERROR_ACCESS_DENIED.
//
do {
RetCode = NtQueryInformationFile(NtHandle,
&IoStatusBlock,
&FileAccessInfo,
sizeof(FILE_ACCESS_INFORMATION),
FileAccessInformation
);
} while (RetryIO(RetCode, NtHandle));
if (!(NT_SUCCESS(RetCode))) {
return (Or2MapNtStatusToOs2Error(RetCode, ERROR_INVALID_ACCESS));
}
if (!(FileAccessInfo.AccessFlags & FILE_WRITE_DATA)) {
return(ERROR_ACCESS_DENIED);
}
if (FileInformationLevel == FIL_STANDARD) {
//
// This is here to fix a problem with DosSetfileInfo for this function
// the attrFile is not used but SetFileInfo wants it to be set
//
if (Length >= ((sizeof(FILESTATUS)) - sizeof(USHORT))) {
PFILESTATUS InfoBufPtr;
InfoBufPtr = (PFILESTATUS) Buffer;
(*(PUSHORT) (&(InfoBufPtr->attrFile))) = (USHORT) FILE_ARCHIVED;
}
RetCode = SetFileInfo(NtHandle,
Buffer,
Length);
}
else if (FileInformationLevel == FIL_SETEAS) {
RetCode = SetEAList(NtHandle,
Buffer,
Length);
}
else
RetCode = ERROR_NOT_SUPPORTED;
return RetCode;
}
APIRET
DosQueryPathInfo(
IN PSZ pszPath,
IN ULONG FileInformationLevel,
OUT PBYTE Buffer,
IN ULONG Length
)
/*++
Routine Description:
This routine retrieves attributes, EA size, and EAs for a given file
and canonicalizes filenames.
Arguments:
pszPath - filename to retrieve information about
FileInformationLevel - type of data to return
UserBuffer - buffer to store information in
Length - length of UserBuffer
Return Value:
ERROR_INVALID_LEVEL - infolevel is invalid
ERROR_INVALID_ACCESS - user doesn't have access to retrieve the attributes
or EAs
ERROR_BUFFER_OVERFLOW - requested information won't fit in buffer
--*/
{
NTSTATUS Status;
APIRET RetCode = NO_ERROR;
STRING CanonicalNameString;
UNICODE_STRING CanonicalNameString_U;
ULONG FileType;
ULONG FileFlags;
STRING AddString;
STRING TransBuffer;
USHORT PrefixLength;
PCHAR pServerName;
USHORT DriveIndex;
HANDLE PathHandle;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
ULONG Flags;
ACCESS_MASK RequestedAccess;
ULONG ShareAccess;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("entering qpathinfo with %s\n",pszPath);
}
#endif
if ((FileInformationLevel < FIL_STANDARD) ||
(FileInformationLevel > MAXQPATHINFOLEVEL) ||
(FileInformationLevel == FIL_QUERYALLEAS))
return ERROR_INVALID_LEVEL;
if (FileInformationLevel == FIL_QUERYFULLNAME)
{
Flags = FULL_PATH_REQUIRED;
}
else {
Flags = 0;
}
RetCode = Od2Canonicalize(pszPath,
CANONICALIZE_FILE_DEV_OR_PIPE,
&CanonicalNameString,
NULL,
&FileFlags,
&FileType
);
if (RetCode != NO_ERROR) {
return RetCode;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("canonicalize returned %s and file type %lu\n",
CanonicalNameString.Buffer, FileType);
}
#endif
//
// If Od2Canonicalize return with NO_ERROR then the file name is valid
//
if (FileInformationLevel == FIL_NAMEISVALID) {
return(NO_ERROR);
}
//
// Special handling of <boot-drive>:\config.sys
// opening this file is mapped to the OS/2 SS config.sys
//
if (Od2FileIsConfigSys(&CanonicalNameString, OPEN_ACCESS_READONLY, &Status)) {
if (!NT_SUCCESS(Status)) {
// failed to init for config.sys
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
}
FileFlags = 0;
FileType = FILE_TYPE_FILE;
}
if (FileInformationLevel == FIL_QUERYFULLNAME)
{
//
// Alocate space for translation buffers.
//
if ((AddString.Buffer = RtlAllocateHeap (Od2Heap, 0, 12)) == NULL) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Not enough memory to allocate buffer for AddString\n");
}
#endif
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Process the string.
//
TransBuffer.Length = 0;
switch (FileType) {
case FILE_TYPE_FILE:
//
// Od2Canonicalize returns \OS2SS\DRIVES\fullpath
// DosQPathInfo should return fullpath
//
PrefixLength = FILE_PREFIX_LENGTH;
AddString.Length = 0;
// convert path to lower case and drive letter to upper case
for (DriveIndex = 0;
DriveIndex < CanonicalNameString.Length - FILE_PREFIX_LENGTH;
DriveIndex++
) {
if (CanonicalNameString.Buffer[FILE_PREFIX_LENGTH + DriveIndex] == ':') {
CanonicalNameString.Buffer[FILE_PREFIX_LENGTH + DriveIndex - 1] =
toupper(CanonicalNameString.Buffer[FILE_PREFIX_LENGTH + DriveIndex - 1]);
}
else {
CanonicalNameString.Buffer[FILE_PREFIX_LENGTH + DriveIndex] =
tolower(CanonicalNameString.Buffer[FILE_PREFIX_LENGTH + DriveIndex]);
}
}
break;
case FILE_TYPE_DEV:
//
// Od2Canonicalize returns \DosDevices\device
// DosQPathInfo should return \DEV\device
//
PrefixLength = DEV_PREFIX_LENGTH;
AddString.Length = 5;
RtlMoveMemory(AddString.Buffer, "\\DEV\\", AddString.Length);
break;
case FILE_TYPE_NMPIPE:
//
// Od2Canonicalize returns \OS2SS\PIPE\pipename
// DosQPathInfo should return \PIPE\pipename
//
PrefixLength = NMPIPE_PREFIX_LENGTH;
AddString.Length = 6;
RtlMoveMemory(AddString.Buffer, "\\PIPE\\", AddString.Length);
RtlUpperString(&CanonicalNameString, &CanonicalNameString);
break;
case FILE_TYPE_UNC:
//
// Od2Canonicalize returns \OS2SS\UNC\fullpath
// DosQPathInfo should return full UNC path
//
PrefixLength = UNC_PREFIX_LENGTH;
AddString.Length = 2;
RtlMoveMemory(AddString.Buffer, "\\\\", AddString.Length);
// convert server name to upper case
for (pServerName = CanonicalNameString.Buffer + UNC_PREFIX_LENGTH;
*pServerName != '\\';
pServerName++
) {
*pServerName = toupper(*pServerName);
}
break;
case FILE_TYPE_PSDEV:
//
// Od2Canonicalize returns @n
// DosQPathInfo should return \DEV\device
//
PrefixLength = PSDEV_PREFIX_LENGTH;
AddString.Length = 5;
RtlMoveMemory(AddString.Buffer, "\\DEV\\", AddString.Length);
TransBuffer.Length = TransBuffer.MaximumLength = strlen(pszPath);
if ((TransBuffer.Buffer = RtlAllocateHeap (Od2Heap, 0, TransBuffer.Length)) == NULL) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Not enough memory to allocate buffer for translation\n");
}
#endif
RtlFreeHeap(Od2Heap,0,AddString.Buffer);
return(ERROR_NOT_ENOUGH_MEMORY);
}
RtlMoveMemory(TransBuffer.Buffer, pszPath, TransBuffer.Length);
RtlUpperString(&TransBuffer, &TransBuffer);
break;
case FILE_TYPE_MAILSLOT:
//
// Od2Canonicalize returns \OS2SS\MAILSLOT\mailslotname
// DosQPathInfo should return fullpath as a file
//
PrefixLength = MAILSLOT_PREFIX_LENGTH;
AddString.Buffer[0] = CONVERTTOASCII(Od2CurrentDisk);
AddString.Length = 12;
RtlMoveMemory(&AddString.Buffer[1], ":\\mailslot\\", AddString.Length);
break;
case FILE_TYPE_COM:
//
// Od2Canonicalize returns \DosDevices\device
// DosQPathInfo should return \DEV\device
//
PrefixLength = COM_PREFIX_LENGTH;
AddString.Length = 5;
RtlMoveMemory(AddString.Buffer, "\\DEV\\", AddString.Length);
break;
default:
PrefixLength = FILE_PREFIX_LENGTH;
AddString.Length = 0;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Unexpected file type %lu after canonicalize\n", FileType);
}
#endif
}
//
// Check user's buffer size.
//
if ((ULONG)(CanonicalNameString.Length + 1 - PrefixLength +
AddString.Length + TransBuffer.Length) > Length) {
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
RtlFreeHeap(Od2Heap,0,AddString.Buffer);
if (TransBuffer.Length) {
RtlFreeHeap(Od2Heap,0,TransBuffer.Buffer);
}
return ERROR_BUFFER_OVERFLOW;
}
//
// Copy full name to user's buffer.
//
try {
RtlMoveMemory(Buffer, AddString.Buffer, AddString.Length);
if (FileType == FILE_TYPE_PSDEV) {
RtlMoveMemory(Buffer + AddString.Length, TransBuffer.Buffer, TransBuffer.Length);
Buffer[AddString.Length + TransBuffer.Length] = 0;
}
else {
RtlMoveMemory(Buffer + AddString.Length,
CanonicalNameString.Buffer + PrefixLength,
CanonicalNameString.Length - PrefixLength);
Buffer[CanonicalNameString.Length + AddString.Length - PrefixLength] = 0;
}
} except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
RtlFreeHeap(Od2Heap,0,AddString.Buffer);
if (TransBuffer.Length) {
RtlFreeHeap(Od2Heap,0,TransBuffer.Buffer);
}
return NO_ERROR;
}
//
// check for file name being invalid type
//
if (FileFlags & CANONICALIZE_META_CHARS_FOUND)
{
RetCode = ERROR_INVALID_PATH;
}
else if ((FileFlags & CANONICALIZE_IS_ROOT_DIRECTORY) &&
(FileInformationLevel > FIL_QUERYEASIZE))
{
RetCode = ERROR_ACCESS_DENIED;
}
else if (FileType & (FILE_TYPE_DEV | FILE_TYPE_PSDEV))
{
RetCode = ERROR_INVALID_ACCESS;
}
if (RetCode != NO_ERROR)
{
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return RetCode;
}
if (FileFlags & CANONICALIZE_IS_ROOT_DIRECTORY)
{
RtlZeroMemory(Buffer,sizeof(FILESTATUS));
((PFILESTATUS)Buffer)->attrFile = FILE_DIRECTORY;
if (FileInformationLevel == FIL_QUERYEASIZE)
((PFILESTATUS2)Buffer)->cbList = MINFEALISTSIZE;
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return NO_ERROR;
}
//
// OS/2 requires open-for-read/deny-write for qpathinfo, except for
// FIL_STANDARD, which does not use sharing.
//
if (FileInformationLevel == FIL_STANDARD)
{
RequestedAccess = SYNCHRONIZE | FILE_READ_ATTRIBUTES;
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
else if (FileInformationLevel == FIL_QUERYEASIZE)
{
RequestedAccess = SYNCHRONIZE | FILE_READ_EA | FILE_READ_ATTRIBUTES;
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
else if (FileInformationLevel == FIL_QUERYEASFROMLIST)
{
RequestedAccess = SYNCHRONIZE | FILE_READ_EA | FILE_READ_ATTRIBUTES;
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
else
{
RequestedAccess = SYNCHRONIZE | FILE_READ_EA;
ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&CanonicalNameString_U,
&CanonicalNameString,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("DosQueryPathInfo: no memory for Unicode Conversion\n");
}
#endif
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return RetCode;
}
InitializeObjectAttributes(&Obja,
&CanonicalNameString_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
do {
Status = NtOpenFile(&PathHandle,
RequestedAccess,
&Obja,
&IoStatus,
ShareAccess,
FILE_SYNCHRONOUS_IO_NONALERT
);
} while (RetryCreateOpen(Status, &Obja));
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
RtlFreeUnicodeString (&CanonicalNameString_U);
if (!(NT_SUCCESS(Status)))
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("NtOpenFile returned %X\n",Status);
}
#endif
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_ACCESS);
return (RetCode);
}
//
// now that the file is opened, we need to make sure that it isn't a
// device that Canonicalize didn't detect.
//
if (CheckFileType(PathHandle,FILE_TYPE_DEV))
{
NtClose(PathHandle);
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("path is a device\n");
}
#endif
return ERROR_INVALID_ACCESS;
}
if (FileInformationLevel <= FIL_QUERYEASIZE)
{
RtlZeroMemory(Buffer,Length);
RetCode = GetFileInfo(PathHandle,
FileInformationLevel,
Buffer,
Length);
}
else if (FileInformationLevel == FIL_QUERYEASFROMLIST)
{
RetCode = GetGEAList(PathHandle,
Buffer,
Length);
}
else
{
RetCode = ERROR_NOT_SUPPORTED;
}
NtClose(PathHandle);
return RetCode;
}
APIRET
DosSetPathInfo(
IN PSZ pszPath,
IN ULONG FileInformationLevel,
IN OUT PBYTE Buffer,
IN ULONG Length,
IN ULONG Flags
)
/*++
Routine Description:
This routine sets attributes and EAs for a given file.
Arguments:
pszPath - filename to set information for
FileInformationLevel - type of data to set
UserBuffer - buffer containing information to set
Length - length of data in UserBuffer
Flags - if the DSPI_WRTTHRU bit is set then all disk writes will
go through any cache to the disk.
Return Value:
ERROR_INVALID_LEVEL - infolevel is invalid
ERROR_INVALID_ACCESS - user doesn't have access to set the attributes or
EAs
ERROR_BUFFER_OVERFLOW - requested information won't fit in buffer
--*/
{
NTSTATUS Status;
APIRET RetCode = NO_ERROR;
STRING CanonicalNameString;
UNICODE_STRING CanonicalNameString_U;
ULONG FileType;
ULONG FileFlags;
HANDLE PathHandle;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
ACCESS_MASK RequestedAccess;
ULONG CreateOptions;
if ((FileInformationLevel < FIL_STANDARD) ||
(FileInformationLevel > MAXSETPATHINFOLEVEL)) {
return ERROR_INVALID_LEVEL;
}
//
// check flags value.
//
if ((Flags != 0) && (Flags != DSPI_WRTTHRU)) {
return ERROR_INVALID_PARAMETER;
}
RetCode = Od2Canonicalize(pszPath,
CANONICALIZE_FILE_DEV_OR_PIPE,
&CanonicalNameString,
&PathHandle,
&FileFlags,
&FileType
);
if (RetCode != NO_ERROR) {
return RetCode;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("canonicalize returned %s\n",CanonicalNameString.Buffer);
}
#endif
//
// Special handling of <boot-drive>:\config.sys
// opening this file is mapped to the OS/2 SS config.sys
//
if (Od2FileIsConfigSys(&CanonicalNameString, OPEN_ACCESS_READWRITE, &Status)) {
if (!NT_SUCCESS(Status))
{
// failed to init for config.sys
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
}
FileFlags = 0;
FileType = FILE_TYPE_FILE;
PathHandle = NULL;
}
//
// check for file name being invalid type
//
if (FileFlags & CANONICALIZE_META_CHARS_FOUND) {
RetCode = ERROR_INVALID_PATH;
}
else if (FileType & (FILE_TYPE_DEV | FILE_TYPE_PSDEV)) {
RetCode = ERROR_INVALID_ACCESS;
}
else if (FileFlags & CANONICALIZE_IS_ROOT_DIRECTORY) {
RetCode = ERROR_ACCESS_DENIED;
}
if (RetCode != NO_ERROR) {
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return RetCode;
}
//
// OS/2 requires open-for-write/deny-both for setpathinfo.
//
if (FileInformationLevel == FIL_STANDARD)
RequestedAccess = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES;
else
RequestedAccess = SYNCHRONIZE | FILE_WRITE_EA;
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&CanonicalNameString_U,
&CanonicalNameString,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("DosSetPathInfo: no memory for Unicode Conversion\n");
}
#endif
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return RetCode;
}
InitializeObjectAttributes(&Obja,
&CanonicalNameString_U,
OBJ_CASE_INSENSITIVE,
PathHandle,
NULL);
CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
if (Flags & DSPI_WRTTHRU)
CreateOptions |= FILE_WRITE_THROUGH;
do {
Status = NtOpenFile(&PathHandle,
RequestedAccess,
&Obja,
&IoStatus,
FILE_SHARE_VALID_FLAGS,
CreateOptions
);
} while (RetryCreateOpen(Status, &Obja));
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
RtlFreeUnicodeString (&CanonicalNameString_U);
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtCreateFile returned %X\n",Status);
}
#endif
return (Or2MapNtStatusToOs2Error(Status, ERROR_INVALID_ACCESS));
}
//
// now that the file is opened, we need to make sure that it isn't a
// device that Canonicalize didn't detect.
//
if (CheckFileType(PathHandle,FILE_TYPE_DEV)) {
NtClose(PathHandle);
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("path is a device\n");
}
#endif
return ERROR_INVALID_ACCESS;
}
if (FileInformationLevel == FIL_STANDARD) {
RetCode = SetFileInfo(PathHandle,
Buffer,
Length);
}
else if (FileInformationLevel == FIL_SETEAS) {
RetCode = SetEAList(PathHandle,
Buffer,
Length);
}
else
RetCode = ERROR_NOT_SUPPORTED;
NtClose(PathHandle);
return RetCode;
}
APIRET
DosEnumAttribute(
IN ULONG RefType,
IN PVOID FileRef,
IN ULONG EntryNum,
OUT PVOID Buffer,
IN ULONG Length,
IN OUT PULONG ActualLength,
IN ULONG FileInformationLevel
)
/*++
Routine Description:
This routine enumerates a specific file's extended attributes.
Arguments:
RefType - type of file reference (handle or filename)
FileRef - handle or filename
EntryNum - starting entry in EA list
Buffer - data buffer
Length - data buffer size
ActualLength - on input, the number of entries to return. on output,
the number of entries returned.
FileInformationLevel - type of information requested.
Return Value:
ERROR_INVALID_LEVEL - infolevel is invalid
ERROR_BUFFER_OVERFLOW - requested information won't fit in buffer
--*/
{
NTSTATUS Status;
APIRET RetCode;
STRING CanonicalNameString;
UNICODE_STRING CanonicalNameString_U;
ULONG FileType;
ULONG FileFlags;
HANDLE PathHandle;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
ULONG NumberFound;
PDENA1 Os2Ea, PrevEa;
PFEA2 NtEa;
PFILE_HANDLE hFileRecord;
ULONG BufferLength;
PVOID EaBuffer;
#if DBG
PSZ RoutineName;
RoutineName = "DosEnumAttribute";
#endif
//
// check the parameters
//
if ((RefType > ENUMEA_REFTYPE_MAX) || EntryNum == 0)
{
return ERROR_INVALID_PARAMETER;
}
if (FileInformationLevel != ENUM_EANAME)
{
return ERROR_INVALID_LEVEL;
}
try {
*(volatile PULONG) ActualLength = *(volatile PULONG) ActualLength;
Od2ProbeForWrite(Buffer,Length,1);
} except( EXCEPTION_EXECUTE_HANDLER ) {
Od2ExitGP();
}
if (*ActualLength == 0)
{
return NO_ERROR;
}
//
// if the FileRef is a pathname, we need to open it to have a handle
// to pass to NtQueryEaFile
//
if (RefType == ENUMEA_REFTYPE_PATH)
{
RetCode = Od2Canonicalize(FileRef,
/* BUGBUG - Why allow a pipe ? */
CANONICALIZE_FILE_DEV_OR_PIPE,
&CanonicalNameString,
&PathHandle,
&FileFlags,
&FileType
);
if (RetCode != NO_ERROR)
{
return RetCode;
}
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("canonicalize returned %s\n",CanonicalNameString.Buffer);
}
#endif
//
// check for file name being invalid type
//
if (FileFlags & CANONICALIZE_META_CHARS_FOUND)
{
RetCode = ERROR_INVALID_PATH;
}
else if (FileFlags & CANONICALIZE_IS_ROOT_DIRECTORY)
{
RetCode = ERROR_ACCESS_DENIED;
}
else if (FileType & (FILE_TYPE_DEV | FILE_TYPE_PSDEV | FILE_TYPE_PIPE))
{
RetCode = ERROR_INVALID_ACCESS;
}
if (RetCode != NO_ERROR)
{
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return RetCode;
}
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&CanonicalNameString_U,
&CanonicalNameString,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("DosEnumAttribute: no memory for Unicode Conversion\n");
}
#endif
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
return RetCode;
}
InitializeObjectAttributes(&Obja,
&CanonicalNameString_U,
OBJ_CASE_INSENSITIVE,
PathHandle,
NULL);
do {
Status = NtOpenFile(&PathHandle,
SYNCHRONIZE | FILE_READ_EA,
&Obja,
&IoStatus,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT
);
} while (RetryCreateOpen(Status, &Obja));
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
RtlFreeUnicodeString (&CanonicalNameString_U);
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtOpenFile returned %X\n",Status);
}
#endif
return (Or2MapNtStatusToOs2Error(Status, ERROR_PATH_NOT_FOUND));
}
//
// now that the file is opened, we need to make sure that it isn't a
// device that Canonicalize didn't detect.
//
if (CheckFileType(PathHandle,FILE_TYPE_DEV)) {
NtClose(PathHandle);
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("path is a device\n");
}
#endif
return ERROR_INVALID_ACCESS;
}
}
else
{
/* BUGBUG - Note that the call below is translated to some other call
if DBG is NOT defined => DosEnumAttribute() needs to be tested without
DBG */
AcquireFileLockShared( // prevent the handle from being closed
#if DBG
RoutineName
#endif
);
RetCode = DereferenceFileHandle(
*(HFILE *)FileRef,
&hFileRecord);
if (RetCode != NO_ERROR)
{
/* BUGBUG - Note that the call below is translated to some other call
if DBG is NOT defined => DosEnumAttribute() needs to be tested without
DBG */
ReleaseFileLockShared(
#if DBG
RoutineName
#endif
);
return RetCode;
}
PathHandle = hFileRecord->NtHandle;
}
//
// allocate a buffer large enough to hold all the EAs for a file.
//
BufferLength = MAX_ALIGNED_EA_LIST_SIZE;
EaBuffer = 0;
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
&EaBuffer,
0,
&BufferLength,
MEM_COMMIT,
PAGE_READWRITE
);
if (!(NT_SUCCESS(Status))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// since we're passing in an EntryNum, as opposed to a GEA, NtQueryFile
// will return STATUS_NO_EAS_ON_FILE if the requested entry does not
// exist. NtQueryEaFile returns STATUS_BUFFER_OVERFLOW if no EAs will
// fit in the buffer.
//
// Around build 304, entries are now 1-based (not 0-based) so there is no
// need to decrement EntryNum
do
{
Status = NtQueryEaFile(PathHandle,
&IoStatus,
EaBuffer,
BufferLength,
FALSE,
NULL,
0,
&EntryNum,
FALSE);
} while (RetryIO(Status, PathHandle));
if (Status != STATUS_SUCCESS)
{
if (Status == STATUS_NO_EAS_ON_FILE)
{
*ActualLength = 0;
RetCode = NO_ERROR;
}
/* BUGBUG - Seems like this error code below is returned by HPFS &
NTFS when a file has no EA's */
else if ((Status == STATUS_NONEXISTENT_EA_ENTRY) || (EntryNum == 0))
{
*ActualLength = 0;
RetCode = NO_ERROR;
}
else
{
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_EAS_NOT_SUPPORTED);
}
goto CleanUp;
}
else if (IoStatus.Information == 0) /* Size of retrieved EA's is 0 ! */
{
*ActualLength = 0;
RetCode = NO_ERROR;
goto CleanUp;
}
NumberFound = 0;
RetCode = NO_ERROR;
Os2Ea = PrevEa = Buffer;
NtEa = EaBuffer;
while ((Length >= (ULONG)(DENA1_sizeof(NtEa))) && (NumberFound < *ActualLength)) {
RtlMoveMemory(Os2Ea,NtEa,DENA1_sizeof(NtEa));
Os2Ea->oNextEntryOffset = DENA1_oNextEntryOffset(Os2Ea);
if (Length < Os2Ea->oNextEntryOffset)
Length = 0;
else
Length -= Os2Ea->oNextEntryOffset;
NumberFound++;
PrevEa = Os2Ea;
if (NtEa->oNextEntryOffset == 0) {
break;
}
else {
Os2Ea = (PDENA1) ((PCHAR) Os2Ea + Os2Ea->oNextEntryOffset);
NtEa = (PFEA2) ((PCHAR) NtEa + NtEa->oNextEntryOffset);
}
}
//
// if NtQueryDirectoryFile returned an EA but it wouldn't fit in
// the user's buffer, return ERROR_BUFFER_OVERFLOW.
//
if (NumberFound == 0) {
RetCode = ERROR_BUFFER_OVERFLOW;
goto CleanUp;
}
PrevEa->oNextEntryOffset = 0;
*ActualLength = NumberFound;
CleanUp:
if (RefType == ENUMEA_REFTYPE_PATH) {
NtClose(PathHandle);
}
else {
ReleaseFileLockShared(
#if DBG
RoutineName
#endif
);
}
NtFreeVirtualMemory(NtCurrentProcess(),
&EaBuffer,
&BufferLength,
MEM_RELEASE
);
return RetCode;
}