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