source: branches/stable/mindi-busybox/e2fsprogs/blkid/read.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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