sunlabel.c revision 113896
1/*- 2 * Copyright (c) 2003 Jake Burkholder. 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 AUTHOR 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 AUTHOR 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 * Copyright (c) 1994, 1995 Gordon W. Ross 28 * Copyright (c) 1994 Theo de Raadt 29 * All rights reserved. 30 * Copyright (c) 1987, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * This code is derived from software contributed to Berkeley by 34 * Symmetric Computer Systems. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. All advertising materials mentioning features or use of this software 45 * must display the following acknowledgement: 46 * This product includes software developed by the University of 47 * California, Berkeley and its contributors. 48 * This product includes software developed by Theo de Raadt. 49 * 4. Neither the name of the University nor the names of its contributors 50 * may be used to endorse or promote products derived from this software 51 * without specific prior written permission. 52 * 53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 * SUCH DAMAGE. 64 * 65 * from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $ 66 */ 67 68#include <sys/cdefs.h> 69__FBSDID("$FreeBSD: head/sbin/sunlabel/sunlabel.c 113896 2003-04-23 08:25:20Z phk $"); 70 71#include <sys/types.h> 72#include <sys/param.h> 73#include <sys/disk.h> 74#include <sys/ioctl.h> 75#include <sys/sun_disklabel.h> 76#include <sys/wait.h> 77 78#include <err.h> 79#include <fcntl.h> 80#include <inttypes.h> 81#include <libgeom.h> 82#include <paths.h> 83#include <stdio.h> 84#include <stdlib.h> 85#include <string.h> 86#include <unistd.h> 87 88#define _PATH_TMPFILE "/tmp/EdDk.XXXXXXXXXX" 89#define _PATH_BOOT "/boot/boot1" 90 91static int bflag; 92static int Bflag; 93static int eflag; 94static int nflag; 95static int Rflag; 96static int wflag; 97 98static int check_label(struct sun_disklabel *sl); 99static void read_label(struct sun_disklabel *sl, const char *disk); 100static void write_label(struct sun_disklabel *sl, const char *disk, 101 const char *bootpath); 102static int edit_label(struct sun_disklabel *sl, const char *disk, 103 const char *bootpath); 104static int parse_label(struct sun_disklabel *sl, const char *file); 105static void print_label(struct sun_disklabel *sl, const char *disk, FILE *out); 106 107static int parse_size(struct sun_disklabel *sl, int part, char *size); 108static int parse_offset(struct sun_disklabel *sl, int part, char *offset); 109 110static void usage(void); 111 112extern char *__progname; 113 114/* 115 * Disk label editor for sun disklabels. 116 */ 117int 118main(int ac, char **av) 119{ 120 struct sun_disklabel sl; 121 const char *bootpath; 122 const char *proto; 123 const char *disk; 124 int ch; 125 126 bootpath = _PATH_BOOT; 127 while ((ch = getopt(ac, av, "b:BenrRw")) != -1) 128 switch (ch) { 129 case 'b': 130 bflag = 1; 131 bootpath = optarg; 132 break; 133 case 'B': 134 Bflag = 1; 135 break; 136 case 'e': 137 eflag = 1; 138 break; 139 case 'n': 140 nflag = 1; 141 break; 142 case 'r': 143 fprintf(stderr, "Obsolete -r flag ignored\n"); 144 break; 145 case 'R': 146 Rflag = 1; 147 break; 148 case 'w': 149 wflag = 1; 150 break; 151 default: 152 usage(); 153 break; 154 } 155 if (bflag && !Bflag) 156 usage(); 157 if (nflag && !(Bflag || eflag || Rflag || wflag)) 158 usage(); 159 if (eflag && (Rflag || wflag)) 160 usage(); 161 ac -= optind; 162 av += optind; 163 if (ac == 0) 164 usage(); 165 bzero(&sl, sizeof(sl)); 166 disk = av[0]; 167 if (wflag) { 168 if (ac != 2 || strcmp(av[1], "auto") != 0) 169 usage(); 170 read_label(&sl, disk); 171 bzero(sl.sl_part, sizeof(sl.sl_part)); 172 sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 173 sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders * 174 sl.sl_ntracks * sl.sl_nsectors; 175 write_label(&sl, disk, bootpath); 176 } else if (eflag) { 177 if (ac != 1) 178 usage(); 179 read_label(&sl, disk); 180 if (sl.sl_magic != SUN_DKMAGIC) 181 errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 182 while (edit_label(&sl, disk, bootpath) != 0) 183 ; 184 } else if (Rflag) { 185 if (ac != 2) 186 usage(); 187 proto = av[1]; 188 read_label(&sl, disk); 189 if (parse_label(&sl, proto) != 0) 190 errx(1, "%s: invalid label", proto); 191 write_label(&sl, disk, bootpath); 192 } else if (Bflag) { 193 read_label(&sl, disk); 194 if (sl.sl_magic != SUN_DKMAGIC) 195 errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 196 write_label(&sl, disk, bootpath); 197 } else { 198 read_label(&sl, disk); 199 if (sl.sl_magic != SUN_DKMAGIC) 200 errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 201 print_label(&sl, disk, stdout); 202 } 203 return (0); 204} 205 206static int 207check_label(struct sun_disklabel *sl) 208{ 209 uint64_t nsectors; 210 uint64_t ostart; 211 uint64_t start; 212 uint64_t oend; 213 uint64_t end; 214 int i; 215 int j; 216 217 nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors; 218 if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 || 219 sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) { 220 warnx("partition c is incorrect, must start at 0 and cover " 221 "whole disk"); 222 return (1); 223 } 224 for (i = 0; i < SUN_NPART; i++) { 225 if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0) 226 continue; 227 start = (uint64_t)sl->sl_part[i].sdkp_cyloffset * 228 sl->sl_ntracks * sl->sl_nsectors; 229 end = start + sl->sl_part[i].sdkp_nsectors; 230 if (end > nsectors) { 231 warnx("partition %c extends past end of disk", 232 'a' + i); 233 return (1); 234 } 235 for (j = 0; j < SUN_NPART; j++) { 236 if (j == 2 || j == i || 237 sl->sl_part[j].sdkp_nsectors == 0) 238 continue; 239 ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset * 240 sl->sl_ntracks * sl->sl_nsectors; 241 oend = ostart + sl->sl_part[j].sdkp_nsectors; 242 if ((start <= ostart && end >= oend) || 243 (start > ostart && start < oend) || 244 (end > ostart && end < oend)) { 245 warnx("partition %c overlaps partition %c", 246 'a' + i, 'a' + j); 247 return (1); 248 } 249 } 250 } 251 return (0); 252} 253 254static void 255read_label(struct sun_disklabel *sl, const char *disk) 256{ 257 char path[MAXPATHLEN]; 258 uint32_t sectorsize; 259 uint32_t fwsectors; 260 uint32_t fwheads; 261 off_t mediasize; 262 char buf[SUN_SIZE]; 263 int fd, error; 264 265 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 266 if ((fd = open(path, O_RDONLY)) < 0) 267 err(1, "open %s", path); 268 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) 269 err(1, "read"); 270 error = sunlabel_dec(buf, sl); 271 if (error) { 272 bzero(sl, sizeof(*sl)); 273 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0) 274 err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk); 275 if (ioctl(fd, DIOCGSECTORSIZE, §orsize) != 0) 276 err(1, "%s: DIOCGSECTORSIZE failed", disk); 277 if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0) 278 fwsectors = 63; 279 if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) { 280 if (mediasize <= 63 * 1024 * sectorsize) 281 fwheads = 1; 282 else if (mediasize <= 63 * 16 * 1024 * sectorsize) 283 fwheads = 16; 284 else 285 fwheads = 255; 286 } 287 sl->sl_rpm = 3600; 288 sl->sl_pcylinders = mediasize / (fwsectors * fwheads * 289 sectorsize); 290 sl->sl_sparespercyl = 0; 291 sl->sl_interleave = 1; 292 sl->sl_ncylinders = sl->sl_pcylinders - 2; 293 sl->sl_acylinders = 2; 294 sl->sl_nsectors = fwsectors; 295 sl->sl_ntracks = fwheads; 296 sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 297 sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders * 298 sl->sl_ntracks * sl->sl_nsectors; 299 if (mediasize > (off_t)4999L * 1024L * 1024L) { 300 sprintf(sl->sl_text, 301 "FreeBSD%jdG cyl %u alt %u hd %u sec %u", 302 (intmax_t)(mediasize + 512 * 1024 * 1024) / 303 (1024 * 1024 * 1024), 304 sl->sl_ncylinders, sl->sl_acylinders, 305 sl->sl_ntracks, sl->sl_nsectors); 306 } else { 307 sprintf(sl->sl_text, 308 "FreeBSD%jdM cyl %u alt %u hd %u sec %u", 309 (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024), 310 sl->sl_ncylinders, sl->sl_acylinders, 311 sl->sl_ntracks, sl->sl_nsectors); 312 } 313 } 314 close(fd); 315} 316 317static void 318write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 319{ 320 char path[MAXPATHLEN]; 321 char boot[SUN_BOOTSIZE]; 322 char buf[SUN_SIZE]; 323 const char *errstr; 324 off_t off; 325 int bfd; 326 int fd; 327 int i; 328 struct gctl_req *grq; 329 330 sl->sl_magic = SUN_DKMAGIC; 331 332 if (check_label(sl) != 0) 333 errx(1, "invalid label"); 334 335 bzero(buf, sizeof(buf)); 336 sunlabel_enc(buf, sl); 337 338 if (nflag) { 339 print_label(sl, disk, stdout); 340 return; 341 } 342 if (Bflag) { 343 if ((bfd = open(bootpath, O_RDONLY)) < 0) 344 err(1, "open %s", bootpath); 345 i = read(bfd, boot, sizeof(boot)); 346 if (i < 0) 347 err(1, "read"); 348 else if (i != sizeof (boot)) 349 errx(1, "read wrong size boot code (%d)", i); 350 close(bfd); 351 } 352 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 353 fd = open(path, O_RDWR); 354 if (fd < 0) { 355 grq = gctl_get_handle(GCTL_CONFIG_GEOM); 356 gctl_ro_param(grq, "class", -1, "SUN"); 357 gctl_ro_param(grq, "geom", -1, disk); 358 gctl_ro_param(grq, "verb", -1, "write label"); 359 gctl_ro_param(grq, "label", sizeof buf, buf); 360 errstr = gctl_issue(grq); 361 if (errstr != NULL) 362 errx(1, "%s", errstr); 363 gctl_free(grq); 364 if (Bflag) { 365 grq = gctl_get_handle(GCTL_CONFIG_GEOM); 366 gctl_ro_param(grq, "class", -1, "SUN"); 367 gctl_ro_param(grq, "geom", -1, disk); 368 gctl_ro_param(grq, "verb", -1, "write bootcode"); 369 gctl_ro_param(grq, "bootcode", sizeof boot, boot); 370 errstr = gctl_issue(grq); 371 if (errstr != NULL) 372 errx(1, "%s", errstr); 373 gctl_free(grq); 374 } 375 } else { 376 if (lseek(fd, 0, SEEK_SET) < 0) 377 err(1, "lseek"); 378 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) 379 err (1, "write"); 380 if (Bflag) { 381 for (i = 0; i < SUN_NPART; i++) { 382 if (sl->sl_part[i].sdkp_nsectors == 0) 383 continue; 384 off = sl->sl_part[i].sdkp_cyloffset * 385 sl->sl_ntracks * sl->sl_nsectors * 512; 386 /* 387 * Ignore first SUN_SIZE bytes of boot code to 388 * avoid overwriting the label. 389 */ 390 if (lseek(fd, off + SUN_SIZE, SEEK_SET) < 0) 391 err(1, "lseek"); 392 if (write(fd, boot + SUN_SIZE, 393 sizeof(boot) - SUN_SIZE) != 394 sizeof(boot) - SUN_SIZE) 395 err(1, "write"); 396 } 397 } 398 close(fd); 399 } 400 exit(0); 401} 402 403static int 404edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 405{ 406 char tmpfil[] = _PATH_TMPFILE; 407 const char *editor; 408 int status; 409 FILE *fp; 410 pid_t pid; 411 pid_t r; 412 int fd; 413 int c; 414 415 if ((fd = mkstemp(tmpfil)) < 0) 416 err(1, "mkstemp"); 417 if ((fp = fdopen(fd, "w")) == NULL) 418 err(1, "fdopen"); 419 print_label(sl, disk, fp); 420 fflush(fp); 421 if ((pid = fork()) < 0) 422 err(1, "fork"); 423 if (pid == 0) { 424 if ((editor = getenv("EDITOR")) == NULL) 425 editor = _PATH_VI; 426 execlp(editor, editor, tmpfil, NULL); 427 err(1, "execlp %s", editor); 428 } 429 status = 0; 430 while ((r = wait(&status)) > 0 && r != pid) 431 ; 432 if (WIFEXITED(status)) { 433 if (parse_label(sl, tmpfil) == 0) { 434 fclose(fp); 435 unlink(tmpfil); 436 write_label(sl, disk, bootpath); 437 return (0); 438 } 439 printf("re-edit the label? [y]: "); 440 fflush(stdout); 441 c = getchar(); 442 if (c != EOF && c != '\n') 443 while (getchar() != '\n') 444 ; 445 if (c == 'n') { 446 fclose(fp); 447 unlink(tmpfil); 448 return (0); 449 } 450 } 451 fclose(fp); 452 unlink(tmpfil); 453 return (1); 454} 455 456static int 457parse_label(struct sun_disklabel *sl, const char *file) 458{ 459 char offset[32]; 460 char size[32]; 461 char buf[128]; 462 uint8_t part; 463 FILE *fp; 464 int line; 465 466 line = 0; 467 if ((fp = fopen(file, "r")) == NULL) 468 err(1, "fopen"); 469 bzero(sl->sl_part, sizeof(sl->sl_part)); 470 while (fgets(buf, sizeof(buf), fp) != NULL) { 471 if (buf[0] != ' ' || buf[1] != ' ') 472 continue; 473 if (sscanf(buf, " %c: %s %s\n", &part, size, offset) != 3 || 474 parse_size(sl, part - 'a', size) || 475 parse_offset(sl, part - 'a', offset)) { 476 warnx("%s: syntex error on line %d", 477 file, line); 478 fclose(fp); 479 return (1); 480 } 481 line++; 482 } 483 fclose(fp); 484 return (check_label(sl)); 485} 486 487static int 488parse_size(struct sun_disklabel *sl, int part, char *size) 489{ 490 uintmax_t nsectors; 491 uintmax_t total; 492 uintmax_t n; 493 char *p; 494 int i; 495 496 nsectors = 0; 497 n = strtoumax(size, &p, 10); 498 if (*p != '\0') { 499 if (strcmp(size, "*") == 0) { 500 total = sl->sl_ncylinders * sl->sl_ntracks * 501 sl->sl_nsectors; 502 for (i = 0; i < part; i++) { 503 if (i == 2) 504 continue; 505 nsectors += sl->sl_part[i].sdkp_nsectors; 506 } 507 n = total - nsectors; 508 } else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) { 509 n = roundup((n * 1024) / 512, 510 sl->sl_ntracks * sl->sl_nsectors); 511 } else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) { 512 n = roundup((n * 1024 * 1024) / 512, 513 sl->sl_ntracks * sl->sl_nsectors); 514 } else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) { 515 n = roundup((n * 1024 * 1024 * 1024) / 512, 516 sl->sl_ntracks * sl->sl_nsectors); 517 } else 518 return (-1); 519 } 520 sl->sl_part[part].sdkp_nsectors = n; 521 return (0); 522} 523 524static int 525parse_offset(struct sun_disklabel *sl, int part, char *offset) 526{ 527 uintmax_t nsectors; 528 uintmax_t n; 529 char *p; 530 int i; 531 532 nsectors = 0; 533 n = strtoumax(offset, &p, 10); 534 if (*p != '\0') { 535 if (strcmp(offset, "*") == 0) { 536 for (i = 0; i < part; i++) { 537 if (i == 2) 538 continue; 539 nsectors += sl->sl_part[i].sdkp_nsectors; 540 } 541 n = nsectors / (sl->sl_nsectors * sl->sl_ntracks); 542 } else 543 return (-1); 544 } 545 sl->sl_part[part].sdkp_cyloffset = n; 546 return (0); 547} 548 549static void 550print_label(struct sun_disklabel *sl, const char *disk, FILE *out) 551{ 552 int i; 553 554 fprintf(out, 555"# /dev/%s:\n" 556"text: %s\n" 557"bytes/sectors: 512\n" 558"sectors/cylinder: %d\n" 559"sectors/unit: %d\n" 560"\n" 561"%d partitions:\n" 562"#\n" 563"# Size is in sectors, use %%dK, %%dM or %%dG to specify in kilobytes,\n" 564"# megabytes or gigabytes respectively, or '*' to specify rest of disk.\n" 565"# Offset is in cylinders, use '*' to calculate offsets automatically.\n" 566"#\n" 567"# size offset\n" 568"# ---------- ----------\n", 569 disk, 570 sl->sl_text, 571 sl->sl_nsectors * sl->sl_ntracks, 572 sl->sl_nsectors * sl->sl_ntracks * sl->sl_ncylinders, 573 SUN_NPART); 574 for (i = 0; i < SUN_NPART; i++) { 575 if (sl->sl_part[i].sdkp_nsectors == 0) 576 continue; 577 fprintf(out, " %c: %10u %10u\n", 578 'a' + i, 579 sl->sl_part[i].sdkp_nsectors, 580 sl->sl_part[i].sdkp_cyloffset); 581 } 582} 583 584static void 585usage(void) 586{ 587 588 fprintf(stderr, "usage:" 589"\t%s [-r] disk\n" 590"\t\t(to read label)\n" 591"\t%s -B [-b boot1] [-n] disk\n" 592"\t\t(to install boot program only)\n" 593"\t%s -R [-B [-b boot1]] [-r] [-n] disk protofile\n" 594"\t\t(to restore label)\n" 595"\t%s -e [-B [-b boot1]] [-r] [-n] disk\n" 596"\t\t(to edit label)\n" 597"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n" 598"\t\t(to write default label)\n", 599 __progname, 600 __progname, 601 __progname, 602 __progname, 603 __progname); 604 exit(1); 605} 606