geom_part.c revision 185046
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 185046 2008-11-18 04:04:01Z 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_show(struct gctl_req *, unsigned int); 67 68struct g_command PUBSYM(class_commands)[] = { 69 { "add", 0, NULL, { 70 { 'b', "start", NULL, G_TYPE_STRING }, 71 { 's', "size", NULL, G_TYPE_STRING }, 72 { 't', "type", NULL, G_TYPE_STRING }, 73 { 'i', index_param, optional, G_TYPE_STRING }, 74 { 'l', "label", optional, G_TYPE_STRING }, 75 { 'f', "flags", flags, G_TYPE_STRING }, 76 G_OPT_SENTINEL }, 77 "geom", NULL 78 }, 79 { "bootcode", 0, gpart_bootcode, { 80 { 'b', bootcode_param, optional, G_TYPE_STRING }, 81 { 'p', partcode_param, optional, G_TYPE_STRING }, 82 { 'i', index_param, optional, G_TYPE_STRING }, 83 { 'f', "flags", flags, G_TYPE_STRING }, 84 G_OPT_SENTINEL }, 85 "geom", NULL 86 }, 87 { "commit", 0, NULL, G_NULL_OPTS, "geom", NULL }, 88 { "create", 0, NULL, { 89 { 's', "scheme", NULL, G_TYPE_STRING }, 90 { 'n', "entries", optional, G_TYPE_STRING }, 91 { 'f', "flags", flags, G_TYPE_STRING }, 92 G_OPT_SENTINEL }, 93 "provider", NULL 94 }, 95 { "delete", 0, NULL, { 96 { 'i', index_param, NULL, G_TYPE_STRING }, 97 { 'f', "flags", flags, G_TYPE_STRING }, 98 G_OPT_SENTINEL }, 99 "geom", NULL 100 }, 101 { "destroy", 0, NULL, { 102 { 'f', "flags", flags, G_TYPE_STRING }, 103 G_OPT_SENTINEL }, 104 "geom", NULL }, 105 { "modify", 0, NULL, { 106 { 'i', index_param, NULL, G_TYPE_STRING }, 107 { 'l', "label", optional, G_TYPE_STRING }, 108 { 't', "type", optional, G_TYPE_STRING }, 109 { 'f', "flags", flags, G_TYPE_STRING }, 110 G_OPT_SENTINEL }, 111 "geom", NULL 112 }, 113 { "set", 0, NULL, { 114 { 'a', "attrib", NULL, G_TYPE_STRING }, 115 { 'i', index_param, NULL, G_TYPE_STRING }, 116 { 'f', "flags", flags, G_TYPE_STRING }, 117 G_OPT_SENTINEL }, 118 "geom", NULL 119 }, 120 { "show", 0, gpart_show, { 121 { 'l', "show_label", NULL, G_TYPE_BOOL }, 122 { 'r', "show_rawtype", NULL, G_TYPE_BOOL }, 123 G_OPT_SENTINEL }, 124 NULL, "[-lr] [geom ...]" 125 }, 126 { "undo", 0, NULL, G_NULL_OPTS, "geom", NULL }, 127 { "unset", 0, NULL, { 128 { 'a', "attrib", NULL, G_TYPE_STRING }, 129 { 'i', index_param, NULL, G_TYPE_STRING }, 130 { 'f', "flags", flags, G_TYPE_STRING }, 131 G_OPT_SENTINEL }, 132 "geom", NULL 133 }, 134 G_CMD_SENTINEL 135}; 136 137static struct gclass * 138find_class(struct gmesh *mesh, const char *name) 139{ 140 struct gclass *classp; 141 142 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 143 if (strcmp(classp->lg_name, name) == 0) 144 return (classp); 145 } 146 return (NULL); 147} 148 149static struct ggeom * 150find_geom(struct gclass *classp, const char *name) 151{ 152 struct ggeom *gp; 153 154 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 155 if (strcmp(gp->lg_name, name) == 0) 156 return (gp); 157 } 158 return (NULL); 159} 160 161static const char * 162find_geomcfg(struct ggeom *gp, const char *cfg) 163{ 164 struct gconfig *gc; 165 166 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 167 if (!strcmp(gc->lg_name, cfg)) 168 return (gc->lg_val); 169 } 170 return (NULL); 171} 172 173static const char * 174find_provcfg(struct gprovider *pp, const char *cfg) 175{ 176 struct gconfig *gc; 177 178 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 179 if (!strcmp(gc->lg_name, cfg)) 180 return (gc->lg_val); 181 } 182 return (NULL); 183} 184 185static struct gprovider * 186find_provider(struct ggeom *gp, unsigned long long minsector) 187{ 188 struct gprovider *pp, *bestpp; 189 unsigned long long offset; 190 unsigned long long sector, bestsector; 191 192 bestpp = NULL; 193 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 194 offset = atoll(find_provcfg(pp, "offset")); 195 sector = offset / pp->lg_sectorsize; 196 if (sector < minsector) 197 continue; 198 if (bestpp != NULL && sector >= bestsector) 199 continue; 200 bestpp = pp; 201 bestsector = sector; 202 } 203 return (bestpp); 204} 205 206static const char * 207fmtsize(int64_t rawsz) 208{ 209 static char buf[5]; 210 211 humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE, 212 HN_B | HN_NOSPACE | HN_DECIMAL); 213 return (buf); 214} 215 216static const char * 217fmtattrib(struct gprovider *pp) 218{ 219 static char buf[128]; 220 struct gconfig *gc; 221 u_int idx; 222 223 buf[0] = '\0'; 224 idx = 0; 225 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 226 if (strcmp(gc->lg_name, "attrib") != 0) 227 continue; 228 idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s", 229 (idx == 0) ? " [" : ",", gc->lg_val); 230 } 231 if (idx > 0) 232 snprintf(buf + idx, sizeof(buf) - idx, "] "); 233 return (buf); 234} 235 236static void 237gpart_show_geom(struct ggeom *gp, const char *element) 238{ 239 struct gprovider *pp; 240 const char *s, *scheme; 241 unsigned long long first, last, sector, end; 242 unsigned long long offset, length, secsz; 243 int idx, wblocks, wname; 244 245 scheme = find_geomcfg(gp, "scheme"); 246 s = find_geomcfg(gp, "first"); 247 first = atoll(s); 248 s = find_geomcfg(gp, "last"); 249 last = atoll(s); 250 wblocks = strlen(s); 251 wname = strlen(gp->lg_name); 252 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 253 secsz = pp->lg_sectorsize; 254 printf("=>%*llu %*llu %*s %s (%s)\n", 255 wblocks, first, wblocks, (last - first + 1), 256 wname, gp->lg_name, 257 scheme, fmtsize(pp->lg_mediasize)); 258 259 while ((pp = find_provider(gp, first)) != NULL) { 260 s = find_provcfg(pp, "offset"); 261 offset = atoll(s); 262 sector = offset / secsz; 263 s = find_provcfg(pp, "length"); 264 length = atoll(s); 265 s = find_provcfg(pp, "index"); 266 idx = atoi(s); 267 end = sector + length / secsz; 268 if (first < sector) { 269 printf(" %*llu %*llu %*s - free - (%s)\n", 270 wblocks, first, wblocks, sector - first, 271 wname, "", 272 fmtsize((sector - first) * secsz)); 273 } 274 printf(" %*llu %*llu %*d %s %s (%s)\n", 275 wblocks, sector, wblocks, end - sector, 276 wname, idx, find_provcfg(pp, element), 277 fmtattrib(pp), fmtsize(pp->lg_mediasize)); 278 first = end; 279 } 280 if (first <= last) { 281 printf(" %*llu %*llu %*s - free - (%s)\n", 282 wblocks, first, wblocks, last - first + 1, 283 wname, "", 284 fmtsize((last - first + 1) * secsz)); 285 } 286 printf("\n"); 287} 288 289static int 290gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt) 291{ 292 293 if (!gctl_get_int(req, opt)) 294 return (0); 295 296 if (elt != NULL) 297 errx(EXIT_FAILURE, "-l and -r are mutually exclusive"); 298 299 return (1); 300} 301 302static void 303gpart_show(struct gctl_req *req, unsigned int fl __unused) 304{ 305 struct gmesh mesh; 306 struct gclass *classp; 307 struct ggeom *gp; 308 const char *element, *name; 309 int error, i, nargs; 310 311 element = NULL; 312 if (gpart_show_hasopt(req, "show_label", element)) 313 element = "label"; 314 if (gpart_show_hasopt(req, "show_rawtype", element)) 315 element = "rawtype"; 316 if (element == NULL) 317 element = "type"; 318 319 name = gctl_get_ascii(req, "class"); 320 if (name == NULL) 321 abort(); 322 error = geom_gettree(&mesh); 323 if (error != 0) 324 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 325 classp = find_class(&mesh, name); 326 if (classp == NULL) { 327 geom_deletetree(&mesh); 328 errx(EXIT_FAILURE, "Class %s not found.", name); 329 } 330 nargs = gctl_get_int(req, "nargs"); 331 if (nargs > 0) { 332 for (i = 0; i < nargs; i++) { 333 name = gctl_get_ascii(req, "arg%d", i); 334 gp = find_geom(classp, name); 335 if (gp != NULL) 336 gpart_show_geom(gp, element); 337 else 338 errx(EXIT_FAILURE, "No such geom: %s.", name); 339 } 340 } else { 341 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 342 gpart_show_geom(gp, element); 343 } 344 } 345 geom_deletetree(&mesh); 346} 347 348static void * 349gpart_bootfile_read(const char *bootfile, ssize_t *size) 350{ 351 struct stat sb; 352 void *code; 353 int fd; 354 355 if (stat(bootfile, &sb) == -1) 356 err(EXIT_FAILURE, "%s", bootfile); 357 if (!S_ISREG(sb.st_mode)) 358 errx(EXIT_FAILURE, "%s: not a regular file", bootfile); 359 if (sb.st_size == 0) 360 errx(EXIT_FAILURE, "%s: empty file", bootfile); 361 if (*size > 0 && sb.st_size >= *size) 362 errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, 363 *size); 364 365 *size = sb.st_size; 366 367 fd = open(bootfile, O_RDONLY); 368 if (fd == -1) 369 err(EXIT_FAILURE, "%s", bootfile); 370 code = malloc(*size); 371 if (code == NULL) 372 err(EXIT_FAILURE, NULL); 373 if (read(fd, code, *size) != *size) 374 err(EXIT_FAILURE, "%s", bootfile); 375 close(fd); 376 377 return (code); 378} 379 380static void 381gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size) 382{ 383 char dsf[128]; 384 struct gmesh mesh; 385 struct gclass *classp; 386 struct ggeom *gp; 387 struct gprovider *pp; 388 const char *s; 389 char *buf; 390 off_t bsize; 391 int error, fd; 392 393 s = gctl_get_ascii(req, "class"); 394 if (s == NULL) 395 abort(); 396 error = geom_gettree(&mesh); 397 if (error != 0) 398 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 399 classp = find_class(&mesh, s); 400 if (classp == NULL) { 401 geom_deletetree(&mesh); 402 errx(EXIT_FAILURE, "Class %s not found.", s); 403 } 404 s = gctl_get_ascii(req, "geom"); 405 gp = find_geom(classp, s); 406 if (gp == NULL) 407 errx(EXIT_FAILURE, "No such geom: %s.", s); 408 409 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 410 s = find_provcfg(pp, "index"); 411 if (s == NULL) 412 continue; 413 if (atoi(s) == idx) 414 break; 415 } 416 417 if (pp != NULL) { 418 snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); 419 fd = open(dsf, O_WRONLY); 420 if (fd == -1) 421 err(EXIT_FAILURE, "%s", dsf); 422 if (lseek(fd, size, SEEK_SET) != size) 423 errx(EXIT_FAILURE, "%s: not enough space", dsf); 424 if (lseek(fd, 0, SEEK_SET) != 0) 425 err(EXIT_FAILURE, "%s", dsf); 426 427 /* 428 * When writing to a disk device, the write must be 429 * sector aligned and not write to any partial sectors, 430 * so round up the buffer size to the next sector and zero it. 431 */ 432 bsize = (size + pp->lg_sectorsize - 1) / 433 pp->lg_sectorsize * pp->lg_sectorsize; 434 buf = calloc(1, bsize); 435 if (buf == NULL) 436 err(EXIT_FAILURE, "%s", dsf); 437 bcopy(code, buf, size); 438 if (write(fd, buf, bsize) != bsize) 439 err(EXIT_FAILURE, "%s", dsf); 440 free(buf); 441 close(fd); 442 } else 443 errx(EXIT_FAILURE, "invalid partition index"); 444 445 geom_deletetree(&mesh); 446} 447 448static void 449gpart_bootcode(struct gctl_req *req, unsigned int fl __unused) 450{ 451 const char *s; 452 char *sp; 453 void *bootcode, *partcode; 454 size_t bootsize, partsize; 455 int error, idx; 456 457 if (gctl_has_param(req, bootcode_param)) { 458 s = gctl_get_ascii(req, bootcode_param); 459 bootsize = 64 * 1024; /* Arbitrary limit. */ 460 bootcode = gpart_bootfile_read(s, &bootsize); 461 error = gctl_change_param(req, bootcode_param, bootsize, 462 bootcode); 463 if (error) 464 errc(EXIT_FAILURE, error, "internal error"); 465 } else { 466 bootcode = NULL; 467 bootsize = 0; 468 } 469 470 if (gctl_has_param(req, partcode_param)) { 471 s = gctl_get_ascii(req, partcode_param); 472 partsize = bootsize * 1024; 473 partcode = gpart_bootfile_read(s, &partsize); 474 error = gctl_delete_param(req, partcode_param); 475 if (error) 476 errc(EXIT_FAILURE, error, "internal error"); 477 } else { 478 partcode = NULL; 479 partsize = 0; 480 } 481 482 if (gctl_has_param(req, index_param)) { 483 if (partcode == NULL) 484 errx(EXIT_FAILURE, "-i is only valid with -p"); 485 s = gctl_get_ascii(req, index_param); 486 idx = strtol(s, &sp, 10); 487 if (idx < 1 || *s == '\0' || *sp != '\0') 488 errx(EXIT_FAILURE, "invalid partition index"); 489 error = gctl_delete_param(req, index_param); 490 if (error) 491 errc(EXIT_FAILURE, error, "internal error"); 492 } else 493 idx = 0; 494 495 if (partcode != NULL) { 496 if (idx == 0) 497 errx(EXIT_FAILURE, "missing -i option"); 498 gpart_write_partcode(req, idx, partcode, partsize); 499 } else { 500 if (bootcode == NULL) 501 errx(EXIT_FAILURE, "no -b nor -p"); 502 } 503 504 if (bootcode != NULL) { 505 s = gctl_issue(req); 506 if (s != NULL) 507 errx(EXIT_FAILURE, "%s", s); 508 } 509} 510