]>
Commit | Line | Data |
---|---|---|
6bd8b7a7 KZ |
1 | /* |
2 | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> | |
3 | * | |
4 | * This file may be redistributed under the terms of the | |
5 | * GNU Lesser General Public License. | |
6 | * | |
7 | * Note: | |
8 | * mnt_tab_find_* functions are mount(8) compatible. It means it tries | |
9 | * to found an entry in more iterations where the first attempt is always | |
10 | * based on comparison with unmodified (non-canonicalized or un-evaluated) | |
11 | * paths or tags. For example fstab with two entries: | |
12 | * | |
13 | * LABEL=foo /foo auto rw | |
14 | * /dev/foo /foo auto rw | |
15 | * | |
16 | * where both lines are used for the *same* device, then | |
17 | * | |
18 | * mnt_tab_find_source(tb, "/dev/foo", &fs); | |
19 | * | |
20 | * will returns the second line, and | |
21 | * | |
22 | * mnt_tab_find_source(tb, "LABEL=foo", &fs); | |
23 | * | |
24 | * will returns the first entry, and | |
25 | * | |
26 | * mnt_tab_find_source(tb, "UUID=<anyuuid>", &fs); | |
27 | * | |
28 | * will returns the first entry (if UUID matches with the device). | |
29 | */ | |
30 | ||
31 | #include <string.h> | |
32 | #include <stdlib.h> | |
33 | #include <ctype.h> | |
3fca8422 | 34 | #include <errno.h> |
6bd8b7a7 KZ |
35 | #include <limits.h> |
36 | #include <sys/types.h> | |
37 | #include <sys/stat.h> | |
38 | #include <unistd.h> | |
39 | #include <blkid/blkid.h> | |
40 | ||
41 | #include "nls.h" | |
42 | #include "mountP.h" | |
43 | ||
44 | /** | |
45 | * mnt_new_tab: | |
46 | * @filename: file name or NULL | |
47 | * | |
48 | * The tab is a container for mnt_fs entries that usually represents a fstab, | |
49 | * mtab or mountinfo file from your system. | |
50 | * | |
51 | * Note that this function does not parse the file. See also | |
52 | * mnt_tab_parse_file(). | |
53 | * | |
54 | * Returns newly allocated tab struct. | |
55 | */ | |
56 | mnt_tab *mnt_new_tab(const char *filename) | |
57 | { | |
58 | mnt_tab *tb = NULL; | |
59 | ||
60 | tb = calloc(1, sizeof(struct _mnt_tab)); | |
61 | if (!tb) | |
62 | goto err; | |
63 | ||
64 | if (filename) { | |
65 | tb->filename = strdup(filename); | |
66 | if (!tb->filename) | |
67 | goto err; | |
68 | } | |
69 | INIT_LIST_HEAD(&tb->ents); | |
70 | return tb; | |
71 | err: | |
72 | free(tb); | |
73 | return NULL; | |
74 | } | |
75 | ||
76 | /** | |
77 | * mnt_free_tab: | |
78 | * @tab: tab pointer | |
79 | * | |
80 | * Deallocates tab struct and all entries. | |
81 | */ | |
82 | void mnt_free_tab(mnt_tab *tb) | |
83 | { | |
84 | if (!tb) | |
85 | return; | |
86 | free(tb->filename); | |
87 | ||
88 | while (!list_empty(&tb->ents)) { | |
89 | mnt_fs *fs = list_entry(tb->ents.next, mnt_fs, ents); | |
90 | mnt_free_fs(fs); | |
91 | } | |
92 | ||
93 | free(tb); | |
94 | } | |
95 | ||
96 | /** | |
97 | * mnt_tab_get_nents: | |
98 | * @tb: pointer to tab | |
99 | * | |
100 | * Returns number of valid entries in tab. | |
101 | */ | |
102 | int mnt_tab_get_nents(mnt_tab *tb) | |
103 | { | |
104 | assert(tb); | |
105 | return tb ? tb->nents : 0; | |
106 | } | |
107 | ||
108 | /** | |
109 | * mnt_tab_set_cache: | |
110 | * @tb: pointer to tab | |
111 | * @mpc: pointer to mnt_cache instance | |
112 | * | |
113 | * Setups a cache for canonicalized paths and evaluated tags (LABEL/UUID). The | |
114 | * cache is recommended for mnt_tab_find_*() functions. | |
115 | * | |
116 | * The cache could be shared between more tabs. Be careful when you share the | |
117 | * same cache between more threads -- currently the cache does not provide any | |
118 | * locking method. | |
119 | * | |
120 | * See also mnt_new_cache(). | |
121 | * | |
122 | * Returns 0 on success or -1 in case of error. | |
123 | */ | |
124 | int mnt_tab_set_cache(mnt_tab *tb, mnt_cache *mpc) | |
125 | { | |
126 | assert(tb); | |
127 | if (!tb) | |
128 | return -1; | |
129 | tb->cache = mpc; | |
130 | return 0; | |
131 | } | |
132 | ||
133 | /** | |
134 | * mnt_tab_get_cache: | |
135 | * @tb: pointer to tab | |
136 | * | |
137 | * Returns pointer to mnt_cache instance or NULL. | |
138 | */ | |
139 | mnt_cache *mnt_tab_get_cache(mnt_tab *tb) | |
140 | { | |
141 | assert(tb); | |
142 | return tb ? tb->cache : NULL; | |
143 | } | |
144 | ||
145 | /** | |
146 | * mnt_tab_get_name: | |
147 | * @tb: tab pointer | |
148 | * | |
149 | * Returns tab filename or NULL. | |
150 | */ | |
151 | const char *mnt_tab_get_name(mnt_tab *tb) | |
152 | { | |
153 | assert(tb); | |
154 | return tb ? tb->filename : NULL; | |
155 | } | |
156 | ||
157 | /** | |
158 | * mnt_tab_add_fs: | |
159 | * @tb: tab pointer | |
160 | * @fs: new entry | |
161 | * | |
162 | * Adds a new entry to tab. | |
163 | * | |
164 | * Returns 0 on success or -1 in case of error. | |
165 | */ | |
166 | int mnt_tab_add_fs(mnt_tab *tb, mnt_fs *fs) | |
167 | { | |
168 | assert(tb); | |
169 | assert(fs); | |
170 | ||
171 | if (!tb || !fs) | |
172 | return -1; | |
173 | ||
174 | list_add_tail(&fs->ents, &tb->ents); | |
175 | ||
176 | DBG(DEBUG_TAB, fprintf(stderr, | |
177 | "libmount: %s: add entry: %s %s\n", | |
178 | tb->filename, mnt_fs_get_source(fs), | |
179 | mnt_fs_get_target(fs))); | |
180 | ||
181 | if (fs->flags & MNT_FS_ERROR) | |
182 | tb->nerrs++; | |
183 | else | |
184 | tb->nents++; | |
185 | return 0; | |
186 | } | |
187 | ||
188 | /** | |
189 | * mnt_tab_remove_fs: | |
190 | * @tb: tab pointer | |
191 | * @fs: new entry | |
192 | * | |
193 | * Returns 0 on success or -1 in case of error. | |
194 | */ | |
195 | int mnt_tab_remove_fs(mnt_tab *tb, mnt_fs *fs) | |
196 | { | |
197 | assert(tb); | |
198 | assert(fs); | |
199 | ||
200 | if (!tb || !fs) | |
201 | return -1; | |
202 | ||
203 | list_del(&fs->ents); | |
204 | ||
205 | if (fs->flags & MNT_FS_ERROR) | |
206 | tb->nerrs--; | |
207 | else | |
208 | tb->nents--; | |
209 | return 0; | |
210 | } | |
211 | ||
26b4f9e4 KZ |
212 | /** |
213 | * mnt_tab_get_root_fs: | |
214 | * @tb: mountinfo file (/proc/self/mountinfo) | |
215 | * @root: returns pointer to the root filesystem (/) | |
216 | * | |
217 | * Returns: 0 on success or -1 case of error. | |
218 | */ | |
219 | int mnt_tab_get_root_fs(mnt_tab *tb, mnt_fs **root) | |
220 | { | |
221 | mnt_iter itr; | |
222 | mnt_fs *fs; | |
223 | int root_id = 0; | |
224 | ||
225 | assert(tb); | |
226 | assert(root); | |
227 | ||
228 | if (!tb || !root) | |
229 | return -1; | |
230 | ||
231 | mnt_reset_iter(&itr, MNT_ITER_FORWARD); | |
232 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
233 | int id = mnt_fs_get_parent_id(fs); | |
234 | if (!id) | |
235 | break; /* @tab is not mountinfo file? */ | |
236 | ||
237 | if (!*root || id < root_id) { | |
238 | *root = fs; | |
239 | root_id = id; | |
240 | } | |
241 | } | |
242 | ||
243 | return root_id ? 0 : -1; | |
244 | } | |
245 | ||
246 | /** | |
247 | * mnt_tab_next_child_fs: | |
248 | * @tb: mountinfo file (/proc/self/mountinfo) | |
249 | * @parent: parental FS | |
250 | * @chld: returns the next child filesystem | |
251 | * | |
252 | * Note that filesystems are returned in the order how was mounted (according to | |
253 | * IDs in /proc/self/mountinfo). | |
254 | * | |
255 | * Returns 0 on success, -1 in case of error or 1 at end of list. | |
256 | */ | |
257 | int mnt_tab_next_child_fs(mnt_tab *tb, mnt_iter *itr, | |
258 | mnt_fs *parent, mnt_fs **chld) | |
259 | { | |
260 | mnt_fs *fs; | |
261 | int parent_id, lastchld_id = 0, chld_id = 0; | |
262 | ||
263 | if (!tb || !itr || !parent) | |
264 | return -1; | |
265 | ||
266 | parent_id = mnt_fs_get_id(parent); | |
267 | if (!parent_id) | |
268 | return -1; | |
269 | ||
270 | /* get ID of the previously returned child */ | |
271 | if (itr->head && itr->p != itr->head) { | |
272 | MNT_ITER_ITERATE(itr, fs, struct _mnt_fs, ents); | |
273 | lastchld_id = mnt_fs_get_id(fs); | |
274 | } | |
275 | ||
276 | *chld = NULL; | |
277 | ||
278 | mnt_reset_iter(itr, MNT_ITER_FORWARD); | |
279 | while(mnt_tab_next_fs(tb, itr, &fs) == 0) { | |
280 | int id; | |
281 | ||
282 | if (mnt_fs_get_parent_id(fs) != parent_id) | |
283 | continue; | |
284 | ||
285 | id = mnt_fs_get_id(fs); | |
286 | ||
287 | if ((!lastchld_id || id > lastchld_id) && | |
288 | (!*chld || id < chld_id)) { | |
289 | *chld = fs; | |
290 | chld_id = id; | |
291 | } | |
292 | } | |
293 | ||
294 | if (!chld_id) | |
295 | return 1; /* end of iterator */ | |
296 | ||
297 | /* set the iterator to the @chld for the next call */ | |
298 | mnt_tab_set_iter(tb, itr, *chld); | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
6bd8b7a7 KZ |
303 | /** |
304 | * mnt_tab_next_fs: | |
305 | * @tb: tab pointer | |
306 | * @itr: iterator | |
307 | * @fs: returns the next tab entry | |
308 | * | |
309 | * Returns 0 on success, -1 in case of error or 1 at end of list. | |
310 | * | |
311 | * Example (list all mountpoints from fstab in backward order): | |
312 | * | |
313 | * mnt_fs *fs; | |
314 | * mnt_tab *tb = mnt_new_tab("/etc/fstab"); | |
315 | * mnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD); | |
316 | * | |
317 | * mnt_tab_parse_file(tb); | |
318 | * | |
319 | * while(mnt_tab_next_fs(tb, itr, &fs) == 0) { | |
320 | * const char *dir = mnt_fs_get_target(fs); | |
321 | * printf("mount point: %s\n", dir); | |
322 | * } | |
323 | * mnt_free_tab(fi); | |
324 | */ | |
325 | int mnt_tab_next_fs(mnt_tab *tb, mnt_iter *itr, mnt_fs **fs) | |
326 | { | |
3fca8422 KZ |
327 | int rc; |
328 | ||
6bd8b7a7 KZ |
329 | assert(tb); |
330 | assert(itr); | |
331 | assert(fs); | |
332 | ||
333 | if (!tb || !itr || !fs) | |
334 | return -1; | |
335 | again: | |
3fca8422 | 336 | rc = 1; |
6bd8b7a7 KZ |
337 | if (!itr->head) |
338 | MNT_ITER_INIT(itr, &tb->ents); | |
339 | if (itr->p != itr->head) { | |
340 | MNT_ITER_ITERATE(itr, *fs, struct _mnt_fs, ents); | |
3fca8422 | 341 | rc = 0; |
6bd8b7a7 KZ |
342 | } |
343 | ||
344 | /* ignore broken entries */ | |
345 | if (*fs && ((*fs)->flags & MNT_FS_ERROR)) | |
346 | goto again; | |
347 | ||
3fca8422 KZ |
348 | return rc; |
349 | } | |
350 | ||
351 | /** | |
352 | * mnt_tab_find_next_fs: | |
353 | * @tb: table | |
354 | * @itr: iterator | |
355 | * @match_func: function returns 1 or 0 | |
356 | * @fs: returns pointer to the next matching table entry | |
357 | * | |
358 | * This function allows search in @tb. | |
359 | * | |
360 | * Returns -1 in case of error, 1 at end of table or 0 o success. | |
361 | */ | |
362 | int mnt_tab_find_next_fs(mnt_tab *tb, mnt_iter *itr, | |
363 | int (*match_func)(mnt_fs *, void *), void *userdata, | |
364 | mnt_fs **fs) | |
365 | { | |
366 | if (!tb || !itr || !fs || !match_func) | |
367 | return -1; | |
368 | ||
369 | if (!itr->head) | |
370 | MNT_ITER_INIT(itr, &tb->ents); | |
371 | ||
372 | do { | |
373 | if (itr->p != itr->head) | |
374 | MNT_ITER_ITERATE(itr, *fs, struct _mnt_fs, ents); | |
375 | else | |
376 | break; /* end */ | |
377 | ||
378 | if ((*fs)->flags & MNT_FS_ERROR) | |
379 | continue; | |
380 | if (match_func(*fs, userdata)) | |
381 | return 0; | |
382 | } while(1); | |
383 | ||
26b4f9e4 | 384 | *fs = NULL; |
6bd8b7a7 KZ |
385 | return 1; |
386 | } | |
387 | ||
388 | /** | |
389 | * mnt_tab_set_iter: | |
390 | * @tb: tab pointer | |
391 | * @itr: iterator | |
392 | * @fs: tab entry | |
393 | * | |
394 | * Sets @iter to the position of @fs in the file @tb. | |
395 | * | |
396 | * Returns 0 on success, -1 in case of error. | |
397 | */ | |
398 | int mnt_tab_set_iter(mnt_tab *tb, mnt_iter *itr, mnt_fs *fs) | |
399 | { | |
400 | assert(tb); | |
401 | assert(itr); | |
402 | assert(fs); | |
403 | ||
404 | if (!tb || !itr || !fs) | |
405 | return -1; | |
406 | ||
407 | MNT_ITER_INIT(itr, &tb->ents); | |
408 | itr->p = &fs->ents; | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | /** | |
414 | * mnt_tab_find_target: | |
415 | * @tb: tab pointer | |
416 | * @path: mountpoint directory | |
417 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
418 | * | |
419 | * Try to lookup an entry in given tab, possible are three iterations, first | |
420 | * with @path, second with realpath(@path) and third with realpath(@path) | |
421 | * against realpath(fs->target). The 2nd and 3rd iterations are not performed | |
422 | * when @tb cache is not set (see mnt_tab_set_cache()). | |
423 | * | |
424 | * Returns a tab entry or NULL. | |
425 | */ | |
426 | mnt_fs *mnt_tab_find_target(mnt_tab *tb, const char *path, int direction) | |
427 | { | |
428 | mnt_iter itr; | |
429 | mnt_fs *fs = NULL; | |
430 | char *cn; | |
431 | ||
432 | assert(tb); | |
433 | assert(path); | |
434 | ||
435 | DBG(DEBUG_TAB, fprintf(stderr, | |
436 | "libmount: %s: lookup target: %s\n", tb->filename, path)); | |
437 | ||
438 | /* native @target */ | |
439 | mnt_reset_iter(&itr, direction); | |
440 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) | |
441 | if (fs->target && strcmp(fs->target, path) == 0) | |
442 | return fs; | |
443 | ||
444 | if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) | |
445 | return NULL; | |
446 | ||
447 | /* canonicalized paths in mnt_tab */ | |
448 | mnt_reset_iter(&itr, direction); | |
449 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
450 | if (fs->target && strcmp(fs->target, cn) == 0) | |
451 | return fs; | |
452 | } | |
453 | ||
454 | /* non-canonicaled path in mnt_tab */ | |
455 | mnt_reset_iter(&itr, direction); | |
456 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
457 | char *p; | |
458 | if (!fs->target) | |
459 | continue; | |
460 | p = mnt_resolve_path(fs->target, tb->cache); | |
461 | if (strcmp(cn, p) == 0) | |
462 | return fs; | |
463 | } | |
464 | return NULL; | |
465 | } | |
466 | ||
467 | /** | |
468 | * mnt_tab_find_srcpath: | |
469 | * @tb: tab pointer | |
470 | * @path: source path (devname or dirname) | |
471 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
472 | * | |
473 | * Try to lookup an entry in given tab, possible are four iterations, first | |
474 | * with @path, second with realpath(@path), third with tags (LABEL, UUID, ..) | |
475 | * from @path and fourth with realpath(@path) against realpath(entry->srcpath). | |
476 | * | |
477 | * The 2nd, 3rd and 4th iterations are not performed when @tb cache is not | |
478 | * set (see mnt_tab_set_cache()). | |
479 | * | |
480 | * Returns a tab entry or NULL. | |
481 | */ | |
482 | mnt_fs *mnt_tab_find_srcpath(mnt_tab *tb, const char *path, int direction) | |
483 | { | |
484 | mnt_iter itr; | |
485 | mnt_fs *fs = NULL; | |
486 | int ntags = 0; | |
487 | char *cn; | |
488 | const char *p; | |
489 | ||
490 | assert(tb); | |
491 | assert(path); | |
492 | ||
493 | DBG(DEBUG_TAB, fprintf(stderr, | |
494 | "libmount: %s: lookup srcpath: %s\n", tb->filename, path)); | |
495 | ||
496 | /* native paths */ | |
497 | mnt_reset_iter(&itr, direction); | |
498 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
499 | p = mnt_fs_get_srcpath(fs); | |
500 | if (p && strcmp(p, path) == 0) | |
501 | return fs; | |
502 | if (!p) | |
503 | /* mnt_fs_get_srcpath() returs nothing, it's TAG */ | |
504 | ntags++; | |
505 | } | |
506 | ||
507 | if (!tb->cache || !(cn = mnt_resolve_path(path, tb->cache))) | |
508 | return NULL; | |
509 | ||
510 | /* canonicalized paths in mnt_tab */ | |
511 | if (ntags < mnt_tab_get_nents(tb)) { | |
512 | mnt_reset_iter(&itr, direction); | |
513 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
514 | p = mnt_fs_get_srcpath(fs); | |
515 | if (p && strcmp(p, cn) == 0) | |
516 | return fs; | |
517 | } | |
518 | } | |
519 | ||
520 | /* evaluated tag */ | |
3fca8422 | 521 | if (ntags) { |
6bd8b7a7 | 522 | mnt_reset_iter(&itr, direction); |
6bd8b7a7 | 523 | |
3fca8422 KZ |
524 | if (mnt_cache_read_tags(tb->cache, cn) > 0) { |
525 | /* @path's TAGs are in the cache */ | |
526 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
527 | const char *t, *v; | |
6bd8b7a7 | 528 | |
3fca8422 KZ |
529 | if (mnt_fs_get_tag(fs, &t, &v)) |
530 | continue; | |
531 | ||
532 | if (mnt_cache_device_has_tag(tb->cache, cn, t, v)) | |
533 | return fs; | |
534 | } | |
535 | } else if (errno == EACCES) { | |
536 | /* @path is unaccessible, try evaluate all TAGs in @tb | |
537 | * by udev symlinks -- this could be expensive on systems | |
538 | * with huge fstab/mtab */ | |
539 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
540 | const char *t, *v, *x; | |
541 | if (mnt_fs_get_tag(fs, &t, &v)) | |
542 | continue; | |
543 | x = mnt_resolve_tag(t, v, tb->cache); | |
544 | if (x && !strcmp(x, cn)) | |
545 | return fs; | |
546 | } | |
6bd8b7a7 KZ |
547 | } |
548 | } | |
549 | ||
550 | /* non-canonicalized paths in mnt_tab */ | |
551 | if (ntags <= mnt_tab_get_nents(tb)) { | |
552 | mnt_reset_iter(&itr, direction); | |
553 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
554 | p = mnt_fs_get_srcpath(fs); | |
555 | if (p) | |
556 | p = mnt_resolve_path(p, tb->cache); | |
557 | if (p && strcmp(cn, p) == 0) | |
558 | return fs; | |
559 | } | |
560 | } | |
561 | ||
562 | return NULL; | |
563 | } | |
564 | ||
565 | ||
566 | /** | |
567 | * mnt_tab_find_tag: | |
568 | * @tb: tab pointer | |
569 | * @tag: tag name (e.g "LABEL", "UUID", ...) | |
570 | * @val: tag value | |
571 | * @direction: MNT_ITER_{FORWARD,BACKWARD} | |
572 | * | |
573 | * Try to lookup an entry in given tab, first attempt is lookup by @tag and | |
574 | * @val, for the second attempt the tag is evaluated (converted to the device | |
575 | * name) and mnt_tab_find_srcpath() is preformed. The second attempt is not | |
576 | * performed when @tb cache is not set (see mnt_tab_set_cache()). | |
577 | ||
578 | * Returns a tab entry or NULL. | |
579 | */ | |
580 | mnt_fs *mnt_tab_find_tag(mnt_tab *tb, const char *tag, | |
581 | const char *val, int direction) | |
582 | { | |
583 | mnt_iter itr; | |
584 | mnt_fs *fs = NULL; | |
585 | ||
586 | assert(tb); | |
587 | assert(tag); | |
588 | assert(val); | |
589 | ||
590 | if (!tb || !tag || !val) | |
591 | return NULL; | |
592 | ||
593 | DBG(DEBUG_TAB, fprintf(stderr, | |
594 | "libmount: %s: lookup by TAG: %s %s\n", tb->filename, tag, val)); | |
595 | ||
596 | /* look up by TAG */ | |
597 | mnt_reset_iter(&itr, direction); | |
598 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
599 | if (fs->tagname && fs->tagval && | |
600 | strcmp(fs->tagname, tag) == 0 && | |
601 | strcmp(fs->tagval, val) == 0) | |
602 | return fs; | |
603 | } | |
604 | ||
605 | if (tb->cache) { | |
606 | /* look up by device name */ | |
607 | char *cn = mnt_resolve_tag(tag, val, tb->cache); | |
608 | if (cn) | |
609 | return mnt_tab_find_srcpath(tb, cn, direction); | |
610 | } | |
611 | return NULL; | |
612 | } | |
613 | ||
614 | /** | |
615 | * mnt_tab_find_source: | |
616 | * @tb: tab pointer | |
617 | * @source: TAG or path | |
618 | * | |
619 | * This is high-level API for mnt_tab_find_{srcpath,tag}. You needn't to care | |
620 | * about @source format (device, LABEL, UUID, ...). This function parses @source | |
621 | * and calls mnt_tab_find_tag() or mnt_tab_find_srcpath(). | |
622 | * | |
623 | * Returns a tab entry or NULL. | |
624 | */ | |
625 | mnt_fs *mnt_tab_find_source(mnt_tab *tb, const char *source, int direction) | |
626 | { | |
627 | mnt_fs *fs = NULL; | |
628 | ||
629 | assert(tb); | |
630 | assert(source); | |
631 | ||
632 | if (!tb || !source) | |
633 | return NULL; | |
634 | ||
635 | DBG(DEBUG_TAB, fprintf(stderr, | |
636 | "libmount: %s: lookup SOURCE: %s\n", tb->filename, source)); | |
637 | ||
638 | if (strchr(source, '=')) { | |
639 | char *tag, *val; | |
640 | ||
641 | if (blkid_parse_tag_string(source, &tag, &val) == 0) { | |
642 | ||
643 | fs = mnt_tab_find_tag(tb, tag, val, direction); | |
644 | ||
645 | free(tag); | |
646 | free(val); | |
647 | } | |
648 | } else | |
649 | fs = mnt_tab_find_srcpath(tb, source, direction); | |
650 | ||
651 | return fs; | |
652 | } | |
653 | ||
6bd8b7a7 KZ |
654 | |
655 | /** | |
656 | * mnt_tab_fprintf: | |
657 | * @f: FILE | |
658 | * @fmt: per line printf-like format string (see MNT_MFILE_PRINTFMT) | |
659 | * @tb: tab pointer | |
660 | * | |
661 | * Returns 0 on success, -1 in case of error. | |
662 | */ | |
663 | int mnt_tab_fprintf(mnt_tab *tb, FILE *f, const char *fmt) | |
664 | { | |
665 | mnt_iter itr; | |
666 | mnt_fs *fs; | |
667 | ||
668 | assert(f); | |
669 | assert(fmt); | |
670 | assert(tb); | |
671 | ||
672 | if (!f || !fmt || !tb) | |
673 | return -1; | |
674 | ||
675 | mnt_reset_iter(&itr, MNT_ITER_FORWARD); | |
676 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
677 | if (mnt_fs_fprintf(fs, f, fmt) == -1) | |
678 | return -1; | |
679 | } | |
680 | ||
681 | return 0; | |
682 | } | |
683 | ||
684 | /** | |
685 | * mnt_tab_update_file | |
686 | * @tb: tab pointer | |
687 | * | |
688 | * Writes tab to disk. Don't forget to lock the file (see mnt_lock()). | |
689 | * | |
690 | * Returns 0 on success, -1 in case of error. | |
691 | */ | |
692 | int mnt_tab_update_file(mnt_tab *tb) | |
693 | { | |
694 | FILE *f = NULL; | |
695 | char tmpname[PATH_MAX]; | |
696 | const char *filename; | |
697 | struct stat st; | |
698 | int fd; | |
699 | ||
700 | assert(tb); | |
701 | if (!tb) | |
702 | goto error; | |
703 | ||
704 | filename = mnt_tab_get_name(tb); | |
705 | if (!filename) | |
706 | goto error; | |
707 | ||
708 | if (snprintf(tmpname, sizeof(tmpname), "%s.tmp", filename) | |
709 | >= sizeof(tmpname)) | |
710 | goto error; | |
711 | ||
712 | f = fopen(tmpname, "w"); | |
713 | if (!f) | |
714 | goto error; | |
715 | ||
716 | if (mnt_tab_fprintf(tb, f, MNT_MFILE_PRINTFMT) != 0) | |
717 | goto error; | |
718 | ||
719 | fd = fileno(f); | |
720 | ||
721 | if (fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) | |
722 | goto error; | |
723 | ||
724 | /* Copy uid/gid from the present file before renaming. */ | |
725 | if (stat(filename, &st) == 0) { | |
726 | if (fchown(fd, st.st_uid, st.st_gid) < 0) | |
727 | goto error; | |
728 | } | |
729 | ||
730 | fclose(f); | |
731 | f = NULL; | |
732 | ||
733 | if (rename(tmpname, filename) < 0) | |
734 | goto error; | |
735 | ||
736 | return 0; | |
737 | error: | |
738 | if (f) | |
739 | fclose(f); | |
740 | return -1; | |
741 | } | |
742 | ||
743 | #ifdef TEST_PROGRAM | |
744 | int test_strerr(struct mtest *ts, int argc, char *argv[]) | |
745 | { | |
746 | char buf[BUFSIZ]; | |
747 | mnt_tab *tb; | |
748 | int i; | |
749 | ||
750 | tb = mnt_new_tab("-test-"); | |
751 | if (!tb) | |
752 | goto err; | |
753 | ||
754 | for (i = 0; i < 10; i++) { | |
755 | mnt_fs *fs = mnt_new_fs(); | |
756 | if (!fs) | |
757 | goto err; | |
758 | if (i % 2) | |
759 | fs->flags |= MNT_FS_ERROR; /* mark entry as broken */ | |
760 | fs->lineno = i+1; | |
761 | mnt_tab_add_fs(tb, fs); | |
762 | } | |
763 | ||
764 | printf("\tadded %d valid lines\n", mnt_tab_get_nents(tb)); | |
765 | printf("\tadded %d broken lines\n", mnt_tab_get_nerrs(tb)); | |
766 | ||
767 | if (!mnt_tab_get_nerrs(tb)) /* report broken entries */ | |
768 | goto err; | |
769 | mnt_tab_strerror(tb, buf, sizeof(buf)); | |
770 | printf("\t%s\n", buf); | |
771 | ||
772 | mnt_free_tab(tb); | |
773 | return 0; | |
774 | err: | |
775 | return -1; | |
776 | } | |
777 | ||
778 | mnt_tab *create_tab(const char *file) | |
779 | { | |
780 | mnt_tab *tb; | |
781 | ||
782 | if (!file) | |
783 | return NULL; | |
784 | tb = mnt_new_tab(file); | |
785 | if (!tb) | |
786 | goto err; | |
787 | if (mnt_tab_parse_file(tb) != 0) | |
788 | goto err; | |
789 | if (mnt_tab_get_nerrs(tb)) { | |
790 | char buf[BUFSIZ]; | |
791 | mnt_tab_strerror(tb, buf, sizeof(buf)); | |
792 | fprintf(stderr, "%s\n", buf); | |
793 | goto err; | |
794 | } | |
795 | return tb; | |
796 | err: | |
797 | mnt_free_tab(tb); | |
798 | return NULL; | |
799 | } | |
800 | ||
801 | int test_parse(struct mtest *ts, int argc, char *argv[]) | |
802 | { | |
803 | mnt_tab *tb; | |
efe73c3e KZ |
804 | mnt_iter *itr; |
805 | mnt_fs *fs; | |
6bd8b7a7 KZ |
806 | |
807 | tb = create_tab(argv[1]); | |
808 | if (!tb) | |
809 | return -1; | |
810 | ||
efe73c3e KZ |
811 | itr = mnt_new_iter(MNT_ITER_FORWARD); |
812 | if (!itr) | |
813 | goto err; | |
814 | while(mnt_tab_next_fs(tb, itr, &fs) == 0) | |
815 | mnt_fs_print_debug(fs, stdout); | |
816 | err: | |
817 | mnt_free_iter(itr); | |
6bd8b7a7 KZ |
818 | mnt_free_tab(tb); |
819 | return 0; | |
820 | } | |
821 | ||
822 | int test_find(struct mtest *ts, int argc, char *argv[], int dr) | |
823 | { | |
824 | mnt_tab *tb; | |
825 | mnt_fs *fs = NULL; | |
826 | mnt_cache *mpc; | |
827 | const char *file, *find, *what; | |
828 | ||
829 | if (argc != 4) { | |
830 | fprintf(stderr, "try --help\n"); | |
831 | goto err; | |
832 | } | |
833 | ||
834 | file = argv[1], find = argv[2], what = argv[3]; | |
835 | ||
836 | tb = create_tab(file); | |
837 | if (!tb) | |
838 | goto err; | |
839 | ||
840 | /* create a cache for canonicalized paths */ | |
841 | mpc = mnt_new_cache(); | |
842 | if (!mpc) | |
843 | goto err; | |
844 | mnt_tab_set_cache(tb, mpc); | |
845 | ||
846 | if (strcasecmp(find, "source") == 0) | |
847 | fs = mnt_tab_find_source(tb, what, dr); | |
848 | else if (strcasecmp(find, "target") == 0) | |
849 | fs = mnt_tab_find_target(tb, what, dr); | |
850 | ||
851 | if (!fs) | |
852 | fprintf(stderr, "%s: not found %s '%s'\n", file, find, what); | |
853 | else { | |
854 | const char *s = mnt_fs_get_srcpath(fs); | |
855 | if (s) | |
856 | printf("%s", s); | |
857 | else { | |
858 | const char *tag, *val; | |
859 | mnt_fs_get_tag(fs, &tag, &val); | |
860 | printf("%s=%s", tag, val); | |
861 | } | |
862 | printf("|%s|%s\n", mnt_fs_get_target(fs), | |
863 | mnt_fs_get_optstr(fs)); | |
864 | } | |
865 | mnt_free_tab(tb); | |
866 | mnt_free_cache(mpc); | |
867 | return 0; | |
868 | err: | |
869 | return -1; | |
870 | } | |
871 | ||
872 | int test_find_bw(struct mtest *ts, int argc, char *argv[]) | |
873 | { | |
874 | return test_find(ts, argc, argv, MNT_ITER_BACKWARD); | |
875 | } | |
876 | ||
877 | int test_find_fw(struct mtest *ts, int argc, char *argv[]) | |
878 | { | |
879 | return test_find(ts, argc, argv, MNT_ITER_FORWARD); | |
880 | } | |
881 | ||
882 | int main(int argc, char *argv[]) | |
883 | { | |
884 | struct mtest tss[] = { | |
885 | { "--strerror", test_strerr, " test tab error reporting" }, | |
886 | { "--parse", test_parse, "<file> parse and print tab" }, | |
887 | { "--find-forward", test_find_fw, "<file> <source|target> <string>" }, | |
888 | { "--find-backward", test_find_bw, "<file> <source|target> <string>" }, | |
889 | { NULL } | |
890 | }; | |
891 | ||
892 | return mnt_run_test(tss, argc, argv); | |
893 | } | |
894 | ||
895 | #endif /* TEST_PROGRAM */ |