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