VirtualBox

source: vbox/trunk/src/VBox/Additions/darwin/VBoxSF/VBoxSF-VfsOps.cpp@ 75666

Last change on this file since 75666 was 75666, checked in by vboxsync, 6 years ago

darwin/VBoxSF: updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: VBoxSF-VfsOps.cpp 75666 2018-11-22 14:13:32Z vboxsync $ */
2/** @file
3 * VBoxFS - Darwin Shared Folders, Virtual File System Operations.
4 */
5
6/*
7 * Copyright (C) 2013-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "VBoxSFInternal.h"
23
24#include <iprt/assert.h>
25#include <iprt/mem.h>
26#include <iprt/asm.h>
27
28
29/* States of VBoxVFS object used in atomic variables
30 * in order to reach sync between several concurrently running threads. */
31#define VBOXVFS_OBJECT_UNINITIALIZED (0)
32#define VBOXVFS_OBJECT_INITIALIZING (1)
33#define VBOXVFS_OBJECT_INITIALIZED (2)
34#define VBOXVFS_OBJECT_INVALID (3)
35
36
37/**
38 * Mount helper: Get mounting parameters from user space and validate them.
39 *
40 * @param pUserData Mounting parameters provided by user space mount tool.
41 * @param pKernelData Buffer to store pUserData in kernel space.
42 *
43 * @return 0 on success or BSD error code otherwise.
44 */
45static int
46vboxvfs_get_mount_info(user_addr_t pUserData, PVBOXSFDRWNMOUNTINFO pKernelData)
47{
48 AssertReturn(pKernelData, EINVAL);
49 AssertReturn(pUserData, EINVAL);
50
51 /* Get mount parameters from user space */
52 if (copyin(pUserData, pKernelData, sizeof(*pKernelData)) != 0)
53 {
54 PERROR("Unable to copy mount parameters from user space");
55 return EINVAL;
56 }
57
58 /* Validate data magic */
59 if (pKernelData->u32Magic != VBOXSFDRWNMOUNTINFO_MAGIC)
60 {
61 PERROR("Mount parameter magic mismatch");
62 return EINVAL;
63 }
64
65 return 0;
66}
67
68
69/**
70 * Mount helper: Provide VFS layer with a VBox share name (stored as mounted device).
71 *
72 * @param mp Mount data provided by VFS layer.
73 * @param szShareName VBox share name.
74 * @param cbShareName Returning parameter which contains VBox share name string length.
75 *
76 * @return 0 on success or BSD error code otherwise.
77 */
78static int
79vboxvfs_set_share_name(struct mount *mp, char *szShareName, size_t *cbShareName)
80{
81 struct vfsstatfs *pVfsInfo;
82
83 AssertReturn(mp, EINVAL);
84 AssertReturn(szShareName, EINVAL);
85 AssertReturn(cbShareName, EINVAL);
86
87 pVfsInfo = vfs_statfs(mp);
88 if (!pVfsInfo)
89 {
90 PERROR("Unable to get VFS data for the mount structure");
91 return EINVAL;
92 }
93
94 return copystr(szShareName, pVfsInfo->f_mntfromname, MAXPATHLEN, cbShareName);
95}
96
97
98/**
99 * Mount helper: allocate locking group attribute and locking group itself.
100 * Store allocated data into VBoxVFS private data.
101 *
102 * @param pMount VBoxVFS global data which will be updated with
103 * locking group and its attribute in case of success;
104 * otherwise pMount unchanged.
105 *
106 * @return 0 on success or BSD error code otherwise.
107 *
108 */
109static int
110vboxvfs_prepare_locking(vboxvfs_mount_t *pMount)
111{
112 lck_grp_attr_t *pGrpAttr;
113 lck_grp_t *pGrp;
114
115 AssertReturn(pMount, EINVAL);
116
117 pGrpAttr = lck_grp_attr_alloc_init();
118 if (pGrpAttr)
119 {
120 pGrp = lck_grp_alloc_init("VBoxVFS", pGrpAttr);
121 if (pGrp)
122 {
123 pMount->pLockGroupAttr = pGrpAttr;
124 pMount->pLockGroup = pGrp;
125
126 return 0;
127 }
128 else
129 PERROR("Unable to allocate locking group");
130
131 lck_grp_attr_free(pGrpAttr);
132 }
133 else
134 PERROR("Unable to allocate locking group attribute");
135
136 return ENOMEM;
137}
138
139
140/**
141 * Mount and unmount helper: destroy locking group attribute and locking group itself.
142 *
143 * @param pMount VBoxVFS global data for which locking
144 * group and attribute will be deallocated and set to NULL.
145 */
146static void
147vboxvfs_destroy_locking(vboxvfs_mount_t *pMount)
148{
149 AssertReturnVoid(pMount);
150
151 if (pMount->pLockGroup)
152 {
153 lck_grp_free(pMount->pLockGroup);
154 pMount->pLockGroup = NULL;
155 }
156
157 if (pMount->pLockGroupAttr)
158 {
159 lck_grp_attr_free(pMount->pLockGroupAttr);
160 pMount->pLockGroupAttr = NULL;
161 }
162}
163
164/**
165 * Mount helper: Allocate and init VBoxVFS global data.
166 *
167 * @param mp Mount data provided by VFS layer.
168 * @param pUserData Mounting parameters provided by user space mount tool.
169 *
170 * @return VBoxVFS global data or NULL.
171 */
172static vboxvfs_mount_t *
173vboxvfs_alloc_internal_data(struct mount *mp, user_addr_t pUserData)
174{
175 vboxvfs_mount_t *pMount;
176 struct vboxvfs_mount_info mountInfo;
177 struct vfsstatfs *pVfsInfo;
178 size_t cbShareName;
179
180 int rc;
181
182 AssertReturn(mp, NULL);
183 AssertReturn(pUserData, NULL);
184
185 pVfsInfo = vfs_statfs(mp);
186 AssertReturn(pVfsInfo, NULL);
187
188 /* Allocate memory for VBoxVFS internal data */
189 pMount = (vboxvfs_mount_t *)RTMemAllocZ(sizeof(vboxvfs_mount_t));
190 if (pMount)
191 {
192 rc = vboxvfs_get_mount_info(pUserData, &mountInfo);
193 if (rc == 0)
194 {
195 PDEBUG("Mounting shared folder '%s'", mountInfo.szFolder);
196
197 /* Prepare for locking. We prepare locking group and attr data here,
198 * but allocate and initialize real lock in vboxvfs_create_vnode_internal().
199 * We use the same pLockGroup and pLockAttr for all vnodes related to this mount point. */
200 rc = vboxvfs_prepare_locking(pMount);
201 if (rc == 0)
202 {
203 rc = vboxvfs_set_share_name(mp, (char *)mountInfo.szFolder, &cbShareName);
204 if (rc == 0)
205 {
206 pMount->pShareName = vboxvfs_construct_shflstring(mountInfo.szFolder, cbShareName);
207 if (pMount->pShareName)
208 {
209 /* Remember user who mounted this share */
210 pMount->owner = pVfsInfo->f_owner;
211
212 /* Mark root vnode as uninitialized */
213 ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_UNINITIALIZED);
214
215 return pMount;
216 }
217 }
218 }
219
220 vboxvfs_destroy_locking(pMount);
221 }
222
223 RTMemFree(pMount);
224 }
225
226 return NULL;
227}
228
229
230/**
231 * Mount and unmount helper: Release VBoxVFS internal resources.
232 * Deallocates ppMount as well.
233 *
234 * @param ppMount Pointer to reference of VBoxVFS internal data.
235 */
236static void
237vboxvfs_destroy_internal_data(vboxvfs_mount_t **ppMount)
238{
239 AssertReturnVoid(ppMount);
240 AssertReturnVoid(*ppMount);
241 AssertReturnVoid((*ppMount)->pShareName);
242
243 RTMemFree((*ppMount)->pShareName);
244 (*ppMount)->pShareName = NULL;
245
246 vboxvfs_destroy_locking(*ppMount);
247 RTMemFree(*ppMount);
248 *ppMount = NULL;
249}
250
251
252/**
253 * Mount VBoxVFS.
254 *
255 * @param mp Mount data provided by VFS layer.
256 * @param pDev Device structure provided by VFS layer.
257 * @param pUserData Mounting parameters provided by user space mount tool.
258 * @param pContext kAuth context needed in order to authentificate mount operation.
259 *
260 * @return 0 on success or BSD error code otherwise.
261 */
262static int
263vboxvfs_mount(struct mount *mp, vnode_t pDev, user_addr_t pUserData, vfs_context_t pContext)
264{
265 NOREF(pDev);
266 NOREF(pContext);
267
268 vboxvfs_mount_t *pMount;
269
270 int rc = ENOMEM;
271
272 PDEBUG("Mounting...");
273
274 pMount = vboxvfs_alloc_internal_data(mp, pUserData);
275 if (pMount)
276 {
277 rc = VbglR0SfMapFolder(&g_SfClient, pMount->pShareName, &pMount->pMap);
278 if (RT_SUCCESS(rc))
279 {
280 /* Private data should be set before vboxvfs_create_vnode_internal() call
281 * because we need it in order to create vnode. */
282 vfs_setfsprivate(mp, pMount);
283
284 /* Reset root vnode */
285 pMount->pRootVnode = NULL;
286
287 vfs_setflags(mp, MNT_RDONLY | MNT_SYNCHRONOUS | MNT_NOEXEC | MNT_NOSUID | MNT_NODEV);
288
289 PDEBUG("VirtualBox shared folder successfully mounted");
290
291 return 0;
292 }
293
294 PDEBUG("Unable to map shared folder");
295 vboxvfs_destroy_internal_data(&pMount);
296 }
297 else
298 PDEBUG("Unable to allocate internal data");
299
300 return rc;
301}
302
303
304/**
305 * Unmount VBoxVFS.
306 *
307 * @param mp Mount data provided by VFS layer.
308 * @param fFlags Unmounting flags.
309 * @param pContext kAuth context needed in order to authentificate mount operation.
310 *
311 * @return 0 on success or BSD error code otherwise.
312 */
313static int
314vboxvfs_unmount(struct mount *mp, int fFlags, vfs_context_t pContext)
315{
316 NOREF(pContext);
317
318 vboxvfs_mount_t *pMount;
319 int rc = EBUSY;
320 int fFlush = (fFlags & MNT_FORCE) ? FORCECLOSE : 0;
321
322 PDEBUG("Attempting to %s unmount a shared folder", (fFlags & MNT_FORCE) ? "forcibly" : "normally");
323
324 AssertReturn(mp, EINVAL);
325
326 pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp);
327
328 AssertReturn(pMount, EINVAL);
329 AssertReturn(pMount->pRootVnode, EINVAL);
330
331 /* Check if we can do unmount at the moment */
332 if (!vnode_isinuse(pMount->pRootVnode, 1))
333 {
334 /* Flush child vnodes first */
335 rc = vflush(mp, pMount->pRootVnode, fFlush);
336 if (rc == 0)
337 {
338 /* Flush root vnode */
339 rc = vflush(mp, NULL, fFlush);
340 if (rc == 0)
341 {
342 vfs_setfsprivate(mp, NULL);
343
344 rc = VbglR0SfUnmapFolder(&g_SfClient, &pMount->pMap);
345 if (RT_SUCCESS(rc))
346 {
347 vboxvfs_destroy_internal_data(&pMount);
348 PDEBUG("A shared folder has been successfully unmounted");
349 return 0;
350 }
351
352 PDEBUG("Unable to unmount shared folder");
353 rc = EPROTO;
354 }
355 else
356 PDEBUG("Unable to flush filesystem before unmount, some data might be lost");
357 }
358 else
359 PDEBUG("Unable to flush child vnodes");
360
361 }
362 else
363 PDEBUG("Root vnode is in use, can't unmount");
364
365 vnode_put(pMount->pRootVnode);
366
367 return rc;
368}
369
370
371/**
372 * Get VBoxVFS root vnode.
373 *
374 * Handle three cases here:
375 * - vnode does not exist yet: create a new one
376 * - currently creating vnode: wait till the end, increment usage count and return existing one
377 * - vnode already created: increment usage count and return existing one
378 * - vnode was failed to create: give a chance to try to re-create it later
379 *
380 * @param mp Mount data provided by VFS layer.
381 * @param ppVnode vnode to return.
382 * @param pContext kAuth context needed in order to authentificate mount operation.
383 *
384 * @return 0 on success or BSD error code otherwise.
385 */
386static int
387vboxvfs_root(struct mount *mp, struct vnode **ppVnode, vfs_context_t pContext)
388{
389 NOREF(pContext);
390
391 vboxvfs_mount_t *pMount;
392 int rc = 0;
393 uint32_t vid;
394
395 PDEBUG("Getting root vnode...");
396
397 AssertReturn(mp, EINVAL);
398 AssertReturn(ppVnode, EINVAL);
399
400 pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp);
401 AssertReturn(pMount, EINVAL);
402
403 /* Check case when vnode does not exist yet */
404 if (ASMAtomicCmpXchgU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INITIALIZING, VBOXVFS_OBJECT_UNINITIALIZED))
405 {
406 PDEBUG("Create new root vnode");
407
408 /* Allocate empty SHFLSTRING to indicate path to root vnode within Shared Folder */
409 char szEmpty[1];
410 SHFLSTRING *pSFVnodePath;
411
412 pSFVnodePath = vboxvfs_construct_shflstring((char *)szEmpty, 0);
413 if (pSFVnodePath)
414 {
415 int rc2;
416 rc2 = vboxvfs_create_vnode_internal(mp, VDIR, NULL, TRUE, pSFVnodePath, &pMount->pRootVnode);
417 if (rc2 != 0)
418 {
419 RTMemFree(pSFVnodePath);
420 rc = ENOTSUP;
421 }
422 }
423 else
424 rc = ENOMEM;
425
426 /* Notify other threads about result */
427 if (rc == 0)
428 ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INITIALIZED);
429 else
430 ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INVALID);
431 }
432 else
433 {
434 /* Check case if we are currently creating vnode. Wait while other thread to finish allocation. */
435 uint8_t fRootVnodeState = VBOXVFS_OBJECT_UNINITIALIZED;
436 while (fRootVnodeState != VBOXVFS_OBJECT_INITIALIZED
437 && fRootVnodeState != VBOXVFS_OBJECT_INVALID)
438 {
439 /** @todo Currently, we are burning CPU cycles while waiting. This is for a short
440 * time but we should relax here! */
441 fRootVnodeState = ASMAtomicReadU8(&pMount->fRootVnodeState);
442
443 }
444
445 /* Check if the other thread initialized root vnode and it is ready to be returned */
446 if (fRootVnodeState == VBOXVFS_OBJECT_INITIALIZED)
447 {
448 /* Take care about iocount */
449 vid = vnode_vid(pMount->pRootVnode);
450 rc = vnode_getwithvid(pMount->pRootVnode, vid);
451 }
452 else
453 {
454 /* Other thread reported initialization failure.
455 * Set vnode state VBOXVFS_OBJECT_UNINITIALIZED in order to try recreate root
456 * vnode in other attempt */
457 ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_UNINITIALIZED);
458 }
459
460 }
461
462 /* Only return vnode if we got success */
463 if (rc == 0)
464 {
465 PDEBUG("Root vnode can be returned");
466 *ppVnode = pMount->pRootVnode;
467 }
468 else
469 PDEBUG("Root vnode cannot be returned: 0x%X", rc);
470
471 return rc;
472}
473
474/**
475 * VBoxVFS get VFS layer object attribute callback.
476 *
477 * @param mp Mount data provided by VFS layer.
478 * @param pAttr Output buffer to return attributes.
479 * @param pContext kAuth context needed in order to authentificate mount operation.
480 *
481 * @returns 0 for success, else an error code.
482 */
483static int
484vboxvfs_getattr(struct mount *mp, struct vfs_attr *pAttr, vfs_context_t pContext)
485{
486 NOREF(pContext);
487
488 vboxvfs_mount_t *pMount;
489 SHFLVOLINFO SHFLVolumeInfo;
490
491 int rc;
492 uint32_t cbBuffer = sizeof(SHFLVolumeInfo);
493
494 uint32_t u32bsize;
495 uint64_t u64blocks;
496 uint64_t u64bfree;
497
498 PDEBUG("Getting attribute...\n");
499
500 AssertReturn(mp, EINVAL);
501
502 pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp);
503 AssertReturn(pMount, EINVAL);
504 AssertReturn(pMount->pShareName, EINVAL);
505
506 rc = VbglR0SfFsInfo(&g_SfClient, &pMount->pMap, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME,
507 &cbBuffer, (PSHFLDIRINFO)&SHFLVolumeInfo);
508 AssertReturn(rc == 0, EPROTO);
509
510 u32bsize = (uint32_t)SHFLVolumeInfo.ulBytesPerAllocationUnit;
511 AssertReturn(u32bsize > 0, ENOTSUP);
512
513 u64blocks = (uint64_t)SHFLVolumeInfo.ullTotalAllocationBytes / (uint64_t)u32bsize;
514 u64bfree = (uint64_t)SHFLVolumeInfo.ullAvailableAllocationBytes / (uint64_t)u32bsize;
515
516 VFSATTR_RETURN(pAttr, f_bsize, u32bsize);
517 VFSATTR_RETURN(pAttr, f_blocks, u64blocks);
518 VFSATTR_RETURN(pAttr, f_bfree, u64bfree);
519 VFSATTR_RETURN(pAttr, f_bavail, u64bfree);
520 VFSATTR_RETURN(pAttr, f_bused, u64blocks - u64bfree);
521
522 VFSATTR_RETURN(pAttr, f_owner, pMount->owner);
523
524 VFSATTR_CLEAR_ACTIVE(pAttr, f_iosize);
525 VFSATTR_CLEAR_ACTIVE(pAttr, f_files);
526 VFSATTR_CLEAR_ACTIVE(pAttr, f_ffree);
527 VFSATTR_CLEAR_ACTIVE(pAttr, f_fssubtype);
528
529 /** @todo take care about f_capabilities and f_attributes, f_fsid */
530 VFSATTR_CLEAR_ACTIVE(pAttr, f_capabilities);
531 VFSATTR_CLEAR_ACTIVE(pAttr, f_attributes);
532 VFSATTR_CLEAR_ACTIVE(pAttr, f_fsid);
533
534 /** @todo take care about f_create_time, f_modify_time, f_access_time, f_backup_time */
535 VFSATTR_CLEAR_ACTIVE(pAttr, f_create_time);
536 VFSATTR_CLEAR_ACTIVE(pAttr, f_modify_time);
537 VFSATTR_CLEAR_ACTIVE(pAttr, f_access_time);
538 VFSATTR_CLEAR_ACTIVE(pAttr, f_backup_time);
539
540 VFSATTR_CLEAR_ACTIVE(pAttr, f_signature);
541 VFSATTR_CLEAR_ACTIVE(pAttr, f_carbon_fsid);
542 VFSATTR_CLEAR_ACTIVE(pAttr, f_uuid);
543
544 if (VFSATTR_IS_ACTIVE(pAttr, f_vol_name))
545 {
546 strlcpy(pAttr->f_vol_name, (char*)pMount->pShareName->String.utf8, MAXPATHLEN);
547 VFSATTR_SET_SUPPORTED(pAttr, f_vol_name);
548 }
549
550 VFSATTR_ALL_SUPPORTED(pAttr);
551
552 return 0;
553}
554
555/**
556 * VFS operations
557 */
558struct vfsops g_VBoxSfVfsOps =
559{
560 /* Standard operations */
561 &vboxvfs_mount,
562 NULL, /* Skipped: vfs_start() */
563 &vboxvfs_unmount,
564 &vboxvfs_root,
565 NULL, /* Skipped: vfs_quotactl */
566 &vboxvfs_getattr,
567 NULL, /* Skipped: vfs_sync */
568 NULL, /* Skipped: vfs_vget */
569 NULL, /* Skipped: vfs_fhtovp */
570 NULL, /* Skipped: vfs_vptofh */
571 NULL, /* Skipped: vfs_init */
572 NULL, /* Skipped: vfs_sysctl */
573 NULL, /* Skipped: vfs_setattr */
574 /* Reserved */
575 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, },
576};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette