]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxcmd/paths.c
xfsprogs: libxcmd: allow 0 as a wildcard fs_table entry type selector
[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>
27#include <xfs/path.h>
28#include <xfs/input.h>
29#include <xfs/project.h>
30
2a1888c5
NS
31extern char *progname;
32
3d93ccb7
NS
33int fs_count;
34struct fs_path *fs_table;
35struct fs_path *fs_path;
8fc372bc 36
3d93ccb7 37char *mtab_file;
8fc372bc 38#define PROC_MOUNTS "/proc/self/mounts"
3d93ccb7 39
bd3c6f44 40static int
2c6a405a 41fs_device_number(
bd3c6f44 42 const char *name,
2c6a405a
AE
43 dev_t *devnum)
44{
45 struct stat64 sbuf;
46
47 if (stat64(name, &sbuf) < 0)
bd3c6f44
AE
48 return errno;
49 /*
50 * We want to match st_rdev if the path provided is a device
51 * special file. Otherwise we are looking for the the
52 * device id for the containing filesystem, in st_dev.
53 */
54 if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode))
55 *devnum = sbuf.st_rdev;
56 else
57 *devnum = sbuf.st_dev;
2c6a405a 58
bd3c6f44 59 return 0;
2c6a405a
AE
60}
61
80a3572b
AE
62/*
63 * Find the FS table entry for the given path. The "flags" argument
64 * is a mask containing FS_MOUNT_POINT or FS_PROJECT_PATH (or both)
65 * to indicate the type of table entry sought.
66 */
3d93ccb7
NS
67struct fs_path *
68fs_table_lookup(
69 const char *dir,
70 uint flags)
71{
3d93ccb7 72 uint i;
bd3c6f44 73 dev_t dev = 0;
3d93ccb7 74
bd3c6f44 75 if (fs_device_number(dir, &dev))
3d93ccb7 76 return NULL;
80a3572b 77
3d93ccb7 78 for (i = 0; i < fs_count; i++) {
ee0aba2e 79 if (flags && !(flags & fs_table[i].fs_flags))
3d93ccb7 80 continue;
80a3572b 81 if (fs_table[i].fs_datadev == dev)
3d93ccb7
NS
82 return &fs_table[i];
83 }
84 return NULL;
85}
86
87static int
88fs_table_insert(
89 char *dir,
90 uint prid,
91 uint flags,
92 char *fsname,
93 char *fslog,
94 char *fsrt)
95{
3d93ccb7 96 dev_t datadev, logdev, rtdev;
8e96bcac 97 struct fs_path *tmp_fs_table;
3d93ccb7
NS
98
99 if (!dir || !fsname)
100 return EINVAL;
101
102 datadev = logdev = rtdev = 0;
bd3c6f44 103 if (fs_device_number(dir, &datadev))
d9ebd5d7 104 return errno;
bd3c6f44 105 if (fslog && fs_device_number(fslog, &logdev))
d9ebd5d7 106 return errno;
bd3c6f44 107 if (fsrt && fs_device_number(fsrt, &rtdev))
d9ebd5d7 108 return errno;
3d93ccb7 109
8e96bcac
AE
110 tmp_fs_table = realloc(fs_table, sizeof(fs_path_t) * (fs_count + 1));
111 if (!tmp_fs_table)
112 return ENOMEM;
113 fs_table = tmp_fs_table;
3d93ccb7
NS
114
115 fs_path = &fs_table[fs_count];
116 fs_path->fs_dir = dir;
117 fs_path->fs_prid = prid;
118 fs_path->fs_flags = flags;
119 fs_path->fs_name = fsname;
120 fs_path->fs_log = fslog;
121 fs_path->fs_rt = fsrt;
122 fs_path->fs_datadev = datadev;
123 fs_path->fs_logdev = logdev;
124 fs_path->fs_rtdev = rtdev;
125 fs_count++;
126 return 0;
3d93ccb7
NS
127}
128
129void
130fs_table_destroy(void)
131{
132 while (--fs_count >= 0) {
133 free(fs_table[fs_count].fs_name);
134 if (fs_table[fs_count].fs_log)
135 free(fs_table[fs_count].fs_log);
136 if (fs_table[fs_count].fs_rt)
137 free(fs_table[fs_count].fs_rt);
138 free(fs_table[fs_count].fs_dir);
139 }
140 if (fs_table)
141 free(fs_table);
142 fs_table = NULL;
143 fs_count = 0;
144}
145
146
cfe6e3f0 147#if defined(HAVE_GETMNTENT)
3d93ccb7
NS
148#include <mntent.h>
149
150static void
151fs_extract_mount_options(
152 struct mntent *mnt,
153 char **logp,
154 char **rtp)
155{
2a1888c5 156 char *fslog, *fsrt;
3d93ccb7
NS
157
158 /* Extract log device and realtime device from mount options */
0c222c69 159 if ((fslog = hasmntopt(mnt, "logdev=")))
3d93ccb7 160 fslog += 7;
0c222c69 161 if ((fsrt = hasmntopt(mnt, "rtdev=")))
3d93ccb7 162 fsrt += 6;
3d93ccb7
NS
163
164 /* Do this only after we've finished processing mount options */
165 if (fslog) {
2a1888c5 166 strtok(fslog, " ,");
3d93ccb7
NS
167 fslog = strdup(fslog);
168 }
169 if (fsrt) {
2a1888c5 170 strtok(fsrt, " ,");
3d93ccb7
NS
171 fsrt = strdup(fsrt);
172 }
173
174 *logp = fslog;
175 *rtp = fsrt;
176}
177
178static int
179fs_table_initialise_mounts(
180 char *path)
181{
182 struct mntent *mnt;
183 FILE *mtp;
d9ebd5d7
NS
184 char *dir, *fsname, *fslog, *fsrt;
185 int error, found;
186
187 error = found = 0;
188 dir = fsname = fslog = fsrt = NULL;
3d93ccb7 189
8fc372bc
NS
190 if (!mtab_file) {
191 mtab_file = PROC_MOUNTS;
192 if (access(mtab_file, R_OK) != 0)
193 mtab_file = MOUNTED;
194 }
3d93ccb7
NS
195
196 if ((mtp = setmntent(mtab_file, "r")) == NULL)
197 return ENOENT;
198
199 while ((mnt = getmntent(mtp)) != NULL) {
200 if (strcmp(mnt->mnt_type, "xfs") != 0)
201 continue;
202 if (path &&
203 ((strcmp(path, mnt->mnt_dir) != 0) &&
204 (strcmp(path, mnt->mnt_fsname) != 0)))
205 continue;
206 found = 1;
207 dir = strdup(mnt->mnt_dir);
208 fsname = strdup(mnt->mnt_fsname);
209 if (!dir || !fsname) {
210 error = ENOMEM;
211 break;
212 }
213 fs_extract_mount_options(mnt, &fslog, &fsrt);
214 if ((error = fs_table_insert(dir, 0, FS_MOUNT_POINT,
215 fsname, fslog, fsrt)))
216 break;
217 }
218 endmntent(mtp);
219 if (!error && path && !found)
220 error = ENXIO;
221 if (error) {
d9ebd5d7
NS
222 if (dir) free(dir);
223 if (fsrt) free(fsrt);
224 if (fslog) free(fslog);
225 if (fsname) free(fsname);
3d93ccb7
NS
226 }
227 return error;
228}
229
230#elif defined(HAVE_GETMNTINFO)
231#include <sys/mount.h>
232
233static int
234fs_table_initialise_mounts(
235 char *path)
236{
237 struct statfs *stats;
d9ebd5d7
NS
238 char *dir, *fsname, *fslog, *fsrt;
239 int i, count, error, found;
240
241 error = found = 0;
242 dir = fsname = fslog = fsrt = NULL;
3d93ccb7
NS
243
244 if ((count = getmntinfo(&stats, 0)) < 0) {
16875fa6
AM
245 fprintf(stderr, _("%s: getmntinfo() failed: %s\n"),
246 progname, strerror(errno));
3d93ccb7
NS
247 return 0;
248 }
249
250 for (i = 0; i < count; i++) {
251 if (strcmp(stats[i].f_fstypename, "xfs") != 0)
252 continue;
253 if (path &&
254 ((strcmp(path, stats[i].f_mntonname) != 0) &&
255 (strcmp(path, stats[i].f_mntfromname) != 0)))
256 continue;
257 found = 1;
258 dir = strdup(stats[i].f_mntonname);
259 fsname = strdup(stats[i].f_mntfromname);
260 if (!dir || !fsname) {
261 error = ENOMEM;
262 break;
263 }
264 /* TODO: external log and realtime device? */
265 if ((error = fs_table_insert(dir, 0, FS_MOUNT_POINT,
cfe6e3f0 266 fsname, fslog, fsrt)))
3d93ccb7
NS
267 break;
268 }
269 if (!error && path && !found)
270 error = ENXIO;
271 if (error) {
d9ebd5d7
NS
272 if (dir) free(dir);
273 if (fsrt) free(fsrt);
274 if (fslog) free(fslog);
275 if (fsname) free(fsname);
3d93ccb7
NS
276 }
277 return error;
278}
279
280#else
281# error "How do I extract info about mounted filesystems on this platform?"
282#endif
283
284/*
285 * Given a directory, match it up to a filesystem mount point.
286 */
287static struct fs_path *
288fs_mount_point_from_path(
289 const char *dir)
290{
291 fs_cursor_t cursor;
292 fs_path_t *fs;
bd3c6f44 293 dev_t dev = 0;
3d93ccb7 294
bd3c6f44 295 if (fs_device_number(dir, &dev))
3d93ccb7 296 return NULL;
3d93ccb7
NS
297
298 fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
299 while ((fs = fs_cursor_next_entry(&cursor))) {
bd3c6f44 300 if (fs->fs_datadev == dev)
3d93ccb7
NS
301 break;
302 }
303 return fs;
304}
305
306static int
307fs_table_initialise_projects(
308 char *project)
309{
310 fs_project_path_t *path;
311 fs_path_t *fs;
312 prid_t prid = 0;
313 char *dir = NULL, *fsname = NULL;
314 int error = 0, found = 0;
315
316 if (project)
317 prid = prid_from_string(project);
318
319 setprpathent();
320 while ((path = getprpathent()) != NULL) {
321 if (project && prid != path->pp_prid)
322 continue;
16875fa6
AM
323 if ((fs = fs_mount_point_from_path(path->pp_pathname)) == NULL) {
324 fprintf(stderr, _("%s: cannot find mount point for path `%s': %s\n"),
325 progname, path->pp_pathname, strerror(errno));
3d93ccb7 326 continue;
16875fa6 327 }
3d93ccb7
NS
328 found = 1;
329 dir = strdup(path->pp_pathname);
330 fsname = strdup(fs->fs_name);
331 if (!dir || !fsname) {
332 error = ENOMEM;
333 break;
334 }
335 if ((error = fs_table_insert(dir, path->pp_prid,
336 FS_PROJECT_PATH, fsname, NULL, NULL)))
337 break;
338 }
339 endprpathent();
340
341 if (!error && project && !found)
342 error = ENOENT;
343 if (error) {
d9ebd5d7
NS
344 if (dir) free(dir);
345 if (fsname) free(fsname);
3d93ccb7
NS
346 }
347 return error;
348}
349
350void
351fs_table_initialise(void)
352{
353 int error;
354
355 error = fs_table_initialise_mounts(NULL);
356 if (!error)
357 error = fs_table_initialise_projects(NULL);
358 if (error) {
359 fs_table_destroy();
360 fprintf(stderr, _("%s: cannot initialise path table: %s\n"),
361 progname, strerror(error));
362 exit(1);
363 }
364}
365
366void
367fs_table_insert_mount(
368 char *mount)
369{
370 int error;
371
372 error = fs_table_initialise_mounts(mount);
373 if (error) {
374 fs_table_destroy();
375 fprintf(stderr, _("%s: cannot setup path for mount %s: %s\n"),
376 progname, mount, strerror(error));
377 exit(1);
378 }
379}
380
381void
382fs_table_insert_project(
383 char *project)
384{
385 int error;
386
387 if (!fs_count) {
388 fprintf(stderr, _("%s: no mount table yet, so no projects\n"),
389 progname);
390 exit(1);
391 }
392 error = fs_table_initialise_projects(project);
393 if (error) {
394 fs_table_destroy();
395 fprintf(stderr, _("%s: cannot setup path for project %s: %s\n"),
396 progname, project, strerror(error));
397 exit(1);
398 }
399}
400
02b26a6d
AM
401void
402fs_table_insert_project_path(
403 char *udir,
404 prid_t prid)
405{
406 fs_path_t *fs;
407 char *dir = NULL, *fsname = NULL;
408 int error = 0;
409
410 if ((fs = fs_mount_point_from_path(udir)) != NULL) {
411 dir = strdup(udir);
412 fsname = strdup(fs->fs_name);
413 if (dir && fsname)
414 error = fs_table_insert(dir, prid,
415 FS_PROJECT_PATH, fsname, NULL, NULL);
416 else
417 error = ENOMEM;
418 } else
419 error = ENOENT;
420
421 if (error) {
422 if (dir)
423 free(dir);
424 if (fsname)
425 free(fsname);
426 fprintf(stderr, _("%s: cannot setup path for project dir %s: %s\n"),
427 progname, udir, strerror(error));
428 exit(1);
429 }
430}
3d93ccb7
NS
431/*
432 * Table iteration (cursor-based) interfaces
433 */
434
ee0aba2e
AE
435/*
436 * Initialize an fs_table cursor. If a directory path is supplied,
437 * the cursor is set up to appear as though the table contains only
438 * a single entry which represents the directory specified.
439 * Otherwise it is set up to prepare for visiting all entries in the
440 * global table, starting with the first. "flags" can be either
441 * FS_MOUNT_POINT or FS_PROJECT_PATH to limit what type of entries
442 * will be selected by fs_cursor_next_entry(). 0 can be used as a
443 * wild card (selecting either type).
444 */
3d93ccb7
NS
445void
446fs_cursor_initialise(
447 char *dir,
448 uint flags,
449 fs_cursor_t *cur)
450{
451 fs_path_t *path;
452
453 memset(cur, 0, sizeof(*cur));
454 if (dir) {
455 if ((path = fs_table_lookup(dir, flags)) == NULL)
456 return;
457 cur->local = *path;
458 cur->count = 1;
459 cur->table = &cur->local;
460 } else {
461 cur->count = fs_count;
462 cur->table = fs_table;
463 }
464 cur->flags = flags;
465}
466
ee0aba2e
AE
467/*
468 * Use the cursor to find the next entry in the table having the
469 * type specified by the cursor's "flags" field.
470 */
3d93ccb7
NS
471struct fs_path *
472fs_cursor_next_entry(
473 fs_cursor_t *cur)
474{
3d93ccb7 475 while (cur->index < cur->count) {
ee0aba2e
AE
476 fs_path_t *next = &cur->table[cur->index++];
477
478 if (!cur->flags || (cur->flags & next->fs_flags))
479 return next;
3d93ccb7 480 }
ee0aba2e 481 return NULL;
3d93ccb7 482}