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