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