Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

comportement realpath

6 réponses
Avatar
phep
Bonjour,

Juste par curiosité...

Ma machine de dev (debian testing) est en php 5.2.0 depuis fin décembre
et jusqu'à pas si longtemps (a priori fin mars), le code suivant
(l'exemple est simplifié à dessein, bien sûr) :

$path = "/home/pillot/public_html/realpath.php/../../";
echo "<p>path = $path</p>";
echo "<p>rpath = " . realpath($path) . "</p>";

donnait le résultat suivant :

path = /home/toto/public_html/realpath.php/../../
rpath = /home/toto

On peut discuter du bien-fondé de la chose (l'écriture
"...fichier/../.."), mais ça marchait : j'en suis sûr, c'est dans des
routines couvertes par des tests unitaires qui ont tourné tous les jours
de décembre à mars ;-)

Me remettant à travailler sur cette librairie, je viens de découvrir que
ce code me donne maintenant celà :

path = /home/pillot/public_html/realpath.php/../../
rpath =

c'est-à-dire que t'chi alors que je suis toujours en 5.2.0, mais avec
qques màj de sécurité des paquets debian.

Comme j'ai sur ma machine un PHP 4.4.4, j'ai fait l'essai et ça donne
toujours rien. J'ai donc d'abord pensé que ça ne venait pas forcément de
php (sinon ça marcherait dans 4.4) mais d'une librairie système de plus
bas niveau (libc, probablement), et effectivement, si je tape :
% ls /home/pillot/public_html/realpath.php/../../
j'ai une erreur "Not a directory"

J'allais abandonné en pensant que le jeu ne valait pas la chandelle mais
j'ai eu qd même l'envie de voir ce que celà donnait sur un serveur de
prod encore en sarge et en php 4.3.10 :
eh bien le 'ls' donne toujours "Not a directory", mais ce coup-ci
realpath ne renacle plus et renvoie bien "/home/toto".

Quelqu'un a-t-il remarqué ça et/ou aurait une idée sur l'origine du
problème ?

Conclusion : je m'en vais écrire un petit script de plus dans
cron.daily... ;-)

Cordialement,

phep

6 réponses

Avatar
Olivier Miakinen
Bonjour,


Ma machine de dev (debian testing) est en php 5.2.0 depuis fin décembre
et jusqu'à pas si longtemps (a priori fin mars), le code suivant
(l'exemple est simplifié à dessein, bien sûr) :

$path = "/home/pillot/public_html/realpath.php/../../";


Je suppose que realpath.php est un fichier de type « regular » et pas un
répertoire, n'est-ce pas ?

echo "<p>path = $path</p>";
echo "<p>rpath = " . realpath($path) . "</p>";

donnait le résultat suivant :

path = /home/toto/public_html/realpath.php/../../
rpath = /home/toto


Je ne vois pas bien comment « pillot » peut se transformer en « toto »,
mais aussi je ne vois pas comment il pourrait exister un fichier de type
directory et de nom .. sous un fichier regular.

On peut discuter du bien-fondé de la chose (l'écriture
"...fichier/../.."), mais ça marchait : j'en suis sûr, c'est dans des
routines couvertes par des tests unitaires qui ont tourné tous les jours
de décembre à mars ;-)


Ah, donc tu avais un comportement voulu basé sur un bug... et je suppose
que tu voudrais rester bug-compatible.

[...] si je tape :
% ls /home/pillot/public_html/realpath.php/../../
j'ai une erreur "Not a directory"


Normal. Qui plus est, même si realpath.php était de type directory,
encore faudrait-il vérifier les droits d'exécution pour savoir si on a
le droit d'aller chercher « .. ». Se contenter de supprimer un bout du
« path » sous prétexte qu'on reconnaît la chaîne « .. » est un vrai bug
à mon avis.

J'allais abandonné en pensant que le jeu ne valait pas la chandelle mais
j'ai eu qd même l'envie de voir ce que celà donnait sur un serveur de
prod encore en sarge et en php 4.3.10 :
eh bien le 'ls' donne toujours "Not a directory",


Normal, le ls n'est pas bugué.

mais ce coup-ci
realpath ne renacle plus et renvoie bien "/home/toto".


C'est donc l'implémentation de la fonction realpath qui est buguée. Si
cela t'amuse tu peux en écrire une du même genre à base de recherches
sur les chaînes, mais ce ne sera pas un vrai realpath.

Cordialement,
--
Olivier Miakinen

Avatar
phep
Je suppose que realpath.php est un fichier de type « regular » et pas un
répertoire, n'est-ce pas ?


Oui, pardon d'avoir omis cette précision.

Je ne vois pas bien comment « pillot » peut se transformer en « toto »,


Erreur de copier-coller, mais tu as compris la logique de l'exemple,
au-delà de sa lettre.

mais aussi je ne vois pas comment il pourrait exister un fichier de type
directory et de nom .. sous un fichier regular.


<off-topic mode=slightly">
Ça, ça se discute. Je ne sais pas (honte à moi) ce que dit la norme
(POSIX je suppose ?) et il est possible (et j'accepte comme tel) qu'elle
indique /arbitrairement/ que l'expression '/..' ne puisse être suffixée
qu'à un path de directory, pas à celui d'un fichier, mais il ne me
semble pas délirant de considérer que puisque le token '..' indique le
répertoire père du path auquel il est suffixé (après un token
séparateur) il puisse donc être suffixé à n'importe quel path, celui
d'un fichier comme celui d'un directory. Non ? D'ailleurs (et ça
répondra peut-être à ma question), où sont précisées la syntaxe et la
sémantique des expressions de path ?
</off-topic>

Ah, donc tu avais un comportement voulu basé sur un bug... et je suppose


Oui, avec cette réserve tout de même que j'ignorais que c'était un bug,
cf. supra), je ne suis pas pervers à ce point là tout de même.

que tu voudrais rester bug-compatible.


Non, la question n'est pas là, le code de remplacement est trivial (du
moins en ce qui me concerne). Je ne cherche qu'à comprendre, cf. infra.

Normal. Qui plus est, même si realpath.php était de type directory,
encore faudrait-il vérifier les droits d'exécution pour savoir si on a
le droit d'aller chercher « .. ». Se contenter de supprimer un bout du
« path » sous prétexte qu'on reconnaît la chaîne « .. » est un vrai bug
à mon avis.


Oups, encore une fois omis de préciser que j'ai fait en sorte que les
différents path existent, _mais_, quant à cette histoire de permissions,
je ne vois nulle part dans la doc php que realpath les considère à
quelque moment que ce soit. realpath ne renvoie FALSE que si le path
résultant n'existe pas, sinon il renvoie un path canonique, point barre.
Les permissions du path renvoyé (ou renvoyable) ne font l'objet d'aucune
mention dans la doc de l'API.

C'est donc l'implémentation de la fonction realpath qui est buguée. Si


Oui, Ou plutôt qui "était", visiblement elle ne l'est plus (encore que ...).

cela t'amuse tu peux en écrire une du même genre à base de recherches
sur les chaînes, mais ce ne sera pas un vrai realpath.


Encore une fois ma question ne portait pas là-dessus, mais sur ce qui
avait pu changer dans mon environnement (php, libc, ... ?) pour que ce
que je veux bien qualifier de bug (modulo les remarques off-topic
ci-dessus) ait été corrigé.

Merci de ta réponse en tous les cas.

Cordialement,

phep

Avatar
phep
Comme suite à mon message précédent, je viens de faire un tour sur la
toile. Effectivement, la sémantique de '..' semble fixée par POSIX, mais
vu le prix de cette norme... J'ai trouvé en revanche le paragraphe
suivant dans la norme SUS (Single UNIX Specification) de l'Open Group :

The special filename dot shall refer to the directory specified by its
predecessor. The special filename dot-dot shall refer to the parent
directory of its predecessor directory. As a special case, in the root
directory, dot-dot may refer to the root directory itself.

Donc pas de dot-dot après un composant nom de fichier.

mais aussi celà dans la doc de l'implémentation de zsh :

"b. For each dot-dot component, if there is a preceding component
and it is neither root nor dot-dot, then:

i. If the preceding component does not refer (in the
context of pathname resolution with symbolic links followed)
to a directory, then the cd utility shall display an
appropriate error message and no further steps shall be
taken.

ii. The preceding component, all slashes separating the
preceding component from dot-dot, dot-dot and all slashes
separating dot-dot from the following component (if any)
shall be deleted."


Je trouve cet extrait particulièrement intéressant car il montre bien
l'arbitraire de l'application de '..' aux seuls répertoires, l'algo
décrit dans le ii. fonctionne sans problèmes si le composant qui précède
'..' est un nom de fichier.

Mais bon, on ne va pas réécrire les normes....

En revanche, ça ne me permet toujours pas de savoir ce qui a provoqué la
modification de comportement de realpath...
Avatar
Olivier Miakinen

mais aussi je ne vois pas comment il pourrait exister un fichier de type
directory et de nom .. sous un fichier regular.


<off-topic mode=slightly">
Ça, ça se discute. Je ne sais pas (honte à moi) ce que dit la norme
(POSIX je suppose ?) et il est possible (et j'accepte comme tel) qu'elle
indique /arbitrairement/ que l'expression '/..' ne puisse être suffixée
qu'à un path de directory, pas à celui d'un fichier, mais il ne me
semble pas délirant de considérer que puisque le token '..' indique le
répertoire père du path auquel il est suffixé (après un token
séparateur) il puisse donc être suffixé à n'importe quel path, celui
d'un fichier comme celui d'un directory. Non ? D'ailleurs (et ça
répondra peut-être à ma question), où sont précisées la syntaxe et la
sémantique des expressions de path ?
</off-topic>


Bon, prenons l'exemple d'un fichier de type lien symbolique.


1) realpath.php -> directory

/home/pillot/public_html/realpath.php/../../
/home/pillot/public_html/directory/../../
/home/pillot/


2) realpath.php -> dir1/dir2

/home/pillot/public_html/realpath.php/../../
/home/pillot/public_html/dir1/dir2/../../
/home/pillot/public_html/


3) realpath.php -> ../..

/home/pillot/public_html/realpath.php/../../
/home/pillot/public_html/../../../../
/


Avatar
phep
Bon, prenons l'exemple d'un fichier de type lien symbolique.


Ben et alors ? Il ne t'échappe pas, je pense, que les exemples que tu
décrit, fonctionnent déjà, je veux dire qu'ils ne provoquent pas
d'erreurs ! Rien ne m'empêche de créer un lien symbolique nommé
'realpath.php' qui pointe vers un répertoire, qqsoit la position du
répertoire par rapport à ce lien symbolique ?

Au contraire ton exemple semble apporter de l'eau à mon moulin et ton
lapsus (1) montre bien qu'il n'y aurait rien de choquant à ce que
dot-dot puisse être suffixé à un path qui se résoudrait par un nom de
fichier. On aurait alors un truc genre :

"dot-dot indique le répertoire père du composant qui le précède dans le
path, que celui-ci soit un fichier ou un répertoire, donné directement
ou via un lien symbolique"

à la place de

"dot-dot indique le répertoire père du composant qui le précède dans le
path, seulement si celui-ci est un répertoire ou un lien vers un
répertoire sinon on lève un drapeau"

Les rédacteurs n'ont pas vu cet aspect, ou ont pensé que cette
sémantique pouvait être source de confusion (2), j'imagine. Je trouve ça
dommage, pas plus logique que ça, mais tant pis, on va pas y passer la
nuit, hein ;-)

Ma question c'était, je le répète, qu'est-ce qui a changé dans php ou
ailleurs pour que le comportement ait changé ! C'est surtout ça qui
m'intéresse.

Cordialement,

phep

(1) il n'existe pas de "fichier de type lien symbolique" ni de
"répertoire de type lien symbolique" d'ailleurs, seulement des liens
symboliques.

(2) '/' n'est "posixement" utilisable qu'après un nom de répertoire,
encore que, encore une fois, ton exemple montre qu'il reste utilisable
après un lien symboliqe mais seulement si celui-ci résoud un
répertoire... L'un et l'autre cas ont leurs ambiguités qui me semblent
bien légères, et en tous cas loin d'être aussi tirées par les cheveux
que la sémantique de chmod a+x fichier // chmod a+x directory, pour ne
prendre que la plus sympathique des subtilités des permissions
unixiennes, mais enfin...

Avatar
Olivier Miakinen

Bon, prenons l'exemple d'un fichier de type lien symbolique.


Ben et alors ? Il ne t'échappe pas, je pense, que les exemples que tu
décrit, fonctionnent déjà, je veux dire qu'ils ne provoquent pas
d'erreurs !


Bien sûr, puisque j'ai supposé que les liens se résolvaient en
répertoires et pas en fichiers « regular » ou « special » ou autres.

En revanche, je croyais que « .. » ramenait toujours au même répertoire
parent quel que soit le chemin pris pour y arriver, or j'ai vérifié
qu'il n'en est rien. Et d'ailleurs cela n'aurait eu aucun sens à
l'époque où les « hards links » étaient permis entre répertoires.
C'est *ça* qui apporte de l'eau à ton moulin.

[...]

(1) il n'existe pas de "fichier de type lien symbolique" ni de
"répertoire de type lien symbolique" d'ailleurs, seulement des liens
symboliques.


Je ne sais pas comment on dit en français, mais en anglais sur Unix le
terme « file » est générique pour désigner tout machin référençable
dans un inode, qu'il soit de type « regular » (fichier au sens habituel
du terme) ou « directory » (répertoire) ou « special character » (un
terminal par exemple) ou « special block » (un disque), ou « named
pipe » (file nommée), et j'en oublie probablement. Ah oui, déjà j'ai
oublié les liens symboliques, mais pas les « hard links » qui ne sont
pas un type de fichier spécial.


Cela dit, pour en revenir à ton problème de départ (et revenir en charte
par la même occasion), tu peux toujours remplacer :
realpath($path)
par :
realpath(preg_replace('|/[^/]*[^/.]/../|', '/', $path));

Attention, cela ne marchera pas si ton fichier se termine par un « . »
mais au moins cela evitera de remplacer à tort « /./../ » et « /../../ »
par « / ».