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:
CreateFileW
avec la chaîne “\\? \ C: \ 123 […] 012345” qui fait 32744 caractères de long (sans compter le zéro final ). 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
. CreateFileW
appelle ensuite NtCreateFile
dans ntdll.dll
, qui est juste un wrapper autour de l’instruction syscall
. NtCreateFile
renvoie 0xC0000106 ( STATUS_NAME_TOO_LONG
). 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:
NtCreateFile
appelle IopCreateFile
IopCreateFile
appelle ObOpenObjectByName
ObOpenObjectByName
appelle ObpLookupObjectName
ObpLookupObjectName
vérifie ObpDosDevicesShortNamePrefix
( "\??\"
) -> succès "C:"
et "\1234..."
"C:"
avec un appel à ObpLookupDirectoryEntry
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. 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.