]>
Commit | Line | Data |
---|---|---|
892a404c KZ |
1 | /* |
2 | * evaluate.c - very high-level API to evaluate LABELs or UUIDs | |
3 | * | |
892a404c KZ |
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 | */ | |
892a404c KZ |
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 | |
892a404c KZ |
19 | #ifdef HAVE_ERRNO_H |
20 | #include <errno.h> | |
21 | #endif | |
22 | #include <stdint.h> | |
892a404c KZ |
23 | #include <stdarg.h> |
24 | ||
25 | #include "pathnames.h" | |
26 | #include "canonicalize.h" | |
8d21d9ab | 27 | #include "closestream.h" |
892a404c | 28 | |
892a404c KZ |
29 | #include "blkidP.h" |
30 | ||
488e52be KZ |
31 | /** |
32 | * SECTION:evaluate | |
fd7c9e35 | 33 | * @title: Tags and Spec evaluation |
488e52be KZ |
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 | |
fd7c9e35 KZ |
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. | |
488e52be KZ |
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 | ||
a01e15f1 | 52 | #ifdef CONFIG_BLKID_VERIFY_UDEV |
892a404c KZ |
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; | |
6d042d0d | 59 | const char *data; |
e9799b6d | 60 | int errsv = 0; |
892a404c | 61 | |
b3391f3a KZ |
62 | if (strcmp(token, "ID") == 0) |
63 | return 0; /* non-content tag */ | |
64 | ||
892a404c KZ |
65 | pr = blkid_new_probe(); |
66 | if (!pr) | |
67 | return -1; | |
68 | ||
1b780848 KZ |
69 | blkid_probe_enable_superblocks(pr, TRUE); |
70 | blkid_probe_set_superblocks_flags(pr, | |
71 | BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID); | |
892a404c | 72 | |
fc387ee1 KZ |
73 | blkid_probe_enable_partitions(pr, TRUE); |
74 | blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); | |
75 | ||
39f5af25 | 76 | fd = open(devname, O_RDONLY|O_CLOEXEC|O_NONBLOCK); |
e9799b6d KZ |
77 | if (fd < 0) { |
78 | errsv = errno; | |
892a404c | 79 | goto done; |
e9799b6d | 80 | } |
892a404c KZ |
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: | |
c62a6311 | 90 | DBG(EVALUATE, ul_debug("%s: %s verification %s", |
892a404c KZ |
91 | devname, name, rc == 0 ? "PASS" : "FAILED")); |
92 | if (fd >= 0) | |
93 | close(fd); | |
94 | blkid_free_probe(pr); | |
e9799b6d KZ |
95 | |
96 | /* for non-root users we use unverified udev links */ | |
97 | return errsv == EACCES ? 0 : rc; | |
892a404c | 98 | } |
a01e15f1 | 99 | #endif /* CONFIG_BLKID_VERIFY_UDEV*/ |
892a404c KZ |
100 | |
101 | /** | |
102 | * blkid_send_uevent: | |
103 | * @devname: absolute path to the device | |
fd7c9e35 | 104 | * @action: event string |
892a404c | 105 | * |
488e52be | 106 | * Returns: -1 in case of failure, or 0 on success. |
892a404c KZ |
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 | ||
c62a6311 | 115 | DBG(EVALUATE, ul_debug("%s: uevent '%s' requested", devname, action)); |
892a404c KZ |
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 | ||
4000fc12 | 125 | f = fopen(uevent, "w" UL_CLOEXECSTR); |
892a404c KZ |
126 | if (f) { |
127 | rc = 0; | |
128 | if (fputs(action, f) >= 0) | |
129 | rc = 0; | |
8d21d9ab | 130 | if (close_stream(f) != 0) |
c62a6311 | 131 | DBG(EVALUATE, ul_debug("write failed: %s", uevent)); |
892a404c | 132 | } |
c62a6311 | 133 | DBG(EVALUATE, ul_debug("%s: send uevent %s", |
9e930041 | 134 | uevent, rc == 0 ? "SUCCESS" : "FAILED")); |
892a404c KZ |
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 | ||
c62a6311 | 145 | DBG(EVALUATE, ul_debug("evaluating by udev %s=%s", token, value)); |
892a404c KZ |
146 | |
147 | if (!strcmp(token, "UUID")) | |
148 | strcpy(dev, _PATH_DEV_BYUUID "/"); | |
149 | else if (!strcmp(token, "LABEL")) | |
150 | strcpy(dev, _PATH_DEV_BYLABEL "/"); | |
fc387ee1 KZ |
151 | else if (!strcmp(token, "PARTLABEL")) |
152 | strcpy(dev, _PATH_DEV_BYPARTLABEL "/"); | |
153 | else if (!strcmp(token, "PARTUUID")) | |
154 | strcpy(dev, _PATH_DEV_BYPARTUUID "/"); | |
b3391f3a KZ |
155 | else if (!strcmp(token, "ID")) |
156 | strcpy(dev, _PATH_DEV_BYID "/"); | |
892a404c | 157 | else { |
c62a6311 | 158 | DBG(EVALUATE, ul_debug("unsupported token %s", token)); |
892a404c KZ |
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 | ||
c62a6311 | 166 | DBG(EVALUATE, ul_debug("expected udev link: %s", dev)); |
892a404c KZ |
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 | ||
a01e15f1 | 178 | #ifdef CONFIG_BLKID_VERIFY_UDEV |
892a404c KZ |
179 | if (verify_tag(path, token, value)) |
180 | goto failed; | |
a01e15f1 | 181 | #endif |
892a404c KZ |
182 | return path; |
183 | ||
184 | failed: | |
c62a6311 | 185 | DBG(EVALUATE, ul_debug("failed to evaluate by udev")); |
892a404c KZ |
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, | |
bb5f2876 | 194 | blkid_cache *cache, struct blkid_config *conf) |
892a404c KZ |
195 | { |
196 | blkid_cache c = cache ? *cache : NULL; | |
197 | char *res; | |
198 | ||
c62a6311 | 199 | DBG(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", token, value)); |
892a404c | 200 | |
bb5f2876 KZ |
201 | if (!c) { |
202 | char *cachefile = blkid_get_cache_filename(conf); | |
0aa42a81 | 203 | int rc = blkid_get_cache(&c, cachefile); |
bb5f2876 | 204 | free(cachefile); |
0aa42a81 KZ |
205 | if (rc < 0) |
206 | return NULL; | |
bb5f2876 | 207 | } |
892a404c KZ |
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 | /** | |
cee95a95 | 222 | * blkid_evaluate_tag: |
5298f728 KZ |
223 | * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo") |
224 | * @value: token data (e.g. "foo") | |
892a404c KZ |
225 | * @cache: pointer to cache (or NULL when you don't want to re-use the cache) |
226 | * | |
488e52be | 227 | * Returns: allocated string with a device name. |
892a404c | 228 | */ |
cee95a95 | 229 | char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache) |
892a404c KZ |
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 | ||
c62a6311 | 239 | DBG(EVALUATE, ul_debug("evaluating %s%s%s", token, value ? "=" : "", |
892a404c KZ |
240 | value ? value : "")); |
241 | ||
242 | if (!value) { | |
243 | if (!strchr(token, '=')) { | |
e0a9b8cf | 244 | ret = strdup(token); |
892a404c KZ |
245 | goto out; |
246 | } | |
e12e917b | 247 | if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v) |
892a404c KZ |
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) | |
bb5f2876 | 261 | ret = evaluate_by_scan(token, value, cache, conf); |
892a404c KZ |
262 | if (ret) |
263 | break; | |
264 | } | |
265 | ||
c62a6311 | 266 | DBG(EVALUATE, ul_debug("%s=%s evaluated as %s", token, value, ret)); |
892a404c KZ |
267 | out: |
268 | blkid_free_config(conf); | |
269 | free(t); | |
270 | free(v); | |
271 | return ret; | |
272 | } | |
273 | ||
5298f728 KZ |
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 | |
fd7c9e35 | 280 | * to the /dev/mapper/name format. |
5298f728 KZ |
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 | ||
892a404c KZ |
306 | #ifdef TEST_PROGRAM |
307 | int main(int argc, char *argv[]) | |
308 | { | |
309 | blkid_cache cache = NULL; | |
310 | char *res; | |
311 | ||
5298f728 KZ |
312 | if (argc < 2) { |
313 | fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]); | |
892a404c KZ |
314 | return EXIT_FAILURE; |
315 | } | |
316 | ||
5298f728 | 317 | res = blkid_evaluate_spec(argv[1], &cache); |
892a404c KZ |
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 |