1 /*
2  * blkid.c - User command-line interface for libblkid
3  *
4  * Copyright (C) 2001 Andreas Dilger
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the
8  * GNU Lesser General Public License.
9  * %End-Header%
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #ifdef HAVE_GETOPT_H
21 #include <getopt.h>
22 #else
23 extern int getopt(int argc, char * const argv[], const char *optstring);
24 extern char *optarg;
25 extern int optind;
26 #endif
27 
28 #define OUTPUT_VALUE_ONLY	(1 << 1)
29 #define OUTPUT_DEVICE_ONLY	(1 << 2)
30 #define OUTPUT_PRETTY_LIST	(1 << 3)		/* deprecated */
31 #define OUTPUT_UDEV_LIST	(1 << 4)		/* deprecated */
32 #define OUTPUT_EXPORT_LIST	(1 << 5)
33 
34 #define LOWPROBE_TOPOLOGY	(1 << 1)
35 #define LOWPROBE_SUPERBLOCKS	(1 << 2)
36 
37 #define BLKID_EXIT_NOTFOUND	2	/* token or device not found */
38 #define BLKID_EXIT_OTHER	4	/* bad usage or other error */
39 #define BLKID_EXIT_AMBIVAL	8	/* ambivalent low-level probing detected */
40 
41 #include <blkid.h>
42 
43 #include "ismounted.h"
44 
45 #define STRTOXX_EXIT_CODE	BLKID_EXIT_OTHER	/* strtoxx_or_err() */
46 #include "strutils.h"
47 #define OPTUTILS_EXIT_CODE	BLKID_EXIT_OTHER	/* exclusive_option() */
48 #include "optutils.h"
49 
50 #include "closestream.h"
51 #include "ttyutils.h"
52 #include "xalloc.h"
53 
54 int raw_chars;
55 
print_version(FILE * out)56 static void print_version(FILE *out)
57 {
58 	fprintf(out, "%s from %s  (libblkid %s, %s)\n",
59 		program_invocation_short_name, PACKAGE_STRING,
60 		LIBBLKID_VERSION, LIBBLKID_DATE);
61 }
62 
usage(int error)63 static void usage(int error)
64 {
65 	FILE *out = error ? stderr : stdout;
66 
67 	print_version(out);
68 	fprintf(out,
69 		"Usage:\n"
70 		" %1$s -L <label> | -U <uuid>\n\n"
71 		" %1$s [-c <file>] [-ghlLv] [-o <format>] [-s <tag>] \n"
72 		"       [-t <token>] [<dev> ...]\n\n"
73 		" %1$s -p [-s <tag>] [-O <offset>] [-S <size>] \n"
74 		"       [-o <format>] <dev> ...\n\n"
75 		" %1$s -i [-s <tag>] [-o <format>] <dev> ...\n\n"
76 		"Options:\n"
77 		" -c <file>   read from <file> instead of reading from the default\n"
78 		"               cache file (-c /dev/null means no cache)\n"
79 		" -d          don't encode non-printing characters\n"
80 		" -h          print this usage message and exit\n"
81 		" -g          garbage collect the blkid cache\n"
82 		" -o <format> output format; can be one of:\n"
83 		"               value, device, export or full; (default: full)\n"
84 		" -k          list all known filesystems/RAIDs and exit\n"
85 		" -s <tag>    show specified tag(s) (default show all tags)\n"
86 		" -t <token>  find device with a specific token (NAME=value pair)\n"
87 		" -l          look up only first device with token specified by -t\n"
88 		" -L <label>  convert LABEL to device name\n"
89 		" -U <uuid>   convert UUID to device name\n"
90 		" -V          print version and exit\n"
91 		" <dev>       specify device(s) to probe (default: all devices)\n\n"
92 		"Low-level probing options:\n"
93 		" -p          low-level superblocks probing (bypass cache)\n"
94 		" -i          gather information about I/O limits\n"
95 		" -S <size>   overwrite device size\n"
96 		" -O <offset> probe at the given offset\n"
97 		" -u <list>   filter by \"usage\" (e.g. -u filesystem,raid)\n"
98 		" -n <list>   filter by filesystem type (e.g. -n vfat,ext3)\n"
99 		"\n", program_invocation_short_name);
100 
101 	exit(error);
102 }
103 
104 /*
105  * This function does "safe" printing.  It will convert non-printable
106  * ASCII characters using '^' and M- notation.
107  *
108  * If 'esc' is defined then escape all chars from esc by \.
109  */
safe_print(const char * cp,int len,const char * esc)110 static void safe_print(const char *cp, int len, const char *esc)
111 {
112 	unsigned char	ch;
113 
114 	if (len < 0)
115 		len = strlen(cp);
116 
117 	while (len--) {
118 		ch = *cp++;
119 		if (!raw_chars) {
120 			if (ch >= 128) {
121 				fputs("M-", stdout);
122 				ch -= 128;
123 			}
124 			if ((ch < 32) || (ch == 0x7f)) {
125 				fputc('^', stdout);
126 				ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
127 
128 			} else if (esc && strchr(esc, ch))
129 				fputc('\\', stdout);
130 		}
131 		fputc(ch, stdout);
132 	}
133 }
134 
pretty_print_word(const char * str,int max_len,int left_len,int overflow_nl)135 static int pretty_print_word(const char *str, int max_len,
136 			     int left_len, int overflow_nl)
137 {
138 	int len = strlen(str) + left_len;
139 	int ret = 0;
140 
141 	fputs(str, stdout);
142 	if (overflow_nl && len > max_len) {
143 		fputc('\n', stdout);
144 		len = 0;
145 	} else if (len > max_len)
146 		ret = len - max_len;
147 	do
148 		fputc(' ', stdout);
149 	while (len++ < max_len);
150 	return ret;
151 }
152 
pretty_print_line(const char * device,const char * fs_type,const char * label,const char * mtpt,const char * uuid)153 static void pretty_print_line(const char *device, const char *fs_type,
154 			      const char *label, const char *mtpt,
155 			      const char *uuid)
156 {
157 	static int device_len = 10, fs_type_len = 7;
158 	static int label_len = 8, mtpt_len = 14;
159 	static int term_width = -1;
160 	int len, w;
161 
162 	if (term_width < 0) {
163 		term_width = get_terminal_width();
164 		if (term_width <= 0)
165 			term_width = 80;
166 	}
167 	if (term_width > 80) {
168 		term_width -= 80;
169 		w = term_width / 10;
170 		if (w > 8)
171 			w = 8;
172 		term_width -= 2*w;
173 		label_len += w;
174 		fs_type_len += w;
175 		w = term_width/2;
176 		device_len += w;
177 		mtpt_len +=w;
178 	}
179 
180 	len = pretty_print_word(device, device_len, 0, 1);
181 	len = pretty_print_word(fs_type, fs_type_len, len, 0);
182 	len = pretty_print_word(label, label_len, len, 0);
183 	pretty_print_word(mtpt, mtpt_len, len, 0);
184 
185 	fputs(uuid, stdout);
186 	fputc('\n', stdout);
187 }
188 
pretty_print_dev(blkid_dev dev)189 static void pretty_print_dev(blkid_dev dev)
190 {
191 	blkid_tag_iterate	iter;
192 	const char		*type, *value, *devname;
193 	const char		*uuid = "", *fs_type = "", *label = "";
194 	int			len, mount_flags;
195 	char			mtpt[80];
196 	int			retval;
197 
198 	if (dev == NULL) {
199 		pretty_print_line("device", "fs_type", "label",
200 				  "mount point", "UUID");
201 		for (len=get_terminal_width()-1; len > 0; len--)
202 			fputc('-', stdout);
203 		fputc('\n', stdout);
204 		return;
205 	}
206 
207 	devname = blkid_dev_devname(dev);
208 	if (access(devname, F_OK))
209 		return;
210 
211 	/* Get the uuid, label, type */
212 	iter = blkid_tag_iterate_begin(dev);
213 	while (blkid_tag_next(iter, &type, &value) == 0) {
214 		if (!strcmp(type, "UUID"))
215 			uuid = value;
216 		if (!strcmp(type, "TYPE"))
217 			fs_type = value;
218 		if (!strcmp(type, "LABEL"))
219 			label = value;
220 	}
221 	blkid_tag_iterate_end(iter);
222 
223 	/* Get the mount point */
224 	mtpt[0] = 0;
225 	retval = check_mount_point(devname, &mount_flags, mtpt, sizeof(mtpt));
226 	if (retval == 0) {
227 		if (mount_flags & MF_MOUNTED) {
228 			if (!mtpt[0])
229 				strcpy(mtpt, "(mounted, mtpt unknown)");
230 		} else if (mount_flags & MF_BUSY)
231 			strcpy(mtpt, "(in use)");
232 		else
233 			strcpy(mtpt, "(not mounted)");
234 	}
235 
236 	pretty_print_line(devname, fs_type, label, mtpt, uuid);
237 }
238 
print_udev_format(const char * name,const char * value)239 static void print_udev_format(const char *name, const char *value)
240 {
241 	char enc[265], safe[256];
242 	size_t namelen = strlen(name);
243 
244 	*safe = *enc = '\0';
245 
246 	if (!strcmp(name, "TYPE") || !strcmp(name, "VERSION")) {
247 		blkid_encode_string(value, enc, sizeof(enc));
248 		printf("ID_FS_%s=%s\n", name, enc);
249 
250 	} else if (!strcmp(name, "UUID") ||
251 		 !strcmp(name, "LABEL") ||
252 		 !strcmp(name, "UUID_SUB")) {
253 
254 		blkid_safe_string(value, safe, sizeof(safe));
255 		printf("ID_FS_%s=%s\n", name, safe);
256 
257 		blkid_encode_string(value, enc, sizeof(enc));
258 		printf("ID_FS_%s_ENC=%s\n", name, enc);
259 
260 	} else if (!strcmp(name, "PTUUID")) {
261 		printf("ID_PART_TABLE_UUID=%s\n", value);
262 
263 	} else if (!strcmp(name, "PTTYPE")) {
264 		printf("ID_PART_TABLE_TYPE=%s\n", value);
265 
266 	} else if (!strcmp(name, "PART_ENTRY_NAME") ||
267 		  !strcmp(name, "PART_ENTRY_TYPE")) {
268 
269 		blkid_encode_string(value, enc, sizeof(enc));
270 		printf("ID_%s=%s\n", name, enc);
271 
272 	} else if (!strncmp(name, "PART_ENTRY_", 11))
273 		printf("ID_%s=%s\n", name, value);
274 
275 	else if (namelen >= 15 && (
276 		   !strcmp(name + (namelen - 12), "_SECTOR_SIZE") ||
277 		   !strcmp(name + (namelen - 8), "_IO_SIZE") ||
278 		   !strcmp(name, "ALIGNMENT_OFFSET")))
279 			printf("ID_IOLIMIT_%s=%s\n", name, value);
280 	else
281 		printf("ID_FS_%s=%s\n", name, value);
282 }
283 
has_item(char * ary[],const char * item)284 static int has_item(char *ary[], const char *item)
285 {
286 	char **p;
287 
288 	for (p = ary; *p != NULL; p++)
289 		if (!strcmp(item, *p))
290 			return 1;
291 	return 0;
292 }
293 
print_value(int output,int num,const char * devname,const char * value,const char * name,size_t valsz)294 static void print_value(int output, int num, const char *devname,
295 			const char *value, const char *name, size_t valsz)
296 {
297 	if (output & OUTPUT_VALUE_ONLY) {
298 		fputs(value, stdout);
299 		fputc('\n', stdout);
300 
301 	} else if (output & OUTPUT_UDEV_LIST) {
302 		print_udev_format(name, value);
303 
304 	} else if (output & OUTPUT_EXPORT_LIST) {
305 		if (num == 1 && devname)
306 			printf("DEVNAME=%s\n", devname);
307 		fputs(name, stdout);
308 		fputs("=", stdout);
309 		safe_print(value, valsz, NULL);
310 		fputs("\n", stdout);
311 
312 	} else {
313 		if (num == 1 && devname)
314 			printf("%s:", devname);
315 		fputs(" ", stdout);
316 		fputs(name, stdout);
317 		fputs("=\"", stdout);
318 		safe_print(value, valsz, "\"");
319 		fputs("\"", stdout);
320 	}
321 }
322 
print_tags(blkid_dev dev,char * show[],int output)323 static void print_tags(blkid_dev dev, char *show[], int output)
324 {
325 	blkid_tag_iterate	iter;
326 	const char		*type, *value, *devname;
327 	int			num = 1;
328 	static int		first = 1;
329 
330 	if (!dev)
331 		return;
332 
333 	if (output & OUTPUT_PRETTY_LIST) {
334 		pretty_print_dev(dev);
335 		return;
336 	}
337 
338 	devname = blkid_dev_devname(dev);
339 
340 	if (output & OUTPUT_DEVICE_ONLY) {
341 		printf("%s\n", devname);
342 		return;
343 	}
344 
345 	iter = blkid_tag_iterate_begin(dev);
346 	while (blkid_tag_next(iter, &type, &value) == 0) {
347 		if (show[0] && !has_item(show, type))
348 			continue;
349 
350 		if (num == 1 && !first &&
351 		    (output & (OUTPUT_UDEV_LIST | OUTPUT_EXPORT_LIST)))
352 			/* add extra line between output from more devices */
353 			fputc('\n', stdout);
354 
355 		print_value(output, num++, devname, value, type, strlen(value));
356 	}
357 	blkid_tag_iterate_end(iter);
358 
359 	if (num > 1) {
360 		if (!(output & (OUTPUT_VALUE_ONLY | OUTPUT_UDEV_LIST |
361 						OUTPUT_EXPORT_LIST)))
362 			printf("\n");
363 		first = 0;
364 	}
365 }
366 
367 
append_str(char ** res,size_t * sz,const char * a,const char * b)368 static int append_str(char **res, size_t *sz, const char *a, const char *b)
369 {
370 	char *str = *res;
371 	size_t asz = a ? strlen(a) : 0;
372 	size_t bsz = b ? strlen(b) : 0;
373 	size_t len = *sz + asz + bsz;
374 
375 	if (!len)
376 		return -1;
377 
378 	*res = str = xrealloc(str, len + 1);
379 	str += *sz;
380 
381 	if (a) {
382 		memcpy(str, a, asz);
383 		str += asz;
384 	}
385 	if (b) {
386 		memcpy(str, b, bsz);
387 		str += bsz;
388 	}
389 	*str = '\0';
390 	*sz = len;
391 	return 0;
392 }
393 
394 /*
395  * Compose and print ID_FS_AMBIVALENT for udev
396  */
print_udev_ambivalent(blkid_probe pr)397 static int print_udev_ambivalent(blkid_probe pr)
398 {
399 	char *val = NULL;
400 	size_t valsz = 0;
401 	int count = 0, rc = -1;
402 
403 	while (!blkid_do_probe(pr)) {
404 		const char *usage_txt = NULL, *type = NULL, *version = NULL;
405 		char enc[256];
406 
407 		blkid_probe_lookup_value(pr, "USAGE", &usage_txt, NULL);
408 		blkid_probe_lookup_value(pr, "TYPE", &type, NULL);
409 		blkid_probe_lookup_value(pr, "VERSION", &version, NULL);
410 
411 		if (!usage_txt || !type)
412 			continue;
413 
414 		blkid_encode_string(usage_txt, enc, sizeof(enc));
415 		if (append_str(&val, &valsz, enc, ":"))
416 			goto done;
417 
418 		blkid_encode_string(type, enc, sizeof(enc));
419 		if (append_str(&val, &valsz, enc, version ? ":" : " "))
420 			goto done;
421 
422 		if (version) {
423 			blkid_encode_string(version, enc, sizeof(enc));
424 			if (append_str(&val, &valsz, enc, " "))
425 				goto done;
426 		}
427 		count++;
428 	}
429 
430 	if (count > 1) {
431 		*(val + valsz - 1) = '\0';		/* rem tailing whitespace */
432 		printf("ID_FS_AMBIVALENT=%s\n", val);
433 		rc = 0;
434 	}
435 done:
436 	free(val);
437 	return rc;
438 }
439 
lowprobe_superblocks(blkid_probe pr)440 static int lowprobe_superblocks(blkid_probe pr)
441 {
442 	struct stat st;
443 	int rc, fd = blkid_probe_get_fd(pr);
444 
445 	if (fd < 0 || fstat(fd, &st))
446 		return -1;
447 
448 	blkid_probe_enable_partitions(pr, 1);
449 
450 	if (!S_ISCHR(st.st_mode) && blkid_probe_get_size(pr) <= 1024 * 1440 &&
451 	    blkid_probe_is_wholedisk(pr)) {
452 		/*
453 		 * check if the small disk is partitioned, if yes then
454 		 * don't probe for filesystems.
455 		 */
456 		blkid_probe_enable_superblocks(pr, 0);
457 
458 		rc = blkid_do_fullprobe(pr);
459 		if (rc < 0)
460 			return rc;	/* -1 = error, 1 = nothing, 0 = succes */
461 
462 		if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0)
463 			return 0;	/* partition table detected */
464 	}
465 
466 	blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
467 	blkid_probe_enable_superblocks(pr, 1);
468 
469 	return blkid_do_safeprobe(pr);
470 }
471 
lowprobe_topology(blkid_probe pr)472 static int lowprobe_topology(blkid_probe pr)
473 {
474 	/* enable topology probing only */
475 	blkid_probe_enable_topology(pr, 1);
476 
477 	blkid_probe_enable_superblocks(pr, 0);
478 	blkid_probe_enable_partitions(pr, 0);
479 
480 	return blkid_do_fullprobe(pr);
481 }
482 
lowprobe_device(blkid_probe pr,const char * devname,int chain,char * show[],int output,blkid_loff_t offset,blkid_loff_t size)483 static int lowprobe_device(blkid_probe pr, const char *devname,
484 			int chain, char *show[], int output,
485 			blkid_loff_t offset, blkid_loff_t size)
486 {
487 	const char *data;
488 	const char *name;
489 	int nvals = 0, n, num = 1;
490 	size_t len;
491 	int fd;
492 	int rc = 0;
493 	static int first = 1;
494 
495 	fd = open(devname, O_RDONLY|O_CLOEXEC);
496 	if (fd < 0) {
497 		fprintf(stderr, "error: %s: %m\n", devname);
498 		return BLKID_EXIT_NOTFOUND;
499 	}
500 	if (blkid_probe_set_device(pr, fd, offset, size))
501 		goto done;
502 
503 	if (chain & LOWPROBE_TOPOLOGY)
504 		rc = lowprobe_topology(pr);
505 	if (rc >= 0 && (chain & LOWPROBE_SUPERBLOCKS))
506 		rc = lowprobe_superblocks(pr);
507 	if (rc < 0)
508 		goto done;
509 
510 	if (!rc)
511 		nvals = blkid_probe_numof_values(pr);
512 
513 	if (nvals &&
514 	    !(chain & LOWPROBE_TOPOLOGY) &&
515 	    !(output & OUTPUT_UDEV_LIST) &&
516 	    !blkid_probe_has_value(pr, "TYPE") &&
517 	    !blkid_probe_has_value(pr, "PTTYPE"))
518 		/*
519 		 * Ignore probing result if there is not any filesystem or
520 		 * partition table on the device and udev output is not
521 		 * requested.
522 		 *
523 		 * The udev db stores information about partitions, so
524 		 * PART_ENTRY_* values are alway important.
525 		 */
526 		nvals = 0;
527 
528 	if (nvals && !first && output & (OUTPUT_UDEV_LIST | OUTPUT_EXPORT_LIST))
529 		/* add extra line between output from devices */
530 		fputc('\n', stdout);
531 
532 	if (nvals && (output & OUTPUT_DEVICE_ONLY)) {
533 		printf("%s\n", devname);
534 		goto done;
535 	}
536 
537 	for (n = 0; n < nvals; n++) {
538 		if (blkid_probe_get_value(pr, n, &name, &data, &len))
539 			continue;
540 		if (show[0] && !has_item(show, name))
541 			continue;
542 		len = strnlen((char *) data, len);
543 		print_value(output, num++, devname, (char *) data, name, len);
544 	}
545 
546 	if (first)
547 		first = 0;
548 	if (nvals >= 1 && !(output & (OUTPUT_VALUE_ONLY |
549 					OUTPUT_UDEV_LIST | OUTPUT_EXPORT_LIST)))
550 		printf("\n");
551 done:
552 	if (rc == -2) {
553 		if (output & OUTPUT_UDEV_LIST)
554 			print_udev_ambivalent(pr);
555 		else
556 			fprintf(stderr,
557 				"%s: ambivalent result (probably more "
558 				"filesystems on the device, use wipefs(8) "
559 				"to see more details)\n",
560 				devname);
561 	}
562 	close(fd);
563 
564 	if (rc == -2)
565 		return BLKID_EXIT_AMBIVAL;	/* ambivalent probing result */
566 	if (!nvals)
567 		return BLKID_EXIT_NOTFOUND;	/* nothing detected */
568 
569 	return 0;		/* success */
570 }
571 
572 /* converts comma separated list to BLKID_USAGE_* mask */
list_to_usage(const char * list,int * flag)573 static int list_to_usage(const char *list, int *flag)
574 {
575 	int mask = 0;
576 	const char *word = NULL, *p = list;
577 
578 	if (p && strncmp(p, "no", 2) == 0) {
579 		*flag = BLKID_FLTR_NOTIN;
580 		p += 2;
581 	}
582 	if (!p || !*p)
583 		goto err;
584 	while(p) {
585 		word = p;
586 		p = strchr(p, ',');
587 		if (p)
588 			p++;
589 		if (!strncmp(word, "filesystem", 10))
590 			mask |= BLKID_USAGE_FILESYSTEM;
591 		else if (!strncmp(word, "raid", 4))
592 			mask |= BLKID_USAGE_RAID;
593 		else if (!strncmp(word, "crypto", 6))
594 			mask |= BLKID_USAGE_CRYPTO;
595 		else if (!strncmp(word, "other", 5))
596 			mask |= BLKID_USAGE_OTHER;
597 		else
598 			goto err;
599 	}
600 	return mask;
601 err:
602 	*flag = 0;
603 	fprintf(stderr, "unknown keyword in -u <list> argument: '%s'\n",
604 			word ? word : list);
605 	exit(BLKID_EXIT_OTHER);
606 }
607 
608 /* converts comma separated list to types[] */
list_to_types(const char * list,int * flag)609 static char **list_to_types(const char *list, int *flag)
610 {
611 	int i;
612 	const char *p = list;
613 	char **res = NULL;
614 
615 	if (p && strncmp(p, "no", 2) == 0) {
616 		*flag = BLKID_FLTR_NOTIN;
617 		p += 2;
618 	}
619 	if (!p || !*p) {
620 		fprintf(stderr, "error: -u <list> argument is empty\n");
621 		goto err;
622 	}
623 	for (i = 1; p && (p = strchr(p, ',')); i++, p++);
624 
625 	res = xcalloc(i + 1, sizeof(char *));
626 	p = *flag & BLKID_FLTR_NOTIN ? list + 2 : list;
627 	i = 0;
628 
629 	while(p) {
630 		const char *word = p;
631 		p = strchr(p, ',');
632 		res[i++] = p ? xstrndup(word, p - word) : xstrdup(word);
633 		if (p)
634 			p++;
635 	}
636 	res[i] = NULL;
637 	return res;
638 err:
639 	*flag = 0;
640 	free(res);
641 	exit(BLKID_EXIT_OTHER);
642 }
643 
free_types_list(char * list[])644 static void free_types_list(char *list[])
645 {
646 	char **n;
647 
648 	if (!list)
649 		return;
650 	for (n = list; *n; n++)
651 		free(*n);
652 	free(list);
653 }
654 
main(int argc,char ** argv)655 int main(int argc, char **argv)
656 {
657 	blkid_cache cache = NULL;
658 	char **devices = NULL;
659 	char *show[128] = { NULL, };
660 	char *search_type = NULL, *search_value = NULL;
661 	char *read = NULL;
662 	int fltr_usage = 0;
663 	char **fltr_type = NULL;
664 	int fltr_flag = BLKID_FLTR_ONLYIN;
665 	unsigned int numdev = 0, numtag = 0;
666 	int version = 0;
667 	int err = BLKID_EXIT_OTHER;
668 	unsigned int i;
669 	int output_format = 0;
670 	int lookup = 0, gc = 0, lowprobe = 0, eval = 0;
671 	int c;
672 	uintmax_t offset = 0, size = 0;
673 
674 	static const ul_excl_t excl[] = {       /* rows and cols in in ASCII order */
675 		{ 'n','u' },
676 		{ 0 }
677 	};
678 	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
679 
680 	show[0] = NULL;
681 	atexit(close_stdout);
682 
683 	while ((c = getopt (argc, argv,
684 			    "c:df:ghilL:n:ko:O:ps:S:t:u:U:w:Vv")) != EOF) {
685 
686 		err_exclusive_options(c, NULL, excl, excl_st);
687 
688 		switch (c) {
689 		case 'c':
690 			if (optarg && !*optarg)
691 				read = NULL;
692 			else
693 				read = optarg;
694 			break;
695 		case 'd':
696 			raw_chars = 1;
697 			break;
698 		case 'L':
699 			eval++;
700 			search_value = xstrdup(optarg);
701 			search_type = xstrdup("LABEL");
702 			break;
703 		case 'n':
704 			fltr_type = list_to_types(optarg, &fltr_flag);
705 			break;
706 		case 'u':
707 			fltr_usage = list_to_usage(optarg, &fltr_flag);
708 			break;
709 		case 'U':
710 			eval++;
711 			search_value = xstrdup(optarg);
712 			search_type = xstrdup("UUID");
713 			break;
714 		case 'i':
715 			lowprobe |= LOWPROBE_TOPOLOGY;
716 			break;
717 		case 'l':
718 			lookup++;
719 			break;
720 		case 'g':
721 			gc = 1;
722 			break;
723 		case 'k':
724 		{
725 			size_t idx = 0;
726 			const char *name = NULL;
727 
728 			while (blkid_superblocks_get_name(idx++, &name, NULL) == 0)
729 				printf("%s\n", name);
730 			exit(EXIT_SUCCESS);
731 		}
732 		case 'o':
733 			if (!strcmp(optarg, "value"))
734 				output_format = OUTPUT_VALUE_ONLY;
735 			else if (!strcmp(optarg, "device"))
736 				output_format = OUTPUT_DEVICE_ONLY;
737 			else if (!strcmp(optarg, "list"))
738 				output_format = OUTPUT_PRETTY_LIST;	/* deprecated */
739 			else if (!strcmp(optarg, "udev"))
740 				output_format = OUTPUT_UDEV_LIST;
741 			else if (!strcmp(optarg, "export"))
742 				output_format = OUTPUT_EXPORT_LIST;
743 			else if (!strcmp(optarg, "full"))
744 				output_format = 0;
745 			else {
746 				fprintf(stderr, "Invalid output format %s. "
747 					"Choose from value,\n\t"
748 					"device, list, udev or full\n", optarg);
749 				exit(BLKID_EXIT_OTHER);
750 			}
751 			break;
752 		case 'O':
753 			offset = strtosize_or_err(optarg, "invalid offset argument");
754 			break;
755 		case 'p':
756 			lowprobe |= LOWPROBE_SUPERBLOCKS;
757 			break;
758 		case 's':
759 			if (numtag + 1 >= sizeof(show) / sizeof(*show)) {
760 				fprintf(stderr, "Too many tags specified\n");
761 				usage(err);
762 			}
763 			show[numtag++] = optarg;
764 			show[numtag] = NULL;
765 			break;
766 		case 'S':
767 			size = strtosize_or_err(optarg, "invalid size argument");
768 			break;
769 		case 't':
770 			if (search_type) {
771 				fprintf(stderr, "Can only search for "
772 						"one NAME=value pair\n");
773 				usage(err);
774 			}
775 			if (blkid_parse_tag_string(optarg,
776 						   &search_type,
777 						   &search_value)) {
778 				fprintf(stderr, "-t needs NAME=value pair\n");
779 				usage(err);
780 			}
781 			break;
782 		case 'V':
783 		case 'v':
784 			version = 1;
785 			break;
786 		case 'w':
787 			/* ignore - backward compatibility */
788 			break;
789 		case 'h':
790 			err = 0;
791 			/* fallthrough */
792 		default:
793 			usage(err);
794 		}
795 	}
796 
797 
798 	/* The rest of the args are device names */
799 	if (optind < argc) {
800 		devices = xcalloc(argc - optind, sizeof(char *));
801 		while (optind < argc)
802 			devices[numdev++] = argv[optind++];
803 	}
804 
805 	if (version) {
806 		print_version(stdout);
807 		goto exit;
808 	}
809 
810 	/* convert LABEL/UUID lookup to evaluate request */
811 	if (lookup && output_format == OUTPUT_DEVICE_ONLY && search_type &&
812 	    (!strcmp(search_type, "LABEL") || !strcmp(search_type, "UUID"))) {
813 		eval++;
814 		lookup = 0;
815 	}
816 
817 	if (!lowprobe && !eval && blkid_get_cache(&cache, read) < 0)
818 		goto exit;
819 
820 	if (gc) {
821 		blkid_gc_cache(cache);
822 		err = 0;
823 		goto exit;
824 	}
825 	err = BLKID_EXIT_NOTFOUND;
826 
827 	if (eval == 0 && (output_format & OUTPUT_PRETTY_LIST)) {
828 		if (lowprobe) {
829 			fprintf(stderr, "The low-level probing mode does not "
830 					"support 'list' output format\n");
831 			exit(BLKID_EXIT_OTHER);
832 		}
833 		pretty_print_dev(NULL);
834 	}
835 
836 	if (lowprobe) {
837 		/*
838 		 * Low-level API
839 		 */
840 		blkid_probe pr;
841 
842 		if (!numdev) {
843 			fprintf(stderr, "The low-level probing mode "
844 					"requires a device\n");
845 			exit(BLKID_EXIT_OTHER);
846 		}
847 
848 		/* automatically enable 'export' format for I/O Limits */
849 		if (!output_format  && (lowprobe & LOWPROBE_TOPOLOGY))
850 			output_format = OUTPUT_EXPORT_LIST;
851 
852 		pr = blkid_new_probe();
853 		if (!pr)
854 			goto exit;
855 
856 		if (lowprobe & LOWPROBE_SUPERBLOCKS) {
857 			blkid_probe_set_superblocks_flags(pr,
858 				BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
859 				BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
860 				BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION);
861 
862 			if (fltr_usage && blkid_probe_filter_superblocks_usage(
863 						pr, fltr_flag, fltr_usage))
864 				goto exit;
865 
866 			else if (fltr_type && blkid_probe_filter_superblocks_type(
867 						pr, fltr_flag, fltr_type))
868 				goto exit;
869 		}
870 
871 		for (i = 0; i < numdev; i++) {
872 			err = lowprobe_device(pr, devices[i], lowprobe, show,
873 					output_format,
874 					(blkid_loff_t) offset,
875 					(blkid_loff_t) size);
876 			if (err)
877 				break;
878 		}
879 		blkid_free_probe(pr);
880 	} else if (eval) {
881 		/*
882 		 * Evaluate API
883 		 */
884 		char *res = blkid_evaluate_tag(search_type, search_value, NULL);
885 		if (res) {
886 			err = 0;
887 			printf("%s\n", res);
888 		}
889 	} else if (lookup) {
890 		/*
891 		 * Classic (cache based) API
892 		 */
893 		blkid_dev dev;
894 
895 		if (!search_type) {
896 			fprintf(stderr, "The lookup option requires a "
897 				"search type specified using -t\n");
898 			exit(BLKID_EXIT_OTHER);
899 		}
900 		/* Load any additional devices not in the cache */
901 		for (i = 0; i < numdev; i++)
902 			blkid_get_dev(cache, devices[i], BLKID_DEV_NORMAL);
903 
904 		if ((dev = blkid_find_dev_with_tag(cache, search_type,
905 						   search_value))) {
906 			print_tags(dev, show, output_format);
907 			err = 0;
908 		}
909 	/* If we didn't specify a single device, show all available devices */
910 	} else if (!numdev) {
911 		blkid_dev_iterate	iter;
912 		blkid_dev		dev;
913 
914 		blkid_probe_all(cache);
915 
916 		iter = blkid_dev_iterate_begin(cache);
917 		blkid_dev_set_search(iter, search_type, search_value);
918 		while (blkid_dev_next(iter, &dev) == 0) {
919 			dev = blkid_verify(cache, dev);
920 			if (!dev)
921 				continue;
922 			print_tags(dev, show, output_format);
923 			err = 0;
924 		}
925 		blkid_dev_iterate_end(iter);
926 	/* Add all specified devices to cache (optionally display tags) */
927 	} else for (i = 0; i < numdev; i++) {
928 		blkid_dev dev = blkid_get_dev(cache, devices[i],
929 						  BLKID_DEV_NORMAL);
930 
931 		if (dev) {
932 			if (search_type &&
933 			    !blkid_dev_has_tag(dev, search_type,
934 					       search_value))
935 				continue;
936 			print_tags(dev, show, output_format);
937 			err = 0;
938 		}
939 	}
940 
941 exit:
942 	free(search_type);
943 	free(search_value);
944 	free_types_list(fltr_type);
945 	if (!lowprobe && !eval)
946 		blkid_put_cache(cache);
947 	free(devices);
948 	return err;
949 }
950