VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/SharedFolders/vboxvfs_vfsops.c@ 9672

Last change on this file since 9672 was 9672, checked in by vboxsync, 17 years ago

Solaris vboxvfs: more untested code.

  • Property svn:eol-style set to native
  • Property svn:keyword set to Id
  • Property svn:keywords set to Id
File size: 17.4 KB
Line 
1/* $Id: vboxvfs_vfsops.c 9672 2008-06-13 08:01:07Z vboxsync $ */
2/** @file
3 * VirtualBox File System Driver for Solaris Guests.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * Sun Microsystems, Inc. confidential
10 * All rights reserved
11 */
12
13
14/*******************************************************************************
15* Header Files *
16*******************************************************************************/
17#include <sys/types.h>
18#include <sys/mntent.h>
19#include <sys/param.h>
20#include <sys/modctl.h>
21#include <sys/mount.h>
22#include <sys/policy.h>
23#include <sys/ddi.h>
24#include <sys/sunddi.h>
25#include "vboxvfs.h"
26
27#include <VBox/log.h>
28#include <iprt/string.h>
29#include <iprt/mem.h>
30
31
32/*******************************************************************************
33* Defined Constants And Macros *
34*******************************************************************************/
35/** The module name. */
36#define DEVICE_NAME "vboxvfs"
37/** The module description as seen in 'modinfo'. */
38#define DEVICE_DESC "VirtualBox Shared Filesystem"
39
40/** Mount Options */
41#define MNTOPT_VBOXVFS_UID "uid"
42#define MNTOPT_VBOXVFS_GID "gid"
43
44/** Helpers */
45#define VFS2VBOXVFS(vfs) ((vboxvfs_globinfo_t *)((vfs)->vfs_data))
46#define VBOXVFS2VFS(vboxvfs) ((vboxvfs)->pVFS)
47
48
49/*******************************************************************************
50* Internal Functions *
51*******************************************************************************/
52static int VBoxVFS_Init(int fType, char *pszName);
53static int VBoxVFS_Mount(vfs_t *pVFS, vnode_t *pVNode, struct mounta *pMount, cred_t *pCred);
54static int VBoxVFS_Unmount(vfs_t *pVFS, int fFlags, cred_t *pCred);
55static int VBoxVFS_Root(vfs_t *pVFS, vnode_t **ppVNode);
56static int VBoxVFS_Statfs(register vfs_t *pVFS, struct statvfs64 *pStat);
57static int VBoxVFS_VGet(vfs_t *pVFS, vnode_t **ppVNode, struct fid *pFid);
58static void VBoxVFS_FreeVFS(vfs_t *pVFS);
59
60static int vboxvfs_CheckMountPerm(vfs_t *pVFS, struct mounta *pMount, vnode_t *pVNodeSpec, cred_t *pCred);
61static int vboxvfs_GetIntOpt(vfs_t *pVFS, char *pszOpt, int *pValue);
62
63
64/*******************************************************************************
65* Structures and Typedefs *
66*******************************************************************************/
67/**
68 * mntopts_t: mount options table array
69 */
70static mntopt_t g_VBoxVFSMountOptions[] =
71{
72 /* Option Name Cancel Opt. Default Arg Flags Data */
73 { MNTOPT_VBOXVFS_UID, NULL, NULL, MO_HASVALUE, NULL },
74 { MNTOPT_VBOXVFS_GID, NULL, NULL, MO_HASVALUE, NULL }
75};
76
77/**
78 * mntopts_t: mount options table prototype
79 */
80static mntopts_t g_VBoxVFSMountTableProt =
81{
82 sizeof(g_VBoxVFSMountOptions) / sizeof(mntopt_t),
83 g_VBoxVFSMountOptions
84};
85
86/**
87 * vfsdef_t: driver specific mount options
88 */
89static vfsdef_t g_VBoxVFSDef =
90{
91 VFSDEF_VERSION,
92 DEVICE_NAME,
93 VBoxVFS_Init,
94 VSW_HASPROTO,
95 &g_VBoxVFSMountTableProt
96};
97
98/**
99 * modlfs: loadable file system
100 */
101static struct modlfs g_VBoxVFSLoadMod =
102{
103 &mod_fsops, /* extern from kernel */
104 DEVICE_DESC,
105 &g_VBoxVFSDef
106};
107
108/**
109 * modlinkage: export install/remove/info to the kernel
110 */
111static struct modlinkage g_VBoxVFSModLinkage =
112{
113 MODREV_1, /* loadable module system revision */
114 &g_VBoxVFSLoadMod,
115 NULL /* terminate array of linkage structures */
116};
117
118/**
119 * state info. for vboxvfs
120 */
121typedef struct
122{
123 /** Device Info handle. */
124 dev_info_t *pDip;
125 /** Driver Mutex. */
126 kmutex_t Mtx;
127} vboxvfs_state_t;
128
129
130/*******************************************************************************
131* Global Variables *
132*******************************************************************************/
133/** Opaque pointer to list of states. */
134static void *g_pVBoxVFSState;
135/** GCC C++ hack. */
136unsigned __gxx_personality_v0 = 0xdecea5ed;
137/** Global connection to the client. */
138static VBSFCLIENT g_VBoxVFSClient;
139/** Global VFS Operations pointer. */
140vfsops_t *g_pVBoxVFS_vfsops;
141/** The file system type identifier. */
142static int g_VBoxVFSType;
143
144
145/**
146 * Kernel entry points
147 */
148int _init(void)
149{
150 LogFlow((DEVICE_NAME ":_init\n"));
151 int rc = ddi_soft_state_init(&g_pVBoxVFSState, sizeof(vboxvfs_state_t), 1);
152 if (!rc)
153 {
154 rc = mod_install(&g_VBoxVFSModLinkage);
155 if (rc)
156 ddi_soft_state_fini(&g_pVBoxVFSState);
157 }
158 return rc;
159}
160
161
162int _fini(void)
163{
164 LogFlow((DEVICE_NAME ":_fini\n"));
165 int rc = mod_remove(&g_VBoxVFSModLinkage);
166 if (!rc)
167 ddi_soft_state_fini(&g_pVBoxVFSState);
168 return rc;
169}
170
171
172int _info(struct modinfo *pModInfo)
173{
174 LogFlow((DEVICE_NAME ":_info\n"));
175 return mod_info(&g_VBoxVFSModLinkage, pModInfo);
176}
177
178
179static int VBoxVFS_Init(int fType, char *pszName)
180{
181 int rc;
182
183 LogFlow((DEVICE_NAME ":VBoxVFS_Init\n"));
184
185 /* Initialize the R0 guest library. */
186 rc = vboxInit();
187 if (VBOX_SUCCESS(rc))
188 {
189 /* Connect to the host service. */
190 rc = vboxConnect(&g_VBoxVFSClient);
191 if (VBOX_SUCCESS(rc))
192 {
193 /* Use UTF-8 encoding. */
194 rc = vboxCallSetUtf8 (&g_VBoxVFSClient);
195 if (VBOX_SUCCESS(rc))
196 {
197 /* Fill up VFS user entry points. */
198 static const fs_operation_def_t s_VBoxVFS_vfsops_template[] =
199 {
200 VFSNAME_MOUNT, { .vfs_mount = VBoxVFS_Mount },
201 VFSNAME_UNMOUNT, { .vfs_unmount = VBoxVFS_Unmount },
202 VFSNAME_ROOT, { .vfs_root = VBoxVFS_Root },
203 VFSNAME_STATVFS, { .vfs_statvfs = VBoxVFS_Statfs },
204 VFSNAME_VGET, { .vfs_vget = VBoxVFS_VGet },
205 VFSNAME_FREEVFS, { .vfs_freevfs = VBoxVFS_FreeVFS },
206 NULL, NULL
207 };
208
209 rc = vfs_setfsops(fType, s_VBoxVFS_vfsops_template, &g_pVBoxVFS_vfsops);
210 if (!rc)
211 {
212 /* Set VNode operations. */
213 rc = vn_make_ops(pszName, g_VBoxVFS_vnodeops_template, &g_pVBoxVFS_vnodeops);
214 if (!rc)
215 {
216 g_VBoxVFSType = fType;
217 LogFlow((DEVICE_NAME ":Successfully loaded vboxvfs.\n"));
218 return 0;
219 }
220 }
221 else
222 LogRel((DEVICE_NAME ":vfs_setfsops failed. rc=%d\n", rc));
223 }
224 else
225 {
226 LogRel((DEVICE_NAME ":vboxCallSetUtf8 failed. rc=%d\n", rc));
227 rc = EPROTO;
228 }
229 vboxDisconnect(&g_VBoxVFSClient);
230 }
231 else
232 {
233 LogRel((DEVICE_NAME ":Failed to connect to host! rc=%d\n", rc));
234 rc = ENXIO;
235 }
236 vboxUninit();
237 }
238 else
239 {
240 LogRel((DEVICE_NAME ":Failed to initialize R0 lib. rc=%d\n", rc));
241 rc = ENXIO;
242 }
243 return rc;
244}
245
246static int VBoxVFS_Mount(vfs_t *pVFS, vnode_t *pVNode, struct mounta *pMount, cred_t *pCred)
247{
248 int rc;
249 int Uid = 0;
250 int Gid = 0;
251 char *pszShare;
252 size_t cbShare;
253 pathname_t PathName;
254 vnode_t *pVNodeSpec;
255 vnode_t *pVNodeDev;
256 dev_t Dev;
257 SHFLSTRING *pShflShareName = NULL;
258 size_t cbShflShareName;
259 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo;
260 int AddrSpace = (pMount->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE;
261#if 0
262 caddr_t pData;
263 size_t cbData;
264 vboxvfs_mountinfo_t Args;
265#endif
266
267 LogFlow((DEVICE_NAME ":VBoxVFS_Mount\n"));
268
269 /* Check user credentials for mounting in the specified target. */
270 rc = secpolicy_fs_mount(pCred, pVNode, pVFS);
271 if (rc)
272 {
273 LogRel((DEVICE_NAME ":VBoxVFS_Mount: secpolicy_fs_mount failed! invalid credentials.rc=%d\n", rc));
274 return EPERM;
275 }
276
277 /* We can mount to only directories. */
278 if (pVNode->v_type != VDIR)
279 return ENOTDIR;
280
281 /* We don't support remounting. */
282 if (pMount->flags & MS_REMOUNT)
283 return ENOTSUP;
284
285 mutex_enter(&pVNode->v_lock);
286 if ( !(pMount->flags & MS_REMOUNT)
287 && !(pMount->flags & MS_OVERLAY)
288 && (pVNode->v_count != -1 || (pVNode->v_flag & VROOT)))
289 {
290 LogRel((DEVICE_NAME ":VBoxVFS_Mount: device busy.\n"));
291 mutex_exit(&pVNode->v_lock);
292 return EBUSY;
293 }
294 mutex_exit(&pVNode->v_lock);
295
296 /* From what I understood the options are already parsed at a higher level */
297 if ( (pMount->flags & MS_DATA)
298 && pMount->datalen > 0)
299 {
300 LogRel((DEVICE_NAME ":VBoxVFS_Mount: unparsed options not supported.\n"));
301 return EINVAL;
302 }
303
304 /* Will be removed eventually... */
305#if 0
306 /* Retreive arguments. */
307 bzero(&Args, sizeof(Args));
308 cbData = pMount->datalen;
309 pData = pMount->data;
310 if ( (pMount->flags & MS_DATA)
311 && pData != NULL
312 && cbData > 0)
313 {
314 if (cbData > sizeof(Args))
315 {
316 LogRel((DEVICE_NAME: "VBoxVFS_Mount: argument length too long. expected=%d. received=%d\n", sizeof(Args), cbData));
317 return EINVAL;
318 }
319
320 /* Copy arguments; they can be in kernel or user space. */
321 rc = ddi_copyin(pData, &Args, cbData, (pMount->flags & MS_SYSSPACE) ? FKIOCTL : 0);
322 if (rc)
323 {
324 LogRel((DEVICE_NAME: "VBoxVFS_Mount: ddi_copyin failed to copy arguments.rc=%d\n", rc));
325 return EFAULT;
326 }
327 }
328 else
329 {
330 cbData = 0;
331 pData = NULL;
332 }
333#endif
334
335 /* Get UID argument (optional). */
336 rc = vboxvfs_GetIntOpt(pVFS, MNTOPT_VBOXVFS_UID, &Uid);
337 if (rc < 0)
338 {
339 LogRel((DEVICE_NAME ":VBoxVFS_Mount: invalid uid value.\n"));
340 return EINVAL;
341 }
342
343 /* Get GID argument (optional). */
344 rc = vboxvfs_GetIntOpt(pVFS, MNTOPT_VBOXVFS_GID, &Gid);
345 if (rc < 0)
346 {
347 LogRel((DEVICE_NAME ":VBoxVFS_Mount: invalid gid value.\n"));
348 return EINVAL;
349 }
350
351 /* Get special (sharename). */
352 rc = pn_get(pMount->spec, AddrSpace, &PathName);
353 if (!rc)
354 {
355 /* Get the vnode for the special file for storing the device identifier and path. */
356 rc = lookupname(PathName.pn_path, AddrSpace, FOLLOW, NULLVPP, &pVNodeSpec);
357 if (!rc)
358 {
359 /* Check if user has permission to use the special file for mounting. */
360 rc = vboxvfs_CheckMountPerm(pVFS, pMount, pVNodeSpec, pCred);
361 VN_RELE(pVNodeSpec);
362 if (!rc)
363 {
364 Dev = pVNodeSpec->v_rdev;
365 pszShare = RTStrDup(PathName.pn_path);
366 cbShare = strlen(pszShare);
367 }
368 else
369 LogRel((DEVICE_NAME ":VBoxVFS_Mount: invalid permissions to mount %s.rc=%d\n", pszShare, rc));
370 rc = EPERM;
371 }
372 else
373 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to lookup sharename.rc=%d\n", rc));
374 rc = EINVAL;
375 }
376 else
377 {
378 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to get special file path.rc=%d\n", rc));
379 rc = EINVAL;
380 }
381 pn_free(&PathName);
382
383 if (!rc)
384 return rc;
385
386 /* Get VNode of the special file being mounted. */
387 pVNodeDev = makespecvp(Dev, VBLK);
388 if (!IS_SWAPVP(pVNodeDev))
389 {
390 /* Open the vnode for mounting. */
391 rc = VOP_OPEN(&pVNodeDev, (pVFS->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, pCred, NULL);
392 if (rc)
393 {
394 LogRel((DEVICE_NAME ":VBoxVFS_Mount: failed to mount.\n"));
395 rc = EINVAL;
396 }
397 }
398 else
399 {
400 LogRel((DEVICE_NAME ":VBoxVFS_Mount: cannot mount from swap.\n"));
401 rc = EINVAL;
402 }
403 if (!rc)
404 {
405 VN_RELE(pVNodeDev);
406 return rc;
407 }
408
409 /* Allocate the global info. structure. */
410 pVBoxVFSGlobalInfo = RTMemAllocZ(sizeof(*pVBoxVFSGlobalInfo));
411 if (!pVBoxVFSGlobalInfo)
412 {
413 LogRel((DEVICE_NAME ":VBoxVFS_Mount: RTMemAllocZ failed to alloc %d bytes for global struct.\n", sizeof(*pVBoxVFSGlobalInfo)));
414 return ENOMEM;
415 }
416
417 cbShflShareName = offsetof(SHFLSTRING, String.utf8) + cbShare + 1;
418 pShflShareName = RTMemAllocZ(cbShflShareName);
419 if (!pShflShareName)
420 {
421 RTMemFree(pVBoxVFSGlobalInfo);
422 LogRel((DEVICE_NAME ":VBoxVFS_Mount: RTMemAllocZ failed to alloc %d bytes for ShFlShareName.\n", cbShflShareName));
423 return ENOMEM;
424 }
425
426 pShflShareName->u16Length = cbShflShareName;
427 pShflShareName->u16Size = cbShflShareName + 1;
428 memcpy (pShflShareName->String.utf8, pszShare, cbShare + 1);
429
430 rc = vboxCallMapFolder(&g_VBoxVFSClient, pShflShareName, &pVBoxVFSGlobalInfo->Map);
431 RTMemFree(pShflShareName);
432 if (VBOX_FAILURE (rc))
433 {
434 RTMemFree(pVBoxVFSGlobalInfo);
435 LogRel((DEVICE_NAME ":VBoxVFS_Mount: vboxCallMapFolder failed rc=%d\n", rc));
436 return EPROTO;
437 }
438
439 /* @todo mutex for protecting the structure. */
440 pVBoxVFSGlobalInfo->Uid = Uid;
441 pVBoxVFSGlobalInfo->Gid = Gid;
442 pVBoxVFSGlobalInfo->pVFS = pVFS;
443 pVBoxVFSGlobalInfo->pVNodeDev = pVNodeDev;
444 pVFS->vfs_data = pVBoxVFSGlobalInfo;
445 pVFS->vfs_fstype = g_VBoxVFSType;
446 pVFS->vfs_dev = Dev;
447 vfs_make_fsid(&pVFS->vfs_fsid, Dev, g_VBoxVFSType);
448
449 /* @todo root vnode */
450
451 return 0;
452}
453
454static int VBoxVFS_Unmount(vfs_t *pVFS, int fUnmount, cred_t *pCred)
455{
456 int rc;
457 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo;
458
459 LogFlow((DEVICE_NAME ":VBoxVFS_Unmount.\n"));
460
461 /* Check if user can unmount. */
462 rc = secpolicy_fs_unmount(pCred, pVFS);
463 if (rc)
464 {
465 LogRel((DEVICE_NAME ":VBoxVFS_Unmount: insufficient privileges to unmount.rc=%d\n", rc));
466 return EPERM;
467 }
468
469 if (fUnmount & MS_FORCE)
470 pVFS->vfs_flag |= VFS_UNMOUNTED;
471
472 /* @todo implement ref-counting of active vnodes & check for busy state here. */
473 /* @todo mutex protection needed here */
474 pVBoxVFSGlobalInfo = VFS2VBOXVFS(pVFS);
475
476 rc = vboxCallUnmapFolder(&g_VBoxVFSClient, &pVBoxVFSGlobalInfo->Map);
477 if (VBOX_FAILURE(rc))
478 LogRel((DEVICE_NAME ":VBoxVFS_Unmount: failed to unmap shared folder. rc=%d\n", rc));
479
480 VN_RELE(pVBoxVFSGlobalInfo->pVNodeRoot);
481
482 RTMemFree(pVBoxVFSGlobalInfo);
483 pVFS->vfs_data = NULL;
484
485 return 0;
486}
487
488static int VBoxVFS_Root(vfs_t *pVFS, vnode_t **ppVNode)
489{
490 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo = VFS2VBOXVFS(pVFS);
491 *ppVNode = pVBoxVFSGlobalInfo->pVNodeRoot;
492 VN_HOLD(*ppVNode);
493
494 return 0;
495}
496
497static int VBoxVFS_Statfs(register vfs_t *pVFS, struct statvfs64 *pStat)
498{
499 SHFLVOLINFO VolumeInfo;
500 uint32_t cbBuffer;
501 vboxvfs_globinfo_t *pVBoxVFSGlobalInfo;
502 dev32_t Dev32;
503 int rc;
504
505 pVBoxVFSGlobalInfo = VFS2VBOXVFS(pVFS);
506 cbBuffer = sizeof(VolumeInfo);
507 rc = vboxCallFSInfo(&g_VBoxVFSClient, &pVBoxVFSGlobalInfo->Map, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME, &cbBuffer,
508 (PSHFLDIRINFO)&VolumeInfo);
509 if (VBOX_FAILURE(rc))
510 return RTErrConvertToErrno(rc);
511
512 bzero(pStat, sizeof(*pStat));
513 cmpldev(&Dev32, pVFS->vfs_dev);
514 pStat->f_fsid = Dev32;
515 pStat->f_flag = vf_to_stf(pVFS->vfs_flag);
516 pStat->f_bsize = VolumeInfo.ulBytesPerAllocationUnit;
517 pStat->f_frsize = VolumeInfo.ulBytesPerAllocationUnit;
518 pStat->f_bfree = VolumeInfo.ullAvailableAllocationBytes / VolumeInfo.ulBytesPerAllocationUnit;
519 pStat->f_bavail = VolumeInfo.ullAvailableAllocationBytes / VolumeInfo.ulBytesPerAllocationUnit;
520 pStat->f_blocks = VolumeInfo.ullTotalAllocationBytes / VolumeInfo.ulBytesPerAllocationUnit;
521 pStat->f_files = 1000;
522 pStat->f_ffree = 1000; /* don't return 0 here since the guest may think that it is not possible to create any more files */
523 pStat->f_namemax = 255; /* @todo is this correct?? */
524
525 strlcpy(pStat->f_basetype, vfssw[pVFS->vfs_fstype].vsw_name, sizeof(pStat->f_basetype));
526 strlcpy(pStat->f_fstr, DEVICE_NAME, sizeof(pStat->f_fstr));
527
528 return 0;
529}
530
531static int VBoxVFS_VGet(vfs_t *pVFS, vnode_t **ppVNode, struct fid *pFid)
532{
533 /* -- TODO -- */
534 return 0;
535}
536
537static void VBoxVFS_FreeVFS(vfs_t *pVFS)
538{
539 vboxDisconnect(&g_VBoxVFSClient);
540 vboxUninit();
541}
542
543static int vboxvfs_CheckMountPerm(vfs_t *pVFS, struct mounta *pMount, vnode_t *pVNodeSpec, cred_t *pCred)
544{
545 /* Check if user has the rights to mount the special file. */
546 int fOpen = FREAD | FWRITE;
547 int fAccess = VREAD | VWRITE;
548 int rc;
549
550 if (pVNodeSpec->v_type != VBLK)
551 return ENOTBLK;
552
553 if ( (pVFS->vfs_flag & VFS_RDONLY)
554 || (pMount->flags & MS_RDONLY))
555 {
556 fOpen = FREAD;
557 fAccess = VREAD;
558 }
559
560 rc = VOP_ACCESS(pVNodeSpec, fAccess, 0, pCred, NULL /* caller_context */);
561 if (!rc)
562 rc = secpolicy_spec_open(pCred, pVNodeSpec, fOpen);
563
564 return rc;
565}
566
567static int vboxvfs_GetIntOpt(vfs_t *pVFS, char *pszOpt, int *pValue)
568{
569 int rc;
570 long Val;
571 char *pchOpt = NULL;
572 char *pchEnd = NULL;
573
574 rc = vfs_optionisset(pVFS, pszOpt, &pchOpt);
575 if (rc)
576 {
577 rc = ddi_strtol(pchOpt, &pchEnd, 10 /* base */, &Val);
578 if ( !rc
579 && Val > INT_MIN
580 && Val < INT_MAX
581 && pchEnd == pchOpt + strlen(pchOpt))
582 {
583 *pValue = (int)Val;
584 return 0;
585 }
586 return -1;
587 }
588 return 1;
589}
590
Note: See TracBrowser for help on using the repository browser.

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