disklabel64 UFS+HAMMER boot support (was previously just HAMMER boot support)
[dragonfly.git] / sys / boot / common / ufsread.c
CommitLineData
5ee58eed
MD
1/*-
2 * Copyright (c) 2002 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by Marshall
6 * Kirk McKusick and Network Associates Laboratories, the Security
7 * Research Division of Network Associates, Inc. under DARPA/SPAWAR
8 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
9 * research program
10 *
11 * Copyright (c) 1998 Robert Nordier
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms are freely
15 * permitted provided that the above copyright notice and this
16 * paragraph and the following disclaimer are duplicated in all
17 * such forms.
18 *
19 * This software is provided "AS IS" and without any express or
20 * implied warranties, including, without limitation, the implied
21 * warranties of merchantability and fitness for a particular
22 * purpose.
23 *
24 * $FreeBSD: src/sys/boot/common/ufsread.c,v 1.12 2003/08/25 23:30:41 obrien Exp $
9c92cc07 25 * $DragonFly: src/sys/boot/common/ufsread.c,v 1.5 2008/09/13 11:46:28 corecode Exp $
5ee58eed
MD
26 */
27
d64b2e33
MD
28#ifdef BOOT2
29#include "boot2.h"
30#else
31#include <sys/param.h>
32#endif
33#include <sys/dtype.h>
34#include <sys/dirent.h>
35#include <machine/bootinfo.h>
36#include <machine/elf.h>
b349062c 37#include <vfs/ufs/dir.h>
5ee58eed
MD
38#include "dinode.h"
39#include "fs.h"
d64b2e33 40
5ee58eed
MD
41#ifdef __i386__
42/* XXX: Revert to old (broken for over 1.5Tb filesystems) version of cgbase
43 (see sys/ufs/ffs/fs.h rev 1.39) so that i386 boot loader (boot2) can
44 support both UFS1 and UFS2 again. */
45#undef cgbase
46#define cgbase(fs, c) ((ufs2_daddr_t)((fs)->fs_fpg * (c)))
47#endif
48
49/*
50 * We use 4k `virtual' blocks for filesystem data, whatever the actual
51 * filesystem block size. FFS blocks are always a multiple of 4k.
52 */
53#define VBLKSHIFT 12
54#define VBLKSIZE (1 << VBLKSHIFT)
55#define VBLKMASK (VBLKSIZE - 1)
56#define DBPERVBLK (VBLKSIZE / DEV_BSIZE)
57#define INDIRPERVBLK(fs) (NINDIR(fs) / ((fs)->fs_bsize >> VBLKSHIFT))
58#define IPERVBLK(fs) (INOPB(fs) / ((fs)->fs_bsize >> VBLKSHIFT))
59#define INO_TO_VBA(fs, ipervblk, x) \
60 (fsbtodb(fs, cgimin(fs, ino_to_cg(fs, x))) + \
61 (((x) % (fs)->fs_ipg) / (ipervblk) * DBPERVBLK))
62#define INO_TO_VBO(ipervblk, x) ((x) % ipervblk)
63#define FS_TO_VBA(fs, fsb, off) (fsbtodb(fs, fsb) + \
64 ((off) / VBLKSIZE) * DBPERVBLK)
65#define FS_TO_VBO(fs, fsb, off) ((off) & VBLKMASK)
66
67/* Buffers that must not span a 64k boundary. */
d64b2e33
MD
68struct ufs_dmadat {
69 struct boot2_dmadat boot2;
5ee58eed
MD
70 char blkbuf[VBLKSIZE]; /* filesystem blocks */
71 char indbuf[VBLKSIZE]; /* indir blocks */
72 char sbbuf[SBLOCKSIZE]; /* superblock */
5ee58eed 73};
5ee58eed 74
d64b2e33
MD
75#define fsdmadat ((struct ufs_dmadat *)boot2_dmadat)
76
77static boot2_ino_t boot2_ufs_lookup(const char *);
78static ssize_t boot2_ufs_read(boot2_ino_t, void *, size_t);
79static int boot2_ufs_init(void);
80
81const struct boot2_fsapi boot2_ufs_api = {
82 .fsinit = boot2_ufs_init,
83 .fslookup = boot2_ufs_lookup,
84 .fsread = boot2_ufs_read
85};
5ee58eed 86
5ee58eed 87static __inline__ int
d64b2e33 88fsfind(const char *name, ufs_ino_t *ino)
5ee58eed
MD
89{
90 char buf[DEV_BSIZE];
b349062c 91 struct direct *d;
5ee58eed
MD
92 char *s;
93 ssize_t n;
94
95 fs_off = 0;
d64b2e33 96 while ((n = boot2_ufs_read(*ino, buf, DEV_BSIZE)) > 0)
5ee58eed
MD
97 for (s = buf; s < buf + DEV_BSIZE;) {
98 d = (void *)s;
99 if (ls)
100 printf("%s ", d->d_name);
101 else if (!strcmp(name, d->d_name)) {
ca0ebb6b 102 *ino = d->d_ino;
5ee58eed
MD
103 return d->d_type;
104 }
105 s += d->d_reclen;
106 }
107 if (n != -1 && ls)
108 printf("\n");
109 return 0;
110}
111
d64b2e33
MD
112static boot2_ino_t
113boot2_ufs_lookup(const char *path)
5ee58eed
MD
114{
115 char name[MAXNAMLEN + 1];
116 const char *s;
d64b2e33 117 ufs_ino_t ino;
5ee58eed
MD
118 ssize_t n;
119 int dt;
120
121 ino = ROOTINO;
122 dt = DT_DIR;
123 name[0] = '/';
124 name[1] = '\0';
125 for (;;) {
126 if (*path == '/')
127 path++;
128 if (!*path)
129 break;
130 for (s = path; *s && *s != '/'; s++);
131 if ((n = s - path) > MAXNAMLEN)
132 return 0;
133 ls = *path == '?' && n == 1 && !*s;
134 memcpy(name, path, n);
135 name[n] = 0;
136 if (dt != DT_DIR) {
137 printf("%s: not a directory.\n", name);
138 return (0);
139 }
140 if ((dt = fsfind(name, &ino)) <= 0)
141 break;
142 path = s;
143 }
144 return dt == DT_REG ? ino : 0;
145}
146
147/*
148 * Possible superblock locations ordered from most to least likely.
149 */
150static int sblock_try[] = SBLOCKSEARCH;
151
152#if defined(UFS2_ONLY)
153#define DIP(field) dp2.field
154#elif defined(UFS1_ONLY)
155#define DIP(field) dp1.field
156#else
157#define DIP(field) fs->fs_magic == FS_UFS1_MAGIC ? dp1.field : dp2.field
158#endif
159
d64b2e33
MD
160static ufs_ino_t inomap;
161static ufs2_daddr_t blkmap, indmap;
162
163static int
164boot2_ufs_init(void)
165{
166 struct fs *fs;
167 size_t n;
168
169 fs = (struct fs *)fsdmadat->sbbuf;
170
171 for (n = 0; sblock_try[n] != -1; n++) {
172 if (dskread(fs, sblock_try[n] / DEV_BSIZE,
173 SBLOCKSIZE / DEV_BSIZE)) {
174 return -1;
175 }
176 if ((
177#if defined(UFS1_ONLY)
178 fs->fs_magic == FS_UFS1_MAGIC
179#elif defined(UFS2_ONLY)
180 (fs->fs_magic == FS_UFS2_MAGIC &&
181 fs->fs_sblockloc == sblock_try[n])
182#else
183 fs->fs_magic == FS_UFS1_MAGIC ||
184 (fs->fs_magic == FS_UFS2_MAGIC &&
185 fs->fs_sblockloc == sblock_try[n])
186#endif
187 ) &&
188 fs->fs_bsize <= MAXBSIZE &&
189 fs->fs_bsize >= sizeof(struct fs))
190 break;
191 }
192 if (sblock_try[n] == -1)
193 return -1;
194 return 0;
195}
196
5ee58eed 197static ssize_t
d64b2e33 198boot2_ufs_read(boot2_ino_t boot2_inode, void *buf, size_t nbyte)
5ee58eed
MD
199{
200#ifndef UFS2_ONLY
201 static struct ufs1_dinode dp1;
202#endif
203#ifndef UFS1_ONLY
204 static struct ufs2_dinode dp2;
205#endif
d64b2e33 206 ufs_ino_t ufs_inode = (ufs_ino_t)boot2_inode;
5ee58eed
MD
207 char *blkbuf;
208 void *indbuf;
209 struct fs *fs;
210 char *s;
211 size_t n, nb, size, off, vboff;
212 ufs_lbn_t lbn;
213 ufs2_daddr_t addr, vbaddr;
5ee58eed
MD
214 u_int u;
215
d64b2e33
MD
216 blkbuf = fsdmadat->blkbuf;
217 indbuf = fsdmadat->indbuf;
218 fs = (struct fs *)fsdmadat->sbbuf;
5ee58eed 219
d64b2e33 220 if (!ufs_inode)
5ee58eed 221 return 0;
d64b2e33 222 if (inomap != ufs_inode) {
5ee58eed 223 n = IPERVBLK(fs);
d64b2e33 224 if (dskread(blkbuf, INO_TO_VBA(fs, n, ufs_inode), DBPERVBLK))
5ee58eed 225 return -1;
d64b2e33 226 n = INO_TO_VBO(n, ufs_inode);
5ee58eed
MD
227#if defined(UFS1_ONLY)
228 dp1 = ((struct ufs1_dinode *)blkbuf)[n];
229#elif defined(UFS2_ONLY)
230 dp2 = ((struct ufs2_dinode *)blkbuf)[n];
231#else
232 if (fs->fs_magic == FS_UFS1_MAGIC)
233 dp1 = ((struct ufs1_dinode *)blkbuf)[n];
234 else
235 dp2 = ((struct ufs2_dinode *)blkbuf)[n];
236#endif
d64b2e33 237 inomap = ufs_inode;
5ee58eed
MD
238 fs_off = 0;
239 blkmap = indmap = 0;
240 }
241 s = buf;
242 size = DIP(di_size);
243 n = size - fs_off;
244 if (nbyte > n)
245 nbyte = n;
246 nb = nbyte;
247 while (nb) {
248 lbn = lblkno(fs, fs_off);
249 off = blkoff(fs, fs_off);
250 if (lbn < NDADDR) {
251 addr = DIP(di_db[lbn]);
252 } else if (lbn < NDADDR + NINDIR(fs)) {
253 n = INDIRPERVBLK(fs);
254 addr = DIP(di_ib[0]);
c7401c3b 255 u = (u_int)(lbn - NDADDR) / n * DBPERVBLK;
5ee58eed
MD
256 vbaddr = fsbtodb(fs, addr) + u;
257 if (indmap != vbaddr) {
258 if (dskread(indbuf, vbaddr, DBPERVBLK))
259 return -1;
260 indmap = vbaddr;
261 }
262 n = (lbn - NDADDR) & (n - 1);
263#if defined(UFS1_ONLY)
264 addr = ((ufs1_daddr_t *)indbuf)[n];
265#elif defined(UFS2_ONLY)
266 addr = ((ufs2_daddr_t *)indbuf)[n];
267#else
268 if (fs->fs_magic == FS_UFS1_MAGIC)
269 addr = ((ufs1_daddr_t *)indbuf)[n];
270 else
271 addr = ((ufs2_daddr_t *)indbuf)[n];
272#endif
273 } else {
274 return -1;
275 }
276 vbaddr = fsbtodb(fs, addr) + (off >> VBLKSHIFT) * DBPERVBLK;
277 vboff = off & VBLKMASK;
278 n = sblksize(fs, size, lbn) - (off & ~VBLKMASK);
279 if (n > VBLKSIZE)
280 n = VBLKSIZE;
281 if (blkmap != vbaddr) {
282 if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT))
283 return -1;
284 blkmap = vbaddr;
285 }
286 n -= vboff;
287 if (n > nb)
288 n = nb;
289 memcpy(s, blkbuf + vboff, n);
290 s += n;
291 fs_off += n;
292 nb -= n;
293 }
294 return nbyte;
295}