Whamcloud - gitweb
[COVERITY] Fix (error case) memory leak in blkid library (parse_dev)
[tools/e2fsprogs.git] / lib / blkid / read.c
index 89a7bfd..583b549 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * read.c - read the blkid cache from disk, to avoid scanning all devices
  *
- * Copyright (C) 2001 Theodore Y. Ts'o
+ * Copyright (C) 2001, 2003 Theodore Y. Ts'o
  * Copyright (C) 2001 Andreas Dilger
  *
  * %Begin-Header%
@@ -14,7 +14,9 @@
 #include <ctype.h>
 #include <string.h>
 #include <time.h>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include <unistd.h>
 #if HAVE_ERRNO_H
 #include <errno.h>
 #include "blkidP.h"
 #include "uuid/uuid.h"
 
-#ifdef DEBUG_CACHE
-#define DBG(x) x
-#else
-#define DBG(x)
-#endif
-
 #ifdef HAVE_STRTOULL
 #define __USE_ISOC9X
 #define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
 #include <stdlib.h>
 #endif
 
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev)      (debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#endif
+
 /*
  * File format:
  *
@@ -146,7 +147,7 @@ static int parse_start(char **cp)
                return 0;
 
        if (!strncmp(p, "<device", 7)) {
-               DBG(printf("found device header: %8s\n", p));
+               DBG(DEBUG_READ, printf("found device header: %8s\n", p));
                p += 7;
 
                *cp = p;
@@ -165,7 +166,7 @@ static int parse_end(char **cp)
        *cp = skip_over_blank(*cp);
 
        if (!strncmp(*cp, "</device>", 9)) {
-               DBG(printf("found device trailer %9s\n", *cp));
+               DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
                *cp += 9;
                return 0;
        }
@@ -179,10 +180,9 @@ static int parse_end(char **cp)
  * <device foo=bar>devname</device>
  * <device>devname<foo>bar</foo></device>
  */
-static int parse_dev(blkid_dev *dev, char **cp)
+static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
 {
-       char **name;
-       char *start, *tmp, *end;
+       char *start, *tmp, *end, *name;
        int ret;
 
        if ((ret = parse_start(cp)) <= 0)
@@ -190,13 +190,14 @@ static int parse_dev(blkid_dev *dev, char **cp)
 
        start = tmp = strchr(*cp, '>');
        if (!start) {
-               fprintf(stderr, "blkid: short line parsing dev: %s\n", *cp);
+               DBG(DEBUG_READ,
+                   printf("blkid: short line parsing dev: %s\n", *cp));
                return -BLKID_ERR_CACHE;
        }
        start = skip_over_blank(start + 1);
        end = skip_over_word(start);
 
-       DBG(printf("device should be %*s\n", end - start, start));
+       DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start));
 
        if (**cp == '>')
                *cp = end;
@@ -205,31 +206,29 @@ static int parse_dev(blkid_dev *dev, char **cp)
 
        *tmp = '\0';
 
-       if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0)
-               fprintf(stderr, "blkid: missing </device> ending: %s\n", end);
-       else if (tmp)
+       if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+               DBG(DEBUG_READ,
+                   printf("blkid: missing </device> ending: %s\n", end));
+       } else if (tmp)
                *tmp = '\0';
 
        if (end - start <= 1) {
-               fprintf(stderr, "blkid: empty device name: %s\n", *cp);
+               DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
                return -BLKID_ERR_CACHE;
        }
 
-       if (!(*dev = blkid_new_dev()))
+       name = blkid_strndup(start, end-start);
+       if (name == NULL)
                return -BLKID_ERR_MEM;
 
-       name = &(*dev)->bid_name;
-       *name = (char *)malloc(end - start + 1);
-       if (*name == NULL) {
-               blkid_free_dev(*dev);
+       DBG(DEBUG_READ, printf("found dev %s\n", name));
+
+       if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) {
+               free(name);
                return -BLKID_ERR_MEM;
        }
 
-       memcpy(*name, start, end - start);
-       (*name)[end - start] = '\0';
-
-       DBG(printf("found dev %s\n", *name));
-
+       free(name);
        return 1;
 }
 
@@ -253,7 +252,8 @@ static int parse_token(char **name, char **value, char **cp)
        if (**value == '"') {
                end = strchr(*value + 1, '"');
                if (!end) {
-                       fprintf(stderr, "unbalanced quotes at: %s\n", *value);
+                       DBG(DEBUG_READ,
+                           printf("unbalanced quotes at: %s\n", *value));
                        *cp = *value;
                        return -BLKID_ERR_CACHE;
                }
@@ -313,21 +313,17 @@ static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
                return ret;
 
        /* Some tags are stored directly in the device struct */
-       if (!strcmp(name, "ID")) {
-               dev->bid_id = (unsigned int)strtoul(value, 0, 0);
-               if (dev->bid_id > cache->bic_idmax)
-                       cache->bic_idmax = dev->bid_id;
-       } else if (!strcmp(name, "DEVNO"))
-               dev->bid_devno = STRTOULL(value, 0, 0);
-       else if (!strcmp(name, "DEVSIZE"))
+       if (!strcmp(name, "DEVNO")) 
                dev->bid_devno = STRTOULL(value, 0, 0);
+       else if (!strcmp(name, "PRI"))
+               dev->bid_pri = strtol(value, 0, 0);
        else if (!strcmp(name, "TIME"))
                /* FIXME: need to parse a long long eventually */
                dev->bid_time = strtol(value, 0, 0);
        else
-               ret = blkid_create_tag(dev, name, value, strlen(value));
+               ret = blkid_set_tag(dev, name, value, strlen(value));
 
-       DBG(printf("    tag: %s=\"%s\"\n", name, value));
+       DBG(DEBUG_READ, printf("    tag: %s=\"%s\"\n", name, value));
 
        return ret < 0 ? ret : 1;
 }
@@ -353,9 +349,9 @@ static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
 
        *dev_p = NULL;
 
-       DBG(printf("line: %s\n", cp));
+       DBG(DEBUG_READ, printf("line: %s\n", cp));
 
-       if ((ret = parse_dev(dev_p, &cp)) <= 0)
+       if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
                return ret;
 
        dev = *dev_p;
@@ -365,140 +361,131 @@ static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
        }
 
        if (dev->bid_type == NULL) {
-               fprintf(stderr, "blkid: device %s has no TYPE\n",dev->bid_name);
+               DBG(DEBUG_READ,
+                   printf("blkid: device %s has no TYPE\n",dev->bid_name));
                blkid_free_dev(dev);
        }
 
-       DEB_DUMP_DEV(dev);
-
-       *dev_p = blkid_add_dev_to_cache(cache, dev);
+       DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
 
        return ret;
 }
 
 /*
- * Read the given file stream for cached device data, and return it
- * in a newly allocated cache struct.
- *
- * Returns 0 on success, or -ve error value.
+ * Parse the specified filename, and return the data in the supplied or
+ * a newly allocated cache struct.  If the file doesn't exist, return a
+ * new empty cache struct.
  */
-int blkid_read_cache_file(blkid_cache *cache, FILE *file)
+void blkid_read_cache(blkid_cache cache)
 {
+       FILE *file;
        char buf[4096];
-       int lineno = 0;
+       int fd, lineno = 0;
+       struct stat st;
 
-       if (!file || !cache)
-               return -BLKID_ERR_PARAM;
+       if (!cache)
+               return;
 
-       if (!*cache)
-               *cache = blkid_new_cache();
+       /*
+        * If the file doesn't exist, then we just return an empty
+        * struct so that the cache can be populated.
+        */
+       if ((fd = open(cache->bic_filename, O_RDONLY)) < 0)
+               return;
+       if (fstat(fd, &st) < 0)
+               goto errout;
+       if ((st.st_mtime == cache->bic_ftime) ||
+           (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+               DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
+                                       cache->bic_filename));
+               goto errout;
+       }
+       
+       DBG(DEBUG_CACHE, printf("reading cache file %s\n",
+                               cache->bic_filename));
 
-       if (!*cache)
-               return -BLKID_ERR_MEM;
+       file = fdopen(fd, "r");
+       if (!file)
+               goto errout;
 
        while (fgets(buf, sizeof(buf), file)) {
                blkid_dev dev;
-
-               int end = strlen(buf) - 1;
+               unsigned int end;
 
                lineno++;
+               if (buf[0] == 0)
+                       continue;
+               end = strlen(buf) - 1;
                /* Continue reading next line if it ends with a backslash */
                while (buf[end] == '\\' && end < sizeof(buf) - 2 &&
-                      fgets(buf + end, sizeof(buf) - end, stdin)) {
+                      fgets(buf + end, sizeof(buf) - end, file)) {
                        end = strlen(buf) - 1;
                        lineno++;
                }
 
-               if (blkid_parse_line(*cache, &dev, buf) < 0) {
-                       fprintf(stderr, "blkid: bad format on line %d\n",
-                               lineno);
+               if (blkid_parse_line(cache, &dev, buf) < 0) {
+                       DBG(DEBUG_READ,
+                           printf("blkid: bad format on line %d\n", lineno));
                        continue;
                }
        }
+       fclose(file);
 
        /*
-        * Initially assume that we do not need to write out the cache file.
-        * This would be incorrect if we probed first, and parsed the cache
-        * afterwards, or parsed two caches and wanted to write it out, but
-        * the alternative is to force manually marking the cache dirty when
-        * any device is added, and that is also prone to error.
+        * Initially we do not need to write out the cache file.
         */
-       (*cache)->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+       cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+       cache->bic_ftime = st.st_mtime;
 
-       return 0;
+       return;
+errout:
+       close(fd);
+       return;
 }
 
-/*
- * Parse the specified filename, and return the data in the supplied or
- * a newly allocated cache struct.  If the file doesn't exist, return a
- * new empty cache struct.
- */
-int blkid_read_cache(blkid_cache *cache, const char *filename)
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
 {
-       FILE *file;
-       int ret;
-
-       if (!cache)
-               return -BLKID_ERR_PARAM;
-
-       if (!filename || !strlen(filename))
-               filename = BLKID_CACHE_FILE;
-
-       DBG(printf("cache file %s\n", filename));
+       struct list_head *p;
 
-       /* If we read the standard cache file, do not do so again */
-       if (!strcmp(filename, BLKID_CACHE_FILE) && (*cache) &&
-           ((*cache)->bic_flags & BLKID_BIC_FL_PARSED))
-               return 0;
-
-       if (!strcmp(filename, "-") || !strcmp(filename, "stdin"))
-               file = stdin;
-       else {
-               /*
-                * If the file doesn't exist, then we just return an empty
-                * struct so that the cache can be populated.
-                */
-               if (access(filename, R_OK) < 0) {
-                       *cache = blkid_new_cache();
-
-                       return *cache ? 0 : -BLKID_ERR_MEM;
-               }
-
-               file = fopen(filename, "r");
-               if (!file) {
-                       perror(filename);
-                       return errno;
-               }
+       if (!dev) {
+               printf("  dev: NULL\n");
+               return;
        }
 
-       ret = blkid_read_cache_file(cache, file);
-
-       if (file != stdin)
-               fclose(file);
-
-       /* Mark us as having read the standard cache file */
-       if (!strcmp(filename, BLKID_CACHE_FILE))
-               (*cache)->bic_flags |= BLKID_BIC_FL_PARSED;
-
-       return ret;
+       printf("  dev: name = %s\n", dev->bid_name);
+       printf("  dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+       printf("  dev: TIME=\"%lld\"\n", (long long)dev->bid_time);
+       printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+       printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+       list_for_each(p, &dev->bid_tags) {
+               blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+               if (tag)
+                       printf("    tag: %s=\"%s\"\n", tag->bit_name, 
+                              tag->bit_val);
+               else
+                       printf("    tag: NULL\n");
+       }
+       printf("\n");
 }
 
-#ifdef TEST_PROGRAM
 int main(int argc, char**argv)
 {
        blkid_cache cache = NULL;
        int ret;
 
+       blkid_debug_mask = DEBUG_ALL;
        if (argc > 2) {
                fprintf(stderr, "Usage: %s [filename]\n"
                        "Test parsing of the cache (filename)\n", argv[0]);
                exit(1);
        }
-       if ((ret = blkid_read_cache(&cache, argv[1])) < 0)
+       if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
                fprintf(stderr, "error %d reading cache file %s\n", ret,
                        argv[1] ? argv[1] : BLKID_CACHE_FILE);
 
-       blkid_free_cache(cache);
+       blkid_put_cache(cache);
 
        return ret;
 }