]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/wipefs.c
Merge branch 'uclampset-v3' of https://github.com/qais-yousef/util-linux
[thirdparty/util-linux.git] / misc-utils / wipefs.c
1 /*
2 * wipefs - utility to wipe filesystems from device
3 *
4 * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
5 * Written by Karel Zak <kzak@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <getopt.h>
30 #include <string.h>
31 #include <limits.h>
32 #include <libgen.h>
33
34 #include <blkid.h>
35 #include <libsmartcols.h>
36
37 #include "nls.h"
38 #include "xalloc.h"
39 #include "strutils.h"
40 #include "all-io.h"
41 #include "match.h"
42 #include "c.h"
43 #include "closestream.h"
44 #include "optutils.h"
45 #include "blkdev.h"
46
47 struct wipe_desc {
48 loff_t offset; /* magic string offset */
49 size_t len; /* length of magic string */
50 unsigned char *magic; /* magic string */
51
52 char *usage; /* raid, filesystem, ... */
53 char *type; /* FS type */
54 char *label; /* FS label */
55 char *uuid; /* FS uuid */
56
57 struct wipe_desc *next;
58
59 unsigned int on_disk : 1,
60 is_parttable : 1;
61
62 };
63
64 struct wipe_control {
65 char *devname;
66 const char *type_pattern; /* -t <pattern> */
67 const char *lockmode;
68
69 struct libscols_table *outtab;
70 struct wipe_desc *offsets; /* -o <offset> -o <offset> ... */
71
72 size_t ndevs; /* number of devices to probe */
73
74 char **reread; /* devices to BLKRRPART */
75 size_t nrereads; /* size of reread */
76
77 unsigned int noact : 1,
78 all : 1,
79 quiet : 1,
80 backup : 1,
81 force : 1,
82 json : 1,
83 no_headings : 1,
84 parsable : 1;
85 };
86
87
88 /* column IDs */
89 enum {
90 COL_UUID = 0,
91 COL_LABEL,
92 COL_LEN,
93 COL_TYPE,
94 COL_OFFSET,
95 COL_USAGE,
96 COL_DEVICE
97 };
98
99 /* column names */
100 struct colinfo {
101 const char *name; /* header */
102 double whint; /* width hint (N < 1 is in percent of termwidth) */
103 int flags; /* SCOLS_FL_* */
104 const char *help;
105 };
106
107 /* columns descriptions */
108 static const struct colinfo infos[] = {
109 [COL_UUID] = {"UUID", 4, 0, N_("partition/filesystem UUID")},
110 [COL_LABEL] = {"LABEL", 5, 0, N_("filesystem LABEL")},
111 [COL_LEN] = {"LENGTH", 6, 0, N_("magic string length")},
112 [COL_TYPE] = {"TYPE", 4, 0, N_("superblok type")},
113 [COL_OFFSET] = {"OFFSET", 5, 0, N_("magic string offset")},
114 [COL_USAGE] = {"USAGE", 5, 0, N_("type description")},
115 [COL_DEVICE] = {"DEVICE", 5, 0, N_("block device name")}
116 };
117
118 static int columns[ARRAY_SIZE(infos) * 2];
119 static size_t ncolumns;
120
121 static int column_name_to_id(const char *name, size_t namesz)
122 {
123 size_t i;
124
125 assert(name);
126
127 for (i = 0; i < ARRAY_SIZE(infos); i++) {
128 const char *cn = infos[i].name;
129 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
130 return i;
131 }
132 warnx(_("unknown column: %s"), name);
133 return -1;
134 }
135
136 static int get_column_id(size_t num)
137 {
138 assert(num < ncolumns);
139 assert(columns[num] < (int)ARRAY_SIZE(infos));
140 return columns[num];
141 }
142
143 static const struct colinfo *get_column_info(int num)
144 {
145 return &infos[get_column_id(num)];
146 }
147
148
149 static void init_output(struct wipe_control *ctl)
150 {
151 struct libscols_table *tb;
152 size_t i;
153
154 scols_init_debug(0);
155 tb = scols_new_table();
156 if (!tb)
157 err(EXIT_FAILURE, _("failed to allocate output table"));
158
159 if (ctl->json) {
160 scols_table_enable_json(tb, 1);
161 scols_table_set_name(tb, "signatures");
162 }
163 scols_table_enable_noheadings(tb, ctl->no_headings);
164
165 if (ctl->parsable) {
166 scols_table_enable_raw(tb, 1);
167 scols_table_set_column_separator(tb, ",");
168 }
169
170 for (i = 0; i < ncolumns; i++) {
171 const struct colinfo *col = get_column_info(i);
172 struct libscols_column *cl;
173
174 cl = scols_table_new_column(tb, col->name, col->whint,
175 col->flags);
176 if (!cl)
177 err(EXIT_FAILURE,
178 _("failed to initialize output column"));
179 if (ctl->json) {
180 int id = get_column_id(i);
181
182 if (id == COL_LEN)
183 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
184 }
185 }
186 ctl->outtab = tb;
187 }
188
189 static void finalize_output(struct wipe_control *ctl)
190 {
191 if (ctl->parsable && !ctl->no_headings
192 && !scols_table_is_empty(ctl->outtab)) {
193 struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
194 struct libscols_column *cl;
195 int i = 0;
196
197 if (!itr)
198 err_oom();
199
200 fputs("# ", stdout);
201 while (scols_table_next_column(ctl->outtab, itr, &cl) == 0) {
202 struct libscols_cell *hdr = scols_column_get_header(cl);
203 const char *name = scols_cell_get_data(hdr);
204
205 if (i)
206 fputc(',', stdout);
207 fputs(name, stdout);
208 i++;
209 }
210 fputc('\n', stdout);
211 scols_free_iter(itr);
212 }
213 scols_print_table(ctl->outtab);
214 scols_unref_table(ctl->outtab);
215 }
216
217 static void fill_table_row(struct wipe_control *ctl, struct wipe_desc *wp)
218 {
219 static struct libscols_line *ln;
220 size_t i;
221
222 ln = scols_table_new_line(ctl->outtab, NULL);
223 if (!ln)
224 errx(EXIT_FAILURE, _("failed to allocate output line"));
225
226 for (i = 0; i < ncolumns; i++) {
227 char *str = NULL;
228
229 switch (get_column_id(i)) {
230 case COL_UUID:
231 if (wp->uuid)
232 str = xstrdup(wp->uuid);
233 break;
234 case COL_LABEL:
235 if (wp->label)
236 str = xstrdup(wp->label);
237 break;
238 case COL_OFFSET:
239 xasprintf(&str, "0x%jx", (intmax_t)wp->offset);
240 break;
241 case COL_LEN:
242 xasprintf(&str, "%zu", wp->len);
243 break;
244 case COL_USAGE:
245 if (wp->usage)
246 str = xstrdup(wp->usage);
247 break;
248 case COL_TYPE:
249 if (wp->type)
250 str = xstrdup(wp->type);
251 break;
252 case COL_DEVICE:
253 if (ctl->devname) {
254 char *dev = xstrdup(ctl->devname);
255 str = xstrdup(basename(dev));
256 free(dev);
257 }
258 break;
259 default:
260 abort();
261 }
262
263 if (str && scols_line_refer_data(ln, i, str))
264 errx(EXIT_FAILURE, _("failed to add output data"));
265 }
266 }
267
268 static void add_to_output(struct wipe_control *ctl, struct wipe_desc *wp)
269 {
270 for (/*nothing*/; wp; wp = wp->next)
271 fill_table_row(ctl, wp);
272 }
273
274 /* Allocates a new wipe_desc and add to the wp0 if not NULL */
275 static struct wipe_desc *add_offset(struct wipe_desc **wp0, loff_t offset)
276 {
277 struct wipe_desc *wp, *last = NULL;
278
279 if (wp0) {
280 /* check if already exists */
281 for (wp = *wp0; wp; wp = wp->next) {
282 if (wp->offset == offset)
283 return wp;
284 last = wp;
285 }
286 }
287
288 wp = xcalloc(1, sizeof(struct wipe_desc));
289 wp->offset = offset;
290 wp->next = NULL;
291
292 if (last)
293 last->next = wp;
294 if (wp0 && !*wp0)
295 *wp0 = wp;
296 return wp;
297 }
298
299 /* Read data from libblkid and if detected type pass -t and -o filters than:
300 * - allocates a new wipe_desc
301 * - add the new wipe_desc to wp0 list (if not NULL)
302 *
303 * The function always returns offset and len if libblkid detected something.
304 */
305 static struct wipe_desc *get_desc_for_probe(struct wipe_control *ctl,
306 struct wipe_desc **wp0,
307 blkid_probe pr,
308 loff_t *offset,
309 size_t *len)
310 {
311 const char *off, *type, *mag, *p, *use = NULL;
312 struct wipe_desc *wp;
313 int rc, ispt = 0;
314
315 *len = 0;
316
317 /* superblocks */
318 if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) {
319 rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
320 if (!rc)
321 rc = blkid_probe_lookup_value(pr, "SBMAGIC", &mag, len);
322 if (rc)
323 return NULL;
324
325 /* partitions */
326 } else if (blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL) == 0) {
327 rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
328 if (!rc)
329 rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, len);
330 if (rc)
331 return NULL;
332 use = N_("partition-table");
333 ispt = 1;
334 } else
335 return NULL;
336
337 *offset = strtoll(off, NULL, 10);
338
339 /* Filter out by -t <type> */
340 if (ctl->type_pattern && !match_fstype(type, ctl->type_pattern))
341 return NULL;
342
343 /* Filter out by -o <offset> */
344 if (ctl->offsets) {
345 struct wipe_desc *w = NULL;
346
347 for (w = ctl->offsets; w; w = w->next) {
348 if (w->offset == *offset)
349 break;
350 }
351 if (!w)
352 return NULL;
353
354 w->on_disk = 1; /* mark as "found" */
355 }
356
357 wp = add_offset(wp0, *offset);
358 if (!wp)
359 return NULL;
360
361 if (use || blkid_probe_lookup_value(pr, "USAGE", &use, NULL) == 0)
362 wp->usage = xstrdup(use);
363
364 wp->type = xstrdup(type);
365 wp->on_disk = 1;
366 wp->is_parttable = ispt ? 1 : 0;
367
368 wp->magic = xmalloc(*len);
369 memcpy(wp->magic, mag, *len);
370 wp->len = *len;
371
372 if (blkid_probe_lookup_value(pr, "LABEL", &p, NULL) == 0)
373 wp->label = xstrdup(p);
374
375 if (blkid_probe_lookup_value(pr, "UUID", &p, NULL) == 0)
376 wp->uuid = xstrdup(p);
377
378 return wp;
379 }
380
381 static blkid_probe
382 new_probe(const char *devname, int mode)
383 {
384 blkid_probe pr = NULL;
385
386 if (!devname)
387 return NULL;
388
389 if (mode) {
390 int fd = open(devname, mode | O_NONBLOCK);
391 if (fd < 0)
392 goto error;
393
394 pr = blkid_new_probe();
395 if (!pr || blkid_probe_set_device(pr, fd, 0, 0) != 0) {
396 close(fd);
397 goto error;
398 }
399 } else
400 pr = blkid_new_probe_from_filename(devname);
401
402 if (!pr)
403 goto error;
404
405 blkid_probe_enable_superblocks(pr, 1);
406 blkid_probe_set_superblocks_flags(pr,
407 BLKID_SUBLKS_MAGIC | /* return magic string and offset */
408 BLKID_SUBLKS_TYPE | /* return superblock type */
409 BLKID_SUBLKS_USAGE | /* return USAGE= */
410 BLKID_SUBLKS_LABEL | /* return LABEL= */
411 BLKID_SUBLKS_UUID | /* return UUID= */
412 BLKID_SUBLKS_BADCSUM); /* accept bad checksums */
413
414 blkid_probe_enable_partitions(pr, 1);
415 blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC |
416 BLKID_PARTS_FORCE_GPT);
417 return pr;
418 error:
419 blkid_free_probe(pr);
420 err(EXIT_FAILURE, _("error: %s: probing initialization failed"), devname);
421 }
422
423 static struct wipe_desc *read_offsets(struct wipe_control *ctl)
424 {
425 blkid_probe pr = new_probe(ctl->devname, 0);
426 struct wipe_desc *wp0 = NULL;
427
428 if (!pr)
429 return NULL;
430
431 while (blkid_do_probe(pr) == 0) {
432 size_t len = 0;
433 loff_t offset = 0;
434
435 /* add a new offset to wp0 */
436 get_desc_for_probe(ctl, &wp0, pr, &offset, &len);
437
438 /* hide last detected signature and scan again */
439 if (len) {
440 blkid_probe_hide_range(pr, offset, len);
441 blkid_probe_step_back(pr);
442 }
443 }
444
445 blkid_free_probe(pr);
446 return wp0;
447 }
448
449 static void free_wipe(struct wipe_desc *wp)
450 {
451 while (wp) {
452 struct wipe_desc *next = wp->next;
453
454 free(wp->usage);
455 free(wp->type);
456 free(wp->magic);
457 free(wp->label);
458 free(wp->uuid);
459 free(wp);
460
461 wp = next;
462 }
463 }
464
465 static void do_wipe_real(struct wipe_control *ctl, blkid_probe pr,
466 struct wipe_desc *w)
467 {
468 size_t i;
469
470 if (blkid_do_wipe(pr, ctl->noact) != 0)
471 err(EXIT_FAILURE, _("%s: failed to erase %s magic string at offset 0x%08jx"),
472 ctl->devname, w->type, (intmax_t)w->offset);
473
474 if (ctl->quiet)
475 return;
476
477 printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
478 "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
479 w->len),
480 ctl->devname, w->len, (intmax_t)w->offset, w->type);
481
482 for (i = 0; i < w->len; i++) {
483 printf("%02x", w->magic[i]);
484 if (i + 1 < w->len)
485 fputc(' ', stdout);
486 }
487 putchar('\n');
488 }
489
490 static void do_backup(struct wipe_desc *wp, const char *base)
491 {
492 char *fname = NULL;
493 int fd;
494
495 xasprintf(&fname, "%s0x%08jx.bak", base, (intmax_t)wp->offset);
496
497 fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
498 if (fd < 0)
499 goto err;
500 if (write_all(fd, wp->magic, wp->len) != 0)
501 goto err;
502 close(fd);
503 free(fname);
504 return;
505 err:
506 err(EXIT_FAILURE, _("%s: failed to create a signature backup"), fname);
507 }
508
509 #ifdef BLKRRPART
510 static void rereadpt(int fd, const char *devname)
511 {
512 struct stat st;
513 int try = 0;
514
515 if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
516 return;
517
518 do {
519 /*
520 * Unfortunately, it's pretty common that the first re-read
521 * without delay is uncuccesful. The reason is probably kernel
522 * and/or udevd. Let's wait a moment and try more attempts.
523 */
524 xusleep(25000);
525
526 errno = 0;
527 ioctl(fd, BLKRRPART);
528 if (errno != EBUSY)
529 break;
530 } while (try++ < 4);
531
532 printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname);
533 }
534 #endif
535
536 static int do_wipe(struct wipe_control *ctl)
537 {
538 int mode = O_RDWR, reread = 0, need_force = 0;
539 blkid_probe pr;
540 char *backup = NULL;
541 struct wipe_desc *w;
542
543 if (!ctl->force)
544 mode |= O_EXCL;
545
546 pr = new_probe(ctl->devname, mode);
547 if (!pr)
548 return -errno;
549
550 if (blkdev_lock(blkid_probe_get_fd(pr),
551 ctl->devname, ctl->lockmode) != 0) {
552 blkid_free_probe(pr);
553 return -1;
554 }
555
556 if (ctl->backup) {
557 const char *home = getenv ("HOME");
558 char *tmp = xstrdup(ctl->devname);
559
560 if (!home)
561 errx(EXIT_FAILURE, _("failed to create a signature backup, $HOME undefined"));
562 xasprintf (&backup, "%s/wipefs-%s-", home, basename(tmp));
563 free(tmp);
564 }
565
566 while (blkid_do_probe(pr) == 0) {
567 int wiped = 0;
568 size_t len = 0;
569 loff_t offset = 0;
570 struct wipe_desc *wp;
571
572 wp = get_desc_for_probe(ctl, NULL, pr, &offset, &len);
573 if (!wp)
574 goto done;
575
576 if (!ctl->force
577 && wp->is_parttable
578 && !blkid_probe_is_wholedisk(pr)) {
579 warnx(_("%s: ignoring nested \"%s\" partition table "
580 "on non-whole disk device"), ctl->devname, wp->type);
581 need_force = 1;
582 goto done;
583 }
584
585 if (backup)
586 do_backup(wp, backup);
587 do_wipe_real(ctl, pr, wp);
588 if (wp->is_parttable)
589 reread = 1;
590 wiped = 1;
591 done:
592 if (!wiped && len) {
593 /* if the offset has not been wiped (probably because
594 * filtered out by -t or -o) we need to hide it for
595 * libblkid to try another magic string for the same
596 * superblock, otherwise libblkid will continue with
597 * another superblock. Don't forget that the same
598 * superblock could be detected by more magic strings
599 * */
600 blkid_probe_hide_range(pr, offset, len);
601 blkid_probe_step_back(pr);
602 }
603 free_wipe(wp);
604 }
605
606 for (w = ctl->offsets; w; w = w->next) {
607 if (!w->on_disk && !ctl->quiet)
608 warnx(_("%s: offset 0x%jx not found"),
609 ctl->devname, (uintmax_t)w->offset);
610 }
611
612 if (need_force)
613 warnx(_("Use the --force option to force erase."));
614
615 fsync(blkid_probe_get_fd(pr));
616
617 #ifdef BLKRRPART
618 if (reread && (mode & O_EXCL)) {
619 if (ctl->ndevs > 1) {
620 /*
621 * We're going to probe more device, let's postpone
622 * re-read PT ioctl until all is erased to avoid
623 * situation we erase PT on /dev/sda before /dev/sdaN
624 * devices are processed.
625 */
626 if (!ctl->reread)
627 ctl->reread = xcalloc(ctl->ndevs, sizeof(char *));
628
629 ctl->reread[ctl->nrereads++] = ctl->devname;
630 } else
631 rereadpt(blkid_probe_get_fd(pr), ctl->devname);
632 }
633 #endif
634
635 close(blkid_probe_get_fd(pr));
636 blkid_free_probe(pr);
637 free(backup);
638 return 0;
639 }
640
641
642 static void __attribute__((__noreturn__))
643 usage(void)
644 {
645 size_t i;
646
647 fputs(USAGE_HEADER, stdout);
648 printf(_(" %s [options] <device>\n"), program_invocation_short_name);
649
650 fputs(USAGE_SEPARATOR, stdout);
651 puts(_("Wipe signatures from a device."));
652
653 fputs(USAGE_OPTIONS, stdout);
654 puts(_(" -a, --all wipe all magic strings (BE CAREFUL!)"));
655 puts(_(" -b, --backup create a signature backup in $HOME"));
656 puts(_(" -f, --force force erasure"));
657 puts(_(" -i, --noheadings don't print headings"));
658 puts(_(" -J, --json use JSON output format"));
659 puts(_(" -n, --no-act do everything except the actual write() call"));
660 puts(_(" -o, --offset <num> offset to erase, in bytes"));
661 puts(_(" -O, --output <list> COLUMNS to display (see below)"));
662 puts(_(" -p, --parsable print out in parsable instead of printable format"));
663 puts(_(" -q, --quiet suppress output messages"));
664 puts(_(" -t, --types <list> limit the set of filesystem, RAIDs or partition tables"));
665 printf(
666 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
667
668 printf(USAGE_HELP_OPTIONS(21));
669
670 fputs(USAGE_ARGUMENTS, stdout);
671 printf(USAGE_ARG_SIZE(_("<num>")));
672
673 fputs(USAGE_COLUMNS, stdout);
674 for (i = 0; i < ARRAY_SIZE(infos); i++)
675 fprintf(stdout, " %8s %s\n", infos[i].name, _(infos[i].help));
676
677 printf(USAGE_MAN_TAIL("wipefs(8)"));
678 exit(EXIT_SUCCESS);
679 }
680
681
682 int
683 main(int argc, char **argv)
684 {
685 struct wipe_control ctl = { .devname = NULL };
686 int c;
687 size_t i;
688 char *outarg = NULL;
689 enum {
690 OPT_LOCK = CHAR_MAX + 1,
691 };
692 static const struct option longopts[] = {
693 { "all", no_argument, NULL, 'a' },
694 { "backup", no_argument, NULL, 'b' },
695 { "force", no_argument, NULL, 'f' },
696 { "help", no_argument, NULL, 'h' },
697 { "lock", optional_argument, NULL, OPT_LOCK },
698 { "no-act", no_argument, NULL, 'n' },
699 { "offset", required_argument, NULL, 'o' },
700 { "parsable", no_argument, NULL, 'p' },
701 { "quiet", no_argument, NULL, 'q' },
702 { "types", required_argument, NULL, 't' },
703 { "version", no_argument, NULL, 'V' },
704 { "json", no_argument, NULL, 'J'},
705 { "noheadings",no_argument, NULL, 'i'},
706 { "output", required_argument, NULL, 'O'},
707 { NULL, 0, NULL, 0 }
708 };
709
710 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
711 { 'O','a','o' },
712 { 0 }
713 };
714 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
715
716 setlocale(LC_ALL, "");
717 bindtextdomain(PACKAGE, LOCALEDIR);
718 textdomain(PACKAGE);
719 close_stdout_atexit();
720
721 while ((c = getopt_long(argc, argv, "abfhiJnO:o:pqt:V", longopts, NULL)) != -1) {
722
723 err_exclusive_options(c, longopts, excl, excl_st);
724
725 switch(c) {
726 case 'a':
727 ctl.all = 1;
728 break;
729 case 'b':
730 ctl.backup = 1;
731 break;
732 case 'f':
733 ctl.force = 1;
734 break;
735 case 'J':
736 ctl.json = 1;
737 break;
738 case 'i':
739 ctl.no_headings = 1;
740 break;
741 case 'O':
742 outarg = optarg;
743 break;
744 case 'n':
745 ctl.noact = 1;
746 break;
747 case 'o':
748 add_offset(&ctl.offsets, strtosize_or_err(optarg,
749 _("invalid offset argument")));
750 break;
751 case 'p':
752 ctl.parsable = 1;
753 ctl.no_headings = 1;
754 break;
755 case 'q':
756 ctl.quiet = 1;
757 break;
758 case 't':
759 ctl.type_pattern = optarg;
760 break;
761 case OPT_LOCK:
762 ctl.lockmode = "1";
763 if (optarg) {
764 if (*optarg == '=')
765 optarg++;
766 ctl.lockmode = optarg;
767 }
768 break;
769 case 'h':
770 usage();
771 case 'V':
772 print_version(EXIT_SUCCESS);
773 default:
774 errtryhelp(EXIT_FAILURE);
775 }
776 }
777
778 if (optind == argc) {
779 warnx(_("no device specified"));
780 errtryhelp(EXIT_FAILURE);
781
782 }
783
784 if (ctl.backup && !(ctl.all || ctl.offsets))
785 warnx(_("The --backup option is meaningless in this context"));
786
787 if (!ctl.all && !ctl.offsets) {
788 /*
789 * Print only
790 */
791 if (ctl.parsable) {
792 /* keep it backward compatible */
793 columns[ncolumns++] = COL_OFFSET;
794 columns[ncolumns++] = COL_UUID;
795 columns[ncolumns++] = COL_LABEL;
796 columns[ncolumns++] = COL_TYPE;
797 } else {
798 /* default, may be modified by -O <list> */
799 columns[ncolumns++] = COL_DEVICE;
800 columns[ncolumns++] = COL_OFFSET;
801 columns[ncolumns++] = COL_TYPE;
802 columns[ncolumns++] = COL_UUID;
803 columns[ncolumns++] = COL_LABEL;
804 }
805
806 if (outarg
807 && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
808 &ncolumns, column_name_to_id) < 0)
809 return EXIT_FAILURE;
810
811 init_output(&ctl);
812
813 while (optind < argc) {
814 struct wipe_desc *wp;
815
816 ctl.devname = argv[optind++];
817 wp = read_offsets(&ctl);
818 if (wp)
819 add_to_output(&ctl, wp);
820 free_wipe(wp);
821 }
822 finalize_output(&ctl);
823 } else {
824 /*
825 * Erase
826 */
827 ctl.ndevs = argc - optind;
828
829 while (optind < argc) {
830 ctl.devname = argv[optind++];
831 do_wipe(&ctl);
832 ctl.ndevs--;
833 }
834
835 #ifdef BLKRRPART
836 /* Re-read partition tables on whole-disk devices. This is
837 * postponed until all is done to avoid conflicts.
838 */
839 for (i = 0; i < ctl.nrereads; i++) {
840 char *devname = ctl.reread[i];
841 int fd = open(devname, O_RDONLY);
842
843 if (fd >= 0) {
844 rereadpt(fd, devname);
845 close(fd);
846 }
847 }
848 free(ctl.reread);
849 #endif
850 }
851 return EXIT_SUCCESS;
852 }