/src/util-linux/libblkid/src/evaluate.c
Line | Count | Source |
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 | 0 | { |
110 | 0 | char uevent[PATH_MAX]; |
111 | 0 | struct stat st; |
112 | 0 | FILE *f; |
113 | 0 | int rc = -1; |
114 | |
|
115 | 0 | DBG(EVALUATE, ul_debug("%s: uevent '%s' requested", devname, action)); |
116 | |
|
117 | 0 | if (!devname || !action) |
118 | 0 | return -1; |
119 | 0 | if (stat(devname, &st) || !S_ISBLK(st.st_mode)) |
120 | 0 | return -1; |
121 | | |
122 | 0 | snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent", |
123 | 0 | major(st.st_rdev), minor(st.st_rdev)); |
124 | |
|
125 | 0 | f = fopen(uevent, "w" UL_CLOEXECSTR); |
126 | 0 | if (f) { |
127 | 0 | rc = 0; |
128 | 0 | if (fputs(action, f) >= 0) |
129 | 0 | rc = 0; |
130 | 0 | if (close_stream(f) != 0) |
131 | 0 | DBG(EVALUATE, ul_debug("write failed: %s", uevent)); |
132 | 0 | } |
133 | 0 | DBG(EVALUATE, ul_debug("%s: send uevent %s", |
134 | 0 | uevent, rc == 0 ? "SUCCESS" : "FAILED")); |
135 | 0 | return rc; |
136 | 0 | } |
137 | | |
138 | | static char *evaluate_by_udev(const char *token, const char *value, int uevent) |
139 | 45 | { |
140 | 45 | char dev[PATH_MAX]; |
141 | 45 | char *path = NULL; |
142 | 45 | size_t len; |
143 | 45 | struct stat st; |
144 | | |
145 | 45 | DBG(EVALUATE, ul_debug("evaluating by udev %s=%s", token, value)); |
146 | | |
147 | 45 | if (!strcmp(token, "UUID")) |
148 | 45 | strcpy(dev, _PATH_DEV_BYUUID "/"); |
149 | 0 | else if (!strcmp(token, "LABEL")) |
150 | 0 | strcpy(dev, _PATH_DEV_BYLABEL "/"); |
151 | 0 | else if (!strcmp(token, "PARTLABEL")) |
152 | 0 | strcpy(dev, _PATH_DEV_BYPARTLABEL "/"); |
153 | 0 | else if (!strcmp(token, "PARTUUID")) |
154 | 0 | strcpy(dev, _PATH_DEV_BYPARTUUID "/"); |
155 | 0 | else if (!strcmp(token, "ID")) |
156 | 0 | strcpy(dev, _PATH_DEV_BYID "/"); |
157 | 0 | else { |
158 | 0 | DBG(EVALUATE, ul_debug("unsupported token %s", token)); |
159 | 0 | return NULL; /* unsupported tag */ |
160 | 0 | } |
161 | | |
162 | 45 | len = strlen(dev); |
163 | 45 | if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0) |
164 | 0 | return NULL; |
165 | | |
166 | 45 | DBG(EVALUATE, ul_debug("expected udev link: %s", dev)); |
167 | | |
168 | 45 | if (stat(dev, &st)) |
169 | 45 | goto failed; /* link or device does not exist */ |
170 | | |
171 | 0 | if (!S_ISBLK(st.st_mode)) |
172 | 0 | return NULL; |
173 | | |
174 | 0 | path = ul_canonicalize_path(dev); |
175 | 0 | if (!path) |
176 | 0 | return NULL; |
177 | | |
178 | | #ifdef CONFIG_BLKID_VERIFY_UDEV |
179 | | if (verify_tag(path, token, value)) |
180 | | goto failed; |
181 | | #endif |
182 | 0 | return path; |
183 | | |
184 | 45 | failed: |
185 | 45 | DBG(EVALUATE, ul_debug("failed to evaluate by udev")); |
186 | | |
187 | 45 | if (uevent && path) |
188 | 0 | blkid_send_uevent(path, "change"); |
189 | 45 | free(path); |
190 | 45 | return NULL; |
191 | 0 | } |
192 | | |
193 | | static char *evaluate_by_scan(const char *token, const char *value, |
194 | | blkid_cache *cache, struct blkid_config *conf) |
195 | 45 | { |
196 | 45 | blkid_cache c = cache ? *cache : NULL; |
197 | 45 | char *res; |
198 | | |
199 | 45 | DBG(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", token, value)); |
200 | | |
201 | 45 | if (!c) { |
202 | 45 | char *cachefile = blkid_get_cache_filename(conf); |
203 | 45 | int rc = blkid_get_cache(&c, cachefile); |
204 | 45 | free(cachefile); |
205 | 45 | if (rc < 0) |
206 | 0 | return NULL; |
207 | 45 | } |
208 | 45 | if (!c) |
209 | 0 | return NULL; |
210 | | |
211 | 45 | res = blkid_get_devname(c, token, value); |
212 | | |
213 | 45 | if (cache) |
214 | 0 | *cache = c; |
215 | 45 | else |
216 | 45 | blkid_put_cache(c); |
217 | | |
218 | 45 | return res; |
219 | 45 | } |
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 | | * If the @value is NULL and @token is not in the NAME=value format, then return |
228 | | * a copy of the @token. |
229 | | * |
230 | | * Returns: allocated string with a device name. |
231 | | */ |
232 | | char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache) |
233 | 45 | { |
234 | 45 | struct blkid_config *conf = NULL; |
235 | 45 | char *t = NULL, *v = NULL; |
236 | 45 | char *ret = NULL; |
237 | 45 | int i; |
238 | | |
239 | 45 | if (!token) |
240 | 0 | return NULL; |
241 | | |
242 | 45 | DBG(EVALUATE, ul_debug("evaluating %s%s%s", token, value ? "=" : "", |
243 | 45 | value ? value : "")); |
244 | | |
245 | 45 | if (!value) { |
246 | 0 | if (!strchr(token, '=')) { |
247 | 0 | ret = strdup(token); |
248 | 0 | goto out; |
249 | 0 | } |
250 | 0 | if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v) |
251 | 0 | goto out; |
252 | 0 | token = t; |
253 | 0 | value = v; |
254 | 0 | } |
255 | | |
256 | 45 | conf = blkid_read_config(NULL); |
257 | 45 | if (!conf) |
258 | 0 | goto out; |
259 | | |
260 | 90 | for (i = 0; i < conf->nevals; i++) { |
261 | 90 | if (conf->eval[i] == BLKID_EVAL_UDEV) |
262 | 45 | ret = evaluate_by_udev(token, value, conf->uevent); |
263 | 45 | else if (conf->eval[i] == BLKID_EVAL_SCAN) |
264 | 45 | ret = evaluate_by_scan(token, value, cache, conf); |
265 | 90 | if (ret) |
266 | 45 | break; |
267 | 90 | } |
268 | | |
269 | 45 | DBG(EVALUATE, ul_debug("%s=%s evaluated as %s", token, value, ret)); |
270 | 45 | out: |
271 | 45 | blkid_free_config(conf); |
272 | 45 | free(t); |
273 | 45 | free(v); |
274 | 45 | return ret; |
275 | 45 | } |
276 | | |
277 | | /** |
278 | | * blkid_evaluate_spec: |
279 | | * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0) |
280 | | * @cache: pointer to cache (or NULL when you don't want to re-use the cache) |
281 | | * |
282 | | * All returned paths are canonicalized, device-mapper paths are converted |
283 | | * to the /dev/mapper/name format. |
284 | | * |
285 | | * Returns: allocated string with a device name. |
286 | | */ |
287 | | char *blkid_evaluate_spec(const char *spec, blkid_cache *cache) |
288 | 0 | { |
289 | 0 | char *t = NULL, *v = NULL, *res; |
290 | |
|
291 | 0 | if (!spec) |
292 | 0 | return NULL; |
293 | | |
294 | 0 | if (strchr(spec, '=') && |
295 | 0 | blkid_parse_tag_string(spec, &t, &v) != 0) /* parse error */ |
296 | 0 | return NULL; |
297 | | |
298 | 0 | if (v) |
299 | 0 | res = blkid_evaluate_tag(t, v, cache); |
300 | 0 | else |
301 | 0 | res = ul_canonicalize_path(spec); |
302 | |
|
303 | 0 | free(t); |
304 | 0 | free(v); |
305 | 0 | return res; |
306 | 0 | } |
307 | | |
308 | | |
309 | | #ifdef TEST_PROGRAM |
310 | | int main(int argc, char *argv[]) |
311 | | { |
312 | | blkid_cache cache = NULL; |
313 | | char *res; |
314 | | |
315 | | if (argc < 2) { |
316 | | fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]); |
317 | | return EXIT_FAILURE; |
318 | | } |
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 |