]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - libxcmd/paths.c
xfsprogs: libxcmd: use fs_device_number() consistently
[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
31 extern char *progname;
32
33 int fs_count;
34 struct fs_path *fs_table;
35 struct fs_path *fs_path;
36
37 char *mtab_file;
38 #define PROC_MOUNTS "/proc/self/mounts"
39
40 static int
41 fs_device_number(
42 const char *name,
43 dev_t *devnum)
44 {
45 struct stat64 sbuf;
46
47 if (stat64(name, &sbuf) < 0)
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;
58
59 return 0;
60 }
61
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 */
67 struct fs_path *
68 fs_table_lookup(
69 const char *dir,
70 uint flags)
71 {
72 uint i;
73 dev_t dev = 0;
74
75 if (fs_device_number(dir, &dev))
76 return NULL;
77
78 for (i = 0; i < fs_count; i++) {
79 if ((flags & fs_table[i].fs_flags) == 0)
80 continue;
81 if (fs_table[i].fs_datadev == dev)
82 return &fs_table[i];
83 }
84 return NULL;
85 }
86
87 static int
88 fs_table_insert(
89 char *dir,
90 uint prid,
91 uint flags,
92 char *fsname,
93 char *fslog,
94 char *fsrt)
95 {
96 dev_t datadev, logdev, rtdev;
97 struct fs_path *tmp_fs_table;
98
99 if (!dir || !fsname)
100 return EINVAL;
101
102 datadev = logdev = rtdev = 0;
103 if (fs_device_number(dir, &datadev))
104 return errno;
105 if (fslog && fs_device_number(fslog, &logdev))
106 return errno;
107 if (fsrt && fs_device_number(fsrt, &rtdev))
108 return errno;
109
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;
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;
127 }
128
129 void
130 fs_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
147 #if defined(HAVE_GETMNTENT)
148 #include <mntent.h>
149
150 static void
151 fs_extract_mount_options(
152 struct mntent *mnt,
153 char **logp,
154 char **rtp)
155 {
156 char *fslog, *fsrt;
157
158 /* Extract log device and realtime device from mount options */
159 if ((fslog = hasmntopt(mnt, "logdev=")))
160 fslog += 7;
161 if ((fsrt = hasmntopt(mnt, "rtdev=")))
162 fsrt += 6;
163
164 /* Do this only after we've finished processing mount options */
165 if (fslog) {
166 strtok(fslog, " ,");
167 fslog = strdup(fslog);
168 }
169 if (fsrt) {
170 strtok(fsrt, " ,");
171 fsrt = strdup(fsrt);
172 }
173
174 *logp = fslog;
175 *rtp = fsrt;
176 }
177
178 static int
179 fs_table_initialise_mounts(
180 char *path)
181 {
182 struct mntent *mnt;
183 FILE *mtp;
184 char *dir, *fsname, *fslog, *fsrt;
185 int error, found;
186
187 error = found = 0;
188 dir = fsname = fslog = fsrt = NULL;
189
190 if (!mtab_file) {
191 mtab_file = PROC_MOUNTS;
192 if (access(mtab_file, R_OK) != 0)
193 mtab_file = MOUNTED;
194 }
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) {
222 if (dir) free(dir);
223 if (fsrt) free(fsrt);
224 if (fslog) free(fslog);
225 if (fsname) free(fsname);
226 }
227 return error;
228 }
229
230 #elif defined(HAVE_GETMNTINFO)
231 #include <sys/mount.h>
232
233 static int
234 fs_table_initialise_mounts(
235 char *path)
236 {
237 struct statfs *stats;
238 char *dir, *fsname, *fslog, *fsrt;
239 int i, count, error, found;
240
241 error = found = 0;
242 dir = fsname = fslog = fsrt = NULL;
243
244 if ((count = getmntinfo(&stats, 0)) < 0) {
245 fprintf(stderr, _("%s: getmntinfo() failed: %s\n"),
246 progname, strerror(errno));
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,
266 fsname, fslog, fsrt)))
267 break;
268 }
269 if (!error && path && !found)
270 error = ENXIO;
271 if (error) {
272 if (dir) free(dir);
273 if (fsrt) free(fsrt);
274 if (fslog) free(fslog);
275 if (fsname) free(fsname);
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 */
287 static struct fs_path *
288 fs_mount_point_from_path(
289 const char *dir)
290 {
291 fs_cursor_t cursor;
292 fs_path_t *fs;
293 dev_t dev = 0;
294
295 if (fs_device_number(dir, &dev))
296 return NULL;
297
298 fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
299 while ((fs = fs_cursor_next_entry(&cursor))) {
300 if (fs->fs_datadev == dev)
301 break;
302 }
303 return fs;
304 }
305
306 static int
307 fs_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;
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));
326 continue;
327 }
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) {
344 if (dir) free(dir);
345 if (fsname) free(fsname);
346 }
347 return error;
348 }
349
350 void
351 fs_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
366 void
367 fs_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
381 void
382 fs_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
401 void
402 fs_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 }
431 /*
432 * Table iteration (cursor-based) interfaces
433 */
434
435 void
436 fs_cursor_initialise(
437 char *dir,
438 uint flags,
439 fs_cursor_t *cur)
440 {
441 fs_path_t *path;
442
443 memset(cur, 0, sizeof(*cur));
444 if (dir) {
445 if ((path = fs_table_lookup(dir, flags)) == NULL)
446 return;
447 cur->local = *path;
448 cur->count = 1;
449 cur->table = &cur->local;
450 } else {
451 cur->count = fs_count;
452 cur->table = fs_table;
453 }
454 cur->flags = flags;
455 }
456
457 struct fs_path *
458 fs_cursor_next_entry(
459 fs_cursor_t *cur)
460 {
461 fs_path_t *next = NULL;
462
463 while (cur->index < cur->count) {
464 next = &cur->table[cur->index++];
465 if (cur->flags & next->fs_flags)
466 break;
467 next = NULL;
468 }
469 return next;
470 }