2
* Copyright (C) 2009 Karel Zak <kzak@redhat.com>
4
* This file may be redistributed under the terms of the
5
* GNU Lesser General Public License.
11
* @short_description: paths and tags (UUID/LABEL) caching
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.
16
* All returned paths are always canonicalized.
22
#include <sys/types.h>
28
#include "canonicalize.h"
32
* Canonicalized (resolved) paths & tags cache
34
#define MNT_CACHE_CHUNKSZ 128
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() */
40
/* path cache entry */
41
struct mnt_cache_entry {
42
char *native; /* the original path */
43
char *real; /* canonicalized path */
48
struct mnt_cache_entry *ents;
52
/* blkid_evaluate_tag() works in two ways:
54
* 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
55
* then the blkid_cache is NULL.
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.
70
* Returns: new struct libmnt_cache instance or NULL in case of ENOMEM error.
72
struct libmnt_cache *mnt_new_cache(void)
74
struct libmnt_cache *cache = calloc(1, sizeof(*cache));
77
DBG(CACHE, mnt_debug_h(cache, "alloc"));
83
* @cache: pointer to struct libmnt_cache instance
85
* Deallocates the cache.
87
void mnt_free_cache(struct libmnt_cache *cache)
94
DBG(CACHE, mnt_debug_h(cache, "free"));
96
for (i = 0; i < cache->nents; i++) {
97
struct mnt_cache_entry *e = &cache->ents[i];
98
if (e->real != e->native)
103
free(cache->filename);
105
blkid_put_cache(cache->bc);
106
blkid_free_probe(cache->pr);
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)
114
struct mnt_cache_entry *e;
120
if (cache->nents == cache->nallocs) {
121
size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
123
e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
130
e = &cache->ents[cache->nents];
136
DBG(CACHE, mnt_debug_h(cache, "add entry [%2zd] (%s): %s: %s",
138
(flag & MNT_CACHE_ISPATH) ? "path" : "tag",
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)
156
/* add into cache -- cache format for TAGs is
157
* native = "NAME\0VALUE\0"
160
tksz = strlen(token);
161
vlsz = strlen(value);
163
native = malloc(tksz + vlsz + 2);
167
memcpy(native, token, tksz + 1); /* include '\0' */
168
memcpy(native + tksz + 1, value, vlsz + 1);
170
rc = mnt_cache_add_entry(cache, native, real, flag | MNT_CACHE_ISTAG);
180
* Returns cached canonicalized path or NULL.
182
static const char *mnt_cache_find_path(struct libmnt_cache *cache, const char *path)
192
for (i = 0; i < cache->nents; i++) {
193
struct mnt_cache_entry *e = &cache->ents[i];
194
if (!(e->flag & MNT_CACHE_ISPATH))
196
if (strcmp(path, e->native) == 0)
203
* Returns cached path or NULL.
205
static const char *mnt_cache_find_tag(struct libmnt_cache *cache,
206
const char *token, const char *value)
215
if (!cache || !token || !value)
218
tksz = strlen(token);
220
for (i = 0; i < cache->nents; i++) {
221
struct mnt_cache_entry *e = &cache->ents[i];
222
if (!(e->flag & MNT_CACHE_ISTAG))
224
if (strcmp(token, e->native) == 0 &&
225
strcmp(value, e->native + tksz + 1) == 0)
232
* returns (in @res) blkid prober, the @cache argument is optional
234
static int mnt_cache_get_probe(struct libmnt_cache *cache, const char *devname,
237
blkid_probe pr = cache ? cache->pr : NULL;
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;
251
pr = blkid_new_probe_from_filename(devname);
256
cache->filename = strdup(devname);
257
if (!cache->filename)
268
* mnt_cache_read_tags
269
* @cache: pointer to struct libmnt_cache instance
270
* @devname: path device
272
* Reads @devname LABEL and UUID to the @cache.
274
* Returns: 0 if at least one tag was added, 1 if no tag was added or
275
* negative number in case of error.
277
int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
279
int i, ntags = 0, rc;
281
const char *tags[] = { "LABEL", "UUID", "TYPE" };
286
if (!cache || !devname)
289
DBG(CACHE, mnt_debug_h(cache, "tags for %s requested", devname));
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))
296
if (strcmp(e->real, devname) == 0)
297
/* tags has been already read */
301
rc = mnt_cache_get_probe(cache, devname, &pr);
305
blkid_probe_enable_superblocks(cache->pr, 1);
307
blkid_probe_set_superblocks_flags(cache->pr,
308
BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
311
if (blkid_do_safeprobe(cache->pr))
314
DBG(CACHE, mnt_debug_h(cache, "reading tags for: %s", devname));
316
for (i = 0; i < ARRAY_SIZE(tags); i++) {
320
if (blkid_probe_lookup_value(cache->pr, tags[i], &data, NULL))
322
if (mnt_cache_find_tag(cache, tags[i], data))
323
continue; /* already cached */
325
dev = strdup(devname);
328
if (mnt_cache_add_tag(cache, tags[i], data, dev,
329
MNT_CACHE_TAGREAD)) {
336
return ntags ? 0 : 1;
342
* mnt_cache_device_has_tag:
343
* @cache: paths cache
344
* @devname: path to the device
345
* @token: tag name (e.g "LABEL")
348
* Look up @cache to check it @tag+@value are associated with @devname.
350
* Returns: 1 on success or 0.
352
int mnt_cache_device_has_tag(struct libmnt_cache *cache, const char *devname,
353
const char *token, const char *value)
355
const char *path = mnt_cache_find_tag(cache, token, value);
357
if (path && strcmp(path, devname) == 0)
363
* mnt_cache_find_tag_value:
364
* @cache: cache for results
365
* @devname: device name
366
* @token: tag name ("LABEL" or "UUID")
368
* Returns: LABEL or UUID for the @devname or NULL in case of error.
370
char *mnt_cache_find_tag_value(struct libmnt_cache *cache,
371
const char *devname, const char *token)
375
if (!cache || !devname || !token)
378
if (mnt_cache_read_tags(cache, devname) != 0)
381
for (i = 0; i < cache->nents; i++) {
382
struct mnt_cache_entry *e = &cache->ents[i];
383
if (!(e->flag & MNT_CACHE_ISTAG))
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 */
395
* @devname: device name
396
* @ambi: returns TRUE if probing result is ambivalent (optional argument)
397
* @cache: cache for results or NULL
399
* Returns: filesystem type or NULL in case of error. The result has to be
400
* deallocated by free() if @cache is NULL.
402
char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache)
409
DBG(CACHE, mnt_debug_h(cache, "get %s FS type", devname));
412
return mnt_cache_find_tag_value(cache, devname, "TYPE");
414
if (mnt_cache_get_probe(NULL, devname, &pr))
417
blkid_probe_enable_superblocks(pr, 1);
419
blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
421
rc = blkid_do_safeprobe(pr);
423
if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
427
*ambi = rc == -2 ? TRUE : FALSE;
429
blkid_free_probe(pr);
435
* @path: "native" path
436
* @cache: cache for results or NULL
438
* Returns: absolute path or NULL in case of error. The result has to be
439
* deallocated by free() if @cache is NULL.
441
char *mnt_resolve_path(const char *path, struct libmnt_cache *cache)
449
DBG(CACHE, mnt_debug_h(cache, "resolving path %s", path));
454
p = (char *) mnt_cache_find_path(cache, path);
457
p = canonicalize_path(path);
461
native = strcmp(path, p) == 0 ? real : strdup(path);
463
if (!native || !real)
466
if (mnt_cache_add_entry(cache, native, real,
484
* @cache: for results or NULL
486
* Returns: device name or NULL in case of error. The result has to be
487
* deallocated by free() if @cache is NULL.
489
char *mnt_resolve_tag(const char *token, const char *value,
490
struct libmnt_cache *cache)
497
DBG(CACHE, mnt_debug_h(cache, "resolving tag token=%s value=%s",
500
if (!token || !value)
504
p = (char *) mnt_cache_find_tag(cache, token, value);
507
/* returns newly allocated string */
508
p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL);
511
mnt_cache_add_tag(cache, token, value, p, 0))
526
* @cache: paths cache
528
* Returns: canonicalized path or NULL. The result has to be
529
* deallocated by free() if @cache is NULL.
531
char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
538
if (strchr(spec, '=')) {
541
if (!blkid_parse_tag_string(spec, &tag, &val)) {
542
cn = mnt_resolve_tag(tag, val, cache);
548
cn = mnt_resolve_path(spec, cache);
556
int test_resolve_path(struct libmnt_test *ts, int argc, char *argv[])
559
struct libmnt_cache *cache;
561
cache = mnt_new_cache();
565
while(fgets(line, sizeof(line), stdin)) {
566
size_t sz = strlen(line);
569
if (line[sz - 1] == '\n')
572
p = mnt_resolve_path(line, cache);
573
printf("%s : %s\n", line, p);
575
mnt_free_cache(cache);
579
int test_resolve_spec(struct libmnt_test *ts, int argc, char *argv[])
582
struct libmnt_cache *cache;
584
cache = mnt_new_cache();
588
while(fgets(line, sizeof(line), stdin)) {
589
size_t sz = strlen(line);
592
if (line[sz - 1] == '\n')
595
p = mnt_resolve_spec(line, cache);
596
printf("%s : %s\n", line, p);
598
mnt_free_cache(cache);
602
int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
605
struct libmnt_cache *cache;
608
cache = mnt_new_cache();
612
while(fgets(line, sizeof(line), stdin)) {
613
size_t sz = strlen(line);
615
if (line[sz - 1] == '\n')
618
if (!strcmp(line, "quit"))
622
if (mnt_cache_read_tags(cache, line) < 0)
623
fprintf(stderr, "%s: read tags faild\n", line);
625
} else if (strchr(line, '=')) {
627
const char *cn = NULL;
629
if (!blkid_parse_tag_string(line, &tag, &val)) {
630
cn = mnt_cache_find_tag(cache, tag, val);
636
printf("%s: %s\n", line, cn);
638
printf("%s: not cached\n", line);
642
for (i = 0; i < cache->nents; i++) {
643
struct mnt_cache_entry *e = &cache->ents[i];
644
if (!(e->flag & MNT_CACHE_ISTAG))
647
printf("%15s : %5s : %s\n", e->real, e->native,
648
e->native + strlen(e->native) + 1);
651
mnt_free_cache(cache);
656
int main(int argc, char *argv[])
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)" },
665
return mnt_run_test(ts, argc, argv);