]>
Commit | Line | Data |
---|---|---|
a827d5c6 MK |
1 | .\" Copyright (c) 2014 by Michael Kerrisk <mtk.manpages@gmail.com> |
2 | .\" | |
3 | .\" %%%LICENSE_START(VERBATIM) | |
4 | .\" Permission is granted to make and distribute verbatim copies of this | |
5 | .\" manual provided the copyright notice and this permission notice are | |
6 | .\" preserved on all copies. | |
7 | .\" | |
8 | .\" Permission is granted to copy and distribute modified versions of this | |
9 | .\" manual under the conditions for verbatim copying, provided that the | |
10 | .\" entire resulting derived work is distributed under the terms of a | |
11 | .\" permission notice identical to this one. | |
12 | .\" | |
13 | .\" Since the Linux kernel and libraries are constantly changing, this | |
14 | .\" manual page may be incorrect or out-of-date. The author(s) assume no | |
15 | .\" responsibility for errors or omissions, or for damages resulting from | |
16 | .\" the use of the information contained herein. The author(s) may not | |
17 | .\" have taken the same level of care in the production of this manual, | |
18 | .\" which is licensed free of charge, as they might when working | |
19 | .\" professionally. | |
20 | .\" | |
21 | .\" Formatted or processed versions of this manual, if unaccompanied by | |
22 | .\" the source, must acknowledge the copyright and authors of this work. | |
23 | .\" %%%LICENSE_END | |
24 | .\" | |
ad5b45ab | 25 | .TH OPEN_BY_HANDLE_AT 2 2014-06-13 "Linux" "Linux Programmer's Manual" |
a827d5c6 MK |
26 | .SH NAME |
27 | name_to_handle_at, open_by_handle_at \- obtain handle | |
28 | for a pathname and open file via a handle | |
29 | .SH SYNOPSIS | |
30 | .nf | |
237f3803 | 31 | .BR "#define _GNU_SOURCE" " /* See feature_test_macros(7) */" |
a827d5c6 MK |
32 | .B #include <sys/types.h> |
33 | .B #include <sys/stat.h> | |
34 | .B #include <fcntl.h> | |
dbfe9c70 | 35 | .PP |
a827d5c6 MK |
36 | .BI "int name_to_handle_at(int " dirfd ", const char *" pathname , |
37 | .BI " struct file_handle *" handle , | |
fb968310 | 38 | .BI " int *" mount_id ", int " flags ); |
dbfe9c70 | 39 | .PP |
fb968310 | 40 | .BI "int open_by_handle_at(int " mount_fd ", struct file_handle *" handle , |
a827d5c6 MK |
41 | .BI " int " flags ); |
42 | .fi | |
43 | .SH DESCRIPTION | |
44 | The | |
45 | .BR name_to_handle_at () | |
46 | and | |
47 | .BR open_by_handle_at () | |
48 | system calls split the functionality of | |
49 | .BR openat (2) | |
50 | into two parts: | |
51 | .BR name_to_handle_at () | |
52 | returns an opaque handle that corresponds to a specified file; | |
53 | .BR open_by_handle_at () | |
54 | opens the file corresponding to a handle returned by a previous call to | |
55 | .BR name_to_handle_at () | |
56 | and returns an open file descriptor. | |
fb968310 MK |
57 | .\" |
58 | .\" | |
a827d5c6 MK |
59 | .SS name_to_handle_at() |
60 | The | |
61 | .BR name_to_handle_at () | |
62 | system call returns a file handle and a mount ID corresponding to | |
fb968310 MK |
63 | the file specified by the |
64 | .IR dirfd | |
65 | and | |
66 | .IR pathname | |
67 | arguments. | |
a827d5c6 MK |
68 | The file handle is returned via the argument |
69 | .IR handle , | |
70 | which is a pointer to a structure of the following form: | |
71 | ||
72 | .in +4n | |
73 | .nf | |
74 | struct file_handle { | |
75 | unsigned int handle_bytes; /* Size of f_handle [in, out] */ | |
76 | int handle_type; /* Handle type [out] */ | |
77 | unsigned char f_handle[0]; /* File identifier (sized by | |
78 | caller) [out] */ | |
79 | }; | |
80 | .fi | |
81 | .in | |
82 | .PP | |
83 | It is the caller's responsibility to allocate the structure | |
84 | with a size large enough to hold the handle returned in | |
85 | .IR f_handle . | |
86 | Before the call, the | |
87 | .IR handle_bytes | |
88 | field should be initialized to contain the allocated size for | |
89 | .IR f_handle . | |
90 | (The constant | |
91 | .BR MAX_HANDLE_SZ , | |
92 | defined in | |
93 | .IR <fcntl.h> , | |
94 | specifies the maximum possible size for a file handle.) | |
95 | Upon successful return, the | |
96 | .IR handle_bytes | |
97 | field is updated to contain the number of bytes actually written to | |
98 | .IR f_handle . | |
99 | ||
100 | The caller can discover the required size for the | |
101 | .I file_handle | |
102 | structure by making a call in which | |
103 | .IR handle->handle_bytes | |
104 | is zero; | |
105 | in this case, the call fails with the error | |
106 | .BR EOVERFLOW | |
107 | and | |
108 | .IR handle->handle_bytes | |
109 | is set to indicate the required size; | |
110 | the caller can then use this information to allocate a structure | |
111 | of the correct size (see EXAMPLE below). | |
112 | ||
113 | Other than the use of the | |
114 | .IR handle_bytes | |
115 | field, the caller should treat the | |
116 | .IR file_handle | |
117 | structure as an opaque data type: the | |
118 | .IR handle_type | |
119 | and | |
120 | .IR f_handle | |
121 | fields are needed only by a subsequent call to | |
122 | .BR open_by_handle_at (). | |
123 | ||
b2fe7451 MK |
124 | The |
125 | .I flags | |
126 | argument is a bit mask constructed by ORing together zero or more of | |
127 | .BR AT_EMPTY_PATH | |
128 | and | |
129 | .BR AT_SYMLINK_FOLLOW , | |
130 | described below. | |
131 | ||
fb968310 | 132 | Together, the |
a827d5c6 | 133 | .I pathname |
fb968310 | 134 | and |
a827d5c6 | 135 | .I dirfd |
e2199ccb | 136 | arguments identify the file for which a handle is to be obtained. |
fb968310 MK |
137 | There are four distinct cases: |
138 | .IP * 3 | |
139 | If | |
140 | .I pathname | |
141 | is a nonempty string containing an absolute pathname, | |
142 | then a handle is returned for the file referred to by that pathname. | |
143 | In this case, | |
144 | .IR dirfd | |
145 | is ignored. | |
146 | .IP * | |
147 | If | |
148 | .I pathname | |
149 | is a nonempty string containing a relative pathname and | |
150 | .IR dirfd | |
a827d5c6 MK |
151 | has the special value |
152 | .BR AT_FDCWD , | |
153 | then | |
154 | .I pathname | |
fb968310 MK |
155 | is interpreted relative to the current working directory of the caller, |
156 | and a handle is returned for the file to which it refers. | |
157 | .IP * | |
158 | If | |
159 | .I pathname | |
160 | is a nonempty string containing a relative pathname and | |
a827d5c6 | 161 | .IR dirfd |
fb968310 | 162 | is a file descriptor referring to a directory, then |
a827d5c6 | 163 | .I pathname |
fb968310 MK |
164 | is interpreted relative to the directory referred to by |
165 | .IR dirfd , | |
166 | and a handle is returned for the file to which it refers. | |
167 | (See | |
f29d0171 | 168 | .BR openat (2) |
fb968310 MK |
169 | for an explanation of why "directory file descriptors" are useful.) |
170 | .IP * | |
a827d5c6 MK |
171 | If |
172 | .I pathname | |
fb968310 MK |
173 | is an empty string and |
174 | .I flags | |
175 | specifies the value | |
176 | .BR AT_EMPTY_PATH , | |
177 | then | |
178 | .IR dirfd | |
179 | can be an open file descriptor referring to any type of file, | |
180 | or | |
181 | .BR AT_FDCWD , | |
182 | meaning the current working directory, | |
183 | and a handle is returned for the file to which it refers. | |
184 | .PP | |
a827d5c6 | 185 | The |
fb968310 | 186 | .I mount_id |
a827d5c6 MK |
187 | argument returns an identifier for the filesystem |
188 | mount that corresponds to | |
189 | .IR pathname . | |
190 | This corresponds to the first field in one of the records in | |
191 | .IR /proc/self/mountinfo . | |
192 | Opening the pathname in the fifth field of that record yields a file | |
193 | descriptor for the mount point; | |
194 | that file descriptor can be used in a subsequent call to | |
195 | .BR open_by_handle_at (). | |
196 | ||
a827d5c6 MK |
197 | By default, |
198 | .BR name_to_handle_at () | |
199 | does not dereference | |
200 | .I pathname | |
b2fe7451 MK |
201 | if it is a symbolic link, and thus returns a handle for the link itself. |
202 | If | |
a827d5c6 | 203 | .B AT_SYMLINK_FOLLOW |
b2fe7451 MK |
204 | is specified in |
205 | .IR flags , | |
a827d5c6 | 206 | .I pathname |
b2fe7451 MK |
207 | is dereferenced if it is a symbolic link |
208 | (so that the call returns a handle for the file referred to by the link). | |
a827d5c6 MK |
209 | .SS open_by_handle_at() |
210 | The | |
211 | .BR open_by_handle_at () | |
212 | system call opens the file referred to by | |
213 | .IR handle , | |
214 | a file handle returned by a previous call to | |
215 | .BR name_to_handle_at (). | |
216 | ||
217 | The | |
fb968310 MK |
218 | .IR mount_fd |
219 | argument is a file descriptor for any object (file, directory, etc.) | |
220 | in the mounted filesystem with respect to which | |
a827d5c6 MK |
221 | .IR handle |
222 | should be interpreted. | |
223 | The special value | |
224 | .B AT_FDCWD | |
225 | can be specified, meaning the current working directory of the caller. | |
226 | ||
227 | The | |
228 | .I flags | |
229 | argument | |
230 | is as for | |
231 | .BR open (2). | |
fb968310 MK |
232 | If |
233 | .I handle | |
234 | refers to a symbolic link, the caller must specify the | |
235 | .B O_PATH | |
97236fae | 236 | flag, and the symbolic link is not dereferenced; the |
fb968310 | 237 | .B O_NOFOLLOW |
97236fae MK |
238 | flag, if specified, is ignored. |
239 | ||
a827d5c6 MK |
240 | The caller must have the |
241 | .B CAP_DAC_READ_SEARCH | |
242 | capability to invoke | |
243 | .BR open_by_handle_at (). | |
244 | .SH RETURN VALUE | |
245 | On success, | |
246 | .BR name_to_handle_at () | |
247 | returns 0, | |
248 | and | |
249 | .BR open_by_handle_at () | |
250 | returns a nonnegative file descriptor. | |
251 | ||
252 | In the event of an error, both system calls return \-1 and set | |
253 | .I errno | |
254 | to indicate the cause of the error. | |
255 | .SH ERRORS | |
256 | .BR name_to_handle_at () | |
257 | and | |
258 | .BR open_by_handle_at () | |
259 | can fail for the same errors as | |
3be5b709 | 260 | .BR openat (2). |
a827d5c6 MK |
261 | In addition, they can fail with the errors noted below. |
262 | ||
263 | .BR name_to_handle_at () | |
264 | can fail with the following errors: | |
265 | .TP | |
e2199ccb MK |
266 | .B EFAULT |
267 | .IR pathname , | |
268 | .IR mount_id , | |
269 | or | |
270 | .IR handle | |
271 | points outside your accessible address space. | |
272 | .TP | |
a827d5c6 MK |
273 | .B EINVAL |
274 | .I flags | |
275 | includes an invalid bit value. | |
276 | .TP | |
277 | .B EINVAL | |
85ad779b | 278 | .IR handle\->handle_bytes |
a827d5c6 MK |
279 | is greater than |
280 | .BR MAX_HANDLE_SZ . | |
281 | .TP | |
fb968310 MK |
282 | .B ENOENT |
283 | .I pathname | |
284 | is an empty string, but | |
285 | .BR AT_EMPTY_PATH | |
286 | was not specified in | |
287 | .IR flags . | |
288 | .TP | |
a827d5c6 MK |
289 | .B ENOTDIR |
290 | The file descriptor supplied in | |
291 | .I dirfd | |
292 | does not refer to a directory, | |
b2fe7451 | 293 | and it is not the case that both |
a827d5c6 MK |
294 | .I flags |
295 | includes | |
296 | .BR AT_EMPTY_PATH | |
297 | and | |
298 | .I pathname | |
299 | is an empty string. | |
300 | .TP | |
301 | .B EOPNOTSUPP | |
302 | The filesystem does not support decoding of a pathname to a file handle. | |
303 | .TP | |
304 | .B EOVERFLOW | |
305 | The | |
306 | .I handle->handle_bytes | |
307 | value passed into the call was too small. | |
308 | When this error occurs, | |
309 | .I handle->handle_bytes | |
310 | is updated to indicate the required size for the handle. | |
311 | .\" | |
312 | .\" | |
313 | .PP | |
314 | .BR open_by_handle_at () | |
315 | can fail with the following errors: | |
316 | .TP | |
317 | .B EBADF | |
fb968310 | 318 | .IR mount_fd |
a827d5c6 MK |
319 | is not an open file descriptor. |
320 | .TP | |
e2199ccb MK |
321 | .B EFAULT |
322 | .IR handle | |
323 | points outside your accessible address space. | |
324 | .TP | |
a827d5c6 MK |
325 | .B EINVAL |
326 | .I handle->handle_bytes | |
327 | is greater than | |
328 | .BR MAX_HANDLE_SZ | |
329 | or is equal to zero. | |
330 | .TP | |
fb968310 | 331 | .B ELOOP |
fb968310 MK |
332 | .I handle |
333 | refers to a symbolic link, but | |
334 | .B O_PATH | |
335 | was not specified in | |
336 | .IR flags . | |
337 | .TP | |
a827d5c6 MK |
338 | .B EPERM |
339 | The caller does not have the | |
340 | .BR CAP_DAC_READ_SEARCH | |
341 | capability. | |
342 | .TP | |
343 | .B ESTALE | |
344 | The specified | |
345 | .I handle | |
e2199ccb MK |
346 | is not valid. |
347 | This error will occur if, for example, the file has been deleted. | |
a827d5c6 MK |
348 | .SH VERSIONS |
349 | These system calls first appeared in Linux 2.6.39. | |
39c98c7d | 350 | Library support is provided in glibc since version 2.14. |
a827d5c6 MK |
351 | .SH CONFORMING TO |
352 | These system calls are nonstandard Linux extensions. | |
22956293 MK |
353 | |
354 | FreeBSD has a broadly similar pair of system calls in the form of | |
355 | .BR getfh () | |
356 | and | |
357 | .BR openfh (). | |
a827d5c6 MK |
358 | .SH NOTES |
359 | A file handle can be generated in one process using | |
360 | .BR name_to_handle_at () | |
361 | and later used in a different process that calls | |
362 | .BR open_by_handle_at (). | |
363 | ||
e2199ccb | 364 | Some filesystem don't support the translation of pathnames to |
3ad5d1b9 | 365 | file handles, for example, |
e2199ccb MK |
366 | .IR /proc , |
367 | .IR /sys , | |
368 | and various network filesystems. | |
fb968310 MK |
369 | |
370 | A file handle may become invalid ("stale") if a file is deleted, | |
371 | or for other filesystem-specific reasons. | |
372 | Invalid handles are notified by an | |
373 | .B ESTALE | |
374 | error from | |
e2199ccb | 375 | .BR open_by_handle_at (). |
fb968310 | 376 | |
a827d5c6 MK |
377 | These system calls are designed for use by user-space file servers. |
378 | For example, a user-space NFS server might generate a file handle | |
379 | and pass it to an NFS client. | |
380 | Later, when the client wants to open the file, | |
381 | it could pass the handle back to the server. | |
382 | .\" https://lwn.net/Articles/375888/ | |
383 | .\" "Open by handle" - Jonathan Corbet, 2010-02-23 | |
384 | This sort of functionality allows a user-space file server to operate in | |
385 | a stateless fashion with respect to the files it serves. | |
386 | ||
fb968310 MK |
387 | If |
388 | .I pathname | |
389 | refers to a symbolic link and | |
390 | .IR flags | |
391 | does not specify | |
392 | .BR AT_SYMLINK_FOLLOW , | |
393 | then | |
a827d5c6 | 394 | .BR name_to_handle_at () |
fb968310 | 395 | returns a handle for the link (rather than the file to which it refers). |
a827d5c6 MK |
396 | .\" commit bcda76524cd1fa32af748536f27f674a13e56700 |
397 | The process receiving the handle can later perform operations | |
398 | on the symbolic link by converting the handle to a file descriptor using | |
399 | .BR open_by_handle_at () | |
fb968310 MK |
400 | with the |
401 | .BR O_PATH | |
402 | flag, and then passing the file descriptor as the | |
a827d5c6 MK |
403 | .IR dirfd |
404 | argument in system calls such as | |
405 | .BR readlinkat (2) | |
406 | and | |
407 | .BR fchownat (2). | |
408 | .SS Obtaining a persistent filesystem ID | |
409 | The mount IDs in | |
410 | .IR /proc/self/mountinfo | |
411 | can be reused as filesystems are unmounted and mounted. | |
412 | Therefore, the mount ID returned by | |
b2fe7451 | 413 | .BR name_to_handle_at () |
a827d5c6 | 414 | (in |
fb968310 | 415 | .IR *mount_id ) |
a827d5c6 MK |
416 | should not be treated as a persistent identifier |
417 | for the corresponding mounted filesystem. | |
418 | However, an application can use the information in the | |
419 | .I mountinfo | |
420 | record that corresponds to the mount ID | |
421 | to derive a persistent identifier. | |
422 | ||
423 | For example, one can use the device name in the fifth field of the | |
424 | .I mountinfo | |
425 | record to search for the corresponding device UUID via the symbolic links in | |
426 | .IR /dev/disks/by-uuid . | |
427 | (A more comfortable way of obtaining the UUID is to use the | |
428 | .\" e.g., http://stackoverflow.com/questions/6748429/using-libblkid-to-find-uuid-of-a-partition | |
429 | .BR libblkid (3) | |
fb968310 | 430 | library.) |
a827d5c6 MK |
431 | That process can then be reversed, |
432 | using the UUID to look up the device name, | |
433 | and then obtaining the corresponding mount point, | |
434 | in order to produce the | |
fb968310 | 435 | .IR mount_fd |
a827d5c6 | 436 | argument used by |
e2199ccb | 437 | .BR open_by_handle_at (). |
a827d5c6 MK |
438 | .SH EXAMPLE |
439 | The two programs below demonstrate the use of | |
440 | .BR name_to_handle_at () | |
441 | and | |
442 | .BR open_by_handle_at (). | |
443 | The first program | |
444 | .RI ( t_name_to_handle_at.c ) | |
445 | uses | |
446 | .BR name_to_handle_at () | |
447 | to obtain the file handle and mount ID | |
448 | for the file specified in its command-line argument; | |
e2199ccb | 449 | the handle and mount ID are written to standard output. |
a827d5c6 MK |
450 | |
451 | The second program | |
452 | .RI ( t_open_by_handle_at.c ) | |
453 | reads a mount ID and file handle from standard input. | |
454 | The program then employs | |
455 | .BR open_by_handle_at () | |
456 | to open the file using that handle. | |
457 | If an optional command-line argument is supplied, then the | |
fb968310 | 458 | .IR mount_fd |
a827d5c6 MK |
459 | argument for |
460 | .BR open_by_handle_at () | |
461 | is obtained by opening the directory named in that argument. | |
462 | Otherwise, | |
fb968310 | 463 | .IR mount_fd |
a827d5c6 MK |
464 | is obtained by scanning |
465 | .IR /proc/self/mountinfo | |
466 | to find a record whose mount ID matches the mount ID | |
467 | read from standard input, | |
468 | and the mount directory specified in that record is opened. | |
469 | (These programs do not deal with the fact that mount IDs are not persistent.) | |
470 | ||
471 | The following shell session demonstrates the use of these two programs: | |
472 | ||
473 | .in +4n | |
474 | .nf | |
b2fe7451 | 475 | $ \fBecho 'Can you please think about it?' > cecilia.txt\fP |
a827d5c6 MK |
476 | $ \fB./t_name_to_handle_at cecilia.txt > fh\fP |
477 | $ \fB./t_open_by_handle_at < fh\fP | |
478 | open_by_handle_at: Operation not permitted | |
479 | $ \fBsudo ./t_open_by_handle_at < fh\fP # Need CAP_SYS_ADMIN | |
b2fe7451 | 480 | Read 31 bytes |
a827d5c6 MK |
481 | $ \fBrm cecilia.txt\fP |
482 | .fi | |
483 | .in | |
484 | ||
fb968310 MK |
485 | Now we delete and (quickly) re-create the file so that |
486 | it has the same content and (by chance) the same inode. | |
487 | Nevertheless, | |
a827d5c6 | 488 | .BR open_by_handle_at () |
269ad843 MK |
489 | .\" Christoph Hellwig: That's why the file handles contain a generation |
490 | .\" counter that gets incremented in this case. | |
fb968310 MK |
491 | recognizes that the original file referred to by the file handle |
492 | no longer exists. | |
a827d5c6 MK |
493 | |
494 | .in +4n | |
495 | .nf | |
3ad5d1b9 | 496 | $ \fBstat \-\-printf="%i\\n" cecilia.txt\fP # Display inode number |
a827d5c6 | 497 | 4072121 |
fb968310 | 498 | $ \fBrm cecilia.txt\fP |
b2fe7451 | 499 | $ \fBecho 'Can you please think about it?' > cecilia.txt\fP |
886f35af | 500 | $ \fBstat \-\-printf="%i\\n" cecilia.txt\fP # Check inode number |
a827d5c6 MK |
501 | 4072121 |
502 | $ \fBsudo ./t_open_by_handle_at < fh\fP | |
503 | open_by_handle_at: Stale NFS file handle | |
504 | .fi | |
505 | .in | |
506 | .SS Program source: t_name_to_handle_at.c | |
507 | \& | |
508 | .nf | |
509 | #define _GNU_SOURCE | |
510 | #include <sys/types.h> | |
511 | #include <sys/stat.h> | |
512 | #include <fcntl.h> | |
513 | #include <stdio.h> | |
514 | #include <stdlib.h> | |
515 | #include <unistd.h> | |
516 | #include <errno.h> | |
517 | #include <string.h> | |
518 | ||
519 | #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \\ | |
520 | } while (0) | |
521 | ||
522 | int | |
523 | main(int argc, char *argv[]) | |
524 | { | |
525 | struct file_handle *fhp; | |
3ad5d1b9 MK |
526 | int mount_id, fhsize, flags, dirfd, j; |
527 | char *pathname; | |
a827d5c6 | 528 | |
3ad5d1b9 | 529 | if (argc != 2) { |
a827d5c6 MK |
530 | fprintf(stderr, "Usage: %s pathname\\n", argv[0]); |
531 | exit(EXIT_FAILURE); | |
532 | } | |
533 | ||
3ad5d1b9 MK |
534 | pathname = argv[1]; |
535 | ||
a827d5c6 MK |
536 | /* Allocate file_handle structure */ |
537 | ||
3ad5d1b9 | 538 | fhsize = sizeof(*fhp); |
a827d5c6 MK |
539 | fhp = malloc(fhsize); |
540 | if (fhp == NULL) | |
541 | errExit("malloc"); | |
542 | ||
543 | /* Make an initial call to name_to_handle_at() to discover | |
544 | the size required for file handle */ | |
545 | ||
3ad5d1b9 MK |
546 | dirfd = AT_FDCWD; /* For name_to_handle_at() calls */ |
547 | flags = 0; /* For name_to_handle_at() calls */ | |
a827d5c6 | 548 | fhp\->handle_bytes = 0; |
3ad5d1b9 MK |
549 | if (name_to_handle_at(dirfd, pathname, fhp, |
550 | &mount_id, flags) != \-1 || errno != EOVERFLOW) { | |
a827d5c6 MK |
551 | fprintf(stderr, "Unexpected result from name_to_handle_at()\\n"); |
552 | exit(EXIT_FAILURE); | |
553 | } | |
554 | ||
555 | /* Reallocate file_handle structure with correct size */ | |
556 | ||
557 | fhsize = sizeof(struct file_handle) + fhp\->handle_bytes; | |
558 | fhp = realloc(fhp, fhsize); /* Copies fhp\->handle_bytes */ | |
559 | if (fhp == NULL) | |
560 | errExit("realloc"); | |
561 | ||
562 | /* Get file handle from pathname supplied on command line */ | |
563 | ||
3ad5d1b9 | 564 | if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == \-1) |
a827d5c6 MK |
565 | errExit("name_to_handle_at"); |
566 | ||
567 | /* Write mount ID, file handle size, and file handle to stdout, | |
568 | for later reuse by t_open_by_handle_at.c */ | |
569 | ||
3ad5d1b9 MK |
570 | printf("%d\\n", mount_id); |
571 | printf("%d %d ", fhp\->handle_bytes, fhp\->handle_type); | |
572 | for (j = 0; j < fhp\->handle_bytes; j++) | |
573 | printf(" %02x", fhp\->f_handle[j]); | |
574 | printf("\\n"); | |
a827d5c6 MK |
575 | |
576 | exit(EXIT_SUCCESS); | |
577 | } | |
578 | .fi | |
579 | .SS Program source: t_open_by_handle_at.c | |
580 | \& | |
581 | .nf | |
582 | #define _GNU_SOURCE | |
583 | #include <sys/types.h> | |
584 | #include <sys/stat.h> | |
585 | #include <fcntl.h> | |
586 | #include <limits.h> | |
587 | #include <stdio.h> | |
588 | #include <stdlib.h> | |
589 | #include <unistd.h> | |
590 | #include <string.h> | |
591 | ||
592 | #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \\ | |
593 | } while (0) | |
594 | ||
595 | /* Scan /proc/self/mountinfo to find the line whose mount ID matches | |
596 | \(aqmount_id\(aq. (An easier way to do this is to install and use the | |
597 | \(aqlibmount\(aq library provided by the \(aqutil\-linux\(aq project.) | |
598 | Open the corresponding mount path and return the resulting file | |
599 | descriptor. */ | |
600 | ||
601 | static int | |
602 | open_mount_path_by_id(int mount_id) | |
603 | { | |
604 | char *linep; | |
605 | size_t lsize; | |
606 | char mount_path[PATH_MAX]; | |
3ad5d1b9 MK |
607 | int mi_mount_id, found; |
608 | ssize_t nread; | |
a827d5c6 MK |
609 | FILE *fp; |
610 | ||
3ad5d1b9 | 611 | fp = fopen("/proc/self/mountinfo", "r"); |
a827d5c6 MK |
612 | if (fp == NULL) |
613 | errExit("fopen"); | |
614 | ||
3ad5d1b9 MK |
615 | found = 0; |
616 | linep = NULL; | |
617 | while (!found) { | |
a827d5c6 MK |
618 | nread = getline(&linep, &lsize, fp); |
619 | if (nread == \-1) | |
620 | break; | |
621 | ||
3ad5d1b9 MK |
622 | nread = sscanf(linep, "%d %*d %*s %*s %s", |
623 | &mi_mount_id, mount_path); | |
a827d5c6 MK |
624 | if (nread != 2) { |
625 | fprintf(stderr, "Bad sscanf()\\n"); | |
626 | exit(EXIT_FAILURE); | |
627 | } | |
628 | ||
3ad5d1b9 MK |
629 | if (mi_mount_id == mount_id) |
630 | found = 1; | |
a827d5c6 | 631 | } |
3ad5d1b9 | 632 | free(linep); |
a827d5c6 MK |
633 | |
634 | fclose(fp); | |
635 | ||
3ad5d1b9 | 636 | if (!found) { |
a827d5c6 MK |
637 | fprintf(stderr, "Could not find mount point\\n"); |
638 | exit(EXIT_FAILURE); | |
639 | } | |
640 | ||
3ad5d1b9 | 641 | return open(mount_path, O_RDONLY); |
a827d5c6 MK |
642 | } |
643 | ||
644 | int | |
645 | main(int argc, char *argv[]) | |
646 | { | |
647 | struct file_handle *fhp; | |
3ad5d1b9 | 648 | int mount_id, fd, mount_fd, handle_bytes, j; |
a827d5c6 | 649 | ssize_t nread; |
3ad5d1b9 MK |
650 | char buf[1000]; |
651 | #define LINE_SIZE 100 | |
652 | char line1[LINE_SIZE], line2[LINE_SIZE]; | |
653 | char *nextp; | |
a827d5c6 | 654 | |
3ad5d1b9 MK |
655 | if ((argc > 1 && strcmp(argv[1], "\-\-help") == 0) || argc > 2) { |
656 | fprintf(stderr, "Usage: %s [mount\-path]\\n", argv[0]); | |
a827d5c6 MK |
657 | exit(EXIT_FAILURE); |
658 | } | |
659 | ||
3ad5d1b9 | 660 | /* Standard input contains mount ID and file handle information: |
a827d5c6 | 661 | |
3ad5d1b9 MK |
662 | Line 1: <mount_id> |
663 | Line 2: <handle_bytes> <handle_type> <bytes of handle in hex> | |
664 | */ | |
a827d5c6 | 665 | |
3ad5d1b9 MK |
666 | if ((fgets(line1, sizeof(line1), stdin) == NULL) || |
667 | (fgets(line2, sizeof(line2), stdin) == NULL)) { | |
668 | fprintf(stderr, "Missing mount_id / file handle\\n"); | |
669 | exit(EXIT_FAILURE); | |
670 | } | |
a827d5c6 | 671 | |
3ad5d1b9 MK |
672 | mount_id = atoi(line1); |
673 | ||
674 | handle_bytes = strtoul(line2, &nextp, 0); | |
675 | ||
676 | /* Given handle_bytes, we can now allocate file_handle structure */ | |
677 | ||
678 | fhp = malloc(sizeof(struct file_handle) + handle_bytes); | |
a827d5c6 MK |
679 | if (fhp == NULL) |
680 | errExit("malloc"); | |
681 | ||
3ad5d1b9 MK |
682 | fhp\->handle_bytes = handle_bytes; |
683 | ||
684 | fhp\->handle_type = strtoul(nextp, &nextp, 0); | |
685 | ||
686 | for (j = 0; j < fhp\->handle_bytes; j++) | |
687 | fhp\->f_handle[j] = strtoul(nextp, &nextp, 16); | |
a827d5c6 MK |
688 | |
689 | /* Obtain file descriptor for mount point, either by opening | |
690 | the pathname specified on the command line, or by scanning | |
691 | /proc/self/mounts to find a mount that matches the \(aqmount_id\(aq | |
3ad5d1b9 | 692 | that we received from stdin. */ |
a827d5c6 MK |
693 | |
694 | if (argc > 1) | |
3ad5d1b9 | 695 | mount_fd = open(argv[1], O_RDONLY); |
a827d5c6 MK |
696 | else |
697 | mount_fd = open_mount_path_by_id(mount_id); | |
698 | ||
699 | if (mount_fd == \-1) | |
700 | errExit("opening mount fd"); | |
701 | ||
3ad5d1b9 | 702 | /* Open file using handle and mount point */ |
a827d5c6 MK |
703 | |
704 | fd = open_by_handle_at(mount_fd, fhp, O_RDONLY); | |
705 | if (fd == \-1) | |
706 | errExit("open_by_handle_at"); | |
707 | ||
708 | /* Try reading a few bytes from the file */ | |
709 | ||
3ad5d1b9 | 710 | nread = read(fd, buf, sizeof(buf)); |
a827d5c6 MK |
711 | if (nread == \-1) |
712 | errExit("read"); | |
3ad5d1b9 MK |
713 | |
714 | printf("Read %zd bytes\\n", nread); | |
a827d5c6 MK |
715 | |
716 | exit(EXIT_SUCCESS); | |
717 | } | |
718 | .fi | |
719 | .SH SEE ALSO | |
a827d5c6 MK |
720 | .BR open (2), |
721 | .BR libblkid (3), | |
b2fe7451 MK |
722 | .BR blkid (8), |
723 | .BR findfs (8), | |
a827d5c6 MK |
724 | .BR mount (8) |
725 | ||
726 | The | |
727 | .I libblkid | |
728 | and | |
729 | .I libmount | |
e2199ccb | 730 | documentation in the latest |
a827d5c6 MK |
731 | .I util-linux |
732 | release at | |
5465ae95 | 733 | .UR https://www.kernel.org/pub/linux/utils/util\-linux/ |
a827d5c6 | 734 | .UE |