Parce que je suis paresseux, je n'ai pas écrit de programme de test mais je l'ai testé en utilisant l'excellent Far Manager qui gère des choses comme les longs chemins (plus longs que MAX_PATH
) ou des noms de fichiers spéciaux (con
, prn
, etc) très bien.
J'ai créé une chaîne de exactement 255 caractères ("12345678901234...012345") et j'ai commencé à créer des répertoires imbriqués. Heureusement, la fonction "Créer un répertoire" de Far prend une chaîne séparée par des barres obliques pour signifier "créer des répertoires imbriqués" donc j'ai pu le faire en quelques étapes en préparant une chaîne dans l'éditeur interne avec un peu de copier-coller.
Le chemin le plus long que j'ai pu créer était de 32739 caractères, en comptant à partir de "C:\" (c'est-à-dire qu'il n'inclut pas "\\?\" ajouté par Far). L'erreur que j'obtiens en essayant de créer un répertoire ou un fichier avec juste un caractère supplémentaire est "Le nom de fichier ou d'extension est trop long.". Si j'essaie d'entrer dans ce répertoire, j'obtiens la même erreur.
MODIFIER : j'ai passé du temps dans le débogueur et voici ce qui se passe au niveau de l'API Win32 :
- J'essaie de créer un fichier avec un caractère au-dessus de la limite.
- Far appelle
CreateFileW
avec la chaîne "\\?\C:\123[...]012345" qui fait 32744 caractères de large (sans compter le zéro terminal).
CreateFileW
effectue quelques vérifications supplémentaires, convertit la chaîne null-terminée en UNICODE_STRING
(Longueur=65488, Longueur maximale=65490) et prépare une structure OBJECT_ATTRIBUTES
.
CreateFileW
appelle ensuite NtCreateFile
dans ntdll.dll
, qui n'est qu'un wrapper autour de l'instruction syscall
.
NtCreateFile
renvoie 0xC0000106 (STATUS_NAME_TOO_LONG
).
- Cette valeur de statut est ensuite convertie (en utilisant
RtlNtStatusToDosError
) en l'erreur Win32 206 (ERROR_FILENAME_EXCED_RANGE
).
Je ne me suis pas ennuyé à vérifier ce qui se passe dans le noyau, mais je suppose que je pourrais jeter un coup d'œil aussi.
MODIFIER 2 : j'ai exécuté WinObj et j'ai découvert que sur mon système, C:
est un lien symbolique vers \Device\HarddiskVolume1
. Cette chaîne fait 23 caractères. Si nous remplaçons le \C:
dans la chaîne passée à NtCreateFile
par cela, nous obtenons 32744 - 3 + 23 = 32764 caractères. Avec le zéro terminal, cela nécessite 65530 octets. Toujours en dessous de la limite (0xFFFF=65535) donc je suppose qu'il y a quelque chose en plus ajouté, comme un nom de session ou d'espace de noms.
MODIFIER 3 : après avoir passé par le noyau :
NtCreateFile
appelle IopCreateFile
IopCreateFile
appelle ObOpenObjectByName
ObOpenObjectByName
appelle ObpLookupObjectName
ObpLookupObjectName
vérifie ObpDosDevicesShortNamePrefix
("\??\"
) -> succès
- il saute le préfixe et divise la partie restante en
"C:"
et "\1234..."
- il résout le
"C:"
avec un appel à ObpLookupDirectoryEntry
- ensuite, il appelle
ObpParseSymbolicLink
en lui passant l'entrée de répertoire recherchée (_OBJECT_SYMBOLIC_LINK
avec LinkTarget
== "\Device\HarddiskVolume1"
et DosDeviceDriveIndex
== 3) et la partie restante du nom.
-
Il fait ensuite quelque chose comme ceci (fidèlement reproduit par ReactOS) :
CheminCible = &SymlinkObject->LinkTarget;
LongueurTemp = CheminCible->Longueur;
LongueurTotale = LongueurTemp + LongueurRestante->Longueur;
si (LongueurUtilisée > 0xFFF0)
return STATUS_NAME_TOO_LONG;
Dans notre cas, 46 + 65476 = 65522 (0xfff2) ce qui est juste au-dessus de la limite.
Voilà, mystère résolu (j'espère !).
P.S. tout a été testé sous Windows 7 x64 SP1.