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       EFAULT handle points outside your accessible address space.
171
172       EINVAL handle->handle_bytes is greater than MAX_HANDLE_SZ or  is  equal
173              to zero.
174
175       ELOOP  handle  refers  to a symbolic link, but O_PATH was not specified
176              in flags.
177
178       EPERM  The caller does not have the CAP_DAC_READ_SEARCH capability.
179
180       ESTALE The specified handle is not valid.  This error  will  occur  if,
181              for example, the file has been deleted.
182

VERSIONS

184       These  system calls first appeared in Linux 2.6.39.  Library support is
185       provided in glibc since version 2.14.
186

CONFORMING TO

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

NOTES

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

EXAMPLES

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

SEE ALSO

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

COLOPHON

495       This page is part of release 5.12 of the Linux  man-pages  project.   A
496       description  of  the project, information about reporting bugs, and the
497       latest    version    of    this    page,    can     be     found     at
498       https://www.kernel.org/doc/man-pages/.
499
500
501
502Linux                             2021-03-22              OPEN_BY_HANDLE_AT(2)
Impressum