2 * wipefs - utility to wipe filesystems from device
4 * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
5 * Written by Karel Zak <kzak@redhat.com>
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.
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.
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.
22 #include <sys/types.h>
35 #include <libsmartcols.h>
43 #include "closestream.h"
48 loff_t offset
; /* magic string offset */
49 size_t len
; /* length of magic string */
50 unsigned char *magic
; /* magic string */
52 char *usage
; /* raid, filesystem, ... */
53 char *type
; /* FS type */
54 char *label
; /* FS label */
55 char *uuid
; /* FS uuid */
57 struct wipe_desc
*next
;
59 unsigned int on_disk
: 1,
66 const char *type_pattern
; /* -t <pattern> */
69 struct libscols_table
*outtab
;
70 struct wipe_desc
*offsets
; /* -o <offset> -o <offset> ... */
72 size_t ndevs
; /* number of devices to probe */
74 char **reread
; /* devices to BLKRRPART */
75 size_t nrereads
; /* size of reread */
77 unsigned int noact
: 1,
101 const char *name
; /* header */
102 double whint
; /* width hint (N < 1 is in percent of termwidth) */
103 int flags
; /* SCOLS_FL_* */
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")}
118 static int columns
[ARRAY_SIZE(infos
) * 2];
119 static size_t ncolumns
;
121 static int column_name_to_id(const char *name
, size_t namesz
)
127 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
128 const char *cn
= infos
[i
].name
;
129 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
132 warnx(_("unknown column: %s"), name
);
136 static int get_column_id(size_t num
)
138 assert(num
< ncolumns
);
139 assert(columns
[num
] < (int)ARRAY_SIZE(infos
));
143 static const struct colinfo
*get_column_info(int num
)
145 return &infos
[get_column_id(num
)];
149 static void init_output(struct wipe_control
*ctl
)
151 struct libscols_table
*tb
;
155 tb
= scols_new_table();
157 err(EXIT_FAILURE
, _("failed to allocate output table"));
160 scols_table_enable_json(tb
, 1);
161 scols_table_set_name(tb
, "signatures");
163 scols_table_enable_noheadings(tb
, ctl
->no_headings
);
166 scols_table_enable_raw(tb
, 1);
167 scols_table_set_column_separator(tb
, ",");
170 for (i
= 0; i
< ncolumns
; i
++) {
171 const struct colinfo
*col
= get_column_info(i
);
172 struct libscols_column
*cl
;
174 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
,
178 _("failed to initialize output column"));
180 int id
= get_column_id(i
);
183 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
189 static void finalize_output(struct wipe_control
*ctl
)
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
;
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
);
211 scols_free_iter(itr
);
213 scols_print_table(ctl
->outtab
);
214 scols_unref_table(ctl
->outtab
);
217 static void fill_table_row(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
219 static struct libscols_line
*ln
;
222 ln
= scols_table_new_line(ctl
->outtab
, NULL
);
224 errx(EXIT_FAILURE
, _("failed to allocate output line"));
226 for (i
= 0; i
< ncolumns
; i
++) {
229 switch (get_column_id(i
)) {
232 str
= xstrdup(wp
->uuid
);
236 str
= xstrdup(wp
->label
);
239 xasprintf(&str
, "0x%jx", (intmax_t)wp
->offset
);
242 xasprintf(&str
, "%zu", wp
->len
);
246 str
= xstrdup(wp
->usage
);
250 str
= xstrdup(wp
->type
);
254 char *dev
= xstrdup(ctl
->devname
);
255 str
= xstrdup(basename(dev
));
263 if (str
&& scols_line_refer_data(ln
, i
, str
))
264 errx(EXIT_FAILURE
, _("failed to add output data"));
268 static void add_to_output(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
270 for (/*nothing*/; wp
; wp
= wp
->next
)
271 fill_table_row(ctl
, wp
);
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
)
277 struct wipe_desc
*wp
, *last
= NULL
;
280 /* check if already exists */
281 for (wp
= *wp0
; wp
; wp
= wp
->next
) {
282 if (wp
->offset
== offset
)
288 wp
= xcalloc(1, sizeof(struct wipe_desc
));
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)
303 * The function always returns offset and len if libblkid detected something.
305 static struct wipe_desc
*get_desc_for_probe(struct wipe_control
*ctl
,
306 struct wipe_desc
**wp0
,
311 const char *off
, *type
, *mag
, *p
, *use
= NULL
;
312 struct wipe_desc
*wp
;
318 if (blkid_probe_lookup_value(pr
, "TYPE", &type
, NULL
) == 0) {
319 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC_OFFSET", &off
, NULL
);
321 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC", &mag
, len
);
326 } else if (blkid_probe_lookup_value(pr
, "PTTYPE", &type
, NULL
) == 0) {
327 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC_OFFSET", &off
, NULL
);
329 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC", &mag
, len
);
332 use
= N_("partition-table");
337 *offset
= strtoll(off
, NULL
, 10);
339 /* Filter out by -t <type> */
340 if (ctl
->type_pattern
&& !match_fstype(type
, ctl
->type_pattern
))
343 /* Filter out by -o <offset> */
345 struct wipe_desc
*w
= NULL
;
347 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
348 if (w
->offset
== *offset
)
354 w
->on_disk
= 1; /* mark as "found" */
357 wp
= add_offset(wp0
, *offset
);
361 if (use
|| blkid_probe_lookup_value(pr
, "USAGE", &use
, NULL
) == 0)
362 wp
->usage
= xstrdup(use
);
364 wp
->type
= xstrdup(type
);
366 wp
->is_parttable
= ispt
? 1 : 0;
368 wp
->magic
= xmalloc(*len
);
369 memcpy(wp
->magic
, mag
, *len
);
372 if (blkid_probe_lookup_value(pr
, "LABEL", &p
, NULL
) == 0)
373 wp
->label
= xstrdup(p
);
375 if (blkid_probe_lookup_value(pr
, "UUID", &p
, NULL
) == 0)
376 wp
->uuid
= xstrdup(p
);
382 new_probe(const char *devname
, int mode
)
384 blkid_probe pr
= NULL
;
390 int fd
= open(devname
, mode
| O_NONBLOCK
);
394 pr
= blkid_new_probe();
395 if (!pr
|| blkid_probe_set_device(pr
, fd
, 0, 0) != 0) {
400 pr
= blkid_new_probe_from_filename(devname
);
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 */
414 blkid_probe_enable_partitions(pr
, 1);
415 blkid_probe_set_partitions_flags(pr
, BLKID_PARTS_MAGIC
|
416 BLKID_PARTS_FORCE_GPT
);
419 blkid_free_probe(pr
);
420 err(EXIT_FAILURE
, _("error: %s: probing initialization failed"), devname
);
423 static struct wipe_desc
*read_offsets(struct wipe_control
*ctl
)
425 blkid_probe pr
= new_probe(ctl
->devname
, 0);
426 struct wipe_desc
*wp0
= NULL
;
431 while (blkid_do_probe(pr
) == 0) {
435 /* add a new offset to wp0 */
436 get_desc_for_probe(ctl
, &wp0
, pr
, &offset
, &len
);
438 /* hide last detected signature and scan again */
440 blkid_probe_hide_range(pr
, offset
, len
);
441 blkid_probe_step_back(pr
);
445 blkid_free_probe(pr
);
449 static void free_wipe(struct wipe_desc
*wp
)
452 struct wipe_desc
*next
= wp
->next
;
465 static void do_wipe_real(struct wipe_control
*ctl
, blkid_probe pr
,
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
);
477 printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
478 "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
480 ctl
->devname
, w
->len
, (intmax_t)w
->offset
, w
->type
);
482 for (i
= 0; i
< w
->len
; i
++) {
483 printf("%02x", w
->magic
[i
]);
490 static void do_backup(struct wipe_desc
*wp
, const char *base
)
495 xasprintf(&fname
, "%s0x%08jx.bak", base
, (intmax_t)wp
->offset
);
497 fd
= open(fname
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
500 if (write_all(fd
, wp
->magic
, wp
->len
) != 0)
506 err(EXIT_FAILURE
, _("%s: failed to create a signature backup"), fname
);
510 static void rereadpt(int fd
, const char *devname
)
515 if (fstat(fd
, &st
) || !S_ISBLK(st
.st_mode
))
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.
527 ioctl(fd
, BLKRRPART
);
532 printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname
);
536 static int do_wipe(struct wipe_control
*ctl
)
538 int mode
= O_RDWR
, reread
= 0, need_force
= 0;
546 pr
= new_probe(ctl
->devname
, mode
);
550 if (blkdev_lock(blkid_probe_get_fd(pr
),
551 ctl
->devname
, ctl
->lockmode
) != 0) {
552 blkid_free_probe(pr
);
557 const char *home
= getenv ("HOME");
558 char *tmp
= xstrdup(ctl
->devname
);
561 errx(EXIT_FAILURE
, _("failed to create a signature backup, $HOME undefined"));
562 xasprintf (&backup
, "%s/wipefs-%s-", home
, basename(tmp
));
566 while (blkid_do_probe(pr
) == 0) {
570 struct wipe_desc
*wp
;
572 wp
= get_desc_for_probe(ctl
, NULL
, pr
, &offset
, &len
);
578 && !blkid_probe_is_wholedisk(pr
)) {
579 warnx(_("%s: ignoring nested \"%s\" partition table "
580 "on non-whole disk device"), ctl
->devname
, wp
->type
);
586 do_backup(wp
, backup
);
587 do_wipe_real(ctl
, pr
, wp
);
588 if (wp
->is_parttable
)
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
600 blkid_probe_hide_range(pr
, offset
, len
);
601 blkid_probe_step_back(pr
);
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
);
613 warnx(_("Use the --force option to force erase."));
615 fsync(blkid_probe_get_fd(pr
));
618 if (reread
&& (mode
& O_EXCL
)) {
619 if (ctl
->ndevs
> 1) {
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.
627 ctl
->reread
= xcalloc(ctl
->ndevs
, sizeof(char *));
629 ctl
->reread
[ctl
->nrereads
++] = ctl
->devname
;
631 rereadpt(blkid_probe_get_fd(pr
), ctl
->devname
);
635 close(blkid_probe_get_fd(pr
));
636 blkid_free_probe(pr
);
642 static void __attribute__((__noreturn__
))
647 fputs(USAGE_HEADER
, stdout
);
648 printf(_(" %s [options] <device>\n"), program_invocation_short_name
);
650 fputs(USAGE_SEPARATOR
, stdout
);
651 puts(_("Wipe signatures from a device."));
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"));
666 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
668 printf(USAGE_HELP_OPTIONS(21));
670 fputs(USAGE_ARGUMENTS
, stdout
);
671 printf(USAGE_ARG_SIZE(_("<num>")));
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
));
677 printf(USAGE_MAN_TAIL("wipefs(8)"));
683 main(int argc
, char **argv
)
685 struct wipe_control ctl
= { .devname
= NULL
};
690 OPT_LOCK
= CHAR_MAX
+ 1,
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'},
710 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
714 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
716 setlocale(LC_ALL
, "");
717 bindtextdomain(PACKAGE
, LOCALEDIR
);
719 close_stdout_atexit();
721 while ((c
= getopt_long(argc
, argv
, "abfhiJnO:o:pqt:V", longopts
, NULL
)) != -1) {
723 err_exclusive_options(c
, longopts
, excl
, excl_st
);
748 add_offset(&ctl
.offsets
, strtosize_or_err(optarg
,
749 _("invalid offset argument")));
759 ctl
.type_pattern
= optarg
;
766 ctl
.lockmode
= optarg
;
772 print_version(EXIT_SUCCESS
);
774 errtryhelp(EXIT_FAILURE
);
778 if (optind
== argc
) {
779 warnx(_("no device specified"));
780 errtryhelp(EXIT_FAILURE
);
784 if (ctl
.backup
&& !(ctl
.all
|| ctl
.offsets
))
785 warnx(_("The --backup option is meaningless in this context"));
787 if (!ctl
.all
&& !ctl
.offsets
) {
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
;
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
;
807 && string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
808 &ncolumns
, column_name_to_id
) < 0)
813 while (optind
< argc
) {
814 struct wipe_desc
*wp
;
816 ctl
.devname
= argv
[optind
++];
817 wp
= read_offsets(&ctl
);
819 add_to_output(&ctl
, wp
);
822 finalize_output(&ctl
);
827 ctl
.ndevs
= argc
- optind
;
829 while (optind
< argc
) {
830 ctl
.devname
= argv
[optind
++];
836 /* Re-read partition tables on whole-disk devices. This is
837 * postponed until all is done to avoid conflicts.
839 for (i
= 0; i
< ctl
.nrereads
; i
++) {
840 char *devname
= ctl
.reread
[i
];
841 int fd
= open(devname
, O_RDONLY
);
844 rereadpt(fd
, devname
);