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