From 0ebc0224588b720f371e73a27b17f524c7cb51be Mon Sep 17 00:00:00 2001 From: ae Date: Sat, 27 Nov 2010 13:53:21 +0000 Subject: [PATCH] MFC r215570: Add to gpart(8) an ability to backup partition table and restore it from given backup. MFC r215671: Always dump partition labels with `gpart backup`, but `gpart restore` does restore them only when -l option is specified [1]. Make number of entries field in backup format optional. Document -l and -r options of `gpart show` action. MFC r215672: Add SIGINT handler to `gpart restore` action. git-svn-id: svn://svn.freebsd.org/base/stable/8@215941 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sbin/geom/class/part/geom_part.c | 317 +++++++++++++++++++++++++++++++ sbin/geom/class/part/gpart.8 | 73 ++++++- 2 files changed, 389 insertions(+), 1 deletion(-) diff --git a/sbin/geom/class/part/geom_part.c b/sbin/geom/class/part/geom_part.c index 97a58944f..6c8a4249d 100644 --- a/sbin/geom/class/part/geom_part.c +++ b/sbin/geom/class/part/geom_part.c @@ -31,12 +31,14 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -64,6 +66,7 @@ static char flags[] = "C"; static char sstart[32]; static char ssize[32]; +volatile sig_atomic_t undo_restore; static const char const bootcode_param[] = "bootcode"; static const char const index_param[] = "index"; @@ -86,6 +89,8 @@ 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_destroy(struct gctl_req *, unsigned int); +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, { @@ -98,6 +103,9 @@ struct g_command PUBSYM(class_commands)[] = { G_OPT_SENTINEL }, "geom", NULL }, + { "backup", 0, gpart_backup, G_NULL_OPTS, + NULL, "geom" + }, { "bootcode", 0, gpart_bootcode, { { 'b', bootcode_param, optional, G_TYPE_STRING }, { 'p', partcode_param, optional, G_TYPE_STRING }, @@ -161,6 +169,13 @@ struct g_command PUBSYM(class_commands)[] = { G_OPT_SENTINEL }, "geom", NULL }, + { "restore", 0, gpart_restore, { + { 'F', "force", NULL, G_TYPE_BOOL }, + { 'l', "restore_labels", NULL, G_TYPE_BOOL }, + { 'f', "flags", flags, G_TYPE_STRING }, + G_OPT_SENTINEL }, + NULL, "[-lF] [-f flags] provider [...]" + }, { "recover", 0, gpart_issue, { { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, @@ -652,6 +667,308 @@ 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, 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(); + 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 %s %s\n", + windex, find_provcfg(pp, "index"), + wtype, find_provcfg(pp, "type"), + wblocks, (intmax_t)sector, + wblocks, (intmax_t)length, + (s != NULL) ? s: "", 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_sighndl(int sig __unused) +{ + undo_restore = 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; + struct sigaction si_sa; + const char *s, *flagss, *errstr, *label; + char **ap, *argv[6], line[BUFSIZ], *pline; + int error, forced, i, l, nargs, created, rl; + + nargs = gctl_get_int(req, "nargs"); + if (nargs < 1) + errx(EXIT_FAILURE, "Invalid number of arguments."); + + forced = gctl_get_int(req, "force"); + flagss = gctl_get_ascii(req, "flags"); + rl = gctl_get_int(req, "restore_labels"); + 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); + } + + sigemptyset(&si_sa.sa_mask); + si_sa.sa_flags = 0; + si_sa.sa_handler = gpart_sighndl; + if (sigaction(SIGINT, &si_sa, 0) == -1) + err(EXIT_FAILURE, "sigaction SIGINT"); + + 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, "geom", -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 (undo_restore == 0 && + fgets(line, sizeof(line) - 1, stdin) != NULL) { + /* Format of backup entries: + * + * [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 == 1 || l == 2) { /* create table */ + if (created) + errx(EXIT_FAILURE, "Incorrect backup format."); + 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, "create"); + gctl_ro_param(r, "scheme", -1, argv[0]); + if (l == 2) + gctl_ro_param(r, "entries", + -1, argv[1]); + gctl_ro_param(r, "flags", -1, "restore"); + gctl_ro_param(r, "provider", -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(); + 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, index_param, -1, argv[0]); + 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 (rl != 0 && label != NULL) + gctl_ro_param(r, "label", -1, argv[4]); + gctl_ro_param(r, "geom", -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++; + label = argv[0]; /* save pointer to index_param */ + 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, index_param, -1, label); + gctl_ro_param(r, "attrib", -1, argv[--l]); + gctl_ro_param(r, "geom", -1, s); + errstr = gctl_issue(r); + if (errstr != NULL && errstr[0] != '\0') { + gpart_print_error(errstr); + gctl_free(r); + goto backout; + } + gctl_free(r); + } + } + } + if (undo_restore) + goto backout; + /* commit changes if needed */ + if (strchr(flagss, '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, "geom", -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, "geom", -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) { diff --git a/sbin/geom/class/part/gpart.8 b/sbin/geom/class/part/gpart.8 index 9aed69ad3..90a944ca8 100644 --- a/sbin/geom/class/part/gpart.8 +++ b/sbin/geom/class/part/gpart.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 25, 2010 +.Dd November 22, 2010 .Dt GPART 8 .Os .Sh NAME @@ -91,6 +91,10 @@ utility: .Op Fl l Ar label .Op Fl f Ar flags .Ar geom +.\" ==== BACKUP ==== +.Nm +.Cm backup +.Ar geom .\" ==== BOOTCODE ==== .Nm .Cm bootcode @@ -141,6 +145,13 @@ utility: .Op Fl s Ar size .Op Fl f Ar flags .Ar geom +.\" ==== RESTORE ==== +.Nm +.Cm restore +.Op Fl lF +.Op Fl f Ar flags +.Ar provider +.Op Ar ... .\" ==== SET ==== .Nm .Cm set @@ -151,6 +162,7 @@ utility: .\" ==== SHOW ==== .Nm .Cm show +.Op Fl lr .Op Ar geom ... .\" ==== UNDO ==== .Nm @@ -208,6 +220,11 @@ 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. .\" ==== BOOTCODE ==== .It Cm bootcode Embed bootstrap code into the partitioning scheme's metadata on the @@ -401,6 +418,30 @@ 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 l +Restore partition labels for partitioning schemes that support them. +.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. @@ -421,6 +462,14 @@ about its use. .It Cm show Show the current partition information of the specified geoms or all geoms if none are specified. +Additional options include: +.Bl -tag -width 10n +.It Fl l +For partition schemes that support partition labels print them +instead of partition type. +.It Fl r +Show raw partition type instead of symbolic name. +.El .\" ==== UNDO ==== .It Cm undo Revert any pending changes for geom @@ -770,6 +819,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 da0 > da0.backup +.Ed +.Pp +Restore partition table from backup to +.Pa da0 +.Bd -literal -offset indent +/sbin/gpart restore -l 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 , -- 2.45.0