从我的网络服务器应用程序中,我需要检查应用程序所在硬盘的物理扇区大小。为此,我使用DeviceIoControl
和IOCTL_STORAGE_QUERY_PROPERTY
来查询StorageAccessAlignmentProperty
。问题是,当我尝试从网络服务器运行这些命令时,我得到“访问被拒绝”错误。
如何从网络服务器应用程序中检索inetpub所在的硬盘的物理扇区大小?
我从https://msdn.microsoft.com/windows/compatibility/advanced-format-disk-compatibility-update了解到,在Windows 8中,微软推出了一种新的API,可以从一个非特权的应用程序启用Calling。 API采用新的信息类FileFsSectorSizeInformation
的形式,其结构为FILE_FS_SECTOR_SIZE_INFORMATION
,但我不知道如何使用Delphi
这是我的实际代码不起作用(用Delphi编写):
{~~~~~~~~~~~~~~~~~~~~~~~~~}
procedure _CheckSectorSize;
type
STORAGE_PROPERTY_ID = (StorageDeviceProperty = 0,
StorageAdapterProperty,
StorageDeviceIdProperty,
StorageDeviceUniqueIdProperty,
StorageDeviceWriteCacheProperty,
StorageMiniportProperty,
StorageAccessAlignmentProperty,
StorageDeviceSeekPenaltyProperty,
StorageDeviceTrimProperty,
StorageDeviceWriteAggregationProperty,
StorageDeviceDeviceTelemetryProperty,
StorageDeviceLBProvisioningProperty,
StorageDevicePowerProperty,
StorageDeviceCopyOffloadProperty,
StorageDeviceResiliencyProperty,
StorageDeviceMediumProductType,
StorageAdapterCryptoProperty,
StorageDeviceIoCapabilityProperty = 48,
StorageAdapterProtocolSpecificProperty,
StorageDeviceProtocolSpecificProperty,
StorageAdapterTemperatureProperty,
StorageDeviceTemperatureProperty,
StorageAdapterPhysicalTopologyProperty,
StorageDevicePhysicalTopologyProperty,
StorageDeviceAttributesProperty);
STORAGE_QUERY_TYPE = (PropertyStandardQuery = 0,
PropertyExistsQuery = 1,
PropertyMaskQuery = 2,
PropertyQueryMaxDefined = 3);
_STORAGE_PROPERTY_QUERY = packed record
PropertyId: STORAGE_PROPERTY_ID;
QueryType: STORAGE_QUERY_TYPE;
AdditionalParameters: array[0..9] of Byte;
end;
_STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR = packed record
Version: DWORD; // Contains the size of this structure, in bytes. The value of this member will change as members are added to the structure.
Size: DWORD; // Specifies the total size of the data returned, in bytes. This may include data that follows this structure.
BytesPerCacheLine: DWORD; // The number of bytes in a cache line of the device.
BytesOffsetForCacheAlignment: DWORD; // The address offset necessary for proper cache access alignment, in bytes.
BytesPerLogicalSector: DWORD; // The number of bytes in a logical sector of the device.
BytesPerPhysicalSector: DWORD; // The number of bytes in a physical sector of the device.
BytesOffsetForSectorAlignment: DWORD; // The logical sector offset within the first physical sector where the first logical sector is placed, in bytes.
end;
var
aVolumePath: array[0..MAX_PATH] of AnsiChar;
aVolumeName: array[0..MAX_PATH] of AnsiChar;
hFile: THANDLE;
inbuf: _STORAGE_PROPERTY_QUERY;
outbuf: _STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR;
dwLen: DWORD;
i: integer;
begin
// Convert the directory to a Volume Name
aVolumePath[0] := #$00;
if not GetVolumePathNameA(pAnsiChar(DFRooter_HayStackDirectory), // _In_ LPCTSTR lpszFileName,
aVolumePath, // _Out_ LPTSTR lpszVolumePathName,
length(aVolumePath)) then raiseLastOsError; // _In_ DWORD cchBufferLength
aVolumeName[0] := #$00;
if not GetVolumeNameForVolumeMountPointA(aVolumePath, // _In_ LPCTSTR lpszVolumeMountPoint,
aVolumeName, // _Out_ LPTSTR lpszVolumeName,
length(aVolumeName)) then raiseLastOsError; // _In_ DWORD cchBufferLength
// Opening a physical device so no trailing '\'. Trailing '\' would open the ROOT DIR instead of the volume
for i := 1 to High(aVolumeName) do
if aVolumeName[i] = #0 then begin
if aVolumeName[i-1] = '\' then aVolumeName[i-1] := #0;
break;
end;
//create the file
hFile := CreateFileA(PAnsiChar(@aVolumeName[0]), // _In_ LPCTSTR lpFileName,
GENERIC_READ, // _In_ DWORD dwDesiredAccess,
FILE_SHARE_READ or FILE_SHARE_WRITE, //_In_ DWORD dwShareMode,
0, // _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
OPEN_EXISTING, // _In_ DWORD dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL, // _In_ DWORD dwFlagsAndAttributes,
0); // _In_opt_ HANDLE hTemplateFile
if (hFile = INVALID_HANDLE_VALUE) then raiseLastOsError;
try
ZeroMemory(@inbuf, SizeOf(inbuf));
ZeroMemory(@outbuf, SizeOf(outbuf));
inbuf.QueryType := PropertyStandardQuery;
inbuf.PropertyId := StorageAccessAlignmentProperty;
outbuf.Size := sizeOf(outbuf);
if not DeviceIoControl(hFile, // _In_ HANDLE hDevice,
IOCTL_STORAGE_QUERY_PROPERTY, // _In_ DWORD dwIoControlCode,
@inbuf, // _In_opt_ LPVOID lpInBuffer,
sizeof(inbuf), // _In_ DWORD nInBufferSize,
@outbuf, // _Out_opt_ LPVOID lpOutBuffer,
sizeof(outbuf), // _In_ DWORD nOutBufferSize,
dwLen, // _Out_opt_ LPDWORD lpBytesReturned,
nil) then raiseLastOsError; // _Inout_opt_ LPOVERLAPPED lpOverlapped
finally
CloseHandle(hFile);
end;
end;
答案 0 :(得分:5)
让我们查找IOCTL_STORAGE_QUERY_PROPERTY
定义:
CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
- 此处使用FILE_ANY_ACCESS
。这意味着任何文件句柄,任何访问权限都适用于此 IOCTL 。但是如何打开设备发送此ioctl?您在通话GENERIC_READ
中使用CreateFileA
(为什么不在CreateFileW
?)。正是在这一点上我猜你有访问被拒绝的错误。也可以使用IOCTL_DISK_GET_DRIVE_GEOMETRY
来获取扇区大小 - 它也使用FILE_ANY_ACCESS
。所以,如果你有确切的设备名称,你可以使用下一个代码( c / c ++ ):
HANDLE hFile = CreateFileW(DeviceName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
DISK_GEOMETRY dg;
STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR sad;
static STORAGE_PROPERTY_QUERY spq = { StorageAccessAlignmentProperty, PropertyStandardQuery };
ULONG BytesReturned;
if (!DeviceIoControl(hFile, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), &sad, sizeof(sad), &BytesReturned, 0))
{
GetLastError();
}
if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, &dg, sizeof(dg), &BytesReturned, 0))
{
GetLastError();
}
CloseHandle(hFile);
}
else
{
GetLastError();
}
这个代码甚至可以从低完整性过程中完美地运行。没有任何特权或管理员sid要求。
请注意DeviceName
必须完全设备名称,而不是文件/文件夹名称。
像"\\\\?\\c:"
这样的平均名称是可以的,但对于名称"\\\\?\\c:\\"
或"\\\\?\\c:\\anypath"
,您已经获得了ERROR_INVALID_PARAMETER
(或STATUS_INVALID_PARAMETER
),如果磁盘是通过文件系统。这是因为IOCTL_STORAGE_QUERY_PROPERTY
或IOCTL_DISK_GET_DRIVE_GEOMETRY
仅由磁盘设备对象处理。但是当磁盘由文件系统挂载时 - io子系统将请求重定向到文件系统设备对象,而不是通过VPB(除非您通过确切的设备名称和非常低的访问权限打开文件)。文件系统设备just return STATUS_INVALID_PARAMETER
在任何IOCTL
(IRP_MJ_DEVICE_CONTROL
)上,如果这不是卷打开,而是文件或目录。否则将其传递给磁盘设备对象(不要将其与FSCTL
(IRP_MJ_FILE_SYSTEM_CONTROL
) - DeviceIoControl
内部呼叫或ZwDeviceIoControlFile
(发送ioctl)或{{1}混淆}(发送fsctl))
另一个选项,获取磁盘扇区信息 - 查询文件系统,当然万一磁盘是由某些文件系统挂载的。我们可以使用此NtQueryVolumeInformationFile
FileFsSectorSizeInformation
(从win8开始)或FileFsSizeInformation
。再次 - 对于此请求,我们可以使用任何访问权限打开文件句柄。我们不需要ZwFsControlFile
GENERIC_READ
注意 - 在这里我们也可以使用任何文件路径和确切的设备路径(有一个重要的注释) - 所以HANDLE hFile = CreateFileW(FileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILE_FS_SECTOR_SIZE_INFORMATION ffssi;
FILE_FS_SIZE_INFORMATION ffsi;
IO_STATUS_BLOCK iosb;
NtQueryVolumeInformationFile(hFile, &iosb, &ffsi, sizeof(ffsi), FileFsSizeInformation);
NtQueryVolumeInformationFile(hFile, &iosb, &ffssi, sizeof(ffssi), FileFsSectorSizeInformation);
CloseHandle(hFile);
}
和"\\\\?\\c:"
并说"\\\\?\\c:\\"
- 这一切都可以。但是,如果确切的设备名称("\\\\?\\c:\\windows\\notepad.exe"
),您需要使用"\\\\?\\c:"
对呼叫FILE_EXECUTE
中的设备进行访问,否则文件系统设备将被打开磁盘设备而FO_DIRECT_DEVICE_OPEN
将在文件对象中设置。结果请求将被发送到磁盘设备对象,它不处理它,你得到CreateFileW
范妮msdn说
使用此(
STATUS_INVALID_DEVICE_REQUEST
)IOCTL来获取物理 扇区大小确实有一些局限性。它:
- 需要提升权限;如果您的应用没有使用权限运行,则可能需要将Windows服务应用程序编写为
如上所述
这是错误或有意识的谎言 - 再次不需要任何提升的特权。 this code甚至从来宾帐户工作,整体水平也很低。我们当然可以在调用IOCTL_STORAGE_QUERY_PROPERTY
中使用STANDARD_RIGHTS_READ
(注意 - 这不是GENERIC_READ
- 使用GENERIC_READ
是严重错误),但可以使用和0(在此case CreateFileW
实际上使用CreateFile
访问请求)。所以文件是坏的和错误的
答案 1 :(得分:0)
我知道这个年龄已经两岁了,但是我今天和这个人奋斗了一段时间,由于它们的复杂性/缺乏完整性,我对找到的任何答案都不满意。也许这个答案可以为别人省事。
为了以旧方式获取扇区信息,应用程序必须打开与文件存储位置关联的物理设备。简而言之,过程如下:
当为新的Windows 8和更高版本设计的应用程序存在很多可用的新文件信息类时,这会在以下两个步骤中有效地完成所有这些工作:
这将产生人们可能想要的有关扇区大小和对齐方式的所有相关信息,而与具有以下定义的FILE_STORAGE_INFO结构形式的底层设备技术无关:
typedef struct _FILE_STORAGE_INFO {
ULONG LogicalBytesPerSector;
ULONG PhysicalBytesPerSectorForAtomicity;
ULONG PhysicalBytesPerSectorForPerformance;
ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
ULONG Flags;
ULONG ByteOffsetForSectorAlignment;
ULONG ByteOffsetForPartitionAlignment;
} FILE_STORAGE_INFO, *PFILE_STORAGE_INFO;