summaryrefslogtreecommitdiffstats
path: root/man2/open_by_handle_at.2
diff options
context:
space:
mode:
Diffstat (limited to 'man2/open_by_handle_at.2')
-rw-r--r--man2/open_by_handle_at.2751
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