~ubuntu-branches/ubuntu/lucid/loop-aes-utils/lucid-security

« back to all changes in this revision

Viewing changes to libs/blkid/src/read.c

  • Committer: Bazaar Package Importer
  • Author(s): Max Vozeler
  • Date: 2009-07-06 02:08:18 UTC
  • mfrom: (1.3.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090706020818-11pxao7bhgjenfv9
Tags: 2.15.1~rc1-2
Disable ncurses (--without-ncurses), not used in
mount/. Fixes FTBFS (closes: #535676).

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