blob: 2e1ca574daa42851e6db461a3d230dcd57d76b1d [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>
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
28#include "blkidP.h"
29
30/**
31 * SECTION:evaluate
32 * @title: Tags and Spec evaluation
33 * @short_description: top-level API for LABEL and UUID evaluation.
34 *
35 * This API provides very simple and portable way how evaluate LABEL and UUID
36 * tags. The blkid_evaluate_tag() and blkid_evaluate_spec() work on 2.4 and
37 * 2.6 systems and on systems with or without udev. Currently, the libblkid
38 * library supports "udev" and "scan" methods. The "udev" method uses udev
39 * /dev/disk/by-* symlinks and the "scan" method scans all block devices from
40 * the /proc/partitions file. The evaluation could be controlled by the
41 * /etc/blkid.conf config file. The default is to try "udev" and then "scan"
42 * method.
43 *
44 * The blkid_evaluate_tag() also automatically informs udevd when an obsolete
45 * /dev/disk/by-* symlink is detected.
46 *
47 * If you are not sure how translate LABEL or UUID to the device name use this
48 * API.
49 */
50
51#ifdef CONFIG_BLKID_VERIFY_UDEV
52/* returns zero when the device has NAME=value (LABEL/UUID) */
53static int verify_tag(const char *devname, const char *name, const char *value)
54{
55 blkid_probe pr;
56 int fd = -1, rc = -1;
57 size_t len;
58 const char *data;
59 int errsv = 0;
60
61 pr = blkid_new_probe();
62 if (!pr)
63 return -1;
64
65 blkid_probe_enable_superblocks(pr, TRUE);
66 blkid_probe_set_superblocks_flags(pr,
67 BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
68
69 blkid_probe_enable_partitions(pr, TRUE);
70 blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
71
72 fd = open(devname, O_RDONLY|O_CLOEXEC);
73 if (fd < 0) {
74 errsv = errno;
75 goto done;
76 }
77 if (blkid_probe_set_device(pr, fd, 0, 0))
78 goto done;
79 rc = blkid_do_safeprobe(pr);
80 if (rc)
81 goto done;
82 rc = blkid_probe_lookup_value(pr, name, &data, &len);
83 if (!rc)
84 rc = memcmp(value, data, len);
85done:
86 DBG(DEBUG_EVALUATE, printf("%s: %s verification %s\n",
87 devname, name, rc == 0 ? "PASS" : "FAILED"));
88 if (fd >= 0)
89 close(fd);
90 blkid_free_probe(pr);
91
92 /* for non-root users we use unverified udev links */
93 return errsv == EACCES ? 0 : rc;
94}
95#endif /* CONFIG_BLKID_VERIFY_UDEV*/
96
97/**
98 * blkid_send_uevent:
99 * @devname: absolute path to the device
100 * @action: event string
101 *
102 * Returns: -1 in case of failure, or 0 on success.
103 */
104int blkid_send_uevent(const char *devname, const char *action)
105{
106 char uevent[PATH_MAX];
107 struct stat st;
108 FILE *f;
109 int rc = -1;
110
111 DBG(DEBUG_EVALUATE, printf("%s: uevent '%s' requested\n", devname, action));
112
113 if (!devname || !action)
114 return -1;
115 if (stat(devname, &st) || !S_ISBLK(st.st_mode))
116 return -1;
117
118 snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent",
119 major(st.st_rdev), minor(st.st_rdev));
120
121 f = fopen(uevent, "w");
122 if (f) {
123 rc = 0;
124 if (fputs(action, f) >= 0)
125 rc = 0;
126 fclose(f);
127 }
128 DBG(DEBUG_EVALUATE, printf("%s: send uevent %s\n",
129 uevent, rc == 0 ? "SUCCES" : "FAILED"));
130 return rc;
131}
132
133static char *evaluate_by_udev(const char *token, const char *value, int uevent)
134{
135 char dev[PATH_MAX];
136 char *path = NULL;
137 size_t len;
138 struct stat st;
139
140 DBG(DEBUG_EVALUATE,
141 printf("evaluating by udev %s=%s\n", token, value));
142
143 if (!strcmp(token, "UUID"))
144 strcpy(dev, _PATH_DEV_BYUUID "/");
145 else if (!strcmp(token, "LABEL"))
146 strcpy(dev, _PATH_DEV_BYLABEL "/");
147 else if (!strcmp(token, "PARTLABEL"))
148 strcpy(dev, _PATH_DEV_BYPARTLABEL "/");
149 else if (!strcmp(token, "PARTUUID"))
150 strcpy(dev, _PATH_DEV_BYPARTUUID "/");
151 else {
152 DBG(DEBUG_EVALUATE,
153 printf("unsupported token %s\n", token));
154 return NULL; /* unsupported tag */
155 }
156
157 len = strlen(dev);
158 if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0)
159 return NULL;
160
161 DBG(DEBUG_EVALUATE,
162 printf("expected udev link: %s\n", dev));
163
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:
181 DBG(DEBUG_EVALUATE, printf("failed to evaluate by udev\n"));
182
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
195 DBG(DEBUG_EVALUATE,
196 printf("evaluating by blkid scan %s=%s\n", token, value));
197
198 if (!c) {
199 char *cachefile = blkid_get_cache_filename(conf);
200 blkid_get_cache(&c, cachefile);
201 free(cachefile);
202 }
203 if (!c)
204 return NULL;
205
206 res = blkid_get_devname(c, token, value);
207
208 if (cache)
209 *cache = c;
210 else
211 blkid_put_cache(c);
212
213 return res;
214}
215
216/**
217 * blkid_evaluate_tag:
218 * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo")
219 * @value: token data (e.g. "foo")
220 * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
221 *
222 * Returns: allocated string with a device name.
223 */
224char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache)
225{
226 struct blkid_config *conf = NULL;
227 char *t = NULL, *v = NULL;
228 char *ret = NULL;
229 int i;
230
231 if (!token)
232 return NULL;
233
234 if (!cache || !*cache)
235 blkid_init_debug(0);
236
237 DBG(DEBUG_EVALUATE,
238 printf("evaluating %s%s%s\n", token, value ? "=" : "",
239 value ? value : ""));
240
241 if (!value) {
242 if (!strchr(token, '=')) {
243 ret = strdup(token);
244 goto out;
245 }
246 blkid_parse_tag_string(token, &t, &v);
247 if (!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(DEBUG_EVALUATE,
267 printf("%s=%s evaluated as %s\n", token, value, ret));
268out:
269 blkid_free_config(conf);
270 free(t);
271 free(v);
272 return ret;
273}
274
275/**
276 * blkid_evaluate_spec:
277 * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0)
278 * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
279 *
280 * All returned paths are canonicalized, device-mapper paths are converted
281 * to the /dev/mapper/name format.
282 *
283 * Returns: allocated string with a device name.
284 */
285char *blkid_evaluate_spec(const char *spec, blkid_cache *cache)
286{
287 char *t = NULL, *v = NULL, *res;
288
289 if (!spec)
290 return NULL;
291
292 if (strchr(spec, '=') &&
293 blkid_parse_tag_string(spec, &t, &v) != 0) /* parse error */
294 return NULL;
295
296 if (v)
297 res = blkid_evaluate_tag(t, v, cache);
298 else
299 res = canonicalize_path(spec);
300
301 free(t);
302 free(v);
303 return res;
304}
305
306
307#ifdef TEST_PROGRAM
308int main(int argc, char *argv[])
309{
310 blkid_cache cache = NULL;
311 char *res;
312
313 if (argc < 2) {
314 fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]);
315 return EXIT_FAILURE;
316 }
317
318 blkid_init_debug(0);
319
320 res = blkid_evaluate_spec(argv[1], &cache);
321 if (res)
322 printf("%s\n", res);
323 if (cache)
324 blkid_put_cache(cache);
325
326 return res ? EXIT_SUCCESS : EXIT_FAILURE;
327}
328#endif