]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - libxcmd/paths.c
libxcmd: add cvt{int, long} to convert strings to int and long
[thirdparty/xfsprogs-dev.git] / libxcmd / 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 /*
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 */
98 struct fs_path *
99 fs_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
120 static int
121 fs_table_insert(
122 char *dir,
123 uint prid,
124 uint flags,
125 char *fsname,
126 char *fslog,
127 char *fsrt)
128 {
129 dev_t datadev, logdev, rtdev;
130 struct fs_path *tmp_fs_table;
131 int error;
132
133 datadev = logdev = rtdev = 0;
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
148 if (!platform_test_xfs_path(dir))
149 flags |= FS_FOREIGN;
150
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;
167
168 tmp_fs_table = realloc(fs_table, sizeof(fs_path_t) * (fs_count + 1));
169 if (!tmp_fs_table)
170 goto out_norealloc;
171 fs_table = tmp_fs_table;
172
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];
181 }
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++;
192 if (!(flags & FS_FOREIGN))
193 xfs_fs_count++;
194
195 return 0;
196
197 out_norealloc:
198 free(fsname);
199 out_noname:
200 free(dir);
201 out_nodev:
202 /* "Consume" fslog and fsrt even if there's an error */
203 free(fslog);
204 free(fsrt);
205
206 return error;
207 }
208
209 /*
210 * Table iteration (cursor-based) interfaces
211 */
212
213 /*
214 * Initialize an fs_table cursor. If a directory path is supplied,
215 * the cursor is set up to appear as though the table contains only
216 * a single entry which represents the directory specified.
217 * Otherwise it is set up to prepare for visiting all entries in the
218 * global table, starting with the first. "flags" can be either
219 * FS_MOUNT_POINT or FS_PROJECT_PATH to limit what type of entries
220 * will be selected by fs_cursor_next_entry(). 0 can be used as a
221 * wild card (selecting either type).
222 */
223 void
224 fs_cursor_initialise(
225 char *dir,
226 uint flags,
227 fs_cursor_t *cur)
228 {
229 fs_path_t *path;
230
231 memset(cur, 0, sizeof(*cur));
232 if (dir) {
233 if ((path = fs_table_lookup(dir, flags)) == NULL)
234 return;
235 cur->local = *path;
236 cur->count = 1;
237 cur->table = &cur->local;
238 } else {
239 cur->count = fs_count;
240 cur->table = fs_table;
241 }
242 cur->flags = flags;
243 }
244
245 /*
246 * Use the cursor to find the next entry in the table having the
247 * type specified by the cursor's "flags" field.
248 */
249 struct fs_path *
250 fs_cursor_next_entry(
251 fs_cursor_t *cur)
252 {
253 while (cur->index < cur->count) {
254 fs_path_t *next = &cur->table[cur->index++];
255
256 if (!cur->flags || (cur->flags & next->fs_flags))
257 return next;
258 }
259 return NULL;
260 }
261
262
263 #if defined(HAVE_GETMNTENT)
264 #include <mntent.h>
265
266 /*
267 * Determines whether the "logdev" or "rtdev" mount options are
268 * present for the given mount point. If so, the value for each (a
269 * device path) is returned in the pointers whose addresses are
270 * provided. The pointers are assigned NULL for an option not
271 * present. Note that the path buffers returned are allocated
272 * dynamically and it is the caller's responsibility to free them.
273 */
274 static int
275 fs_extract_mount_options(
276 struct mntent *mnt,
277 char **logp,
278 char **rtp)
279 {
280 char *fslog, *fsrt;
281
282 /*
283 * Extract log device and realtime device from mount options.
284 *
285 * Note: the glibc hasmntopt implementation requires that the
286 * character in mnt_opts immediately after the search string
287 * must be a NULL ('\0'), a comma (','), or an equals ('=').
288 * Therefore we cannot search for 'logdev=' directly.
289 */
290 if ((fslog = hasmntopt(mnt, "logdev")) && fslog[6] == '=')
291 fslog += 7;
292 if ((fsrt = hasmntopt(mnt, "rtdev")) && fsrt[5] == '=')
293 fsrt += 6;
294
295 /* Do this only after we've finished processing mount options */
296 if (fslog) {
297 fslog = strndup(fslog, strcspn(fslog, " ,"));
298 if (!fslog)
299 goto out_nomem;
300 }
301 if (fsrt) {
302 fsrt = strndup(fsrt, strcspn(fsrt, " ,"));
303 if (!fsrt) {
304 free(fslog);
305 goto out_nomem;
306 }
307 }
308 *logp = fslog;
309 *rtp = fsrt;
310
311 return 0;
312
313 out_nomem:
314 *logp = NULL;
315 *rtp = NULL;
316 fprintf(stderr, _("%s: unable to extract mount options for \"%s\"\n"),
317 progname, mnt->mnt_dir);
318 return ENOMEM;
319 }
320
321 /*
322 * If *path is NULL, initialize the fs table with all xfs mount points in mtab
323 * If *path is specified, search for that path in mtab
324 *
325 * Everything - path, devices, and mountpoints - are boiled down to realpath()
326 * for comparison, but fs_table is populated with what comes from getmntent.
327 */
328 static int
329 fs_table_initialise_mounts(
330 char *path)
331 {
332 struct mntent *mnt;
333 FILE *mtp;
334 char *fslog, *fsrt;
335 int error, found;
336 char rpath[PATH_MAX], rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX];
337
338 error = found = 0;
339 fslog = fsrt = NULL;
340
341 if (!mtab_file) {
342 mtab_file = PROC_MOUNTS;
343 if (access(mtab_file, R_OK) != 0)
344 mtab_file = MOUNTED;
345 }
346
347 if ((mtp = setmntent(mtab_file, "r")) == NULL)
348 return ENOENT;
349
350 /* Use realpath to resolve symlinks, relative paths, etc */
351 if (path)
352 if (!realpath(path, rpath))
353 return errno;
354
355 while ((mnt = getmntent(mtp)) != NULL) {
356 if (!realpath(mnt->mnt_dir, rmnt_dir))
357 continue;
358 if (!realpath(mnt->mnt_fsname, rmnt_fsname))
359 continue;
360
361 if (path &&
362 ((strcmp(rpath, rmnt_dir) != 0) &&
363 (strcmp(rpath, rmnt_fsname) != 0)))
364 continue;
365 if (fs_extract_mount_options(mnt, &fslog, &fsrt))
366 continue;
367 (void) fs_table_insert(mnt->mnt_dir, 0, FS_MOUNT_POINT,
368 mnt->mnt_fsname, fslog, fsrt);
369 if (path) {
370 found = 1;
371 break;
372 }
373 }
374 endmntent(mtp);
375
376 if (path && !found)
377 error = ENXIO;
378
379 return error;
380 }
381
382 #elif defined(HAVE_GETMNTINFO)
383 #include <sys/mount.h>
384
385 /*
386 * If *path is NULL, initialize the fs table with all xfs mount points in mtab
387 * If *path is specified, search for that path in mtab
388 *
389 * Everything - path, devices, and mountpoints - are boiled down to realpath()
390 * for comparison, but fs_table is populated with what comes from getmntinfo.
391 */
392 static int
393 fs_table_initialise_mounts(
394 char *path)
395 {
396 struct statfs *stats;
397 int i, count, error, found;
398 char rpath[PATH_MAX], rmntfromname[PATH_MAX], rmntonname[PATH_MAX];
399
400 error = found = 0;
401 if ((count = getmntinfo(&stats, 0)) < 0) {
402 fprintf(stderr, _("%s: getmntinfo() failed: %s\n"),
403 progname, strerror(errno));
404 return 0;
405 }
406
407 /* Use realpath to resolve symlinks, relative paths, etc */
408 if (path)
409 if (!realpath(path, rpath))
410 return errno;
411
412 for (i = 0; i < count; i++) {
413 if (!realpath(stats[i].f_mntfromname, rmntfromname))
414 continue;
415 if (!realpath(stats[i].f_mntonname, rmntonname))
416 continue;
417
418 if (path &&
419 ((strcmp(rpath, rmntonname) != 0) &&
420 (strcmp(rpath, rmntfromname) != 0)))
421 continue;
422 /* TODO: external log and realtime device? */
423 (void) fs_table_insert(stats[i].f_mntonname, 0,
424 FS_MOUNT_POINT, stats[i].f_mntfromname,
425 NULL, NULL);
426 if (path) {
427 found = 1;
428 break;
429 }
430 }
431 if (path && !found)
432 error = ENXIO;
433
434 return error;
435 }
436
437 #else
438 # error "How do I extract info about mounted filesystems on this platform?"
439 #endif
440
441 /*
442 * Given a directory, match it up to a filesystem mount point.
443 */
444 static struct fs_path *
445 fs_mount_point_from_path(
446 const char *dir)
447 {
448 fs_cursor_t cursor;
449 fs_path_t *fs;
450 dev_t dev = 0;
451
452 if (fs_device_number(dir, &dev))
453 return NULL;
454
455 fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
456 while ((fs = fs_cursor_next_entry(&cursor))) {
457 if (fs->fs_datadev == dev)
458 break;
459 }
460 return fs;
461 }
462
463 static void
464 fs_table_insert_mount(
465 char *mount)
466 {
467 int error;
468
469 error = fs_table_initialise_mounts(mount);
470 if (error)
471 fprintf(stderr, _("%s: cannot setup path for mount %s: %s\n"),
472 progname, mount, strerror(error));
473 }
474
475 static int
476 fs_table_initialise_projects(
477 char *project)
478 {
479 fs_project_path_t *path;
480 fs_path_t *fs;
481 prid_t prid = 0;
482 int error = 0, found = 0;
483
484 if (project)
485 prid = prid_from_string(project);
486
487 setprpathent();
488 while ((path = getprpathent()) != NULL) {
489 if (project && prid != path->pp_prid)
490 continue;
491 fs = fs_mount_point_from_path(path->pp_pathname);
492 if (!fs) {
493 fprintf(stderr, _("%s: cannot find mount point for path `%s': %s\n"),
494 progname, path->pp_pathname, strerror(errno));
495 continue;
496 }
497 (void) fs_table_insert(path->pp_pathname, path->pp_prid,
498 FS_PROJECT_PATH, fs->fs_name,
499 NULL, NULL);
500 if (project) {
501 found = 1;
502 break;
503 }
504 }
505 endprpathent();
506
507 if (project && !found)
508 error = ENOENT;
509
510 return error;
511 }
512
513 static void
514 fs_table_insert_project(
515 char *project)
516 {
517 int error;
518
519 error = fs_table_initialise_projects(project);
520 if (error)
521 fprintf(stderr, _("%s: cannot setup path for project %s: %s\n"),
522 progname, project, strerror(error));
523 }
524
525 /*
526 * Initialize fs_table to contain the given set of mount points and
527 * projects. If mount_count is zero, mounts is ignored and the
528 * table is populated with mounted filesystems. If project_count is
529 * zero, projects is ignored and the table is populated with all
530 * projects defined in the projects file.
531 */
532 void
533 fs_table_initialise(
534 int mount_count,
535 char *mounts[],
536 int project_count,
537 char *projects[])
538 {
539 int error;
540 int i;
541
542 if (mount_count) {
543 for (i = 0; i < mount_count; i++)
544 fs_table_insert_mount(mounts[i]);
545 } else {
546 error = fs_table_initialise_mounts(NULL);
547 if (error)
548 goto out_error;
549 }
550 if (project_count) {
551 for (i = 0; i < project_count; i++)
552 fs_table_insert_project(projects[i]);
553 } else {
554 error = fs_table_initialise_projects(NULL);
555 if (error)
556 goto out_error;
557 }
558
559 return;
560
561 out_error:
562 fprintf(stderr, _("%s: cannot initialise path table: %s\n"),
563 progname, strerror(error));
564 }
565
566 void
567 fs_table_insert_project_path(
568 char *dir,
569 prid_t prid)
570 {
571 fs_path_t *fs;
572 int error = 0;
573
574 fs = fs_mount_point_from_path(dir);
575 if (fs)
576 error = fs_table_insert(dir, prid, FS_PROJECT_PATH,
577 fs->fs_name, NULL, NULL);
578 else
579 error = ENOENT;
580
581 if (error) {
582 fprintf(stderr, _("%s: cannot setup path for project dir %s: %s\n"),
583 progname, dir, strerror(error));
584 exit(1);
585 }
586 }