1 /*        $NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $         */
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matt Fredette.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
35 
36 #include <sys/cdefs.h>
37 #if !defined(__lint)
38 __RCSID("$NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $");
39 #endif    /* !__lint */
40 
41 #include <sys/param.h>
42 
43 #if !HAVE_NBTOOL_CONFIG_H
44 #include <sys/mount.h>
45 #endif
46 
47 #include <assert.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 #include "installboot.h"
58 
59 /* From <dev/raidframe/raidframevar.h> */
60 #define RF_PROTECTED_SECTORS 64L
61 
62 #undef DIRBLKSIZ
63 
64 #include <ufs/ufs/dinode.h>
65 #include <ufs/ufs/dir.h>
66 #include <ufs/ffs/fs.h>
67 #include <ufs/ffs/ffs_extern.h>
68 #ifndef NO_FFS_SWAP
69 #include <ufs/ufs/ufs_bswap.h>
70 #else
71 #define   ffs_sb_swap(fs_a, fs_b)
72 #define   ffs_dinode1_swap(inode_a, inode_b)
73 #define   ffs_dinode2_swap(inode_a, inode_b)
74 #endif
75 
76 static int          ffs_match_common(ib_params *, off_t);
77 static int          ffs_read_disk_block(ib_params *, uint64_t, int, char []);
78 static int          ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
79                         int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
80 static int          ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
81                         int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
82 static int          ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
83 static int          ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
84 
85 static int is_ufs2;
86 
87 
88 /* This reads a disk block from the filesystem. */
89 static int
ffs_read_disk_block(ib_params * params,uint64_t blkno,int size,char blk[])90 ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char blk[])
91 {
92           int       rv;
93 
94           assert(params != NULL);
95           assert(params->filesystem != NULL);
96           assert(params->fsfd != -1);
97           assert(size > 0);
98           assert(blk != NULL);
99 
100           rv = pread(params->fsfd, blk, size, blkno * params->sectorsize);
101           if (rv == -1) {
102                     warn("Reading block %llu in `%s'",
103                         (unsigned long long)blkno, params->filesystem);
104                     return (0);
105           } else if (rv != size) {
106                     warnx("Reading block %llu in `%s': short read",
107                         (unsigned long long)blkno, params->filesystem);
108                     return (0);
109           }
110 
111           return (1);
112 }
113 
114 /*
115  * This iterates over the data blocks belonging to an inode,
116  * making a callback each iteration with the disk block number
117  * and the size.
118  */
119 static int
ffs_find_disk_blocks_ufs1(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)120 ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
121           int (*callback)(ib_params *, void *, uint64_t, uint32_t),
122           void *state)
123 {
124           char                sbbuf[SBLOCKSIZE];
125           struct fs *fs;
126           char                inodebuf[MAXBSIZE];
127           struct ufs1_dinode  *inode;
128           int                 level_i;
129           int32_t   blk, lblk, nblk;
130           int                 rv;
131 #define LEVELS 4
132           struct {
133                     int32_t             *blknums;
134                     unsigned long       blkcount;
135                     char                diskbuf[MAXBSIZE];
136           } level[LEVELS];
137 
138           assert(params != NULL);
139           assert(params->fstype != NULL);
140           assert(callback != NULL);
141           assert(state != NULL);
142 
143           /* Read the superblock. */
144           if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
145               sbbuf))
146                     return (0);
147           fs = (struct fs *)sbbuf;
148 #ifndef NO_FFS_SWAP
149           if (params->fstype->needswap)
150                     ffs_sb_swap(fs, fs);
151 #endif
152 
153           if (fs->fs_inopb <= 0) {
154                     warnx("Bad inopb %d in superblock in `%s'",
155                         fs->fs_inopb, params->filesystem);
156                     return (0);
157           }
158 
159           /* Read the inode. */
160           if (! ffs_read_disk_block(params,
161                     FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
162                     fs->fs_bsize, inodebuf))
163                     return (0);
164           inode = (struct ufs1_dinode *)inodebuf;
165           inode += ino_to_fsbo(fs, ino);
166 #ifndef NO_FFS_SWAP
167           if (params->fstype->needswap)
168                     ffs_dinode1_swap(inode, inode);
169 #endif
170 
171           /* Get the block count and initialize for our block walk. */
172           nblk = howmany(inode->di_size, fs->fs_bsize);
173           lblk = 0;
174           level_i = 0;
175           level[0].blknums = &inode->di_db[0];
176           level[0].blkcount = UFS_NDADDR;
177           level[1].blknums = &inode->di_ib[0];
178           level[1].blkcount = 1;
179           level[2].blknums = &inode->di_ib[1];
180           level[2].blkcount = 1;
181           level[3].blknums = &inode->di_ib[2];
182           level[3].blkcount = 1;
183 
184           /* Walk the data blocks. */
185           while (nblk > 0) {
186 
187                     /*
188                      * If there are no more blocks at this indirection
189                      * level, move up one indirection level and loop.
190                      */
191                     if (level[level_i].blkcount == 0) {
192                               if (++level_i == LEVELS)
193                                         break;
194                               continue;
195                     }
196 
197                     /* Get the next block at this level. */
198                     blk = *(level[level_i].blknums++);
199                     level[level_i].blkcount--;
200                     if (params->fstype->needswap)
201                               blk = bswap32(blk);
202 
203 #if 0
204                     fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
205                         level_i);
206 #endif
207 
208                     /*
209                      * If we're not at the direct level, descend one
210                      * level, read in that level's new block list,
211                      * and loop.
212                      */
213                     if (level_i > 0) {
214                               level_i--;
215                               if (blk == 0)
216                                         memset(level[level_i].diskbuf, 0, MAXBSIZE);
217                               else if (! ffs_read_disk_block(params,
218                                         FFS_FSBTODB(fs, blk) + params->fstype->offset,
219                                         fs->fs_bsize, level[level_i].diskbuf))
220                                         return (0);
221                               /* XXX ondisk32 */
222                               level[level_i].blknums =
223                                         (int32_t *)level[level_i].diskbuf;
224                               level[level_i].blkcount = FFS_NINDIR(fs);
225                               continue;
226                     }
227 
228                     /* blk is the next direct level block. */
229 #if 0
230                     fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
231                         FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
232 #endif
233                     rv = (*callback)(params, state,
234                         FFS_FSBTODB(fs, blk) + params->fstype->offset,
235                         ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
236                     lblk++;
237                     nblk--;
238                     if (rv != 1)
239                               return (rv);
240           }
241 
242           if (nblk != 0) {
243                     warnx("Inode %llu in `%s' ran out of blocks?",
244                         (unsigned long long)ino, params->filesystem);
245                     return (0);
246           }
247 
248           return (1);
249 }
250 
251 /*
252  * This iterates over the data blocks belonging to an inode,
253  * making a callback each iteration with the disk block number
254  * and the size.
255  */
256 static int
ffs_find_disk_blocks_ufs2(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)257 ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
258           int (*callback)(ib_params *, void *, uint64_t, uint32_t),
259           void *state)
260 {
261           char                sbbuf[SBLOCKSIZE];
262           struct fs *fs;
263           char                inodebuf[MAXBSIZE];
264           struct ufs2_dinode  *inode;
265           int                 level_i;
266           int64_t             blk, lblk, nblk;
267           int                 rv;
268 #define LEVELS 4
269           struct {
270                     int64_t             *blknums;
271                     unsigned long       blkcount;
272                     char                diskbuf[MAXBSIZE];
273           } level[LEVELS];
274 
275           assert(params != NULL);
276           assert(params->fstype != NULL);
277           assert(callback != NULL);
278           assert(state != NULL);
279 
280           /* Read the superblock. */
281           if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
282               sbbuf))
283                     return (0);
284           fs = (struct fs *)sbbuf;
285 #ifndef NO_FFS_SWAP
286           if (params->fstype->needswap)
287                     ffs_sb_swap(fs, fs);
288 #endif
289 
290           if (fs->fs_inopb <= 0) {
291                     warnx("Bad inopb %d in superblock in `%s'",
292                         fs->fs_inopb, params->filesystem);
293                     return (0);
294           }
295 
296           /* Read the inode. */
297           if (! ffs_read_disk_block(params,
298                     FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
299                     fs->fs_bsize, inodebuf))
300                     return (0);
301           inode = (struct ufs2_dinode *)inodebuf;
302           inode += ino_to_fsbo(fs, ino);
303 #ifndef NO_FFS_SWAP
304           if (params->fstype->needswap)
305                     ffs_dinode2_swap(inode, inode);
306 #endif
307 
308           /* Get the block count and initialize for our block walk. */
309           nblk = howmany(inode->di_size, fs->fs_bsize);
310           lblk = 0;
311           level_i = 0;
312           level[0].blknums = &inode->di_db[0];
313           level[0].blkcount = UFS_NDADDR;
314           level[1].blknums = &inode->di_ib[0];
315           level[1].blkcount = 1;
316           level[2].blknums = &inode->di_ib[1];
317           level[2].blkcount = 1;
318           level[3].blknums = &inode->di_ib[2];
319           level[3].blkcount = 1;
320 
321           /* Walk the data blocks. */
322           while (nblk > 0) {
323 
324                     /*
325                      * If there are no more blocks at this indirection
326                      * level, move up one indirection level and loop.
327                      */
328                     if (level[level_i].blkcount == 0) {
329                               if (++level_i == LEVELS)
330                                         break;
331                               continue;
332                     }
333 
334                     /* Get the next block at this level. */
335                     blk = *(level[level_i].blknums++);
336                     level[level_i].blkcount--;
337                     if (params->fstype->needswap)
338                               blk = bswap64(blk);
339 
340 #if 0
341                     fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
342                         (unsigned long long)blk, level_i);
343 #endif
344 
345                     /*
346                      * If we're not at the direct level, descend one
347                      * level, read in that level's new block list,
348                      * and loop.
349                      */
350                     if (level_i > 0) {
351                               level_i--;
352                               if (blk == 0)
353                                         memset(level[level_i].diskbuf, 0, MAXBSIZE);
354                               else if (! ffs_read_disk_block(params,
355                                         FFS_FSBTODB(fs, blk) + params->fstype->offset,
356                                         fs->fs_bsize, level[level_i].diskbuf))
357                                         return (0);
358                               level[level_i].blknums =
359                                         (int64_t *)level[level_i].diskbuf;
360                               level[level_i].blkcount = FFS_NINDIR(fs);
361                               continue;
362                     }
363 
364                     /* blk is the next direct level block. */
365 #if 0
366                     fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
367                         FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
368 #endif
369                     rv = (*callback)(params, state,
370                         FFS_FSBTODB(fs, blk) + params->fstype->offset,
371                         ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
372                     lblk++;
373                     nblk--;
374                     if (rv != 1)
375                               return (rv);
376           }
377 
378           if (nblk != 0) {
379                     warnx("Inode %llu in `%s' ran out of blocks?",
380                         (unsigned long long)ino, params->filesystem);
381                     return (0);
382           }
383 
384           return (1);
385 }
386 
387 /*
388  * This callback reads a block of the root directory,
389  * searches for an entry for the secondary bootstrap,
390  * and saves the inode number if one is found.
391  */
392 static int
ffs_findstage2_ino(ib_params * params,void * _ino,uint64_t blk,uint32_t blksize)393 ffs_findstage2_ino(ib_params *params, void *_ino,
394           uint64_t blk, uint32_t blksize)
395 {
396           char                dirbuf[MAXBSIZE];
397           struct direct       *de, *ede;
398           uint32_t  ino;
399 
400           assert(params != NULL);
401           assert(params->fstype != NULL);
402           assert(params->stage2 != NULL);
403           assert(_ino != NULL);
404 
405           /* Skip directory holes. */
406           if (blk == 0)
407                     return (1);
408 
409           /* Read the directory block. */
410           if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
411                     return (0);
412 
413           /* Loop over the directory entries. */
414           de = (struct direct *)&dirbuf[0];
415           ede = (struct direct *)&dirbuf[blksize];
416           while (de < ede) {
417                     ino = de->d_fileno;
418                     if (params->fstype->needswap) {
419                               ino = bswap32(ino);
420                               de->d_reclen = bswap16(de->d_reclen);
421                     }
422                     if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
423                               *((uint32_t *)_ino) = ino;
424                               return (2);
425                     }
426                     if (de->d_reclen == 0)
427                               break;
428                     de = (struct direct *)((char *)de + de->d_reclen);
429           }
430 
431           return (1);
432 }
433 
434 struct findblks_state {
435           uint32_t  maxblk;
436           uint32_t  nblk;
437           ib_block  *blocks;
438 };
439 
440 /* This callback records the blocks of the secondary bootstrap. */
441 static int
ffs_findstage2_blocks(ib_params * params,void * _state,uint64_t blk,uint32_t blksize)442 ffs_findstage2_blocks(ib_params *params, void *_state,
443           uint64_t blk, uint32_t blksize)
444 {
445           struct findblks_state *state = _state;
446 
447           assert(params != NULL);
448           assert(params->stage2 != NULL);
449           assert(_state != NULL);
450 
451           if (state->nblk == state->maxblk) {
452                     warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
453                         params->stage2, state->maxblk);
454                     return (0);
455           }
456           state->blocks[state->nblk].block = blk;
457           state->blocks[state->nblk].blocksize = blksize;
458           state->nblk++;
459           return (1);
460 }
461 
462 /*
463  *        publicly visible functions
464  */
465 
466 static off_t sblock_try[] = SBLOCKSEARCH;
467 
468 int
ffs_match(ib_params * params)469 ffs_match(ib_params *params)
470 {
471           return ffs_match_common(params, (off_t) 0);
472 }
473 
474 int
raid_match(ib_params * params)475 raid_match(ib_params *params)
476 {
477           /* XXX Assumes 512 bytes / sector */
478           if (params->sectorsize != 512) {
479                     warnx("Media is %d bytes/sector."
480                               "  RAID is only supported on 512 bytes/sector media.",
481                               params->sectorsize);
482                     return 0;
483           }
484           return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
485 }
486 
487 int
ffs_match_common(ib_params * params,off_t offset)488 ffs_match_common(ib_params *params, off_t offset)
489 {
490           char                sbbuf[SBLOCKSIZE];
491           struct fs *fs;
492           int i;
493           off_t loc;
494 
495           assert(params != NULL);
496           assert(params->fstype != NULL);
497 
498           fs = (struct fs *)sbbuf;
499           for (i = 0; sblock_try[i] != -1; i++) {
500                     loc = sblock_try[i] / params->sectorsize + offset;
501                     if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
502                               continue;
503                     switch (fs->fs_magic) {
504                     case FS_UFS2_MAGIC:
505                     case FS_UFS2EA_MAGIC:
506                               is_ufs2 = 1;
507                               /* FALLTHROUGH */
508                     case FS_UFS1_MAGIC:
509                               params->fstype->needswap = 0;
510                               params->fstype->blocksize = fs->fs_bsize;
511                               params->fstype->sblockloc = loc;
512                               params->fstype->offset = offset;
513                               break;
514 #ifndef FFS_NO_SWAP
515                     case FS_UFS2_MAGIC_SWAPPED:
516                     case FS_UFS2EA_MAGIC_SWAPPED:
517                               is_ufs2 = 1;
518                               /* FALLTHROUGH */
519                     case FS_UFS1_MAGIC_SWAPPED:
520                               params->fstype->needswap = 1;
521                               params->fstype->blocksize = bswap32(fs->fs_bsize);
522                               params->fstype->sblockloc = loc;
523                               params->fstype->offset = offset;
524                               break;
525 #endif
526                     default:
527                               continue;
528                     }
529                     if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
530                               continue;
531                     return 1;
532           }
533 
534           return (0);
535 }
536 
537 int
ffs_findstage2(ib_params * params,uint32_t * maxblk,ib_block * blocks)538 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
539 {
540           int                           rv;
541           uint32_t            ino;
542           struct findblks_state         state;
543 
544           assert(params != NULL);
545           assert(params->stage2 != NULL);
546           assert(maxblk != NULL);
547           assert(blocks != NULL);
548 
549           if (params->flags & IB_STAGE2START)
550                     return (hardcode_stage2(params, maxblk, blocks));
551 
552           /* The secondary bootstrap must be clearly in /. */
553           if (params->stage2[0] == '/')
554                     params->stage2++;
555           if (strchr(params->stage2, '/') != NULL) {
556                     warnx("The secondary bootstrap `%s' must be in /",
557                         params->stage2);
558                     warnx("(Path must be relative to the file system in `%s')",
559                         params->filesystem);
560                     return (0);
561           }
562 
563           /* Get the inode number of the secondary bootstrap. */
564           if (is_ufs2)
565                     rv = ffs_find_disk_blocks_ufs2(params, UFS_ROOTINO,
566                         ffs_findstage2_ino, &ino);
567           else
568                     rv = ffs_find_disk_blocks_ufs1(params, UFS_ROOTINO,
569                         ffs_findstage2_ino, &ino);
570           if (rv != 2) {
571                     warnx("Could not find secondary bootstrap `%s' in `%s'",
572                         params->stage2, params->filesystem);
573                     warnx("(Path must be relative to the file system in `%s')",
574                         params->filesystem);
575                     return (0);
576           }
577 
578           /* Record the disk blocks of the secondary bootstrap. */
579           state.maxblk = *maxblk;
580           state.nblk = 0;
581           state.blocks = blocks;
582           if (is_ufs2)
583                     rv = ffs_find_disk_blocks_ufs2(params, ino,
584                         ffs_findstage2_blocks, &state);
585           else
586                     rv = ffs_find_disk_blocks_ufs1(params, ino,
587                         ffs_findstage2_blocks, &state);
588           if (! rv) {
589                     return (0);
590           }
591 
592           *maxblk = state.nblk;
593           return (1);
594 }
595