/* * Allocate an inode in the file system. * * we leave the actual allocation strategy to the (modified) * ext2_new_inode(), to make sure we get the policies right */ int ext2_valloc(struct vnode *pvp, int mode, struct ucred *cred, struct vnode **vpp) { struct inode *pip; struct ext2_sb_info *fs; struct inode *ip; ino_t ino; int i, error; *vpp = NULL; pip = VTOI(pvp); fs = pip->i_e2fs; if (fs->s_es->s_free_inodes_count == 0) goto noinodes; /* call the Linux routine - it returns the inode number only */ ino = ext2_new_inode(pip, mode); if (ino == 0) goto noinodes; error = VFS_VGET(pvp->v_mount, NULL, ino, vpp); if (error) { EXT2_VFREE(pvp, ino, mode); return (error); } ip = VTOI(*vpp); /* the question is whether using VGET was such good idea at all - Linux doesn't read the old inode in when it's allocating a new one. I will set at least i_size & i_blocks the zero. */ ip->i_mode = 0; ip->i_size = 0; ip->i_blocks = 0; ip->i_flags = 0; /* now we want to make sure that the block pointers are zeroed out */ for (i = 0; i < NDADDR; i++) ip->i_db[i] = 0; for (i = 0; i < NIADDR; i++) ip->i_ib[i] = 0; /* * Set up a new generation number for this inode. * XXX check if this makes sense in ext2 */ if (ip->i_gen == 0 || ++ip->i_gen == 0) ip->i_gen = krandom() / 2 + 1; /* kprintf("ext2_valloc: allocated inode %d\n", ino); */ return (0); noinodes: ext2_fserr(fs, cred->cr_uid, "out of inodes"); uprintf("\n%s: create/symlink failed, no inodes free\n", fs->fs_fsmnt); return (ENOSPC); }
/* * Allocate a block in the filesystem. * * A preference may be optionally specified. If a preference is given * the following hierarchy is used to allocate a block: * 1) allocate the requested block. * 2) allocate a rotationally optimal block in the same cylinder. * 3) allocate a block in the same cylinder group. * 4) quadradically rehash into other cylinder groups, until an * available block is located. * If no block preference is given the following hierarchy is used * to allocate a block: * 1) allocate a block in the cylinder group that contains the * inode for the file. * 2) quadradically rehash into other cylinder groups, until an * available block is located. */ int ext2_alloc(struct inode *ip, daddr_t lbn, e4fs_daddr_t bpref, int size, struct ucred *cred, e4fs_daddr_t *bnp) { struct m_ext2fs *fs; struct ext2mount *ump; int32_t bno; int cg; *bnp = 0; fs = ip->i_e2fs; ump = ip->i_ump; mtx_assert(EXT2_MTX(ump), MA_OWNED); #ifdef INVARIANTS if ((u_int)size > fs->e2fs_bsize || blkoff(fs, size) != 0) { vn_printf(ip->i_devvp, "bsize = %lu, size = %d, fs = %s\n", (long unsigned int)fs->e2fs_bsize, size, fs->e2fs_fsmnt); panic("ext2_alloc: bad size"); } if (cred == NOCRED) panic("ext2_alloc: missing credential"); #endif /* INVARIANTS */ if (size == fs->e2fs_bsize && fs->e2fs->e2fs_fbcount == 0) goto nospace; if (cred->cr_uid != 0 && fs->e2fs->e2fs_fbcount < fs->e2fs->e2fs_rbcount) goto nospace; if (bpref >= fs->e2fs->e2fs_bcount) bpref = 0; if (bpref == 0) cg = ino_to_cg(fs, ip->i_number); else cg = dtog(fs, bpref); bno = (daddr_t)ext2_hashalloc(ip, cg, bpref, fs->e2fs_bsize, ext2_alloccg); if (bno > 0) { /* set next_alloc fields as done in block_getblk */ ip->i_next_alloc_block = lbn; ip->i_next_alloc_goal = bno; ip->i_blocks += btodb(fs->e2fs_bsize); ip->i_flag |= IN_CHANGE | IN_UPDATE; *bnp = bno; return (0); } nospace: EXT2_UNLOCK(ump); ext2_fserr(fs, cred->cr_uid, "filesystem full"); uprintf("\n%s: write failed, filesystem is full\n", fs->e2fs_fsmnt); return (ENOSPC); }
/* * Free a block or fragment. * */ void ext2_blkfree(struct inode *ip, e4fs_daddr_t bno, long size) { struct m_ext2fs *fs; struct buf *bp; struct ext2mount *ump; int cg, error; char *bbp; fs = ip->i_e2fs; ump = ip->i_ump; cg = dtog(fs, bno); if ((u_int)bno >= fs->e2fs->e2fs_bcount) { printf("bad block %lld, ino %llu\n", (long long)bno, (unsigned long long)ip->i_number); ext2_fserr(fs, ip->i_uid, "bad block"); return; } error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_b_bitmap), (int)fs->e2fs_bsize, NOCRED, &bp); if (error) { brelse(bp); return; } bbp = (char *)bp->b_data; bno = dtogd(fs, bno); if (isclr(bbp, bno)) { printf("block = %lld, fs = %s\n", (long long)bno, fs->e2fs_fsmnt); panic("ext2_blkfree: freeing free block"); } clrbit(bbp, bno); EXT2_LOCK(ump); ext2_clusteracct(fs, bbp, cg, bno, 1); fs->e2fs->e2fs_fbcount++; fs->e2fs_gd[cg].ext2bgd_nbfree++; fs->e2fs_fmod = 1; EXT2_UNLOCK(ump); bdwrite(bp); }
/* * inode to raw ext2 inode */ int ext2_i2ei(struct inode *ip, struct ext2fs_dinode *ei) { struct m_ext2fs *fs; fs = ip->i_e2fs; ei->e2di_mode = ip->i_mode; ei->e2di_nlink = ip->i_nlink; /* * Godmar thinks: if dtime is nonzero, ext2 says this inode has been * deleted, this would correspond to a zero link count */ ei->e2di_dtime = ei->e2di_nlink ? 0 : ip->i_mtime; ei->e2di_size = ip->i_size; if (S_ISREG(ip->i_mode)) ei->e2di_size_high = ip->i_size >> 32; ei->e2di_atime = ip->i_atime; ei->e2di_mtime = ip->i_mtime; ei->e2di_ctime = ip->i_ctime; if (E2DI_HAS_XTIME(ip)) { ei->e2di_ctime_extra = NSEC_TO_XTIME(ip->i_ctimensec); ei->e2di_mtime_extra = NSEC_TO_XTIME(ip->i_mtimensec); ei->e2di_atime_extra = NSEC_TO_XTIME(ip->i_atimensec); ei->e2di_crtime = ip->i_birthtime; ei->e2di_crtime_extra = NSEC_TO_XTIME(ip->i_birthnsec); } ei->e2di_flags = 0; ei->e2di_flags |= (ip->i_flags & SF_APPEND) ? EXT2_APPEND : 0; ei->e2di_flags |= (ip->i_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE : 0; ei->e2di_flags |= (ip->i_flags & UF_NODUMP) ? EXT2_NODUMP : 0; ei->e2di_flags |= (ip->i_flag & IN_E3INDEX) ? EXT3_INDEX : 0; ei->e2di_flags |= (ip->i_flag & IN_E4EXTENTS) ? EXT4_EXTENTS : 0; if (ip->i_blocks > ~0U && !EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_HUGE_FILE)) { ext2_fserr(fs, ip->i_uid, "i_blocks value is out of range"); return (EIO); } if (ip->i_blocks <= 0xffffffffffffULL) { ei->e2di_nblock = ip->i_blocks & 0xffffffff; ei->e2di_nblock_high = ip->i_blocks >> 32 & 0xffff; } else {
/* * Allocate a block in the file system. * * this takes the framework from ffs_alloc. To implement the * actual allocation, it calls ext2_new_block, the ported version * of the same Linux routine. * * we note that this is always called in connection with ext2_blkpref * * preallocation is done as Linux does it */ int ext2_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size, struct ucred *cred, daddr_t *bnp) { struct ext2_sb_info *fs; daddr_t bno; #if QUOTA int error; #endif *bnp = 0; fs = ip->i_e2fs; #if DIAGNOSTIC if ((u_int)size > fs->s_blocksize || blkoff(fs, size) != 0) { kprintf("dev = %s, bsize = %lu, size = %d, fs = %s\n", devtoname(ip->i_dev), fs->s_blocksize, size, fs->fs_fsmnt); panic("ext2_alloc: bad size"); } if (cred == NOCRED) panic("ext2_alloc: missing credential"); #endif /* DIAGNOSTIC */ if (size == fs->s_blocksize && fs->s_es->s_free_blocks_count == 0) goto nospace; if (cred->cr_uid != 0 && fs->s_es->s_free_blocks_count < fs->s_es->s_r_blocks_count) goto nospace; #if QUOTA if ((error = ext2_chkdq(ip, (long)btodb(size), cred, 0)) != 0) return (error); #endif if (bpref >= fs->s_es->s_blocks_count) bpref = 0; /* call the Linux code */ #ifdef EXT2_PREALLOCATE /* To have a preallocation hit, we must * - have at least one block preallocated * - and our preferred block must have that block number or one below */ if (ip->i_prealloc_count && (bpref == ip->i_prealloc_block || bpref + 1 == ip->i_prealloc_block)) { bno = ip->i_prealloc_block++; ip->i_prealloc_count--; /* ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); */ /* Linux gets, clears, and releases the buffer at this point - we don't have to that; we leave it to the caller */ } else { ext2_discard_prealloc (ip); /* ext2_debug ("preallocation miss (%lu/%lu).\n", alloc_hits, ++alloc_attempts); */ if (S_ISREG(ip->i_mode)) bno = ext2_new_block (ITOV(ip)->v_mount, bpref, &ip->i_prealloc_count, &ip->i_prealloc_block); else bno = (daddr_t)ext2_new_block(ITOV(ip)->v_mount, bpref, 0, 0); } #else bno = (daddr_t)ext2_new_block(ITOV(ip)->v_mount, bpref, 0, 0); #endif if (bno > 0) { /* set next_alloc fields as done in block_getblk */ ip->i_next_alloc_block = lbn; ip->i_next_alloc_goal = bno; ip->i_blocks += btodb(size); ip->i_flag |= IN_CHANGE | IN_UPDATE; *bnp = bno; return (0); } #if QUOTA /* * Restore user's disk quota because allocation failed. */ ext2_chkdq(ip, (long)-btodb(size), cred, FORCE); #endif nospace: ext2_fserr(fs, cred->cr_uid, "file system full"); uprintf("\n%s: write failed, file system is full\n", fs->fs_fsmnt); return (ENOSPC); }
/* * Allocate an inode in the filesystem. * */ int ext2_valloc(struct vnode *pvp, int mode, struct ucred *cred, struct vnode **vpp) { struct timespec ts; struct inode *pip; struct m_ext2fs *fs; struct inode *ip; struct ext2mount *ump; ino_t ino, ipref; int i, error, cg; *vpp = NULL; pip = VTOI(pvp); fs = pip->i_e2fs; ump = pip->i_ump; EXT2_LOCK(ump); if (fs->e2fs->e2fs_ficount == 0) goto noinodes; /* * If it is a directory then obtain a cylinder group based on * ext2_dirpref else obtain it using ino_to_cg. The preferred inode is * always the next inode. */ if ((mode & IFMT) == IFDIR) { cg = ext2_dirpref(pip); if (fs->e2fs_contigdirs[cg] < 255) fs->e2fs_contigdirs[cg]++; } else { cg = ino_to_cg(fs, pip->i_number); if (fs->e2fs_contigdirs[cg] > 0) fs->e2fs_contigdirs[cg]--; } ipref = cg * fs->e2fs->e2fs_ipg + 1; ino = (ino_t)ext2_hashalloc(pip, cg, (long)ipref, mode, ext2_nodealloccg); if (ino == 0) goto noinodes; error = VFS_VGET(pvp->v_mount, ino, LK_EXCLUSIVE, vpp); if (error) { ext2_vfree(pvp, ino, mode); return (error); } ip = VTOI(*vpp); /* * The question is whether using VGET was such good idea at all: * Linux doesn't read the old inode in when it is allocating a * new one. I will set at least i_size and i_blocks to zero. */ ip->i_size = 0; ip->i_blocks = 0; ip->i_mode = 0; ip->i_flags = 0; /* now we want to make sure that the block pointers are zeroed out */ for (i = 0; i < NDADDR; i++) ip->i_db[i] = 0; for (i = 0; i < NIADDR; i++) ip->i_ib[i] = 0; /* * Set up a new generation number for this inode. * XXX check if this makes sense in ext2 */ if (ip->i_gen == 0 || ++ip->i_gen == 0) ip->i_gen = random() / 2 + 1; vfs_timestamp(&ts); ip->i_birthtime = ts.tv_sec; ip->i_birthnsec = ts.tv_nsec; /* printf("ext2_valloc: allocated inode %d\n", ino); */ return (0); noinodes: EXT2_UNLOCK(ump); ext2_fserr(fs, cred->cr_uid, "out of inodes"); uprintf("\n%s: create/symlink failed, no inodes free\n", fs->e2fs_fsmnt); return (ENOSPC); }