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