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 printf("%s: not a directory.\n", name); 148 return (0); 149 } 150 if ((dt = fsfind(name, &ino)) <= 0) 151 break; 152 path = s; 153 } 154 return dt == DT_REG ? ino : 0; 155 } 156 157 /* 158 * Possible superblock locations ordered from most to least likely. 159 */ 160 static int sblock_try[] = SBLOCKSEARCH; 161 162 #if defined(UFS2_ONLY) 163 #define DIP(field) dp2.field 164 #elif defined(UFS1_ONLY) 165 #define DIP(field) dp1.field 166 #else 167 #define DIP(field) fs->fs_magic == FS_UFS1_MAGIC ? dp1.field : dp2.field 168 #endif 169 170 static ufs_ino_t inomap; 171 static ufs2_daddr_t blkmap, indmap; 172 173 static int 174 boot2_ufs_init(void) 175 { 176 struct fs *fs; 177 size_t n; 178 179 inomap = 0; 180 fs = (struct fs *)fsdmadat->sbbuf; 181 182 for (n = 0; sblock_try[n] != -1; n++) { 183 if (dskread(fs, sblock_try[n] / DEV_BSIZE, 184 SBLOCKSIZE / DEV_BSIZE)) { 185 return -1; 186 } 187 if (( 188 #if defined(UFS1_ONLY) 189 fs->fs_magic == FS_UFS1_MAGIC 190 #elif defined(UFS2_ONLY) 191 (fs->fs_magic == FS_UFS2_MAGIC && 192 fs->fs_sblockloc == sblock_try[n]) 193 #else 194 fs->fs_magic == FS_UFS1_MAGIC || 195 (fs->fs_magic == FS_UFS2_MAGIC && 196 fs->fs_sblockloc == sblock_try[n]) 197 #endif 198 ) && 199 fs->fs_bsize <= MAXBSIZE && 200 fs->fs_bsize >= (int)sizeof(struct fs)) 201 break; 202 } 203 if (sblock_try[n] == -1) 204 return -1; 205 return 0; 206 } 207 208 #ifndef BOOT2 209 int dsk_meta = 0; 210 #endif 211 212 static ssize_t 213 boot2_ufs_read_size(boot2_ino_t boot2_inode, void *buf, size_t nbyte, 214 size_t *fsizep) 215 { 216 #ifndef UFS2_ONLY 217 static struct ufs1_dinode dp1; 218 #endif 219 #ifndef UFS1_ONLY 220 static struct ufs2_dinode dp2; 221 #endif 222 ufs_ino_t ufs_inode = (ufs_ino_t)boot2_inode; 223 char *blkbuf; 224 void *indbuf; 225 struct fs *fs; 226 char *s; 227 size_t n, nb, size, off, vboff; 228 ufs_lbn_t lbn; 229 ufs2_daddr_t addr, vbaddr; 230 u_int u; 231 232 blkbuf = fsdmadat->blkbuf; 233 indbuf = fsdmadat->indbuf; 234 fs = (struct fs *)fsdmadat->sbbuf; 235 236 #ifndef BOOT2 237 /* 238 * Force probe if inode is zero to ensure we have a valid fs, otherwise 239 * when probing multiple paritions, reads from subsequent parititions 240 * will incorrectly succeed. 241 */ 242 if (!dsk_meta || boot2_inode == 0) { 243 inomap = 0; 244 dsk_meta = 0; 245 if (boot2_ufs_init() == 0) 246 dsk_meta++; 247 } 248 #endif 249 250 if (!ufs_inode) 251 return 0; 252 #ifndef BOOT2 253 else if (!dsk_meta) 254 return -1; 255 #endif 256 if (inomap != ufs_inode) { 257 n = IPERVBLK(fs); 258 if (dskread(blkbuf, INO_TO_VBA(fs, n, ufs_inode), DBPERVBLK)) 259 return -1; 260 n = INO_TO_VBO(n, ufs_inode); 261 #if defined(UFS1_ONLY) 262 dp1 = ((struct ufs1_dinode *)blkbuf)[n]; 263 #elif defined(UFS2_ONLY) 264 dp2 = ((struct ufs2_dinode *)blkbuf)[n]; 265 #else 266 if (fs->fs_magic == FS_UFS1_MAGIC) 267 dp1 = ((struct ufs1_dinode *)blkbuf)[n]; 268 else 269 dp2 = ((struct ufs2_dinode *)blkbuf)[n]; 270 #endif 271 inomap = ufs_inode; 272 fs_off = 0; 273 blkmap = indmap = 0; 274 } 275 s = buf; 276 size = DIP(di_size); 277 n = size - fs_off; 278 if (nbyte > n) 279 nbyte = n; 280 nb = nbyte; 281 while (nb) { 282 lbn = lblkno(fs, fs_off); 283 off = blkoff(fs, fs_off); 284 if (lbn < NDADDR) { 285 addr = DIP(di_db[lbn]); 286 } else if (lbn < NDADDR + NINDIR(fs)) { 287 n = INDIRPERVBLK(fs); 288 addr = DIP(di_ib[0]); 289 u = (u_int)(lbn - NDADDR) / n * DBPERVBLK; 290 vbaddr = fsbtodb(fs, addr) + u; 291 if (indmap != vbaddr) { 292 if (dskread(indbuf, vbaddr, DBPERVBLK)) 293 return -1; 294 indmap = vbaddr; 295 } 296 n = (lbn - NDADDR) & (n - 1); 297 #if defined(UFS1_ONLY) 298 addr = ((ufs1_daddr_t *)indbuf)[n]; 299 #elif defined(UFS2_ONLY) 300 addr = ((ufs2_daddr_t *)indbuf)[n]; 301 #else 302 if (fs->fs_magic == FS_UFS1_MAGIC) 303 addr = ((ufs1_daddr_t *)indbuf)[n]; 304 else 305 addr = ((ufs2_daddr_t *)indbuf)[n]; 306 #endif 307 } else { 308 return -1; 309 } 310 vbaddr = fsbtodb(fs, addr) + (off >> VBLKSHIFT) * DBPERVBLK; 311 vboff = off & VBLKMASK; 312 n = sblksize(fs, (ssize_t)size, lbn) - (off & ~VBLKMASK); 313 if (n > VBLKSIZE) 314 n = VBLKSIZE; 315 if (blkmap != vbaddr) { 316 if (dskread(blkbuf, vbaddr, n >> DEV_BSHIFT)) 317 return -1; 318 blkmap = vbaddr; 319 } 320 n -= vboff; 321 if (n > nb) 322 n = nb; 323 memcpy(s, blkbuf + vboff, n); 324 s += n; 325 fs_off += n; 326 nb -= n; 327 } 328 329 if (fsizep != NULL) 330 *fsizep = size; 331 332 return nbyte; 333 } 334 335 static ssize_t 336 boot2_ufs_read(boot2_ino_t boot2_inode, void *buf, size_t nbyte) 337 { 338 return boot2_ufs_read_size(boot2_inode, buf, nbyte, NULL); 339 } 340