1/* $NetBSD: lfs_inode.c,v 1.29 2024/05/12 23:55:57 msaitoh Exp $ */ 2 3/*- 4 * Copyright (c) 1980, 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\ 35 The Regents of the University of California. All rights reserved."); 36#endif /* not lint */ 37 38#ifndef lint 39#if 0 40static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; 41#else 42__RCSID("$NetBSD: lfs_inode.c,v 1.29 2024/05/12 23:55:57 msaitoh Exp $"); 43#endif 44#endif /* not lint */ 45 46#include <sys/param.h> 47#include <sys/time.h> 48#include <sys/stat.h> 49#include <sys/mount.h> 50 51#include <ctype.h> 52#include <errno.h> 53#include <fcntl.h> 54#include <fts.h> 55#include <stdio.h> 56#include <string.h> 57#include <unistd.h> 58 59#include "dump.h" 60#undef di_inumber 61 62#define HASDUMPEDFILE 0x1 63#define HASSUBDIRS 0x2 64 65struct lfs *sblock; 66 67int is_ufs2 = 0; 68 69/* 70 * Read the superblock from disk, and check its magic number. 71 * Determine whether byte-swapping needs to be done on this filesystem. 72 */ 73int 74fs_read_sblock(char *superblock) 75{ 76 /* 77 * XXX this should not be assuming that the in-memory 78 * superblock has the on-disk superblock at the front of the 79 * structure. 80 */ 81 union { 82 char tbuf[LFS_SBPAD]; 83 struct lfs lfss; 84 } u; 85 86 off_t sboff = LFS_LABELPAD; 87 88 sblock = (struct lfs *)superblock; 89 while(1) { 90 rawread(sboff, (char *) sblock, LFS_SBPAD); 91 switch (sblock->lfs_dlfs_u.u_32.dlfs_magic) { 92 case LFS_MAGIC: 93 sblock->lfs_is64 = false; 94 sblock->lfs_dobyteswap = false; 95 break; 96 case LFS_MAGIC_SWAPPED: 97 sblock->lfs_is64 = false; 98 sblock->lfs_dobyteswap = true; 99 break; 100 case LFS64_MAGIC: 101 sblock->lfs_is64 = true; 102 sblock->lfs_dobyteswap = false; 103 break; 104 case LFS64_MAGIC_SWAPPED: 105 sblock->lfs_is64 = true; 106 sblock->lfs_dobyteswap = true; 107 break; 108 default: 109 quit("bad sblock magic number\n"); 110 break; 111 } 112 if (lfs_fsbtob(sblock, (off_t)lfs_sb_getsboff(sblock, 0)) != sboff) { 113 sboff = lfs_fsbtob(sblock, (off_t)lfs_sb_getsboff(sblock, 0)); 114 continue; 115 } 116 break; 117 } 118 119 /* 120 * Read the secondary and take the older of the two 121 */ 122 rawread(lfs_fsbtob(sblock, (off_t)lfs_sb_getsboff(sblock, 1)), u.tbuf, 123 sizeof(u.tbuf)); 124 125 if (u.lfss.lfs_dlfs_u.u_32.dlfs_magic != 126 sblock->lfs_dlfs_u.u_32.dlfs_magic) { 127 msg("Warning: secondary superblock at 0x%" PRIx64 " mismatched or wrong magic\n", 128 LFS_FSBTODB(sblock, (off_t)lfs_sb_getsboff(sblock, 1))); 129 } else { 130 u.lfss.lfs_is64 = sblock->lfs_is64; 131 u.lfss.lfs_dobyteswap = sblock->lfs_dobyteswap; 132 133 if (lfs_sb_getversion(sblock) > 1) { 134 if (lfs_sb_getserial(&u.lfss) < lfs_sb_getserial(sblock)) { 135 memcpy(sblock, u.tbuf, sizeof(u.tbuf)); 136 sboff = lfs_fsbtob(sblock, (off_t)lfs_sb_getsboff(sblock, 1)); 137 } 138 } else { 139 if (lfs_sb_getotstamp(&u.lfss) < lfs_sb_getotstamp(sblock)) { 140 memcpy(sblock, u.tbuf, sizeof(u.tbuf)); 141 sboff = lfs_fsbtob(sblock, (off_t)lfs_sb_getsboff(sblock, 1)); 142 } 143 } 144 } 145 if (sboff != LFS_SBPAD) { 146 msg("Using superblock at alternate location 0x%lx\n", 147 (unsigned long)(btodb(sboff))); 148 } 149 150 /* ugh */ 151 is_ufs2 = sblock->lfs_is64; 152 153 /* 154 * XXX for now dump won't work on lfs64 because of the 64-bit 155 * inodes in directories. dump needs more abstraction. 156 */ 157 if (sblock->lfs_is64) { 158 quit("LFS64 directory entries not supported yet"); 159 } 160 161 return sblock->lfs_dobyteswap; 162} 163 164/* 165 * Fill in the ufsi struct, as well as the maxino and dev_bsize global 166 * variables. 167 */ 168struct ufsi * 169fs_parametrize(void) 170{ 171 static struct ufsi ufsi; 172 173 spcl.c_flags = iswap32(iswap32(spcl.c_flags) | DR_NEWINODEFMT); 174 175 ufsi.ufs_dsize = LFS_FSBTODB(sblock, lfs_sb_getsize(sblock)); 176 if (lfs_sb_getversion(sblock) == 1) 177 ufsi.ufs_dsize = lfs_sb_getsize(sblock) >> lfs_sb_getblktodb(sblock); 178 ufsi.ufs_bsize = lfs_sb_getbsize(sblock); 179 ufsi.ufs_bshift = lfs_sb_getbshift(sblock); 180 ufsi.ufs_fsize = lfs_sb_getfsize(sblock); 181 ufsi.ufs_frag = lfs_sb_getfrag(sblock); 182 ufsi.ufs_fsatoda = lfs_sb_getfsbtodb(sblock); 183 if (lfs_sb_getversion(sblock) == 1) 184 ufsi.ufs_fsatoda = 0; 185 ufsi.ufs_nindir = lfs_sb_getnindir(sblock); 186 ufsi.ufs_inopb = lfs_sb_getinopb(sblock); 187 ufsi.ufs_maxsymlinklen = lfs_sb_getmaxsymlinklen(sblock); 188 ufsi.ufs_bmask = ~(lfs_sb_getbmask(sblock)); 189 ufsi.ufs_qbmask = lfs_sb_getbmask(sblock); 190 ufsi.ufs_fmask = ~(lfs_sb_getffmask(sblock)); 191 ufsi.ufs_qfmask = lfs_sb_getffmask(sblock); 192 193 dev_bsize = lfs_sb_getbsize(sblock) >> lfs_sb_getblktodb(sblock); 194 195 return &ufsi; 196} 197 198ino_t 199fs_maxino(void) 200{ 201 return ((getino(LFS_IFILE_INUM)->dp1.di_size 202 - (lfs_sb_getcleansz(sblock) + lfs_sb_getsegtabsz(sblock)) 203 * lfs_sb_getbsize(sblock)) 204 / lfs_sb_getbsize(sblock)) * lfs_sb_getifpb(sblock) - 1; 205} 206 207void 208fs_mapinodes(ino_t maxino, u_int64_t *tapesz, int *anydirskipped) 209{ 210 ino_t ino; 211 212 for (ino = ULFS_ROOTINO; ino < maxino; ino++) 213 mapfileino(ino, tapesz, anydirskipped); 214} 215 216/* 217 * XXX KS - I know there's a better way to do this. 218 */ 219#define BASE_SINDIR (ULFS_NDADDR) 220#define BASE_DINDIR (ULFS_NDADDR+LFS_NINDIR(fs)) 221#define BASE_TINDIR (ULFS_NDADDR+LFS_NINDIR(fs)+LFS_NINDIR(fs)*LFS_NINDIR(fs)) 222 223#define D_UNITS (LFS_NINDIR(fs)) 224#define T_UNITS (LFS_NINDIR(fs)*LFS_NINDIR(fs)) 225 226static daddr_t 227lfs_bmap(struct lfs *fs, union lfs_dinode *idinode, daddr_t lbn) 228{ 229 daddr_t residue, up; 230 int off=0; 231 char bp[MAXBSIZE]; 232 233 up = UNASSIGNED; /* XXXGCC -Wuninitialized [sh3] */ 234 235 if(lbn > 0 && lbn > lfs_lblkno(fs, lfs_dino_getsize(fs, idinode))) { 236 return UNASSIGNED; 237 } 238 /* 239 * Indirect blocks: if it is a first-level indirect, pull its 240 * address from the inode; otherwise, call ourselves to find the 241 * address of the parent indirect block, and load that to find 242 * the desired address. 243 */ 244 if(lbn < 0) { 245 lbn *= -1; 246 if (lbn == ULFS_NDADDR) { 247 /* printf("lbn %d: single indir base\n", -lbn); */ 248 return lfs_dino_getib(fs, idinode, 0); /* single indirect */ 249 } else if(lbn == BASE_DINDIR+1) { 250 /* printf("lbn %d: double indir base\n", -lbn); */ 251 return lfs_dino_getib(fs, idinode, 1); /* double indirect */ 252 } else if(lbn == BASE_TINDIR+2) { 253 /* printf("lbn %d: triple indir base\n", -lbn); */ 254 return lfs_dino_getib(fs, idinode, 2); /* triple indirect */ 255 } 256 257 /* 258 * Find the immediate parent. This is essentially finding the 259 * residue of modulus, and then rounding accordingly. 260 */ 261 residue = (lbn-ULFS_NDADDR) % LFS_NINDIR(fs); 262 if(residue == 1) { 263 /* Double indirect. Parent is the triple. */ 264 up = lfs_dino_getib(fs, idinode, 2); 265 off = (lbn-2-BASE_TINDIR)/(LFS_NINDIR(fs)*LFS_NINDIR(fs)); 266 if(up == UNASSIGNED || up == LFS_UNUSED_DADDR) 267 return UNASSIGNED; 268 /* printf("lbn %d: parent is the triple\n", -lbn); */ 269 bread(LFS_FSBTODB(sblock, up), bp, lfs_sb_getbsize(sblock)); 270 return lfs_iblock_get(fs, bp, off); 271 } else /* residue == 0 */ { 272 /* Single indirect. Two cases. */ 273 if(lbn < BASE_TINDIR) { 274 /* Parent is the double, simple */ 275 up = -(BASE_DINDIR) - 1; 276 off = (lbn-BASE_DINDIR) / D_UNITS; 277 /* printf("lbn %d: parent is %d/%d\n", -lbn, up,off); */ 278 } else { 279 /* Ancestor is the triple, more complex */ 280 up = ((lbn-BASE_TINDIR) / T_UNITS) 281 * T_UNITS + BASE_TINDIR + 1; 282 off = (lbn/D_UNITS) - (up/D_UNITS); 283 up = -up; 284 /* printf("lbn %d: parent is %d/%d\n", -lbn, up,off); */ 285 } 286 } 287 } else { 288 /* Direct block. Its parent must be a single indirect. */ 289 if (lbn < ULFS_NDADDR) 290 return lfs_dino_getdb(fs, idinode, lbn); 291 else { 292 /* Parent is an indirect block. */ 293 up = -(((lbn-ULFS_NDADDR) / D_UNITS) * D_UNITS + ULFS_NDADDR); 294 off = (lbn-ULFS_NDADDR) % D_UNITS; 295 /* printf("lbn %d: parent is %d/%d\n", lbn,up,off); */ 296 } 297 } 298 up = lfs_bmap(fs,idinode,up); 299 if(up == UNASSIGNED || up == LFS_UNUSED_DADDR) 300 return UNASSIGNED; 301 bread(LFS_FSBTODB(sblock, up), bp, lfs_sb_getbsize(sblock)); 302 return lfs_iblock_get(fs, bp, off); 303} 304 305static IFILE * 306lfs_ientry(ino_t ino) 307{ 308 static char ifileblock[MAXBSIZE]; 309 static daddr_t ifblkno; 310 daddr_t lbn; 311 daddr_t blkno; 312 union dinode *dp; 313 union lfs_dinode *ldp; 314 unsigned index; 315 316 lbn = ino/lfs_sb_getifpb(sblock) + lfs_sb_getcleansz(sblock) + lfs_sb_getsegtabsz(sblock); 317 dp = getino(LFS_IFILE_INUM); 318 /* XXX this is foolish */ 319 if (sblock->lfs_is64) { 320 ldp = (union lfs_dinode *)&dp->dlp64; 321 } else { 322 ldp = (union lfs_dinode *)&dp->dlp32; 323 } 324 blkno = lfs_bmap(sblock, ldp, lbn); 325 if (blkno != ifblkno) 326 bread(LFS_FSBTODB(sblock, blkno), ifileblock, 327 lfs_sb_getbsize(sblock)); 328 index = ino % lfs_sb_getifpb(sblock); 329 if (sblock->lfs_is64) { 330 return (IFILE *) &((IFILE64 *)ifileblock)[index]; 331 } else if (lfs_sb_getversion(sblock) > 1) { 332 return (IFILE *) &((IFILE32 *)ifileblock)[index]; 333 } else { 334 return (IFILE *) &((IFILE_V1 *)ifileblock)[index]; 335 } 336} 337 338/* Search a block for a specific dinode. */ 339static union lfs_dinode * 340lfs_ifind(struct lfs *fs, ino_t ino, void *block) 341{ 342 union lfs_dinode *dip; 343 unsigned i, num; 344 345 num = LFS_INOPB(fs); 346 for (i = num; i-- > 0; ) { 347 dip = DINO_IN_BLOCK(fs, block, i); 348 if (lfs_dino_getinumber(fs, dip) == ino) 349 return dip; 350 } 351 return NULL; 352} 353 354union dinode * 355getino(ino_t inum) 356{ 357 static daddr_t inoblkno; 358 daddr_t blkno; 359 static union { 360 char space[MAXBSIZE]; 361 struct lfs64_dinode u_64[MAXBSIZE/sizeof(struct lfs64_dinode)]; 362 struct lfs32_dinode u_32[MAXBSIZE/sizeof(struct lfs32_dinode)]; 363 } inoblock; 364 static union dinode ifile_dinode; /* XXX fill this in */ 365 static union dinode empty_dinode; /* Always stays zeroed */ 366 union lfs_dinode *dp; 367 ino_t inum2; 368 369 if (inum == LFS_IFILE_INUM) { 370 /* Load the ifile inode if not already */ 371 inum2 = sblock->lfs_is64 ? 372 ifile_dinode.dlp64.di_inumber : 373 ifile_dinode.dlp32.di_inumber; 374 if (inum2 == 0) { 375 blkno = lfs_sb_getidaddr(sblock); 376 bread(LFS_FSBTODB(sblock, blkno), inoblock.space, 377 (int)lfs_sb_getbsize(sblock)); 378 dp = lfs_ifind(sblock, inum, inoblock.space); 379 /* Structure copy */ 380 if (sblock->lfs_is64) { 381 ifile_dinode.dlp64 = dp->u_64; 382 } else { 383 ifile_dinode.dlp32 = dp->u_32; 384 } 385 } 386 return &ifile_dinode; 387 } 388 389 curino = inum; 390 blkno = lfs_if_getdaddr(sblock, lfs_ientry(inum)); 391 if(blkno == LFS_UNUSED_DADDR) 392 return &empty_dinode; 393 394 if(blkno != inoblkno) { 395 bread(LFS_FSBTODB(sblock, blkno), inoblock.space, 396 (int)lfs_sb_getbsize(sblock)); 397 } 398 return (void *)lfs_ifind(sblock, inum, inoblock.space); 399} 400 401/* 402 * Tell the filesystem not to overwrite any currently dirty segments 403 * until we are finished. (It may still write into clean segments, of course, 404 * since we're not using those.) This is only called when dump_lfs is called 405 * with -X, i.e. we are working on a mounted filesystem. 406 */ 407static int root_fd = -1; 408char *wrap_mpname; 409 410int 411lfs_wrap_stop(char *mpname) 412{ 413 int waitfor = 0; 414 415 root_fd = open(mpname, O_RDONLY, 0); 416 if (root_fd < 0) 417 return -1; 418 wrap_mpname = mpname; 419 fcntl(root_fd, LFCNREWIND, -1); /* Ignore return value */ 420 if (fcntl(root_fd, LFCNWRAPSTOP, &waitfor) < 0) { 421 perror("LFCNWRAPSTOP"); 422 return -1; 423 } 424 msg("Disabled log wrap on %s\n", mpname); 425 return 0; 426} 427 428/* 429 * Allow the filesystem to continue normal operation. 430 * This would happen anyway when we exit; we do it explicitly here 431 * to show the message, for the user's benefit. 432 */ 433void 434lfs_wrap_go(void) 435{ 436 int waitfor = 0; 437 438 if (root_fd < 0) 439 return; 440 441 fcntl(root_fd, LFCNWRAPGO, &waitfor); 442 close(root_fd); 443 root_fd = -1; 444 msg("Re-enabled log wrap on %s\n", wrap_mpname); 445} 446