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