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