open_by_handle_at(2) System Calls Manual open_by_handle_at(2) name_to_handle_at, open_by_handle_at - (libc -lc) #define _GNU_SOURCE /* feature_test_macros(7) */ #include int name_to_handle_at(int dirfd, const char *path, struct file_handle *handle, int *mount_id, int flags); int open_by_handle_at(int mount_fd, struct file_handle *handle, int flags); name_to_handle_at() open_by_handle_at() openat(2) : name_to_handle_at() open_by_handle_at() name_to_handle_at() . name_to_handle_at() name_to_handle_at() dirfd path. handle : struct file_handle { unsigned int handle_bytes; /* f_handle [ ] */ int handle_type; /* [] */ unsigned char f_handle[0]; /* ( ) [] */ }; 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. handle_bytes file_handle : handle_type f_handle open_by_handle_at(). file_handle . fanotify(7) file_handle . flags OR AT_HANDLE_FID AT_HANDLE_MNT_ID_UNIQUE AT_HANDLE_CONNECTABLE AT_EMPTY_PATH AT_SYMLINK_FOLLOW . flags AT_HANDLE_FID ( 6.5) file_handle open_by_handle_at() file_handle . flags AT_HANDLE_MNT_ID_UNIQUE ( 6.12) mount_id 64 statx(2) STATX_MNT_ID_UNIQUE. flags AT_HANDLE_CONNECTABLE ( 6.13) file_handle open_by_handle_at() file_handle /proc/pid/fd/*. AT_HANDLE_FID / AT_EMPTY_PATH. path dirfd . : o path . dirfd. o path dirfd AT_FDCWD path . o path dirfd path dirfd . ( openat(2) " ".) o path flags AT_EMPTY_PATH dirfd AT_FDCWD . mount_id path. /proc/self/mountinfo. open_by_handle_at(). mount_id EOVERFLOW. name_to_handle_at() path . AT_SYMLINK_FOLLOW flags path ( ). name_to_handle_at() . name_to_handle_at() EOVERFLOW handle_bytes. 4.13 NFS . "/" . 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 . CAP_DAC_READ_SEARCH open_by_handle_at(). name_to_handle_at() 0 open_by_handle_at() ( ). -1 errno . name_to_handle_at() open_by_handle_at() openat(2). . name_to_handle_at() : EFAULT path mount_id handle . EINVAL flags . EINVAL handle->handle_bytes MAX_HANDLE_SZ. ENOENT path AT_EMPTY_PATH flags. ENOTDIR dirfd flags AT_EMPTY_PATH path . EOPNOTSUPP . EOVERFLOW handle->handle_bytes . handle->handle_bytes . open_by_handle_at() : EBADF mount_fd . EBADF path dirfd AT_FDCWD . EFAULT handle . EINVAL handle->handle_bytes MAX_HANDLE_SZ . ELOOP handle O_PATH flags. EPERM CAP_DAC_READ_SEARCH. ESTALE handle . . handle AT_HANDLE_FID open_by_handle_at(). handle AT_HANDLE_CONNECTABLE . FreeBSD getfh() fhopen(). . 2.6.39 glibc 2.14. name_to_handle_at() open_by_handle_at(). /proc /sys . open_by_handle_at(). ("") . ESTALE open_by_handle_at(). . NFS NFS. . . path 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 *path; struct file_handle *fhp; if (argc != 2) { fprintf(stderr, "Usage: %s path\n", argv[0]); exit(EXIT_FAILURE); } path = 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, path, 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, path, 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 #include #define streq(...) (strcmp(__VA_ARGS__) == 0) /* 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 && streq(argv[1], "--help")) || 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 3 . . : . 6.18 22 2026 open_by_handle_at(2)