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