(root)/
util-linux-2.39/
misc-utils/
wipefs.c
       1  /*
       2   * wipefs - utility to wipe filesystems from device
       3   *
       4   * Copyright (C) 2009 Red Hat, Inc. All rights reserved.
       5   * Written by Karel Zak <kzak@redhat.com>
       6   *
       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.
      11   *
      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.
      16   *
      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.
      20   */
      21  #include <sys/stat.h>
      22  #include <sys/types.h>
      23  #include <ctype.h>
      24  #include <errno.h>
      25  #include <fcntl.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  #include <unistd.h>
      29  #include <getopt.h>
      30  #include <string.h>
      31  #include <limits.h>
      32  #include <libgen.h>
      33  
      34  #include <blkid.h>
      35  #include <libsmartcols.h>
      36  
      37  #include "nls.h"
      38  #include "xalloc.h"
      39  #include "strutils.h"
      40  #include "all-io.h"
      41  #include "match.h"
      42  #include "c.h"
      43  #include "closestream.h"
      44  #include "optutils.h"
      45  #include "blkdev.h"
      46  
      47  struct wipe_desc {
      48  	loff_t		offset;		/* magic string offset */
      49  	size_t		len;		/* length of magic string */
      50  	unsigned char	*magic;		/* magic string */
      51  
      52  	char		*usage;		/* raid, filesystem, ... */
      53  	char		*type;		/* FS type */
      54  	char		*label;		/* FS label */
      55  	char		*uuid;		/* FS uuid */
      56  
      57  	struct wipe_desc	*next;
      58  
      59  	unsigned int	on_disk : 1,
      60  			is_parttable : 1;
      61  
      62  };
      63  
      64  struct wipe_control {
      65  	char		*devname;
      66  	const char	*type_pattern;		/* -t <pattern> */
      67  	const char	*lockmode;
      68  
      69  	struct libscols_table *outtab;
      70  	struct wipe_desc *offsets;		/* -o <offset> -o <offset> ... */
      71  
      72  	size_t		ndevs;			/* number of devices to probe */
      73  
      74  	char		**reread;		/* devices to BLKRRPART */
      75  	size_t		nrereads;		/* size of reread */
      76  
      77  	unsigned int	noact : 1,
      78  			all : 1,
      79  			quiet : 1,
      80  			backup : 1,
      81  			force : 1,
      82  			json : 1,
      83  			no_headings : 1,
      84  			parsable : 1;
      85  };
      86  
      87  
      88  /* column IDs */
      89  enum {
      90  	COL_UUID = 0,
      91  	COL_LABEL,
      92  	COL_LEN,
      93  	COL_TYPE,
      94  	COL_OFFSET,
      95  	COL_USAGE,
      96  	COL_DEVICE
      97  };
      98  
      99  /* column names */
     100  struct colinfo {
     101  	const char *name;	/* header */
     102  	double whint;		/* width hint (N < 1 is in percent of termwidth) */
     103  	int flags;		/* SCOLS_FL_* */
     104  	const char *help;
     105  };
     106  
     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")}
     116  };
     117  
     118  static int columns[ARRAY_SIZE(infos) * 2];
     119  static size_t ncolumns;
     120  
     121  static int column_name_to_id(const char *name, size_t namesz)
     122  {
     123  	size_t i;
     124  
     125  	assert(name);
     126  
     127  	for (i = 0; i < ARRAY_SIZE(infos); i++) {
     128  		const char *cn = infos[i].name;
     129  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     130  			return i;
     131  	}
     132  	warnx(_("unknown column: %s"), name);
     133  	return -1;
     134  }
     135  
     136  static int get_column_id(size_t num)
     137  {
     138  	assert(num < ncolumns);
     139  	assert(columns[num] < (int)ARRAY_SIZE(infos));
     140  	return columns[num];
     141  }
     142  
     143  static const struct colinfo *get_column_info(int num)
     144  {
     145  	return &infos[get_column_id(num)];
     146  }
     147  
     148  
     149  static void init_output(struct wipe_control *ctl)
     150  {
     151  	struct libscols_table *tb;
     152  	size_t i;
     153  
     154  	scols_init_debug(0);
     155  	tb = scols_new_table();
     156  	if (!tb)
     157  		err(EXIT_FAILURE, _("failed to allocate output table"));
     158  
     159  	if (ctl->json) {
     160  		scols_table_enable_json(tb, 1);
     161  		scols_table_set_name(tb, "signatures");
     162  	}
     163  	scols_table_enable_noheadings(tb, ctl->no_headings);
     164  
     165  	if (ctl->parsable) {
     166  		scols_table_enable_raw(tb, 1);
     167  		scols_table_set_column_separator(tb, ",");
     168  	}
     169  
     170  	for (i = 0; i < ncolumns; i++) {
     171  		const struct colinfo *col = get_column_info(i);
     172  		struct libscols_column *cl;
     173  
     174  		cl = scols_table_new_column(tb, col->name, col->whint,
     175  					    col->flags);
     176  		if (!cl)
     177  			err(EXIT_FAILURE,
     178  			    _("failed to initialize output column"));
     179  		if (ctl->json) {
     180  			int id = get_column_id(i);
     181  
     182  			if (id == COL_LEN)
     183  				scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
     184  		}
     185  	}
     186  	ctl->outtab = tb;
     187  }
     188  
     189  static void finalize_output(struct wipe_control *ctl)
     190  {
     191  	scols_print_table(ctl->outtab);
     192  	scols_unref_table(ctl->outtab);
     193  }
     194  
     195  static void fill_table_row(struct wipe_control *ctl, struct wipe_desc *wp)
     196  {
     197  	static struct libscols_line *ln;
     198  	size_t i;
     199  
     200  	ln = scols_table_new_line(ctl->outtab, NULL);
     201  	if (!ln)
     202  		errx(EXIT_FAILURE, _("failed to allocate output line"));
     203  
     204  	for (i = 0; i < ncolumns; i++) {
     205  		char *str = NULL;
     206  
     207  		switch (get_column_id(i)) {
     208  		case COL_UUID:
     209  			if (wp->uuid)
     210  				str = xstrdup(wp->uuid);
     211  			break;
     212  		case COL_LABEL:
     213  			if (wp->label)
     214  				str = xstrdup(wp->label);
     215  			break;
     216  		case COL_OFFSET:
     217  			xasprintf(&str, "0x%jx", (intmax_t)wp->offset);
     218  			break;
     219  		case COL_LEN:
     220  			xasprintf(&str, "%zu", wp->len);
     221  			break;
     222  		case COL_USAGE:
     223  			if (wp->usage)
     224  				str = xstrdup(wp->usage);
     225  			break;
     226  		case COL_TYPE:
     227  			if (wp->type)
     228  				str = xstrdup(wp->type);
     229  			break;
     230  		case COL_DEVICE:
     231  			if (ctl->devname) {
     232  				char *dev = xstrdup(ctl->devname);
     233  				str = xstrdup(basename(dev));
     234  				free(dev);
     235  			}
     236  			break;
     237  		default:
     238  			abort();
     239  		}
     240  
     241  		if (str && scols_line_refer_data(ln, i, str))
     242  			errx(EXIT_FAILURE, _("failed to add output data"));
     243  	}
     244  }
     245  
     246  static void add_to_output(struct wipe_control *ctl, struct wipe_desc *wp)
     247  {
     248  	for (/*nothing*/; wp; wp = wp->next)
     249  		fill_table_row(ctl, wp);
     250  }
     251  
     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)
     254  {
     255  	struct wipe_desc *wp, *last = NULL;
     256  
     257  	if (wp0) {
     258  		/* check if already exists */
     259  		for (wp = *wp0; wp; wp = wp->next) {
     260  			if (wp->offset == offset)
     261  				return wp;
     262  			last = wp;
     263  		}
     264  	}
     265  
     266  	wp = xcalloc(1, sizeof(struct wipe_desc));
     267  	wp->offset = offset;
     268  	wp->next = NULL;
     269  
     270  	if (last)
     271  		last->next = wp;
     272  	if (wp0 && !*wp0)
     273  		*wp0 = wp;
     274  	return wp;
     275  }
     276  
     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)
     280   *
     281   * The function always returns offset and len if libblkid detected something.
     282   */
     283  static struct wipe_desc *get_desc_for_probe(struct wipe_control *ctl,
     284  					    struct wipe_desc **wp0,
     285  					    blkid_probe pr,
     286  					    loff_t *offset,
     287  					    size_t *len)
     288  {
     289  	const char *off, *type, *mag, *p, *use = NULL;
     290  	struct wipe_desc *wp;
     291  	int rc, ispt = 0;
     292  
     293  	*len = 0;
     294  
     295  	/* superblocks */
     296  	if (blkid_probe_lookup_value(pr, "TYPE", &type, NULL) == 0) {
     297  		rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
     298  		if (!rc)
     299  			rc = blkid_probe_lookup_value(pr, "SBMAGIC", &mag, len);
     300  		if (rc)
     301  			return NULL;
     302  
     303  	/* partitions */
     304  	} else if (blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL) == 0) {
     305  		rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
     306  		if (!rc)
     307  			rc = blkid_probe_lookup_value(pr, "PTMAGIC", &mag, len);
     308  		if (rc)
     309  			return NULL;
     310  		use = N_("partition-table");
     311  		ispt = 1;
     312  	} else
     313  		return NULL;
     314  
     315  	errno = 0;
     316  	*offset = strtoll(off, NULL, 10);
     317  	if (errno)
     318  		return NULL;
     319  
     320  	/* Filter out by -t <type> */
     321  	if (ctl->type_pattern && !match_fstype(type, ctl->type_pattern))
     322  		return NULL;
     323  
     324  	/* Filter out by -o <offset> */
     325  	if (ctl->offsets) {
     326  		struct wipe_desc *w = NULL;
     327  
     328  		for (w = ctl->offsets; w; w = w->next) {
     329  			if (w->offset == *offset)
     330  				break;
     331  		}
     332  		if (!w)
     333  			return NULL;
     334  
     335  		w->on_disk = 1; /* mark as "found" */
     336  	}
     337  
     338  	wp = add_offset(wp0, *offset);
     339  	if (!wp)
     340  		return NULL;
     341  
     342  	if (use || blkid_probe_lookup_value(pr, "USAGE", &use, NULL) == 0)
     343  		wp->usage = xstrdup(use);
     344  
     345  	wp->type = xstrdup(type);
     346  	wp->on_disk = 1;
     347  	wp->is_parttable = ispt ? 1 : 0;
     348  
     349  	wp->magic = xmalloc(*len);
     350  	memcpy(wp->magic, mag, *len);
     351  	wp->len = *len;
     352  
     353  	if (blkid_probe_lookup_value(pr, "LABEL", &p, NULL) == 0)
     354  		wp->label = xstrdup(p);
     355  
     356  	if (blkid_probe_lookup_value(pr, "UUID", &p, NULL) == 0)
     357  		wp->uuid = xstrdup(p);
     358  
     359  	return wp;
     360  }
     361  
     362  static blkid_probe
     363  new_probe(const char *devname, int mode)
     364  {
     365  	blkid_probe pr = NULL;
     366  
     367  	if (!devname)
     368  		return NULL;
     369  
     370  	if (mode) {
     371  		int fd = open(devname, mode | O_NONBLOCK);
     372  		if (fd < 0)
     373  			goto error;
     374  
     375  		pr = blkid_new_probe();
     376  		if (!pr || blkid_probe_set_device(pr, fd, 0, 0) != 0) {
     377  			close(fd);
     378  			goto error;
     379  		}
     380  	} else
     381  		pr = blkid_new_probe_from_filename(devname);
     382  
     383  	if (!pr)
     384  		goto error;
     385  
     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 */
     394  
     395  	blkid_probe_enable_partitions(pr, 1);
     396  	blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC |
     397  					     BLKID_PARTS_FORCE_GPT);
     398  	return pr;
     399  error:
     400  	blkid_free_probe(pr);
     401  	err(EXIT_FAILURE, _("error: %s: probing initialization failed"), devname);
     402  }
     403  
     404  static struct wipe_desc *read_offsets(struct wipe_control *ctl)
     405  {
     406  	blkid_probe pr = new_probe(ctl->devname, 0);
     407  	struct wipe_desc *wp0 = NULL;
     408  
     409  	if (!pr)
     410  		return NULL;
     411  
     412  	while (blkid_do_probe(pr) == 0) {
     413  		size_t len = 0;
     414  		loff_t offset = 0;
     415  
     416  		/* add a new offset to wp0 */
     417  		get_desc_for_probe(ctl, &wp0, pr, &offset, &len);
     418  
     419  		/* hide last detected signature and scan again */
     420  		if (len) {
     421  			blkid_probe_hide_range(pr, offset, len);
     422  			blkid_probe_step_back(pr);
     423  		}
     424  	}
     425  
     426  	blkid_free_probe(pr);
     427  	return wp0;
     428  }
     429  
     430  static void free_wipe(struct wipe_desc *wp)
     431  {
     432  	while (wp) {
     433  		struct wipe_desc *next = wp->next;
     434  
     435  		free(wp->usage);
     436  		free(wp->type);
     437  		free(wp->magic);
     438  		free(wp->label);
     439  		free(wp->uuid);
     440  		free(wp);
     441  
     442  		wp = next;
     443  	}
     444  }
     445  
     446  static void do_wipe_real(struct wipe_control *ctl, blkid_probe pr,
     447  			struct wipe_desc *w)
     448  {
     449  	size_t i;
     450  
     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);
     454  
     455  	if (ctl->quiet)
     456  		return;
     457  
     458  	printf(P_("%s: %zd byte was erased at offset 0x%08jx (%s): ",
     459  		  "%s: %zd bytes were erased at offset 0x%08jx (%s): ",
     460  		  w->len),
     461  	       ctl->devname, w->len, (intmax_t)w->offset, w->type);
     462  
     463  	for (i = 0; i < w->len; i++) {
     464  		printf("%02x", w->magic[i]);
     465  		if (i + 1 < w->len)
     466  			fputc(' ', stdout);
     467  	}
     468  	putchar('\n');
     469  }
     470  
     471  static void do_backup(struct wipe_desc *wp, const char *base)
     472  {
     473  	char *fname = NULL;
     474  	int fd;
     475  
     476  	xasprintf(&fname, "%s0x%08jx.bak", base, (intmax_t)wp->offset);
     477  
     478  	fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
     479  	if (fd < 0)
     480  		goto err;
     481  	if (write_all(fd, wp->magic, wp->len) != 0)
     482  		goto err;
     483  	close(fd);
     484  	free(fname);
     485  	return;
     486  err:
     487  	err(EXIT_FAILURE, _("%s: failed to create a signature backup"), fname);
     488  }
     489  
     490  #ifdef BLKRRPART
     491  static void rereadpt(int fd, const char *devname)
     492  {
     493  	struct stat st;
     494  	int try = 0;
     495  
     496  	if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
     497  		return;
     498  
     499  	do {
     500  		/*
     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.
     504  		 */
     505  		xusleep(250000);
     506  
     507  		errno = 0;
     508  		ioctl(fd, BLKRRPART);
     509  		if (errno != EBUSY)
     510  			break;
     511  	} while (try++ < 4);
     512  
     513  	printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname);
     514  }
     515  #endif
     516  
     517  static int do_wipe(struct wipe_control *ctl)
     518  {
     519  	int mode = O_RDWR, reread = 0, need_force = 0;
     520  	blkid_probe pr;
     521  	char *backup = NULL;
     522  	struct wipe_desc *w;
     523  
     524  	if (!ctl->force)
     525  		mode |= O_EXCL;
     526  
     527  	pr = new_probe(ctl->devname, mode);
     528  	if (!pr)
     529  		return -errno;
     530  
     531  	if (blkdev_lock(blkid_probe_get_fd(pr),
     532  			ctl->devname, ctl->lockmode) != 0) {
     533  		blkid_free_probe(pr);
     534  		return -1;
     535  	}
     536  
     537  	if (ctl->backup) {
     538  		const char *home = getenv ("HOME");
     539  		char *tmp = xstrdup(ctl->devname);
     540  
     541  		if (!home)
     542  			errx(EXIT_FAILURE, _("failed to create a signature backup, $HOME undefined"));
     543  		xasprintf (&backup, "%s/wipefs-%s-", home, basename(tmp));
     544  		free(tmp);
     545  	}
     546  
     547  	while (blkid_do_probe(pr) == 0) {
     548  		int wiped = 0;
     549  		size_t len = 0;
     550  		loff_t offset = 0;
     551  		struct wipe_desc *wp;
     552  
     553  		wp = get_desc_for_probe(ctl, NULL, pr, &offset, &len);
     554  		if (!wp)
     555  			goto done;
     556  
     557  		if (!ctl->force
     558  		    && wp->is_parttable
     559  		    && !blkid_probe_is_wholedisk(pr)) {
     560  			warnx(_("%s: ignoring nested \"%s\" partition table "
     561  				"on non-whole disk device"), ctl->devname, wp->type);
     562  			need_force = 1;
     563  			goto done;
     564  		}
     565  
     566  		if (backup)
     567  			do_backup(wp, backup);
     568  		do_wipe_real(ctl, pr, wp);
     569  		if (wp->is_parttable)
     570  			reread = 1;
     571  		wiped = 1;
     572  	done:
     573  		if (!wiped && len) {
     574  			/* if the offset has not been wiped (probably because
     575  			 * filtered out by -t or -o) we need to hide it for
     576  			 * libblkid to try another magic string for the same
     577  			 * superblock, otherwise libblkid will continue with
     578  			 * another superblock. Don't forget that the same
     579  			 * superblock could be detected by more magic strings
     580  			 * */
     581  			blkid_probe_hide_range(pr, offset, len);
     582  			blkid_probe_step_back(pr);
     583  		}
     584  		free_wipe(wp);
     585  	}
     586  
     587  	for (w = ctl->offsets; w; w = w->next) {
     588  		if (!w->on_disk && !ctl->quiet)
     589  			warnx(_("%s: offset 0x%jx not found"),
     590  					ctl->devname, (uintmax_t)w->offset);
     591  	}
     592  
     593  	if (need_force)
     594  		warnx(_("Use the --force option to force erase."));
     595  
     596  	if (fsync(blkid_probe_get_fd(pr)) != 0)
     597  		err(EXIT_FAILURE, _("%s: cannot flush modified buffers"),
     598  				ctl->devname);
     599  
     600  #ifdef BLKRRPART
     601  	if (reread && (mode & O_EXCL)) {
     602  		if (ctl->ndevs > 1) {
     603  			/*
     604  			 * We're going to probe more device, let's postpone
     605  			 * re-read PT ioctl until all is erased to avoid
     606  			 * situation we erase PT on /dev/sda before /dev/sdaN
     607  			 * devices are processed.
     608  			 */
     609  			if (!ctl->reread)
     610  				ctl->reread = xcalloc(ctl->ndevs, sizeof(char *));
     611  
     612  			ctl->reread[ctl->nrereads++] = ctl->devname;
     613  		} else
     614  			rereadpt(blkid_probe_get_fd(pr), ctl->devname);
     615  	}
     616  #endif
     617  
     618  	if (close(blkid_probe_get_fd(pr)) != 0)
     619  		err(EXIT_FAILURE, _("%s: close device failed"), ctl->devname);
     620  
     621  	blkid_free_probe(pr);
     622  	free(backup);
     623  	return 0;
     624  }
     625  
     626  
     627  static void __attribute__((__noreturn__))
     628  usage(void)
     629  {
     630  	size_t i;
     631  
     632  	fputs(USAGE_HEADER, stdout);
     633  	printf(_(" %s [options] <device>\n"), program_invocation_short_name);
     634  
     635  	fputs(USAGE_SEPARATOR, stdout);
     636  	puts(_("Wipe signatures from a device."));
     637  
     638  	fputs(USAGE_OPTIONS, stdout);
     639  	puts(_(" -a, --all           wipe all magic strings (BE CAREFUL!)"));
     640  	puts(_(" -b, --backup        create a signature backup in $HOME"));
     641  	puts(_(" -f, --force         force erasure"));
     642  	puts(_(" -i, --noheadings    don't print headings"));
     643  	puts(_(" -J, --json          use JSON output format"));
     644  	puts(_(" -n, --no-act        do everything except the actual write() call"));
     645  	puts(_(" -o, --offset <num>  offset to erase, in bytes"));
     646  	puts(_(" -O, --output <list> COLUMNS to display (see below)"));
     647  	puts(_(" -p, --parsable      print out in parsable instead of printable format"));
     648  	puts(_(" -q, --quiet         suppress output messages"));
     649  	puts(_(" -t, --types <list>  limit the set of filesystem, RAIDs or partition tables"));
     650  	printf(
     651  	     _("     --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
     652  
     653  	printf(USAGE_HELP_OPTIONS(21));
     654  
     655  	fputs(USAGE_ARGUMENTS, stdout);
     656  	printf(USAGE_ARG_SIZE(_("<num>")));
     657  
     658  	fputs(USAGE_COLUMNS, stdout);
     659  	for (i = 0; i < ARRAY_SIZE(infos); i++)
     660  		fprintf(stdout, " %8s  %s\n", infos[i].name, _(infos[i].help));
     661  
     662  	printf(USAGE_MAN_TAIL("wipefs(8)"));
     663  	exit(EXIT_SUCCESS);
     664  }
     665  
     666  
     667  int
     668  main(int argc, char **argv)
     669  {
     670  	struct wipe_control ctl = { .devname = NULL };
     671  	int c;
     672  	size_t i;
     673  	char *outarg = NULL;
     674  	enum {
     675  		OPT_LOCK = CHAR_MAX + 1,
     676  	};
     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  	    { "lock",      optional_argument, NULL, OPT_LOCK },
     683  	    { "no-act",    no_argument,       NULL, 'n' },
     684  	    { "offset",    required_argument, NULL, 'o' },
     685  	    { "parsable",  no_argument,       NULL, 'p' },
     686  	    { "quiet",     no_argument,       NULL, 'q' },
     687  	    { "types",     required_argument, NULL, 't' },
     688  	    { "version",   no_argument,       NULL, 'V' },
     689  	    { "json",      no_argument,       NULL, 'J'},
     690  	    { "noheadings",no_argument,       NULL, 'i'},
     691  	    { "output",    required_argument, NULL, 'O'},
     692  	    { NULL,        0, NULL, 0 }
     693  	};
     694  
     695  	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
     696  		{ 'O','a','o' },
     697  		{ 0 }
     698  	};
     699  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     700  
     701  	setlocale(LC_ALL, "");
     702  	bindtextdomain(PACKAGE, LOCALEDIR);
     703  	textdomain(PACKAGE);
     704  	close_stdout_atexit();
     705  
     706  	while ((c = getopt_long(argc, argv, "abfhiJnO:o:pqt:V", longopts, NULL)) != -1) {
     707  
     708  		err_exclusive_options(c, longopts, excl, excl_st);
     709  
     710  		switch(c) {
     711  		case 'a':
     712  			ctl.all = 1;
     713  			break;
     714  		case 'b':
     715  			ctl.backup = 1;
     716  			break;
     717  		case 'f':
     718  			ctl.force = 1;
     719  			break;
     720  		case 'J':
     721  			ctl.json = 1;
     722  			break;
     723  		case 'i':
     724  			ctl.no_headings = 1;
     725  			break;
     726  		case 'O':
     727  			outarg = optarg;
     728  			break;
     729  		case 'n':
     730  			ctl.noact = 1;
     731  			break;
     732  		case 'o':
     733  			add_offset(&ctl.offsets, strtosize_or_err(optarg,
     734  					 _("invalid offset argument")));
     735  			break;
     736  		case 'p':
     737  			ctl.parsable = 1;
     738  			ctl.no_headings = 1;
     739  			break;
     740  		case 'q':
     741  			ctl.quiet = 1;
     742  			break;
     743  		case 't':
     744  			ctl.type_pattern = optarg;
     745  			break;
     746  		case OPT_LOCK:
     747  			ctl.lockmode = "1";
     748  			if (optarg) {
     749  				if (*optarg == '=')
     750  					optarg++;
     751  				ctl.lockmode = optarg;
     752  			}
     753  			break;
     754  		case 'h':
     755  			usage();
     756  		case 'V':
     757  			print_version(EXIT_SUCCESS);
     758  		default:
     759  			errtryhelp(EXIT_FAILURE);
     760  		}
     761  	}
     762  
     763  	if (optind == argc) {
     764  		warnx(_("no device specified"));
     765  		errtryhelp(EXIT_FAILURE);
     766  
     767  	}
     768  
     769  	if (ctl.backup && !(ctl.all || ctl.offsets))
     770  		warnx(_("The --backup option is meaningless in this context"));
     771  
     772  	if (!ctl.all && !ctl.offsets) {
     773  		/*
     774  		 * Print only
     775  		 */
     776  		if (ctl.parsable) {
     777  			/* keep it backward compatible */
     778  			columns[ncolumns++] = COL_OFFSET;
     779  			columns[ncolumns++] = COL_UUID;
     780  			columns[ncolumns++] = COL_LABEL;
     781  			columns[ncolumns++] = COL_TYPE;
     782  		} else {
     783  			/* default, may be modified by -O <list> */
     784  			columns[ncolumns++] = COL_DEVICE;
     785  			columns[ncolumns++] = COL_OFFSET;
     786  			columns[ncolumns++] = COL_TYPE;
     787  			columns[ncolumns++] = COL_UUID;
     788  			columns[ncolumns++] = COL_LABEL;
     789  		}
     790  
     791  		if (outarg
     792  		    && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
     793  					     &ncolumns, column_name_to_id) < 0)
     794  			return EXIT_FAILURE;
     795  
     796  		init_output(&ctl);
     797  
     798  		while (optind < argc) {
     799  			struct wipe_desc *wp;
     800  
     801  			ctl.devname = argv[optind++];
     802  			wp = read_offsets(&ctl);
     803  			if (wp)
     804  				add_to_output(&ctl, wp);
     805  			free_wipe(wp);
     806  		}
     807  		finalize_output(&ctl);
     808  	} else {
     809  		/*
     810  		 * Erase
     811  		 */
     812  		ctl.ndevs = argc - optind;
     813  
     814  		while (optind < argc) {
     815  			ctl.devname = argv[optind++];
     816  			do_wipe(&ctl);
     817  			ctl.ndevs--;
     818  		}
     819  
     820  #ifdef BLKRRPART
     821  		/* Re-read partition tables on whole-disk devices. This is
     822  		 * postponed until all is done to avoid conflicts.
     823  		 */
     824  		for (i = 0; i < ctl.nrereads; i++) {
     825  			char *devname = ctl.reread[i];
     826  			int fd = open(devname, O_RDONLY);
     827  
     828  			if (fd >= 0) {
     829  				rereadpt(fd, devname);
     830  				close(fd);
     831  			}
     832  		}
     833  		free(ctl.reread);
     834  #endif
     835  	}
     836  	return EXIT_SUCCESS;
     837  }