/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 |