open_by_handle_at(2) System Calls Manual open_by_handle_at(2) NOM name_to_handle_at, open_by_handle_at - Recuperer le gestionnaire d'un chemin et ouvrir le fichier au moyen d'un gestionnaire BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #define _GNU_SOURCE /* Consultez feature_test_macros(7) */ #include int name_to_handle_at(int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags); int open_by_handle_at(int mount_fd, struct file_handle *handle, int flags); DESCRIPTION Les appels systeme name_to_handle_at() et open_by_handle_at() scindent la fonction de openat(2) en deux parties : name_to_handle_at() renvoie un gestionnaire opaque qui correspond a un fichier indique ; open_by_handle_at() ouvre le fichier correspondant a un gestionnaire renvoye par un appel anterieur a name_to_handle_at() et renvoie le descripteur d'un fichier ouvert. name_to_handle_at() L'appel systeme name_to_handle_at() renvoie un gestionnaire de fichier et un identifiant de montage (<< mount ID >>) correspondant au fichier designe par les arguments dirfd et pathname. Le gestionnaire de fichier est renvoye au moyen de l'argument handle. Cet argument est un pointeur vers une structure qui presente la forme suivante : struct file_handle { unsigned int handle_bytes; /* taille de f_handle [in, out] */ int handle_type; /* type du gestionnaire [out] */ unsigned char f_handle[0]; /* identifiant du fichier (taille definie par l'appelant) [out] */ }; L'appelant a l'origine de l'appel doit s'assurer que la structure est creee avec une taille suffisante pour contenir le gestionnaire renvoye dans f_handle. Avant l'appel, le champ handle_bytes devrait etre initialise de sorte que l'espace alloue puisse recevoir f_handle. (La constante MAX_HANDLE_SZ, definie dans , precise la taille maximale autorisee pour un gestionnaire de fichier. La limite superieure n'est pas garantie car de futurs systemes de fichiers pourraient avoir besoin de davantage d'espace). Lorsque l'appel reussit, le champ handle_bytes est mis a jour afin de contenir le nombre d'octets effectivement ecrits dans f_handle. L'appelant peut prendre connaissance de l'espace necessaire a la structure file_handle en effectuant un appel dans lequel handle->handle_bytes vaut zero ; dans ce cas, l'appel echoue en renvoyant l'erreur EOVERFLOW et handle->handle_bytes prend pour valeur la taille requise ; l'appelant peut alors utiliser cette information pour allouer une structure ayant la taille convenable (consultez EXEMPLES ci-dessous). Il faut faire attention ici car EOVERFLOW peut indiquer qu'aucun gestionnaire de fichier n'est disponible pour ce nom particulier dans un systeme de fichiers qui prend normalement en charge la recherche de gestionnaire de fichiers. Ce cas peut se detecter quand l'erreur EOVERFLOW est renvoyee sans que handle_bytes ne soit augmente. Other than the use of the handle_bytes field, the caller should treat the file_handle structure as an opaque data type: the handle_type and f_handle fields can be used in a subsequent call to open_by_handle_at(). The caller can also use the opaque file_handle to compare the identity of filesystem objects that were queried at different times and possibly at different paths. The fanotify(7) subsystem can report events with an information record containing a file_handle to identify the filesystem object. L'argument flags est un masque de bits construit par OU binaire entre zero ou plus de AT_EMPTY_PATH et AT_SYMLINK_FOLLOW, decrits plus bas. When flags contain the AT_HANDLE_FID (since Linux 6.5) flag, the caller indicates that the returned file_handle is needed to identify the filesystem object, and not for opening the file later, so it should be expected that a subsequent call to open_by_handle_at() with the returned file_handle may fail. Ensemble, les arguments pathname et dirfd designent le fichier pour lequel on souhaite obtenir un gestionnaire. On distingue quatre cas : - Si pathname est une chaine non vide contenant un chemin d'acces absolu, alors un gestionnaire est renvoye pour le fichier indique par ce chemin. Dans ce cas, dirfd est ignore. - Si pathname est une chaine non vide contenant un chemin relatif, et si dirfd a la valeur speciale AT_FDCWD, alors pathname est interprete par rapport au repertoire courant du processus appelant, et un gestionnaire est renvoye pour le fichier indique par le chemin. - Si pathname est une chaine non vide contenant un chemin d'acces relatif et si dirfd est le descripteur de fichier d'un repertoire, alors pathname est interprete par rapport au repertoire designe par dirfd, et un gestionnaire est renvoye pour le fichier indique par le chemin. (Consultez openat(3) si vous souhaitez comprendre a quoi servent les << descripteurs de fichier de repertoires >>). - Si pathname est une chaine vide, et si flags precise la valeur de AT_EMPTY_PATH, alors dirfd peut etre un descripteur de fichiers ouvert faisant reference a n'importe quel type de fichier ou a AT_FDCWD (repertoire de travail courant), et un gestionnaire est renvoye pour le fichier auquel il fait reference. L'argument mount_id renvoie un identifiant pour le point de montage du systeme de fichiers correspondant a pathname. Cet identifiant correspond au premier champ des entrees de /proc/self/mountinfo. L'ouverture du chemin indique dans le cinquieme champ delivre un descripteur de fichier pour le point de montage ; ce descripteur de fichier peut etre utilise par la suite lors d'un appel a open_by_handle_at(). mount_id est renvoye tant en cas de succes qu'en cas d'erreur EOVERFLOW de l'appel. Par defaut, name_to_handle_at() ne dereference pas pathname s'il s'agit d'un lien symbolique, et donc renvoie un gestionnaire pour le lien lui-meme. Si AT_SYMLINK_FOLLOW est precise dans flags, pathname est dereference s'il s'agit d'un lien symbolique (de sorte que l'appel renvoie un indicateur pour le fichier vers lequel pointe le lien symbolique). name_to_handle_at() ne recupere pas un montage quand le composant final du chemin est un point de montage automatique. Quand un systeme de fichiers gere a la fois les gestionnaires de fichier et les points de montage automatique, un appel name_to_handle_at() sur un point de montage automatique renverra une erreur EOVERFLOW sans augmenter handle_bytes. Cela peut arriver depuis Linux 4.13 avec NFS lors d'un acces a un repertoire sur un systeme de fichiers separe du serveur. Dans ce cas, le montage automatique peut etre provoque en ajoutant un << / >> a la fin du chemin. open_by_handle_at() L'appel systeme open_by_handle_at() ouvre le fichier auquel handle fait reference, via un indicateur de fichier renvoye lors d'un precedent appel a name_to_handle_at(). L'argument mount_fd est un descripteur de fichier pour n'importe quel type d'objet (fichier, repertoire, etc.) du systeme de fichiers monte qui permet d'interpreter l'indicateur de fichier (handle). La valeur speciale AT_FDCWD peut etre precisee, et indique le repertoire courant du processus appelant. L'argument flags a la meme fonction que pour open(2). Si l'indicateur handle fait reference a un lien symbolique, le processus appelant doit preciser l'attribut O_PATH et le lien symbolique n'est pas dereference. L'attribut O_NOFOLLOW est ignore. L'appelant doit avoir la capacite CAP_DAC_READ_SEARCH pour utiliser open_by_handle_at(). VALEUR RENVOYEE Lorsqu'il reussit, l'appel name_to_handle_at() renvoie 0 et open_by_handle_at() renvoie un descripteur de fichier (un entier non negatif). En cas d'echec, les deux appels renvoient -1 et definissent errno pour indiquer l'erreur. ERREURS Les appels name_to_handle_at() et open_by_handle_at() peuvent echouer pour les memes raisons que openat(2). En outre, ils peuvent egalement echouer pour les motifs decrits plus bas. name_to_handle_at() peut echouer avec les erreurs suivantes : EFAULT pathname, mount_id ou handle pointe en dehors de l'espace d'adressage accessible. EINVAL flags comprend un bit incorrect. EINVAL handle->handle_bytes est superieur a MAX_HANDLE_SZ. ENOENT pathname est une chaine vide et AT_EMPTY_PATH n'etait pas indique dans flags. ENOTDIR Le descripteur de fichiers fourni dans dirfd ne fait pas reference a un repertoire, et il ne s'agit pas du cas ou flags comprend AT_EMPTY_PATH et pathname est une chaine vide. EOPNOTSUPP Le systeme de fichiers ne permet pas la transcription du chemin de fichier en indicateur de fichier. EOVERFLOW La valeur handle->handle_bytes transmise dans l'appel est trop faible. Lorsque cette erreur se produit, handle->handle_bytes est modifie afin d'indiquer la taille requise pour cet indicateur. open_by_handle_at() peut echouer avec les erreurs suivantes : EBADF mount_fd n'est pas un descripteur de fichier ouvert. EBADF pathname est relatif mais dirfd n'est ni AT_FDWCD ni un descripteur de fichier valable. EFAULT handle pointe en dehors de l'espace d'adressage accessible. EINVAL handle->handle_bytes est superieur a MAX_HANDLE_SZ ou egal a zero. ELOOP handle correspond a un lien symbolique et O_PATH n'etait pas indique dans flags. EPERM L'appelant n'a pas la capacite CAP_DAC_READ_SEARCH. ESTALE The specified handle is not valid for opening a file. This error will occur if, for example, the file has been deleted. This error can also occur if the handle was acquired using the AT_HANDLE_FID flag and the filesystem does not support open_by_handle_at(). VERSIONS FreeBSD offre un couple d'appels systeme similaires : getfh() et openfh(). STANDARDS Linux. HISTORIQUE Linux 2.6.39, glibc 2.14. NOTES Un indicateur de fichier peut etre cree dans un processus au moyen de name_to_handle_at() et utilise plus tard dans un autre processus qui appelle open_by_handle_at(). Certains systemes de fichiers ne permettent pas la transcription des chemins en indicateurs de fichier, par exemple, /proc, /sys, ainsi que divers systemes de fichiers en reseaux. Certains systemes de fichiers permettent la transcription des chemins en indicateurs de fichier, mais ne prennent pas en charge l'utilisation de ces indicateurs de fichier dans open_by_handle_at(). Un indicateur de fichier peut devenir non valable (<< stale >>) si un fichier est supprime, ou pour une raison propre au systeme de fichiers. Les indicateurs non autorises sont signales par une erreur ESTALE provenant de open_by_handle_at(). Ces appels systemes sont concus pour etre utilises par des serveurs de fichiers en espace utilisateur. Par exemple, un serveur NFS en espace utilisateur produit un indicateur de fichier et le transmet au client NFS. Plus tard, lorsque le client souhaite acceder au fichier, il peut renvoyer l'indicateur au serveur. Ce type de fonctionnalite permet a un serveur de fichiers en espace utilisateur d'operer sans etat vis a vis des fichiers qu'il delivre. Si pathname fait reference a un lien symbolique et si flags ne precise pas AT_SYMLINK_FOLLOW, alors name_to_handle_at() renvoie un indicateur pour le lien (plutot que pour le fichier vers lequel le lien pointe). Le processus recevant l'indicateur peut effectuer plus tard une operation sur ce lien symbolique, en convertissant l'indicateur en descripteur de fichier au moyen de open_by_handle_at() utilise avec l'argument O_PATH, et en passant le descripteur de fichier en argument dirfd d'appels systeme (tels que readlinkat(2) et fchownat(2)). Obtenir un identifiant persistant de systeme de fichier Les identifiants de montage dans /proc/self/mountinfo peuvent etre reutilises meme lorsque les systemes de fichiers sont demontes et remontes. Ainsi, l'identifiant de montage renvoye par name_to_handle_at() (dans *mount_id) ne doit pas etre considere comme un identifiant persistant pour le systeme de fichiers considere. Neanmoins, il est possible pour une application d'utiliser l'information fournie dans mountinfo et correspondant a l'identifiant de montage pour en deduire un identifiant persistant. Par exemple, on peut utiliser le nom de peripherique present dans le cinquieme champ de mountinfo pour retrouver l'UUID du peripherique correspondant au moyen des liens symboliques dans /dev/disks/by-uuid. (Un moyen plus simple d'obtenir cet UUID consiste a utiliser la bibliotheque libblkid(3)). Cette facon de proceder peut etre inversee, en utilisant l'UUID pour retrouver le nom du peripherique, et ainsi obtenir le point de montage correspondant, et enfin construire l'argument de mount_fd utile a open_by_handle_at(). EXEMPLES Les deux programmes suivants illustrent l'utilisation de name_to_handle_at() et de open_by_handle_at(). Le premier programme (t_name_to_handle_at.c) utilise name_to_handle_at() pour recuperer l'indicateur de fichier et l'identifiant de montage du fichier indique dans les arguments en ligne de commande ; l'indicateur et l'identifiant de montage sont ecrits sur la sortie standard. Le second programme (t_open_by_handle_at.c) lit un identifiant de montage et un indicateur de fichier depuis l'entree standard. Le programme utilise ensuite open_by_handle_at() pour lire le fichier au moyen de cet indicateur. Si un argument optionnel est fourni dans la ligne de commande, alors l'argument mount_fd de open_by_handle_at() est obtenu en ouvrant le repertoire precise en argument. Sinon, mount_fd est obtenu en parcourant /proc/self/mountinfo a la recherche d'un identifiant de montage correspondant a celui fourni via l'entree standard, et le repertoire monte qui a ete trouve est ouvert. (Ces programmes ne tiennent pas compte du fait que les identifiants de montage ne sont pas persistants.) La session shell suivante montre des exemples d'utilisation de ces deux programmes : $ echo 'Can you please think about it?' > cecilia.txt $ ./t_name_to_handle_at cecilia.txt > fh $ ./t_open_by_handle_at < fh open_by_handle_at: Operation not permitted $ sudo ./t_open_by_handle_at < fh # Necessite CAP_SYS_ADMIN Read 31 bytes $ rm cecilia.txt A ce stade, on supprime et recree (rapidement) le fichier, de sorte qu'il ait le meme contenu et (avec un peu de chance) le meme inoeud. Cependant, open_by_handle_at() s'apercoit que le fichier original auquel l'indicateur fait reference n'existe plus. $ stat --printf="%i\n" cecilia.txt # Affiche le numero d'inoeud 4072121 $ rm cecilia.txt $ echo 'Can you please think about it?' > cecilia.txt $ stat --printf="%i\n" cecilia.txt # Verifie le numero d'inoeud 4072121 $ sudo ./t_open_by_handle_at < fh open_by_handle_at: Stale NFS file handle Source du programme : t_name_to_handle_at.c #define _GNU_SOURCE #include #include #include #include #include int main(int argc, char *argv[]) { int mount_id, fhsize, flags, dirfd; char *pathname; struct file_handle *fhp; if (argc != 2) { fprintf(stderr, "Utilisation : %s pathname\n", argv[0]); exit(EXIT_FAILURE); } pathname = argv[1]; /* Allocate file_handle structure. */ fhsize = sizeof(*fhp); fhp = malloc(fhsize); if (fhp == NULL) err(EXIT_FAILURE, "malloc"); /* Effectue un appel initial a name_to_handle_at() afin de connaitre la taille necessaire a l'indicateur de fichier. */ dirfd = AT_FDCWD; /* Pour les appels a name_to_handle_at() */ flags = 0; /* Pour les appels a name_to_handle_at() */ fhp->handle_bytes = 0; if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) != -1 || errno != EOVERFLOW) { fprintf(stderr, "Resultat inattendu de name_to_handle_at()\n"); exit(EXIT_FAILURE); } /* Re-alloue la structure file_handle avec la bonne taille. */ fhsize = sizeof(*fhp) + fhp->handle_bytes; fhp = realloc(fhp, fhsize); /* Copies fhp->handle_bytes */ if (fhp == NULL) err(EXIT_FAILURE, "realloc"); /* Retrouve l'indicateur de fichier a partir du chemin fourni dans la ligne de commande. */ if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == -1) err(EXIT_FAILURE, "name_to_handle_at"); /* Ecrit l'identifiant de montage, la taille de l'indicateur et l'indicateur vers la sortie standard pour etre utilises plus tard par t_open_by_handle_at.c. */ printf("%d\n", mount_id); printf("%u %d ", fhp->handle_bytes, fhp->handle_type); for (size_t j = 0; j < fhp->handle_bytes; j++) printf(" %02x", fhp->f_handle[j]); printf("\n"); exit(EXIT_SUCCESS); } Source du programme : t_open_by_handle_at.c #define _GNU_SOURCE #include #include #include #include #include #include #include /* Parcourt /proc/self/mountinfo pour trouver la ligne correspondant a l'ID de montage 'mount_id'. (Une methode plus simple consiste a installer et a utiliser la bibliotheque ('libmount' fournie par le projet 'util-linux'.) Ouvre le point de montage correspondant et renvoie le descripteur de fichier associe. */ static int open_mount_path_by_id(int mount_id) { int mi_mount_id, found; char mount_path[PATH_MAX]; char *linep; FILE *fp; size_t lsize; ssize_t nread; fp = fopen("/proc/self/mountinfo", "r"); if (fp == NULL) err(EXIT_FAILURE, "fopen"); found = 0; linep = NULL; while (!found) { nread = getline(&linep, &lsize, fp); if (nread == -1) break; nread = sscanf(linep, "%d %*d %*s %*s %s", &mi_mount_id, mount_path); if (nread != 2) { fprintf(stderr, "Mauvais sscanf()\n"); exit(EXIT_FAILURE); } if (mi_mount_id == mount_id) found = 1; } free(linep); fclose(fp); if (!found) { fprintf(stderr, "Point de montage non trouve\n"); exit(EXIT_FAILURE); } return open(mount_path, O_RDONLY); } int main(int argc, char *argv[]) { int mount_id, fd, mount_fd, handle_bytes; char buf[1000]; #define LINE_SIZE 100 char line1[LINE_SIZE], line2[LINE_SIZE]; char *nextp; ssize_t nread; struct file_handle *fhp; if ((argc > 1 && strcmp(argv[1], "--help") == 0) || argc > 2) { fprintf(stderr, "Utilisation : %s [mount-path]\n", argv[0]); exit(EXIT_FAILURE); } /* L'entree standard contient l'identifiant de montage et les informations de l'indicateur : Ligne 1 : Ligne 2 : if (fgets(line1, sizeof(line1), stdin) == NULL || fgets(line2, sizeof(line2), stdin) == NULL) { fprintf(stderr, "mount_id ou descripteur de fichier absent\n"); exit(EXIT_FAILURE); } mount_id = atoi(line1); handle_bytes = strtoul(line2, &nextp, 0); /* handle_bytes etant connu, on peut maintenant allouer la structure file_handle. */ fhp = malloc(sizeof(*fhp) + handle_bytes); if (fhp == NULL) err(EXIT_FAILURE, "malloc"); fhp->handle_bytes = handle_bytes; fhp->handle_type = strtoul(nextp, &nextp, 0); for (size_t j = 0; j < fhp->handle_bytes; j++) fhp->f_handle[j] = strtoul(nextp, &nextp, 16); /* Recupere le descripteur de fichier du point de montage, soit en ouvrant le chemin indique dans la ligne de commande, soit en parcourant /proc/self/mounts pour retrouver un montage qui corresponde a 'mount_id' qui a ete recu de stdin. */ if (argc > 1) mount_fd = open(argv[1], O_RDONLY); else mount_fd = open_mount_path_by_id(mount_id); if (mount_fd == -1) err(EXIT_FAILURE, "opening mount fd"); /* Ouvre le fichier en utilisant l'indicateur et le point de montage. */ fd = open_by_handle_at(mount_fd, fhp, O_RDONLY); if (fd == -1) err(EXIT_FAILURE, "open_by_handle_at"); /* On essaie de lire quelques octets depuis le fichier. */ nread = read(fd, buf, sizeof(buf)); if (nread == -1) err(EXIT_FAILURE, "read"); printf("Lire %zd octets\n", nread); exit(EXIT_SUCCESS); } VOIR AUSSI open(2), libblkid(3), blkid(8), findfs(8), mount(8) La documentation relative a libblkid et a libmount de la derniere publication de util-linux a TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot , Frederic Hantrais et Jean- Philippe MENGUAL Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 31 octobre 2023 open_by_handle_at(2)