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