diff --git a/sbin/geom/class/part/geom_part.c b/sbin/geom/class/part/geom_part.c index 94e44bef39b3..58dbc3c1c07a 100644 --- a/sbin/geom/class/part/geom_part.c +++ b/sbin/geom/class/part/geom_part.c @@ -56,6 +56,10 @@ uint32_t PUBSYM(version) = 0; static char optional[] = ""; static char flags[] = "C"; +static char bootcode_param[] = "bootcode"; +static char index_param[] = "index"; +static char partcode_param[] = "partcode"; + static void gpart_bootcode(struct gctl_req *, unsigned int); static void gpart_show(struct gctl_req *, unsigned int); @@ -64,14 +68,16 @@ struct g_command PUBSYM(class_commands)[] = { { 'b', "start", NULL, G_TYPE_STRING }, { 's', "size", NULL, G_TYPE_STRING }, { 't', "type", NULL, G_TYPE_STRING }, - { 'i', "index", optional, G_TYPE_STRING }, + { 'i', index_param, optional, G_TYPE_STRING }, { 'l', "label", optional, G_TYPE_STRING }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL }, { "bootcode", 0, gpart_bootcode, { - { 'b', "bootcode", NULL, G_TYPE_STRING }, + { 'b', bootcode_param, optional, G_TYPE_STRING }, + { 'p', partcode_param, optional, G_TYPE_STRING }, + { 'i', index_param, optional, G_TYPE_STRING }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL @@ -85,7 +91,7 @@ struct g_command PUBSYM(class_commands)[] = { "provider", NULL }, { "delete", 0, NULL, { - { 'i', "index", NULL, G_TYPE_STRING }, + { 'i', index_param, NULL, G_TYPE_STRING }, { 'f', "flags", flags, G_TYPE_STRING }, G_OPT_SENTINEL }, "geom", NULL @@ -95,7 +101,7 @@ struct g_command PUBSYM(class_commands)[] = { G_OPT_SENTINEL }, "geom", NULL }, { "modify", 0, NULL, { - { 'i', "index", NULL, G_TYPE_STRING }, + { 'i', index_param, NULL, G_TYPE_STRING }, { 'l', "label", optional, G_TYPE_STRING }, { 't', "type", optional, G_TYPE_STRING }, { 'f', "flags", flags, G_TYPE_STRING }, @@ -286,38 +292,150 @@ gpart_show(struct gctl_req *req, unsigned int fl __unused) geom_deletetree(&mesh); } -static void -gpart_bootcode(struct gctl_req *req, unsigned int fl __unused) +static void * +gpart_bootfile_read(const char *bootfile, ssize_t *size) { struct stat sb; - const char *bootfile; void *code; - int error, fd, size; + int fd; - bootfile = gctl_get_ascii(req, "bootcode"); - if (bootfile == NULL) - errx(EXIT_FAILURE, "Missing bootfile argument"); - - error = stat(bootfile, &sb); - if (error) - errx(EXIT_FAILURE, "%s: not found", bootfile); + if (stat(bootfile, &sb) == -1) + err(EXIT_FAILURE, "%s", bootfile); if (!S_ISREG(sb.st_mode)) errx(EXIT_FAILURE, "%s: not a regular file", bootfile); - if (sb.st_size >= 1024*1024) - errx(EXIT_FAILURE, "%s: file too big", bootfile); + if (sb.st_size == 0) + errx(EXIT_FAILURE, "%s: empty file", bootfile); + if (*size > 0 && sb.st_size >= *size) + errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, + *size); - size = sb.st_size; + *size = sb.st_size; fd = open(bootfile, O_RDONLY); if (fd == -1) - errx(EXIT_FAILURE, "%s: unable to open", bootfile); - code = malloc(size); + err(EXIT_FAILURE, "%s", bootfile); + code = malloc(*size); if (code == NULL) - errx(EXIT_FAILURE, "out of memory"); - if (read(fd, code, size) != size) - errx(EXIT_FAILURE, "%s: unable to read", bootfile); + err(EXIT_FAILURE, NULL); + if (read(fd, code, *size) != *size) + err(EXIT_FAILURE, "%s", bootfile); close(fd); - gctl_change_param(req, "bootcode", size, code); - gctl_issue(req); + return (code); +} + +static void +gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size) +{ + char dsf[128]; + struct gmesh mesh; + struct gclass *classp; + struct ggeom *gp; + struct gprovider *pp; + const char *s; + int error, fd; + + 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); + } + s = gctl_get_ascii(req, "geom"); + gp = find_geom(classp, s); + if (gp == NULL) + errx(EXIT_FAILURE, "No such geom: %s.", s); + + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + s = find_provcfg(pp, "index"); + if (s == NULL) + continue; + if (atoi(s) == idx) + break; + } + + if (pp != NULL) { + snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); + fd = open(dsf, O_WRONLY); + if (fd == -1) + err(EXIT_FAILURE, "%s", dsf); + if (lseek(fd, size, SEEK_SET) != size) + errx(EXIT_FAILURE, "%s: not enough space", dsf); + if (lseek(fd, 0, SEEK_SET) != 0) + err(EXIT_FAILURE, "%s", dsf); + if (write(fd, code, size) != size) + err(EXIT_FAILURE, "%s", dsf); + close(fd); + } else + errx(EXIT_FAILURE, "invalid partition index"); + + geom_deletetree(&mesh); +} + +static void +gpart_bootcode(struct gctl_req *req, unsigned int fl __unused) +{ + const char *s; + char *sp; + void *bootcode, *partcode; + size_t bootsize, partsize; + int error, idx; + + if (gctl_has_param(req, bootcode_param)) { + s = gctl_get_ascii(req, bootcode_param); + bootsize = 64 * 1024; /* Arbitrary limit. */ + bootcode = gpart_bootfile_read(s, &bootsize); + error = gctl_change_param(req, bootcode_param, bootsize, + bootcode); + if (error) + errc(EXIT_FAILURE, error, "internal error"); + } else { + bootcode = NULL; + bootsize = 0; + } + + if (gctl_has_param(req, partcode_param)) { + s = gctl_get_ascii(req, partcode_param); + partsize = bootsize * 1024; + partcode = gpart_bootfile_read(s, &partsize); + error = gctl_delete_param(req, partcode_param); + if (error) + errc(EXIT_FAILURE, error, "internal error"); + } else { + partcode = NULL; + partsize = 0; + } + + if (gctl_has_param(req, index_param)) { + if (partcode == NULL) + errx(EXIT_FAILURE, "-i is only valid with -p"); + s = gctl_get_ascii(req, index_param); + idx = strtol(s, &sp, 10); + if (idx < 1 || *s == '\0' || *sp != '\0') + errx(EXIT_FAILURE, "invalid partition index"); + error = gctl_delete_param(req, index_param); + if (error) + errc(EXIT_FAILURE, error, "internal error"); + } else + idx = 0; + + if (partcode != NULL) { + if (idx == 0) + errx(EXIT_FAILURE, "missing -i option"); + gpart_write_partcode(req, idx, partcode, partsize); + } else { + if (bootcode == NULL) + errx(EXIT_FAILURE, "no -b nor -p"); + } + + if (bootcode != NULL) { + s = gctl_issue(req); + if (s != NULL) + errx(EXIT_FAILURE, "%s", s); + } }