read.c revision 76b07bb1bc9cbcb70a94cb235954eaac993920ad
1/* 2 * read.c - read the blkid cache from disk, to avoid scanning all devices 3 * 4 * Copyright (C) 2001 Theodore Y. Ts'o 5 * Copyright (C) 2001 Andreas Dilger 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 <stdio.h> 14#include <ctype.h> 15#include <string.h> 16#include <time.h> 17#include <sys/stat.h> 18#include <unistd.h> 19#if HAVE_ERRNO_H 20#include <errno.h> 21#endif 22 23#include "blkidP.h" 24#include "uuid/uuid.h" 25 26#ifdef DEBUG_CACHE 27#define DBG(x) x 28#else 29#define DBG(x) 30#endif 31 32#ifdef HAVE_STRTOULL 33#define __USE_ISOC9X 34#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */ 35#else 36/* FIXME: need to support real strtoull here */ 37#define STRTOULL strtoul 38#endif 39 40#if HAVE_STDLIB_H 41#include <stdlib.h> 42#endif 43 44/* 45 * File format: 46 * 47 * <device [<NAME="value"> ...]>device_name</device> 48 * 49 * The following tags are required for each entry: 50 * <ID="id"> unique (within this file) ID number of this device 51 * <TIME="time"> (ascii time_t) time this entry was last read from disk 52 * <TYPE="type"> (detected) type of filesystem/data for this partition 53 * 54 * The following tags may be present, depending on the device contents 55 * <LABEL="label"> (user supplied) label (volume name, etc) 56 * <UUID="uuid"> (generated) universally unique identifier (serial no) 57 */ 58 59static char *skip_over_blank(char *cp) 60{ 61 while (*cp && isspace(*cp)) 62 cp++; 63 return cp; 64} 65 66static char *skip_over_word(char *cp) 67{ 68 char ch; 69 70 while ((ch = *cp)) { 71 /* If we see a backslash, skip the next character */ 72 if (ch == '\\') { 73 cp++; 74 if (*cp == '\0') 75 break; 76 cp++; 77 continue; 78 } 79 if (isspace(ch) || ch == '<' || ch == '>') 80 break; 81 cp++; 82 } 83 return cp; 84} 85 86static char *strip_line(char *line) 87{ 88 char *p; 89 90 line = skip_over_blank(line); 91 92 p = line + strlen(line) - 1; 93 94 while (*line) { 95 if (isspace(*p)) 96 *p-- = '\0'; 97 else 98 break; 99 } 100 101 return line; 102} 103 104#if 0 105static char *parse_word(char **buf) 106{ 107 char *word, *next; 108 109 word = *buf; 110 if (*word == '\0') 111 return NULL; 112 113 word = skip_over_blank(word); 114 next = skip_over_word(word); 115 if (*next) { 116 char *end = next - 1; 117 if (*end == '"' || *end == '\'') 118 *end = '\0'; 119 *next++ = '\0'; 120 } 121 *buf = next; 122 123 if (*word == '"' || *word == '\'') 124 word++; 125 return word; 126} 127#endif 128 129/* 130 * Start parsing a new line from the cache. 131 * 132 * line starts with "<device" return 1 -> continue parsing line 133 * line starts with "<foo", empty, or # return 0 -> skip line 134 * line starts with other, return -BLKID_ERR_CACHE -> error 135 */ 136static int parse_start(char **cp) 137{ 138 char *p; 139 140 p = strip_line(*cp); 141 142 /* Skip comment or blank lines. We can't just NUL the first '#' char, 143 * in case it is inside quotes, or escaped. 144 */ 145 if (*p == '\0' || *p == '#') 146 return 0; 147 148 if (!strncmp(p, "<device", 7)) { 149 DBG(printf("found device header: %8s\n", p)); 150 p += 7; 151 152 *cp = p; 153 return 1; 154 } 155 156 if (*p == '<') 157 return 0; 158 159 return -BLKID_ERR_CACHE; 160} 161 162/* Consume the remaining XML on the line (cosmetic only) */ 163static int parse_end(char **cp) 164{ 165 *cp = skip_over_blank(*cp); 166 167 if (!strncmp(*cp, "</device>", 9)) { 168 DBG(printf("found device trailer %9s\n", *cp)); 169 *cp += 9; 170 return 0; 171 } 172 173 return -BLKID_ERR_CACHE; 174} 175 176/* 177 * Allocate a new device struct with device name filled in. Will handle 178 * finding the device on lines of the form: 179 * <device foo=bar>devname</device> 180 * <device>devname<foo>bar</foo></device> 181 */ 182static int parse_dev(blkid_dev *dev, char **cp) 183{ 184 char **name; 185 char *start, *tmp, *end; 186 int ret; 187 188 if ((ret = parse_start(cp)) <= 0) 189 return ret; 190 191 start = tmp = strchr(*cp, '>'); 192 if (!start) { 193 fprintf(stderr, "blkid: short line parsing dev: %s\n", *cp); 194 return -BLKID_ERR_CACHE; 195 } 196 start = skip_over_blank(start + 1); 197 end = skip_over_word(start); 198 199 DBG(printf("device should be %*s\n", end - start, start)); 200 201 if (**cp == '>') 202 *cp = end; 203 else 204 (*cp)++; 205 206 *tmp = '\0'; 207 208 if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) 209 fprintf(stderr, "blkid: missing </device> ending: %s\n", end); 210 else if (tmp) 211 *tmp = '\0'; 212 213 if (end - start <= 1) { 214 fprintf(stderr, "blkid: empty device name: %s\n", *cp); 215 return -BLKID_ERR_CACHE; 216 } 217 218 if (!(*dev = blkid_new_dev())) 219 return -BLKID_ERR_MEM; 220 221 name = &(*dev)->bid_name; 222 *name = (char *)malloc(end - start + 1); 223 if (*name == NULL) { 224 blkid_free_dev(*dev); 225 return -BLKID_ERR_MEM; 226 } 227 228 memcpy(*name, start, end - start); 229 (*name)[end - start] = '\0'; 230 231 DBG(printf("found dev %s\n", *name)); 232 233 return 1; 234} 235 236/* 237 * Extract a tag of the form NAME="value" from the line. 238 */ 239static int parse_token(char **name, char **value, char **cp) 240{ 241 char *end; 242 243 if (!name || !value || !cp) 244 return -BLKID_ERR_PARAM; 245 246 if (!(*value = strchr(*cp, '='))) 247 return 0; 248 249 **value = '\0'; 250 *name = strip_line(*cp); 251 *value = skip_over_blank(*value + 1); 252 253 if (**value == '"') { 254 end = strchr(*value + 1, '"'); 255 if (!end) { 256 fprintf(stderr, "unbalanced quotes at: %s\n", *value); 257 *cp = *value; 258 return -BLKID_ERR_CACHE; 259 } 260 (*value)++; 261 *end = '\0'; 262 end++; 263 } else { 264 end = skip_over_word(*value); 265 if (*end) { 266 *end = '\0'; 267 end++; 268 } 269 } 270 *cp = end; 271 272 return 1; 273} 274 275/* 276 * Extract a tag of the form <NAME>value</NAME> from the line. 277 */ 278/* 279static int parse_xml(char **name, char **value, char **cp) 280{ 281 char *end; 282 283 if (!name || !value || !cp) 284 return -BLKID_ERR_PARAM; 285 286 *name = strip_line(*cp); 287 288 if ((*name)[0] != '<' || (*name)[1] == '/') 289 return 0; 290 291 FIXME: finish this. 292} 293*/ 294 295/* 296 * Extract a tag from the line. 297 * 298 * Return 1 if a valid tag was found. 299 * Return 0 if no tag found. 300 * Return -ve error code. 301 */ 302static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp) 303{ 304 char *name; 305 char *value; 306 int ret; 307 308 if (!cache || !dev) 309 return -BLKID_ERR_PARAM; 310 311 if ((ret = parse_token(&name, &value, cp)) <= 0 /* && 312 (ret = parse_xml(&name, &value, cp)) <= 0 */) 313 return ret; 314 315 /* Some tags are stored directly in the device struct */ 316 if (!strcmp(name, "ID")) { 317 dev->bid_id = (unsigned int)strtoul(value, 0, 0); 318 if (dev->bid_id > cache->bic_idmax) 319 cache->bic_idmax = dev->bid_id; 320 } else if (!strcmp(name, "DEVNO")) 321 dev->bid_devno = STRTOULL(value, 0, 0); 322 else if (!strcmp(name, "DEVSIZE")) 323 dev->bid_devno = STRTOULL(value, 0, 0); 324 else if (!strcmp(name, "TIME")) 325 /* FIXME: need to parse a long long eventually */ 326 dev->bid_time = strtol(value, 0, 0); 327 else 328 ret = blkid_create_tag(dev, name, value, strlen(value)); 329 330 DBG(printf(" tag: %s=\"%s\"\n", name, value)); 331 332 return ret < 0 ? ret : 1; 333} 334 335/* 336 * Parse a single line of data, and return a newly allocated dev struct. 337 * Add the new device to the cache struct, if one was read. 338 * 339 * Lines are of the form <device [TAG="value" ...]>/dev/foo</device> 340 * 341 * Returns -ve value on error. 342 * Returns 0 otherwise. 343 * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL 344 * (e.g. comment lines, unknown XML content, etc). 345 */ 346static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp) 347{ 348 blkid_dev dev; 349 int ret; 350 351 if (!cache || !dev_p) 352 return -BLKID_ERR_PARAM; 353 354 *dev_p = NULL; 355 356 DBG(printf("line: %s\n", cp)); 357 358 if ((ret = parse_dev(dev_p, &cp)) <= 0) 359 return ret; 360 361 dev = *dev_p; 362 363 while ((ret = parse_tag(cache, dev, &cp)) > 0) { 364 ; 365 } 366 367 if (dev->bid_type == NULL) { 368 fprintf(stderr, "blkid: device %s has no TYPE\n",dev->bid_name); 369 blkid_free_dev(dev); 370 } 371 372 DEB_DUMP_DEV(dev); 373 374 *dev_p = blkid_add_dev_to_cache(cache, dev); 375 376 return ret; 377} 378 379/* 380 * Read the given file stream for cached device data, and return it 381 * in a newly allocated cache struct. 382 * 383 * Returns 0 on success, or -ve error value. 384 */ 385int blkid_read_cache_file(blkid_cache *cache, FILE *file) 386{ 387 char buf[4096]; 388 int lineno = 0; 389 390 if (!file || !cache) 391 return -BLKID_ERR_PARAM; 392 393 if (!*cache) 394 *cache = blkid_new_cache(); 395 396 if (!*cache) 397 return -BLKID_ERR_MEM; 398 399 while (fgets(buf, sizeof(buf), file)) { 400 blkid_dev dev; 401 402 int end = strlen(buf) - 1; 403 404 lineno++; 405 /* Continue reading next line if it ends with a backslash */ 406 while (buf[end] == '\\' && end < sizeof(buf) - 2 && 407 fgets(buf + end, sizeof(buf) - end, stdin)) { 408 end = strlen(buf) - 1; 409 lineno++; 410 } 411 412 if (blkid_parse_line(*cache, &dev, buf) < 0) { 413 fprintf(stderr, "blkid: bad format on line %d\n", 414 lineno); 415 continue; 416 } 417 } 418 419 /* 420 * Initially assume that we do not need to write out the cache file. 421 * This would be incorrect if we probed first, and parsed the cache 422 * afterwards, or parsed two caches and wanted to write it out, but 423 * the alternative is to force manually marking the cache dirty when 424 * any device is added, and that is also prone to error. 425 */ 426 (*cache)->bic_flags &= ~BLKID_BIC_FL_CHANGED; 427 428 return 0; 429} 430 431/* 432 * Parse the specified filename, and return the data in the supplied or 433 * a newly allocated cache struct. If the file doesn't exist, return a 434 * new empty cache struct. 435 */ 436int blkid_read_cache(blkid_cache *cache, const char *filename) 437{ 438 FILE *file; 439 int ret; 440 441 if (!cache) 442 return -BLKID_ERR_PARAM; 443 444 if (!filename || !strlen(filename)) 445 filename = BLKID_CACHE_FILE; 446 447 DBG(printf("cache file %s\n", filename)); 448 449 /* If we read the standard cache file, do not do so again */ 450 if (!strcmp(filename, BLKID_CACHE_FILE) && (*cache) && 451 ((*cache)->bic_flags & BLKID_BIC_FL_PARSED)) 452 return 0; 453 454 if (!strcmp(filename, "-") || !strcmp(filename, "stdin")) 455 file = stdin; 456 else { 457 /* 458 * If the file doesn't exist, then we just return an empty 459 * struct so that the cache can be populated. 460 */ 461 if (access(filename, R_OK) < 0) { 462 *cache = blkid_new_cache(); 463 464 return *cache ? 0 : -BLKID_ERR_MEM; 465 } 466 467 file = fopen(filename, "r"); 468 if (!file) { 469 perror(filename); 470 return errno; 471 } 472 } 473 474 ret = blkid_read_cache_file(cache, file); 475 476 if (file != stdin) 477 fclose(file); 478 479 /* Mark us as having read the standard cache file */ 480 if (!strcmp(filename, BLKID_CACHE_FILE)) 481 (*cache)->bic_flags |= BLKID_BIC_FL_PARSED; 482 483 return ret; 484} 485 486#ifdef TEST_PROGRAM 487int main(int argc, char**argv) 488{ 489 blkid_cache cache = NULL; 490 int ret; 491 492 if (argc > 2) { 493 fprintf(stderr, "Usage: %s [filename]\n" 494 "Test parsing of the cache (filename)\n", argv[0]); 495 exit(1); 496 } 497 if ((ret = blkid_read_cache(&cache, argv[1])) < 0) 498 fprintf(stderr, "error %d reading cache file %s\n", ret, 499 argv[1] ? argv[1] : BLKID_CACHE_FILE); 500 501 blkid_free_cache(cache); 502 503 return ret; 504} 505#endif 506