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> */
68 struct libscols_table
*outtab
;
69 struct wipe_desc
*offsets
; /* -o <offset> -o <offset> ... */
71 size_t ndevs
; /* number of devices to probe */
73 char **reread
; /* devices to BLKRRPART */
74 size_t nrereads
; /* size of reread */
76 unsigned int noact
: 1,
100 const char *name
; /* header */
101 double whint
; /* width hint (N < 1 is in percent of termwidth) */
102 int flags
; /* SCOLS_FL_* */
106 /* columns descriptions */
107 static const struct colinfo infos
[] = {
108 [COL_UUID
] = {"UUID", 4, 0, N_("partition/filesystem UUID")},
109 [COL_LABEL
] = {"LABEL", 5, 0, N_("filesystem LABEL")},
110 [COL_LEN
] = {"LENGTH", 6, 0, N_("magic string length")},
111 [COL_TYPE
] = {"TYPE", 4, 0, N_("superblok type")},
112 [COL_OFFSET
] = {"OFFSET", 5, 0, N_("magic string offset")},
113 [COL_USAGE
] = {"USAGE", 5, 0, N_("type description")},
114 [COL_DEVICE
] = {"DEVICE", 5, 0, N_("block device name")}
117 static int columns
[ARRAY_SIZE(infos
) * 2];
118 static size_t ncolumns
;
120 static int column_name_to_id(const char *name
, size_t namesz
)
126 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
127 const char *cn
= infos
[i
].name
;
128 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
131 warnx(_("unknown column: %s"), name
);
135 static int get_column_id(size_t num
)
137 assert(num
< ncolumns
);
138 assert(columns
[num
] < (int)ARRAY_SIZE(infos
));
142 static const struct colinfo
*get_column_info(int num
)
144 return &infos
[get_column_id(num
)];
148 static void init_output(struct wipe_control
*ctl
)
150 struct libscols_table
*tb
;
154 tb
= scols_new_table();
156 err(EXIT_FAILURE
, _("failed to allocate output table"));
159 scols_table_enable_json(tb
, 1);
160 scols_table_set_name(tb
, "signatures");
162 scols_table_enable_noheadings(tb
, ctl
->no_headings
);
165 scols_table_enable_raw(tb
, 1);
166 scols_table_set_column_separator(tb
, ",");
169 for (i
= 0; i
< ncolumns
; i
++) {
170 const struct colinfo
*col
= get_column_info(i
);
171 struct libscols_column
*cl
;
173 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
,
177 _("failed to initialize output column"));
179 int id
= get_column_id(i
);
182 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
188 static void finalize_output(struct wipe_control
*ctl
)
190 if (ctl
->parsable
&& !ctl
->no_headings
191 && !scols_table_is_empty(ctl
->outtab
)) {
192 struct libscols_iter
*itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
193 struct libscols_column
*cl
;
200 while (scols_table_next_column(ctl
->outtab
, itr
, &cl
) == 0) {
201 struct libscols_cell
*hdr
= scols_column_get_header(cl
);
202 const char *name
= scols_cell_get_data(hdr
);
210 scols_free_iter(itr
);
212 scols_print_table(ctl
->outtab
);
213 scols_unref_table(ctl
->outtab
);
216 static void fill_table_row(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
218 static struct libscols_line
*ln
;
221 ln
= scols_table_new_line(ctl
->outtab
, NULL
);
223 errx(EXIT_FAILURE
, _("failed to allocate output line"));
225 for (i
= 0; i
< ncolumns
; i
++) {
228 switch (get_column_id(i
)) {
231 str
= xstrdup(wp
->uuid
);
235 str
= xstrdup(wp
->label
);
238 xasprintf(&str
, "0x%jx", (intmax_t)wp
->offset
);
241 xasprintf(&str
, "%zu", wp
->len
);
245 str
= xstrdup(wp
->usage
);
249 str
= xstrdup(wp
->type
);
253 char *dev
= xstrdup(ctl
->devname
);
254 str
= xstrdup(basename(dev
));
262 if (str
&& scols_line_refer_data(ln
, i
, str
))
263 errx(EXIT_FAILURE
, _("failed to add output data"));
267 static void add_to_output(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
269 for (/*nothing*/; wp
; wp
= wp
->next
)
270 fill_table_row(ctl
, wp
);
273 /* Allocates a new wipe_desc and add to the wp0 if not NULL */
274 static struct wipe_desc
*add_offset(struct wipe_desc
**wp0
, loff_t offset
)
276 struct wipe_desc
*wp
, *last
= NULL
;
279 /* check if already exists */
280 for (wp
= *wp0
; wp
; wp
= wp
->next
) {
281 if (wp
->offset
== offset
)
287 wp
= xcalloc(1, sizeof(struct wipe_desc
));
298 /* Read data from libblkid and if detected type pass -t and -o filters than:
299 * - allocates a new wipe_desc
300 * - add the new wipe_desc to wp0 list (if not NULL)
302 * The function always returns offset and len if libblkid detected something.
304 static struct wipe_desc
*get_desc_for_probe(struct wipe_control
*ctl
,
305 struct wipe_desc
**wp0
,
310 const char *off
, *type
, *mag
, *p
, *usage
= NULL
;
311 struct wipe_desc
*wp
;
317 if (blkid_probe_lookup_value(pr
, "TYPE", &type
, NULL
) == 0) {
318 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC_OFFSET", &off
, NULL
);
320 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC", &mag
, len
);
325 } else if (blkid_probe_lookup_value(pr
, "PTTYPE", &type
, NULL
) == 0) {
326 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC_OFFSET", &off
, NULL
);
328 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC", &mag
, len
);
331 usage
= N_("partition-table");
336 *offset
= strtoll(off
, NULL
, 10);
338 /* Filter out by -t <type> */
339 if (ctl
->type_pattern
&& !match_fstype(type
, ctl
->type_pattern
))
342 /* Filter out by -o <offset> */
344 struct wipe_desc
*w
= NULL
;
346 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
347 if (w
->offset
== *offset
)
353 w
->on_disk
= 1; /* mark as "found" */
356 wp
= add_offset(wp0
, *offset
);
360 if (usage
|| blkid_probe_lookup_value(pr
, "USAGE", &usage
, NULL
) == 0)
361 wp
->usage
= xstrdup(usage
);
363 wp
->type
= xstrdup(type
);
365 wp
->is_parttable
= ispt
? 1 : 0;
367 wp
->magic
= xmalloc(*len
);
368 memcpy(wp
->magic
, mag
, *len
);
371 if (blkid_probe_lookup_value(pr
, "LABEL", &p
, NULL
) == 0)
372 wp
->label
= xstrdup(p
);
374 if (blkid_probe_lookup_value(pr
, "UUID", &p
, NULL
) == 0)
375 wp
->uuid
= xstrdup(p
);
381 new_probe(const char *devname
, int mode
)
383 blkid_probe pr
= NULL
;
389 int fd
= open(devname
, mode
);
393 pr
= blkid_new_probe();
394 if (!pr
|| blkid_probe_set_device(pr
, fd
, 0, 0) != 0) {
399 pr
= blkid_new_probe_from_filename(devname
);
404 blkid_probe_enable_superblocks(pr
, 1);
405 blkid_probe_set_superblocks_flags(pr
,
406 BLKID_SUBLKS_MAGIC
| /* return magic string and offset */
407 BLKID_SUBLKS_TYPE
| /* return superblock type */
408 BLKID_SUBLKS_USAGE
| /* return USAGE= */
409 BLKID_SUBLKS_LABEL
| /* return LABEL= */
410 BLKID_SUBLKS_UUID
| /* return UUID= */
411 BLKID_SUBLKS_BADCSUM
); /* accept bad checksums */
413 blkid_probe_enable_partitions(pr
, 1);
414 blkid_probe_set_partitions_flags(pr
, BLKID_PARTS_MAGIC
|
415 BLKID_PARTS_FORCE_GPT
);
418 blkid_free_probe(pr
);
419 err(EXIT_FAILURE
, _("error: %s: probing initialization failed"), devname
);
422 static struct wipe_desc
*read_offsets(struct wipe_control
*ctl
)
424 blkid_probe pr
= new_probe(ctl
->devname
, 0);
425 struct wipe_desc
*wp0
= NULL
;
430 while (blkid_do_probe(pr
) == 0) {
434 /* add a new offset to wp0 */
435 get_desc_for_probe(ctl
, &wp0
, pr
, &offset
, &len
);
437 /* hide last detected signature and scan again */
439 blkid_probe_hide_range(pr
, offset
, len
);
440 blkid_probe_step_back(pr
);
444 blkid_free_probe(pr
);
448 static void free_wipe(struct wipe_desc
*wp
)
451 struct wipe_desc
*next
= wp
->next
;
464 static void do_wipe_real(struct wipe_control
*ctl
, blkid_probe pr
,
469 if (blkid_do_wipe(pr
, ctl
->noact
) != 0)
470 err(EXIT_FAILURE
, _("%s: failed to erase %s magic string at offset 0x%08jx"),
471 ctl
->devname
, w
->type
, (intmax_t)w
->offset
);
476 printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
477 "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
479 ctl
->devname
, w
->len
, (intmax_t)w
->offset
, w
->type
);
481 for (i
= 0; i
< w
->len
; i
++) {
482 printf("%02x", w
->magic
[i
]);
489 static void do_backup(struct wipe_desc
*wp
, const char *base
)
494 xasprintf(&fname
, "%s0x%08jx.bak", base
, (intmax_t)wp
->offset
);
496 fd
= open(fname
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
499 if (write_all(fd
, wp
->magic
, wp
->len
) != 0)
505 err(EXIT_FAILURE
, _("%s: failed to create a signature backup"), fname
);
509 static void rereadpt(int fd
, const char *devname
)
514 if (fstat(fd
, &st
) || !S_ISBLK(st
.st_mode
))
519 * Unfortunately, it's pretty common that the first re-read
520 * without delay is uncuccesful. The reason is probably kernel
521 * and/or udevd. Let's wait a moment and try more attempts.
526 ioctl(fd
, BLKRRPART
);
531 printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname
);
535 static int do_wipe(struct wipe_control
*ctl
)
537 int mode
= O_RDWR
, reread
= 0, need_force
= 0;
545 pr
= new_probe(ctl
->devname
, mode
);
550 const char *home
= getenv ("HOME");
551 char *tmp
= xstrdup(ctl
->devname
);
554 errx(EXIT_FAILURE
, _("failed to create a signature backup, $HOME undefined"));
555 xasprintf (&backup
, "%s/wipefs-%s-", home
, basename(tmp
));
559 while (blkid_do_probe(pr
) == 0) {
563 struct wipe_desc
*wp
;
565 wp
= get_desc_for_probe(ctl
, NULL
, pr
, &offset
, &len
);
571 && !blkid_probe_is_wholedisk(pr
)) {
572 warnx(_("%s: ignoring nested \"%s\" partition table "
573 "on non-whole disk device"), ctl
->devname
, wp
->type
);
579 do_backup(wp
, backup
);
580 do_wipe_real(ctl
, pr
, wp
);
581 if (wp
->is_parttable
)
586 /* if the offset has not been wiped (probably because
587 * filtered out by -t or -o) we need to hide it for
588 * libblkid to try another magic string for the same
589 * superblock, otherwise libblkid will continue with
590 * another superblock. Don't forget that the same
591 * superblock could be detected by more magic strings
593 blkid_probe_hide_range(pr
, offset
, len
);
594 blkid_probe_step_back(pr
);
599 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
600 if (!w
->on_disk
&& !ctl
->quiet
)
601 warnx(_("%s: offset 0x%jx not found"),
602 ctl
->devname
, (uintmax_t)w
->offset
);
606 warnx(_("Use the --force option to force erase."));
608 fsync(blkid_probe_get_fd(pr
));
611 if (reread
&& (mode
& O_EXCL
)) {
612 if (ctl
->ndevs
> 1) {
614 * We're going to probe more device, let's postpone
615 * re-read PT ioctl until all is erased to avoid
616 * situation we erase PT on /dev/sda before /dev/sdaN
617 * devices are processed.
620 ctl
->reread
= xcalloc(ctl
->ndevs
, sizeof(char *));
622 ctl
->reread
[ctl
->nrereads
++] = ctl
->devname
;
624 rereadpt(blkid_probe_get_fd(pr
), ctl
->devname
);
628 close(blkid_probe_get_fd(pr
));
629 blkid_free_probe(pr
);
635 static void __attribute__((__noreturn__
))
640 fputs(USAGE_HEADER
, stdout
);
641 printf(_(" %s [options] <device>\n"), program_invocation_short_name
);
643 fputs(USAGE_SEPARATOR
, stdout
);
644 puts(_("Wipe signatures from a device."));
646 fputs(USAGE_OPTIONS
, stdout
);
647 puts(_(" -a, --all wipe all magic strings (BE CAREFUL!)"));
648 puts(_(" -b, --backup create a signature backup in $HOME"));
649 puts(_(" -f, --force force erasure"));
650 puts(_(" -i, --noheadings don't print headings"));
651 puts(_(" -J, --json use JSON output format"));
652 puts(_(" -n, --no-act do everything except the actual write() call"));
653 puts(_(" -o, --offset <num> offset to erase, in bytes"));
654 puts(_(" -O, --output <list> COLUMNS to display (see below)"));
655 puts(_(" -p, --parsable print out in parsable instead of printable format"));
656 puts(_(" -q, --quiet suppress output messages"));
657 puts(_(" -t, --types <list> limit the set of filesystem, RAIDs or partition tables"));
659 printf(USAGE_HELP_OPTIONS(21));
661 fputs(USAGE_COLUMNS
, stdout
);
662 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
663 fprintf(stdout
, " %8s %s\n", infos
[i
].name
, _(infos
[i
].help
));
665 printf(USAGE_MAN_TAIL("wipefs(8)"));
671 main(int argc
, char **argv
)
673 struct wipe_control ctl
= { .devname
= NULL
};
677 static const struct option longopts
[] = {
678 { "all", no_argument
, NULL
, 'a' },
679 { "backup", no_argument
, NULL
, 'b' },
680 { "force", no_argument
, NULL
, 'f' },
681 { "help", no_argument
, NULL
, 'h' },
682 { "no-act", no_argument
, NULL
, 'n' },
683 { "offset", required_argument
, NULL
, 'o' },
684 { "parsable", no_argument
, NULL
, 'p' },
685 { "quiet", no_argument
, NULL
, 'q' },
686 { "types", required_argument
, NULL
, 't' },
687 { "version", no_argument
, NULL
, 'V' },
688 { "json", no_argument
, NULL
, 'J'},
689 { "noheadings",no_argument
, NULL
, 'i'},
690 { "output", required_argument
, NULL
, 'O'},
694 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
698 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
700 setlocale(LC_ALL
, "");
701 bindtextdomain(PACKAGE
, LOCALEDIR
);
703 atexit(close_stdout
);
705 while ((c
= getopt_long(argc
, argv
, "abfhiJnO:o:pqt:V", longopts
, NULL
)) != -1) {
707 err_exclusive_options(c
, longopts
, excl
, excl_st
);
735 add_offset(&ctl
.offsets
, strtosize_or_err(optarg
,
736 _("invalid offset argument")));
746 ctl
.type_pattern
= optarg
;
749 printf(UTIL_LINUX_VERSION
);
752 errtryhelp(EXIT_FAILURE
);
756 if (optind
== argc
) {
757 warnx(_("no device specified"));
758 errtryhelp(EXIT_FAILURE
);
762 if (ctl
.backup
&& !(ctl
.all
|| ctl
.offsets
))
763 warnx(_("The --backup option is meaningless in this context"));
765 if (!ctl
.all
&& !ctl
.offsets
) {
770 /* keep it backward compatible */
771 columns
[ncolumns
++] = COL_OFFSET
;
772 columns
[ncolumns
++] = COL_UUID
;
773 columns
[ncolumns
++] = COL_LABEL
;
774 columns
[ncolumns
++] = COL_TYPE
;
776 /* default, may be modified by -O <list> */
777 columns
[ncolumns
++] = COL_DEVICE
;
778 columns
[ncolumns
++] = COL_OFFSET
;
779 columns
[ncolumns
++] = COL_TYPE
;
780 columns
[ncolumns
++] = COL_UUID
;
781 columns
[ncolumns
++] = COL_LABEL
;
785 && string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
786 &ncolumns
, column_name_to_id
) < 0)
791 while (optind
< argc
) {
792 struct wipe_desc
*wp
;
794 ctl
.devname
= argv
[optind
++];
795 wp
= read_offsets(&ctl
);
797 add_to_output(&ctl
, wp
);
800 finalize_output(&ctl
);
805 ctl
.ndevs
= argc
- optind
;
807 while (optind
< argc
) {
808 ctl
.devname
= argv
[optind
++];
814 /* Re-read partition tables on whole-disk devices. This is
815 * postponed until all is done to avoid conflicts.
817 for (size_t i
= 0; i
< ctl
.nrereads
; i
++) {
818 char *devname
= ctl
.reread
[i
];
819 int fd
= open(devname
, O_RDONLY
);
822 rereadpt(fd
, devname
);