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