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 unsigned int noact
: 1,
95 const char *name
; /* header */
96 double whint
; /* width hint (N < 1 is in percent of termwidth) */
97 int flags
; /* SCOLS_FL_* */
101 /* columns descriptions */
102 static const struct colinfo infos
[] = {
103 [COL_UUID
] = {"UUID", 4, 0, N_("partition/filesystem UUID")},
104 [COL_LABEL
] = {"LABEL", 5, 0, N_("filesystem LABEL")},
105 [COL_LEN
] = {"LENGTH", 6, 0, N_("magic string length")},
106 [COL_TYPE
] = {"TYPE", 4, 0, N_("superblok type")},
107 [COL_OFFSET
] = {"OFFSET", 5, 0, N_("magic string offset")},
108 [COL_USAGE
] = {"USAGE", 5, 0, N_("type description")},
109 [COL_DEVICE
] = {"DEVICE", 5, 0, N_("block device name")}
112 static int columns
[ARRAY_SIZE(infos
) * 2];
113 static size_t ncolumns
;
115 static int column_name_to_id(const char *name
, size_t namesz
)
121 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
122 const char *cn
= infos
[i
].name
;
123 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
126 warnx(_("unknown column: %s"), name
);
130 static int get_column_id(size_t num
)
132 assert(num
< ncolumns
);
133 assert(columns
[num
] < (int)ARRAY_SIZE(infos
));
137 static const struct colinfo
*get_column_info(int num
)
139 return &infos
[get_column_id(num
)];
143 static void init_output(struct wipe_control
*ctl
)
145 struct libscols_table
*tb
;
149 tb
= scols_new_table();
151 err(EXIT_FAILURE
, _("failed to allocate output table"));
154 scols_table_enable_json(tb
, 1);
155 scols_table_set_name(tb
, "signatures");
157 scols_table_enable_noheadings(tb
, ctl
->no_headings
);
160 scols_table_enable_raw(tb
, 1);
161 scols_table_set_column_separator(tb
, ",");
164 for (i
= 0; i
< ncolumns
; i
++) {
165 const struct colinfo
*col
= get_column_info(i
);
166 struct libscols_column
*cl
;
168 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
,
172 _("failed to initialize output column"));
174 int id
= get_column_id(i
);
177 scols_column_set_json_type(cl
, SCOLS_JSON_NUMBER
);
183 static void finalize_output(struct wipe_control
*ctl
)
185 if (ctl
->parsable
&& !ctl
->no_headings
186 && !scols_table_is_empty(ctl
->outtab
)) {
187 struct libscols_iter
*itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
188 struct libscols_column
*cl
;
195 while (scols_table_next_column(ctl
->outtab
, itr
, &cl
) == 0) {
196 struct libscols_cell
*hdr
= scols_column_get_header(cl
);
197 const char *name
= scols_cell_get_data(hdr
);
205 scols_free_iter(itr
);
207 scols_print_table(ctl
->outtab
);
208 scols_unref_table(ctl
->outtab
);
211 static void fill_table_row(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
213 static struct libscols_line
*ln
;
216 ln
= scols_table_new_line(ctl
->outtab
, NULL
);
218 errx(EXIT_FAILURE
, _("failed to allocate output line"));
220 for (i
= 0; i
< ncolumns
; i
++) {
223 switch (get_column_id(i
)) {
226 str
= xstrdup(wp
->uuid
);
230 str
= xstrdup(wp
->label
);
233 xasprintf(&str
, "0x%jx", (intmax_t)wp
->offset
);
236 xasprintf(&str
, "%zu", wp
->len
);
240 str
= xstrdup(wp
->usage
);
244 str
= xstrdup(wp
->type
);
248 char *dev
= xstrdup(ctl
->devname
);
249 str
= xstrdup(basename(dev
));
257 if (str
&& scols_line_refer_data(ln
, i
, str
))
258 errx(EXIT_FAILURE
, _("failed to add output data"));
262 static void add_to_output(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
264 for (/*nothing*/; wp
; wp
= wp
->next
)
265 fill_table_row(ctl
, wp
);
268 /* Allocates a new wipe_desc and add to the wp0 if not NULL */
269 static struct wipe_desc
*add_offset(struct wipe_desc
**wp0
, loff_t offset
)
271 struct wipe_desc
*wp
, *last
= NULL
;
274 /* check if already exists */
275 for (wp
= *wp0
; wp
; wp
= wp
->next
) {
276 if (wp
->offset
== offset
)
282 wp
= xcalloc(1, sizeof(struct wipe_desc
));
293 /* Read data from libblkid and if detected type pass -t and -o filters than:
294 * - allocates a new wipe_desc
295 * - add the new wipe_desc to wp0 list (if not NULL)
297 * The function always returns offset and len if libblkid detected something.
299 static struct wipe_desc
*get_desc_for_probe(struct wipe_control
*ctl
,
300 struct wipe_desc
**wp0
,
305 const char *off
, *type
, *mag
, *p
, *usage
= NULL
;
306 struct wipe_desc
*wp
;
312 if (blkid_probe_lookup_value(pr
, "TYPE", &type
, NULL
) == 0) {
313 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC_OFFSET", &off
, NULL
);
315 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC", &mag
, len
);
320 } else if (blkid_probe_lookup_value(pr
, "PTTYPE", &type
, NULL
) == 0) {
321 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC_OFFSET", &off
, NULL
);
323 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC", &mag
, len
);
326 usage
= N_("partition-table");
331 *offset
= strtoll(off
, NULL
, 10);
333 /* Filter out by -t <type> */
334 if (ctl
->type_pattern
&& !match_fstype(type
, ctl
->type_pattern
))
337 /* Filter out by -o <offset> */
339 struct wipe_desc
*w
= NULL
;
341 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
342 if (w
->offset
== *offset
)
348 w
->on_disk
= 1; /* mark as "found" */
351 wp
= add_offset(wp0
, *offset
);
355 if (usage
|| blkid_probe_lookup_value(pr
, "USAGE", &usage
, NULL
) == 0)
356 wp
->usage
= xstrdup(usage
);
358 wp
->type
= xstrdup(type
);
360 wp
->is_parttable
= ispt
? 1 : 0;
362 wp
->magic
= xmalloc(*len
);
363 memcpy(wp
->magic
, mag
, *len
);
366 if (blkid_probe_lookup_value(pr
, "LABEL", &p
, NULL
) == 0)
367 wp
->label
= xstrdup(p
);
369 if (blkid_probe_lookup_value(pr
, "UUID", &p
, NULL
) == 0)
370 wp
->uuid
= xstrdup(p
);
376 new_probe(const char *devname
, int mode
)
378 blkid_probe pr
= NULL
;
384 int fd
= open(devname
, mode
);
388 pr
= blkid_new_probe();
389 if (!pr
|| blkid_probe_set_device(pr
, fd
, 0, 0) != 0) {
394 pr
= blkid_new_probe_from_filename(devname
);
399 blkid_probe_enable_superblocks(pr
, 1);
400 blkid_probe_set_superblocks_flags(pr
,
401 BLKID_SUBLKS_MAGIC
| /* return magic string and offset */
402 BLKID_SUBLKS_TYPE
| /* return superblock type */
403 BLKID_SUBLKS_USAGE
| /* return USAGE= */
404 BLKID_SUBLKS_LABEL
| /* return LABEL= */
405 BLKID_SUBLKS_UUID
| /* return UUID= */
406 BLKID_SUBLKS_BADCSUM
); /* accept bad checksums */
408 blkid_probe_enable_partitions(pr
, 1);
409 blkid_probe_set_partitions_flags(pr
, BLKID_PARTS_MAGIC
|
410 BLKID_PARTS_FORCE_GPT
);
413 blkid_free_probe(pr
);
414 err(EXIT_FAILURE
, _("error: %s: probing initialization failed"), devname
);
417 static struct wipe_desc
*read_offsets(struct wipe_control
*ctl
)
419 blkid_probe pr
= new_probe(ctl
->devname
, 0);
420 struct wipe_desc
*wp0
= NULL
;
425 while (blkid_do_probe(pr
) == 0) {
429 /* add a new offset to wp0 */
430 get_desc_for_probe(ctl
, &wp0
, pr
, &offset
, &len
);
432 /* hide last detected signature and scan again */
434 blkid_probe_hide_range(pr
, offset
, len
);
435 blkid_probe_step_back(pr
);
439 blkid_free_probe(pr
);
443 static void free_wipe(struct wipe_desc
*wp
)
446 struct wipe_desc
*next
= wp
->next
;
459 static void do_wipe_real(struct wipe_control
*ctl
, blkid_probe pr
,
464 if (blkid_do_wipe(pr
, ctl
->noact
) != 0)
465 err(EXIT_FAILURE
, _("%s: failed to erase %s magic string at offset 0x%08jx"),
466 ctl
->devname
, w
->type
, (intmax_t)w
->offset
);
471 printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
472 "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
474 ctl
->devname
, w
->len
, (intmax_t)w
->offset
, w
->type
);
476 for (i
= 0; i
< w
->len
; i
++) {
477 printf("%02x", w
->magic
[i
]);
484 static void do_backup(struct wipe_desc
*wp
, const char *base
)
489 xasprintf(&fname
, "%s0x%08jx.bak", base
, (intmax_t)wp
->offset
);
491 fd
= open(fname
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
494 if (write_all(fd
, wp
->magic
, wp
->len
) != 0)
500 err(EXIT_FAILURE
, _("%s: failed to create a signature backup"), fname
);
504 static void rereadpt(int fd
, const char *devname
)
508 if (fstat(fd
, &st
) || !S_ISBLK(st
.st_mode
))
512 ioctl(fd
, BLKRRPART
);
513 printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname
);
517 static int do_wipe(struct wipe_control
*ctl
)
519 int mode
= O_RDWR
, reread
= 0, need_force
= 0;
527 pr
= new_probe(ctl
->devname
, mode
);
532 const char *home
= getenv ("HOME");
533 char *tmp
= xstrdup(ctl
->devname
);
536 errx(EXIT_FAILURE
, _("failed to create a signature backup, $HOME undefined"));
537 xasprintf (&backup
, "%s/wipefs-%s-", home
, basename(tmp
));
541 while (blkid_do_probe(pr
) == 0) {
545 struct wipe_desc
*wp
;
547 wp
= get_desc_for_probe(ctl
, NULL
, pr
, &offset
, &len
);
553 && !blkid_probe_is_wholedisk(pr
)) {
554 warnx(_("%s: ignoring nested \"%s\" partition table "
555 "on non-whole disk device"), ctl
->devname
, wp
->type
);
561 do_backup(wp
, backup
);
562 do_wipe_real(ctl
, pr
, wp
);
563 if (wp
->is_parttable
)
568 /* if the offset has not been wiped (probably because
569 * filtered out by -t or -o) we need to hide it for
570 * libblkid to try another magic string for the same
571 * superblock, otherwise libblkid will continue with
572 * another superblock. Don't forget that the same
573 * superblock could be detected by more magic strings
575 blkid_probe_hide_range(pr
, offset
, len
);
576 blkid_probe_step_back(pr
);
581 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
582 if (!w
->on_disk
&& !ctl
->quiet
)
583 warnx(_("%s: offset 0x%jx not found"),
584 ctl
->devname
, (uintmax_t)w
->offset
);
588 warnx(_("Use the --force option to force erase."));
590 fsync(blkid_probe_get_fd(pr
));
593 if (reread
&& (mode
& O_EXCL
))
594 rereadpt(blkid_probe_get_fd(pr
), ctl
->devname
);
597 close(blkid_probe_get_fd(pr
));
598 blkid_free_probe(pr
);
604 static void __attribute__((__noreturn__
))
609 fputs(USAGE_HEADER
, stdout
);
610 printf(_(" %s [options] <device>\n"), program_invocation_short_name
);
612 fputs(USAGE_SEPARATOR
, stdout
);
613 puts(_("Wipe signatures from a device."));
615 fputs(USAGE_OPTIONS
, stdout
);
616 puts(_(" -a, --all wipe all magic strings (BE CAREFUL!)"));
617 puts(_(" -b, --backup create a signature backup in $HOME"));
618 puts(_(" -f, --force force erasure"));
619 puts(_(" -i, --noheadings don't print headings"));
620 puts(_(" -J, --json use JSON output format"));
621 puts(_(" -n, --no-act do everything except the actual write() call"));
622 puts(_(" -o, --offset <num> offset to erase, in bytes"));
623 puts(_(" -O, --output <list> COLUMNS to display (see below)"));
624 puts(_(" -p, --parsable print out in parsable instead of printable format"));
625 puts(_(" -q, --quiet suppress output messages"));
626 puts(_(" -t, --types <list> limit the set of filesystem, RAIDs or partition tables"));
628 printf(USAGE_HELP_OPTIONS(21));
630 fputs(USAGE_COLUMNS
, stdout
);
631 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
632 fprintf(stdout
, " %8s %s\n", infos
[i
].name
, _(infos
[i
].help
));
634 printf(USAGE_MAN_TAIL("wipefs(8)"));
640 main(int argc
, char **argv
)
642 struct wipe_control ctl
= { .devname
= NULL
};
646 static const struct option longopts
[] = {
647 { "all", no_argument
, NULL
, 'a' },
648 { "backup", no_argument
, NULL
, 'b' },
649 { "force", no_argument
, NULL
, 'f' },
650 { "help", no_argument
, NULL
, 'h' },
651 { "no-act", no_argument
, NULL
, 'n' },
652 { "offset", required_argument
, NULL
, 'o' },
653 { "parsable", no_argument
, NULL
, 'p' },
654 { "quiet", no_argument
, NULL
, 'q' },
655 { "types", required_argument
, NULL
, 't' },
656 { "version", no_argument
, NULL
, 'V' },
657 { "json", no_argument
, NULL
, 'J'},
658 { "noheadings",no_argument
, NULL
, 'i'},
659 { "output", required_argument
, NULL
, 'O'},
663 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
667 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
669 setlocale(LC_ALL
, "");
670 bindtextdomain(PACKAGE
, LOCALEDIR
);
672 atexit(close_stdout
);
674 while ((c
= getopt_long(argc
, argv
, "abfhiJnO:o:pqt:V", longopts
, NULL
)) != -1) {
676 err_exclusive_options(c
, longopts
, excl
, excl_st
);
704 add_offset(&ctl
.offsets
, strtosize_or_err(optarg
,
705 _("invalid offset argument")));
715 ctl
.type_pattern
= optarg
;
718 printf(UTIL_LINUX_VERSION
);
721 errtryhelp(EXIT_FAILURE
);
725 if (optind
== argc
) {
726 warnx(_("no device specified"));
727 errtryhelp(EXIT_FAILURE
);
731 if (ctl
.backup
&& !(ctl
.all
|| ctl
.offsets
))
732 warnx(_("The --backup option is meaningless in this context"));
734 if (!ctl
.all
&& !ctl
.offsets
) {
739 /* keep it backward compatible */
740 columns
[ncolumns
++] = COL_OFFSET
;
741 columns
[ncolumns
++] = COL_UUID
;
742 columns
[ncolumns
++] = COL_LABEL
;
743 columns
[ncolumns
++] = COL_TYPE
;
745 /* default, may be modified by -O <list> */
746 columns
[ncolumns
++] = COL_DEVICE
;
747 columns
[ncolumns
++] = COL_OFFSET
;
748 columns
[ncolumns
++] = COL_TYPE
;
749 columns
[ncolumns
++] = COL_UUID
;
750 columns
[ncolumns
++] = COL_LABEL
;
754 && string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
755 &ncolumns
, column_name_to_id
) < 0)
760 while (optind
< argc
) {
761 struct wipe_desc
*wp
;
763 ctl
.devname
= argv
[optind
++];
764 wp
= read_offsets(&ctl
);
766 add_to_output(&ctl
, wp
);
769 finalize_output(&ctl
);
774 while (optind
< argc
) {
775 ctl
.devname
= argv
[optind
++];