From 7755ca95c943e8690a351c0580c2ad06950c057b Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 27 Nov 2009 14:35:03 +0100 Subject: [PATCH] libblkid: add paths and tags cache Signed-off-by: Karel Zak --- shlibs/mount/src/Makefile.am | 5 +- shlibs/mount/src/cache.c | 533 +++++++++++++++++++++++++++++++++++ shlibs/mount/src/mount.h.in | 20 ++ shlibs/mount/src/mountP.h | 3 +- 4 files changed, 558 insertions(+), 3 deletions(-) create mode 100644 shlibs/mount/src/cache.c diff --git a/shlibs/mount/src/Makefile.am b/shlibs/mount/src/Makefile.am index f9cc68f9..59829631 100644 --- a/shlibs/mount/src/Makefile.am +++ b/shlibs/mount/src/Makefile.am @@ -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 index 00000000..7b9040ff --- /dev/null +++ b/shlibs/mount/src/cache.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2009 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/shlibs/mount/src/mount.h.in b/shlibs/mount/src/mount.h.in index 6990e5ce..e0f95807 100644 --- a/shlibs/mount/src/mount.h.in +++ b/shlibs/mount/src/mount.h.in @@ -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 diff --git a/shlibs/mount/src/mountP.h b/shlibs/mount/src/mountP.h index 5ac3b6bc..21f3e67e 100644 --- a/shlibs/mount/src/mountP.h +++ b/shlibs/mount/src/mountP.h @@ -30,13 +30,14 @@ #endif #define DEBUG_INIT (1 << 1) +#define DEBUG_CACHE (1 << 2) #define DEBUG_ALL 0xFFFF #ifdef CONFIG_LIBMOUNT_DEBUG #include 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) -- 2.39.5