1 /* $OpenBSD: installboot.c,v 1.11 2002/03/14 01:26:27 millert 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 int main(int, char *[]); 74 75 int isofsblk = 0; 76 int isofseblk = 0; 77 78 static void 79 usage() 80 { 81 (void)fprintf(stderr, 82 "usage: installboot [-n] [-v] [-s isofsblk -e isofseblk] " 83 "<boot> <proto> <device>\n"); 84 exit(1); 85 } 86 87 int 88 main(argc, argv) 89 int argc; 90 char *argv[]; 91 { 92 int c; 93 int devfd; 94 char *protostore; 95 long protosize; 96 struct stat disksb, bootsb; 97 struct disklabel dl; 98 unsigned long partoffset; 99 100 while ((c = getopt(argc, argv, "vns:e:")) != -1) { 101 switch (c) { 102 case 'n': 103 /* Do not actually write the bootblock to disk */ 104 nowrite = 1; 105 break; 106 case 'v': 107 /* Chat */ 108 verbose = 1; 109 break; 110 case 's': 111 isofsblk = atoi(optarg); 112 break; 113 case 'e': 114 isofseblk = atoi(optarg); 115 break; 116 default: 117 usage(); 118 } 119 } 120 121 if (argc - optind < 3) 122 usage(); 123 124 boot = argv[optind]; 125 proto = argv[optind + 1]; 126 dev = argv[optind + 2]; 127 128 if (verbose) { 129 (void)printf("boot: %s\n", boot); 130 (void)printf("proto: %s\n", proto); 131 (void)printf("device: %s\n", dev); 132 } 133 134 /* Load proto blocks into core */ 135 if ((protostore = loadprotoblocks(proto, &protosize)) == NULL) 136 exit(1); 137 138 /* Open and check raw disk device */ 139 if ((devfd = opendev(dev, O_RDONLY, OPENDEV_PART, &dev)) < 0) 140 err(1, "open: %s", dev); 141 if (fstat(devfd, &disksb) == -1) 142 err(1, "fstat: %s", dev); 143 if (!S_ISCHR(disksb.st_mode)) 144 errx(1, "%s must be a character device node", dev); 145 if ((minor(disksb.st_rdev) % getmaxpartitions()) != getrawpartition()) 146 errx(1, "%s must be the raw partition", dev); 147 148 /* Extract and load block numbers */ 149 if (stat(boot, &bootsb) == -1) 150 err(1, "stat: %s", boot); 151 if (!S_ISREG(bootsb.st_mode)) 152 errx(1, "%s must be a regular file", boot); 153 if ((minor(disksb.st_rdev) / getmaxpartitions()) != 154 (minor(bootsb.st_dev) / getmaxpartitions())) 155 errx(1, "%s must be somewhere on %s", boot, dev); 156 157 /* 158 * Find the offset of the secondary boot block's partition 159 * into the disk. If disklabels not supported, assume zero. 160 */ 161 if (ioctl(devfd, DIOCGDINFO, &dl) != -1) { 162 partoffset = dl.d_partitions[minor(bootsb.st_dev) % 163 getmaxpartitions()].p_offset; 164 } else { 165 if (errno != ENOTTY) 166 err(1, "read disklabel: %s", dev); 167 warnx("couldn't read label from %s, using part offset of 0", 168 dev); 169 partoffset = 0; 170 } 171 if (verbose) 172 (void)printf("%s partition offset = 0x%lx\n", boot, partoffset); 173 174 /* Sync filesystems (make sure boot's block numbers are stable) */ 175 sync(); 176 sleep(2); 177 sync(); 178 sleep(2); 179 180 if (loadblocknums(boot, devfd, partoffset) != 0) 181 exit(1); 182 183 (void)close(devfd); 184 185 if (nowrite) 186 return 0; 187 188 #if 0 189 /* Write patched proto bootblocks into the superblock */ 190 if (protosize > SBSIZE - DEV_BSIZE) 191 errx(1, "proto bootblocks too big"); 192 #endif 193 194 if ((devfd = opendev(dev, O_RDWR, OPENDEV_PART, &dev)) < 0) 195 err(1, "open: %s", dev); 196 197 if (lseek(devfd, DEV_BSIZE, SEEK_SET) != DEV_BSIZE) 198 err(1, "lseek bootstrap"); 199 200 if (write(devfd, protostore, protosize) != protosize) 201 err(1, "write bootstrap"); 202 203 { 204 205 #define BBPAD 0x1e0 206 struct bb { 207 char bb_pad[BBPAD]; /* disklabel lives in here, actually */ 208 long bb_secsize; /* size of secondary boot block */ 209 long bb_secstart; /* start of secondary boot block */ 210 long bb_flags; /* unknown; always zero */ 211 long bb_cksum; /* checksum of the boot block, as longs. */ 212 } bb; 213 long *lp, *ep; 214 215 if (lseek(devfd, 0, SEEK_SET) != 0) 216 err(1, "lseek label"); 217 218 if (read(devfd, &bb, sizeof (bb)) != sizeof (bb)) 219 err(1, "read label"); 220 221 bb.bb_secsize = 15; 222 bb.bb_secstart = 1; 223 bb.bb_flags = 0; 224 bb.bb_cksum = 0; 225 226 for (lp = (long *)&bb, ep = &bb.bb_cksum; lp < ep; lp++) 227 bb.bb_cksum += *lp; 228 229 if (lseek(devfd, 0, SEEK_SET) != 0) 230 err(1, "lseek label 2"); 231 232 if (write(devfd, &bb, sizeof bb) != sizeof bb) 233 err(1, "write label "); 234 } 235 236 (void)close(devfd); 237 return 0; 238 } 239 240 char * 241 loadprotoblocks(fname, size) 242 char *fname; 243 long *size; 244 { 245 int fd, sz; 246 char *bp; 247 struct stat statbuf; 248 u_int64_t *matchp; 249 250 /* 251 * Read the prototype boot block into memory. 252 */ 253 if ((fd = open(fname, O_RDONLY)) < 0) { 254 warn("open: %s", fname); 255 return NULL; 256 } 257 if (fstat(fd, &statbuf) != 0) { 258 warn("fstat: %s", fname); 259 close(fd); 260 return NULL; 261 } 262 sz = roundup(statbuf.st_size, DEV_BSIZE); 263 if ((bp = calloc(sz, 1)) == NULL) { 264 warnx("malloc: %s: no memory", fname); 265 close(fd); 266 return NULL; 267 } 268 if (read(fd, bp, statbuf.st_size) != statbuf.st_size) { 269 warn("read: %s", fname); 270 free(bp); 271 close(fd); 272 return NULL; 273 } 274 close(fd); 275 276 /* 277 * Find the magic area of the program, and figure out where 278 * the 'blocks' struct is, from that. 279 */ 280 bbinfolocp = NULL; 281 for (matchp = (u_int64_t *)bp; (char *)matchp < bp + sz; matchp++) { 282 if (*matchp != 0xbabefacedeadbeef) 283 continue; 284 bbinfolocp = (struct bbinfoloc *)matchp; 285 if (bbinfolocp->magic1 == 0xbabefacedeadbeef && 286 bbinfolocp->magic2 == 0xdeadbeeffacebabe) 287 break; 288 bbinfolocp = NULL; 289 } 290 291 if (bbinfolocp == NULL) { 292 warnx("%s: not a valid boot block?", fname); 293 return NULL; 294 } 295 296 bbinfop = (struct bbinfo *)(bp + bbinfolocp->end - bbinfolocp->start); 297 memset(bbinfop, 0, sz - (bbinfolocp->end - bbinfolocp->start)); 298 max_block_count = 299 ((char *)bbinfop->blocks - bp) / sizeof (bbinfop->blocks[0]); 300 301 if (verbose) { 302 (void)printf("boot block info locator at offset 0x%x\n", 303 (char *)bbinfolocp - bp); 304 (void)printf("boot block info at offset 0x%x\n", 305 (char *)bbinfop - bp); 306 (void)printf("max number of blocks: %d\n", max_block_count); 307 } 308 309 *size = sz; 310 return (bp); 311 } 312 313 static void 314 devread(fd, buf, blk, size, msg) 315 int fd; 316 void *buf; 317 daddr_t blk; 318 size_t size; 319 char *msg; 320 { 321 if (lseek(fd, dbtob(blk), SEEK_SET) != dbtob(blk)) 322 err(1, "%s: devread: lseek", msg); 323 324 if (read(fd, buf, size) != size) 325 err(1, "%s: devread: read", msg); 326 } 327 328 static char sblock[SBSIZE]; 329 330 int 331 loadblocknums(boot, devfd, partoffset) 332 char *boot; 333 int devfd; 334 unsigned long partoffset; 335 { 336 int i, fd; 337 struct stat statbuf; 338 struct statfs statfsbuf; 339 struct fs *fs; 340 char *buf; 341 daddr_t blk, *ap; 342 struct dinode *ip; 343 int ndb; 344 int32_t cksum; 345 346 /* 347 * Open 2nd-level boot program and record the block numbers 348 * it occupies on the filesystem represented by `devfd'. 349 */ 350 if ((fd = open(boot, O_RDONLY)) < 0) 351 err(1, "open: %s", boot); 352 353 if (fstatfs(fd, &statfsbuf) != 0) 354 err(1, "statfs: %s", boot); 355 356 if (isofsblk) { 357 bbinfop->bsize = ISO_DEFAULT_BLOCK_SIZE; 358 bbinfop->nblocks = isofseblk - isofsblk + 1; 359 if (bbinfop->nblocks > max_block_count) 360 errx(1, "%s: Too many blocks", boot); 361 if (verbose) 362 (void)printf("%s: starting block %d (%d total):\n\t", 363 boot, isofsblk, bbinfop->nblocks); 364 for (i = 0; i < bbinfop->nblocks; i++) { 365 blk = (isofsblk + i) * (bbinfop->bsize / DEV_BSIZE); 366 bbinfop->blocks[i] = blk; 367 if (verbose) 368 (void)printf("%d ", blk); 369 } 370 if (verbose) 371 (void)printf("\n"); 372 373 cksum = 0; 374 for (i = 0; i < bbinfop->nblocks + 375 (sizeof(*bbinfop) / sizeof(bbinfop->blocks[0])) - 1; i++) 376 cksum += ((int32_t *)bbinfop)[i]; 377 bbinfop->cksum = -cksum; 378 379 return 0; 380 } 381 382 if (strncmp(statfsbuf.f_fstypename, MOUNT_FFS, MFSNAMELEN)) 383 errx(1, "%s: must be on a FFS filesystem", boot); 384 385 if (fsync(fd) != 0) 386 err(1, "fsync: %s", boot); 387 388 if (fstat(fd, &statbuf) != 0) 389 err(1, "fstat: %s", boot); 390 391 close(fd); 392 393 /* Read superblock */ 394 devread(devfd, sblock, btodb(SBOFF) + partoffset, SBSIZE, 395 "superblock"); 396 fs = (struct fs *)sblock; 397 398 /* Read inode */ 399 if ((buf = malloc(fs->fs_bsize)) == NULL) 400 errx(1, "No memory for filesystem block"); 401 402 blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino)); 403 devread(devfd, buf, blk + partoffset, fs->fs_bsize, "inode"); 404 ip = (struct dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino); 405 406 /* 407 * Register filesystem block size. 408 */ 409 bbinfop->bsize = fs->fs_bsize; 410 411 /* 412 * Get the block numbers; we don't handle fragments 413 */ 414 ndb = howmany(ip->di_size, fs->fs_bsize); 415 if (ndb > max_block_count) 416 errx(1, "%s: Too many blocks", boot); 417 418 /* 419 * Register block count. 420 */ 421 bbinfop->nblocks = ndb; 422 423 if (verbose) 424 (void)printf("%s: block numbers: ", boot); 425 ap = ip->di_db; 426 for (i = 0; i < NDADDR && *ap && ndb; i++, ap++, ndb--) { 427 blk = fsbtodb(fs, *ap); 428 bbinfop->blocks[i] = blk + partoffset; 429 if (verbose) 430 (void)printf("%d ", bbinfop->blocks[i]); 431 } 432 if (verbose) 433 (void)printf("\n"); 434 435 if (ndb == 0) 436 goto checksum; 437 438 /* 439 * Just one level of indirections; there isn't much room 440 * for more in the 1st-level bootblocks anyway. 441 */ 442 if (verbose) 443 (void)printf("%s: block numbers (indirect): ", boot); 444 blk = ip->di_ib[0]; 445 devread(devfd, buf, blk + partoffset, fs->fs_bsize, 446 "indirect block"); 447 ap = (daddr_t *)buf; 448 for (; i < NINDIR(fs) && *ap && ndb; i++, ap++, ndb--) { 449 blk = fsbtodb(fs, *ap); 450 bbinfop->blocks[i] = blk + partoffset; 451 if (verbose) 452 (void)printf("%d ", bbinfop->blocks[i]); 453 } 454 if (verbose) 455 (void)printf("\n"); 456 457 if (ndb) 458 errx(1, "%s: Too many blocks", boot); 459 460 checksum: 461 cksum = 0; 462 for (i = 0; i < bbinfop->nblocks + 463 (sizeof (*bbinfop) / sizeof (bbinfop->blocks[0])) - 1; i++) { 464 cksum += ((int32_t *)bbinfop)[i]; 465 } 466 bbinfop->cksum = -cksum; 467 468 return 0; 469 } 470