Que se passe-t-il en interne lorsqu’un chemin de fichier dépasse env. 32767 caractères dans Windows?

Sous Windows (à partir de 2000), un chemin de fichier peut comporter au maximum environ 32 767 caractères. Cette limitation est due à la gestion interne avec UNICODE_STRING dans l’API native (également du côté du kernel, dans les pilotes, etc.). Jusqu’ici tout va bien. Je connais la théorie derrière cette partie.

La raison de la limite est que les membres Length et MaximumLength de UNICODE_STRING comptent le nombre d’octets dans le Buffer , mais sont des entiers non signés 16 bits eux-mêmes.

Je sais aussi pourquoi la limite est une approximation plutôt qu’une limite fixée. Ceci est principalement dû à la façon dont votre nom de fichier (par exemple, \\.\C:\boot.ini ) est résolu à sa forme native (par exemple, \??\C:\boot.ini ) et ensuite à quelque chose qui est préfixé par le nom de périphérique de volume réel, suivi du chemin relatif à ce volume, par exemple \Device\HarddiskVolume2\boot.ini .

De plus, à partir de l’Explorateur Windows, le symptôme connu lors du MAX_PATH limite (“ANSI”) MAX_PATH est de prétendre que le fichier ou le dossier n’existe pas dans certaines versions de Windows (cela a été corrigé à un moment donné).

Mais que se passe-t-il respectivement au niveau du gestionnaire d’object, du gestionnaire d’E / S et du pilote de système de fichiers lorsque j’appelle CreateFile() avec un chemin qui ressemble à \\.\C:\...\filename.ext et que tout le chemin ne dépasse la limite, mais y parvient , dans mon appel à CreateFile() , puis se développe? …

Ni les SDK ni les WDK ne semblent être particulièrement bavards sur le sujet. Ou ai-je cherché dans les mauvaises sections?

Comme je suis paresseux, je n’ai pas écrit de programme de test mais je l’ai testé avec l’excellent Far Manager qui gère des choses comme les longs chemins (plus longs que MAX_PATH ) ou les noms de fichiers spéciaux ( con , prn etc.).

J’ai créé une chaîne de 255 caractères exactement (“12345678901234 … 012345”) et j’ai commencé à créer des répertoires nesteds. Heureusement, la fonction “Make Directory” de Far prend une chaîne séparée par une barre oblique pour signifier “créer des répertoires nesteds”. J’ai donc pu le faire en quelques étapes en préparant une chaîne dans l’éditeur interne avec un copier / coller.

Le plus long chemin que j’ai pu créer était de 32739 caractères, à partir de “C: \” (c’est-à-dire qu’il n’inclut pas “\\? \” Ajouté par Far). L’erreur que j’obtiens lorsque j’essaie de créer un répertoire ou un fichier avec un seul caractère supplémentaire est ” Le nom de fichier ou l’extension est trop long “. Si j’essaie d’entrer dans ce répertoire, j’obtiens la même erreur.

EDIT : passé du temps dans le débogueur et voici ce qui se passe au niveau de l’API Win32:

  1. J’essaie de créer un fichier avec un caractère au dessus de la limite
  2. Far appelle CreateFileW avec la chaîne “\\? \ C: \ 123 […] 012345” qui fait 32744 caractères de long (sans compter le zéro final ).
  3. CreateFileW effectue des vérifications supplémentaires, convertit la chaîne terminée par UNICODE_STRING en UNICODE_STRING (Length = 65488, MaximumLength = 65490) et prépare une structure OBJECT_ATTRIBUTES .
  4. CreateFileW appelle ensuite NtCreateFile dans ntdll.dll , qui est juste un wrapper autour de l’instruction syscall .
  5. NtCreateFile renvoie 0xC0000106 ( STATUS_NAME_TOO_LONG ).
  6. Cette valeur d’état est ensuite convertie (en utilisant RtlNtStatusToDosError ) à l’erreur Win32 206 ( ERROR_FILENAME_EXCED_RANGE ).

Je n’ai pas pris la peine de vérifier ce qui se passe dans le kernel, mais je suppose que je pourrais y jeter un coup d’oeil .

EDIT2 : J’ai couru WinObj et trouvé que sur mon système C: est un lien symbolique vers \Device\HarddiskVolume1 . Cette chaîne comporte 23 caractères . Si nous remplaçons le \C: dans la chaîne transmise à NtCreateFile , nous obtenons 32744 – 3 + 23 = 32764 caractères. Avec le zéro final, cela nécessite 65530 octets. Toujours en deçà de la limite (0xFFFF = 65535), je suppose que quelque chose de plus est ajouté, comme un nom de session ou d’espace de noms.

EDIT3 : après avoir parcouru le kernel:

  1. NtCreateFile appelle IopCreateFile
  2. IopCreateFile appelle ObOpenObjectByName
  3. ObOpenObjectByName appelle ObpLookupObjectName
  4. ObpLookupObjectName vérifie ObpDosDevicesShortNamePrefix ( "\??\" ) -> succès
  5. il ignore le préfixe et divise la partie restante en "C:" et "\1234..."
  6. il résout le "C:" avec un appel à ObpLookupDirectoryEntry
  7. il appelle ensuite ObpParseSymbolicLink transmettant l’entrée d’annuaire recherchée ( _OBJECT_SYMBOLIC_LINK avec LinkTarget == "\Device\HarddiskVolume1" et DosDeviceDriveIndex == 3) et la partie restante du nom.
  8. Il fait alors quelque chose comme ça (fidèlement reproduit par ReactOS ):

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

    Dans notre cas, 46 + 65476 = 65522 (0xfff2) qui est juste au-dessus de la limite.

    Donc là, mystère résolu (j’espère!).

PS tout testé sous Windows 7 x64 SP1.