Hi,

So, first working patch is attached.
Two new sub-commands added to gpart(8):
        gpart backup [-l] geom
        gpart restore [-F] [-f flags] geom [...]

"backup" command does dump partition table in simple text format
to standard output. With -l specified it does dump with partition's
labels. The format is simple, e.g:
# gpart show ada0
=>        34  1250263661  ada0  GPT  (596G)
          34         256     1  freebsd-boot  (128K)
         290     8388608     2  freebsd-swap  (4.0G)
     8388898  1241874797     3  freebsd-zfs  (592G)

# gpart show -l ada0
=>        34  1250263661  ada0  GPT  (596G)
          34         256     1  boot00  (128K)
         290     8388608     2  swap00  (4.0G)
     8388898  1241874797     3  zfs00  (592G)

# gpart backup ada0
GPT 128
1   freebsd-boot         34        256
2   freebsd-swap        290    8388608
3    freebsd-zfs    8388898 1241874797
# gpart backup -l ada0
GPT 128
1   freebsd-boot         34        256 boot00
2   freebsd-swap        290    8388608 swap00
3    freebsd-zfs    8388898 1241874797 zfs00

First line - partition scheme name and number of entries.
Next lines - partition table entries in the following format:
<index> <type> <start> <size> [label] [attributes]

Last two fields are optional.

"restore" command reads these commands from standard input.
With -F option it destroys partition table on specified geoms before
doing restore.

gpart does try to done all commands with auto-commit disabled. And
it does rollback when error occurs. If all commands successfully done
it calls "commit".

Small example:
# mdconfig -s 100m
md0
# mdconfig -s 100m
md1
# mdconfig -s 100m
md2
# gpart create -s gpt md0
md0 created
# gpart add -t freebsd-boot -s 128k md0
md0p1 added
# gpart add -t freebsd-zfs md0
md0p2 added
# gpart show md0
=>    34  204733  md0  GPT  (100M)
      34     256    1  freebsd-boot  (128K)
     290  204477    2  freebsd-zfs  (100M)

# gpart backup md0 | gpart restore md1 md2
# gpart show md0 md1 md2
=>    34  204733  md0  GPT  (100M)
      34     256    1  freebsd-boot  (128K)
     290  204477    2  freebsd-zfs  (100M)

=>    34  204733  md1  GPT  (100M)
      34     256    1  freebsd-boot  (128K)
     290  204477    2  freebsd-zfs  (100M)

=>    34  204733  md2  GPT  (100M)
      34     256    1  freebsd-boot  (128K)
     290  204477    2  freebsd-zfs  (100M)

# gpart restore -F md0 md1
mbr 4
1 freebsd * * [active]
^D
# gpart show md0 md1
=>     9  204786  md0  MBR  (100M)
       9  204786    1  freebsd  [active]  (100M)

=>     9  204786  md1  MBR  (100M)
       9  204786    1  freebsd  [active]  (100M)

-- 
WBR, Andrey V. Elsukov
Index: head/sbin/geom/class/part/geom_part.c
===================================================================
--- head/sbin/geom/class/part/geom_part.c       (revision 215454)
+++ head/sbin/geom/class/part/geom_part.c       (working copy)
@@ -85,6 +85,8 @@ static int gpart_show_hasopt(struct gctl_req *, co
 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 +99,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 +172,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] geom ..."
+       },
        { "recover", 0, gpart_issue, {
                { 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
                G_OPT_SENTINEL },
@@ -654,6 +667,279 @@ gpart_show(struct gctl_req *req, unsigned int fl _
        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 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;
+               for (ap = argv, l = 0;
+                   (*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, l = 0;
+                   (*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)
 {

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to