J'utilise une distribution basée sur Linux 4.x, et j'ai récemment remarqué que l' open()
appel système du noyau prend en charge un O_PATH
indicateur ouvert.
Bien que la man
page correspondante contienne une liste d'appels système avec lesquels elle pourrait théoriquement être utilisée, je ne comprends pas très bien quelle est l'idée. Dois-je open(O_PATH)
uniquement des répertoires, plutôt que des fichiers? Et si je le fais, pourquoi est-ce que je veux utiliser un descripteur de fichier au lieu du chemin du répertoire? De plus, la plupart des appels système répertoriés ne semblent pas être particuliers aux répertoires; alors, est-ce que j'ouvre également des fichiers réguliers avec O_PATH
pour obtenir en quelque sorte leur répertoire en tant que descripteur de fichier? Ou pour obtenir un descripteur de fichier pour eux mais avec des fonctionnalités limitées?
Quelqu'un peut-il donner une explication convaincante de quoi il O_PATH
s'agit et comment, et pourquoi, nous sommes censés l'utiliser?
Remarques:
- Pas besoin de décrire l'historique de la façon dont cela a évolué (les pages de manuel pertinentes mentionnent les changements dans Linux 2.6.x, 3.5 et 3.6) à moins que cela ne soit nécessaire - je me soucie juste de la façon dont les choses sont maintenant.
- S'il vous plaît, ne me dites pas d'utiliser simplement libc ou d'autres installations de niveau supérieur, je le sais.
la source
Réponses:
La description dans la
open(2)
page de manuel donne quelques indices pour commencer:Parfois, nous ne voulons pas ouvrir un fichier ou un répertoire. Au lieu de cela, nous voulons juste une référence à cet objet de système de fichiers afin d'effectuer certaines opérations (par exemple,
fchdir()
vers un répertoire auquel fait référence un descripteur de fichier que nous avons ouvert à l'aideO_PATH
). Donc, un point trivial: si c'est notre objectif, alors l'ouverture avecO_PATH
devrait être un peu moins chère, car le fichier lui-même n'est pas réellement ouvert.Et un point moins trivial: avant l'existence de
O_PATH
, la manière d'obtenir une telle référence à un objet de système de fichiers était d'ouvrir l'objet avecO_RDONLY
. Mais l'utilisation deO_RDONLY
nécessite que nous ayons l'autorisation de lecture sur l'objet. Cependant, il existe différents cas d'utilisation où nous n'avons pas besoin de lire réellement l'objet: par exemple, exécuter un binaire ou accéder à un répertoire (fchdir()
) ou atteindre un répertoire pour toucher un objet à l'intérieur du répertoire.Utilisation avec les appels système "* at ()"
La commune, mais pas la seule, l' utilisation
O_PATH
est d'ouvrir un répertoire, afin d'avoir une référence à ce répertoire pour être utilisé avec le « * » à des appels système, tels queopenat()
,fstatat()
,fchownat()
et ainsi de suite. Cette famille d'appels système, que nous pouvons penser à peu près comme les successeurs modernes aux anciens appels système avec des noms similaires (open()
,fstat()
,fchown()
, etc.), servent deux buts, dont le premier vous touchez quand vous demandez " pourquoi est-ce que je veux utiliser un descripteur de fichier au lieu du chemin du répertoire? ". Si nous regardons plus loin dans laopen(2)
page de manuel, nous trouvons ce texte (sous une sous-rubrique avec la justification des appels système "* at"):Pour rendre cela plus concret ... Supposons que nous ayons un programme qui souhaite effectuer plusieurs opérations dans un répertoire autre que son répertoire de travail actuel, ce qui signifie que nous devons spécifier un préfixe de répertoire dans le cadre des noms de fichiers que nous utilisons. Supposons, par exemple, que le chemin d'accès soit
/dir1/dir2/file
et que nous voulons effectuer deux opérations:/dir1/dir2/file
(par exemple, à qui appartient le fichier ou à quelle heure a-t-il été modifié pour la dernière fois)./dir1/dir2/file.new
.Supposons maintenant que nous ayons tout fait en utilisant des appels système basés sur des chemins traditionnels:
Maintenant, supposons en outre que dans le préfixe de répertoire, l'
/dir1/dir2
un des composants (disonsdir2
) était en fait un lien symbolique (qui fait référence à un répertoire), et qu'entre l'appelstat()
et l'appel àopen()
une personne malveillante a pu changer la cible du lien symboliquedir2
pour pointer vers un répertoire différent. Il s'agit d'une condition de concurrence classique au moment de la vérification du temps d'utilisation. Notre programme a vérifié un fichier dans un répertoire mais a ensuite été amené à créer un fichier dans un répertoire différent - peut-être un répertoire sensible à la sécurité. Le point clé ici est que le chemin d'accès est/dir/dir2
le même, mais ce à quoi il fait référence a complètement changé.Nous pouvons éviter ce genre de problèmes en utilisant les appels "* at". Tout d'abord, nous obtenons un handle faisant référence au répertoire où nous ferons notre travail:
Le point critique ici est qu'il
dirfd
s'agit d'une référence stable au répertoire auquel faisait référence le chemin/dir1/dir2
au moment de l'open()
appel. Si la cible du lien symboliquedir2
est modifiée par la suite, cela n'affectera pas ce quidirfd
fait référence. Maintenant, nous pouvons effectuer notre opération check + en utilisant les appels "* at" qui sont équivalents aux appelsstat()
etopen()
ci-dessus:Au cours de ces étapes, toute manipulation de liens symboliques dans le chemin
/dir/dir2
n'aura aucun impact: la vérification (fstatat()
) et l'opération (openat()
) sont assurées d'avoir lieu dans le même répertoire.Il y a un autre but à utiliser les appels "* at ()", qui se rapporte à l'idée de "répertoires de travail actuels par thread" dans les programmes multithread (et encore une fois, nous pourrions ouvrir les répertoires en utilisant
O_PATH
), mais je pense que cette utilisation est probablement moins pertinent pour votre question, et je vous laisse lire laopen(2)
page de manuel si vous souhaitez en savoir plus.Utilisation avec des descripteurs de fichiers pour les fichiers normaux
Une utilisation de
O_PATH
avec des fichiers normaux consiste à ouvrir un binaire pour lequel nous avons une autorisation d'exécution (mais pas nécessairement une autorisation de lecture, afin que nous ne puissions pas ouvrir le fichier avecO_RDONLY
). Ce descripteur de fichier peut ensuite être transmis àfexecve(3)
pour exécuter le programme. Tout ce quefexecve(fd, argv, envp)
fait sonfd
argumentation est essentiellement:(Bien que, à partir de la glibc 2.27, l'implémentation utilise à la place l'
execveat(2)
appel système, sur les noyaux qui fournissent cet appel système.)la source
The problem is that between the existence check and the file creation step, path or to ... could be modified
- ne peut pas analyser cette phrase. Mais je comprends l'essentiel, je pense. Il sert donc comme une sorte de mécanisme de verrouillage sur un répertoire. Mais pourquoi utiliser leopen()
résultat plutôt qu'un verrou réel?