]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - libhandle/handle.c
misc: only build with lto if explicitly enabled
[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 int handle_to_fsfd(void *, char **);
33 static char *path_to_fspath(char *path);
34
35
36 /*
37 * Filesystem Handle -> Open File Descriptor Cache
38 *
39 * Maps filesystem handles to a corresponding open file descriptor for that
40 * filesystem. We need this because we're doing handle operations via xfsctl
41 * and we need to remember the open file descriptor for each filesystem.
42 */
43
44 struct fdhash {
45 int fsfd;
46 char fsh[FSIDSIZE];
47 struct fdhash *fnxt;
48 char fspath[MAXPATHLEN];
49 };
50
51 static struct fdhash *fdhash_head = NULL;
52
53 void
54 fshandle_destroy(void)
55 {
56 struct fdhash *nexth;
57 struct fdhash *h = fdhash_head;
58
59 while (h) {
60 nexth = h->fnxt;
61 free(h);
62 h = nexth;
63 }
64 fdhash_head = NULL;
65 }
66
67 int
68 path_to_fshandle(
69 char *path, /* input, path to convert */
70 void **fshanp, /* output, pointer to data */
71 size_t *fshlen) /* output, size of returned data */
72 {
73 int result;
74 int fd;
75 comarg_t obj;
76 struct fdhash *fdhp;
77 char *tmppath;
78 char *fspath;
79
80 fspath = path_to_fspath(path);
81 if (fspath == NULL)
82 return -1;
83
84 fd = open(fspath, O_RDONLY);
85 if (fd < 0)
86 return -1;
87
88 obj.path = path;
89 result = obj_to_handle(fspath, fd, XFS_IOC_PATH_TO_FSHANDLE,
90 obj, fshanp, fshlen);
91 if (result < 0) {
92 close(fd);
93 return result;
94 }
95
96 if (handle_to_fsfd(*fshanp, &tmppath) >= 0) {
97 /* this filesystem is already in the cache */
98 close(fd);
99 } else {
100 /* new filesystem. add it to the cache */
101 fdhp = malloc(sizeof(struct fdhash));
102 if (fdhp == NULL) {
103 free(*fshanp);
104 close(fd);
105 errno = ENOMEM;
106 return -1;
107 }
108
109 fdhp->fsfd = fd;
110 strncpy(fdhp->fspath, fspath, sizeof(fdhp->fspath));
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 static int
206 handle_to_fsfd(void *hanp, char **path)
207 {
208 struct fdhash *fdhp;
209
210 /*
211 * Look in cache for matching fsid field in the handle
212 * (which is at the start of the handle).
213 * When found return the file descriptor and path that
214 * we have in the cache.
215 */
216 for (fdhp = fdhash_head; fdhp != NULL; fdhp = fdhp->fnxt) {
217 if (memcmp(fdhp->fsh, hanp, FSIDSIZE) == 0) {
218 *path = fdhp->fspath;
219 return fdhp->fsfd;
220 }
221 }
222 errno = EBADF;
223 return -1;
224 }
225
226 static int
227 obj_to_handle(
228 char *fspath,
229 int fsfd,
230 unsigned int opcode,
231 comarg_t obj,
232 void **hanp,
233 size_t *hlen)
234 {
235 char hbuf [MAXHANSIZ];
236 int ret;
237 uint32_t handlen;
238 xfs_fsop_handlereq_t hreq;
239
240 if (opcode == XFS_IOC_FD_TO_HANDLE) {
241 hreq.fd = obj.fd;
242 hreq.path = NULL;
243 } else {
244 hreq.fd = 0;
245 hreq.path = obj.path;
246 }
247
248 hreq.oflags = O_LARGEFILE;
249 hreq.ihandle = NULL;
250 hreq.ihandlen = 0;
251 hreq.ohandle = hbuf;
252 hreq.ohandlen = &handlen;
253
254 ret = xfsctl(fspath, fsfd, opcode, &hreq);
255 if (ret)
256 return ret;
257
258 *hanp = malloc(handlen);
259 if (*hanp == NULL) {
260 errno = ENOMEM;
261 return -1;
262 }
263
264 memcpy(*hanp, hbuf, handlen);
265 *hlen = handlen;
266 return 0;
267 }
268
269 int
270 open_by_fshandle(
271 void *fshanp,
272 size_t fshlen,
273 int rw)
274 {
275 int fsfd;
276 char *path;
277 xfs_fsop_handlereq_t hreq;
278
279 if ((fsfd = handle_to_fsfd(fshanp, &path)) < 0)
280 return -1;
281
282 hreq.fd = 0;
283 hreq.path = NULL;
284 hreq.oflags = rw | O_LARGEFILE;
285 hreq.ihandle = fshanp;
286 hreq.ihandlen = fshlen;
287 hreq.ohandle = NULL;
288 hreq.ohandlen = NULL;
289
290 return xfsctl(path, fsfd, XFS_IOC_OPEN_BY_HANDLE, &hreq);
291 }
292
293 int
294 open_by_handle(
295 void *hanp,
296 size_t hlen,
297 int rw)
298 {
299 int fsfd;
300 char *path;
301 xfs_fsop_handlereq_t hreq;
302
303 if ((fsfd = handle_to_fsfd(hanp, &path)) < 0)
304 return -1;
305
306 hreq.fd = 0;
307 hreq.path = NULL;
308 hreq.oflags = rw | O_LARGEFILE;
309 hreq.ihandle = hanp;
310 hreq.ihandlen = hlen;
311 hreq.ohandle = NULL;
312 hreq.ohandlen = NULL;
313
314 return xfsctl(path, fsfd, XFS_IOC_OPEN_BY_HANDLE, &hreq);
315 }
316
317 int
318 readlink_by_handle(
319 void *hanp,
320 size_t hlen,
321 void *buf,
322 size_t bufsiz)
323 {
324 int fd;
325 __u32 buflen = (__u32)bufsiz;
326 char *path;
327 xfs_fsop_handlereq_t hreq;
328
329 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
330 return -1;
331
332 hreq.fd = 0;
333 hreq.path = NULL;
334 hreq.oflags = O_LARGEFILE;
335 hreq.ihandle = hanp;
336 hreq.ihandlen = hlen;
337 hreq.ohandle = buf;
338 hreq.ohandlen = &buflen;
339
340 return xfsctl(path, fd, XFS_IOC_READLINK_BY_HANDLE, &hreq);
341 }
342
343 /*ARGSUSED4*/
344 int
345 attr_multi_by_handle(
346 void *hanp,
347 size_t hlen,
348 void *buf,
349 int rtrvcnt,
350 int flags)
351 {
352 int fd;
353 char *path;
354 xfs_fsop_attrmulti_handlereq_t amhreq;
355
356 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
357 return -1;
358
359 amhreq.hreq.fd = 0;
360 amhreq.hreq.path = NULL;
361 amhreq.hreq.oflags = O_LARGEFILE;
362 amhreq.hreq.ihandle = hanp;
363 amhreq.hreq.ihandlen = hlen;
364 amhreq.hreq.ohandle = NULL;
365 amhreq.hreq.ohandlen = NULL;
366
367 amhreq.opcount = rtrvcnt;
368 amhreq.ops = buf;
369
370 return xfsctl(path, fd, XFS_IOC_ATTRMULTI_BY_HANDLE, &amhreq);
371 }
372
373 int
374 attr_list_by_handle(
375 void *hanp,
376 size_t hlen,
377 void *buf,
378 size_t bufsize,
379 int flags,
380 struct attrlist_cursor *cursor)
381 {
382 int error, fd;
383 char *path;
384 xfs_fsop_attrlist_handlereq_t alhreq;
385
386 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
387 return -1;
388
389 alhreq.hreq.fd = 0;
390 alhreq.hreq.path = NULL;
391 alhreq.hreq.oflags = O_LARGEFILE;
392 alhreq.hreq.ihandle = hanp;
393 alhreq.hreq.ihandlen = hlen;
394 alhreq.hreq.ohandle = NULL;
395 alhreq.hreq.ohandlen = NULL;
396
397 memcpy(&alhreq.pos, cursor, sizeof(alhreq.pos));
398 alhreq.flags = flags;
399 alhreq.buffer = buf;
400 alhreq.buflen = bufsize;
401 /* prevent needless EINVAL from the kernel */
402 if (alhreq.buflen > XFS_XATTR_LIST_MAX)
403 alhreq.buflen = XFS_XATTR_LIST_MAX;
404
405 error = xfsctl(path, fd, XFS_IOC_ATTRLIST_BY_HANDLE, &alhreq);
406
407 memcpy(cursor, &alhreq.pos, sizeof(alhreq.pos));
408 return error;
409 }
410
411 int
412 parents_by_handle(
413 void *hanp,
414 size_t hlen,
415 parent_t *buf,
416 size_t bufsiz,
417 unsigned int *count)
418
419 {
420 errno = EOPNOTSUPP;
421 return -1;
422 }
423
424 int
425 parentpaths_by_handle(
426 void *hanp,
427 size_t hlen,
428 parent_t *buf,
429 size_t bufsiz,
430 unsigned int *count)
431 {
432 errno = EOPNOTSUPP;
433 return -1;
434 }
435
436 int
437 fssetdm_by_handle(
438 void *hanp,
439 size_t hlen,
440 struct fsdmidata *fsdmidata)
441 {
442 int fd;
443 char *path;
444 xfs_fsop_setdm_handlereq_t dmhreq;
445
446 if ((fd = handle_to_fsfd(hanp, &path)) < 0)
447 return -1;
448
449 dmhreq.hreq.fd = 0;
450 dmhreq.hreq.path = NULL;
451 dmhreq.hreq.oflags = O_LARGEFILE;
452 dmhreq.hreq.ihandle = hanp;
453 dmhreq.hreq.ihandlen = hlen;
454 dmhreq.hreq.ohandle = NULL;
455 dmhreq.hreq.ohandlen = NULL;
456
457 dmhreq.data = fsdmidata;
458
459 return xfsctl(path, fd, XFS_IOC_FSSETDM_BY_HANDLE, &dmhreq);
460 }
461
462 /*ARGSUSED1*/
463 void
464 free_handle(
465 void *hanp,
466 size_t hlen)
467 {
468 free(hanp);
469 }