Coverage Report

Created: 2026-08-31 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libmount/src/cache.c
Line
Count
Source
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
/* sd-device is a replacement for libudev */
34
#ifdef USE_LIBMOUNT_UDEV_SUPPORT
35
# include "dl-systemd.h"
36
#endif
37
38
#include "canonicalize.h"
39
#include "mountP.h"
40
#include "loopdev.h"
41
#include "strutils.h"
42
#include "mangle.h"
43
44
/*
45
 * Canonicalized (resolved) paths & tags cache
46
 */
47
0
#define MNT_CACHE_CHUNKSZ 128
48
49
0
#define MNT_CACHE_ISTAG   (1 << 1) /* entry is TAG */
50
0
#define MNT_CACHE_ISPATH  (1 << 2) /* entry is path */
51
0
#define MNT_CACHE_TAGREAD (1 << 3) /* tag read by mnt_cache_read_tags() */
52
53
/* path cache entry */
54
struct mnt_cache_entry {
55
  char      *key; /* search key (e.g. uncanonicalized path) */
56
  char      *value; /* value (e.g. canonicalized path) */
57
  int     flag;
58
};
59
60
struct libmnt_cache {
61
  struct mnt_cache_entry  *ents;
62
  size_t      nents;
63
  size_t      nallocs;
64
  int     refcount;
65
  int     probe_sb_extra; /* extra BLKID_SUBLKS_* flags */
66
  bool      noprobe;  /* disable libblkid device probing */
67
68
  const struct ul_vfs_ops *vfs;   /* borrowed VFS ops (not owned) */
69
70
  /* blkid_evaluate_tag() works in two ways:
71
   *
72
   * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
73
   *    then the blkid_cache is NULL.
74
   *
75
   * 2/ all tags are read from blkid.tab and verified by /dev
76
   *    scanning, then the blkid_cache is not NULL and then it's
77
   *    better to reuse the blkid_cache.
78
   */
79
  blkid_cache   bc;
80
81
  struct libmnt_table *mountinfo;
82
};
83
84
85
struct libmnt_cachetag {
86
  const char *mnt_name; /* tag name used by libmount */
87
  const char *blk_name; /* tag name used by libblkid */
88
  const char *udev_name;  /* tag name used by udev db */
89
};
90
91
static const struct libmnt_cachetag mnttags[] =
92
{
93
  /* mount  blkid     udev */
94
  { "LABEL",  "LABEL",    "ID_FS_LABEL_ENC" },
95
  { "UUID", "UUID",     "ID_FS_UUID_ENC" },
96
  { "TYPE", "TYPE",     "ID_FS_TYPE" },
97
  { "PARTUUID", "PART_ENTRY_UUID",  "ID_PART_ENTRY_UUID" },
98
  { "PARTLABEL",  "PART_ENTRY_NAME",  "ID_PART_ENTRY_NAME" },
99
100
  { NULL, NULL }
101
};
102
103
/**
104
 * mnt_new_cache:
105
 *
106
 * Returns: new struct libmnt_cache instance or NULL in case of ENOMEM error.
107
 */
108
struct libmnt_cache *mnt_new_cache(void)
109
0
{
110
0
  struct libmnt_cache *cache = calloc(1, sizeof(*cache));
111
0
  if (!cache)
112
0
    return NULL;
113
0
  DBG_OBJ(CACHE, cache, ul_debug("alloc"));
114
0
  cache->refcount = 1;
115
0
  return cache;
116
0
}
117
118
/**
119
 * mnt_free_cache:
120
 * @cache: pointer to struct libmnt_cache instance
121
 *
122
 * Deallocates the cache. This function does not care about reference count. Don't
123
 * use this function directly -- it's better to use mnt_unref_cache().
124
 */
125
void mnt_free_cache(struct libmnt_cache *cache)
126
0
{
127
0
  size_t i;
128
129
0
  if (!cache)
130
0
    return;
131
132
0
  DBG_OBJ(CACHE, cache, ul_debug("free [refcount=%d]", cache->refcount));
133
134
0
  for (i = 0; i < cache->nents; i++) {
135
0
    struct mnt_cache_entry *e = &cache->ents[i];
136
0
    if (e->value != e->key)
137
0
      free(e->value);
138
0
    free(e->key);
139
0
  }
140
0
  free(cache->ents);
141
0
  if (cache->bc)
142
0
    blkid_put_cache(cache->bc);
143
0
  free(cache);
144
0
}
145
146
/**
147
 * mnt_ref_cache:
148
 * @cache: cache pointer
149
 *
150
 * Increments reference counter.
151
 */
152
void mnt_ref_cache(struct libmnt_cache *cache)
153
0
{
154
0
  if (cache) {
155
0
    cache->refcount++;
156
    /*DBG_OBJ(CACHE, cache, ul_debug("ref=%d", cache->refcount));*/
157
0
  }
158
0
}
159
160
/**
161
 * mnt_unref_cache:
162
 * @cache: cache pointer
163
 *
164
 * De-increments reference counter, on zero the cache is automatically
165
 * deallocated by mnt_free_cache().
166
 */
167
void mnt_unref_cache(struct libmnt_cache *cache)
168
3.50k
{
169
3.50k
  if (cache) {
170
0
    cache->refcount--;
171
    /*DBG_OBJ(CACHE, cache, ul_debug("unref=%d", cache->refcount));*/
172
0
    if (cache->refcount <= 0) {
173
0
      mnt_unref_table(cache->mountinfo);
174
175
0
      mnt_free_cache(cache);
176
0
    }
177
0
  }
178
3.50k
}
179
180
/**
181
 * mnt_cache_set_targets:
182
 * @cache: cache pointer
183
 * @mountinfo: table with already canonicalized mountpoints
184
 *
185
 * Add to @cache reference to @mountinfo. This can be used to avoid unnecessary paths
186
 * canonicalization in mnt_resolve_target().
187
 *
188
 * Returns: negative number in case of error, or 0 o success.
189
 */
190
int mnt_cache_set_targets(struct libmnt_cache *cache,
191
        struct libmnt_table *mountinfo)
192
0
{
193
0
  if (!cache)
194
0
    return -EINVAL;
195
196
0
  mnt_ref_table(mountinfo);
197
0
  mnt_unref_table(cache->mountinfo);
198
0
  cache->mountinfo = mountinfo;
199
0
  return 0;
200
0
}
201
202
/**
203
 * mnt_cache_set_sbprobe:
204
 * @cache: cache pointer
205
 * @flags: BLKID_SUBLKS_* flags
206
 *
207
 * Add extra flags to the libblkid prober. Don't use if not sure.
208
 *
209
 * Returns: negative number in case of error, or 0 o success.
210
 */
211
int mnt_cache_set_sbprobe(struct libmnt_cache *cache, int flags)
212
0
{
213
0
  if (!cache)
214
0
    return -EINVAL;
215
216
0
  cache->probe_sb_extra = flags;
217
0
  return 0;
218
0
}
219
220
void mnt_cache_enable_noprobe(struct libmnt_cache *cache, int enable)
221
0
{
222
0
  if (cache)
223
0
    cache->noprobe = !!enable;
224
0
}
225
226
/**
227
 * mnt_cache_refer_vfs:
228
 * @cache: cache pointer
229
 * @vfs: VFS operations or NULL
230
 *
231
 * Set reference to VFS I/O operations. The cache does not own the @vfs
232
 * pointer -- the caller is responsible for its lifetime (e.g. the mount
233
 * context owns it).
234
 *
235
 * The VFS is used for blkid device probing (see mnt_cache_read_tags()).
236
 *
237
 * Returns: 0 on success, negative number in case of error.
238
 */
239
int mnt_cache_refer_vfs(struct libmnt_cache *cache, const struct ul_vfs_ops *vfs)
240
0
{
241
0
  if (!cache)
242
0
    return -EINVAL;
243
0
  cache->vfs = vfs;
244
0
  return 0;
245
0
}
246
247
/* note that the @key could be the same pointer as @value */
248
static int cache_add_entry(struct libmnt_cache *cache, char *key,
249
          char *value, int flag)
250
0
{
251
0
  struct mnt_cache_entry *e;
252
253
0
  assert(cache);
254
0
  assert(value);
255
0
  assert(key);
256
257
0
  if (cache->nents == cache->nallocs) {
258
0
    size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
259
260
0
    e = reallocarray(cache->ents, sz, sizeof(struct mnt_cache_entry));
261
0
    if (!e)
262
0
      return -ENOMEM;
263
0
    cache->ents = e;
264
0
    cache->nallocs = sz;
265
0
  }
266
267
0
  e = &cache->ents[cache->nents];
268
0
  e->key = key;
269
0
  e->value = value;
270
0
  e->flag = flag;
271
0
  cache->nents++;
272
273
0
  DBG_OBJ(CACHE, cache, ul_debug("add entry [%2zu] (%s): %s: %s",
274
0
      cache->nents,
275
0
      (flag & MNT_CACHE_ISPATH) ? "path" : "tag",
276
0
      value, key));
277
0
  return 0;
278
0
}
279
280
/* add tag to the cache, @devname has to be an allocated string */
281
static int cache_add_tag(struct libmnt_cache *cache, const char *tagname,
282
        const char *tagval, char *devname, int flag)
283
0
{
284
0
  size_t tksz, vlsz;
285
0
  char *key;
286
0
  int rc;
287
288
0
  assert(cache);
289
0
  assert(devname);
290
0
  assert(tagname);
291
0
  assert(tagval);
292
293
  /* add into cache -- cache format for TAGs is
294
   *  key    = "TAG_NAME\0TAG_VALUE\0"
295
   *  value  = "/dev/foo"
296
   */
297
0
  tksz = strlen(tagname);
298
0
  vlsz = strlen(tagval);
299
300
0
  key = malloc(tksz + vlsz + 2);
301
0
  if (!key)
302
0
    return -ENOMEM;
303
304
0
  memcpy(key, tagname, tksz + 1);    /* include '\0' */
305
0
  memcpy(key + tksz + 1, tagval, vlsz + 1);
306
307
0
  rc = cache_add_entry(cache, key, devname, flag | MNT_CACHE_ISTAG);
308
0
  if (!rc)
309
0
    return 0;
310
311
0
  free(key);
312
0
  return rc;
313
0
}
314
315
316
/*
317
 * Returns cached canonicalized path or NULL.
318
 */
319
static const char *cache_find_path(struct libmnt_cache *cache, const char *path)
320
0
{
321
0
  size_t i;
322
323
0
  if (!cache || !path)
324
0
    return NULL;
325
326
0
  for (i = 0; i < cache->nents; i++) {
327
0
    struct mnt_cache_entry *e = &cache->ents[i];
328
0
    if (!(e->flag & MNT_CACHE_ISPATH))
329
0
      continue;
330
0
    if (streq_paths(path, e->key))
331
0
      return e->value;
332
0
  }
333
0
  return NULL;
334
0
}
335
336
/*
337
 * Returns cached path or NULL.
338
 */
339
static const char *cache_find_tag(struct libmnt_cache *cache,
340
      const char *token, const char *value)
341
0
{
342
0
  size_t i;
343
0
  size_t tksz;
344
345
0
  if (!cache || !token || !value)
346
0
    return NULL;
347
348
0
  tksz = strlen(token);
349
350
0
  for (i = 0; i < cache->nents; i++) {
351
0
    struct mnt_cache_entry *e = &cache->ents[i];
352
0
    if (!(e->flag & MNT_CACHE_ISTAG))
353
0
      continue;
354
0
    if (strcmp(token, e->key) == 0 &&
355
0
        strcmp(value, e->key + tksz + 1) == 0)
356
0
      return e->value;
357
0
  }
358
0
  return NULL;
359
0
}
360
361
static char *cache_find_tag_value(struct libmnt_cache *cache,
362
      const char *devname, const char *token)
363
0
{
364
0
  size_t i;
365
366
0
  assert(cache);
367
0
  assert(devname);
368
0
  assert(token);
369
370
0
  for (i = 0; i < cache->nents; i++) {
371
0
    struct mnt_cache_entry *e = &cache->ents[i];
372
0
    if (!(e->flag & MNT_CACHE_ISTAG))
373
0
      continue;
374
0
    if (strcmp(e->value, devname) == 0 && /* dev name */
375
0
        strcmp(token, e->key) == 0) /* tag name */
376
0
      return e->key + strlen(token) + 1; /* tag value */
377
0
  }
378
379
0
  return NULL;
380
0
}
381
382
static bool is_device_cached(struct libmnt_cache *cache, const char *devname)
383
0
{
384
0
  size_t i;
385
386
0
  for (i = 0; i < cache->nents; i++) {
387
0
    struct mnt_cache_entry *e = &cache->ents[i];
388
0
    if (!(e->flag & MNT_CACHE_TAGREAD))
389
0
      continue;
390
0
    if (strcmp(e->value, devname) == 0)
391
0
      return 1; /* already in cache */
392
0
  }
393
394
0
  return 0;
395
0
}
396
397
/*
398
 * read data from libblkid into local cache
399
 * returns: <0 on error; 0 success; 1 nothing
400
*/
401
static int read_from_blkid(struct libmnt_cache *cache, const char *devname)
402
0
{
403
0
  blkid_probe pr;
404
0
  const struct libmnt_cachetag *t;
405
0
  size_t ntags = 0;
406
0
  int rc;
407
0
  char *cacheval = NULL;
408
409
0
  assert(cache);
410
0
  assert(devname);
411
412
0
  if (cache->noprobe) {
413
0
    DBG_OBJ(CACHE, cache, ul_debug("%s: skip blkid probe (noprobe)", devname));
414
0
    return 1;
415
0
  }
416
417
0
  DBG_OBJ(CACHE, cache, ul_debug("%s: reading from blkid", devname));
418
419
0
  pr = blkid_new_probe();
420
0
  if (!pr)
421
0
    return -EINVAL;
422
423
0
  if (cache->vfs)
424
0
    blkid_probe_set_vfs(pr, cache->vfs);
425
0
  if (blkid_probe_open_device(pr, devname, 0)) {
426
0
    blkid_free_probe(pr);
427
0
    return -errno;
428
0
  }
429
430
0
  blkid_probe_enable_superblocks(pr, 1);
431
0
  blkid_probe_set_superblocks_flags(pr,
432
0
      BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
433
0
      BLKID_SUBLKS_TYPE | cache->probe_sb_extra);
434
435
0
  blkid_probe_enable_partitions(pr, 1);
436
0
  blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
437
438
0
  rc = blkid_do_safeprobe(pr);
439
0
  if (rc)
440
0
    goto done;
441
442
0
  for (t = mnttags; t && t->mnt_name; t++) {
443
0
    const char *data;
444
445
0
    if (cache_find_tag_value(cache, devname, t->mnt_name))
446
0
      continue;
447
0
    if (blkid_probe_lookup_value(pr, t->blk_name, &data, NULL))
448
0
      continue;
449
450
0
    cacheval = strdup(devname);
451
0
    rc = !cacheval ? -ENOMEM :
452
0
      cache_add_tag(cache, t->mnt_name, data, cacheval, MNT_CACHE_TAGREAD);
453
0
    if (rc)
454
0
      break;
455
0
    ntags++;
456
0
    cacheval = NULL; /* stored into cache */
457
0
  }
458
459
0
done:
460
0
  DBG_OBJ(CACHE, cache, ul_debug("\tread %zu tags [rc=%d]", ntags, rc));
461
0
  blkid_free_probe(pr);
462
0
  free(cacheval);
463
464
0
  return rc ? rc : ntags ? 0 : 1;
465
0
}
466
467
#ifdef USE_LIBMOUNT_UDEV_SUPPORT
468
/*
469
 * read data from udev into local cache
470
 * returns: <0 on error; 0 success; 1 nothing
471
 */
472
static int read_from_udev(struct libmnt_cache *cache, const char *devname)
473
{
474
  sd_device *sd = NULL;
475
  const struct libmnt_cachetag *t;
476
  size_t ntags = 0;
477
  char *tagval = NULL, *cacheval = NULL;
478
  int rc;
479
480
  assert(cache);
481
  assert(devname);
482
483
  if (ul_dlopen_libsystemd() != 0)
484
    return -ENOSYS;
485
486
  rc = systemd_call(sd_device_new_from_devname)(&sd, devname);
487
  if (rc < 0)
488
    return rc;
489
490
  DBG_OBJ(CACHE, cache, ul_debug("%s: reading from udev", devname));
491
492
  for (t = mnttags; t && t->mnt_name; t++) {
493
    const char *data;
494
495
    if (cache_find_tag_value(cache, devname, t->mnt_name))
496
      continue;
497
    if (systemd_call(sd_device_get_property_value)(sd, t->udev_name, &data) < 0)
498
      continue;
499
500
    tagval = strdup(data); /* temporary for unhexmangle() */
501
    cacheval = strdup(devname);
502
503
    if (tagval && cacheval) {
504
      unhexmangle_string(tagval);
505
      rc = cache_add_tag(cache, t->mnt_name,
506
          tagval, cacheval, MNT_CACHE_TAGREAD);
507
    } else
508
      rc = -ENOMEM;
509
    if (rc)
510
      break;
511
    ntags++;
512
    cacheval = NULL; /* stored into cache */
513
    free(tagval), tagval = NULL;
514
  }
515
516
  DBG_OBJ(CACHE, cache, ul_debug("\tread %zu tags [rc=%d]", ntags, rc));
517
  systemd_call(sd_device_unref)(sd);
518
  free(cacheval);
519
  free(tagval);
520
521
  return rc ? rc : ntags ? 0 : 1;
522
}
523
#endif /* USE_LIBMOUNT_UDEV_SUPPORT */
524
525
526
/**
527
 * mnt_cache_read_tags
528
 * @cache: pointer to struct libmnt_cache instance
529
 * @devname: path device
530
 *
531
 * Reads @devname information into the @cache.
532
 *
533
 * Returns: 0 if at least one tag was added, 1 if no tag was added or
534
 *          negative number in case of error.
535
 */
536
int mnt_cache_read_tags(struct libmnt_cache *cache, const char *devname)
537
0
{
538
0
  if (!cache || !devname)
539
0
    return -EINVAL;
540
541
0
  DBG_OBJ(CACHE, cache, ul_debug("tags for %s requested", devname));
542
543
0
  if (is_device_cached(cache, devname))
544
0
    return 0;
545
546
#ifdef USE_LIBMOUNT_UDEV_SUPPORT
547
  if (read_from_udev(cache, devname) == 0)
548
    return 0;
549
#endif
550
0
  return read_from_blkid(cache, devname);
551
0
}
552
553
/**
554
 * mnt_cache_device_has_tag:
555
 * @cache: paths cache
556
 * @devname: path to the device
557
 * @token: tag name (e.g "LABEL")
558
 * @value: tag value
559
 *
560
 * Look up @cache to check if @tag+@value are associated with @devname.
561
 *
562
 * Returns: 1 on success or 0.
563
 */
564
int mnt_cache_device_has_tag(struct libmnt_cache *cache, const char *devname,
565
        const char *token, const char *value)
566
0
{
567
0
  const char *path = cache_find_tag(cache, token, value);
568
569
0
  if (path && devname && strcmp(path, devname) == 0)
570
0
    return 1;
571
0
  return 0;
572
0
}
573
574
static int __mnt_cache_find_tag_value(struct libmnt_cache *cache,
575
    const char *devname, const char *token, char **data)
576
0
{
577
0
  int rc = 0;
578
579
0
  if (!cache || !devname || !token || !data)
580
0
    return -EINVAL;
581
582
0
  rc = mnt_cache_read_tags(cache, devname);
583
0
  if (rc)
584
0
    return rc;
585
586
0
  *data = cache_find_tag_value(cache, devname, token);
587
0
  return *data ? 0 : -1;
588
0
}
589
590
/**
591
 * mnt_cache_find_tag_value:
592
 * @cache: cache for results
593
 * @devname: device name
594
 * @token: tag name ("LABEL", "UUID", ...)
595
 *
596
 * Returns: LABEL or UUID for the @devname or NULL in case of error.
597
 */
598
char *mnt_cache_find_tag_value(struct libmnt_cache *cache,
599
    const char *devname, const char *token)
600
0
{
601
0
  char *data = NULL;
602
603
0
  if (__mnt_cache_find_tag_value(cache, devname, token, &data) == 0)
604
0
    return data;
605
0
  return NULL;
606
0
}
607
608
static char *fstype_from_cache(const char *devname, struct libmnt_cache *cache)
609
0
{
610
0
  char *val = NULL;
611
612
0
  assert(cache);
613
614
0
  if (__mnt_cache_find_tag_value(cache, devname, "TYPE", &val) != 0)
615
0
    return NULL;
616
0
  return val;
617
0
}
618
619
static char *fstype_from_blkid(const char *devname, int *ambi,
620
             const struct ul_vfs_ops *vfs)
621
0
{
622
0
  blkid_probe pr;
623
0
  const char *data;
624
0
  char *type = NULL;
625
0
  int rc;
626
627
0
  pr = blkid_new_probe();
628
0
  if (!pr)
629
0
    return NULL;
630
0
  if (vfs)
631
0
    blkid_probe_set_vfs(pr, vfs);
632
0
  if (blkid_probe_open_device(pr, devname, 0)) {
633
0
    blkid_free_probe(pr);
634
0
    return NULL;
635
0
  }
636
637
0
  blkid_probe_enable_superblocks(pr, 1);
638
0
  blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
639
640
0
  rc = blkid_do_safeprobe(pr);
641
642
0
  if (!rc && !blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
643
0
    type = strdup(data);
644
0
  if (ambi)
645
0
    *ambi = rc == -2 ? TRUE : FALSE;
646
647
0
  blkid_free_probe(pr);
648
0
  return type;
649
0
}
650
651
/**
652
 * mnt_get_fstype:
653
 * @devname: device name
654
 * @ambi: returns TRUE if probing result is ambivalent (optional argument)
655
 * @cache: cache for results or NULL
656
 *
657
 * Note: If the cache is not specified, it reads the file system type from the
658
 * device, and in this case, there is no optimization like udev db, etc. *
659
 *
660
 * Returns: filesystem type or NULL in case of error. The result has to be
661
 * deallocated by free() if @cache is NULL.
662
 */
663
char *mnt_get_fstype(const char *devname, int *ambi, struct libmnt_cache *cache)
664
0
{
665
0
  DBG_OBJ(CACHE, cache, ul_debug("get %s FS type", devname));
666
667
0
  if (cache)
668
0
    return fstype_from_cache(devname, cache);
669
670
0
  return fstype_from_blkid(devname, ambi, NULL);
671
0
}
672
673
static char *canonicalize_path_and_cache(const char *path,
674
            struct libmnt_cache *cache)
675
460
{
676
460
  char *p;
677
460
  char *key;
678
460
  char *value;
679
680
460
  DBG_OBJ(CACHE, cache, ul_debug("canonicalize path %s", path));
681
460
  p = ul_canonicalize_path(path);
682
683
460
  if (p && cache) {
684
0
    value = p;
685
0
    key = strcmp(path, p) == 0 ? value : strdup(path);
686
687
0
    if (!key || !value)
688
0
      goto error;
689
690
0
    if (cache_add_entry(cache, key, value,
691
0
        MNT_CACHE_ISPATH))
692
0
      goto error;
693
0
  }
694
695
460
  return p;
696
0
error:
697
0
  if (value != key)
698
0
    free(value);
699
0
  free(key);
700
0
  return NULL;
701
460
}
702
703
/**
704
 * mnt_resolve_path:
705
 * @path: "native" path
706
 * @cache: cache for results or NULL
707
 *
708
 * Converts path:
709
 *  - to the absolute path
710
 *  - /dev/dm-N to /dev/mapper/name
711
 *
712
 * Returns: absolute path or NULL in case of error. The result has to be
713
 * deallocated by free() if @cache is NULL.
714
 */
715
char *mnt_resolve_path(const char *path, struct libmnt_cache *cache)
716
460
{
717
460
  char *p = NULL;
718
719
  /*DBG_OBJ(CACHE, cache, ul_debug("resolving path %s", path));*/
720
721
460
  if (!path)
722
0
    return NULL;
723
460
  if (cache)
724
0
    p = (char *) cache_find_path(cache, path);
725
460
  if (!p)
726
460
    p = canonicalize_path_and_cache(path, cache);
727
728
460
  return p;
729
460
}
730
731
/**
732
 * mnt_resolve_target:
733
 * @path: "native" path, a potential mount point
734
 * @cache: cache for results or NULL.
735
 *
736
 * Like mnt_resolve_path(), unless @cache is not NULL and
737
 * mnt_cache_set_targets(cache, mountinfo) was called: if @path is found in the
738
 * cached @mountinfo and the matching entry was provided by the kernel, assume that
739
 * @path is already canonicalized. By avoiding a call to realpath(2) on
740
 * known mount points, there is a lower risk of stepping on a stale mount
741
 * point, which can result in an application freeze. This is also faster in
742
 * general, as stat(2) on a mount point is slower than on a regular file.
743
 *
744
 * Returns: absolute path or NULL in case of error. The result has to be
745
 * deallocated by free() if @cache is NULL.
746
 */
747
char *mnt_resolve_target(const char *path, struct libmnt_cache *cache)
748
0
{
749
0
  char *p = NULL;
750
751
0
  if (!path)
752
0
    return NULL;
753
754
  /*DBG_OBJ(CACHE, cache, ul_debug("resolving target %s", path));*/
755
756
0
  if (!cache || !cache->mountinfo)
757
0
    return mnt_resolve_path(path, cache);
758
759
0
  p = (char *) cache_find_path(cache, path);
760
0
  if (p)
761
0
    return p;
762
763
0
  {
764
0
    struct libmnt_iter itr;
765
0
    struct libmnt_fs *fs = NULL;
766
767
0
    mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
768
0
    while (mnt_table_next_fs(cache->mountinfo, &itr, &fs) == 0) {
769
770
0
      if (!mnt_fs_is_kernel(fs)
771
0
           || mnt_fs_is_swaparea(fs)
772
0
           || !mnt_fs_streq_target(fs, path))
773
0
        continue;
774
775
0
      p = strdup(path);
776
0
      if (!p)
777
0
        return NULL; /* ENOMEM */
778
779
0
      if (cache_add_entry(cache, p, p, MNT_CACHE_ISPATH)) {
780
0
        free(p);
781
0
        return NULL; /* ENOMEM */
782
0
      }
783
0
      break;
784
0
    }
785
0
  }
786
787
0
  if (!p)
788
0
    p = canonicalize_path_and_cache(path, cache);
789
0
  return p;
790
0
}
791
792
/**
793
 * mnt_pretty_path:
794
 * @path: any path
795
 * @cache: NULL or pointer to the cache
796
 *
797
 * Converts path:
798
 *  - to the absolute path
799
 *  - /dev/dm-N to /dev/mapper/name
800
 *  - /dev/loopN to the loop backing filename
801
 *  - empty path (NULL) to 'none'
802
 *
803
 * Returns: newly allocated string with path, result always has to be deallocated
804
 *          by free().
805
 */
806
char *mnt_pretty_path(const char *path, struct libmnt_cache *cache)
807
0
{
808
0
  char *pretty = mnt_resolve_path(path, cache);
809
810
0
  if (!pretty)
811
0
    return strdup("none");
812
813
0
#ifdef __linux__
814
  /* users assume backing file name rather than /dev/loopN in
815
   * output if the device has been initialized by mount(8).
816
   */
817
0
  if (strncmp(pretty, "/dev/loop", 9) == 0) {
818
0
    struct loopdev_cxt lc;
819
820
0
    if (loopcxt_init(&lc, 0) || loopcxt_set_device(&lc, pretty))
821
0
      goto done;
822
823
0
    if (loopcxt_is_autoclear(&lc)) {
824
0
      char *tmp = loopcxt_get_backing_file(&lc);
825
0
      if (tmp) {
826
0
        loopcxt_deinit(&lc);
827
0
        if (!cache)
828
0
          free(pretty); /* not cached, deallocate */
829
0
        return tmp;   /* return backing file */
830
0
      }
831
0
    }
832
0
    loopcxt_deinit(&lc);
833
834
0
  }
835
0
#endif
836
837
0
done:
838
  /* don't return pointer to the cache, allocate a new string */
839
0
  return cache ? strdup(pretty) : pretty;
840
0
}
841
842
/**
843
 * mnt_resolve_tag:
844
 * @token: tag name
845
 * @value: tag value
846
 * @cache: for results or NULL
847
 *
848
 * Returns: device name or NULL in case of error. The result has to be
849
 * deallocated by free() if @cache is NULL.
850
 */
851
char *mnt_resolve_tag(const char *token, const char *value,
852
          struct libmnt_cache *cache)
853
38
{
854
38
  char *p = NULL;
855
856
38
  if (!token || !value)
857
0
    return NULL;
858
859
38
  if (cache)
860
0
    p = (char *) cache_find_tag(cache, token, value);
861
862
38
  if (!p) {
863
38
    DBG_OBJ(CACHE, cache, ul_debug("evaluating (by blkid) tag %s=%s", token, value));
864
865
    /* returns newly allocated string */
866
38
    p = blkid_evaluate_tag2(token, value,
867
38
        cache ? &cache->bc : NULL,
868
38
        cache && cache->noprobe ? BLKID_EVALUATE_NOPROBE : 0);
869
870
38
    if (p && cache &&
871
0
        cache_add_tag(cache, token, value, p, 0))
872
0
        goto error;
873
38
  }
874
875
38
  DBG_OBJ(CACHE, cache, ul_debug("resolve tag %s=%s -> %s",
876
38
        token, value, p ? p : "NOT FOUND"));
877
38
  return p;
878
0
error:
879
0
  free(p);
880
0
  return NULL;
881
38
}
882
883
884
885
/**
886
 * mnt_resolve_spec:
887
 * @spec: path or tag
888
 * @cache: paths cache
889
 *
890
 * Returns: canonicalized path or NULL. The result has to be
891
 * deallocated by free() if @cache is NULL.
892
 */
893
char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
894
38
{
895
38
  char *cn = NULL;
896
38
  char *t = NULL, *v = NULL;
897
898
38
  if (!spec)
899
0
    return NULL;
900
901
38
  if (blkid_parse_tag_string(spec, &t, &v) == 0 && mnt_valid_tagname(t))
902
38
    cn = mnt_resolve_tag(t, v, cache);
903
0
  else
904
0
    cn = mnt_resolve_path(spec, cache);
905
906
38
  free(t);
907
38
  free(v);
908
38
  return cn;
909
38
}
910
911
912
#ifdef TEST_PROGRAM
913
914
static int test_resolve_path(struct libmnt_test *ts __attribute__((unused)),
915
           int argc __attribute__((unused)),
916
           char *argv[] __attribute__((unused)))
917
{
918
  char line[BUFSIZ];
919
  struct libmnt_cache *cache;
920
921
  cache = mnt_new_cache();
922
  if (!cache)
923
    return -ENOMEM;
924
925
  while(fgets(line, sizeof(line), stdin)) {
926
    size_t sz = strlen(line);
927
    char *p;
928
929
    if (sz > 0 && line[sz - 1] == '\n')
930
      line[sz - 1] = '\0';
931
932
    p = mnt_resolve_path(line, cache);
933
    printf("%s : %s\n", line, p);
934
  }
935
  mnt_unref_cache(cache);
936
  return 0;
937
}
938
939
static int test_resolve_spec(struct libmnt_test *ts __attribute__((unused)),
940
           int argc __attribute__((unused)),
941
           char *argv[] __attribute__((unused)))
942
{
943
  char line[BUFSIZ];
944
  struct libmnt_cache *cache;
945
946
  cache = mnt_new_cache();
947
  if (!cache)
948
    return -ENOMEM;
949
950
  while(fgets(line, sizeof(line), stdin)) {
951
    size_t sz = strlen(line);
952
    char *p;
953
954
    if (sz > 0 && line[sz - 1] == '\n')
955
      line[sz - 1] = '\0';
956
957
    p = mnt_resolve_spec(line, cache);
958
    printf("%s : %s\n", line, p);
959
  }
960
  mnt_unref_cache(cache);
961
  return 0;
962
}
963
964
static int test_read_tags(struct libmnt_test *ts __attribute__((unused)),
965
        int argc __attribute__((unused)),
966
        char *argv[] __attribute__((unused)))
967
{
968
  char line[BUFSIZ];
969
  struct libmnt_cache *cache;
970
  size_t i;
971
972
  cache = mnt_new_cache();
973
  if (!cache)
974
    return -ENOMEM;
975
976
  while(fgets(line, sizeof(line), stdin)) {
977
    size_t sz = strlen(line);
978
    char *t = NULL, *v = NULL;
979
980
    if (sz > 0 && line[sz - 1] == '\n')
981
      line[sz - 1] = '\0';
982
983
    if (!strcmp(line, "quit"))
984
      break;
985
986
    if (*line == '/') {
987
      if (mnt_cache_read_tags(cache, line) < 0)
988
        fprintf(stderr, "%s: read tags failed\n", line);
989
990
    } else if (blkid_parse_tag_string(line, &t, &v) == 0) {
991
      const char *cn = NULL;
992
993
      if (mnt_valid_tagname(t))
994
        cn = cache_find_tag(cache, t, v);
995
      free(t);
996
      free(v);
997
998
      if (cn)
999
        printf("%s: %s\n", line, cn);
1000
      else
1001
        printf("%s: not cached\n", line);
1002
    }
1003
  }
1004
1005
  for (i = 0; i < cache->nents; i++) {
1006
    struct mnt_cache_entry *e = &cache->ents[i];
1007
    if (!(e->flag & MNT_CACHE_ISTAG))
1008
      continue;
1009
1010
    printf("%15s : %5s : %s\n", e->value, e->key,
1011
        e->key + strlen(e->key) + 1);
1012
  }
1013
1014
  mnt_unref_cache(cache);
1015
  return 0;
1016
1017
}
1018
1019
int main(int argc, char *argv[])
1020
{
1021
  struct libmnt_test ts[] = {
1022
    { "--resolve-path", test_resolve_path, "  resolve paths from stdin" },
1023
    { "--resolve-spec", test_resolve_spec, "  evaluate specs from stdin" },
1024
    { "--read-tags", test_read_tags,       "  read devname or TAG from stdin (\"quit\" to exit)" },
1025
    { NULL }
1026
  };
1027
1028
  return mnt_run_test(ts, argc, argv);
1029
}
1030
#endif