#include <getopt.h>
#include <string.h>
#include <limits.h>
+#include <libgen.h>
#include <blkid.h>
+#include <libsmartcols.h>
#include "nls.h"
#include "xalloc.h"
#include "c.h"
#include "closestream.h"
#include "optutils.h"
+#include "blkdev.h"
struct wipe_desc {
loff_t offset; /* magic string offset */
size_t len; /* length of magic string */
unsigned char *magic; /* magic string */
- int zap; /* zap this offset? */
char *usage; /* raid, filesystem, ... */
char *type; /* FS type */
char *label; /* FS label */
char *uuid; /* FS uuid */
- int on_disk;
-
struct wipe_desc *next;
+
+ unsigned int on_disk : 1,
+ is_parttable : 1;
+
+};
+
+struct wipe_control {
+ char *devname;
+ const char *type_pattern; /* -t <pattern> */
+
+ struct libscols_table *outtab;
+ struct wipe_desc *offsets; /* -o <offset> -o <offset> ... */
+
+ size_t ndevs; /* number of devices to probe */
+
+ char **reread; /* devices to BLKRRPART */
+ size_t nrereads; /* size of reread */
+
+ unsigned int noact : 1,
+ all : 1,
+ quiet : 1,
+ backup : 1,
+ force : 1,
+ json : 1,
+ no_headings : 1,
+ parsable : 1;
};
+
+/* column IDs */
enum {
- WP_MODE_PRETTY, /* default */
- WP_MODE_PARSABLE
+ COL_UUID = 0,
+ COL_LABEL,
+ COL_LEN,
+ COL_TYPE,
+ COL_OFFSET,
+ COL_USAGE,
+ COL_DEVICE
+};
+
+/* column names */
+struct colinfo {
+ const char *name; /* header */
+ double whint; /* width hint (N < 1 is in percent of termwidth) */
+ int flags; /* SCOLS_FL_* */
+ const char *help;
+};
+
+/* columns descriptions */
+static const struct colinfo infos[] = {
+ [COL_UUID] = {"UUID", 4, 0, N_("partition/filesystem UUID")},
+ [COL_LABEL] = {"LABEL", 5, 0, N_("filesystem LABEL")},
+ [COL_LEN] = {"LENGTH", 6, 0, N_("magic string length")},
+ [COL_TYPE] = {"TYPE", 4, 0, N_("superblok type")},
+ [COL_OFFSET] = {"OFFSET", 5, 0, N_("magic string offset")},
+ [COL_USAGE] = {"USAGE", 5, 0, N_("type description")},
+ [COL_DEVICE] = {"DEVICE", 5, 0, N_("block device name")}
};
-static const char *type_pattern;
+static int columns[ARRAY_SIZE(infos) * 2];
+static size_t ncolumns;
-static void
-print_pretty(struct wipe_desc *wp, int line)
+static int column_name_to_id(const char *name, size_t namesz)
{
- if (!line) {
- printf("offset type\n");
- printf("----------------------------------------------------------------\n");
+ size_t i;
+
+ assert(name);
+
+ for (i = 0; i < ARRAY_SIZE(infos); i++) {
+ const char *cn = infos[i].name;
+ if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
+ return i;
}
+ warnx(_("unknown column: %s"), name);
+ return -1;
+}
- printf("0x%-17jx %s [%s]", wp->offset, wp->type, wp->usage);
+static int get_column_id(size_t num)
+{
+ assert(num < ncolumns);
+ assert(columns[num] < (int)ARRAY_SIZE(infos));
+ return columns[num];
+}
- if (wp->label && *wp->label)
- printf("\n%27s %s", "LABEL:", wp->label);
- if (wp->uuid)
- printf("\n%27s %s", "UUID: ", wp->uuid);
- puts("\n");
+static const struct colinfo *get_column_info(int num)
+{
+ return &infos[get_column_id(num)];
}
-static void
-print_parsable(struct wipe_desc *wp, int line)
+
+static void init_output(struct wipe_control *ctl)
{
- char enc[256];
+ struct libscols_table *tb;
+ size_t i;
- if (!line)
- printf("# offset,uuid,label,type\n");
+ scols_init_debug(0);
+ tb = scols_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, _("failed to allocate output table"));
- printf("0x%jx,", wp->offset);
+ if (ctl->json) {
+ scols_table_enable_json(tb, 1);
+ scols_table_set_name(tb, "signatures");
+ }
+ scols_table_enable_noheadings(tb, ctl->no_headings);
- if (wp->uuid) {
- blkid_encode_string(wp->uuid, enc, sizeof(enc));
- printf("%s,", enc);
- } else
- fputc(',', stdout);
+ if (ctl->parsable) {
+ scols_table_enable_raw(tb, 1);
+ scols_table_set_column_separator(tb, ",");
+ }
- if (wp->label) {
- blkid_encode_string(wp->label, enc, sizeof(enc));
- printf("%s,", enc);
- } else
- fputc(',', stdout);
+ for (i = 0; i < ncolumns; i++) {
+ const struct colinfo *col = get_column_info(i);
+ struct libscols_column *cl;
- blkid_encode_string(wp->type, enc, sizeof(enc));
- printf("%s\n", enc);
+ cl = scols_table_new_column(tb, col->name, col->whint,
+ col->flags);
+ if (!cl)
+ err(EXIT_FAILURE,
+ _("failed to initialize output column"));
+ if (ctl->json) {
+ int id = get_column_id(i);
+
+ if (id == COL_LEN)
+ scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
+ }
+ }
+ ctl->outtab = tb;
}
-static void
-print_all(struct wipe_desc *wp, int mode)
+static void finalize_output(struct wipe_control *ctl)
{
- int n = 0;
+ if (ctl->parsable && !ctl->no_headings
+ && !scols_table_is_empty(ctl->outtab)) {
+ struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
+ struct libscols_column *cl;
+ int i = 0;
+
+ if (!itr)
+ err_oom();
+
+ fputs("# ", stdout);
+ while (scols_table_next_column(ctl->outtab, itr, &cl) == 0) {
+ struct libscols_cell *hdr = scols_column_get_header(cl);
+ const char *name = scols_cell_get_data(hdr);
+
+ if (i)
+ fputc(',', stdout);
+ fputs(name, stdout);
+ i++;
+ }
+ fputc('\n', stdout);
+ scols_free_iter(itr);
+ }
+ scols_print_table(ctl->outtab);
+ scols_unref_table(ctl->outtab);
+}
- while (wp) {
- switch (mode) {
- case WP_MODE_PRETTY:
- print_pretty(wp, n++);
+static void fill_table_row(struct wipe_control *ctl, struct wipe_desc *wp)
+{
+ static struct libscols_line *ln;
+ size_t i;
+
+ ln = scols_table_new_line(ctl->outtab, NULL);
+ if (!ln)
+ errx(EXIT_FAILURE, _("failed to allocate output line"));
+
+ for (i = 0; i < ncolumns; i++) {
+ char *str = NULL;
+
+ switch (get_column_id(i)) {
+ case COL_UUID:
+ if (wp->uuid)
+ str = xstrdup(wp->uuid);
+ break;
+ case COL_LABEL:
+ if (wp->label)
+ str = xstrdup(wp->label);
+ break;
+ case COL_OFFSET:
+ xasprintf(&str, "0x%jx", (intmax_t)wp->offset);
+ break;
+ case COL_LEN:
+ xasprintf(&str, "%zu", wp->len);
+ break;
+ case COL_USAGE:
+ if (wp->usage)
+ str = xstrdup(wp->usage);
+ break;
+ case COL_TYPE:
+ if (wp->type)
+ str = xstrdup(wp->type);
break;
- case WP_MODE_PARSABLE:
- print_parsable(wp, n++);
+ case COL_DEVICE:
+ if (ctl->devname) {
+ char *dev = xstrdup(ctl->devname);
+ str = xstrdup(basename(dev));
+ free(dev);
+ }
break;
default:
abort();
}
- wp = wp->next;
+
+ if (str && scols_line_refer_data(ln, i, str))
+ errx(EXIT_FAILURE, _("failed to add output data"));
}
}
-static struct wipe_desc *
-add_offset(struct wipe_desc *wp0, loff_t offset, int zap)
+static void add_to_output(struct wipe_control *ctl, struct wipe_desc *wp)
{
- struct wipe_desc *wp = wp0;
+ for (/*nothing*/; wp; wp = wp->next)
+ fill_table_row(ctl, wp);
+}
- while (wp) {
- if (wp->offset == offset)
- return wp;
- wp = wp->next;
+/* Allocates a new wipe_desc and add to the wp0 if not NULL */
+static struct wipe_desc *add_offset(struct wipe_desc **wp0, loff_t offset)
+{
+ struct wipe_desc *wp, *last = NULL;
+
+ if (wp0) {
+ /* check if already exists */
+ for (wp = *wp0; wp; wp = wp->next) {
+ if (wp->offset == offset)
+ return wp;
+ last = wp;
+ }
}
wp = xcalloc(1, sizeof(struct wipe_desc));
wp->offset = offset;
- wp->next = wp0;
- wp->zap = zap;
- return wp;
-}
-
-static struct wipe_desc *
-clone_offset(struct wipe_desc *wp0)
-{
- struct wipe_desc *wp = NULL;
-
- while(wp0) {
- wp = add_offset(wp, wp0->offset, wp0->zap);
- wp0 = wp0->next;
- }
+ wp->next = NULL;
+ if (last)
+ last->next = wp;
+ if (wp0 && !*wp0)
+ *wp0 = wp;
return wp;
}
-static struct wipe_desc *
-get_desc_for_probe(struct wipe_desc *wp, blkid_probe pr)
+/* Read data from libblkid and if detected type pass -t and -o filters than:
+ * - allocates a new wipe_desc
+ * - add the new wipe_desc to wp0 list (if not NULL)
+ *
+ * The function always returns offset and len if libblkid detected something.
+ */
+static struct wipe_desc *get_desc_for_probe(struct wipe_control *ctl,
+ struct wipe_desc **wp0,
+ blkid_probe pr,
+ loff_t *offset,
+ size_t *len)
{
- const char *off, *type, *mag, *p, *usage = NULL;
- size_t len;
- loff_t offset;
- int rc;
+ const char *off, *type, *mag, *p, *use = NULL;
+ struct wipe_desc *wp;
+ int rc, ispt = 0;
+
+ *len = 0;
/* superblocks */
if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) {
rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
if (!rc)
- rc = blkid_probe_lookup_value(pr, "SBMAGIC", &mag, &len);
+ rc = blkid_probe_lookup_value(pr, "SBMAGIC", &mag, len);
if (rc)
- return wp;
+ return NULL;
/* partitions */
} else if (blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL) == 0) {
rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
if (!rc)
- rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, &len);
+ rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, len);
if (rc)
- return wp;
- usage = "partition table";
+ return NULL;
+ use = N_("partition-table");
+ ispt = 1;
} else
- return wp;
+ return NULL;
- if (type_pattern && !match_fstype(type, type_pattern))
- return wp;
+ *offset = strtoll(off, NULL, 10);
+
+ /* Filter out by -t <type> */
+ if (ctl->type_pattern && !match_fstype(type, ctl->type_pattern))
+ return NULL;
- offset = strtoll(off, NULL, 10);
+ /* Filter out by -o <offset> */
+ if (ctl->offsets) {
+ struct wipe_desc *w = NULL;
- wp = add_offset(wp, offset, 0);
+ for (w = ctl->offsets; w; w = w->next) {
+ if (w->offset == *offset)
+ break;
+ }
+ if (!w)
+ return NULL;
+
+ w->on_disk = 1; /* mark as "found" */
+ }
+
+ wp = add_offset(wp0, *offset);
if (!wp)
return NULL;
- if (usage || blkid_probe_lookup_value(pr, "USAGE", &usage, NULL) == 0)
- wp->usage = xstrdup(usage);
+ if (use || blkid_probe_lookup_value(pr, "USAGE", &use, NULL) == 0)
+ wp->usage = xstrdup(use);
wp->type = xstrdup(type);
wp->on_disk = 1;
+ wp->is_parttable = ispt ? 1 : 0;
- wp->magic = xmalloc(len);
- memcpy(wp->magic, mag, len);
- wp->len = len;
+ wp->magic = xmalloc(*len);
+ memcpy(wp->magic, mag, *len);
+ wp->len = *len;
if (blkid_probe_lookup_value(pr, "LABEL", &p, NULL) == 0)
wp->label = xstrdup(p);
static blkid_probe
new_probe(const char *devname, int mode)
{
- blkid_probe pr;
+ blkid_probe pr = NULL;
if (!devname)
return NULL;
if (mode) {
- int fd = open(devname, mode);
+ int fd = open(devname, mode | O_NONBLOCK);
if (fd < 0)
goto error;
pr = blkid_new_probe();
- if (pr && blkid_probe_set_device(pr, fd, 0, 0))
+ if (!pr || blkid_probe_set_device(pr, fd, 0, 0) != 0) {
+ close(fd);
goto error;
+ }
} else
pr = blkid_new_probe_from_filename(devname);
goto error;
blkid_probe_enable_superblocks(pr, 1);
- blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC |
- BLKID_SUBLKS_TYPE | BLKID_SUBLKS_USAGE |
- BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
+ blkid_probe_set_superblocks_flags(pr,
+ BLKID_SUBLKS_MAGIC | /* return magic string and offset */
+ BLKID_SUBLKS_TYPE | /* return superblock type */
+ BLKID_SUBLKS_USAGE | /* return USAGE= */
+ BLKID_SUBLKS_LABEL | /* return LABEL= */
+ BLKID_SUBLKS_UUID | /* return UUID= */
+ BLKID_SUBLKS_BADCSUM); /* accept bad checksums */
blkid_probe_enable_partitions(pr, 1);
- blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC);
-
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC |
+ BLKID_PARTS_FORCE_GPT);
return pr;
error:
+ blkid_free_probe(pr);
err(EXIT_FAILURE, _("error: %s: probing initialization failed"), devname);
- return NULL;
}
-static struct wipe_desc *
-read_offsets(struct wipe_desc *wp, const char *devname)
+static struct wipe_desc *read_offsets(struct wipe_control *ctl)
{
- blkid_probe pr = new_probe(devname, 0);
+ blkid_probe pr = new_probe(ctl->devname, 0);
+ struct wipe_desc *wp0 = NULL;
if (!pr)
return NULL;
while (blkid_do_probe(pr) == 0) {
- wp = get_desc_for_probe(wp, pr);
- if (!wp)
- break;
+ size_t len = 0;
+ loff_t offset = 0;
+
+ /* add a new offset to wp0 */
+ get_desc_for_probe(ctl, &wp0, pr, &offset, &len);
+
+ /* hide last detected signature and scan again */
+ if (len) {
+ blkid_probe_hide_range(pr, offset, len);
+ blkid_probe_step_back(pr);
+ }
}
blkid_free_probe(pr);
- return wp;
+ return wp0;
}
-static void
-free_wipe(struct wipe_desc *wp)
+static void free_wipe(struct wipe_desc *wp)
{
while (wp) {
struct wipe_desc *next = wp->next;
}
}
-static void do_wipe_real(blkid_probe pr, const char *devname, struct wipe_desc *w, int noact, int quiet)
+static void do_wipe_real(struct wipe_control *ctl, blkid_probe pr,
+ struct wipe_desc *w)
{
size_t i;
- if (blkid_do_wipe(pr, noact))
- warn(_("%s: failed to erase %s magic string at offset 0x%08jx"),
- devname, w->type, w->offset);
+ if (blkid_do_wipe(pr, ctl->noact) != 0)
+ err(EXIT_FAILURE, _("%s: failed to erase %s magic string at offset 0x%08jx"),
+ ctl->devname, w->type, (intmax_t)w->offset);
- if (quiet)
+ if (ctl->quiet)
return;
- printf(_("%s: %zd bytes were erased at offset 0x%08jx (%s): "),
- devname, w->len, w->offset, w->type);
+ printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
+ "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
+ w->len),
+ ctl->devname, w->len, (intmax_t)w->offset, w->type);
for (i = 0; i < w->len; i++) {
printf("%02x", w->magic[i]);
putchar('\n');
}
-static struct wipe_desc *
-do_wipe(struct wipe_desc *wp, const char *devname, int noact, int all, int quiet)
+static void do_backup(struct wipe_desc *wp, const char *base)
{
- blkid_probe pr = new_probe(devname, O_RDWR | O_EXCL);
- struct wipe_desc *w, *wp0 = clone_offset(wp);
- int zap = all ? 1 : wp->zap;
+ char *fname = NULL;
+ int fd;
+
+ xasprintf(&fname, "%s0x%08jx.bak", base, (intmax_t)wp->offset);
+
+ fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ goto err;
+ if (write_all(fd, wp->magic, wp->len) != 0)
+ goto err;
+ close(fd);
+ free(fname);
+ return;
+err:
+ err(EXIT_FAILURE, _("%s: failed to create a signature backup"), fname);
+}
- if (!pr)
- return NULL;
+#ifdef BLKRRPART
+static void rereadpt(int fd, const char *devname)
+{
+ struct stat st;
+ int try = 0;
- while (blkid_do_probe(pr) == 0) {
- wp = get_desc_for_probe(wp, pr);
- if (!wp)
+ if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
+ return;
+
+ do {
+ /*
+ * Unfortunately, it's pretty common that the first re-read
+ * without delay is uncuccesful. The reason is probably kernel
+ * and/or udevd. Let's wait a moment and try more attempts.
+ */
+ xusleep(25000);
+
+ errno = 0;
+ ioctl(fd, BLKRRPART);
+ if (errno != EBUSY)
break;
+ } while (try++ < 4);
- /* Check if offset is in provided list */
- w = wp0;
- while(w && w->offset != wp->offset)
- w = w->next;
- if (wp0 && !w)
- continue;
+ printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname);
+}
+#endif
- /* Mark done if found in provided list */
- if (w)
- w->on_disk = wp->on_disk;
+static int do_wipe(struct wipe_control *ctl)
+{
+ int mode = O_RDWR, reread = 0, need_force = 0;
+ blkid_probe pr;
+ char *backup = NULL;
+ struct wipe_desc *w;
- if (!wp->on_disk)
- continue;
+ if (!ctl->force)
+ mode |= O_EXCL;
+
+ pr = new_probe(ctl->devname, mode);
+ if (!pr)
+ return -errno;
- if (zap)
- do_wipe_real(pr, devname, wp, noact, quiet);
+ if (ctl->backup) {
+ const char *home = getenv ("HOME");
+ char *tmp = xstrdup(ctl->devname);
+
+ if (!home)
+ errx(EXIT_FAILURE, _("failed to create a signature backup, $HOME undefined"));
+ xasprintf (&backup, "%s/wipefs-%s-", home, basename(tmp));
+ free(tmp);
}
- for (w = wp0; w != NULL; w = w->next) {
- if (!w->on_disk && !quiet)
- warnx(_("%s: offset 0x%jx not found"), devname, w->offset);
+ while (blkid_do_probe(pr) == 0) {
+ int wiped = 0;
+ size_t len = 0;
+ loff_t offset = 0;
+ struct wipe_desc *wp;
+
+ wp = get_desc_for_probe(ctl, NULL, pr, &offset, &len);
+ if (!wp)
+ goto done;
+
+ if (!ctl->force
+ && wp->is_parttable
+ && !blkid_probe_is_wholedisk(pr)) {
+ warnx(_("%s: ignoring nested \"%s\" partition table "
+ "on non-whole disk device"), ctl->devname, wp->type);
+ need_force = 1;
+ goto done;
+ }
+
+ if (backup)
+ do_backup(wp, backup);
+ do_wipe_real(ctl, pr, wp);
+ if (wp->is_parttable)
+ reread = 1;
+ wiped = 1;
+ done:
+ if (!wiped && len) {
+ /* if the offset has not been wiped (probably because
+ * filtered out by -t or -o) we need to hide it for
+ * libblkid to try another magic string for the same
+ * superblock, otherwise libblkid will continue with
+ * another superblock. Don't forget that the same
+ * superblock could be detected by more magic strings
+ * */
+ blkid_probe_hide_range(pr, offset, len);
+ blkid_probe_step_back(pr);
+ }
+ free_wipe(wp);
+ }
+
+ for (w = ctl->offsets; w; w = w->next) {
+ if (!w->on_disk && !ctl->quiet)
+ warnx(_("%s: offset 0x%jx not found"),
+ ctl->devname, (uintmax_t)w->offset);
}
+ if (need_force)
+ warnx(_("Use the --force option to force erase."));
+
fsync(blkid_probe_get_fd(pr));
+
+#ifdef BLKRRPART
+ if (reread && (mode & O_EXCL)) {
+ if (ctl->ndevs > 1) {
+ /*
+ * We're going to probe more device, let's postpone
+ * re-read PT ioctl until all is erased to avoid
+ * situation we erase PT on /dev/sda before /dev/sdaN
+ * devices are processed.
+ */
+ if (!ctl->reread)
+ ctl->reread = xcalloc(ctl->ndevs, sizeof(char *));
+
+ ctl->reread[ctl->nrereads++] = ctl->devname;
+ } else
+ rereadpt(blkid_probe_get_fd(pr), ctl->devname);
+ }
+#endif
+
close(blkid_probe_get_fd(pr));
blkid_free_probe(pr);
- free_wipe(wp0);
-
- return wp;
+ free(backup);
+ return 0;
}
static void __attribute__((__noreturn__))
-usage(FILE *out)
+usage(void)
{
- fputs(_("\nUsage:\n"), out);
- fprintf(out,
- _(" %s [options] <device>\n"), program_invocation_short_name);
-
- fputs(_("\nOptions:\n"), out);
- fputs(_(" -a, --all wipe all magic strings (BE CAREFUL!)\n"
- " -h, --help show this help text\n"
- " -n, --no-act do everything except the actual write() call\n"
- " -o, --offset <num> offset to erase, in bytes\n"
- " -p, --parsable print out in parsable instead of printable format\n"
- " -q, --quiet suppress output messages\n"
- " -t, --types <list> limit the set of filesystem, RAIDs or partition tables\n"
- " -V, --version output version information and exit\n"), out);
-
- fprintf(out, _("\nFor more information see wipefs(8).\n"));
-
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ size_t i;
+
+ fputs(USAGE_HEADER, stdout);
+ printf(_(" %s [options] <device>\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ puts(_("Wipe signatures from a device."));
+
+ fputs(USAGE_OPTIONS, stdout);
+ puts(_(" -a, --all wipe all magic strings (BE CAREFUL!)"));
+ puts(_(" -b, --backup create a signature backup in $HOME"));
+ puts(_(" -f, --force force erasure"));
+ puts(_(" -i, --noheadings don't print headings"));
+ puts(_(" -J, --json use JSON output format"));
+ puts(_(" -n, --no-act do everything except the actual write() call"));
+ puts(_(" -o, --offset <num> offset to erase, in bytes"));
+ puts(_(" -O, --output <list> COLUMNS to display (see below)"));
+ puts(_(" -p, --parsable print out in parsable instead of printable format"));
+ puts(_(" -q, --quiet suppress output messages"));
+ puts(_(" -t, --types <list> limit the set of filesystem, RAIDs or partition tables"));
+
+ printf(USAGE_HELP_OPTIONS(21));
+
+ fputs(USAGE_ARGUMENTS, stdout);
+ printf(USAGE_ARG_SIZE(_("<num>")));
+
+ fputs(USAGE_COLUMNS, stdout);
+ for (i = 0; i < ARRAY_SIZE(infos); i++)
+ fprintf(stdout, " %8s %s\n", infos[i].name, _(infos[i].help));
+
+ printf(USAGE_MAN_TAIL("wipefs(8)"));
+ exit(EXIT_SUCCESS);
}
int
main(int argc, char **argv)
{
- struct wipe_desc *wp0 = NULL, *wp;
- int c, all = 0, has_offset = 0, noact = 0, quiet = 0;
- int mode = WP_MODE_PRETTY;
+ struct wipe_control ctl = { .devname = NULL };
+ int c;
+ char *outarg = NULL;
static const struct option longopts[] = {
- { "all", 0, 0, 'a' },
- { "help", 0, 0, 'h' },
- { "no-act", 0, 0, 'n' },
- { "offset", 1, 0, 'o' },
- { "parsable", 0, 0, 'p' },
- { "quiet", 0, 0, 'q' },
- { "types", 1, 0, 't' },
- { "version", 0, 0, 'V' },
- { NULL, 0, 0, 0 }
+ { "all", no_argument, NULL, 'a' },
+ { "backup", no_argument, NULL, 'b' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "no-act", no_argument, NULL, 'n' },
+ { "offset", required_argument, NULL, 'o' },
+ { "parsable", no_argument, NULL, 'p' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "types", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "json", no_argument, NULL, 'J'},
+ { "noheadings",no_argument, NULL, 'i'},
+ { "output", required_argument, NULL, 'O'},
+ { NULL, 0, NULL, 0 }
};
- static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
- { 'a','o' },
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'O','a','o' },
{ 0 }
};
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
- while ((c = getopt_long(argc, argv, "ahno:pqt:V", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "abfhiJnO:o:pqt:V", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
switch(c) {
case 'a':
- all++;
+ ctl.all = 1;
break;
- case 'h':
- usage(stdout);
+ case 'b':
+ ctl.backup = 1;
+ break;
+ case 'f':
+ ctl.force = 1;
+ break;
+ case 'J':
+ ctl.json = 1;
+ break;
+ case 'i':
+ ctl.no_headings = 1;
+ break;
+ case 'O':
+ outarg = optarg;
break;
case 'n':
- noact++;
+ ctl.noact = 1;
break;
case 'o':
- wp0 = add_offset(wp0, strtosize_or_err(optarg,
- _("invalid offset argument")), 1);
- has_offset++;
+ add_offset(&ctl.offsets, strtosize_or_err(optarg,
+ _("invalid offset argument")));
break;
case 'p':
- mode = WP_MODE_PARSABLE;
+ ctl.parsable = 1;
+ ctl.no_headings = 1;
break;
case 'q':
- quiet++;
+ ctl.quiet = 1;
break;
case 't':
- type_pattern = optarg;
+ ctl.type_pattern = optarg;
break;
+
+ case 'h':
+ usage();
case 'V':
- printf(_("%s from %s\n"), program_invocation_short_name,
- PACKAGE_STRING);
- return EXIT_SUCCESS;
+ print_version(EXIT_SUCCESS);
default:
- usage(stderr);
- break;
+ errtryhelp(EXIT_FAILURE);
}
}
- if (optind == argc)
- usage(stderr);
+ if (optind == argc) {
+ warnx(_("no device specified"));
+ errtryhelp(EXIT_FAILURE);
+
+ }
+
+ if (ctl.backup && !(ctl.all || ctl.offsets))
+ warnx(_("The --backup option is meaningless in this context"));
- if (!all && !has_offset) {
+ if (!ctl.all && !ctl.offsets) {
/*
* Print only
*/
+ if (ctl.parsable) {
+ /* keep it backward compatible */
+ columns[ncolumns++] = COL_OFFSET;
+ columns[ncolumns++] = COL_UUID;
+ columns[ncolumns++] = COL_LABEL;
+ columns[ncolumns++] = COL_TYPE;
+ } else {
+ /* default, may be modified by -O <list> */
+ columns[ncolumns++] = COL_DEVICE;
+ columns[ncolumns++] = COL_OFFSET;
+ columns[ncolumns++] = COL_TYPE;
+ columns[ncolumns++] = COL_UUID;
+ columns[ncolumns++] = COL_LABEL;
+ }
+
+ if (outarg
+ && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
+ &ncolumns, column_name_to_id) < 0)
+ return EXIT_FAILURE;
+
+ init_output(&ctl);
+
while (optind < argc) {
- wp0 = read_offsets(NULL, argv[optind++]);
- if (wp0)
- print_all(wp0, mode);
- free_wipe(wp0);
+ struct wipe_desc *wp;
+
+ ctl.devname = argv[optind++];
+ wp = read_offsets(&ctl);
+ if (wp)
+ add_to_output(&ctl, wp);
+ free_wipe(wp);
}
+ finalize_output(&ctl);
} else {
/*
* Erase
*/
+ ctl.ndevs = argc - optind;
+
while (optind < argc) {
- wp = clone_offset(wp0);
- wp = do_wipe(wp, argv[optind++], noact, all, quiet);
- free_wipe(wp);
+ ctl.devname = argv[optind++];
+ do_wipe(&ctl);
+ ctl.ndevs--;
}
- }
+#ifdef BLKRRPART
+ /* Re-read partition tables on whole-disk devices. This is
+ * postponed until all is done to avoid conflicts.
+ */
+ for (size_t i = 0; i < ctl.nrereads; i++) {
+ char *devname = ctl.reread[i];
+ int fd = open(devname, O_RDONLY);
+
+ if (fd >= 0) {
+ rereadpt(fd, devname);
+ close(fd);
+ }
+ }
+ free(ctl.reread);
+#endif
+ }
return EXIT_SUCCESS;
}