1 /*
2 * Copyright (C) 2009-2011 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
8 /**
9 * SECTION: cache
10 * @title: Cache
11 * @short_description: paths and tags (UUID/LABEL) caching
12 *
13 * The cache is a very simple API for working with tags (LABEL, UUID, ...) and
14 * paths. The cache uses libblkid as a backend for TAGs resolution.
15 *
16 * All returned paths are always canonicalized.
17 */
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <limits.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <blkid.h>
26
27 #include "canonicalize.h"
28 #include "mountP.h"
29 #include "loopdev.h"
30 #include "strutils.h"
31
32 /*
33 * Canonicalized (resolved) paths & tags cache
34 */
35 #define MNT_CACHE_CHUNKSZ 128
36
37 #define MNT_CACHE_ISTAG (1 << 1) /* entry is TAG */
38 #define MNT_CACHE_ISPATH (1 << 2) /* entry is path */
39 #define MNT_CACHE_TAGREAD (1 << 3) /* tag read by mnt_cache_read_tags() */
40
41 /* path cache entry */
42 struct mnt_cache_entry {
43 char *key; /* search key (e.g. uncanonicalized path) */
44 char *value; /* value (e.g. canonicalized path) */
45 int flag;
46 };
47
48 struct libmnt_cache {
49 struct mnt_cache_entry *ents;
50 size_t nents;
51 size_t nallocs;
52 int refcount;
53
54 /* blkid_evaluate_tag() works in two ways:
55 *
56 * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
57 * then the blkid_cache is NULL.
58 *
59 * 2/ all tags are read from blkid.tab and verified by /dev
60 * scanning, then the blkid_cache is not NULL and then it's
61 * better to reuse the blkid_cache.
62 */
63 blkid_cache bc;
64
65 struct libmnt_table *mtab;
66 };
67
68 /**
69 * mnt_new_cache:
70 *
71 * Returns: new struct libmnt_cache instance or NULL in case of ENOMEM error.
72 */
mnt_new_cache(void)73 struct libmnt_cache *mnt_new_cache(void)
74 {
75 struct libmnt_cache *cache = calloc(1, sizeof(*cache));
76 if (!cache)
77 return NULL;
78 DBG(CACHE, ul_debugobj(cache, "alloc"));
79 cache->refcount = 1;
80 return cache;
81 }
82
83 /**
84 * mnt_free_cache:
85 * @cache: pointer to struct libmnt_cache instance
86 *
87 * Deallocates the cache. This function does not care about reference count. Don't
88 * use this function directly -- it's better to use use mnt_unref_cache().
89 */
mnt_free_cache(struct libmnt_cache * cache)90 void mnt_free_cache(struct libmnt_cache *cache)
91 {
92 size_t i;
93
94 if (!cache)
95 return;
96
97 DBG(CACHE, ul_debugobj(cache, "free [refcount=%d]", cache->refcount));
98
99 for (i = 0; i < cache->nents; i++) {
100 struct mnt_cache_entry *e = &cache->ents[i];
101 if (e->value != e->key)
102 free(e->value);
103 free(e->key);
104 }
105 free(cache->ents);
106 if (cache->bc)
107 blkid_put_cache(cache->bc);
108 free(cache);
109 }
110
111 /**
112 * mnt_ref_cache:
113 * @cache: cache pointer
114 *
115 * Increments reference counter.
116 */
mnt_ref_cache(struct libmnt_cache * cache)117 void mnt_ref_cache(struct libmnt_cache *cache)
118 {
119 if (cache) {
120 cache->refcount++;
121 /*DBG(CACHE, ul_debugobj(cache, "ref=%d", cache->refcount));*/
122 }
123 }
124
125 /**
126 * mnt_unref_cache:
127 * @cache: cache pointer
128 *
129 * De-increments reference counter, on zero the cache is automatically
130 * deallocated by mnt_free_cache().
131 */
mnt_unref_cache(struct libmnt_cache * cache)132 void mnt_unref_cache(struct libmnt_cache *cache)
133 {
134 if (cache) {
135 cache->refcount--;
136 /*DBG(CACHE, ul_debugobj(cache, "unref=%d", cache->refcount));*/
137 if (cache->refcount <= 0) {
138 mnt_unref_table(cache->mtab);
139
140 mnt_free_cache(cache);
141 }
142 }
143 }
144
145 /**
146 * mnt_cache_set_targets:
147 * @cache: cache pointer
148 * @mtab: table with already canonicalized mountpoints
149 *
150 * Add to @cache reference to @mtab. This allows to avoid unnecessary paths
151 * canonicalization in mnt_resolve_target().
152 *
153 * Returns: negative number in case of error, or 0 o success.
154 */
mnt_cache_set_targets(struct libmnt_cache * cache,struct libmnt_table * mtab)155 int mnt_cache_set_targets(struct libmnt_cache *cache,
156 struct libmnt_table *mtab)
157 {
158 assert(cache);
159 if (!cache)
160 return -EINVAL;
161
162 mnt_ref_table(mtab);
163 mnt_unref_table(cache->mtab);
164 cache->mtab = mtab;
165 return 0;
166 }
167
168
169 /* note that the @key could be the same pointer as @value */
cache_add_entry(struct libmnt_cache * cache,char * key,char * value,int flag)170 static int cache_add_entry(struct libmnt_cache *cache, char *key,
171 char *value, int flag)
172 {
173 struct mnt_cache_entry *e;
174
175 assert(cache);
176 assert(value);
177 assert(key);
178
179 if (cache->nents == cache->nallocs) {
180 size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
181
182 e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
183 if (!e)
184 return -ENOMEM;
185 cache->ents = e;
186 cache->nallocs = sz;
187 }
188
189 e = &cache->ents[cache->nents];
190 e->key = key;
191 e->value = value;
192 e->flag = flag;
193 cache->nents++;
194
195 DBG(CACHE, ul_debugobj(cache, "add entry [%2zd] (%s): %s: %s",
196 cache->nents,
197 (flag & MNT_CACHE_ISPATH) ? "path" : "tag",
198 value, key));
199 return 0;
200 }
201
202 /* add tag to the cache, @devname has to be an allocated string */
cache_add_tag(struct libmnt_cache * cache,const char * tagname,const char * tagval,char * devname,int flag)203 static int cache_add_tag(struct libmnt_cache *cache, const char *tagname,
204 const char *tagval, char *devname, int flag)
205 {
206 size_t tksz, vlsz;
207 char *key;
208 int rc;
209
210 assert(cache);
211 assert(devname);
212 assert(tagname);
213 assert(tagval);
214
215 /* add into cache -- cache format for TAGs is
216 * key = "TAG_NAME\0TAG_VALUE\0"
217 * value = "/dev/foo"
218 */
219 tksz = strlen(tagname);
220 vlsz = strlen(tagval);
221
222 key = malloc(tksz + vlsz + 2);
223 if (!key)
224 return -ENOMEM;
225
226 memcpy(key, tagname, tksz + 1); /* include '\0' */
227 memcpy(key + tksz + 1, tagval, vlsz + 1);
228
229 rc = cache_add_entry(cache, key, devname, flag | MNT_CACHE_ISTAG);
230 if (!rc)
231 return 0;
232
233 free(key);
234 return rc;
235 }
236
237
238 /*
239 * Returns cached canonicalized path or NULL.
240 */
cache_find_path(struct libmnt_cache * cache,const char * path)241 static const char *cache_find_path(struct libmnt_cache *cache, const char *path)
242 {
243 size_t i;
244
245 assert(cache);
246 assert(path);
247
248 if (!cache || !path)
249 return NULL;
250
251 for (i = 0; i < cache->nents; i++) {
252 struct mnt_cache_entry *e = &cache->ents[i];
253 if (!(e->flag & MNT_CACHE_ISPATH))
254 continue;
255 if (streq_except_trailing_slash(path, e->key))
256 return e->value;
257 }
258 return NULL;
259 }
260
261 /*
262 * Returns cached path or NULL.
263 */
cache_find_tag(struct libmnt_cache * cache,const char * token,const char * value)264 static const char *cache_find_tag(struct libmnt_cache *cache,
265 const char *token, const char *value)
266 {
267 size_t i;
268 size_t tksz;
269
270 assert(cache);
271 assert(token);
272 assert(value);
273
274 if (!cache || !token || !value)
275 return NULL;
276
277 tksz = strlen(token);
278
279 for (i = 0; i < cache->nents; i++) {
280 struct mnt_cache_entry *e = &cache->ents[i];
281 if (!(e->flag & MNT_CACHE_ISTAG))
282 continue;
283 if (strcmp(token, e->key) == 0 &&
284 strcmp(value, e->key + tksz + 1) == 0)
285 return e->value;
286 }
287 return NULL;
288 }
289
cache_find_tag_value(struct libmnt_cache * cache,const char * devname,const char * token)290 static char *cache_find_tag_value(struct libmnt_cache *cache,
291 const char *devname, const char *token)
292 {
293 size_t i;
294
295 assert(cache);
296 assert(devname);
297 assert(token);
298
299 for (i = 0; i < cache->nents; i++) {
300 struct mnt_cache_entry *e = &cache->ents[i];
301 if (!(e->flag & MNT_CACHE_ISTAG))
302 continue;
303 if (strcmp(e->value, devname) == 0 && /* dev name */
304 strcmp(token, e->key) == 0) /* tag name */
305 return e->key + strlen(token) + 1; /* tag value */
306 }
307
308 return NULL;
309 }
310
311 /**
312 * mnt_cache_read_tags
313 * @cache: pointer to struct libmnt_cache instance
314 * @devname: path device
315 *
316 * Reads @devname LABEL and UUID to the @cache.
317 *
318 * Returns: 0 if at least one tag was added, 1 if no tag was added or
319 * negative number in case of error.
320 */
mnt_cache_read_tags(struct libmnt_cache * cache,const char * devname)321 int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
322 {
323 blkid_probe pr;
324 size_t i, ntags = 0;
325 int rc;
326 const char *tags[] = { "LABEL", "UUID", "TYPE", "PARTUUID", "PARTLABEL" };
327 const char *blktags[] = { "LABEL", "UUID", "TYPE", "PART_ENTRY_UUID", "PART_ENTRY_NAME" };
328
329 assert(cache);
330 assert(devname);
331
332 if (!cache || !devname)
333 return -EINVAL;
334
335 DBG(CACHE, ul_debugobj(cache, "tags for %s requested", devname));
336
337 /* check if device is already cached */
338 for (i = 0; i < cache->nents; i++) {
339 struct mnt_cache_entry *e = &cache->ents[i];
340 if (!(e->flag & MNT_CACHE_TAGREAD))
341 continue;
342 if (strcmp(e->value, devname) == 0)
343 /* tags have already been read */
344 return 0;
345 }
346
347 pr = blkid_new_probe_from_filename(devname);
348 if (!pr)
349 return -1;
350
351 blkid_probe_enable_superblocks(pr, 1);
352 blkid_probe_set_superblocks_flags(pr,
353 BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
354 BLKID_SUBLKS_TYPE);
355
356 blkid_probe_enable_partitions(pr, 1);
357 blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
358
359 rc = blkid_do_safeprobe(pr);
360 if (rc)
361 goto error;
362
363 DBG(CACHE, ul_debugobj(cache, "reading tags for: %s", devname));
364
365 for (i = 0; i < ARRAY_SIZE(tags); i++) {
366 const char *data;
367 char *dev;
368
369 if (cache_find_tag_value(cache, devname, tags[i])) {
370 DBG(CACHE, ul_debugobj(cache,
371 "\ntag %s already cached", tags[i]));
372 continue;
373 }
374 if (blkid_probe_lookup_value(pr, blktags[i], &data, NULL))
375 continue;
376 dev = strdup(devname);
377 if (!dev)
378 goto error;
379 if (cache_add_tag(cache, tags[i], data, dev,
380 MNT_CACHE_TAGREAD)) {
381 free(dev);
382 goto error;
383 }
384 ntags++;
385 }
386
387 DBG(CACHE, ul_debugobj(cache, "\tread %zd tags", ntags));
388 blkid_free_probe(pr);
389 return ntags ? 0 : 1;
390 error:
391 blkid_free_probe(pr);
392 return rc < 0 ? rc : -1;
393 }
394
395 /**
396 * mnt_cache_device_has_tag:
397 * @cache: paths cache
398 * @devname: path to the device
399 * @token: tag name (e.g "LABEL")
400 * @value: tag value
401 *
402 * Look up @cache to check if @tag+@value are associated with @devname.
403 *
404 * Returns: 1 on success or 0.
405 */
mnt_cache_device_has_tag(struct libmnt_cache * cache,const char * devname,const char * token,const char * value)406 int mnt_cache_device_has_tag(struct libmnt_cache *cache, const char *devname,
407 const char *token, const char *value)
408 {
409 const char *path = cache_find_tag(cache, token, value);
410
411 if (path && devname && strcmp(path, devname) == 0)
412 return 1;
413 return 0;
414 }
415
__mnt_cache_find_tag_value(struct libmnt_cache * cache,const char * devname,const char * token,char ** data)416 static int __mnt_cache_find_tag_value(struct libmnt_cache *cache,
417 const char *devname, const char *token, char **data)
418 {
419 int rc = 0;
420
421 if (!cache || !devname || !token || !data)
422 return -EINVAL;
423
424 rc = mnt_cache_read_tags(cache, devname);
425 if (rc)
426 return rc;
427
428 *data = cache_find_tag_value(cache, devname, token);
429 return *data ? 0 : -1;
430 }
431
432 /**
433 * mnt_cache_find_tag_value:
434 * @cache: cache for results
435 * @devname: device name
436 * @token: tag name ("LABEL" or "UUID")
437 *
438 * Returns: LABEL or UUID for the @devname or NULL in case of error.
439 */
mnt_cache_find_tag_value(struct libmnt_cache * cache,const char * devname,const char * token)440 char *mnt_cache_find_tag_value(struct libmnt_cache *cache,
441 const char *devname, const char *token)
442 {
443 char *data = NULL;
444
445 if (__mnt_cache_find_tag_value(cache, devname, token, &data) == 0)
446 return data;
447 return NULL;
448 }
449
450 /**
451 * mnt_get_fstype:
452 * @devname: device name
453 * @ambi: returns TRUE if probing result is ambivalent (optional argument)
454 * @cache: cache for results or NULL
455 *
456 * Returns: filesystem type or NULL in case of error. The result has to be
457 * deallocated by free() if @cache is NULL.
458 */
mnt_get_fstype(const char * devname,int * ambi,struct libmnt_cache * cache)459 char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache)
460 {
461 blkid_probe pr;
462 const char *data;
463 char *type = NULL;
464 int rc;
465
466 DBG(CACHE, ul_debugobj(cache, "get %s FS type", devname));
467
468 if (cache) {
469 char *val = NULL;
470 rc = __mnt_cache_find_tag_value(cache, devname, "TYPE", &val);
471 if (ambi)
472 *ambi = rc == -2 ? TRUE : FALSE;
473 return rc ? NULL : val;
474 }
475
476 /*
477 * no cache, probe directly
478 */
479 pr = blkid_new_probe_from_filename(devname);
480 if (!pr)
481 return NULL;
482
483 blkid_probe_enable_superblocks(pr, 1);
484 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
485
486 rc = blkid_do_safeprobe(pr);
487
488 DBG(CACHE, ul_debugobj(cache, "libblkid rc=%d", rc));
489
490 if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
491 type = strdup(data);
492
493 if (ambi)
494 *ambi = rc == -2 ? TRUE : FALSE;
495
496 blkid_free_probe(pr);
497 return type;
498 }
499
canonicalize_path_and_cache(const char * path,struct libmnt_cache * cache)500 static char *canonicalize_path_and_cache(const char *path,
501 struct libmnt_cache *cache)
502 {
503 char *p = NULL;
504 char *key = NULL;
505 char *value = NULL;
506
507 DBG(CACHE, ul_debugobj(cache, "canonicalize path %s", path));
508 p = canonicalize_path(path);
509
510 if (p && cache) {
511 value = p;
512 key = strcmp(path, p) == 0 ? value : strdup(path);
513
514 if (!key || !value)
515 goto error;
516
517 if (cache_add_entry(cache, key, value,
518 MNT_CACHE_ISPATH))
519 goto error;
520 }
521
522 return p;
523 error:
524 if (value != key)
525 free(value);
526 free(key);
527 return NULL;
528 }
529
530 /**
531 * mnt_resolve_path:
532 * @path: "native" path
533 * @cache: cache for results or NULL
534 *
535 * Converts path:
536 * - to the absolute path
537 * - /dev/dm-N to /dev/mapper/name
538 *
539 * Returns: absolute path or NULL in case of error. The result has to be
540 * deallocated by free() if @cache is NULL.
541 */
mnt_resolve_path(const char * path,struct libmnt_cache * cache)542 char *mnt_resolve_path(const char *path, struct libmnt_cache *cache)
543 {
544 char *p = NULL;
545
546 /*DBG(CACHE, ul_debugobj(cache, "resolving path %s", path));*/
547
548 if (!path)
549 return NULL;
550 if (cache)
551 p = (char *) cache_find_path(cache, path);
552 if (!p)
553 p = canonicalize_path_and_cache(path, cache);
554
555 return p;
556 }
557
558 /**
559 * mnt_resolve_target:
560 * @path: "native" path, a potential mount point
561 * @cache: cache for results or NULL.
562 *
563 * Like mnt_resolve_path(), unless @cache is not NULL and
564 * mnt_cache_set_targets(cache, mtab) was called: if @path is found in the
565 * cached @mtab and the matching entry was provided by the kernel, assume that
566 * @path is already canonicalized. By avoiding a call to canonicalize_path() on
567 * known mount points, there is a lower risk of stepping on a stale mount
568 * point, which can result in an application freeze. This is also faster in
569 * general, as stat(2) on a mount point is slower than on a regular file.
570 *
571 * Returns: absolute path or NULL in case of error. The result has to be
572 * deallocated by free() if @cache is NULL.
573 */
mnt_resolve_target(const char * path,struct libmnt_cache * cache)574 char *mnt_resolve_target(const char *path, struct libmnt_cache *cache)
575 {
576 char *p = NULL;
577
578 /*DBG(CACHE, ul_debugobj(cache, "resolving target %s", path));*/
579
580 if (!cache || !cache->mtab)
581 return mnt_resolve_path(path, cache);
582
583 p = (char *) cache_find_path(cache, path);
584 if (p)
585 return p;
586 else {
587 struct libmnt_iter itr;
588 struct libmnt_fs *fs = NULL;
589
590 mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
591 while (mnt_table_next_fs(cache->mtab, &itr, &fs) == 0) {
592
593 if (!mnt_fs_is_kernel(fs)
594 || mnt_fs_is_swaparea(fs)
595 || !mnt_fs_streq_target(fs, path))
596 continue;
597
598 p = strdup(path);
599 if (!p)
600 return NULL; /* ENOMEM */
601
602 if (cache_add_entry(cache, p, p, MNT_CACHE_ISPATH)) {
603 free(p);
604 return NULL; /* ENOMEM */
605 }
606 break;
607 }
608 }
609
610 if (!p)
611 p = canonicalize_path_and_cache(path, cache);
612 return p;
613 }
614
615 /**
616 * mnt_pretty_path:
617 * @path: any path
618 * @cache: NULL or pointer to the cache
619 *
620 * Converts path:
621 * - to the absolute path
622 * - /dev/dm-N to /dev/mapper/name
623 * - /dev/loopN to the loop backing filename
624 * - empty path (NULL) to 'none'
625 *
626 * Returns: newly allocated string with path, result always has to be deallocated
627 * by free().
628 */
mnt_pretty_path(const char * path,struct libmnt_cache * cache)629 char *mnt_pretty_path(const char *path, struct libmnt_cache *cache)
630 {
631 char *pretty = mnt_resolve_path(path, cache);
632
633 if (!pretty)
634 return strdup("none");
635
636 #ifdef __linux__
637 /* users assume backing file name rather than /dev/loopN in
638 * output if the device has been initialized by mount(8).
639 */
640 if (strncmp(pretty, "/dev/loop", 9) == 0) {
641 struct loopdev_cxt lc;
642
643 if (loopcxt_init(&lc, 0) || loopcxt_set_device(&lc, pretty))
644 goto done;
645
646 if (loopcxt_is_autoclear(&lc)) {
647 char *tmp = loopcxt_get_backing_file(&lc);
648 if (tmp) {
649 if (!cache)
650 free(pretty); /* not cached, deallocate */
651 return tmp; /* return backing file */
652 }
653 }
654 loopcxt_deinit(&lc);
655
656 }
657 #endif
658
659 done:
660 /* don't return pointer to the cache, allocate a new string */
661 return cache ? strdup(pretty) : pretty;
662 }
663
664 /**
665 * mnt_resolve_tag:
666 * @token: tag name
667 * @value: tag value
668 * @cache: for results or NULL
669 *
670 * Returns: device name or NULL in case of error. The result has to be
671 * deallocated by free() if @cache is NULL.
672 */
mnt_resolve_tag(const char * token,const char * value,struct libmnt_cache * cache)673 char *mnt_resolve_tag(const char *token, const char *value,
674 struct libmnt_cache *cache)
675 {
676 char *p = NULL;
677
678 assert(token);
679 assert(value);
680
681 /*DBG(CACHE, ul_debugobj(cache, "resolving tag token=%s value=%s",
682 token, value));*/
683
684 if (!token || !value)
685 return NULL;
686
687 if (cache)
688 p = (char *) cache_find_tag(cache, token, value);
689
690 if (!p) {
691 /* returns newly allocated string */
692 p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL);
693
694 if (p && cache &&
695 cache_add_tag(cache, token, value, p, 0))
696 goto error;
697 }
698
699 return p;
700 error:
701 free(p);
702 return NULL;
703 }
704
705
706
707 /**
708 * mnt_resolve_spec:
709 * @spec: path or tag
710 * @cache: paths cache
711 *
712 * Returns: canonicalized path or NULL. The result has to be
713 * deallocated by free() if @cache is NULL.
714 */
mnt_resolve_spec(const char * spec,struct libmnt_cache * cache)715 char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
716 {
717 char *cn = NULL;
718 char *t = NULL, *v = NULL;
719
720 if (!spec)
721 return NULL;
722
723 if (blkid_parse_tag_string(spec, &t, &v) == 0 && mnt_valid_tagname(t))
724 cn = mnt_resolve_tag(t, v, cache);
725 else
726 cn = mnt_resolve_path(spec, cache);
727
728 free(t);
729 free(v);
730 return cn;
731 }
732
733
734 #ifdef TEST_PROGRAM
735
test_resolve_path(struct libmnt_test * ts,int argc,char * argv[])736 int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
737 {
738 char line[BUFSIZ];
739 struct libmnt_cache *cache;
740
741 cache = mnt_new_cache();
742 if (!cache)
743 return -ENOMEM;
744
745 while(fgets(line, sizeof(line), stdin)) {
746 size_t sz = strlen(line);
747 char *p;
748
749 if (sz > 0 && line[sz - 1] == '\n')
750 line[sz - 1] = '\0';
751
752 p = mnt_resolve_path(line, cache);
753 printf("%s : %s\n", line, p);
754 }
755 mnt_unref_cache(cache);
756 return 0;
757 }
758
test_resolve_spec(struct libmnt_test * ts,int argc,char * argv[])759 int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
760 {
761 char line[BUFSIZ];
762 struct libmnt_cache *cache;
763
764 cache = mnt_new_cache();
765 if (!cache)
766 return -ENOMEM;
767
768 while(fgets(line, sizeof(line), stdin)) {
769 size_t sz = strlen(line);
770 char *p;
771
772 if (sz > 0 && line[sz - 1] == '\n')
773 line[sz - 1] = '\0';
774
775 p = mnt_resolve_spec(line, cache);
776 printf("%s : %s\n", line, p);
777 }
778 mnt_unref_cache(cache);
779 return 0;
780 }
781
test_read_tags(struct libmnt_test * ts,int argc,char * argv[])782 int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
783 {
784 char line[BUFSIZ];
785 struct libmnt_cache *cache;
786 size_t i;
787
788 cache = mnt_new_cache();
789 if (!cache)
790 return -ENOMEM;
791
792 while(fgets(line, sizeof(line), stdin)) {
793 size_t sz = strlen(line);
794 char *t = NULL, *v = NULL;
795
796 if (sz > 0 && line[sz - 1] == '\n')
797 line[sz - 1] = '\0';
798
799 if (!strcmp(line, "quit"))
800 break;
801
802 if (*line == '/') {
803 if (mnt_cache_read_tags(cache, line) < 0)
804 fprintf(stderr, "%s: read tags failed\n", line);
805
806 } else if (blkid_parse_tag_string(line, &t, &v) == 0) {
807 const char *cn = NULL;
808
809 if (mnt_valid_tagname(t))
810 cn = cache_find_tag(cache, t, v);
811 free(t);
812 free(v);
813
814 if (cn)
815 printf("%s: %s\n", line, cn);
816 else
817 printf("%s: not cached\n", line);
818 }
819 }
820
821 for (i = 0; i < cache->nents; i++) {
822 struct mnt_cache_entry *e = &cache->ents[i];
823 if (!(e->flag & MNT_CACHE_ISTAG))
824 continue;
825
826 printf("%15s : %5s : %s\n", e->value, e->key,
827 e->key + strlen(e->key) + 1);
828 }
829
830 mnt_unref_cache(cache);
831 return 0;
832
833 }
834
main(int argc,char * argv[])835 int main(int argc, char *argv[])
836 {
837 struct libmnt_test ts[] = {
838 { "--resolve-path", test_resolve_path, " resolve paths from stdin" },
839 { "--resolve-spec", test_resolve_spec, " evaluate specs from stdin" },
840 { "--read-tags", test_read_tags, " read devname or TAG from stdin (\"quit\" to exit)" },
841 { NULL }
842 };
843
844 return mnt_run_test(ts, argc, argv);
845 }
846 #endif
847