1
/*********************************************************
2
* Copyright (C) 2006 VMware, Inc. All rights reserved.
4
* The contents of this file are subject to the terms of the Common
5
* Development and Distribution License (the "License") version 1.0
6
* and no later version. You may not use this file except in
7
* compliance with the License.
9
* You can obtain a copy of the License at
10
* http://www.opensource.org/licenses/cddl1.php
12
* See the License for the specific language governing permissions
13
* and limitations under the License.
15
*********************************************************/
21
* VFS operations for vmblock file system.
24
#include <sys/types.h>
25
#include <sys/kmem.h> /* kmem_zalloc() */
26
#include <sys/errno.h> /* error codes */
27
#include <sys/mount.h> /* MS_OVERLAY */
28
#include <sys/sysmacros.h> /* makedevice macro */
29
#include <sys/systm.h> /* cmpldev() */
30
#include <sys/policy.h> /* secpolicy_fs_mount() */
38
vfsops_t *vmblockVfsOps;
40
static major_t vmblockMajor;
41
static minor_t vmblockMinor;
42
static kmutex_t vmblockMutex;
48
int VMBlockVnodeGet(struct vnode **vpp, struct vnode *realVp,
49
const char *name, size_t nameLen,
50
struct vnode *dvp, struct vfs *vfsp, Bool isRoot);
51
int VMBlockVnodePut(struct vnode *vp);
52
static int VMBlockMount(struct vfs *vfsp, struct vnode *vnodep,
53
struct mounta *mntp, struct cred *credp);
54
static int VMBlockUnmount(struct vfs *vfsp, int mflag, struct cred *credp);
55
static int VMBlockRoot(struct vfs *vfsp, struct vnode **vnodepp);
56
static int VMBlockStatvfs(struct vfs *vfsp, struct statvfs64 *stats);
61
*----------------------------------------------------------------------------
67
* Note that realVp is assumed to be held (see the comment in the function
68
* for further explanation).
71
* Returns zero on success and a non-zero error code on failure. On
72
* success, vpp is filled in with a new, held vnode.
77
*----------------------------------------------------------------------------
81
VMBlockVnodeGet(struct vnode **vpp, // OUT: Filled with address of new vnode
82
struct vnode *realVp, // IN: Real vnode (assumed held)
83
const char *name, // IN: Relative name of the file
84
size_t nameLen, // IN: Size of name
85
struct vnode *dvp, // IN: Parent directory's vnode
86
struct vfs *vfsp, // IN: Filesystem structure
87
Bool isRoot) // IN: If is root directory of fs
89
VMBlockVnodeInfo *vip;
94
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockVnodeGet: entry\n");
100
ASSERT(dvp || isRoot);
102
vp = vn_alloc(KM_SLEEP);
107
vip = kmem_zalloc(sizeof *vip, KM_SLEEP);
108
vp->v_data = (void *)vip;
111
* Store the path that this file redirects to. For the root vnode we just
112
* store the provided path, but for all others we first copy in the parent
118
VMBlockVnodeInfo *dvip = VPTOVIP(dvp);
119
if (dvip->nameLen + 1 + nameLen + 1 >= sizeof vip->name) {
124
memcpy(vip->name, dvip->name, dvip->nameLen);
125
vip->name[dvip->nameLen] = '/';
126
curr = vip->name + dvip->nameLen + 1;
129
if (nameLen + 1 > (sizeof vip->name - (curr - vip->name))) {
134
memcpy(curr, name, nameLen);
135
curr[nameLen] = '\0';
136
vip->nameLen = nameLen + (curr - vip->name);
139
* We require the caller to have held realVp so we don't need VN_HOLD() it
140
* here here even though we VN_RELE() this vnode in VMBlockVnodePut().
141
* Despite seeming awkward, this is more natural since the function that our
142
* caller obtained realVp from provided a held vnode.
144
vip->realVnode = realVp;
147
* Now we'll initialize the vnode. We need to set the file type, vnode
148
* operations, flags, filesystem pointer, reference count, and device.
150
/* The root directory is our only directory; the rest are symlinks. */
151
vp->v_type = isRoot ? VDIR : VLNK;
153
vn_setops(vp, vmblockVnodeOps);
155
vp->v_flag = VNOMAP | VNOMOUNT | VNOSWAP | isRoot ? VROOT : 0;
159
/* Fill in the provided address with the new vnode. */
165
kmem_free(vip, sizeof *vip);
172
*----------------------------------------------------------------------------
176
* Frees state associated with provided vnode.
179
* Zero on success, non-zero error code on failure.
184
*----------------------------------------------------------------------------
188
VMBlockVnodePut(struct vnode *vp)
190
VMBlockVnodeInfo *vip;
191
struct vnode *realVnode;
193
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockVnodePut: entry (%p)\n", vp);
195
mutex_enter(&vp->v_lock);
196
if (vp->v_count > 1) {
198
mutex_exit(&vp->v_lock);
201
mutex_exit(&vp->v_lock);
203
vip = (VMBlockVnodeInfo *)vp->v_data;
204
realVnode = vip->realVnode;
206
kmem_free(vip, sizeof *vip);
209
* VMBlockVnodeGet() doesn't VN_HOLD() the real vnode, but all callers of it
210
* will have the vnode held, so we need to VN_RELE() here.
219
*----------------------------------------------------------------------------
223
* This is the file system initialization routine. It creates an array of
224
* fs_operation_def_t for all the vfs operations, then calls vfs_makefsops()
225
* and vfs_setfsops() to assign them to the file system properly.
228
* Returns zero on success and a non-zero error code on error.
233
*----------------------------------------------------------------------------
237
VMBlockInit(int fstype, // IN: file system type
238
char *name) // IN: Name of the file system
241
static const fs_operation_def_t vfsOpsArr[] = {
242
VMBLOCK_VOP(VFSNAME_MOUNT, vfs_mount, VMBlockMount),
243
VMBLOCK_VOP(VFSNAME_UNMOUNT, vfs_unmount, VMBlockUnmount),
244
VMBLOCK_VOP(VFSNAME_ROOT, vfs_root, VMBlockRoot),
245
VMBLOCK_VOP(VFSNAME_STATVFS, vfs_statvfs, VMBlockStatvfs),
250
Warning("VMBlockInit: received NULL input from kernel.\n");
254
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockInit: fstype=%d, name=\"%s\"\n", fstype, name);
257
* Set our file system type and the vfs operations in the kernel's VFS
260
vmblockType = fstype;
262
ret = vfs_setfsops(vmblockType, vfsOpsArr, &vmblockVfsOps);
264
Warning("VMBlockInit: could not set vfs operations.\n");
268
ret = vn_make_ops(name, vnodeOpsArr, &vmblockVnodeOps);
270
Warning("VMBlockInit: could not create vnode operations.\n");
272
* It's important not to call vfs_freevfsops() here; that's only for
273
* freeing ops created with vfs_makefsops().
275
vfs_freevfsops_by_type(vmblockType);
280
* We need to find a unique device number for this instance of the module;
281
* it will be used at each mount to secure a unique device number and file
282
* system identifier. If one cannot be located, we'll just use zero like
283
* other Solaris file systems.
285
if ((vmblockMajor = getudev()) == (major_t)-1) {
286
Warning("VMBlockInit: could not obtain unique device.\n");
290
mutex_init(&vmblockMutex, NULL, MUTEX_DEFAULT, NULL);
301
*----------------------------------------------------------------------------
305
* This function is invoked when mount(2) is called on our file system.
306
* The file system is mounted on the supplied vnode.
309
* Returns zero on success and an appropriate error code on error.
312
* The file system is mounted on top of vnodep.
314
*----------------------------------------------------------------------------
318
VMBlockMount(struct vfs *vfsp, // IN: file system to mount
319
struct vnode *vnodep, // IN: Vnode that we are mounting on
320
struct mounta *mntp, // IN: Arguments to mount(2) from user
321
struct cred *credp) // IN: Credentials of caller
323
VMBlockMountInfo *mip;
326
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockMount: entry\n");
329
* These next few checks are done by all other Solaris file systems, so
330
* let's follow their lead.
332
ret = secpolicy_fs_mount(credp, vnodep, vfsp);
334
Warning("VMBlockMount: mounting security check failed.\n");
338
if (vnodep->v_type != VDIR) {
339
Warning("VMBlockMount: not mounting on a directory.\n");
343
mutex_enter(&vnodep->v_lock);
344
if ((mntp->flags & MS_OVERLAY) == 0 &&
345
(vnodep->v_count != 1 || (vnodep->v_flag & VROOT))) {
346
mutex_exit(&vnodep->v_lock);
347
Warning("VMBlockMount: cannot allow unrequested overlay mount.\n");
350
mutex_exit(&vnodep->v_lock);
353
* The directory we are redirecting to is specified as the special file
354
* since we have no actual device to mount on. We store that path in the
355
* mount information structure (note that there's another allocation inside
356
* pn_get() so we must pn_free() that path at unmount time). KM_SLEEP
357
* guarantees our memory allocation will succeed (pn_get() uses this flag
360
mip = kmem_zalloc(sizeof *mip, KM_SLEEP);
361
ret = pn_get(mntp->spec,
362
(mntp->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE,
365
Warning("VMBlockMount: could not obtain redirecting directory.\n");
366
kmem_free(mip, sizeof *mip);
370
/* Do a lookup on the specified path. */
371
ret = lookupname(mntp->spec,
372
(mntp->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE,
375
&mip->redirectVnode);
377
Warning("VMBlockMount: could not obtain redirecting directory.\n");
381
if (mip->redirectVnode->v_type != VDIR) {
382
Warning("VMBlockMount: not redirecting to a directory.\n");
388
* Initialize our vfs structure.
390
vfsp->vfs_vnodecovered = vnodep;
391
vfsp->vfs_flag &= ~VFS_UNMOUNTED;
392
vfsp->vfs_flag |= VMBLOCK_VFS_FLAGS;
393
vfsp->vfs_bsize = PAGESIZE;
394
vfsp->vfs_fstype = vmblockType;
395
vfsp->vfs_bcount = 0;
396
/* If we had mount options, we'd call vfs_setmntopt with vfsp->vfs_mntopts */
398
/* Locate a unique device minor number for this mount. */
399
mutex_enter(&vmblockMutex);
401
vfsp->vfs_dev = makedevice(vmblockMajor, vmblockMinor);
402
vmblockMinor = (vmblockMinor + 1) & L_MAXMIN32;
403
} while (vfs_devismounted(vfsp->vfs_dev));
404
mutex_exit(&vmblockMutex);
406
vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, vmblockType);
407
vfsp->vfs_data = (caddr_t)mip;
410
* Now create the root vnode of the file system.
412
ret = VMBlockVnodeGet(&mip->root, mip->redirectVnode,
413
mip->redirectPath.pn_path,
414
mip->redirectPath.pn_pathlen,
417
Warning("VMBlockMount: couldn't create root vnode.\n");
422
VN_HOLD(vfsp->vfs_vnodecovered);
426
/* lookupname() provides a held vnode. */
427
VN_RELE(mip->redirectVnode);
429
pn_free(&mip->redirectPath);
430
kmem_free(mip, sizeof *mip);
436
*----------------------------------------------------------------------------
440
* This function is invoked when umount(2) is called on our file system.
443
* Returns zero on success and an error code on error.
446
* The root vnode will be freed.
448
*----------------------------------------------------------------------------
452
VMBlockUnmount(struct vfs *vfsp, // IN: This file system
453
int flag, // IN: Unmount flags
454
struct cred *credp) // IN: Credentials of caller
456
VMBlockMountInfo *mip;
459
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockUnmount: entry\n");
461
ret = secpolicy_fs_unmount(credp, vfsp);
466
mip = (VMBlockMountInfo *)vfsp->vfs_data;
468
mutex_enter(&mip->root->v_lock);
469
if (mip->root->v_count > 1) {
470
mutex_exit(&mip->root->v_lock);
473
mutex_exit(&mip->root->v_lock);
475
VN_RELE(vfsp->vfs_vnodecovered);
477
* We don't need to VN_RELE() mip->redirectVnode since it's the realVnode
478
* for mip->root. That means when we VN_RELE() mip->root and
479
* VMBlockInactive() is called, VMBlockVnodePut() will VN_RELE()
480
* mip->redirectVnode for us. It's like magic, but better.
484
pn_free(&mip->redirectPath);
485
kmem_free(mip, sizeof *mip);
487
vfsp->vfs_flag |= VFS_UNMOUNTED;
494
*----------------------------------------------------------------------------
498
* This supplies the root vnode for the file system.
501
* Returns zero on success and an error code on error. On success vnodepp
502
* is set to the pointer of the root vnode.
505
* The root vnode's reference count is incremented by one.
507
*----------------------------------------------------------------------------
511
VMBlockRoot(struct vfs *vfsp, // IN: file system to find root vnode of
512
struct vnode **vnodepp) // OUT: Set to pointer to root vnode of this fs
514
VMBlockMountInfo *mip;
516
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockRoot: entry\n");
518
mip = (VMBlockMountInfo *)vfsp->vfs_data;
521
*vnodepp = mip->root;
528
*----------------------------------------------------------------------------
532
* Provides statistics for the provided file system. The values provided
533
* by this function are fake.
536
* Returns zero on success and a non-zero error code on exit.
541
*----------------------------------------------------------------------------
545
VMBlockStatvfs(struct vfs *vfsp, // IN: file system to get statistics for
546
struct statvfs64 *stats) // OUT: Statistics are placed into this struct
550
Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockStatvfs: entry\n");
552
/* Clear stats struct, then fill it in with our values. */
553
memset(stats, 0, sizeof *stats);
556
* Macros in case we need these elsewhere later.
558
* Since vmblock does not provide any actual storage, we use zero so that
559
* the output of df(1) is pleasant for users.
561
#define VMBLOCK_BLOCKSIZE PAGESIZE
562
#define VMBLOCK_BLOCKS_TOTAL 0
563
#define VMBLOCK_BLOCKS_FREE 0
564
#define VMBLOCK_BLOCKS_AVAIL 0
565
#define VMBLOCK_FILES_TOTAL 0
566
#define VMBLOCK_FILES_FREE 0
567
#define VMBLOCK_FILES_AVAIL 0
569
/* Compress the device number to 32-bits for consistency on 64-bit systems. */
570
cmpldev(&dev32, vfsp->vfs_dev);
572
stats->f_bsize = VMBLOCK_BLOCKSIZE; /* Preferred fs block size */
573
stats->f_frsize = VMBLOCK_BLOCKSIZE; /* Fundamental fs block size */
574
/* Next six are u_longlong_t */
575
stats->f_blocks = VMBLOCK_BLOCKS_TOTAL; /* Total blocks on fs */
576
stats->f_bfree = VMBLOCK_BLOCKS_FREE; /* Total free blocks */
577
stats->f_bavail = VMBLOCK_BLOCKS_AVAIL; /* Total blocks avail to non-root */
578
stats->f_files = VMBLOCK_FILES_TOTAL; /* Total files (inodes) */
579
stats->f_ffree = VMBLOCK_FILES_FREE; /* Total files free */
580
stats->f_favail = VMBLOCK_FILES_AVAIL; /* Total files avail to non-root */
581
stats->f_fsid = dev32; /* file system id */
582
stats->f_flag &= ST_NOSUID; /* Flags: we don't support setuid. */
583
stats->f_namemax = MAXNAMELEN; /* Max filename; use Solaris default. */
585
/* Memset above and -1 of array size as n below ensure NUL termination. */
586
strncpy(stats->f_basetype, VMBLOCK_FS_NAME, sizeof stats->f_basetype - 1);
587
strncpy(stats->f_fstr, VMBLOCK_FS_NAME, sizeof stats->f_fstr - 1);