Autor Téma: Seekování v rámci disku  (Přečteno 13117 krát)

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Seekování v rámci disku
« kdy: 31-12-2013, 10:13:12 »
Nějak nemohu přijít na to, jak přimět SetFilePointer, aby skočil na konec disku:
Kód: Delphi [Vybrat]
  1.   Handle := CreateFileW('\\.\PhysicalDrive0', GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  2.   if Handle <> INVALID_HANDLE_VALUE then
  3.     try
  4.       LowDWORD := 0;
  5.       HighDWORD := 0;
  6.       LowDWORD := SetFilePointer(Handle, LowDWORD, @HighDWORD, FILE_END);
  7.       Writeln(Format('Delka = $%08.8x%08.8x, Error = %d', [HighDWORD, LowDWORD, GetLastError]));
  8.     finally
  9.       CloseHandle(Handle);
  10.       end;
Očekával bych, že se mi vrátí délka disku, ale ve skutečnosti dostanu chybu ERROR_INVALID_FUNCTION. To by naznačovalo, že disk je "non-seeking device", ale když zkusím nastavit pozici na 512 bajtů od začátku, tak to v pohodě projde. Pouze seekování od konce se mi nějak nedaří. Nějaký nápad, proč?

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Re:Seekování v rámci disku
« Odpověď #1 kdy: 31-12-2013, 10:23:15 »
Btw., GetFileSize končí stejně. FILE_FLAG_NO_BUFFERING na to nemá vliv.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Re:Seekování v rámci disku
« Odpověď #2 kdy: 31-12-2013, 10:34:21 »
Prozatímní řešení, které se mi ale vůbec nelíbí:
Kód: Delphi [Vybrat]
  1. const
  2.   FILE_DEVICE_DISK                = $00000007;
  3.   FILE_READ_ACCESS    = $0001;    // for files and pipes
  4.   METHOD_BUFFERED             = 0;
  5.   IOCTL_DISK_BASE = FILE_DEVICE_DISK;
  6.   IOCTL_DISK_GET_LENGTH_INFO =
  7.     (IOCTL_DISK_BASE shl 16) or (FILE_READ_ACCESS shl 14) or ($0017 shl 2) or (METHOD_BUFFERED);
  8. var
  9.   LowDWORD, HighDWORD: DWORD;
  10.   BytesReturned, LastError: DWORD;
  11.   Buffer: int64;
  12. begin
  13.   if GetFileType(Handle) = FILE_TYPE_DISK then
  14.     if DeviceIoControl(Handle, IOCTL_DISK_GET_LENGTH_INFO, nil, 0, @Buffer, Sizeof(Buffer), BytesReturned, nil) then
  15.       Result := Buffer
  16.     else
  17.       Result := -1
  18.   else
  19.     begin
  20.     LowDWORD := GetFileSize(Handle, @HighDWORD);
  21.     LastError := GetLastError;
  22.     if (LastError = NO_ERROR) or (LowDWORD <> INVALID_FILE_SIZE) then
  23.       Result := (Int64(HighDWORD) shl 32) or Int64(LowDWORD)
  24.     else
  25.       Result := -1;
  26.     end;

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Re:Seekování v rámci disku
« Odpověď #3 kdy: 31-12-2013, 14:53:45 »
Koukám, že jsem trochu rychle skočil k velikosti souboru. Tím jsem chtěl říct, že pro disková zařízení nefunguje seek od konce souboru (origin=FILE_END). Seek od začátku souboru i od aktuální pozice funguje. Takže je potřeba řešit akorát SetFilePointer s FILE_END, a to se dá dobře ošetřit přes FILE_BEGIN, pokud známe velikost souboru. Tak proto jsem se najednou začal zabývat velikostí.

Offline Vrtule

  • Mladík
  • **
  • Příspěvků: 54
  • Karma: 10
    • Verze Delphi: XE2
    • Jádro systému Windows
Re:Seekování v rámci disku
« Odpověď #4 kdy: 01-01-2014, 12:13:28 »
Ahoj,

nezkoušel jsi při těchto pokusech SetFilePointerEx či SetFileSizeEx? I když moc nevěřím, že by pomohly.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Re:Seekování v rámci disku
« Odpověď #5 kdy: 01-01-2014, 13:32:46 »
Zkoušel, nepomohly.

Offline Vrtule

  • Mladík
  • **
  • Příspěvků: 54
  • Karma: 10
    • Verze Delphi: XE2
    • Jádro systému Windows
Re:Seekování v rámci disku
« Odpověď #6 kdy: 02-01-2014, 21:16:29 »
Když seekuješ v tom disku od konce (poslední parametr SetFilePointer je FILE_END), dáváš tam tu hodnotu posunutí zápornou? Když mrknu na MSDN, tak tam píšou, že u souboru není problém posunout se i za jeho konec, neb se v případě potřeby jeho konec posune. U disků to ale asi půjde těžko; řekl bych, že i samotné posunutí na 0 bajtů od konce už je nevalidní pozice, protože validní pozice budou od nuly do konce - 1.

Citace
It is not an error to set a file pointer to a position beyond the end of the file. The size of the file does not increase until you call the SetEndOfFile, WriteFile, or WriteFileEx function. A write operation increases the size of the file to the file pointer position plus the size of the buffer written, which results in the intervening bytes uninitialized.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Re:Seekování v rámci disku
« Odpověď #7 kdy: 03-01-2014, 05:10:48 »
Když seekuješ v tom disku od konce (poslední parametr SetFilePointer je FILE_END), dáváš tam tu hodnotu posunutí zápornou?
Tradičně se tam dává nula (skok na konec souboru). Ale pro disky nefunguje ani -1.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3527
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:Seekování v rámci disku
« Odpověď #8 kdy: 03-01-2014, 09:07:04 »
Když seekuješ v tom disku od konce (poslední parametr SetFilePointer je FILE_END), dáváš tam tu hodnotu posunutí zápornou?
Tradičně se tam dává nula (skok na konec souboru). Ale pro disky nefunguje ani -1.
Ta nula se u souboru dava proto, ze se tim nastavuje  pozice pro append tj. pro zapis za konec souboru tj. pozice 1. pomyslneho bytu za koncem souboru.

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1574
  • Karma: 37
    • Pepak.net
Re:Seekování v rámci disku
« Odpověď #9 kdy: 03-01-2014, 09:33:42 »
To je jen terminologický rozdíl - já si pod "konec souboru" představuju "místo za posledním bajtem obsahu". Pro samotné téma threadu to není podstatné, protože seek u disků nefunguje (při použití FILE_END) pro žádný offset.

Offline Vrtule

  • Mladík
  • **
  • Příspěvků: 54
  • Karma: 10
    • Verze Delphi: XE2
    • Jádro systému Windows
Re:Seekování v rámci disku
« Odpověď #10 kdy: 03-01-2014, 12:56:19 »
Díval jsem se na SetFilePointer přes IDA. V zásadě se dá říci následující:

1) pro nastavení pozice se používá nativní funkce NtSetInformationFile s parametrem FilePositionInformation,
2) pokud dwMoveMethod = FILE_BEGIN, SetFilePointer jen z druhého a třetího parametru utvoří novou hodnotu pozice v "souboru" a volá NtSetInformatioNFile,
3) pokud dwMoveMethod = FILE_CURRENT, SetFilePointer použije NtQueryInformationFile s parametrem FilePositionInformation na zjištění aktuální pozice, sečte ji s hodnotami v druhém a třetím parametru a nastaví novou pozici přes NtSetInformationFile,
4) pokud dwMoveMethod = FILE_END, SetFilePointer nejprve provede NtQueryInformationFile s parametrem FileStandardInformation, čímž mj. zjistí velikost "souboru" (položka EndOfFile vrácené struktury FILE_STANDARD_INFORMATION). K velikosti přičte hodnoty druhého a třetího parametru a opět použije NtSetInformationFile.

Jelikož se nikde nenastavuje chyba na ERROR_INVALID_FUNCTION, zřejmě je vrácena právě oním voláním NtQueryInformationFile s parametrem FileStandardInformation. Což nasvědčuje tomu, že disky nepodporují IRP_MJ_QUERY_INFORMATION s FileStandardInformation, nebo je mezi jejich drivery nějaká mezivrstva, která tak činí.