/* * changed so that it confirms to ext2fs_check_dir_entry */ static int ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de, int entryoffsetinblock) { struct ufsmount *ump = VFSTOUFS(dp->v_mount); int dirblksiz = ump->um_dirblksiz; const char *error_msg = NULL; int reclen = fs2h16(de->e2d_reclen); int namlen = de->e2d_namlen; if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */ error_msg = "rec_len is smaller than minimal"; else if (reclen % 4 != 0) error_msg = "rec_len % 4 != 0"; else if (namlen > EXT2FS_MAXNAMLEN) error_msg = "namlen > EXT2FS_MAXNAMLEN"; else if (reclen < EXT2FS_DIRSIZ(namlen)) error_msg = "reclen is too small for name_len"; else if (entryoffsetinblock + reclen > dirblksiz) error_msg = "directory entry across blocks"; else if (fs2h32(de->e2d_ino) > VTOI(dp)->i_e2fs->e2fs.e2fs_icount) error_msg = "inode out of bounds"; if (error_msg != NULL) { printf( "bad directory entry: %s\n" "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n", error_msg, entryoffsetinblock, (unsigned long) fs2h32(de->e2d_ino), reclen, namlen); panic("ext2fs_dirbadentry"); } return error_msg == NULL ? 0 : 1; }
/* * Attempt to expand the size of a directory */ static int expanddir(struct ext2fs_dinode *dp, char *name) { daddr_t lastbn, newblk; struct bufarea *bp; char *firstblk; lastbn = lblkno(&sblock, inosize(dp)); if (lastbn >= NDADDR - 1 || fs2h32(dp->e2di_blocks[lastbn]) == 0 || inosize(dp) == 0) { return (0); } if ((newblk = allocblk()) == 0) { return (0); } dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn]; dp->e2di_blocks[lastbn] = h2fs32(newblk); inossize(dp, inosize(dp) + sblock.e2fs_bsize); dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) + 1); bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), sblock.e2fs_bsize); if (bp->b_errs) goto bad; if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) err(8, "cannot allocate first block"); memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize); bp = getdirblk(newblk, sblock.e2fs_bsize); if (bp->b_errs) { free(firstblk); goto bad; } memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize); free(firstblk); dirty(bp); bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), sblock.e2fs_bsize); if (bp->b_errs) goto bad; emptydir.dot_reclen = h2fs16(sblock.e2fs_bsize); memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir); pwarn("NO SPACE LEFT IN %s", name); if (preen) printf(" (EXPANDED)\n"); else if (reply("EXPAND") == 0) goto bad; dirty(bp); inodirty(); return (1); bad: dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1]; dp->e2di_blocks[lastbn + 1] = 0; inossize(dp, inosize(dp) - sblock.e2fs_bsize); dp->e2di_nblock = h2fs32(fs2h32(dp->e2di_nblock) - 1); freeblk(newblk); return (0); }
__compactcall void ext2fs_ls(struct open_file *f, const char *pattern) { struct file *fp = (struct file *)f->f_fsdata; size_t block_size = fp->f_fs->e2fs_bsize; char *buf; size_t buf_size; lsentry_t *names = NULL; fp->f_seekp = 0; while (fp->f_seekp < (off_t)fp->f_di.e2di_size) { struct ext2fs_direct *dp, *edp; int rc = buf_read_file(f, &buf, &buf_size); if (rc) goto out; if (buf_size != block_size || buf_size == 0) goto out; dp = (struct ext2fs_direct *)buf; edp = (struct ext2fs_direct *)(buf + buf_size); for (; dp < edp; dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) { const char *t; if (fs2h16(dp->e2d_reclen) <= 0) goto out; if (fs2h32(dp->e2d_ino) == 0) continue; if (dp->e2d_type >= NELEM(typestr) || !(t = typestr[dp->e2d_type])) { /* * This does not handle "old" * filesystems properly. On little * endian machines, we get a bogus * type name if the namlen matches a * valid type identifier. We could * check if we read namlen "0" and * handle this case specially, if * there were a pressing need... */ printf("bad dir entry\n"); goto out; } lsadd(&names, pattern, dp->e2d_name, strlen(dp->e2d_name), fs2h32(dp->e2d_ino), t); } fp->f_seekp += buf_size; } lsprint(names); out: lsfree(names); }
u_int64_t inosize(struct ext2fs_dinode *dp) { u_int64_t size = fs2h32(dp->e2di_size); if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG) size |= (u_int64_t)fs2h32(dp->e2di_size_high) << 32; if (size > INT32_MAX) (void)setlarge(); return size; }
/* * Search a directory for a name and return its * inode number. */ static int search_directory(const char *name, int length, struct open_file *f, ino32_t *inumber_p) { struct file *fp = (struct file *)f->f_fsdata; struct mfs_sblock *fs = fp->f_fs; struct mfs_direct *dp; struct mfs_direct *dbuf; size_t buf_size; int namlen; int rc; fp->f_seekp = 0; while (fp->f_seekp < (off_t)fp->f_di.mdi_size) { rc = buf_read_file(f, (void *)&dbuf, &buf_size); if (rc) return rc; if (buf_size == 0) return EIO; /* XXX we assume, that buf_read_file reads an fs block and * doesn't truncate buffer. Currently i_size in MFS doesn't * the same as size of allocated blocks, it makes buf_read_file * to truncate buf_size. */ if (buf_size < fs->mfs_block_size) buf_size = fs->mfs_block_size; for (dp = dbuf; dp < &dbuf[NR_DIR_ENTRIES(fs)]; dp++) { char *cp; if (fs2h32(dp->mfsd_ino) == (ino32_t) 0) continue; /* Compute the length of the name */ cp = memchr(dp->mfsd_name, '\0', sizeof(dp->mfsd_name)); if (cp == NULL) namlen = sizeof(dp->mfsd_name); else namlen = cp - (dp->mfsd_name); if (namlen == length && !memcmp(name, dp->mfsd_name, length)) { /* found entry */ *inumber_p = fs2h32(dp->mfsd_ino); return 0; } } fp->f_seekp += buf_size; } return ENOENT; }
/* * Select the desired position for the next block in a file. The file is * logically divided into sections. The first section is composed of the * direct blocks. Each additional section contains fs_maxbpg blocks. * * If no blocks have been allocated in the first section, the policy is to * request a block in the same cylinder group as the inode that describes * the file. Otherwise, the policy is to try to allocate the blocks * contigously. The two fields of the ext2 inode extension (see * ufs/ufs/inode.h) help this. */ daddr_t ext2fs_blkpref(struct inode *ip, int32_t lbn, int indx, int32_t *bap) { struct m_ext2fs *fs; int cg, i; fs = ip->i_e2fs; /* * if we are doing contigous lbn allocation, try to alloc blocks * contigously on disk */ if ( ip->i_e2fs_last_blk && lbn == ip->i_e2fs_last_lblk + 1) { return ip->i_e2fs_last_blk + 1; } /* * bap, if provided, gives us a list of blocks to which we want to * stay close */ if (bap) { for (i = indx; i >= 0 ; i--) { if (bap[i]) { return fs2h32(bap[i]) + 1; } } } /* fall back to the first block of the cylinder containing the inode */ cg = ino_to_cg(fs, ip->i_number); return fs->e2fs.e2fs_bpg * cg + fs->e2fs.e2fs_first_dblock + 1; }
/* * the problem that is tackled below is the fact that FFS * includes the terminating zero on disk while EXT2FS doesn't * this implies that we need to introduce some padding. * For instance, a filename "sbin" has normally a reclen 12 * in EXT2, but 16 in FFS. * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...' * If it wasn't for that, the complete ufs code for directories would * have worked w/o changes (except for the difference in DIRBLKSIZ) */ static void ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, struct dirent *ffsdir) { memset(ffsdir, 0, sizeof(struct dirent)); ffsdir->d_fileno = fs2h32(e2dir->e2d_ino); ffsdir->d_namlen = e2dir->e2d_namlen; ffsdir->d_type = DT_UNKNOWN; /* don't know more here */ #ifdef DIAGNOSTIC #if MAXNAMLEN < E2FS_MAXNAMLEN /* * we should handle this more gracefully ! */ if (e2dir->e2d_namlen > MAXNAMLEN) panic("ext2fs: e2dir->e2d_namlen"); #endif #endif strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen); /* Godmar thinks: since e2dir->e2d_reclen can be big and means nothing anyway, we compute our own reclen according to what we think is right */ ffsdir->d_reclen = _DIRENT_SIZE(ffsdir); }
/* * Verify that a directory entry is valid. * This is a superset of the checks made in the kernel. */ int dircheck(struct inodesc *idesc, struct ext2fs_direct *dp) { int size; char *cp; int spaceleft; u_int16_t reclen = fs2h16(dp->e2d_reclen); spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); if (fs2h32(dp->e2d_ino) > maxino || reclen == 0 || reclen > spaceleft || (reclen & 0x3) != 0) return (0); if (dp->e2d_ino == 0) return (1); if (sblock.e2fs.e2fs_rev < E2FS_REV1 || (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) == 0) if (dp->e2d_type != 0) return (1); size = EXT2FS_DIRSIZ(dp->e2d_namlen); if (reclen < size || idesc->id_filesize < size /* || dp->e2d_namlen > EXT2FS_MAXNAMLEN */) return (0); for (cp = dp->e2d_name, size = 0; size < dp->e2d_namlen; size++) if (*cp == '\0' || (*cp++ == '/')) return (0); return (1); }
/* * the problem that is tackled below is the fact that FFS * includes the terminating zero on disk while EXT2FS doesn't * this implies that we need to introduce some padding. * For instance, a filename "sbin" has normally a reclen 12 * in EXT2, but 16 in FFS. * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...' * If it wasn't for that, the complete ufs code for directories would * have worked w/o changes (except for the difference in DIRBLKSIZ) */ static void ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, struct dirent *ffsdir) { memset(ffsdir, 0, sizeof(struct dirent)); ffsdir->d_fileno = fs2h32(e2dir->e2d_ino); ffsdir->d_namlen = e2dir->e2d_namlen; ffsdir->d_type = DT_UNKNOWN; /* don't know more here */ #ifdef DIAGNOSTIC /* * XXX Rigth now this can't happen, but if one day * MAXNAMLEN != E2FS_MAXNAMLEN we should handle this more gracefully ! */ /* XXX: e2d_namlen is to small for such comparison if (e2dir->e2d_namlen > MAXNAMLEN) panic("ext2fs: e2dir->e2d_namlen"); */ #endif strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen); /* Godmar thinks: since e2dir->e2d_reclen can be big and means nothing anyway, we compute our own reclen according to what we think is right */ ffsdir->d_reclen = DIRENT_SIZE(ffsdir); }
/* * allocate a new directory */ int allocdir(ino_t parent, ino_t request, int mode) { ino_t ino; struct ext2fs_dinode *dp; struct bufarea *bp; struct ext2fs_dirtemplate *dirp; ino = allocino(request, IFDIR|mode); dirhead.dot_reclen = h2fs16(12); /* XXX */ dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */ dirhead.dot_namlen = 1; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) dirhead.dot_type = EXT2_FT_DIR; else dirhead.dot_type = 0; dirhead.dotdot_namlen = 2; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) dirhead.dotdot_type = EXT2_FT_DIR; else dirhead.dotdot_type = 0; dirp = &dirhead; dirp->dot_ino = h2fs32(ino); dirp->dotdot_ino = h2fs32(parent); dp = ginode(ino); bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize); if (bp->b_errs) { freeino(ino); return (0); } memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate)); dirty(bp); dp->e2di_nlink = h2fs16(2); inodirty(); if (ino == EXT2_ROOTINO) { lncntp[ino] = fs2h16(dp->e2di_nlink); cacheino(dp, ino); return(ino); } if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { freeino(ino); return (0); } cacheino(dp, ino); statemap[ino] = statemap[parent]; if (statemap[ino] == DSTATE) { lncntp[ino] = fs2h16(dp->e2di_nlink); lncntp[parent]++; } dp = ginode(parent); dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1); inodirty(); return (ino); }
/* * Search a directory for a name and return its * inode number. */ static int search_directory(const char *name, int length, struct open_file *f, ino32_t *inumber_p) { struct file *fp = (struct file *)f->f_fsdata; struct ext2fs_direct *dp; struct ext2fs_direct *edp; char *buf; size_t buf_size; int namlen; int rc; fp->f_seekp = 0; /* XXX should handle LARGEFILE */ while (fp->f_seekp < (off_t)fp->f_di.e2di_size) { rc = buf_read_file(f, &buf, &buf_size); if (rc) return rc; dp = (struct ext2fs_direct *)buf; edp = (struct ext2fs_direct *)(buf + buf_size); for (; dp < edp; dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) { if (fs2h16(dp->e2d_reclen) <= 0) break; if (fs2h32(dp->e2d_ino) == (ino32_t)0) continue; namlen = dp->e2d_namlen; if (namlen == length && !memcmp(name, dp->e2d_name, length)) { /* found entry */ *inumber_p = fs2h32(dp->e2d_ino); return 0; } } fp->f_seekp += buf_size; } return ENOENT; }
/* ARGSUSED */ int ext2fs_getattr(void *v) { struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; kauth_cred_t a_cred; } */ *ap = v; struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct vattr *vap = ap->a_vap; EXT2FS_ITIMES(ip, NULL, NULL, NULL); /* * Copy from inode table */ vap->va_fsid = ip->i_dev; vap->va_fileid = ip->i_number; vap->va_mode = ip->i_e2fs_mode & ALLPERMS; vap->va_nlink = ip->i_e2fs_nlink; vap->va_uid = ip->i_uid; vap->va_gid = ip->i_gid; vap->va_rdev = (dev_t)fs2h32(ip->i_din.e2fs_din->e2di_rdev); vap->va_size = vp->v_size; vap->va_atime.tv_sec = ip->i_e2fs_atime; vap->va_atime.tv_nsec = 0; vap->va_mtime.tv_sec = ip->i_e2fs_mtime; vap->va_mtime.tv_nsec = 0; vap->va_ctime.tv_sec = ip->i_e2fs_ctime; vap->va_ctime.tv_nsec = 0; #ifdef EXT2FS_SYSTEM_FLAGS vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0; vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0; #else vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0; vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0; #endif vap->va_gen = ip->i_e2fs_gen; /* this doesn't belong here */ if (vp->v_type == VBLK) vap->va_blocksize = BLKDEV_IOSIZE; else if (vp->v_type == VCHR) vap->va_blocksize = MAXBSIZE; else vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock); vap->va_type = vp->v_type; vap->va_filerev = ip->i_modrev; return (0); }
static int ext2fs_checksb(struct ext2fs *fs, int ronly) { uint32_t u32; if (fs2h16(fs->e2fs_magic) != E2FS_MAGIC) { return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_rev) > E2FS_REV1) { #ifdef DIAGNOSTIC printf("ext2fs: unsupported revision number: %x\n", fs2h32(fs->e2fs_rev)); #endif return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_log_bsize) > 2) { /* block size = 1024|2048|4096 */ #ifdef DIAGNOSTIC printf("ext2fs: bad block size: %d " "(expected <= 2 for ext2 fs)\n", fs2h32(fs->e2fs_log_bsize)); #endif return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_rev) > E2FS_REV0) { char buf[256]; if (fs2h32(fs->e2fs_first_ino) != EXT2_FIRSTINO) { printf("ext2fs: unsupported first inode position\n"); return (EINVAL); /* XXX needs translation */ } u32 = fs2h32(fs->e2fs_features_incompat) & ~EXT2F_INCOMPAT_SUPP; if (u32) { snprintb(buf, sizeof(buf), EXT2F_INCOMPAT_BITS, u32); printf("ext2fs: unsupported incompat features: %s\n", buf); return EINVAL; /* XXX needs translation */ } u32 = fs2h32(fs->e2fs_features_rocompat) & ~EXT2F_ROCOMPAT_SUPP; if (!ronly && u32) { snprintb(buf, sizeof(buf), EXT2F_ROCOMPAT_BITS, u32); printf("ext2fs: unsupported ro-incompat features: %s\n", buf); return EROFS; /* XXX needs translation */ } } return (0); }
/* * Check if a directory is empty or not. * Inode supplied must be locked. * * Using a struct dirtemplate here is not precisely * what we want, but better than using a struct ext2fs_direct. * * NB: does not handle corrupted directories. */ int ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred) { off_t off; struct ext2fs_dirtemplate dbuf; struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf; int error, namlen; size_t count; #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2) for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) { error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off, IO_NODELOCKED, cred, &count, NULL); /* * Since we read MINDIRSIZ, residual must * be 0 unless we're at end of file. */ if (error || count != 0) return (0); /* avoid infinite loops */ if (dp->e2d_reclen == 0) return (0); /* skip empty entries */ if (dp->e2d_ino == 0) continue; /* accept only "." and ".." */ namlen = dp->e2d_namlen; if (namlen > 2) return (0); if (dp->e2d_name[0] != '.') return (0); /* * At this point namlen must be 1 or 2. * 1 implies ".", 2 implies ".." if second * char is also "." */ if (namlen == 1) continue; if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino) continue; return (0); } return (1); }
(void)ext2fs_setsize(ip, 0); memset(ip->i_e2fs_blocks, 0, sizeof(ip->i_e2fs_blocks)); } *vpp = vp; return (0); } static int ext2fs_checksb(struct ext2fs *fs, int ronly) { if (fs2h16(fs->e2fs_magic) != E2FS_MAGIC) { return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_rev) > E2FS_REV1) { #ifdef DIAGNOSTIC printf("Ext2 fs: unsupported revision number: %x\n", fs2h32(fs->e2fs_rev)); #endif return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_log_bsize) > 2) { /* block size = 1024|2048|4096 */ #ifdef DIAGNOSTIC printf("Ext2 fs: bad block size: %d " "(expected <= 2 for ext2 fs)\n", fs2h32(fs->e2fs_log_bsize)); #endif return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_rev) > E2FS_REV0) { if (fs2h32(fs->e2fs_first_ino) != EXT2_FIRSTINO) { printf("Ext2 fs: unsupported first inode position\n"); return (EINVAL); /* XXX needs translation */ } if (fs2h32(fs->e2fs_features_incompat) & ~EXT2F_INCOMPAT_SUPP) { printf("Ext2 fs: unsupported optional feature\n"); return (EINVAL); /* XXX needs translation */
/* * ext2fs_rename_replace_dotdot: Change the target of the `..' entry of * the directory vp from fdvp to tdvp. */ static int ext2fs_rename_replace_dotdot(struct vnode *vp, struct vnode *fdvp, struct vnode *tdvp, kauth_cred_t cred) { struct ext2fs_dirtemplate dirbuf; int error; /* XXX Does it make sense to do this before the sanity checks below? */ KASSERT(0 < VTOI(fdvp)->i_e2fs_nlink); VTOI(fdvp)->i_e2fs_nlink--; VTOI(fdvp)->i_flag |= IN_CHANGE; error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); if (error) return error; if (dirbuf.dotdot_namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { ufs_dirbad(VTOI(vp), (doff_t)12, "bad `..' entry"); return 0; } if (fs2h32(dirbuf.dotdot_ino) != VTOI(fdvp)->i_number) { ufs_dirbad(VTOI(vp), (doff_t)12, "`..' does not point at parent"); return 0; } dirbuf.dotdot_ino = h2fs32(VTOI(tdvp)->i_number); /* XXX WTF? Why not check error? */ (void)vn_rdwr(UIO_WRITE, vp, &dirbuf, sizeof dirbuf, (off_t)0, UIO_SYSSPACE, (IO_NODELOCKED | IO_SYNC), cred, NULL, NULL); return 0; }
/* * ext2fs_read_dotdot: Store in *ino_ret the inode number of the parent * of the directory vp. */ static int ext2fs_read_dotdot(struct vnode *vp, kauth_cred_t cred, ino_t *ino_ret) { struct ext2fs_dirtemplate dirbuf; int error; KASSERT(vp != NULL); KASSERT(ino_ret != NULL); KASSERT(vp->v_type == VDIR); error = vn_rdwr(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, NULL); if (error) return error; if (dirbuf.dotdot_namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') /* XXX Panic? Print warning? */ return ENOTDIR; *ino_ret = fs2h32(dirbuf.dotdot_ino); return 0; }
/* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. * If the file system is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending * on whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".". When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and vput * instead of two vputs. * * Overall outline of ext2fs_lookup: * * check accessibility of directory * look for name in cache, if found, then if at end of path * and deleting or creating, drop it, else return name * search for name in directory, to found or notfound * notfound: * if creating, return locked directory, leaving info on available slots * else return error * found: * if at end of path and deleting, return information to allow delete * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * inode and return info to allow rewrite * if not at end, add name to cache; if at end and neither creating * nor deleting, add name to cache */ int ext2fs_lookup(void *v) { struct vop_lookup_args *ap = v; struct vnode *vdp; /* vnode for directory being searched */ struct inode *dp; /* inode for directory being searched */ struct buf *bp; /* a buffer of directory entries */ struct ext2fs_direct *ep; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ enum {NONE, COMPACT, FOUND} slotstatus; doff_t slotoffset; /* offset of area with free space */ int slotsize; /* size of area at slotoffset */ int slotfreespace; /* amount of space free in slot */ int slotneeded; /* size of the entry we're seeking */ int numdirpasses; /* strategy for directory search */ doff_t endsearch; /* offset to end directory search */ doff_t prevoff; /* prev entry dp->i_offset */ struct vnode *pdp; /* saved dp during symlink work */ struct vnode *tdp; /* returned by VFS_VGET */ doff_t enduseful; /* pointer past last used dir slot */ u_long bmask; /* block offset mask */ int lockparent; /* 1 => lockparent flag is set */ int wantparent; /* 1 => wantparent or lockparent flag */ int namlen, error; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; struct ucred *cred = cnp->cn_cred; int flags = cnp->cn_flags; int nameiop = cnp->cn_nameiop; struct proc *p = cnp->cn_proc; int dirblksize = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize; bp = NULL; slotoffset = -1; *vpp = NULL; vdp = ap->a_dvp; dp = VTOI(vdp); lockparent = flags & LOCKPARENT; wantparent = flags & (LOCKPARENT|WANTPARENT); /* * Check accessiblity of directory. */ if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) return (error); if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); /* * We now have a segment name to search for, and a directory to search. * * Before tediously performing a linear scan of the directory, * check the name cache to see if the directory/name pair * we are looking for is known already. */ if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) return (error); /* * Suppress search for slots unless creating * file and at end of pathname, in which case * we watch for a place to put the new file in * case it doesn't already exist. */ slotstatus = FOUND; slotfreespace = slotsize = slotneeded = 0; if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { slotstatus = NONE; slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen); } /* * If there is cached information on a previous search of * this directory, pick up where we last left off. * We cache only lookups as these are the most common * and have the greatest payoff. Caching CREATE has little * benefit as it usually must search the entire directory * to determine that the entry does not exist. Caching the * location of the last DELETE or RENAME has not reduced * profiling time and hence has been removed in the interest * of simplicity. */ bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1; if (nameiop != LOOKUP || dp->i_diroff == 0 || dp->i_diroff >ext2fs_size(dp)) { entryoffsetinblock = 0; dp->i_offset = 0; numdirpasses = 1; } else { dp->i_offset = dp->i_diroff; if ((entryoffsetinblock = dp->i_offset & bmask) && (error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp))) return (error); numdirpasses = 2; } prevoff = dp->i_offset; endsearch = roundup(ext2fs_size(dp), dirblksize); enduseful = 0; searchloop: while (dp->i_offset < endsearch) { /* * If necessary, get the next directory block. */ if ((dp->i_offset & bmask) == 0) { if (bp != NULL) brelse(bp); error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, NULL, &bp); if (error != 0) return (error); entryoffsetinblock = 0; } /* * If still looking for a slot, and at a dirblksize * boundary, have to start looking for free space again. */ if (slotstatus == NONE && (entryoffsetinblock & (dirblksize - 1)) == 0) { slotoffset = -1; slotfreespace = 0; } /* * Get pointer to next entry. * Full validation checks are slow, so we only check * enough to insure forward progress through the * directory. Complete checks can be run by patching * "dirchk" to be true. */ ep = (struct ext2fs_direct *) ((char *)bp->b_data + entryoffsetinblock); if (ep->e2d_reclen == 0 || (dirchk && ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) { int i; ufs_dirbad(dp, dp->i_offset, "mangled entry"); i = dirblksize - (entryoffsetinblock & (dirblksize - 1)); dp->i_offset += i; entryoffsetinblock += i; continue; } /* * If an appropriate sized slot has not yet been found, * check to see if one is available. Also accumulate space * in the current block so that we can determine if * compaction is viable. */ if (slotstatus != FOUND) { int size = fs2h16(ep->e2d_reclen); if (ep->e2d_ino != 0) size -= EXT2FS_DIRSIZ(ep->e2d_namlen); if (size > 0) { if (size >= slotneeded) { slotstatus = FOUND; slotoffset = dp->i_offset; slotsize = fs2h16(ep->e2d_reclen); } else if (slotstatus == NONE) { slotfreespace += size; if (slotoffset == -1) slotoffset = dp->i_offset; if (slotfreespace >= slotneeded) { slotstatus = COMPACT; slotsize = dp->i_offset + fs2h16(ep->e2d_reclen) - slotoffset; } } } } /* * Check for a name match. */ if (ep->e2d_ino) { namlen = ep->e2d_namlen; if (namlen == cnp->cn_namelen && !memcmp(cnp->cn_nameptr, ep->e2d_name, (unsigned)namlen)) { /* * Save directory entry's inode number and * reclen in ndp->ni_ufs area, and release * directory buffer. */ dp->i_ino = fs2h32(ep->e2d_ino); dp->i_reclen = fs2h16(ep->e2d_reclen); brelse(bp); goto found; } } prevoff = dp->i_offset; dp->i_offset += fs2h16(ep->e2d_reclen); entryoffsetinblock += fs2h16(ep->e2d_reclen); if (ep->e2d_ino) enduseful = dp->i_offset; } /* notfound: */ /* * If we started in the middle of the directory and failed * to find our target, we must check the beginning as well. */ if (numdirpasses == 2) { numdirpasses--; dp->i_offset = 0; endsearch = dp->i_diroff; goto searchloop; } if (bp != NULL) brelse(bp); /* * If creating, and at end of pathname and current * directory has not been removed, then can consider * allowing file to be created. */ if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) { /* * Creation of files on a read-only mounted file system * is pointless, so don't proceed any further. */ if (vdp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); /* * Access for write is interpreted as allowing * creation of files in the directory. */ if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) return (error); /* * Return an indication of where the new directory * entry should be put. If we didn't find a slot, * then set dp->i_count to 0 indicating * that the new slot belongs at the end of the * directory. If we found a slot, then the new entry * can be put in the range from dp->i_offset to * dp->i_offset + dp->i_count. */ if (slotstatus == NONE) { dp->i_offset = roundup(ext2fs_size(dp), dirblksize); dp->i_count = 0; enduseful = dp->i_offset; } else { dp->i_offset = slotoffset; dp->i_count = slotsize; if (enduseful < slotoffset + slotsize) enduseful = slotoffset + slotsize; } dp->i_endoff = roundup(enduseful, dirblksize); dp->i_flag |= IN_CHANGE | IN_UPDATE; /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory inode in ndp->ni_dvp. * The pathname buffer is saved so that the name * can be obtained later. * * NB - if the directory is unlocked, then this * information cannot be used. */ cnp->cn_flags |= SAVENAME; if (!lockparent) { VOP_UNLOCK(vdp, 0); cnp->cn_flags |= PDIRUNLOCK; } return (EJUSTRETURN); } /* * Insert name into cache (as non-existent) if appropriate. */ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE) cache_enter(vdp, *vpp, cnp); return (ENOENT); found: /* * Check that directory length properly reflects presence * of this entry. */ if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) { ufs_dirbad(dp, dp->i_offset, "i_size too small"); error = ext2fs_setsize(dp, entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)); if (error) { brelse(bp); return(error); } dp->i_flag |= IN_CHANGE | IN_UPDATE; } /* * Found component in pathname. * If the final component of path name, save information * in the cache as to where the entry was found. */ if ((flags & ISLASTCN) && nameiop == LOOKUP) dp->i_diroff = dp->i_offset &~ (dirblksize - 1); /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. * If the wantparent flag isn't set, we return only * the directory (in ndp->ni_dvp), otherwise we go * on and lock the inode, being careful with ".". */ if (nameiop == DELETE && (flags & ISLASTCN)) { /* * Write access to directory required to delete files. */ if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) return (error); /* * Return pointer to current entry in dp->i_offset, * and distance past previous entry (if there * is a previous entry in this block) in dp->i_count. * Save directory inode pointer in ndp->ni_dvp for dirremove(). */ if ((dp->i_offset & (dirblksize - 1)) == 0) dp->i_count = 0; else dp->i_count = dp->i_offset - prevoff; if (dp->i_number == dp->i_ino) { vref(vdp); *vpp = vdp; return (0); } if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) return (error); /* * If directory is "sticky", then user must own * the directory, or the file in it, else she * may not delete it (unless she's root). This * implements append-only directories. */ if ((dp->i_e2fs_mode & ISVTX) && cred->cr_uid != 0 && cred->cr_uid != dp->i_e2fs_uid && VTOI(tdp)->i_e2fs_uid != cred->cr_uid) { vput(tdp); return (EPERM); } *vpp = tdp; if (!lockparent) { VOP_UNLOCK(vdp, 0); cnp->cn_flags |= PDIRUNLOCK; } return (0); } /* * If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a * regular file, or empty directory. */ if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->i_number == dp->i_ino) return (EISDIR); if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) return (error); *vpp = tdp; cnp->cn_flags |= SAVENAME; if (!lockparent) { VOP_UNLOCK(vdp, 0); cnp->cn_flags |= PDIRUNLOCK; } return (0); } /* * Step through the translation in the name. We do not `vput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead we save it * unlocked as "pdp". We must get the target inode before unlocking * the directory to insure that the inode will not be removed * before we get it. We prevent deadlock by always fetching * inodes from the root, moving down the directory tree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if both the current * and parent directories are removed before the VFS_VGET for the * inode associated with ".." returns. We hope that this occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detection algorithm. * Note also that this simple deadlock detection scheme will not * work if the file system has any hard links other than ".." * that point backwards in the directory structure. */ pdp = vdp; if (flags & ISDOTDOT) { VOP_UNLOCK(pdp, 0); /* race to get the inode */ cnp->cn_flags |= PDIRUNLOCK; if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) { if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0) cnp->cn_flags &= ~PDIRUNLOCK; return (error); } if (lockparent && (flags & ISLASTCN)) { if ((error = vn_lock(pdp, LK_EXCLUSIVE, p)) != 0) { vput(tdp); return (error); } cnp->cn_flags &= ~PDIRUNLOCK; } *vpp = tdp; } else if (dp->i_number == dp->i_ino) { vref(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) return (error); if (!lockparent || !(flags & ISLASTCN)) { VOP_UNLOCK(pdp, 0); cnp->cn_flags |= PDIRUNLOCK; } *vpp = tdp; } /* * Insert name into cache if appropriate. */ if (cnp->cn_flags & MAKEENTRY) cache_enter(vdp, *vpp, cnp); return (0); }
/* * ext2fs_rename_recalculate_fulr: If we have just entered a directory * into dvp at tulr, and we were about to remove one at fulr for an * entry named fcnp, fulr may be invalid. So, if necessary, * recalculate it. */ static int ext2fs_rename_recalculate_fulr(struct vnode *dvp, struct ufs_lookup_results *fulr, const struct ufs_lookup_results *tulr, const struct componentname *fcnp) { struct mount *mp; struct ufsmount *ump; /* XXX int is a silly type for this; blame ufsmount::um_dirblksiz. */ int dirblksiz; doff_t search_start, search_end; doff_t offset; /* Offset of entry we're examining. */ struct buf *bp; /* I/O block we're examining. */ char *dirbuf; /* Pointer into directory at search_start. */ struct ext2fs_direct *ep; /* Pointer to the entry we're examining. */ /* XXX direct::d_reclen is 16-bit; * ufs_lookup_results::ulr_reclen is 32-bit. Blah. */ uint32_t reclen; /* Length of the entry we're examining. */ uint32_t prev_reclen; /* Length of the preceding entry. */ int error; KASSERT(dvp != NULL); KASSERT(dvp->v_mount != NULL); KASSERT(VTOI(dvp) != NULL); KASSERT(fulr != NULL); KASSERT(tulr != NULL); KASSERT(fulr != tulr); KASSERT(ext2fs_rename_ulr_overlap_p(fulr, tulr)); mp = dvp->v_mount; ump = VFSTOUFS(mp); KASSERT(ump != NULL); KASSERT(ump == VTOI(dvp)->i_ump); dirblksiz = ump->um_dirblksiz; KASSERT(0 < dirblksiz); KASSERT((dirblksiz & (dirblksiz - 1)) == 0); /* A directory block may not span across multiple I/O blocks. */ KASSERT(dirblksiz <= mp->mnt_stat.f_iosize); /* Find the bounds of the search. */ search_start = tulr->ulr_offset; KASSERT(fulr->ulr_reclen < (EXT2FS_MAXDIRSIZE - fulr->ulr_offset)); search_end = (fulr->ulr_offset + fulr->ulr_reclen); /* Compaction must happen only within a directory block. (*) */ KASSERT(search_start <= search_end); KASSERT((search_end - (search_start &~ (dirblksiz - 1))) <= dirblksiz); dirbuf = NULL; bp = NULL; error = ext2fs_blkatoff(dvp, (off_t)search_start, &dirbuf, &bp); if (error) return error; KASSERT(dirbuf != NULL); KASSERT(bp != NULL); /* * Guarantee we sha'n't go past the end of the buffer we got. * dirbuf is bp->b_data + (search_start & (iosize - 1)), and * the valid range is [bp->b_data, bp->b_data + bp->b_bcount). */ KASSERT((search_end - search_start) <= (bp->b_bcount - (search_start & (mp->mnt_stat.f_iosize - 1)))); prev_reclen = fulr->ulr_count; offset = search_start; /* * Search from search_start to search_end for the entry matching * fcnp, which must be there because we found it before and it * should only at most have moved earlier. */ for (;;) { KASSERT(search_start <= offset); KASSERT(offset < search_end); /* * Examine the directory entry at offset. */ ep = (struct ext2fs_direct *) (dirbuf + (offset - search_start)); reclen = fs2h16(ep->e2d_reclen); if (ep->e2d_ino == 0) goto next; /* Entry is unused. */ if (fs2h32(ep->e2d_ino) == UFS_WINO) goto next; /* Entry is whiteout. */ if (fcnp->cn_namelen != ep->e2d_namlen) goto next; /* Wrong name length. */ if (memcmp(ep->e2d_name, fcnp->cn_nameptr, fcnp->cn_namelen)) goto next; /* Wrong name. */ /* Got it! */ break; next: if (! ((reclen < search_end) && (offset < (search_end - reclen)))) { brelse(bp, 0); return EIO; /* XXX Panic? What? */ } /* We may not move past the search end. */ KASSERT(reclen < search_end); KASSERT(offset < (search_end - reclen)); /* * We may not move across a directory block boundary; * see (*) above. */ KASSERT((offset &~ (dirblksiz - 1)) == ((offset + reclen) &~ (dirblksiz - 1))); prev_reclen = reclen; offset += reclen; } /* * Found the entry. Record where. */ fulr->ulr_offset = offset; fulr->ulr_reclen = reclen; /* * Record the preceding record length, but not if we're at the * start of a directory block. */ fulr->ulr_count = ((offset & (dirblksiz - 1))? prev_reclen : 0); brelse(bp, 0); return 0; }
/* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. * If the file system is not maintained in a strict tree hierarchy, * this can result in a deadlock situation (see comments in code below). * * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending * on whether the name is to be looked up, created, renamed, or deleted. * When CREATE, RENAME, or DELETE is specified, information usable in * creating, renaming, or deleting a directory entry may be calculated. * If flag has LOCKPARENT or'ed into it and the target of the pathname * exists, lookup returns both the target and its parent directory locked. * When creating or renaming and LOCKPARENT is specified, the target may * not be ".". When deleting and LOCKPARENT is specified, the target may * be "."., but the caller must check to ensure it does an vrele and vput * instead of two vputs. * * Overall outline of ext2fs_lookup: * * check accessibility of directory * look for name in cache, if found, then if at end of path * and deleting or creating, drop it, else return name * search for name in directory, to found or notfound * notfound: * if creating, return locked directory, leaving info on available slots * else return error * found: * if at end of path and deleting, return information to allow delete * if at end of path and rewriting (RENAME and LOCKPARENT), lock target * inode and return info to allow rewrite * if not at end, add name to cache; if at end and neither creating * nor deleting, add name to cache */ int ext2fs_lookup(void *v) { struct vop_lookup_v2_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap = v; struct vnode *vdp = ap->a_dvp; /* vnode for directory being searched */ struct inode *dp = VTOI(vdp); /* inode for directory being searched */ struct buf *bp; /* a buffer of directory entries */ struct ext2fs_direct *ep; /* the current directory entry */ int entryoffsetinblock; /* offset of ep in bp's buffer */ enum {NONE, COMPACT, FOUND} slotstatus; doff_t slotoffset; /* offset of area with free space */ int slotsize; /* size of area at slotoffset */ int slotfreespace; /* amount of space free in slot */ int slotneeded; /* size of the entry we're seeking */ int numdirpasses; /* strategy for directory search */ doff_t endsearch; /* offset to end directory search */ doff_t prevoff; /* prev entry dp->i_offset */ struct vnode *tdp; /* returned by vcache_get */ doff_t enduseful; /* pointer past last used dir slot */ u_long bmask; /* block offset mask */ int namlen, error; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; kauth_cred_t cred = cnp->cn_cred; int flags; int nameiop = cnp->cn_nameiop; struct ufsmount *ump = dp->i_ump; int dirblksiz = ump->um_dirblksiz; ino_t foundino; struct ufs_lookup_results *results; flags = cnp->cn_flags; bp = NULL; slotoffset = -1; *vpp = NULL; /* * Produce the auxiliary lookup results into i_crap. Increment * its serial number so elsewhere we can tell if we're using * stale results. This should not be done this way. XXX. */ results = &dp->i_crap; dp->i_crapcounter++; /* * Check accessiblity of directory. */ if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) return (error); if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) return (EROFS); /* * We now have a segment name to search for, and a directory to search. * * Before tediously performing a linear scan of the directory, * check the name cache to see if the directory/name pair * we are looking for is known already. */ if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) { return *vpp == NULLVP ? ENOENT : 0; } /* * Suppress search for slots unless creating * file and at end of pathname, in which case * we watch for a place to put the new file in * case it doesn't already exist. */ slotstatus = FOUND; slotfreespace = slotsize = slotneeded = 0; if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { slotstatus = NONE; slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen); } /* * If there is cached information on a previous search of * this directory, pick up where we last left off. * We cache only lookups as these are the most common * and have the greatest payoff. Caching CREATE has little * benefit as it usually must search the entire directory * to determine that the entry does not exist. Caching the * location of the last DELETE or RENAME has not reduced * profiling time and hence has been removed in the interest * of simplicity. */ bmask = vdp->v_mount->mnt_stat.f_iosize - 1; if (nameiop != LOOKUP || results->ulr_diroff == 0 || results->ulr_diroff >= ext2fs_size(dp)) { entryoffsetinblock = 0; results->ulr_offset = 0; numdirpasses = 1; } else { results->ulr_offset = results->ulr_diroff; if ((entryoffsetinblock = results->ulr_offset & bmask) && (error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL, &bp))) return (error); numdirpasses = 2; namecache_count_2passes(); } prevoff = results->ulr_offset; endsearch = roundup(ext2fs_size(dp), dirblksiz); enduseful = 0; searchloop: while (results->ulr_offset < endsearch) { if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD) preempt(); /* * If necessary, get the next directory block. */ if ((results->ulr_offset & bmask) == 0) { if (bp != NULL) brelse(bp, 0); error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL, &bp); if (error != 0) return (error); entryoffsetinblock = 0; } /* * If still looking for a slot, and at a dirblksize * boundary, have to start looking for free space again. */ if (slotstatus == NONE && (entryoffsetinblock & (dirblksiz - 1)) == 0) { slotoffset = -1; slotfreespace = 0; } /* * Get pointer to next entry. * Full validation checks are slow, so we only check * enough to insure forward progress through the * directory. Complete checks can be run by patching * "dirchk" to be true. */ KASSERT(bp != NULL); ep = (struct ext2fs_direct *) ((char *)bp->b_data + entryoffsetinblock); if (ep->e2d_reclen == 0 || (dirchk && ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) { int i; ufs_dirbad(dp, results->ulr_offset, "mangled entry"); i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1)); results->ulr_offset += i; entryoffsetinblock += i; continue; } /* * If an appropriate sized slot has not yet been found, * check to see if one is available. Also accumulate space * in the current block so that we can determine if * compaction is viable. */ if (slotstatus != FOUND) { int size = fs2h16(ep->e2d_reclen); if (ep->e2d_ino != 0) size -= EXT2FS_DIRSIZ(ep->e2d_namlen); if (size > 0) { if (size >= slotneeded) { slotstatus = FOUND; slotoffset = results->ulr_offset; slotsize = fs2h16(ep->e2d_reclen); } else if (slotstatus == NONE) { slotfreespace += size; if (slotoffset == -1) slotoffset = results->ulr_offset; if (slotfreespace >= slotneeded) { slotstatus = COMPACT; slotsize = results->ulr_offset + fs2h16(ep->e2d_reclen) - slotoffset; } } } } /* * Check for a name match. */ if (ep->e2d_ino) { namlen = ep->e2d_namlen; if (namlen == cnp->cn_namelen && !memcmp(cnp->cn_nameptr, ep->e2d_name, (unsigned)namlen)) { /* * Save directory entry's inode number and * reclen in ndp->ni_ufs area, and release * directory buffer. */ foundino = fs2h32(ep->e2d_ino); results->ulr_reclen = fs2h16(ep->e2d_reclen); goto found; } } prevoff = results->ulr_offset; results->ulr_offset += fs2h16(ep->e2d_reclen); entryoffsetinblock += fs2h16(ep->e2d_reclen); if (ep->e2d_ino) enduseful = results->ulr_offset; } /* notfound: */ /* * If we started in the middle of the directory and failed * to find our target, we must check the beginning as well. */ if (numdirpasses == 2) { numdirpasses--; results->ulr_offset = 0; endsearch = results->ulr_diroff; goto searchloop; } if (bp != NULL) brelse(bp, 0); /* * If creating, and at end of pathname and current * directory has not been removed, then can consider * allowing file to be created. */ if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) { /* * Access for write is interpreted as allowing * creation of files in the directory. */ error = VOP_ACCESS(vdp, VWRITE, cred); if (error) return (error); /* * Return an indication of where the new directory * entry should be put. If we didn't find a slot, * then set results->ulr_count to 0 indicating * that the new slot belongs at the end of the * directory. If we found a slot, then the new entry * can be put in the range from results->ulr_offset to * results->ulr_offset + results->ulr_count. */ if (slotstatus == NONE) { results->ulr_offset = roundup(ext2fs_size(dp), dirblksiz); results->ulr_count = 0; enduseful = results->ulr_offset; } else { results->ulr_offset = slotoffset; results->ulr_count = slotsize; if (enduseful < slotoffset + slotsize) enduseful = slotoffset + slotsize; } results->ulr_endoff = roundup(enduseful, dirblksiz); #if 0 dp->i_flag |= IN_CHANGE | IN_UPDATE; #endif /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory inode in ndp->ni_dvp. * * NB - if the directory is unlocked, then this * information cannot be used. */ return (EJUSTRETURN); } /* * Insert name into cache (as non-existent) if appropriate. */ if (nameiop != CREATE) { cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); } return ENOENT; found: if (numdirpasses == 2) namecache_count_pass2(); /* * Check that directory length properly reflects presence * of this entry. */ if (results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) { ufs_dirbad(dp, results->ulr_offset, "i_size too small"); error = ext2fs_setsize(dp, results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen)); if (error) { brelse(bp, 0); return (error); } dp->i_flag |= IN_CHANGE | IN_UPDATE; uvm_vnp_setsize(vdp, ext2fs_size(dp)); } brelse(bp, 0); /* * Found component in pathname. * If the final component of path name, save information * in the cache as to where the entry was found. */ if ((flags & ISLASTCN) && nameiop == LOOKUP) results->ulr_diroff = results->ulr_offset &~ (dirblksiz - 1); /* * If deleting, and at end of pathname, return * parameters which can be used to remove file. * Lock the inode, being careful with ".". */ if (nameiop == DELETE && (flags & ISLASTCN)) { /* * Return pointer to current entry in results->ulr_offset, * and distance past previous entry (if there * is a previous entry in this block) in results->ulr_count. * Save directory inode pointer in ndp->ni_dvp for dirremove(). */ if ((results->ulr_offset & (dirblksiz - 1)) == 0) results->ulr_count = 0; else results->ulr_count = results->ulr_offset - prevoff; if (dp->i_number == foundino) { vref(vdp); tdp = vdp; } else { error = vcache_get(vdp->v_mount, &foundino, sizeof(foundino), &tdp); if (error) return (error); } /* * Write access to directory required to delete files. */ if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) { vrele(tdp); return (error); } /* * If directory is "sticky", then user must own * the directory, or the file in it, else she * may not delete it (unless she's root). This * implements append-only directories. */ if (dp->i_e2fs_mode & ISVTX) { error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, tdp, vdp, genfs_can_sticky(cred, dp->i_uid, VTOI(tdp)->i_uid)); if (error) { vrele(tdp); return (EPERM); } } *vpp = tdp; return (0); } /* * If rewriting (RENAME), return the inode and the * information required to rewrite the present directory * Must get inode of directory entry to verify it's a * regular file, or empty directory. */ if (nameiop == RENAME && (flags & ISLASTCN)) { error = VOP_ACCESS(vdp, VWRITE, cred); if (error) return (error); /* * Careful about locking second inode. * This can only occur if the target is ".". */ if (dp->i_number == foundino) return (EISDIR); error = vcache_get(vdp->v_mount, &foundino, sizeof(foundino), &tdp); if (error) return (error); *vpp = tdp; return (0); } if (dp->i_number == foundino) { vref(vdp); /* we want ourself, ie "." */ *vpp = vdp; } else { error = vcache_get(vdp->v_mount, &foundino, sizeof(foundino), &tdp); if (error) return (error); *vpp = tdp; } /* * Insert name into cache if appropriate. */ cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); return 0; }
int ext2fs_bmaparray(struct vnode *vp, int32_t bn, daddr_t *bnp, struct indir *ap, int *nump, int *runp) { struct inode *ip; struct buf *bp; struct ufsmount *ump; struct mount *mp; struct vnode *devvp; struct indir a[NIADDR+1], *xap; int32_t daddr; long metalbn; int error, maxrun = 0, num; ip = VTOI(vp); mp = vp->v_mount; ump = VFSTOUFS(mp); #ifdef DIAGNOSTIC if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL)) panic("ext2fs_bmaparray: invalid arguments"); #endif if (runp) { /* * XXX * If MAXBSIZE is the largest transfer the disks can handle, * we probably want maxrun to be 1 block less so that we * don't create a block larger than the device can handle. */ *runp = 0; maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1; } xap = ap == NULL ? a : ap; if (!nump) nump = # if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0) return (error); num = *nump; if (num == 0) { *bnp = blkptrtodb(ump, fs2h32(ip->i_e2fs_blocks[bn])); if (*bnp == 0) *bnp = -1; else if (runp) for (++bn; bn < NDADDR && *runp < maxrun && is_sequential(ump, fs2h32(ip->i_e2fs_blocks[bn - 1]), fs2h32(ip->i_e2fs_blocks[bn])); ++bn, ++*runp); return (0); } /* Get disk address out of indirect block array */ daddr = fs2h32(ip->i_e2fs_blocks[NDADDR + xap->in_off]); devvp = VFSTOUFS(vp->v_mount)->um_devvp; #ifdef DIAGNOSTIC if (num > NIADDR + 1 || num < 1) { printf("ext2fs_bmaparray: num=%d\n", num); panic("ext2fs_bmaparray: num"); } #endif for (bp = NULL, ++xap; --num; ++xap) { /* * Exit the loop if there is no disk address assigned yet and * the indirect block isn't in the cache, or if we were * looking for an indirect block and we've found it. */ metalbn = xap->in_lbn; if ((daddr == 0 && !incore(vp, metalbn)) || metalbn == bn) break; /* * If we get here, we've either got the block in the cache * or we have a disk address for it, go fetch it. */ if (bp) brelse(bp); xap->in_exists = 1; bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0); if (bp->b_flags & (B_DONE | B_DELWRI)) { ; } #ifdef DIAGNOSTIC else if (!daddr) panic("ext2fs_bmaparry: indirect block not in cache"); #endif else { bp->b_blkno = blkptrtodb(ump, daddr); bp->b_flags |= B_READ; VOP_STRATEGY(bp); curproc->p_ru.ru_inblock++; /* XXX */ bcstats.pendingreads++; if ((error = biowait(bp)) != 0) { brelse(bp); return (error); } } daddr = fs2h32(((int32_t *)bp->b_data)[xap->in_off]); if (num == 1 && daddr && runp) for (bn = xap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump, ((int32_t *)bp->b_data)[bn - 1], ((int32_t *)bp->b_data)[bn]); ++bn, ++*runp); } if (bp) brelse(bp); daddr = blkptrtodb(ump, daddr); *bnp = daddr == 0 ? -1 : daddr; return (0); }
static int pass2check(struct inodesc *idesc) { struct ext2fs_direct *dirp = idesc->id_dirp; struct inoinfo *inp; int n, entrysize, ret = 0; struct ext2fs_dinode *dp; const char *errmsg; struct ext2fs_direct proto; char namebuf[MAXPATHLEN + 1]; char pathbuf[MAXPATHLEN + 1]; /* * check for "." */ if (idesc->id_entryno != 0) goto chk1; if (fs2h32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 && dirp->e2d_name[0] == '.') { if (fs2h32(dirp->e2d_ino) != idesc->id_number) { direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); dirp->e2d_ino = h2fs32(idesc->id_number); if (reply("FIX") == 1) ret |= ALTERED; } if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) && (dirp->e2d_type != EXT2_FT_DIR)) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); dirp->e2d_type = EXT2_FT_DIR; if (reply("FIX") == 1) ret |= ALTERED; } goto chk1; } direrror(idesc->id_number, "MISSING '.'"); proto.e2d_ino = h2fs32(idesc->id_number); proto.e2d_namlen = 1; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) proto.e2d_type = EXT2_FT_DIR; else proto.e2d_type = 0; (void)strlcpy(proto.e2d_name, ".", sizeof(proto.e2d_name)); entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen); if (fs2h32(dirp->e2d_ino) != 0 && strcmp(dirp->e2d_name, "..") != 0) { pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n", dirp->e2d_name); } else if (fs2h16(dirp->e2d_reclen) < entrysize) { pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n"); } else if (fs2h16(dirp->e2d_reclen) < 2 * entrysize) { proto.e2d_reclen = dirp->e2d_reclen; memcpy(dirp, &proto, (size_t)entrysize); if (reply("FIX") == 1) ret |= ALTERED; } else { n = fs2h16(dirp->e2d_reclen) - entrysize; proto.e2d_reclen = h2fs16(entrysize); memcpy(dirp, &proto, (size_t)entrysize); idesc->id_entryno++; lncntp[fs2h32(dirp->e2d_ino)]--; dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize); memset(dirp, 0, (size_t)n); dirp->e2d_reclen = h2fs16(n); if (reply("FIX") == 1) ret |= ALTERED; } chk1: if (idesc->id_entryno > 1) goto chk2; inp = getinoinfo(idesc->id_number); proto.e2d_ino = h2fs32(inp->i_parent); proto.e2d_namlen = 2; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) proto.e2d_type = EXT2_FT_DIR; else proto.e2d_type = 0; (void)strlcpy(proto.e2d_name, "..", sizeof(proto.e2d_name)); entrysize = EXT2FS_DIRSIZ(2); if (idesc->id_entryno == 0) { n = EXT2FS_DIRSIZ(dirp->e2d_namlen); if (fs2h16(dirp->e2d_reclen) < n + entrysize) goto chk2; proto.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - n); dirp->e2d_reclen = h2fs16(n); idesc->id_entryno++; lncntp[fs2h32(dirp->e2d_ino)]--; dirp = (struct ext2fs_direct *)((char *)(dirp) + n); memset(dirp, 0, (size_t)fs2h16(proto.e2d_reclen)); dirp->e2d_reclen = proto.e2d_reclen; } if (fs2h32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 2 && strncmp(dirp->e2d_name, "..", 2) == 0) { inp->i_dotdot = fs2h32(dirp->e2d_ino); if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) && dirp->e2d_type != EXT2_FT_DIR) { direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); dirp->e2d_type = EXT2_FT_DIR; if (reply("FIX") == 1) ret |= ALTERED; } goto chk2; } if (fs2h32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 && strncmp(dirp->e2d_name, ".", 1) != 0) { fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n", dirp->e2d_name); inp->i_dotdot = (ino_t)-1; } else if (fs2h16(dirp->e2d_reclen) < entrysize) { fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n"); inp->i_dotdot = (ino_t)-1; } else if (inp->i_parent != 0) { /* * We know the parent, so fix now. */ inp->i_dotdot = inp->i_parent; fileerror(inp->i_parent, idesc->id_number, "MISSING '..'"); proto.e2d_reclen = dirp->e2d_reclen; memcpy(dirp, &proto, (size_t)entrysize); if (reply("FIX") == 1) ret |= ALTERED; } idesc->id_entryno++; if (fs2h32(dirp->e2d_ino) != 0) lncntp[fs2h32(dirp->e2d_ino)]--; return (ret|KEEPON); chk2: if (fs2h32(dirp->e2d_ino) == 0) return (ret|KEEPON); if (dirp->e2d_namlen <= 2 && dirp->e2d_name[0] == '.' && idesc->id_entryno >= 2) { if (dirp->e2d_namlen == 1) { direrror(idesc->id_number, "EXTRA '.' ENTRY"); dirp->e2d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; return (KEEPON | ret); } if (dirp->e2d_name[1] == '.') { direrror(idesc->id_number, "EXTRA '..' ENTRY"); dirp->e2d_ino = 0; if (reply("FIX") == 1) ret |= ALTERED; return (KEEPON | ret); } } idesc->id_entryno++; n = 0; if (fs2h32(dirp->e2d_ino) > maxino || (fs2h32(dirp->e2d_ino) < EXT2_FIRSTINO && fs2h32(dirp->e2d_ino) != EXT2_ROOTINO)) { fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "I OUT OF RANGE"); n = reply("REMOVE"); } else { again: switch (statemap[fs2h32(dirp->e2d_ino)]) { case USTATE: if (idesc->id_entryno <= 2) break; fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "UNALLOCATED"); n = reply("REMOVE"); break; case DCLEAR: case FCLEAR: if (idesc->id_entryno <= 2) break; if (statemap[fs2h32(dirp->e2d_ino)] == FCLEAR) errmsg = "DUP/BAD"; else if (!preen) errmsg = "ZERO LENGTH DIRECTORY"; else { n = 1; break; } fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), errmsg); if ((n = reply("REMOVE")) == 1) break; dp = ginode(fs2h32(dirp->e2d_ino)); statemap[fs2h32(dirp->e2d_ino)] = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE; lncntp[fs2h32(dirp->e2d_ino)] = fs2h16(dp->e2di_nlink); goto again; case DSTATE: case DFOUND: inp = getinoinfo(fs2h32(dirp->e2d_ino)); if (inp->i_parent != 0 && idesc->id_entryno > 2) { getpathname(pathbuf, sizeof(pathbuf), idesc->id_number, idesc->id_number); getpathname(namebuf, sizeof(namebuf), fs2h32(dirp->e2d_ino), fs2h32(dirp->e2d_ino)); pwarn("%s %s %s\n", pathbuf, "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", namebuf); if (preen) printf(" (IGNORED)\n"); else if ((n = reply("REMOVE")) == 1) break; } if (idesc->id_entryno > 2) inp->i_parent = idesc->id_number; /* fall through */ case FSTATE: if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) && dirp->e2d_type != inot2ext2dt(typemap[fs2h32(dirp->e2d_ino)])) { dirp->e2d_type = inot2ext2dt(typemap[fs2h32(dirp->e2d_ino)]); fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "BAD TYPE VALUE"); if (reply("FIX") == 1) ret |= ALTERED; } lncntp[fs2h32(dirp->e2d_ino)]--; break; default: errexit("BAD STATE %d FOR INODE I=%d", statemap[fs2h32(dirp->e2d_ino)], fs2h32(dirp->e2d_ino)); } } if (n == 0) return (ret|KEEPON); dirp->e2d_ino = 0; return (ret|KEEPON|ALTERED); }
/* * Given an offset in a file, find the disk block number that * contains that block. */ static int block_map(struct open_file *f, indp_t file_block, indp_t *disk_block_p) { struct file *fp = (struct file *)f->f_fsdata; struct m_ext2fs *fs = fp->f_fs; uint level; indp_t ind_cache; indp_t ind_block_num; size_t rsize; int rc; indp_t *buf = (void *)fp->f_buf; /* * Index structure of an inode: * * e2di_blocks[0..EXT2FS_NDADDR-1] * hold block numbers for blocks * 0..EXT2FS_NDADDR-1 * * e2di_blocks[EXT2FS_NDADDR+0] * block EXT2FS_NDADDR+0 is the single indirect block * holds block numbers for blocks * EXT2FS_NDADDR .. EXT2FS_NDADDR + EXT2_NINDIR(fs)-1 * * e2di_blocks[EXT2FS_NDADDR+1] * block EXT2FS_NDADDR+1 is the double indirect block * holds block numbers for INDEX blocks for blocks * EXT2FS_NDADDR + EXT2_NINDIR(fs) .. * EXT2FS_NDADDR + EXT2_NINDIR(fs) + EXT2_NINDIR(fs)**2 - 1 * * e2di_blocks[EXT2FS_NDADDR+2] * block EXT2FS_NDADDR+2 is the triple indirect block * holds block numbers for double-indirect * blocks for blocks * EXT2FS_NDADDR + EXT2_NINDIR(fs) + EXT2_NINDIR(fs)**2 .. * EXT2FS_NDADDR + EXT2_NINDIR(fs) + EXT2_NINDIR(fs)**2 * + EXT2_NINDIR(fs)**3 - 1 */ if (file_block < EXT2FS_NDADDR) { /* Direct block. */ *disk_block_p = fs2h32(fp->f_di.e2di_blocks[file_block]); return 0; } file_block -= EXT2FS_NDADDR; ind_cache = file_block >> LN2_IND_CACHE_SZ; if (ind_cache == fp->f_ind_cache_block) { *disk_block_p = fs2h32(fp->f_ind_cache[file_block & IND_CACHE_MASK]); return 0; } for (level = 0;;) { level += fp->f_nishift; if (file_block < (indp_t)1 << level) break; if (level > EXT2FS_NIADDR * fp->f_nishift) /* Block number too high */ return EFBIG; file_block -= (indp_t)1 << level; } ind_block_num = fs2h32(fp->f_di.e2di_blocks[EXT2FS_NDADDR + (level / fp->f_nishift - 1)]); for (;;) { level -= fp->f_nishift; if (ind_block_num == 0) { *disk_block_p = 0; /* missing */ return 0; } twiddle(); /* * If we were feeling brave, we could work out the number * of the disk sector and read a single disk sector instead * of a filesystem block. * However we don't do this very often anyway... */ rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, FSBTODB(fp->f_fs, ind_block_num), fs->e2fs_bsize, buf, &rsize); if (rc) return rc; if (rsize != fs->e2fs_bsize) return EIO; ind_block_num = fs2h32(buf[file_block >> level]); if (level == 0) break; file_block &= (1 << level) - 1; } /* Save the part of the block that contains this sector */ memcpy(fp->f_ind_cache, &buf[file_block & ~IND_CACHE_MASK], IND_CACHE_SZ * sizeof fp->f_ind_cache[0]); fp->f_ind_cache_block = ind_cache; *disk_block_p = ind_block_num; return 0; }
__compactcall void ext2fs_ls(struct open_file *f, const char *pattern) { struct file *fp = (struct file *)f->f_fsdata; size_t block_size = fp->f_fs->e2fs_bsize; char *buf; size_t buf_size; entry_t *names = 0, *n, **np; fp->f_seekp = 0; while (fp->f_seekp < (off_t)fp->f_di.e2di_size) { struct ext2fs_direct *dp, *edp; int rc = buf_read_file(f, &buf, &buf_size); if (rc) goto out; if (buf_size != block_size || buf_size == 0) goto out; dp = (struct ext2fs_direct *)buf; edp = (struct ext2fs_direct *)(buf + buf_size); for (; dp < edp; dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) { const char *t; if (fs2h16(dp->e2d_reclen) <= 0) goto out; if (fs2h32(dp->e2d_ino) == 0) continue; if (dp->e2d_type >= NELEM(typestr) || !(t = typestr[dp->e2d_type])) { /* * This does not handle "old" * filesystems properly. On little * endian machines, we get a bogus * type name if the namlen matches a * valid type identifier. We could * check if we read namlen "0" and * handle this case specially, if * there were a pressing need... */ printf("bad dir entry\n"); goto out; } if (pattern && !fnmatch(dp->e2d_name, pattern)) continue; n = alloc(sizeof *n + strlen(dp->e2d_name)); if (!n) { printf("%d: %s (%s)\n", fs2h32(dp->e2d_ino), dp->e2d_name, t); continue; } n->e_ino = fs2h32(dp->e2d_ino); n->e_type = dp->e2d_type; strcpy(n->e_name, dp->e2d_name); for (np = &names; *np; np = &(*np)->e_next) { if (strcmp(n->e_name, (*np)->e_name) < 0) break; } n->e_next = *np; *np = n; } fp->f_seekp += buf_size; } if (names) { entry_t *p_names = names; do { n = p_names; printf("%d: %s (%s)\n", n->e_ino, n->e_name, typestr[n->e_type]); p_names = n->e_next; } while (p_names); } else { printf("not found\n"); } out: if (names) { do { n = names; names = n->e_next; dealloc(n, 0); } while (names); } return; }
void pass5(void) { int c; struct m_ext2fs *fs = &sblock; daddr_t dbase, dmax; daddr_t d; long i, j; struct inodesc idesc[3]; struct bufarea *ino_bitmap = NULL, *blk_bitmap = NULL; char *ibmap, *bbmap; u_int32_t cs_ndir, cs_nbfree, cs_nifree; char msg[255]; cs_ndir = 0; cs_nbfree = 0; cs_nifree = 0; ibmap = malloc(fs->e2fs_bsize); bbmap = malloc(fs->e2fs_bsize); if (ibmap == NULL || bbmap == NULL) { errexit("out of memory\n"); } for (c = 0; c < fs->e2fs_ncg; c++) { u_int32_t nbfree = 0; u_int32_t nifree = 0; u_int32_t ndirs = 0; nbfree = 0; nifree = fs->e2fs.e2fs_ipg; ndirs = 0; if (blk_bitmap == NULL) { blk_bitmap = getdatablk(fs2h32(fs->e2fs_gd[c].ext2bgd_b_bitmap), fs->e2fs_bsize); } else { getblk(blk_bitmap, fs2h32(fs->e2fs_gd[c].ext2bgd_b_bitmap), fs->e2fs_bsize); } if (ino_bitmap == NULL) { ino_bitmap = getdatablk(fs2h32(fs->e2fs_gd[c].ext2bgd_i_bitmap), fs->e2fs_bsize); } else { getblk(ino_bitmap, fs2h32(fs->e2fs_gd[c].ext2bgd_i_bitmap), fs->e2fs_bsize); } memset(bbmap, 0, fs->e2fs_bsize); memset(ibmap, 0, fs->e2fs_bsize); memset(&idesc[0], 0, sizeof idesc); for (i = 0; i < 3; i++) { idesc[i].id_type = ADDR; } j = fs->e2fs.e2fs_ipg * c + 1; for (i = 0; i < fs->e2fs.e2fs_ipg; j++, i++) { if ((j < EXT2_FIRSTINO) && (j != EXT2_ROOTINO)) { setbit(ibmap, i); nifree--; continue; } if (j > fs->e2fs.e2fs_icount) { setbit(ibmap, i); continue; } switch (statemap[j]) { case USTATE: break; case DSTATE: case DCLEAR: case DFOUND: ndirs++; /* fall through */ case FSTATE: case FCLEAR: nifree--; setbit(ibmap, i); break; default: errexit("BAD STATE %d FOR INODE I=%ld\n", statemap[j], j); } } /* fill in unused par of the inode map */ for (i = fs->e2fs.e2fs_ipg / NBBY; i < fs->e2fs_bsize; i++) ibmap[i] = 0xff; dbase = c * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock; dmax = (c+1) * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock; for (i = 0, d = dbase; d < dmax; d ++, i ++) { if (testbmap(d) || d >= sblock.e2fs.e2fs_bcount) { setbit(bbmap, i); continue; } else { nbfree++; } } cs_nbfree += nbfree; cs_nifree += nifree; cs_ndir += ndirs; if (debug && (fs2h16(fs->e2fs_gd[c].ext2bgd_nbfree) != nbfree || fs2h16(fs->e2fs_gd[c].ext2bgd_nifree) != nifree || fs2h16(fs->e2fs_gd[c].ext2bgd_ndirs) != ndirs)) { printf("summary info for cg %d is %d, %d, %d," "should be %d, %d, %d\n", c, fs2h16(fs->e2fs_gd[c].ext2bgd_nbfree), fs2h16(fs->e2fs_gd[c].ext2bgd_nifree), fs2h16(fs->e2fs_gd[c].ext2bgd_ndirs), nbfree, nifree, ndirs); } (void)snprintf(msg, sizeof(msg), "SUMMARY INFORMATIONS WRONG FOR CG #%d", c); if ((fs2h16(fs->e2fs_gd[c].ext2bgd_nbfree) != nbfree || fs2h16(fs->e2fs_gd[c].ext2bgd_nifree) != nifree || fs2h16(fs->e2fs_gd[c].ext2bgd_ndirs) != ndirs) && dofix(&idesc[0], msg)) { fs->e2fs_gd[c].ext2bgd_nbfree = h2fs16(nbfree); fs->e2fs_gd[c].ext2bgd_nifree = h2fs16(nifree); fs->e2fs_gd[c].ext2bgd_ndirs = h2fs16(ndirs); sbdirty(); } if (debug && memcmp(blk_bitmap->b_un.b_buf, bbmap, fs->e2fs_bsize)) { printf("blk_bitmap:\n"); print_bmap(blk_bitmap->b_un.b_buf, fs->e2fs_bsize); printf("bbmap:\n"); print_bmap(bbmap, fs->e2fs_bsize); } (void)snprintf(msg, sizeof(msg), "BLK(S) MISSING IN BIT MAPS #%d", c); if (memcmp(blk_bitmap->b_un.b_buf, bbmap, fs->e2fs_bsize) && dofix(&idesc[1], msg)) { memcpy(blk_bitmap->b_un.b_buf, bbmap, fs->e2fs_bsize); dirty(blk_bitmap); } if (debug && memcmp(ino_bitmap->b_un.b_buf, ibmap, fs->e2fs_bsize)) { printf("ino_bitmap:\n"); print_bmap(ino_bitmap->b_un.b_buf, fs->e2fs_bsize); printf("ibmap:\n"); print_bmap(ibmap, fs->e2fs_bsize); } (void)snprintf(msg, sizeof(msg), "INODE(S) MISSING IN BIT MAPS #%d", c); if (memcmp(ino_bitmap->b_un.b_buf, ibmap, fs->e2fs_bsize) && dofix(&idesc[1], msg)) { memcpy(ino_bitmap->b_un.b_buf, ibmap, fs->e2fs_bsize); dirty(ino_bitmap); } } if (debug && (fs->e2fs.e2fs_fbcount != cs_nbfree || fs->e2fs.e2fs_ficount != cs_nifree)) { printf("summary info bad in superblock: %d, %d should be %d, %d\n", fs->e2fs.e2fs_fbcount, fs->e2fs.e2fs_ficount, cs_nbfree, cs_nifree); } if ((fs->e2fs.e2fs_fbcount != cs_nbfree || fs->e2fs.e2fs_ficount != cs_nifree) && dofix(&idesc[0], "SUPERBLK SUMMARY INFORMATION BAD")) { fs->e2fs.e2fs_fbcount = cs_nbfree; fs->e2fs.e2fs_ficount = cs_nifree; sbdirty(); } free(ibmap); free(bbmap); }
/* * Given an offset in a file, find the disk block number (not zone!) * that contains that block. */ static int block_map(struct open_file *f, block_t file_block, block_t *disk_block_p) { struct file *fp = (struct file *)f->f_fsdata; struct mfs_sblock *fs = fp->f_fs; uint level; block_t ind_cache; block_t ind_block_num; zone_t zone; size_t rsize; int rc; int boff; int scale = fs->mfs_log_zone_size; /* for block-zone conversion */ block_t *buf = (void *)fp->f_buf; /* * Index structure of an inode: * * mdi_blocks[0..NR_DZONES-1] * hold zone numbers for zones * 0..NR_DZONES-1 * * mdi_blocks[NR_DZONES+0] * block NDADDR+0 is the single indirect block * holds zone numbers for zones * NR_DZONES .. NR_DZONES + MFS_NINDIR(fs)-1 * * mdi_blocks[NR_DZONES+1] * block NDADDR+1 is the double indirect block * holds zone numbers for INDEX blocks for zones * NR_DZONES + MFS_NINDIR(fs) .. * NR_TZONES + MFS_NINDIR(fs) + MFS_NINDIR(fs)**2 - 1 */ zone = file_block >> scale; boff = (int) (file_block - (zone << scale) ); /* relative blk in zone */ if (zone < NR_DZONES) { /* Direct zone */ zone_t z = fs2h32(fp->f_di.mdi_zone[zone]); if (z == NO_ZONE) { *disk_block_p = NO_BLOCK; return 0; } *disk_block_p = (block_t) ((z << scale) + boff); return 0; } zone -= NR_DZONES; ind_cache = zone >> LN2_IND_CACHE_SZ; if (ind_cache == fp->f_ind_cache_block) { *disk_block_p = fs2h32(fp->f_ind_cache[zone & IND_CACHE_MASK]); return 0; } for (level = 0;;) { level += fp->f_nishift; if (zone < (block_t)1 << level) break; if (level > NIADDR * fp->f_nishift) /* Zone number too high */ return EFBIG; zone -= (block_t)1 << level; } ind_block_num = fs2h32(fp->f_di.mdi_zone[NR_DZONES + (level / fp->f_nishift - 1)]); for (;;) { level -= fp->f_nishift; if (ind_block_num == 0) { *disk_block_p = NO_BLOCK; /* missing */ return 0; } twiddle(); /* * If we were feeling brave, we could work out the number * of the disk sector and read a single disk sector instead * of a filesystem block. * However we don't do this very often anyway... */ rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, FSBTODB(fs, ind_block_num), fs->mfs_block_size, buf, &rsize); if (rc) return rc; if (rsize != fs->mfs_block_size) return EIO; ind_block_num = fs2h32(buf[zone >> level]); if (level == 0) break; zone &= (1 << level) - 1; } /* Save the part of the block that contains this sector */ memcpy(fp->f_ind_cache, &buf[zone & ~IND_CACHE_MASK], IND_CACHE_SZ * sizeof fp->f_ind_cache[0]); fp->f_ind_cache_block = ind_cache; zone = (zone_t)ind_block_num; *disk_block_p = (block_t)((zone << scale) + boff); return 0; }
/* * Check if source directory is in the path of the target directory. * Target is supplied locked, source is unlocked. * The target is always vput before returning. */ int ext2fs_checkpath(struct inode *source, struct inode *target, struct ucred *cred) { struct vnode *vp; int error, rootino, namlen; struct ext2fs_dirtemplate dirbuf; u_int32_t ino; vp = ITOV(target); if (target->i_number == source->i_number) { error = EEXIST; goto out; } rootino = ROOTINO; error = 0; if (target->i_number == rootino) goto out; for (;;) { if (vp->v_type != VDIR) { error = ENOTDIR; break; } error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf, sizeof (struct ext2fs_dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, cred, NULL, curproc); if (error != 0) break; namlen = dirbuf.dotdot_namlen; if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { error = ENOTDIR; break; } ino = fs2h32(dirbuf.dotdot_ino); if (ino == source->i_number) { error = EINVAL; break; } if (ino == rootino) break; vput(vp); error = VFS_VGET(vp->v_mount, ino, &vp); if (error != 0) { vp = NULL; break; } } out: if (error == ENOTDIR) { printf("checkpath: .. not a directory\n"); panic("checkpath"); } if (vp != NULL) vput(vp); return (error); }
/* * Read in the super block and its summary info, convert to host byte order. */ static int readsb(int listerr) { daddr_t super = bflag ? bflag : SBOFF / dev_bsize; if (bread(fsreadfd, (char *)sblk.b_un.b_fs, super, (long)SBSIZE) != 0) return 0; sblk.b_bno = super; sblk.b_size = SBSIZE; /* Copy the superblock in memory */ e2fs_sbload(sblk.b_un.b_fs, &sblock.e2fs); /* * run a few consistency checks of the super block */ if (sblock.e2fs.e2fs_magic != E2FS_MAGIC) { badsb(listerr, "MAGIC NUMBER WRONG"); return 0; } if (sblock.e2fs.e2fs_log_bsize > 2) { badsb(listerr, "BAD LOG_BSIZE"); return 0; } if (sblock.e2fs.e2fs_rev > E2FS_REV0 && (!powerof2(sblock.e2fs.e2fs_inode_size) || sblock.e2fs.e2fs_inode_size < EXT2_REV0_DINODE_SIZE || sblock.e2fs.e2fs_inode_size > (1024 << sblock.e2fs.e2fs_log_bsize))) { badsb(listerr, "BAD INODE_SIZE"); return 0; } /* compute the dynamic fields of the in-memory sb */ /* compute dynamic sb infos */ sblock.e2fs_ncg = howmany(sblock.e2fs.e2fs_bcount - sblock.e2fs.e2fs_first_dblock, sblock.e2fs.e2fs_bpg); /* XXX assume hw bsize = 512 */ sblock.e2fs_fsbtodb = sblock.e2fs.e2fs_log_bsize + 1; sblock.e2fs_bsize = 1024 << sblock.e2fs.e2fs_log_bsize; sblock.e2fs_bshift = LOG_MINBSIZE + sblock.e2fs.e2fs_log_bsize; sblock.e2fs_qbmask = sblock.e2fs_bsize - 1; sblock.e2fs_bmask = ~sblock.e2fs_qbmask; sblock.e2fs_ngdb = howmany(sblock.e2fs_ncg, sblock.e2fs_bsize / sizeof(struct ext2_gd)); sblock.e2fs_ipb = sblock.e2fs_bsize / EXT2_DINODE_SIZE(&sblock); sblock.e2fs_itpg = howmany(sblock.e2fs.e2fs_ipg, sblock.e2fs_ipb); /* * Compute block size that the filesystem is based on, * according to fsbtodb, and adjust superblock block number * so we can tell if this is an alternate later. */ super *= dev_bsize; dev_bsize = sblock.e2fs_bsize / EXT2_FSBTODB(&sblock, 1); sblk.b_bno = super / dev_bsize; if (sblock.e2fs_ncg == 1) { /* no alternate superblock; assume it's okay */ havesb = 1; return 1; } getblk(&asblk, 1 * sblock.e2fs.e2fs_bpg + sblock.e2fs.e2fs_first_dblock, (long)SBSIZE); if (asblk.b_errs) return 0; if (bflag) { havesb = 1; return 1; } /* * Set all possible fields that could differ, then do check * of whole super block against an alternate super block. * When an alternate super-block is specified this check is skipped. */ asblk.b_un.b_fs->e2fs_rbcount = sblk.b_un.b_fs->e2fs_rbcount; asblk.b_un.b_fs->e2fs_fbcount = sblk.b_un.b_fs->e2fs_fbcount; asblk.b_un.b_fs->e2fs_ficount = sblk.b_un.b_fs->e2fs_ficount; asblk.b_un.b_fs->e2fs_mtime = sblk.b_un.b_fs->e2fs_mtime; asblk.b_un.b_fs->e2fs_wtime = sblk.b_un.b_fs->e2fs_wtime; asblk.b_un.b_fs->e2fs_mnt_count = sblk.b_un.b_fs->e2fs_mnt_count; asblk.b_un.b_fs->e2fs_max_mnt_count = sblk.b_un.b_fs->e2fs_max_mnt_count; asblk.b_un.b_fs->e2fs_state = sblk.b_un.b_fs->e2fs_state; asblk.b_un.b_fs->e2fs_beh = sblk.b_un.b_fs->e2fs_beh; asblk.b_un.b_fs->e2fs_lastfsck = sblk.b_un.b_fs->e2fs_lastfsck; asblk.b_un.b_fs->e2fs_fsckintv = sblk.b_un.b_fs->e2fs_fsckintv; asblk.b_un.b_fs->e2fs_ruid = sblk.b_un.b_fs->e2fs_ruid; asblk.b_un.b_fs->e2fs_rgid = sblk.b_un.b_fs->e2fs_rgid; asblk.b_un.b_fs->e2fs_block_group_nr = sblk.b_un.b_fs->e2fs_block_group_nr; asblk.b_un.b_fs->e2fs_features_rocompat &= ~EXT2F_ROCOMPAT_LARGEFILE; asblk.b_un.b_fs->e2fs_features_rocompat |= sblk.b_un.b_fs->e2fs_features_rocompat & EXT2F_ROCOMPAT_LARGEFILE; if (sblock.e2fs.e2fs_rev > E2FS_REV0 && ((sblock.e2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP_FSCK) || (sblock.e2fs.e2fs_features_rocompat & ~EXT2F_ROCOMPAT_SUPP_FSCK))) { if (debug) { printf("compat 0x%08x, incompat 0x%08x, compat_ro " "0x%08x\n", sblock.e2fs.e2fs_features_compat, sblock.e2fs.e2fs_features_incompat, sblock.e2fs.e2fs_features_rocompat); if ((sblock.e2fs.e2fs_features_rocompat & ~EXT2F_ROCOMPAT_SUPP_FSCK)) { char buf[512]; snprintb(buf, sizeof(buf), EXT2F_ROCOMPAT_BITS, sblock.e2fs.e2fs_features_rocompat & ~EXT2F_ROCOMPAT_SUPP_FSCK); printf("unsupported rocompat features: %s\n", buf); } if ((sblock.e2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP_FSCK)) { char buf[512]; snprintb(buf, sizeof(buf), EXT2F_INCOMPAT_BITS, sblock.e2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP_FSCK); printf("unsupported incompat features: %s\n", buf); } } badsb(listerr, "INCOMPATIBLE FEATURE BITS IN SUPER BLOCK"); return 0; } if (memcmp(sblk.b_un.b_fs, asblk.b_un.b_fs, SBSIZE)) { if (debug) { u_int32_t *nlp, *olp, *endlp; printf("superblock mismatches\n"); nlp = (u_int32_t *)asblk.b_un.b_fs; olp = (u_int32_t *)sblk.b_un.b_fs; endlp = olp + (SBSIZE / sizeof(*olp)); for ( ; olp < endlp; olp++, nlp++) { if (*olp == *nlp) continue; printf("offset %ld, original %ld, " "alternate %ld\n", (long)(olp - (u_int32_t *)sblk.b_un.b_fs), (long)fs2h32(*olp), (long)fs2h32(*nlp)); } } badsb(listerr, "VALUES IN SUPER BLOCK DISAGREE WITH " "THOSE IN FIRST ALTERNATE"); return 0; } havesb = 1; return 1; }
int ext2fs_bmaparray(struct vnode *vp, #undef struct daddr_t bn, daddr_t *bnp, struct indir *ap, int *nump, int *runp) { struct inode *ip; struct buf *bp, *cbp; #define struct // struct ufsmount *ump; struct mount *mp; #undef struct struct indir a[NIADDR+1], *xap; daddr_t daddr; daddr_t metalbn; int error, maxrun = 0, num; ip = VTOI(vp); mp = EXT2_SIMPLE_FILE_SYSTEM_PRIVATE_DATA_FROM_THIS(vp->Filesystem); // mp = vp->v_mount; !!!! need to fix this badly! // ump = ip->i_ump; NEED TO DO SOMETHING ABOUT ufsmount #ifdef DIAGNOSTIC if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL)) panic("ext2fs_bmaparray: invalid arguments"); #endif if (runp) { /* * XXX * If MAXBSIZE is the largest transfer the disks can handle, * we probably want maxrun to be 1 block less so that we * don't create a block larger than the device can handle. */ *runp = 0; maxrun = MAXBSIZE / //mp->mnt_stat.f_iosize - 1; NEEDS FIX!!! mp->fs->e2fs_bsize - 1; } if (bn >= 0 && bn < NDADDR) { /* XXX ondisk32 */ *bnp = blkptrtodb(ump, fs2h32(ip->i_e2fs_blocks[bn])); if (*bnp == 0) *bnp = -1; else if (runp) /* XXX ondisk32 */ for (++bn; bn < NDADDR && *runp < maxrun && is_sequential(ump, (daddr_t)fs2h32(ip->i_e2fs_blocks[bn - 1]), (daddr_t)fs2h32(ip->i_e2fs_blocks[bn])); ++bn, ++*runp); return (0); } xap = ap == NULL ? a : ap; if (!nump) nump = # if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0) return (error); num = *nump; /* Get disk address out of indirect block array */ /* XXX ondisk32 */ daddr = fs2h32(ip->i_e2fs_blocks[NDADDR + xap->in_off]); #ifdef DIAGNOSTIC if (num > NIADDR + 1 || num < 1) { printf("ext2fs_bmaparray: num=%d\n", num); panic("ext2fs_bmaparray: num"); } #endif for (bp = NULL, ++xap; --num; ++xap) { /* * Exit the loop if there is no disk address assigned yet and * the indirect block isn't in the cache, or if we were * looking for an indirect block and we've found it. */ metalbn = xap->in_lbn; if (metalbn == bn) break; if (daddr == 0) { mutex_enter(&bufcache_lock); cbp = incore(vp, metalbn); mutex_exit(&bufcache_lock); if (cbp == NULL) break; } /* * If we get here, we've either got the block in the cache * or we have a disk address for it, go fetch it. */ if (bp) brelse(bp, 0); xap->in_exists = 1; //!!!!!!!!!!!!!!replaced 3rd param with 1 ftw bp = getblk(vp, metalbn, 1, 0, 0); if (bp == NULL) { /* * getblk() above returns NULL only iff we are * pagedaemon. See the implementation of getblk * for detail. */ return (ENOMEM); } if (bp->b_oflags & (BO_DONE | BO_DELWRI)) { trace(TR_BREADHIT, pack(vp, size), metalbn); } #ifdef DIAGNOSTIC else if (!daddr) panic("ext2fs_bmaparry: indirect block not in cache"); #endif else { trace(TR_BREADMISS, pack(vp, size), metalbn); bp->b_blkno = blkptrtodb(ump, daddr); bp->b_flags |= B_READ; VOP_STRATEGY(vp, bp); // curlwp->l_ru.ru_inblock++; *//* XXX */ if ((error = biowait(bp)) != 0) { brelse(bp, 0); return (error); } } /* XXX ondisk32 */ daddr = fs2h32(((int32_t *)bp->b_data)[xap->in_off]); if (num == 1 && daddr && runp) /* XXX ondisk32 */ for (bn = xap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump, ((int32_t *)bp->b_data)[bn - 1], ((int32_t *)bp->b_data)[bn]); ++bn, ++*runp); } if (bp) brelse(bp, 0); daddr = blkptrtodb(ump, daddr); *bnp = daddr == 0 ? -1 : daddr; return (0); }
/* * Balloc defines the structure of file system storage * by allocating the physical blocks on a device given * the inode and the logical block number in a file. */ int ext2fs_balloc(struct inode *ip, daddr_t bn, int size, kauth_cred_t cred, struct buf **bpp, int flags) { struct m_ext2fs *fs; daddr_t nb; struct buf *bp, *nbp; struct vnode *vp = ITOV(ip); struct indir indirs[EXT2FS_NIADDR + 2]; daddr_t newb, lbn, pref; int32_t *bap; /* XXX ondisk32 */ int num, i, error; u_int deallocated; daddr_t *blkp, *allocblk, allociblk[EXT2FS_NIADDR + 1]; int32_t *allocib; /* XXX ondisk32 */ int unwindidx = -1; UVMHIST_FUNC("ext2fs_balloc"); UVMHIST_CALLED(ubchist); UVMHIST_LOG(ubchist, "bn 0x%x", bn,0,0,0); if (bpp != NULL) { *bpp = NULL; } if (bn < 0) return (EFBIG); fs = ip->i_e2fs; lbn = bn; /* * The first EXT2FS_NDADDR blocks are direct blocks */ if (bn < EXT2FS_NDADDR) { /* XXX ondisk32 */ nb = fs2h32(ip->i_e2fs_blocks[bn]); if (nb != 0) { /* * the block is already allocated, just read it. */ if (bpp != NULL) { error = bread(vp, bn, fs->e2fs_bsize, NOCRED, B_MODIFY, &bp); if (error) { return (error); } *bpp = bp; } return (0); } /* * allocate a new direct block. */ error = ext2fs_alloc(ip, bn, ext2fs_blkpref(ip, bn, bn, &ip->i_e2fs_blocks[0]), cred, &newb); if (error) return (error); ip->i_e2fs_last_lblk = lbn; ip->i_e2fs_last_blk = newb; /* XXX ondisk32 */ ip->i_e2fs_blocks[bn] = h2fs32((int32_t)newb); ip->i_flag |= IN_CHANGE | IN_UPDATE; if (bpp != NULL) { bp = getblk(vp, bn, fs->e2fs_bsize, 0, 0); bp->b_blkno = EXT2_FSBTODB(fs, newb); if (flags & B_CLRBUF) clrbuf(bp); *bpp = bp; } return (0); } /* * Determine the number of levels of indirection. */ pref = 0; if ((error = ufs_getlbns(vp, bn, indirs, &num)) != 0) return(error); #ifdef DIAGNOSTIC if (num < 1) panic ("ext2fs_balloc: ufs_getlbns returned indirect block\n"); #endif /* * Fetch the first indirect block allocating if necessary. */ --num; /* XXX ondisk32 */ nb = fs2h32(ip->i_e2fs_blocks[EXT2FS_NDADDR + indirs[0].in_off]); allocib = NULL; allocblk = allociblk; if (nb == 0) { pref = ext2fs_blkpref(ip, lbn, 0, (int32_t *)0); error = ext2fs_alloc(ip, lbn, pref, cred, &newb); if (error) return (error); nb = newb; *allocblk++ = nb; ip->i_e2fs_last_blk = newb; bp = getblk(vp, indirs[1].in_lbn, fs->e2fs_bsize, 0, 0); bp->b_blkno = EXT2_FSBTODB(fs, newb); clrbuf(bp); /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(bp)) != 0) goto fail; unwindidx = 0; allocib = &ip->i_e2fs_blocks[EXT2FS_NDADDR + indirs[0].in_off]; /* XXX ondisk32 */ *allocib = h2fs32((int32_t)newb); ip->i_flag |= IN_CHANGE | IN_UPDATE; } /* * Fetch through the indirect blocks, allocating as necessary. */ for (i = 1;;) { error = bread(vp, indirs[i].in_lbn, (int)fs->e2fs_bsize, NOCRED, 0, &bp); if (error) { goto fail; } bap = (int32_t *)bp->b_data; /* XXX ondisk32 */ nb = fs2h32(bap[indirs[i].in_off]); if (i == num) break; i++; if (nb != 0) { brelse(bp, 0); continue; } pref = ext2fs_blkpref(ip, lbn, 0, (int32_t *)0); error = ext2fs_alloc(ip, lbn, pref, cred, &newb); if (error) { brelse(bp, 0); goto fail; } nb = newb; *allocblk++ = nb; ip->i_e2fs_last_blk = newb; nbp = getblk(vp, indirs[i].in_lbn, fs->e2fs_bsize, 0, 0); nbp->b_blkno = EXT2_FSBTODB(fs, nb); clrbuf(nbp); /* * Write synchronously so that indirect blocks * never point at garbage. */ if ((error = bwrite(nbp)) != 0) { brelse(bp, 0); goto fail; } if (unwindidx < 0) unwindidx = i - 1; /* XXX ondisk32 */ bap[indirs[i - 1].in_off] = h2fs32((int32_t)nb); /* * If required, write synchronously, otherwise use * delayed write. */ if (flags & B_SYNC) { bwrite(bp); } else { bdwrite(bp); } } /* * Get the data block, allocating if necessary. */ if (nb == 0) { pref = ext2fs_blkpref(ip, lbn, indirs[num].in_off, &bap[0]); error = ext2fs_alloc(ip, lbn, pref, cred, &newb); if (error) { brelse(bp, 0); goto fail; } nb = newb; *allocblk++ = nb; ip->i_e2fs_last_lblk = lbn; ip->i_e2fs_last_blk = newb; /* XXX ondisk32 */ bap[indirs[num].in_off] = h2fs32((int32_t)nb); /* * If required, write synchronously, otherwise use * delayed write. */ if (flags & B_SYNC) { bwrite(bp); } else { bdwrite(bp); } if (bpp != NULL) { nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0); nbp->b_blkno = EXT2_FSBTODB(fs, nb); if (flags & B_CLRBUF) clrbuf(nbp); *bpp = nbp; } return (0); } brelse(bp, 0); if (bpp != NULL) { if (flags & B_CLRBUF) { error = bread(vp, lbn, (int)fs->e2fs_bsize, NOCRED, B_MODIFY, &nbp); if (error) { goto fail; } } else { nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0); nbp->b_blkno = EXT2_FSBTODB(fs, nb); } *bpp = nbp; } return (0); fail: /* * If we have failed part way through block allocation, we * have to deallocate any indirect blocks that we have allocated. */ for (deallocated = 0, blkp = allociblk; blkp < allocblk; blkp++) { ext2fs_blkfree(ip, *blkp); deallocated += fs->e2fs_bsize; } if (unwindidx >= 0) { if (unwindidx == 0) { *allocib = 0; } else { int r; r = bread(vp, indirs[unwindidx].in_lbn, (int)fs->e2fs_bsize, NOCRED, B_MODIFY, &bp); if (r) { panic("Could not unwind indirect block, error %d", r); } else { bap = (int32_t *)bp->b_data; /* XXX ondisk32 */ bap[indirs[unwindidx].in_off] = 0; if (flags & B_SYNC) bwrite(bp); else bdwrite(bp); } } for (i = unwindidx + 1; i <= num; i++) { bp = getblk(vp, indirs[i].in_lbn, (int)fs->e2fs_bsize, 0, 0); brelse(bp, BC_INVAL); } } if (deallocated) { ext2fs_setnblock(ip, ext2fs_nblock(ip) - btodb(deallocated)); ip->i_e2fs_flags |= IN_CHANGE | IN_UPDATE; } return error; }