| /* |
| * 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/sysmacros.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 ? "SUCCES" : "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; |
| } |
| blkid_parse_tag_string(token, &t, &v); |
| if (!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 |