Index: src/VBox/Additions/freebsd/vboxvfs/Makefile.kmk =================================================================== --- src/VBox/Additions/freebsd/vboxvfs/Makefile.kmk (revision 4) +++ src/VBox/Additions/freebsd/vboxvfs/Makefile.kmk (working copy) @@ -33,12 +33,13 @@ $(vboxvfs_0_OUTDIR) vboxvfs_SOURCES = \ vboxvfs_vfsops.c \ - vboxvfs_vnops.c + vboxvfs_vnops.c \ + vboxvfs_prov.c vboxvfs_LIBS = \ $(VBOX_LIB_VBGL_R0) \ $(VBOX_LIB_IPRT_GUEST_R0) vboxvfs_DEPS = \ - $$(vboxvfs_0_OUTDIR)/vnode_if.h \ +# $$(vboxvfs_0_OUTDIR)/vnode_if.h \ $$(vboxvfs_0_OUTDIR)/vnode_if_newproto.h \ $$(vboxvfs_0_OUTDIR)/vnode_if_typedef.h vboxvfs_CLEAN += $(vboxvfs_DEPS) Index: src/VBox/Additions/freebsd/vboxvfs/vboxvfs_vfsops.c =================================================================== --- src/VBox/Additions/freebsd/vboxvfs/vboxvfs_vfsops.c (revision 4) +++ src/VBox/Additions/freebsd/vboxvfs/vboxvfs_vfsops.c (working copy) @@ -1,4 +1,4 @@ -/* $Id: vboxvfs_vfsops.c $ */ +/* $Id: vboxfs_vfsops.c $ */ /** @file * Description. */ @@ -14,245 +14,525 @@ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ - -#include "vboxvfs.h" +#include #include #include #include #include #include +#include +#include #include #include #include #include +#include +#include +#include #include #include #include -#include +#include +#include +#include "vboxvfs.h" + #define VFSMP2SFGLOBINFO(mp) ((struct sf_glob_info *)mp->mnt_data) -static int vboxvfs_version = VBOXVFS_VERSION; +#ifdef MALLOC_DECLARE +MALLOC_DEFINE(M_VBOXVFS, "vboxvfs", "VBOX VFS"); +#endif -SYSCTL_NODE(_vfs, OID_AUTO, vboxvfs, CTLFLAG_RW, 0, "VirtualBox shared filesystem"); -SYSCTL_INT(_vfs_vboxvfs, OID_AUTO, version, CTLFLAG_RD, &vboxvfs_version, 0, ""); +static int vboxfs_version = VBOXVFS_VERSION; +SYSCTL_NODE(_vfs, OID_AUTO, vboxfs, CTLFLAG_RW, 0, "VirtualBox shared filesystem"); +SYSCTL_INT(_vfs_vboxfs, OID_AUTO, version, CTLFLAG_RD, &vboxfs_version, 0, ""); + /* global connection to the host service. */ static VBSFCLIENT g_vboxSFClient; +static sfp_connection_t *sfprov = NULL; -static vfs_init_t vboxvfs_init; -static vfs_uninit_t vboxvfs_uninit; -static vfs_cmount_t vboxvfs_cmount; -static vfs_mount_t vboxvfs_mount; -static vfs_root_t vboxvfs_root; -static vfs_quotactl_t vboxvfs_quotactl; -static vfs_statfs_t vboxvfs_statfs; -static vfs_unmount_t vboxvfs_unmount; +static vfs_init_t vboxfs_init; +static vfs_uninit_t vboxfs_uninit; +static vfs_cmount_t vboxfs_cmount; +static vfs_mount_t vboxfs_mount; +static vfs_root_t vboxfs_root; +static vfs_quotactl_t vboxfs_quotactl; +static vfs_statfs_t vboxfs_statfs; +static vfs_unmount_t vboxfs_unmount; -static struct vfsops vboxvfs_vfsops = { - .vfs_init = vboxvfs_init, - .vfs_cmount = vboxvfs_cmount, - .vfs_mount = vboxvfs_mount, - .vfs_quotactl = vboxvfs_quotactl, - .vfs_root = vboxvfs_root, - .vfs_statfs = vboxvfs_statfs, - .vfs_sync = vfs_stdsync, - .vfs_uninit = vboxvfs_uninit, - .vfs_unmount = vboxvfs_unmount, +static struct vfsops vboxfs_vfsops = { + .vfs_init = vboxfs_init, + .vfs_cmount = vboxfs_cmount, + .vfs_mount = vboxfs_mount, + .vfs_quotactl = vboxfs_quotactl, + .vfs_root = vboxfs_root, + .vfs_statfs = vboxfs_statfs, + .vfs_sync = vfs_stdsync, + .vfs_uninit = vboxfs_uninit, + .vfs_unmount = vboxfs_unmount, + .vfs_vget = vboxfs_vget, }; -VFS_SET(vboxvfs_vfsops, vboxvfs, VFCF_NETWORK); +VFS_SET(vboxfs_vfsops, vboxvfs, VFCF_NETWORK); MODULE_DEPEND(vboxvfs, vboxguest, 1, 1, 1); -static int vboxvfs_cmount(struct mntarg *ma, void * data, int flags, struct thread *td) +static int vboxfs_cmount(struct mntarg *ma, void *data, uint64_t flags) { - struct vboxvfs_mount_info args; - int rc = 0; + struct vboxfs_mount_info args; + int error = 0; - printf("%s: Enter\n", __FUNCTION__); + printf("%s: Enter\n", __FUNCTION__); + + if (data == NULL) + return (EINVAL); + error = copyin(data, &args, sizeof(struct vboxfs_mount_info)); + if (error) + return (error); - rc = copyin(data, &args, sizeof(struct vboxvfs_mount_info)); - if (rc) - return rc; + ma = mount_argf(ma, "uid", "%d", args.uid); + ma = mount_argf(ma, "gid", "%d", args.gid); + ma = mount_argf(ma, "file_mode", "%d", args.fmode); + ma = mount_argf(ma, "dir_mode", "%d", args.dmode); + ma = mount_arg(ma, "from", args.name, -1); - ma = mount_argf(ma, "uid", "%d", args.uid); - ma = mount_argf(ma, "gid", "%d", args.gid); - ma = mount_arg(ma, "from", args.name, -1); + error = kernel_mount(ma, flags); - rc = kernel_mount(ma, flags); + printf("%s: Leave error=%d\n", __FUNCTION__, error); - printf("%s: Leave rc=%d\n", __FUNCTION__, rc); + return (error); +}; - return rc; -} - -static const char *vboxvfs_opts[] = { - "uid", "gid", "from", "fstype", "fspath", "errmsg", NULL +static const char *vboxfs_opts[] = { + "fstype", "fspath", "from", "uid", "gid", "file_mode", "dir_mode", "errmsg", NULL }; -static int vboxvfs_mount(struct mount *mp, struct thread *td) +static int vboxfs_mount(struct mount *mp) { - int rc; - char *pszShare; - int cbShare, cbOption; - int uid = 0, gid = 0; - struct sf_glob_info *pShFlGlobalInfo; - SHFLSTRING *pShFlShareName = NULL; - int cbShFlShareName; + struct vboxfs_mnt *vboxfsmp = NULL; + struct vboxfs_node *unode; + struct buf *bp = NULL; + struct cdev *dev; + struct g_consumer *cp; + struct bufobj *bo; + struct vnode *devvp; /* vnode of the mount device */ + struct thread *td = curthread; + struct vfsoptlist *opts = mp->mnt_optnew; + struct nameidata nd, *ndp = &nd; + sfp_mount_t *handle; + int error, share_len; + char *share_name; + mode_t file_mode, dir_mode; + char *tmp, *ep; + uid_t uid = 0; + gid_t gid = 0; - printf("%s: Enter\n", __FUNCTION__); + printf("%s: Enter \n", __FUNCTION__); - if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) - return EOPNOTSUPP; + if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) + return (EOPNOTSUPP); - if (vfs_filteropt(mp->mnt_optnew, vboxvfs_opts)) - { - vfs_mount_error(mp, "%s", "Invalid option"); - return EINVAL; - } + if (vfs_filteropt(opts, vboxfs_opts)) + { + vfs_mount_error(mp, "%s", "Invalid option"); + return (EINVAL); + } - rc = vfs_getopt(mp->mnt_optnew, "from", (void **)&pszShare, &cbShare); - if (rc || pszShare[cbShare-1] != '\0' || cbShare > 0xfffe) - return EINVAL; + if (vfs_getopt(opts, "uid", (void **)&tmp, NULL) == 0) { + if (tmp != NULL) + uid = (uid_t)strtol(tmp, &ep, 10); + if (tmp == NULL || *ep) { + vfs_mount_error(mp, "Invalid uid"); + return (EINVAL); + } + } + + if (vfs_getopt(opts, "gid", (void **)&tmp, NULL) == 0) { + if (tmp != NULL) + gid = (gid_t)strtol(tmp, &ep, 10); + if (tmp == NULL || *ep) { + vfs_mount_error(mp, "Invalid gid"); + return (EINVAL); + } + } - rc = vfs_getopt(mp->mnt_optnew, "gid", (void **)&gid, &cbOption); - if ((rc != ENOENT) && (rc || cbOption != sizeof(gid))) - return EINVAL; + if (vfs_getopt(opts, "file_mode", (void **)&tmp, NULL) == 0) { + if (tmp != NULL) + file_mode = (mode_t)strtol(tmp, &ep, 8); +#if 0 + if (tmp == NULL || *ep) { + vfs_mount_error(mp, "Invalid file_mode"); + return (EINVAL); + } +#endif + file_mode &= S_IRWXU | S_IRWXG | S_IRWXO; + } - rc = vfs_getopt(mp->mnt_optnew, "uid", (void **)&uid, &cbOption); - if ((rc != ENOENT) && (rc || cbOption != sizeof(uid))) - return EINVAL; + if (vfs_getopt(opts, "dir_mode", (void **)&tmp, NULL) == 0) { + if (tmp != NULL) + dir_mode = (mode_t)strtol(tmp, &ep, 8); +#if 0 + if (tmp == NULL || *ep) { + vfs_mount_error(mp, "Invalid dir_mode"); + return (EINVAL); + } +#endif + dir_mode &= S_IRWXU | S_IRWXG | S_IRWXO; + } - pShFlGlobalInfo = RTMemAllocZ(sizeof(struct sf_glob_info)); - if (!pShFlGlobalInfo) - return ENOMEM; + vboxfsmp = malloc(sizeof(struct vboxfs_mnt), M_VBOXVFS, M_WAITOK | M_ZERO); + vboxfsmp->sf_uid = uid; + vboxfsmp->sf_gid = gid; + vboxfsmp->sf_fmode = file_mode; + vboxfsmp->sf_dmode = dir_mode; - cbShFlShareName = offsetof (SHFLSTRING, String.utf8) + cbShare + 1; - pShFlShareName = RTMemAllocZ(cbShFlShareName); - if (!pShFlShareName) - return VERR_NO_MEMORY; + /* + * Invoke Hypervisor mount interface before proceeding + */ + //error = sfprov_mount(share_name, &handle); + // if (error) { + // return (error); + //} - pShFlShareName->u16Length = cbShFlShareName; - pShFlShareName->u16Size = cbShFlShareName + 1; - memcpy (pShFlShareName->String.utf8, pszShare, cbShare + 1); + mp->mnt_data = handle; - rc = vboxCallMapFolder (&g_vboxSFClient, pShFlShareName, &pShFlGlobalInfo->map); - RTMemFree(pShFlShareName); + error = vfs_getopt(opts, "from", (void **)&share_name, &share_len); - if (RT_FAILURE (rc)) - { - RTMemFree(pShFlGlobalInfo); - printf("vboxCallMapFolder failed rc=%d\n", rc); - return EPROTO; - } + if (error || share_name[share_len - 1] != '\0' || share_len > 0xfffe) + { + vfs_mount_error(mp, "Invalid from"); + return (EINVAL); + } - pShFlGlobalInfo->uid = uid; - pShFlGlobalInfo->gid = gid; + /* Check that the mount device exists */ + if (share_name == NULL) + return (EINVAL); - mp->mnt_data = pShFlGlobalInfo; + NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, share_name, td); + printf("Device exist_0 %d", namei(ndp)); + if ((error = namei(ndp))) + return (error); + printf("Device exist"); + NDFREE(ndp, NDF_ONLY_PNBUF); + devvp = ndp->ni_vp; - /* @todo root vnode. */ + printf("Device exist_2"); + if (vn_isdisk(devvp, &error) == 0) { + vput(devvp); + return (error); + } - vfs_getnewfsid(mp); - vfs_mountedfrom(mp, pszShare); + printf("Device exist_3"); + /* Check the access rights on the mount device */ + error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td); + if (error) + error = priv_check(td, PRIV_VFS_MOUNT_PERM); + if (error) { + vput(devvp); + return (error); + } - printf("%s: Leave rc=0\n", __FUNCTION__); + printf("Device exist_4"); + dev = devvp->v_rdev; + dev_ref(dev); + DROP_GIANT(); + g_topology_lock(); + error = g_vfs_open(devvp, &cp, "vboxvfs", 0); + g_topology_unlock(); + PICKUP_GIANT(); + VOP_UNLOCK(devvp, 0); + if (error) + goto bail; - return 0; -} + bo = &devvp->v_bufobj; -static int vboxvfs_unmount(struct mount *mp, int mntflags, struct thread *td) + if (devvp->v_rdev->si_iosize_max != 0) + mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max; + if (mp->mnt_iosize_max > MAXPHYS) + mp->mnt_iosize_max = MAXPHYS; + + mp->mnt_data = vboxfsmp; + mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev); + mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED | + MNTK_EXTENDED_SHARED; + MNT_IUNLOCK(mp); + vboxfsmp->sf_vfsp = mp; + vboxfsmp->sf_dev = dev; + vboxfsmp->sf_devvp = devvp; + vboxfsmp->sf_cp = cp; + vboxfsmp->sf_bo = bo; + vboxfsmp->size = cp->provider->mediasize; + vboxfsmp->bsize = cp->provider->sectorsize; + vboxfsmp->bmask = vboxfsmp->bsize - 1; + vboxfsmp->bshift = ffs(vboxfsmp->bsize) - 1; + + vfs_getnewfsid(mp); + vfs_mountedfrom(mp, share_name); + + printf("%s: Leave error=0\n", __FUNCTION__); + + return (0); +bail: + if (vboxfsmp != NULL) + free(vboxfsmp, M_VBOXVFS); + if (bp != NULL) + brelse(bp); + if (cp != NULL) { + DROP_GIANT(); + g_topology_lock(); + g_vfs_close(cp); + g_topology_unlock(); + PICKUP_GIANT(); + } + dev_rel(dev); + vrele(devvp); + return error; +}; + +/* + * Unmount a shared folder. + * + * vboxfs_unmount umounts the mounted file system. It return 0 + * on sucess and any relevant errno on failure. + */ +static int vboxfs_unmount(struct mount *mp, int mntflags) { - struct sf_glob_info *pShFlGlobalInfo = VFSMP2SFGLOBINFO(mp); - int rc; - int flags = 0; + struct vboxfs_mnt *vboxfsmp; + struct thread *td; + int error; + int flags; - rc = vboxCallUnmapFolder(&g_vboxSFClient, &pShFlGlobalInfo->map); - if (RT_FAILURE(rc)) - printf("Failed to unmap shared folder\n"); + printf("vboxfs_unmount: flags=%04x\n", mntflags); + vboxfsmp = VFSTOVBOXFS(mp); + td = curthread; + flags = 0; + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; - if (mntflags & MNT_FORCE) - flags |= FORCECLOSE; + /* There is 1 extra root vnode reference (vnode_root). */ + error = vflush(mp, 1, flags, td); + if (error) + return (error); - /* There is 1 extra root vnode reference (vnode_root). */ - rc = vflush(mp, 1, flags, td); - if (rc) - return rc; + /* + * Invoke Hypervisor unmount interface before proceeding + */ + error = sfprov_unmount(vboxfsmp->sf_handle); + if (error != 0) { + /* TBD anything here? */ + } + free(vboxfsmp->sf_share_name, M_VBOXVFS); + free(vboxfsmp->sf_mntpath, M_VBOXVFS); + free(vboxfsmp, M_VBOXVFS); + mp->mnt_data = NULL; + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_LOCAL; + MNT_IUNLOCK(mp); - RTMemFree(pShFlGlobalInfo); - mp->mnt_data = NULL; + printf("vboxfs_unmount() done\n"); - return 0; + return (0); } -static int vboxvfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) +static int vboxfs_root(struct mount *mp, int flags, struct vnode **vpp) { - int rc = 0; - struct sf_glob_info *pShFlGlobalInfo = VFSMP2SFGLOBINFO(mp); - struct vnode *vp; + ino_t id = 1; - printf("%s: Enter\n", __FUNCTION__); + return (vboxfs_vget(mp, id, flags, vpp)); +} - vp = pShFlGlobalInfo->vnode_root; - VREF(vp); +int +vboxfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) +{ + struct vboxfs_mnt *vboxfsmp; + struct thread *td; + struct vnode *vp; + struct vboxfs_node *unode; + int error; - vn_lock(vp, flags | LK_RETRY, td); - *vpp = vp; + error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL); + if (error || *vpp != NULL) + return (error); - printf("%s: Leave\n", __FUNCTION__); + /* + * We must promote to an exclusive lock for vnode creation. This + * can happen if lookup is passed LOCKSHARED. + */ + if ((flags & LK_TYPE_MASK) == LK_SHARED) { + flags &= ~LK_TYPE_MASK; + flags |= LK_EXCLUSIVE; + } - return rc; + /* + * We do not lock vnode creation as it is believed to be too + * expensive for such rare case as simultaneous creation of vnode + * for same ino by different processes. We just allow them to race + * and check later to decide who wins. Let the race begin! + */ + td = curthread; + if ((error = vboxfs_allocv(mp, &vp, td))) { + printf("Error from vboxfs_allocv\n"); + return (error); + } + + vboxfsmp = VFSTOVBOXFS(mp); + unode = malloc(sizeof(struct vboxfs_node), M_VBOXVFS, M_WAITOK | M_ZERO); + unode->sf_vnode = vp; + unode->sf_ino = ino; + unode->vboxfsmp = vboxfsmp; + vp->v_data = unode; + + lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); + error = insmntque(vp, mp); + if (error != 0) + return (error); + + error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL); + if (error || *vpp != NULL) + return (error); + + switch (ino) { + default: + vp->v_type = VBAD; + break; + case ROOTDIR_INO: + vp->v_type = VDIR; + break; + case THEFILE_INO: + vp->v_type = VREG; + break; + } + + if (vp->v_type != VFIFO) + VN_LOCK_ASHARE(vp); + + if (ino == ROOTDIR_INO) + vp->v_vflag |= VV_ROOT; + + *vpp = vp; + + return (0); } -static int vboxvfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, struct thread *td) +/* + * Do operation associated with quotas, not supported + */ +static int vboxfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg) { - return EOPNOTSUPP; + return (EOPNOTSUPP); } -int vboxvfs_init(struct vfsconf *vfsp) +/* + * Initialize the filesystem. + */ +static int vboxfs_init(struct vfsconf *vfsp) { - int rc; + int rc = -1; - /* Initialize the R0 guest library. */ - rc = vboxInit(); - if (RT_FAILURE(rc)) - return ENXIO; + /* Initialize the R0 guest library. */ + rc = vboxInit(); + if (RT_FAILURE(rc)) + { + printf("vboxInit failed rc=%d\n", rc); + return (EPROTO); + } - /* Connect to the host service. */ - rc = vboxConnect(&g_vboxSFClient); - if (RT_FAILURE(rc)) - { - printf("Failed to get connection to host! rc=%d\n", rc); - vboxUninit(); - return ENXIO; - } + /* Connect to the host service. */ + rc = vboxConnect(&g_vboxSFClient); + if (RT_FAILURE(rc)) + { + printf("Failed to get connection to host! rc=%d\n", rc); + vboxUninit(); + return (EPROTO); + } - rc = vboxCallSetUtf8 (&g_vboxSFClient); - if (RT_FAILURE (rc)) - { - printf("vboxCallSetUtf8 failed, rc=%d\n", rc); - vboxDisconnect(&g_vboxSFClient); - vboxUninit(); - return EPROTO; - } + rc = vboxCallSetUtf8(&g_vboxSFClient); + if (RT_FAILURE (rc)) + { + printf("vboxCallSetUtf8 failed, rc=%d\n", rc); + vboxDisconnect(&g_vboxSFClient); + vboxUninit(); + return (EPROTO); + } - printf("Successfully loaded shared folder module\n"); + printf("Successfully loaded shared folder module %d\n", vboxfs_version); +#if 0 + int error; - return 0; + printf("vboxfs_init()\n"); + + /* + * This may seem a silly way to do things for now. But the code + * is structured to easily allow it to be used on other hypervisors + * which would have a different implementation of the provider. + * Hopefully that'll never happen. :) + */ + sfprov = sfprov_connect(SFPROV_VERSION); + if (sfprov == NULL) { + printf("vbox_init: couldn't init sffs provider"); + return (ENODEV); + } + + error = sfprov_set_show_symlinks(); + if (error != 0) { + printf("sffs_init: host unable to show symlinks, " + "error=%d\n", error); + } +#endif + return (0); } -int vboxvfs_uninit(struct vfsconf *vfsp) +/* + * Undo the work of vboxfs_init(). + */ +static int vboxfs_uninit(struct vfsconf *vfsp) { - vboxDisconnect(&g_vboxSFClient); - vboxUninit(); + vboxDisconnect(&g_vboxSFClient); + vboxUninit(); + /* + * close connection to the provider + */ + //sfprov_disconnect(sfprov); - return 0; + return (0); } -int vboxvfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) +/* + * Get filesystem statistics. + */ +static int vboxfs_statfs(struct mount *mp, struct statfs *sbp) { - return 0; + struct vboxfs_mnt *vboxfsmp; + sffs_fsinfo_t fsinfo; +// dev32_t d32; + int error; + + vboxfsmp = VFSTOVBOXFS(mp); + + bzero(sbp, sizeof(*sbp)); + error = sfprov_get_fsinfo(vboxfsmp->sf_handle, &fsinfo); + if (error != 0) + return (error); + + sbp->f_bsize = fsinfo.blksize; + + sbp->f_bfree = fsinfo.blksavail; + sbp->f_bavail = fsinfo.blksavail; + sbp->f_files = fsinfo.blksavail / 4; /* some kind of reasonable value */ + sbp->f_ffree = fsinfo.blksavail / 4; + + sbp->f_blocks = fsinfo.blksused + sbp->f_bavail; +#if 0 + (void) cmpldev(&d32, vfsp->vfs_dev); + sbp->f_fsid = d32; +#endif + sbp->f_namemax = fsinfo.maxnamesize; + + return (0); } Index: src/VBox/Additions/freebsd/vboxvfs/vboxvfs.h =================================================================== --- src/VBox/Additions/freebsd/vboxvfs/vboxvfs.h (revision 4) +++ src/VBox/Additions/freebsd/vboxvfs/vboxvfs.h (working copy) @@ -1,4 +1,4 @@ -/* $Id: vboxvfs.h $ */ +/* $Id: vboxfs.h $ */ /** @file * Description. */ @@ -18,49 +18,211 @@ #ifndef ___VBOXVFS_H___ #define ___VBOXVFS_H___ -#define VBOXVFS_VFSNAME "vboxvfs" +#define VBOXVFS_VFSNAME "vboxfs" #define VBOXVFS_VERSION 1 -#define MAX_HOST_NAME 256 -#define MAX_NLS_NAME 32 +#define MAX_HOST_NAME 256 +#define MAX_NLS_NAME 32 +//#define MODEMASK 07777 /* mode bits plus permission bits */ +/** Helper macros */ +#define VFSTOVBOXFS(mp) ((struct vboxfs_mnt *)((mp)->mnt_data)) +#define VTOVBOXFS(vp) ((struct vboxfs_node *)(vp)->v_data) +#define VBOXTOV(np) ((struct vnode *)(np)->n_vnode) -struct vboxvfs_mount_info { - char name[MAX_HOST_NAME]; - char nls_name[MAX_NLS_NAME]; - int uid; - int gid; - int ttl; -}; +#define ROOTDIR_INO 1 +#define THEFILE_INO 2 +#define THEFILE_NAME "thefile" +MALLOC_DECLARE(M_VBOXVFS); + #ifdef _KERNEL +#include "../../../../../include/iprt/nocrt/limits.h" +#include "../../../../../include/iprt/alloc.h" +#include "../../../../../include/iprt/asm.h" +#include "../../../../../include/iprt/asm-amd64-x86.h" +#include "../../../../../include/iprt/asm-math.h" +#include "../../../../../include/iprt/assert.h" +#include "../../../../../include/iprt/cdefs.h" +#include "../../../../../include/iprt/err.h" +#include "../../../../../include/iprt/fs.h" +#include "../../../../../include/iprt/log.h" +#include "../../../../../include/iprt/mangling.h" +#include "../../../../../include/iprt/mem.h" +#include "../../../../../include/iprt/param.h" +#include "../../../../../include/iprt/path.h" +#include "../../../../../include/iprt/semaphore.h" +#include "../../../../../include/iprt/stdarg.h" +#include "../../../../../include/iprt/stdint.h" +#include "../../../../../include/iprt/string.h" +#include "../../../../../include/iprt/time.h" +#include "../../../../../include/iprt/types.h" +#include "../../../../../include/iprt/uni.h" +#include "../../../../../include/iprt/nocrt/limits.h" +#include "../../../../../include/iprt/alloc.h" +#include "../../../../../include/iprt/asm.h" +#include "../../../../../include/iprt/asm-amd64-x86.h" +#include "../../../../../include/iprt/asm-math.h" +#include "../../../../../include/iprt/assert.h" +#include "../../../../../include/iprt/cdefs.h" +#include "../../../../../include/iprt/err.h" +#include "../../../../../include/iprt/fs.h" +#include "../../../../../include/iprt/log.h" +#include "../../../../../include/iprt/mangling.h" +#include "../../../../../include/iprt/mem.h" +#include "../../../../../include/iprt/param.h" +#include "../../../../../include/iprt/path.h" +#include "../../../../../include/iprt/semaphore.h" +#include "../../../../../include/iprt/stdarg.h" +#include "../../../../../include/iprt/stdint.h" +#include "../../../../../include/iprt/string.h" +#include "../../../../../include/iprt/time.h" +#include "../../../../../include/iprt/types.h" +#include "../../../../../include/iprt/uni.h" +#include "../../common/VBoxGuestLib/SysHlp.h" #include "../../common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.h" #include -#include +#include +#include -struct vboxvfsmount { - uid_t uid; - gid_t gid; - mode_t file_mode; - mode_t dir_mode; - struct mount *mp; - struct ucred *owner; - u_int flags; - long nextino; - int caseopt; - int didrele; +/* + * representation of an active mount point + */ +struct sfp_mount { + VBSFMAP map; }; -/* structs - stolen from the linux shared module code */ +/* + * Mount / Unmount a shared folder. + * + * sfprov_mount() takes as input the connection pointer and the name of + * the shared folder. On success, it returns zero and supplies an + * sfp_mount_t handle. On failure it returns any relevant errno value. + * + * sfprov_unmount() unmounts the mounted file system. It returns 0 on + * success and any relevant errno on failure. + */ +typedef struct sfp_mount sfp_mount_t; + +struct sfp_file { + SHFLHANDLE handle; + VBSFMAP map; /* need this again for the close operation */ +}; + +typedef struct sfp_file sfp_file_t; + +/* + * File operations: open/close/read/write/etc. + * + * open/create can return any relevant errno, however ENOENT + * generally means that the host file didn't exist. + */ +typedef struct sffs_stat { + mode_t sf_mode; + off_t sf_size; + off_t sf_alloc; + struct timespec sf_atime; + struct timespec sf_mtime; + struct timespec sf_ctime; +} sffs_stat_t; + +/* + * Read directory entries. + */ +/* + * a singly linked list of buffers, each containing an array of stat's+dirent's. + * sf_len is length of the sf_entries array, in bytes. + */ +typedef struct sffs_dirents { + struct sffs_dirents *sf_next; + long long sf_len; + struct sffs_dirent { + sffs_stat_t sf_stat; + struct dirent sf_entry; /* this is variable length */ + } sf_entries[1]; +} sffs_dirents_t; + +/* + * Shared Folders filesystem per-mount data structure. + */ +struct vboxfs_mnt { + struct mount *sf_vfsp; /* filesystem's vfs struct */ + struct vnode *sf_devvp; /* of vnode of the root directory */ + uid_t sf_uid; /* owner of all shared folders */ + gid_t sf_gid; /* group of all shared folders */ + mode_t sf_dmode; /* mode of all directories */ + mode_t sf_fmode; /* mode of all files */ + mode_t sf_dmask; /* mask of all directories */ + mode_t sf_fmask; /* mask of all files */ + int sf_stat_ttl; /* ttl for stat caches (in ms) */ + int sf_fsync; /* whether to honor fsync or not */ + char *sf_share_name; + char *sf_mntpath; /* name of mount point */ + sfp_mount_t *sf_handle; + uint64_t sf_ino; /* per FS ino generator */ + off_t size; + int bsize; + int bshift; + int bmask; + struct bufobj *sf_bo; + struct cdev *sf_dev; + struct g_consumer *sf_cp; +}; + +/* + * vboxfs_node is the file system dependent vnode data for vboxsf. + * vboxfs_node's also track all files ever accessed, both open and closed. + * It duplicates some of the information in vnode, since it holds + * information for files that may have been completely closed. + * + */ +struct vboxfs_node { + struct vboxfs_mnt *vboxfsmp; /* containing mounted file system */ + char *sf_path; /* full pathname to file or dir */ + uint64_t sf_ino; /* assigned unique ID number */ + struct vnode *sf_vnode; /* vnode if active */ + sfp_file_t *sf_file; /* non NULL if open */ + struct vboxfs_node *sf_parent; /* parent sfnode of this one */ + uint16_t sf_children; /* number of children sfnodes */ + uint8_t sf_type; /* VDIR or VREG */ + uint8_t sf_is_stale; /* this is stale and should be purged */ + sffs_stat_t sf_stat; /* cached file attrs for this node */ + uint64_t sf_stat_time; /* last-modified time of sf_stat */ + sffs_dirents_t *sf_dir_list; /* list of entries for this directory */ +}; + +struct vboxfs_mount_info { + char name[MAX_HOST_NAME]; /* share name */ + char nls_name[MAX_NLS_NAME];/* name of an I/O charset */ + int uid; /* user ID for all entries, default 0=root */ + int gid; /* group ID for all entries, default 0=root */ + int ttl; /* time to live */ + int dmode; /* mode for directories if != 0xffffffff */ + int fmode; /* mode for regular files if != 0xffffffff */ + int dmask; /* umask applied to directories */ + int fmask; /* umask applied to regular files */ +}; + struct sf_glob_info { - VBSFMAP map; + VBSFMAP map; /* struct nls_table *nls;*/ - int ttl; - int uid; - int gid; - struct vnode *vnode_root; + int ttl; + int uid; + int gid; + struct vnode *vnode_root; }; +/** Per-file system mount instance data. */ +typedef struct vboxfs_globinfo +{ + VBSFMAP Map; + int Ttl; + int Uid; + int Gid; + struct mount *pVFS; + struct vboxfs_node *pVNodeRoot; +} vboxfs_globinfo_t; + struct sf_inode_info { SHFLSTRING *path; int force_restat; @@ -86,6 +248,96 @@ SHFLHANDLE handle; }; +int vboxfs_allocv(struct mount *, struct vnode **, struct thread *); +int vboxfs_vget(struct mount *, ino_t, int, struct vnode **); + +/* + * These are the provider interfaces used by sffs to access the underlying + * shared file system. + */ +#define SFPROV_VERSION 1 + +/* + * Initialization and termination. + * sfprov_connect() is called once before any other interfaces and returns + * a handle used in further calls. The argument should be SFPROV_VERSION + * from above. On failure it returns a NULL pointer. + * + * sfprov_disconnect() must only be called after all sf file systems have been + * unmounted. + */ +typedef struct sfp_connection sfp_connection_t; + +extern sfp_connection_t *sfprov_connect(int); +extern void sfprov_disconnect(sfp_connection_t *); + +extern int sfprov_mount(char *, sfp_mount_t **); +extern int sfprov_unmount(sfp_mount_t *); + +/* + * query information about a mounted file system + */ +typedef struct sffs_fsinfo { + uint64_t blksize; + uint64_t blksused; + uint64_t blksavail; + uint32_t maxnamesize; + uint32_t readonly; +} sffs_fsinfo_t; + +extern int sfprov_get_fsinfo(sfp_mount_t *, sffs_fsinfo_t *); + +extern int sfprov_create(sfp_mount_t *, char *path, mode_t mode, + sfp_file_t **fp, sffs_stat_t *stat); +extern int sfprov_open(sfp_mount_t *, char *path, sfp_file_t **fp); +extern int sfprov_close(sfp_file_t *fp); +extern int sfprov_read(sfp_file_t *, char * buffer, uint64_t offset, + uint32_t *numbytes); +extern int sfprov_write(sfp_file_t *, char * buffer, uint64_t offset, + uint32_t *numbytes); +extern int sfprov_fsync(sfp_file_t *fp); + + +/* + * get/set information about a file (or directory) using pathname + */ +extern int sfprov_get_mode(sfp_mount_t *, char *, mode_t *); +extern int sfprov_get_size(sfp_mount_t *, char *, uint64_t *); +extern int sfprov_get_atime(sfp_mount_t *, char *, struct timespec *); +extern int sfprov_get_mtime(sfp_mount_t *, char *, struct timespec *); +extern int sfprov_get_ctime(sfp_mount_t *, char *, struct timespec *); +extern int sfprov_get_attr(sfp_mount_t *, char *, sffs_stat_t *); +extern int sfprov_set_attr(sfp_mount_t *, char *, mode_t, + struct timespec, struct timespec, struct timespec); +extern int sfprov_set_size(sfp_mount_t *, char *, uint64_t); + + +/* + * File/Directory operations + */ +extern int sfprov_trunc(sfp_mount_t *, char *); +extern int sfprov_remove(sfp_mount_t *, char *path, u_int is_link); +extern int sfprov_mkdir(sfp_mount_t *, char *path, mode_t mode, + sfp_file_t **fp, sffs_stat_t *stat); +extern int sfprov_rmdir(sfp_mount_t *, char *path); +extern int sfprov_rename(sfp_mount_t *, char *from, char *to, u_int is_dir); + + +/* + * Symbolic link operations + */ +extern int sfprov_set_show_symlinks(void); +extern int sfprov_readlink(sfp_mount_t *, char *path, char *target, + size_t tgt_size); +extern int sfprov_symlink(sfp_mount_t *, char *linkname, char *target, + sffs_stat_t *stat); + +#define SFFS_DIRENTS_SIZE 8192 +#define SFFS_DIRENTS_OFF (offsetof(sffs_dirents_t, sf_entries[0])) + +extern int sfprov_readdir(sfp_mount_t *mnt, char *path, + sffs_dirents_t **dirents); + #endif /* KERNEL */ #endif /* !___VBOXVFS_H___ */ Index: src/VBox/Additions/freebsd/vboxvfs/vboxvfs_vnops.c =================================================================== --- src/VBox/Additions/freebsd/vboxvfs/vboxvfs_vnops.c (revision 4) +++ src/VBox/Additions/freebsd/vboxvfs/vboxvfs_vnops.c (working copy) @@ -1,4 +1,4 @@ -/* $Id: vboxvfs_vnops.c $ */ +/* $Id: vboxfs_vnops.c $ */ /** @file * Description. */ @@ -14,228 +14,1137 @@ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ - -#include "vboxvfs.h" #include #include #include #include -#include +#include +#include +#include #include +#include #include -#include +#include #include +#include +#include +#include #include -#include -#include -#include -#include +#include -#include -#include +#include +#include "vboxvfs.h" + /* + * For now we'll use an I/O buffer that doesn't page fault for VirtualBox + * to transfer data into. + */ +char *vboxfs_buffer; + +/* * Prototypes for VBOXVFS vnode operations */ -static vop_create_t vboxvfs_create; -static vop_mknod_t vboxvfs_mknod; -static vop_open_t vboxvfs_open; -static vop_close_t vboxvfs_close; -static vop_access_t vboxvfs_access; -static vop_getattr_t vboxvfs_getattr; -static vop_setattr_t vboxvfs_setattr; -static vop_read_t vboxvfs_read; -static vop_write_t vboxvfs_write; -static vop_fsync_t vboxvfs_fsync; -static vop_remove_t vboxvfs_remove; -static vop_link_t vboxvfs_link; -static vop_lookup_t vboxvfs_lookup; -static vop_rename_t vboxvfs_rename; -static vop_mkdir_t vboxvfs_mkdir; -static vop_rmdir_t vboxvfs_rmdir; -static vop_symlink_t vboxvfs_symlink; -static vop_readdir_t vboxvfs_readdir; -static vop_strategy_t vboxvfs_strategy; -static vop_print_t vboxvfs_print; -static vop_pathconf_t vboxvfs_pathconf; -static vop_advlock_t vboxvfs_advlock; -static vop_getextattr_t vboxvfs_getextattr; -static vop_ioctl_t vboxvfs_ioctl; -static vop_getpages_t vboxvfs_getpages; -static vop_inactive_t vboxvfs_inactive; -static vop_putpages_t vboxvfs_putpages; -static vop_reclaim_t vboxvfs_reclaim; +static vop_create_t vboxfs_create; +static vop_mknod_t vboxfs_mknod; +static vop_open_t vboxfs_open; +static vop_close_t vboxfs_close; +static vop_access_t vboxfs_access; +static vop_getattr_t vboxfs_getattr; +static vop_setattr_t vboxfs_setattr; +static vop_read_t vboxfs_read; +static vop_write_t vboxfs_write; +static vop_fsync_t vboxfs_fsync; +static vop_remove_t vboxfs_remove; +static vop_link_t vboxfs_link; +static vop_lookup_t vboxfs_lookup; +static vop_rename_t vboxfs_rename; +static vop_mkdir_t vboxfs_mkdir; +static vop_rmdir_t vboxfs_rmdir; +static vop_symlink_t vboxfs_symlink; +static vop_readdir_t vboxfs_readdir; +static vop_strategy_t vboxfs_strategy; +static vop_bmap_t vboxfs_bmap; +static vop_print_t vboxfs_print; +static vop_pathconf_t vboxfs_pathconf; +static vop_advlock_t vboxfs_advlock; +static vop_getextattr_t vboxfs_getextattr; +static vop_ioctl_t vboxfs_ioctl; +static vop_getpages_t vboxfs_getpages; +static vop_inactive_t vboxfs_inactive; +static vop_putpages_t vboxfs_putpages; +static vop_reclaim_t vboxfs_reclaim; +static vop_vptofh_t vboxfs_vptofh; -struct vop_vector vboxvfs_vnodeops = { - .vop_default = &default_vnodeops, +struct vop_vector vboxfs_vnodeops = { + .vop_default = &default_vnodeops, - .vop_access = vboxvfs_access, - .vop_advlock = vboxvfs_advlock, - .vop_close = vboxvfs_close, - .vop_create = vboxvfs_create, - .vop_fsync = vboxvfs_fsync, - .vop_getattr = vboxvfs_getattr, - .vop_getextattr = vboxvfs_getextattr, - .vop_getpages = vboxvfs_getpages, - .vop_inactive = vboxvfs_inactive, - .vop_ioctl = vboxvfs_ioctl, - .vop_link = vboxvfs_link, - .vop_lookup = vboxvfs_lookup, - .vop_mkdir = vboxvfs_mkdir, - .vop_mknod = vboxvfs_mknod, - .vop_open = vboxvfs_open, - .vop_pathconf = vboxvfs_pathconf, - .vop_print = vboxvfs_print, - .vop_putpages = vboxvfs_putpages, - .vop_read = vboxvfs_read, - .vop_readdir = vboxvfs_readdir, - .vop_reclaim = vboxvfs_reclaim, - .vop_remove = vboxvfs_remove, - .vop_rename = vboxvfs_rename, - .vop_rmdir = vboxvfs_rmdir, - .vop_setattr = vboxvfs_setattr, - .vop_strategy = vboxvfs_strategy, - .vop_symlink = vboxvfs_symlink, - .vop_write = vboxvfs_write, + .vop_access = vboxfs_access, + .vop_bmap = vboxfs_bmap, + .vop_advlock = vboxfs_advlock, + .vop_close = vboxfs_close, + .vop_create = vboxfs_create, + .vop_fsync = vboxfs_fsync, + .vop_getattr = vboxfs_getattr, + .vop_getextattr = vboxfs_getextattr, + .vop_getpages = vboxfs_getpages, + .vop_inactive = vboxfs_inactive, + .vop_ioctl = vboxfs_ioctl, + .vop_link = vboxfs_link, + .vop_lookup = vboxfs_lookup, + .vop_mkdir = vboxfs_mkdir, + .vop_mknod = vboxfs_mknod, + .vop_open = vboxfs_open, + .vop_pathconf = vboxfs_pathconf, + .vop_print = vboxfs_print, + .vop_putpages = vboxfs_putpages, + .vop_read = vboxfs_read, + .vop_readdir = vboxfs_readdir, + .vop_reclaim = vboxfs_reclaim, + .vop_remove = vboxfs_remove, + .vop_rename = vboxfs_rename, + .vop_rmdir = vboxfs_rmdir, + .vop_setattr = vboxfs_setattr, + .vop_strategy = vboxfs_strategy, + .vop_vptofh = vboxfs_vptofh, + .vop_symlink = vboxfs_symlink, + .vop_write = vboxfs_write, }; -static int vboxvfs_access(struct vop_access_args *ap) +int +vboxfs_allocv(struct mount *mp, struct vnode **vpp, struct thread *td) { - return 0; + int error; + struct vnode *vp; + + error = getnewvnode("vboxfs", mp, &vboxfs_vnodeops, &vp); + if (error) { + printf("vboxfs_allocv: failed to allocate new vnode\n"); + return (error); + } + + *vpp = vp; + return (0); } -static int vboxvfs_open(struct vop_open_args *ap) +/* + * uid and gid in sffs determine owner and group for all files. + */ +#if 0 +static int +sfnode_access(sfnode_t *node, mode_t mode, cred_t *cr) { - return 0; + sffs_data_t *sffs = node->sf_sffs; + mode_t m; + int shift = 0; + int error; + vnode_t *vp; + + ASSERT(MUTEX_HELD(&sffs_lock)); + + /* + * get the mode from the cache or provider + */ + if (sfnode_stat_cached(node)) + error = 0; + else + error = sfnode_update_stat_cache(node); + m = (error == 0) ? node->sf_stat.sf_mode : 0; + + /* + * mask off the permissions based on uid/gid + */ + if (crgetuid(cr) != sffs->sf_uid) { + shift += 3; + if (groupmember(sffs->sf_gid, cr) == 0) + shift += 3; + } + mode &= ~(m << shift); + + if (mode == 0) { + error = 0; + } else { + vp = sfnode_get_vnode(node); + error = secpolicy_vnode_access(cr, vp, sffs->sf_uid, mode); + VN_RELE(vp); + } + return (error); } +#endif +static int vboxfs_access(struct vop_access_args *ap) +{ +#if 0 + sfnode_t *node = VN2SFN(vp->a_vp); + int error; -static int vboxvfs_close(struct vop_close_args *ap) + error = sfnode_access(node, mode, cr); + + return (error); +#endif + struct vnode *vp = ap->a_vp; + accmode_t accmode = ap->a_accmode; + + if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { + switch (vp->v_type) { + case VDIR: + case VLNK: + case VREG: + return (EROFS); + /* NOT REACHED */ + default: + break; + } + } + return (vaccess(vp->v_type, 0444, 0, 0, + accmode, ap->a_cred, NULL)); +} + +/* + * Clears the (cached) directory listing for the node. + */ +static void +vfsnode_clear_dir_list(struct vboxfs_node *np) { - return 0; + while (np->sf_dir_list != NULL) { + sffs_dirents_t *next = np->sf_dir_list->sf_next; + free(np->sf_dir_list, M_VBOXVFS); + np->sf_dir_list = next; + } } -static int vboxvfs_getattr(struct vop_getattr_args *ap) +/* + * Open the provider file associated with a vnode. Holding the file open is + * the only way we have of trying to have a vnode continue to refer to the + * same host file in the host in light of the possibility of host side renames. + */ +static void +vfsnode_open(struct vboxfs_node *np) { - return 0; + int error; + sfp_file_t *fp; + + if (np->sf_file != NULL) + return; + error = sfprov_open(np->vboxfsmp->sf_handle, np->sf_path, &fp); + if (error == 0) + np->sf_file = fp; } -static int vboxvfs_setattr(struct vop_setattr_args *ap) +/* + * get a new vnode reference for an sfnode + */ +#if 0 +struct vnode * +vfsnode_get_vnode(struct vboxfs_node *node) { - return 0; + struct vnode *vp; + + if (node->sf_vnode != NULL) { + vref(node->sf_vnode); + } else { + vp = vn_alloc(KM_SLEEP); + printf(" %s gets vnode 0x%p\n", node->sf_path, vp); + vp->v_type = node->sf_type; + vp->v_vfsp = node->sf_sffs->sf_vfsp; + vn_setops(vp, sffs_ops); + vp->v_flag = VNOSWAP; +#ifndef VBOXVFS_WITH_MMAP + vp->v_flag |= VNOMAP; +#endif + vn_exists(vp); + vp->v_data = node; + node->sf_vnode = vp; + } + return (node->sf_vnode); } +#endif +static int vboxfs_open(struct vop_open_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct vboxfs_node *np; + int error = 0; + off_t fsize; -static int vboxvfs_read(struct vop_read_args *ap) + np = VTOVBOXFS(vp); + vfsnode_open(np); + if (np->sf_file == NULL) + error = EINVAL; + + fsize = np->vboxfsmp->size; + vnode_create_vobject(ap->a_vp, fsize, ap->a_td); + + return (error); +} +#if 0 +/* + * Some sort of host operation on an vboxfs_node has failed or it has been + * deleted. Mark this node and any children as stale, deleting knowledge + * about any which do not have active vnodes or children + * This also handle deleting an inactive node that was already stale. + */ +static void +fvsnode_make_stale(struct vboxfs_node *node) { - return 0; + struct vboxfs_node *np; + int len; + avl_index_t where; + + /* + * First deal with any children of a directory node. + * If a directory becomes stale, anything below it becomes stale too. + */ + if (!node->sf_is_stale && node->sf_type == VDIR) { + len = strlen(node->sf_path); + + np= node; + while ((np = AVL_NEXT(&sfnodes, node)) != NULL) { + ASSERT(!n->sf_is_stale); + + /* + * quit when no longer seeing children of node + */ + if (n->sf_sffs != node->sf_sffs || + strncmp(node->sf_path, n->sf_path, len) != 0 || + n->sf_path[len] != '/') + break; + + /* + * Either mark the child as stale or destroy it + */ + if (n->sf_vnode == NULL && n->sf_children == 0) { + sfnode_destroy(n); + } else { + LogFlowFunc(("sffs_make_stale(%s) sub\n", n->sf_path)); + sfnode_clear_dir_list(n); + if (avl_find(&sfnodes, n, &where) == NULL) + panic("sfnode_make_stale(%s)" + " not in sfnodes", n->sf_path); + avl_remove(&sfnodes, n); + n->sf_is_stale = 1; + if (avl_find(&stale_sfnodes, n, &where) != NULL) + panic("sffs_make_stale(%s) duplicates", + n->sf_path); + avl_insert(&stale_sfnodes, n, where); + } + } + } + + /* + * Now deal with the given node. + */ + if (node->sf_vnode == NULL && node->sf_children == 0) { + sfnode_destroy(node); + } else if (!node->sf_is_stale) { + LogFlowFunc(("sffs_make_stale(%s)\n", node->sf_path)); + sfnode_clear_dir_list(node); + if (node->sf_parent) + sfnode_clear_dir_list(node->sf_parent); + if (avl_find(&sfnodes, node, &where) == NULL) + panic("sfnode_make_stale(%s) not in sfnodes", + node->sf_path); + avl_remove(&sfnodes, node); + node->sf_is_stale = 1; + if (avl_find(&stale_sfnodes, node, &where) != NULL) + panic("sffs_make_stale(%s) duplicates", node->sf_path); + avl_insert(&stale_sfnodes, node, where); + } } +static uint64_t +sfnode_cur_time_usec(void) +{ + clock_t now = drv_hztousec(ddi_get_lbolt()); + return now; +} -static int vboxvfs_write(struct vop_write_args *ap) +static int +sfnode_stat_cached(sfnode_t *node) { - return 0; + return (sfnode_cur_time_usec() - node->sf_stat_time) < + node->sf_sffs->sf_stat_ttl * 1000L; } +#endif +static void +vfsnode_invalidate_stat_cache(struct vboxfs_node *np) +{ + np->sf_stat_time = 0; +} -static int vboxvfs_create(struct vop_create_args *ap) +static int vboxfs_close(struct vop_close_args *ap) { - return 0; + + struct vnode *vp = ap->a_vp; + struct vboxfs_node *np; + + np = VTOVBOXFS(vp); + + /* + * Free the directory entries for the node. We do this on this call + * here because the directory node may not become inactive for a long + * time after the readdir is over. Case in point, if somebody cd's into + * the directory then it won't become inactive until they cd away again. + * In such a case we would end up with the directory listing not getting + * updated (i.e. the result of 'ls' always being the same) until they + * change the working directory. + */ + vfsnode_clear_dir_list(np); + + vfsnode_invalidate_stat_cache(np); + + if (np->sf_file != NULL) + { + (void)sfprov_close(np->sf_file); + np->sf_file = NULL; + } + + return (0); } -static int vboxvfs_remove(struct vop_remove_args *ap) +static int vboxfs_getattr(struct vop_getattr_args *ap) { - return 0; + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + struct vboxfs_node *np = VTOVBOXFS(vp); + struct vboxfs_mnt *mp = np->vboxfsmp; + mode_t mode; + int error = 0; + + vap->va_type = vp->v_type; + + vap->va_nlink = 1; /* number of references to file */ + vap->va_uid = mp->sf_uid; /* owner user id */ + vap->va_gid = mp->sf_gid; /* owner group id */ + vap->va_rdev = NODEV; /* device the special file represents */ + vap->va_gen = VNOVAL; /* generation number of file */ + vap->va_flags = 0; /* flags defined for file */ + vap->va_filerev = 0; /* file modification number */ + vap->va_vaflags = 0; /* operations flags */ + vap->va_fileid = np->sf_ino; /* file id */ + vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; + if (vap->va_fileid == 0) + vap->va_fileid = 2; +#if 0 + if (!sfnode_stat_cached(node)) { + error = sfnode_update_stat_cache(node); + if (error != 0) + goto done; + } + + vap->va_atime = node->sf_stat.sf_atime; + vap->va_mtime = node->sf_stat.sf_mtime; + vap->va_ctime = node->sf_stat.sf_ctime; +#endif + vap->va_atime.tv_sec = VNOVAL; + vap->va_atime.tv_nsec = VNOVAL; + vap->va_mtime.tv_sec = VNOVAL; + vap->va_mtime.tv_nsec = VNOVAL; + vap->va_ctime.tv_sec = VNOVAL; + vap->va_ctime.tv_nsec = VNOVAL; + + mode = np->sf_stat.sf_mode; +#if 0 + vap->va_mode = mode & MODEMASK; /* files access mode and type */ + if (S_ISDIR(mode)) + { + vap->va_type = VDIR; /* vnode type (for create) */ + vap->va_mode = mp->sf_dmode != ~0 ? (mp->sf_dmode & 0777) : vap->va_mode; + vap->va_mode &= ~mp->sf_dmask; + vap->va_mode |= S_IFDIR; + } + else if (S_ISREG(mode)) + { + vap->va_type = VREG; + vap->va_mode = mp->sf_fmode != ~0 ? (mp->sf_fmode & 0777) : vap->va_mode; + vap->va_mode &= ~mp->sf_fmask; + vap->va_mode |= S_IFREG; + } + else if (S_ISFIFO(mode)) + vap->va_type = VFIFO; + else if (S_ISCHR(mode)) + vap->va_type = VCHR; + else if (S_ISBLK(mode)) + vap->va_type = VBLK; + else if (S_ISLNK(mode)) + { + vap->va_type = VLNK; + vap->va_mode = mp->sf_fmode != ~0 ? (mp->sf_fmode & 0777) : vap->va_mode; + vap->va_mode &= ~mp->sf_fmask; + vap->va_mode |= S_IFLNK; + } + else if (S_ISSOCK(mode)) + vap->va_type = VSOCK; +#endif + if (vp->v_type & VDIR) { + vap->va_nlink = 2; + vap->va_mode = 0555; + } else { + vap->va_nlink = 1; + vap->va_mode = 0444; + } + + vap->va_size = np->sf_stat.sf_size; + vap->va_blocksize = 512; + vap->va_bytes = (np->sf_stat.sf_alloc + 511) / 512; /* bytes of disk space held by file */ + +done: + return (error); } -static int vboxvfs_rename(struct vop_rename_args *ap) +static int vboxfs_setattr(struct vop_setattr_args *ap) { - return 0; + + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + struct vboxfs_node *np = VTOVBOXFS(vp); + int error; + mode_t mode; + + mode = vap->va_mode; + if (vp->v_type == VREG) + mode |= S_IFREG; + else if (vp->v_type == VDIR) + mode |= S_IFDIR; + else if (vp->v_type == VBLK) + mode |= S_IFBLK; + else if (vp->v_type == VCHR) + mode |= S_IFCHR; + else if (vp->v_type == VLNK) + mode |= S_IFLNK; + else if (vp->v_type == VFIFO) + mode |= S_IFIFO; + else if (vp->v_type == VSOCK) + mode |= S_IFSOCK; + + vfsnode_invalidate_stat_cache(np); + error = sfprov_set_attr(np->vboxfsmp->sf_handle, np->sf_path, + mode, vap->va_atime, vap->va_mtime, vap->va_ctime); +#if 0 + if (error == ENOENT) + sfnode_make_stale(np); +#endif + if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL || + vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || + vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL) + return (EROFS); + if (vap->va_size != (u_quad_t)VNOVAL) { + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VLNK: + case VREG: + return (EROFS); + case VCHR: + case VBLK: + case VSOCK: + case VFIFO: + case VNON: + case VBAD: + case VMARKER: + return (0); + } + } + return (error); } -static int vboxvfs_link(struct vop_link_args *ap) +#define blkoff(vboxfsmp, loc) ((loc) & (vboxfsmp)->bmask) + +static int vboxfs_read(struct vop_read_args *ap) { - return EOPNOTSUPP; + static const int clustersize = MAXBSIZE; + + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct vboxfs_node *np = VTOVBOXFS(vp); + int error = 0; + uint32_t bytes; + uint32_t done; + unsigned long offset; + ssize_t total; + long on; + + if (vp->v_type == VDIR) + return (EISDIR); + if (vp->v_type != VREG) + return (EINVAL); +#if 0 + if (uio->uio_loffset >= MAXOFFSET_T) + { + proc_t *p = ttoproc(curthread); + (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE], p->p_rctls, + p, RCA_UNSAFE_SIGINFO); + return (EFBIG); + } + if (uio->uio_loffset < 0) + return (EINVAL); +#endif + total = uio->uio_resid; + if (total == 0) + return (0); + + vfsnode_open(np); + if (np->sf_file == NULL) { + return (EINVAL); + } + + do { + offset = uio->uio_offset; + on = blkoff(np->vboxfsmp, uio->uio_offset); + done = bytes = min((u_int)(clustersize - on), uio->uio_resid); + error = sfprov_read(np->sf_file, vboxfs_buffer, offset, &done); + if (error == 0 && done > 0) + error = uiomove(vboxfs_buffer, done, uio); + } while (error == 0 && uio->uio_resid > 0 && done > 0); + + + /* + * a partial read is never an error + */ + if (total != uio->uio_resid) + error = 0; + return (error); } -static int vboxvfs_symlink(struct vop_symlink_args *ap) +static int vboxfs_write(struct vop_write_args *ap) { - return EOPNOTSUPP; + return (0); } -static int vboxvfs_mknod(struct vop_mknod_args *ap) +static int vboxfs_create(struct vop_create_args *ap) { - return EOPNOTSUPP; + return (0); } -static int vboxvfs_mkdir(struct vop_mkdir_args *ap) +static int vboxfs_remove(struct vop_remove_args *ap) { - return 0; + return (0); } -static int vboxvfs_rmdir(struct vop_rmdir_args *ap) +static int vboxfs_rename(struct vop_rename_args *ap) { - return 0; + return (0); } -static int vboxvfs_readdir(struct vop_readdir_args *ap) +static int vboxfs_link(struct vop_link_args *ap) { - return 0; + return (EOPNOTSUPP); } -static int vboxvfs_fsync(struct vop_fsync_args *ap) +static int vboxfs_symlink(struct vop_symlink_args *ap) { - return 0; + return (EOPNOTSUPP); } -static int vboxvfs_print (struct vop_print_args *ap) +static int vboxfs_mknod(struct vop_mknod_args *ap) { - return 0; + return (EOPNOTSUPP); } -static int vboxvfs_pathconf (struct vop_pathconf_args *ap) +static int vboxfs_mkdir(struct vop_mkdir_args *ap) { - return 0; + return (0); } -static int vboxvfs_strategy (struct vop_strategy_args *ap) +static int vboxfs_rmdir(struct vop_rmdir_args *ap) { - return 0; + return (0); } +#if 0 +struct vboxfs_uiodir { + struct dirent *dirent; + u_long *cookies; + int ncookies; + int acookies; + int eofflag; +}; -static int vboxvfs_ioctl(struct vop_ioctl_args *ap) +static int +vboxfs_uiodir(struct vboxfs_uiodir *uiodir, int de_size, struct uio *uio, long cookie) { - return ENOTTY; + if (uiodir->cookies != NULL) { + if (++uiodir->acookies > uiodir->ncookies) { + uiodir->eofflag = 0; + return (-1); + } + *uiodir->cookies++ = cookie; + } + + if (uio->uio_resid < de_size) { + uiodir->eofflag = 0; + return (-1); + } + + return (uiomove(uiodir->dirent, de_size, uio)); } +#endif +static int vboxfs_readdir(struct vop_readdir_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct vboxfs_node *dir = VTOVBOXFS(vp); + struct vboxfs_node *node; + struct sffs_dirent *dirent = NULL; + sffs_dirents_t *cur_buf; + off_t offset = 0; + off_t orig_off = uio->uio_offset; + int error = 0; +#if 0 + if (uio->uio_iovcnt != 1) + return (EINVAL); -static int vboxvfs_getextattr(struct vop_getextattr_args *ap) + if (vp->v_type != VDIR) + return (ENOTDIR); + + if (eofp == NULL) + eofp = &dummy_eof; + *eofp = 0; + + if (uio->uio_loffset >= MAXOFFSET_T) { + *eofp = 1; + return (0); + } +#endif + /* + * Get the directory entry names from the host. This gets all + * entries. These are stored in a linked list of sffs_dirents_t + * buffers, each of which contains a list of dirent64_t's. + */ + + if (dir->sf_dir_list == NULL) { + error = sfprov_readdir(dir->vboxfsmp->sf_handle, dir->sf_path, + &dir->sf_dir_list); + if (error != 0) + goto done; + } + + /* + * Validate and skip to the desired offset. + */ + cur_buf = dir->sf_dir_list; + offset = 0; + + while (cur_buf != NULL && + offset + cur_buf->sf_len <= uio->uio_offset) { + offset += cur_buf->sf_len; + cur_buf = cur_buf->sf_next; + } + + if (cur_buf == NULL && offset != uio->uio_offset) { + error = EINVAL; + goto done; + } + if (cur_buf != NULL && offset != uio->uio_offset) { + off_t off = offset; + int step; + dirent = &cur_buf->sf_entries[0]; + + while (off < uio->uio_offset) { + step = sizeof(sffs_stat_t) + dirent->sf_entry.d_reclen; + dirent = (struct sffs_dirent *) (((char *) dirent) + step); + off += step; + } + + if (off >= uio->uio_offset) { + error = EINVAL; + goto done; + } + } + + offset = uio->uio_offset - offset; + + /* + * Lookup each of the names, so that we have ino's, and copy to + * result buffer. + */ + while (cur_buf != NULL) { + if (offset >= cur_buf->sf_len) { + cur_buf = cur_buf->sf_next; + offset = 0; + continue; + } + + dirent = (struct sffs_dirent *) + (((char *) &cur_buf->sf_entries[0]) + offset); + if (dirent->sf_entry.d_reclen > uio->uio_resid) + break; + + if (strcmp(dirent->sf_entry.d_name, ".") == 0) { + node = dir; + } else if (strcmp(dirent->sf_entry.d_name, "..") == 0) { + node = dir->sf_parent; + if (node == NULL) + node = dir; + } else { +#if 0 + node = sfnode_lookup(dir, dirent->sf_entry.d_name, VNON, + 0, &dirent->sf_stat, sfnode_cur_time_usec(), NULL); + if (node == NULL) + panic("sffs_readdir() lookup failed"); +#endif + } + + error = uiomove(&dirent->sf_entry, dirent->sf_entry.d_reclen, uio); + if (error != 0) + break; + + uio->uio_offset= dirent->sf_entry.d_fileno; + offset += sizeof(sffs_stat_t) + dirent->sf_entry.d_reclen; + } +done: +#if 0 + if (error != 0) + uio->uio_offset = orig_off; +#endif + return (error); +} + +static int vboxfs_fsync(struct vop_fsync_args *ap) { - return 0; + return (0); } -static int vboxvfs_advlock(struct vop_advlock_args *ap) +static int vboxfs_print (struct vop_print_args *ap) { - return 0; + struct vnode *vp = ap->a_vp; + struct vboxfs_node *np; + + np = VTOVBOXFS(vp); + + if (np == NULL) { + printf("No vboxfs_node data\n"); + return (0); + } + + printf("\tpath = %s, parent = %p", np->sf_path, + np->sf_parent ? np->sf_parent : NULL); + printf("\n"); + return (0); } -static int vboxvfs_lookup(struct vop_lookup_args *ap) +static int vboxfs_pathconf (struct vop_pathconf_args *ap) { - return 0; + struct vnode *vp = ap->a_vp; + register_t *retval = ap->a_retval; + int error = 0; + + switch (ap->a_name) { + case _PC_LINK_MAX: + *retval = 65535; + break; + case _PC_NAME_MAX: + *retval = NAME_MAX; + break; + case _PC_PATH_MAX: + *retval = PATH_MAX; + break; + default: + error = EINVAL; + } + return (error); } -static int vboxvfs_inactive(struct vop_inactive_args *ap) +static int vboxfs_strategy (struct vop_strategy_args *ap) { - return 0; + struct buf *bp; + struct vnode *vp; + struct vboxfs_node *np; + struct bufobj *bo; + + bp = ap->a_bp; + vp = ap->a_vp; + np = VTOVBOXFS(vp); + + if (bp->b_blkno == bp->b_lblkno) { + bp->b_blkno = bp->b_lblkno << (np->vboxfsmp->bshift - DEV_BSHIFT); + } + bo = np->vboxfsmp->sf_bo; + bp->b_iooffset = dbtob(bp->b_blkno); + BO_STRATEGY(bo, bp); + return (0); } -static int vboxvfs_reclaim(struct vop_reclaim_args *ap) +static int +vboxfs_bmap(struct vop_bmap_args *ap) { - return 0; + struct vnode *vp; + struct vboxfs_node *np; + + vp = ap->a_vp; + np = VTOVBOXFS(vp); + + if (ap->a_bop != NULL) + *ap->a_bop = &np->vboxfsmp->sf_devvp->v_bufobj; + if (ap->a_bnp == NULL) + return (0); + if (ap->a_runb) + *ap->a_runb = 0; + + /* Translate logical to physical sector number */ + *ap->a_bnp = ap->a_bn << (np->vboxfsmp->bshift - DEV_BSHIFT); + + if (ap->a_runp) + *ap->a_runp = 0; + if (ap->a_runb) + *ap->a_runb = 0; + return (0); } -static int vboxvfs_getpages(struct vop_getpages_args *ap) +/* + * File specific ioctls. + */ +static int vboxfs_ioctl(struct vop_ioctl_args *ap) { - return 0; + printf("%s called\n", __func__); + return (ENOTTY); } -static int vboxvfs_putpages(struct vop_putpages_args *ap) +static int vboxfs_getextattr(struct vop_getextattr_args *ap) { - return 0; + return (0); } +static int vboxfs_advlock(struct vop_advlock_args *ap) +{ + return (0); +} + +/* + * Look for a cached node, if not found either handle ".." or try looking + * via the provider. Create an entry in sfnodes if found but not cached yet. + * If the create flag is set, a file or directory is created. If the file + * already existed, an error is returned. + * Nodes returned from this routine always have a vnode with its ref count + * bumped by 1. + */ +#if 0 +static struct vboxfs_node * +vfsnode_lookup( + struct vboxfs_node *dir, + char *name, + vtype_t create, + mode_t c_mode, + sffs_stat_t *stat, + uint64_t stat_time, + int *err) +{ + avl_index_t where; + vboxfs_node template; + vboxfs_node *node; + int error = 0; + int type; + char *fullpath; + sfp_file_t *fp; + sffs_stat_t tmp_stat; + + if (err) + *err = error; + + /* + * handle referencing myself + */ + if (strcmp(name, "") == 0 || strcmp(name, ".") == 0) + return (dir); + + /* + * deal with parent + */ + if (strcmp(name, "..") == 0) + return (dir->sf_parent); + + /* + * Look for an existing node. + */ + fullpath = sfnode_construct_path(dir, name); + template.sf_sffs = dir->sf_sffs; + template.sf_path = fullpath; + template.sf_is_stale = 0; + node = avl_find(&sfnodes, &template, &where); + if (node != NULL) { + free(fullpath, M_VBOXVFS); + if (create != VNON) + return (NULL); + return (node); + } + + /* + * No entry for this path currently. + * Check if the file exists with the provider and get the type from + * there. + */ + if (create == VREG) { + type = VREG; + stat = &tmp_stat; + error = sfprov_create(dir->sf_sffs->sf_handle, fullpath, c_mode, + &fp, stat); + stat_time = sfnode_cur_time_usec(); + } else if (create == VDIR) { + type = VDIR; + stat = &tmp_stat; + error = sfprov_mkdir(dir->sf_sffs->sf_handle, fullpath, c_mode, + &fp, stat); + stat_time = sfnode_cur_time_usec(); + } else { + mode_t m; + fp = NULL; + type = VNON; + if (stat == NULL) { + stat = &tmp_stat; + error = sfprov_get_attr(dir->sf_sffs->sf_handle, + fullpath, stat); + stat_time = sfnode_cur_time_usec(); + } else { + error = 0; + } + m = stat->sf_mode; + if (error != 0) + error = ENOENT; + else if (S_ISDIR(m)) + type = VDIR; + else if (S_ISREG(m)) + type = VREG; + else if (S_ISLNK(m)) + type = VLNK; + } + + if (err) + *err = error; + + /* + * If no errors, make a new node and return it. + */ + if (error) { + kmem_free(fullpath, strlen(fullpath) + 1); + return (NULL); + } + node = sfnode_make(dir->sf_sffs, fullpath, type, fp, dir, stat, + stat_time); + return (node); +} +#endif + +/* + * Lookup an entry in a directory and create a new vnode if found. + */ +static int +vboxfs_lookup(struct vop_lookup_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap) +{ + struct componentname *cnp = ap->a_cnp; + struct vnode *dvp = ap->a_dvp; /* the directory vnode */ + char *nameptr = cnp->cn_nameptr; /* the name of the file or directory */ + struct vnode **vpp = ap->a_vpp; /* the vnode we found or NULL */ + struct vnode *tdp = NULL; + struct vboxfs_node *node = VTOVBOXFS(dvp); + struct vboxfs_mnt *vboxfsmp = node->vboxfsmp; + u_long nameiop = cnp->cn_nameiop; + u_long flags = cnp->cn_flags; + long namelen; + ino_t id = 0; + int ltype, offset = 0, error = 0; + int lkflags = cnp->cn_lkflags; + + /* + * dvp must be a directory + */ + if (dvp->v_type != VDIR) + return (ENOTDIR); + + if (strcmp(nameptr, THEFILE_NAME) == 0) + id = THEFILE_INO; + else if (flags & ISDOTDOT) + id = ROOTDIR_INO; + + /* Did we have a match? */ + if (id) { + if (flags & ISDOTDOT) { + error = vn_vget_ino(dvp, id, lkflags, &tdp); + } else if (node->sf_ino == id) { + VREF(dvp); /* we want ourself, ie "." */ + /* + * When we lookup "." we still can be asked to lock it + * differently. + */ + ltype = lkflags & LK_TYPE_MASK; + if (ltype != VOP_ISLOCKED(dvp)) { + if (ltype == LK_EXCLUSIVE) + vn_lock(dvp, LK_UPGRADE | LK_RETRY); + else /* if (ltype == LK_SHARED) */ + vn_lock(dvp, LK_DOWNGRADE | LK_RETRY); + } + tdp = dvp; + } else + error = vboxfs_vget(vboxfsmp->sf_vfsp, id, lkflags, &tdp); + if (!error) { + *vpp = tdp; + /* Put this entry in the cache */ + if (flags & MAKEENTRY) + cache_enter(dvp, *vpp, cnp); + } + } else { + /* Enter name into cache as non-existant */ + if (flags & MAKEENTRY) + cache_enter(dvp, *vpp, cnp); + + if ((flags & ISLASTCN) && + (nameiop == CREATE || nameiop == RENAME)) { + error = EROFS; + } else { + error = ENOENT; + } + } + + return (error); +} + +static int vboxfs_inactive(struct vop_inactive_args *ap) +{ + return (0); +} + +static int vboxfs_reclaim(struct vop_reclaim_args *ap) +{ + struct vnode *vp; + struct vboxfs_node *np; + + vp = ap->a_vp; + np = VTOVBOXFS(vp); + + /* + * Destroy the vm object and flush associated pages. + */ + vnode_destroy_vobject(vp); + + if (np != NULL) { + vfs_hash_remove(vp); + free(np, M_VBOXVFS); + vp->v_data = NULL; + } + return (0); +} + +static int vboxfs_vptofh(struct vop_vptofh_args *ap) +{ +#if 0 + struct vboxfs_node *node; + struct ifid *ifhp; + + node = VTON(ap->a_vp); + ifhp = (struct ifid *)ap->a_fhp; + ifhp->ifid_len = sizeof(struct ifid); + ifhp->ifid_ino = node->hash_id; +#endif + return (0); +} + +static int vboxfs_getpages(struct vop_getpages_args *ap) +{ + return (0); +} + +static int vboxfs_putpages(struct vop_putpages_args *ap) +{ + return (0); +} + Index: src/VBox/Additions/freebsd/vboxvfs/vboxvfs_prov.c =================================================================== --- src/VBox/Additions/freebsd/vboxvfs/vboxvfs_prov.c (revision 0) +++ src/VBox/Additions/freebsd/vboxvfs/vboxvfs_prov.c (working copy) @@ -0,0 +1,1014 @@ +/** @file + * VirtualBox File System for FreeBSD Guests, provider implementation. + * Portions contributed by: Ronald. + */ + +/* + * Copyright (C) 2008-2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +/* + * Provider interfaces for shared folder file system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vboxvfs.h" + +#define DIRENT_RECLEN(namelen) ((sizeof(struct dirent) - \ + sizeof(((struct dirent *)NULL)->d_name) + \ + (namelen) + 1 + 7) & ~7) +#define DIRENT_NAMELEN(reclen) \ + (sizeof((reclen) - (sizeof(((struct dirent *)NULL)->d_name)))) + +static VBSFCLIENT vbox_client; + +static int sfprov_vbox2errno(int rc) +{ + if (rc == VERR_ACCESS_DENIED) + return (EACCES); + if (rc == VERR_INVALID_NAME) + return (ENOENT); + return (RTErrConvertToErrno(rc)); +} + +/* + * utility to create strings + */ +static SHFLSTRING * +sfprov_string(char *path, int *sz) +{ + SHFLSTRING *str; + int len = strlen(path); + + *sz = len + 1 + sizeof (*str) - sizeof (str->String); + str = malloc(*sz, M_VBOXVFS, M_WAITOK | M_ZERO); + str->u16Size = len + 1; + str->u16Length = len; + strcpy(str->String.utf8, path); + return (str); +} + +sfp_connection_t * +sfprov_connect(int version) +{ + /* + * only one version for now, so must match + */ + int error = -1; + if (version != SFPROV_VERSION) + { + printf("sfprov_connect: wrong version. version=%d expected=%d\n", version, SFPROV_VERSION); + return NULL; + } + error = vboxInit(); + if (RT_SUCCESS(error)) + { + error = vboxConnect(&vbox_client); + if (RT_SUCCESS(error)) + { + error = vboxCallSetUtf8(&vbox_client); + if (RT_SUCCESS(error)) + { + return ((sfp_connection_t *)&vbox_client); + } + else + printf("sfprov_connect: vboxCallSetUtf8() failed\n"); + + vboxDisconnect(&vbox_client); + } + else + printf("sfprov_connect: vboxConnect() failed error=%d\n", error); + vboxUninit(); + } + else + printf("sfprov_connect: vboxInit() failed error=%d\n", error); + return (NULL); +} + +void +sfprov_disconnect(sfp_connection_t *conn) +{ + if (conn != (sfp_connection_t *)&vbox_client) + printf("sfprov_disconnect: bad argument\n"); + vboxDisconnect(&vbox_client); + vboxUninit(); +} + +int +sfprov_mount(char *path, sfp_mount_t **mnt) +{ + sfp_mount_t *m; + SHFLSTRING *str; + int size; + int error; + + m = malloc(sizeof (*m), M_VBOXVFS, M_WAITOK | M_ZERO); + str = sfprov_string(path, &size); + error = vboxCallMapFolder(&vbox_client, str, &m->map); + if (RT_FAILURE(error)) { + printf("sfprov_mount: vboxCallMapFolder() failed error=%d\n", error); + free(m, M_VBOXVFS); + *mnt = NULL; + error = EINVAL; + } else { + *mnt = m; + error = 0; + } + free(str, M_VBOXVFS); + return (error); +} + +int +sfprov_unmount(sfp_mount_t *mnt) +{ + int rc; + + rc = vboxCallUnmapFolder(&vbox_client, &mnt->map); + if (RT_FAILURE(rc)) { + printf("sfprov_mount: vboxCallUnmapFolder() failed rc=%d\n", rc); + rc = EINVAL; + } else { + rc = 0; + } + + free(mnt, M_VBOXVFS); + return (rc); +} + +/* + * query information about a mounted file system + */ +int +sfprov_get_fsinfo(sfp_mount_t *mnt, sffs_fsinfo_t *fsinfo) +{ + int rc; + SHFLVOLINFO info; + uint32_t bytes = sizeof(SHFLVOLINFO); + + rc = vboxCallFSInfo(&vbox_client, &mnt->map, 0, + (SHFL_INFO_GET | SHFL_INFO_VOLUME), &bytes, (SHFLDIRINFO *)&info); + if (RT_FAILURE(rc)) + return (EINVAL); + + fsinfo->blksize = info.ulBytesPerAllocationUnit; + fsinfo->blksused = (info.ullTotalAllocationBytes - info.ullAvailableAllocationBytes) / info.ulBytesPerAllocationUnit; + fsinfo->blksavail = info.ullAvailableAllocationBytes / info.ulBytesPerAllocationUnit; + fsinfo->maxnamesize = info.fsProperties.cbMaxComponent; + fsinfo->readonly = info.fsProperties.fReadOnly; + return (0); +} + +/* + * file/directory information conversions. + */ +static void +sfprov_fmode_from_mode(RTFMODE *fMode, mode_t mode) +{ + RTFMODE m = 0; + +#define mode_set(r) ((mode & (S_##r)) ? RTFS_UNIX_##r : 0) + m = mode_set (ISUID); + m |= mode_set (ISGID); + m |= (mode & S_ISVTX) ? RTFS_UNIX_ISTXT : 0; + m |= mode_set (IRUSR); + m |= mode_set (IWUSR); + m |= mode_set (IXUSR); + m |= mode_set (IRGRP); + m |= mode_set (IWGRP); + m |= mode_set (IXGRP); + m |= mode_set (IROTH); + m |= mode_set (IWOTH); + m |= mode_set (IXOTH); + + if (S_ISDIR(mode)) + m |= RTFS_TYPE_DIRECTORY; + else if (S_ISREG(mode)) + m |= RTFS_TYPE_FILE; + else if (S_ISFIFO(mode)) + m |= RTFS_TYPE_FIFO; + else if (S_ISCHR(mode)) + m |= RTFS_TYPE_DEV_CHAR; + else if (S_ISBLK(mode)) + m |= RTFS_TYPE_DEV_BLOCK; + else if (S_ISLNK(mode)) + m |= RTFS_TYPE_SYMLINK; + else if (S_ISSOCK(mode)) + m |= RTFS_TYPE_SOCKET; + else + m |= RTFS_TYPE_FILE; + + *fMode = m; +} + +static void +sfprov_mode_from_fmode(mode_t *mode, RTFMODE fMode) +{ + mode_t m = 0; + + if (RTFS_IS_DIRECTORY(fMode)) + m |= S_IFDIR; + else if (RTFS_IS_FILE(fMode)) + m |= S_IFREG; + else if (RTFS_IS_FIFO(fMode)) + m |= S_IFIFO; + else if (RTFS_IS_DEV_CHAR(fMode)) + m |= S_IFCHR; + else if (RTFS_IS_DEV_BLOCK(fMode)) + m |= S_IFBLK; + else if (RTFS_IS_SYMLINK(fMode)) + m |= S_IFLNK; + else if (RTFS_IS_SOCKET(fMode)) + m |= S_IFSOCK; + + if (fMode & RTFS_UNIX_IRUSR) + m |= S_IRUSR; + if (fMode & RTFS_UNIX_IWUSR) + m |= S_IWUSR; + if (fMode & RTFS_UNIX_IXUSR) + m |= S_IXUSR; + if (fMode & RTFS_UNIX_IRGRP) + m |= S_IRGRP; + if (fMode & RTFS_UNIX_IWGRP) + m |= S_IWGRP; + if (fMode & RTFS_UNIX_IXGRP) + m |= S_IXGRP; + if (fMode & RTFS_UNIX_IROTH) + m |= S_IROTH; + if (fMode & RTFS_UNIX_IWOTH) + m |= S_IWOTH; + if (fMode & RTFS_UNIX_IXOTH) + m |= S_IXOTH; + if (fMode & RTFS_UNIX_ISUID) + m |= S_ISUID; + if (fMode & RTFS_UNIX_ISGID) + m |= S_ISGID; + if (fMode & RTFS_UNIX_ISTXT) + m |= S_ISVTX; + *mode = m; +} + +static void +sfprov_ftime_from_timespec(struct timespec *time, RTTIMESPEC *ts) +{ + uint64_t nanosec = RTTimeSpecGetNano(ts); + time->tv_sec = nanosec / UINT64_C(1000000000); + time->tv_nsec = nanosec % UINT64_C(1000000000); +} + +static void +sfprov_stat_from_info(sffs_stat_t *stat, SHFLFSOBJINFO *info) +{ + sfprov_mode_from_fmode(&stat->sf_mode, info->Attr.fMode); + stat->sf_size = info->cbObject; + stat->sf_alloc = info->cbAllocated; + sfprov_ftime_from_timespec(&stat->sf_atime, &info->AccessTime); + sfprov_ftime_from_timespec(&stat->sf_mtime, &info->ModificationTime); + sfprov_ftime_from_timespec(&stat->sf_ctime, &info->ChangeTime); +} + +/* + * File operations: open/close/read/write/etc. + * + * open/create can return any relevant errno, however ENOENT + * generally means that the host file didn't exist. + */ +int +sfprov_create( + sfp_mount_t *mnt, + char *path, + mode_t mode, + sfp_file_t **fp, + sffs_stat_t *stat) +{ + + int rc; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + int size; + sfp_file_t *newfp; + + str = sfprov_string(path, &size); + parms.Handle = SHFL_HANDLE_NIL; + parms.Info.cbObject = 0; + sfprov_fmode_from_mode(&parms.Info.Attr.fMode, mode); + parms.CreateFlags = SHFL_CF_ACT_CREATE_IF_NEW | + SHFL_CF_ACT_REPLACE_IF_EXISTS | SHFL_CF_ACCESS_READWRITE; + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + free(str, M_VBOXVFS); + + if (RT_FAILURE(rc)) + { + if (rc != VERR_ACCESS_DENIED && rc != VERR_WRITE_PROTECT) + printf("sfprov_create: vboxCallCreate failed! path=%s rc=%d\n", path, rc); + return (sfprov_vbox2errno(rc)); + } + if (parms.Handle == SHFL_HANDLE_NIL) { + if (parms.Result == SHFL_FILE_EXISTS) + return (EEXIST); + return (ENOENT); + } + newfp = malloc(sizeof(sfp_file_t), M_VBOXVFS, M_WAITOK | M_ZERO); + newfp->handle = parms.Handle; + newfp->map = mnt->map; + *fp = newfp; + sfprov_stat_from_info(stat, &parms.Info); + return (0); +} + +int +sfprov_open(sfp_mount_t *mnt, char *path, sfp_file_t **fp) +{ + int rc; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + int size; + sfp_file_t *newfp; + + /* + * First we attempt to open it read/write. If that fails we + * try read only. + */ + bzero(&parms, sizeof(parms)); + str = sfprov_string(path, &size); + parms.Handle = SHFL_HANDLE_NIL; + parms.Info.cbObject = 0; + parms.CreateFlags = SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READWRITE; + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + if (RT_FAILURE(rc) && rc != VERR_ACCESS_DENIED) { + free(str, M_VBOXVFS); + return (sfprov_vbox2errno(rc)); + } + if (parms.Handle == SHFL_HANDLE_NIL) { + if (parms.Result == SHFL_PATH_NOT_FOUND || + parms.Result == SHFL_FILE_NOT_FOUND) { + free(str, M_VBOXVFS); + return (ENOENT); + } + parms.CreateFlags = + SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ; + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + if (RT_FAILURE(rc)) { + free(str, M_VBOXVFS); + return (sfprov_vbox2errno(rc)); + } + if (parms.Handle == SHFL_HANDLE_NIL) { + free(str, M_VBOXVFS); + return (ENOENT); + } + } + else + free(str, M_VBOXVFS); + newfp = malloc(sizeof(sfp_file_t), M_VBOXVFS, M_WAITOK | M_ZERO); + newfp->handle = parms.Handle; + newfp->map = mnt->map; + *fp = newfp; + return (0); +} + +int +sfprov_trunc(sfp_mount_t *mnt, char *path) +{ + int rc; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + int size; + + /* + * open it read/write. + */ + str = sfprov_string(path, &size); + parms.Handle = 0; + parms.Info.cbObject = 0; + parms.CreateFlags = SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READWRITE | + SHFL_CF_ACT_OVERWRITE_IF_EXISTS; + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + free(str, M_VBOXVFS); + + if (RT_FAILURE(rc)) { + return (EINVAL); + } + (void)vboxCallClose(&vbox_client, &mnt->map, parms.Handle); + return (0); +} + +int +sfprov_close(sfp_file_t *fp) +{ + int rc; + + rc = vboxCallClose(&vbox_client, &fp->map, fp->handle); + free(fp, M_VBOXVFS); + return (0); +} + +int +sfprov_read(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes) +{ + int rc; + + rc = vboxCallRead(&vbox_client, &fp->map, fp->handle, offset, + numbytes, (uint8_t *)buffer, 0); /* what is that last arg? */ + if (RT_FAILURE(rc)) + return (EINVAL); + return (0); +} + +int +sfprov_write(sfp_file_t *fp, char *buffer, uint64_t offset, uint32_t *numbytes) +{ + int rc; + + rc = vboxCallWrite(&vbox_client, &fp->map, fp->handle, offset, + numbytes, (uint8_t *)buffer, 0); /* what is that last arg? */ + if (RT_FAILURE(rc)) + return (EINVAL); + return (0); +} + +int +sfprov_fsync(sfp_file_t *fp) +{ + int rc; + + rc = vboxCallFlush(&vbox_client, &fp->map, fp->handle); + if (RT_FAILURE(rc)) + return (EIO); + return (0); +} + + +static int +sfprov_getinfo(sfp_mount_t *mnt, char *path, PSHFLFSOBJINFO info) +{ + int rc; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + int size; + + str = sfprov_string(path, &size); + parms.Handle = 0; + parms.Info.cbObject = 0; + parms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW; + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + free(str, M_VBOXVFS); + + if (RT_FAILURE(rc)) + return (EINVAL); + if (parms.Result != SHFL_FILE_EXISTS) + return (ENOENT); + *info = parms.Info; + return (0); +} + +/* + * get information about a file (or directory) + */ +int +sfprov_get_mode(sfp_mount_t *mnt, char *path, mode_t *mode) +{ + int rc; + SHFLFSOBJINFO info; + + rc = sfprov_getinfo(mnt, path, &info); + if (rc) + return (rc); + sfprov_mode_from_fmode(mode, info.Attr.fMode); + return (0); +} + +int +sfprov_get_size(sfp_mount_t *mnt, char *path, uint64_t *size) +{ + int rc; + SHFLFSOBJINFO info; + + rc = sfprov_getinfo(mnt, path, &info); + if (rc) + return (rc); + *size = info.cbObject; + return (0); +} + + +int +sfprov_get_atime(sfp_mount_t *mnt, char *path, struct timespec *time) +{ + int rc; + SHFLFSOBJINFO info; + + rc = sfprov_getinfo(mnt, path, &info); + if (rc) + return (rc); + sfprov_ftime_from_timespec(time, &info.AccessTime); + return (0); +} + +int +sfprov_get_mtime(sfp_mount_t *mnt, char *path, struct timespec *time) +{ + int rc; + SHFLFSOBJINFO info; + + rc = sfprov_getinfo(mnt, path, &info); + if (rc) + return (rc); + sfprov_ftime_from_timespec(time, &info.ModificationTime); + return (0); +} + +int +sfprov_get_ctime(sfp_mount_t *mnt, char *path, struct timespec *time) +{ + int rc; + SHFLFSOBJINFO info; + + rc = sfprov_getinfo(mnt, path, &info); + if (rc) + return (rc); + sfprov_ftime_from_timespec(time, &info.ChangeTime); + return (0); +} + +int +sfprov_get_attr(sfp_mount_t *mnt, char *path, sffs_stat_t *attr) +{ + int rc; + SHFLFSOBJINFO info; + + rc = sfprov_getinfo(mnt, path, &info); + if (rc) + return (rc); + sfprov_stat_from_info(attr, &info); + return (0); +} + +static void +sfprov_timespec_from_ftime(RTTIMESPEC *ts, struct timespec time) +{ + uint64_t nanosec = UINT64_C(1000000000) * time.tv_sec + time.tv_nsec; + RTTimeSpecSetNano(ts, nanosec); +} + +int +sfprov_set_attr( + sfp_mount_t *mnt, + char *path, + mode_t mode, + struct timespec atime, + struct timespec mtime, + struct timespec ctime) +{ + int rc, err; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + SHFLFSOBJINFO info; + uint32_t bytes; + int str_size; + + str = sfprov_string(path, &str_size); + parms.Handle = 0; + parms.Info.cbObject = 0; + parms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW + | SHFL_CF_ACCESS_ATTR_WRITE; + + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + + if (RT_FAILURE(rc)) { + printf("sfprov_set_attr: vboxCallCreate(%s) failed rc=%d\n", + path, rc); + err = EINVAL; + goto fail2; + } + if (parms.Result != SHFL_FILE_EXISTS) { + err = ENOENT; + goto fail1; + } + + RT_ZERO(info); +#if 0 + if (mask & AT_MODE) + sfprov_fmode_from_mode(&info.Attr.fMode, mode); + if (mask & AT_ATIME) + sfprov_timespec_from_ftime(&info.AccessTime, atime); + if (mask & AT_MTIME) + sfprov_timespec_from_ftime(&info.ModificationTime, mtime); + if (mask & AT_CTIME) + sfprov_timespec_from_ftime(&info.ChangeTime, ctime); +#endif + bytes = sizeof(info); + rc = vboxCallFSInfo(&vbox_client, &mnt->map, parms.Handle, + (SHFL_INFO_SET | SHFL_INFO_FILE), &bytes, (SHFLDIRINFO *)&info); + if (RT_FAILURE(rc)) { + if (rc != VERR_ACCESS_DENIED && rc != VERR_WRITE_PROTECT) + { + printf("sfprov_set_attr: vboxCallFSInfo(%s, FILE) failed rc=%d\n", + path, rc); + } + err = sfprov_vbox2errno(rc); + goto fail1; + } + + err = 0; + +fail1: + rc = vboxCallClose(&vbox_client, &mnt->map, parms.Handle); + if (RT_FAILURE(rc)) { + printf("sfprov_set_attr: vboxCallClose(%s) failed rc=%d\n", + path, rc); + } +fail2: + free(str, M_VBOXVFS); + return err; +} + +int +sfprov_set_size(sfp_mount_t *mnt, char *path, uint64_t size) +{ + int rc, err; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + SHFLFSOBJINFO info; + uint32_t bytes; + int str_size; + + str = sfprov_string(path, &str_size); + parms.Handle = 0; + parms.Info.cbObject = 0; + parms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW + | SHFL_CF_ACCESS_WRITE; + + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + + if (RT_FAILURE(rc)) { + printf("sfprov_set_size: vboxCallCreate(%s) failed rc=%d\n", + path, rc); + err = EINVAL; + goto fail2; + } + if (parms.Result != SHFL_FILE_EXISTS) { + err = ENOENT; + goto fail1; + } + + RT_ZERO(info); + info.cbObject = size; + bytes = sizeof(info); + rc = vboxCallFSInfo(&vbox_client, &mnt->map, parms.Handle, + (SHFL_INFO_SET | SHFL_INFO_SIZE), &bytes, (SHFLDIRINFO *)&info); + if (RT_FAILURE(rc)) { + printf("sfprov_set_size: vboxCallFSInfo(%s, SIZE) failed rc=%d\n", + path, rc); + err = sfprov_vbox2errno(rc); + goto fail1; + } + + err = 0; + +fail1: + rc = vboxCallClose(&vbox_client, &mnt->map, parms.Handle); + if (RT_FAILURE(rc)) { + printf("sfprov_set_size: vboxCallClose(%s) failed rc=%d\n", + path, rc); + } +fail2: + free(str, M_VBOXVFS); + return err; +} + +/* + * Directory operations + */ +int +sfprov_mkdir( + sfp_mount_t *mnt, + char *path, + mode_t mode, + sfp_file_t **fp, + sffs_stat_t *stat) +{ + int rc; + SHFLCREATEPARMS parms; + SHFLSTRING *str; + int size; + sfp_file_t *newfp; + + str = sfprov_string(path, &size); + parms.Handle = SHFL_HANDLE_NIL; + parms.Info.cbObject = 0; + sfprov_fmode_from_mode(&parms.Info.Attr.fMode, mode); + parms.CreateFlags = SHFL_CF_DIRECTORY | SHFL_CF_ACT_CREATE_IF_NEW | + SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACCESS_READ; + rc = vboxCallCreate(&vbox_client, &mnt->map, str, &parms); + free(str, M_VBOXVFS); + + if (RT_FAILURE(rc)) + return (sfprov_vbox2errno(rc)); + if (parms.Handle == SHFL_HANDLE_NIL) { + if (parms.Result == SHFL_FILE_EXISTS) + return (EEXIST); + return (ENOENT); + } + newfp = malloc(sizeof(sfp_file_t), M_VBOXVFS, M_WAITOK | M_ZERO); + newfp->handle = parms.Handle; + newfp->map = mnt->map; + *fp = newfp; + sfprov_stat_from_info(stat, &parms.Info); + return (0); +} + +int +sfprov_set_show_symlinks(void) +{ + int rc; + + rc = vboxCallSetSymlinks(&vbox_client); + if (RT_FAILURE(rc)) + return (sfprov_vbox2errno(rc)); + + return (0); +} + +int +sfprov_remove(sfp_mount_t *mnt, char *path, u_int is_link) +{ + int rc; + SHFLSTRING *str; + int size; + + str = sfprov_string(path, &size); + rc = vboxCallRemove(&vbox_client, &mnt->map, str, + SHFL_REMOVE_FILE | (is_link ? SHFL_REMOVE_SYMLINK : 0)); + free(str, M_VBOXVFS); + if (RT_FAILURE(rc)) + return (sfprov_vbox2errno(rc)); + return (0); +} + +int +sfprov_readlink( + sfp_mount_t *mnt, + char *path, + char *target, + size_t tgt_size) +{ + int rc; + SHFLSTRING *str; + int size; + + str = sfprov_string(path, &size); + + rc = vboxReadLink(&vbox_client, &mnt->map, str, (uint32_t) tgt_size, + target); + if (RT_FAILURE(rc)) + rc = sfprov_vbox2errno(rc); + + free(str, M_VBOXVFS); + return (rc); +} + +int +sfprov_symlink( + sfp_mount_t *mnt, + char *linkname, + char *target, + sffs_stat_t *stat) +{ + int rc; + SHFLSTRING *lnk, *tgt; + int lnk_size, tgt_size; + SHFLFSOBJINFO info; + + lnk = sfprov_string(linkname, &lnk_size); + tgt = sfprov_string(target, &tgt_size); + + rc = vboxCallSymlink(&vbox_client, &mnt->map, lnk, tgt, &info); + if (RT_FAILURE(rc)) { + rc = sfprov_vbox2errno(rc); + goto done; + } + + if (stat != NULL) + sfprov_stat_from_info(stat, &info); + +done: + free(lnk, M_VBOXVFS); + free(tgt, M_VBOXVFS); + + return (rc); +} + +int +sfprov_rmdir(sfp_mount_t *mnt, char *path) +{ + int rc; + SHFLSTRING *str; + int size; + + str = sfprov_string(path, &size); + rc = vboxCallRemove(&vbox_client, &mnt->map, str, SHFL_REMOVE_DIR); + free(str, M_VBOXVFS); + if (RT_FAILURE(rc)) + return (sfprov_vbox2errno(rc)); + return (0); +} + +int +sfprov_rename(sfp_mount_t *mnt, char *from, char *to, u_int is_dir) +{ + int rc; + SHFLSTRING *old, *new; + int old_size, new_size; + + old = sfprov_string(from, &old_size); + new = sfprov_string(to, &new_size); + rc = vboxCallRename(&vbox_client, &mnt->map, old, new, + (is_dir ? SHFL_RENAME_DIR : SHFL_RENAME_FILE) | + SHFL_RENAME_REPLACE_IF_EXISTS); + free(old, M_VBOXVFS); + free(new, M_VBOXVFS); + if (RT_FAILURE(rc)) + return (sfprov_vbox2errno(rc)); + return (0); +} + +/* + * Read all filenames in a directory. + * + * - success - all entries read and returned + * - ENOENT - Couldn't open the directory for reading + * - EINVAL - Internal error of some kind + * + * On successful return, *dirents points to a list of sffs_dirents_t; + * for each dirent, all fields except the d_ino will be set appropriately. + * The caller is responsible for freeing the dirents buffer. + */ +int +sfprov_readdir( + sfp_mount_t *mnt, + char *path, + sffs_dirents_t **dirents) +{ + int error; + char *cp; + int len; + SHFLSTRING *mask_str = NULL; /* must be path with "/" appended */ + int mask_size; + sfp_file_t *fp; + uint32_t infobuff_alloc = 16384; + SHFLDIRINFO *infobuff = NULL, *info; + uint32_t numbytes; + uint32_t nents; + uint32_t size; + off_t offset; + sffs_dirents_t *cur_buf; + struct sffs_dirent *dirent; + unsigned short reclen; + unsigned short entlen; + + *dirents = NULL; + + error = sfprov_open(mnt, path, &fp); + if (error != 0) + return (ENOENT); + + /* + * Allocate the first dirents buffers. + */ + *dirents = malloc(SFFS_DIRENTS_SIZE, M_VBOXVFS, M_WAITOK | M_ZERO); + if (*dirents == NULL) { + error = (ENOSPC); + goto done; + } + cur_buf = *dirents; + cur_buf->sf_next = NULL; + cur_buf->sf_len = 0; + + /* + * Create mask that VBox expects. This needs to be the directory path, + * plus a "*" wildcard to get all files. + */ + len = strlen(path) + 3; + cp = malloc(len, M_VBOXVFS, M_WAITOK | M_ZERO); + if (cp == NULL) { + error = (ENOSPC); + goto done; + } + strcpy(cp, path); + strcat(cp, "/*"); + mask_str = sfprov_string(cp, &mask_size); + free(cp, M_VBOXVFS); + + /* + * Now loop using vboxCallDirInfo + */ + infobuff = malloc(infobuff_alloc, M_VBOXVFS, M_WAITOK | M_ZERO); + if (infobuff == NULL) { + error = (ENOSPC); + goto done; + } + + offset = 0; + for (;;) { + numbytes = infobuff_alloc; + error = vboxCallDirInfo(&vbox_client, &fp->map, fp->handle, + mask_str, 0, 0, &numbytes, infobuff, &nents); + switch (error) { + + case VINF_SUCCESS: + /* fallthrough */ + case VERR_NO_MORE_FILES: + break; + + case VERR_NO_TRANSLATION: + /* XXX ??? */ + break; + + default: + error = sfprov_vbox2errno(error); + goto done; + } + + /* + * Create the dirent_t's and save the stats for each name + */ + for (info = infobuff; (char *) info < (char *) infobuff + numbytes; nents--) { + /* expand buffers if we need more space */ + reclen = DIRENT_RECLEN(strlen(info->name.String.utf8)); + entlen = sizeof(sffs_stat_t) + reclen; + if (SFFS_DIRENTS_OFF + cur_buf->sf_len + entlen > SFFS_DIRENTS_SIZE) { + cur_buf->sf_next = malloc(SFFS_DIRENTS_SIZE, M_VBOXVFS, M_WAITOK | M_ZERO); + if (cur_buf->sf_next == NULL) { + error = ENOSPC; + goto done; + } + cur_buf = cur_buf->sf_next; + cur_buf->sf_next = NULL; + cur_buf->sf_len = 0; + } + + /* create the dirent with the name, offset, and len */ + dirent = (struct sffs_dirent *) + (((char *) &cur_buf->sf_entries[0]) + cur_buf->sf_len); + strncpy(&dirent->sf_entry.d_name[0], info->name.String.utf8, DIRENT_NAMELEN(reclen)); + dirent->sf_entry.d_reclen = reclen; + offset += entlen; + //dirent->sf_entry.d_off = offset; + + /* save the stats */ + sfprov_stat_from_info(&dirent->sf_stat, &info->Info); + + /* next info */ + cur_buf->sf_len += entlen; + size = offsetof (SHFLDIRINFO, name.String) + info->name.u16Size; + info = (SHFLDIRINFO *) ((uintptr_t) info + size); + } + CTASSERT(nents == 0); + CTASSERT((char *) info == (char *) infobuff + numbytes); + + if (error == VERR_NO_MORE_FILES) + break; + } + error = 0; + +done: + if (error != 0) { + while (*dirents) { + cur_buf = (*dirents)->sf_next; + free(*dirents, M_VBOXVFS); + *dirents = cur_buf; + } + } + if (infobuff != NULL) + free(infobuff, M_VBOXVFS); + if (mask_str != NULL) + free(mask_str, M_VBOXVFS); + sfprov_close(fp); + return (error); +} Index: src/VBox/Additions/freebsd/Makefile.kmk =================================================================== --- src/VBox/Additions/freebsd/Makefile.kmk (revision 4) +++ src/VBox/Additions/freebsd/Makefile.kmk (working copy) @@ -23,7 +23,7 @@ endif # Include sub-makefiles. -#include $(PATH_SUB_CURRENT)/vboxvfs/Makefile.kmk +include $(PATH_SUB_CURRENT)/vboxvfs/Makefile.kmk include $(PATH_SUB_CURRENT)/drm/Makefile.kmk #