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