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
usage(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
main(int argc,char * argv[])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 *
loadprotoblocks(char * fname,long * size)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
devread(int fd,void * buf,daddr_t blk,size_t size,char * msg)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
loadblocknums(char * boot,int devfd,unsigned long partoffset)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
sbchk(struct fs * fs,daddr_t sbloc)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
sbread(int fd,daddr_t poffset,struct fs ** fs,char * sblock)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