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