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 const char *backup
; /* location of backups */
70 struct libscols_table
*outtab
;
71 struct wipe_desc
*offsets
; /* -o <offset> -o <offset> ... */
73 size_t ndevs
; /* number of devices to probe */
75 char **reread
; /* devices to BLKRRPART */
76 size_t nrereads
; /* size of reread */
78 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 scols_print_table(ctl
->outtab
);
192 scols_unref_table(ctl
->outtab
);
195 static void fill_table_row(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
197 static struct libscols_line
*ln
;
200 ln
= scols_table_new_line(ctl
->outtab
, NULL
);
202 errx(EXIT_FAILURE
, _("failed to allocate output line"));
204 for (i
= 0; i
< ncolumns
; i
++) {
207 switch (get_column_id(i
)) {
210 str
= xstrdup(wp
->uuid
);
214 str
= xstrdup(wp
->label
);
217 xasprintf(&str
, "0x%jx", (intmax_t)wp
->offset
);
220 xasprintf(&str
, "%zu", wp
->len
);
224 str
= xstrdup(wp
->usage
);
228 str
= xstrdup(wp
->type
);
232 char *dev
= xstrdup(ctl
->devname
);
233 str
= xstrdup(basename(dev
));
241 if (str
&& scols_line_refer_data(ln
, i
, str
))
242 errx(EXIT_FAILURE
, _("failed to add output data"));
246 static void add_to_output(struct wipe_control
*ctl
, struct wipe_desc
*wp
)
248 for (/*nothing*/; wp
; wp
= wp
->next
)
249 fill_table_row(ctl
, wp
);
252 /* Allocates a new wipe_desc and add to the wp0 if not NULL */
253 static struct wipe_desc
*add_offset(struct wipe_desc
**wp0
, loff_t offset
)
255 struct wipe_desc
*wp
, *last
= NULL
;
258 /* check if already exists */
259 for (wp
= *wp0
; wp
; wp
= wp
->next
) {
260 if (wp
->offset
== offset
)
266 wp
= xcalloc(1, sizeof(struct wipe_desc
));
277 /* Read data from libblkid and if detected type pass -t and -o filters than:
278 * - allocates a new wipe_desc
279 * - add the new wipe_desc to wp0 list (if not NULL)
281 * The function always returns offset and len if libblkid detected something.
283 static struct wipe_desc
*get_desc_for_probe(struct wipe_control
*ctl
,
284 struct wipe_desc
**wp0
,
289 const char *off
, *type
, *mag
, *p
, *use
= NULL
;
290 struct wipe_desc
*wp
;
296 if (blkid_probe_lookup_value(pr
, "TYPE", &type
, NULL
) == 0) {
297 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC_OFFSET", &off
, NULL
);
299 rc
= blkid_probe_lookup_value(pr
, "SBMAGIC", &mag
, len
);
304 } else if (blkid_probe_lookup_value(pr
, "PTTYPE", &type
, NULL
) == 0) {
305 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC_OFFSET", &off
, NULL
);
307 rc
= blkid_probe_lookup_value(pr
, "PTMAGIC", &mag
, len
);
310 use
= N_("partition-table");
316 *offset
= strtoll(off
, NULL
, 10);
320 /* Filter out by -t <type> */
321 if (ctl
->type_pattern
&& !match_fstype(type
, ctl
->type_pattern
))
324 /* Filter out by -o <offset> */
326 struct wipe_desc
*w
= NULL
;
328 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
329 if (w
->offset
== *offset
)
335 w
->on_disk
= 1; /* mark as "found" */
338 wp
= add_offset(wp0
, *offset
);
342 if (use
|| blkid_probe_lookup_value(pr
, "USAGE", &use
, NULL
) == 0)
343 wp
->usage
= xstrdup(use
);
345 wp
->type
= xstrdup(type
);
347 wp
->is_parttable
= ispt
? 1 : 0;
349 wp
->magic
= xmalloc(*len
);
350 memcpy(wp
->magic
, mag
, *len
);
353 if (blkid_probe_lookup_value(pr
, "LABEL", &p
, NULL
) == 0)
354 wp
->label
= xstrdup(p
);
356 if (blkid_probe_lookup_value(pr
, "UUID", &p
, NULL
) == 0)
357 wp
->uuid
= xstrdup(p
);
363 new_probe(const char *devname
, int mode
)
365 blkid_probe pr
= NULL
;
371 int fd
= open(devname
, mode
| O_NONBLOCK
);
375 pr
= blkid_new_probe();
376 if (!pr
|| blkid_probe_set_device(pr
, fd
, 0, 0) != 0) {
381 pr
= blkid_new_probe_from_filename(devname
);
386 blkid_probe_enable_superblocks(pr
, 1);
387 blkid_probe_set_superblocks_flags(pr
,
388 BLKID_SUBLKS_MAGIC
| /* return magic string and offset */
389 BLKID_SUBLKS_TYPE
| /* return superblock type */
390 BLKID_SUBLKS_USAGE
| /* return USAGE= */
391 BLKID_SUBLKS_LABEL
| /* return LABEL= */
392 BLKID_SUBLKS_UUID
| /* return UUID= */
393 BLKID_SUBLKS_BADCSUM
); /* accept bad checksums */
395 blkid_probe_enable_partitions(pr
, 1);
396 blkid_probe_set_partitions_flags(pr
, BLKID_PARTS_MAGIC
|
397 BLKID_PARTS_FORCE_GPT
);
400 blkid_free_probe(pr
);
401 err(EXIT_FAILURE
, _("error: %s: probing initialization failed"), devname
);
404 static struct wipe_desc
*read_offsets(struct wipe_control
*ctl
)
406 blkid_probe pr
= new_probe(ctl
->devname
, 0);
407 struct wipe_desc
*wp0
= NULL
;
412 while (blkid_do_probe(pr
) == 0) {
416 /* add a new offset to wp0 */
417 get_desc_for_probe(ctl
, &wp0
, pr
, &offset
, &len
);
419 /* hide last detected signature and scan again */
421 blkid_probe_hide_range(pr
, offset
, len
);
422 blkid_probe_step_back(pr
);
426 blkid_free_probe(pr
);
430 static void free_wipe(struct wipe_desc
*wp
)
433 struct wipe_desc
*next
= wp
->next
;
446 static void do_wipe_real(struct wipe_control
*ctl
, blkid_probe pr
,
451 if (blkid_do_wipe(pr
, ctl
->noact
) != 0)
452 err(EXIT_FAILURE
, _("%s: failed to erase %s magic string at offset 0x%08jx"),
453 ctl
->devname
, w
->type
, (intmax_t)w
->offset
);
458 printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
459 "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
461 ctl
->devname
, w
->len
, (intmax_t)w
->offset
, w
->type
);
463 for (i
= 0; i
< w
->len
; i
++) {
464 printf("%02x", w
->magic
[i
]);
471 static void do_backup(struct wipe_desc
*wp
, const char *base
)
476 xasprintf(&fname
, "%s0x%08jx.bak", base
, (intmax_t)wp
->offset
);
478 fd
= open(fname
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
481 if (write_all(fd
, wp
->magic
, wp
->len
) != 0)
487 err(EXIT_FAILURE
, _("%s: failed to create a signature backup"), fname
);
491 static void rereadpt(int fd
, const char *devname
)
496 if (fstat(fd
, &st
) || !S_ISBLK(st
.st_mode
))
501 * Unfortunately, it's pretty common that the first re-read
502 * without delay is uncuccesful. The reason is probably kernel
503 * and/or udevd. Let's wait a moment and try more attempts.
508 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
);
531 if (blkdev_lock(blkid_probe_get_fd(pr
),
532 ctl
->devname
, ctl
->lockmode
) != 0) {
533 blkid_free_probe(pr
);
538 char *tmp
= xstrdup(ctl
->devname
);
540 xasprintf(&backup
, "%s/wipefs-%s-", ctl
->backup
, basename(tmp
));
544 while (blkid_do_probe(pr
) == 0) {
548 struct wipe_desc
*wp
;
550 wp
= get_desc_for_probe(ctl
, NULL
, pr
, &offset
, &len
);
556 && !blkid_probe_is_wholedisk(pr
)) {
557 warnx(_("%s: ignoring nested \"%s\" partition table "
558 "on non-whole disk device"), ctl
->devname
, wp
->type
);
564 do_backup(wp
, backup
);
565 do_wipe_real(ctl
, pr
, wp
);
566 if (wp
->is_parttable
)
571 /* if the offset has not been wiped (probably because
572 * filtered out by -t or -o) we need to hide it for
573 * libblkid to try another magic string for the same
574 * superblock, otherwise libblkid will continue with
575 * another superblock. Don't forget that the same
576 * superblock could be detected by more magic strings
578 blkid_probe_hide_range(pr
, offset
, len
);
579 blkid_probe_step_back(pr
);
584 for (w
= ctl
->offsets
; w
; w
= w
->next
) {
585 if (!w
->on_disk
&& !ctl
->quiet
)
586 warnx(_("%s: offset 0x%jx not found"),
587 ctl
->devname
, (uintmax_t)w
->offset
);
591 warnx(_("Use the --force option to force erase."));
593 if (fsync(blkid_probe_get_fd(pr
)) != 0)
594 err(EXIT_FAILURE
, _("%s: cannot flush modified buffers"),
598 if (reread
&& (mode
& O_EXCL
)) {
599 if (ctl
->ndevs
> 1) {
601 * We're going to probe more device, let's postpone
602 * re-read PT ioctl until all is erased to avoid
603 * situation we erase PT on /dev/sda before /dev/sdaN
604 * devices are processed.
607 ctl
->reread
= xcalloc(ctl
->ndevs
, sizeof(char *));
609 ctl
->reread
[ctl
->nrereads
++] = ctl
->devname
;
611 rereadpt(blkid_probe_get_fd(pr
), ctl
->devname
);
615 if (close(blkid_probe_get_fd(pr
)) != 0)
616 err(EXIT_FAILURE
, _("%s: close device failed"), ctl
->devname
);
618 blkid_free_probe(pr
);
624 static void __attribute__((__noreturn__
))
629 fputs(USAGE_HEADER
, stdout
);
630 fprintf(stdout
, _(" %s [options] <device>\n"), program_invocation_short_name
);
632 fputs(USAGE_SEPARATOR
, stdout
);
633 fputsln(_("Wipe signatures from a device."), stdout
);
635 fputs(USAGE_OPTIONS
, stdout
);
636 fputsln(_(" -a, --all wipe all magic strings (BE CAREFUL!)"), stdout
);
637 fputsln(_(" -b, --backup[=<dir>] create a signature backup in <dir> or $HOME"), stdout
);
638 fputsln(_(" -f, --force force erasure"), stdout
);
639 fputsln(_(" -i, --noheadings don't print headings"), stdout
);
640 fputsln(_(" -J, --json use JSON output format"), stdout
);
641 fputsln(_(" -n, --no-act do everything except the actual write() call"), stdout
);
642 fputsln(_(" -o, --offset <num> offset to erase, in bytes"), stdout
);
643 fputsln(_(" -O, --output <list> COLUMNS to display (see below)"), stdout
);
644 fputsln(_(" -p, --parsable print out in parsable instead of printable format"), stdout
);
645 fputsln(_(" -q, --quiet suppress output messages"), stdout
);
646 fputsln(_(" -t, --types <list> limit the set of filesystem, RAIDs or partition tables"), stdout
);
648 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
650 fprintf(stdout
, USAGE_HELP_OPTIONS(22));
652 fputs(USAGE_ARGUMENTS
, stdout
);
653 fprintf(stdout
, USAGE_ARG_SIZE(_("<num>")));
655 fputs(USAGE_COLUMNS
, stdout
);
656 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
657 fprintf(stdout
, " %8s %s\n", infos
[i
].name
, _(infos
[i
].help
));
659 fprintf(stdout
, USAGE_MAN_TAIL("wipefs(8)"));
665 main(int argc
, char **argv
)
667 struct wipe_control ctl
= { .devname
= NULL
};
672 OPT_LOCK
= CHAR_MAX
+ 1,
674 static const struct option longopts
[] = {
675 { "all", no_argument
, NULL
, 'a' },
676 { "backup", optional_argument
, NULL
, 'b' },
677 { "force", no_argument
, NULL
, 'f' },
678 { "help", no_argument
, NULL
, 'h' },
679 { "lock", optional_argument
, NULL
, OPT_LOCK
},
680 { "no-act", no_argument
, NULL
, 'n' },
681 { "offset", required_argument
, NULL
, 'o' },
682 { "parsable", no_argument
, NULL
, 'p' },
683 { "quiet", no_argument
, NULL
, 'q' },
684 { "types", required_argument
, NULL
, 't' },
685 { "version", no_argument
, NULL
, 'V' },
686 { "json", no_argument
, NULL
, 'J'},
687 { "noheadings",no_argument
, NULL
, 'i'},
688 { "output", required_argument
, NULL
, 'O'},
692 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
696 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
698 setlocale(LC_ALL
, "");
699 bindtextdomain(PACKAGE
, LOCALEDIR
);
701 close_stdout_atexit();
703 while ((c
= getopt_long(argc
, argv
, "ab::fhiJnO:o:pqt:V", longopts
, NULL
)) != -1) {
705 err_exclusive_options(c
, longopts
, excl
, excl_st
);
715 ctl
.backup
= getenv("HOME");
718 _("failed to create a signature backup, $HOME undefined"));
737 add_offset(&ctl
.offsets
, strtosize_or_err(optarg
,
738 _("invalid offset argument")));
748 ctl
.type_pattern
= optarg
;
755 ctl
.lockmode
= optarg
;
761 print_version(EXIT_SUCCESS
);
763 errtryhelp(EXIT_FAILURE
);
767 if (optind
== argc
) {
768 warnx(_("no device specified"));
769 errtryhelp(EXIT_FAILURE
);
773 if (ctl
.backup
&& !(ctl
.all
|| ctl
.offsets
))
774 warnx(_("The --backup option is meaningless in this context"));
776 if (!ctl
.all
&& !ctl
.offsets
) {
781 /* keep it backward compatible */
782 columns
[ncolumns
++] = COL_OFFSET
;
783 columns
[ncolumns
++] = COL_UUID
;
784 columns
[ncolumns
++] = COL_LABEL
;
785 columns
[ncolumns
++] = COL_TYPE
;
787 /* default, may be modified by -O <list> */
788 columns
[ncolumns
++] = COL_DEVICE
;
789 columns
[ncolumns
++] = COL_OFFSET
;
790 columns
[ncolumns
++] = COL_TYPE
;
791 columns
[ncolumns
++] = COL_UUID
;
792 columns
[ncolumns
++] = COL_LABEL
;
796 && string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
797 &ncolumns
, column_name_to_id
) < 0)
802 while (optind
< argc
) {
803 struct wipe_desc
*wp
;
805 ctl
.devname
= argv
[optind
++];
806 wp
= read_offsets(&ctl
);
808 add_to_output(&ctl
, wp
);
811 finalize_output(&ctl
);
816 ctl
.ndevs
= argc
- optind
;
818 while (optind
< argc
) {
819 ctl
.devname
= argv
[optind
++];
825 /* Re-read partition tables on whole-disk devices. This is
826 * postponed until all is done to avoid conflicts.
828 for (i
= 0; i
< ctl
.nrereads
; i
++) {
829 char *devname
= ctl
.reread
[i
];
830 int fd
= open(devname
, O_RDONLY
);
833 rereadpt(fd
, devname
);