Pertanyaan Apa yang terjadi secara internal ketika jalur file melebihi perkiraan. 32767 karakter di Windows?


Di Windows (diasumsikan 2000 dan seterusnya), sebuah file path bisa paling panjang sekitar 32767 karakter. Keterbatasan ini ada karena penanganan internal dengan UNICODE_STRING di API asli (juga di sisi kernel, di driver dll). Sejauh ini bagus. Saya tahu teori di balik bagian itu.

Alasan untuk batas adalah bahwa Length dan MaximumLength anggota dari UNICODE_STRING menghitung jumlah byte dalam Buffer, tetapi 16 bit unsigned integer sendiri.

Saya juga tahu mengapa batas adalah perkiraan daripada batas yang ditetapkan. Ini sebagian besar karena bagaimana nama file Anda (mis. \\.\C:\boot.ini) diselesaikan ke bentuk asalnya (mis. \??\C:\boot.ini) dan kemudian ke sesuatu yang diawali oleh sebenarnya nama perangkat volume dan kemudian diikuti oleh jalur yang relatif terhadap volume itu, mis. \Device\HarddiskVolume2\boot.ini.

Lebih jauh lagi dari Windows Explorer gejala yang dikenal ketika memukul ("ANSI") MAX_PATH limit adalah untuk berpura-pura file atau folder tidak ada di beberapa versi Windows (mungkin ini diperbaiki di beberapa titik).

Tapi apa yang terjadi pada manajer objek, I / O manager dan tingkat driver sistem file masing-masing ketika saya menelepon CreateFile() dengan jalan yang terlihat seperti \\.\C:\...\filename.ext dan seluruh jalannya tidak melebihi batas, tapi mencapai itu, di panggil saya untuk kernel32.dll's CreateFile() dan kemudian akan diperluas? ...

Baik SDK maupun WDK tampaknya tidak terlalu menyukai topik tersebut. Atau apakah saya melihat di bagian yang salah?


32
2018-03-07 02:42


asal


Jawaban:


Karena saya malas, saya tidak menulis program tes tetapi mengujinya dengan sangat baik Manajer Jauh yang menangani hal-hal seperti jalur panjang (lebih lama dari MAX_PATH) atau nama file khusus (con, prn dll) baik-baik saja.

Saya membuat string persis 255 karakter ("12345678901234 ... 012345") dan mulai membuat direktori bertingkat. Untungnya, Far's "Make Directory" berfungsi mengambil string yang dipisahkan slash yang berarti "membuat direktori yang disarang" sehingga saya bisa melakukannya hanya dalam beberapa langkah dengan menyiapkan string di editor internal dengan beberapa copy & paste.

Jalur terpanjang yang bisa saya ciptakan adalah 32739 karakter panjang, dihitung dari "C: \" (itu tidak termasuk "\\? \" ditambahkan oleh Far). Kesalahan yang saya dapatkan ketika mencoba membuat direktori atau file hanya dengan satu karakter tambahan adalah "Nama file atau ekstensi terlalu panjang.". Jika saya mencoba masuk ke direktori itu, saya mendapatkan kesalahan yang sama.

EDIT: menghabiskan beberapa waktu di debugger dan inilah yang terjadi pada tingkat API Win32:

  1. Saya mencoba membuat file dengan satu karakter di atas batas
  2. Panggilan jauh CreateFileW dengan string "\\? \ C: \ 123 [...] 012345" yang mana 32744 panjang lebar karakter (tidak termasuk nol yang berhenti).
  3. CreateFileW melakukan beberapa pemeriksaan tambahan, mengubah string yang diakhiri null menjadi UNICODE_STRING (Panjang = 65488, MaksimumLength = 65490) dan menyiapkan OBJECT_ATTRIBUTESstruct.
  4. CreateFileW lalu menelepon NtCreateFile di ntdll.dll, yang hanya pembungkus syscall petunjuk.
  5. NtCreateFile kembali 0xC0000106 (STATUS_NAME_TOO_LONG).
  6. Nilai status tersebut kemudian dikonversi (menggunakan RtlNtStatusToDosError) ke kesalahan Win32 206 (ERROR_FILENAME_EXCED_RANGE).

Saya tidak repot-repot memeriksa apa yang terjadi di kernel, tapi saya kira saya bisa melihat itu juga.

EDIT2: Saya berlari WinObj dan menemukan itu di sistem saya C: adalah symlink ke \Device\HarddiskVolume1. String ini 23 karakter panjang. Jika kita mengganti \C: dalam string yang diteruskan ke NtCreateFile dengan itu, kita dapat 32744 - 3 + 23 = 32764 karakter. Bersama dengan nol yang berhenti, ini membutuhkan 65530 byte. Masih kurang dari batas (0xFFFF = 65535) jadi saya kira ada sesuatu yang ditambahkan tambahan, seperti nama sesi atau nama ruang.

EDIT3: setelah melalui kernel:

  1. NtCreateFile panggilan IopCreateFile
  2. IopCreateFile panggilan ObOpenObjectByName
  3. ObOpenObjectByName panggilan ObpLookupObjectName
  4. ObpLookupObjectName memeriksa ObpDosDevicesShortNamePrefix ("\??\") -> sukses
  5. itu melompati awalan dan membagi bagian yang tersisa menjadi "C:" dan "\1234..."
  6. itu menyelesaikan "C:" dengan panggilan ke ObpLookupDirectoryEntry
  7. kemudian memanggil ObpParseSymbolicLink meneruskannya ke entri direktori yang dicari (_OBJECT_SYMBOLIC_LINK dengan LinkTarget == "\Device\HarddiskVolume1" dan DosDeviceDriveIndex == 3) dan bagian yang tersisa dari nama.
  8. Kemudian melakukan sesuatu seperti ini (dengan setia direproduksi oleh ReactOS):

    TargetPath = &SymlinkObject->LinkTarget;
    TempLength = TargetPath->Length;
    TotalLength = TempLength + RemainingName->Length;
    if (LengthUsed > 0xFFF0)
        return STATUS_NAME_TOO_LONG;
    

    Dalam kasus kami, 46 + 65476 = 65522 (0xfff2) yang hanya di atas batas.

    Jadi di sana, misteri terpecahkan (saya harap!).

P.S. semuanya diuji di bawah Windows 7 x64 SP1.


38
2018-03-12 22:49