/** * @internal * @brief Retrieves the volume label. * @param[in] hRoot handle to the * root directory. * @param[out] pointer to the structure * receiving the volume label. */ static void get_volume_label(HANDLE hRoot,winx_volume_information *v) { FILE_FS_VOLUME_INFORMATION *ffvi; int buffer_size; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; /* reset label */ v->label[0] = 0; /* allocate memory */ buffer_size = (sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(wchar_t)) + (MAX_PATH + 1) * sizeof(wchar_t); ffvi = winx_malloc(buffer_size); /* try to get actual label */ RtlZeroMemory(ffvi,buffer_size); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,ffvi, buffer_size,FileFsVolumeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get volume label of drive %c:", v->volume_letter); winx_free(ffvi); return; } wcsncpy(v->label,ffvi->VolumeLabel,MAX_PATH); v->label[MAX_PATH] = 0; winx_free(ffvi); }
static int winfs_statfs(struct file *f, struct statfs64 *buf) { AcquireSRWLockShared(&f->rw_lock); struct winfs_file *winfile = (struct winfs_file *) f; FILE_FS_FULL_SIZE_INFORMATION info; IO_STATUS_BLOCK status_block; NTSTATUS status = NtQueryVolumeInformationFile(winfile->handle, &status_block, &info, sizeof(info), FileFsFullSizeInformation); int r = 0; if (!NT_SUCCESS(status)) { log_warning("NtQueryVolumeInformationFile() failed, status: %x", status); r = -L_EIO; goto out; } buf->f_type = 0x5346544e; /* NTFS_SB_MAGIC */ buf->f_bsize = info.SectorsPerAllocationUnit * info.BytesPerSector; buf->f_blocks = info.TotalAllocationUnits.QuadPart; buf->f_bfree = info.ActualAvailableAllocationUnits.QuadPart; buf->f_bavail = info.CallerAvailableAllocationUnits.QuadPart; buf->f_files = 0; buf->f_ffree = 0; buf->f_fsid.val[0] = 0; buf->f_fsid.val[1] = 0; buf->f_namelen = PATH_MAX; buf->f_frsize = 0; buf->f_flags = 0; buf->f_spare[0] = 0; buf->f_spare[1] = 0; buf->f_spare[2] = 0; buf->f_spare[3] = 0; out: ReleaseSRWLockShared(&f->rw_lock); return r; }
BOOLEAN DiskDriveQueryVolumeFreeSpace( _In_ HANDLE DosDeviceHandle, _Out_ ULONG64* TotalLength, _Out_ ULONG64* FreeLength ) { IO_STATUS_BLOCK isb; FILE_FS_FULL_SIZE_INFORMATION result; memset(&result, 0, sizeof(FILE_FS_FULL_SIZE_INFORMATION)); if (NT_SUCCESS(NtQueryVolumeInformationFile( DosDeviceHandle, &isb, &result, sizeof(FILE_FS_FULL_SIZE_INFORMATION), FileFsFullSizeInformation ))) { *TotalLength = result.TotalAllocationUnits.QuadPart * result.SectorsPerAllocationUnit * result.BytesPerSector; *FreeLength = result.ActualAvailableAllocationUnits.QuadPart * result.SectorsPerAllocationUnit * result.BytesPerSector; return TRUE; } return FALSE; }
RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType) { *penmType = RTFSTYPE_UNKNOWN; AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER); /* * Convert the path and try open it. */ PRTUTF16 pwszFsPath; int rc = RTStrToUtf16(pszFsPath, &pwszFsPath); if (RT_SUCCESS(rc)) { HANDLE hFile = CreateFileW(pwszFsPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile != INVALID_HANDLE_VALUE) { /* * Use the NT api directly to get the file system name. */ char abBuf[8192]; IO_STATUS_BLOCK Ios; NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, abBuf, sizeof(abBuf), FileFsAttributeInformation); if (rcNt >= 0) { PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)abBuf; if (pFsAttrInfo->FIleSystemNameLength) { } #define IS_FS(szName) \ rtFsWinAreEqual(pFsAttrInfo->FileSystemName, pFsAttrInfo->FIleSystemNameLength, szName, sizeof(szName) - 1) if (IS_FS("NTFS")) *penmType = RTFSTYPE_NTFS; else if (IS_FS("FAT")) *penmType = RTFSTYPE_FAT; else if (IS_FS("FAT32")) *penmType = RTFSTYPE_FAT; else if (IS_FS("VBoxSharedFolderFS")) *penmType = RTFSTYPE_VBOXSHF; #undef IS_FS } else rc = RTErrConvertFromNtStatus(rcNt); CloseHandle(hFile); } else rc = RTErrConvertFromWin32(GetLastError()); RTUtf16Free(pwszFsPath); } return rc; }
NTSTATUS DiskDriveQueryVolumeAttributes( _In_ HANDLE DosDeviceHandle, _Out_ PFILE_FS_ATTRIBUTE_INFORMATION* AttributeInfo ) { NTSTATUS status; ULONG bufferLength; IO_STATUS_BLOCK isb; PFILE_FS_ATTRIBUTE_INFORMATION buffer; bufferLength = sizeof(FILE_FS_ATTRIBUTE_INFORMATION); buffer = PhAllocate(bufferLength); memset(buffer, 0, bufferLength); status = NtQueryVolumeInformationFile( DosDeviceHandle, &isb, buffer, bufferLength, FileFsAttributeInformation ); if (status == STATUS_BUFFER_OVERFLOW) { bufferLength = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + buffer->FileSystemNameLength; buffer = PhReAllocate(buffer, bufferLength); memset(buffer, 0, bufferLength); status = NtQueryVolumeInformationFile( DosDeviceHandle, &isb, buffer, bufferLength, FileFsAttributeInformation ); } if (NT_SUCCESS(status)) { *AttributeInfo = buffer; return status; } PhFree(buffer); return status; }
RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType) { /* * Validate input. */ *penmType = RTFSTYPE_UNKNOWN; AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER); /* * Open the file/dir/whatever. */ HANDLE hFile; int rc = rtNtPathOpen(pszFsPath, GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT, OBJ_CASE_INSENSITIVE, &hFile, NULL); if (RT_SUCCESS(rc)) { /* * Get the file system name. */ union { FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo; uint8_t abBuf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 4096]; } u; IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsAttributeInformation); if (NT_SUCCESS(rcNt)) { #define IS_FS(a_szName) \ rtNtCompWideStrAndAscii(u.FsAttrInfo.FileSystemName, u.FsAttrInfo.FileSystemNameLength, RT_STR_TUPLE(a_szName)) if (IS_FS("NTFS")) *penmType = RTFSTYPE_NTFS; else if (IS_FS("FAT")) *penmType = RTFSTYPE_FAT; else if (IS_FS("FAT32")) *penmType = RTFSTYPE_FAT; else if (IS_FS("VBoxSharedFolderFS")) *penmType = RTFSTYPE_VBOXSHF; #undef IS_FS } else rc = RTErrConvertFromNtStatus(rcNt); rtNtPathClose(hFile); } return rc; }
/** * Queries information from a file or directory handle. * * This is shared between the RTPathQueryInfo, RTFileQueryInfo and * RTDirQueryInfo code. * * @returns IPRT status code. * @param hFile The handle to query information from. Must have * the necessary privileges. * @param pvBuf Pointer to a scratch buffer. * @param cbBuf The size of the buffer. This must be large * enough to hold a FILE_ALL_INFORMATION struct. * @param pObjInfo Where to return information about the handle. * @param enmAddAttr What extra info to return. * @param pszPath The path if this is a file (for exe detect). * @param uReparseTag The reparse tag number (0 if not applicable) for * symlink detection/whatnot. */ DECLHIDDEN(int) rtPathNtQueryInfoFromHandle(HANDLE hFile, void *pvBuf, size_t cbBuf, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr, const char *pszPath, ULONG uReparseTag) { Assert(cbBuf >= sizeof(FILE_ALL_INFORMATION)); /** @todo Try optimize this for when RTFSOBJATTRADD_UNIX isn't set? */ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, pvBuf, sizeof(FILE_ALL_INFORMATION), FileAllInformation); if ( NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW) { FILE_ALL_INFORMATION *pAllInfo = (FILE_ALL_INFORMATION *)pvBuf; pObjInfo->cbObject = pAllInfo->StandardInformation.EndOfFile.QuadPart; pObjInfo->cbAllocated = pAllInfo->StandardInformation.AllocationSize.QuadPart; RTTimeSpecSetNtTime(&pObjInfo->BirthTime, pAllInfo->BasicInformation.CreationTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->AccessTime, pAllInfo->BasicInformation.LastAccessTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, pAllInfo->BasicInformation.LastWriteTime.QuadPart); RTTimeSpecSetNtTime(&pObjInfo->ChangeTime, pAllInfo->BasicInformation.ChangeTime.QuadPart); pObjInfo->Attr.fMode = rtFsModeFromDos( (pAllInfo->BasicInformation.FileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, pszPath, pszPath ? strlen(pszPath) : 0, uReparseTag); 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 = RT_MAX(1, pAllInfo->StandardInformation.NumberOfLinks); pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /* below */ pObjInfo->Attr.u.Unix.INodeId = pAllInfo->InternalInformation.IndexNumber.QuadPart; 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, pvBuf, (ULONG)RT_MIN(cbBuf, _2K), FileFsVolumeInformation); if (NT_SUCCESS(rcNt) || rcNt == STATUS_BUFFER_OVERFLOW) { FILE_FS_VOLUME_INFORMATION *pVolInfo = (FILE_FS_VOLUME_INFORMATION *)pvBuf; pObjInfo->Attr.u.Unix.INodeIdDevice = pVolInfo->VolumeSerialNumber; } } return rtPathNtQueryInfoFillInDummyData(VINF_SUCCESS, pObjInfo, enmAddAttr); } return RTErrConvertFromNtStatus(rcNt); }
NTSTATUS WINAPI SafeNtQueryVolumeInformationFile( HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileSystemInformation, ULONG Length, FS_INFORMATION_CLASS FileSystemInformationClass ) { NTSTATUS rc; if (CheckOldFunction(&OldNtQueryVolumeInformationFile)) rc=OldNtQueryVolumeInformationFile(FileHandle,IoStatusBlock,FileSystemInformation,Length,FileSystemInformationClass); else rc=NtQueryVolumeInformationFile(FileHandle,IoStatusBlock,FileSystemInformation,Length,FileSystemInformationClass); return rc; }
/** * @internal * @brief Retrieves the drive geometry. * @param[in] hRoot handle to the * root directory. * @param[out] pointer to the structure * receiving the drive geometry. * @return Zero for success, negative * value otherwise. */ static int get_drive_geometry(HANDLE hRoot,winx_volume_information *v) { FILE_FS_SIZE_INFORMATION ffs; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; WINX_FILE *f; DISK_GEOMETRY dg; char buffer[32]; /* get drive geometry */ RtlZeroMemory(&ffs,sizeof(FILE_FS_SIZE_INFORMATION)); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,&ffs, sizeof(FILE_FS_SIZE_INFORMATION),FileFsSizeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get geometry of drive %c:",v->volume_letter); return (-1); } /* fill all geometry related fields of the output structure */ v->total_bytes = (ULONGLONG)ffs.TotalAllocationUnits.QuadPart * \ ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->free_bytes = (ULONGLONG)ffs.AvailableAllocationUnits.QuadPart * \ ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->total_clusters = (ULONGLONG)ffs.TotalAllocationUnits.QuadPart; v->bytes_per_cluster = ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->sectors_per_cluster = ffs.SectorsPerAllocationUnit; v->bytes_per_sector = ffs.BytesPerSector; /* optional: get device capacity */ v->device_capacity = 0; f = winx_vopen(v->volume_letter); if(f != NULL){ if(winx_ioctl(f,IOCTL_DISK_GET_DRIVE_GEOMETRY, "get_drive_geometry: device geometry request",NULL,0, &dg,sizeof(dg),NULL) >= 0){ v->device_capacity = dg.Cylinders.QuadPart * \ dg.TracksPerCylinder * dg.SectorsPerTrack * dg.BytesPerSector; winx_bytes_to_hr(v->device_capacity,1,buffer,sizeof(buffer)); itrace("%c: device capacity = %s",v->volume_letter,buffer); } winx_fclose(f); } return 0; }
RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial) { /* * Validate & get valid root path. */ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER); /* * Open the file/dir/whatever. */ HANDLE hFile; int rc = rtNtPathOpen(pszFsPath, GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT, OBJ_CASE_INSENSITIVE, &hFile, NULL); if (RT_SUCCESS(rc)) { /* * Get the volume information. */ union { FILE_FS_VOLUME_INFORMATION FsVolInfo; uint8_t abBuf[sizeof(FILE_FS_VOLUME_INFORMATION) + 4096]; } u; IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsVolumeInformation); if (NT_SUCCESS(rcNt)) *pu32Serial = u.FsVolInfo.VolumeSerialNumber; else rc = RTErrConvertFromNtStatus(rcNt); rtNtPathClose(hFile); } return rc; }
/* * SfuCalcVolumeMD5 * * Purpose: * * Calculate MD5 from system volume information. * */ BOOLEAN SfuCalcVolumeMD5( _Inout_ PBYTE MD5Hash ) { OBJECT_ATTRIBUTES obja; IO_STATUS_BLOCK iost; UNICODE_STRING str; NTSTATUS Status; BOOLEAN result = FALSE; HANDLE hVolume = NULL; FILE_FS_VOLUME_INFORMATION fsVolumeInfo; MD5_CTX ctx; if (MD5Hash == NULL) return result; RtlSecureZeroMemory(&str, sizeof(str)); RtlInitUnicodeString(&str, L"\\systemroot"); InitializeObjectAttributes(&obja, &str, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&hVolume, FILE_GENERIC_READ, &obja, &iost, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (NT_SUCCESS(Status)) { Status = NtQueryVolumeInformationFile(hVolume, &iost, &fsVolumeInfo, sizeof(FILE_FS_VOLUME_INFORMATION), FileFsVolumeInformation); if ((NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)) { fsVolumeInfo.VolumeCreationTime.HighPart ^= 0x1010101; MD5Init(&ctx); MD5Update(&ctx, (unsigned char*)&fsVolumeInfo.VolumeCreationTime, sizeof(LARGE_INTEGER)); MD5Final(&ctx); RtlCopyMemory(MD5Hash, &ctx.buf, 16); result = TRUE; } NtClose(hVolume); } return result; }
/** * @internal * @brief Retrieves the name of the file system. * @param[in] hRoot handle to the root directory. * @param[out] pointer to the structure receiving * the filesystem name. * @return Zero for success, negative value otherwise. * @note We could analyze the first sector of the * partition directly, but this method is not so swift * as it accesses the disk physically. */ static int get_filesystem_name(HANDLE hRoot,winx_volume_information *v) { FILE_FS_ATTRIBUTE_INFORMATION *pfa; int fs_attr_info_size; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; wchar_t fs_name[MAX_FS_NAME_LENGTH + 1]; int length; fs_attr_info_size = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION); pfa = winx_malloc(fs_attr_info_size); RtlZeroMemory(pfa,fs_attr_info_size); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,pfa, fs_attr_info_size,FileFsAttributeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get file system name of drive %c:",v->volume_letter); winx_free(pfa); return (-1); } /* * pfa->FileSystemName.Buffer may be not NULL terminated * (theoretically), so name extraction is more tricky * than it should be. */ length = min(MAX_FS_NAME_LENGTH,pfa->FileSystemNameLength / sizeof(wchar_t)); wcsncpy(fs_name,pfa->FileSystemName,length); fs_name[length] = 0; _snprintf(v->fs_name,MAX_FS_NAME_LENGTH,"%ws",fs_name); v->fs_name[MAX_FS_NAME_LENGTH] = 0; /* cleanup */ winx_free(pfa); return 0; }
/** * @brief A Win32 GetDriveType() native equivalent. * @param[in] letter the volume letter * @return The drive type, negative value indicates failure. */ int winx_get_drive_type(char letter) { wchar_t link_name[] = L"\\??\\A:"; #define MAX_TARGET_LENGTH 256 wchar_t link_target[MAX_TARGET_LENGTH]; PROCESS_DEVICEMAP_INFORMATION pdi; FILE_FS_DEVICE_INFORMATION ffdi; IO_STATUS_BLOCK iosb; NTSTATUS status; int drive_type; HANDLE hRoot; /* The additional checks for DFS were suggested by Stefan Pendl ([email protected]). */ /* DFS shares have DRIVE_NO_ROOT_DIR type though they are actually remote. */ letter = winx_toupper(letter); if(letter < 'A' || letter > 'Z'){ etrace("invalid letter %c",letter); return (-1); } /* check for the drive existence */ link_name[4] = (wchar_t)letter; if(winx_query_symbolic_link(link_name,link_target,MAX_TARGET_LENGTH) < 0) return (-1); /* check for an assignment made by subst command */ if(wcsstr(link_target,L"\\??\\") == (wchar_t *)link_target) return DRIVE_ASSIGNED_BY_SUBST_COMMAND; /* check for classical floppies */ if(wcsstr(link_target,L"Floppy")) return DRIVE_REMOVABLE; /* try to define exactly which type has the specified drive */ RtlZeroMemory(&pdi,sizeof(PROCESS_DEVICEMAP_INFORMATION)); status = NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap,&pdi, sizeof(PROCESS_DEVICEMAP_INFORMATION), NULL); if(NT_SUCCESS(status)){ drive_type = (int)pdi.Query.DriveType[letter - 'A']; /* * Type DRIVE_NO_ROOT_DIR have the following drives: * 1. assigned by subst command * 2. SCSI external drives * 3. RAID volumes * 4. DFS shares * We need additional checks to know exactly. */ if(drive_type != DRIVE_NO_ROOT_DIR) return drive_type; } else { strace(status,"cannot get device map"); return (-1); } /* try to define exactly again which type has the specified drive */ /* note that the drive motor can be powered on during this check */ hRoot = OpenRootDirectory(letter); if(hRoot == NULL) return (-1); RtlZeroMemory(&ffdi,sizeof(FILE_FS_DEVICE_INFORMATION)); status = NtQueryVolumeInformationFile(hRoot,&iosb, &ffdi,sizeof(FILE_FS_DEVICE_INFORMATION), FileFsDeviceInformation); NtClose(hRoot); if(!NT_SUCCESS(status)){ strace(status,"cannot get volume type for \'%c\'",letter); return (-1); } /* detect remote/cd/dvd/unknown drives */ if(ffdi.Characteristics & FILE_REMOTE_DEVICE) return DRIVE_REMOTE; switch(ffdi.DeviceType){ case FILE_DEVICE_CD_ROM: case FILE_DEVICE_CD_ROM_FILE_SYSTEM: case FILE_DEVICE_DVD: return DRIVE_CDROM; case FILE_DEVICE_NETWORK_FILE_SYSTEM: case FILE_DEVICE_NETWORK: /* ? */ case FILE_DEVICE_NETWORK_BROWSER: /* ? */ case FILE_DEVICE_DFS_FILE_SYSTEM: case FILE_DEVICE_DFS_VOLUME: case FILE_DEVICE_DFS: return DRIVE_REMOTE; case FILE_DEVICE_UNKNOWN: return DRIVE_UNKNOWN; } /* detect removable disks */ if(ffdi.Characteristics & FILE_REMOVABLE_MEDIA) return DRIVE_REMOVABLE; /* detect fixed disks */ switch(ffdi.DeviceType){ case FILE_DEVICE_DISK: case FILE_DEVICE_FILE_SYSTEM: /* ? */ /*case FILE_DEVICE_VIRTUAL_DISK:*/ /*case FILE_DEVICE_MASS_STORAGE:*/ case FILE_DEVICE_DISK_FILE_SYSTEM: return DRIVE_FIXED; default: break; } /* nothing detected => drive type is unknown */ return DRIVE_UNKNOWN; }
NTSTATUS NTAPI SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume) { NTSTATUS Status; LARGE_INTEGER FreeSpace, FinalFreeSpace; FILE_FS_SIZE_INFORMATION SizeInfo; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING VolumeName; HANDLE VolumeHandle; WCHAR PathString[32]; ASSERT(Volume->Flags & SMP_VOLUME_IS_BOOT); // ASSERT says "BootVolume == 1" /* Build the standard path */ wcscpy(PathString, L"\\??\\A:\\"); RtlInitUnicodeString(&VolumeName, PathString); VolumeName.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter; DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space \n", &VolumeName); /* Open the volume */ InitializeObjectAttributes(&ObjectAttributes, &VolumeName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&VolumeHandle, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); if (!NT_SUCCESS(Status)) { DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X \n", &VolumeName, Status); return Status; } /* Now get size information on the volume */ Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, &SizeInfo, sizeof(SizeInfo), FileFsSizeInformation); if (!NT_SUCCESS(Status)) { /* We failed */ DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed" " with status %X \n", &VolumeName, VolumeHandle, Status); NtClose(VolumeHandle); return Status; } NtClose(VolumeHandle); /* Compute how much free space we have */ FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart * SizeInfo.SectorsPerAllocationUnit; FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector; /* Check if there's less than 32MB free so we don't starve the disk */ if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE) { /* In this case, act as if there's no free space */ Volume->FreeSpace.QuadPart = 0; } else { /* Trim off 32MB to give the disk a bit of breathing room */ Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart - MINIMUM_TO_KEEP_FREE; } return STATUS_SUCCESS; }
/** * 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; }
NTSTATUS NTAPI SmpCreateVolumeDescriptors(VOID) { NTSTATUS Status; UNICODE_STRING VolumePath; BOOLEAN BootVolumeFound = FALSE; WCHAR StartChar, Drive, DriveDiff; HANDLE VolumeHandle; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; PROCESS_DEVICEMAP_INFORMATION ProcessInformation; FILE_FS_DEVICE_INFORMATION DeviceInfo; FILE_FS_SIZE_INFORMATION SizeInfo; PSMP_VOLUME_DESCRIPTOR Volume; LARGE_INTEGER FreeSpace, FinalFreeSpace; WCHAR Buffer[32]; /* We should be starting with an empty list */ ASSERT(IsListEmpty(&SmpVolumeDescriptorList)); /* Query the device map so we can get the drive letters */ Status = NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap, &ProcessInformation, sizeof(ProcessInformation), NULL); if (!NT_SUCCESS(Status)) { DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X \n", Status); return Status; } /* Build the volume string, starting with A: (we'll edit this in place) */ wcscpy(Buffer, L"\\??\\A:\\"); RtlInitUnicodeString(&VolumePath, Buffer); /* Start with the C drive except on weird Japanese NECs... */ StartChar = SharedUserData->AlternativeArchitecture ? L'A' : L'C'; for (Drive = StartChar, DriveDiff = StartChar - L'A'; Drive <= L'Z'; Drive++, DriveDiff++) { /* Skip the disk if it's not in the drive map */ if (!((1 << DriveDiff) & ProcessInformation.Query.DriveMap)) continue; /* Write the drive letter and try to open the volume */ VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Drive; InitializeObjectAttributes(&ObjectAttributes, &VolumePath, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&VolumeHandle, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); if (!NT_SUCCESS(Status)) { /* Skip the volume if we failed */ DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X \n", &VolumePath, Status); continue; } /* Now query device information on the volume */ Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, &DeviceInfo, sizeof(DeviceInfo), FileFsDeviceInformation); if (!NT_SUCCESS(Status)) { /* Move to the next volume if we failed */ DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info" " failed with status %X \n", &VolumePath, VolumeHandle, Status); NtClose(VolumeHandle); continue; } /* Check if this is a fixed disk */ if (DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE | FILE_READ_ONLY_DEVICE | FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA)) { /* It isn't, so skip it */ DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file \n", &VolumePath, DeviceInfo.Characteristics); NtClose(VolumeHandle); continue; } /* We found a fixed volume, allocate a descriptor for it */ Volume = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SMP_VOLUME_DESCRIPTOR)); if (!Volume) { /* Failed to allocate memory, try the next disk */ DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes) \n", sizeof(SMP_VOLUME_DESCRIPTOR)); NtClose(VolumeHandle); continue; } /* Save the drive letter and device information */ Volume->DriveLetter = Drive; Volume->DeviceInfo = DeviceInfo; /* Check if this is the boot volume */ if (RtlUpcaseUnicodeChar(Drive) == RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0])) { /* Save it */ ASSERT(BootVolumeFound == FALSE); Volume->Flags |= SMP_VOLUME_IS_BOOT; BootVolumeFound = TRUE; } /* Now get size information on the volume */ Status = NtQueryVolumeInformationFile(VolumeHandle, &IoStatusBlock, &SizeInfo, sizeof(SizeInfo), FileFsSizeInformation); if (!NT_SUCCESS(Status)) { /* We failed -- keep going */ DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed" " with status %X \n", &VolumePath, VolumeHandle, Status); RtlFreeHeap(RtlGetProcessHeap(), 0, Volume); NtClose(VolumeHandle); continue; } /* Done querying volume information, close the handle */ NtClose(VolumeHandle); /* Compute how much free space we have */ FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart * SizeInfo.SectorsPerAllocationUnit; FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector; /* Check if there's less than 32MB free so we don't starve the disk */ if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE) { /* In this case, act as if there's no free space */ Volume->FreeSpace.QuadPart = 0; } else { /* Trim off 32MB to give the disk a bit of breathing room */ Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart - MINIMUM_TO_KEEP_FREE; } /* All done, add this volume to our descriptor list */ InsertTailList(&SmpVolumeDescriptorList, &Volume->Entry); Volume->Flags |= SMP_VOLUME_INSERTED; DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ' \n", &VolumePath); } /* We must've found at least the boot volume */ ASSERT(BootVolumeFound == TRUE); ASSERT(!IsListEmpty(&SmpVolumeDescriptorList)); if (!IsListEmpty(&SmpVolumeDescriptorList)) return STATUS_SUCCESS; /* Something is really messed up if we found no disks at all */ return STATUS_UNEXPECTED_IO_ERROR; }
RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties) { /* * Validate & get valid root path. */ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); AssertPtrReturn(pProperties, VERR_INVALID_POINTER); /* * Open the file/dir/whatever. */ HANDLE hFile; int rc = rtNtPathOpen(pszFsPath, GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT, OBJ_CASE_INSENSITIVE, &hFile, NULL); if (RT_SUCCESS(rc)) { /* * Get the volume information. */ union { FILE_FS_ATTRIBUTE_INFORMATION FsAttrInfo; uint8_t abBuf[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 4096]; } u; IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &u, sizeof(u), FileFsAttributeInformation); if (NT_SUCCESS(rcNt)) { FILE_FS_DEVICE_INFORMATION FsDevInfo; rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &FsDevInfo, sizeof(FsDevInfo), FileFsDeviceInformation); if (NT_SUCCESS(rcNt)) { /* * Fill in the return structure. */ memset(pProperties, 0, sizeof(*pProperties)); pProperties->cbMaxComponent = u.FsAttrInfo.MaximumComponentNameLength; pProperties->fFileCompression = !!(u.FsAttrInfo.FileSystemAttributes & FILE_FILE_COMPRESSION); pProperties->fCompressed = !!(u.FsAttrInfo.FileSystemAttributes & FILE_VOLUME_IS_COMPRESSED); pProperties->fReadOnly = !!(u.FsAttrInfo.FileSystemAttributes & FILE_READ_ONLY_VOLUME); pProperties->fSupportsUnicode = !!(u.FsAttrInfo.FileSystemAttributes & FILE_UNICODE_ON_DISK); pProperties->fCaseSensitive = false; /* win32 is case preserving only */ /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS * as well perchance? If so, better mention it instead of just setting * fCaseSensitive to false. */ /* figure the remote stuff */ pProperties->fRemote = RT_BOOL(FsDevInfo.Characteristics & FILE_REMOTE_DEVICE); } else rc = RTErrConvertFromNtStatus(rcNt); } else rc = RTErrConvertFromNtStatus(rcNt); rtNtPathClose(hFile); } return rc; }
/* * @implemented */ BOOL WINAPI GetVolumeInformationW(IN LPCWSTR lpRootPathName, IN LPWSTR lpVolumeNameBuffer, IN DWORD nVolumeNameSize, OUT LPDWORD lpVolumeSerialNumber OPTIONAL, OUT LPDWORD lpMaximumComponentLength OPTIONAL, OUT LPDWORD lpFileSystemFlags OPTIONAL, OUT LPWSTR lpFileSystemNameBuffer OPTIONAL, IN DWORD nFileSystemNameSize) { PFILE_FS_VOLUME_INFORMATION FileFsVolume; PFILE_FS_ATTRIBUTE_INFORMATION FileFsAttribute; IO_STATUS_BLOCK IoStatusBlock; WCHAR RootPathName[MAX_PATH]; UCHAR Buffer[max(FS_VOLUME_BUFFER_SIZE, FS_ATTRIBUTE_BUFFER_SIZE)]; HANDLE hFile; NTSTATUS errCode; FileFsVolume = (PFILE_FS_VOLUME_INFORMATION)Buffer; FileFsAttribute = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; TRACE("FileFsVolume %p\n", FileFsVolume); TRACE("FileFsAttribute %p\n", FileFsAttribute); if (!lpRootPathName || !wcscmp(lpRootPathName, L"")) { GetCurrentDirectoryW (MAX_PATH, RootPathName); } else { wcsncpy (RootPathName, lpRootPathName, 3); } RootPathName[3] = 0; hFile = InternalOpenDirW(RootPathName, FALSE); if (hFile == INVALID_HANDLE_VALUE) { return FALSE; } TRACE("hFile: %p\n", hFile); errCode = NtQueryVolumeInformationFile(hFile, &IoStatusBlock, FileFsVolume, FS_VOLUME_BUFFER_SIZE, FileFsVolumeInformation); if ( !NT_SUCCESS(errCode) ) { WARN("Status: %x\n", errCode); CloseHandle(hFile); BaseSetLastNTError (errCode); return FALSE; } if (lpVolumeSerialNumber) *lpVolumeSerialNumber = FileFsVolume->VolumeSerialNumber; if (lpVolumeNameBuffer) { if (nVolumeNameSize * sizeof(WCHAR) >= FileFsVolume->VolumeLabelLength + sizeof(WCHAR)) { memcpy(lpVolumeNameBuffer, FileFsVolume->VolumeLabel, FileFsVolume->VolumeLabelLength); lpVolumeNameBuffer[FileFsVolume->VolumeLabelLength / sizeof(WCHAR)] = 0; } else { CloseHandle(hFile); SetLastError(ERROR_MORE_DATA); return FALSE; } } errCode = NtQueryVolumeInformationFile (hFile, &IoStatusBlock, FileFsAttribute, FS_ATTRIBUTE_BUFFER_SIZE, FileFsAttributeInformation); CloseHandle(hFile); if (!NT_SUCCESS(errCode)) { WARN("Status: %x\n", errCode); BaseSetLastNTError (errCode); return FALSE; } if (lpFileSystemFlags) *lpFileSystemFlags = FileFsAttribute->FileSystemAttributes; if (lpMaximumComponentLength) *lpMaximumComponentLength = FileFsAttribute->MaximumComponentNameLength; if (lpFileSystemNameBuffer) { if (nFileSystemNameSize * sizeof(WCHAR) >= FileFsAttribute->FileSystemNameLength + sizeof(WCHAR)) { memcpy(lpFileSystemNameBuffer, FileFsAttribute->FileSystemName, FileFsAttribute->FileSystemNameLength); lpFileSystemNameBuffer[FileFsAttribute->FileSystemNameLength / sizeof(WCHAR)] = 0; } else { SetLastError(ERROR_MORE_DATA); return FALSE; } } return TRUE; }
RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree, uint32_t *pcbBlock, uint32_t *pcbSector) { AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER); /* * Open the file/dir/whatever. */ HANDLE hFile; int rc = rtNtPathOpen(pszFsPath, GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT, OBJ_CASE_INSENSITIVE, &hFile, NULL); if (RT_SUCCESS(rc)) { /* * Get the volume information. */ FILE_FS_SIZE_INFORMATION FsSizeInfo; IO_STATUS_BLOCK Ios = MY_IO_STATUS_BLOCK_INITIALIZER; NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios, &FsSizeInfo, sizeof(FsSizeInfo), FileFsSizeInformation); if (NT_SUCCESS(rcNt)) { /* * Calculate the return values. */ if (pcbTotal) { *pcbTotal = FsSizeInfo.TotalAllocationUnits.QuadPart * FsSizeInfo.SectorsPerAllocationUnit * FsSizeInfo.BytesPerSector; if ( *pcbTotal / FsSizeInfo.SectorsPerAllocationUnit / FsSizeInfo.BytesPerSector != FsSizeInfo.TotalAllocationUnits.QuadPart) *pcbTotal = UINT64_MAX; } if (pcbFree) { *pcbFree = FsSizeInfo.AvailableAllocationUnits.QuadPart * FsSizeInfo.SectorsPerAllocationUnit * FsSizeInfo.BytesPerSector; if ( *pcbFree / FsSizeInfo.SectorsPerAllocationUnit / FsSizeInfo.BytesPerSector != FsSizeInfo.AvailableAllocationUnits.QuadPart) *pcbFree = UINT64_MAX; } if (pcbBlock) { *pcbBlock = FsSizeInfo.SectorsPerAllocationUnit * FsSizeInfo.BytesPerSector; if (*pcbBlock / FsSizeInfo.BytesPerSector != FsSizeInfo.SectorsPerAllocationUnit) rc = VERR_OUT_OF_RANGE; } if (pcbSector) *pcbSector = FsSizeInfo.BytesPerSector; } else rc = RTErrConvertFromNtStatus(rcNt); rtNtPathClose(hFile); } return rc; }
BOOLEAN QueryVolumeTest( VOID ) { HANDLE fsHandle; IO_STATUS_BLOCK ioStatusBlock; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING nameString; NTSTATUS status; PFILE_FS_ATTRIBUTE_INFORMATION fsAttributeInfo; RtlInitUnicodeString( &nameString, L"\\Device\\Mailslot" ); InitializeObjectAttributes( &objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, NULL, NULL ); printf( "Attempting to open mailslot fs \"%wZ\"\n", &nameString ); status = NtOpenFile ( &fsHandle, GENERIC_READ, &objectAttributes, &ioStatusBlock, 0, 0L ); printf( "MSFS open status = %lx\n", status ); if (!NT_SUCCESS(status)) { return FALSE; } status = NtQueryVolumeInformationFile( fsHandle, &ioStatusBlock, Buffer, sizeof( Buffer ), FileFsAttributeInformation ); printf("Query volume status = %lx\n", status ); if ( !NT_SUCCESS( status )) { return FALSE; } status = ioStatusBlock.Status; printf( "Query volume final status = %d\n", status ); fsAttributeInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)Buffer; printf (" FileSystemAttributes = %lx\n", fsAttributeInfo->FileSystemAttributes); printf (" MaximumComponentNameLength = %ld\n", fsAttributeInfo->MaximumComponentNameLength); printf (" FileSystemNameLength = %ld\n", fsAttributeInfo->FileSystemNameLength); printf (" FileSystemName = "); DisplayUnicode(fsAttributeInfo->FileSystemName, fsAttributeInfo->FileSystemNameLength); putchar ('\n'); return( (BOOLEAN)NT_SUCCESS( status )); }