geom_part.c revision 179629
1/*- 2 * Copyright (c) 2007 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 179629 2008-06-06 23:58:29Z 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 { "show", 0, gpart_show, G_NULL_OPTS, NULL, "[geom ...]" }, 112 { "undo", 0, NULL, G_NULL_OPTS, "geom", NULL }, 113 G_CMD_SENTINEL 114}; 115 116static struct gclass * 117find_class(struct gmesh *mesh, const char *name) 118{ 119 struct gclass *classp; 120 121 LIST_FOREACH(classp, &mesh->lg_class, lg_class) { 122 if (strcmp(classp->lg_name, name) == 0) 123 return (classp); 124 } 125 return (NULL); 126} 127 128static struct ggeom * 129find_geom(struct gclass *classp, const char *name) 130{ 131 struct ggeom *gp; 132 133 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 134 if (strcmp(gp->lg_name, name) == 0) 135 return (gp); 136 } 137 return (NULL); 138} 139 140static const char * 141find_geomcfg(struct ggeom *gp, const char *cfg) 142{ 143 struct gconfig *gc; 144 145 LIST_FOREACH(gc, &gp->lg_config, lg_config) { 146 if (!strcmp(gc->lg_name, cfg)) 147 return (gc->lg_val); 148 } 149 return (NULL); 150} 151 152static const char * 153find_provcfg(struct gprovider *pp, const char *cfg) 154{ 155 struct gconfig *gc; 156 157 LIST_FOREACH(gc, &pp->lg_config, lg_config) { 158 if (!strcmp(gc->lg_name, cfg)) 159 return (gc->lg_val); 160 } 161 return (NULL); 162} 163 164static struct gprovider * 165find_provider(struct ggeom *gp, unsigned long long minsector) 166{ 167 struct gprovider *pp, *bestpp; 168 unsigned long long offset; 169 unsigned long long sector, bestsector; 170 171 bestpp = NULL; 172 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 173 offset = atoll(find_provcfg(pp, "offset")); 174 sector = offset / pp->lg_sectorsize; 175 if (sector < minsector) 176 continue; 177 if (bestpp != NULL && sector >= bestsector) 178 continue; 179 bestpp = pp; 180 bestsector = sector; 181 } 182 return (bestpp); 183} 184 185static const char * 186fmtsize(long double rawsz) 187{ 188 static char buf[32]; 189 static const char *sfx[] = { "B", "KB", "MB", "GB", "TB" }; 190 long double sz; 191 int sfxidx; 192 193 sfxidx = 0; 194 sz = (long double)rawsz; 195 while (sfxidx < 4 && sz > 1099.0) { 196 sz /= 1000; 197 sfxidx++; 198 } 199 200 sprintf(buf, "%.1Lf%s", sz, sfx[sfxidx]); 201 return (buf); 202} 203 204static void 205gpart_show_geom(struct ggeom *gp) 206{ 207 struct gprovider *pp; 208 const char *s, *scheme; 209 unsigned long long first, last, sector, end; 210 unsigned long long offset, length, secsz; 211 int idx, wblocks, wname; 212 213 scheme = find_geomcfg(gp, "scheme"); 214 s = find_geomcfg(gp, "first"); 215 first = atoll(s); 216 s = find_geomcfg(gp, "last"); 217 last = atoll(s); 218 wblocks = strlen(s); 219 wname = strlen(gp->lg_name); 220 pp = LIST_FIRST(&gp->lg_consumer)->lg_provider; 221 secsz = pp->lg_sectorsize; 222 printf("=>%*llu %*llu %*s %s (%s)\n", 223 wblocks, first, wblocks, (last - first + 1), 224 wname, gp->lg_name, 225 scheme, fmtsize(pp->lg_mediasize)); 226 227 while ((pp = find_provider(gp, first)) != NULL) { 228 s = find_provcfg(pp, "offset"); 229 offset = atoll(s); 230 sector = offset / secsz; 231 s = find_provcfg(pp, "length"); 232 length = atoll(s); 233 s = find_provcfg(pp, "index"); 234 idx = atoi(s); 235 end = sector + length / secsz; 236 if (first < sector) { 237 printf(" %*llu %*llu %*s - free - (%s)\n", 238 wblocks, first, wblocks, sector - first, 239 wname, "", 240 fmtsize((sector - first) * secsz)); 241 } 242 printf(" %*llu %*llu %*d %s (%s)\n", 243 wblocks, sector, wblocks, end - sector, 244 wname, idx, 245 find_provcfg(pp, "type"), fmtsize(pp->lg_mediasize)); 246 first = end; 247 } 248 if (first <= last) { 249 printf(" %*llu %*llu %*s - free - (%s)\n", 250 wblocks, first, wblocks, last - first + 1, 251 wname, "", 252 fmtsize((last - first + 1) * secsz)); 253 } 254 printf("\n"); 255} 256 257static void 258gpart_show(struct gctl_req *req, unsigned int fl __unused) 259{ 260 struct gmesh mesh; 261 struct gclass *classp; 262 struct ggeom *gp; 263 const char *name; 264 int error, i, nargs; 265 266 name = gctl_get_ascii(req, "class"); 267 if (name == NULL) 268 abort(); 269 error = geom_gettree(&mesh); 270 if (error != 0) 271 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 272 classp = find_class(&mesh, name); 273 if (classp == NULL) { 274 geom_deletetree(&mesh); 275 errx(EXIT_FAILURE, "Class %s not found.", name); 276 } 277 nargs = gctl_get_int(req, "nargs"); 278 if (nargs > 0) { 279 for (i = 0; i < nargs; i++) { 280 name = gctl_get_ascii(req, "arg%d", i); 281 gp = find_geom(classp, name); 282 if (gp != NULL) 283 gpart_show_geom(gp); 284 else 285 errx(EXIT_FAILURE, "No such geom: %s.", name); 286 } 287 } else { 288 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { 289 gpart_show_geom(gp); 290 } 291 } 292 geom_deletetree(&mesh); 293} 294 295static void * 296gpart_bootfile_read(const char *bootfile, ssize_t *size) 297{ 298 struct stat sb; 299 void *code; 300 int fd; 301 302 if (stat(bootfile, &sb) == -1) 303 err(EXIT_FAILURE, "%s", bootfile); 304 if (!S_ISREG(sb.st_mode)) 305 errx(EXIT_FAILURE, "%s: not a regular file", bootfile); 306 if (sb.st_size == 0) 307 errx(EXIT_FAILURE, "%s: empty file", bootfile); 308 if (*size > 0 && sb.st_size >= *size) 309 errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile, 310 *size); 311 312 *size = sb.st_size; 313 314 fd = open(bootfile, O_RDONLY); 315 if (fd == -1) 316 err(EXIT_FAILURE, "%s", bootfile); 317 code = malloc(*size); 318 if (code == NULL) 319 err(EXIT_FAILURE, NULL); 320 if (read(fd, code, *size) != *size) 321 err(EXIT_FAILURE, "%s", bootfile); 322 close(fd); 323 324 return (code); 325} 326 327static void 328gpart_write_partcode(struct gctl_req *req, int idx, void *code, ssize_t size) 329{ 330 char dsf[128]; 331 struct gmesh mesh; 332 struct gclass *classp; 333 struct ggeom *gp; 334 struct gprovider *pp; 335 const char *s; 336 int error, fd; 337 338 s = gctl_get_ascii(req, "class"); 339 if (s == NULL) 340 abort(); 341 error = geom_gettree(&mesh); 342 if (error != 0) 343 errc(EXIT_FAILURE, error, "Cannot get GEOM tree"); 344 classp = find_class(&mesh, s); 345 if (classp == NULL) { 346 geom_deletetree(&mesh); 347 errx(EXIT_FAILURE, "Class %s not found.", s); 348 } 349 s = gctl_get_ascii(req, "geom"); 350 gp = find_geom(classp, s); 351 if (gp == NULL) 352 errx(EXIT_FAILURE, "No such geom: %s.", s); 353 354 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { 355 s = find_provcfg(pp, "index"); 356 if (s == NULL) 357 continue; 358 if (atoi(s) == idx) 359 break; 360 } 361 362 if (pp != NULL) { 363 snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name); 364 fd = open(dsf, O_WRONLY); 365 if (fd == -1) 366 err(EXIT_FAILURE, "%s", dsf); 367 if (lseek(fd, size, SEEK_SET) != size) 368 errx(EXIT_FAILURE, "%s: not enough space", dsf); 369 if (lseek(fd, 0, SEEK_SET) != 0) 370 err(EXIT_FAILURE, "%s", dsf); 371 if (write(fd, code, size) != size) 372 err(EXIT_FAILURE, "%s", dsf); 373 close(fd); 374 } else 375 errx(EXIT_FAILURE, "invalid partition index"); 376 377 geom_deletetree(&mesh); 378} 379 380static void 381gpart_bootcode(struct gctl_req *req, unsigned int fl __unused) 382{ 383 const char *s; 384 char *sp; 385 void *bootcode, *partcode; 386 size_t bootsize, partsize; 387 int error, idx; 388 389 if (gctl_has_param(req, bootcode_param)) { 390 s = gctl_get_ascii(req, bootcode_param); 391 bootsize = 64 * 1024; /* Arbitrary limit. */ 392 bootcode = gpart_bootfile_read(s, &bootsize); 393 error = gctl_change_param(req, bootcode_param, bootsize, 394 bootcode); 395 if (error) 396 errc(EXIT_FAILURE, error, "internal error"); 397 } else { 398 bootcode = NULL; 399 bootsize = 0; 400 } 401 402 if (gctl_has_param(req, partcode_param)) { 403 s = gctl_get_ascii(req, partcode_param); 404 partsize = bootsize * 1024; 405 partcode = gpart_bootfile_read(s, &partsize); 406 error = gctl_delete_param(req, partcode_param); 407 if (error) 408 errc(EXIT_FAILURE, error, "internal error"); 409 } else { 410 partcode = NULL; 411 partsize = 0; 412 } 413 414 if (gctl_has_param(req, index_param)) { 415 if (partcode == NULL) 416 errx(EXIT_FAILURE, "-i is only valid with -p"); 417 s = gctl_get_ascii(req, index_param); 418 idx = strtol(s, &sp, 10); 419 if (idx < 1 || *s == '\0' || *sp != '\0') 420 errx(EXIT_FAILURE, "invalid partition index"); 421 error = gctl_delete_param(req, index_param); 422 if (error) 423 errc(EXIT_FAILURE, error, "internal error"); 424 } else 425 idx = 0; 426 427 if (partcode != NULL) { 428 if (idx == 0) 429 errx(EXIT_FAILURE, "missing -i option"); 430 gpart_write_partcode(req, idx, partcode, partsize); 431 } else { 432 if (bootcode == NULL) 433 errx(EXIT_FAILURE, "no -b nor -p"); 434 } 435 436 if (bootcode != NULL) { 437 s = gctl_issue(req); 438 if (s != NULL) 439 errx(EXIT_FAILURE, "%s", s); 440 } 441} 442