Desde mi aplicación de servidor web, necesito verificar el tamaño del sector físico del disco duro donde se encuentra la aplicación. Para esto, uso DeviceIoControl con IOCTL_STORAGE_QUERY_PROPERTY para consultar StorageAccessAlignmentProperty. El problema es que cuando intento ejecutar estos comandos desde el servidor web, aparece el error " Acceso denegado ".

¿Cómo puedo recuperar el tamaño del sector físico del disco duro donde se encuentra inetpub desde la aplicación del servidor web?

Lo sé por https://msdn.microsoft.com/windows / compatibilidad / actualización-de-compatibilidad-de-disco-de-formato-avanzada que con Windows 8 Microsoft ha introducido una nueva API que permite realizar llamadas desde una aplicación sin privilegios. La API tiene la forma de una nueva clase de información FileFsSectorSizeInformation con estructura asociada FILE_FS_SECTOR_SIZE_INFORMATION, pero no sé cómo hacer que funcione con Delphi.

Este es mi código real que no funciona (escrito en 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
loki 17 feb. 2018 a las 12:18

2 respuestas

La mejor respuesta

Busquemos la definición de IOCTL_STORAGE_QUERY_PROPERTY:

CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS) - FILE_ANY_ACCESS utilizado aquí. esto significa que cualquier identificador de archivo, con cualquier derecho de acceso, está bien para este IOCTL . pero ¿cómo abres el dispositivo para enviar este ioctl? usas GENERIC_READ en la llamada CreateFileA (¿y por qué no CreateFileW?). exactamente en este punto, supongo que tiene un error de acceso denegado. también para obtener el tamaño del sector, puede usar say IOCTL_DISK_GET_DRIVE_GEOMETRY; también usa FILE_ANY_ACCESS. entonces, si tiene exactamente el nombre del dispositivo, puede usar el siguiente código ( 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();
}

Este código funcionó perfectamente incluso desde un proceso de baja integridad. no se requieren privilegios o sid de administrador para esto.

Tenga en cuenta que DeviceName debe ser exactamente el nombre del dispositivo, no el nombre del archivo / carpeta.

Este nombre medio como "\\\\?\\c:" está bien, pero para el nombre "\\\\?\\c:\\" o "\\\\?\\c:\\anypath" ya tienes ERROR_INVALID_PARAMETER (o STATUS_INVALID_PARAMETER), si el sistema de archivos monta el disco. esto se debe a que IOCTL_STORAGE_QUERY_PROPERTY o IOCTL_DISK_GET_DRIVE_GEOMETRY es manejado solo por el objeto de dispositivo de disco. pero cuando el sistema de archivos monta el disco, el subsistema io redirecciona la solicitud al objeto del dispositivo del sistema de archivos a través de VPB (a menos que abra el archivo exactamente con el nombre del dispositivo y con derechos de acceso muy bajos). dispositivo de sistema de archivos solo devuelve STATUS_INVALID_PARAMETER en cualquier IOCTL (IRP_MJ_DEVICE_CONTROL) si este no es un volumen abierto, sino un archivo o directorio. de lo contrario, lo pasa al objeto del dispositivo de disco (no confunda esto con FSCTL (IRP_MJ_FILE_SYSTEM_CONTROL) - la llamada interna DeviceIoControl o ZwDeviceIoControlFile (enviar ioctl) o {{X14} } (enviar fsctl))

Otra opción, obtener información del sector del disco: consulta al sistema de archivos sobre esto, por supuesto, en caso de que algún sistema de archivos monte el disco. que podemos utilizar para este { {X0}} FileFsSectorSizeInformation (comience desde win8) o FileFsSizeInformation. de nuevo, para esta solicitud podemos abrir el identificador de archivo con cualquier acceso. no necesitamos tener 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);

}

Tenga en cuenta que aquí podemos usar cualquier ruta de archivo y exactamente la ruta del dispositivo también (con una nota importante), así que "\\\\?\\c:" y "\\\\?\\c:\\" y diga "\\\\?\\c:\\windows\\notepad.exe", todo estará bien aquí. sin embargo, en caso de que sea exactamente el nombre del dispositivo ("\\\\?\\c:"), debe usar el acceso FILE_EXECUTE al dispositivo en la llamada CreateFileW, de lo contrario, el dispositivo del sistema de archivos se abrirá dispositivo de disco y FO_DIRECT_DEVICE_OPEN se establecerá en el objeto de archivo. como resultado, la solicitud se enviará al objeto del dispositivo de disco, que no lo maneja y obtiene STATUS_INVALID_DEVICE_REQUEST


Fanny que msdn dice

Usando este (IOCTL_STORAGE_QUERY_PROPERTY) IOCTL para obtener el físico el tamaño del sector tiene varias limitaciones. Eso:

  • Requiere privilegios elevados; si su aplicación no se ejecuta con privilegios, es posible que deba escribir una aplicación de servicio de Windows como
    anotado arriba

Esto es un error o una mentira consciente; nuevamente, no se necesita ningún privilegio elevado para esto. este código funcionó incluso desde una cuenta de invitado con un nivel de integridad bajo. por supuesto, podemos usar y STANDARD_RIGHTS_READ (nota: esto no es GENERIC_READ; usar GENERIC_READ es un error crítico aquí) en la llamada CreateFileW, pero podemos usar y 0 (en este caso CreateFile realmente usa FILE_READ_ATTRIBUTES | SYNCHRONIZE solicitud de acceso). entonces la documentación es mala e incorrecta

5
RbMm 18 feb. 2018 a las 01:06

Sé que esto tiene dos años en este momento, pero luché con este hoy por un tiempo y no estaba satisfecho con las respuestas que pude encontrar debido a su complejidad / falta de integridad. Quizás esta respuesta evite problemas a otros.

Para obtener la información del sector de la forma anterior, la aplicación debe abrir el dispositivo físico asociado con la ubicación en la que se almacena el archivo. El proceso en pocas palabras es el siguiente:

  1. Obtenga la ruta del volumen para un archivo en la ubicación: GetVolumePathName
  2. Abra el volumen: CreateFile
  3. Obtenga las extensiones de volumen: VOLUME_DISK_EXTENTS (puede que tenga que llamar dos veces)
  4. Para cada extensión de volumen ...
    1. Abra el dispositivo asociado - CreateFile
    2. Obtenga el descriptor de alineación: STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
  5. Fusionar los descriptores de alineación de todas las extensiones

Esto es un gran problema para las aplicaciones diseñadas para Windows 8 y versiones posteriores cuando hay una nueva clase de información de archivo disponible que efectivamente hace todo esto en los dos pasos siguientes:

  1. Abra un archivo en la ubicación - CreateFile
  2. Obtenga la información de almacenamiento del archivo: GetFileInformationByHandleEx ( FileStorageInfo)

Esto proporcionará toda la información relevante que uno podría desear sobre el tamaño y la alineación de los sectores, independientemente de la tecnología del dispositivo subyacente en forma de FILE_STORAGE_INFO estructura que tiene la siguiente definición:

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;
0
240DL 26 feb. 2020 a las 22:13