]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
sfdisk: add --backup and --backup-file
authorKarel Zak <kzak@redhat.com>
Fri, 19 Sep 2014 09:31:07 +0000 (11:31 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 7 Oct 2014 12:55:32 +0000 (14:55 +0200)
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>
disk-utils/sfdisk.c

index 45121f5e60d3904dc918dbaca7079a2d8cb1de20..1a6db9877240381cde918f3fffa1762b424e2ced 100644 (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;