1/*	$NetBSD: readufs_lfs.c,v 1.2 2015/07/28 16:50:12 christos Exp $	*/
2/*	from Id: readufs_lfs.c,v 1.8 2003/12/16 13:54:11 itohy Exp	*/
3
4/*
5 * FS specific support for 4.4BSD Log-structured Filesystem
6 *
7 * Written in 1999, 2002, 2003 by ITOH Yasufumi.
8 * Public domain.
9 *
10 * Intended to be used for boot programs (first stage).
11 * DON'T ADD ANY FANCY FEATURE.  THIS SHALL BE COMPACT.
12 */
13
14#include "readufs.h"
15
16#include <sys/mount.h>
17#include <ufs/lfs/lfs.h>
18
19#ifndef USE_UFS1
20 #error LFS currently requires USE_UFS1
21#endif
22
23static struct lfs32_dinode	ifile_dinode;
24
25#define fsi	(*ufsinfo)
26#define fsi_lfs	fsi.fs_u.u_lfs
27
28/*
29 * Read and check superblock.
30 * If it is an LFS, save information from the superblock.
31 */
32int
33try_lfs(void)
34{
35	struct ufs_info	*ufsinfo = &ufs_info;
36	struct dlfs	sblk, sblk2;
37	struct dlfs	*s = &sblk;
38	daddr_t		sbpos;
39	int		fsbshift;
40
41#ifdef DEBUG_WITH_STDIO
42	printf("trying LFS\n");
43#endif
44	sbpos =  btodb(LFS_LABELPAD);
45
46	/* read primary superblock */
47	for (;;) {
48#ifdef DEBUG_WITH_STDIO
49		printf("LFS: reading primary sblk at: 0x%x\n", (unsigned)sbpos);
50#endif
51		RAW_READ(&sblk, sbpos, sizeof sblk);
52
53#ifdef DEBUG_WITH_STDIO
54		printf("LFS: sblk: magic: 0x%x, version: %d\n",
55			sblk.dlfs_magic, sblk.dlfs_version);
56#endif
57
58		if (sblk.dlfs_magic != LFS_MAGIC)
59			return 1;
60
61#ifdef DEBUG_WITH_STDIO
62		printf("LFS: bsize %d, fsize %d, bshift %d, blktodb %d, fsbtodb %d, inopf %d, inopb %d\n",
63		    sblk.dlfs_bsize, sblk.dlfs_fsize,
64		    sblk.dlfs_bshift, sblk.dlfs_blktodb, sblk.dlfs_fsbtodb,
65		    sblk.dlfs_inopf, sblk.dlfs_inopb);
66#endif
67		if ((fsi_lfs.version = sblk.dlfs_version) == 1) {
68			fsbshift = 0;
69			break;
70		} else {
71			daddr_t	sbpos1;
72#if 0
73			fsbshift = sblk.dlfs_bshift - sblk.dlfs_blktodb + sblk.dlfs_fsbtodb - DEV_BSHIFT;
74#endif
75			fsbshift = sblk.dlfs_fsbtodb;
76			sbpos1 = sblk.dlfs_sboffs[0] << fsbshift;
77			if (sbpos == sbpos1)
78				break;
79#ifdef DEBUG_WITH_STDIO
80			printf("LFS: correcting primary sblk location\n");
81#endif
82			sbpos = sbpos1;
83		}
84	}
85
86#ifdef DEBUG_WITH_STDIO
87	printf("fsbshift: %d\n", fsbshift);
88	printf("sboff[1]: %d\n", sblk.dlfs_sboffs[1]);
89#endif
90
91	if (sblk.dlfs_sboffs[1] > 0) {
92#ifdef DEBUG_WITH_STDIO
93		printf("LFS: reading secondary sblk at: 0x%x\n",
94		    sblk.dlfs_sboffs[1] << fsbshift);
95#endif
96		/* read secondary superblock */
97		RAW_READ(&sblk2, (daddr_t) sblk.dlfs_sboffs[1] << fsbshift,
98		    sizeof sblk2);
99
100#ifdef DEBUG_WITH_STDIO
101		printf("LFS: sblk2: magic: 0x%x, version: %d\n",
102			sblk2.dlfs_magic, sblk2.dlfs_version);
103#endif
104
105		if (sblk2.dlfs_magic == LFS_MAGIC) {
106			if (fsi_lfs.version == 1) {
107				if (sblk.dlfs_inopf > sblk2.dlfs_inopf)
108					s = &sblk2;
109			} else {
110				if (sblk.dlfs_serial > sblk2.dlfs_serial)
111					s = &sblk2;
112			}
113		}
114	}
115
116	/* This partition looks like an LFS. */
117#if 0
118	fsi.get_inode = get_lfs_inode;
119#endif
120	/*
121	 * version 1: disk addr is in disk sector --- no shifting
122	 * version 2: disk addr is in fragment
123	 */
124	fsi.fsbtodb = fsbshift;
125
126	/* Get information from the superblock. */
127	fsi.bsize = s->dlfs_bsize;
128	fsi.nindir = s->dlfs_nindir;
129	fsi_lfs.idaddr = s->dlfs_idaddr;
130	fsi_lfs.ibsize = (fsi_lfs.version == 1) ? s->dlfs_bsize : s->dlfs_fsize;
131
132	/*
133	 * version 1: number of inode per block
134	 * version 2: number of inode per fragment (but in dlfs_inopb)
135	 */
136	fsi_lfs.inopb = s->dlfs_inopb;
137
138	fsi_lfs.ifpb = s->dlfs_ifpb;
139	fsi_lfs.ioffset = s->dlfs_cleansz + s->dlfs_segtabsz;
140
141	/* ifile is always used to look-up other inodes, so keep its inode. */
142	if (get_lfs_inode(LFS_IFILE_INUM, (union ufs_dinode *)&ifile_dinode))
143		return 1;	/* OOPS, failed to find inode of ifile! */
144
145	fsi.fstype = UFSTYPE_LFS;
146
147	return 0;
148}
149
150/*
151 * Get inode from disk.
152 */
153int
154get_lfs_inode(ino32_t ino, union ufs_dinode *dibuf)
155{
156	struct ufs_info *ufsinfo = &ufs_info;
157	daddr_t daddr;
158	char *buf = alloca(fsi.bsize);
159	struct lfs32_dinode *di, *diend;
160	int i;
161
162	/* Get fs block which contains the specified inode. */
163	if (ino == LFS_IFILE_INUM)
164		daddr = fsi_lfs.idaddr;
165	else {
166#ifdef DEBUG_WITH_STDIO
167		printf("LFS: ino: %d\nifpb: %d, bsize: %d\n",
168			ino, fsi_lfs.ifpb, fsi.bsize);
169#endif
170		ufs_read((union ufs_dinode *) &ifile_dinode, buf,
171			 ino / fsi_lfs.ifpb + fsi_lfs.ioffset,
172			 fsi.bsize);
173		i = ino % fsi_lfs.ifpb;
174		daddr = (fsi_lfs.version == 1) ?
175		    ((IFILE_V1 *) buf + i)->if_daddr
176		    : ((IFILE32 *) buf + i)->if_daddr;
177	}
178#ifdef DEBUG_WITH_STDIO
179	printf("LFS(%d): daddr: %d\n", ino, (int) daddr);
180#endif
181
182	if (daddr == LFS_UNUSED_DADDR)
183		return 1;
184
185	/* Read the inode block. */
186	RAW_READ(buf, daddr << fsi.fsbtodb, fsi_lfs.ibsize);
187
188	/* Search for the inode. */
189	di = (struct lfs32_dinode *) buf;
190	diend = di + fsi_lfs.inopb;
191
192	for ( ; di < diend; di++)
193		if (di->di_inumber == ino)
194			goto found;
195	/* not found */
196	return 1;
197
198found:
199#ifdef DEBUG_WITH_STDIO
200	printf("LFS: dinode(%d): mode 0%o, nlink %d, inumber %d, size %d, uid %d, db[0] %d\n",
201		ino, di->di_mode, di->di_nlink, di->di_inumber,
202		(int) di->di_size, di->di_uid, di->di_db[0]);
203#endif
204
205#if 0	/* currently UFS1 only */
206#if defined(USE_UFS1) && defined(USE_UFS2)
207	/* XXX for DI_SIZE() macro */
208	if (ufsinfo->ufstype != UFSTYPE_UFS1)
209		di->di1.di_size = di->si2.di_size;
210#endif
211#endif
212
213	dibuf->dil32 = *di;
214
215	return 0;
216}
217