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