diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:49:25 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:49:25 +0000 |
commit | 464df1d5e5ab1322e2dd0a7796939fff1aeefa9a (patch) | |
tree | 6a403684e0978f0287d7f0ec0e5aab1fd31a59e1 /lib/blkid/tag.c | |
parent | Initial commit. (diff) | |
download | e2fsprogs-upstream.tar.xz e2fsprogs-upstream.zip |
Adding upstream version 1.47.0.upstream/1.47.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/blkid/tag.c')
-rw-r--r-- | lib/blkid/tag.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/lib/blkid/tag.c b/lib/blkid/tag.c new file mode 100644 index 0000000..e88ebc4 --- /dev/null +++ b/lib/blkid/tag.c @@ -0,0 +1,471 @@ +/* + * tag.c - allocation/initialization/free routines for tag structs + * + * Copyright (C) 2001 Andreas Dilger + * Copyright (C) 2003 Theodore Ts'o + * + * %Begin-Header% + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * %End-Header% + */ + +#include "config.h" +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "blkidP.h" + +static blkid_tag blkid_new_tag(void) +{ + blkid_tag tag; + + if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag)))) + return NULL; + + INIT_LIST_HEAD(&tag->bit_tags); + INIT_LIST_HEAD(&tag->bit_names); + + return tag; +} + +#ifdef CONFIG_BLKID_DEBUG +void blkid_debug_dump_tag(blkid_tag tag) +{ + if (!tag) { + printf(" tag: NULL\n"); + return; + } + + printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); +} +#endif + +void blkid_free_tag(blkid_tag tag) +{ + if (!tag) + return; + + DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name, + tag->bit_val ? tag->bit_val : "(NULL)")); + DBG(DEBUG_TAG, blkid_debug_dump_tag(tag)); + + list_del(&tag->bit_tags); /* list of tags for this device */ + list_del(&tag->bit_names); /* list of tags with this type */ + + free(tag->bit_name); + free(tag->bit_val); + + free(tag); +} + +/* + * Find the desired tag on a device. If value is NULL, then the + * first such tag is returned, otherwise return only exact tag if found. + */ +blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) +{ + struct list_head *p; + + if (!dev || !type) + return NULL; + + list_for_each(p, &dev->bid_tags) { + blkid_tag tmp = list_entry(p, struct blkid_struct_tag, + bit_tags); + + if (!strcmp(tmp->bit_name, type)) + return tmp; + } + return NULL; +} + +extern int blkid_dev_has_tag(blkid_dev dev, const char *type, + const char *value) +{ + blkid_tag tag; + + if (!dev || !type) + return -1; + + tag = blkid_find_tag_dev(dev, type); + if (!value) + return (tag != NULL); + if (!tag || strcmp(tag->bit_val, value)) + return 0; + return 1; +} + +/* + * Find the desired tag type in the cache. + * We return the head tag for this tag type. + */ +static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) +{ + blkid_tag head = NULL, tmp; + struct list_head *p; + + if (!cache || !type) + return NULL; + + list_for_each(p, &cache->bic_tags) { + tmp = list_entry(p, struct blkid_struct_tag, bit_tags); + if (!strcmp(tmp->bit_name, type)) { + DBG(DEBUG_TAG, + printf(" found cache tag head %s\n", type)); + head = tmp; + break; + } + } + return head; +} + +/* + * Set a tag on an existing device. + * + * If value is NULL, then delete the tagsfrom the device. + */ +int blkid_set_tag(blkid_dev dev, const char *name, + const char *value, const int vlength) +{ + blkid_tag t = 0, head = 0; + char *val = 0; + char **dev_var = 0; + + if (!dev || !name) + return -BLKID_ERR_PARAM; + + if (!(val = blkid_strndup(value, vlength)) && value) + return -BLKID_ERR_MEM; + + /* + * Certain common tags are linked directly to the device struct + * We need to know what they are before we do anything else because + * the function name parameter might get freed later on. + */ + if (!strcmp(name, "TYPE")) + dev_var = &dev->bid_type; + else if (!strcmp(name, "LABEL")) + dev_var = &dev->bid_label; + else if (!strcmp(name, "UUID")) + dev_var = &dev->bid_uuid; + + t = blkid_find_tag_dev(dev, name); + if (!value) { + if (t) + blkid_free_tag(t); + } else if (t) { + if (!strcmp(t->bit_val, val)) { + /* Same thing, exit */ + free(val); + return 0; + } + free(t->bit_val); + t->bit_val = val; + } else { + /* Existing tag not present, add to device */ + if (!(t = blkid_new_tag())) + goto errout; + t->bit_name = blkid_strdup(name); + t->bit_val = val; + t->bit_dev = dev; + + list_add_tail(&t->bit_tags, &dev->bid_tags); + + if (dev->bid_cache) { + head = blkid_find_head_cache(dev->bid_cache, + t->bit_name); + if (!head) { + head = blkid_new_tag(); + if (!head) + goto errout; + + DBG(DEBUG_TAG, + printf(" creating new cache tag head %s\n", name)); + head->bit_name = blkid_strdup(name); + if (!head->bit_name) + goto errout; + list_add_tail(&head->bit_tags, + &dev->bid_cache->bic_tags); + } + list_add_tail(&t->bit_names, &head->bit_names); + } + } + + /* Link common tags directly to the device struct */ + if (dev_var) + *dev_var = val; + + if (dev->bid_cache) + dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; + return 0; + +errout: + if (t) + blkid_free_tag(t); + else free(val); + if (head) + blkid_free_tag(head); + return -BLKID_ERR_MEM; +} + + +/* + * Parse a "NAME=value" string. This is slightly different than + * parse_token, because that will end an unquoted value at a space, while + * this will assume that an unquoted value is the rest of the token (e.g. + * if we are passed an already quoted string from the command-line we don't + * have to both quote and escape quote so that the quotes make it to + * us). + * + * Returns 0 on success, and -1 on failure. + */ +int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) +{ + char *name, *value, *cp; + + DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token)); + + if (!token || !(cp = strchr(token, '='))) + return -1; + + name = blkid_strdup(token); + if (!name) + return -1; + value = name + (cp - token); + *value++ = '\0'; + if (*value == '"' || *value == '\'') { + char c = *value++; + if (!(cp = strrchr(value, c))) + goto errout; /* missing closing quote */ + *cp = '\0'; + } + value = blkid_strdup(value); + if (!value) + goto errout; + + *ret_type = name; + *ret_val = value; + + return 0; + +errout: + free(name); + return -1; +} + +/* + * Tag iteration routines for the public libblkid interface. + * + * These routines do not expose the list.h implementation, which are a + * contamination of the namespace, and which force us to reveal far, far + * too much of our internal implementation. I'm not convinced I want + * to keep list.h in the long term, anyway. It's fine for kernel + * programming, but performance is not the #1 priority for this + * library, and I really don't like the tradeoff of type-safety for + * performance for this application. [tytso:20030125.2007EST] + */ + +/* + * This series of functions iterate over all tags in a device + */ +#define TAG_ITERATE_MAGIC 0x01a5284c + +struct blkid_struct_tag_iterate { + int magic; + blkid_dev dev; + struct list_head *p; +}; + +extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) +{ + blkid_tag_iterate iter; + + iter = malloc(sizeof(struct blkid_struct_tag_iterate)); + if (iter) { + iter->magic = TAG_ITERATE_MAGIC; + iter->dev = dev; + iter->p = dev->bid_tags.next; + } + return (iter); +} + +/* + * Return 0 on success, -1 on error + */ +extern int blkid_tag_next(blkid_tag_iterate iter, + const char **type, const char **value) +{ + blkid_tag tag; + + *type = 0; + *value = 0; + if (!iter || iter->magic != TAG_ITERATE_MAGIC || + iter->p == &iter->dev->bid_tags) + return -1; + tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); + *type = tag->bit_name; + *value = tag->bit_val; + iter->p = iter->p->next; + return 0; +} + +extern void blkid_tag_iterate_end(blkid_tag_iterate iter) +{ + if (!iter || iter->magic != TAG_ITERATE_MAGIC) + return; + iter->magic = 0; + free(iter); +} + +/* + * This function returns a device which matches a particular + * type/value pair. If there is more than one device that matches the + * search specification, it returns the one with the highest priority + * value. This allows us to give preference to EVMS or LVM devices. + */ +extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, + const char *type, + const char *value) +{ + blkid_tag head; + blkid_dev dev; + int pri; + struct list_head *p; + int probe_new = 0; + + if (!cache || !type || !value) + return NULL; + + blkid_read_cache(cache); + + DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value)); + +try_again: + pri = -1; + dev = 0; + head = blkid_find_head_cache(cache, type); + + if (head) { + list_for_each(p, &head->bit_names) { + blkid_tag tmp = list_entry(p, struct blkid_struct_tag, + bit_names); + + if (!strcmp(tmp->bit_val, value) && + (tmp->bit_dev->bid_pri > pri) && + !access(tmp->bit_dev->bid_name, F_OK)) { + dev = tmp->bit_dev; + pri = dev->bid_pri; + } + } + } + if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { + dev = blkid_verify(cache, dev); + if (!dev || (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))) + goto try_again; + } + + if (!dev && !probe_new) { + if (blkid_probe_all_new(cache) < 0) + return NULL; + probe_new++; + goto try_again; + } + + if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { + if (blkid_probe_all(cache) < 0) + return NULL; + goto try_again; + } + return dev; +} + +#ifdef TEST_PROGRAM +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern char *optarg; +extern int optind; +#endif + +void usage(char *prog) +{ + fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device " + "[type value]\n", + prog); + fprintf(stderr, "\tList all tags for a device and exit\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + blkid_tag_iterate iter; + blkid_cache cache = NULL; + blkid_dev dev; + int c, ret, found; + int flags = BLKID_DEV_FIND; + char *tmp; + char *file = NULL; + char *devname = NULL; + char *search_type = NULL; + char *search_value = NULL; + const char *type, *value; + + while ((c = getopt (argc, argv, "m:f:")) != EOF) + switch (c) { + case 'f': + file = optarg; + break; + case 'm': + blkid_debug_mask = strtoul (optarg, &tmp, 0); + if (*tmp) { + fprintf(stderr, "Invalid debug mask: %s\n", + optarg); + exit(1); + } + break; + case '?': + usage(argv[0]); + } + if (argc > optind) + devname = argv[optind++]; + if (argc > optind) + search_type = argv[optind++]; + if (argc > optind) + search_value = argv[optind++]; + if (!devname || (argc != optind)) + usage(argv[0]); + + if ((ret = blkid_get_cache(&cache, file)) != 0) { + fprintf(stderr, "%s: error creating cache (%d)\n", + argv[0], ret); + exit(1); + } + + dev = blkid_get_dev(cache, devname, flags); + if (!dev) { + fprintf(stderr, "%s: Can not find device in blkid cache\n", + devname); + exit(1); + } + if (search_type) { + found = blkid_dev_has_tag(dev, search_type, search_value); + printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev), + search_type, search_value ? search_value : "NULL", + found ? "FOUND" : "NOT FOUND"); + return(!found); + } + printf("Device %s...\n", blkid_dev_devname(dev)); + + iter = blkid_tag_iterate_begin(dev); + while (blkid_tag_next(iter, &type, &value) == 0) { + printf("\tTag %s has value %s\n", type, value); + } + blkid_tag_iterate_end(iter); + + blkid_put_cache(cache); + return (0); +} +#endif |