#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/bitmap.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/disp.h>
#include <sys/dnlc.h>
#include <sys/mode.h>
#include <sys/cmn_err.h>
#include <sys/kstat.h>
#include <sys/acl.h>
#include <sys/var.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_trans.h>
#include <sys/fs/ufs_acl.h>
#include <sys/fs/ufs_bio.h>
#include <sys/fs/ufs_quota.h>
#include <sys/fs/ufs_log.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/pvn.h>
#include <vm/seg.h>
#include <sys/swap.h>
#include <sys/cpuvar.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <fs/fs_subr.h>
#include <sys/policy.h>
struct kmem_cache *inode_cache;
struct instats ins = {
{ "size", KSTAT_DATA_ULONG },
{ "maxsize", KSTAT_DATA_ULONG },
{ "hits", KSTAT_DATA_ULONG },
{ "misses", KSTAT_DATA_ULONG },
{ "kmem allocs", KSTAT_DATA_ULONG },
{ "kmem frees", KSTAT_DATA_ULONG },
{ "maxsize reached", KSTAT_DATA_ULONG },
{ "puts at frontlist", KSTAT_DATA_ULONG },
{ "puts at backlist", KSTAT_DATA_ULONG },
{ "queues to free", KSTAT_DATA_ULONG },
{ "scans", KSTAT_DATA_ULONG },
{ "thread idles", KSTAT_DATA_ULONG },
{ "lookup idles", KSTAT_DATA_ULONG },
{ "vget idles", KSTAT_DATA_ULONG },
{ "cache allocs", KSTAT_DATA_ULONG },
{ "cache frees", KSTAT_DATA_ULONG },
{ "pushes at close", KSTAT_DATA_ULONG }
};
static kstat_t *ufs_inode_kstat = NULL;
union ihead *ihead;
kmutex_t *ih_lock;
static int ino_hashlen = 4;
int inohsz;
struct timeval32 iuniqtime;
kmutex_t ufs_scan_lock;
kmutex_t ufs_iuniqtime_lock;
kmutex_t ufsvfs_mutex;
struct ufsvfs *oldufsvfslist, *ufsvfslist;
clock_t ufs_iowait;
int ufs_idle_max;
ulong_t ufs_inode_max;
#define UFS_IDLE_MAX (16)
#define UFS_HW_DEFAULT (16 * 1024 * 1024)
#define UFS_LW_DEFAULT (8 * 1024 * 1024)
int ufs_HW = UFS_HW_DEFAULT;
int ufs_LW = UFS_LW_DEFAULT;
static void ihinit(void);
extern int hash2ints(int, int);
static int ufs_iget_internal(struct vfs *, ino_t, struct inode **,
struct cred *, int);
static int
ufs_inode_kstat_update(kstat_t *ksp, int rw)
{
if (rw == KSTAT_WRITE)
return (EACCES);
ins.in_malloc.value.ul = (ulong_t)kmem_cache_stat(inode_cache,
"slab_alloc");
ins.in_mfree.value.ul = (ulong_t)kmem_cache_stat(inode_cache,
"slab_free");
ins.in_kcalloc.value.ul = (ulong_t)kmem_cache_stat(inode_cache,
"alloc");
ins.in_kcfree.value.ul = (ulong_t)kmem_cache_stat(inode_cache,
"free");
ins.in_size.value.ul = (ulong_t)kmem_cache_stat(inode_cache,
"buf_inuse");
ins.in_maxreached.value.ul = (ulong_t)kmem_cache_stat(inode_cache,
"buf_max");
ins.in_misses.value.ul = ins.in_kcalloc.value.ul;
return (0);
}
void
ufs_iinit(void)
{
if (ufs_HW <= ufs_LW) {
cmn_err(CE_WARN,
"ufs_HW (%d) <= ufs_LW (%d). Check /etc/system.",
ufs_HW, ufs_LW);
ufs_LW = UFS_LW_DEFAULT;
ufs_HW = UFS_HW_DEFAULT;
cmn_err(CE_CONT, "using defaults, ufs_HW = %d, ufs_LW = %d\n",
ufs_HW, ufs_LW);
}
if (ufs_ninode <= 0)
ufs_ninode = ncsize;
if (ufs_inode_max == 0)
ufs_inode_max =
(ulong_t)((kmem_maxavail() >> 2) / sizeof (struct inode));
if (ufs_ninode > ufs_inode_max || (ufs_ninode == 0 && ncsize == 0)) {
cmn_err(CE_NOTE, "setting ufs_ninode to max value of %ld",
ufs_inode_max);
ufs_ninode = ufs_inode_max;
}
ufs_iowait = v.v_autoup * hz * 2;
if (ufs_idle_max == 0)
ufs_idle_max = ufs_ninode >> 2;
if (ufs_idle_max < UFS_IDLE_MAX)
ufs_idle_max = UFS_IDLE_MAX;
if (ufs_idle_max > ufs_ninode)
ufs_idle_max = ufs_ninode;
ufs_thread_init(&ufs_idle_q, ufs_idle_max);
ufs_thread_start(&ufs_idle_q, ufs_thread_idle, NULL);
ufs_thread_init(&ufs_hlock, 1);
ufs_thread_start(&ufs_hlock, ufs_thread_hlock, NULL);
ihinit();
qtinit();
ins.in_maxsize.value.ul = ufs_ninode;
if ((ufs_inode_kstat = kstat_create("ufs", 0, "inode_cache", "ufs",
KSTAT_TYPE_NAMED, sizeof (ins) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL)) != NULL) {
ufs_inode_kstat->ks_data = (void *)&ins;
ufs_inode_kstat->ks_update = ufs_inode_kstat_update;
kstat_install(ufs_inode_kstat);
}
ufsfx_init();
si_cache_init();
ufs_directio_init();
lufs_init();
mutex_init(&ufs_iuniqtime_lock, NULL, MUTEX_DEFAULT, NULL);
}
static int
ufs_inode_cache_constructor(void *buf, void *cdrarg, int kmflags)
{
struct inode *ip = buf;
struct vnode *vp;
vp = ip->i_vnode = vn_alloc(kmflags);
if (vp == NULL) {
return (-1);
}
vn_setops(vp, ufs_vnodeops);
vp->v_data = ip;
rw_init(&ip->i_rwlock, NULL, RW_DEFAULT, NULL);
rw_init(&ip->i_contents, NULL, RW_DEFAULT, NULL);
mutex_init(&ip->i_tlock, NULL, MUTEX_DEFAULT, NULL);
dnlc_dir_init(&ip->i_danchor);
cv_init(&ip->i_wrcv, NULL, CV_DRIVER, NULL);
return (0);
}
static void
ufs_inode_cache_destructor(void *buf, void *cdrarg)
{
struct inode *ip = buf;
struct vnode *vp;
vp = ITOV(ip);
rw_destroy(&ip->i_rwlock);
rw_destroy(&ip->i_contents);
mutex_destroy(&ip->i_tlock);
if (vp->v_type == VDIR) {
dnlc_dir_fini(&ip->i_danchor);
}
cv_destroy(&ip->i_wrcv);
vn_free(vp);
}
void
ihinit(void)
{
int i;
union ihead *ih = ihead;
mutex_init(&ufs_scan_lock, NULL, MUTEX_DEFAULT, NULL);
inohsz = 1 << highbit(ufs_ninode / ino_hashlen);
ihead = kmem_zalloc(inohsz * sizeof (union ihead), KM_SLEEP);
ih_lock = kmem_zalloc(inohsz * sizeof (kmutex_t), KM_SLEEP);
for (i = 0, ih = ihead; i < inohsz; i++, ih++) {
ih->ih_head[0] = ih;
ih->ih_head[1] = ih;
mutex_init(&ih_lock[i], NULL, MUTEX_DEFAULT, NULL);
}
inode_cache = kmem_cache_create("ufs_inode_cache",
sizeof (struct inode), 0, ufs_inode_cache_constructor,
ufs_inode_cache_destructor, ufs_inode_cache_reclaim,
NULL, NULL, 0);
}
void
ufs_free_inode(struct inode *ip)
{
vn_invalid(ITOV(ip));
kmem_cache_free(inode_cache, ip);
}
struct inode *
ufs_alloc_inode(ufsvfs_t *ufsvfsp, ino_t ino)
{
struct inode *ip;
vnode_t *vp;
ip = kmem_cache_alloc(inode_cache, KM_SLEEP);
ip->i_freef = ip;
ip->i_freeb = ip;
ip->i_flag = IREF;
ip->i_seq = 0xFF;
ip->i_dev = ufsvfsp->vfs_dev;
ip->i_ufsvfs = ufsvfsp;
ip->i_devvp = ufsvfsp->vfs_devvp;
ip->i_number = ino;
ip->i_diroff = 0;
ip->i_nextr = 0;
ip->i_map = NULL;
ip->i_rdev = 0;
ip->i_writes = 0;
ip->i_mode = 0;
ip->i_delaylen = 0;
ip->i_delayoff = 0;
ip->i_nextrio = 0;
ip->i_ufs_acl = NULL;
ip->i_cflags = 0;
ip->i_mapcnt = 0;
ip->i_dquot = NULL;
ip->i_cachedir = CD_ENABLED;
ip->i_writer = NULL;
vp = ITOV(ip);
vn_reinit(vp);
if (ino == (ino_t)UFSROOTINO)
vp->v_flag = VROOT;
vp->v_vfsp = ufsvfsp->vfs_vfs;
vn_exists(vp);
return (ip);
}
int
ufs_iget(struct vfs *vfsp, ino_t ino, struct inode **ipp, struct cred *cr)
{
return (ufs_iget_internal(vfsp, ino, ipp, cr, 0));
}
int
ufs_iget_alloced(struct vfs *vfsp, ino_t ino, struct inode **ipp,
struct cred *cr)
{
return (ufs_iget_internal(vfsp, ino, ipp, cr, 1));
}
void
ufs_reset_vnode(vnode_t *vp)
{
if ((VTOI(vp)->i_mode & (ISVTX | IEXEC | IFDIR)) == ISVTX)
vp->v_flag |= VSWAPLIKE;
else
vp->v_flag &= ~VSWAPLIKE;
if (vp->v_type == VREG)
vp->v_flag |= VMODSORT;
else
vp->v_flag &= ~VMODSORT;
if ((VTOI(vp)->i_mode & IFMT) == IFATTRDIR)
vp->v_flag |= V_XATTRDIR;
else
vp->v_flag &= ~V_XATTRDIR;
}
static int
ufs_iget_internal(struct vfs *vfsp, ino_t ino, struct inode **ipp,
struct cred *cr, int validate)
{
struct inode *ip, *sp;
union ihead *ih;
kmutex_t *ihm;
struct buf *bp;
struct dinode *dp;
struct vnode *vp;
extern vfs_t EIO_vfs;
int error;
int ftype;
dev_t vfs_dev;
struct ufsvfs *ufsvfsp;
struct fs *fs;
int hno;
daddr_t bno;
ulong_t ioff;
CPU_STATS_ADD_K(sys, ufsiget, 1);
vfs_dev = vfsp->vfs_dev;
hno = INOHASH(ino);
ih = &ihead[hno];
ihm = &ih_lock[hno];
again:
mutex_enter(ihm);
for (ip = ih->ih_chain[0]; ip != (struct inode *)ih; ip = ip->i_forw) {
if (ino != ip->i_number || vfs_dev != ip->i_dev ||
(ip->i_flag & ISTALE))
continue;
vp = ITOV(ip);
VN_HOLD(vp);
mutex_exit(ihm);
rw_enter(&ip->i_contents, RW_READER);
if ((ip->i_flag & IREF) == 0) {
if (ufs_rmidle(ip))
VN_RELE(vp);
}
if (ip->i_flag & ISTALE) {
rw_exit(&ip->i_contents);
VN_RELE(vp);
goto again;
}
ins.in_hits.value.ul++;
*ipp = ip;
mutex_enter(&vp->v_lock);
ufs_reset_vnode(vp);
mutex_exit(&vp->v_lock);
rw_exit(&ip->i_contents);
return (0);
}
mutex_exit(ihm);
ufsvfsp = (struct ufsvfs *)vfsp->vfs_data;
fs = ufsvfsp->vfs_fs;
ip = ufs_alloc_inode(ufsvfsp, ino);
vp = ITOV(ip);
bno = fsbtodb(fs, itod(fs, ino));
ioff = (sizeof (struct dinode)) * (itoo(fs, ino));
ip->i_doff = (offset_t)ioff + ldbtob(bno);
mutex_enter(ihm);
for (sp = ih->ih_chain[0]; sp != (struct inode *)ih; sp = sp->i_forw)
if (ino == sp->i_number && vfs_dev == sp->i_dev &&
((sp->i_flag & ISTALE) == 0)) {
mutex_exit(ihm);
ufs_free_inode(ip);
goto again;
}
rw_enter(&ip->i_contents, RW_WRITER);
insque(ip, ih);
mutex_exit(ihm);
bp = UFS_BREAD(ufsvfsp, ip->i_dev, bno, (int)fs->fs_bsize);
error = ((bp->b_flags & B_ERROR) ? geterror(bp) : 0);
if (error) {
brelse(bp);
ip->i_flag |= ISTALE;
rw_exit(&ip->i_contents);
vp->v_vfsp = &EIO_vfs;
VN_RELE(vp);
return (error);
}
dp = (struct dinode *)(ioff + bp->b_un.b_addr);
ip->i_ic = dp->di_ic;
brelse(bp);
if (ip->i_suid != UID_LONG)
ip->i_uid = ip->i_suid;
if (ip->i_sgid != GID_LONG)
ip->i_gid = ip->i_sgid;
ftype = ip->i_mode & IFMT;
if (ftype == IFBLK || ftype == IFCHR) {
dev_t dv;
uint_t top16 = ip->i_ordev & 0xffff0000u;
if (top16 == 0 || top16 == 0xffff0000u)
dv = expdev(ip->i_ordev);
else
dv = expldev(ip->i_ordev);
vp->v_rdev = ip->i_rdev = dv;
}
if (validate) {
if ((ftype == 0) || (ip->i_nlink <= 0)) {
ip->i_flag |= ISTALE;
rw_exit(&ip->i_contents);
vp->v_vfsp = &EIO_vfs;
VN_RELE(vp);
cmn_err(CE_NOTE,
"%s: unexpected free inode %d, run fsck(8)%s",
fs->fs_fsmnt, (int)ino,
(TRANS_ISTRANS(ufsvfsp) ? " -o f" : ""));
return (EIO);
}
}
if (ftype == IFSHAD) {
vp->v_type = VREG;
} else {
vp->v_type = IFTOVT((mode_t)ip->i_mode);
}
ufs_reset_vnode(vp);
if (ftype != 0 && ip->i_shadow != 0) {
if ((error = ufs_si_load(ip, cr)) != 0) {
ip->i_flag |= ISTALE;
ip->i_ufs_acl = NULL;
rw_exit(&ip->i_contents);
vp->v_vfsp = &EIO_vfs;
VN_RELE(vp);
return (error);
}
}
if (ip->i_mode && ((ip->i_mode & IFMT) != IFSHAD) &&
((ip->i_mode & IFMT) != IFATTRDIR)) {
ip->i_dquot = getinoquota(ip);
}
TRANS_MATA_IGET(ufsvfsp, ip);
*ipp = ip;
rw_exit(&ip->i_contents);
return (0);
}
void
ufs_iinactive(struct inode *ip)
{
int front;
struct inode *iq;
struct inode *hip;
struct ufs_q *uq;
struct vnode *vp = ITOV(ip);
struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
struct ufs_delq_info *delq_info = &ufsvfsp->vfs_delete_info;
dnlc_dir_purge(&ip->i_danchor);
rw_enter(&ip->i_contents, RW_WRITER);
ASSERT(ip->i_flag & IREF);
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
VN_RELE_LOCKED(vp);
mutex_exit(&vp->v_lock);
rw_exit(&ip->i_contents);
return;
}
mutex_exit(&vp->v_lock);
if (ip->i_ufsvfs == NULL) {
rw_exit(&ip->i_contents);
ufs_si_del(ip);
ASSERT((vp->v_type == VCHR) || !vn_has_cached_data(vp));
ufs_free_inode(ip);
return;
}
front = 1;
if ((ip->i_flag & ISTALE) == 0 && ip->i_fs->fs_ronly == 0 &&
ip->i_mode && ip->i_nlink <= 0) {
ip->i_flag |= IDEL;
if (ULOCKFS_IS_NOIDEL(ITOUL(ip))) {
mutex_enter(&vp->v_lock);
VN_RELE_LOCKED(vp);
mutex_exit(&vp->v_lock);
rw_exit(&ip->i_contents);
return;
}
if (!TRANS_ISTRANS(ip->i_ufsvfs)) {
rw_exit(&ip->i_contents);
ufs_delete(ip->i_ufsvfs, ip, 0);
return;
}
ins.in_qfree.value.ul++;
uq = &ip->i_ufsvfs->vfs_delete;
mutex_enter(&uq->uq_mutex);
if ((iq = uq->uq_ihead) != 0) {
ip->i_freef = iq;
ip->i_freeb = iq->i_freeb;
iq->i_freeb->i_freef = ip;
iq->i_freeb = ip;
if (front)
uq->uq_ihead = ip;
} else {
uq->uq_ihead = ip;
ip->i_freef = ip;
ip->i_freeb = ip;
}
delq_info->delq_unreclaimed_files += 1;
delq_info->delq_unreclaimed_blocks += ip->i_blocks;
} else {
mutex_enter(&vp->v_lock);
if (vp->v_count > 1) {
VN_RELE_LOCKED(vp);
mutex_exit(&vp->v_lock);
rw_exit(&ip->i_contents);
return;
}
mutex_exit(&vp->v_lock);
uq = &ufs_idle_q;
mutex_enter(&uq->uq_mutex);
ip->i_flag &= ~(IREF | IDIRECTIO);
if (vn_has_cached_data(vp) || ip->i_flag & IFASTSYMLNK) {
ins.in_frback.value.ul++;
hip = (inode_t *)&ufs_useful_iq[IQHASH(ip)];
ufs_nuseful_iq++;
} else {
ins.in_frfront.value.ul++;
hip = (inode_t *)&ufs_junk_iq[IQHASH(ip)];
ip->i_flag |= IJUNKIQ;
ufs_njunk_iq++;
}
ip->i_freef = hip;
ip->i_freeb = hip->i_freeb;
hip->i_freeb->i_freef = ip;
hip->i_freeb = ip;
}
if (++uq->uq_ne == uq->uq_lowat)
cv_broadcast(&uq->uq_cv);
mutex_exit(&uq->uq_mutex);
rw_exit(&ip->i_contents);
}
void
ufs_iupdat(struct inode *ip, int waitfor)
{
struct buf *bp;
struct fs *fp;
struct dinode *dp;
struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
int i;
int do_trans_times;
ushort_t flag;
o_uid_t suid;
o_gid_t sgid;
ASSERT(RW_LOCK_HELD(&ip->i_contents));
if (ufsvfsp == NULL)
return;
flag = ip->i_flag;
if (flag & ISTALE)
return;
fp = ip->i_fs;
if ((flag & (IUPD|IACC|ICHG|IMOD|IMODACC|IATTCHG)) != 0) {
if (fp->fs_ronly) {
mutex_enter(&ip->i_tlock);
ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD|IMODACC|IATTCHG);
mutex_exit(&ip->i_tlock);
return;
}
mutex_enter(&ufsvfsp->vfs_lock);
ufs_notclean(ufsvfsp);
bp = UFS_BREAD(ufsvfsp, ip->i_dev,
(daddr_t)fsbtodb(fp, itod(fp, ip->i_number)),
(int)fp->fs_bsize);
if (bp->b_flags & B_ERROR) {
mutex_enter(&ip->i_tlock);
ip->i_flag &=
~(IUPD|IACC|ICHG|IMOD|IMODACC|IATTCHG);
mutex_exit(&ip->i_tlock);
brelse(bp);
return;
}
mutex_enter(&ip->i_tlock);
ITIMES_NOLOCK(ip);
do_trans_times = ((ip->i_flag & (IMOD|IMODACC)) == IMODACC);
ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD|IMODACC|IATTCHG);
mutex_exit(&ip->i_tlock);
if (do_trans_times) {
TRANS_INODE_TIMES(ufsvfsp, ip);
}
suid = (ulong_t)ip->i_uid > (ulong_t)USHRT_MAX ?
UID_LONG : ip->i_uid;
sgid = (ulong_t)ip->i_gid > (ulong_t)USHRT_MAX ?
GID_LONG : ip->i_gid;
if ((ip->i_suid != suid) || (ip->i_sgid != sgid)) {
ip->i_suid = suid;
ip->i_sgid = sgid;
TRANS_INODE(ufsvfsp, ip);
}
if ((ip->i_mode & IFMT) == IFBLK ||
(ip->i_mode & IFMT) == IFCHR) {
dev_t d = ip->i_rdev;
dev32_t dev32;
if (!cmpldev(&dev32, d)) {
panic("ip %p: i_rdev too big", (void *)ip);
}
if (dev32 & ~((O_MAXMAJ << L_BITSMINOR32) | O_MAXMIN)) {
ip->i_ordev = dev32;
} else {
ip->i_ordev = cmpdev(d);
}
}
dp = (struct dinode *)bp->b_un.b_addr + itoo(fp, ip->i_number);
dp->di_ic = ip->i_ic;
if (flag & IFASTSYMLNK) {
for (i = 1; i < NDADDR; i++)
dp->di_db[i] = 0;
for (i = 0; i < NIADDR; i++)
dp->di_ib[i] = 0;
}
if (TRANS_ISTRANS(ufsvfsp)) {
TRANS_LOG(ufsvfsp, (caddr_t)dp, ip->i_doff,
sizeof (struct dinode),
(caddr_t)P2ALIGN((uintptr_t)dp, DEV_BSIZE),
DEV_BSIZE);
brelse(bp);
} else if (waitfor && (ip->i_ufsvfs->vfs_dio == 0)) {
UFS_BRWRITE(ufsvfsp, bp);
mutex_enter(&ip->i_tlock);
ip->i_flag &= ~IBDWRITE;
mutex_exit(&ip->i_tlock);
} else {
bdrwrite(bp);
mutex_enter(&ip->i_tlock);
ip->i_flag |= IBDWRITE;
mutex_exit(&ip->i_tlock);
}
} else {
if (waitfor && (flag & IBDWRITE)) {
blkflush(ip->i_dev,
(daddr_t)fsbtodb(fp, itod(fp, ip->i_number)));
mutex_enter(&ip->i_tlock);
ip->i_flag &= ~IBDWRITE;
mutex_exit(&ip->i_tlock);
}
}
}
#define SINGLE 0
#define DOUBLE 1
#define TRIPLE 2
static long
indirtrunc(struct inode *ip, daddr_t bn, daddr_t lastbn, int level, int flags)
{
int i;
struct buf *bp, *copy;
daddr32_t *bap;
struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
struct fs *fs = ufsvfsp->vfs_fs;
daddr_t nb, last;
long factor;
int blocksreleased = 0, nblocks;
ASSERT(RW_WRITE_HELD(&ip->i_contents));
factor = 1;
for (i = SINGLE; i < level; i++)
factor *= NINDIR(fs);
last = lastbn;
if (lastbn > 0)
last /= factor;
nblocks = btodb(fs->fs_bsize);
bp = UFS_BREAD(ufsvfsp,
ip->i_dev, (daddr_t)fsbtodb(fs, bn), (int)fs->fs_bsize);
if (bp->b_flags & B_ERROR) {
brelse(bp);
return (0);
}
bap = bp->b_un.b_daddr;
if ((flags & I_CHEAP) == 0) {
uint_t zb;
zb = (uint_t)((NINDIR(fs) - (last + 1)) * sizeof (daddr32_t));
if (zb) {
if (bp->b_flags & B_DELWRI)
TRANS_LOG(ufsvfsp, (caddr_t)bap,
ldbtob(bp->b_blkno), bp->b_bcount,
bp->b_un.b_addr, bp->b_bcount);
copy = ngeteblk(fs->fs_bsize);
bcopy((caddr_t)bap, (caddr_t)copy->b_un.b_daddr,
(uint_t)fs->fs_bsize);
bzero((caddr_t)&bap[last + 1], zb);
TRANS_BUF(ufsvfsp,
(caddr_t)&bap[last + 1] - (caddr_t)bap,
zb, bp, DT_ABZERO);
UFS_BRWRITE(ufsvfsp, bp);
bp = copy, bap = bp->b_un.b_daddr;
}
} else {
bp->b_flags &= ~(B_DELWRI | B_RETRYWRI);
bp->b_flags |= B_STALE | B_AGE;
}
flags |= I_CHEAP;
for (i = NINDIR(fs) - 1; i > last; i--) {
nb = bap[i];
if (nb == 0)
continue;
if (level > SINGLE) {
blocksreleased +=
indirtrunc(ip, nb, (daddr_t)-1, level - 1, flags);
free(ip, nb, (off_t)fs->fs_bsize, flags | I_IBLK);
} else
free(ip, nb, (off_t)fs->fs_bsize, flags);
blocksreleased += nblocks;
}
flags &= ~I_CHEAP;
if (level > SINGLE && lastbn >= 0) {
last = lastbn % factor;
nb = bap[i];
if (nb != 0)
blocksreleased +=
indirtrunc(ip, nb, last, level - 1, flags);
}
brelse(bp);
return (blocksreleased);
}
static int i_genrand = 1234;
int
ufs_itrunc(struct inode *oip, u_offset_t length, int flags, cred_t *cr)
{
struct fs *fs = oip->i_fs;
struct ufsvfs *ufsvfsp = oip->i_ufsvfs;
struct inode *ip;
daddr_t lastblock;
off_t bsize;
int boff;
daddr_t bn, lastiblock[NIADDR];
int level;
long nblocks, blocksreleased = 0;
int i;
ushort_t mode;
struct inode tip;
int err;
u_offset_t maxoffset = (ufsvfsp->vfs_lfflags & UFS_LARGEFILES) ?
(UFS_MAXOFFSET_T) : (MAXOFF32_T);
ASSERT((oip->i_mode & IFMT) == IFSHAD ||
RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
ASSERT(RW_WRITE_HELD(&oip->i_contents));
TRANS_INODE(ufsvfsp, oip);
mode = oip->i_mode & IFMT;
if (flags & I_FREE) {
i_genrand *= 16843009;
i_genrand++;
oip->i_gen += ((i_genrand + ddi_get_lbolt()) & 0xffff) + 1;
oip->i_flag |= ICHG |IUPD;
oip->i_seq++;
if (length == oip->i_size)
return (0);
flags |= I_CHEAP;
}
if (mode == IFIFO)
return (0);
if (mode != IFREG && mode != IFDIR && mode != IFATTRDIR &&
!(mode == IFLNK && length == (offset_t)0) && mode != IFSHAD)
return (EINVAL);
if (length > maxoffset)
return (EFBIG);
if ((mode == IFDIR) || (mode == IFATTRDIR))
flags |= I_DIR;
if (mode == IFSHAD)
flags |= I_SHAD;
if (oip == ufsvfsp->vfs_qinod)
flags |= I_QUOTA;
if (length == oip->i_size) {
oip->i_flag |= ICHG |IUPD;
oip->i_seq++;
if (length == 0) {
oip->i_flag &= ~IFASTSYMLNK;
}
return (0);
}
if (oip->i_flag & IFASTSYMLNK) {
int j;
ASSERT(ITOV(oip)->v_type == VLNK);
oip->i_flag &= ~IFASTSYMLNK;
for (j = 1; j < NDADDR; j++)
oip->i_db[j] = 0;
for (j = 0; j < NIADDR; j++)
oip->i_ib[j] = 0;
}
boff = (int)blkoff(fs, length);
if (length > oip->i_size) {
if (boff == 0)
err = BMAPALLOC(oip, length - 1, (int)fs->fs_bsize, cr);
else
err = BMAPALLOC(oip, length - 1, boff, cr);
if (err == 0) {
u_offset_t osize = oip->i_size;
oip->i_size = length;
if ((boff = (int)blkoff(fs, osize)) != 0) {
bsize = (int)lblkno(fs, osize - 1) >= NDADDR ?
fs->fs_bsize : fragroundup(fs, boff);
pvn_vpzero(ITOV(oip), osize,
(size_t)(bsize - boff));
}
oip->i_flag |= ICHG|IATTCHG;
oip->i_seq++;
ITIMES_NOLOCK(oip);
if ((length > (u_offset_t)MAXOFF32_T) &&
!(fs->fs_flags & FSLARGEFILES)) {
ASSERT(ufsvfsp->vfs_lfflags & UFS_LARGEFILES);
mutex_enter(&ufsvfsp->vfs_lock);
fs->fs_flags |= FSLARGEFILES;
ufs_sbwrite(ufsvfsp);
mutex_exit(&ufsvfsp->vfs_lock);
}
}
return (err);
}
if (boff == 0) {
(void) pvn_vplist_dirty(ITOV(oip), length, ufs_putapage,
B_INVAL | B_TRUNC, CRED());
} else {
err = BMAPALLOC(oip, length - 1, boff, cr);
if (err)
return (err);
if (oip->i_flag & (ICHG|IUPD))
oip->i_seq++;
{
uio_t uio;
iovec_t iov[1];
char buffer;
uio.uio_iov = iov;
uio.uio_iovcnt = 1;
uio.uio_loffset = length - 1;
uio.uio_resid = 1;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_extflg = UIO_COPY_CACHED;
iov[0].iov_base = &buffer;
iov[0].iov_len = 1;
err = rdip(oip, &uio, UIO_READ, NULL);
if (err)
return (err);
}
bsize = (int)lblkno(fs, length - 1) >= NDADDR ?
fs->fs_bsize : fragroundup(fs, boff);
pvn_vpzero(ITOV(oip), length, (size_t)(bsize - boff));
(void) pvn_vplist_dirty(ITOV(oip), length + (bsize - boff),
ufs_putapage, B_INVAL | B_TRUNC, CRED());
}
lastblock = lblkno(fs, length + fs->fs_bsize - 1) - 1;
lastiblock[SINGLE] = lastblock - NDADDR;
lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
nblocks = btodb(fs->fs_bsize);
tip = *oip;
ip = &tip;
for (level = TRIPLE; level >= SINGLE; level--)
if (lastiblock[level] < 0) {
oip->i_ib[level] = 0;
lastiblock[level] = -1;
}
for (i = NDADDR - 1; i > lastblock; i--) {
oip->i_db[i] = 0;
flags |= I_CHEAP;
}
oip->i_size = length;
oip->i_flag |= ICHG|IUPD|IATTCHG;
oip->i_seq++;
if (!TRANS_ISTRANS(ufsvfsp))
ufs_iupdat(oip, I_SYNC);
for (level = TRIPLE; level >= SINGLE; level--) {
bn = ip->i_ib[level];
if (bn != 0) {
blocksreleased +=
indirtrunc(ip, bn, lastiblock[level], level, flags);
if (lastiblock[level] < 0) {
ip->i_ib[level] = 0;
free(ip, bn, (off_t)fs->fs_bsize,
flags | I_IBLK);
blocksreleased += nblocks;
}
}
if (lastiblock[level] >= 0)
goto done;
}
for (i = NDADDR - 1; i > lastblock; i--) {
bn = ip->i_db[i];
if (bn == 0)
continue;
ip->i_db[i] = 0;
bsize = (off_t)blksize(fs, ip, i);
free(ip, bn, bsize, flags);
blocksreleased += btodb(bsize);
}
if (lastblock < 0)
goto done;
bn = ip->i_db[lastblock];
if (bn != 0) {
off_t oldspace, newspace;
oldspace = blksize(fs, ip, lastblock);
UFS_SET_ISIZE(length, ip);
newspace = blksize(fs, ip, lastblock);
if (newspace == 0) {
err = ufs_fault(ITOV(ip), "ufs_itrunc: newspace == 0");
return (err);
}
if (oldspace - newspace > 0) {
bn += numfrags(fs, newspace);
free(ip, bn, oldspace - newspace, flags);
blocksreleased += btodb(oldspace - newspace);
}
}
done:
for (level = SINGLE; level <= TRIPLE; level++)
if (ip->i_ib[level] != oip->i_ib[level]) {
err = ufs_fault(ITOV(ip), "ufs_itrunc: indirect block");
return (err);
}
for (i = 0; i < NDADDR; i++)
if (ip->i_db[i] != oip->i_db[i]) {
err = ufs_fault(ITOV(ip), "ufs_itrunc: direct block");
return (err);
}
oip->i_blocks -= blocksreleased;
if (oip->i_blocks < 0) {
cmn_err(CE_NOTE,
"ufs_itrunc: %s/%d new size = %lld, blocks = %d\n",
fs->fs_fsmnt, (int)oip->i_number, oip->i_size,
(int)oip->i_blocks);
oip->i_blocks = 0;
}
oip->i_flag |= ICHG|IATTCHG;
oip->i_seq++;
(void) chkdq(oip, -blocksreleased, 0, cr, (char **)NULL,
(size_t *)NULL);
return (0);
}
int
ufs_iaccess(struct inode *ip, int mode, struct cred *cr, int dolock)
{
int shift = 0;
int ret = 0;
if (dolock)
rw_enter(&ip->i_contents, RW_READER);
ASSERT(RW_LOCK_HELD(&ip->i_contents));
if (mode & IWRITE) {
if (ip->i_fs->fs_ronly != 0) {
if ((ip->i_mode & IFMT) != IFCHR &&
(ip->i_mode & IFMT) != IFBLK &&
(ip->i_mode & IFMT) != IFIFO) {
ret = EROFS;
goto out;
}
}
}
if (ip->i_ufs_acl && ip->i_ufs_acl->aowner) {
ret = ufs_acl_access(ip, mode, cr);
goto out;
}
if (crgetuid(cr) != ip->i_uid) {
shift += 3;
if (!groupmember((uid_t)ip->i_gid, cr))
shift += 3;
}
ret = secpolicy_vnode_access2(cr, ITOV(ip), ip->i_uid,
ip->i_mode << shift, mode);
out:
if (dolock)
rw_exit(&ip->i_contents);
return (ret);
}
int
ufs_rmidle(struct inode *ip)
{
int rval = 0;
mutex_enter(&ip->i_tlock);
if ((ip->i_flag & IREF) == 0) {
mutex_enter(&ufs_idle_q.uq_mutex);
ip->i_freef->i_freeb = ip->i_freeb;
ip->i_freeb->i_freef = ip->i_freef;
ip->i_freef = ip;
ip->i_freeb = ip;
ip->i_flag |= IREF;
ufs_idle_q.uq_ne--;
if (ip->i_flag & IJUNKIQ) {
ufs_njunk_iq--;
ip->i_flag &= ~IJUNKIQ;
} else {
ufs_nuseful_iq--;
}
mutex_exit(&ufs_idle_q.uq_mutex);
rval = 1;
}
mutex_exit(&ip->i_tlock);
return (rval);
}
int
ufs_scan_inodes(int rwtry, int (*func)(struct inode *, void *), void *arg,
struct ufsvfs *ufsvfsp)
{
struct inode *ip;
struct inode *lip = NULL;
union ihead *ih;
int error, i;
int saverror = 0;
int lip_held;
for (i = 0, ih = ihead; i < inohsz; i++, ih++) {
mutex_enter(&ih_lock[i]);
for (ip = ih->ih_chain[0], lip_held = 0;
ip != (struct inode *)ih;
ip = lip->i_forw) {
ins.in_scan.value.ul++;
if (lip_held)
VN_RELE(ITOV(lip));
lip = ip;
if (ufsvfsp != NULL && ip->i_ufsvfs != ufsvfsp) {
lip_held = 0;
continue;
}
VN_HOLD(ITOV(ip));
lip_held = 1;
mutex_exit(&ih_lock[i]);
if (rwtry) {
if (!rw_tryenter(&ip->i_contents, RW_WRITER)) {
mutex_enter(&ih_lock[i]);
continue;
}
} else {
rw_enter(&ip->i_contents, RW_WRITER);
}
rw_exit(&ip->i_contents);
if ((ip->i_flag & ISTALE) == 0) {
if ((error = (*func)(ip, arg)) != 0)
saverror = error;
}
mutex_enter(&ih_lock[i]);
}
if (lip_held)
VN_RELE(ITOV(lip));
mutex_exit(&ih_lock[i]);
}
return (saverror);
}
void
ufs_imark(struct inode *ip)
{
timestruc_t now;
int32_t usec, nsec;
if (ip->i_flag & ISEQ) {
ASSERT(ip->i_flag & (IUPD|ICHG));
ip->i_seq++;
ip->i_flag &= ~ISEQ;
}
gethrestime(&now);
nsec = now.tv_nsec;
usec = nsec + (nsec >> 2);
usec = nsec + (usec >> 1);
usec = nsec + (usec >> 2);
usec = nsec + (usec >> 4);
usec = nsec - (usec >> 3);
usec = nsec + (usec >> 2);
usec = nsec + (usec >> 3);
usec = nsec + (usec >> 4);
usec = nsec + (usec >> 1);
usec = nsec + (usec >> 6);
usec = usec >> 10;
mutex_enter(&ufs_iuniqtime_lock);
if (now.tv_sec > (time_t)iuniqtime.tv_sec ||
usec > iuniqtime.tv_usec) {
if (now.tv_sec < TIME32_MAX) {
iuniqtime.tv_sec = (time32_t)now.tv_sec;
iuniqtime.tv_usec = usec;
}
} else {
if (iuniqtime.tv_sec < TIME32_MAX) {
iuniqtime.tv_usec++;
if (iuniqtime.tv_usec >= MICROSEC) {
iuniqtime.tv_sec++;
iuniqtime.tv_usec = 0;
}
}
}
if ((ip->i_flag & IACC) && !(ip->i_ufsvfs->vfs_noatime)) {
ip->i_atime = iuniqtime;
}
if (ip->i_flag & IUPD) {
ip->i_mtime = iuniqtime;
ip->i_flag |= IMODTIME;
}
if (ip->i_flag & ICHG) {
ip->i_diroff = 0;
ip->i_ctime = iuniqtime;
}
mutex_exit(&ufs_iuniqtime_lock);
}
void
ufs_itimes_nolock(struct inode *ip)
{
if (((ip->i_flag & (IUPD|IACC|ICHG)) == IACC) &&
(ip->i_ufsvfs->vfs_noatime)) {
return;
}
if (ip->i_flag & (IUPD|IACC|ICHG)) {
if (ip->i_flag & ICHG)
ip->i_flag |= IMOD;
else
ip->i_flag |= IMODACC;
ufs_imark(ip);
ip->i_flag &= ~(IACC|IUPD|ICHG);
}
}