/** * Mount helper: Allocate and init VBoxVFS global data. * * @param mp Mount data provided by VFS layer. * @param pUserData Mounting parameters provided by user space mount tool. * * @return VBoxVFS global data or NULL. */ static vboxvfs_mount_t * vboxvfs_alloc_internal_data(struct mount *mp, user_addr_t pUserData) { vboxvfs_mount_t *pMount; struct vboxvfs_mount_info mountInfo; struct vfsstatfs *pVfsInfo; size_t cbShareName; int rc; AssertReturn(mp, NULL); AssertReturn(pUserData, NULL); pVfsInfo = vfs_statfs(mp); AssertReturn(pVfsInfo, NULL); /* Allocate memory for VBoxVFS internal data */ pMount = (vboxvfs_mount_t *)RTMemAllocZ(sizeof(vboxvfs_mount_t)); if (pMount) { rc = vboxvfs_get_mount_info(pUserData, &mountInfo); if (rc == 0) { PDEBUG("Mounting shared folder '%s'", mountInfo.name); /* Prepare for locking. We prepare locking group and attr data here, * but allocate and initialize real lock in vboxvfs_create_vnode_internal(). * We use the same pLockGroup and pLockAttr for all vnodes related to this mount point. */ rc = vboxvfs_prepare_locking(pMount); if (rc == 0) { rc = vboxvfs_set_share_name(mp, (char *)&mountInfo.name, &cbShareName); if (rc == 0) { pMount->pShareName = vboxvfs_construct_shflstring((char *)&mountInfo.name, cbShareName); if (pMount->pShareName) { /* Remember user who mounted this share */ pMount->owner = pVfsInfo->f_owner; /* Mark root vnode as uninitialized */ ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_UNINITIALIZED); return pMount; } } } vboxvfs_destroy_locking(pMount); } RTMemFree(pMount); } return NULL; }
static int vboxvfs_vnode_readdir(struct vnop_readdir_args *args) { vboxvfs_mount_t *pMount; vboxvfs_vnode_t *pVnodeData; SHFLDIRINFO *Info; uint32_t cbInfo; mount_t mp; vnode_t vnode; struct uio *uio; int rc = 0, rc2; PDEBUG("Reading directory..."); AssertReturn(args, EINVAL); AssertReturn(args->a_eofflag, EINVAL); AssertReturn(args->a_numdirent, EINVAL); uio = args->a_uio; AssertReturn(uio, EINVAL); vnode = args->a_vp; AssertReturn(vnode, EINVAL); AssertReturn(vnode_isdir(vnode), EINVAL); pVnodeData = (vboxvfs_vnode_t *)vnode_fsnode(vnode); AssertReturn(pVnodeData, EINVAL); mp = vnode_mount(vnode); AssertReturn(mp, EINVAL); pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL); lck_rw_lock_shared(pVnodeData->pLock); cbInfo = sizeof(Info) + MAXPATHLEN; Info = (SHFLDIRINFO *)RTMemAllocZ(cbInfo); if (!Info) { PDEBUG("No memory to allocate internal data"); lck_rw_unlock_shared(pVnodeData->pLock); return ENOMEM; } uint32_t index = (uint32_t)uio_offset(uio) / (uint32_t)sizeof(struct dirent); uint32_t cFiles = 0; PDEBUG("Exploring VBoxVFS directory (%s), handle (0x%.8X), offset (0x%X), count (%d)", (char *)pVnodeData->pPath->String.utf8, (int)pVnodeData->pHandle, index, uio_iovcnt(uio)); /* Currently, there is a problem when VbglR0SfDirInfo() is not able to * continue retrieve directory content if the same VBoxVFS handle is used. * This macro forces to use a new handle in readdir() callback. If enabled, * the original handle (obtained in open() callback is ignored). */ SHFLHANDLE Handle; rc = vboxvfs_open_internal(pMount, pVnodeData->pPath, SHFL_CF_DIRECTORY | SHFL_CF_ACCESS_READ | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW, &Handle); if (rc != 0) { PDEBUG("Unable to open dir: %d", rc); RTMemFree(Info); lck_rw_unlock_shared(pVnodeData->pLock); return rc; } #if 0 rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0, 0, index, &cbInfo, (PSHFLDIRINFO)Info, &cFiles); #else SHFLSTRING *pMask = vboxvfs_construct_shflstring("*", strlen("*")); if (pMask) { for (uint32_t cSkip = 0; (cSkip < index + 1) && (rc == VINF_SUCCESS); cSkip++) { //rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0 /* pMask */, 0 /* SHFL_LIST_RETURN_ONE */, 0, &cbInfo, (PSHFLDIRINFO)Info, &cFiles); uint32_t cbReturned = cbInfo; //rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, pMask, SHFL_LIST_RETURN_ONE, 0, &cbReturned, (PSHFLDIRINFO)Info, &cFiles); rc = VbglR0SfDirInfo(&g_vboxSFClient, &pMount->pMap, Handle, 0, SHFL_LIST_RETURN_ONE, 0, &cbReturned, (PSHFLDIRINFO)Info, &cFiles); } PDEBUG("read %d files", cFiles); RTMemFree(pMask); } else { PDEBUG("Can't alloc mask"); rc = ENOMEM; } #endif rc2 = vboxvfs_close_internal(pMount, Handle); if (rc2 != 0) { PDEBUG("Unable to close directory: %s: %d", pVnodeData->pPath->String.utf8, rc2); } switch (rc) { case VINF_SUCCESS: { rc = vboxvfs_vnode_readdir_copy_data((ino_t)(index + 1), Info, uio, args->a_numdirent); break; } case VERR_NO_MORE_FILES: { PDEBUG("No more entries in directory"); *(args->a_eofflag) = 1; break; } default: { PDEBUG("VbglR0SfDirInfo() for item #%d has failed: %d", (int)index, (int)rc); rc = EINVAL; break; } } RTMemFree(Info); lck_rw_unlock_shared(pVnodeData->pLock); return rc; }
/** * Get VBoxVFS root vnode. * * Handle three cases here: * - vnode does not exist yet: create a new one * - currently creating vnode: wait till the end, increment usage count and return existing one * - vnode already created: increment usage count and return existing one * - vnode was failed to create: give a chance to try to re-create it later * * @param mp Mount data provided by VFS layer. * @param ppVnode vnode to return. * @param pContext kAuth context needed in order to authentificate mount operation. * * @return 0 on success or BSD error code otherwise. */ static int vboxvfs_root(struct mount *mp, struct vnode **ppVnode, vfs_context_t pContext) { NOREF(pContext); vboxvfs_mount_t *pMount; int rc = 0; uint32_t vid; PDEBUG("Getting root vnode..."); AssertReturn(mp, EINVAL); AssertReturn(ppVnode, EINVAL); pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp); AssertReturn(pMount, EINVAL); /* Check case when vnode does not exist yet */ if (ASMAtomicCmpXchgU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INITIALIZING, VBOXVFS_OBJECT_UNINITIALIZED)) { PDEBUG("Create new root vnode"); /* Allocate empty SHFLSTRING to indicate path to root vnode within Shared Folder */ char szEmpty[1]; SHFLSTRING *pSFVnodePath; pSFVnodePath = vboxvfs_construct_shflstring((char *)szEmpty, 0); if (pSFVnodePath) { int rc2; rc2 = vboxvfs_create_vnode_internal(mp, VDIR, NULL, TRUE, pSFVnodePath, &pMount->pRootVnode); if (rc2 != 0) { RTMemFree(pSFVnodePath); rc = ENOTSUP; } } else rc = ENOMEM; /* Notify other threads about result */ if (rc == 0) ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INITIALIZED); else ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INVALID); } else { /* Check case if we are currently creating vnode. Wait while other thread to finish allocation. */ uint8_t fRootVnodeState = VBOXVFS_OBJECT_UNINITIALIZED; while (fRootVnodeState != VBOXVFS_OBJECT_INITIALIZED && fRootVnodeState != VBOXVFS_OBJECT_INVALID) { /* @todo: Currently, we are burning CPU cycles while waiting. This is for a short * time but we should relax here! */ fRootVnodeState = ASMAtomicReadU8(&pMount->fRootVnodeState); } /* Check if the other thread initialized root vnode and it is ready to be returned */ if (fRootVnodeState == VBOXVFS_OBJECT_INITIALIZED) { /* Take care about iocount */ vid = vnode_vid(pMount->pRootVnode); rc = vnode_getwithvid(pMount->pRootVnode, vid); } else { /* Other thread reported initialization failure. * Set vnode state VBOXVFS_OBJECT_UNINITIALIZED in order to try recreate root * vnode in other attempt */ ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_UNINITIALIZED); } } /* Only return vnode if we got success */ if (rc == 0) { PDEBUG("Root vnode can be returned"); *ppVnode = pMount->pRootVnode; } else PDEBUG("Root vnode cannot be returned: 0x%X", rc); return rc; }