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