(root)/
util-linux-2.39/
libblkid/
src/
evaluate.c
       1  /*
       2   * evaluate.c - very high-level API to evaluate LABELs or UUIDs
       3   *
       4   * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
       5   *
       6   * This file may be redistributed under the terms of the
       7   * GNU Lesser General Public License.
       8   */
       9  #include <stdio.h>
      10  #include <string.h>
      11  #include <stdlib.h>
      12  #include <unistd.h>
      13  #include <fcntl.h>
      14  #include <ctype.h>
      15  #include <sys/types.h>
      16  #ifdef HAVE_SYS_STAT_H
      17  #include <sys/stat.h>
      18  #endif
      19  #ifdef HAVE_ERRNO_H
      20  #include <errno.h>
      21  #endif
      22  #include <stdint.h>
      23  #include <stdarg.h>
      24  
      25  #include "pathnames.h"
      26  #include "canonicalize.h"
      27  #include "closestream.h"
      28  
      29  #include "blkidP.h"
      30  
      31  /**
      32   * SECTION:evaluate
      33   * @title: Tags and Spec evaluation
      34   * @short_description: top-level API for LABEL and UUID evaluation.
      35   *
      36   * This API provides very simple and portable way how evaluate LABEL and UUID
      37   * tags.  The blkid_evaluate_tag() and blkid_evaluate_spec() work on 2.4 and
      38   * 2.6 systems and on systems with or without udev. Currently, the libblkid
      39   * library supports "udev" and "scan" methods. The "udev" method uses udev
      40   * /dev/disk/by-* symlinks and the "scan" method scans all block devices from
      41   * the /proc/partitions file. The evaluation could be controlled by the
      42   * /etc/blkid.conf config file. The default is to try "udev" and then "scan"
      43   * method.
      44   *
      45   * The blkid_evaluate_tag() also automatically informs udevd when an obsolete
      46   * /dev/disk/by-* symlink is detected.
      47   *
      48   * If you are not sure how translate LABEL or UUID to the device name use this
      49   * API.
      50   */
      51  
      52  #ifdef CONFIG_BLKID_VERIFY_UDEV
      53  /* returns zero when the device has NAME=value (LABEL/UUID) */
      54  static int verify_tag(const char *devname, const char *name, const char *value)
      55  {
      56  	blkid_probe pr;
      57  	int fd = -1, rc = -1;
      58  	size_t len;
      59  	const char *data;
      60  	int errsv = 0;
      61  
      62  	if (strcmp(token, "ID") == 0)
      63  		return 0; /* non-content tag */
      64  
      65  	pr = blkid_new_probe();
      66  	if (!pr)
      67  		return -1;
      68  
      69  	blkid_probe_enable_superblocks(pr, TRUE);
      70  	blkid_probe_set_superblocks_flags(pr,
      71  			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
      72  
      73  	blkid_probe_enable_partitions(pr, TRUE);
      74  	blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
      75  
      76  	fd = open(devname, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
      77  	if (fd < 0) {
      78  		errsv = errno;
      79  		goto done;
      80  	}
      81  	if (blkid_probe_set_device(pr, fd, 0, 0))
      82  		goto done;
      83  	rc = blkid_do_safeprobe(pr);
      84  	if (rc)
      85  		goto done;
      86  	rc = blkid_probe_lookup_value(pr, name, &data, &len);
      87  	if (!rc)
      88  		rc = memcmp(value, data, len);
      89  done:
      90  	DBG(EVALUATE, ul_debug("%s: %s verification %s",
      91  			devname, name, rc == 0 ? "PASS" : "FAILED"));
      92  	if (fd >= 0)
      93  		close(fd);
      94  	blkid_free_probe(pr);
      95  
      96  	/* for non-root users we use unverified udev links */
      97  	return errsv == EACCES ? 0 : rc;
      98  }
      99  #endif /* CONFIG_BLKID_VERIFY_UDEV*/
     100  
     101  /**
     102   * blkid_send_uevent:
     103   * @devname: absolute path to the device
     104   * @action: event string
     105   *
     106   * Returns: -1 in case of failure, or 0 on success.
     107   */
     108  int blkid_send_uevent(const char *devname, const char *action)
     109  {
     110  	char uevent[PATH_MAX];
     111  	struct stat st;
     112  	FILE *f;
     113  	int rc = -1;
     114  
     115  	DBG(EVALUATE, ul_debug("%s: uevent '%s' requested", devname, action));
     116  
     117  	if (!devname || !action)
     118  		return -1;
     119  	if (stat(devname, &st) || !S_ISBLK(st.st_mode))
     120  		return -1;
     121  
     122  	snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent",
     123  			major(st.st_rdev), minor(st.st_rdev));
     124  
     125  	f = fopen(uevent, "w" UL_CLOEXECSTR);
     126  	if (f) {
     127  		rc = 0;
     128  		if (fputs(action, f) >= 0)
     129  			rc = 0;
     130  		if (close_stream(f) != 0)
     131  			DBG(EVALUATE, ul_debug("write failed: %s", uevent));
     132  	}
     133  	DBG(EVALUATE, ul_debug("%s: send uevent %s",
     134  			uevent, rc == 0 ? "SUCCESS" : "FAILED"));
     135  	return rc;
     136  }
     137  
     138  static char *evaluate_by_udev(const char *token, const char *value, int uevent)
     139  {
     140  	char dev[PATH_MAX];
     141  	char *path = NULL;
     142  	size_t len;
     143  	struct stat st;
     144  
     145  	DBG(EVALUATE, ul_debug("evaluating by udev %s=%s", token, value));
     146  
     147  	if (!strcmp(token, "UUID"))
     148  		strcpy(dev, _PATH_DEV_BYUUID "/");
     149  	else if (!strcmp(token, "LABEL"))
     150  		strcpy(dev, _PATH_DEV_BYLABEL "/");
     151  	else if (!strcmp(token, "PARTLABEL"))
     152  		strcpy(dev, _PATH_DEV_BYPARTLABEL "/");
     153  	else if (!strcmp(token, "PARTUUID"))
     154  		strcpy(dev, _PATH_DEV_BYPARTUUID "/");
     155  	else if (!strcmp(token, "ID"))
     156  		strcpy(dev, _PATH_DEV_BYID "/");
     157  	else {
     158  		DBG(EVALUATE, ul_debug("unsupported token %s", token));
     159  		return NULL;	/* unsupported tag */
     160  	}
     161  
     162  	len = strlen(dev);
     163  	if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0)
     164  		return NULL;
     165  
     166  	DBG(EVALUATE, ul_debug("expected udev link: %s", dev));
     167  
     168  	if (stat(dev, &st))
     169  		goto failed;	/* link or device does not exist */
     170  
     171  	if (!S_ISBLK(st.st_mode))
     172  		return NULL;
     173  
     174  	path = canonicalize_path(dev);
     175  	if (!path)
     176  		return NULL;
     177  
     178  #ifdef CONFIG_BLKID_VERIFY_UDEV
     179  	if (verify_tag(path, token, value))
     180  		goto failed;
     181  #endif
     182  	return path;
     183  
     184  failed:
     185  	DBG(EVALUATE, ul_debug("failed to evaluate by udev"));
     186  
     187  	if (uevent && path)
     188  		blkid_send_uevent(path, "change");
     189  	free(path);
     190  	return NULL;
     191  }
     192  
     193  static char *evaluate_by_scan(const char *token, const char *value,
     194  		blkid_cache *cache, struct blkid_config *conf)
     195  {
     196  	blkid_cache c = cache ? *cache : NULL;
     197  	char *res;
     198  
     199  	DBG(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", token, value));
     200  
     201  	if (!c) {
     202  		char *cachefile = blkid_get_cache_filename(conf);
     203  		int rc = blkid_get_cache(&c, cachefile);
     204  		free(cachefile);
     205  		if (rc < 0)
     206  			return NULL;
     207  	}
     208  	if (!c)
     209  		return NULL;
     210  
     211  	res = blkid_get_devname(c, token, value);
     212  
     213  	if (cache)
     214  		*cache = c;
     215  	else
     216  		blkid_put_cache(c);
     217  
     218  	return res;
     219  }
     220  
     221  /**
     222   * blkid_evaluate_tag:
     223   * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo")
     224   * @value: token data (e.g. "foo")
     225   * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
     226   *
     227   * Returns: allocated string with a device name.
     228   */
     229  char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache)
     230  {
     231  	struct blkid_config *conf = NULL;
     232  	char *t = NULL, *v = NULL;
     233  	char *ret = NULL;
     234  	int i;
     235  
     236  	if (!token)
     237  		return NULL;
     238  
     239  	DBG(EVALUATE, ul_debug("evaluating  %s%s%s", token, value ? "=" : "",
     240  		   value ? value : ""));
     241  
     242  	if (!value) {
     243  		if (!strchr(token, '=')) {
     244  			ret = strdup(token);
     245  			goto out;
     246  		}
     247  		if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v)
     248  			goto out;
     249  		token = t;
     250  		value = v;
     251  	}
     252  
     253  	conf = blkid_read_config(NULL);
     254  	if (!conf)
     255  		goto out;
     256  
     257  	for (i = 0; i < conf->nevals; i++) {
     258  		if (conf->eval[i] == BLKID_EVAL_UDEV)
     259  			ret = evaluate_by_udev(token, value, conf->uevent);
     260  		else if (conf->eval[i] == BLKID_EVAL_SCAN)
     261  			ret = evaluate_by_scan(token, value, cache, conf);
     262  		if (ret)
     263  			break;
     264  	}
     265  
     266  	DBG(EVALUATE, ul_debug("%s=%s evaluated as %s", token, value, ret));
     267  out:
     268  	blkid_free_config(conf);
     269  	free(t);
     270  	free(v);
     271  	return ret;
     272  }
     273  
     274  /**
     275   * blkid_evaluate_spec:
     276   * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0)
     277   * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
     278   *
     279   * All returned paths are canonicalized, device-mapper paths are converted
     280   * to the /dev/mapper/name format.
     281   *
     282   * Returns: allocated string with a device name.
     283   */
     284  char *blkid_evaluate_spec(const char *spec, blkid_cache *cache)
     285  {
     286  	char *t = NULL, *v = NULL, *res;
     287  
     288  	if (!spec)
     289  		return NULL;
     290  
     291  	if (strchr(spec, '=') &&
     292  	    blkid_parse_tag_string(spec, &t, &v) != 0)	/* parse error */
     293  		return NULL;
     294  
     295  	if (v)
     296  		res = blkid_evaluate_tag(t, v, cache);
     297  	else
     298  		res = canonicalize_path(spec);
     299  
     300  	free(t);
     301  	free(v);
     302  	return res;
     303  }
     304  
     305  
     306  #ifdef TEST_PROGRAM
     307  int main(int argc, char *argv[])
     308  {
     309  	blkid_cache cache = NULL;
     310  	char *res;
     311  
     312  	if (argc < 2) {
     313  		fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]);
     314  		return EXIT_FAILURE;
     315  	}
     316  
     317  	res = blkid_evaluate_spec(argv[1], &cache);
     318  	if (res)
     319  		printf("%s\n", res);
     320  	if (cache)
     321  		blkid_put_cache(cache);
     322  
     323  	return res ? EXIT_SUCCESS : EXIT_FAILURE;
     324  }
     325  #endif