~ubuntu-branches/ubuntu/precise/util-linux/precise-proposed

« back to all changes in this revision

Viewing changes to shlibs/mount/src/cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Langasek
  • Date: 2011-06-20 22:31:50 UTC
  • mfrom: (1.6.3 upstream) (4.5.1 sid)
  • Revision ID: james.westby@ubuntu.com-20110620223150-lz8wrv0946ihcz3z
Tags: 2.19.1-2ubuntu1
* Merge from Debian unstable, remaining changes:
  - Build for multiarch.
  - Add pre-depends on multiarch-support.
  - configure.ac: don't try to be clever about extracting a path name from
    $libdir to append to /usr in a way that's not overridable; instead,
    reuse the built-in configurable libexecdir.
  - Fix up the .pc.in files to know about libexecdir, so our substitutions
    don't leave us with unusable pkg-config files.
  - Install custom blkid.conf to use /dev/.blkid.tab since we don't
    expect device names to survive a reboot
  - Mention mountall(8) in fstab(5) manpages, along with its special
    options.
  - Since upstart is required in Ubuntu, the hwclock.sh init script is not
    called on startup and the hwclockfirst.sh init script is removed.
  - Drop depends on initscripts for the above.
  - Replace hwclock udev rule with an Upstart job.
  - For the case where mount is called with a directory to mount, look
    that directory up in mountall's /lib/init/fstab if we couldn't find
    it mentioned anywhere else.  This means "mount /proc", "mount /sys",
    etc. work.
  - mount.8 points to the cifs-utils package, not the obsolete smbfs one. 
* Dropped changes:
  - mount.preinst: lsb_release has been fixed in lucid and above to be
    usable without configuration, so we don't have to diverge from Debian
    here anymore.
* Changes merged upstream:
  - sfdisk support for '+' with '-N'
  - mount/umount.c: fix a segfault on umount with empty mtab entry
  - Fix arbitrary unmount with fuse security issue

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2009 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 work with tags (LABEL, UUID, ...) and
 
14
 * paths. The cache uses libblkid as a backend from 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/types.h>
 
23
#include <sys/stat.h>
 
24
#include <unistd.h>
 
25
#include <fcntl.h>
 
26
#include <blkid.h>
 
27
 
 
28
#include "canonicalize.h"
 
29
#include "mountP.h"
 
30
 
 
31
/*
 
32
 * Canonicalized (resolved) paths & tags cache
 
33
 */
 
34
#define MNT_CACHE_CHUNKSZ       128
 
35
 
 
36
#define MNT_CACHE_ISTAG         (1 << 1) /* entry is TAG */
 
37
#define MNT_CACHE_ISPATH        (1 << 2) /* entry is path */
 
38
#define MNT_CACHE_TAGREAD       (1 << 3) /* tag read by mnt_cache_read_tags() */
 
39
 
 
40
/* path cache entry */
 
41
struct mnt_cache_entry {
 
42
        char                    *native;        /* the original path */
 
43
        char                    *real;          /* canonicalized path */
 
44
        int                     flag;
 
45
};
 
46
 
 
47
struct libmnt_cache {
 
48
        struct mnt_cache_entry  *ents;
 
49
        size_t                  nents;
 
50
        size_t                  nallocs;
 
51
 
 
52
        /* blkid_evaluate_tag() works in two ways:
 
53
         *
 
54
         * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
 
55
         *    then the blkid_cache is NULL.
 
56
         *
 
57
         * 2/ all tags are read from /etc/blkid.tab and verified by /dev
 
58
         *    scanning, then the blkid_cache is not NULL and then it's
 
59
         *    better to reuse the blkid_cache.
 
60
         */
 
61
        blkid_cache             bc;
 
62
        blkid_probe             pr;
 
63
 
 
64
        char                    *filename;
 
65
};
 
66
 
 
67
/**
 
68
 * mnt_new_cache:
 
69
 *
 
70
 * Returns: new struct libmnt_cache instance or NULL in case of ENOMEM error.
 
71
 */
 
72
struct libmnt_cache *mnt_new_cache(void)
 
73
{
 
74
        struct libmnt_cache *cache = calloc(1, sizeof(*cache));
 
75
        if (!cache)
 
76
                return NULL;
 
77
        DBG(CACHE, mnt_debug_h(cache, "alloc"));
 
78
        return cache;
 
79
}
 
80
 
 
81
/**
 
82
 * mnt_free_cache:
 
83
 * @cache: pointer to struct libmnt_cache instance
 
84
 *
 
85
 * Deallocates the cache.
 
86
 */
 
87
void mnt_free_cache(struct libmnt_cache *cache)
 
88
{
 
89
        int i;
 
90
 
 
91
        if (!cache)
 
92
                return;
 
93
 
 
94
        DBG(CACHE, mnt_debug_h(cache, "free"));
 
95
 
 
96
        for (i = 0; i < cache->nents; i++) {
 
97
                struct mnt_cache_entry *e = &cache->ents[i];
 
98
                if (e->real != e->native)
 
99
                        free(e->real);
 
100
                free(e->native);
 
101
        }
 
102
        free(cache->ents);
 
103
        free(cache->filename);
 
104
        if (cache->bc)
 
105
                blkid_put_cache(cache->bc);
 
106
        blkid_free_probe(cache->pr);
 
107
        free(cache);
 
108
}
 
109
 
 
110
/* note that the @native could be tha same pointer as @real */
 
111
static int mnt_cache_add_entry(struct libmnt_cache *cache, char *native,
 
112
                                        char *real, int flag)
 
113
{
 
114
        struct mnt_cache_entry *e;
 
115
 
 
116
        assert(cache);
 
117
        assert(real);
 
118
        assert(native);
 
119
 
 
120
        if (cache->nents == cache->nallocs) {
 
121
                size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
 
122
 
 
123
                e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
 
124
                if (!e)
 
125
                        return -ENOMEM;
 
126
                cache->ents = e;
 
127
                cache->nallocs = sz;
 
128
        }
 
129
 
 
130
        e = &cache->ents[cache->nents];
 
131
        e->native = native;
 
132
        e->real = real;
 
133
        e->flag = flag;
 
134
        cache->nents++;
 
135
 
 
136
        DBG(CACHE, mnt_debug_h(cache, "add entry [%2zd] (%s): %s: %s",
 
137
                        cache->nents,
 
138
                        (flag & MNT_CACHE_ISPATH) ? "path" : "tag",
 
139
                        real, native));
 
140
        return 0;
 
141
}
 
142
 
 
143
/* add tag to the cache, @real has to be allocated string */
 
144
static int mnt_cache_add_tag(struct libmnt_cache *cache, const char *token,
 
145
                                const char *value, char *real, int flag)
 
146
{
 
147
        size_t tksz, vlsz;
 
148
        char *native;
 
149
        int rc;
 
150
 
 
151
        assert(cache);
 
152
        assert(real);
 
153
        assert(token);
 
154
        assert(value);
 
155
 
 
156
        /* add into cache -- cache format for TAGs is
 
157
         *      native = "NAME\0VALUE\0"
 
158
         *      real   = "/dev/foo"
 
159
         */
 
160
        tksz = strlen(token);
 
161
        vlsz = strlen(value);
 
162
 
 
163
        native = malloc(tksz + vlsz + 2);
 
164
        if (!native)
 
165
                return -ENOMEM;
 
166
 
 
167
        memcpy(native, token, tksz + 1);           /* include '\0' */
 
168
        memcpy(native + tksz + 1, value, vlsz + 1);
 
169
 
 
170
        rc = mnt_cache_add_entry(cache, native, real, flag | MNT_CACHE_ISTAG);
 
171
        if (!rc)
 
172
                return 0;
 
173
 
 
174
        free(native);
 
175
        return rc;
 
176
}
 
177
 
 
178
 
 
179
/*
 
180
 * Returns cached canonicalized path or NULL.
 
181
 */
 
182
static const char *mnt_cache_find_path(struct libmnt_cache *cache, const char *path)
 
183
{
 
184
        int i;
 
185
 
 
186
        assert(cache);
 
187
        assert(path);
 
188
 
 
189
        if (!cache || !path)
 
190
                return NULL;
 
191
 
 
192
        for (i = 0; i < cache->nents; i++) {
 
193
                struct mnt_cache_entry *e = &cache->ents[i];
 
194
                if (!(e->flag & MNT_CACHE_ISPATH))
 
195
                        continue;
 
196
                if (strcmp(path, e->native) == 0)
 
197
                        return e->real;
 
198
        }
 
199
        return NULL;
 
200
}
 
201
 
 
202
/*
 
203
 * Returns cached path or NULL.
 
204
 */
 
205
static const char *mnt_cache_find_tag(struct libmnt_cache *cache,
 
206
                        const char *token, const char *value)
 
207
{
 
208
        int i;
 
209
        size_t tksz;
 
210
 
 
211
        assert(cache);
 
212
        assert(token);
 
213
        assert(value);
 
214
 
 
215
        if (!cache || !token || !value)
 
216
                return NULL;
 
217
 
 
218
        tksz = strlen(token);
 
219
 
 
220
        for (i = 0; i < cache->nents; i++) {
 
221
                struct mnt_cache_entry *e = &cache->ents[i];
 
222
                if (!(e->flag & MNT_CACHE_ISTAG))
 
223
                        continue;
 
224
                if (strcmp(token, e->native) == 0 &&
 
225
                    strcmp(value, e->native + tksz + 1) == 0)
 
226
                        return e->real;
 
227
        }
 
228
        return NULL;
 
229
}
 
230
 
 
231
/*
 
232
 * returns (in @res) blkid prober, the @cache argument is optional
 
233
 */
 
234
static int mnt_cache_get_probe(struct libmnt_cache *cache, const char *devname,
 
235
                           blkid_probe *res)
 
236
{
 
237
        blkid_probe pr = cache ? cache->pr : NULL;
 
238
 
 
239
        assert(devname);
 
240
        assert(res);
 
241
 
 
242
        if (cache && cache->pr && (!cache->filename ||
 
243
                                   strcmp(devname, cache->filename))) {
 
244
                blkid_free_probe(cache->pr);
 
245
                free(cache->filename);
 
246
                cache->filename = NULL;
 
247
                pr = cache->pr = NULL;
 
248
        }
 
249
 
 
250
        if (!pr) {
 
251
                pr = blkid_new_probe_from_filename(devname);
 
252
                if (!pr)
 
253
                        return -1;
 
254
                if (cache) {
 
255
                        cache->pr = pr;
 
256
                        cache->filename = strdup(devname);
 
257
                        if (!cache->filename)
 
258
                                return -ENOMEM;
 
259
                }
 
260
 
 
261
        }
 
262
 
 
263
        *res = pr;
 
264
        return 0;
 
265
}
 
266
 
 
267
/**
 
268
 * mnt_cache_read_tags
 
269
 * @cache: pointer to struct libmnt_cache instance
 
270
 * @devname: path device
 
271
 *
 
272
 * Reads @devname LABEL and UUID to the @cache.
 
273
 *
 
274
 * Returns: 0 if at least one tag was added, 1 if no tag was added or
 
275
 *          negative number in case of error.
 
276
 */
 
277
int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
 
278
{
 
279
        int i, ntags = 0, rc;
 
280
        blkid_probe pr;
 
281
        const char *tags[] = { "LABEL", "UUID", "TYPE" };
 
282
 
 
283
        assert(cache);
 
284
        assert(devname);
 
285
 
 
286
        if (!cache || !devname)
 
287
                return -EINVAL;
 
288
 
 
289
        DBG(CACHE, mnt_debug_h(cache, "tags for %s requested", devname));
 
290
 
 
291
        /* check is device is already cached */
 
292
        for (i = 0; i < cache->nents; i++) {
 
293
                struct mnt_cache_entry *e = &cache->ents[i];
 
294
                if (!(e->flag & MNT_CACHE_TAGREAD))
 
295
                        continue;
 
296
                if (strcmp(e->real, devname) == 0)
 
297
                        /* tags has been already read */
 
298
                        return 0;
 
299
        }
 
300
 
 
301
        rc = mnt_cache_get_probe(cache, devname, &pr);
 
302
        if (rc)
 
303
                return rc;
 
304
 
 
305
        blkid_probe_enable_superblocks(cache->pr, 1);
 
306
 
 
307
        blkid_probe_set_superblocks_flags(cache->pr,
 
308
                        BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
 
309
                        BLKID_SUBLKS_TYPE);
 
310
 
 
311
        if (blkid_do_safeprobe(cache->pr))
 
312
                goto error;
 
313
 
 
314
        DBG(CACHE, mnt_debug_h(cache, "reading tags for: %s", devname));
 
315
 
 
316
        for (i = 0; i < ARRAY_SIZE(tags); i++) {
 
317
                const char *data;
 
318
                char *dev;
 
319
 
 
320
                if (blkid_probe_lookup_value(cache->pr, tags[i], &data, NULL))
 
321
                        continue;
 
322
                if (mnt_cache_find_tag(cache, tags[i], data))
 
323
                        continue; /* already cached */
 
324
 
 
325
                dev = strdup(devname);
 
326
                if (!dev)
 
327
                        goto error;
 
328
                if (mnt_cache_add_tag(cache, tags[i], data, dev,
 
329
                                        MNT_CACHE_TAGREAD)) {
 
330
                        free(dev);
 
331
                        goto error;
 
332
                }
 
333
                ntags++;
 
334
        }
 
335
 
 
336
        return ntags ? 0 : 1;
 
337
error:
 
338
        return -1;
 
339
}
 
340
 
 
341
/**
 
342
 * mnt_cache_device_has_tag:
 
343
 * @cache: paths cache
 
344
 * @devname: path to the device
 
345
 * @token: tag name (e.g "LABEL")
 
346
 * @value: tag value
 
347
 *
 
348
 * Look up @cache to check it @tag+@value are associated with @devname.
 
349
 *
 
350
 * Returns: 1 on success or 0.
 
351
 */
 
352
int mnt_cache_device_has_tag(struct libmnt_cache *cache, const char *devname,
 
353
                                const char *token, const char *value)
 
354
{
 
355
        const char *path = mnt_cache_find_tag(cache, token, value);
 
356
 
 
357
        if (path && strcmp(path, devname) == 0)
 
358
                return 1;
 
359
        return 0;
 
360
}
 
361
 
 
362
/**
 
363
 * mnt_cache_find_tag_value:
 
364
 * @cache: cache for results
 
365
 * @devname: device name
 
366
 * @token: tag name ("LABEL" or "UUID")
 
367
 *
 
368
 * Returns: LABEL or UUID for the @devname or NULL in case of error.
 
369
 */
 
370
char *mnt_cache_find_tag_value(struct libmnt_cache *cache,
 
371
                const char *devname, const char *token)
 
372
{
 
373
        int i;
 
374
 
 
375
        if (!cache || !devname || !token)
 
376
                return NULL;
 
377
 
 
378
        if (mnt_cache_read_tags(cache, devname) != 0)
 
379
                return NULL;
 
380
 
 
381
        for (i = 0; i < cache->nents; i++) {
 
382
                struct mnt_cache_entry *e = &cache->ents[i];
 
383
                if (!(e->flag & MNT_CACHE_ISTAG))
 
384
                        continue;
 
385
                if (strcmp(e->real, devname) == 0 &&    /* dev name */
 
386
                    strcmp(token, e->native) == 0)      /* tag name */
 
387
                        return e->native + strlen(token) + 1;   /* tag value */
 
388
        }
 
389
 
 
390
        return NULL;
 
391
}
 
392
 
 
393
/**
 
394
 * mnt_get_fstype:
 
395
 * @devname: device name
 
396
 * @ambi: returns TRUE if probing result is ambivalent (optional argument)
 
397
 * @cache: cache for results or NULL
 
398
 *
 
399
 * Returns: filesystem type or NULL in case of error. The result has to be
 
400
 * deallocated by free() if @cache is NULL.
 
401
 */
 
402
char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache)
 
403
{
 
404
        blkid_probe pr;
 
405
        const char *data;
 
406
        char *type = NULL;
 
407
        int rc;
 
408
 
 
409
        DBG(CACHE, mnt_debug_h(cache, "get %s FS type", devname));
 
410
 
 
411
        if (cache)
 
412
                return mnt_cache_find_tag_value(cache, devname, "TYPE");
 
413
 
 
414
        if (mnt_cache_get_probe(NULL, devname, &pr))
 
415
                return NULL;
 
416
 
 
417
        blkid_probe_enable_superblocks(pr, 1);
 
418
 
 
419
        blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
 
420
 
 
421
        rc = blkid_do_safeprobe(pr);
 
422
 
 
423
        if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
 
424
                type = strdup(data);
 
425
 
 
426
        if (ambi)
 
427
                *ambi = rc == -2 ? TRUE : FALSE;
 
428
 
 
429
        blkid_free_probe(pr);
 
430
        return type;
 
431
}
 
432
 
 
433
/**
 
434
 * mnt_resolve_path:
 
435
 * @path: "native" path
 
436
 * @cache: cache for results or NULL
 
437
 *
 
438
 * Returns: absolute path or NULL in case of error. The result has to be
 
439
 * deallocated by free() if @cache is NULL.
 
440
 */
 
441
char *mnt_resolve_path(const char *path, struct libmnt_cache *cache)
 
442
{
 
443
        char *p = NULL;
 
444
        char *native = NULL;
 
445
        char *real = NULL;
 
446
 
 
447
        assert(path);
 
448
 
 
449
        DBG(CACHE, mnt_debug_h(cache, "resolving path %s", path));
 
450
 
 
451
        if (!path)
 
452
                return NULL;
 
453
        if (cache)
 
454
                p = (char *) mnt_cache_find_path(cache, path);
 
455
 
 
456
        if (!p) {
 
457
                p = canonicalize_path(path);
 
458
 
 
459
                if (p && cache) {
 
460
                        real = p;
 
461
                        native = strcmp(path, p) == 0 ? real : strdup(path);
 
462
 
 
463
                        if (!native || !real)
 
464
                                goto error;
 
465
 
 
466
                        if (mnt_cache_add_entry(cache, native, real,
 
467
                                                        MNT_CACHE_ISPATH))
 
468
                                goto error;
 
469
                }
 
470
        }
 
471
 
 
472
        return p;
 
473
error:
 
474
        if (real != native)
 
475
                free(real);
 
476
        free(native);
 
477
        return NULL;
 
478
}
 
479
 
 
480
/**
 
481
 * mnt_resolve_tag:
 
482
 * @token: tag name
 
483
 * @value: tag value
 
484
 * @cache: for results or NULL
 
485
 *
 
486
 * Returns: device name or NULL in case of error. The result has to be
 
487
 * deallocated by free() if @cache is NULL.
 
488
 */
 
489
char *mnt_resolve_tag(const char *token, const char *value,
 
490
                      struct libmnt_cache *cache)
 
491
{
 
492
        char *p = NULL;
 
493
 
 
494
        assert(token);
 
495
        assert(value);
 
496
 
 
497
        DBG(CACHE, mnt_debug_h(cache, "resolving tag token=%s value=%s",
 
498
                                token, value));
 
499
 
 
500
        if (!token || !value)
 
501
                return NULL;
 
502
 
 
503
        if (cache)
 
504
                p = (char *) mnt_cache_find_tag(cache, token, value);
 
505
 
 
506
        if (!p) {
 
507
                /* returns newly allocated string */
 
508
                p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL);
 
509
 
 
510
                if (p && cache &&
 
511
                    mnt_cache_add_tag(cache, token, value, p, 0))
 
512
                                goto error;
 
513
        }
 
514
 
 
515
        return p;
 
516
error:
 
517
        free(p);
 
518
        return NULL;
 
519
}
 
520
 
 
521
 
 
522
 
 
523
/**
 
524
 * mnt_resolve_spec:
 
525
 * @spec: path or tag
 
526
 * @cache: paths cache
 
527
 *
 
528
 * Returns: canonicalized path or NULL. The result has to be
 
529
 * deallocated by free() if @cache is NULL.
 
530
 */
 
531
char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
 
532
{
 
533
        char *cn = NULL;
 
534
 
 
535
        if (!spec)
 
536
                return NULL;
 
537
 
 
538
        if (strchr(spec, '=')) {
 
539
                char *tag, *val;
 
540
 
 
541
                if (!blkid_parse_tag_string(spec, &tag, &val)) {
 
542
                        cn = mnt_resolve_tag(tag, val, cache);
 
543
 
 
544
                        free(tag);
 
545
                        free(val);
 
546
                }
 
547
        } else
 
548
                cn = mnt_resolve_path(spec, cache);
 
549
 
 
550
        return cn;
 
551
}
 
552
 
 
553
 
 
554
#ifdef TEST_PROGRAM
 
555
 
 
556
int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
 
557
{
 
558
        char line[BUFSIZ];
 
559
        struct libmnt_cache *cache;
 
560
 
 
561
        cache = mnt_new_cache();
 
562
        if (!cache)
 
563
                return -ENOMEM;
 
564
 
 
565
        while(fgets(line, sizeof(line), stdin)) {
 
566
                size_t sz = strlen(line);
 
567
                char *p;
 
568
 
 
569
                if (line[sz - 1] == '\n')
 
570
                        line[sz - 1] = '\0';
 
571
 
 
572
                p = mnt_resolve_path(line, cache);
 
573
                printf("%s : %s\n", line, p);
 
574
        }
 
575
        mnt_free_cache(cache);
 
576
        return 0;
 
577
}
 
578
 
 
579
int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
 
580
{
 
581
        char line[BUFSIZ];
 
582
        struct libmnt_cache *cache;
 
583
 
 
584
        cache = mnt_new_cache();
 
585
        if (!cache)
 
586
                return -ENOMEM;
 
587
 
 
588
        while(fgets(line, sizeof(line), stdin)) {
 
589
                size_t sz = strlen(line);
 
590
                char *p;
 
591
 
 
592
                if (line[sz - 1] == '\n')
 
593
                        line[sz - 1] = '\0';
 
594
 
 
595
                p = mnt_resolve_spec(line, cache);
 
596
                printf("%s : %s\n", line, p);
 
597
        }
 
598
        mnt_free_cache(cache);
 
599
        return 0;
 
600
}
 
601
 
 
602
int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
 
603
{
 
604
        char line[BUFSIZ];
 
605
        struct libmnt_cache *cache;
 
606
        int i;
 
607
 
 
608
        cache = mnt_new_cache();
 
609
        if (!cache)
 
610
                return -ENOMEM;
 
611
 
 
612
        while(fgets(line, sizeof(line), stdin)) {
 
613
                size_t sz = strlen(line);
 
614
 
 
615
                if (line[sz - 1] == '\n')
 
616
                        line[sz - 1] = '\0';
 
617
 
 
618
                if (!strcmp(line, "quit"))
 
619
                        break;
 
620
 
 
621
                if (*line == '/') {
 
622
                        if (mnt_cache_read_tags(cache, line) < 0)
 
623
                                fprintf(stderr, "%s: read tags faild\n", line);
 
624
 
 
625
                } else if (strchr(line, '=')) {
 
626
                        char *tag, *val;
 
627
                        const char *cn = NULL;
 
628
 
 
629
                        if (!blkid_parse_tag_string(line, &tag, &val)) {
 
630
                                cn = mnt_cache_find_tag(cache, tag, val);
 
631
 
 
632
                                free(tag);
 
633
                                free(val);
 
634
                        }
 
635
                        if (cn)
 
636
                                printf("%s: %s\n", line, cn);
 
637
                        else
 
638
                                printf("%s: not cached\n", line);
 
639
                }
 
640
        }
 
641
 
 
642
        for (i = 0; i < cache->nents; i++) {
 
643
                struct mnt_cache_entry *e = &cache->ents[i];
 
644
                if (!(e->flag & MNT_CACHE_ISTAG))
 
645
                        continue;
 
646
 
 
647
                printf("%15s : %5s : %s\n", e->real, e->native,
 
648
                                e->native + strlen(e->native) + 1);
 
649
        }
 
650
 
 
651
        mnt_free_cache(cache);
 
652
        return 0;
 
653
 
 
654
}
 
655
 
 
656
int main(int argc, char *argv[])
 
657
{
 
658
        struct libmnt_test ts[] = {
 
659
                { "--resolve-path", test_resolve_path, "  resolve paths from stdin" },
 
660
                { "--resolve-spec", test_resolve_spec, "  evaluate specs from stdin" },
 
661
                { "--read-tags", test_read_tags,       "  read devname or TAG from stdin (\"quit\" to exit)" },
 
662
                { NULL }
 
663
        };
 
664
 
 
665
        return mnt_run_test(ts, argc, argv);
 
666
}
 
667
#endif