VirtualBox

source: vbox/trunk/src/VBox/Additions/darwin/vboxfs/VBoxVFS-VFSOps.cpp@ 69496

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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