#include <rev_endian_fs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/file_internal.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/kauth.h>
#include <sys/conf.h>
#include <sys/mount_internal.h>
#include <sys/vnode_internal.h>
#include <sys/malloc.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/ubc.h>
#include <sys/quota.h>
#include <sys/uio_internal.h>
#include <kern/thread.h>
#include <sys/vm.h>
#include <miscfs/specfs/specdev.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#if REV_ENDIAN_FS
#include <ufs/ufs/ufs_byte_order.h>
#endif
static int ufs_chmod(struct vnode *, int, kauth_cred_t, struct proc *);
static int ufs_chown(struct vnode *, uid_t, gid_t, kauth_cred_t,
struct proc *);
static int filt_ufsread(struct knote *kn, long hint);
static int filt_ufswrite(struct knote *kn, long hint);
static int filt_ufsvnode(struct knote *kn, long hint);
static void filt_ufsdetach(struct knote *kn);
#if FIFO
extern void fifo_printinfo(struct vnode *vp);
#endif
extern int ufs_direnter2(struct vnode *dvp, struct direct *dirp,
vfs_context_t ctx);
static int ufs_readdirext(vnode_t vp, uio_t uio, int *eofflag, int *numdirent,
vfs_context_t context);
int
ufs_create(ap)
struct vnop_create_args *ap;
{
int error;
if ( (error = ufs_makeinode(ap->a_vap, ap->a_dvp, ap->a_vpp, ap->a_cnp)) )
return (error);
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
return (0);
}
int
ufs_mknod(ap)
struct vnop_mknod_args *ap;
{
struct vnode_attr *vap = ap->a_vap;
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
struct vnode *tvp;
struct inode *ip;
struct componentname *cnp = ap->a_cnp;
int error;
cnp->cn_flags &= ~MODMASK;
cnp->cn_flags |= (WANTPARENT | NOCACHE);
cnp->cn_nameiop = CREATE;
(void) relookup(dvp, &tvp, cnp);
if (tvp)
vnode_put(tvp);
if ( (error =
ufs_makeinode(ap->a_vap, ap->a_dvp, vpp, ap->a_cnp)) )
return (error);
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
ip = VTOI(*vpp);
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
if (vap->va_rdev != VNOVAL) {
ip->i_rdev = vap->va_rdev;
}
return (0);
}
int
ufs_open(ap)
struct vnop_open_args *ap;
{
if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
return (0);
}
int
ufs_close(ap)
struct vnop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct inode *ip = VTOI(vp);
struct timeval tv;
if (vnode_isinuse(vp, 1)) {
microtime(&tv);
ITIMES(ip, &tv, &tv);
}
cluster_push(vp, IO_CLOSE);
return (0);
}
int
ufs_getattr(ap)
struct vnop_getattr_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct inode *ip = VTOI(vp);
register struct vnode_attr *vap = ap->a_vap;
int devBlockSize=0;
struct timeval tv;
microtime(&tv);
ITIMES(ip, &tv, &tv);
VATTR_RETURN(vap, va_fsid, ip->i_dev);
VATTR_RETURN(vap, va_fileid, ip->i_number);
VATTR_RETURN(vap, va_mode, ip->i_mode & ~IFMT);
VATTR_RETURN(vap, va_nlink, ip->i_nlink);
VATTR_RETURN(vap, va_uid, ip->i_uid);
VATTR_RETURN(vap, va_gid, ip->i_gid);
VATTR_RETURN(vap, va_rdev, (dev_t)ip->i_rdev);
VATTR_RETURN(vap, va_data_size, ip->i_din.di_size);
vap->va_access_time.tv_sec = ip->i_atime;
vap->va_access_time.tv_nsec = ip->i_atimensec;
VATTR_SET_SUPPORTED(vap, va_access_time);
vap->va_modify_time.tv_sec = ip->i_mtime;
vap->va_modify_time.tv_nsec = ip->i_mtimensec;
VATTR_SET_SUPPORTED(vap, va_modify_time);
vap->va_change_time.tv_sec = ip->i_ctime;
vap->va_change_time.tv_nsec = ip->i_ctimensec;
VATTR_SET_SUPPORTED(vap, va_change_time);
VATTR_RETURN(vap, va_flags, ip->i_flags);
VATTR_RETURN(vap, va_gen, ip->i_gen);
if (vp->v_type == VBLK)
VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE);
else if (vp->v_type == VCHR)
VATTR_RETURN(vap, va_iosize, MAXPHYSIO);
else
VATTR_RETURN(vap, va_iosize, vp->v_mount->mnt_vfsstat.f_iosize);
devBlockSize = vfs_devblocksize(vnode_mount(vp));
VATTR_RETURN(vap, va_data_alloc, dbtob((u_quad_t)ip->i_blocks, devBlockSize));
VATTR_RETURN(vap, va_type, vp->v_type);
VATTR_RETURN(vap, va_filerev, ip->i_modrev);
return (0);
}
int
ufs_setattr(ap)
struct vnop_setattr_args *ap;
{
struct vnode_attr *vap = ap->a_vap;
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
kauth_cred_t cred = vfs_context_ucred(ap->a_context);
struct proc *p = vfs_context_proc(ap->a_context);
struct timeval atimeval, mtimeval;
int error;
uid_t nuid;
gid_t ngid;
if (VATTR_IS_ACTIVE(vap, va_flags)) {
ip->i_flags = vap->va_flags;
ip->i_flag |= IN_CHANGE;
}
VATTR_SET_SUPPORTED(vap, va_flags);
nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL;
ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL;
if (nuid != (uid_t)VNOVAL || ngid != (gid_t)VNOVAL) {
if ( (error = ufs_chown(vp, nuid, ngid, cred, p)) )
return (error);
}
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
if (VATTR_IS_ACTIVE(vap, va_data_size)) {
if ( (error = ffs_truncate_internal(vp, vap->va_data_size, vap->va_vaflags & 0xffff, cred)) )
return (error);
}
VATTR_SET_SUPPORTED(vap, va_data_size);
ip = VTOI(vp);
if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
if (VATTR_IS_ACTIVE(vap, va_access_time))
ip->i_flag |= IN_ACCESS;
if (VATTR_IS_ACTIVE(vap, va_modify_time))
ip->i_flag |= IN_CHANGE | IN_UPDATE;
atimeval.tv_sec = vap->va_access_time.tv_sec;
atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000;
mtimeval.tv_sec = vap->va_modify_time.tv_sec;
mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000;
if ( (error = ffs_update(vp, &atimeval, &mtimeval, 1)) )
return (error);
}
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
if (VATTR_IS_ACTIVE(vap, va_mode)) {
if ((error = ufs_chmod(vp, (int)vap->va_mode, cred, p)))
return (error);
}
VATTR_SET_SUPPORTED(vap, va_mode);
VN_KNOTE(vp, NOTE_ATTRIB);
return (0);
}
static int
ufs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct proc *p)
{
register struct inode *ip = VTOI(vp);
ip->i_mode &= ~ALLPERMS;
ip->i_mode |= (mode & ALLPERMS);
ip->i_flag |= IN_CHANGE;
return (0);
}
static int
ufs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
struct proc *p)
{
register struct inode *ip = VTOI(vp);
uid_t ouid;
gid_t ogid;
int error = 0;
int is_member;
#if QUOTA
register int i;
int64_t change;
int devBlockSize=0;
#endif
if (uid == (uid_t)VNOVAL)
uid = ip->i_uid;
if (gid == (gid_t)VNOVAL)
gid = ip->i_gid;
ogid = ip->i_gid;
ouid = ip->i_uid;
#if QUOTA
if ( (error = getinoquota(ip)) )
return (error);
if (ouid == uid) {
dqrele(ip->i_dquot[USRQUOTA]);
ip->i_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(ip->i_dquot[GRPQUOTA]);
ip->i_dquot[GRPQUOTA] = NODQUOT;
}
devBlockSize = vfs_devblocksize(vnode_mount(vp));
change = dbtob((int64_t)ip->i_blocks, devBlockSize);
(void) chkdq(ip, -change, cred, CHOWN);
(void) chkiq(ip, -1, cred, CHOWN);
for (i = 0; i < MAXQUOTAS; i++) {
dqrele(ip->i_dquot[i]);
ip->i_dquot[i] = NODQUOT;
}
#endif
ip->i_gid = gid;
ip->i_uid = uid;
#if QUOTA
if ((error = getinoquota(ip)) == 0) {
if (ouid == uid) {
dqrele(ip->i_dquot[USRQUOTA]);
ip->i_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(ip->i_dquot[GRPQUOTA]);
ip->i_dquot[GRPQUOTA] = NODQUOT;
}
if ((error = chkdq(ip, change, cred, CHOWN)) == 0) {
if ((error = chkiq(ip, 1, cred, CHOWN)) == 0)
goto good;
else
(void) chkdq(ip, -change, cred, CHOWN|FORCE);
}
for (i = 0; i < MAXQUOTAS; i++) {
dqrele(ip->i_dquot[i]);
ip->i_dquot[i] = NODQUOT;
}
}
ip->i_gid = ogid;
ip->i_uid = ouid;
if (getinoquota(ip) == 0) {
if (ouid == uid) {
dqrele(ip->i_dquot[USRQUOTA]);
ip->i_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(ip->i_dquot[GRPQUOTA]);
ip->i_dquot[GRPQUOTA] = NODQUOT;
}
(void) chkdq(ip, change, cred, FORCE|CHOWN);
(void) chkiq(ip, 1, cred, FORCE|CHOWN);
(void) getinoquota(ip);
}
return (error);
good:
if (getinoquota(ip))
panic("chown: lost quota");
#endif
if (ouid != uid || ogid != gid)
ip->i_flag |= IN_CHANGE;
return (0);
}
int
ufs_ioctl(ap)
struct vnop_ioctl_args *ap;
{
switch (ap->a_command) {
case 1:
{ register struct inode *ip;
register struct vnode *vp;
register struct fs *fs;
register struct radvisory *ra;
int devBlockSize = 0;
int error;
vp = ap->a_vp;
ra = (struct radvisory *)(ap->a_data);
ip = VTOI(vp);
fs = ip->i_fs;
if ((u_int64_t)ra->ra_offset >= ip->i_size) {
return (EFBIG);
}
devBlockSize = vfs_devblocksize(vnode_mount(vp));
error = advisory_read(vp, ip->i_size, ra->ra_offset, ra->ra_count);
return (error);
}
default:
return (ENOTTY);
}
}
int
ufs_select(__unused struct vnop_select_args *ap)
{
return (1);
}
int
ufs_mmap(__unused struct vnop_mmap_args *ap)
{
return (EINVAL);
}
int
ufs_remove(ap)
struct vnop_remove_args *ap;
{
return(ufs_remove_internal(ap->a_dvp, ap->a_vp, ap->a_cnp, ap->a_flags));
}
int
ufs_remove_internal(vnode_t dvp, vnode_t vp, struct componentname *cnp, int flags)
{
struct inode *ip;
struct vnode *tvp;
int error;
if (flags & VNODE_REMOVE_NODELETEBUSY) {
if (vnode_isinuse(vp, 0)) {
error = EBUSY;
goto out;
}
}
cnp->cn_flags &= ~MODMASK;
cnp->cn_flags |= (WANTPARENT | NOCACHE);
cnp->cn_nameiop = DELETE;
(void) relookup(dvp, &tvp, cnp);
if (tvp == NULL)
return (ENOENT);
if (tvp != vp) {
vnode_put(tvp);
return ENOENT;
}
vnode_put(tvp);
ip = VTOI(vp);
if ((error = ufs_dirremove(dvp, cnp)) == 0) {
ip->i_nlink--;
ip->i_flag |= IN_CHANGE;
VN_KNOTE(vp, NOTE_DELETE);
VN_KNOTE(dvp, NOTE_WRITE);
}
out:
return (error);
}
int
ufs_link(ap)
struct vnop_link_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode *tdvp = ap->a_tdvp;
struct componentname *cnp = ap->a_cnp;
vfs_context_t ctx = cnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
struct inode *ip;
struct timeval tv;
int error;
ip = VTOI(vp);
if ((nlink_t)ip->i_nlink >= LINK_MAX) {
error = EMLINK;
goto out1;
}
ip->i_nlink++;
ip->i_flag |= IN_CHANGE;
microtime(&tv);
error = ffs_update(vp, &tv, &tv, 1);
if (!error)
error = ufs_direnter(ip, tdvp, cnp);
if (error) {
ip->i_nlink--;
ip->i_flag |= IN_CHANGE;
}
VN_KNOTE(vp, NOTE_LINK);
VN_KNOTE(tdvp, NOTE_WRITE);
out1:
return (error);
}
int
ufs_whiteout(ap)
struct vnop_whiteout_args *ap;
{
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
struct direct newdir;
int error = 0;
switch (ap->a_flags) {
case LOOKUP:
if (dvp->v_mount->mnt_maxsymlinklen > 0)
return (0);
return (ENOTSUP);
case CREATE:
#if DIAGNOSTIC
if (dvp->v_mount->mnt_maxsymlinklen <= 0)
panic("ufs_whiteout: old format filesystem");
#endif
newdir.d_ino = WINO;
newdir.d_namlen = cnp->cn_namelen;
bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1);
newdir.d_type = DT_WHT;
error = ufs_direnter2(dvp, &newdir, cnp->cn_context);
break;
case DELETE:
#if DIAGNOSTIC
if (dvp->v_mount->mnt_maxsymlinklen <= 0)
panic("ufs_whiteout: old format filesystem");
#endif
cnp->cn_flags &= ~DOWHITEOUT;
error = ufs_dirremove(dvp, cnp);
break;
}
return (error);
}
int
ufs_rename(ap)
struct vnop_rename_args *ap;
{
struct vnode *tvp = ap->a_tvp;
register struct vnode *tdvp = ap->a_tdvp;
struct vnode *fvp = ap->a_fvp;
struct vnode *fdvp = ap->a_fdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
vfs_context_t ctx = fcnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
struct inode *ip, *xp, *dp;
struct dirtemplate dirbuf;
struct timeval tv;
ino_t doingdirectory = 0, oldparent = 0, newparent = 0;
int error = 0, ioflag;
u_char namlen;
struct vnode *rl_vp = NULL;
if (fvp == tvp) {
if (fvp->v_type == VDIR) {
#ifdef UFS_RENAME_DEBUG
printf("ufs_rename: fvp == tvp for directories\n");
#endif
error = ENOENT;
goto abortit;
}
error = ufs_remove_internal(fdvp, fvp, fcnp, 0);
return (error);
}
tcnp->cn_flags &= ~MODMASK;
tcnp->cn_flags |= (WANTPARENT | NOCACHE);
if ( (error = relookup(tdvp, &rl_vp, tcnp)) )
panic("ufs_rename: relookup on target returned error");
if (rl_vp != tvp) {
if (rl_vp)
vnode_put(rl_vp);
error = ERESTART;
goto abortit;
}
if (rl_vp) {
vnode_put(rl_vp);
rl_vp = NULL;
}
dp = VTOI(fdvp);
ip = VTOI(fvp);
if ((ip->i_mode & IFMT) == IFDIR) {
if (ip->i_flag & IN_RENAME) {
error = EINVAL;
goto abortit;
}
ip->i_flag |= IN_RENAME;
oldparent = dp->i_number;
doingdirectory++;
}
VN_KNOTE(fdvp, NOTE_WRITE);
dp = VTOI(tdvp);
xp = NULL;
if (tvp)
xp = VTOI(tvp);
ip->i_nlink++;
ip->i_flag |= IN_CHANGE;
microtime(&tv);
if ( (error = ffs_update(fvp, &tv, &tv, 1)) ) {
goto bad;
}
if (oldparent != dp->i_number)
newparent = dp->i_number;
if (doingdirectory && newparent) {
if (error)
goto bad;
if ( (error = ufs_checkpath(ip, dp, vfs_context_ucred(tcnp->cn_context))) )
goto bad;
if ( (error = relookup(tdvp, &tvp, tcnp)) )
goto bad;
rl_vp = tvp;
dp = VTOI(tdvp);
if (tvp)
xp = VTOI(tvp);
else
xp = NULL;
}
if (xp == NULL) {
if (dp->i_dev != ip->i_dev)
panic("rename: EXDEV");
if (doingdirectory && newparent) {
if ((nlink_t)dp->i_nlink >= LINK_MAX) {
error = EMLINK;
goto bad;
}
dp->i_nlink++;
dp->i_flag |= IN_CHANGE;
if ( (error = ffs_update(tdvp, &tv, &tv, 1)) )
goto bad;
}
if ( (error = ufs_direnter(ip, tdvp, tcnp)) ) {
if (doingdirectory && newparent) {
dp->i_nlink--;
dp->i_flag |= IN_CHANGE;
(void)ffs_update(tdvp, &tv, &tv, 1);
}
goto bad;
}
VN_KNOTE(tdvp, NOTE_WRITE);
} else {
if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
panic("rename: EXDEV");
if (xp->i_number == ip->i_number)
panic("rename: same file");
if ((xp->i_mode&IFMT) == IFDIR) {
if (!ufs_dirempty(xp, dp->i_number, vfs_context_ucred(tcnp->cn_context)) ||
xp->i_nlink > 2) {
error = ENOTEMPTY;
goto bad;
}
if (!doingdirectory) {
error = ENOTDIR;
goto bad;
}
cache_purge(tdvp);
} else if (doingdirectory) {
error = EISDIR;
goto bad;
}
if ( (error = ufs_dirrewrite(dp, ip, tcnp)) )
goto bad;
if (doingdirectory && !newparent) {
dp->i_nlink--;
dp->i_flag |= IN_CHANGE;
}
VN_KNOTE(tdvp, NOTE_WRITE);
xp->i_nlink--;
if (doingdirectory) {
if (--xp->i_nlink != 0)
panic("rename: linked directory");
ioflag = ((tvp)->v_mount->mnt_flag & MNT_ASYNC) ?
0 : IO_SYNC;
error = ffs_truncate_internal(tvp, (off_t)0, ioflag, vfs_context_ucred(tcnp->cn_context));
}
xp->i_flag |= IN_CHANGE;
VN_KNOTE(tvp, NOTE_DELETE);
xp = NULL;
}
if (rl_vp)
vnode_put(rl_vp);
rl_vp = NULL;
fcnp->cn_flags &= ~MODMASK;
fcnp->cn_flags |= (WANTPARENT | NOCACHE);
(void) relookup(fdvp, &fvp, fcnp);
if (fvp != NULL) {
xp = VTOI(fvp);
dp = VTOI(fdvp);
rl_vp = fvp;
} else {
if (doingdirectory)
panic("rename: lost dir entry");
return (0);
}
if (xp != ip) {
if (doingdirectory)
panic("rename: lost dir entry");
} else {
if (doingdirectory && newparent) {
dp->i_nlink--;
dp->i_flag |= IN_CHANGE;
error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
sizeof (struct dirtemplate), (off_t)0,
UIO_SYSSPACE32, IO_NODELOCKED,
vfs_context_ucred(tcnp->cn_context), (int *)0, (struct proc *)0);
if (error == 0) {
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (fvp->v_mount->mnt_maxsymlinklen <= 0)
namlen = dirbuf.dotdot_type;
else
namlen = dirbuf.dotdot_namlen;
# else
namlen = dirbuf.dotdot_namlen;
# endif
if (namlen != 2 ||
dirbuf.dotdot_name[0] != '.' ||
dirbuf.dotdot_name[1] != '.') {
ufs_dirbad(xp, (doff_t)12,
"rename: mangled dir");
} else {
dirbuf.dotdot_ino = newparent;
(void) vn_rdwr(UIO_WRITE, fvp,
(caddr_t)&dirbuf,
sizeof (struct dirtemplate),
(off_t)0, UIO_SYSSPACE32,
IO_NODELOCKED|IO_SYNC,
vfs_context_ucred(tcnp->cn_context), (int *)0,
(struct proc *)0);
cache_purge(fdvp);
}
}
}
error = ufs_dirremove(fdvp, fcnp);
if (!error) {
xp->i_nlink--;
xp->i_flag |= IN_CHANGE;
}
xp->i_flag &= ~IN_RENAME;
}
VN_KNOTE(fvp, NOTE_RENAME);
if (rl_vp)
vnode_put(rl_vp);
return (error);
bad:
if (rl_vp)
vnode_put(rl_vp);
if (doingdirectory)
ip->i_flag &= ~IN_RENAME;
ip->i_nlink--;
ip->i_flag |= IN_CHANGE;
ip->i_flag &= ~IN_RENAME;
abortit:
return (error);
}
static struct dirtemplate mastertemplate = {
0, 12, DT_DIR, 1, ".",
0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
};
static struct odirtemplate omastertemplate = {
0, 12, 1, ".",
0, DIRBLKSIZ - 12, 2, ".."
};
int
ufs_mkdir(ap)
struct vnop_mkdir_args *ap;
{
register struct vnode *dvp = ap->a_dvp;
register struct vnode_attr *vap = ap->a_vap;
register struct componentname *cnp = ap->a_cnp;
register struct inode *ip, *dp;
struct vnode *tvp;
struct dirtemplate dirtemplate, *dtp;
struct timeval tv;
int error, dmode;
cnp->cn_flags &= ~MODMASK;
cnp->cn_flags |= (WANTPARENT | NOCACHE);
cnp->cn_nameiop = CREATE;
(void) relookup(dvp, &tvp, cnp);
if (tvp)
vnode_put(tvp);
dp = VTOI(dvp);
if ((nlink_t)dp->i_nlink >= LINK_MAX) {
error = EMLINK;
goto out;
}
dmode = vap->va_mode & 0777;
dmode |= IFDIR;
if ( (error = ffs_valloc(dvp, (mode_t)dmode, vfs_context_ucred(cnp->cn_context), &tvp)) )
goto out;
ip = VTOI(tvp);
ip->i_uid = ap->a_vap->va_uid;
ip->i_gid = ap->a_vap->va_gid;
VATTR_SET_SUPPORTED(ap->a_vap, va_mode);
VATTR_SET_SUPPORTED(ap->a_vap, va_uid);
VATTR_SET_SUPPORTED(ap->a_vap, va_gid);
#if QUOTA
if ((error = getinoquota(ip)) ||
(error = chkiq(ip, 1, vfs_context_ucred(cnp->cn_context), 0))) {
ffs_vfree(tvp, ip->i_number, dmode);
vnode_put(tvp);
return (error);
}
#endif
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
ip->i_mode = dmode;
ip->i_nlink = 2;
if (cnp->cn_flags & ISWHITEOUT)
ip->i_flags |= UF_OPAQUE;
microtime(&tv);
error = ffs_update(tvp, &tv, &tv, 1);
dp->i_nlink++;
dp->i_flag |= IN_CHANGE;
if ( (error = ffs_update(dvp, &tv, &tv, 1)) )
goto bad;
if (dvp->v_mount->mnt_maxsymlinklen > 0)
dtp = &mastertemplate;
else
dtp = (struct dirtemplate *)&omastertemplate;
dirtemplate = *dtp;
dirtemplate.dot_ino = ip->i_number;
dirtemplate.dotdot_ino = dp->i_number;
error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE32,
IO_NODELOCKED|IO_SYNC, vfs_context_ucred(cnp->cn_context), (int *)0, (struct proc *)0);
if (error) {
dp->i_nlink--;
dp->i_flag |= IN_CHANGE;
goto bad;
}
if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_vfsstat.f_bsize)
panic("ufs_mkdir: blksize");
else {
ip->i_size = DIRBLKSIZ;
ip->i_flag |= IN_CHANGE;
}
if ( (error = ufs_direnter(ip, dvp, cnp)) ) {
dp->i_nlink--;
dp->i_flag |= IN_CHANGE;
}
bad:
if (error) {
ip->i_nlink = 0;
ip->i_flag |= IN_CHANGE;
vnode_put(tvp);
} else {
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
*ap->a_vpp = tvp;
};
out:
return (error);
}
int
ufs_rmdir(ap)
struct vnop_rmdir_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct vnode *tvp;
struct componentname *cnp = ap->a_cnp;
struct inode *ip, *dp;
int error, ioflag;
ip = VTOI(vp);
dp = VTOI(dvp);
if (dp == ip)
return (EINVAL);
cnp->cn_flags &= ~MODMASK;
cnp->cn_flags |= (WANTPARENT | NOCACHE);
(void) relookup(dvp, &tvp, cnp);
if (tvp == NULL)
return (ENOENT);
if (tvp != vp)
panic("ufs_rmdir: relookup returned a different vp");
vnode_put(tvp);
error = 0;
if (ip->i_nlink != 2 ||
!ufs_dirempty(ip, dp->i_number, vfs_context_ucred(cnp->cn_context))) {
error = ENOTEMPTY;
goto out;
}
if ( (error = ufs_dirremove(dvp, cnp)) )
goto out;
VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
dp->i_nlink--;
dp->i_flag |= IN_CHANGE;
cache_purge(dvp);
ip->i_nlink -= 2;
ioflag = ((vp)->v_mount->mnt_flag & MNT_ASYNC) ? 0 : IO_SYNC;
error = ffs_truncate_internal(vp, (off_t)0, ioflag, vfs_context_ucred(cnp->cn_context));
cache_purge(ITOV(ip));
out:
VN_KNOTE(vp, NOTE_DELETE);
return (error);
}
int
ufs_symlink(ap)
struct vnop_symlink_args *ap;
{
register struct vnode *vp, **vpp = ap->a_vpp;
register struct inode *ip;
int len, error;
if ( (error = ufs_makeinode(ap->a_vap, ap->a_dvp, vpp, ap->a_cnp)) )
return (error);
VN_KNOTE(ap->a_dvp, NOTE_WRITE);
vp = *vpp;
len = strlen(ap->a_target);
if (len < vp->v_mount->mnt_maxsymlinklen) {
ip = VTOI(vp);
bcopy(ap->a_target, (char *)ip->i_shortlink, len);
ip->i_size = len;
ip->i_flag |= IN_CHANGE | IN_UPDATE;
} else
error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
UIO_SYSSPACE32, IO_NODELOCKED, vfs_context_ucred(ap->a_cnp->cn_context), (int *)0,
(struct proc *)0);
return (error);
}
int
ufs_readdir(ap)
struct vnop_readdir_args *ap;
{
struct uio *uio = ap->a_uio;
int error;
size_t count, lost;
if (ap->a_flags & VNODE_READDIR_EXTENDED) {
return ufs_readdirext(ap->a_vp, uio, ap->a_eofflag,
ap->a_numdirent, ap->a_context);
}
count = uio_resid(uio);
count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
if (count <= 0)
return (EINVAL);
lost = uio_resid(uio) - count;
uio_setresid(uio, count);
uio_iov_len_set(uio, count);
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) {
error = ffs_read_internal(ap->a_vp, uio, 0);
} else {
struct dirent *dp, *edp;
struct uio auio;
struct iovec_32 aiov;
caddr_t dirbuf;
int readcnt;
u_char tmp;
auio = *uio;
auio.uio_iovs.iov32p = &aiov;
auio.uio_iovcnt = 1;
#if 1
auio.uio_segflg = UIO_SYSSPACE;
#else
auio.uio_segflg = UIO_SYSSPACE32;
#endif
aiov.iov_len = count;
MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
aiov.iov_base = (uintptr_t)dirbuf;
error = ffs_read_internal(ap->a_vp, &auio, 0);
if (error == 0) {
readcnt = count - uio_resid(&auio);
edp = (struct dirent *)&dirbuf[readcnt];
for (dp = (struct dirent *)dirbuf; dp < edp; ) {
tmp = dp->d_namlen;
dp->d_namlen = dp->d_type;
dp->d_type = tmp;
if (dp->d_reclen > 0) {
dp = (struct dirent *)
((char *)dp + dp->d_reclen);
} else {
error = EIO;
break;
}
}
if (dp >= edp)
error = uiomove(dirbuf, readcnt, uio);
}
FREE(dirbuf, M_TEMP);
}
# else
error = ffs_read_internal(ap->a_vp, uio, 0);
# endif
uio_setresid(uio, (uio_resid(uio) + lost));
if (ap->a_eofflag)
*ap->a_eofflag = (off_t)VTOI(ap->a_vp)->i_size <= uio->uio_offset;
return (error);
}
#define EXT_DIRENT_LEN(namlen) \
((sizeof(struct direntry) + (namlen) - (MAXPATHLEN-1) + 3) & ~3)
static int
ufs_readdirext(vnode_t vp, uio_t uio, int *eofflag, int *numdirent,
__unused vfs_context_t context)
{
int error;
size_t count, lost;
off_t off = uio->uio_offset;
struct dirent *dp, *edp;
struct uio auio;
struct iovec_32 aiov;
caddr_t dirbuf;
struct direntry *xdp;
int nentries = 0;
count = uio_resid(uio);
count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
if (count <= 0)
return (EINVAL);
lost = uio_resid(uio) - count;
uio_setresid(uio, count);
uio_iov_len_set(uio, count);
auio = *uio;
auio.uio_iovs.iov32p = &aiov;
auio.uio_iovcnt = 1;
auio.uio_segflg = UIO_SYSSPACE;
aiov.iov_len = count;
MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
aiov.iov_base = (uintptr_t)dirbuf;
MALLOC(xdp, struct direntry *, sizeof(struct direntry), M_TEMP, M_WAITOK);
error = ffs_read_internal(vp, &auio, 0);
if (error)
goto out;
edp = (struct dirent *)&dirbuf[count - uio_resid(&auio)];
for (dp = (struct dirent *)dirbuf; dp < edp; ) {
#if (BYTE_ORDER == LITTLE_ENDIAN)
u_char tmp;
if (vp->v_mount->mnt_maxsymlinklen <= 0) {
tmp = dp->d_namlen;
dp->d_namlen = dp->d_type;
dp->d_type = tmp;
}
#endif
xdp->d_reclen = EXT_DIRENT_LEN(dp->d_namlen);
if (xdp->d_reclen > uio_resid(uio)) {
break;
}
xdp->d_ino = dp->d_ino;
xdp->d_namlen = dp->d_namlen;
xdp->d_type = dp->d_type;
bcopy(dp->d_name, xdp->d_name, dp->d_namlen + 1);
off += dp->d_reclen;
xdp->d_seekoff = off;
error = uiomove((caddr_t)xdp, xdp->d_reclen, uio);
if (error) {
off -= dp->d_reclen;
break;
}
nentries++;
if (dp->d_reclen > 0) {
dp = (struct dirent *)
((char *)dp + dp->d_reclen);
} else {
error = EIO;
break;
}
}
out:
FREE(dirbuf, M_TEMP);
FREE(xdp, M_TEMP);
uio_setoffset(uio, off);
*numdirent = nentries;
uio_setresid(uio, (uio_resid(uio) + lost));
if (eofflag)
*eofflag = (off_t)VTOI(vp)->i_size <= uio->uio_offset;
return (error);
}
int
ufs_readlink(ap)
struct vnop_readlink_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct inode *ip = VTOI(vp);
int isize;
isize = ip->i_size;
if (isize < vp->v_mount->mnt_maxsymlinklen) {
uiomove((char *)ip->i_shortlink, isize, ap->a_uio);
return (0);
}
return (ffs_read_internal(vp, ap->a_uio, 0));
}
errno_t
ufs_strategy(ap)
struct vnop_strategy_args *ap;
{
buf_t bp = ap->a_bp;
vnode_t vp = buf_vnode(bp);
struct inode *ip = VTOI(vp);
return (buf_strategy(ip->i_devvp, ap));
}
int
ufsspec_read(ap)
struct vnop_read_args *ap;
{
VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
}
int
ufsspec_write(
struct vnop_write_args *ap)
{
VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
}
int
ufsspec_close(ap)
struct vnop_close_args *ap;
{
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
struct timeval tv;
if (ap->a_vp->v_usecount > 1) {
microtime(&tv);
ITIMES(ip, &tv, &tv);
}
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
}
#if FIFO
int
ufsfifo_read(ap)
struct vnop_read_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOI(ap->a_vp)->i_flag |= IN_ACCESS;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap));
}
int
ufsfifo_write(
struct vnop_write_args *ap)
{
extern int (**fifo_vnodeop_p)(void *);
VTOI(ap->a_vp)->i_flag |= IN_CHANGE | IN_UPDATE;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap));
}
int
ufsfifo_close(ap)
struct vnop_close_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
struct vnode *vp = ap->a_vp;
struct inode *ip = VTOI(vp);
struct timeval tv;
if (ap->a_vp->v_usecount > 1) {
microtime(&tv);
ITIMES(ip, &tv, &tv);
}
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
}
int
ufsfifo_kqfilt_add(ap)
struct vnop_kqfilt_add_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
int error;
error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_add), ap);
if (error)
error = ufs_kqfilt_add(ap);
return (error);
}
#if 0
int
ufsfifo_kqfilt_remove(ap)
struct vnop_kqfilt_remove_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
int error;
error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_remove), ap);
if (error)
error = ufs_kqfilt_remove(ap);
return (error);
}
#endif
#endif
static struct filterops ufsread_filtops =
{ 1, NULL, filt_ufsdetach, filt_ufsread };
static struct filterops ufswrite_filtops =
{ 1, NULL, filt_ufsdetach, filt_ufswrite };
static struct filterops ufsvnode_filtops =
{ 1, NULL, filt_ufsdetach, filt_ufsvnode };
int
ufs_kqfilt_add(ap)
struct vnop_kqfilt_add_args *ap;
{
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &ufsread_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &ufswrite_filtops;
break;
case EVFILT_VNODE:
kn->kn_fop = &ufsvnode_filtops;
break;
default:
return (1);
}
kn->kn_hook = (caddr_t)vp;
kn->kn_hookid = vnode_vid(vp);
KNOTE_ATTACH(&VTOI(vp)->i_knotes, kn);
return (0);
}
static void
filt_ufsdetach(struct knote *kn)
{
struct vnode *vp;
int result;
struct proc *p = current_proc();
vp = (struct vnode *)kn->kn_hook;
if (vnode_getwithvid(vp, kn->kn_hookid))
return;
result = KNOTE_DETACH(&VTOI(vp)->i_knotes, kn);
vnode_put(vp);
}
static int
filt_ufsread(struct knote *kn, long hint)
{
struct vnode *vp = (struct vnode *)kn->kn_hook;
struct inode *ip;
int dropvp = 0;
int result;
if (hint == 0) {
if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) {
hint = NOTE_REVOKE;
} else
dropvp = 1;
}
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
if (kn->kn_flags & EV_POLL) {
kn->kn_data = 1;
result = 1;
} else {
off_t amount;
ip = VTOI(vp);
amount = ip->i_size - kn->kn_fp->f_fglob->fg_offset;
if (amount > (off_t)INTPTR_MAX)
kn->kn_data = INTPTR_MAX;
else if (amount < (off_t)INTPTR_MIN)
kn->kn_data = INTPTR_MIN;
else
kn->kn_data = (intptr_t)amount;
result = (kn->kn_data != 0);
}
if (dropvp)
vnode_put(vp);
return (result);
}
static int
filt_ufswrite(struct knote *kn, long hint)
{
int dropvp = 0;
int result;
if (hint == 0) {
if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
hint = NOTE_REVOKE;
} else
vnode_put(kn->kn_hook);
}
if (hint == NOTE_REVOKE) {
kn->kn_data = 0;
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
kn->kn_data = 0;
return (1);
}
static int
filt_ufsvnode(struct knote *kn, long hint)
{
if (hint == 0) {
if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
hint = NOTE_REVOKE;
} else
vnode_put(kn->kn_hook);
}
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if ((hint == NOTE_REVOKE)) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
return (kn->kn_fflags != 0);
}
int
ufs_pathconf(ap)
struct vnop_pathconf_args *ap;
{
switch (ap->a_name) {
case _PC_LINK_MAX:
*ap->a_retval = LINK_MAX;
return (0);
case _PC_NAME_MAX:
*ap->a_retval = NAME_MAX;
return (0);
case _PC_PATH_MAX:
*ap->a_retval = PATH_MAX;
return (0);
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
return (0);
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 200112;
return (0);
case _PC_NO_TRUNC:
*ap->a_retval = 200112;
return (0);
case _PC_FILESIZEBITS:
*ap->a_retval = 34;
return (0);
default:
return (EINVAL);
}
}
int
ufs_makeinode(vap, dvp, vpp, cnp)
struct vnode_attr *vap;
struct vnode *dvp;
struct vnode **vpp;
struct componentname *cnp;
{
register struct inode *ip, *pdir;
struct timeval tv;
struct vnode *tvp;
int error;
int is_member;
int mode;
mode = MAKEIMODE(vap->va_type, vap->va_mode);
pdir = VTOI(dvp);
*vpp = NULL;
if ((mode & IFMT) == 0)
mode |= IFREG;
if ( (error = ffs_valloc(dvp, (mode_t)mode, vfs_context_ucred(cnp->cn_context), &tvp)) )
return (error);
ip = VTOI(tvp);
ip->i_gid = vap->va_gid;
ip->i_uid = vap->va_uid;
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
#if QUOTA
if ((error = getinoquota(ip)) ||
(error = chkiq(ip, 1, vfs_context_ucred(cnp->cn_context), 0))) {
ffs_vfree(tvp, ip->i_number, mode);
vnode_put(tvp);
return (error);
}
#endif
ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
ip->i_mode = mode;
ip->i_nlink = 1;
if (cnp->cn_flags & ISWHITEOUT)
ip->i_flags |= UF_OPAQUE;
microtime(&tv);
if ( (error = ffs_update(tvp, &tv, &tv, 1)) )
goto bad;
if ( (error = ufs_direnter(ip, dvp, cnp)) )
goto bad;
*vpp = tvp;
return (0);
bad:
ip->i_nlink = 0;
ip->i_flag |= IN_CHANGE;
vnode_put(tvp);
return (error);
}