Coverage Report

Created: 2025-08-09 06:31

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