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