open_by_handle_at(2) | System Calls Manual | open_by_handle_at(2) |
name_to_handle_at, open_by_handle_at - Récupérer le gestionnaire d'un chemin et ouvrir le fichier au moyen d'un gestionnaire
Bibliothèque C standard (libc, -lc)
#define _GNU_SOURCE /* Consultez feature_test_macros(7) */ #include <fcntl.h>
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);
Les appels système 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 à un fichier indiqué ; open_by_handle_at() ouvre le fichier correspondant à un gestionnaire renvoyé par un appel antérieur à name_to_handle_at() et renvoie le descripteur d'un fichier ouvert.
L'appel système name_to_handle_at() renvoie un gestionnaire de fichier et un identifiant de montage (« mount ID ») correspondant au fichier désigné par les arguments dirfd et pathname. Le gestionnaire de fichier est renvoyé au moyen de l'argument handle. Cet argument est un pointeur vers une structure qui présente 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
définie par l’appelant) [out] */ };
L'appelant à l'origine de l'appel doit s'assurer que la structure est créée avec une taille suffisante pour contenir le gestionnaire renvoyé dans f_handle. Avant l'appel, le champ handle_bytes devrait être initialisé de sorte que l'espace alloué puisse recevoir f_handle. (La constante MAX_HANDLE_SZ, définie dans <fcntl.h>, précise la taille maximale autorisée pour un gestionnaire de fichier. La limite supérieure n'est pas garantie car de futurs systèmes de fichiers pourraient avoir besoin de davantage d'espace). Lorsque l'appel réussit, le champ handle_bytes est mis à jour afin de contenir le nombre d'octets effectivement écrits dans f_handle.
L'appelant peut prendre connaissance de l'espace nécessaire à la structure file_handle en effectuant un appel dans lequel handle->handle_bytes vaut zéro ; dans ce cas, l'appel échoue 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 système de fichiers qui prend normalement en charge la recherche de gestionnaire de fichiers. Ce cas peut se détecter quand l'erreur EOVERFLOW est renvoyée sans que handle_bytes ne soit augmenté.
Mise à part l'utilisation du champ handle_bytes, l'appelant doit considérer la structure file_handle comme de type opaque de données : les champs handle_type et f_handle ne sont utiles que pour un appel ultérieur à open_by_handle_at().
L'argument flags est un masque de bits construit par OU binaire entre zéro ou plus de AT_EMPTY_PATH et AT_SYMLINK_FOLLOW, décrits plus bas.
Ensemble, les arguments pathname et dirfd désignent le fichier pour lequel on souhaite obtenir un gestionnaire. On distingue quatre cas :
L'argument mount_id renvoie un identifiant pour le point de montage du système de fichiers correspondant à pathname. Cet identifiant correspond au premier champ des entrées de /proc/self/mountinfo. L'ouverture du chemin indiqué dans le cinquième champ délivre un descripteur de fichier pour le point de montage ; ce descripteur de fichier peut être utilisé par la suite lors d'un appel à open_by_handle_at(). mount_id est renvoyé tant en cas de succès qu'en cas d'erreur EOVERFLOW de l'appel.
Par défaut, name_to_handle_at() ne déréférence pas pathname s'il s'agit d'un lien symbolique, et donc renvoie un gestionnaire pour le lien lui-même. Si AT_SYMLINK_FOLLOW est précisé dans flags, pathname est déréférencé 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 récupère pas un montage quand le composant final du chemin est un point de montage automatique. Quand un système de fichiers gère à 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 accès à un répertoire sur un système de fichiers séparé du serveur. Dans ce cas, le montage automatique peut être provoqué en ajoutant un « / » à la fin du chemin.
L'appel système open_by_handle_at() ouvre le fichier auquel handle fait référence, via un indicateur de fichier renvoyé lors d'un précédent appel à name_to_handle_at().
L'argument mount_fd est un descripteur de fichier pour n'importe quel type d'objet (fichier, répertoire, etc.) du système de fichiers monté qui permet d'interpréter l'indicateur de fichier (handle). La valeur spéciale AT_FDCWD peut être précisée, et indique le répertoire courant du processus appelant.
L'argument flags a la même fonction que pour open(2). Si l'indicateur handle fait référence à un lien symbolique, le processus appelant doit préciser l'attribut O_PATH et le lien symbolique n'est pas déréférencé. L'attribut O_NOFOLLOW est ignoré.
L'appelant doit avoir la capacité CAP_DAC_READ_SEARCH pour utiliser open_by_handle_at().
Lorsqu'il réussit, l'appel name_to_handle_at() renvoie 0 et open_by_handle_at() renvoie un descripteur de fichier (un entier non négatif).
En cas d'échec, les deux appels renvoient -1 et définissent errno pour indiquer l'erreur.
Les appels name_to_handle_at() et open_by_handle_at() peuvent échouer pour les mêmes raisons que openat(2). En outre, ils peuvent également échouer pour les motifs décrits plus bas.
name_to_handle_at() peut échouer avec les erreurs suivantes :
open_by_handle_at() peut échouer avec les erreurs suivantes :
Ces appels système sont apparus dans Linux 2.6.39. La prise en charge de la bibliothèque a été ajoutée dans la glibc 2.14.
Ces appels système sont des extensions spécifiques à Linux.
FreeBSD offre un couple d'appels système similaires : getfh() et openfh().
Un indicateur de fichier peut être créé dans un processus au moyen de name_to_handle_at() et utilisé plus tard dans un autre processus qui appelle open_by_handle_at().
Certains systèmes de fichiers ne permettent pas la transcription des chemins de fichier en indicateurs (par exemple, /proc, /sys, ainsi que divers systèmes de fichiers en réseaux).
Un indicateur de fichier peut devenir non valable (« stale ») si un fichier est supprimé, ou pour une raison propre au système de fichiers. Les indicateurs non autorisés sont signalés par une erreur ESTALE provenant de open_by_handle_at().
Ces appels systèmes sont conçus pour être utilisés 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 accéder au fichier, il peut renvoyer l'indicateur au serveur. Ce type de fonctionnalité permet à un serveur de fichiers en espace utilisateur d'opérer sans état vis à vis des fichiers qu'il délivre.
Si pathname fait référence à un lien symbolique et si flags ne précise pas AT_SYMLINK_FOLLOW, alors name_to_handle_at() renvoie un indicateur pour le lien (plutôt que pour le fichier vers lequel le lien pointe). Le processus recevant l'indicateur peut effectuer plus tard une opération sur ce lien symbolique, en convertissant l'indicateur en descripteur de fichier au moyen de open_by_handle_at() utilisé avec l'argument O_PATH, et en passant le descripteur de fichier en argument dirfd d’appels système (tels que readlinkat(2) et fchownat(2)).
Les identifiants de montage dans /proc/self/mountinfo peuvent être réutilisés même lorsque les systèmes de fichiers sont démontés et remontés. Ainsi, l'identifiant de montage renvoyé par name_to_handle_at() (dans *mount_id) ne doit pas être considéré comme un identifiant persistant pour le système de fichiers considéré. Néanmoins, il est possible pour une application d'utiliser l'information fournie dans mountinfo et correspondant à l'identifiant de montage pour en déduire un identifiant persistant.
Par exemple, on peut utiliser le nom de périphérique présent dans le cinquième champ de mountinfo pour retrouver l'UUID du périphérique correspondant au moyen des liens symboliques dans /dev/disks/by-uuid. (Un moyen plus simple d'obtenir cet UUID consiste à utiliser la bibliothèque libblkid(3)). Cette façon de procéder peut être inversée, en utilisant l'UUID pour retrouver le nom du périphérique, et ainsi obtenir le point de montage correspondant, et enfin construire l'argument de mount_fd utile à open_by_handle_at().
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 récupérer l'indicateur de fichier et l'identifiant de montage du fichier indiqué dans les arguments en ligne de commande ; l'indicateur et l'identifiant de montage sont écrits 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'entrée 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 répertoire précisé en argument. Sinon, mount_fd est obtenu en parcourant /proc/self/mountinfo à la recherche d'un identifiant de montage correspondant à celui fourni via l'entrée standard, et le répertoire monté qui a été trouvé 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 # Nécessite CAP_SYS_ADMIN Read 31 bytes $ rm cecilia.txt
A ce stade, on supprime et recrée (rapidement) le fichier, de sorte qu'il ait le même contenu et (avec un peu de chance) le même inœud. Cependant, open_by_handle_at() s'aperçoit que le fichier original auquel l'indicateur fait référence n'existe plus.
$ stat --printf="%i\n" cecilia.txt # Affiche le numéro d'inœud 4072121 $ rm cecilia.txt $ echo 'Can you please think about it?' > cecilia.txt $ stat --printf="%i\n" cecilia.txt # Vérifie le numéro d'inœud 4072121 $ sudo ./t_open_by_handle_at < fh open_by_handle_at: Stale NFS file handle
#define _GNU_SOURCE #include <err.h> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) {
int mount_id, fhsize, flags, dirfd;
char *pathname;
struct file_handle *fhp;
if (argc != 2) {
fprintf(stderr, "Usage: %s pathname\n", argv[0]);
exit(EXIT_FAILURE);
}
pathname = argv[1];
/* Alloue la structure file_handle. */
fhsize = sizeof(*fhp);
fhp = malloc(fhsize);
if (fhp == NULL)
err(EXIT_FAILURE, "malloc");
/* Effectue un appel initial à name_to_handle_at() afin de connaître
la taille nécessaire à l'indicateur de fichier. */
dirfd = AT_FDCWD; /* Pour les appels à name_to_handle_at() */
flags = 0; /* Pour les appels à name_to_handle_at() */
fhp->handle_bytes = 0;
if (name_to_handle_at(dirfd, pathname, fhp,
&mount_id, flags) != -1
|| errno != EOVERFLOW)
{
fprintf(stderr, "Unexpected result from name_to_handle_at()\n");
exit(EXIT_FAILURE);
}
/* Ré-alloue la structure file_handle avec la bonne taille. */
fhsize = sizeof(*fhp) + fhp->handle_bytes;
fhp = realloc(fhp, fhsize); /* Copie fhp->handle_bytes */
if (fhp == NULL)
err(EXIT_FAILURE, "realloc");
/* Retrouve l'indicateur de fichier à 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");
/* Écrit l'identifiant de montage, la taille de l'indicateur et
l'indicateur vers la sortie standard
pour être utilisés 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); }
#define _GNU_SOURCE #include <err.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /* Parcourt /proc/self/mountinfo pour trouver la ligne correspondant à
l'ID de montage 'mount_id'. (Une méthode plus simple consiste à
installer et à utiliser la bibliothèque ('libmount' fournie par
le projet 'util-linux'.) Ouvre le point de montage correspondant
et renvoie le descripteur de fichier associé. */ 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, "Bad 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 trouvé\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, "Usage: %s [mount-path]\n", argv[0]);
exit(EXIT_FAILURE);
}
/* L'entrée standard contient l'identifiant de montage et
les informations de l'indicateur :
Ligne 1: <mount_id>
Ligne 2: <handle_bytes> <handle_type> <octets du descripteur en hexadécimal>
*/
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 étant 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);
/* Récupère le descripteur de fichier du point de montage, soit en ouvrant
le chemin indiqué dans la ligne de commande, soit en parcourant
/proc/self/mounts pour retrouver un montage qui corresponde à 'mount_id'
qui a été reçu 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("Read %zd bytes\n", nread);
exit(EXIT_SUCCESS); }
open(2), libblkid(3), blkid(8), findfs(8), mount(8)
La documentation relative à libblkid et à libmount de la dernière publication de util-linux à https://www.kernel.org/pub/linux/utils/util-linux/
La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org>, Frédéric Hantrais <fhantrais@gmail.com> et Jean-Philippe MENGUAL <jpmengual@debian.org>
Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.
5 février 2023 | Pages du manuel de Linux 6.03 |