#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/buf.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/dirent.h>
#include <sys/namei.h>
#include <sys/attr.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
extern void cache_purge (struct vnode *vp);
extern int cache_lookup (struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);
extern void cache_enter (struct vnode *dvp, struct vnode *vpp, struct componentname *cnp);
extern int groupmember(gid_t gid, struct ucred* cred);
#define VOPFUNC int (*)(void *)
int (**synthfs_vnodeop_p) (void *);
struct vnodeopv_entry_desc synthfs_vnodeop_entries[] = {
{&vop_default_desc, (VOPFUNC)vn_default_error},
{&vop_strategy_desc, (VOPFUNC)err_strategy},
{&vop_bwrite_desc, (VOPFUNC)err_bwrite},
{&vop_lookup_desc, (VOPFUNC)synthfs_cached_lookup},
{&vop_create_desc, (VOPFUNC)synthfs_create},
{&vop_whiteout_desc, (VOPFUNC)err_whiteout},
{&vop_mknod_desc, (VOPFUNC)err_mknod},
{&vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex},
{&vop_open_desc, (VOPFUNC)synthfs_open},
{&vop_close_desc, (VOPFUNC)nop_close},
{&vop_access_desc, (VOPFUNC)synthfs_access},
{&vop_getattr_desc, (VOPFUNC)synthfs_getattr},
{&vop_setattr_desc, (VOPFUNC)synthfs_setattr},
{&vop_getattrlist_desc, (VOPFUNC)err_getattrlist},
{&vop_setattrlist_desc, (VOPFUNC)err_setattrlist},
{&vop_read_desc, (VOPFUNC)err_read},
{&vop_write_desc, (VOPFUNC)err_write},
{&vop_lease_desc, (VOPFUNC)err_lease},
{&vop_ioctl_desc, (VOPFUNC)err_ioctl},
{&vop_select_desc, (VOPFUNC)synthfs_select},
{&vop_exchange_desc, (VOPFUNC)err_exchange},
{&vop_revoke_desc, (VOPFUNC)nop_revoke},
{&vop_mmap_desc, (VOPFUNC)synthfs_mmap},
{&vop_fsync_desc, (VOPFUNC)nop_fsync},
{&vop_seek_desc, (VOPFUNC)nop_seek},
{&vop_remove_desc, (VOPFUNC)synthfs_remove},
{&vop_link_desc, (VOPFUNC)err_link},
{&vop_rename_desc, (VOPFUNC)synthfs_rename},
{&vop_mkdir_desc, (VOPFUNC)synthfs_mkdir},
{&vop_rmdir_desc, (VOPFUNC)synthfs_rmdir},
{&vop_symlink_desc, (VOPFUNC)synthfs_symlink},
{&vop_readdir_desc, (VOPFUNC)synthfs_readdir},
{&vop_readdirattr_desc, (VOPFUNC)err_readdirattr},
{&vop_readlink_desc, (VOPFUNC)synthfs_readlink},
{&vop_abortop_desc, (VOPFUNC)nop_abortop},
{&vop_inactive_desc, (VOPFUNC)synthfs_inactive},
{&vop_reclaim_desc, (VOPFUNC)synthfs_reclaim},
{&vop_lock_desc, (VOPFUNC)synthfs_lock},
{&vop_unlock_desc, (VOPFUNC)synthfs_unlock},
{&vop_bmap_desc, (VOPFUNC)err_bmap},
{&vop_print_desc, (VOPFUNC)err_print},
{&vop_islocked_desc, (VOPFUNC)synthfs_islocked},
{&vop_pathconf_desc, (VOPFUNC)synthfs_pathconf},
{&vop_advlock_desc, (VOPFUNC)err_advlock},
{&vop_blkatoff_desc, (VOPFUNC)err_blkatoff},
{&vop_valloc_desc, (VOPFUNC)err_valloc},
{&vop_reallocblks_desc, (VOPFUNC)err_reallocblks},
{&vop_vfree_desc, (VOPFUNC)err_vfree},
{&vop_truncate_desc, (VOPFUNC)err_truncate},
{&vop_allocate_desc, (VOPFUNC)err_allocate},
{&vop_update_desc, (VOPFUNC)synthfs_update},
{&vop_pgrd_desc, (VOPFUNC)err_pgrd},
{&vop_pgwr_desc, (VOPFUNC)err_pgwr},
{&vop_pagein_desc, (VOPFUNC)err_pagein},
{&vop_pageout_desc, (VOPFUNC)err_pageout},
{&vop_devblocksize_desc, (VOPFUNC)err_devblocksize},
{&vop_searchfs_desc, (VOPFUNC)err_searchfs},
{&vop_copyfile_desc, (VOPFUNC)err_copyfile},
{ &vop_blktooff_desc, (VOPFUNC)err_blktooff },
{ &vop_offtoblk_desc, (VOPFUNC)err_offtoblk },
{ &vop_cmap_desc, (VOPFUNC)err_cmap },
{(struct vnodeop_desc *) NULL, (int (*) ()) NULL}
};
struct vnodeopv_desc synthfs_vnodeop_opv_desc =
{&synthfs_vnodeop_p, synthfs_vnodeop_entries};
int
synthfs_create(ap)
struct vop_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 EOPNOTSUPP;
}
int
synthfs_open(ap)
struct vop_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(ap)
struct vop_mmap_args *ap;
{
#if DEBUG
struct vnode *vp = ap->a_vp;
char debugmsg[255];
sprintf(debugmsg, "synthfs_mmap: attempt to map '/%s' ?!", VTOS(vp)->s_name);
Debugger(debugmsg);
#endif
return EINVAL;
}
int
synthfs_access(ap)
struct vop_access_args *ap;
{
struct vnode *vp = ap->a_vp;
mode_t mode = ap->a_mode;
struct ucred *cred = ap->a_cred;
struct synthfsnode *sp = VTOS(vp);
register gid_t *gp;
mode_t mask;
int retval = 0;
int i;
if (mode & VWRITE) {
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
return (EROFS);
break;
default:
break;
}
}
if ((mode & VWRITE) && (sp->s_flags & IMMUTABLE))
return (EPERM);
if (ap->a_cred->cr_uid == 0) {
retval = 0;
goto Exit;
};
mask = 0;
if (cred->cr_uid == sp->s_uid) {
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
mask |= S_IRUSR;
if (mode & VWRITE)
mask |= S_IWUSR;
retval = ((sp->s_mode & mask) == mask ? 0 : EACCES);
goto Exit;
}
for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
if (sp->s_gid == *gp) {
if (mode & VEXEC)
mask |= S_IXGRP;
if (mode & VREAD)
mask |= S_IRGRP;
if (mode & VWRITE)
mask |= S_IWGRP;
retval = ((sp->s_mode & mask) == mask ? 0 : EACCES);
goto Exit;
}
if (mode & VEXEC)
mask |= S_IXOTH;
if (mode & VREAD)
mask |= S_IROTH;
if (mode & VWRITE)
mask |= S_IWOTH;
retval = ((sp->s_mode & mask) == mask ? 0 : EACCES);
Exit:
return (retval);
}
int
synthfs_getattr(ap)
struct vop_getattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vattr *vap = ap->a_vap;
struct synthfsnode *sp = VTOS(vp);
struct synthfs_mntdata *smp = VTOSFS(vp);
vap->va_type = vp->v_type;
vap->va_mode = sp->s_mode;
vap->va_nlink = sp->s_linkcount;
vap->va_uid = sp->s_uid;
vap->va_gid = sp->s_gid;
vap->va_fsid = VTOVFS(vp)->mnt_stat.f_fsid.val[0];
vap->va_fileid = sp->s_nodeid;
switch (vp->v_type) {
case VDIR:
vap->va_size = (sp->s_u.d.d_entrycount + 2) * sizeof(struct dirent);
break;
case VREG:
vap->va_size = sp->s_u.f.f_size;
break;
case VLNK:
vap->va_size = sp->s_u.s.s_length;
break;
default:
vap->va_size = 0;
};
vap->va_blocksize = 512;
vap->va_atime.tv_sec = sp->s_accesstime.tv_sec;
vap->va_atime.tv_nsec = sp->s_accesstime.tv_usec * 1000;
vap->va_mtime.tv_sec = sp->s_modificationtime.tv_sec;
vap->va_mtime.tv_nsec = sp->s_modificationtime.tv_usec * 1000;
vap->va_ctime.tv_sec = sp->s_changetime.tv_sec;
vap->va_ctime.tv_nsec = sp->s_changetime.tv_usec * 1000;
vap->va_gen = sp->s_generation;
vap->va_flags = sp->s_flags;
vap->va_rdev = sp->s_rdev;
vap->va_bytes = vap->va_blocksize * ((vap->va_size + vap->va_blocksize - 1) / vap->va_blocksize);
vap->va_filerev = 0;
vap->va_vaflags = 0;
return (0);
}
int synthfs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
{
struct synthfsnode *sp = VTOS(vp);
int result;
if ((cred->cr_uid != sp->s_uid) &&
(result = suser(cred, &p->p_acflag)))
return result;
if (cred->cr_uid) {
if (vp->v_type != VDIR && (mode & S_ISTXT))
return EFTYPE;
if (!groupmember(sp->s_gid, cred) && (mode & S_ISGID))
return (EPERM);
}
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, struct ucred *cred, struct proc *p)
{
struct synthfsnode *sp = VTOS(vp);
int result;
if (cred->cr_uid != sp->s_uid &&
(result = suser(cred, &p->p_acflag)))
return result;
if (cred->cr_uid == 0) {
if ((sp->s_flags & (SF_IMMUTABLE | SF_APPEND)) &&
securelevel > 0) {
return EPERM;
};
sp->s_flags = flags;
} else {
if (sp->s_flags & (SF_IMMUTABLE | SF_APPEND) ||
(flags & UF_SETTABLE) != flags) {
return EPERM;
};
sp->s_flags &= SF_SETTABLE;
sp->s_flags |= (flags & UF_SETTABLE);
}
sp->s_nodeflags |= IN_CHANGE;
return 0;
}
int synthfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p)
{
struct synthfsnode *sp = VTOS(vp);
uid_t ouid;
gid_t ogid;
int result = 0;
if (uid == (uid_t)VNOVAL) uid = sp->s_uid;
if (gid == (gid_t)VNOVAL) gid = sp->s_gid;
if ((cred->cr_uid != sp->s_uid || uid != sp->s_uid ||
(gid != sp->s_gid && !groupmember((gid_t)gid, cred))) &&
(result = suser(cred, &p->p_acflag)))
return result;
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 && cred->cr_uid != 0) sp->s_mode &= ~S_ISUID;
if (ogid != gid && cred->cr_uid != 0) sp->s_mode &= ~S_ISGID;
return 0;
}
int
synthfs_setattr(ap)
struct vop_setattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct synthfsnode *sp = VTOS(vp);
struct vattr *vap = ap->a_vap;
struct ucred *cred = ap->a_cred;
struct proc *p = ap->a_p;
struct timeval atimeval, mtimeval;
int result;
if (((vap->va_type != VNON) && (vap->va_type != vp->v_type)) ||
(vap->va_nlink != VNOVAL) ||
(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
result = EINVAL;
goto Err_Exit;
}
if (vap->va_flags != VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
result = EROFS;
goto Err_Exit;
};
if ((result = synthfs_chflags(vp, vap->va_flags, cred, p))) {
goto Err_Exit;
};
if (vap->va_flags & (IMMUTABLE | APPEND)) {
result = 0;
goto Err_Exit;
};
}
if (sp->s_flags & (IMMUTABLE | APPEND)) {
result = EPERM;
goto Err_Exit;
};
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
result = EROFS;
goto Err_Exit;
};
if ((result = synthfs_chown(vp, vap->va_uid, vap->va_gid, cred, p))) {
goto Err_Exit;
};
}
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
result = EISDIR;
goto Err_Exit;
case VLNK:
case VREG:
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
result = EROFS;
goto Err_Exit;
};
break;
default:
break;
}
#if RWSUPPORT
if ((result = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))) {
goto Err_Exit;
};
#else
result = EINVAL;
goto Err_Exit;
#endif
}
sp = VTOS(vp);
if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
result = EROFS;
goto Err_Exit;
};
if (cred->cr_uid != sp->s_uid &&
(result = suser(cred, &p->p_acflag)) &&
((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
(result = VOP_ACCESS(vp, VWRITE, cred, p)))) {
goto Err_Exit;
};
if (vap->va_atime.tv_sec != VNOVAL)
sp->s_nodeflags |= IN_ACCESS;
if (vap->va_mtime.tv_sec != VNOVAL)
sp->s_nodeflags |= IN_CHANGE | IN_UPDATE;
atimeval.tv_sec = vap->va_atime.tv_sec;
atimeval.tv_usec = vap->va_atime.tv_nsec / 1000;
mtimeval.tv_sec = vap->va_mtime.tv_sec;
mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000;
if ((result = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))) {
goto Err_Exit;
};
}
result = 0;
if (vap->va_mode != (mode_t)VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
result = EROFS;
goto Err_Exit;
};
result = synthfs_chmod(vp, (int)vap->va_mode, cred, p);
};
Err_Exit: ;
DBG_VOP(("synthfs_setattr: returning %d...\n", result));
return (result);
}
int
synthfs_rename(ap)
struct vop_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 proc *p = source_cnp->cn_proc;
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;
if ((source_vp->v_mount != targetPar_vp->v_mount) ||
(target_vp && (source_vp->v_mount != target_vp->v_mount))) {
retval = EXDEV;
goto abortit;
}
if (target_vp && ((VTOS(target_vp)->s_pflags & (IMMUTABLE | APPEND)) ||
(VTOS(targetPar_vp)->s_pflags & APPEND))) {
retval = EPERM;
goto abortit;
}
if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p)))
goto abortit;
sourcePar_sp = VTOS(sourcePar_vp);
source_sp = VTOS(source_vp);
oldparent = sourcePar_sp->s_nodeid;
if ((source_sp->s_pflags & (IMMUTABLE | APPEND)) || (sourcePar_sp->s_pflags & APPEND)) {
VOP_UNLOCK(source_vp, 0, p);
retval = EPERM;
goto abortit;
}
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)) {
VOP_UNLOCK(source_vp, 0, p);
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;
retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc);
if (doingdirectory && (newparent != oldparent)) {
if (retval)
goto bad;
}
if (target_vp) {
if ((targetPar_sp->s_mode & S_ISTXT) && target_cnp->cn_cred->cr_uid != 0 &&
target_cnp->cn_cred->cr_uid != targetPar_sp->s_uid &&
target_sp->s_uid != target_cnp->cn_cred->cr_uid) {
retval = EPERM;
goto bad;
}
VREF(targetPar_vp);
#if RWSUPPORT
if (target_vp->v_type == VREG) {
(void) vnode_uncache(target_vp);
};
#endif
cache_purge(target_vp);
target_cnp->cn_flags &= ~SAVENAME;
retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp);
(void) vn_lock(targetPar_vp, LK_EXCLUSIVE | LK_RETRY, p);
target_vp = NULL;
target_sp = NULL;
if (retval) goto bad;
};
if (newparent != oldparent)
vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (source_vp->v_type == VREG) cache_purge(source_vp);
retval = synthfs_move_rename_entry( source_vp, targetPar_vp, target_cnp->cn_nameptr);
if (newparent != oldparent)
VOP_UNLOCK(sourcePar_vp, 0, p);
if (retval) goto bad;
source_sp->s_nodeflags &= ~IN_RENAME;
targetPar_sp->s_nodeflags |= IN_UPDATE;
sourcePar_sp->s_nodeflags |= IN_UPDATE;
tv = time;
SYNTHFSTIMES(targetPar_sp, &tv, &tv);
SYNTHFSTIMES(sourcePar_sp, &tv, &tv);
vput(targetPar_vp);
vrele(sourcePar_vp);
vput(source_vp);
return (retval);
bad:;
if (retval && doingdirectory)
source_sp->s_nodeflags &= ~IN_RENAME;
if (targetPar_vp == target_vp)
vrele(targetPar_vp);
else
vput(targetPar_vp);
if (target_vp)
vput(target_vp);
vrele(sourcePar_vp);
if (VOP_ISLOCKED(source_vp))
vput(source_vp);
else
vrele(source_vp);
return (retval);
abortit:;
VOP_ABORTOP(targetPar_vp, target_cnp);
if (targetPar_vp == target_vp)
vrele(targetPar_vp);
else
vput(targetPar_vp);
if (target_vp)
vput(target_vp);
VOP_ABORTOP(sourcePar_vp, source_cnp);
vrele(sourcePar_vp);
vrele(source_vp);
return (retval);
}
int
synthfs_mkdir(ap)
struct vop_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, ap->a_cnp->cn_proc, &vp);
if (retval) goto Error_Exit;
retval = VOP_SETATTR(vp, ap->a_vap, cnp->cn_cred, cnp->cn_proc);
if (retval != 0) goto Error_Exit;
*ap->a_vpp = vp;
Error_Exit:;
if (retval != 0) {
if (vp) synthfs_remove_directory(vp);
VOP_ABORTOP(dvp, cnp);
}
vput(dvp);
return retval;
}
int
synthfs_remove(ap)
struct vop_remove_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct synthfsnode *sp = VTOS(vp);
struct timeval tv;
int retval = 0;
if ((sp->s_flags & (IMMUTABLE | APPEND)) ||
(VTOS(dvp)->s_flags & APPEND)) {
retval = EPERM;
goto out;
};
if (sp->s_nodeflags & IN_MODIFIED) {
tv = time;
VOP_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;
if (dvp == vp) {
vrele(vp);
} else {
vput(vp);
};
vput(dvp);
return (retval);
}
int
synthfs_rmdir(ap)
struct vop_rmdir_args *ap;
{
DBG_VOP(("synthfs_rmdir called\n"));
return synthfs_remove((struct vop_remove_args *)ap);
}
int
synthfs_select(ap)
struct vop_select_args *ap;
{
DBG_VOP(("synthfs_select called\n"));
return (1);
}
int
synthfs_symlink(ap)
struct vop_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, ap->a_cnp->cn_proc, vpp);
if (retval) goto Error_Exit;
VOP_UNLOCK(*vpp, 0, cnp->cn_proc);
Error_Exit:;
if (retval != 0) {
VOP_ABORTOP(dvp, cnp);
}
vput(dvp);
return (retval);
}
int
synthfs_readlink(ap)
struct vop_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->uio_resid <= sp->s_u.s.s_length) {
count = uio->uio_resid;
} 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 vop_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 = %d\n", (int) uio->uio_offset, uio->uio_resid));
if (uio->uio_iovcnt > 1) {
DBG_VOP(("\tuio->uio_iovcnt = %d?\n", uio->uio_iovcnt));
return EINVAL;
};
if ((ap->a_cookies != NULL) || (ap->a_ncookies != NULL)) {
return EINVAL;
};
diroffset = 0;
DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid));
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 = %d\n", (int) uio->uio_offset, uio->uio_resid));
}
if ((uio->uio_resid > 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 = %d\n", (int) uio->uio_offset, uio->uio_resid));
}
if ((uio->uio_resid > 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->uio_resid > 0) && (diroffset > uio->uio_offset)) {
return EINVAL;
};
};
if (ap->a_eofflag)
*ap->a_eofflag = (entry == NULL);
return 0;
}
int
synthfs_cached_lookup(ap)
struct vop_cachedlookup_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;
boolean_t lockparent = (flags & LOCKPARENT);
struct proc *p = cnp->cn_proc;
struct ucred *cred = cnp->cn_cred;
struct vnode *target_vp = NULL;
u_int32_t target_vnode_id;
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 (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n"));
if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
*vpp = NULL;
if (dp->v_type != VDIR) {
result = ENOTDIR;
goto Err_Exit;
};
if ((flags & ISLASTCN) &&
(VTOVFS(dp)->mnt_flag & MNT_RDONLY) &&
((nameiop == DELETE) || (nameiop == RENAME))) {
result = EROFS;
goto Err_Exit;
};
result = VOP_ACCESS(dp, VEXEC, cred, cnp->cn_proc);
if (result != 0) goto Err_Exit;
result = cache_lookup(dp, vpp, cnp);
if (result == 0) {
return synthfs_lookup(ap);
};
if (result == ENOENT) return result;
target_vp = *vpp;
target_vnode_id = target_vp->v_id;
if (target_vp == dp) {
VREF(target_vp);
result = 0;
} else if (flags & ISDOTDOT) {
VOP_UNLOCK(dp, 0, p);
result = vget(target_vp, LK_EXCLUSIVE, p);
if ((result == 0) && lockparent && (flags & ISLASTCN)) {
result = vn_lock(dp, LK_EXCLUSIVE, p);
}
} else {
result = vget(target_vp, LK_EXCLUSIVE, p);
if (!lockparent || (result != 0) || !(flags & ISLASTCN)) {
VOP_UNLOCK(dp, 0, p);
};
};
if (result == 0) {
if (target_vnode_id == target_vp->v_id) {
return 0;
};
vput(target_vp);
if (lockparent && (dp != target_vp) && (flags & ISLASTCN)) {
VOP_UNLOCK(dp, 0, p);
};
};
result = vn_lock(dp, LK_EXCLUSIVE, p);
if (result == 0) {
return synthfs_lookup(ap);
};
Err_Exit:;
return result;
}
int
synthfs_lookup(ap)
struct vop_cachedlookup_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;
struct proc *p = cnp->cn_proc;
struct ucred *cred = cnp->cn_cred;
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 (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n"));
if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
*ap->a_vpp = NULL;
if (dp->v_type != VDIR) {
result = ENOTDIR;
goto Err_Exit;
};
if ((flags & ISLASTCN) &&
(VTOVFS(dp)->mnt_flag & MNT_RDONLY) &&
((nameiop == DELETE) || (nameiop == RENAME))) {
result = EROFS;
goto Err_Exit;
};
result = VOP_ACCESS(dp, VEXEC, cred, cnp->cn_proc);
if (result != 0) goto Err_Exit;
if (cnp->cn_nameptr[0] == '.') {
if (namelen == 1) {
isDot = TRUE;
found = TRUE;
target_vp = dp;
VREF(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);
VOP_UNLOCK(dp, 0, p);
result = vget(target_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (result != 0) {
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
goto Err_Exit;
}
if ((flags & LOCKPARENT) && (flags & ISLASTCN)) {
result = vn_lock(dp, LK_EXCLUSIVE, p);
}
} 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 = vget(target_vp, LK_EXCLUSIVE | LK_RETRY, p);
if (result != 0) {
vrele(target_vp);
goto Err_Exit;
};
goto Std_Exit;
};
};
found = FALSE;
Std_Exit:;
if (found) {
if ((nameiop == DELETE) && (flags & ISLASTCN)) {
result = VOP_ACCESS(dp, VWRITE, cred, p);
if (result != 0) goto Err_Exit;
if ((dsp->s_mode & S_ISVTX) &&
(cred->cr_uid != 0) &&
(cred->cr_uid != dsp->s_uid) &&
(target_vp != NULL) &&
(target_vp->v_type != VLNK) &&
(VTOS(target_vp)->s_uid != cred->cr_uid)) {
vput(target_vp);
result = EPERM;
goto Err_Exit;
};
};
if ((nameiop == RENAME) && (flags & WANTPARENT) && (flags * ISLASTCN)) {
result = VOP_ACCESS(dp, VWRITE, cred, p);
if (result != 0) goto Err_Exit;
if (isDot) {
vrele(target_vp);
result = EISDIR;
goto Err_Exit;
};
};
} else {
result = ENOENT;
if ((flags & ISLASTCN) &&
((nameiop == CREATE) ||
(nameiop == RENAME) ||
((nameiop == DELETE) && (flags & DOWHITEOUT) && (flags & ISWHITEOUT)))) {
result = VOP_ACCESS(dp, VWRITE, cred, p);
if (result != 0) goto Err_Exit;
cnp->cn_flags |= SAVENAME;
result = EJUSTRETURN;
}
};
if (found && !isDot && !isDotDot && (!(flags & LOCKPARENT) || !(flags & ISLASTCN))) {
VOP_UNLOCK(dp, 0, p);
};
*ap->a_vpp = target_vp;
Err_Exit:;
DBG_VOP(("synthfs_lookup: result = %d.\n", result));
if (found) {
if (target_vp) {
if (VOP_ISLOCKED(target_vp)) {
DBG_VOP(("synthfs_lookup: target_vp = 0x%08X (locked).\n", (u_long)target_vp));
} else {
DBG_VOP(("synthfs_lookup: target_vp = 0x%08X (NOT locked?).\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"));
};
if (VOP_ISLOCKED(starting_parent)) {
DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X (LOCKED).\n", (u_long)dp, (u_long)starting_parent));
} else {
DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X (UNLOCKED).\n", (u_long)dp, (u_long)starting_parent));
};
return result;
}
int
synthfs_pathconf(ap)
struct vop_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(ap)
struct vop_update_args *ap;
{
struct vnode *vp = ap->a_vp;
struct synthfsnode *sp = VTOS(vp);
DBG_ASSERT(sp != NULL);
DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
if (((sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) != 0) &&
!(VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY)) {
if (sp->s_nodeflags & IN_ACCESS) sp->s_accesstime = *ap->a_access;
if (sp->s_nodeflags & IN_UPDATE) sp->s_modificationtime = *ap->a_modify;
if (sp->s_nodeflags & IN_CHANGE) sp->s_changetime = time;
};
sp->s_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
return 0;
}
int
synthfs_lock(ap)
struct vop_lock_args *ap;
{
return lockmgr(&VTOS(ap->a_vp)->s_lock, ap->a_flags, &ap->a_vp->v_interlock, ap->a_p);
}
int
synthfs_unlock(ap)
struct vop_unlock_args *ap;
{
return lockmgr(&VTOS(ap->a_vp)->s_lock, ap->a_flags | LK_RELEASE, &ap->a_vp->v_interlock, ap->a_p);
}
int
synthfs_islocked(ap)
struct vop_islocked_args *ap;
{
return lockstatus(&VTOS(ap->a_vp)->s_lock);
}
int
synthfs_inactive(ap)
struct vop_inactive_args *ap;
{
struct vnode *vp = ap->a_vp;
struct proc *p = ap->a_p;
struct synthfsnode *sp = VTOS(vp);
struct timeval tv;
if (vp->v_usecount != 0)
DBG_VOP(("synthfs_inactive: bad usecount = %d\n", vp->v_usecount ));
if (vp->v_type == VNON)
goto out;
if (sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
tv = time;
VOP_UPDATE(vp, &tv, &tv, 0);
}
out:
VOP_UNLOCK(vp, 0, p);
if (vp->v_type == VNON) {
vrecycle(vp, (struct slock *)0, p);
};
return 0;
}
int
synthfs_reclaim(ap)
struct vop_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);
}