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