#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ucred.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/file.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <libkern/libkern.h>
#include "webdav.h"
#include "webdav_utils.h"
extern struct vnodeopv_desc webdav_vnodeop_opv_desc;
char webdav_name[MFSNAMELEN] = "webdav";
lck_grp_t *webdav_rwlock_group;
lck_rw_t ref_tbl_rwlock;
static long webdav_mnt_cnt = 0;
static vfstable_t webdav_vfsconf;
#define WEBDAV_MAX_REFS 256
static struct open_associatecachefile *webdav_ref_table[WEBDAV_MAX_REFS];
static int webdav_vfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, struct webdav_vfsstatfs in_statfs);
extern void webdav_up(struct webdavmount *);
static struct vnodeopv_desc *webdav_vnodeop_opv_desc_list[1] =
{
&webdav_vnodeop_opv_desc
};
static void webdav_init_ref_table(void)
{
int ref;
lck_rw_lock_exclusive(&ref_tbl_rwlock);
for ( ref = 0; ref < WEBDAV_MAX_REFS; ++ref )
{
webdav_ref_table[ref] = NULL;
}
lck_rw_done(&ref_tbl_rwlock);
}
__private_extern__
int webdav_assign_ref(struct open_associatecachefile *associatecachefile, int *ref)
{
int i;
int error;
struct timespec ts;
while ( TRUE )
{
lck_rw_lock_exclusive(&ref_tbl_rwlock);
for ( i = 0; i < WEBDAV_MAX_REFS; ++i )
{
if ( webdav_ref_table[i] == NULL )
{
webdav_ref_table[i] = associatecachefile;
*ref = i;
lck_rw_done(&ref_tbl_rwlock);
return ( 0 );
}
}
lck_rw_done(&ref_tbl_rwlock);
ts.tv_sec = 1;
ts.tv_nsec = 0;
error = msleep((caddr_t)&webdav_ref_table, NULL, PCATCH, "webdav_get_open_ref", &ts);
if ( error && (error != EWOULDBLOCK) )
{
*ref = -1;
return ( EIO );
}
}
}
static int webdav_translate_ref(int ref, struct open_associatecachefile **associatecachefile)
{
int error = 0;
if ( (ref < 0) || (ref >= WEBDAV_MAX_REFS) )
{
return ( EIO );
}
lck_rw_lock_shared(&ref_tbl_rwlock);
*associatecachefile = webdav_ref_table[ref];
if ( *associatecachefile == NULL )
{
error = EIO;
}
lck_rw_done(&ref_tbl_rwlock);
return (error);
}
__private_extern__
void webdav_release_ref(int ref)
{
if ( (ref >= 0) && (ref < WEBDAV_MAX_REFS) )
{
lck_rw_lock_exclusive(&ref_tbl_rwlock);
webdav_ref_table[ref] = NULL;
lck_rw_done(&ref_tbl_rwlock);
wakeup((caddr_t)&webdav_ref_table);
}
}
static int webdav_init(struct vfsconf *vfsp)
{
#pragma unused(vfsp)
START_MARKER("webdav_init");
webdav_rwlock_group = lck_grp_alloc_init("webdav-rwlock", LCK_GRP_ATTR_NULL);
lck_rw_init(&ref_tbl_rwlock, webdav_rwlock_group, LCK_ATTR_NULL);
webdav_init_ref_table();
webdav_hashinit();
RET_ERR("webdav_init", 0);
}
static int webdav_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context)
{
#pragma unused(devvp)
struct user_webdav_args args;
struct webdavmount *fmp = NULL;
vnode_t rvp;
size_t size;
int error;
struct timeval tv;
struct timespec ts;
struct vfsstatfs *vfsp;
struct webdav_timespec64 wts;
START_MARKER("webdav_mount");
++webdav_mnt_cnt;
if (vfs_isupdate(mp))
{
error = ENOTSUP;
goto bad;
}
vfs_setflags(mp, MNT_NOEXEC);
if ( vfs_context_is64bit(context) )
{
error = copyin(data, (caddr_t)&args, sizeof(struct user_webdav_args));
if (error)
{
goto bad;
}
}
else
{
struct webdav_args args_32;
error = copyin(data, (caddr_t)&args_32, sizeof(struct webdav_args));
if (error)
{
goto bad;
}
args.pa_mntfromname = CAST_USER_ADDR_T(args_32.pa_mntfromname);
args.pa_version = args_32.pa_version;
args.pa_socket_namelen = args_32.pa_socket_namelen;
args.pa_socket_name = CAST_USER_ADDR_T(args_32.pa_socket_name);
args.pa_vol_name = CAST_USER_ADDR_T(args_32.pa_vol_name);
args.pa_flags = args_32.pa_flags;
args.pa_server_ident = args_32.pa_server_ident;
args.pa_root_id = args_32.pa_root_id;
args.pa_root_fileid = args_32.pa_root_fileid;
args.pa_uid = args_32.pa_uid;
args.pa_gid = args_32.pa_gid;
args.pa_dir_size = args_32.pa_dir_size;
args.pa_link_max = args_32.pa_link_max;
args.pa_name_max = args_32.pa_name_max;
args.pa_path_max = args_32.pa_path_max;
args.pa_pipe_buf = args_32.pa_pipe_buf;
args.pa_chown_restricted = args_32.pa_chown_restricted;
args.pa_no_trunc = args_32.pa_no_trunc;
bcopy (&args_32.pa_vfsstatfs, &args.pa_vfsstatfs, sizeof (args.pa_vfsstatfs));
}
if (args.pa_version != kCurrentWebdavArgsVersion)
{
error = EINVAL;
printf("the webdav_fs.kext and mount_webdav executables are incompatible\n");
goto bad;
}
MALLOC(fmp, struct webdavmount *, sizeof(struct webdavmount), M_TEMP, M_WAITOK);
if ( fmp == NULL )
{
error = EINVAL;
goto bad;
}
bzero(fmp, sizeof(struct webdavmount));
lck_mtx_init(&fmp->pm_mutex, webdav_rwlock_group, LCK_ATTR_NULL);
fmp->pm_status = WEBDAV_MOUNT_SUPPORTS_STATFS;
if ( args.pa_flags & WEBDAV_SUPPRESSALLUI )
{
fmp->pm_status |= WEBDAV_MOUNT_SUPPRESS_ALL_UI;
}
if ( args.pa_flags & WEBDAV_SECURECONNECTION )
{
fmp->pm_status |= WEBDAV_MOUNT_SECURECONNECTION;
}
fmp->pm_server_ident = args.pa_server_ident;
fmp->pm_uid = args.pa_uid;
fmp->pm_gid = args.pa_gid;
fmp->pm_mountp = mp;
MALLOC(fmp->pm_vol_name, caddr_t, NAME_MAX + 1, M_TEMP, M_WAITOK);
bzero(fmp->pm_vol_name, NAME_MAX + 1);
error = copyinstr(args.pa_vol_name, fmp->pm_vol_name, NAME_MAX, &size);
if (error)
{
goto bad;
}
if ((unsigned int)args.pa_socket_namelen > sizeof(struct sockaddr_un))
{
printf("%s: socket_name_len arg too big: %d\n", __FUNCTION__, args.pa_socket_namelen);
error = EINVAL;
goto bad;
}
MALLOC(fmp->pm_socket_name, struct sockaddr *, args.pa_socket_namelen, M_TEMP, M_WAITOK);
error = copyin(args.pa_socket_name, fmp->pm_socket_name, args.pa_socket_namelen);
if (error)
{
goto bad;
}
fmp->pm_open_connections = 0;
fmp->pm_dir_size = args.pa_dir_size;
fmp->pm_link_max = args.pa_link_max;
fmp->pm_name_max = args.pa_name_max;
fmp->pm_path_max = args.pa_path_max;
fmp->pm_pipe_buf = args.pa_pipe_buf;
fmp->pm_chown_restricted = args.pa_chown_restricted;
fmp->pm_no_trunc = args.pa_no_trunc;
vfs_setfsprivate(mp, (void *)fmp);
vfs_getnewfsid(mp);
(void)copyinstr(args.pa_mntfromname, vfs_statfs(mp)->f_mntfromname, MNAMELEN - 1, &size);
bzero(vfs_statfs(mp)->f_mntfromname + size, MNAMELEN - size);
microtime(&tv);
TIMEVAL_TO_TIMESPEC(&tv, &ts);
timespec_to_webdav_timespec64(ts, &wts);
error = webdav_get(mp, NULLVP, 1, NULL,
args.pa_root_id, args.pa_root_fileid, VDIR, wts, wts, wts, wts, fmp->pm_dir_size, &rvp);
if (error)
{
goto bad;
}
webdav_unlock(VTOWEBDAV(rvp));
error = vnode_ref(rvp);
(void) vnode_put(rvp);
if (error)
{
goto bad;
}
fmp->pm_root = rvp;
vfs_setauthopaque(mp);
vfsp = vfs_statfs (mp);
(void) webdav_vfs_statfs(mp, vfsp, args.pa_vfsstatfs);
return (0);
bad:
--webdav_mnt_cnt;
if ( fmp != NULL )
{
if ( fmp->pm_vol_name != NULL )
{
FREE(fmp->pm_vol_name, M_TEMP);
}
if ( fmp->pm_socket_name != NULL )
{
FREE(fmp->pm_socket_name, M_TEMP);
}
lck_mtx_destroy(&fmp->pm_mutex, webdav_rwlock_group);
FREE(fmp, M_TEMP);
vfs_setfsprivate(mp, NULL);
}
RET_ERR("webdav_mount", error);
}
static int webdav_start(struct mount *mp, int flags, vfs_context_t context)
{
#pragma unused(mp, flags, context)
START_MARKER("webdav_start");
RET_ERR("webdav_start", 0);
}
static int webdav_unmount(struct mount *mp, int mntflags, vfs_context_t context)
{
vnode_t rootvp = VFSTOWEBDAV(mp)->pm_root;
int error = 0;
int flags = 0;
struct webdavmount *fmp;
int server_error;
struct webdav_request_unmount request_unmount;
START_MARKER("webdav_unmount");
fmp = VFSTOWEBDAV(mp);
if (mntflags & MNT_FORCE)
{
flags |= FORCECLOSE;
}
error = vflush(mp, rootvp, flags);
if (error)
{
return (error);
}
if ( vnode_isinuse(rootvp, 1) && !(flags & FORCECLOSE) )
{
return (EBUSY);
}
webdav_copy_creds(context, &request_unmount.pcr);
(void) webdav_sendmsg(WEBDAV_UNMOUNT, fmp,
&request_unmount, sizeof(struct webdav_request_unmount),
NULL, 0,
&server_error, NULL, 0);
vnode_rele(rootvp);
vflush(mp, NULLVP, FORCECLOSE);
vfs_setfsprivate(mp, NULL);
FREE(fmp->pm_vol_name, M_TEMP);
FREE(fmp->pm_socket_name, M_TEMP);
lck_mtx_destroy(&fmp->pm_mutex, webdav_rwlock_group);
FREE(fmp, M_TEMP);
--webdav_mnt_cnt;
RET_ERR("webdav_unmount", error);
}
static int webdav_root(struct mount *mp, struct vnode **vpp, vfs_context_t context)
{
#pragma unused(context)
int error;
vnode_t vp;
START_MARKER("webdav_root");
vp = VFSTOWEBDAV(mp)->pm_root;
error = vnode_get(vp);
if ( error )
{
*vpp = NULLVP;
}
else
{
*vpp = vp;
}
RET_ERR("webdav_root", error);
}
static int webdav_vfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, struct webdav_vfsstatfs in_statfs)
{
struct webdavmount *fmp = VFSTOWEBDAV(mp);
START_MARKER("webdav_vfs_statfs");
if (!in_statfs.f_bsize)
sbp->f_bsize = (uint32_t) S_BLKSIZE;
else
sbp->f_bsize = (uint32_t) in_statfs.f_bsize;
if (!in_statfs.f_iosize)
sbp->f_iosize = (uint32_t) 0x1000;
else
sbp->f_iosize = (uint32_t) in_statfs.f_iosize;
fmp->pm_iosize = sbp->f_iosize;
if (!in_statfs.f_blocks) {
fmp->pm_status &= ~WEBDAV_MOUNT_SUPPORTS_STATFS;
sbp->f_blocks = (uint64_t) WEBDAV_NUM_BLOCKS;
sbp->f_bfree = (uint64_t) WEBDAV_FREE_BLOCKS;
sbp->f_bavail = (uint64_t) WEBDAV_FREE_BLOCKS;
}
else {
sbp->f_blocks = (uint64_t) in_statfs.f_blocks;
sbp->f_bfree = (uint64_t) in_statfs.f_bfree;
if (!in_statfs.f_bavail) {
sbp->f_bavail = (uint64_t) in_statfs.f_bfree;
}
else {
sbp->f_bavail = (uint64_t) in_statfs.f_bavail;
}
}
if (!in_statfs.f_files)
sbp->f_files = (uint64_t) WEBDAV_NUM_FILES;
else
sbp->f_files = (uint64_t) in_statfs.f_files;
if (!in_statfs.f_ffree)
sbp->f_ffree = (uint64_t) WEBDAV_FREE_FILES;
else
sbp->f_ffree = (uint64_t) in_statfs.f_ffree;
if ( fmp->pm_status & WEBDAV_MOUNT_SECURECONNECTION )
sbp->f_fssubtype = 1;
else
sbp->f_fssubtype = 0;
RET_ERR("webdav_vfs_statfs", 0);
return (0);
}
static int webdav_vfs_getattr(struct mount *mp, struct vfs_attr *sbp, vfs_context_t context)
{
struct webdavmount *fmp;
struct webdav_reply_statfs reply_statfs;
int error = 0;
int server_error = 0;
int callServer = 0;
struct webdav_request_statfs request_statfs;
START_MARKER("webdav_vfs_getattr");
fmp = VFSTOWEBDAV(mp);
bzero(&reply_statfs, sizeof(struct webdav_reply_statfs));
if ( VFSATTR_IS_ACTIVE(sbp, f_bsize) || VFSATTR_IS_ACTIVE(sbp, f_blocks) ||
VFSATTR_IS_ACTIVE(sbp, f_bfree) || VFSATTR_IS_ACTIVE(sbp, f_bavail) ||
VFSATTR_IS_ACTIVE(sbp, f_files) || VFSATTR_IS_ACTIVE(sbp, f_ffree))
{
callServer = 1;
}
if (callServer)
{
lck_mtx_lock(&fmp->pm_mutex);
if (fmp->pm_status & WEBDAV_MOUNT_SUPPORTS_STATFS)
{
while (fmp->pm_status & WEBDAV_MOUNT_STATFS)
{
fmp->pm_status |= WEBDAV_MOUNT_STATFS_WANTED;
error = msleep((caddr_t)&fmp->pm_status, &fmp->pm_mutex, PCATCH, "webdav_vfs_getattr", NULL);
if ( error )
{
lck_mtx_unlock(&fmp->pm_mutex);
goto ready;
}
}
fmp->pm_status |= WEBDAV_MOUNT_STATFS;
lck_mtx_unlock(&fmp->pm_mutex);
webdav_copy_creds(context, &request_statfs.pcr);
request_statfs.root_obj_id = VTOWEBDAV(VFSTOWEBDAV(mp)->pm_root)->pt_obj_id;
error = webdav_sendmsg(WEBDAV_STATFS, fmp,
&request_statfs, sizeof(struct webdav_request_statfs),
NULL, 0,
&server_error, &reply_statfs, sizeof(struct webdav_reply_statfs));
lck_mtx_lock(&fmp->pm_mutex);
fmp->pm_status &= ~WEBDAV_MOUNT_STATFS;
if ( fmp->pm_status & WEBDAV_MOUNT_STATFS_WANTED )
{
fmp->pm_status &= ~WEBDAV_MOUNT_STATFS_WANTED;
wakeup((caddr_t)&fmp->pm_status);
}
}
lck_mtx_unlock(&fmp->pm_mutex);
}
ready:
if (VFSATTR_IS_ACTIVE(sbp, f_bsize))
{
if (!reply_statfs.fs_attr.f_bsize)
{
VFSATTR_RETURN(sbp, f_bsize, S_BLKSIZE);
}
else
{
VFSATTR_RETURN(sbp, f_bsize, (uint32_t)reply_statfs.fs_attr.f_bsize);
}
}
if (VFSATTR_IS_ACTIVE(sbp, f_iosize))
VFSATTR_RETURN(sbp, f_iosize, fmp->pm_iosize);
if (VFSATTR_IS_ACTIVE(sbp, f_blocks))
{
if (!reply_statfs.fs_attr.f_blocks)
{
if ( error == 0 && server_error == 0 )
{
fmp->pm_status &= ~WEBDAV_MOUNT_SUPPORTS_STATFS;
}
}
else
{
VFSATTR_RETURN(sbp, f_blocks, reply_statfs.fs_attr.f_blocks);
VFSATTR_RETURN(sbp, f_bfree, reply_statfs.fs_attr.f_bfree);
if (!reply_statfs.fs_attr.f_bavail)
{
VFSATTR_RETURN(sbp, f_bavail, reply_statfs.fs_attr.f_bfree);
}
else
{
VFSATTR_RETURN(sbp, f_bavail, reply_statfs.fs_attr.f_bavail);
}
}
}
if (VFSATTR_IS_ACTIVE(sbp, f_files))
{
if (!reply_statfs.fs_attr.f_files)
{
VFSATTR_RETURN(sbp, f_files, WEBDAV_NUM_FILES);
}
else
{
VFSATTR_RETURN(sbp, f_files, reply_statfs.fs_attr.f_files);
}
}
if (VFSATTR_IS_ACTIVE(sbp, f_ffree))
{
if (!reply_statfs.fs_attr.f_ffree)
{
VFSATTR_RETURN(sbp, f_ffree, WEBDAV_FREE_FILES);
}
else
{
VFSATTR_RETURN(sbp, f_ffree, reply_statfs.fs_attr.f_ffree);
}
}
if (VFSATTR_IS_ACTIVE(sbp, f_fssubtype))
{
if ( fmp->pm_status & WEBDAV_MOUNT_SECURECONNECTION )
{
VFSATTR_RETURN(sbp, f_fssubtype, 1);
}
else
{
VFSATTR_RETURN(sbp, f_fssubtype, 0);
}
}
if ( VFSATTR_IS_ACTIVE(sbp, f_capabilities) )
{
vol_capabilities_attr_t *vcapattrptr;
vcapattrptr = &sbp->f_capabilities;
vcapattrptr->capabilities[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_FAST_STATFS;
vcapattrptr->capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_2TB_FILESIZE;
vcapattrptr->capabilities[VOL_CAPABILITIES_INTERFACES] =
0;
vcapattrptr->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
vcapattrptr->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
vcapattrptr->valid[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_PERSISTENTOBJECTIDS |
VOL_CAP_FMT_SYMBOLICLINKS |
VOL_CAP_FMT_HARDLINKS |
VOL_CAP_FMT_JOURNAL |
VOL_CAP_FMT_JOURNAL_ACTIVE |
VOL_CAP_FMT_NO_ROOT_TIMES |
VOL_CAP_FMT_SPARSE_FILES |
VOL_CAP_FMT_ZERO_RUNS |
#if 0
VOL_CAP_FMT_CASE_SENSITIVE |
VOL_CAP_FMT_CASE_PRESERVING |
#endif
VOL_CAP_FMT_2TB_FILESIZE |
VOL_CAP_FMT_FAST_STATFS;
if ( !(fmp->pm_status & WEBDAV_MOUNT_SUPPORTS_STATFS)) {
vcapattrptr->capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_VOLUME_SIZES;
vcapattrptr->valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_VOLUME_SIZES;
}
vcapattrptr->valid[VOL_CAPABILITIES_INTERFACES] =
VOL_CAP_INT_SEARCHFS |
VOL_CAP_INT_ATTRLIST |
VOL_CAP_INT_NFSEXPORT |
VOL_CAP_INT_READDIRATTR |
VOL_CAP_INT_EXCHANGEDATA |
VOL_CAP_INT_COPYFILE |
VOL_CAP_INT_ALLOCATE |
VOL_CAP_INT_VOL_RENAME |
VOL_CAP_INT_ADVLOCK |
VOL_CAP_INT_FLOCK;
vcapattrptr->valid[VOL_CAPABILITIES_RESERVED1] = 0;
vcapattrptr->valid[VOL_CAPABILITIES_RESERVED2] = 0;
VFSATTR_SET_SUPPORTED(sbp, f_capabilities);
}
if ( VFSATTR_IS_ACTIVE(sbp, f_attributes) )
{
enum
{
WEBDAV_ATTR_CMN_NATIVE = ATTR_CMN_CRTIME |
ATTR_CMN_MODTIME,
WEBDAV_ATTR_CMN_SUPPORTED = WEBDAV_ATTR_CMN_NATIVE,
WEBDAV_ATTR_VOL_NATIVE = ATTR_VOL_NAME |
ATTR_VOL_CAPABILITIES |
ATTR_VOL_ATTRIBUTES,
WEBDAV_ATTR_VOL_SUPPORTED = WEBDAV_ATTR_VOL_NATIVE,
WEBDAV_ATTR_DIR_NATIVE = 0,
WEBDAV_ATTR_DIR_SUPPORTED = 0,
WEBDAV_ATTR_FILE_NATIVE = 0,
WEBDAV_ATTR_FILE_SUPPORTED = 0,
WEBDAV_ATTR_FORK_NATIVE = 0,
WEBDAV_ATTR_FORK_SUPPORTED = 0,
};
vol_attributes_attr_t *volattrptr;
volattrptr = &sbp->f_attributes;
volattrptr->validattr.commonattr = WEBDAV_ATTR_CMN_SUPPORTED;
volattrptr->validattr.volattr = WEBDAV_ATTR_VOL_SUPPORTED;
volattrptr->validattr.dirattr = WEBDAV_ATTR_DIR_SUPPORTED;
volattrptr->validattr.fileattr = WEBDAV_ATTR_FILE_SUPPORTED;
volattrptr->validattr.forkattr = WEBDAV_ATTR_FORK_SUPPORTED;
volattrptr->nativeattr.commonattr = WEBDAV_ATTR_CMN_NATIVE;
volattrptr->nativeattr.volattr = WEBDAV_ATTR_VOL_NATIVE;
volattrptr->nativeattr.dirattr = WEBDAV_ATTR_DIR_NATIVE;
volattrptr->nativeattr.fileattr = WEBDAV_ATTR_FILE_NATIVE;
volattrptr->nativeattr.forkattr = WEBDAV_ATTR_FORK_NATIVE;
VFSATTR_SET_SUPPORTED(sbp, f_attributes);
}
if ( VFSATTR_IS_ACTIVE(sbp, f_vol_name) )
{
(void) strncpy(sbp->f_vol_name, fmp->pm_vol_name, MAXPATHLEN);
VFSATTR_SET_SUPPORTED(sbp, f_vol_name);
}
RET_ERR("webdav_vfs_getattr", error);
}
static int webdav_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
user_addr_t newp, size_t newlen, vfs_context_t context)
{
#pragma unused(oldlenp, newp)
int error;
struct sysctl_req *req;
struct vfsidctl vc;
struct user_vfsidctl user_vc;
struct mount *mp;
struct webdavmount *fmp;
struct vfsquery vq;
START_MARKER("webdav_sysctl");
switch ( name[0] )
{
case WEBDAV_ASSOCIATECACHEFILE_SYSCTL:
{
int ref;
int fd;
struct open_associatecachefile *associatecachefile;
vnode_t vp;
if ( namelen > 3 )
{
error = ENOTDIR;
break;
}
if ( newlen != 0 )
{
printf("webdav_sysctl: newlen != 0\n");
error = EINVAL;
break;
}
ref = name[1];
fd = name[2];
error = webdav_translate_ref(ref, &associatecachefile);
if ( error != 0 )
{
printf("webdav_sysctl: webdav_translate_ref() failed\n");
break;
}
error = file_vnode_withvid(fd, &vp, NULL);
if ( error != 0 )
{
printf("webdav_sysctl: file_vnode() failed\n");
break;
}
vnode_get(vp);
vnode_ref(vp);
vnode_put(vp);
(void) file_drop(fd);
associatecachefile->cachevp = vp;
associatecachefile->pid = vfs_context_pid(context);
error = 0;
}
break;
case WEBDAV_NOTIFY_RECONNECTED_SYSCTL:
{
fsid_t fsid_num;
fsid_num.val[0] = name[1];
fsid_num.val[1] = name[2];
mp = vfs_getvfs(&fsid_num);
if (mp != NULL) {
webdav_up(VFSTOWEBDAV(mp));
error = 0;
}
else {
error = ENOENT;
}
}
break;
case VFS_CTL_QUERY:
if ( namelen > 1 )
{
error = ENOTDIR;
break;
}
req = CAST_DOWN(struct sysctl_req *, oldp);
if ( vfs_context_is64bit(context) )
{
error = SYSCTL_IN(req, &user_vc, sizeof(user_vc));
if ( error )
{
break;
}
mp = vfs_getvfs(&user_vc.vc_fsid);
}
else
{
error = SYSCTL_IN(req, &vc, sizeof(vc));
if ( error )
{
break;
}
mp = vfs_getvfs(&vc.vc_fsid);
}
if ( mp == NULL )
{
error = ENOENT;
}
else
{
fmp = VFSTOWEBDAV(mp);
bzero(&vq, sizeof(vq));
if ( fmp != NULL )
{
if ( fmp->pm_status & WEBDAV_MOUNT_TIMEO )
{
vq.vq_flags |= VQ_NOTRESP;
}
if ( fmp->pm_status & WEBDAV_MOUNT_DEAD )
{
vq.vq_flags |= VQ_DEAD;
}
}
error = SYSCTL_OUT(req, &vq, sizeof(vq));
}
break;
default:
error = ENOTSUP;
break;
}
RET_ERR("webdav_sysctl", error);
}
#define webdav_sync ((int (*)(struct mount *mp, int waitfor, \
vfs_context_t context)) nullop)
struct vfsops webdav_vfsops = {
webdav_mount,
webdav_start,
webdav_unmount,
webdav_root,
NULL,
webdav_vfs_getattr,
webdav_sync,
NULL,
NULL,
NULL,
webdav_init,
webdav_sysctl,
NULL,
{0}
};
__private_extern__
kern_return_t webdav_fs_module_start(struct kmod_info *ki, void *data)
{
#pragma unused(ki, data)
errno_t error;
struct vfs_fsentry vfe;
bzero(&vfe, sizeof(struct vfs_fsentry));
vfe.vfe_vfsops = &webdav_vfsops;
vfe.vfe_vopcnt = 1;
vfe.vfe_opvdescs = webdav_vnodeop_opv_desc_list;
vfe.vfe_fstypenum = 0;
strncpy(vfe.vfe_fsname, webdav_name, strlen(webdav_name));
vfe.vfe_flags = VFS_TBLNOTYPENUM | VFS_TBL64BITREADY | VFS_TBLTHREADSAFE |
VFS_TBLFSNODELOCK;
error = vfs_fsadd(&vfe, &webdav_vfsconf);
return (error ? KERN_FAILURE : KERN_SUCCESS);
}
__private_extern__
kern_return_t webdav_fs_module_stop(struct kmod_info *ki, void *data)
{
#pragma unused(ki, data)
int error;
if (webdav_mnt_cnt == 0)
{
error = vfs_fsremove(webdav_vfsconf);
if ( error == 0 )
{
webdav_hashdestroy();
lck_rw_destroy(&ref_tbl_rwlock, webdav_rwlock_group);
lck_grp_free(webdav_rwlock_group);
}
}
else
{
error = EBUSY;
}
return (error ? KERN_FAILURE : KERN_SUCCESS);
}