Scroll to navigation

OPEN_BY_HANDLE_AT(2) Manuel du programmeur Linux OPEN_BY_HANDLE_AT(2)

NOM

name_to_handle_at, open_by_handle_at - récupérer le gestionnaire d'un chemin et le ouvrir le fichier au moyen d'un gestionnaire

SYNOPSIS

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <sys/types.h>
#include <sys/stat.h>
#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);

DESCRIPTION

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.

name_to_handle_at()

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 handle [out] */
    unsigned char f_handle[0];    /* identifiant du fichier (dont la taille est définie
                                     lors de l'appel) [out] */
};


It is the caller's responsibility to allocate the structure with a size large enough to hold the handle returned in f_handle. Before the call, the handle_bytes field should be initialized to contain the allocated size for f_handle. (The constant MAX_HANDLE_SZ, defined in <fcntl.h>, specifies the maximum expected size for a file handle. It is not a guaranteed upper limit as future filesystems may require more space.) Upon successful return, the handle_bytes field is updated to contain the number of bytes actually written to f_handle.

The caller can discover the required size for the file_handle structure by making a call in which handle->handle_bytes is zero; in this case, the call fails with the error EOVERFLOW and handle->handle_bytes is set to indicate the required size; the caller can then use this information to allocate a structure of the correct size (see EXAMPLE below). Some care is needed here as EOVERFLOW can also indicate that no file handle is available for this particular name in a filesystem which does normally support file-handle lookup. This case can be detected when the EOVERFLOW error is returned without handle_bytes being increased.

Mise à part l'utilisation du champ handle_bytes, l'appelant doit considérer la structure file_handle comme une « boîte noire » : 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, comme décrit plus bas.

Ensemble, les arguments pathname et dirfd désignent le fichier pour lequel on souhaite obtenir un gestionnaire. On distingue quatre cas :

  • Si pathname est une chaîne non vide contenant un chemin d'accès absolu, alors un gestionnaire est renvoyé pour le fichier indiqué par le chemin. Dans ce cas, dirfd est ignoré.
  • Si pathname est une chaîne non vide contenant un chemin relatif, et si dirfd a la valeur spéciale AT_FDCWD, alors pathname est interprété par rapport au répertoire courant du processus appelant, et un gestionnaire est renvoyé pour le fichier indiqué par le chemin.
  • If pathname is a nonempty string containing a relative pathname and dirfd is a file descriptor referring to a directory, then pathname is interpreted relative to the directory referred to by dirfd, and a handle is returned for the file to which it refers. (See openat(2) for an explanation of why "directory file descriptors" are useful.)
  • Si pathname est une chaîne vide, et si flags précise la valeur de AT_EMPTY_PATH, alors dirfd peut être un descripteur de fichiers ouvert faisant référence à n'importe quel type de fichier ou à AT_FDCWD (répertoire de travail courant), et un gestionnaire est renvoyé pour le fichier auquel il fait référence.

The mount_id argument returns an identifier for the filesystem mount that corresponds to pathname. This corresponds to the first field in one of the records in /proc/self/mountinfo. Opening the pathname in the fifth field of that record yields a file descriptor for the mount point; that file descriptor can be used in a subsequent call to open_by_handle_at(). mount_id is returned both for a successful call and for a call that results in the error EOVERFLOW.

Par défaut, name_to_handle_at() ne déréférence pas pathname s'il s'agit d'un lien symbolique, et donc ne renvoie pas d'indicateur pour le lien symbolique. 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() does not trigger a mount when the final component of the pathname is an automount point. When a filesystem supports both file handles and automount points, a name_to_handle_at() call on an automount point will return with error EOVERFLOW without having increased handle_bytes. This can happen since Linux 4.13 with NFS when accessing a directory which is on a separate filesystem on the server. In this case, the automount can be triggered by adding a "/" to the end of the pathname.

open_by_handle_at()

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().

VALEUR RENVOYÉE

On success, name_to_handle_at() returns 0, and open_by_handle_at() returns a file descriptor (a nonnegative integer).

En cas d'échec, les deux appels renvoient -1 et affectent à errno l'identifiant de la cause de l'échec.

ERREURS

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 :

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 supérieur à MAX_HANDLE_SZ.
ENOENT
pathname est une chaîne vide et AT_EMPTY_PATH n’était pas indiqué dans flags.
ENOTDIR
Le descripteur de fichiers fourni dans dirfd ne fait pas référence à un répertoire, et il ne s'agit pas du cas où flags comprend AT_EMPTY_PATH et pathname est une chaîne vide.
EOPNOTSUPP
Le système 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 modifié afin d'indiquer la taille requise pour cet indicateur.

open_by_handle_at() peut échouer avec les erreurs suivantes :

EBADF
mount_fd n'est pas un descripteur de fichier ouvert.
EFAULT
handle pointe en‐dehors de l'espace d'adressage accessible.
EINVAL
handle->handle_bytes est supérieur à MAX_HANDLE_SZ ou égal à zéro.
ELOOP
handle correspond à un lien symbolique et O_PATH n’était pas indiqué dans flags.
EPERM
L'appelant n'a pas la capacité CAP_DAC_READ_SEARCH.
ESTALE
La valeur handle indiquée n'est pas correcte. Cet erreur se produit par exemple lorsque le fichier a été supprimé.

VERSIONS

Ces appels système sont apparaus dans Linux 2.6.39. La glibc les gère depuis la version 2.14.

CONFORMITÉ

Ces appels système sont des extensions spécifiques à Linux.

FreeBSD offre un couple d'appel système similaire : getfh() et openfh().

NOTES

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 chemin de fichiers en indicateurs (par exemple, /proc, /sys, ainsi que divers systèmes de fichiers en réseaux).

Un indicateur de fichier peut devenir invalide (« stale ») si un fichier est supprimé, ou pour une raison propre au système de fichiers. Les indicateurs invalides 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 en lecture, il peut renvoyer l'indicateur au serveur. Ce type de fonctionnalité permet à un serveur de fichier en espace utilisateur d'opérer sans état vis à vis du fichier 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 de l'appel système (comme pour readlinkat(2) et fchownat(2)).

Obtenir un identifiant persistant de système de fichier

Les identifiant de montage de /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 de /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().

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 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 'Pouvez-vous réflechir à cela ?' > cecilia.txt
$ ./t_name_to_handle_at cecilia.txt > fh
$ ./t_open_by_handle_at < fh
open_by_handle_at: Operation non autorisée
$ sudo ./t_open_by_handle_at < fh      # Nécessite CAP_SYS_ADMIN
31 octets lus
$ 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 inoeud. 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'inoeud
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'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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
int
main(int argc, char *argv[])
{
    struct file_handle *fhp;
    int mount_id, fhsize, flags, dirfd, j;
    char *pathname;
    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)
        errExit("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(struct file_handle) + fhp->handle_bytes;
    fhp = realloc(fhp, fhsize);         /* Copie fhp->handle_bytes */
    if (fhp == NULL)
        errExit("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)
        errExit("name_to_handle_at");
    /* Écrit l'identifiant de montage, la taille de l'indicateur et
       l'indicateur vers la sortie standard
       pour être utilisé plus tard par t_open_by_handle_at.c */
    printf("%d\n", mount_id);
    printf("%d %d   ", fhp->handle_bytes, fhp->handle_type);
    for (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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
/* 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 (aqlibmount' 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)
{
    char *linep;
    size_t lsize;
    char mount_path[PATH_MAX];
    int mi_mount_id, found;
    ssize_t nread;
    FILE *fp;
    fp = fopen("/proc/self/mountinfo", "r");
    if (fp == NULL)
        errExit("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[])
{
    struct file_handle *fhp;
    int mount_id, fd, mount_fd, handle_bytes, j;
    ssize_t nread;
    char buf[1000];
#define LINE_SIZE 100
    char line1[LINE_SIZE], line2[LINE_SIZE];
    char *nextp;
    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 du 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(struct file_handle) + handle_bytes);
    if (fhp == NULL)
        errExit("malloc");
    fhp->handle_bytes = handle_bytes;
    fhp->handle_type = strtoul(nextp, &nextp, 0);
    for (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)
        errExit("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)
        errExit("open_by_handle_at");
    /* On essaie de lire quelques octets depuis le fichier */
    nread = read(fd, buf, sizeof(buf));
    if (nread == -1)
        errExit("read");
    printf("Read %zd bytes\n", nread);
    exit(EXIT_SUCCESS);
}

VOIR AUSSI

open(2), libblkid(3), blkid(8), findfs(8), mount(8)

La document relative à libblkid et à libmount de la dernière publication de util-linux à https://www.kernel.org/pub/linux/utils/util-linux/

COLOPHON

Cette page fait partie de la publication 5.07 du projet man-pages Linux. Une description du projet et des instructions pour signaler des anomalies et la dernière version de cette page, peuvent être trouvées à l'adresse https://www.kernel.org/doc/man-pages/.

TRADUCTION

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> et Frédéric Hantrais <fhantrais@gmail.com>

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>.

11 avril 2020 Linux