Je peux cat /dev
, je peux ls /dev
, je ne peux pas less /dev
. Pourquoi cat
me permet-il de cat
ce répertoire mais pas les autres répertoires?
Réponses
Trop de publicités?Historiquement (jusqu'à UNIX V7, vers 1979), l'appel système read
fonctionnait à la fois sur les fichiers et les répertoires. read
sur un répertoire renvoyait une structure de données simple que le programme utilisateur analysait pour obtenir les entrées du répertoire. En effet, l'outil ls
de V7 faisait exactement cela - read
sur un répertoire, analysait la structure de données résultante, puis affichait dans un format de liste structurée.
Avec la complexification des systèmes de fichiers, cette structure de données "simple" est devenue plus complexe, à tel point qu'une fonction de bibliothèque readdir
a été ajoutée pour aider les programmes à analyser la sortie de read(dossier)
. Différents systèmes et systèmes de fichiers pouvaient avoir des formats différents sur disque, ce qui devenait compliqué.
Lorsque Sun a introduit le Network File System (NFS), ils ont voulu abstraire complètement la structure de répertoire sur disque. Au lieu de faire en sorte que leur read(dossier)
renvoie une représentation du répertoire indépendante de la plate-forme, ils ont ajouté un nouvel appel système - getdirents
- et interdit read
sur les répertoires montés en réseau. Cet appel système a rapidement été adapté pour fonctionner sur tous les répertoires dans diverses variantes UNIX, devenant ainsi le moyen par défaut d'obtenir le contenu des répertoires. (Historique abstrait de https://utcc.utoronto.ca/~cks/space/blog/unix/ReaddirHistory)
Étant donné que readdir
est désormais le moyen par défaut de lire les répertoires, read(dossier)
n'est généralement pas implémenté (renvoyant -EISDIR) sur la plupart des OS modernes (QNX, par exemple, étant une exception notable qui implémente readdir
en tant que read(dossier)
). Cependant, avec la conception de "système de fichiers virtuel" dans la plupart des noyaux modernes, c'est en fait au système de fichiers individuel de décider si la lecture d'un répertoire fonctionne ou non.
Et en effet, sur macOS, le système de fichiers devfs
sous-jacent au point de montage /dev
prend effectivement en charge la lecture (https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/miscfs/devfs/devfs_vnops.c#L629):
static int
devfs_read(struct vnop_read_args *ap)
{
devnode_t * dn_p = VTODN(ap->a_vp);
switch (ap->a_vp->v_type) {
case VDIR: {
dn_p->dn_access = 1;
return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
Cela appelle explicitement READDIR
si vous essayez de lire /dev
(la lecture des fichiers sous /dev
est gérée par une fonction séparée - devfsspec_read
). Donc, si un programme appelle l'appel système read
sur /dev
, il réussira et obtiendra une liste de répertoire !
Il s'agit en fait d'une fonctionnalité héritée des premiers jours d'UNIX, qui n'a pas été touchée depuis très longtemps. Une partie de moi soupçonne que cela est conservé pour une raison de compatibilité ascendante, mais cela pourrait tout aussi bien être parce que personne ne se soucie suffisamment de supprimer la fonctionnalité puisqu'elle ne cause pas vraiment de problème.
Moins est un visualiseur de fichiers texte, cat est un outil pour copier des données arbitraires. Donc moins effectue ses propres vérifications pour s'assurer que vous n'ouvrez pas quelque chose qui contiendra des quantités massives de données ou se comportera de manière très étrange. En revanche, cat n'a aucune vérification du tout - si le noyau vous permet d'ouvrir quelque chose (même s'il s'agit d'un tuyau ou d'un périphérique ou quelque chose de pire), cat le lira.
Alors pourquoi le système d'exploitation permet-il à cat d'ouvrir des répertoires ? Traditionnellement dans les systèmes de type BSD, tous les répertoires pouvaient être lus comme des fichiers, et c'est ainsi que les programmes listeraient un répertoire en premier lieu : en interprétant simplement les structures dirent stockées sur le disque.
Par la suite, ces structures sur disque ont commencé à diverger de dirent utilisé par le noyau : là où auparavant un répertoire était une liste linéaire, les systèmes de fichiers ultérieurs ont commencé à utiliser des tables de hachage, des B-trees, et ainsi de suite. Donc la lecture des répertoires directement n'était plus aussi simple - le noyau a développé des fonctions dédiées à cet effet. (Je ne suis pas sûr si c'était la raison principale, ou si elles ont été principalement ajoutées pour d'autres raisons telles que le caching.)
Certains systèmes BSD continuent de vous permettre d'ouvrir tous les répertoires en lecture ; je ne sais pas s'ils vous donnent les données brutes du disque, ou s'ils renvoient plutôt une liste dirent émulée, ou s'ils laissent le pilote du système de fichiers décider.
Peut-être que macOS est l'un de ces systèmes d'exploitation où le noyau le permet tant que le système de fichiers fournit les données. Et la différence est que /dev
est sur un système de fichiers devfs
qui a été écrit pour permettre cela dans les premiers temps, tandis que /
est sur un système de fichiers APFS qui a omis cette fonctionnalité comme inutile à l'ère moderne.
Avertissement : Je n'ai en fait pas fait de recherches sur les BSD ou macOS. Je me lance juste.