1 /* $OpenBSD: installboot.c,v 1.20 2020/03/11 09:59:31 otto Exp $ */ 2 /* $NetBSD: installboot.c,v 1.2 1997/04/06 08:41:12 cgd Exp $ */ 3 4 /* 5 * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved. 6 * Copyright (c) 1994 Paul Kranenburg 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Paul Kranenburg. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/mount.h> 37 #include <sys/ioctl.h> 38 #include <sys/time.h> 39 #include <sys/stat.h> 40 #include <sys/sysctl.h> 41 #include <ufs/ufs/dinode.h> 42 #include <ufs/ufs/dir.h> 43 #include <ufs/ffs/fs.h> 44 #include <sys/disklabel.h> 45 #include <sys/dkio.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <stdlib.h> 50 #include <stdio.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <util.h> 54 55 #include "bbinfo.h" 56 57 #ifndef ISO_DEFAULT_BLOCK_SIZE 58 #define ISO_DEFAULT_BLOCK_SIZE 2048 59 #endif 60 61 int verbose, nowrite, hflag; 62 char *boot, *proto, *dev; 63 64 struct bbinfoloc *bbinfolocp; 65 struct bbinfo *bbinfop; 66 int max_block_count; 67 68 69 char *loadprotoblocks(char *, long *); 70 int loadblocknums(char *, int, unsigned long); 71 static void devread(int, void *, daddr_t, size_t, char *); 72 static void usage(void); 73 static int sbchk(struct fs *, daddr_t); 74 static void sbread(int, daddr_t, struct fs **, char *); 75 int main(int, char *[]); 76 77 int isofsblk = 0; 78 int isofseblk = 0; 79 80 static const daddr_t sbtry[] = SBLOCKSEARCH; 81 82 static void 83 usage(void) 84 { 85 (void)fprintf(stderr, 86 "usage: installboot [-n] [-v] [-s isofsblk -e isofseblk] " 87 "<boot> <proto> <device>\n"); 88 exit(1); 89 } 90 91 int 92 main(int argc, char *argv[]) 93 { 94 int c, devfd; 95 char *protostore; 96 long protosize; 97 struct stat disksb, bootsb; 98 struct disklabel dl; 99 daddr_t partoffset; 100 #define BBPAD 0x1e0 101 struct bb { 102 char bb_pad[BBPAD]; /* disklabel lives in here, actually */ 103 long bb_secsize; /* size of secondary boot block */ 104 long bb_secstart; /* start of secondary boot block */ 105 long bb_flags; /* unknown; always zero */ 106 long bb_cksum; /* checksum of the boot block, as longs. */ 107 } bb; 108 long *lp, *ep; 109 110 while ((c = getopt(argc, argv, "vns:e:")) != -1) { 111 switch (c) { 112 case 'n': 113 /* Do not actually write the bootblock to disk */ 114 nowrite = 1; 115 break; 116 case 'v': 117 /* Chat */ 118 verbose = 1; 119 break; 120 case 's': 121 isofsblk = atoi(optarg); 122 break; 123 case 'e': 124 isofseblk = atoi(optarg); 125 break; 126 default: 127 usage(); 128 } 129 } 130 131 if (argc - optind < 3) 132 usage(); 133 134 boot = argv[optind]; 135 proto = argv[optind + 1]; 136 dev = argv[optind + 2]; 137 138 if (verbose) { 139 (void)printf("boot: %s\n", boot); 140 (void)printf("proto: %s\n", proto); 141 (void)printf("device: %s\n", dev); 142 } 143 144 /* Load proto blocks into core */ 145 if ((protostore = loadprotoblocks(proto, &protosize)) == NULL) 146 exit(1); 147 148 /* Open and check raw disk device */ 149 if ((devfd = opendev(dev, O_RDONLY, OPENDEV_PART, &dev)) < 0) 150 err(1, "open: %s", dev); 151 if (fstat(devfd, &disksb) == -1) 152 err(1, "fstat: %s", dev); 153 if (!S_ISCHR(disksb.st_mode)) 154 errx(1, "%s must be a character device node", dev); 155 if ((minor(disksb.st_rdev) % getmaxpartitions()) != getrawpartition()) 156 errx(1, "%s must be the raw partition", dev); 157 158 /* Extract and load block numbers */ 159 if (stat(boot, &bootsb) == -1) 160 err(1, "stat: %s", boot); 161 if (!S_ISREG(bootsb.st_mode)) 162 errx(1, "%s must be a regular file", boot); 163 if ((minor(disksb.st_rdev) / getmaxpartitions()) != 164 (minor(bootsb.st_dev) / getmaxpartitions())) 165 errx(1, "%s must be somewhere on %s", boot, dev); 166 167 /* 168 * Find the offset of the secondary boot block's partition 169 * into the disk. If disklabels not supported, assume zero. 170 */ 171 if (ioctl(devfd, DIOCGDINFO, &dl) != -1) { 172 partoffset = DL_GETPOFFSET(&dl.d_partitions[minor(bootsb.st_dev) % 173 getmaxpartitions()]); 174 } else { 175 if (errno != ENOTTY) 176 err(1, "read disklabel: %s", dev); 177 warnx("couldn't read label from %s, using part offset of 0", 178 dev); 179 partoffset = 0; 180 } 181 if (verbose) 182 (void)printf("%s partition offset = 0x%llx\n", boot, partoffset); 183 184 /* Sync filesystems (make sure boot's block numbers are stable) */ 185 sync(); 186 sleep(2); 187 sync(); 188 sleep(2); 189 190 if (loadblocknums(boot, devfd, DL_SECTOBLK(&dl, partoffset)) != 0) 191 exit(1); 192 193 (void)close(devfd); 194 195 if (nowrite) 196 return 0; 197 198 #if 0 199 /* Write patched proto bootblocks into the superblock */ 200 if (protosize > SBSIZE - DEV_BSIZE) 201 errx(1, "proto bootblocks too big"); 202 #endif 203 204 if ((devfd = opendev(dev, O_RDWR, OPENDEV_PART, &dev)) < 0) 205 err(1, "open: %s", dev); 206 207 if (lseek(devfd, DEV_BSIZE, SEEK_SET) != DEV_BSIZE) 208 err(1, "lseek bootstrap"); 209 210 if (write(devfd, protostore, protosize) != protosize) 211 err(1, "write bootstrap"); 212 213 if (lseek(devfd, 0, SEEK_SET) != 0) 214 err(1, "lseek label"); 215 216 if (read(devfd, &bb, sizeof (bb)) != sizeof (bb)) 217 err(1, "read label"); 218 219 bb.bb_secsize = 15; 220 bb.bb_secstart = 1; 221 bb.bb_flags = 0; 222 bb.bb_cksum = 0; 223 224 for (lp = (long *)&bb, ep = &bb.bb_cksum; lp < ep; lp++) 225 bb.bb_cksum += *lp; 226 227 if (lseek(devfd, 0, SEEK_SET) != 0) 228 err(1, "lseek label 2"); 229 230 if (write(devfd, &bb, sizeof bb) != sizeof bb) 231 err(1, "write label "); 232 233 (void)close(devfd); 234 return 0; 235 } 236 237 char * 238 loadprotoblocks(char *fname, long *size) 239 { 240 int fd, sz; 241 char *bp; 242 struct stat statbuf; 243 u_int64_t *matchp; 244 245 /* 246 * Read the prototype boot block into memory. 247 */ 248 if ((fd = open(fname, O_RDONLY)) < 0) { 249 warn("open: %s", fname); 250 return NULL; 251 } 252 if (fstat(fd, &statbuf) != 0) { 253 warn("fstat: %s", fname); 254 close(fd); 255 return NULL; 256 } 257 sz = roundup(statbuf.st_size, DEV_BSIZE); 258 if ((bp = calloc(sz, 1)) == NULL) { 259 warnx("malloc: %s: no memory", fname); 260 close(fd); 261 return NULL; 262 } 263 if (read(fd, bp, statbuf.st_size) != statbuf.st_size) { 264 warn("read: %s", fname); 265 free(bp); 266 close(fd); 267 return NULL; 268 } 269 close(fd); 270 271 /* 272 * Find the magic area of the program, and figure out where 273 * the 'blocks' struct is, from that. 274 */ 275 bbinfolocp = NULL; 276 for (matchp = (u_int64_t *)bp; (char *)matchp < bp + sz; matchp++) { 277 if (*matchp != 0xbabefacedeadbeef) 278 continue; 279 bbinfolocp = (struct bbinfoloc *)matchp; 280 if (bbinfolocp->magic1 == 0xbabefacedeadbeef && 281 bbinfolocp->magic2 == 0xdeadbeeffacebabe) 282 break; 283 bbinfolocp = NULL; 284 } 285 286 if (bbinfolocp == NULL) { 287 warnx("%s: not a valid boot block?", fname); 288 return NULL; 289 } 290 291 bbinfop = (struct bbinfo *)(bp + bbinfolocp->end - bbinfolocp->start); 292 memset(bbinfop, 0, sz - (bbinfolocp->end - bbinfolocp->start)); 293 max_block_count = 294 ((char *)bbinfop->blocks - bp) / sizeof (bbinfop->blocks[0]); 295 296 if (verbose) { 297 (void)printf("boot block info locator at offset 0x%x\n", 298 (char *)bbinfolocp - bp); 299 (void)printf("boot block info at offset 0x%x\n", 300 (char *)bbinfop - bp); 301 (void)printf("max number of blocks: %d\n", max_block_count); 302 } 303 304 *size = sz; 305 return (bp); 306 } 307 308 static void 309 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) 310 { 311 if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size) 312 err(1, "%s: devread: pread", msg); 313 } 314 315 static char sblock[SBSIZE]; 316 317 int 318 loadblocknums(char *boot, int devfd, unsigned long partoffset) 319 { 320 int i, fd, ndb; 321 struct stat statbuf; 322 struct statfs statfsbuf; 323 struct fs *fs; 324 char *buf; 325 daddr32_t *ap1; 326 daddr_t blk, *ap2; 327 struct ufs1_dinode *ip1; 328 struct ufs2_dinode *ip2; 329 int32_t cksum; 330 331 /* 332 * Open 2nd-level boot program and record the block numbers 333 * it occupies on the filesystem represented by `devfd'. 334 */ 335 if ((fd = open(boot, O_RDONLY)) < 0) 336 err(1, "open: %s", boot); 337 338 if (fstatfs(fd, &statfsbuf) != 0) 339 err(1, "statfs: %s", boot); 340 341 if (isofsblk) { 342 bbinfop->bsize = ISO_DEFAULT_BLOCK_SIZE; 343 bbinfop->nblocks = isofseblk - isofsblk + 1; 344 if (bbinfop->nblocks > max_block_count) 345 errx(1, "%s: Too many blocks", boot); 346 if (verbose) 347 (void)printf("%s: starting block %d (%d total):\n\t", 348 boot, isofsblk, bbinfop->nblocks); 349 for (i = 0; i < bbinfop->nblocks; i++) { 350 blk = (isofsblk + i) * (bbinfop->bsize / DEV_BSIZE); 351 bbinfop->blocks[i] = blk; 352 if (verbose) 353 (void)printf("%d ", blk); 354 } 355 if (verbose) 356 (void)printf("\n"); 357 358 cksum = 0; 359 for (i = 0; i < bbinfop->nblocks + 360 (sizeof(*bbinfop) / sizeof(bbinfop->blocks[0])) - 1; i++) 361 cksum += ((int32_t *)bbinfop)[i]; 362 bbinfop->cksum = -cksum; 363 364 return 0; 365 } 366 367 if (strncmp(statfsbuf.f_fstypename, MOUNT_FFS, MFSNAMELEN)) 368 errx(1, "%s: must be on a FFS filesystem", boot); 369 370 if (fsync(fd) != 0) 371 err(1, "fsync: %s", boot); 372 373 if (fstat(fd, &statbuf) != 0) 374 err(1, "fstat: %s", boot); 375 376 close(fd); 377 378 /* Read superblock */ 379 sbread(devfd, partoffset, &fs, sblock); 380 381 /* Read inode */ 382 if ((buf = malloc(fs->fs_bsize)) == NULL) 383 errx(1, "No memory for filesystem block"); 384 385 blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino)); 386 devread(devfd, buf, blk + partoffset, fs->fs_bsize, "inode"); 387 if (fs->fs_magic == FS_UFS1_MAGIC) { 388 ip1 = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, 389 statbuf.st_ino); 390 ndb = howmany(ip1->di_size, fs->fs_bsize); 391 } else { 392 ip2 = (struct ufs2_dinode *)(buf) + ino_to_fsbo(fs, 393 statbuf.st_ino); 394 ndb = howmany(ip2->di_size, fs->fs_bsize); 395 } 396 /* 397 * Check the block numbers; we don't handle fragments 398 */ 399 if (ndb > max_block_count) 400 errx(1, "%s: Too many blocks", boot); 401 402 /* 403 * Register filesystem block size. 404 */ 405 bbinfop->bsize = fs->fs_bsize; 406 407 /* 408 * Register block count. 409 */ 410 bbinfop->nblocks = ndb; 411 412 if (verbose) 413 (void)printf("%s: block numbers: ", boot); 414 if (fs->fs_magic == FS_UFS1_MAGIC) { 415 ap1 = ip1->di_db; 416 for (i = 0; i < NDADDR && *ap1 && ndb; i++, ap1++, ndb--) { 417 blk = fsbtodb(fs, *ap1); 418 bbinfop->blocks[i] = blk + partoffset; 419 if (verbose) 420 (void)printf("%d ", bbinfop->blocks[i]); 421 } 422 } else { 423 ap2 = ip2->di_db; 424 for (i = 0; i < NDADDR && *ap2 && ndb; i++, ap2++, ndb--) { 425 blk = fsbtodb(fs, *ap2); 426 bbinfop->blocks[i] = blk + partoffset; 427 if (verbose) 428 (void)printf("%d ", bbinfop->blocks[i]); 429 } 430 } 431 if (verbose) 432 (void)printf("\n"); 433 434 if (ndb == 0) 435 goto checksum; 436 437 /* 438 * Just one level of indirections; there isn't much room 439 * for more in the 1st-level bootblocks anyway. 440 */ 441 if (verbose) 442 (void)printf("%s: block numbers (indirect): ", boot); 443 if (fs->fs_magic == FS_UFS1_MAGIC) { 444 blk = ip1->di_ib[0]; 445 devread(devfd, buf, blk + partoffset, fs->fs_bsize, 446 "indirect block"); 447 ap1 = (daddr32_t *)buf; 448 for (; i < NINDIR(fs) && *ap1 && ndb; i++, ap1++, ndb--) { 449 blk = fsbtodb(fs, *ap1); 450 bbinfop->blocks[i] = blk + partoffset; 451 if (verbose) 452 (void)printf("%d ", bbinfop->blocks[i]); 453 } 454 } else { 455 blk = ip2->di_ib[0]; 456 devread(devfd, buf, blk + partoffset, fs->fs_bsize, 457 "indirect block"); 458 ap2 = (daddr_t *)buf; 459 for (; i < NINDIR(fs) && *ap2 && ndb; i++, ap2++, ndb--) { 460 blk = fsbtodb(fs, *ap2); 461 bbinfop->blocks[i] = blk + partoffset; 462 if (verbose) 463 (void)printf("%d ", bbinfop->blocks[i]); 464 } 465 } 466 if (verbose) 467 (void)printf("\n"); 468 469 if (ndb) 470 errx(1, "%s: Too many blocks", boot); 471 472 checksum: 473 cksum = 0; 474 for (i = 0; i < bbinfop->nblocks + 475 (sizeof (*bbinfop) / sizeof (bbinfop->blocks[0])) - 1; i++) 476 cksum += ((int32_t *)bbinfop)[i]; 477 bbinfop->cksum = -cksum; 478 479 return 0; 480 } 481 482 static int 483 sbchk(struct fs *fs, daddr_t sbloc) 484 { 485 if (verbose) 486 fprintf(stderr, "looking for superblock at %lld\n", sbloc); 487 488 if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) { 489 if (verbose) 490 fprintf(stderr, "bad superblock magic 0x%x\n", 491 fs->fs_magic); 492 return (0); 493 } 494 495 /* 496 * Looking for an FFS1 file system at SBLOCK_UFS2 will find the 497 * wrong superblock for file systems with 64k block size. 498 */ 499 if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) { 500 if (verbose) 501 fprintf(stderr, "skipping ffs1 superblock at %lld\n", 502 sbloc); 503 return (0); 504 } 505 506 if (fs->fs_bsize <= 0 || fs->fs_bsize < sizeof(struct fs) || 507 fs->fs_bsize > MAXBSIZE) { 508 if (verbose) 509 fprintf(stderr, "invalid superblock block size %d\n", 510 fs->fs_bsize); 511 return (0); 512 } 513 514 if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) { 515 if (verbose) 516 fprintf(stderr, "invalid superblock size %d\n", 517 fs->fs_sbsize); 518 return (0); 519 } 520 521 if (fs->fs_inopb <= 0) { 522 if (verbose) 523 fprintf(stderr, "invalid superblock inodes/block %d\n", 524 fs->fs_inopb); 525 return (0); 526 } 527 528 if (verbose) 529 fprintf(stderr, "found valid %s superblock\n", 530 fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1"); 531 532 return (1); 533 } 534 535 static void 536 sbread(int fd, daddr_t poffset, struct fs **fs, char *sblock) 537 { 538 int i; 539 daddr_t sboff; 540 541 for (i = 0; sbtry[i] != -1; i++) { 542 sboff = sbtry[i] / DEV_BSIZE; 543 devread(fd, sblock, poffset + sboff, SBSIZE, "superblock"); 544 *fs = (struct fs *)sblock; 545 if (sbchk(*fs, sbtry[i])) 546 break; 547 } 548 549 if (sbtry[i] == -1) 550 errx(1, "couldn't find ffs superblock"); 551 } 552