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