#include <mach/mach_types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/filedesc.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/kdebug.h>
#include <sys/queue.h>
#include <sys/vm.h>
#include <sys/errno.h>
#include <vfs/vfs_support.h>
#include "volfs.h"
#define VOPFUNC int (*)(void *)
int (**volfs_vnodeop_p) (void *);
struct vnodeopv_entry_desc volfs_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)volfs_lookup},
{&vop_create_desc, (VOPFUNC)err_create},
{&vop_whiteout_desc, (VOPFUNC)err_whiteout},
{&vop_mknod_desc, (VOPFUNC)err_mknod},
{&vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex},
{&vop_open_desc, (VOPFUNC)nop_open},
{&vop_close_desc, (VOPFUNC)nop_close},
{&vop_access_desc, (VOPFUNC)volfs_access},
{&vop_getattr_desc, (VOPFUNC)volfs_getattr},
{&vop_setattr_desc, (VOPFUNC)err_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)volfs_select},
{&vop_exchange_desc, (VOPFUNC)err_exchange},
{&vop_revoke_desc, (VOPFUNC)nop_revoke},
{&vop_mmap_desc, (VOPFUNC)err_mmap},
{&vop_fsync_desc, (VOPFUNC)err_fsync},
{&vop_seek_desc, (VOPFUNC)nop_seek},
{&vop_remove_desc, (VOPFUNC)err_remove},
{&vop_link_desc, (VOPFUNC)err_link},
{&vop_rename_desc, (VOPFUNC)err_rename},
{&vop_mkdir_desc, (VOPFUNC)err_mkdir},
{&vop_rmdir_desc, (VOPFUNC)volfs_rmdir},
{&vop_symlink_desc, (VOPFUNC)err_symlink},
{&vop_readdir_desc, (VOPFUNC)volfs_readdir},
{&vop_readdirattr_desc, (VOPFUNC)err_readdirattr},
{&vop_readlink_desc, (VOPFUNC)err_readlink},
{&vop_abortop_desc, (VOPFUNC)err_abortop},
{&vop_inactive_desc, (VOPFUNC)err_inactive},
{&vop_reclaim_desc, (VOPFUNC)volfs_reclaim},
{&vop_lock_desc, (VOPFUNC)volfs_lock},
{&vop_unlock_desc, (VOPFUNC)volfs_unlock},
{&vop_bmap_desc, (VOPFUNC)err_bmap},
{&vop_print_desc, (VOPFUNC)err_print},
{&vop_islocked_desc, (VOPFUNC)volfs_islocked},
{&vop_pathconf_desc, (VOPFUNC)volfs_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)err_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 volfs_vnodeop_opv_desc =
{&volfs_vnodeop_p, volfs_vnodeop_entries};
static char gDot[] = ".";
static char gDotDot[] = "..";
struct finfo {
fsobj_id_t parID;
};
struct finfoattrbuf {
unsigned long length;
struct finfo fi;
};
static int validfsnode(struct mount *fsnode);
struct volfs_PLCEntry
{
LIST_ENTRY(volfs_PLCEntry) vplc_hash_link;
TAILQ_ENTRY(volfs_PLCEntry) vplc_lru_link;
int32_t vplc_fsid;
u_int vplc_item_id;
uid_t vplc_uid;
pid_t vplc_pid;
};
#define VOLFSPLCHASH(fsid, inum) ((((unsigned long)fsid) + (unsigned long)(inum)) & volfs_PLCHashMask)
static struct slock volfs_PLChashtable_slock;
static TAILQ_HEAD(volfs_PLCLRUListHead, volfs_PLCEntry) volfs_PLCLRUList;
static TAILQ_HEAD(volfs_PLCFreeListHead, volfs_PLCEntry) volfs_PLCFreeList;
static LIST_HEAD(, volfs_PLCEntry) *volfs_PLCHashTable;
static u_long volfs_PLCHashMask;
static u_long volfs_PLCEntryCount;
#if DBG_VOP_TEST_LOCKS
static void DbgVopTest (int max, int error, VopDbgStoreRec *VopDbgStore, char *funcname);
#endif
__private_extern__ void
volfs_PLChashinit(void)
{
int i;
TAILQ_INIT(&volfs_PLCLRUList);
TAILQ_INIT(&volfs_PLCFreeList);
simple_lock_init(&volfs_PLChashtable_slock);
#if MAXPLCENTRIES
volfs_PLCHashTable = hashinit(PLCHASHSIZE, M_TEMP, &volfs_PLCHashMask);
for (i = 0; i < PLCHASHSIZE; ++i) {
LIST_INIT(&volfs_PLCHashTable[i]);
};
#endif
volfs_PLCEntryCount = 0;
}
__private_extern__ void
volfs_PLC_reclaim_entries(int entrycount)
{
#if MAXPLCENTRIES
int i;
struct volfs_PLCEntry *reclaim_target;
simple_lock(&volfs_PLChashtable_slock);
for (i = entrycount; i > 0; --i) {
if (TAILQ_EMPTY(&volfs_PLCLRUList)) break;
reclaim_target = TAILQ_FIRST(&volfs_PLCLRUList);
TAILQ_REMOVE(&volfs_PLCLRUList, reclaim_target, vplc_lru_link);
LIST_REMOVE(reclaim_target, vplc_hash_link);
TAILQ_INSERT_TAIL(&volfs_PLCFreeList, reclaim_target, vplc_lru_link);
};
simple_unlock(&volfs_PLChashtable_slock);
#endif
}
#if MAXPLCENTRIES
static int
volfs_PLCLookup(int32_t fsid, u_int target_id, uid_t uid, pid_t pid)
{
struct volfs_PLCEntry *hash_entry;
int result = 0;
simple_lock(&volfs_PLChashtable_slock);
LIST_FOREACH(hash_entry, &volfs_PLCHashTable[VOLFSPLCHASH(fsid, target_id)], vplc_hash_link) {
if ((hash_entry->vplc_item_id == target_id) &&
(hash_entry->vplc_pid == pid) &&
(hash_entry->vplc_uid == uid) &&
(hash_entry->vplc_fsid == fsid)) {
result = 1;
#if 0
if (hash_entry != TAILQ_LAST(&volfs_PLCLRUList, volfs_PLCLRUListHead)) {
TAILQ_REMOVE(&volfs_PLCLRUList, hash_entry, vplc_lru_link);
TAILQ_INSERT_TAIL(&volfs_PLCLRUList, hash_entry, vplc_lru_link);
};
#endif
break;
};
};
simple_unlock(&volfs_PLChashtable_slock);
return result;
}
static void
volfs_PLCEnter(int32_t fsid, u_int target_id, uid_t uid, pid_t pid)
{
struct volfs_PLCEntry *new_entry;
simple_lock(&volfs_PLChashtable_slock);
if (!TAILQ_EMPTY(&volfs_PLCFreeList)) {
new_entry = TAILQ_FIRST(&volfs_PLCFreeList);
TAILQ_REMOVE(&volfs_PLCFreeList, new_entry, vplc_lru_link);
} else {
if (volfs_PLCEntryCount < MAXPLCENTRIES) {
simple_unlock(&volfs_PLChashtable_slock);
new_entry = MALLOC(new_entry, struct volfs_PLCEntry *, sizeof(struct volfs_PLCEntry), M_TEMP, M_WAITOK);
simple_lock(&volfs_PLChashtable_slock);
++volfs_PLCEntryCount;
} else {
new_entry = TAILQ_FIRST(&volfs_PLCLRUList);
TAILQ_REMOVE(&volfs_PLCLRUList, new_entry, vplc_lru_link);
LIST_REMOVE(new_entry, vplc_hash_link);
};
};
new_entry->vplc_fsid = fsid;
new_entry->vplc_item_id = target_id;
new_entry->vplc_uid = uid;
new_entry->vplc_pid = pid;
LIST_INSERT_HEAD(&volfs_PLCHashTable[VOLFSPLCHASH(fsid, target_id)], new_entry, vplc_hash_link);
TAILQ_INSERT_TAIL(&volfs_PLCLRUList, new_entry, vplc_lru_link);
simple_unlock(&volfs_PLChashtable_slock);
}
#endif
int
volfs_reclaim(ap)
struct vop_reclaim_args *ap;
{
struct vnode *vp = ap->a_vp;
void *data = vp->v_data;
DBG_FUNC_NAME("volfs_reclaim");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
DBG_VOP_LOCKS_INIT(0, vp, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
vp->v_data = NULL;
FREE(data, M_VOLFSNODE);
DBG_VOP_LOCKS_TEST(0);
return (0);
}
int
volfs_access(ap)
struct vop_access_args *ap;
{
int ret_err;
DBG_FUNC_NAME("volfs_access");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
if ((ap->a_mode & ~(VREAD | VEXEC)) == 0)
ret_err = 0;
else
ret_err = EACCES;
DBG_VOP_LOCKS_TEST(ret_err);
return (ret_err);
}
int
volfs_getattr(ap)
struct vop_getattr_args *ap;
{
struct volfs_vndata *priv_data;
struct vnode *a_vp;
struct vattr *a_vap;
int numMounts = 0;
DBG_FUNC_NAME("volfs_getattr");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
a_vp = ap->a_vp;
a_vap = ap->a_vap;
priv_data = a_vp->v_data;
a_vap->va_type = VDIR;
a_vap->va_mode = 0444;
a_vap->va_nlink = 2;
a_vap->va_uid = 0;
a_vap->va_gid = 0;
a_vap->va_fsid = (int) a_vp->v_mount->mnt_stat.f_fsid.val[0];
a_vap->va_fileid = priv_data->nodeID;
if (priv_data->vnode_type == VOLFS_ROOT)
{
register struct mount *mp, *nmp;
simple_lock(&mountlist_slock);
for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) {
if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, ap->a_p)) {
nmp = mp->mnt_list.cqe_next;
continue;
}
if (mp != a_vp->v_mount && validfsnode(mp))
numMounts++;
simple_lock(&mountlist_slock);
nmp = mp->mnt_list.cqe_next;
vfs_unbusy(mp, ap->a_p);
}
simple_unlock(&mountlist_slock);
DBG_VOP(("found %d file systems that volfs can support\n", numMounts));
a_vap->va_size = (numMounts + 2) * VLFSDIRENTLEN;
}
else
{
a_vap->va_size = 2 * VLFSDIRENTLEN;
}
DBG_VOP(("va_size = %d, VLFSDIRENTLEN = %ld\n", (int) a_vap->va_size, VLFSDIRENTLEN));
a_vap->va_blocksize = 512;
a_vap->va_atime.tv_sec = boottime.tv_sec;
a_vap->va_atime.tv_nsec = 0;
a_vap->va_mtime.tv_sec = boottime.tv_sec;
a_vap->va_mtime.tv_nsec = 0;
a_vap->va_ctime.tv_sec = boottime.tv_sec;
a_vap->va_ctime.tv_nsec = 0;
a_vap->va_gen = 0;
a_vap->va_flags = 0;
a_vap->va_rdev = 0;
a_vap->va_bytes = a_vap->va_size;
a_vap->va_filerev = 0;
a_vap->va_vaflags = 0;
DBG_VOP_LOCKS_TEST(0);
return (0);
}
int
volfs_select(ap)
struct vop_select_args *ap;
{
DBG_VOP(("volfs_select called\n"));
return (1);
}
int
volfs_rmdir(ap)
struct vop_rmdir_args *ap;
{
DBG_VOP(("volfs_rmdir called\n"));
if (ap->a_dvp == ap->a_vp) {
(void) nop_rmdir(ap);
return (EINVAL);
} else
return (err_rmdir(ap));
}
int
volfs_readdir(ap)
struct vop_readdir_args *ap;
{
struct volfs_vndata *priv_data;
register struct uio *uio = ap->a_uio;
int error = 0;
size_t count, lost;
int rec_offset;
struct dirent local_dir;
int i;
int starting_resid;
off_t off;
DBG_FUNC_NAME("volfs_readdir");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
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));
off = uio->uio_offset;
priv_data = ap->a_vp->v_data;
starting_resid = uio->uio_resid;
count = uio->uio_resid;
count -= (uio->uio_offset + count) & (VLFSDIRENTLEN - 1);
if (count <= 0)
{
DBG_VOP(("volfs_readdir: Not enough buffer to read in entries\n"));
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
if (off & (VLFSDIRENTLEN - 1))
{
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
rec_offset = off / VLFSDIRENTLEN;
lost = uio->uio_resid - count;
uio->uio_resid = count;
uio->uio_iov->iov_len = count;
local_dir.d_reclen = VLFSDIRENTLEN;
DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %d\n",
(int) uio->uio_offset, uio->uio_resid));
if (rec_offset == 0)
{
DBG_VOP(("\tAdding .\n"));
local_dir.d_fileno = priv_data->nodeID;
local_dir.d_type = DT_DIR;
local_dir.d_namlen = 1;
local_dir.d_name[0] = '.';
for (i = 1; i < MAXVLFSNAMLEN; i++)
local_dir.d_name[i] = 0;
error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
DBG_VOP(("\t after adding ., uio_offset = %d, uio_resid = %d\n",
(int) uio->uio_offset, uio->uio_resid));
rec_offset++;
}
if (rec_offset == 1)
{
DBG_VOP(("\tAdding ..\n"));
local_dir.d_fileno = ROOT_DIRID;
local_dir.d_type = DT_DIR;
local_dir.d_namlen = 2;
local_dir.d_name[0] = '.';
local_dir.d_name[1] = '.';
for (i = 2; i < MAXVLFSNAMLEN; i++)
local_dir.d_name[i] = 0;
error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
rec_offset++;
DBG_VOP(("\t after adding .., uio_offset = %d, uio_resid = %d\n",
(int) uio->uio_offset, uio->uio_resid));
}
if (priv_data->vnode_type == VOLFS_FSNODE)
{
*ap->a_eofflag = 1;
DBG_VOP_LOCKS_TEST(error);
return (error);
}
if (rec_offset > 1) {
register struct mount *mp, *nmp;
int validnodeindex;
struct proc *p = uio->uio_procp;
validnodeindex = 1;
simple_lock(&mountlist_slock);
for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) {
if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) {
nmp = mp->mnt_list.cqe_next;
continue;
}
if (mp != ap->a_vp->v_mount && validfsnode(mp))
validnodeindex++;
if (rec_offset == validnodeindex)
{
local_dir.d_fileno = mp->mnt_stat.f_fsid.val[0];
local_dir.d_type = DT_DIR;
local_dir.d_reclen = VLFSDIRENTLEN;
DBG_VOP(("\tAdding dir entry %d for offset %d\n", mp->mnt_stat.f_fsid.val[0], rec_offset));
local_dir.d_namlen = sprintf(&local_dir.d_name[0], "%d", mp->mnt_stat.f_fsid.val[0]);
error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio);
DBG_VOP(("\t after adding entry '%s', uio_offset = %d, uio_resid = %d\n",
&local_dir.d_name[0], (int) uio->uio_offset, uio->uio_resid));
rec_offset++;
}
simple_lock(&mountlist_slock);
nmp = mp->mnt_list.cqe_next;
vfs_unbusy(mp, p);
}
simple_unlock(&mountlist_slock);
if (mp == (void *) &mountlist)
*ap->a_eofflag = 1;
}
uio->uio_resid += lost;
if (starting_resid == uio->uio_resid)
uio->uio_offset = 0;
DBG_VOP(("\tExiting, uio_offset = %d, uio_resid = %d, ap->a_eofflag = %d\n",
(int) uio->uio_offset, uio->uio_resid, *ap->a_eofflag));
DBG_VOP_LOCKS_TEST(error);
return (error);
}
static int
validfsnode(struct mount *fsnode)
{
if ((! (fsnode->mnt_kern_flag & MNTK_UNMOUNT)) && (fsnode->mnt_flag & MNT_DOVOLFS))
return 1;
else
return 0;
}
int
volfs_lock(ap)
struct vop_lock_args *ap;
{
int retval;
struct volfs_vndata *priv_data;
DBG_FUNC_NAME("volfs_lock");
DBG_VOP_LOCKS_DECL(1);
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 0)) | DBG_FUNC_START,
(unsigned int)ap->a_vp, (unsigned int)ap->a_flags, (unsigned int)ap->a_p, 0, 0);
#endif
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
priv_data = (struct volfs_vndata *) ap->a_vp->v_data;
retval = lockmgr(&priv_data->lock, ap->a_flags, &ap->a_vp->v_interlock, ap->a_p);
DBG_VOP_LOCKS_TEST(retval);
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 0)) | DBG_FUNC_END,
(unsigned int)ap->a_vp, (unsigned int)ap->a_flags, (unsigned int)ap->a_p, retval, 0);
#endif
return (retval);
}
int
volfs_unlock(ap)
struct vop_unlock_args *ap;
{
int retval;
struct volfs_vndata *priv_data;
DBG_FUNC_NAME("volfs_unlock");
DBG_VOP_LOCKS_DECL(1);
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 4)) | DBG_FUNC_START,
(unsigned int)ap->a_vp, (unsigned int)ap->a_flags, (unsigned int)ap->a_p, 0, 0);
#endif
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
priv_data = (struct volfs_vndata *) ap->a_vp->v_data;
retval = lockmgr(&priv_data->lock, ap->a_flags | LK_RELEASE,
&ap->a_vp->v_interlock, ap->a_p);
DBG_VOP_LOCKS_TEST(retval);
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 4)) | DBG_FUNC_END,
(unsigned int)ap->a_vp, (unsigned int)ap->a_flags, (unsigned int)ap->a_p, retval, 0);
#endif
return (retval);
}
int
volfs_islocked(ap)
struct vop_islocked_args *ap;
{
int retval;
struct volfs_vndata *priv_data;
DBG_FUNC_NAME("volfs_islocked");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
priv_data = (struct volfs_vndata *) ap->a_vp->v_data;
retval = lockstatus(&priv_data->lock);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
volfs_pathconf(ap)
struct vop_pathconf_args *ap;
{
DBG_VOP(("volfs_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);
}
}
static int
vp_getattrlist(struct vnode *vp, struct attrlist alist, void *attrbufptr, size_t bufsize, unsigned long options, struct proc *p) {
struct iovec iov;
struct uio bufuio;
iov.iov_base = (char *)attrbufptr;
iov.iov_len = bufsize;
bufuio.uio_iov = &iov;
bufuio.uio_iovcnt = 1;
bufuio.uio_offset = 0;
bufuio.uio_resid = iov.iov_len;
bufuio.uio_segflg = UIO_SYSSPACE;
bufuio.uio_rw = UIO_READ;
bufuio.uio_procp = p;
return VOP_GETATTRLIST(vp, &alist, &bufuio, p->p_ucred, p);
}
static int
get_parentvp(struct vnode **vpp, struct mount *mp, struct proc *p)
{
int result;
struct attrlist alist;
struct finfoattrbuf finfobuf;
struct vnode *child_vp = *vpp;
alist.bitmapcount = 5;
alist.reserved = 0;
alist.commonattr = ATTR_CMN_PAROBJID;
alist.volattr = 0;
alist.dirattr = 0;
alist.fileattr = 0;
alist.forkattr = 0;
result = vp_getattrlist(child_vp, alist, &finfobuf, sizeof(finfobuf), 0, p);
if (result)
return result;
VOP_UNLOCK(child_vp, 0, p);
result = VFS_VGET(mp, &finfobuf.fi.parID.fid_objno, vpp);
if (result) {
vn_lock(child_vp, LK_EXCLUSIVE | LK_RETRY, p);
}
return result;
}
static int
lookup_parent(u_int id, struct vnode *child_vp, struct vnode **parent_vp, struct proc *p)
{
struct nameidata nd;
struct componentname *cnp = &nd.ni_cnd;
struct filedesc *fdp = p->p_fd;
int error;
*parent_vp = NULL;
if (id != 1) {
VREF(child_vp);
nd.ni_startdir = child_vp;
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, (caddr_t)&gDotDot, p);
} else {
struct vnode *root_vp;
error = VFS_ROOT(child_vp->v_mount, &root_vp);
if (error) return error;
VOP_UNLOCK(root_vp, 0, p);
nd.ni_startdir = root_vp;
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, (caddr_t)&gDot, p);
};
nd.ni_cnd.cn_cred = nd.ni_cnd.cn_proc->p_ucred;
cnp->cn_pnbuf = nd.ni_dirp;
nd.ni_pathlen = strlen(cnp->cn_pnbuf);
cnp->cn_pnlen = nd.ni_pathlen + 1;
cnp->cn_flags |= (HASBUF | SAVENAME);
nd.ni_loopcnt = 0;
if ((nd.ni_rootdir = fdp->fd_rdir) == NULL) nd.ni_rootdir = rootvnode;
cnp->cn_nameptr = cnp->cn_pnbuf;
if (error = lookup(&nd)) {
cnp->cn_pnbuf = NULL;
return (error);
}
if (cnp->cn_flags & ISSYMLINK) return ENOENT;
if (nd.ni_vp == child_vp) return ELOOP;
*parent_vp = nd.ni_vp;
return 0;
}
static int
verify_fullpathaccess(u_int id, struct vnode *targetvp, struct proc *p) {
struct vnode *vp, *parent_vp;
struct mount *mp = targetvp->v_mount;
struct attrlist alist;
struct finfoattrbuf finfobuf;
int result;
struct filedesc *fdp = p->p_fd;
u_int target_id;
u_long vp_id;
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 12)) | DBG_FUNC_START,
(unsigned int)targetvp, (unsigned int)mp, (unsigned int)p, 0, 0);
#endif
vp = targetvp;
vp_id = vp->v_id;
if (vp->v_type != VDIR) {
result = get_parentvp(&vp, mp, p);
if (result) goto err_exit;
};
#if MAXPLCENTRIES
if (volfs_PLCLookup(mp->mnt_stat.f_fsid.val[0], id, p->p_ucred->cr_uid, p->p_pid)) goto lookup_success;
#endif
target_id = id;
while (vp != NULL && (!((vp->v_flag & VROOT) ||
(vp == fdp->fd_cdir) ||
(vp == fdp->fd_rdir)))) {
VOP_UNLOCK(vp, 0, p);
if (result = lookup_parent(target_id, vp, &parent_vp, p)) {
if (result == EACCES && vp == targetvp && vp->v_type == VDIR && (vp->v_flag & VROOT) == 0) {
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
parent_vp = vp;
if (get_parentvp(&parent_vp, mp, p)) {
VOP_UNLOCK(vp, 0, p);
} else {
result = 0;
}
};
if (result) goto lookup_err_exit;
};
if (vp != targetvp) {
vrele(vp);
};
vp = parent_vp;
target_id = 0;
if (((result = VOP_ACCESS(vp, VEXEC, p->p_ucred, p)) != 0) &&
((result = VOP_ACCESS(vp, VREAD, p->p_ucred, p)) != 0)) {
VOP_UNLOCK(vp, 0, p);
goto lookup_err_exit;
};
};
#if MAXPLCENTRIES
volfs_PLCEnter(mp->mnt_stat.f_fsid.val[0], id, p->p_ucred->cr_uid, p->p_pid);
#endif
lookup_success:
result = 0;
if (vp && vp != targetvp) VOP_UNLOCK(vp, 0, p);
lookup_err_exit:
if (vp && vp != targetvp) {
vrele(vp);
vn_lock(targetvp, LK_EXCLUSIVE | LK_RETRY, p);
if (vp_id != targetvp->v_id || targetvp->v_type == VBAD) {
result = EAGAIN;
}
};
err_exit:
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 12)) | DBG_FUNC_END,
(unsigned int)targetvp, (unsigned int)mp, (unsigned int)p, result, 0);
#endif
return result;
};
static int
get_fsvnode(our_mount, id, ret_vnode)
struct mount *our_mount;
int id;
struct vnode **ret_vnode;
{
register struct mount *mp;
struct mount *cur_mount;
struct vnode *cur_vnode;
struct volfs_vndata *cur_privdata;
int retval;
cur_mount = NULL;
simple_lock(&mountlist_slock);
for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next)
{
if (validfsnode(mp) && mp->mnt_stat.f_fsid.val[0] == id)
{
cur_mount = mp;
break;
}
}
simple_unlock(&mountlist_slock);
if (cur_mount == NULL) {
*ret_vnode = NULL;
return ENOENT;
};
search_vnodelist:
cur_vnode = our_mount->mnt_vnodelist.lh_first;
while (cur_vnode != NULL)
{
cur_privdata = (struct volfs_vndata *) cur_vnode->v_data;
if (cur_privdata->nodeID == id)
{
if (cur_privdata->fs_mount != cur_mount) {
DBG_VOP(("volfs get_fsvnode: Updating fs_mount for vnode 0x%08lX (id = %d) from 0x%08lX to 0x%08lX...\n",
(unsigned long)cur_vnode,
cur_privdata->nodeID,
(unsigned long)cur_privdata->fs_mount,
(unsigned long)cur_mount));
cur_privdata->fs_mount = cur_mount;
};
break;
}
cur_vnode = cur_vnode->v_mntvnodes.le_next;
}
if (cur_vnode) {
if (vget(cur_vnode, LK_EXCLUSIVE, current_proc()) != 0) {
goto search_vnodelist;
};
}
else
{
MALLOC(cur_privdata, struct volfs_vndata *,
sizeof(struct volfs_vndata), M_VOLFSNODE, M_WAITOK);
retval = getnewvnode(VT_VOLFS, our_mount, volfs_vnodeop_p, &cur_vnode);
if (retval != 0) {
FREE(cur_privdata, M_VOLFSNODE);
return retval;
};
cur_privdata->vnode_type = VOLFS_FSNODE;
cur_privdata->nodeID = id;
cur_privdata->fs_mount = cur_mount;
lockinit(&cur_privdata->lock, PINOD, "volfsnode", 0, 0);
lockmgr(&cur_privdata->lock, LK_EXCLUSIVE, (struct slock *)0, current_proc());
cur_vnode->v_data = cur_privdata;
cur_vnode->v_type = VDIR;
DBG_VOP(("get_fsvnode returned with new node of "));
DBG_VOP_PRINT_VNODE_INFO(cur_vnode);DBG_VOP(("\n"));
}
*ret_vnode = cur_vnode;
return (0);
}
static int
get_filevnode(parent_fs, id, ret_vnode, p)
struct mount *parent_fs;
u_int id;
struct vnode **ret_vnode;
struct proc *p;
{
int retval;
again:
if (id == 2)
retval = VFS_ROOT(parent_fs, ret_vnode);
else
retval = VFS_VGET(parent_fs, &id, ret_vnode);
if (retval) goto error;
retval = verify_fullpathaccess(id, *ret_vnode, p);
if (retval) {
vput(*ret_vnode);
*ret_vnode = NULL;
if (retval == EAGAIN) {
goto again;
}
};
error:
return (retval);
}
int
volfs_lookup(ap)
struct vop_lookup_args *ap;
{
struct volfs_vndata *priv_data;
char *cnp;
long namelen;
struct mount *parent_fs;
int unlocked_parent = 0, isdot_or_dotdot = 0;
int ret_err = ENOENT;
DBG_FUNC_NAME("volfs_lookup");
DBG_VOP_LOCKS_DECL(2);
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 8)) | DBG_FUNC_START,
(unsigned int)ap->a_dvp, (unsigned int)ap->a_cnp, (unsigned int)p, 0, 0);
#endif
DBG_VOP(("volfs_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP(("\n"));
DBG_VOP(("\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP(("\n"));
if (ap->a_cnp->cn_flags & LOCKPARENT)
DBG_VOP(("\tLOCKPARENT is set\n"));
if (ap->a_cnp->cn_flags & ISLASTCN)
{
DBG_VOP(("\tISLASTCN is set\n"));
if (ap->a_cnp->cn_nameiop == DELETE || ap->a_cnp->cn_nameiop == RENAME)
{
ret_err = EROFS;
goto Err_Exit;
}
}
priv_data = ap->a_dvp->v_data;
cnp = ap->a_cnp->cn_nameptr;
namelen = ap->a_cnp->cn_namelen;
#if VOLFS_DEBUG
switch (priv_data->vnode_type) {
case VOLFS_ROOT:
DBG_VOP(("\tparent directory (vnode 0x%08lX) vnode_type is VOLFS_ROOT.\n", (unsigned long)ap->a_dvp));
break;
case VOLFS_FSNODE:
DBG_VOP(("\tparent directory (vnode 0x%08lX) vnode_type is VOLFS_FSNODE, nodeID = %d, fs_mount = 0x%08lX.\n",
(unsigned long)ap->a_dvp,
priv_data->nodeID,
(unsigned long)priv_data->fs_mount));
default:
DBG_VOP(("\tparent directory (vnode 0x%08lX) has unknown vnode_type (%d), nodeID = %d.\n",
(unsigned long)ap->a_dvp,
priv_data->vnode_type,
priv_data->nodeID));
};
#endif
if (cnp[0] == '.')
{
if (namelen == 1)
{
isdot_or_dotdot = 1;
*ap->a_vpp = ap->a_dvp;
VREF(*ap->a_vpp);
DBG_VOP_LOCKS_TEST(0);
ret_err = 0;
}
else if (cnp[1] == '.' && namelen == 2)
{
isdot_or_dotdot = 1;
ret_err = volfs_root(ap->a_dvp->v_mount, ap->a_vpp);
}
}
else if (cnp[0] == '@')
{
if ((namelen == 1) && (priv_data->vnode_type != VOLFS_ROOT)) {
parent_fs = priv_data->fs_mount;
if (!(ap->a_cnp->cn_flags & LOCKPARENT) || !(ap->a_cnp->cn_flags & ISLASTCN)) {
VOP_UNLOCK(ap->a_dvp, 0, ap->a_cnp->cn_proc);
unlocked_parent = 1;
};
ret_err = VFS_ROOT(parent_fs, ap->a_vpp);
} else {
DBG_VOP(("volfs_lookup: pathname = '@' but namelen = %ld and parent vnode_type = %d.\n", namelen, priv_data->vnode_type));
*ap->a_vpp = NULL;
ret_err = ENOENT;
};
}
else if (namelen <= 10 && cnp[0] > '0' && cnp[0] <= '9')
{
char *check_ptr;
u_long id;
id = strtoul(cnp, &check_ptr, 10);
if ((check_ptr - cnp) == namelen)
{
if (priv_data->vnode_type == VOLFS_ROOT)
ret_err = get_fsvnode(ap->a_dvp->v_mount, id, ap->a_vpp);
else {
parent_fs = priv_data->fs_mount;
if (!(ap->a_cnp->cn_flags & LOCKPARENT) || !(ap->a_cnp->cn_flags & ISLASTCN)) {
VOP_UNLOCK(ap->a_dvp, 0, ap->a_cnp->cn_proc);
unlocked_parent = 1;
};
ret_err = get_filevnode(parent_fs, id, ap->a_vpp, ap->a_cnp->cn_proc);
}
}
}
if (!isdot_or_dotdot && *ap->a_vpp && VPARENT(*ap->a_vpp) == NULL && ap->a_dvp != *ap->a_vpp) {
if (VPARENT(ap->a_dvp) == *ap->a_vpp) {
panic("volfs: ap->a_dvp 0x%x has parent == a_vpp 0x%x\n",
ap->a_dvp, *ap->a_vpp);
}
vget(ap->a_dvp, 0, ap->a_cnp->cn_proc);
VPARENT(*ap->a_vpp) = ap->a_dvp;
}
if (!unlocked_parent && (!(ap->a_cnp->cn_flags & LOCKPARENT) || !(ap->a_cnp->cn_flags & ISLASTCN))) {
VOP_UNLOCK(ap->a_dvp, 0, ap->a_cnp->cn_proc);
};
Err_Exit:
DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
DBG_VOP_LOCKS_TEST(ret_err);
#if 0
KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 8)) | DBG_FUNC_START,
(unsigned int)ap->a_dvp, (unsigned int)ap->a_cnp, (unsigned int)p, ret_err, 0);
#endif
return (ret_err);
}
#if DBG_VOP_TEST_LOCKS
#if 0
static void DbgLookupTest( char *funcname, struct componentname *cnp, struct vnode *dvp, struct vnode *vp)
{
int flags = cnp->cn_flags;
int nameiop = cnp->cn_nameiop;
DBG_VOP (("%s: Action:", funcname));
switch (nameiop)
{
case LOOKUP:
PRINTIT ("LOOKUP");
break;
case CREATE:
PRINTIT ("CREATE");
break;
case DELETE:
PRINTIT ("DELETE");
break;
case RENAME:
PRINTIT ("RENAME");
break;
default:
PRINTIT ("!!!UNKNOWN!!!!");
break;
}
PRINTIT(" flags: 0x%x ",flags );
if (flags & LOCKPARENT)
PRINTIT (" Lock Parent");
if (flags & ISLASTCN)
PRINTIT (" Last Action");
PRINTIT("\n");
if (dvp)
{
PRINTIT ("%s: Parent vnode exited ", funcname);
if (VOP_ISLOCKED(dvp))
PRINTIT("LOCKED\n");
else
PRINTIT("UNLOCKED\n");
}
if (vp && vp==dvp)
{
PRINTIT ("%s: Found and Parent are the same\n", funcname);
}
else if (vp)
{
PRINTIT ("%s: Found vnode exited ", funcname);
if (VOP_ISLOCKED(vp))
PRINTIT("LOCKED\n");
else
PRINTIT("UNLOCKED\n");
}
else
PRINTIT ("%s: Found vnode exited NULL\n", funcname);
}
#endif
static void DbgVopTest( int maxSlots,
int retval,
VopDbgStoreRec *VopDbgStore,
char *funcname)
{
int index;
for (index = 0; index < maxSlots; index++)
{
if (VopDbgStore[index].id != index) {
PRINTIT("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname, VopDbgStore[index].id, index);
return;
};
if ((VopDbgStore[index].vp != NULL) &&
((VopDbgStore[index].vp->v_data==NULL)))
continue;
switch (VopDbgStore[index].inState)
{
case VOPDBG_IGNORE:
case VOPDBG_SAME:
break;
case VOPDBG_LOCKED:
case VOPDBG_UNLOCKED:
case VOPDBG_LOCKNOTNIL:
{
if (VopDbgStore[index].vp == NULL && (VopDbgStore[index].inState != VOPDBG_LOCKNOTNIL)) {
PRINTIT ("%s: InState check: Null vnode ptr in entry #%d\n", funcname, index);
} else if (VopDbgStore[index].vp != NULL) {
switch (VopDbgStore[index].inState)
{
case VOPDBG_LOCKED:
case VOPDBG_LOCKNOTNIL:
if (VopDbgStore[index].inValue == 0)
{
PRINTIT ("%s: %d Entry: not LOCKED:", funcname, index); DBG_VOP(("\n"));
}
break;
case VOPDBG_UNLOCKED:
if (VopDbgStore[index].inValue != 0)
{
PRINTIT ("%s: %d Entry: not UNLOCKED:", funcname, index); DBG_VOP(("\n"));
}
break;
}
}
break;
}
default:
PRINTIT ("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname, VopDbgStore[index].errState);
}
if (retval != 0)
{
switch (VopDbgStore[index].errState)
{
case VOPDBG_IGNORE:
break;
case VOPDBG_LOCKED:
case VOPDBG_UNLOCKED:
case VOPDBG_SAME:
{
if (VopDbgStore[index].vp == NULL) {
PRINTIT ("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname, index);
} else {
VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
switch (VopDbgStore[index].errState)
{
case VOPDBG_LOCKED:
if (VopDbgStore[index].outValue == 0)
{
PRINTIT ("%s: %d Error: not LOCKED:", funcname, index); DBG_VOP(("\n"));
}
break;
case VOPDBG_UNLOCKED:
if (VopDbgStore[index].outValue != 0)
{
PRINTIT ("%s: %d Error: not UNLOCKED:", funcname, index); DBG_VOP(("\n"));
}
break;
case VOPDBG_SAME:
if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
PRINTIT ("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue);
break;
}
}
break;
}
case VOPDBG_LOCKNOTNIL:
if (VopDbgStore[index].vp != NULL) {
VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
if (VopDbgStore[index].outValue == 0)
PRINTIT ("%s: Error: %d Not LOCKED: 0x%x\n", funcname, index, (u_int)VopDbgStore[index].vp);
}
break;
default:
PRINTIT ("%s: Error: bad lock test value: %d\n", funcname, VopDbgStore[index].errState);
}
}
else
{
switch (VopDbgStore[index].outState)
{
case VOPDBG_IGNORE:
break;
case VOPDBG_LOCKED:
case VOPDBG_UNLOCKED:
case VOPDBG_SAME:
if (VopDbgStore[index].vp == NULL) {
PRINTIT ("%s: OutState: Null vnode ptr in entry #%d\n", funcname, index);
};
if (VopDbgStore[index].vp != NULL)
{
VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
switch (VopDbgStore[index].outState)
{
case VOPDBG_LOCKED:
if (VopDbgStore[index].outValue == 0)
{
PRINTIT ("%s: %d Out: not LOCKED:", funcname, index); DBG_VOP(("\n"));
}
break;
case VOPDBG_UNLOCKED:
if (VopDbgStore[index].outValue != 0)
{
PRINTIT ("%s: %d Out: not UNLOCKED:", funcname, index); DBG_VOP(("\n"));
}
break;
case VOPDBG_SAME:
if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
PRINTIT ("%s: Out: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue);
break;
}
}
break;
case VOPDBG_LOCKNOTNIL:
if (VopDbgStore[index].vp != NULL) {
if (&((struct volfs_vndata *)(VopDbgStore[index].vp->v_data))->lock == NULL)
PRINTIT ("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname, (u_int)VopDbgStore[index].vp);
else {
VopDbgStore[index].outValue = VOP_ISLOCKED(VopDbgStore[index].vp);
if (VopDbgStore[index].outValue == 0)
{
PRINTIT ("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname); DBG_VOP(("\n"));
}
}
}
break;
default:
PRINTIT ("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname, VopDbgStore[index].outState);
}
}
VopDbgStore[index].id = -1;
}
}
#endif