geom_part.c revision 185496
1/*- 2 * Copyright (c) 2007, 2008 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sbin/geom/class/part/geom_part.c 185496 2008-11-30 23:46:31Z marcel $"); 29 30#include <sys/stat.h> 31 32#include <assert.h> 33#include <err.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <libgeom.h> 37#include <libutil.h> 38#include <paths.h> 39#include <stdint.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <strings.h> 44#include <unistd.h> 45 46#include "core/geom.h" 47#include "misc/subr.h" 48 49#ifdef STATIC_GEOM_CLASSES 50#define PUBSYM(x) gpart_##x 51#else 52#define PUBSYM(x) x 53#endif 54 55uint32_t PUBSYM(lib_version) = G_LIB_VERSION; 56uint32_t PUBSYM(version) = 0; 57 58static char optional[] = ""; 59static char flags[] = "C"; 60 61static char bootcode_param[] = "bootcode"; 62static char index_param[] = "index"; 63static char partcode_param[] = "partcode"; 64 65static void gpart_bootcode(struct gctl_req *, unsigned int); 66static void gpart_issue(struct gctl_req *, unsigned int); 67static void gpart_show(struct gctl_req *, unsigned int); 68 69struct g_command PUBSYM(class_commands)[] = { 70 { "add", 0, gpart_issue, { 71 { 'b', "start", NULL, G_TYPE_STRING }, 72 { 's', "size", NULL, G_TYPE_STRING }, 73 { 't', "type", NULL, G_TYPE_STRING }, 74 { 'i', index_param, optional, G_TYPE_STRING }, 75 { 'l', "label", optional, G_TYPE_STRING }, 76 { 'f', "flags", flags, G_TYPE_STRING }, 77 G_OPT_SENTINEL }, 78 "geom", NULL 79 }, 80 { "bootcode", 0, gpart_bootcode, { 81 { 'b', bootcode_param, optional, G_TYPE_STRING }, 82 { 'p', partcode_param, optional, G_TYPE_STRING }, 83 { 'i', index_param, optional, G_TYPE_STRING }, 84 { 'f', "flags", flags, G_TYPE_STRING }, 85 G_OPT_SENTINEL }, 86 "geom", NULL 87 }, 88 { "commit", 0, gpart_issue, G_NULL_OPTS, "geom", NULL }, 89 { "create", 0, gpart_issue, { 90 { 's', "scheme", NULL, G_TYPE_STRING }, 91 { 'n', "entries", optional, G_TYPE_STRING }, 92 { 'f', "flags", flags, G_TYPE_STRING }, 93 G_OPT_SENTINEL }, 94 "provider", NULL 95 }, 96 { "delete", 0, gpart_issue, { 97 { 'i', index_param, NULL, G_TYPE_STRING }, 98 { 'f', "flags", flags, G_TYPE_STRING }, 99 G_OPT_SENTINEL }, 100 "geom", NULL 101 }, 102 { "destroy", 0, gpart_issue, { 103 { 'f', "flags", flags, G_TYPE_STRING }, 104 G_OPT_SENTINEL }, 105 "geom", NULL }, 106 { "modify", 0, gpart_issue, { 107 { 'i', index_param, NULL, G_TYPE_STRING }, 108 { 'l', "label", optional, G_TYPE_STRING }, 109 { 't', "type", optional, G_TYPE_STRING }, 110 { 'f', "flags", flags, G_TYPE_STRING }, 111 G_OPT_SENTINEL }, 112 "geom", NULL 113 }, 114 { "set", 0, gpart_issue, { 115 { 'a', "attrib", NULL, G_TYPE_STRING }, 116 { 'i', index_param, NULL, G_TYPE_STRING }, 117 { 'f', "flags", flags, G_TYPE_STRING }, 118 G_OPT_SENTINEL }, 119 "geom", NULL 120 }, 121 { "show", 0, gpart_show, { 122 { 'l', "show_label", NULL, G_TYPE_BOOL }, 123 { 'r', "show_rawtype", NULL, G_TYPE_BOOL }, 124 G_OPT_SENTINEL }, 125 NULL, "[-lr] [geom ...]" 126 }, 127 { "undo", 0, gpart_issue, G_NULL_OPTS, "geom", NULL }, 128 { "unset", 0, gpart_issue, { 129 { 'a', "attrib", NULL, G_TYPE_STRING }, 130 { 'i', index_param, NULL, G_TYPE_STRING }, 131 { 'f', "flags", flags, G_TYPE_STRING }, 132 G_OPT_SENTINEL }, 133 "geom", NULL 134 }, 135 G_CMD_SENTINEL 136}; 137 138static struct gclass * 139find_class(struct gmesh *mesh, const char *name) 140{ 141 struct gclass *classp; 142 143 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 144 if (strcmp(classp->lg_name, name) == 0) 145 return (classp); 146 } 147 return (NULL); 148} 149 150static struct ggeom * 151find_geom(struct gclass *classp, const char *name) 152{ 153 struct ggeom *gp; 154 155 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 156 if (strcmp(gp->lg_name, name) == 0) 157 return (gp); 158 } 159 return (NULL); 160} 161 162static const char * 163find_geomcfg(struct ggeom *gp, const char *cfg) 164{ 165 struct gconfig *gc; 166 167 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 168 if (!strcmp(gc->lg_name, cfg)) 169 return (gc->lg_val); 170 } 171 return (NULL); 172} 173 174static const char * 175find_provcfg(struct gprovider *pp, const char *cfg) 176{ 177 struct gconfig *gc; 178 179 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 180 if (!strcmp(gc->lg_name, cfg)) 181 return (gc->lg_val); 182 } 183 return (NULL); 184} 185 186static struct gprovider * 187find_provider(struct ggeom *gp, unsigned long long minsector) 188{ 189 struct gprovider *pp, *bestpp; 190 unsigned long long offset; 191 unsigned long long sector, bestsector; 192 193 bestpp = NULL; 194 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 195 offset = atoll(find_provcfg(pp, "offset")); 196 sector = offset / pp->lg_sectorsize; 197 if (sector < minsector) 198 continue; 199 if (bestpp != NULL && sector >= bestsector) 200 continue; 201 bestpp = pp; 202 bestsector = sector; 203 } 204 return (bestpp); 205} 206 207static const char * 208fmtsize(int64_t rawsz) 209{ 210 static char buf[5]; 211 212 humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE, 213 HN_B | HN_NOSPACE | HN_DECIMAL); 214 return (buf); 215} 216 217static const char * 218fmtattrib(struct gprovider *pp) 219{ 220 static char buf[128]; 221 struct gconfig *gc; 222 u_int idx; 223 224 buf[0] = '\0'; 225 idx = 0; 226 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 227 if (strcmp(gc->lg_name, "attrib") != 0) 228 continue; 229 idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s", 230 (idx == 0) ? " [" : ",", gc->lg_val); 231 } 232 if (idx > 0) 233 snprintf(buf + idx, sizeof(buf) - idx, "] "); 234 return (buf); 235} 236 237static void 238gpart_show_geom(struct ggeom *gp, const char *element) 239{ 240 struct gprovider *pp; 241 const char *s, *scheme; 242 unsigned long long first, last, sector, end; 243 unsigned long long offset, length, secsz; 244 int idx, wblocks, wname; 245 246 scheme = find_geomcfg(gp, "scheme"); 247 s = find_geomcfg(gp, "first"); 248 first = atoll(s); 249 s = find_geomcfg(gp, "last"); 250 last = atoll(s); 251 wblocks = strlen(s); 252 wname = strlen(gp->lg_name); 253 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 254 secsz = pp->lg_sectorsize; 255 printf("=>%*llu %*llu %*s %s (%s)\n", 256 wblocks, first, wblocks, (last - first + 1), 257 wname, gp->lg_name, 258 scheme, fmtsize(pp->lg_mediasize)); 259 260 while ((pp = find_provider(gp, first)) != NULL) { 261 s = find_provcfg(pp, "offset"); 262 offset = atoll(s); 263 sector = offset / secsz; 264 s = find_provcfg(pp, "length"); 265 length = atoll(s); 266 s = find_provcfg(pp, "index"); 267 idx = atoi(s); 268 end = sector + length / secsz; 269 if (first < sector) { 270 printf(" %*llu %*llu %*s - free - (%s)\n", 271 wblocks, first, wblocks, sector - first, 272 wname, "", 273 fmtsize((sector - first) * secsz)); 274 } 275 printf(" %*llu %*llu %*d %s %s (%s)\n", 276 wblocks, sector, wblocks, end - sector, 277 wname, idx, find_provcfg(pp, element), 278 fmtattrib(pp), fmtsize(pp->lg_mediasize)); 279 first = end; 280 } 281 if (first <= last) { 282 printf(" %*llu %*llu %*s - free - (%s)\n", 283 wblocks, first, wblocks, last - first + 1, 284 wname, "", 285 fmtsize((last - first + 1) * secsz)); 286 } 287 printf("\n"); 288} 289 290static int 291gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt) 292{ 293 294 if (!gctl_get_int(req, opt)) 295 return (0); 296 297 if (elt != NULL) 298 errx(EXIT_FAILURE, "-l and -r are mutually exclusive"); 299 300 return (1); 301} 302 303static void 304gpart_show(struct gctl_req *req, unsigned int fl __unused) 305{ 306 struct gmesh mesh; 307 struct gclass *classp; 308 struct ggeom *gp; 309 const char *element, *name; 310 int error, i, nargs; 311 312 element = NULL; 313 if (gpart_show_hasopt(req, "show_label", element)) 314 element = "label"; 315 if (gpart_show_hasopt(req, "show_rawtype", element)) 316 element = "rawtype"; 317 if (element == NULL) 318 element = "type"; 319 320 name = gctl_get_ascii(req, "class"); 321 if (name == NULL) 322 abort(); 323 error = geom_gettree(&mesh); 324 if (error != 0) 325 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 326 classp = find_class(&mesh, name); 327 if (classp == NULL) { 328 geom_deletetree(&mesh); 329 errx(EXIT_FAILURE, "Class %s not found.", name); 330 } 331 nargs = gctl_get_int(req, "nargs"); 332 if (nargs > 0) { 333 for (i = 0; i < nargs; i++) { 334 name = gctl_get_ascii(req, "arg%d", i); 335 gp = find_geom(classp, name); 336 if (gp != NULL) 337 gpart_show_geom(gp, element); 338 else 339 errx(EXIT_FAILURE, "No such geom: %s.", name); 340 } 341 } else { 342 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 343 gpart_show_geom(gp, element); 344 } 345 } 346 geom_deletetree(&mesh); 347} 348 349static void * 350gpart_bootfile_read(const char *bootfile, ssize_t *size) 351{ 352 struct stat sb; 353 void *code; 354 int fd; 355 356 if (stat(bootfile, &sb) == -1) 357 err(EXIT_FAILURE, "%s", bootfile); 358 if (!S_ISREG(sb.st_mode)) 359 errx(EXIT_FAILURE, "%s: not a regular file", bootfile); 360 if (sb.st_size == 0) 361 errx(EXIT_FAILURE, "%s: empty file", bootfile); 362 if (*size > 0 && sb.st_size >= *size) 363 errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, 364 *size); 365 366 *size = sb.st_size; 367 368 fd = open(bootfile, O_RDONLY); 369 if (fd == -1) 370 err(EXIT_FAILURE, "%s", bootfile); 371 code = malloc(*size); 372 if (code == NULL) 373 err(EXIT_FAILURE, NULL); 374 if (read(fd, code, *size) != *size) 375 err(EXIT_FAILURE, "%s", bootfile); 376 close(fd); 377 378 return (code); 379} 380 381static void 382gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size) 383{ 384 char dsf[128]; 385 struct gmesh mesh; 386 struct gclass *classp; 387 struct ggeom *gp; 388 struct gprovider *pp; 389 const char *s; 390 char *buf; 391 off_t bsize; 392 int error, fd; 393 394 s = gctl_get_ascii(req, "class"); 395 if (s == NULL) 396 abort(); 397 error = geom_gettree(&mesh); 398 if (error != 0) 399 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 400 classp = find_class(&mesh, s); 401 if (classp == NULL) { 402 geom_deletetree(&mesh); 403 errx(EXIT_FAILURE, "Class %s not found.", s); 404 } 405 s = gctl_get_ascii(req, "geom"); 406 gp = find_geom(classp, s); 407 if (gp == NULL) 408 errx(EXIT_FAILURE, "No such geom: %s.", s); 409 410 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 411 s = find_provcfg(pp, "index"); 412 if (s == NULL) 413 continue; 414 if (atoi(s) == idx) 415 break; 416 } 417 418 if (pp != NULL) { 419 snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); 420 fd = open(dsf, O_WRONLY); 421 if (fd == -1) 422 err(EXIT_FAILURE, "%s", dsf); 423 if (lseek(fd, size, SEEK_SET) != size) 424 errx(EXIT_FAILURE, "%s: not enough space", dsf); 425 if (lseek(fd, 0, SEEK_SET) != 0) 426 err(EXIT_FAILURE, "%s", dsf); 427 428 /* 429 * When writing to a disk device, the write must be 430 * sector aligned and not write to any partial sectors, 431 * so round up the buffer size to the next sector and zero it. 432 */ 433 bsize = (size + pp->lg_sectorsize - 1) / 434 pp->lg_sectorsize * pp->lg_sectorsize; 435 buf = calloc(1, bsize); 436 if (buf == NULL) 437 err(EXIT_FAILURE, "%s", dsf); 438 bcopy(code, buf, size); 439 if (write(fd, buf, bsize) != bsize) 440 err(EXIT_FAILURE, "%s", dsf); 441 free(buf); 442 close(fd); 443 } else 444 errx(EXIT_FAILURE, "invalid partition index"); 445 446 geom_deletetree(&mesh); 447} 448 449static void 450gpart_bootcode(struct gctl_req *req, unsigned int fl) 451{ 452 const char *s; 453 char *sp; 454 void *bootcode, *partcode; 455 size_t bootsize, partsize; 456 int error, idx; 457 458 if (gctl_has_param(req, bootcode_param)) { 459 s = gctl_get_ascii(req, bootcode_param); 460 bootsize = 64 * 1024; /* Arbitrary limit. */ 461 bootcode = gpart_bootfile_read(s, &bootsize); 462 error = gctl_change_param(req, bootcode_param, bootsize, 463 bootcode); 464 if (error) 465 errc(EXIT_FAILURE, error, "internal error"); 466 } else { 467 bootcode = NULL; 468 bootsize = 0; 469 } 470 471 if (gctl_has_param(req, partcode_param)) { 472 s = gctl_get_ascii(req, partcode_param); 473 partsize = bootsize * 1024; 474 partcode = gpart_bootfile_read(s, &partsize); 475 error = gctl_delete_param(req, partcode_param); 476 if (error) 477 errc(EXIT_FAILURE, error, "internal error"); 478 } else { 479 partcode = NULL; 480 partsize = 0; 481 } 482 483 if (gctl_has_param(req, index_param)) { 484 if (partcode == NULL) 485 errx(EXIT_FAILURE, "-i is only valid with -p"); 486 s = gctl_get_ascii(req, index_param); 487 idx = strtol(s, &sp, 10); 488 if (idx < 1 || *s == '\0' || *sp != '\0') 489 errx(EXIT_FAILURE, "invalid partition index"); 490 error = gctl_delete_param(req, index_param); 491 if (error) 492 errc(EXIT_FAILURE, error, "internal error"); 493 } else 494 idx = 0; 495 496 if (partcode != NULL) { 497 if (idx == 0) 498 errx(EXIT_FAILURE, "missing -i option"); 499 gpart_write_partcode(req, idx, partcode, partsize); 500 } else { 501 if (bootcode == NULL) 502 errx(EXIT_FAILURE, "no -b nor -p"); 503 } 504 505 if (bootcode != NULL) 506 gpart_issue(req, fl); 507} 508 509static void 510gpart_issue(struct gctl_req *req, unsigned int fl __unused) 511{ 512 char buf[4096]; 513 char *errmsg; 514 const char *errstr; 515 int error, status; 516 517 bzero(buf, sizeof(buf)); 518 gctl_rw_param(req, "output", sizeof(buf), buf); 519 errstr = gctl_issue(req); 520 if (errstr == NULL || errstr[0] == '\0') { 521 if (buf[0] != '\0') 522 printf("%s", buf); 523 status = EXIT_SUCCESS; 524 goto done; 525 } 526 527 error = strtol(errstr, &errmsg, 0); 528 if (errmsg != errstr) { 529 while (errmsg[0] == ' ') 530 errmsg++; 531 if (errmsg[0] != '\0') 532 warnc(error, "%s", errmsg); 533 else 534 warnc(error, NULL); 535 } else 536 warnx("%s", errmsg); 537 538 status = EXIT_FAILURE; 539 540 done: 541 gctl_free(req); 542 exit(status); 543} 544