#include <mach/mach_types.h>
#include <mach/machine/boolean.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/stat.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/namei.h>
#include <sys/attr.h>
#include <sys/uio_internal.h>
#include <sys/vm.h>
#include <sys/errno.h>
#include <vfs/vfs_support.h>
#include "synthfs.h"
#define RWSUPPORT 0
#if RWSUPPORT
#error NOT PORTED FOR UBC
#include <sys/ubc.h>
#endif
static int synthfs_remove_internal(struct vnode *dvp, struct vnode *vp,
struct componentname *cnp, vfs_context_t context);
#define VOPFUNC int (*)(void *)
int (**synthfs_vnodeop_p) (void *);
struct vnodeopv_entry_desc synthfs_vnodeop_entries[] = {
{&vnop_default_desc, (VOPFUNC)vn_default_error},
{&vnop_strategy_desc, (VOPFUNC)err_strategy},
{&vnop_bwrite_desc, (VOPFUNC)err_bwrite},
{&vnop_lookup_desc, (VOPFUNC)synthfs_cached_lookup},
{&vnop_create_desc, (VOPFUNC)synthfs_create},
{&vnop_whiteout_desc, (VOPFUNC)err_whiteout},
{&vnop_mknod_desc, (VOPFUNC)err_mknod},
{&vnop_open_desc, (VOPFUNC)synthfs_open},
{&vnop_close_desc, (VOPFUNC)nop_close},
{&vnop_getattr_desc, (VOPFUNC)synthfs_getattr},
{&vnop_setattr_desc, (VOPFUNC)synthfs_setattr},
{&vnop_getattrlist_desc, (VOPFUNC)err_getattrlist},
{&vnop_setattrlist_desc, (VOPFUNC)err_setattrlist},
{&vnop_read_desc, (VOPFUNC)err_read},
{&vnop_write_desc, (VOPFUNC)err_write},
{&vnop_ioctl_desc, (VOPFUNC)err_ioctl},
{&vnop_select_desc, (VOPFUNC)synthfs_select},
{&vnop_exchange_desc, (VOPFUNC)err_exchange},
{&vnop_revoke_desc, (VOPFUNC)nop_revoke},
{&vnop_mmap_desc, (VOPFUNC)synthfs_mmap},
{&vnop_fsync_desc, (VOPFUNC)nop_fsync},
{&vnop_remove_desc, (VOPFUNC)synthfs_remove},
{&vnop_link_desc, (VOPFUNC)err_link},
{&vnop_rename_desc, (VOPFUNC)synthfs_rename},
{&vnop_mkdir_desc, (VOPFUNC)synthfs_mkdir},
{&vnop_rmdir_desc, (VOPFUNC)synthfs_rmdir},
{&vnop_symlink_desc, (VOPFUNC)synthfs_symlink},
{&vnop_readdir_desc, (VOPFUNC)synthfs_readdir},
{&vnop_readdirattr_desc, (VOPFUNC)err_readdirattr},
{&vnop_readlink_desc, (VOPFUNC)synthfs_readlink},
{&vnop_inactive_desc, (VOPFUNC)synthfs_inactive},
{&vnop_reclaim_desc, (VOPFUNC)synthfs_reclaim},
{&vnop_pathconf_desc, (VOPFUNC)synthfs_pathconf},
{&vnop_advlock_desc, (VOPFUNC)err_advlock},
{&vnop_allocate_desc, (VOPFUNC)err_allocate},
{&vnop_pagein_desc, (VOPFUNC)err_pagein},
{&vnop_pageout_desc, (VOPFUNC)err_pageout},
{&vnop_devblocksize_desc, (VOPFUNC)err_devblocksize},
{&vnop_searchfs_desc, (VOPFUNC)err_searchfs},
{&vnop_copyfile_desc, (VOPFUNC)err_copyfile},
{ &vnop_blktooff_desc, (VOPFUNC)err_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)err_blockmap },
{(struct vnodeop_desc *) NULL, (int (*) ()) NULL}
};
struct vnodeopv_desc synthfs_vnodeop_opv_desc =
{&synthfs_vnodeop_p, synthfs_vnodeop_entries};
int
synthfs_create(ap)
struct vnop_create_args *ap;
{
#if DEBUG
struct vnode *dvp = ap->a_dvp;
char debugmsg[255];
sprintf(debugmsg, "synthfs_create: attempt to create '%s' in '%s' ?!", ap->a_cnp->cn_nameptr, VTOS(dvp)->s_name);
Debugger(debugmsg);
#endif
return err_create(ap);
}
int
synthfs_open(ap)
struct vnop_open_args *ap;
{
struct vnode *vp = ap->a_vp;
if (vp->v_type == VDIR) {
return 0;
} else {
#if DEBUG
struct synthfsnode *sp = VTOS(vp);
char debugmsg[255];
sprintf(debugmsg, "synthfs_open: attempt to open '/%s' ?!", sp->s_name);
Debugger(debugmsg);
#endif
};
return 0;
}
int
synthfs_mmap(__unused struct vnop_mmap_args *ap)
{
return EINVAL;
}
int
synthfs_getattr(ap)
struct vnop_getattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode_attr *vap = ap->a_vap;
struct synthfsnode *sp = VTOS(vp);
VATTR_RETURN(vap, va_type, vp->v_type);
VATTR_RETURN(vap, va_mode, sp->s_mode);
VATTR_RETURN(vap, va_nlink, sp->s_linkcount);
VATTR_RETURN(vap, va_uid, sp->s_uid);
VATTR_RETURN(vap, va_gid, sp->s_gid);
VATTR_RETURN(vap, va_fsid, VTOVFS(vp)->mnt_vfsstat.f_fsid.val[0]);
VATTR_RETURN(vap, va_fileid, sp->s_nodeid);
switch (vp->v_type) {
case VDIR:
VATTR_RETURN(vap, va_data_size, (sp->s_u.d.d_entrycount + 2) * sizeof(struct dirent));
break;
case VREG:
VATTR_RETURN(vap, va_data_size, sp->s_u.f.f_size);
break;
case VLNK:
VATTR_RETURN(vap, va_data_size, sp->s_u.s.s_length);
break;
default:
VATTR_RETURN(vap, va_data_size, 0);
};
VATTR_RETURN(vap, va_iosize, 512);
vap->va_access_time.tv_sec = sp->s_accesstime.tv_sec;
vap->va_access_time.tv_nsec = sp->s_accesstime.tv_usec * 1000;
VATTR_SET_SUPPORTED(vap, va_access_time);
vap->va_modify_time.tv_sec = sp->s_modificationtime.tv_sec;
vap->va_modify_time.tv_nsec = sp->s_modificationtime.tv_usec * 1000;
VATTR_SET_SUPPORTED(vap, va_modify_time);
vap->va_change_time.tv_sec = sp->s_changetime.tv_sec;
vap->va_change_time.tv_nsec = sp->s_changetime.tv_usec * 1000;
VATTR_SET_SUPPORTED(vap, va_change_time);
VATTR_RETURN(vap, va_gen, sp->s_generation);
VATTR_RETURN(vap, va_flags, sp->s_flags);
VATTR_RETURN(vap, va_rdev, sp->s_rdev);
VATTR_RETURN(vap, va_filerev, 0);
VATTR_RETURN(vap, va_acl, NULL);
return (0);
}
int synthfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct proc *p)
{
struct synthfsnode *sp = VTOS(vp);
int result;
sp->s_mode &= ~ALLPERMS;
sp->s_mode |= (mode & ALLPERMS);
sp->s_nodeflags |= IN_CHANGE;
#if RWSUPPORT
if ((vp->v_flag & VTEXT) && (sp->s_mode & S_ISTXT) == 0) (void) vnode_uncache(vp);
#endif
return 0;
}
int synthfs_chflags(struct vnode *vp, u_long flags, kauth_cred_t cred, struct proc *p)
{
struct synthfsnode *sp = VTOS(vp);
sp->s_flags = flags;
sp->s_nodeflags |= IN_CHANGE;
return 0;
}
int synthfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, struct proc *p)
{
struct synthfsnode *sp = VTOS(vp);
uid_t ouid;
gid_t ogid;
int result = 0;
int is_member;
if (uid == (uid_t)VNOVAL) uid = sp->s_uid;
if (gid == (gid_t)VNOVAL) gid = sp->s_gid;
ogid = sp->s_gid;
ouid = sp->s_uid;
sp->s_gid = gid;
sp->s_uid = uid;
if (ouid != uid || ogid != gid) sp->s_nodeflags |= IN_CHANGE;
if (ouid != uid && suser(cred, NULL)) sp->s_mode &= ~S_ISUID;
if (ogid != gid && suser(cred, NULL)) sp->s_mode &= ~S_ISGID;
return 0;
}
int
synthfs_setattr(ap)
struct vnop_setattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct synthfsnode *sp = VTOS(vp);
struct vnode_attr *vap = ap->a_vap;
kauth_cred_t cred = vfs_context_ucred(ap->a_context);
struct proc *p = vfs_context_proc(ap->a_context);
struct timeval atimeval, mtimeval;
uid_t nuid;
gid_t ngid;
int result;
result = 0;
if (VATTR_IS_ACTIVE(vap, va_flags)) {
if ((result = synthfs_chflags(vp, vap->va_flags, cred, p))) {
goto Err_Exit;
}
}
VATTR_SET_SUPPORTED(vap, va_flags);
nuid = (uid_t)ngid = (gid_t)VNOVAL;
if (VATTR_IS_ACTIVE(vap, va_uid))
nuid = vap->va_uid;
if (VATTR_IS_ACTIVE(vap, va_gid))
ngid = vap->va_gid;
if (nuid != (uid_t)VNOVAL || ngid != (gid_t)VNOVAL) {
if ((result = synthfs_chown(vp, nuid, ngid, cred, p))) {
goto Err_Exit;
}
}
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
if (VATTR_IS_ACTIVE(vap, va_data_size)) {
#if RWSUPPORT
if ((result = vnode_setsize(vp, vap->va_data_size, 0, ap->a_context))) {
goto Err_Exit;
};
VATTR_SET_SUPPORTED(vap, va_data_size);
#else
result = EINVAL;
goto Err_Exit;
#endif
}
sp = VTOS(vp);
if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
sp->s_nodeflags |= IN_ACCESS;
atimeval.tv_sec = vap->va_access_time.tv_sec;
atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000;
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
sp->s_nodeflags |= IN_CHANGE | IN_UPDATE;
mtimeval.tv_sec = vap->va_modify_time.tv_sec;
mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000;
}
if ((result = synthfs_update(vp, &atimeval, &mtimeval, 1))) {
goto Err_Exit;
}
}
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
if (VATTR_IS_ACTIVE(vap, va_mode))
result = synthfs_chmod(vp, (int)vap->va_mode, cred, p);
VATTR_SET_SUPPORTED(vap, va_mode);
Err_Exit:
DBG_VOP(("synthfs_setattr: returning %d...\n", result));
return (result);
}
int
synthfs_rename(ap)
struct vnop_rename_args *ap;
{
struct vnode *target_vp = ap->a_tvp;
struct vnode *targetPar_vp = ap->a_tdvp;
struct vnode *source_vp = ap->a_fvp;
struct vnode *sourcePar_vp = ap->a_fdvp;
struct componentname *target_cnp = ap->a_tcnp;
struct componentname *source_cnp = ap->a_fcnp;
struct synthfsnode *target_sp, *targetPar_sp, *source_sp, *sourcePar_sp;
u_short doingdirectory = 0, oldparent = 0, newparent = 0;
int retval = 0;
struct timeval tv;
#if SYNTHFS_DIAGNOSTIC
if ((target_cnp->cn_flags & HASBUF) == 0 ||
(source_cnp->cn_flags & HASBUF) == 0)
panic("synthfs_rename: no name");
#endif
DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
target_sp = targetPar_sp = source_sp = sourcePar_sp = NULL;
sourcePar_sp = VTOS(sourcePar_vp);
source_sp = VTOS(source_vp);
oldparent = sourcePar_sp->s_nodeid;
if (source_sp->s_type == SYNTHFS_DIRECTORY) {
if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
|| sourcePar_sp == source_sp
|| (source_cnp->cn_flags & ISDOTDOT)
|| (source_sp->s_nodeflags & IN_RENAME)) {
retval = EINVAL;
goto abortit;
}
source_sp->s_nodeflags |= IN_RENAME;
doingdirectory = TRUE;
}
targetPar_sp = VTOS(targetPar_vp);
target_sp = target_vp ? VTOS(target_vp) : NULL;
newparent = targetPar_sp->s_nodeid;
if (target_vp) {
#if RWSUPPORT
if (target_vp->v_type == VREG) {
(void) vnode_uncache(target_vp);
};
#endif
cache_purge(target_vp);
retval = synthfs_remove_internal(targetPar_vp, target_vp, target_cnp, ap->a_context);
target_vp = NULL;
target_sp = NULL;
if (retval) goto bad;
};
if (source_vp->v_type == VREG) cache_purge(source_vp);
retval = synthfs_move_rename_entry( source_vp, targetPar_vp, target_cnp->cn_nameptr);
if (retval) goto bad;
source_sp->s_nodeflags &= ~IN_RENAME;
targetPar_sp->s_nodeflags |= IN_UPDATE;
sourcePar_sp->s_nodeflags |= IN_UPDATE;
microtime(&tv);
SYNTHFSTIMES(targetPar_sp, &tv, &tv);
SYNTHFSTIMES(sourcePar_sp, &tv, &tv);
return (retval);
bad:;
if (retval && doingdirectory)
source_sp->s_nodeflags &= ~IN_RENAME;
return (retval);
abortit:;
return (retval);
}
int
synthfs_mkdir(ap)
struct vnop_mkdir_args *ap;
{
int retval;
struct vnode *dvp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
struct vnode *vp = NULL;
*ap->a_vpp = NULL;
retval = synthfs_new_directory(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, mode, vfs_context_proc(cnp->cn_context), &vp);
if (retval) goto Error_Exit;
*ap->a_vpp = vp;
retval = vnode_setattr(vp, ap->a_vap, ap->a_context);
if (retval != 0) goto Error_Exit;
Error_Exit:;
if (retval != 0) {
if (vp) synthfs_remove_directory(vp);
}
return retval;
}
int
synthfs_remove(ap)
struct vnop_remove_args *ap;
{
return synthfs_remove_internal(ap->a_dvp, ap->a_vp, ap->a_cnp, ap->a_context);
}
static int
synthfs_remove_internal(struct vnode *dvp, struct vnode *vp,
__unused struct componentname *cnp,
__unused vfs_context_t context)
{
struct synthfsnode *sp = VTOS(vp);
struct timeval tv;
int retval = 0;
if (sp->s_nodeflags & IN_MODIFIED) {
microtime(&tv);
synthfs_update(vp, &tv, &tv, 0);
};
cache_purge(vp);
switch (sp->s_type) {
case SYNTHFS_DIRECTORY:
synthfs_remove_directory(vp);
break;
case SYNTHFS_SYMLINK:
synthfs_remove_symlink(vp);
break;
case SYNTHFS_FILE:
default:
synthfs_remove_entry(vp);
};
out:
if (! retval)
VTOS(dvp)->s_nodeflags |= IN_CHANGE | IN_UPDATE;
return (retval);
}
int
synthfs_rmdir(ap)
struct vnop_rmdir_args *ap;
{
return synthfs_remove((struct vnop_remove_args *)ap);
}
int
synthfs_select(__unused
struct vnop_select_args *ap)
{
DBG_VOP(("synthfs_select called\n"));
return (1);
}
int
synthfs_symlink(ap)
struct vnop_symlink_args *ap;
{
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
int retval;
*vpp = NULL;
retval = synthfs_new_symlink(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, ap->a_target, vfs_context_proc(cnp->cn_context), vpp);
return (retval);
}
int
synthfs_readlink(ap)
struct vnop_readlink_args *ap;
{
struct vnode *vp = ap->a_vp;
struct synthfsnode *sp = VTOS(vp);
struct uio *uio = ap->a_uio;
int retval;
unsigned long count;
if (ap->a_uio->uio_offset > sp->s_u.s.s_length) {
return 0;
};
if (uio->uio_offset + uio_resid(uio) <= sp->s_u.s.s_length) {
count = uio_resid(uio);
} else {
count = sp->s_u.s.s_length - uio->uio_offset;
};
retval = uiomove((void *)((unsigned char *)sp->s_u.s.s_symlinktarget + uio->uio_offset), count, uio);
return (retval);
}
int
synthfs_readdir(ap)
struct vnop_readdir_args *ap;
{
struct synthfsnode *sp = VTOS(ap->a_vp);
register struct uio *uio = ap->a_uio;
off_t diroffset;
struct synthfsnode *entry;
DBG_VOP(("\tuio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
return (EINVAL);
if (uio->uio_iovcnt > 1) {
DBG_VOP(("\tuio->uio_iovcnt = %d?\n", uio->uio_iovcnt));
return EINVAL;
};
diroffset = 0;
DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
if (uio->uio_offset == diroffset)
{
DBG_VOP(("\tAdding .\n"));
diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, ".", uio);
DBG_VOP(("\t after adding ., uio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
}
if ((uio_resid(uio) > 0) && (diroffset > uio->uio_offset)) {
return EINVAL;
};
if (uio->uio_offset == diroffset)
{
DBG_VOP(("\tAdding ..\n"));
if (sp->s_parent != NULL) {
diroffset += synthfs_adddirentry(sp->s_parent->s_nodeid, DT_DIR, "..", uio);
} else {
diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, "..", uio);
}
DBG_VOP(("\t after adding .., uio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
}
if ((uio_resid(uio) > 0) && (diroffset > uio->uio_offset)) {
return EINVAL;
};
TAILQ_FOREACH(entry, &sp->s_u.d.d_subnodes, s_sibling) {
if (diroffset == uio->uio_offset) {
diroffset += synthfs_adddirentry(entry->s_nodeid, VTTOIF(STOV(entry)->v_type), entry->s_name, uio);
};
if ((uio_resid(uio) > 0) && (diroffset > uio->uio_offset)) {
return EINVAL;
};
};
if (ap->a_eofflag)
*ap->a_eofflag = (entry == NULL);
return 0;
}
int
synthfs_cached_lookup(ap)
struct vnop_lookup_args *ap;
{
struct vnode *dp = ap->a_dvp;
struct componentname *cnp = ap->a_cnp;
u_long nameiop = cnp->cn_nameiop;
u_long flags = cnp->cn_flags;
struct vnode **vpp = ap->a_vpp;
int result = 0;
DBG_VOP(("synthfs_cached_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
#if DEBUG
if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
#endif
*vpp = NULL;
result = cache_lookup(dp, vpp, cnp);
if (result == 0) {
return synthfs_lookup(ap);
};
if (result == ENOENT) return result;
return (0);
Err_Exit:;
return result;
}
int
synthfs_lookup(ap)
struct vnop_lookup_args *ap;
{
struct vnode *dp = ap->a_dvp;
struct synthfsnode *dsp = VTOS(dp);
struct componentname *cnp = ap->a_cnp;
u_long nameiop = cnp->cn_nameiop;
u_long flags = cnp->cn_flags;
long namelen = cnp->cn_namelen;
vfs_context_t ctx = cnp->cn_context;
kauth_cred_t cred = vfs_context_ucred(ctx);
struct synthfsnode *entry;
struct vnode *target_vp = NULL;
int result = 0;
boolean_t found = FALSE;
boolean_t isDot = FALSE;
boolean_t isDotDot = FALSE;
struct vnode *starting_parent = dp;
DBG_VOP(("synthfs_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
#if DEBUG
if (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n"));
if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
#endif
*ap->a_vpp = NULL;
if (cnp->cn_nameptr[0] == '.') {
if (namelen == 1) {
isDot = TRUE;
found = TRUE;
target_vp = dp;
vnode_get(target_vp);
result = 0;
goto Std_Exit;
} else if ((namelen == 2) && (cnp->cn_nameptr[1] == '.')) {
isDotDot = TRUE;
found = TRUE;
if ((dsp->s_parent != NULL) && (dsp->s_parent != VTOS(dp))) {
target_vp = STOV(dsp->s_parent);
result = vnode_get(target_vp);
} else {
target_vp = dp;
result = 0;
}
goto Std_Exit;
}
}
TAILQ_FOREACH(entry, &dsp->s_u.d.d_subnodes, s_sibling) {
if ((bcmp(cnp->cn_nameptr, entry->s_name, (unsigned)namelen) == 0) &&
(*(entry->s_name + namelen) == (char)0)) {
found = TRUE;
target_vp = STOV(entry);
result = vnode_getwithref(target_vp);
if (result != 0) {
goto Err_Exit;
};
goto Std_Exit;
};
};
found = FALSE;
Std_Exit:;
if (found) {
if ((nameiop == DELETE) && (flags & ISLASTCN)) {
if ((dsp->s_mode & S_ISVTX) &&
suser(cred, NULL) &&
(kauth_cred_getuid(cred) != dsp->s_uid) &&
(target_vp != NULL) &&
(target_vp->v_type != VLNK) &&
(VTOS(target_vp)->s_uid != kauth_cred_getuid(cred))) {
vnode_put(target_vp);
result = EPERM;
goto Err_Exit;
};
};
if ((nameiop == RENAME) && (flags & WANTPARENT) && (flags * ISLASTCN)) {
if (isDot) {
vnode_put(target_vp);
result = EISDIR;
goto Err_Exit;
};
};
} else {
result = ENOENT;
if ((flags & ISLASTCN) &&
((nameiop == CREATE) ||
(nameiop == RENAME) ||
((nameiop == DELETE) && (flags & DOWHITEOUT) && (flags & ISWHITEOUT)))) {
result = EJUSTRETURN;
}
};
*ap->a_vpp = target_vp;
Err_Exit:;
DBG_VOP(("synthfs_lookup: result = %d.\n", result));
if (found) {
if (target_vp) {
DBG_VOP(("synthfs_lookup: target_vp = 0x%08X \n", (u_long)target_vp));
} else {
DBG_VOP(("synthfs_lookup: found = true but target_vp = NULL?\n"));
};
} else {
DBG_VOP(("synthf_lookup: target not found.\n"));
};
DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X .\n", (u_long)dp, (u_long)starting_parent));
return result;
}
int
synthfs_pathconf(ap)
struct vnop_pathconf_args *ap;
{
DBG_VOP(("synthfs_pathconf called\n"));
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 = 1;
return (0);
case _PC_NO_TRUNC:
*ap->a_retval = 1;
return (0);
default:
return (EINVAL);
}
}
int
synthfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify, __unused int waitfor)
{
struct synthfsnode *sp = VTOS(vp);
struct timeval tv;
DBG_ASSERT(sp != NULL);
if (((sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) != 0) &&
!(VTOVFS(vp)->mnt_flag & MNT_RDONLY)) {
if (sp->s_nodeflags & IN_ACCESS) sp->s_accesstime = *access;
if (sp->s_nodeflags & IN_UPDATE) sp->s_modificationtime = *modify;
if (sp->s_nodeflags & IN_CHANGE) {
microtime(&tv);
sp->s_changetime = tv;
}
};
sp->s_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
return 0;
}
int
synthfs_inactive(ap)
struct vnop_inactive_args *ap;
{
struct vnode *vp = ap->a_vp;
struct synthfsnode *sp = VTOS(vp);
struct timeval tv;
#if DEBUG
if (vp->v_usecount != 0)
DBG_VOP(("synthfs_inactive: bad usecount = %d\n", vp->v_usecount ));
#endif
if (vp->v_type == VNON)
goto out;
if (sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
microtime(&tv);
synthfs_update(vp, &tv, &tv, 0);
}
out:
if (vp->v_type == VNON) {
vnode_recycle(vp);
};
return 0;
}
int
synthfs_reclaim(ap)
struct vnop_reclaim_args *ap;
{
struct vnode *vp = ap->a_vp;
struct synthfsnode *sp = VTOS(vp);
void *name = sp->s_name;
sp->s_name = NULL;
FREE(name, M_TEMP);
vp->v_data = NULL;
FREE((void *)sp, M_SYNTHFS);
return (0);
}