]> err.no Git - util-linux/commitdiff
libblkid: add paths and tags cache
authorKarel Zak <kzak@redhat.com>
Fri, 27 Nov 2009 13:35:03 +0000 (14:35 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 3 Jun 2010 13:20:10 +0000 (15:20 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/mount/src/Makefile.am
shlibs/mount/src/cache.c [new file with mode: 0644]
shlibs/mount/src/mount.h.in
shlibs/mount/src/mountP.h

index f9cc68f939dff35a0482a45e264e3dc04040592c..598296316fc5b76e6fec08f42f28e937235e4377 100644 (file)
@@ -11,7 +11,8 @@ nodist_mountinc_HEADERS = mount.h
 usrlib_exec_LTLIBRARIES = libmount.la
 libmount_la_SOURCES = $(mountinc_HEADERS)
 
-nodist_libmount_la_SOURCES = mount.h version.c utils.c test.c init.c
+nodist_libmount_la_SOURCES = mount.h version.c utils.c test.c init.c cache.c \
+                       $(top_srcdir)/lib/canonicalize.c
 
 libmount_la_LIBADD = $(ul_libblkid_la)
 
@@ -20,7 +21,7 @@ libmount_la_DEPENDENCIES = $(libmount_la_LIBADD) mount.sym mount.h.in
 libmount_la_LDFLAGS = -Wl,--version-script=$(ul_libmount_srcdir)/mount.sym \
                       -version-info $(LIBMOUNT_VERSION_INFO)
 
-tests = test_version
+tests = test_version test_cache
 
 EXTRA_DIST = mount.sym mount.h.in
 CLEANFILES = $(tests)
diff --git a/shlibs/mount/src/cache.c b/shlibs/mount/src/cache.c
new file mode 100644 (file)
index 0000000..7b9040f
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <blkid.h>
+
+#include "canonicalize.h"
+#include "mountP.h"
+
+/*
+ * Canonicalized (resolved) paths cache
+ */
+#define MNT_CACHE_CHUNKSZ      128
+
+#define MNT_CACHE_ISTAG        (1 << 1) /* entry is TAG */
+#define MNT_CACHE_ISPATH       (1 << 2) /* entry is path */
+#define MNT_CACHE_TAGREAD      (1 << 3) /* tag read by mnt_cache_read_tags() */
+
+/* path cache entry */
+struct mnt_cache_entry {
+       char                    *native;        /* the original path */
+       char                    *real;          /* canonicalized path */
+       int                     flag;
+};
+
+struct _mnt_cache {
+       struct mnt_cache_entry  *ents;
+       size_t                  nents;
+       size_t                  nallocs;
+
+       /* blkid_evaluate_tag() works in two ways:
+        *
+        * 1/ all tags are evaluated by udev /dev/disk/by-* symlinks,
+        *    then the blkid_cache is NULL.
+        *
+        * 2/ all tags are read from /etc/blkid.tab and verified by /dev
+        *    scanning, then the blkid_cache is not NULL and then it's
+        *    better to reuse the blkid_cache.
+        */
+       blkid_cache             bc;
+};
+
+/**
+ * mnt_new_cache:
+ *
+ * Returns new mnt_cache instance or NULL in case of ENOMEM error.
+ */
+mnt_cache *mnt_new_cache(void)
+{
+       return calloc(1, sizeof(struct _mnt_cache));
+}
+
+/**
+ * mnt_free_cache:
+ * @cache: pointer to mnt_cache instance
+ *
+ * Deallocates mnt_cache.
+ */
+void mnt_free_cache(mnt_cache *cache)
+{
+       int i;
+
+       if (!cache)
+               return;
+       for (i = 0; i < cache->nents; i++) {
+               struct mnt_cache_entry *e = &cache->ents[i];
+               if (e->real != e->native)
+                       free(e->real);
+               free(e->native);
+       }
+       free(cache->ents);
+       if (cache->bc)
+               blkid_put_cache(cache->bc);
+       free(cache);
+}
+
+/* note that the @native could be tha same pointer as @real */
+static int mnt_cache_add_entry(mnt_cache *cache, char *native,
+                                       char *real, int flag)
+{
+       struct mnt_cache_entry *e;
+
+       assert(cache);
+       assert(real);
+       assert(native);
+
+       if (cache->nents == cache->nallocs) {
+               size_t sz = cache->nallocs + MNT_CACHE_CHUNKSZ;
+
+               e = realloc(cache->ents, sz * sizeof(struct mnt_cache_entry));
+               if (!e)
+                       return -1;
+               cache->ents = e;
+               cache->nallocs = sz;
+       }
+
+       e = &cache->ents[cache->nents];
+       e->native = native;
+       e->real = real;
+       e->flag = flag;
+       cache->nents++;
+
+       DBG(DEBUG_CACHE,
+               printf("cache: [%zd entry] added %s\n", cache->nents, real));
+
+       return 0;
+}
+
+/* add tag to the cache, @real has to be allocated string */
+static int mnt_cache_add_tag(mnt_cache *cache, const char *token,
+                               const char *value, char *real, int flag)
+{
+       size_t tksz, vlsz;
+       char *native;
+
+       assert(cache);
+       assert(real);
+       assert(token);
+       assert(value);
+
+       /* add into cache -- cache format for TAGs is
+        *      native = "NAME\0VALUE\0"
+        *      real   = "/dev/foo"
+        */
+       tksz = strlen(token);
+       vlsz = strlen(value);
+
+       native = malloc(tksz + vlsz + 2);
+       if (!native)
+               goto error;
+
+       memcpy(native, token, tksz + 1);           /* include '\0' */
+       memcpy(native + tksz + 1, value, vlsz + 1);
+
+       if (mnt_cache_add_entry(cache, native, real, flag))
+               goto error;
+       return 0;
+error:
+       free(native);
+       return -1;
+}
+
+
+/**
+ * mnt_cache_find_path:
+ * @cache: pointer to mnt_cache instance
+ * @path: requested "native" (non-canonicalized) path
+ *
+ * Returns cached canonicalized path or NULL.
+ */
+const char *mnt_cache_find_path(mnt_cache *cache, const char *path)
+{
+       int i;
+
+       assert(cache);
+       assert(path);
+
+       if (!cache || !path)
+               return NULL;
+
+       for (i = 0; i < cache->nents; i++) {
+               struct mnt_cache_entry *e = &cache->ents[i];
+               if (!(e->flag & MNT_CACHE_ISPATH))
+                       continue;
+               if (strcmp(path, e->native) == 0)
+                       return e->real;
+       }
+       return NULL;
+}
+
+/**
+ * mnt_cache_find_tag:
+ * @cache: pointer to mnt_cache instance
+ * @token: tag name
+ * @value: tag value
+ *
+ * Returns cached path or NULL.
+ */
+const char *mnt_cache_find_tag(mnt_cache *cache,
+                       const char *token, const char *value)
+{
+       int i;
+       size_t tksz;
+
+       assert(cache);
+       assert(token);
+       assert(value);
+
+       if (!cache || !token || !value)
+               return NULL;
+
+       tksz = strlen(token);
+
+       for (i = 0; i < cache->nents; i++) {
+               struct mnt_cache_entry *e = &cache->ents[i];
+               if (!(e->flag & MNT_CACHE_ISTAG))
+                       continue;
+               if (strcmp(token, e->native) == 0 &&
+                   strcmp(value, e->native + tksz + 1) == 0)
+                       return e->real;
+       }
+       return NULL;
+}
+
+/**
+ * mnt_cache_read_tags
+ * @cache: pointer to mnt_cache instance
+ * @devname: path device
+ *
+ * Reads @devname LABEL and UUID to the @cache.
+ *
+ * Returns: 1 if at least on tag was added, 0 no tag was added or
+ *          -1 in case of error.
+ */
+int mnt_cache_read_tags(mnt_cache *cache, const char *devname)
+{
+       int i, ntags = 0;
+       int fd;
+       static blkid_probe pr;
+       const char *tags[] = { "LABEL", "UUID" };
+
+       assert(cache);
+       assert(devname);
+
+       if (!cache || !devname)
+               return -1;
+
+       /* check is device is already cached */
+       for (i = 0; i < cache->nents; i++) {
+               struct mnt_cache_entry *e = &cache->ents[i];
+               if (!(e->flag & MNT_CACHE_TAGREAD))
+                       continue;
+               if (strcmp(e->real, devname) == 0)
+                       /* tags has been already read */
+                       return 0;
+       }
+
+       DBG(DEBUG_CACHE,
+               printf("cache: reading tags for: %s\n", devname));
+
+       fd = mnt_open_device(devname, O_RDONLY);
+       if (fd < 0)
+               return -1;
+       pr = blkid_new_probe();
+       if (!pr)
+               goto error;
+       if (blkid_probe_set_device(pr, fd, 0, 0))
+               goto error;
+
+       blkid_probe_enable_superblocks(pr, 1);
+
+       blkid_probe_set_superblocks_flags(pr,
+                       BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
+
+       if (blkid_do_safeprobe(pr))
+               goto error;
+
+       for (i = 0; i < ARRAY_SIZE(tags); i++) {
+               const char *data;
+               char *dev;
+
+               if (blkid_probe_lookup_value(pr, tags[i], &data, NULL))
+                       continue;
+               if (mnt_cache_find_tag(cache, tags[i], data))
+                       continue; /* already cached */
+
+               dev = strdup(devname);
+               if (!dev)
+                       goto error;
+               if (mnt_cache_add_tag(cache, tags[i], data, dev,
+                               (MNT_CACHE_ISTAG | MNT_CACHE_TAGREAD))) {
+                       free(dev);
+                       goto error;
+               }
+               ntags++;
+       }
+
+       return ntags ? 1 : 0;
+error:
+       blkid_free_probe(pr);
+       close(fd);
+       return -1;
+}
+
+/**
+ * mnt_cache_device_has_tag:
+ * @cache: paths cache
+ * @devname: path to the device
+ * @token: tag name (e.g "LABEL")
+ * @value: tag value
+ *
+ * Look up @cache to check it @tag+@value are associated with @devname.
+ *
+ * Returns: 1 on success or 0.
+ */
+int mnt_cache_device_has_tag(mnt_cache *cache, const char *devname,
+                               const char *token, const char *value)
+{
+       const char *path = mnt_cache_find_tag(cache, token, value);
+
+       if (path && strcmp(path, devname) == 0)
+               return 1;
+       return 0;
+}
+
+/**
+ * mnt_resolve_path:
+ * @path: "native" path
+ * @cache: cache for results or NULL
+ *
+ * Returns absolute path or NULL in case of error. The result has to be
+ * deallocated by free() if @cache is NULL.
+ */
+char *mnt_resolve_path(const char *path, mnt_cache *cache)
+{
+       char *p = NULL;
+       char *native = NULL;
+       char *real = NULL;
+
+       assert(path);
+
+       if (!path)
+               return NULL;
+       if (cache)
+               p = (char *) mnt_cache_find_path(cache, path);
+
+       if (!p) {
+               p = canonicalize_path(path);
+
+               if (p && cache) {
+                       native = strdup(path);
+                       real = strcmp(path, p) == 0 ? native : p;
+
+                       if (!native || !real)
+                               goto error;
+
+                       if (mnt_cache_add_entry(cache, native, real,
+                                                       MNT_CACHE_ISPATH))
+                               goto error;
+               }
+       }
+
+       DBG(DEBUG_CACHE, printf("cache: %s --> %s\n", path, p));
+       return p;
+error:
+       if (real != native)
+               free(real);
+       free(native);
+       return NULL;
+}
+
+/**
+ * mnt_resolve_tag:
+ * @token: tag name
+ * @value: tag value
+ * @cache: for results or NULL
+ *
+ * Returns device name or NULL in case of error. The result has to be
+ * deallocated by free() if @cache is NULL.
+ */
+char *mnt_resolve_tag(const char *token, const char *value, mnt_cache *cache)
+{
+       char *p = NULL;
+
+       assert(token);
+       assert(value);
+
+       if (!token || !value)
+               return NULL;
+
+       if (cache)
+               p = (char *) mnt_cache_find_tag(cache, token, value);
+
+       if (!p) {
+               /* returns newly allocated string */
+               p = blkid_evaluate_tag(token, value, cache ? &cache->bc : NULL);
+
+               if (p && cache &&
+                   mnt_cache_add_tag(cache, token, value, p, MNT_CACHE_ISTAG))
+                               goto error;
+       }
+
+       DBG(DEBUG_CACHE, printf("cache: %s=%s --> %s\n", token, value, p));
+       return p;
+error:
+       free(p);
+       return NULL;
+}
+
+
+
+/**
+ * mnt_resolve_spec:
+ * @spec: path or tag
+ * @cache: paths cache
+ *
+ * Returns canonicalized path or NULL.
+ */
+char *mnt_resolve_spec(const char *spec, mnt_cache *cache)
+{
+       char *cn = NULL;
+
+       if (!spec)
+               return NULL;
+
+       if (strchr(spec, '=')) {
+               char *tag, *val;
+
+               if (!blkid_parse_tag_string(spec, &tag, &val)) {
+                       cn = mnt_resolve_tag(tag, val, cache);
+
+                       free(tag);
+                       free(val);
+               }
+       } else
+               cn = mnt_resolve_path(spec, cache);
+
+       return cn;
+}
+
+
+#ifdef TEST_PROGRAM
+
+int test_resolve_path(struct mtest *ts, int argc, char *argv[])
+{
+       char line[BUFSIZ];
+       mnt_cache *cache;
+
+       cache = mnt_new_cache();
+       if (!cache)
+               return -1;
+
+       while(fgets(line, sizeof(line), stdin)) {
+               size_t sz = strlen(line);
+               char *p;
+
+               if (line[sz - 1] == '\n')
+                       line[sz - 1] = '\0';
+
+               p = mnt_resolve_path(line, cache);
+               printf("%s : %s\n", line, p);
+       }
+       mnt_free_cache(cache);
+       return 0;
+}
+
+int test_resolve_spec(struct mtest *ts, int argc, char *argv[])
+{
+       char line[BUFSIZ];
+       mnt_cache *cache;
+
+       cache = mnt_new_cache();
+       if (!cache)
+               return -1;
+
+       while(fgets(line, sizeof(line), stdin)) {
+               size_t sz = strlen(line);
+               char *p;
+
+               if (line[sz - 1] == '\n')
+                       line[sz - 1] = '\0';
+
+               p = mnt_resolve_spec(line, cache);
+               printf("%s : %s\n", line, p);
+       }
+       mnt_free_cache(cache);
+       return 0;
+}
+
+int test_read_tags(struct mtest *ts, int argc, char *argv[])
+{
+       char line[BUFSIZ];
+       mnt_cache *cache;
+
+       cache = mnt_new_cache();
+       if (!cache)
+               return -1;
+
+       while(fgets(line, sizeof(line), stdin)) {
+               size_t sz = strlen(line);
+
+               if (line[sz - 1] == '\n')
+                       line[sz - 1] = '\0';
+
+               if (*line == '/') {
+                       if (mnt_cache_read_tags(cache, line) < 0)
+                               fprintf(stderr, "%s: read tags faild\n", line);
+
+               } else if (strchr(line, '=')) {
+                       char *tag, *val;
+                       const char *cn = NULL;
+
+                       if (!blkid_parse_tag_string(line, &tag, &val)) {
+                               cn = mnt_cache_find_tag(cache, tag, val);
+
+                               free(tag);
+                               free(val);
+                       }
+                       if (cn)
+                               printf("%s: %s\n", line, cn);
+                       else
+                               printf("%s: not cached\n", line);
+               }
+       }
+       mnt_free_cache(cache);
+       return 0;
+
+}
+
+int main(int argc, char *argv[])
+{
+       struct mtest ts[] = {
+               { "--resolve-path", test_resolve_path, "  resolve paths from stdin" },
+               { "--resolve-spec", test_resolve_spec, "  evaluate specs from stdin" },
+               { "--read-tags", test_read_tags,       "  read devname or TAG stdin" },
+               { NULL }
+       };
+
+       return mnt_run_test(ts, argc, argv);
+}
+#endif
index 6990e5cebb45ec1b0b7baf1c0a5f23a7e0c309e7..e0f95807ec6c33f0dc50598cda3d951872f2a9bc 100644 (file)
@@ -27,6 +27,13 @@ extern "C" {
 
 #define LIBMOUNT_VERSION   "@LIBMOUNT_VERSION@"
 
+/**
+ * mnt_cache
+ *
+ * Stores canonicalized paths and evaluated tags
+ */
+typedef struct _mnt_cache mnt_cache;
+
 /* version.c */
 extern int mnt_parse_version_string(const char *ver_string);
 extern int mnt_get_library_version(const char **ver_string);
@@ -36,6 +43,19 @@ extern int mnt_fstype_is_netfs(const char *type);
 extern int mnt_fstype_is_pseudofs(const char *type);
 extern int mnt_open_device(const char *devname, int flags);
 
+/* cache.c */
+extern mnt_cache *mnt_new_cache(void);
+extern void mnt_free_cache(mnt_cache *cache);
+extern const char *mnt_cache_find_path(mnt_cache *cache, const char *path);
+extern const char *mnt_cache_find_tag(mnt_cache *cache,
+                        const char *token, const char *value);
+extern int mnt_cache_read_tags(mnt_cache *cache, const char *devname);
+extern int mnt_cache_device_has_tag(mnt_cache *cache, const char *devname,
+                                const char *token, const char *value);
+extern char *mnt_resolve_path(const char *path, mnt_cache *cache);
+extern char *mnt_resolve_tag(const char *token, const char *value, mnt_cache *cache);
+extern char *mnt_resolve_spec(const char *spec, mnt_cache *cache);
+
 #ifdef __cplusplus
 }
 #endif
index 5ac3b6bc01cdfc37c5614f7b0d4fe98e6fa2ff8c..21f3e67ed416be8de0acb07e857d4393c591cdfd 100644 (file)
 #endif
 
 #define DEBUG_INIT     (1 << 1)
+#define DEBUG_CACHE    (1 << 2)
 #define DEBUG_ALL      0xFFFF
 
 #ifdef CONFIG_LIBMOUNT_DEBUG
 #include <stdio.h>
 extern int libmount_debug_mask;
 extern void mnt_init_debug(int mask);
-#define DBG(m,x)       if ((m) & libmount_debug_mask) x;
+#define DBG(m,x)       if ((m) & libmount_debug_mask) x
 #else
 #define DBG(m,x)
 #define mnt_init_debug(x)