diff options
Diffstat (limited to 'libblkid/src/evaluate.c')
-rw-r--r-- | libblkid/src/evaluate.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/libblkid/src/evaluate.c b/libblkid/src/evaluate.c new file mode 100644 index 0000000..51e5b05 --- /dev/null +++ b/libblkid/src/evaluate.c @@ -0,0 +1,323 @@ +/* + * evaluate.c - very high-level API to evaluate LABELs or UUIDs + * + * Copyright (C) 2009 Karel Zak <kzak@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/types.h> +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdint.h> +#include <stdarg.h> + +#include "pathnames.h" +#include "canonicalize.h" +#include "closestream.h" + +#include "blkidP.h" + +/** + * SECTION:evaluate + * @title: Tags and Spec evaluation + * @short_description: top-level API for LABEL and UUID evaluation. + * + * This API provides very simple and portable way how evaluate LABEL and UUID + * tags. The blkid_evaluate_tag() and blkid_evaluate_spec() work on 2.4 and + * 2.6 systems and on systems with or without udev. Currently, the libblkid + * library supports "udev" and "scan" methods. The "udev" method uses udev + * /dev/disk/by-* symlinks and the "scan" method scans all block devices from + * the /proc/partitions file. The evaluation could be controlled by the + * /etc/blkid.conf config file. The default is to try "udev" and then "scan" + * method. + * + * The blkid_evaluate_tag() also automatically informs udevd when an obsolete + * /dev/disk/by-* symlink is detected. + * + * If you are not sure how translate LABEL or UUID to the device name use this + * API. + */ + +#ifdef CONFIG_BLKID_VERIFY_UDEV +/* returns zero when the device has NAME=value (LABEL/UUID) */ +static int verify_tag(const char *devname, const char *name, const char *value) +{ + blkid_probe pr; + int fd = -1, rc = -1; + size_t len; + const char *data; + int errsv = 0; + + pr = blkid_new_probe(); + if (!pr) + return -1; + + blkid_probe_enable_superblocks(pr, TRUE); + blkid_probe_set_superblocks_flags(pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID); + + blkid_probe_enable_partitions(pr, TRUE); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); + + fd = open(devname, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + errsv = errno; + goto done; + } + if (blkid_probe_set_device(pr, fd, 0, 0)) + goto done; + rc = blkid_do_safeprobe(pr); + if (rc) + goto done; + rc = blkid_probe_lookup_value(pr, name, &data, &len); + if (!rc) + rc = memcmp(value, data, len); +done: + DBG(EVALUATE, ul_debug("%s: %s verification %s", + devname, name, rc == 0 ? "PASS" : "FAILED")); + if (fd >= 0) + close(fd); + blkid_free_probe(pr); + + /* for non-root users we use unverified udev links */ + return errsv == EACCES ? 0 : rc; +} +#endif /* CONFIG_BLKID_VERIFY_UDEV*/ + +/** + * blkid_send_uevent: + * @devname: absolute path to the device + * @action: event string + * + * Returns: -1 in case of failure, or 0 on success. + */ +int blkid_send_uevent(const char *devname, const char *action) +{ + char uevent[PATH_MAX]; + struct stat st; + FILE *f; + int rc = -1; + + DBG(EVALUATE, ul_debug("%s: uevent '%s' requested", devname, action)); + + if (!devname || !action) + return -1; + if (stat(devname, &st) || !S_ISBLK(st.st_mode)) + return -1; + + snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent", + major(st.st_rdev), minor(st.st_rdev)); + + f = fopen(uevent, "w" UL_CLOEXECSTR); + if (f) { + rc = 0; + if (fputs(action, f) >= 0) + rc = 0; + if (close_stream(f) != 0) + DBG(EVALUATE, ul_debug("write failed: %s", uevent)); + } + DBG(EVALUATE, ul_debug("%s: send uevent %s", + uevent, rc == 0 ? "SUCCESS" : "FAILED")); + return rc; +} + +static char *evaluate_by_udev(const char *token, const char *value, int uevent) +{ + char dev[PATH_MAX]; + char *path = NULL; + size_t len; + struct stat st; + + DBG(EVALUATE, ul_debug("evaluating by udev %s=%s", token, value)); + + if (!strcmp(token, "UUID")) + strcpy(dev, _PATH_DEV_BYUUID "/"); + else if (!strcmp(token, "LABEL")) + strcpy(dev, _PATH_DEV_BYLABEL "/"); + else if (!strcmp(token, "PARTLABEL")) + strcpy(dev, _PATH_DEV_BYPARTLABEL "/"); + else if (!strcmp(token, "PARTUUID")) + strcpy(dev, _PATH_DEV_BYPARTUUID "/"); + else { + DBG(EVALUATE, ul_debug("unsupported token %s", token)); + return NULL; /* unsupported tag */ + } + + len = strlen(dev); + if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0) + return NULL; + + DBG(EVALUATE, ul_debug("expected udev link: %s", dev)); + + if (stat(dev, &st)) + goto failed; /* link or device does not exist */ + + if (!S_ISBLK(st.st_mode)) + return NULL; + + path = canonicalize_path(dev); + if (!path) + return NULL; + +#ifdef CONFIG_BLKID_VERIFY_UDEV + if (verify_tag(path, token, value)) + goto failed; +#endif + return path; + +failed: + DBG(EVALUATE, ul_debug("failed to evaluate by udev")); + + if (uevent && path) + blkid_send_uevent(path, "change"); + free(path); + return NULL; +} + +static char *evaluate_by_scan(const char *token, const char *value, + blkid_cache *cache, struct blkid_config *conf) +{ + blkid_cache c = cache ? *cache : NULL; + char *res; + + DBG(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", token, value)); + + if (!c) { + char *cachefile = blkid_get_cache_filename(conf); + blkid_get_cache(&c, cachefile); + free(cachefile); + } + if (!c) + return NULL; + + res = blkid_get_devname(c, token, value); + + if (cache) + *cache = c; + else + blkid_put_cache(c); + + return res; +} + +/** + * blkid_evaluate_tag: + * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo") + * @value: token data (e.g. "foo") + * @cache: pointer to cache (or NULL when you don't want to re-use the cache) + * + * Returns: allocated string with a device name. + */ +char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache) +{ + struct blkid_config *conf = NULL; + char *t = NULL, *v = NULL; + char *ret = NULL; + int i; + + if (!token) + return NULL; + + if (!cache || !*cache) + blkid_init_debug(0); + + DBG(EVALUATE, ul_debug("evaluating %s%s%s", token, value ? "=" : "", + value ? value : "")); + + if (!value) { + if (!strchr(token, '=')) { + ret = strdup(token); + goto out; + } + if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v) + goto out; + token = t; + value = v; + } + + conf = blkid_read_config(NULL); + if (!conf) + goto out; + + for (i = 0; i < conf->nevals; i++) { + if (conf->eval[i] == BLKID_EVAL_UDEV) + ret = evaluate_by_udev(token, value, conf->uevent); + else if (conf->eval[i] == BLKID_EVAL_SCAN) + ret = evaluate_by_scan(token, value, cache, conf); + if (ret) + break; + } + + DBG(EVALUATE, ul_debug("%s=%s evaluated as %s", token, value, ret)); +out: + blkid_free_config(conf); + free(t); + free(v); + return ret; +} + +/** + * blkid_evaluate_spec: + * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0) + * @cache: pointer to cache (or NULL when you don't want to re-use the cache) + * + * All returned paths are canonicalized, device-mapper paths are converted + * to the /dev/mapper/name format. + * + * Returns: allocated string with a device name. + */ +char *blkid_evaluate_spec(const char *spec, blkid_cache *cache) +{ + char *t = NULL, *v = NULL, *res; + + if (!spec) + return NULL; + + if (strchr(spec, '=') && + blkid_parse_tag_string(spec, &t, &v) != 0) /* parse error */ + return NULL; + + if (v) + res = blkid_evaluate_tag(t, v, cache); + else + res = canonicalize_path(spec); + + free(t); + free(v); + return res; +} + + +#ifdef TEST_PROGRAM +int main(int argc, char *argv[]) +{ + blkid_cache cache = NULL; + char *res; + + if (argc < 2) { + fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]); + return EXIT_FAILURE; + } + + blkid_init_debug(0); + + res = blkid_evaluate_spec(argv[1], &cache); + if (res) + printf("%s\n", res); + if (cache) + blkid_put_cache(cache); + + return res ? EXIT_SUCCESS : EXIT_FAILURE; +} +#endif |