Problema scrittura RAW su disco USB

di il
7 risposte

Problema scrittura RAW su disco USB

Ho uno strano problema quando scrivo un singolo settore su un drive USB

Queste sono le funzioni che uso (ovviamente semplificate)

Lettura di un singolo settore (questa funziona corrrettamente)
 hDevice := CreateFile(PChar('\\.\G:'),
                       GENERIC_READ,
                       FILE_SHARE_READ or FILE_SHARE_WRITE,
                       nil,
                       OPEN_EXISTING,
                       0,
                       0);

 If hDevice <> INVALID_HANDLE_VALUE Then
    If SetFilePointerEx(hDevice, 10*512, nil, FILE_BEGIN) Then
       begin
         ReadFile(hDevice, Data[1], 512, BytesRead, nil);
       end;
Scrittura di un singolo settore (qui' avviene l'errore)
 hDevice := CreateFile(PChar('\\.\G:'),
                       GENERIC_WRITE,
                       FILE_SHARE_READ or FILE_SHARE_WRITE,
                       nil,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       0);
 If hDevice <> INVALID_HANDLE_VALUE Then
    If SetFilePointerEx(hDevice, 10*512, nil, FILE_BEGIN) Then
       begin
         WriteFile(hDevice, Data[1], 512, BytesWrite, nil);
       end;

Praticamente WriteFile ritorna sempre con errore 5 (accesso negato). Tenete presente che il programma lavora con i permessi di amministratore,ho provato ad utilizzare la funzione DeviceIoControl con parametyro FSCTL_LOCK_VOLUME in maniera da bloccare il disco, ho provato ad eseguire la funzione DeleteVolumeMountPoint per smontare l'USB e lavorare con il nome fisico dell'unità ma nulla.

Quindi, per concludere

Il programma è un 32bit
CreateFile funziona perfettamente
SetFilePointerEx funziona perfettamente
ReadFile funziona perfettamente
WriteFile mi da sempre l'errore 5 (access denied) ma solo su Windows 10 e Windows 11

Qualche idea?

Grazie

7 Risposte

  • Re: Problema scrittura RAW su disco USB

    Fermo restando che non uso quel codice da almeno 15 anni, a me pare corretto, ovviamente premettendo che il file esista (ma da quanto dici è così) e su cui puoi operare (cioè non è un file protetto oppure già in uso).. e ovviamente che nel PChar(...) ci sia il nome del file corretto e non solo il disco ....

    Che versione di Delphi stai usando ?

    A me comunque con l'ultima versione di Delphi (app 32 bit) quel codice funziona esattamente così come è, con un file esistente di 0 bytes (o anche con lunghezze differenti):

    PChar('\\.\E:\Pollo.txt')

    Il file è di 5632 bytes, aggiornato correttamente.

    Se il file non esiste, ovviamente non si arriva neanche al tuo codice ...

    Devi stare solo attento che il file che stai usando non provenga da Internet (o altre fonti) e che abbia l'attributo di blocco (eventualmente sbloccalo).

  • Re: Problema scrittura RAW su disco USB

    Non cerco di scrivere un file ma direttamente un settore sul Drive USB.

    Ovviamente rispetto tutte le limitazioni del caso come scrivere o leggere un intero settore e non parzialmente ed anche il seek lo faccio all'inizio del singolo settore

  • Re: Problema scrittura RAW su disco USB

    Oops ... scusami non l'avevo compreso.

    Dubito che in Windows 10 o 11 tu possa accedere fisicamente (anche virtualmente) al disco in quel modo.

    Intanto se anche potessi farlo devi usare sicuramente l'applicazione con elevazione dei privilegi (ma questo lo sai già).

    Giustamente usi lo smontaggio del volume con FSCTL_LOCK_VOLUME, ma a questo punto dovresti usare il 

    [EDIT]: prova a smontare il volume oltre che a bloccarlo (FSCTL_DISMOUNT_VOLUME) ...

    \\.\PhysicalDriveX

    dove X è il numero identificativo del disco, per scriverci secondo me (e quindi nella CreateFile).

    Caspita, mi sembra di ritornare ai tempi di WIndows 3.1 quando si proteggevano i floppy ...

    P.S.: rileggendo il tuo primo post, mi pare tu abbia già fatto queste prove ...

    Farò qualche prova appena possibile (sperando di non danneggiare i mie dischi ....) ...

  • Re: Problema scrittura RAW su disco USB

    Grazie ed hai ragione mi serve proprio per una protezione di un software tramite chiavettta criptata: praticamente leggo tutte le carattteristiche della chiavetta compreso seriale e tipo di fat, le cripto e le memorizzo in un settore specifico.

  • Re: Problema scrittura RAW su disco USB

    Oltre a smontare il disco, non devi usare l'unità logica "G" ma PhysicalDrive

    se non sai quale sia ma ne conosci l'unità logica puoi aggiungere questo

    var
    DrivePath: string;
    .....
    
    begin
    .....
    DrivePath := GetPhysicalDriveFromLetter('G');

    e sostituisci usando il drivepath

    puoi anche creare la funzione che:

    1) controlla la dimensione del settore

    2) va a scrivere su un determinato settore

    3) controlla che il settore in questione non sia il settore 0

    magari ci lavoro e ti faccio sapere...

  • Re: Problema scrittura RAW su disco USB

    Purtroppo ho provato anche questa e non solo questa, la prova ultima è

    Prelevo il nome del drive fisico

    Smonto l'unità

    Apro una nuova unità con il nome fisico prelevato prima

    blocco l'unità

    Provo a scrivere

    sblocco l'unità

    rimonto il drive

    Ma anche questa fallisce

  • Re: Problema scrittura RAW su disco USB

    Prova a studiare questa. consente di scegliere in quale settore andare a scrivere e controlla la dimensione del cluster, poi fa un controllo che tutto sia andato a buon fine.

    uses
      Windows, SysUtils;
    
    type
      TDiskInfo = record
        PhysicalPath: string;
        BytesPerSector: DWORD;
        PhysicalBytesPerSector: DWORD;
        TotalSectors: Int64;
      end;
    
    function GetPhysicalDriveFromLetter(const DriveLetter: Char): string;
    var
      hVolume: THandle;
      BytesReturned: DWORD;
      Extents: VOLUME_DISK_EXTENTS;
    begin
      Result := '';
    
      hVolume := CreateFile(PChar(Format('\\.\%s:', [UpCase(DriveLetter)])),
        0, FILE_SHARE_READ or FILE_SHARE_WRITE,
        nil, OPEN_EXISTING, 0, 0);
    
      if hVolume = INVALID_HANDLE_VALUE then Exit;
    
      try
        if DeviceIoControl(hVolume,
          IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
          nil, 0,
          @Extents, SizeOf(Extents),
          BytesReturned, nil) then
        begin
          Result := Format('\\.\PhysicalDrive%d',
            [Extents.Extents[0].DiskNumber]);
        end;
      finally
        CloseHandle(hVolume);
      end;
    end;
    
    function GetDiskInfo(const PhysicalPath: string; out Info: TDiskInfo): Boolean;
    var
      hDevice: THandle;
      BytesReturned: DWORD;
      Geo: DISK_GEOMETRY_EX;
      Query: STORAGE_PROPERTY_QUERY;
      Desc: STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR;
    begin
      Result := False;
    
      hDevice := CreateFile(PChar(PhysicalPath),
        0, FILE_SHARE_READ or FILE_SHARE_WRITE,
        nil, OPEN_EXISTING, 0, 0);
    
      if hDevice = INVALID_HANDLE_VALUE then Exit;
    
      try
        // Geometria base
        if not DeviceIoControl(hDevice,
          IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
          nil, 0,
          @Geo, SizeOf(Geo),
          BytesReturned, nil) then Exit;
    
        Info.PhysicalPath := PhysicalPath;
        Info.BytesPerSector := Geo.Geometry.BytesPerSector;
        Info.TotalSectors := Geo.DiskSize.QuadPart div Info.BytesPerSector;
    
        // Query settore fisico reale
        ZeroMemory(@Query, SizeOf(Query));
        Query.PropertyId := StorageAccessAlignmentProperty;
        Query.QueryType := PropertyStandardQuery;
    
        if DeviceIoControl(hDevice,
          IOCTL_STORAGE_QUERY_PROPERTY,
          @Query, SizeOf(Query),
          @Desc, SizeOf(Desc),
          BytesReturned, nil) then
        begin
          Info.PhysicalBytesPerSector := Desc.BytesPerPhysicalSector;
        end
        else
          Info.PhysicalBytesPerSector := Info.BytesPerSector;
    
        Result := True;
    
      finally
        CloseHandle(hDevice);
      end;
    end;
    
    function IsSystemDisk(const PhysicalPath: string): Boolean;
    begin
      // controllo che non sia PhysicalDrive0
      Result := Pos('PhysicalDrive0', PhysicalPath) > 0;
    end;
    
    function ValidateSector(const Info: TDiskInfo; Sector: Int64; out Err: string): Boolean;
    begin
      Result := False;
      Err := '';
    
      if Sector <= 0 then
      begin
        // controllo settore 0
        Err := 'attenzione: settore 0 non consentito';
        Exit;
      end;
    
      if Sector >= Info.TotalSectors then
      begin
        //controllo se il settore esiste
        Err := 'Settore fuori range';
        Exit;
      end;
    
      Result := True;
    end;
    
    function AllocAligned(Size, Align: Cardinal): Pointer;
    var
      Raw: Pointer;
      Addr: UIntPtr;
    begin
      GetMem(Raw, Size + Align);
      Addr := (UIntPtr(Raw) + Align - 1) and not (Align - 1);
      Result := Pointer(Addr);
    end;
    
    function WriteSectorSafe(const Info: TDiskInfo; Sector: Int64): Boolean;
    var
      hDevice: THandle;
      Offset: LARGE_INTEGER;
      Buffer, VerifyBuffer, Backup: Pointer;
      BytesRW: DWORD;
      SectorSize: DWORD;
    begin
      Result := False;
    
      SectorSize := Info.BytesPerSector;
    
      Buffer := AllocAligned(SectorSize, Info.PhysicalBytesPerSector);
      VerifyBuffer := AllocAligned(SectorSize, Info.PhysicalBytesPerSector);
      Backup := AllocAligned(SectorSize, Info.PhysicalBytesPerSector);
    
      try
        FillChar(Buffer^, SectorSize, $AA);
    
        hDevice := CreateFile(PChar(Info.PhysicalPath),
          GENERIC_READ or GENERIC_WRITE,
          FILE_SHARE_READ or FILE_SHARE_WRITE,
          nil, OPEN_EXISTING,
          FILE_FLAG_NO_BUFFERING or FILE_FLAG_WRITE_THROUGH,
          0);
         // usa esplicitamente le opzini FILE_FLAG_NO_BUFFERING or FILE_FLAG_WRITE_THROUGH,
        if hDevice = INVALID_HANDLE_VALUE then Exit;
    
        try
          Offset.QuadPart := Sector * SectorSize;
    
          SetFilePointerEx(hDevice, Offset, nil, FILE_BEGIN);
    
          // BACKUP
          if not ReadFile(hDevice, Backup^, SectorSize, BytesRW, nil) then Exit;
    
          SetFilePointerEx(hDevice, Offset, nil, FILE_BEGIN);
    
          // scrittura
          if not WriteFile(hDevice, Buffer^, SectorSize, BytesRW, nil) then Exit;
    
          SetFilePointerEx(hDevice, Offset, nil, FILE_BEGIN);
    
          // verifica
          if not ReadFile(hDevice, VerifyBuffer^, SectorSize, BytesRW, nil) then Exit;
    
          if CompareMem(Buffer, VerifyBuffer, SectorSize) then
            Result := True
          else
          begin
            // ripristino
            SetFilePointerEx(hDevice, Offset, nil, FILE_BEGIN);
            WriteFile(hDevice, Backup^, SectorSize, BytesRW, nil);
          end;
    
        finally
          CloseHandle(hDevice);
        end;
    
      finally
        FreeMem(Buffer);
        FreeMem(VerifyBuffer);
        FreeMem(Backup);
      end;
    end;
Devi accedere o registrarti per scrivere nel forum
7 risposte