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 $ 25 * $DragonFly: src/sys/boot/common/ufsread.c,v 1.5 2008/09/13 11:46:28 corecode Exp $ 26 */ 27 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> 37 #include <vfs/ufs/dir.h> 38 #include "dinode.h" 39 #include "fs.h" 40 41 #if defined(__i386__) || defined(__x86_64__) 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. */ 68 struct ufs_dmadat { 69 #ifdef BOOT2 70 struct boot2_dmadat boot2; 71 #else 72 char secbuf[DEV_BSIZE*4]; /* for MBR/disklabel */ 73 #endif 74 char blkbuf[VBLKSIZE]; /* filesystem blocks */ 75 char indbuf[VBLKSIZE]; /* indir blocks */ 76 char sbbuf[SBLOCKSIZE]; /* superblock */ 77 }; 78 79 #define fsdmadat ((struct ufs_dmadat *)boot2_dmadat) 80 81 #ifndef BOOT2 82 #define boot2_ino_t ufs_ino_t 83 #endif 84 85 static boot2_ino_t boot2_ufs_lookup(const char *); 86 static ssize_t boot2_ufs_read(boot2_ino_t, void *, size_t); 87 static int boot2_ufs_init(void); 88 89 #ifdef BOOT2 90 const struct boot2_fsapi boot2_ufs_api = { 91 .fsinit = boot2_ufs_init, 92 .fslookup = boot2_ufs_lookup, 93 .fsread = boot2_ufs_read 94 }; 95 #endif 96 97 static __inline__ int 98 fsfind(const char *name, ufs_ino_t *ino) 99 { 100 char buf[DEV_BSIZE]; 101 struct direct *d; 102 char *s; 103 ssize_t n; 104 105 fs_off = 0; 106 while ((n = boot2_ufs_read(*ino, buf, DEV_BSIZE)) > 0) 107 for (s = buf; s < buf + DEV_BSIZE;) { 108 d = (void *)s; 109 if (ls) 110 printf("%s ", d->d_name); 111 else if (!strcmp(name, d->d_name)) { 112 *ino = d->d_ino; 113 return d->d_type; 114 } 115 s += d->d_reclen; 116 } 117 if (n != -1 && ls) 118 printf("\n"); 119 return 0; 120 } 121 122 static boot2_ino_t 123 boot2_ufs_lookup(const char *path) 124 { 125 char name[MAXNAMLEN + 1]; 126 const char *s; 127 ufs_ino_t ino; 128 ssize_t n; 129 int dt; 130 131 ino = ROOTINO; 132 dt = DT_DIR; 133 name[0] = '/'; 134 name[1] = '\0'; 135 for (;;) { 136 if (*path == '/') 137 path++; 138 if (!*path) 139 break; 140 for (s = path; *s && *s != '/'; s++); 141 if ((n = s - path) > MAXNAMLEN) 142 return 0; 143 ls = *path == '?' && n == 1 && !*s; 144 memcpy(name, path, n); 145 name[n] = 0; 146 if (dt != DT_DIR) 147 return (0); 148 if ((dt = fsfind(name, &ino)) <= 0) 149 break; 150 path = s; 151 } 152 return dt == DT_REG ? ino : 0; 153 } 154 155 /* 156 * Possible superblock locations ordered from most to least likely. 157 */ 158 static int sblock_try[] = SBLOCKSEARCH; 159 160 #if defined(UFS2_ONLY) 161 #define DIP(field) dp2.field 162 #elif defined(UFS1_ONLY) 163 #define DIP(field) dp1.field 164 #else 165 #define DIP(field) fs->fs_magic == FS_UFS1_MAGIC ? dp1.field : dp2.field 166 #endif 167 168 static ufs_ino_t inomap; 169 static ufs2_daddr_t blkmap, indmap; 170 171 static int 172 boot2_ufs_init(void) 173 { 174 struct fs *fs; 175 size_t n; 176 177 inomap = 0; 178 fs = (struct fs *)fsdmadat->sbbuf; 179 180 for (n = 0; sblock_try[n] != -1; n++) { 181 if (dskread(fs, sblock_try[n] / DEV_BSIZE, 182 SBLOCKSIZE / DEV_BSIZE)) { 183 return -1; 184 } 185 if (( 186 #if defined(UFS1_ONLY) 187 fs->fs_magic == FS_UFS1_MAGIC 188 #elif defined(UFS2_ONLY) 189 (fs->fs_magic == FS_UFS2_MAGIC && 190 fs->fs_sblockloc == sblock_try[n]) 191 #else 192 fs->fs_magic == FS_UFS1_MAGIC || 193 (fs->fs_magic == FS_UFS2_MAGIC && 194 fs->fs_sblockloc == sblock_try[n]) 195 #endif 196 ) && 197 fs->fs_bsize <= MAXBSIZE && 198 fs->fs_bsize >= (int)sizeof(struct fs)) 199 break; 200 } 201 if (sblock_try[n] == -1) 202 return -1; 203 return 0; 204 } 205 206 #ifndef BOOT2 207 int dsk_meta = 0; 208 #endif 209 210 static ssize_t 211 boot2_ufs_read_size(boot2_ino_t boot2_inode, void *buf, size_t nbyte, 212 size_t *fsizep) 213 { 214 #ifndef UFS2_ONLY 215 static struct ufs1_dinode dp1; 216 #endif 217 #ifndef UFS1_ONLY 218 static struct ufs2_dinode dp2; 219 #endif 220 ufs_ino_t ufs_inode = (ufs_ino_t)boot2_inode; 221 char *blkbuf; 222 void *indbuf; 223 struct fs *fs; 224 char *s; 225 size_t n, nb, size, off, vboff; 226 ufs_lbn_t lbn; 227 ufs2_daddr_t addr, vbaddr; 228 u_int u; 229 230 blkbuf = fsdmadat->blkbuf; 231 indbuf = fsdmadat->indbuf; 232 fs = (struct fs *)fsdmadat->sbbuf; 233 234 #ifndef BOOT2 235 /* 236 * Force probe if inode is zero to ensure we have a valid fs, otherwise 237 * when probing multiple paritions, reads from subsequent parititions 238 * will incorrectly succeed. 239 */ 240 if (!dsk_meta || boot2_inode == 0) { 241 inomap = 0; 242 dsk_meta = 0; 243 if (boot2_ufs_init() == 0) 244 dsk_meta++; 245 } 246 #endif 247 248 if (!ufs_inode) 249 return 0; 250 #ifndef BOOT2 251 else if (!dsk_meta) 252 return -1; 253 #endif 254 if (inomap != ufs_inode) { 255 n = IPERVBLK(fs); 256 if (dskread(blkbuf, INO_TO_VBA(fs, n, ufs_inode), DBPERVBLK)) 257 return -1; 258 n = INO_TO_VBO(n, ufs_inode); 259 #if defined(UFS1_ONLY) 260 dp1 = ((struct ufs1_dinode *)blkbuf)[n]; 261 #elif defined(UFS2_ONLY) 262 dp2 = ((struct ufs2_dinode *)blkbuf)[n]; 263 #else 264 if (fs->fs_magic == FS_UFS1_MAGIC) 265 dp1 = ((struct ufs1_dinode *)blkbuf)[n]; 266 else 267 dp2 = ((struct ufs2_dinode *)blkbuf)[n]; 268 #endif 269 inomap = ufs_inode; 270 fs_off = 0; 271 blkmap = indmap = 0; 272 } 273 s = buf; 274 size = DIP(di_size); 275 n = size - fs_off; 276 if (nbyte > n) 277 nbyte = n; 278 nb = nbyte; 279 while (nb) { 280 lbn = lblkno(fs, fs_off); 281 off = blkoff(fs, fs_off); 282 if (lbn < NDADDR) { 283 addr = DIP(di_db[lbn]); 284 } else if (lbn < NDADDR + NINDIR(fs)) { 285 n = INDIRPERVBLK(fs); 286 addr = DIP(di_ib[0]); 287 u = (u_int)(lbn - NDADDR) / n * DBPERVBLK; 288 vbaddr = fsbtodb(fs, addr) + u; 289 if (indmap != vbaddr) { 290 if (dskread(indbuf, vbaddr, DBPERVBLK)) 291 return -1; 292 indmap = vbaddr; 293 } 294 n = (lbn - NDADDR) & (n - 1); 295 #if defined(UFS1_ONLY) 296 addr = ((ufs1_daddr_t *)indbuf)[n]; 297 #elif defined(UFS2_ONLY) 298 addr = ((ufs2_daddr_t *)indbuf)[n]; 299 #else 300 if (fs->fs_magic == FS_UFS1_MAGIC) 301 addr = ((ufs1_daddr_t *)indbuf)[n]; 302 else 303 addr = ((ufs2_daddr_t *)indbuf)[n]; 304 #endif 305 } else { 306 return -1; 307 } 308 vbaddr = fsbtodb(fs, addr) + (off >> VBLKSHIFT) * DBPERVBLK; 309 vboff = off & VBLKMASK; 310 n = sblksize(fs, (ssize_t)size, lbn) - (off & ~VBLKMASK); 311 if (n > VBLKSIZE) 312 n = VBLKSIZE; 313 if (blkmap != vbaddr) { 314 if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT)) 315 return -1; 316 blkmap = vbaddr; 317 } 318 n -= vboff; 319 if (n > nb) 320 n = nb; 321 memcpy(s, blkbuf + vboff, n); 322 s += n; 323 fs_off += n; 324 nb -= n; 325 } 326 327 if (fsizep != NULL) 328 *fsizep = size; 329 330 return nbyte; 331 } 332 333 static ssize_t 334 boot2_ufs_read(boot2_ino_t boot2_inode, void *buf, size_t nbyte) 335 { 336 return boot2_ufs_read_size(boot2_inode, buf, nbyte, NULL); 337 } 338