]>
Commit | Line | Data |
---|---|---|
a0948ffe KZ |
1 | /* |
2 | * tag.c - allocation/initialization/free routines for tag structs | |
3 | * | |
4 | * Copyright (C) 2001 Andreas Dilger | |
5 | * Copyright (C) 2003 Theodore Ts'o | |
6 | * | |
7 | * %Begin-Header% | |
8 | * This file may be redistributed under the terms of the | |
9 | * GNU Lesser General Public License. | |
10 | * %End-Header% | |
11 | */ | |
12 | ||
13 | #include <unistd.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <stdio.h> | |
17 | ||
18 | #include "blkidP.h" | |
19 | ||
20 | static blkid_tag blkid_new_tag(void) | |
21 | { | |
22 | blkid_tag tag; | |
23 | ||
fea1cbf7 | 24 | if (!(tag = calloc(1, sizeof(struct blkid_struct_tag)))) |
a0948ffe KZ |
25 | return NULL; |
26 | ||
14308bc3 | 27 | DBG(TAG, ul_debugobj(tag, "alloc")); |
a0948ffe KZ |
28 | INIT_LIST_HEAD(&tag->bit_tags); |
29 | INIT_LIST_HEAD(&tag->bit_names); | |
30 | ||
31 | return tag; | |
32 | } | |
33 | ||
a0948ffe KZ |
34 | void blkid_free_tag(blkid_tag tag) |
35 | { | |
36 | if (!tag) | |
37 | return; | |
38 | ||
14308bc3 | 39 | DBG(TAG, ul_debugobj(tag, "freeing tag %s (%s)", tag->bit_name, tag->bit_val)); |
a0948ffe KZ |
40 | |
41 | list_del(&tag->bit_tags); /* list of tags for this device */ | |
42 | list_del(&tag->bit_names); /* list of tags with this type */ | |
43 | ||
0e4ed1aa JM |
44 | free(tag->bit_name); |
45 | free(tag->bit_val); | |
a0948ffe KZ |
46 | |
47 | free(tag); | |
48 | } | |
49 | ||
50 | /* | |
51 | * Find the desired tag on a device. If value is NULL, then the | |
52 | * first such tag is returned, otherwise return only exact tag if found. | |
53 | */ | |
54 | blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) | |
55 | { | |
56 | struct list_head *p; | |
57 | ||
a0948ffe KZ |
58 | list_for_each(p, &dev->bid_tags) { |
59 | blkid_tag tmp = list_entry(p, struct blkid_struct_tag, | |
60 | bit_tags); | |
61 | ||
62 | if (!strcmp(tmp->bit_name, type)) | |
63 | return tmp; | |
64 | } | |
65 | return NULL; | |
66 | } | |
67 | ||
604ba973 | 68 | int blkid_dev_has_tag(blkid_dev dev, const char *type, |
a0948ffe KZ |
69 | const char *value) |
70 | { | |
71 | blkid_tag tag; | |
72 | ||
a0948ffe KZ |
73 | tag = blkid_find_tag_dev(dev, type); |
74 | if (!value) | |
75 | return (tag != NULL); | |
ad296391 | 76 | if (!tag || strcmp(tag->bit_val, value) != 0) |
a0948ffe KZ |
77 | return 0; |
78 | return 1; | |
79 | } | |
80 | ||
81 | /* | |
82 | * Find the desired tag type in the cache. | |
83 | * We return the head tag for this tag type. | |
84 | */ | |
85 | static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) | |
86 | { | |
87 | blkid_tag head = NULL, tmp; | |
88 | struct list_head *p; | |
89 | ||
90 | if (!cache || !type) | |
91 | return NULL; | |
92 | ||
93 | list_for_each(p, &cache->bic_tags) { | |
94 | tmp = list_entry(p, struct blkid_struct_tag, bit_tags); | |
95 | if (!strcmp(tmp->bit_name, type)) { | |
14308bc3 | 96 | DBG(TAG, ul_debug("found cache tag head %s", type)); |
a0948ffe KZ |
97 | head = tmp; |
98 | break; | |
99 | } | |
100 | } | |
101 | return head; | |
102 | } | |
103 | ||
104 | /* | |
105 | * Set a tag on an existing device. | |
106 | * | |
9e930041 | 107 | * If value is NULL, then delete the tags from the device. |
a0948ffe KZ |
108 | */ |
109 | int blkid_set_tag(blkid_dev dev, const char *name, | |
110 | const char *value, const int vlength) | |
111 | { | |
87918040 SK |
112 | blkid_tag t = NULL, head = NULL; |
113 | char *val = NULL; | |
114 | char **dev_var = NULL; | |
a0948ffe | 115 | |
e0a9b8cf | 116 | if (value && !(val = strndup(value, vlength))) |
a0948ffe KZ |
117 | return -BLKID_ERR_MEM; |
118 | ||
119 | /* | |
120 | * Certain common tags are linked directly to the device struct | |
121 | * We need to know what they are before we do anything else because | |
122 | * the function name parameter might get freed later on. | |
123 | */ | |
124 | if (!strcmp(name, "TYPE")) | |
125 | dev_var = &dev->bid_type; | |
126 | else if (!strcmp(name, "LABEL")) | |
127 | dev_var = &dev->bid_label; | |
128 | else if (!strcmp(name, "UUID")) | |
129 | dev_var = &dev->bid_uuid; | |
130 | ||
131 | t = blkid_find_tag_dev(dev, name); | |
132 | if (!value) { | |
133 | if (t) | |
134 | blkid_free_tag(t); | |
135 | } else if (t) { | |
136 | if (!strcmp(t->bit_val, val)) { | |
137 | /* Same thing, exit */ | |
138 | free(val); | |
139 | return 0; | |
140 | } | |
14308bc3 | 141 | DBG(TAG, ul_debugobj(t, "update (%s) '%s' -> '%s'", t->bit_name, t->bit_val, val)); |
a0948ffe KZ |
142 | free(t->bit_val); |
143 | t->bit_val = val; | |
144 | } else { | |
145 | /* Existing tag not present, add to device */ | |
146 | if (!(t = blkid_new_tag())) | |
147 | goto errout; | |
e97c0214 | 148 | t->bit_name = strdup(name); |
a0948ffe KZ |
149 | t->bit_val = val; |
150 | t->bit_dev = dev; | |
151 | ||
14308bc3 | 152 | DBG(TAG, ul_debugobj(t, "setting (%s) '%s'", t->bit_name, t->bit_val)); |
a0948ffe KZ |
153 | list_add_tail(&t->bit_tags, &dev->bid_tags); |
154 | ||
155 | if (dev->bid_cache) { | |
156 | head = blkid_find_head_cache(dev->bid_cache, | |
157 | t->bit_name); | |
158 | if (!head) { | |
159 | head = blkid_new_tag(); | |
160 | if (!head) | |
161 | goto errout; | |
162 | ||
14308bc3 | 163 | DBG(TAG, ul_debugobj(head, "creating new cache tag head %s", name)); |
e97c0214 | 164 | head->bit_name = strdup(name); |
a0948ffe KZ |
165 | if (!head->bit_name) |
166 | goto errout; | |
167 | list_add_tail(&head->bit_tags, | |
168 | &dev->bid_cache->bic_tags); | |
169 | } | |
170 | list_add_tail(&t->bit_names, &head->bit_names); | |
171 | } | |
172 | } | |
173 | ||
174 | /* Link common tags directly to the device struct */ | |
175 | if (dev_var) | |
176 | *dev_var = val; | |
177 | ||
178 | if (dev->bid_cache) | |
179 | dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; | |
180 | return 0; | |
181 | ||
182 | errout: | |
183 | if (t) | |
184 | blkid_free_tag(t); | |
c8f1e920 KZ |
185 | else |
186 | free(val); | |
a0948ffe KZ |
187 | if (head) |
188 | blkid_free_tag(head); | |
189 | return -BLKID_ERR_MEM; | |
190 | } | |
191 | ||
192 | ||
193 | /* | |
194 | * Parse a "NAME=value" string. This is slightly different than | |
195 | * parse_token, because that will end an unquoted value at a space, while | |
196 | * this will assume that an unquoted value is the rest of the token (e.g. | |
197 | * if we are passed an already quoted string from the command-line we don't | |
198 | * have to both quote and escape quote so that the quotes make it to | |
199 | * us). | |
200 | * | |
201 | * Returns 0 on success, and -1 on failure. | |
202 | */ | |
203 | int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) | |
204 | { | |
205 | char *name, *value, *cp; | |
206 | ||
c62a6311 | 207 | DBG(TAG, ul_debug("trying to parse '%s' as a tag", token)); |
a0948ffe KZ |
208 | |
209 | if (!token || !(cp = strchr(token, '='))) | |
210 | return -1; | |
211 | ||
e0a9b8cf | 212 | name = strdup(token); |
a0948ffe KZ |
213 | if (!name) |
214 | return -1; | |
215 | value = name + (cp - token); | |
216 | *value++ = '\0'; | |
217 | if (*value == '"' || *value == '\'') { | |
218 | char c = *value++; | |
219 | if (!(cp = strrchr(value, c))) | |
220 | goto errout; /* missing closing quote */ | |
221 | *cp = '\0'; | |
222 | } | |
c1178175 KZ |
223 | |
224 | if (ret_val) { | |
e97c0214 | 225 | value = *value ? strdup(value) : NULL; |
c1178175 KZ |
226 | if (!value) |
227 | goto errout; | |
228 | *ret_val = value; | |
229 | } | |
a0948ffe | 230 | |
e3436956 KZ |
231 | if (ret_type) |
232 | *ret_type = name; | |
c1178175 KZ |
233 | else |
234 | free(name); | |
a0948ffe KZ |
235 | |
236 | return 0; | |
237 | ||
238 | errout: | |
c62a6311 | 239 | DBG(TAG, ul_debug("parse error: '%s'", token)); |
a0948ffe KZ |
240 | free(name); |
241 | return -1; | |
242 | } | |
243 | ||
244 | /* | |
245 | * Tag iteration routines for the public libblkid interface. | |
246 | * | |
247 | * These routines do not expose the list.h implementation, which are a | |
248 | * contamination of the namespace, and which force us to reveal far, far | |
9e930041 | 249 | * too much of our internal implementation. I'm not convinced I want |
a0948ffe KZ |
250 | * to keep list.h in the long term, anyway. It's fine for kernel |
251 | * programming, but performance is not the #1 priority for this | |
9e930041 | 252 | * library, and I really don't like the trade-off of type-safety for |
a0948ffe KZ |
253 | * performance for this application. [tytso:20030125.2007EST] |
254 | */ | |
255 | ||
256 | /* | |
257 | * This series of functions iterate over all tags in a device | |
258 | */ | |
259 | #define TAG_ITERATE_MAGIC 0x01a5284c | |
260 | ||
261 | struct blkid_struct_tag_iterate { | |
262 | int magic; | |
263 | blkid_dev dev; | |
264 | struct list_head *p; | |
265 | }; | |
266 | ||
604ba973 | 267 | blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) |
a0948ffe KZ |
268 | { |
269 | blkid_tag_iterate iter; | |
270 | ||
2bb7a706 KZ |
271 | if (!dev) { |
272 | errno = EINVAL; | |
273 | return NULL; | |
274 | } | |
275 | ||
a0948ffe KZ |
276 | iter = malloc(sizeof(struct blkid_struct_tag_iterate)); |
277 | if (iter) { | |
278 | iter->magic = TAG_ITERATE_MAGIC; | |
279 | iter->dev = dev; | |
280 | iter->p = dev->bid_tags.next; | |
281 | } | |
282 | return (iter); | |
283 | } | |
284 | ||
285 | /* | |
286 | * Return 0 on success, -1 on error | |
287 | */ | |
604ba973 | 288 | int blkid_tag_next(blkid_tag_iterate iter, |
a0948ffe KZ |
289 | const char **type, const char **value) |
290 | { | |
291 | blkid_tag tag; | |
292 | ||
e3436956 KZ |
293 | if (!type || !value || |
294 | !iter || iter->magic != TAG_ITERATE_MAGIC || | |
a0948ffe KZ |
295 | iter->p == &iter->dev->bid_tags) |
296 | return -1; | |
e3436956 | 297 | |
87918040 SK |
298 | *type = NULL; |
299 | *value = NULL; | |
a0948ffe KZ |
300 | tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); |
301 | *type = tag->bit_name; | |
302 | *value = tag->bit_val; | |
303 | iter->p = iter->p->next; | |
304 | return 0; | |
305 | } | |
306 | ||
604ba973 | 307 | void blkid_tag_iterate_end(blkid_tag_iterate iter) |
a0948ffe KZ |
308 | { |
309 | if (!iter || iter->magic != TAG_ITERATE_MAGIC) | |
310 | return; | |
311 | iter->magic = 0; | |
312 | free(iter); | |
313 | } | |
314 | ||
315 | /* | |
316 | * This function returns a device which matches a particular | |
317 | * type/value pair. If there is more than one device that matches the | |
318 | * search specification, it returns the one with the highest priority | |
319 | * value. This allows us to give preference to EVMS or LVM devices. | |
320 | */ | |
604ba973 | 321 | blkid_dev blkid_find_dev_with_tag(blkid_cache cache, |
a0948ffe KZ |
322 | const char *type, |
323 | const char *value) | |
324 | { | |
325 | blkid_tag head; | |
326 | blkid_dev dev; | |
327 | int pri; | |
328 | struct list_head *p; | |
84d38ae3 | 329 | int probe_new = 0, probe_all = 0; |
a0948ffe KZ |
330 | |
331 | if (!cache || !type || !value) | |
332 | return NULL; | |
333 | ||
334 | blkid_read_cache(cache); | |
335 | ||
84d38ae3 | 336 | DBG(TAG, ul_debug("looking for tag %s=%s in cache", type, value)); |
a0948ffe KZ |
337 | |
338 | try_again: | |
339 | pri = -1; | |
87918040 | 340 | dev = NULL; |
a0948ffe KZ |
341 | head = blkid_find_head_cache(cache, type); |
342 | ||
343 | if (head) { | |
344 | list_for_each(p, &head->bit_names) { | |
345 | blkid_tag tmp = list_entry(p, struct blkid_struct_tag, | |
346 | bit_names); | |
347 | ||
348 | if (!strcmp(tmp->bit_val, value) && | |
349 | (tmp->bit_dev->bid_pri > pri) && | |
350 | !access(tmp->bit_dev->bid_name, F_OK)) { | |
351 | dev = tmp->bit_dev; | |
352 | pri = dev->bid_pri; | |
353 | } | |
354 | } | |
355 | } | |
356 | if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { | |
357 | dev = blkid_verify(cache, dev); | |
7508d991 | 358 | if (!dev || dev->bid_flags & BLKID_BID_FL_VERIFIED) |
a0948ffe KZ |
359 | goto try_again; |
360 | } | |
361 | ||
362 | if (!dev && !probe_new) { | |
363 | if (blkid_probe_all_new(cache) < 0) | |
364 | return NULL; | |
365 | probe_new++; | |
366 | goto try_again; | |
367 | } | |
368 | ||
84d38ae3 KZ |
369 | if (!dev && !probe_all |
370 | && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { | |
a0948ffe KZ |
371 | if (blkid_probe_all(cache) < 0) |
372 | return NULL; | |
84d38ae3 | 373 | probe_all++; |
a0948ffe KZ |
374 | goto try_again; |
375 | } | |
376 | return dev; | |
377 | } | |
378 | ||
379 | #ifdef TEST_PROGRAM | |
380 | #ifdef HAVE_GETOPT_H | |
381 | #include <getopt.h> | |
382 | #else | |
383 | extern char *optarg; | |
384 | extern int optind; | |
385 | #endif | |
386 | ||
5fde1d9f | 387 | static void __attribute__((__noreturn__)) usage(char *prog) |
a0948ffe KZ |
388 | { |
389 | fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device " | |
390 | "[type value]\n", | |
391 | prog); | |
392 | fprintf(stderr, "\tList all tags for a device and exit\n"); | |
393 | exit(1); | |
394 | } | |
395 | ||
396 | int main(int argc, char **argv) | |
397 | { | |
398 | blkid_tag_iterate iter; | |
399 | blkid_cache cache = NULL; | |
400 | blkid_dev dev; | |
401 | int c, ret, found; | |
402 | int flags = BLKID_DEV_FIND; | |
403 | char *tmp; | |
404 | char *file = NULL; | |
405 | char *devname = NULL; | |
406 | char *search_type = NULL; | |
407 | char *search_value = NULL; | |
408 | const char *type, *value; | |
409 | ||
410 | while ((c = getopt (argc, argv, "m:f:")) != EOF) | |
411 | switch (c) { | |
412 | case 'f': | |
413 | file = optarg; | |
414 | break; | |
415 | case 'm': | |
6644688a KZ |
416 | { |
417 | int mask = strtoul (optarg, &tmp, 0); | |
a0948ffe KZ |
418 | if (*tmp) { |
419 | fprintf(stderr, "Invalid debug mask: %s\n", | |
420 | optarg); | |
421 | exit(1); | |
422 | } | |
7a458332 | 423 | blkid_init_debug(mask); |
a0948ffe | 424 | break; |
6644688a | 425 | } |
a0948ffe KZ |
426 | case '?': |
427 | usage(argv[0]); | |
428 | } | |
429 | if (argc > optind) | |
430 | devname = argv[optind++]; | |
431 | if (argc > optind) | |
432 | search_type = argv[optind++]; | |
433 | if (argc > optind) | |
434 | search_value = argv[optind++]; | |
435 | if (!devname || (argc != optind)) | |
436 | usage(argv[0]); | |
437 | ||
438 | if ((ret = blkid_get_cache(&cache, file)) != 0) { | |
439 | fprintf(stderr, "%s: error creating cache (%d)\n", | |
440 | argv[0], ret); | |
441 | exit(1); | |
442 | } | |
443 | ||
444 | dev = blkid_get_dev(cache, devname, flags); | |
445 | if (!dev) { | |
223939d9 | 446 | fprintf(stderr, "%s: cannot find device in blkid cache\n", |
a0948ffe KZ |
447 | devname); |
448 | exit(1); | |
449 | } | |
450 | if (search_type) { | |
451 | found = blkid_dev_has_tag(dev, search_type, search_value); | |
452 | printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev), | |
453 | search_type, search_value ? search_value : "NULL", | |
454 | found ? "FOUND" : "NOT FOUND"); | |
455 | return(!found); | |
456 | } | |
457 | printf("Device %s...\n", blkid_dev_devname(dev)); | |
458 | ||
459 | iter = blkid_tag_iterate_begin(dev); | |
460 | while (blkid_tag_next(iter, &type, &value) == 0) { | |
461 | printf("\tTag %s has value %s\n", type, value); | |
462 | } | |
463 | blkid_tag_iterate_end(iter); | |
464 | ||
465 | blkid_put_cache(cache); | |
466 | return (0); | |
467 | } | |
468 | #endif |