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