Coverage Report

Created: 2026-01-10 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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