]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/sfdisk.c
hwclock: free temporary variable before return
[thirdparty/util-linux.git] / disk-utils / sfdisk.c
index 1c5160c73af21df1326a63ddbe35eba0e0864e7d..d8dd8d296a9b391846f231bfc66a7f24acb76596 100644 (file)
@@ -79,6 +79,7 @@ enum {
        ACT_LIST_FREE,
        ACT_LIST_TYPES,
        ACT_REORDER,
+       ACT_RELOCATE,
        ACT_SHOW_SIZE,
        ACT_SHOW_GEOM,
        ACT_VERIFY,
@@ -87,7 +88,8 @@ enum {
        ACT_PARTLABEL,
        ACT_PARTATTRS,
        ACT_DISKID,
-       ACT_DELETE
+       ACT_DELETE,
+       ACT_BACKUP_SECTORS,
 };
 
 struct sfdisk {
@@ -95,6 +97,7 @@ struct sfdisk {
        int             partno;         /* -N <partno>, default -1 */
        int             wipemode;       /* remove foreign signatures from disk */
        int             pwipemode;      /* remove foreign signatures from partitions */
+       const char      *lockmode;      /* as specified by --lock */
        const char      *label;         /* --label <label> */
        const char      *label_nested;  /* --label-nested <label> */
        const char      *backup_file;   /* -O <path> */
@@ -111,6 +114,7 @@ struct sfdisk {
                     force  : 1,        /* do also stupid things */
                     backup : 1,        /* backup sectors before write PT */
                     container : 1,     /* PT contains container (MBR extended) partitions */
+                    unused : 1,        /* PT contains unused partition */
                     append : 1,        /* don't create new PT, append partitions only */
                     json : 1,          /* JSON dump */
                     movedata: 1,       /* move data after resize */
@@ -227,7 +231,9 @@ static void sfdisk_init(struct sfdisk *sf)
        if (!sf->cxt)
                err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
        fdisk_set_ask(sf->cxt, ask_callback, (void *) sf);
-       fdisk_enable_bootbits_protection(sf->cxt, 1);
+
+       if (sf->wipemode != WIPEMODE_ALWAYS)
+               fdisk_enable_bootbits_protection(sf->cxt, 1);
 
        if (sf->label_nested) {
                struct fdisk_context *x = fdisk_new_nested_context(sf->cxt,
@@ -367,6 +373,25 @@ static void backup_partition_table(struct sfdisk *sf, const char *devname)
        free(tpl);
 }
 
+static int assign_device(struct sfdisk *sf, const char *devname, int rdonly)
+{
+       struct fdisk_context *cxt = sf->cxt;
+
+       if (fdisk_assign_device(cxt, devname, rdonly) != 0)
+               err(EXIT_FAILURE, _("cannot open %s"), devname);
+
+       if (!fdisk_is_readonly(cxt)) {
+               if (blkdev_lock(fdisk_get_devfd(cxt), devname, sf->lockmode) != 0) {
+                       fdisk_deassign_device(cxt, 1);
+                       exit(EXIT_FAILURE);
+               }
+               if (sf->backup)
+                       backup_partition_table(sf, devname);
+       }
+       return 0;
+}
+
+
 static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_partition *orig_pa)
 {
        struct fdisk_partition *pa = get_partition(sf->cxt, partno);
@@ -374,9 +399,9 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
        FILE *f = NULL;
        int ok = 0, fd, backward = 0;
        fdisk_sector_t nsectors, from, to, step, i, prev;
-       size_t io, ss, step_bytes, cc;
+       size_t io, ss, step_bytes, cc, ioerr = 0;
        uintmax_t src, dst, nbytes;
-       int errsv, progress = 0;
+       int progress = 0, rc = 0;
        struct timeval prev_time;
        uint64_t bytes_per_sec = 0;
 
@@ -394,7 +419,7 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
                warnx(_("failed to get start of the old partition; ignoring --move-data"));
        else if (fdisk_partition_get_start(pa) == fdisk_partition_get_start(orig_pa))
                warnx(_("start of the partition has not been moved; ignoring --move-data"));
-       else if (fdisk_partition_get_size(orig_pa) < fdisk_partition_get_size(pa))
+       else if (fdisk_partition_get_size(orig_pa) > fdisk_partition_get_size(pa))
                warnx(_("new partition is smaller than original; ignoring --move-data"));
        else
                ok = 1;
@@ -410,7 +435,6 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
        from = fdisk_partition_get_start(orig_pa);
        to = fdisk_partition_get_start(pa);
 
-
        if ((to >= from && from + nsectors >= to) ||
            (from >= to && to + nsectors >= from)) {
                /* source and target overlay, check if we need to copy
@@ -437,14 +461,15 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
 
 #if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
        if (!backward)
-               posix_fadvise(fd, from * ss, nsectors * ss, POSIX_FADV_SEQUENTIAL);
+               ignore_result( posix_fadvise(fd, from * ss,
+                                       nsectors * ss, POSIX_FADV_SEQUENTIAL) );
 #endif
        devname = fdisk_partname(fdisk_get_devname(sf->cxt), partno+1);
        if (sf->move_typescript)
                typescript = mk_backup_filename_tpl(sf->move_typescript, devname, ".move");
 
        if (!sf->quiet) {
-               fdisk_info(sf->cxt,"");
+               fdisk_info(sf->cxt, "%s", "");
                color_scheme_enable("header", UL_COLOR_BOLD);
                fdisk_info(sf->cxt, sf->noact ? _("Data move: (--no-act)") : _("Data move:"));
                color_disable();
@@ -465,6 +490,7 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
                fdisk_ask_yesno(sf->cxt, _("Do you want to move partition data?"), &yes);
                if (!yes) {
                        fdisk_info(sf->cxt, _("Leaving."));
+                       free(devname);
                        return 0;
                }
        }
@@ -472,8 +498,9 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
        if (typescript) {
                f = fopen(typescript, "w");
                if (!f) {
+                       rc = -errno;
                        fdisk_warn(sf->cxt, _("cannot open %s"), typescript);
-                       goto fail;
+                       goto done;
                }
 
                /* don't translate */
@@ -488,7 +515,7 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
                        (uintmax_t)to, (uintmax_t)to * ss);
                fprintf(f, "# Area size (sectors/bytes): %ju/%ju\n",
                        (uintmax_t)nsectors, (uintmax_t)nsectors * ss);
-                               fprintf(f, "# Step size (sectors/bytes): %zu/%zu\n", step, step_bytes);
+                               fprintf(f, "# Step size (sectors/bytes): %" PRIu64 "/%zu\n", step, step_bytes);
                fprintf(f, "# Steps: %ju\n", ((uintmax_t) nsectors / step) + 1);
                fprintf(f, "#\n");
                fprintf(f, "# <step>: <from> <to> (step offsets in bytes)\n");
@@ -504,36 +531,44 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
        prev = 0;
 
        for (cc = 1, i = 0; i < nsectors && nbytes > 0; i += step, cc++) {
-               ssize_t rc;
-
-               if (backward)
-                       src -= step_bytes, dst -= step_bytes;
-
-               DBG(MISC, ul_debug("#%05zu: src=%ju dst=%ju", cc, src, dst));
 
                if (nbytes < step_bytes) {
-                       DBG(MISC, ul_debug(" aligning step from %ju to %ju",
-                                               step_bytes, nbytes));
+                       DBG(MISC, ul_debug("aligning step #%05zu from %zu to %ju",
+                                               cc, step_bytes, nbytes));
                        step_bytes = nbytes;
                }
                nbytes -= step_bytes;
 
+               if (backward)
+                       src -= step_bytes, dst -= step_bytes;
+
+               DBG(MISC, ul_debug("#%05zu: src=%ju dst=%ju", cc, src, dst));
+
                if (!sf->noact) {
                        /* read source */
-                       if (lseek(fd, src, SEEK_SET) == (off_t) -1)
-                               goto fail;
-                       rc = read(fd, buf, step_bytes);
-                       if (rc < 0 || rc != (ssize_t) step_bytes)
-                               goto fail;
+                       if (lseek(fd, src, SEEK_SET) == (off_t) -1 ||
+                           read_all(fd, buf, step_bytes) != (ssize_t) step_bytes) {
+                               if (f)
+                                       fprintf(f, "%05zu: read error %12ju %12ju\n", cc, src, dst);
+                               fdisk_warn(sf->cxt,
+                                       _("cannot read at offset: %ju; continue"), src);
+                               ioerr++;
+                               goto next;
+                       }
 
                        /* write target */
-                       if (lseek(fd, dst, SEEK_SET) == (off_t) -1)
-                               goto fail;
-                       rc = write(fd, buf, step_bytes);
-                       if (rc < 0 || rc != (ssize_t) step_bytes)
-                               goto fail;
-                       if (sf->movefsync)
-                               fsync(fd);
+                       if (lseek(fd, dst, SEEK_SET) == (off_t) -1 ||
+                           write_all(fd, buf, step_bytes) != 0) {
+                               if (f)
+                                       fprintf(f, "%05zu: write error %12ju %12ju\n", cc, src, dst);
+                               fdisk_warn(sf->cxt,
+                                       _("cannot write at offset: %ju; continue"), dst);
+                               ioerr++;
+                               goto next;
+                       }
+                       if (sf->movefsync && fsync(fd) != 0)
+                               fdisk_warn(sf->cxt,
+                                       _("cannot fsync at offset: %ju; continue"), dst);
                }
 
                /* write log */
@@ -569,42 +604,46 @@ static int move_partition_data(struct sfdisk *sf, size_t partno, struct fdisk_pa
                         fputc('\r', stdout);
 
                }
-
+next:
                if (!backward)
                        src += step_bytes, dst += step_bytes;
        }
 
-       if (progress) {
+       if (progress && nsectors) {
                int x = get_terminal_width(80);
                for (; x > 0; x--)
                        fputc(' ', stdout);
                fflush(stdout);
                fputc('\r', stdout);
+
+               if (i > nsectors)
+                       /* see for() above; @i has to be greater than @nsectors
+                        * on success due to i += step */
+                       i = nsectors;
+
                fprintf(stdout, _("Moved %ju from %ju sectors (%.0f%%)."),
                                i, nsectors,
                                100.0 / ((double) nsectors/(i+1)));
                fputc('\n', stdout);
        }
+       rc = 0;
+done:
        if (f)
                fclose(f);
        free(buf);
-       free(devname);
        free(typescript);
 
        if (sf->noact)
                fdisk_info(sf->cxt, _("Your data has not been moved (--no-act)."));
+       if (ioerr) {
+               fdisk_info(sf->cxt, _("%zu I/O errors detected!"), ioerr);
+               rc = -EIO;
+       } else if (rc)
+               warn(_("%s: failed to move data"), devname);
 
-       return 0;
-fail:
-       errsv = -errno;
-       warn(_("%s: failed to move data"), devname);
-       if (f)
-               fclose(f);
-       free(buf);
        free(devname);
-       free(typescript);
 
-       return errsv;
+       return rc;
 }
 
 static int write_changes(struct sfdisk *sf)
@@ -647,15 +686,11 @@ static int command_list_partitions(struct sfdisk *sf, int argc, char **argv)
        fdisk_enable_listonly(sf->cxt, 1);
 
        if (argc) {
-               int i, ct = 0;
+               int i;
 
-               for (i = 0; i < argc; i++) {
-                       if (ct)
-                               fputs("\n\n", stdout);
-                       if (print_device_pt(sf->cxt, argv[i], 1, sf->verify) != 0)
+               for (i = 0; i < argc; i++)
+                       if (print_device_pt(sf->cxt, argv[i], 1, sf->verify, i) != 0)
                                fail++;
-                       ct++;
-               }
        } else
                print_all_devices_pt(sf->cxt, sf->verify);
 
@@ -671,15 +706,11 @@ static int command_list_freespace(struct sfdisk *sf, int argc, char **argv)
        fdisk_enable_listonly(sf->cxt, 1);
 
        if (argc) {
-               int i, ct = 0;
+               int i;
 
-               for (i = 0; i < argc; i++) {
-                       if (ct)
-                               fputs("\n\n", stdout);
-                       if (print_device_freespace(sf->cxt, argv[i], 1) != 0)
+               for (i = 0; i < argc; i++)
+                       if (print_device_freespace(sf->cxt, argv[i], 1, i) != 0)
                                fail++;
-                       ct++;
-               }
        } else
                print_all_devices_freespace(sf->cxt);
 
@@ -726,10 +757,7 @@ static int verify_device(struct sfdisk *sf, const char *devname)
 
        fdisk_enable_listonly(sf->cxt, 1);
 
-       if (fdisk_assign_device(sf->cxt, devname, 1)) {
-               warn(_("cannot open %s"), devname);
-               return 1;
-       }
+       assign_device(sf, devname, 1);
 
        color_scheme_enable("header", UL_COLOR_BOLD);
        fdisk_info(sf->cxt, "%s:", devname);
@@ -836,10 +864,7 @@ static int print_geom(struct sfdisk *sf, const char *devname)
 {
        fdisk_enable_listonly(sf->cxt, 1);
 
-       if (fdisk_assign_device(sf->cxt, devname, 1)) {
-               warn(_("cannot open %s"), devname);
-               return 1;
-       }
+       assign_device(sf, devname, 1);
 
        fdisk_info(sf->cxt, "%s: %ju cylinders, %ju heads, %ju sectors/track",
                        devname,
@@ -894,9 +919,7 @@ static int command_activate(struct sfdisk *sf, int argc, char **argv)
        /*  --activate <device> */
        listonly = argc == 1;
 
-       rc = fdisk_assign_device(sf->cxt, devname, listonly);
-       if (rc)
-               err(EXIT_FAILURE, _("cannot open %s"), devname);
+       assign_device(sf, devname, listonly);
 
        if (fdisk_is_label(sf->cxt, GPT)) {
                if (fdisk_gpt_is_hybrid(sf->cxt))
@@ -911,9 +934,6 @@ static int command_activate(struct sfdisk *sf, int argc, char **argv)
        } else if (!fdisk_is_label(sf->cxt, DOS))
                errx(EXIT_FAILURE, _("toggle boot flags is supported for MBR or PMBR only"));
 
-       if (!listonly && sf->backup)
-               backup_partition_table(sf, devname);
-
        nparts = fdisk_get_npartitions(sf->cxt);
        for (i = 0; i < nparts; i++) {
                char *data = NULL;
@@ -974,11 +994,7 @@ static int command_delete(struct sfdisk *sf, int argc, char **argv)
                errx(EXIT_FAILURE, _("no disk device specified"));
        devname = argv[0];
 
-       if (fdisk_assign_device(sf->cxt, devname, 0) != 0)
-               err(EXIT_FAILURE, _("cannot open %s"), devname);
-
-       if (sf->backup)
-               backup_partition_table(sf, devname);
+       assign_device(sf, devname, 0);
 
        /* delete all */
        if (argc == 1) {
@@ -1014,12 +1030,7 @@ static int command_reorder(struct sfdisk *sf, int argc, char **argv)
        if (!devname)
                errx(EXIT_FAILURE, _("no disk device specified"));
 
-       rc = fdisk_assign_device(sf->cxt, devname, 0);  /* read-write */
-       if (rc)
-               err(EXIT_FAILURE, _("cannot open %s"), devname);
-
-       if (sf->backup)
-               backup_partition_table(sf, devname);
+       assign_device(sf, devname, 0);  /* read-write */
 
        if (fdisk_reorder_partitions(sf->cxt) == 1)     /* unchanged */
                rc = fdisk_deassign_device(sf->cxt, 1);
@@ -1044,9 +1055,7 @@ static int command_dump(struct sfdisk *sf, int argc, char **argv)
        if (!devname)
                errx(EXIT_FAILURE, _("no disk device specified"));
 
-       rc = fdisk_assign_device(sf->cxt, devname, 1);  /* read-only */
-       if (rc)
-               err(EXIT_FAILURE, _("cannot open %s"), devname);
+       assign_device(sf, devname, 1);  /* read-only */
 
        if (!fdisk_has_label(sf->cxt))
                errx(EXIT_FAILURE, _("%s: does not contain a recognized partition table"), devname);
@@ -1068,6 +1077,29 @@ static int command_dump(struct sfdisk *sf, int argc, char **argv)
        return 0;
 }
 
+/*
+ * sfdisk --backup-pt-sectors <device>
+ */
+static int command_backup_sectors(struct sfdisk *sf, int argc, char **argv)
+{
+       const char *devname = NULL;
+
+       if (argc)
+               devname = argv[0];
+       if (!devname)
+               errx(EXIT_FAILURE, _("no disk device specified"));
+
+       assign_device(sf, devname, 1);  /* read-only */
+
+       if (!fdisk_has_label(sf->cxt))
+               errx(EXIT_FAILURE, _("%s: does not contain a recognized partition table"), devname);
+
+       backup_partition_table(sf, devname);
+
+       fdisk_deassign_device(sf->cxt, 1);              /* no-sync() */
+       return 0;
+}
+
 static void assign_device_partition(struct sfdisk *sf,
                                const char *devname,
                                size_t partno,
@@ -1085,6 +1117,11 @@ static void assign_device_partition(struct sfdisk *sf,
        if (rc)
                err(EXIT_FAILURE, _("cannot open %s"), devname);
 
+       if (!fdisk_is_readonly(sf->cxt)
+           && blkdev_lock(fdisk_get_devfd(sf->cxt), devname, sf->lockmode) != 0) {
+               fdisk_deassign_device(sf->cxt, 1);
+               return;
+       }
        lb = fdisk_get_label(sf->cxt, NULL);
        if (!lb)
                errx(EXIT_FAILURE, _("%s: no partition table found"), devname);
@@ -1151,7 +1188,11 @@ static int command_parttype(struct sfdisk *sf, int argc, char **argv)
                backup_partition_table(sf, devname);
 
        /* parse <type> and apply to PT */
-       type = fdisk_label_parse_parttype(lb, typestr);
+       type = fdisk_label_advparse_parttype(lb, typestr,
+                       FDISK_PARTTYPE_PARSE_DATA
+                       | FDISK_PARTTYPE_PARSE_ALIAS
+                       | FDISK_PARTTYPE_PARSE_NAME
+                       | FDISK_PARTTYPE_PARSE_SHORTCUT);
        if (!type)
                errx(EXIT_FAILURE, _("failed to parse %s partition type '%s'"),
                                fdisk_label_get_name(lb), typestr);
@@ -1345,8 +1386,7 @@ static int command_diskid(struct sfdisk *sf, int argc, char **argv)
        else if (argc > 2)
                errx(EXIT_FAILURE, _("unexpected arguments"));
 
-       if (fdisk_assign_device(sf->cxt, devname, !str) != 0)
-               err(EXIT_FAILURE, _("cannot open %s"), devname);
+       assign_device(sf, devname, !str);
 
        /* print */
        if (!str) {
@@ -1358,16 +1398,45 @@ static int command_diskid(struct sfdisk *sf, int argc, char **argv)
                return 0;
        }
 
-       /* change */
-       if (sf->backup)
-               backup_partition_table(sf, devname);
-
        if (fdisk_set_disklabel_id_from_string(sf->cxt, str) != 0)
                errx(EXIT_FAILURE, _("%s: failed to set disklabel ID"), devname);
 
        return write_changes(sf);
 }
 
+/*
+ * sfdisk --relocate <mode> <device>
+ */
+static int command_relocate(struct sfdisk *sf, int argc, char **argv)
+{
+       const char *devname = NULL;
+       const char *oper = NULL;
+       struct fdisk_label *lb;
+
+       if (!argc)
+               errx(EXIT_FAILURE, _("no relocate operation specified"));
+       if (argc < 2)
+               errx(EXIT_FAILURE, _("no disk device specified"));
+       if (argc > 2)
+               errx(EXIT_FAILURE, _("unexpected arguments"));
+
+       oper = argv[0];
+       devname = argv[1];
+       lb = fdisk_get_label(sf->cxt, "gpt");
+
+       if (strcmp(oper, "gpt-bak-mini") == 0)
+               fdisk_gpt_enable_minimize(lb, 1);
+
+       else if (strcmp(oper, "gpt-bak-std") != 0)
+               errx(EXIT_FAILURE, _("unsupported relocation operation"));
+
+       assign_device(sf, devname, 0);
+
+       fdisk_label_set_changed(lb, 1);
+
+       return write_changes(sf);
+}
+
 static void sfdisk_print_partition(struct sfdisk *sf, size_t n)
 {
        struct fdisk_partition *pa = NULL;
@@ -1432,7 +1501,7 @@ static void command_fdisk_help(void)
 
        fputc('\n', stdout);
        fputs(_("   <type>   The partition type.  Default is a Linux data partition.\n"), stdout);
-       fputs(_("            MBR: hex or L,S,E,X,U,R,V shortcuts.\n"), stdout);
+       fputs(_("            MBR: hex or L,S,Ex,X,U,R,V shortcuts.\n"), stdout);
        fputs(_("            GPT: UUID or L,S,H,U,R,V shortcuts.\n"), stdout);
 
        fputc('\n', stdout);
@@ -1484,26 +1553,29 @@ static int loop_control_commands(struct sfdisk *sf,
        return rc;
 }
 
-static int has_container(struct sfdisk *sf)
+static int has_container_or_unused(struct sfdisk *sf)
 {
        size_t i, nparts;
        struct fdisk_partition *pa = NULL;
 
-       if (sf->container)
-               return sf->container;
+       if (sf->container || sf->unused)
+               return 1;
 
        nparts = fdisk_get_npartitions(sf->cxt);
        for (i = 0; i < nparts; i++) {
+
+               if (!fdisk_is_partition_used(sf->cxt, i)) {
+                       sf->unused = 1;
+                       continue;
+               }
                if (fdisk_get_partition(sf->cxt, i, &pa) != 0)
                        continue;
-               if (fdisk_partition_is_container(pa)) {
+               if (fdisk_partition_is_container(pa))
                        sf->container = 1;
-                       break;
-               }
        }
 
        fdisk_unref_partition(pa);
-       return sf->container;
+       return sf->container || sf->unused;
 }
 
 static size_t last_pt_partno(struct sfdisk *sf)
@@ -1592,7 +1664,7 @@ static void follow_wipe_mode(struct sfdisk *sf)
        if (dowipe) {
                if (!fdisk_is_ptcollision(sf->cxt)) {
                        fdisk_warnx(sf->cxt, _(
-                               "The device contains '%s' signature and it will be removed by a write command. "
+                               "The device contains '%s' signature and it may be removed by a write command. "
                                "See sfdisk(8) man page and --wipe option for more details."),
                                fdisk_get_collision(sf->cxt));
                        fputc('\n', stdout);
@@ -1667,7 +1739,7 @@ static void refresh_prompt_buffer(struct sfdisk *sf, const char *devname,
  */
 static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
 {
-       int rc = 0, partno = sf->partno, created = 0, unused = 0;
+       int rc = 0, partno = sf->partno, created = 0, unused = 0, ignored = 0;
        struct fdisk_script *dp;
        struct fdisk_table *tb = NULL;
        const char *devname = NULL, *label;
@@ -1682,9 +1754,7 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
        if (!devname)
                errx(EXIT_FAILURE, _("no disk device specified"));
 
-       rc = fdisk_assign_device(sf->cxt, devname, 0);
-       if (rc)
-               err(EXIT_FAILURE, _("cannot open %s"), devname);
+       assign_device(sf, devname, 0);
 
        dp = fdisk_new_script(sf->cxt);
        if (!dp)
@@ -1760,9 +1830,6 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
        if (fdisk_get_collision(sf->cxt))
                follow_wipe_mode(sf);
 
-       if (sf->backup)
-               backup_partition_table(sf, devname);
-
        if (!sf->quiet) {
                list_disk_geometry(sf->cxt);
                if (fdisk_has_label(sf->cxt)) {
@@ -1804,7 +1871,7 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
                if (created
                    && partno < 0
                    && next_partno == fdisk_get_npartitions(sf->cxt)
-                   && !has_container(sf)) {
+                   && !has_container_or_unused(sf)) {
                        fdisk_info(sf->cxt, _("All partitions used."));
                        rc = SFDISK_DONE_ASK;
                        break;
@@ -1827,14 +1894,18 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
                        buf[sizeof(buf) - 1] = '\0';
                        fdisk_warnx(sf->cxt, _("Unknown script header '%s' -- ignore."), buf);
                        continue;
-               } else if (rc < 0) {
+               }
+
+               if (rc < 0) {
                        DBG(PARSE, ul_debug("script parsing failed, trying sfdisk specific commands"));
                        buf[sizeof(buf) - 1] = '\0';
                        rc = loop_control_commands(sf, dp, buf);
                        if (rc)
                                break;
                        continue;
-               } else if (rc == 1) {
+               }
+
+               if (rc == 1) {
                        rc = SFDISK_DONE_EOF;
                        if (!sf->quiet)
                                fputs(_("Done.\n"), stdout);
@@ -1851,15 +1922,17 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
                        if (ignore_partition(pa)) {
                                fdisk_info(sf->cxt, _("Ignoring partition."));
                                next_partno++;
+                               ignored++;
                                continue;
                        }
                        if (!created) {         /* create a new disklabel */
                                rc = fdisk_apply_script_headers(sf->cxt, dp);
                                created = !rc;
-                               if (rc)
-                                       fdisk_warnx(sf->cxt, _(
-                                         "Failed to apply script headers, "
-                                         "disk label not created."));
+                               if (rc) {
+                                       errno = -rc;
+                                       fdisk_warn(sf->cxt, _(
+                                         "Failed to apply script headers, disk label not created"));
+                               }
 
                                if (rc == 0 && fdisk_get_collision(sf->cxt))
                                        follow_wipe_mode(sf);
@@ -1868,7 +1941,9 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
                                rc = fdisk_set_partition(sf->cxt, partno, pa);
                                rc = rc == 0 ? SFDISK_DONE_ASK : SFDISK_DONE_ABORT;
                                break;
-                       } else if (!rc) {               /* add partition */
+                       }
+
+                       if (!rc) {              /* add partition */
                                if (!sf->interactive && !sf->quiet &&
                                    (!sf->prompt || startswith(sf->prompt, SFDISK_PROMPT))) {
                                        refresh_prompt_buffer(sf, devname, next_partno, created);
@@ -1877,7 +1952,7 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
                                rc = fdisk_add_partition(sf->cxt, pa, &cur_partno);
                                if (rc) {
                                        errno = -rc;
-                                       fdisk_warn(sf->cxt, _("Failed to add #%d partition"), next_partno + 1);
+                                       fdisk_warn(sf->cxt, _("Failed to add #%zu partition"), next_partno + 1);
                                }
                        }
 
@@ -1911,7 +1986,7 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
        /* create empty disk label if label, but no partition specified */
        if ((rc == SFDISK_DONE_EOF || rc == SFDISK_DONE_WRITE) && created == 0
            && fdisk_script_has_force_label(dp) == 1
-           && fdisk_table_get_nents(tb) == 0
+           && fdisk_table_get_nents(tb) == (size_t) ignored
            && fdisk_script_get_header(dp, "label")) {
 
                int xrc = fdisk_apply_script_headers(sf->cxt, dp);
@@ -1972,6 +2047,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -A, --activate <dev> [<part> ...] list or set bootable (P)MBR partitions\n"), out);
        fputs(_(" -d, --dump <dev>                  dump partition table (usable for later input)\n"), out);
        fputs(_(" -J, --json <dev>                  dump partition table in JSON format\n"), out);
+       fputs(_(" -B, --backup-pt-sectors <dev>     binary partition table backup (see -b and -O)\n"), out);
        fputs(_(" -g, --show-geometry [<dev> ...]   list geometry of all or specified devices\n"), out);
        fputs(_(" -l, --list [<dev> ...]            list partitions of each device\n"), out);
        fputs(_(" -F, --list-free [<dev> ...]       list unpartitioned free areas of each device\n"), out);
@@ -1989,8 +2065,9 @@ static void __attribute__((__noreturn__)) usage(void)
 
        fputs(USAGE_SEPARATOR, out);
        fputs(_(" --disk-id <dev> [<str>]           print or change disk label ID (UUID)\n"), out);
+       fputs(_(" --relocate <oper> <dev>           move partition header\n"), out);
 
-       fputs(USAGE_SEPARATOR, out);
+       fputs(USAGE_ARGUMENTS, 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);
@@ -2002,10 +2079,13 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_("     --move-data[=<typescript>] move partition data after relocation (requires -N)\n"), out);
        fputs(_("     --move-use-fsync      use fsync after each write when move data\n"), out);
        fputs(_(" -f, --force               disable all consistency checking\n"), out);
+
        fprintf(out,
              _("     --color[=<when>]      colorize output (%s, %s or %s)\n"), "auto", "always", "never");
        fprintf(out,
                "                             %s\n", USAGE_COLORS_DEFAULT);
+       fprintf(out,
+             _("     --lock[=<mode>]       use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
        fputs(_(" -N, --partno <num>        specify partition number\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);
@@ -2025,12 +2105,12 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -u, --unit S              deprecated, only sector unit is supported\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
-       printf( " -h, --help                %s\n", USAGE_OPTSTR_HELP);
-       printf( " -v, --version             %s\n", USAGE_OPTSTR_VERSION);
+       fprintf(out, " -h, --help                %s\n", USAGE_OPTSTR_HELP);
+       fprintf(out, " -v, --version             %s\n", USAGE_OPTSTR_VERSION);
 
        list_available_columns(out);
 
-       printf(USAGE_MAN_TAIL("sfdisk(8)"));
+       fprintf(out, USAGE_MAN_TAIL("sfdisk(8)"));
        exit(EXIT_SUCCESS);
 }
 
@@ -2062,16 +2142,20 @@ int main(int argc, char *argv[])
                OPT_MOVEDATA,
                OPT_MOVEFSYNC,
                OPT_DELETE,
-               OPT_NOTELL
+               OPT_NOTELL,
+               OPT_RELOCATE,
+               OPT_LOCK,
        };
 
        static const struct option longopts[] = {
                { "activate",no_argument,       NULL, 'A' },
                { "append",  no_argument,       NULL, 'a' },
+               { "backup-pt-sectors", no_argument,   NULL, 'B' },
                { "backup",  no_argument,       NULL, 'b' },
                { "backup-file", required_argument, NULL, 'O' },
                { "bytes",   no_argument,       NULL, OPT_BYTES },
                { "color",   optional_argument, NULL, OPT_COLOR },
+               { "lock",    optional_argument, NULL, OPT_LOCK },
                { "delete",  no_argument,       NULL, OPT_DELETE },
                { "dump",    no_argument,       NULL, 'd' },
                { "help",    no_argument,       NULL, 'h' },
@@ -2097,6 +2181,8 @@ int main(int argc, char *argv[])
                { "wipe",    required_argument, NULL, 'w' },
                { "wipe-partitions",    required_argument, NULL, 'W' },
 
+               { "relocate", no_argument,      NULL, OPT_RELOCATE },
+
                { "part-uuid",  no_argument,    NULL, OPT_PARTUUID },
                { "part-label", no_argument,    NULL, OPT_PARTLABEL },
                { "part-type",  no_argument,    NULL, OPT_PARTTYPE },
@@ -2116,7 +2202,8 @@ int main(int argc, char *argv[])
                { NULL, 0, NULL, 0 },
        };
        static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
-               { 'F','J','d'},                 /* --list-free --json --dump */
+               { 'F','d'},                     /* --list-free --dump */
+               { 'F','J'},                     /* --list-free --json */
                { 's','u'},                     /* --show-size --unit */
                { 0 }
        };
@@ -2128,7 +2215,7 @@ int main(int argc, char *argv[])
        textdomain(PACKAGE);
        close_stdout_atexit();
 
-       while ((c = getopt_long(argc, argv, "aAbcdfFgGhJlLo:O:nN:qrsTu:vVX:Y:w:W:",
+       while ((c = getopt_long(argc, argv, "aAbBcdfFgGhJlLo:O:nN:qrsTu:vVX:Y:w:W:",
                                        longopts, &longidx)) != -1) {
 
                err_exclusive_options(c, longopts, excl, excl_st);
@@ -2143,6 +2230,9 @@ int main(int argc, char *argv[])
                case 'b':
                        sf->backup = 1;
                        break;
+               case 'B':
+                       sf->act = ACT_BACKUP_SECTORS;
+                       break;
                case OPT_CHANGE_ID:
                case OPT_PRINT_ID:
                case OPT_ID:
@@ -2272,6 +2362,17 @@ int main(int argc, char *argv[])
                case OPT_NOTELL:
                        sf->notell = 1;
                        break;
+               case OPT_RELOCATE:
+                       sf->act = ACT_RELOCATE;
+                       break;
+               case OPT_LOCK:
+                       sf->lockmode = "1";
+                       if (optarg) {
+                               if (*optarg == '=')
+                                       optarg++;
+                               sf->lockmode = optarg;
+                       }
+                       break;
                default:
                        errtryhelp(EXIT_FAILURE);
                }
@@ -2299,6 +2400,10 @@ int main(int argc, char *argv[])
                rc = command_activate(sf, argc - optind, argv + optind);
                break;
 
+       case ACT_BACKUP_SECTORS:
+               rc = command_backup_sectors(sf, argc - optind, argv + optind);
+               break;
+
        case ACT_DELETE:
                rc = command_delete(sf, argc - optind, argv + optind);
                break;
@@ -2358,6 +2463,10 @@ int main(int argc, char *argv[])
        case ACT_REORDER:
                rc = command_reorder(sf, argc - optind, argv + optind);
                break;
+
+       case ACT_RELOCATE:
+               rc = command_relocate(sf, argc - optind, argv + optind);
+               break;
        }
 
        sfdisk_deinit(sf);
@@ -2365,4 +2474,3 @@ int main(int argc, char *argv[])
        DBG(MISC, ul_debug("bye! [rc=%d]", rc));
        return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
-