1open_by_handle_at(2)          System Calls 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

LIBRARY

10       Standard C library (libc, -lc)
11

SYNOPSIS

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

DESCRIPTION

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

RETURN VALUE

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

ERRORS

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

VERSIONS

190       FreeBSD has a broadly similar pair of  system  calls  in  the  form  of
191       getfh() and openfh().
192

STANDARDS

194       Linux.
195

HISTORY

197       Linux 2.6.39, glibc 2.14.
198

NOTES

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

EXAMPLES

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

SEE ALSO

488       open(2), libblkid(3), blkid(8), findfs(8), mount(8)
489
490       The  libblkid  and  libmount documentation in the latest util-linux re‐
491       lease at ⟨https://www.kernel.org/pub/linux/utils/util-linux/
492
493
494
495Linux man-pages 6.04              2023-03-30              open_by_handle_at(2)
Impressum