sfdisk: add --move-data
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
9a17d9460a
commit
f42205d89a
|
@ -133,7 +133,7 @@ Back up the current partition table sectors before starting the partitioning.
|
||||||
The default backup file name is ~/sfdisk-<device>-<offset>.bak; to use another
|
The default backup file name is ~/sfdisk-<device>-<offset>.bak; to use another
|
||||||
name see option \fB\-O\fR, \fB\-\-backup\-file\fR.
|
name see option \fB\-O\fR, \fB\-\-backup\-file\fR.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-color [ \fI=when ]
|
.BR \-\-color[\fI=when]
|
||||||
Colorize the output. The optional argument \fIwhen\fP
|
Colorize the output. The optional argument \fIwhen\fP
|
||||||
can be \fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted,
|
can be \fBauto\fR, \fBnever\fR or \fBalways\fR. If the \fIwhen\fR argument is omitted,
|
||||||
it defaults to \fBauto\fR. The colors can be disabled; for the current built-in default
|
it defaults to \fBauto\fR. The colors can be disabled; for the current built-in default
|
||||||
|
@ -155,6 +155,33 @@ Do not check through the re-read-partition-table ioctl whether the device is in
|
||||||
.BR \-O , " \-\-backup\-file " \fIpath\fR
|
.BR \-O , " \-\-backup\-file " \fIpath\fR
|
||||||
Override the default backup file name. Note that the device name and offset
|
Override the default backup file name. Note that the device name and offset
|
||||||
are always appended to the file name.
|
are always appended to the file name.
|
||||||
|
.TP
|
||||||
|
.BR \-\-move-data[\fI=path]
|
||||||
|
Move data after partition relocation, for example when move begin of the
|
||||||
|
partition to another place on the disk. The size of the partition has to be the
|
||||||
|
same, the new and old location may overlap. The option requires \fB\-N\fR to be
|
||||||
|
processed on one specified partition only.
|
||||||
|
|
||||||
|
The \fIpath\fR overrides the default log file name
|
||||||
|
(the default is ~/sfdisk-<devname>.move). The log file contains information
|
||||||
|
about all read/write oprations with the partition data.
|
||||||
|
|
||||||
|
Note that this operation is ricky and not atomic. \fBDon't forget to backup your data!\fR
|
||||||
|
|
||||||
|
The example below creates 100MiB free area before the first partition and moves
|
||||||
|
data (e.g. filesystem), the next command creates a new partition from the free
|
||||||
|
space (at offset 2048) and the last command reorder partitions to match disk
|
||||||
|
order (original sdc1 will be sdc2).
|
||||||
|
.RS
|
||||||
|
.sp
|
||||||
|
.B "echo '+100M,' | sfdisk --move-data /dev/sdc -N 1"
|
||||||
|
.br
|
||||||
|
.B "echo '2048,' | sfdisk /dev/sdc --append
|
||||||
|
.br
|
||||||
|
.B sfdisk /dev/sdc --reorder
|
||||||
|
.sp
|
||||||
|
.RE
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR \-o , " \-\-output " \fIlist
|
.BR \-o , " \-\-output " \fIlist
|
||||||
Specify which output columns to print. Use
|
Specify which output columns to print. Use
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <libsmartcols.h>
|
#include <libsmartcols.h>
|
||||||
#ifdef HAVE_LIBREADLINE
|
#ifdef HAVE_LIBREADLINE
|
||||||
# include <readline/readline.h>
|
# include <readline/readline.h>
|
||||||
|
@ -46,6 +47,7 @@
|
||||||
#include "all-io.h"
|
#include "all-io.h"
|
||||||
#include "rpmatch.h"
|
#include "rpmatch.h"
|
||||||
#include "loopdev.h"
|
#include "loopdev.h"
|
||||||
|
#include "xalloc.h"
|
||||||
|
|
||||||
#include "libfdisk.h"
|
#include "libfdisk.h"
|
||||||
#include "fdisk-list.h"
|
#include "fdisk-list.h"
|
||||||
|
@ -89,9 +91,11 @@ struct sfdisk {
|
||||||
const char *label; /* --label <label> */
|
const char *label; /* --label <label> */
|
||||||
const char *label_nested; /* --label-nested <label> */
|
const char *label_nested; /* --label-nested <label> */
|
||||||
const char *backup_file; /* -O <path> */
|
const char *backup_file; /* -O <path> */
|
||||||
|
const char *move_typescript; /* --movedata <typescript> */
|
||||||
char *prompt;
|
char *prompt;
|
||||||
|
|
||||||
struct fdisk_context *cxt; /* libfdisk context */
|
struct fdisk_context *cxt; /* libfdisk context */
|
||||||
|
struct fdisk_partition *orig_pa; /* -N <partno> before the change */
|
||||||
|
|
||||||
unsigned int verify : 1, /* call fdisk_verify_disklabel() */
|
unsigned int verify : 1, /* call fdisk_verify_disklabel() */
|
||||||
quiet : 1, /* suppres extra messages */
|
quiet : 1, /* suppres extra messages */
|
||||||
|
@ -102,6 +106,7 @@ struct sfdisk {
|
||||||
container : 1, /* PT contains container (MBR extended) partitions */
|
container : 1, /* PT contains container (MBR extended) partitions */
|
||||||
append : 1, /* don't create new PT, append partitions only */
|
append : 1, /* don't create new PT, append partitions only */
|
||||||
json : 1, /* JSON dump */
|
json : 1, /* JSON dump */
|
||||||
|
movedata: 1, /* move data after resize */
|
||||||
noact : 1; /* do not write to device */
|
noact : 1; /* do not write to device */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -243,6 +248,21 @@ static int sfdisk_deinit(struct sfdisk *sf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct fdisk_partition *get_partition(struct fdisk_context *cxt, size_t partno)
|
||||||
|
{
|
||||||
|
struct fdisk_table *tb = NULL;
|
||||||
|
struct fdisk_partition *pa;
|
||||||
|
|
||||||
|
if (fdisk_get_partitions(cxt, &tb) != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pa = fdisk_table_get_partition_by_partno(tb, partno);
|
||||||
|
if (pa)
|
||||||
|
fdisk_ref_partition(pa);
|
||||||
|
fdisk_unref_table(tb);
|
||||||
|
return pa;
|
||||||
|
}
|
||||||
|
|
||||||
static void backup_sectors(struct sfdisk *sf,
|
static void backup_sectors(struct sfdisk *sf,
|
||||||
const char *tpl,
|
const char *tpl,
|
||||||
const char *name,
|
const char *name,
|
||||||
|
@ -287,6 +307,26 @@ fail:
|
||||||
errx(EXIT_FAILURE, _("%s: failed to create a backup"), devname);
|
errx(EXIT_FAILURE, _("%s: failed to create a backup"), devname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *mk_backup_filename_tpl(const char *filename, const char *devname, const char *suffix)
|
||||||
|
{
|
||||||
|
char *tpl = NULL;
|
||||||
|
char *name, *buf = xstrdup(devname);
|
||||||
|
|
||||||
|
name = basename(buf);
|
||||||
|
|
||||||
|
if (!filename) {
|
||||||
|
const char *home = getenv ("HOME");
|
||||||
|
if (!home)
|
||||||
|
errx(EXIT_FAILURE, _("failed to create a backup file, $HOME undefined"));
|
||||||
|
xasprintf(&tpl, "%s/sfdisk-%s%s", home, name, suffix);
|
||||||
|
} else
|
||||||
|
xasprintf(&tpl, "%s-%s%s", filename, name, suffix);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
return tpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void backup_partition_table(struct sfdisk *sf, const char *devname)
|
static void backup_partition_table(struct sfdisk *sf, const char *devname)
|
||||||
{
|
{
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -300,14 +340,7 @@ static void backup_partition_table(struct sfdisk *sf, const char *devname)
|
||||||
if (!fdisk_has_label(sf->cxt))
|
if (!fdisk_has_label(sf->cxt))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!sf->backup_file) {
|
tpl = mk_backup_filename_tpl(sf->backup_file, devname, "-");
|
||||||
/* 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);
|
color_scheme_enable("header", UL_COLOR_BOLD);
|
||||||
fdisk_info(sf->cxt, _("Backup files:"));
|
fdisk_info(sf->cxt, _("Backup files:"));
|
||||||
|
@ -321,6 +354,145 @@ static void backup_partition_table(struct sfdisk *sf, const char *devname)
|
||||||
free(tpl);
|
free(tpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
char *devname, *typescript;
|
||||||
|
FILE *f;
|
||||||
|
int ok = 0, fd, backward;
|
||||||
|
fdisk_sector_t nsectors, from, to, step, i;
|
||||||
|
size_t ss, step_bytes, cc;
|
||||||
|
uintmax_t src, dst;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
assert(sf->movedata);
|
||||||
|
|
||||||
|
if (!pa)
|
||||||
|
warnx(_("failed to read new partition from device (ignore --move-data)"));
|
||||||
|
else if (!fdisk_partition_has_size(pa))
|
||||||
|
warnx(_("failed to get size of the new partition (ignore --move-data)"));
|
||||||
|
else if (!fdisk_partition_has_start(pa))
|
||||||
|
warnx(_("failed to get start of the new partition (ignore --move-data)"));
|
||||||
|
else if (!fdisk_partition_has_size(orig_pa))
|
||||||
|
warnx(_("failed to get size of the old partition (ignore --move-data)"));
|
||||||
|
else if (!fdisk_partition_has_start(orig_pa))
|
||||||
|
warnx(_("failed to get start of the old partition (ignore --move-data)"));
|
||||||
|
else if (fdisk_partition_get_start(pa) == fdisk_partition_get_start(orig_pa))
|
||||||
|
warnx(_("begin of the partition has not been moved (ignore --move-data)"));
|
||||||
|
else if (fdisk_partition_get_size(orig_pa) < fdisk_partition_get_size(pa))
|
||||||
|
warnx(_("new partition is smaller than original (ignore --move-data)"));
|
||||||
|
else
|
||||||
|
ok = 1;
|
||||||
|
if (!ok)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
fd = fdisk_get_devfd(sf->cxt);
|
||||||
|
|
||||||
|
ss = fdisk_get_sector_size(sf->cxt);
|
||||||
|
nsectors = fdisk_partition_get_size(orig_pa);
|
||||||
|
from = fdisk_partition_get_start(orig_pa);
|
||||||
|
to = fdisk_partition_get_start(pa);
|
||||||
|
backward = from < to;
|
||||||
|
|
||||||
|
/* step cannot be bigger than gap between new and old location */
|
||||||
|
step = from > to ? from - to : to - from;
|
||||||
|
/* step cannot be bigger than original partition size */
|
||||||
|
if (step > fdisk_partition_get_size(orig_pa))
|
||||||
|
step = fdisk_partition_get_size(orig_pa);
|
||||||
|
/* step cannot be begger than ~1MiB */
|
||||||
|
if (step > (getpagesize() * 256) / ss)
|
||||||
|
step = (getpagesize() * 256) / ss;
|
||||||
|
step_bytes = step * ss;
|
||||||
|
|
||||||
|
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
|
||||||
|
posix_fadvise(fd, from * ss, nsectors * ss, POSIX_FADV_SEQUENTIAL);
|
||||||
|
#endif
|
||||||
|
devname = fdisk_partname(fdisk_get_devname(sf->cxt), partno+1);
|
||||||
|
typescript = mk_backup_filename_tpl(sf->move_typescript, devname, ".move");
|
||||||
|
|
||||||
|
if (!sf->quiet) {
|
||||||
|
fdisk_info(sf->cxt,"");
|
||||||
|
color_scheme_enable("header", UL_COLOR_BOLD);
|
||||||
|
fdisk_info(sf->cxt, _("Data move:"));
|
||||||
|
color_disable();
|
||||||
|
fdisk_info(sf->cxt, _(" typescript file: %s"), typescript);
|
||||||
|
printf(_(" old start: %ju, new start: %ju (move %ju sectors)\n"),
|
||||||
|
(uintmax_t) from, (uintmax_t) to, nsectors);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf->interactive) {
|
||||||
|
int yes = 0;
|
||||||
|
fdisk_ask_yesno(sf->cxt, _("Do you want to move partition data?"), &yes);
|
||||||
|
if (!yes) {
|
||||||
|
fdisk_info(sf->cxt, _("Leaving."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(typescript, "w");
|
||||||
|
if (!f)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* don't translate */
|
||||||
|
fprintf(f, "# sfdisk: " PACKAGE_STRING "\n");
|
||||||
|
fprintf(f, "# Disk: %s\n", devname);
|
||||||
|
fprintf(f, "# Partition: %zu\n", partno + 1);
|
||||||
|
fprintf(f, "# Operation: move data\n");
|
||||||
|
fprintf(f, "# Original start offset (in sectors): %ju\n", fdisk_partition_get_start(orig_pa));
|
||||||
|
fprintf(f, "# New start offset (in sectors): %ju\n", fdisk_partition_get_start(pa));
|
||||||
|
fprintf(f, "# Area size (in sectors): %ju\n", nsectors);
|
||||||
|
fprintf(f, "# Step size (in bytes): %zu\n", step_bytes);
|
||||||
|
fprintf(f, "# Steps: %zu\n", nsectors / step);
|
||||||
|
fprintf(f, "#\n");
|
||||||
|
fprintf(f, "# <step>: <from> <to> (step offsets in bytes)\n");
|
||||||
|
|
||||||
|
src = (backward ? from + nsectors : from) * ss;
|
||||||
|
dst = (backward ? to + nsectors : to) * ss;
|
||||||
|
buf = xmalloc(step_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
fprintf(stderr, ">>>%s: src=%ju, dst=%ju, step=%ju (%ju bytes)\n",
|
||||||
|
backward ? "backward" : "forward", src, dst, step, step_bytes);
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (cc = 1, i = 0; i < nsectors; i += step, cc++) {
|
||||||
|
ssize_t rc;
|
||||||
|
|
||||||
|
if (backward)
|
||||||
|
src -= step_bytes, dst -= step_bytes;
|
||||||
|
|
||||||
|
|
||||||
|
lseek(fd, src, SEEK_SET);
|
||||||
|
rc = read(fd, buf, step_bytes);
|
||||||
|
if (rc < 0 || rc != (ssize_t) step_bytes)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
lseek(fd, dst, SEEK_SET);
|
||||||
|
rc = write(fd, buf, step_bytes);
|
||||||
|
if (rc < 0 || rc != (ssize_t) step_bytes)
|
||||||
|
goto fail;
|
||||||
|
fsync(fd);
|
||||||
|
|
||||||
|
fprintf(f, "%05zu: %12ju %12ju\n", cc, src, dst);
|
||||||
|
|
||||||
|
#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
|
||||||
|
posix_fadvise(fd, src, step_bytes, POSIX_FADV_DONTNEED);
|
||||||
|
#endif
|
||||||
|
if (!backward)
|
||||||
|
src += step_bytes, dst += step_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
free(buf);
|
||||||
|
free(devname);
|
||||||
|
free(typescript);
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
fclose(f);
|
||||||
|
err(EXIT_FAILURE, _("%s: failed to move data"), devname);
|
||||||
|
}
|
||||||
|
|
||||||
static int write_changes(struct sfdisk *sf)
|
static int write_changes(struct sfdisk *sf)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -329,6 +501,8 @@ static int write_changes(struct sfdisk *sf)
|
||||||
fdisk_info(sf->cxt, _("The partition table is unchanged (--no-act)."));
|
fdisk_info(sf->cxt, _("The partition table is unchanged (--no-act)."));
|
||||||
else {
|
else {
|
||||||
rc = fdisk_write_disklabel(sf->cxt);
|
rc = fdisk_write_disklabel(sf->cxt);
|
||||||
|
if (rc == 0 && sf->movedata && sf->orig_pa)
|
||||||
|
rc = move_partition_data(sf, sf->partno, sf->orig_pa);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
fdisk_info(sf->cxt, _("\nThe partition table has been altered."));
|
fdisk_info(sf->cxt, _("\nThe partition table has been altered."));
|
||||||
fdisk_reread_partition_table(sf->cxt);
|
fdisk_reread_partition_table(sf->cxt);
|
||||||
|
@ -1195,6 +1369,8 @@ static int ignore_partition(struct fdisk_partition *pa)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sfdisk <device> [[-N] <partno>]
|
* sfdisk <device> [[-N] <partno>]
|
||||||
*
|
*
|
||||||
|
@ -1254,6 +1430,9 @@ static int command_fdisk(struct sfdisk *sf, int argc, char **argv)
|
||||||
devname, partno + 1);
|
devname, partno + 1);
|
||||||
created = 1;
|
created = 1;
|
||||||
next_partno = partno;
|
next_partno = partno;
|
||||||
|
|
||||||
|
if (sf->movedata)
|
||||||
|
sf->orig_pa = get_partition(sf->cxt, partno);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sf->append) {
|
if (sf->append) {
|
||||||
|
@ -1486,6 +1665,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
|
||||||
fputs(_(" -a, --append append partitions to existing partition table\n"), out);
|
fputs(_(" -a, --append append partitions to existing partition table\n"), out);
|
||||||
fputs(_(" -b, --backup backup partition table sectors (see -O)\n"), out);
|
fputs(_(" -b, --backup backup partition table sectors (see -O)\n"), out);
|
||||||
fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
|
fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out);
|
||||||
|
fputs(_(" --move-data[=<typescript>] move partition data after relocation (requires -N)\n"), out);
|
||||||
fputs(_(" -f, --force disable all consistency checking\n"), out);
|
fputs(_(" -f, --force disable all consistency checking\n"), out);
|
||||||
fputs(_(" --color[=<when>] colorize output (auto, always or never)\n"), out);
|
fputs(_(" --color[=<when>] colorize output (auto, always or never)\n"), out);
|
||||||
fprintf(out,
|
fprintf(out,
|
||||||
|
@ -1533,7 +1713,8 @@ int main(int argc, char *argv[])
|
||||||
OPT_PARTTYPE,
|
OPT_PARTTYPE,
|
||||||
OPT_PARTATTRS,
|
OPT_PARTATTRS,
|
||||||
OPT_BYTES,
|
OPT_BYTES,
|
||||||
OPT_COLOR
|
OPT_COLOR,
|
||||||
|
OPT_MOVEDATA,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option longopts[] = {
|
static const struct option longopts[] = {
|
||||||
|
@ -1554,6 +1735,7 @@ int main(int argc, char *argv[])
|
||||||
{ "list-types", no_argument, NULL, 'T' },
|
{ "list-types", no_argument, NULL, 'T' },
|
||||||
{ "no-act", no_argument, NULL, 'n' },
|
{ "no-act", no_argument, NULL, 'n' },
|
||||||
{ "no-reread", no_argument, NULL, OPT_NOREREAD },
|
{ "no-reread", no_argument, NULL, OPT_NOREREAD },
|
||||||
|
{ "move-data", optional_argument, NULL, OPT_MOVEDATA },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
{ "partno", required_argument, NULL, 'N' },
|
{ "partno", required_argument, NULL, 'N' },
|
||||||
{ "reorder", no_argument, NULL, 'r' },
|
{ "reorder", no_argument, NULL, 'r' },
|
||||||
|
@ -1697,6 +1879,10 @@ int main(int argc, char *argv[])
|
||||||
colormode = colormode_or_err(optarg,
|
colormode = colormode_or_err(optarg,
|
||||||
_("unsupported color mode"));
|
_("unsupported color mode"));
|
||||||
break;
|
break;
|
||||||
|
case OPT_MOVEDATA:
|
||||||
|
sf->movedata = 1;
|
||||||
|
sf->move_typescript = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(stderr);
|
usage(stderr);
|
||||||
}
|
}
|
||||||
|
@ -1716,6 +1902,9 @@ int main(int argc, char *argv[])
|
||||||
else if (!sf->act)
|
else if (!sf->act)
|
||||||
sf->act = ACT_FDISK; /* default */
|
sf->act = ACT_FDISK; /* default */
|
||||||
|
|
||||||
|
if (sf->movedata && !(sf->act == ACT_FDISK && sf->partno >= 0))
|
||||||
|
errx(EXIT_FAILURE, _("--movedata requires -N"));
|
||||||
|
|
||||||
switch (sf->act) {
|
switch (sf->act) {
|
||||||
case ACT_ACTIVATE:
|
case ACT_ACTIVATE:
|
||||||
rc = command_activate(sf, argc - optind, argv + optind);
|
rc = command_activate(sf, argc - optind, argv + optind);
|
||||||
|
|
Loading…
Reference in New Issue