1OPEN_BY_HANDLE_AT(2)       Linux Programmer's Manual      OPEN_BY_HANDLE_AT(2)
2
3
4

NAME

6       name_to_handle_at, open_by_handle_at - obtain handle for a pathname and
7       open file via a handle
8

SYNOPSIS

10       #define _GNU_SOURCE         /* See feature_test_macros(7) */
11       #include <fcntl.h>
12
13       int name_to_handle_at(int dirfd, const char *pathname,
14                             struct file_handle *handle,
15                             int *mount_id, int flags);
16       int open_by_handle_at(int mount_fd, struct file_handle *handle,
17                             int flags);
18

DESCRIPTION

20       The name_to_handle_at() and open_by_handle_at() system calls split  the
21       functionality  of openat(2) into two parts: name_to_handle_at() returns
22       an opaque handle that corresponds to  a  specified  file;  open_by_han‐
23       dle_at()  opens the file corresponding to a handle returned by a previ‐
24       ous call to name_to_handle_at() and returns an open file descriptor.
25
26   name_to_handle_at()
27       The name_to_handle_at() system call returns a file handle and  a  mount
28       ID  corresponding to the file specified by the dirfd and pathname argu‐
29       ments.  The file handle is returned via the argument handle, which is a
30       pointer to a structure of the following form:
31
32           struct file_handle {
33               unsigned int  handle_bytes;   /* Size of f_handle [in, out] */
34               int           handle_type;    /* Handle type [out] */
35               unsigned char f_handle[0];    /* File identifier (sized by
36                                                caller) [out] */
37           };
38
39       It is the caller's responsibility to allocate the structure with a size
40       large enough to hold the handle returned in f_handle.  Before the call,
41       the  handle_bytes  field should be initialized to contain the allocated
42       size for f_handle.  (The constant MAX_HANDLE_SZ, defined in  <fcntl.h>,
43       specifies  the  maximum  expected  size for a file handle.  It is not a
44       guaranteed upper limit as future filesystems may require  more  space.)
45       Upon  successful  return,  the handle_bytes field is updated to contain
46       the number of bytes actually written to f_handle.
47
48       The caller can discover the required size for the file_handle structure
49       by  making  a call in which handle->handle_bytes is zero; in this case,
50       the call fails with the error EOVERFLOW and handle->handle_bytes is set
51       to indicate the required size; the caller can then use this information
52       to allocate a structure of the correct size (see EXAMPLES below).  Some
53       care  is needed here as EOVERFLOW can also indicate that no file handle
54       is available for this particular name in a filesystem which  does  nor‐
55       mally  support  file-handle lookup.  This case can be detected when the
56       EOVERFLOW error is returned without handle_bytes being increased.
57
58       Other than the use of the handle_bytes field, the caller  should  treat
59       the  file_handle  structure as an opaque data type: the handle_type and
60       f_handle fields are needed only by a subsequent  call  to  open_by_han‐
61       dle_at().
62
63       The  flags argument is a bit mask constructed by ORing together zero or
64       more of AT_EMPTY_PATH and AT_SYMLINK_FOLLOW, described below.
65
66       Together, the pathname and dirfd arguments identify the file for  which
67       a handle is to be obtained.  There are four distinct cases:
68
69       *  If  pathname  is  a nonempty string containing an absolute pathname,
70          then a handle is returned for the file referred to by that pathname.
71          In this case, dirfd is ignored.
72
73       *  If  pathname is a nonempty string containing a relative pathname and
74          dirfd has the special value AT_FDCWD, then pathname  is  interpreted
75          relative  to the current working directory of the caller, and a han‐
76          dle is returned for the file to which it refers.
77
78       *  If pathname is a nonempty string containing a relative pathname  and
79          dirfd  is  a file descriptor referring to a directory, then pathname
80          is interpreted relative to the directory referred to by dirfd, and a
81          handle  is returned for the file to which it refers.  (See openat(2)
82          for an explanation of why "directory file descriptors" are useful.)
83
84       *  If pathname is  an  empty  string  and  flags  specifies  the  value
85          AT_EMPTY_PATH,  then  dirfd can be an open file descriptor referring
86          to any type of file, or AT_FDCWD, meaning the current working direc‐
87          tory, and a handle is returned for the file to which it refers.
88
89       The  mount_id  argument  returns an identifier for the filesystem mount
90       that corresponds to pathname.  This corresponds to the first  field  in
91       one  of  the  records in /proc/self/mountinfo.  Opening the pathname in
92       the fifth field of that record yields a file descriptor for  the  mount
93       point;  that  file  descriptor  can  be  used  in  a subsequent call to
94       open_by_handle_at().  mount_id is returned both for a  successful  call
95       and for a call that results in the error EOVERFLOW.
96
97       By  default, name_to_handle_at() does not dereference pathname if it is
98       a symbolic link, and thus returns a handle for  the  link  itself.   If
99       AT_SYMLINK_FOLLOW is specified in flags, pathname is dereferenced if it
100       is a symbolic link (so that the call returns a handle for the file  re‐
101       ferred to by the link).
102
103       name_to_handle_at()  does  not trigger a mount when the final component
104       of the pathname is an automount point.  When a filesystem supports both
105       file handles and automount points, a name_to_handle_at() call on an au‐
106       tomount point will return with error EOVERFLOW without having increased
107       handle_bytes.  This can happen since Linux 4.13 with NFS when accessing
108       a directory which is on a separate filesystem on the server.   In  this
109       case,  the automount can be triggered by adding a "/" to the end of the
110       pathname.
111
112   open_by_handle_at()
113       The open_by_handle_at() system call opens the file referred to by  han‐
114       dle, a file handle returned by a previous call to name_to_handle_at().
115
116       The mount_fd argument is a file descriptor for any object (file, direc‐
117       tory, etc.)  in the mounted filesystem with  respect  to  which  handle
118       should  be  interpreted.   The special value AT_FDCWD can be specified,
119       meaning the current working directory of the caller.
120
121       The flags argument is as for open(2).  If handle refers to  a  symbolic
122       link, the caller must specify the O_PATH flag, and the symbolic link is
123       not dereferenced; the O_NOFOLLOW flag, if specified, is ignored.
124
125       The caller must  have  the  CAP_DAC_READ_SEARCH  capability  to  invoke
126       open_by_handle_at().
127

RETURN VALUE

129       On  success, name_to_handle_at() returns 0, and open_by_handle_at() re‐
130       turns a file descriptor (a nonnegative integer).
131
132       In the event of an error, both system calls return -1 and set errno  to
133       indicate the error.
134

ERRORS

136       name_to_handle_at()  and  open_by_handle_at() can fail for the same er‐
137       rors as openat(2).  In addition, they can fail with  the  errors  noted
138       below.
139
140       name_to_handle_at() can fail with the following errors:
141
142       EFAULT pathname, mount_id, or handle points outside your accessible ad‐
143              dress space.
144
145       EINVAL flags includes an invalid bit value.
146
147       EINVAL handle->handle_bytes is greater than MAX_HANDLE_SZ.
148
149       ENOENT pathname is an empty string, but AT_EMPTY_PATH was not specified
150              in flags.
151
152       ENOTDIR
153              The file descriptor supplied in dirfd does not refer to a direc‐
154              tory,  and  it  is  not  the  case  that  both  flags   includes
155              AT_EMPTY_PATH and pathname is an empty string.
156
157       EOPNOTSUPP
158              The filesystem does not support decoding of a pathname to a file
159              handle.
160
161       EOVERFLOW
162              The handle->handle_bytes value passed  into  the  call  was  too
163              small.   When this error occurs, handle->handle_bytes is updated
164              to indicate the required size for the handle.
165
166       open_by_handle_at() can fail with the following errors:
167
168       EBADF  mount_fd is not an open file descriptor.
169
170       EBADF  pathname is relative but dirfd is neither AT_FDCWD nor  a  valid
171              file descriptor.
172
173       EFAULT handle points outside your accessible address space.
174
175       EINVAL handle->handle_bytes  is  greater than MAX_HANDLE_SZ or is equal
176              to zero.
177
178       ELOOP  handle refers to a symbolic link, but O_PATH was  not  specified
179              in flags.
180
181       EPERM  The caller does not have the CAP_DAC_READ_SEARCH capability.
182
183       ESTALE The  specified  handle  is not valid.  This error will occur if,
184              for example, the file has been deleted.
185

VERSIONS

187       These system calls first appeared in Linux 2.6.39.  Library support  is
188       provided in glibc since version 2.14.
189

CONFORMING TO

191       These system calls are nonstandard Linux extensions.
192
193       FreeBSD  has  a  broadly  similar  pair  of system calls in the form of
194       getfh() and openfh().
195

NOTES

197       A file handle can be generated in one process using name_to_handle_at()
198       and later used in a different process that calls open_by_handle_at().
199
200       Some filesystem don't support the translation of pathnames to file han‐
201       dles, for example, /proc, /sys, and various network filesystems.
202
203       A file handle may become invalid ("stale") if a file is deleted, or for
204       other  filesystem-specific reasons.  Invalid handles are notified by an
205       ESTALE error from open_by_handle_at().
206
207       These system calls are designed for use  by  user-space  file  servers.
208       For  example,  a user-space NFS server might generate a file handle and
209       pass it to an NFS client.  Later, when the client  wants  to  open  the
210       file,  it could pass the handle back to the server.  This sort of func‐
211       tionality allows a user-space file server to  operate  in  a  stateless
212       fashion with respect to the files it serves.
213
214       If  pathname  refers  to  a  symbolic  link  and flags does not specify
215       AT_SYMLINK_FOLLOW, then name_to_handle_at() returns a  handle  for  the
216       link  (rather than the file to which it refers).  The process receiving
217       the handle can later perform operations on the symbolic  link  by  con‐
218       verting  the handle to a file descriptor using open_by_handle_at() with
219       the O_PATH flag, and then passing the file descriptor as the dirfd  ar‐
220       gument in system calls such as readlinkat(2) and fchownat(2).
221
222   Obtaining a persistent filesystem ID
223       The  mount IDs in /proc/self/mountinfo can be reused as filesystems are
224       unmounted  and  mounted.   Therefore,  the   mount   ID   returned   by
225       name_to_handle_at()  (in  *mount_id) should not be treated as a persis‐
226       tent identifier for the corresponding mounted filesystem.  However,  an
227       application can use the information in the mountinfo record that corre‐
228       sponds to the mount ID to derive a persistent identifier.
229
230       For example, one can use the device name in  the  fifth  field  of  the
231       mountinfo  record  to  search for the corresponding device UUID via the
232       symbolic links in /dev/disks/by-uuid.  (A more comfortable way  of  ob‐
233       taining  the UUID is to use the libblkid(3) library.)  That process can
234       then be reversed, using the UUID to look up the device name,  and  then
235       obtaining  the  corresponding  mount  point,  in  order  to produce the
236       mount_fd argument used by open_by_handle_at().
237

EXAMPLES

239       The two programs below demonstrate the use of  name_to_handle_at()  and
240       open_by_handle_at().   The  first  program (t_name_to_handle_at.c) uses
241       name_to_handle_at() to obtain the file handle and mount ID for the file
242       specified  in  its  command-line  argument; the handle and mount ID are
243       written to standard output.
244
245       The second program (t_open_by_handle_at.c) reads a mount  ID  and  file
246       handle  from  standard  input.   The  program then employs open_by_han‐
247       dle_at() to open the file using that handle.  If an  optional  command-
248       line  argument is supplied, then the mount_fd argument for open_by_han‐
249       dle_at() is obtained by opening the directory named in  that  argument.
250       Otherwise,  mount_fd  is  obtained  by scanning /proc/self/mountinfo to
251       find a record whose mount ID matches the mount ID  read  from  standard
252       input,  and  the  mount  directory  specified in that record is opened.
253       (These programs do not deal with the fact that mount IDs are  not  per‐
254       sistent.)
255
256       The following shell session demonstrates the use of these two programs:
257
258           $ echo 'Can you please think about it?' > cecilia.txt
259           $ ./t_name_to_handle_at cecilia.txt > fh
260           $ ./t_open_by_handle_at < fh
261           open_by_handle_at: Operation not permitted
262           $ sudo ./t_open_by_handle_at < fh      # Need CAP_SYS_ADMIN
263           Read 31 bytes
264           $ rm cecilia.txt
265
266       Now  we delete and (quickly) re-create the file so that it has the same
267       content and (by chance) the  same  inode.   Nevertheless,  open_by_han‐
268       dle_at() recognizes that the original file referred to by the file han‐
269       dle no longer exists.
270
271           $ stat --printf="%i\n" cecilia.txt     # Display inode number
272           4072121
273           $ rm cecilia.txt
274           $ echo 'Can you please think about it?' > cecilia.txt
275           $ stat --printf="%i\n" cecilia.txt     # Check inode number
276           4072121
277           $ sudo ./t_open_by_handle_at < fh
278           open_by_handle_at: Stale NFS file handle
279
280   Program source: t_name_to_handle_at.c
281
282       #define _GNU_SOURCE
283       #include <sys/types.h>
284       #include <sys/stat.h>
285       #include <fcntl.h>
286       #include <stdio.h>
287       #include <stdlib.h>
288       #include <unistd.h>
289       #include <errno.h>
290       #include <string.h>
291
292       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
293                               } while (0)
294
295       int
296       main(int argc, char *argv[])
297       {
298           struct file_handle *fhp;
299           int mount_id, fhsize, flags, dirfd;
300           char *pathname;
301
302           if (argc != 2) {
303               fprintf(stderr, "Usage: %s pathname\n", argv[0]);
304               exit(EXIT_FAILURE);
305           }
306
307           pathname = argv[1];
308
309           /* Allocate file_handle structure. */
310
311           fhsize = sizeof(*fhp);
312           fhp = malloc(fhsize);
313           if (fhp == NULL)
314               errExit("malloc");
315
316           /* Make an initial call to name_to_handle_at() to discover
317              the size required for file handle. */
318
319           dirfd = AT_FDCWD;           /* For name_to_handle_at() calls */
320           flags = 0;                  /* For name_to_handle_at() calls */
321           fhp->handle_bytes = 0;
322           if (name_to_handle_at(dirfd, pathname, fhp,
323                       &mount_id, flags) != -1 || errno != EOVERFLOW) {
324               fprintf(stderr, "Unexpected result from name_to_handle_at()\n");
325               exit(EXIT_FAILURE);
326           }
327
328           /* Reallocate file_handle structure with correct size. */
329
330           fhsize = sizeof(*fhp) + fhp->handle_bytes;
331           fhp = realloc(fhp, fhsize);         /* Copies fhp->handle_bytes */
332           if (fhp == NULL)
333               errExit("realloc");
334
335           /* Get file handle from pathname supplied on command line. */
336
337           if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == -1)
338               errExit("name_to_handle_at");
339
340           /* Write mount ID, file handle size, and file handle to stdout,
341              for later reuse by t_open_by_handle_at.c. */
342
343           printf("%d\n", mount_id);
344           printf("%u %d   ", fhp->handle_bytes, fhp->handle_type);
345           for (int j = 0; j < fhp->handle_bytes; j++)
346               printf(" %02x", fhp->f_handle[j]);
347           printf("\n");
348
349           exit(EXIT_SUCCESS);
350       }
351
352   Program source: t_open_by_handle_at.c
353
354       #define _GNU_SOURCE
355       #include <sys/types.h>
356       #include <sys/stat.h>
357       #include <fcntl.h>
358       #include <limits.h>
359       #include <stdio.h>
360       #include <stdlib.h>
361       #include <unistd.h>
362       #include <string.h>
363
364       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
365                               } while (0)
366
367       /* Scan /proc/self/mountinfo to find the line whose mount ID matches
368          'mount_id'. (An easier way to do this is to install and use the
369          'libmount' library provided by the 'util-linux' project.)
370          Open the corresponding mount path and return the resulting file
371          descriptor. */
372
373       static int
374       open_mount_path_by_id(int mount_id)
375       {
376           char *linep;
377           size_t lsize;
378           char mount_path[PATH_MAX];
379           int mi_mount_id, found;
380           ssize_t nread;
381           FILE *fp;
382
383           fp = fopen("/proc/self/mountinfo", "r");
384           if (fp == NULL)
385               errExit("fopen");
386
387           found = 0;
388           linep = NULL;
389           while (!found) {
390               nread = getline(&linep, &lsize, fp);
391               if (nread == -1)
392                   break;
393
394               nread = sscanf(linep, "%d %*d %*s %*s %s",
395                              &mi_mount_id, mount_path);
396               if (nread != 2) {
397                   fprintf(stderr, "Bad sscanf()\n");
398                   exit(EXIT_FAILURE);
399               }
400
401               if (mi_mount_id == mount_id)
402                   found = 1;
403           }
404           free(linep);
405
406           fclose(fp);
407
408           if (!found) {
409               fprintf(stderr, "Could not find mount point\n");
410               exit(EXIT_FAILURE);
411           }
412
413           return open(mount_path, O_RDONLY);
414       }
415
416       int
417       main(int argc, char *argv[])
418       {
419           struct file_handle *fhp;
420           int mount_id, fd, mount_fd, handle_bytes;
421           ssize_t nread;
422           char buf[1000];
423       #define LINE_SIZE 100
424           char line1[LINE_SIZE], line2[LINE_SIZE];
425           char *nextp;
426
427           if ((argc > 1 && strcmp(argv[1], "--help") == 0) || argc > 2) {
428               fprintf(stderr, "Usage: %s [mount-path]\n", argv[0]);
429               exit(EXIT_FAILURE);
430           }
431
432           /* Standard input contains mount ID and file handle information:
433
434                Line 1: <mount_id>
435                Line 2: <handle_bytes> <handle_type>   <bytes of handle in hex>
436           */
437
438           if ((fgets(line1, sizeof(line1), stdin) == NULL) ||
439                  (fgets(line2, sizeof(line2), stdin) == NULL)) {
440               fprintf(stderr, "Missing mount_id / file handle\n");
441               exit(EXIT_FAILURE);
442           }
443
444           mount_id = atoi(line1);
445
446           handle_bytes = strtoul(line2, &nextp, 0);
447
448           /* Given handle_bytes, we can now allocate file_handle structure. */
449
450           fhp = malloc(sizeof(*fhp) + handle_bytes);
451           if (fhp == NULL)
452               errExit("malloc");
453
454           fhp->handle_bytes = handle_bytes;
455
456           fhp->handle_type = strtoul(nextp, &nextp, 0);
457
458           for (int j = 0; j < fhp->handle_bytes; j++)
459               fhp->f_handle[j] = strtoul(nextp, &nextp, 16);
460
461           /* Obtain file descriptor for mount point, either by opening
462              the pathname specified on the command line, or by scanning
463              /proc/self/mounts to find a mount that matches the 'mount_id'
464              that we received from stdin. */
465
466           if (argc > 1)
467               mount_fd = open(argv[1], O_RDONLY);
468           else
469               mount_fd = open_mount_path_by_id(mount_id);
470
471           if (mount_fd == -1)
472               errExit("opening mount fd");
473
474           /* Open file using handle and mount point. */
475
476           fd = open_by_handle_at(mount_fd, fhp, O_RDONLY);
477           if (fd == -1)
478               errExit("open_by_handle_at");
479
480           /* Try reading a few bytes from the file. */
481
482           nread = read(fd, buf, sizeof(buf));
483           if (nread == -1)
484               errExit("read");
485
486           printf("Read %zd bytes\n", nread);
487
488           exit(EXIT_SUCCESS);
489       }
490

SEE ALSO

492       open(2), libblkid(3), blkid(8), findfs(8), mount(8)
493
494       The libblkid and libmount documentation in the  latest  util-linux  re‐
495       lease at ⟨https://www.kernel.org/pub/linux/utils/util-linux/
496

COLOPHON

498       This  page  is  part of release 5.13 of the Linux man-pages project.  A
499       description of the project, information about reporting bugs,  and  the
500       latest     version     of     this    page,    can    be    found    at
501       https://www.kernel.org/doc/man-pages/.
502
503
504
505Linux                             2021-08-27              OPEN_BY_HANDLE_AT(2)
Impressum