open_by_handle_at(2) System Calls Manual open_by_handle_at(2) name_to_handle_at, open_by_handle_at - LIBRARY Standard C library (libc, -lc) #define _GNU_SOURCE /* . 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); openat(2) name_to_handle_at() open_by_handle_at(): name_to_handle_at() (opaque), ; open_by_handle_at() , name_to_handle_at() . name_to_handle_at() name_to_handle_at() , dirfd pathname. handle, : struct file_handle { unsigned int handle_bytes; /* f_handle [in, out] */ int handle_type; /* [out] */ unsigned char f_handle[0]; /* ( ) [out] */ }; , f_handle. handle_bytes f_handle ( MAX_HANDLE_SZ, , . , ). handle_bytes f_handle. file_handle handle->handle_bytes ; EOVERFLOW, handle->handle_bytes ; ( ). , EOVERFLOW , , , , . , EOVERFLOW handle_bytes. 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. The flags argument is a bit mask constructed by ORing together zero or more of AT_HANDLE_FID, AT_EMPTY_PATH, and AT_SYMLINK_FOLLOW, described below. 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. pathname dirfd , . : o pathname -- , , , . dirfd . o pathname -- , dirfd AT_FDCWD, pathname , . o pathname -- , dirfd , , pathname , dirfd, , ( openat(2) << >>). o pathname -- flags AT_EMPTY_PATH, dirfd , , AT_FDCWD, , , . mount_id , pathname. /proc/self/mountinfo. ; open_by_handle_at(). mount_id , EOVERFLOW(). , name_to_handle_at() pathname, , . flags AT_SYMLINK_FOLLOW, pathname , ( , ). name_to_handle_at() , . , name_to_handle_at() EOVERFLOW handle_bytes. NFS Linux 4.13, . <> . open_by_handle_at() open_by_handle_at() , handle, , name_to_handle_at(). mount_fd -- (, . .) , handle. AT_FDCWD, . flags open(2). handle , O_PATH, ; O_NOFOLLOW . open_by_handle_at() CAP_DAC_READ_SEARCH. On success, name_to_handle_at() returns 0, and open_by_handle_at() returns a file descriptor (a nonnegative integer). In the event of an error, both system calls return -1 and set errno to indicate the error. name_to_handle_at() open_by_handle_at() openat(2). : name_to_handle_at() : EFAULT pathname, mount_id handle . EINVAL flags . EINVAL handle->handle_bytes MAX_HANDLE_SZ. ENOENT pathname , flags AT_EMPTY_PATH. ENOTDIR , dirfd, , flags AT_EMPTY_PATH pathname . EOPNOTSUPP . EOVERFLOW handle->handle_bytes, , . handle->handle_bytes . open_by_handle_at() : EBADF mount_fd . EBADF pathname , dirfd AT_FDCWD . EFAULT handle . EINVAL handle->handle_bytes MAX_HANDLE_SZ . ELOOP handle , flags O_PATH. EPERM 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(). FreeBSD getfh() openfh(). Linux. Linux 2.6.39, glibc 2.14. name_to_handle_at() open_by_handle_at() . Some filesystem don't support the translation of pathnames to file handles, for example, /proc, /sys, and various network filesystems. Some filesystems support the translation of pathnames to file handles, but do not support using those file handles in open_by_handle_at(). (<<>>), , , . open_by_handle_at() ESTALE. . , NFS NFS. , , . (stateless fashion) , . pathname flags AT_SYMLINK_FOLLOW, name_to_handle_at() ( , ). , , , , open_by_handle_at() O_PATH, dirfd readlinkat(2) fchownat(2). /proc/self/mountinfo . , , name_to_handle_at() ( *mount_id), . , mountinfo, , . , mountinfo UUID /dev/disks/by-uuid ( UUID -- libblkid(3)). -- UUID , , mount_fd, open_by_handle_at(). name_to_handle_at() open_by_handle_at(). (t_name_to_handle_at.c) name_to_handle_at() , ; . (t_open_by_handle_at.c) . , , open_by_handle_at() . , mount_fd open_by_handle_at() , . mount_fd /proc/self/mountinfo , , , ( , ). : $ 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 # Need CAP_SYS_ADMIN Read 31 bytes $ rm cecilia.txt () ( ) . , open_by_handle_at() , , , . $ stat --printf="%i\n" cecilia.txt # Display inode number 4072121 $ rm cecilia.txt $ echo 'Can you please think about it?' > cecilia.txt $ stat --printf="%i\n" cecilia.txt # Check inode number 4072121 $ sudo ./t_open_by_handle_at < fh open_by_handle_at: Stale NFS file handle : 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, "Usage: %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"); /* 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()\n"); 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\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); } : t_open_by_handle_at.c #define _GNU_SOURCE #include #include #include #include #include #include #include /* Scan /proc/self/mountinfo to find the line whose mount ID matches 'mount_id'. (An easier way to do this is to install and use the 'libmount' library provided by the 'util-linux' 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()\n"); exit(EXIT_FAILURE); } if (mi_mount_id == mount_id) found = 1; } free(linep); fclose(fp); if (!found) { fprintf(stderr, "Could not find mount point\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); } /* Standard input contains mount ID and file handle information: Line 1: Line 2: */ if (fgets(line1, sizeof(line1), stdin) == NULL || fgets(line2, sizeof(line2), stdin) == NULL) { fprintf(stderr, "Missing mount_id / file handle\n"); 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 'mount_id' 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\n", nread); exit(EXIT_SUCCESS); } . open(2), libblkid(3), blkid(8), findfs(8), mount(8) libblkid libmount util-linux Azamat Hackimov , Konstantin Shvaykovskiy , Yuri Kozlov ; GNU 3 , . . , , . Linux man-pages 6.06 31 2023 . open_by_handle_at(2)