sunlabel.c revision 128916
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 128916 2004-05-04 09:50:41Z joerg $"); 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 <ctype.h> 79#include <err.h> 80#include <fcntl.h> 81#include <inttypes.h> 82#include <libgeom.h> 83#include <paths.h> 84#include <stdio.h> 85#include <stdlib.h> 86#include <string.h> 87#include <unistd.h> 88 89#define _PATH_TMPFILE "/tmp/EdDk.XXXXXXXXXX" 90#define _PATH_BOOT "/boot/boot1" 91 92static int bflag; 93static int Bflag; 94static int eflag; 95static int nflag; 96static int Rflag; 97static int wflag; 98 99static int check_label(struct sun_disklabel *sl); 100static void read_label(struct sun_disklabel *sl, const char *disk); 101static void write_label(struct sun_disklabel *sl, const char *disk, 102 const char *bootpath); 103static int edit_label(struct sun_disklabel *sl, const char *disk, 104 const char *bootpath); 105static int parse_label(struct sun_disklabel *sl, const char *file); 106static void print_label(struct sun_disklabel *sl, const char *disk, FILE *out); 107 108static int parse_size(struct sun_disklabel *sl, int part, char *size); 109static int parse_offset(struct sun_disklabel *sl, int part, char *offset); 110 111static void usage(void); 112 113extern char *__progname; 114 115/* 116 * Disk label editor for sun disklabels. 117 */ 118int 119main(int ac, char **av) 120{ 121 struct sun_disklabel sl; 122 const char *bootpath; 123 const char *proto; 124 const char *disk; 125 int ch; 126 127 bootpath = _PATH_BOOT; 128 while ((ch = getopt(ac, av, "b:BenrRw")) != -1) 129 switch (ch) { 130 case 'b': 131 bflag = 1; 132 bootpath = optarg; 133 break; 134 case 'B': 135 Bflag = 1; 136 break; 137 case 'e': 138 eflag = 1; 139 break; 140 case 'n': 141 nflag = 1; 142 break; 143 case 'r': 144 fprintf(stderr, "Obsolete -r flag ignored\n"); 145 break; 146 case 'R': 147 Rflag = 1; 148 break; 149 case 'w': 150 wflag = 1; 151 break; 152 default: 153 usage(); 154 break; 155 } 156 if (bflag && !Bflag) 157 usage(); 158 if (nflag && !(Bflag || eflag || Rflag || wflag)) 159 usage(); 160 if (eflag && (Rflag || wflag)) 161 usage(); 162 ac -= optind; 163 av += optind; 164 if (ac == 0) 165 usage(); 166 bzero(&sl, sizeof(sl)); 167 disk = av[0]; 168 if (wflag) { 169 if (ac != 2 || strcmp(av[1], "auto") != 0) 170 usage(); 171 read_label(&sl, disk); 172 bzero(sl.sl_part, sizeof(sl.sl_part)); 173 sl.sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 174 sl.sl_part[SUN_RAWPART].sdkp_nsectors = sl.sl_ncylinders * 175 sl.sl_ntracks * sl.sl_nsectors; 176 write_label(&sl, disk, bootpath); 177 } else if (eflag) { 178 if (ac != 1) 179 usage(); 180 read_label(&sl, disk); 181 if (sl.sl_magic != SUN_DKMAGIC) 182 errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 183 while (edit_label(&sl, disk, bootpath) != 0) 184 ; 185 } else if (Rflag) { 186 if (ac != 2) 187 usage(); 188 proto = av[1]; 189 read_label(&sl, disk); 190 if (parse_label(&sl, proto) != 0) 191 errx(1, "%s: invalid label", proto); 192 write_label(&sl, disk, bootpath); 193 } else if (Bflag) { 194 read_label(&sl, disk); 195 if (sl.sl_magic != SUN_DKMAGIC) 196 errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 197 write_label(&sl, disk, bootpath); 198 } else { 199 read_label(&sl, disk); 200 if (sl.sl_magic != SUN_DKMAGIC) 201 errx(1, "%s%s has no sun disklabel", _PATH_DEV, disk); 202 print_label(&sl, disk, stdout); 203 } 204 return (0); 205} 206 207static int 208check_label(struct sun_disklabel *sl) 209{ 210 uint64_t nsectors; 211 uint64_t ostart; 212 uint64_t start; 213 uint64_t oend; 214 uint64_t end; 215 int i; 216 int j; 217 218 nsectors = sl->sl_ncylinders * sl->sl_ntracks * sl->sl_nsectors; 219 if (sl->sl_part[SUN_RAWPART].sdkp_cyloffset != 0 || 220 sl->sl_part[SUN_RAWPART].sdkp_nsectors != nsectors) { 221 warnx("partition c is incorrect, must start at 0 and cover " 222 "whole disk"); 223 return (1); 224 } 225 for (i = 0; i < SUN_NPART; i++) { 226 if (i == 2 || sl->sl_part[i].sdkp_nsectors == 0) 227 continue; 228 start = (uint64_t)sl->sl_part[i].sdkp_cyloffset * 229 sl->sl_ntracks * sl->sl_nsectors; 230 end = start + sl->sl_part[i].sdkp_nsectors; 231 if (end > nsectors) { 232 warnx("partition %c extends past end of disk", 233 'a' + i); 234 return (1); 235 } 236 for (j = 0; j < SUN_NPART; j++) { 237 if (j == 2 || j == i || 238 sl->sl_part[j].sdkp_nsectors == 0) 239 continue; 240 ostart = (uint64_t)sl->sl_part[j].sdkp_cyloffset * 241 sl->sl_ntracks * sl->sl_nsectors; 242 oend = ostart + sl->sl_part[j].sdkp_nsectors; 243 if ((start <= ostart && end >= oend) || 244 (start > ostart && start < oend) || 245 (end > ostart && end < oend)) { 246 warnx("partition %c overlaps partition %c", 247 'a' + i, 'a' + j); 248 return (1); 249 } 250 } 251 } 252 return (0); 253} 254 255static void 256read_label(struct sun_disklabel *sl, const char *disk) 257{ 258 char path[MAXPATHLEN]; 259 uint32_t sectorsize; 260 uint32_t fwsectors; 261 uint32_t fwheads; 262 off_t mediasize; 263 char buf[SUN_SIZE]; 264 int fd, error; 265 266 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 267 if ((fd = open(path, O_RDONLY)) < 0) 268 err(1, "open %s", path); 269 if (read(fd, buf, sizeof(buf)) != sizeof(buf)) 270 err(1, "read"); 271 error = sunlabel_dec(buf, sl); 272 if (error) { 273 bzero(sl, sizeof(*sl)); 274 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) != 0) 275 err(1, "%s: ioctl(DIOCGMEDIASIZE) failed", disk); 276 if (ioctl(fd, DIOCGSECTORSIZE, §orsize) != 0) 277 err(1, "%s: DIOCGSECTORSIZE failed", disk); 278 if (ioctl(fd, DIOCGFWSECTORS, &fwsectors) != 0) 279 fwsectors = 63; 280 if (ioctl(fd, DIOCGFWHEADS, &fwheads) != 0) { 281 if (mediasize <= 63 * 1024 * sectorsize) 282 fwheads = 1; 283 else if (mediasize <= 63 * 16 * 1024 * sectorsize) 284 fwheads = 16; 285 else 286 fwheads = 255; 287 } 288 sl->sl_rpm = 3600; 289 sl->sl_pcylinders = mediasize / (fwsectors * fwheads * 290 sectorsize); 291 sl->sl_sparespercyl = 0; 292 sl->sl_interleave = 1; 293 sl->sl_ncylinders = sl->sl_pcylinders - 2; 294 sl->sl_acylinders = 2; 295 sl->sl_nsectors = fwsectors; 296 sl->sl_ntracks = fwheads; 297 sl->sl_part[SUN_RAWPART].sdkp_cyloffset = 0; 298 sl->sl_part[SUN_RAWPART].sdkp_nsectors = sl->sl_ncylinders * 299 sl->sl_ntracks * sl->sl_nsectors; 300 if (mediasize > (off_t)4999L * 1024L * 1024L) { 301 sprintf(sl->sl_text, 302 "FreeBSD%jdG cyl %u alt %u hd %u sec %u", 303 (intmax_t)(mediasize + 512 * 1024 * 1024) / 304 (1024 * 1024 * 1024), 305 sl->sl_ncylinders, sl->sl_acylinders, 306 sl->sl_ntracks, sl->sl_nsectors); 307 } else { 308 sprintf(sl->sl_text, 309 "FreeBSD%jdM cyl %u alt %u hd %u sec %u", 310 (intmax_t)(mediasize + 512 * 1024) / (1024 * 1024), 311 sl->sl_ncylinders, sl->sl_acylinders, 312 sl->sl_ntracks, sl->sl_nsectors); 313 } 314 } 315 close(fd); 316} 317 318static void 319write_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 320{ 321 char path[MAXPATHLEN]; 322 char boot[SUN_BOOTSIZE]; 323 char buf[SUN_SIZE]; 324 const char *errstr; 325 off_t off; 326 int bfd; 327 int fd; 328 int i; 329 struct gctl_req *grq; 330 331 sl->sl_magic = SUN_DKMAGIC; 332 333 if (check_label(sl) != 0) 334 errx(1, "invalid label"); 335 336 bzero(buf, sizeof(buf)); 337 sunlabel_enc(buf, sl); 338 339 if (nflag) { 340 print_label(sl, disk, stdout); 341 return; 342 } 343 if (Bflag) { 344 if ((bfd = open(bootpath, O_RDONLY)) < 0) 345 err(1, "open %s", bootpath); 346 i = read(bfd, boot, sizeof(boot)); 347 if (i < 0) 348 err(1, "read"); 349 else if (i != sizeof (boot)) 350 errx(1, "read wrong size boot code (%d)", i); 351 close(bfd); 352 } 353 snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk); 354 fd = open(path, O_RDWR); 355 if (fd < 0) { 356 grq = gctl_get_handle(); 357 gctl_ro_param(grq, "verb", -1, "write label"); 358 gctl_ro_param(grq, "class", -1, "SUN"); 359 gctl_ro_param(grq, "geom", -1, disk); 360 gctl_ro_param(grq, "label", sizeof buf, buf); 361 errstr = gctl_issue(grq); 362 if (errstr != NULL) 363 errx(1, "%s", errstr); 364 gctl_free(grq); 365 if (Bflag) { 366 grq = gctl_get_handle(); 367 gctl_ro_param(grq, "verb", -1, "write bootcode"); 368 gctl_ro_param(grq, "class", -1, "SUN"); 369 gctl_ro_param(grq, "geom", -1, disk); 370 gctl_ro_param(grq, "bootcode", sizeof boot, boot); 371 errstr = gctl_issue(grq); 372 if (errstr != NULL) 373 errx(1, "%s", errstr); 374 gctl_free(grq); 375 } 376 } else { 377 if (lseek(fd, 0, SEEK_SET) < 0) 378 err(1, "lseek"); 379 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) 380 err (1, "write"); 381 if (Bflag) { 382 for (i = 0; i < SUN_NPART; i++) { 383 if (sl->sl_part[i].sdkp_nsectors == 0) 384 continue; 385 off = sl->sl_part[i].sdkp_cyloffset * 386 sl->sl_ntracks * sl->sl_nsectors * 512; 387 /* 388 * Ignore first SUN_SIZE bytes of boot code to 389 * avoid overwriting the label. 390 */ 391 if (lseek(fd, off + SUN_SIZE, SEEK_SET) < 0) 392 err(1, "lseek"); 393 if (write(fd, boot + SUN_SIZE, 394 sizeof(boot) - SUN_SIZE) != 395 sizeof(boot) - SUN_SIZE) 396 err(1, "write"); 397 } 398 } 399 close(fd); 400 } 401 exit(0); 402} 403 404static int 405edit_label(struct sun_disklabel *sl, const char *disk, const char *bootpath) 406{ 407 char tmpfil[] = _PATH_TMPFILE; 408 const char *editor; 409 int status; 410 FILE *fp; 411 pid_t pid; 412 pid_t r; 413 int fd; 414 int c; 415 416 if ((fd = mkstemp(tmpfil)) < 0) 417 err(1, "mkstemp"); 418 if ((fp = fdopen(fd, "w")) == NULL) 419 err(1, "fdopen"); 420 print_label(sl, disk, fp); 421 fflush(fp); 422 if ((pid = fork()) < 0) 423 err(1, "fork"); 424 if (pid == 0) { 425 if ((editor = getenv("EDITOR")) == NULL) 426 editor = _PATH_VI; 427 execlp(editor, editor, tmpfil, (char *)NULL); 428 err(1, "execlp %s", editor); 429 } 430 status = 0; 431 while ((r = wait(&status)) > 0 && r != pid) 432 ; 433 if (WIFEXITED(status)) { 434 if (parse_label(sl, tmpfil) == 0) { 435 fclose(fp); 436 unlink(tmpfil); 437 write_label(sl, disk, bootpath); 438 return (0); 439 } 440 printf("re-edit the label? [y]: "); 441 fflush(stdout); 442 c = getchar(); 443 if (c != EOF && c != '\n') 444 while (getchar() != '\n') 445 ; 446 if (c == 'n') { 447 fclose(fp); 448 unlink(tmpfil); 449 return (0); 450 } 451 } 452 fclose(fp); 453 unlink(tmpfil); 454 return (1); 455} 456 457static int 458parse_label(struct sun_disklabel *sl, const char *file) 459{ 460 char offset[32]; 461 char size[32]; 462 char buf[128]; 463 char *bp; 464 uint8_t part; 465 FILE *fp; 466 int line; 467 468 line = 0; 469 if ((fp = fopen(file, "r")) == NULL) 470 err(1, "fopen"); 471 bzero(sl->sl_part, sizeof(sl->sl_part)); 472 while (fgets(buf, sizeof(buf), fp) != NULL) { 473 /* 474 * In order to recognize a partition entry, we search 475 * for lines starting with a single letter followed by 476 * a colon as their first non-white characters. We 477 * silently ignore any other lines, so any comment etc. 478 * lines in the label template will be ignored. 479 * 480 * XXX We should probably also recognize the geometry 481 * fields on top, and allow changing the geometry 482 * emulated by this disk. 483 */ 484 for (bp = buf; isspace(*bp); bp++) 485 ; 486 if (strlen(bp) < 2 || bp[1] != ':') { 487 line++; 488 continue; 489 } 490 if (sscanf(bp, "%c: %s %s\n", &part, size, offset) != 3 || 491 parse_size(sl, part - 'a', size) || 492 parse_offset(sl, part - 'a', offset)) { 493 warnx("%s: syntax error on line %d", 494 file, line + 1); 495 fclose(fp); 496 return (1); 497 } 498 line++; 499 } 500 fclose(fp); 501 return (check_label(sl)); 502} 503 504static int 505parse_size(struct sun_disklabel *sl, int part, char *size) 506{ 507 uintmax_t nsectors; 508 uintmax_t total; 509 uintmax_t n; 510 char *p; 511 int i; 512 513 nsectors = 0; 514 n = strtoumax(size, &p, 10); 515 if (*p != '\0') { 516 if (strcmp(size, "*") == 0) { 517 total = sl->sl_ncylinders * sl->sl_ntracks * 518 sl->sl_nsectors; 519 for (i = 0; i < part; i++) { 520 if (i == 2) 521 continue; 522 nsectors += sl->sl_part[i].sdkp_nsectors; 523 } 524 n = total - nsectors; 525 } else if (p[1] == '\0' && (p[0] == 'K' || p[0] == 'k')) { 526 n = roundup((n * 1024) / 512, 527 sl->sl_ntracks * sl->sl_nsectors); 528 } else if (p[1] == '\0' && (p[0] == 'M' || p[0] == 'm')) { 529 n = roundup((n * 1024 * 1024) / 512, 530 sl->sl_ntracks * sl->sl_nsectors); 531 } else if (p[1] == '\0' && (p[0] == 'G' || p[0] == 'g')) { 532 n = roundup((n * 1024 * 1024 * 1024) / 512, 533 sl->sl_ntracks * sl->sl_nsectors); 534 } else 535 return (-1); 536 } 537 sl->sl_part[part].sdkp_nsectors = n; 538 return (0); 539} 540 541static int 542parse_offset(struct sun_disklabel *sl, int part, char *offset) 543{ 544 uintmax_t nsectors; 545 uintmax_t n; 546 char *p; 547 int i; 548 549 nsectors = 0; 550 n = strtoumax(offset, &p, 10); 551 if (*p != '\0') { 552 if (strcmp(offset, "*") == 0) { 553 for (i = 0; i < part; i++) { 554 if (i == 2) 555 continue; 556 nsectors += sl->sl_part[i].sdkp_nsectors; 557 } 558 n = nsectors / (sl->sl_nsectors * sl->sl_ntracks); 559 } else 560 return (-1); 561 } 562 sl->sl_part[part].sdkp_cyloffset = n; 563 return (0); 564} 565 566static void 567print_label(struct sun_disklabel *sl, const char *disk, FILE *out) 568{ 569 int i; 570 571 fprintf(out, 572"# /dev/%s:\n" 573"text: %s\n" 574"bytes/sectors: 512\n" 575"sectors/cylinder: %d\n" 576"sectors/unit: %d\n" 577"\n" 578"%d partitions:\n" 579"#\n" 580"# Size is in sectors, use %%dK, %%dM or %%dG to specify in kilobytes,\n" 581"# megabytes or gigabytes respectively, or '*' to specify rest of disk.\n" 582"# Offset is in cylinders, use '*' to calculate offsets automatically.\n" 583"#\n" 584"# size offset\n" 585"# ---------- ----------\n", 586 disk, 587 sl->sl_text, 588 sl->sl_nsectors * sl->sl_ntracks, 589 sl->sl_nsectors * sl->sl_ntracks * sl->sl_ncylinders, 590 SUN_NPART); 591 for (i = 0; i < SUN_NPART; i++) { 592 if (sl->sl_part[i].sdkp_nsectors == 0) 593 continue; 594 fprintf(out, " %c: %10u %10u\n", 595 'a' + i, 596 sl->sl_part[i].sdkp_nsectors, 597 sl->sl_part[i].sdkp_cyloffset); 598 } 599} 600 601static void 602usage(void) 603{ 604 605 fprintf(stderr, "usage:" 606"\t%s [-r] disk\n" 607"\t\t(to read label)\n" 608"\t%s -B [-b boot1] [-n] disk\n" 609"\t\t(to install boot program only)\n" 610"\t%s -R [-B [-b boot1]] [-r] [-n] disk protofile\n" 611"\t\t(to restore label)\n" 612"\t%s -e [-B [-b boot1]] [-r] [-n] disk\n" 613"\t\t(to edit label)\n" 614"\t%s -w [-B [-b boot1]] [-r] [-n] disk type\n" 615"\t\t(to write default label)\n", 616 __progname, 617 __progname, 618 __progname, 619 __progname, 620 __progname); 621 exit(1); 622} 623