~csurbhi/ubuntu/maverick/e2fsprogs/e2fsprogs.fix-505719

« back to all changes in this revision

Viewing changes to lib/blkid/read.c

  • Committer: Bazaar Package Importer
  • Author(s): Matt Zimmerman
  • Date: 2004-09-19 09:43:14 UTC
  • mto: (8.1.1 lenny) (1.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040919094314-2tafd19i76fhu6ei
Tags: upstream-1.35
ImportĀ upstreamĀ versionĀ 1.35

Show diffs side-by-side

added added

removed removed

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