~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to libblkid/src/read.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

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