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