]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - libhandle/handle.c
1e8fe9ac5f10753668a8912211539d351080c3f6
[thirdparty/xfsprogs-dev.git] / libhandle / handle.c
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (c) 1995, 2001-2003, 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include <libgen.h>
8 #include "platform_defs.h"
9 #include "xfs.h"
10 #include "handle.h"
11 #include "parent.h"
12
13 /* just pick a value we know is more than big enough */
14 #define MAXHANSIZ 64
15
16 /*
17 * The actual content of a handle is supposed to be opaque here.
18 * But, to do handle_to_fshandle, we need to know what it is. Sigh.
19 * However we can get by with knowing only that the first 8 bytes of
20 * a file handle are the file system ID, and that a file system handle
21 * consists of only those 8 bytes.
22 */
23
24 #define FSIDSIZE 8
25
26 typedef union {
27 int fd;
28 char *path;
29 } comarg_t;
30
31 static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
32 static char *path_to_fspath(char *path);
33
34
35 /*
36 * Filesystem Handle -> Open File Descriptor Cache
37 *
38 * Maps filesystem handles to a corresponding open file descriptor for that
39 * filesystem. We need this because we're doing handle operations via xfsctl
40 * and we need to remember the open file descriptor for each filesystem.
41 */
42
43 struct fdhash {
44 int fsfd;
45 char fsh[FSIDSIZE];
46 struct fdhash *fnxt;
47 char fspath[MAXPATHLEN];
48 };
49
50 static struct fdhash *fdhash_head = NULL;
51
52 void
53 fshandle_destroy(void)
54 {
55 struct fdhash *nexth;
56 struct fdhash *h = fdhash_head;
57
58 while (h) {
59 nexth = h->fnxt;
60 free(h);
61 h = nexth;
62 }
63 fdhash_head = NULL;
64 }
65
66 int
67 path_to_fshandle(
68 char *path, /* input, path to convert */
69 void **fshanp, /* output, pointer to data */
70 size_t *fshlen) /* output, size of returned data */
71 {
72 int result;
73 int fd;
74 comarg_t obj;
75 struct fdhash *fdhp;
76 char *tmppath;
77 char *fspath;
78
79 fspath = path_to_fspath(path);
80 if (fspath == NULL)
81 return -1;
82
83 fd = open(fspath, O_RDONLY);
84 if (fd < 0)
85 return -1;
86
87 obj.path = path;
88 result = obj_to_handle(fspath, fd, XFS_IOC_PATH_TO_FSHANDLE,
89 obj, fshanp, fshlen);
90 if (result < 0) {
91 close(fd);
92 return result;
93 }
94
95 if (handle_to_fsfd(*fshanp, &tmppath) >= 0) {
96 /* this filesystem is already in the cache */
97 close(fd);
98 } else {
99 /* new filesystem. add it to the cache */
100 fdhp = malloc(sizeof(struct fdhash));
101 if (fdhp == NULL) {
102 free(*fshanp);
103 close(fd);
104 errno = ENOMEM;
105 return -1;
106 }
107
108 fdhp->fsfd = fd;
109 strncpy(fdhp->fspath, fspath, sizeof(fdhp->fspath) - 1);
110 fdhp->fspath[sizeof(fdhp->fspath) - 1] = 0;
111 memcpy(fdhp->fsh, *fshanp, FSIDSIZE);
112
113 fdhp->fnxt = fdhash_head;
114 fdhash_head = fdhp;
115 }
116
117 return result;
118 }
119
120 int
121 path_to_handle(
122 char *path, /* input, path to convert */
123 void **hanp, /* output, pointer to data */
124 size_t *hlen) /* output, size of returned data */
125 {
126 int fd;
127 int result;
128 comarg_t obj;
129 char *fspath;
130
131 fspath = path_to_fspath(path);
132 if (fspath == NULL)
133 return -1;
134
135 fd = open(fspath, O_RDONLY);
136 if (fd < 0)
137 return -1;
138
139 obj.path = path;
140 result = obj_to_handle(fspath, fd, XFS_IOC_PATH_TO_HANDLE,
141 obj, hanp, hlen);
142 close(fd);
143 return result;
144 }
145
146 /* Given a path, return a suitable "fspath" for use in obtaining
147 * an fd for xfsctl calls. For regular files and directories the
148 * input path is sufficient. For other types the parent directory
149 * is used to avoid issues with opening dangling symlinks and
150 * potentially blocking in an open on a named pipe. Also
151 * symlinks to files on other filesystems would be a problem,
152 * since an fd would be obtained for the wrong fs.
153 */
154 static char *
155 path_to_fspath(char *path)
156 {
157 static char dirpath[MAXPATHLEN];
158 struct stat statbuf;
159
160 if (lstat(path, &statbuf) != 0)
161 return NULL;
162
163 if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode))
164 return path;
165
166 strncpy(dirpath, path, MAXPATHLEN);
167 dirpath[MAXPATHLEN-1] = '\0';
168 return dirname(dirpath);
169 }
170
171 int
172 fd_to_handle (
173 int fd, /* input, file descriptor */
174 void **hanp, /* output, pointer to data */
175 size_t *hlen) /* output, size of returned data */
176 {
177 comarg_t obj;
178
179 obj.fd = fd;
180 return obj_to_handle(NULL, fd, XFS_IOC_FD_TO_HANDLE, obj, hanp, hlen);
181 }
182
183
184 int
185 handle_to_fshandle(
186 void *hanp,
187 size_t hlen,
188 void **fshanp,
189 size_t *fshlen)
190 {
191 if (hlen < FSIDSIZE) {
192 errno = EINVAL;
193 return -1;
194 }
195 *fshanp = malloc(FSIDSIZE);
196 if (*fshanp == NULL) {
197 errno = ENOMEM;
198 return -1;
199 }
200 *fshlen = FSIDSIZE;
201 memcpy(*fshanp, hanp, FSIDSIZE);
202 return 0;
203 }
204
205 int
206 handle_to_fsfd(
207 void *hanp,
208 char **path)
209 {
210 struct fdhash *fdhp;
211
212 /*
213 * Look in cache for matching fsid field in the handle
214 * (which is at the start of the handle).
215 * When found return the file descriptor and path that
216 * we have in the cache.
217 */
218 for (fdhp = fdhash_head; fdhp != NULL; fdhp = fdhp->fnxt) {
219 if (memcmp(fdhp->fsh, hanp, FSIDSIZE) == 0) {
220 *path = fdhp->fspath;
221 return fdhp->fsfd;
222 }
223 }
224 errno = EBADF;
225 return -1;
226 }
227
228 static int
229 obj_to_handle(
230 char *fspath,
231 int fsfd,
232 unsigned int opcode,
233 comarg_t obj,
234 void **hanp,
235 size_t *hlen)
236 {
237 char hbuf [MAXHANSIZ];
238 int ret;
239 uint32_t handlen = 0;
240 struct xfs_fsop_handlereq hreq = { };
241
242 memset(hbuf, 0, MAXHANSIZ);
243
244 if (opcode == XFS_IOC_FD_TO_HANDLE) {
245 hreq.fd = obj.fd;
246 hreq.path = NULL;
247 } else {
248 hreq.fd = 0;
249 hreq.path = obj.path;
250 }
251
252 hreq.oflags = O_LARGEFILE;
253 hreq.ihandle = NULL;
254 hreq.ihandlen = 0;
255 hreq.ohandle = hbuf;
256 hreq.ohandlen = &handlen;
257
258 ret = xfsctl(fspath, fsfd, opcode, &hreq);
259 if (ret)
260 return ret;
261
262 *hanp = malloc(handlen);
263 if (*hanp == NULL) {
264 errno = ENOMEM;
265 return -1;
266 }
267
268 memcpy(*hanp, hbuf, handlen);
269 *hlen = handlen;
270 return 0;
271 }
272
273 int
274 open_by_fshandle(
275 void *fshanp,
276 size_t fshlen,
277 int rw)
278 {
279 int fsfd;
280 char *path;
281 struct xfs_fsop_handlereq hreq = { };
282
283 if ((fsfd = handle_to_fsfd(fshanp, &path)) < 0)
284 return -1;
285
286 hreq.fd = 0;
287 hreq.path = NULL;
288 hreq.oflags = rw | O_LARGEFILE;
289 hreq.ihandle = fshanp;
290 hreq.ihandlen = fshlen;
291 hreq.ohandle = NULL;
292 hreq.ohandlen = NULL;
293
294 return xfsctl(path, fsfd, XFS_IOC_OPEN_BY_HANDLE, &hreq);
295 }
296
297 int
298 open_by_handle(
299 void *hanp,
300 size_t hlen,
301 int rw)
302 {
303 int fsfd;
304 char *path;
305 xfs_fsop_handlereq_t hreq;
306
307 if ((fsfd = handle_to_fsfd(hanp, &path)) < 0)
308 return -1;
309
310 hreq.fd = 0;
311 hreq.path = NULL;
312 hreq.oflags = rw | O_LARGEFILE;
313 hreq.ihandle = hanp;
314 hreq.ihandlen = hlen;
315 hreq.ohandle = NULL;
316 hreq.ohandlen = NULL;
317
318 return xfsctl(path, fsfd, XFS_IOC_OPEN_BY_HANDLE, &hreq);
319 }
320
321 int
322 readlink_by_handle(
323 void *hanp,
324 size_t hlen,
325 void *buf,
326 size_t bufsiz)
327 {
328 int fd;
329 __u32 buflen = (__u32)bufsiz;
330 char *path;
331 xfs_fsop_handlereq_t hreq;
332
333 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
334 return -1;
335
336 hreq.fd = 0;
337 hreq.path = NULL;
338 hreq.oflags = O_LARGEFILE;
339 hreq.ihandle = hanp;
340 hreq.ihandlen = hlen;
341 hreq.ohandle = buf;
342 hreq.ohandlen = &buflen;
343
344 return xfsctl(path, fd, XFS_IOC_READLINK_BY_HANDLE, &hreq);
345 }
346
347 /*ARGSUSED4*/
348 int
349 attr_multi_by_handle(
350 void *hanp,
351 size_t hlen,
352 void *buf,
353 int rtrvcnt,
354 int flags)
355 {
356 int fd;
357 char *path;
358 xfs_fsop_attrmulti_handlereq_t amhreq;
359
360 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
361 return -1;
362
363 amhreq.hreq.fd = 0;
364 amhreq.hreq.path = NULL;
365 amhreq.hreq.oflags = O_LARGEFILE;
366 amhreq.hreq.ihandle = hanp;
367 amhreq.hreq.ihandlen = hlen;
368 amhreq.hreq.ohandle = NULL;
369 amhreq.hreq.ohandlen = NULL;
370
371 amhreq.opcount = rtrvcnt;
372 amhreq.ops = buf;
373
374 return xfsctl(path, fd, XFS_IOC_ATTRMULTI_BY_HANDLE, &amhreq);
375 }
376
377 int
378 attr_list_by_handle(
379 void *hanp,
380 size_t hlen,
381 void *buf,
382 size_t bufsize,
383 int flags,
384 struct attrlist_cursor *cursor)
385 {
386 int error, fd;
387 char *path;
388 struct xfs_fsop_attrlist_handlereq alhreq = { };
389
390 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
391 return -1;
392
393 alhreq.hreq.fd = 0;
394 alhreq.hreq.path = NULL;
395 alhreq.hreq.oflags = O_LARGEFILE;
396 alhreq.hreq.ihandle = hanp;
397 alhreq.hreq.ihandlen = hlen;
398 alhreq.hreq.ohandle = NULL;
399 alhreq.hreq.ohandlen = NULL;
400
401 memcpy(&alhreq.pos, cursor, sizeof(alhreq.pos));
402 alhreq.flags = flags;
403 alhreq.buffer = buf;
404 alhreq.buflen = bufsize;
405 /* prevent needless EINVAL from the kernel */
406 if (alhreq.buflen > XFS_XATTR_LIST_MAX)
407 alhreq.buflen = XFS_XATTR_LIST_MAX;
408
409 error = xfsctl(path, fd, XFS_IOC_ATTRLIST_BY_HANDLE, &alhreq);
410
411 memcpy(cursor, &alhreq.pos, sizeof(alhreq.pos));
412 return error;
413 }
414
415 int
416 parents_by_handle(
417 void *hanp,
418 size_t hlen,
419 parent_t *buf,
420 size_t bufsiz,
421 unsigned int *count)
422
423 {
424 errno = EOPNOTSUPP;
425 return -1;
426 }
427
428 int
429 parentpaths_by_handle(
430 void *hanp,
431 size_t hlen,
432 parent_t *buf,
433 size_t bufsiz,
434 unsigned int *count)
435 {
436 errno = EOPNOTSUPP;
437 return -1;
438 }
439
440 /* Deprecated in kernel */
441 int
442 fssetdm_by_handle(
443 void *hanp,
444 size_t hlen,
445 struct fsdmidata *fsdmidata)
446 {
447 errno = EOPNOTSUPP;
448 return -1;
449 }
450
451 /*ARGSUSED1*/
452 void
453 free_handle(
454 void *hanp,
455 size_t hlen)
456 {
457 free(hanp);
458 }