]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libfrog/paths.c
xfsprogs: Release v6.8.0
[thirdparty/xfsprogs-dev.git] / libfrog / paths.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
3d93ccb7 2/*
d9ebd5d7 3 * Copyright (c) 2005-2006 Silicon Graphics, Inc.
da23017d 4 * All Rights Reserved.
3d93ccb7
NS
5 */
6
7#include <paths.h>
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13#include <sys/types.h>
14#include <sys/stat.h>
42b4c8e8 15#include "paths.h"
6b803e5a 16#include "input.h"
59f1f2a6 17#include "projects.h"
dbe764ee 18#include <mntent.h>
6a23747d 19#include <limits.h>
3d93ccb7 20
2a1888c5
NS
21extern char *progname;
22
3d93ccb7 23int fs_count;
bb80e3d6 24int xfs_fs_count;
3d93ccb7
NS
25struct fs_path *fs_table;
26struct fs_path *fs_path;
8fc372bc 27
3d93ccb7 28char *mtab_file;
8fc372bc 29#define PROC_MOUNTS "/proc/self/mounts"
3d93ccb7 30
bd3c6f44 31static int
2c6a405a 32fs_device_number(
bd3c6f44 33 const char *name,
2c6a405a
AE
34 dev_t *devnum)
35{
f594a0d1 36 struct stat sbuf;
2c6a405a 37
f594a0d1 38 if (stat(name, &sbuf) < 0)
bd3c6f44
AE
39 return errno;
40 /*
41 * We want to match st_rdev if the path provided is a device
42 * special file. Otherwise we are looking for the the
43 * device id for the containing filesystem, in st_dev.
44 */
45 if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode))
46 *devnum = sbuf.st_rdev;
47 else
48 *devnum = sbuf.st_dev;
2c6a405a 49
bd3c6f44 50 return 0;
2c6a405a
AE
51}
52
80a3572b
AE
53/*
54 * Find the FS table entry for the given path. The "flags" argument
55 * is a mask containing FS_MOUNT_POINT or FS_PROJECT_PATH (or both)
56 * to indicate the type of table entry sought.
b97815a0
BD
57 * fs_table_lookup() finds the fs table entry for the filesystem hosting
58 * the file represented in the "dir" argument. To compare against actual
59 * mount point entries, use fs_table_lookup_mount() instead.
80a3572b 60 */
3d93ccb7
NS
61struct fs_path *
62fs_table_lookup(
63 const char *dir,
64 uint flags)
65{
3d93ccb7 66 uint i;
bd3c6f44 67 dev_t dev = 0;
3d93ccb7 68
bd3c6f44 69 if (fs_device_number(dir, &dev))
3d93ccb7 70 return NULL;
80a3572b 71
3d93ccb7 72 for (i = 0; i < fs_count; i++) {
ee0aba2e 73 if (flags && !(flags & fs_table[i].fs_flags))
3d93ccb7 74 continue;
80a3572b 75 if (fs_table[i].fs_datadev == dev)
3d93ccb7
NS
76 return &fs_table[i];
77 }
78 return NULL;
79}
80
938f7b70
DW
81static struct fs_path *
82__fs_table_lookup_mount(
83 const char *dir,
84 const char *blkdev)
b97815a0
BD
85{
86 uint i;
b97815a0 87 char rpath[PATH_MAX];
be66eb8e 88 char dpath[PATH_MAX];
b97815a0 89
8aee0483
ES
90 if (!dir && !blkdev)
91 return NULL;
92
938f7b70 93 if (dir && !realpath(dir, dpath))
b97815a0 94 return NULL;
938f7b70 95 if (blkdev && !realpath(blkdev, dpath))
be66eb8e 96 return NULL;
b97815a0
BD
97
98 for (i = 0; i < fs_count; i++) {
99 if (fs_table[i].fs_flags != FS_MOUNT_POINT)
100 continue;
938f7b70
DW
101 if (dir && !realpath(fs_table[i].fs_dir, rpath))
102 continue;
103 if (blkdev && !realpath(fs_table[i].fs_name, rpath))
b97815a0 104 continue;
be66eb8e 105 if (strcmp(rpath, dpath) == 0)
b97815a0
BD
106 return &fs_table[i];
107 }
108 return NULL;
109}
110
938f7b70
DW
111/*
112 * Find the FS table entry describing an actual mount for the given path.
113 * Unlike fs_table_lookup(), fs_table_lookup_mount() compares the "dir"
114 * argument to actual mount point entries in the table. Accordingly, it
115 * will find matches only if the "dir" argument is indeed mounted.
116 */
117struct fs_path *
118fs_table_lookup_mount(
119 const char *dir)
120{
121 return __fs_table_lookup_mount(dir, NULL);
122}
123
124/*
125 * Find the FS table entry describing an actual mount for the block device.
126 * Unlike fs_table_lookup(), fs_table_lookup_blkdev() compares the "bdev"
127 * argument to actual mount point names in the table. Accordingly, it
128 * will find matches only if the "bdev" argument is indeed mounted.
129 */
130struct fs_path *
131fs_table_lookup_blkdev(
132 const char *bdev)
133{
134 return __fs_table_lookup_mount(NULL, bdev);
135}
136
3d93ccb7
NS
137static int
138fs_table_insert(
139 char *dir,
140 uint prid,
141 uint flags,
142 char *fsname,
143 char *fslog,
144 char *fsrt)
145{
3d93ccb7 146 dev_t datadev, logdev, rtdev;
8e96bcac 147 struct fs_path *tmp_fs_table;
1f4e1f7f 148 int error;
3d93ccb7
NS
149
150 datadev = logdev = rtdev = 0;
1f4e1f7f
AE
151 error = fs_device_number(dir, &datadev);
152 if (error)
153 goto out_nodev;
154 if (fslog) {
155 error = fs_device_number(fslog, &logdev);
156 if (error)
157 goto out_nodev;
158 }
159 if (fsrt) {
160 error = fs_device_number(fsrt, &rtdev);
161 if (error)
162 goto out_nodev;
163 }
164
29647c8d
BD
165 if (!platform_test_xfs_path(dir))
166 flags |= FS_FOREIGN;
167
1f4e1f7f
AE
168 /*
169 * Make copies of the directory and data device path.
170 * The log device and real-time device, if non-null,
171 * are already the result of strdup() calls so we
172 * don't need to duplicate those. In fact, this
173 * function is assumed to "consume" both of those
174 * pointers, meaning if an error occurs they will
175 * both get freed.
176 */
177 error = ENOMEM;
178 dir = strdup(dir);
179 if (!dir)
180 goto out_nodev;
181 fsname = strdup(fsname);
182 if (!fsname)
183 goto out_noname;
3d93ccb7 184
8e96bcac
AE
185 tmp_fs_table = realloc(fs_table, sizeof(fs_path_t) * (fs_count + 1));
186 if (!tmp_fs_table)
1f4e1f7f 187 goto out_norealloc;
8e96bcac 188 fs_table = tmp_fs_table;
3d93ccb7 189
bb80e3d6
BD
190 /* Put foreign filesystems at the end, xfs filesystems at the front */
191 if (flags & FS_FOREIGN || fs_count == 0) {
192 fs_path = &fs_table[fs_count];
193 } else {
194 /* move foreign fs entries down, insert new one just before */
195 memmove(&fs_table[xfs_fs_count + 1], &fs_table[xfs_fs_count],
196 sizeof(fs_path_t)*(fs_count - xfs_fs_count));
197 fs_path = &fs_table[xfs_fs_count];
bb80e3d6 198 }
3d93ccb7
NS
199 fs_path->fs_dir = dir;
200 fs_path->fs_prid = prid;
201 fs_path->fs_flags = flags;
202 fs_path->fs_name = fsname;
203 fs_path->fs_log = fslog;
204 fs_path->fs_rt = fsrt;
205 fs_path->fs_datadev = datadev;
206 fs_path->fs_logdev = logdev;
207 fs_path->fs_rtdev = rtdev;
208 fs_count++;
753a71d4
EG
209 if (!(flags & FS_FOREIGN))
210 xfs_fs_count++;
1f4e1f7f 211
3d93ccb7 212 return 0;
1f4e1f7f
AE
213
214out_norealloc:
215 free(fsname);
216out_noname:
217 free(dir);
218out_nodev:
219 /* "Consume" fslog and fsrt even if there's an error */
220 free(fslog);
221 free(fsrt);
222
223 return error;
3d93ccb7
NS
224}
225
ce1abf95
DW
226/* Remove all the cached entries in the fs table. */
227void
228fs_table_destroy(void)
229{
230 int i;
231 struct fs_path *fsp;
232
233 for (i = 0, fsp = fs_table; i < fs_count; i++, fsp++) {
234 free(fsp->fs_name);
235 free(fsp->fs_dir);
236 free(fsp->fs_log);
237 free(fsp->fs_rt);
238 }
239
240 fs_count = 0;
241 xfs_fs_count = 0;
242 free(fs_table);
243 fs_table = NULL;
244}
245
044c6663
AE
246/*
247 * Table iteration (cursor-based) interfaces
248 */
249
250/*
251 * Initialize an fs_table cursor. If a directory path is supplied,
252 * the cursor is set up to appear as though the table contains only
253 * a single entry which represents the directory specified.
254 * Otherwise it is set up to prepare for visiting all entries in the
255 * global table, starting with the first. "flags" can be either
256 * FS_MOUNT_POINT or FS_PROJECT_PATH to limit what type of entries
257 * will be selected by fs_cursor_next_entry(). 0 can be used as a
258 * wild card (selecting either type).
259 */
260void
261fs_cursor_initialise(
262 char *dir,
263 uint flags,
264 fs_cursor_t *cur)
265{
266 fs_path_t *path;
267
268 memset(cur, 0, sizeof(*cur));
269 if (dir) {
270 if ((path = fs_table_lookup(dir, flags)) == NULL)
271 return;
272 cur->local = *path;
273 cur->count = 1;
274 cur->table = &cur->local;
275 } else {
276 cur->count = fs_count;
277 cur->table = fs_table;
278 }
279 cur->flags = flags;
280}
281
282/*
283 * Use the cursor to find the next entry in the table having the
284 * type specified by the cursor's "flags" field.
285 */
286struct fs_path *
287fs_cursor_next_entry(
288 fs_cursor_t *cur)
289{
290 while (cur->index < cur->count) {
291 fs_path_t *next = &cur->table[cur->index++];
292
293 if (!cur->flags || (cur->flags & next->fs_flags))
294 return next;
295 }
296 return NULL;
297}
298
1f4e1f7f
AE
299/*
300 * Determines whether the "logdev" or "rtdev" mount options are
301 * present for the given mount point. If so, the value for each (a
302 * device path) is returned in the pointers whose addresses are
303 * provided. The pointers are assigned NULL for an option not
304 * present. Note that the path buffers returned are allocated
305 * dynamically and it is the caller's responsibility to free them.
306 */
7e760c13 307static int
3d93ccb7
NS
308fs_extract_mount_options(
309 struct mntent *mnt,
310 char **logp,
311 char **rtp)
312{
2a1888c5 313 char *fslog, *fsrt;
3d93ccb7 314
83d4957b
DW
315 /*
316 * Extract log device and realtime device from mount options.
317 *
318 * Note: the glibc hasmntopt implementation requires that the
319 * character in mnt_opts immediately after the search string
320 * must be a NULL ('\0'), a comma (','), or an equals ('=').
321 * Therefore we cannot search for 'logdev=' directly.
322 */
323 if ((fslog = hasmntopt(mnt, "logdev")) && fslog[6] == '=')
3d93ccb7 324 fslog += 7;
83d4957b 325 if ((fsrt = hasmntopt(mnt, "rtdev")) && fsrt[5] == '=')
3d93ccb7 326 fsrt += 6;
3d93ccb7
NS
327
328 /* Do this only after we've finished processing mount options */
329 if (fslog) {
19e78652 330 fslog = strndup(fslog, strcspn(fslog, " ,"));
7e760c13
AE
331 if (!fslog)
332 goto out_nomem;
3d93ccb7
NS
333 }
334 if (fsrt) {
19e78652
AE
335 fsrt = strndup(fsrt, strcspn(fsrt, " ,"));
336 if (!fsrt) {
19e78652 337 free(fslog);
7e760c13 338 goto out_nomem;
19e78652 339 }
3d93ccb7 340 }
3d93ccb7
NS
341 *logp = fslog;
342 *rtp = fsrt;
7e760c13
AE
343
344 return 0;
345
346out_nomem:
347 *logp = NULL;
348 *rtp = NULL;
349 fprintf(stderr, _("%s: unable to extract mount options for \"%s\"\n"),
350 progname, mnt->mnt_dir);
351 return ENOMEM;
3d93ccb7
NS
352}
353
050a7f1f
ES
354/*
355 * If *path is NULL, initialize the fs table with all xfs mount points in mtab
356 * If *path is specified, search for that path in mtab
ed350fc6
ES
357 *
358 * Everything - path, devices, and mountpoints - are boiled down to realpath()
359 * for comparison, but fs_table is populated with what comes from getmntent.
050a7f1f 360 */
3d93ccb7
NS
361static int
362fs_table_initialise_mounts(
363 char *path)
364{
365 struct mntent *mnt;
366 FILE *mtp;
1f4e1f7f 367 char *fslog, *fsrt;
d9ebd5d7 368 int error, found;
ed350fc6 369 char rpath[PATH_MAX], rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX];
d9ebd5d7
NS
370
371 error = found = 0;
1f4e1f7f 372 fslog = fsrt = NULL;
3d93ccb7 373
8fc372bc
NS
374 if (!mtab_file) {
375 mtab_file = PROC_MOUNTS;
376 if (access(mtab_file, R_OK) != 0)
377 mtab_file = MOUNTED;
378 }
3d93ccb7
NS
379
380 if ((mtp = setmntent(mtab_file, "r")) == NULL)
381 return ENOENT;
382
050a7f1f 383 /* Use realpath to resolve symlinks, relative paths, etc */
6a23747d 384 if (path)
ed350fc6
ES
385 if (!realpath(path, rpath))
386 return errno;
6a23747d 387
3d93ccb7 388 while ((mnt = getmntent(mtp)) != NULL) {
5ca4d781
IK
389 if (!strcmp(mnt->mnt_type, "autofs"))
390 continue;
ed350fc6
ES
391 if (!realpath(mnt->mnt_dir, rmnt_dir))
392 continue;
393 if (!realpath(mnt->mnt_fsname, rmnt_fsname))
394 continue;
395
050a7f1f 396 if (path &&
ed350fc6
ES
397 ((strcmp(rpath, rmnt_dir) != 0) &&
398 (strcmp(rpath, rmnt_fsname) != 0)))
3d93ccb7 399 continue;
7e760c13
AE
400 if (fs_extract_mount_options(mnt, &fslog, &fsrt))
401 continue;
6bca0145 402 (void) fs_table_insert(mnt->mnt_dir, 0, FS_MOUNT_POINT,
1f4e1f7f 403 mnt->mnt_fsname, fslog, fsrt);
050a7f1f 404 if (path) {
6bca0145 405 found = 1;
3d93ccb7 406 break;
6bca0145 407 }
3d93ccb7
NS
408 }
409 endmntent(mtp);
050a7f1f
ES
410
411 if (path && !found)
412 error = ENXIO;
1f4e1f7f 413
3d93ccb7
NS
414 return error;
415}
416
3d93ccb7
NS
417/*
418 * Given a directory, match it up to a filesystem mount point.
419 */
420static struct fs_path *
421fs_mount_point_from_path(
422 const char *dir)
423{
424 fs_cursor_t cursor;
425 fs_path_t *fs;
bd3c6f44 426 dev_t dev = 0;
3d93ccb7 427
bd3c6f44 428 if (fs_device_number(dir, &dev))
3d93ccb7 429 return NULL;
3d93ccb7
NS
430
431 fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
432 while ((fs = fs_cursor_next_entry(&cursor))) {
bd3c6f44 433 if (fs->fs_datadev == dev)
3d93ccb7
NS
434 break;
435 }
436 return fs;
437}
438
0900efe4 439static void
044c6663
AE
440fs_table_insert_mount(
441 char *mount)
442{
443 int error;
444
445 error = fs_table_initialise_mounts(mount);
7e760c13 446 if (error)
044c6663
AE
447 fprintf(stderr, _("%s: cannot setup path for mount %s: %s\n"),
448 progname, mount, strerror(error));
044c6663
AE
449}
450
3d93ccb7
NS
451static int
452fs_table_initialise_projects(
9448c82e
PR
453 char *project,
454 bool all_mps_initialised)
3d93ccb7
NS
455{
456 fs_project_path_t *path;
457 fs_path_t *fs;
458 prid_t prid = 0;
3d93ccb7
NS
459 int error = 0, found = 0;
460
461 if (project)
462 prid = prid_from_string(project);
463
464 setprpathent();
465 while ((path = getprpathent()) != NULL) {
466 if (project && prid != path->pp_prid)
467 continue;
1f4e1f7f
AE
468 fs = fs_mount_point_from_path(path->pp_pathname);
469 if (!fs) {
9448c82e
PR
470 if (all_mps_initialised)
471 fprintf(stderr,
472 _("%s: cannot find mount point for path `%s': %s\n"), progname,
473 path->pp_pathname, strerror(errno));
3d93ccb7 474 continue;
16875fa6 475 }
6bca0145 476 (void) fs_table_insert(path->pp_pathname, path->pp_prid,
1f4e1f7f
AE
477 FS_PROJECT_PATH, fs->fs_name,
478 NULL, NULL);
6bca0145
AE
479 if (project) {
480 found = 1;
3d93ccb7 481 break;
6bca0145 482 }
3d93ccb7
NS
483 }
484 endprpathent();
485
6bca0145 486 if (project && !found)
3d93ccb7 487 error = ENOENT;
1f4e1f7f 488
3d93ccb7
NS
489 return error;
490}
491
0900efe4 492static void
044c6663 493fs_table_insert_project(
9448c82e
PR
494 char *project,
495 bool all_mps_initialised)
3d93ccb7
NS
496{
497 int error;
498
9448c82e 499 error = fs_table_initialise_projects(project, all_mps_initialised);
7e760c13 500 if (error)
044c6663
AE
501 fprintf(stderr, _("%s: cannot setup path for project %s: %s\n"),
502 progname, project, strerror(error));
3d93ccb7
NS
503}
504
0900efe4
AE
505/*
506 * Initialize fs_table to contain the given set of mount points and
507 * projects. If mount_count is zero, mounts is ignored and the
508 * table is populated with mounted filesystems. If project_count is
509 * zero, projects is ignored and the table is populated with all
510 * projects defined in the projects file.
511 */
3d93ccb7 512void
0900efe4
AE
513fs_table_initialise(
514 int mount_count,
515 char *mounts[],
516 int project_count,
517 char *projects[])
3d93ccb7 518{
0900efe4
AE
519 int error;
520 int i;
3d93ccb7 521
0900efe4
AE
522 if (mount_count) {
523 for (i = 0; i < mount_count; i++)
524 fs_table_insert_mount(mounts[i]);
525 } else {
526 error = fs_table_initialise_mounts(NULL);
527 if (error)
7e760c13 528 goto out_error;
0900efe4
AE
529 }
530 if (project_count) {
531 for (i = 0; i < project_count; i++)
9448c82e 532 fs_table_insert_project(projects[i], mount_count == 0);
0900efe4 533 } else {
9448c82e 534 error = fs_table_initialise_projects(NULL, mount_count == 0);
0900efe4 535 if (error)
7e760c13 536 goto out_error;
3d93ccb7 537 }
0900efe4
AE
538
539 return;
540
7e760c13 541out_error:
0900efe4
AE
542 fprintf(stderr, _("%s: cannot initialise path table: %s\n"),
543 progname, strerror(error));
3d93ccb7
NS
544}
545
c10023e4 546int
02b26a6d 547fs_table_insert_project_path(
1f4e1f7f 548 char *dir,
02b26a6d
AM
549 prid_t prid)
550{
551 fs_path_t *fs;
02b26a6d
AM
552 int error = 0;
553
1f4e1f7f
AE
554 fs = fs_mount_point_from_path(dir);
555 if (fs)
556 error = fs_table_insert(dir, prid, FS_PROJECT_PATH,
557 fs->fs_name, NULL, NULL);
558 else
02b26a6d
AM
559 error = ENOENT;
560
c10023e4 561 return error;
02b26a6d 562}