sfdisk: add --backup and --backup-file

The option --backup force sfdisk to store *all* fragments of the
partition table (including MBR partition tables store in the
extended partitions) to

	$HOME/sfdisk-<devname>-<offset>.bak

The options -O, -backup-file <path> allows to override the default
path, but sfdisk still appends <devname>-<offset>.bak to the <path>.
The backup files always contain only raw data from the device, so it's
possible to use dd(1) to restore original data on the device.

The original sfdisk also supported -O <file>, but semantic was little
bit different:

   - all was based on 512-byte sectors
   - all sectors was stored to the one file in format
	  <offset>|<sector>|<offset>|...

this original concept makes the backup files specific to sfdisk and with
dependence on sector size.

The new concept is the same we already use for wipefs(8) backup files.

Example (disk with GPT):

   # sfdisk /dev/sda --backup

   Welcome to sfdisk (util-linux 2.25.202-f4deb-dirty).
   Changes will remain in memory only, until you decide to write them.
   Be careful before using the write command.

   Backup files:
          PMBR (offset     0, size   512): /root/sfdisk-sda-0x00000000.bak
    GPT Header (offset   512, size   512): /root/sfdisk-sda-0x00000200.bak
   GPT Entries (offset  1024, size 16384): /root/sfdisk-sda-0x00000400.bak

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2014-09-19 11:31:07 +02:00
parent db48b6a189
commit ab02d87e4f
1 changed files with 113 additions and 13 deletions

View File

@ -39,6 +39,7 @@
#include "closestream.h"
#include "colors.h"
#include "blkdev.h"
#include "all-io.h"
#include "libfdisk.h"
#include "fdisk-list.h"
@ -75,6 +76,7 @@ struct sfdisk {
int act; /* action */
int partno; /* -N <partno>, default -1 */
const char *label; /* --label <label> */
const char *backup_file; /* -O <path> */
struct fdisk_context *cxt; /* libfdisk context */
@ -82,6 +84,7 @@ struct sfdisk {
quiet : 1, /* suppres extra messages */
noreread : 1, /* don't check device is in use */
force : 1, /* do also stupid things */
backup : 1, /* backup sectors before write PT */
noact : 1; /* do not write to device */
};
@ -196,6 +199,85 @@ static int sfdisk_deinit(struct sfdisk *sf)
return rc;
}
static void backup_sectors(struct sfdisk *sf,
const char *tpl,
const char *name,
const char *devname,
off_t offset, size_t size)
{
char *fname;
int fd, devfd;
devfd = fdisk_get_devfd(sf->cxt);
assert(devfd >= 0);
xasprintf(&fname, "%s0x%08jx.bak", tpl, offset);
fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd < 0)
goto fail;
if (lseek(devfd, offset, SEEK_SET) == (off_t) -1) {
fdisk_warn(sf->cxt, _("cannot seek %s"), devname);
goto fail;
} else {
unsigned char *buf = xmalloc(size);
if (read_all(devfd, (char *) buf, size) != (ssize_t) size) {
fdisk_warn(sf->cxt, _("cannot read %s"), devname);
goto fail;
}
if (write_all(fd, buf, size) != 0) {
fdisk_warn(sf->cxt, _("cannot write %s"), fname);
goto fail;
}
free(buf);
}
fdisk_info(sf->cxt, _("%12s (offset %5ju, size %5ju): %s"),
name, (uintmax_t) offset, (uintmax_t) size, fname);
close(fd);
free(fname);
return;
fail:
errx(EXIT_FAILURE, _("%s: failed to create a backup"), devname);
}
static void backup_partition_table(struct sfdisk *sf, const char *devname)
{
const char *name;
char *tpl;
off_t offset = 0;
size_t size = 0;
int i = 0;
assert(sf);
if (!fdisk_has_label(sf->cxt))
return;
if (!sf->backup_file) {
/* initialize default backup filename */
const char *home = getenv ("HOME");
if (!home)
errx(EXIT_FAILURE, _("failed to create a signature backup, $HOME undefined"));
xasprintf(&tpl, "%s/sfdisk-%s-", home, basename(devname));
} else
xasprintf(&tpl, "%s-%s-", sf->backup_file, basename(devname));
color_scheme_enable("header", UL_COLOR_BOLD);
fdisk_info(sf->cxt, _("Backup files:"));
color_disable();
while (fdisk_locate_disklabel(sf->cxt, i++, &name, &offset, &size) == 0 && size)
backup_sectors(sf, tpl, name, devname, offset, size);
if (!sf->quiet)
fputc('\n', stdout);
free(tpl);
}
/*
* sfdisk --list [<device ..]
*/
@ -433,6 +515,8 @@ static int command_activate(struct sfdisk *sf, int argc, char **argv)
if (!fdisk_is_label(sf->cxt, DOS))
errx(EXIT_FAILURE, _("toggle boot flags is supported for MBR only"));
if (!listonly && sf->backup)
backup_partition_table(sf, devname);
nparts = fdisk_get_npartitions(sf->cxt);
for (i = 0; i < nparts; i++) {
@ -573,6 +657,9 @@ static int command_parttype(struct sfdisk *sf, int argc, char **argv)
return 0;
}
if (sf->backup)
backup_partition_table(sf, devname);
/* parse <type> and apply yo PT */
type = fdisk_label_parse_parttype(lb, typestr);
if (!type || fdisk_parttype_is_unknown(type))
@ -800,6 +887,9 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
fputs(_(" OK\n\n"), stdout);
}
if (sf->backup)
backup_partition_table(sf, devname);
if (!sf->quiet) {
list_disk_geometry(sf->cxt);
if (fdisk_has_label(sf->cxt)) {
@ -953,20 +1043,22 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
fputs(USAGE_SEPARATOR, out);
fputs(_(" <dev> device (usually disk) path\n"), out);
fputs(_(" <part> partition number\n"), out);
fputs(_(" <type> partition type, GUID for GPT, hex for MBR\n"), out);
fputs(_(" <dev> device (usually disk) path\n"), out);
fputs(_(" <part> partition number\n"), out);
fputs(_(" <type> partition type, GUID for GPT, hex for MBR\n"), out);
fputs(USAGE_OPTIONS, out);
fputs(_(" -f, --force disable all consistency checking\n"), out);
fputs(_(" -N, --partno <num> specify partition number\n"), out);
fputs(_(" -X, --label <name> specify label type (dos, gpt, ...)\n"), out);
fputs(_(" -q, --quiet suppress extra info messages\n"), out);
fputs(_(" -n, --no-act do everything except write to device\n"), out);
fputs(_(" --no-reread do not check whether the device is in use\n"), out);
fputs(_(" -b, --backup backup partition table sectors (see -O)\n"), out);
fputs(_(" -f, --force disable all consistency checking\n"), out);
fputs(_(" -O, --backup-file <path> override default backout file name\n"), out);
fputs(_(" -N, --partno <num> specify partition number\n"), out);
fputs(_(" -X, --label <name> specify label type (dos, gpt, ...)\n"), out);
fputs(_(" -q, --quiet suppress extra info messages\n"), out);
fputs(_(" -n, --no-act do everything except write to device\n"), out);
fputs(_(" --no-reread do not check whether the device is in use\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(_(" -u, --unit S deprecated, only sector unit is supported\n"), out);
fputs(_(" -L, --Linux deprecated and ignored, only for backward copatibility\n"), out);
fputs(_(" -u, --unit S deprecated, only sector unit is supported\n"), out);
fputs(_(" -L, --Linux deprecated, only for backward copatibility\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
@ -977,7 +1069,6 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
}
int main(int argc, char *argv[])
{
int rc = -EINVAL, c, longidx = -1;
@ -994,6 +1085,8 @@ int main(int argc, char *argv[])
static const struct option longopts[] = {
{ "activate",no_argument, NULL, 'a' },
{ "backup", no_argument, NULL, 'b' },
{ "backup-file", required_argument, NULL, 'O' },
{ "dump", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "force", no_argument, NULL, 'f' },
@ -1025,12 +1118,15 @@ int main(int argc, char *argv[])
textdomain(PACKAGE);
atexit(close_stdout);
while ((c = getopt_long(argc, argv, "adfhglLnN:qsTu:vVX:",
while ((c = getopt_long(argc, argv, "adfhglLO:nN:qsTu:vVX:",
longopts, &longidx)) != -1) {
switch(c) {
case 'a':
sf->act = ACT_ACTIVATE;
break;
case 'b':
sf->backup = 1;
break;
case OPT_CHANGE_ID:
case OPT_PRINT_ID:
case OPT_ID:
@ -1052,6 +1148,10 @@ int main(int argc, char *argv[])
case 'L':
warnx(_("--Linux option is deprecated and unnecessary"));
break;
case 'O':
sf->backup = 1;
sf->backup_file = optarg;
break;
case 'd':
sf->act = ACT_DUMP;
break;