diff options
Diffstat (limited to 'man2/open_by_handle_at.2')
-rw-r--r-- | man2/open_by_handle_at.2 | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/man2/open_by_handle_at.2 b/man2/open_by_handle_at.2 new file mode 100644 index 0000000..b5e3d75 --- /dev/null +++ b/man2/open_by_handle_at.2 @@ -0,0 +1,751 @@ +.\" Copyright (c) 2014 by Michael Kerrisk <mtk.manpages@gmail.com> +.\" +.\" SPDX-License-Identifier: Linux-man-pages-copyleft +.\" +.TH open_by_handle_at 2 2023-05-03 "Linux man-pages 6.05.01" +.SH NAME +name_to_handle_at, open_by_handle_at \- obtain handle +for a pathname and open file via a handle +.SH LIBRARY +Standard C library +.RI ( libc ", " \-lc ) +.SH SYNOPSIS +.nf +.BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */" +.B #include <fcntl.h> +.PP +.BI "int name_to_handle_at(int " dirfd ", const char *" pathname , +.BI " struct file_handle *" handle , +.BI " int *" mount_id ", int " flags ); +.BI "int open_by_handle_at(int " mount_fd ", struct file_handle *" handle , +.BI " int " flags ); +.fi +.SH DESCRIPTION +The +.BR name_to_handle_at () +and +.BR open_by_handle_at () +system calls split the functionality of +.BR openat (2) +into two parts: +.BR name_to_handle_at () +returns an opaque handle that corresponds to a specified file; +.BR open_by_handle_at () +opens the file corresponding to a handle returned by a previous call to +.BR name_to_handle_at () +and returns an open file descriptor. +.\" +.\" +.SS name_to_handle_at() +The +.BR name_to_handle_at () +system call returns a file handle and a mount ID corresponding to +the file specified by the +.I dirfd +and +.I pathname +arguments. +The file handle is returned via the argument +.IR handle , +which is a pointer to a structure of the following form: +.PP +.in +4n +.EX +struct file_handle { + unsigned int handle_bytes; /* Size of f_handle [in, out] */ + int handle_type; /* Handle type [out] */ + unsigned char f_handle[0]; /* File identifier (sized by + caller) [out] */ +}; +.EE +.in +.PP +It is the caller's responsibility to allocate the structure +with a size large enough to hold the handle returned in +.IR f_handle . +Before the call, the +.I handle_bytes +field should be initialized to contain the allocated size for +.IR f_handle . +(The constant +.BR MAX_HANDLE_SZ , +defined in +.IR <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 +.I handle_bytes +field is updated to contain the number of bytes actually written to +.IR f_handle . +.PP +The caller can discover the required size for the +.I file_handle +structure by making a call in which +.I handle\->handle_bytes +is zero; +in this case, the call fails with the error +.B EOVERFLOW +and +.I 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 EXAMPLES below). +Some care is needed here as +.B 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 +.B EOVERFLOW +error is returned without +.I handle_bytes +being increased. +.PP +Other than the use of the +.I handle_bytes +field, the caller should treat the +.I file_handle +structure as an opaque data type: the +.I handle_type +and +.I f_handle +fields are needed only by a subsequent call to +.BR open_by_handle_at (). +.PP +The +.I flags +argument is a bit mask constructed by ORing together zero or more of +.B AT_EMPTY_PATH +and +.BR AT_SYMLINK_FOLLOW , +described below. +.PP +Together, the +.I pathname +and +.I dirfd +arguments identify the file for which a handle is to be obtained. +There are four distinct cases: +.IP \[bu] 3 +If +.I pathname +is a nonempty string containing an absolute pathname, +then a handle is returned for the file referred to by that pathname. +In this case, +.I dirfd +is ignored. +.IP \[bu] +If +.I pathname +is a nonempty string containing a relative pathname and +.I dirfd +has the special value +.BR AT_FDCWD , +then +.I pathname +is interpreted relative to the current working directory of the caller, +and a handle is returned for the file to which it refers. +.IP \[bu] +If +.I pathname +is a nonempty string containing a relative pathname and +.I dirfd +is a file descriptor referring to a directory, then +.I pathname +is interpreted relative to the directory referred to by +.IR dirfd , +and a handle is returned for the file to which it refers. +(See +.BR openat (2) +for an explanation of why "directory file descriptors" are useful.) +.IP \[bu] +If +.I pathname +is an empty string and +.I flags +specifies the value +.BR AT_EMPTY_PATH , +then +.I dirfd +can be an open file descriptor referring to any type of file, +or +.BR AT_FDCWD , +meaning the current working directory, +and a handle is returned for the file to which it refers. +.PP +The +.I mount_id +argument returns an identifier for the filesystem +mount that corresponds to +.IR pathname . +This corresponds to the first field in one of the records in +.IR /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 +.BR open_by_handle_at (). +.I mount_id +is returned both for a successful call and for a call that results +in the error +.BR EOVERFLOW . +.PP +By default, +.BR name_to_handle_at () +does not dereference +.I pathname +if it is a symbolic link, and thus returns a handle for the link itself. +If +.B AT_SYMLINK_FOLLOW +is specified in +.IR flags , +.I pathname +is dereferenced if it is a symbolic link +(so that the call returns a handle for the file referred to by the link). +.PP +.BR 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 +.BR name_to_handle_at () +call on an automount point will return with error +.B EOVERFLOW +without having increased +.IR handle_bytes . +This can happen since Linux 4.13 +.\" commit 20fa19027286983ab2734b5910c4a687436e0c31 +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. +.SS open_by_handle_at() +The +.BR open_by_handle_at () +system call opens the file referred to by +.IR handle , +a file handle returned by a previous call to +.BR name_to_handle_at (). +.PP +The +.I mount_fd +argument is a file descriptor for any object (file, directory, etc.) +in the mounted filesystem with respect to which +.I handle +should be interpreted. +The special value +.B AT_FDCWD +can be specified, meaning the current working directory of the caller. +.PP +The +.I flags +argument +is as for +.BR open (2). +If +.I handle +refers to a symbolic link, the caller must specify the +.B O_PATH +flag, and the symbolic link is not dereferenced; the +.B O_NOFOLLOW +flag, if specified, is ignored. +.PP +The caller must have the +.B CAP_DAC_READ_SEARCH +capability to invoke +.BR open_by_handle_at (). +.SH RETURN VALUE +On success, +.BR name_to_handle_at () +returns 0, +and +.BR open_by_handle_at () +returns a file descriptor (a nonnegative integer). +.PP +In the event of an error, both system calls return \-1 and set +.I errno +to indicate the error. +.SH ERRORS +.BR name_to_handle_at () +and +.BR open_by_handle_at () +can fail for the same errors as +.BR openat (2). +In addition, they can fail with the errors noted below. +.PP +.BR name_to_handle_at () +can fail with the following errors: +.TP +.B EFAULT +.IR pathname , +.IR mount_id , +or +.I handle +points outside your accessible address space. +.TP +.B EINVAL +.I flags +includes an invalid bit value. +.TP +.B EINVAL +.I handle\->handle_bytes +is greater than +.BR MAX_HANDLE_SZ . +.TP +.B ENOENT +.I pathname +is an empty string, but +.B AT_EMPTY_PATH +was not specified in +.IR flags . +.TP +.B ENOTDIR +The file descriptor supplied in +.I dirfd +does not refer to a directory, +and it is not the case that both +.I flags +includes +.B AT_EMPTY_PATH +and +.I pathname +is an empty string. +.TP +.B EOPNOTSUPP +The filesystem does not support decoding of a pathname to a file handle. +.TP +.B EOVERFLOW +The +.I handle\->handle_bytes +value passed into the call was too small. +When this error occurs, +.I handle\->handle_bytes +is updated to indicate the required size for the handle. +.\" +.\" +.PP +.BR open_by_handle_at () +can fail with the following errors: +.TP +.B EBADF +.I mount_fd +is not an open file descriptor. +.TP +.B EBADF +.I pathname +is relative but +.I dirfd +is neither +.B AT_FDCWD +nor a valid file descriptor. +.TP +.B EFAULT +.I handle +points outside your accessible address space. +.TP +.B EINVAL +.I handle\->handle_bytes +is greater than +.B MAX_HANDLE_SZ +or is equal to zero. +.TP +.B ELOOP +.I handle +refers to a symbolic link, but +.B O_PATH +was not specified in +.IR flags . +.TP +.B EPERM +The caller does not have the +.B CAP_DAC_READ_SEARCH +capability. +.TP +.B ESTALE +The specified +.I handle +is not valid. +This error will occur if, for example, the file has been deleted. +.SH VERSIONS +FreeBSD has a broadly similar pair of system calls in the form of +.BR getfh () +and +.BR openfh (). +.SH STANDARDS +Linux. +.SH HISTORY +Linux 2.6.39, +glibc 2.14. +.SH NOTES +A file handle can be generated in one process using +.BR name_to_handle_at () +and later used in a different process that calls +.BR open_by_handle_at (). +.PP +Some filesystem don't support the translation of pathnames to +file handles, for example, +.IR /proc , +.IR /sys , +and various network filesystems. +.PP +A file handle may become invalid ("stale") if a file is deleted, +or for other filesystem-specific reasons. +Invalid handles are notified by an +.B ESTALE +error from +.BR open_by_handle_at (). +.PP +These system calls are designed for use by user-space file servers. +For example, a user-space NFS server might generate a file handle +and pass it to an NFS client. +Later, when the client wants to open the file, +it could pass the handle back to the server. +.\" https://lwn.net/Articles/375888/ +.\" "Open by handle" - Jonathan Corbet, 2010-02-23 +This sort of functionality allows a user-space file server to operate in +a stateless fashion with respect to the files it serves. +.PP +If +.I pathname +refers to a symbolic link and +.I flags +does not specify +.BR AT_SYMLINK_FOLLOW , +then +.BR name_to_handle_at () +returns a handle for the link (rather than the file to which it refers). +.\" commit bcda76524cd1fa32af748536f27f674a13e56700 +The process receiving the handle can later perform operations +on the symbolic link by converting the handle to a file descriptor using +.BR open_by_handle_at () +with the +.B O_PATH +flag, and then passing the file descriptor as the +.I dirfd +argument in system calls such as +.BR readlinkat (2) +and +.BR fchownat (2). +.SS Obtaining a persistent filesystem ID +The mount IDs in +.I /proc/self/mountinfo +can be reused as filesystems are unmounted and mounted. +Therefore, the mount ID returned by +.BR name_to_handle_at () +(in +.IR *mount_id ) +should not be treated as a persistent identifier +for the corresponding mounted filesystem. +However, an application can use the information in the +.I mountinfo +record that corresponds to the mount ID +to derive a persistent identifier. +.PP +For example, one can use the device name in the fifth field of the +.I mountinfo +record to search for the corresponding device UUID via the symbolic links in +.IR /dev/disks/by\-uuid . +(A more comfortable way of obtaining the UUID is to use the +.\" e.g., http://stackoverflow.com/questions/6748429/using-libblkid-to-find-uuid-of-a-partition +.BR libblkid (3) +library.) +That process can then be reversed, +using the UUID to look up the device name, +and then obtaining the corresponding mount point, +in order to produce the +.I mount_fd +argument used by +.BR open_by_handle_at (). +.SH EXAMPLES +The two programs below demonstrate the use of +.BR name_to_handle_at () +and +.BR open_by_handle_at (). +The first program +.RI ( t_name_to_handle_at.c ) +uses +.BR name_to_handle_at () +to obtain the file handle and mount ID +for the file specified in its command-line argument; +the handle and mount ID are written to standard output. +.PP +The second program +.RI ( t_open_by_handle_at.c ) +reads a mount ID and file handle from standard input. +The program then employs +.BR open_by_handle_at () +to open the file using that handle. +If an optional command-line argument is supplied, then the +.I mount_fd +argument for +.BR open_by_handle_at () +is obtained by opening the directory named in that argument. +Otherwise, +.I mount_fd +is obtained by scanning +.I /proc/self/mountinfo +to find a record whose mount ID matches the mount ID +read from standard input, +and the mount directory specified in that record is opened. +(These programs do not deal with the fact that mount IDs are not persistent.) +.PP +The following shell session demonstrates the use of these two programs: +.PP +.in +4n +.EX +$ \fBecho \[aq]Can you please think about it?\[aq] > cecilia.txt\fP +$ \fB./t_name_to_handle_at cecilia.txt > fh\fP +$ \fB./t_open_by_handle_at < fh\fP +open_by_handle_at: Operation not permitted +$ \fBsudo ./t_open_by_handle_at < fh\fP # Need CAP_SYS_ADMIN +Read 31 bytes +$ \fBrm cecilia.txt\fP +.EE +.in +.PP +Now we delete and (quickly) re-create the file so that +it has the same content and (by chance) the same inode. +Nevertheless, +.BR open_by_handle_at () +.\" Christoph Hellwig: That's why the file handles contain a generation +.\" counter that gets incremented in this case. +recognizes that the original file referred to by the file handle +no longer exists. +.PP +.in +4n +.EX +$ \fBstat \-\-printf="%i\en" cecilia.txt\fP # Display inode number +4072121 +$ \fBrm cecilia.txt\fP +$ \fBecho \[aq]Can you please think about it?\[aq] > cecilia.txt\fP +$ \fBstat \-\-printf="%i\en" cecilia.txt\fP # Check inode number +4072121 +$ \fBsudo ./t_open_by_handle_at < fh\fP +open_by_handle_at: Stale NFS file handle +.EE +.in +.SS Program source: t_name_to_handle_at.c +\& +.\" SRC BEGIN (t_name_to_handle_at.c) +.EX +#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\en", 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"); +\& + /* Make an initial call to name_to_handle_at() to discover + the size required for file handle. */ +\& + dirfd = AT_FDCWD; /* For name_to_handle_at() calls */ + flags = 0; /* For name_to_handle_at() calls */ + 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()\en"); + exit(EXIT_FAILURE); + } +\& + /* Reallocate file_handle structure with correct size. */ +\& + fhsize = sizeof(*fhp) + fhp\->handle_bytes; + fhp = realloc(fhp, fhsize); /* Copies fhp\->handle_bytes */ + if (fhp == NULL) + err(EXIT_FAILURE, "realloc"); +\& + /* Get file handle from pathname supplied on command line. */ +\& + if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == \-1) + err(EXIT_FAILURE, "name_to_handle_at"); +\& + /* Write mount ID, file handle size, and file handle to stdout, + for later reuse by t_open_by_handle_at.c. */ +\& + printf("%d\en", 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("\en"); +\& + exit(EXIT_SUCCESS); +} +.EE +.\" SRC END +.SS Program source: t_open_by_handle_at.c +\& +.\" SRC BEGIN (t_open_by_handle_at.c) +.EX +#define _GNU_SOURCE +#include <err.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +\& +/* Scan /proc/self/mountinfo to find the line whose mount ID matches + \[aq]mount_id\[aq]. (An easier way to do this is to install and use the + \[aq]libmount\[aq] library provided by the \[aq]util\-linux\[aq] project.) + Open the corresponding mount path and return the resulting file + descriptor. */ +\& +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()\en"); + exit(EXIT_FAILURE); + } +\& + if (mi_mount_id == mount_id) + found = 1; + } + free(linep); +\& + fclose(fp); +\& + if (!found) { + fprintf(stderr, "Could not find mount point\en"); + 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]\en", argv[0]); + exit(EXIT_FAILURE); + } +\& + /* Standard input contains mount ID and file handle information: +\& + Line 1: <mount_id> + Line 2: <handle_bytes> <handle_type> <bytes of handle in hex> + */ +\& + if (fgets(line1, sizeof(line1), stdin) == NULL || + fgets(line2, sizeof(line2), stdin) == NULL) + { + fprintf(stderr, "Missing mount_id / file handle\en"); + exit(EXIT_FAILURE); + } +\& + mount_id = atoi(line1); +\& + handle_bytes = strtoul(line2, &nextp, 0); +\& + /* Given handle_bytes, we can now allocate file_handle structure. */ +\& + 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); +\& + /* Obtain file descriptor for mount point, either by opening + the pathname specified on the command line, or by scanning + /proc/self/mounts to find a mount that matches the \[aq]mount_id\[aq] + that we received from 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"); +\& + /* Open file using handle and mount point. */ +\& + fd = open_by_handle_at(mount_fd, fhp, O_RDONLY); + if (fd == \-1) + err(EXIT_FAILURE, "open_by_handle_at"); +\& + /* Try reading a few bytes from the file. */ +\& + nread = read(fd, buf, sizeof(buf)); + if (nread == \-1) + err(EXIT_FAILURE, "read"); +\& + printf("Read %zd bytes\en", nread); +\& + exit(EXIT_SUCCESS); +} +.EE +.\" SRC END +.SH SEE ALSO +.BR open (2), +.BR libblkid (3), +.BR blkid (8), +.BR findfs (8), +.BR mount (8) +.PP +The +.I libblkid +and +.I libmount +documentation in the latest +.I util\-linux +release at +.UR https://www.kernel.org/pub/linux/utils/util\-linux/ +.UE |