Add to gpart(8) an ability to backup partition table and

restore it from given backup.

Discussed with:	geom@
Approved by:	kib (mentor)
MFC after:	1 week
This commit is contained in:
ae 2010-11-20 16:03:53 +00:00
parent 951f9264d7
commit ea55525b93
2 changed files with 371 additions and 1 deletions

View File

@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$");
#include <sys/vtoc.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@ -85,6 +86,8 @@ static int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
static void gpart_print_error(const char *);
static void gpart_backup(struct gctl_req *, unsigned int);
static void gpart_restore(struct gctl_req *, unsigned int);
struct g_command PUBSYM(class_commands)[] = {
{ "add", 0, gpart_issue, {
@ -97,6 +100,11 @@ struct g_command PUBSYM(class_commands)[] = {
G_OPT_SENTINEL },
"[-b start] [-s size] -t type [-i index] [-l label] [-f flags] geom"
},
{ "backup", 0, gpart_backup, {
{ 'l', "backup_labels", NULL, G_TYPE_BOOL},
G_OPT_SENTINEL },
"[-l] geom"
},
{ "bootcode", 0, gpart_bootcode, {
{ 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
{ 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
@ -165,6 +173,12 @@ struct g_command PUBSYM(class_commands)[] = {
G_OPT_SENTINEL },
"[-s size] -i index [-f flags] geom"
},
{ "restore", 0, gpart_restore, {
{ 'F', "force", NULL, G_TYPE_BOOL },
{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
G_OPT_SENTINEL },
"[-F] [-f flags] provider [...]"
},
{ "recover", 0, gpart_issue, {
{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
G_OPT_SENTINEL },
@ -654,6 +668,295 @@ gpart_show(struct gctl_req *req, unsigned int fl __unused)
geom_deletetree(&mesh);
}
static void
gpart_backup(struct gctl_req *req, unsigned int fl __unused)
{
struct gmesh mesh;
struct gclass *classp;
struct gprovider *pp;
struct ggeom *gp;
const char *s, *scheme;
off_t sector, end;
off_t length, secsz;
int error, labels, i, windex, wblocks, wtype;
if (gctl_get_int(req, "nargs") != 1)
errx(EXIT_FAILURE, "Invalid number of arguments.");
error = geom_gettree(&mesh);
if (error != 0)
errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
s = gctl_get_ascii(req, "class");
if (s == NULL)
abort();
classp = find_class(&mesh, s);
if (classp == NULL) {
geom_deletetree(&mesh);
errx(EXIT_FAILURE, "Class %s not found.", s);
}
s = gctl_get_ascii(req, "arg0");
if (s == NULL)
abort();
labels = gctl_get_int(req, "backup_labels");
gp = find_geom(classp, s);
if (gp == NULL)
errx(EXIT_FAILURE, "No such geom: %s.", s);
scheme = find_geomcfg(gp, "scheme");
if (scheme == NULL)
abort();
pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
secsz = pp->lg_sectorsize;
s = find_geomcfg(gp, "last");
wblocks = strlen(s);
wtype = 0;
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
s = find_provcfg(pp, "type");
i = strlen(s);
if (i > wtype)
wtype = i;
}
s = find_geomcfg(gp, "entries");
windex = strlen(s);
printf("%s %s\n", scheme, s);
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
s = find_provcfg(pp, "start");
if (s == NULL) {
s = find_provcfg(pp, "offset");
sector = (off_t)strtoimax(s, NULL, 0) / secsz;
} else
sector = (off_t)strtoimax(s, NULL, 0);
s = find_provcfg(pp, "end");
if (s == NULL) {
s = find_provcfg(pp, "length");
length = (off_t)strtoimax(s, NULL, 0) / secsz;
} else {
end = (off_t)strtoimax(s, NULL, 0);
length = end - sector + 1;
}
s = find_provcfg(pp, "label");
printf("%-*s %*s %*jd %*jd",
windex, find_provcfg(pp, "index"),
wtype, find_provcfg(pp, "type"),
wblocks, (intmax_t)sector,
wblocks, (intmax_t)length);
if (labels && s != NULL)
printf(" %s", s);
printf(" %s\n", fmtattrib(pp));
}
geom_deletetree(&mesh);
}
static int
skip_line(const char *p)
{
while (*p != '\0') {
if (*p == '#')
return (1);
if (isspace(*p) == 0)
return (0);
p++;
}
return (1);
}
static void
gpart_restore(struct gctl_req *req, unsigned int fl __unused)
{
struct gmesh mesh;
struct gclass *classp;
struct gctl_req *r;
struct ggeom *gp;
const char *s, *flags, *errstr, *label;
char **ap, *argv[6], line[BUFSIZ], *pline;
int error, forced, i, l, nargs, created;
intmax_t n;
nargs = gctl_get_int(req, "nargs");
if (nargs < 1)
errx(EXIT_FAILURE, "Invalid number of arguments.");
forced = gctl_get_int(req, "force");
flags = gctl_get_ascii(req, "flags");
s = gctl_get_ascii(req, "class");
if (s == NULL)
abort();
error = geom_gettree(&mesh);
if (error != 0)
errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
classp = find_class(&mesh, s);
if (classp == NULL) {
geom_deletetree(&mesh);
errx(EXIT_FAILURE, "Class %s not found.", s);
}
if (forced) {
/* destroy existent partition table before restore */
for (i = 0; i < nargs; i++) {
s = gctl_get_ascii(req, "arg%d", i);
gp = find_geom(classp, s);
if (gp != NULL) {
r = gctl_get_handle();
gctl_ro_param(r, "class", -1,
classp->lg_name);
gctl_ro_param(r, "verb", -1, "destroy");
gctl_ro_param(r, "flags", -1, "restore");
gctl_ro_param(r, "force", sizeof(forced),
&forced);
gctl_ro_param(r, "arg0", -1, s);
errstr = gctl_issue(r);
if (errstr != NULL && errstr[0] != '\0') {
gpart_print_error(errstr);
gctl_free(r);
goto backout;
}
gctl_free(r);
}
}
}
created = 0;
while (fgets(line, sizeof(line) - 1, stdin)) {
/* Format of backup entries:
* <scheme name> <number of entries>
* <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
*/
pline = (char *)line;
pline[strlen(line) - 1] = 0;
if (skip_line(pline))
continue;
for (ap = argv;
(*ap = strsep(&pline, " \t")) != NULL;)
if (**ap != '\0' && ++ap >= &argv[6])
break;
l = ap - &argv[0];
label = pline = NULL;
if (l == 2) { /* create table */
if (created)
errx(EXIT_FAILURE, "Incorrect backup format.");
n = atoi(argv[1]);
for (i = 0; i < nargs; i++) {
s = gctl_get_ascii(req, "arg%d", i);
r = gctl_get_handle();
n = strtoimax(argv[1], NULL, 0);
gctl_ro_param(r, "class", -1,
classp->lg_name);
gctl_ro_param(r, "verb", -1, "create");
gctl_ro_param(r, "scheme", -1, argv[0]);
gctl_ro_param(r, "entries", sizeof(n), &n);
gctl_ro_param(r, "flags", -1, "restore");
gctl_ro_param(r, "arg0", -1, s);
errstr = gctl_issue(r);
if (errstr != NULL && errstr[0] != '\0') {
gpart_print_error(errstr);
gctl_free(r);
goto backout;
}
gctl_free(r);
}
created = 1;
continue;
} else if (l < 4 || created == 0)
errx(EXIT_FAILURE, "Incorrect backup format.");
else if (l == 5) {
if (strchr(argv[4], '[') == NULL)
label = argv[4];
else
pline = argv[4];
} else if (l == 6) {
label = argv[4];
pline = argv[5];
}
/* Add partitions to each table */
for (i = 0; i < nargs; i++) {
s = gctl_get_ascii(req, "arg%d", i);
r = gctl_get_handle();
n = strtoimax(argv[0], NULL, 0);
gctl_ro_param(r, "class", -1, classp->lg_name);
gctl_ro_param(r, "verb", -1, "add");
gctl_ro_param(r, "flags", -1, "restore");
gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
gctl_ro_param(r, "type", -1, argv[1]);
gctl_ro_param(r, "start", -1, argv[2]);
gctl_ro_param(r, "size", -1, argv[3]);
if (label != NULL)
gctl_ro_param(r, "label", -1, argv[4]);
gctl_ro_param(r, "arg0", -1, s);
error = gpart_autofill(r);
if (error != 0)
errc(EXIT_FAILURE, error, "autofill");
errstr = gctl_issue(r);
if (errstr != NULL && errstr[0] != '\0') {
gpart_print_error(errstr);
gctl_free(r);
goto backout;
}
gctl_free(r);
}
if (pline == NULL || *pline != '[')
continue;
/* set attributes */
pline++;
for (ap = argv;
(*ap = strsep(&pline, ",]")) != NULL;)
if (**ap != '\0' && ++ap >= &argv[6])
break;
for (i = 0; i < nargs; i++) {
l = ap - &argv[0];
s = gctl_get_ascii(req, "arg%d", i);
while (l > 0) {
r = gctl_get_handle();
gctl_ro_param(r, "class", -1, classp->lg_name);
gctl_ro_param(r, "verb", -1, "set");
gctl_ro_param(r, "flags", -1, "restore");
gctl_ro_param(r, GPART_PARAM_INDEX,
sizeof(n), &n);
gctl_ro_param(r, "attrib", -1, argv[--l]);
gctl_ro_param(r, "arg0", -1, s);
errstr = gctl_issue(r);
if (errstr != NULL && errstr[0] != '\0') {
gpart_print_error(errstr);
gctl_free(r);
goto backout;
}
gctl_free(r);
}
}
}
/* commit changes if needed */
if (strchr(flags, 'C') != NULL) {
for (i = 0; i < nargs; i++) {
s = gctl_get_ascii(req, "arg%d", i);
r = gctl_get_handle();
gctl_ro_param(r, "class", -1, classp->lg_name);
gctl_ro_param(r, "verb", -1, "commit");
gctl_ro_param(r, "arg0", -1, s);
errstr = gctl_issue(r);
if (errstr != NULL && errstr[0] != '\0') {
gpart_print_error(errstr);
gctl_free(r);
goto backout;
}
gctl_free(r);
}
}
gctl_free(req);
geom_deletetree(&mesh);
exit(EXIT_SUCCESS);
backout:
for (i = 0; i < nargs; i++) {
s = gctl_get_ascii(req, "arg%d", i);
r = gctl_get_handle();
gctl_ro_param(r, "class", -1, classp->lg_name);
gctl_ro_param(r, "verb", -1, "undo");
gctl_ro_param(r, "arg0", -1, s);
gctl_issue(r);
gctl_free(r);
}
gctl_free(req);
geom_deletetree(&mesh);
exit(EXIT_FAILURE);
}
static void *
gpart_bootfile_read(const char *bootfile, ssize_t *size)
{

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd October 25, 2010
.Dd November 20, 2010
.Dt GPART 8
.Os
.Sh NAME
@ -91,6 +91,11 @@ utility:
.Op Fl l Ar label
.Op Fl f Ar flags
.Ar geom
.\" ==== BACKUP ====
.Nm
.Cm backup
.Op Fl l
.Ar geom
.\" ==== BOOTCODE ====
.Nm
.Cm bootcode
@ -141,6 +146,13 @@ utility:
.Op Fl s Ar size
.Op Fl f Ar flags
.Ar geom
.\" ==== RESTORE ====
.Nm
.Cm restore
.Op Fl F
.Op Fl f Ar flags
.Ar provider
.Op Ar ...
.\" ==== SET ====
.Nm
.Cm set
@ -208,6 +220,17 @@ See the section entitled
below for a discussion
about its use.
.El
.\" ==== BACKUP ====
.It Cm backup
Dump a partition table to standard output in special format used by
.Cm restore
action.
.Pp
Additional options include:
.Bl -tag -width 10n
.It Fl l
Dump partition labels for partitioning schemes that support them.
.El
.\" ==== BOOTCODE ====
.It Cm bootcode
Embed bootstrap code into the partitioning scheme's metadata on the
@ -401,6 +424,28 @@ See the section entitled
below for a discussion
about its use.
.El
.\" ==== RESTORE ====
.It Cm restore
Restore the partition table from backup previously created by
.Cm backup
action and given from standard input. Only partition table
may be restored. This action does not affect content of partitions.
This mean that you should copy your data from backup after restoring
partition table and write bootcode again if it is needed.
.Pp
Additional options include:
.Bl -tag -width 10n
.It Fl F
Destroy partition table on the given
.Ar provider
before doing restore.
.It Fl f Ar flags
Additional operational flags.
See the section entitled
.Sx "OPERATIONAL FLAGS"
below for a discussion
about its use.
.El
.\" ==== SET ====
.It Cm set
Set the named attribute on the partition entry.
@ -770,6 +815,28 @@ After having created all required partitions, embed bootstrap code into them.
.Bd -literal -offset indent
/sbin/gpart bootcode -p /boot/boot1 da0
.Ed
.Pp
Create backup of partition table from
.Pa da0
.Bd -literal -offset indent
/sbin/gpart backup -l da0 > da0.backup
.Ed
.Pp
Restore partition table from backup to
.Pa da0
.Bd -literal -offset indent
/sbin/gpart restore da0 < /mnt/da0.backup
.Ed
.Pp
Clone partition table from
.Pa ada0
to
.Pa ada1
and
.Pa ada2
.Bd -literal -offset indent
/sbin/gpart backup ada0 | /sbin/gpart restore -F ada1 ada2
.Ed
.Sh SEE ALSO
.Xr dd 1 ,
.Xr geom 4 ,