Пример #1
0
static void
/*ARGSUSED*/
pty_getvattr(struct mount *mp, struct lwp *l, struct vattr *vattr)
{
	vattr_null(vattr);
	/* get real uid */
	vattr->va_uid = kauth_cred_getuid(l->l_cred);
	vattr->va_gid = TTY_GID;
	vattr->va_mode = TTY_PERM;
}
Пример #2
0
/*
 * Invent attributes for ptyfsnode (vp) and store
 * them in (vap).
 * Directories lengths are returned as zero since
 * any real length would require the genuine size
 * to be computed, and nothing cares anyway.
 *
 * this is relatively minimal for ptyfs.
 */
int
ptyfs_getattr(void *v)
{
	struct vop_getattr_args /* {
		struct vnode *a_vp;
		struct vattr *a_vap;
		kauth_cred_t a_cred;
	} */ *ap = v;
	struct ptyfsnode *ptyfs = VTOPTYFS(ap->a_vp);
	struct vattr *vap = ap->a_vap;

	PTYFS_ITIMES(ptyfs, NULL, NULL, NULL);

	/* start by zeroing out the attributes */
	vattr_null(vap);

	/* next do all the common fields */
	vap->va_type = ap->a_vp->v_type;
	vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
	vap->va_fileid = ptyfs->ptyfs_fileno;
	vap->va_gen = 0;
	vap->va_flags = 0;
	vap->va_blocksize = PAGE_SIZE;

	vap->va_atime = ptyfs->ptyfs_atime;
	vap->va_mtime = ptyfs->ptyfs_mtime;
	vap->va_ctime = ptyfs->ptyfs_ctime;
	vap->va_birthtime = ptyfs->ptyfs_birthtime;
	vap->va_mode = ptyfs->ptyfs_mode;
	vap->va_flags = ptyfs->ptyfs_flags;
	vap->va_uid = ptyfs->ptyfs_uid;
	vap->va_gid = ptyfs->ptyfs_gid;

	switch (ptyfs->ptyfs_type) {
	case PTYFSpts:
	case PTYFSptc:
		if (pty_isfree(ptyfs->ptyfs_pty, 1))
			return ENOENT;
		vap->va_bytes = vap->va_size = 0;
		vap->va_rdev = ap->a_vp->v_rdev;
		vap->va_nlink = 1;
		break;
	case PTYFSroot:
		vap->va_rdev = 0;
		vap->va_bytes = vap->va_size = DEV_BSIZE;
		vap->va_nlink = 2;
		break;
	default:
		return EOPNOTSUPP;
	}

	return 0;
}
Пример #3
0
static int
puffs_vnop_getattr(struct vop_getattr_args *ap)
{
    PUFFS_MSG_VARS(vn, getattr);
    struct vnode *vp = ap->a_vp;
    struct mount *mp = vp->v_mount;
    struct puffs_mount *pmp = MPTOPUFFSMP(mp);
    struct vattr *vap, *rvap;
    struct puffs_node *pn = VPTOPP(vp);
    int error = 0;

    if (vp->v_type == VBLK || vp->v_type == VCHR)
        return ENOTSUP;

    vap = ap->a_vap;

    PUFFS_MSG_ALLOC(vn, getattr);
    vattr_null(&getattr_msg->pvnr_va);
    puffs_credcvt(&getattr_msg->pvnr_cred, curproc->p_ucred);
    puffs_msg_setinfo(park_getattr, PUFFSOP_VN,
                      PUFFS_VN_GETATTR, VPTOPNC(vp));

    PUFFS_MSG_ENQUEUEWAIT2(pmp, park_getattr, vp->v_data, NULL, error);
    error = checkerr(pmp, error, __func__);
    if (error)
        goto out;

    rvap = &getattr_msg->pvnr_va;

    (void) memcpy(vap, rvap, sizeof(struct vattr));
    vap->va_fsid = mp->mnt_stat.f_fsid.val[0];

    if (pn->pn_stat & PNODE_METACACHE_ATIME)
        vap->va_atime = pn->pn_mc_atime;
    if (pn->pn_stat & PNODE_METACACHE_CTIME)
        vap->va_ctime = pn->pn_mc_ctime;
    if (pn->pn_stat & PNODE_METACACHE_MTIME)
        vap->va_mtime = pn->pn_mc_mtime;
    if (pn->pn_stat & PNODE_METACACHE_SIZE) {
        vap->va_size = pn->pn_mc_size;
    } else {
        if (rvap->va_size != VNOVAL
                && vp->v_type != VBLK && vp->v_type != VCHR) {
            pn->pn_serversize = rvap->va_size;
            if (vp->v_type == VREG)
                puffs_meta_setsize(vp, rvap->va_size, 0);
        }
    }

out:
    PUFFS_MSG_RELEASE(getattr);
    return error;
}
Пример #4
0
static void
zfs_init_vattr(vattr_t *vap, uint64_t mask, uint64_t mode,
	uint64_t uid, uint64_t gid, uint64_t rdev, uint64_t nodeid)
{
	vattr_null(vap);
	vap->va_mask = (uint_t)mask;
	if (mask & AT_TYPE)
		vap->va_type = IFTOVT(mode);
	if (mask & AT_MODE)
		vap->va_mode = mode & MODEMASK;
	if (mask & AT_UID)
		vap->va_uid = (uid_t)(IS_EPHEMERAL(uid)) ? -1 : uid;
	if (mask & AT_GID)
		vap->va_gid = (gid_t)(IS_EPHEMERAL(gid)) ? -1 : gid;
	vap->va_rdev = zfs_cmpldev(rdev);
	vap->va_nodeid = nodeid;
}
Пример #5
0
static void
fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
    uint64_t nodeid, enum vtype vtyp)
{
	int i;

	fvdat->nid = nodeid;
	vattr_null(&fvdat->cached_attrs);
	if (nodeid == FUSE_ROOT_ID) {
		vp->v_vflag |= VV_ROOT;
	}
	vp->v_type = vtyp;
	vp->v_data = fvdat;

	for (i = 0; i < FUFH_MAXTYPE; i++)
		fvdat->fufh[i].fh_type = FUFH_INVALID;

	atomic_add_acq_int(&fuse_node_count, 1);
}
Пример #6
0
static int
flushvncache(struct vnode *vp, int waitfor)
{
    struct puffs_node *pn = VPTOPP(vp);
    struct vattr va;
    int error = 0;

    /* flush out information from our metacache, see vop_setattr */
    if (pn->pn_stat & PNODE_METACACHE_MASK
            && (pn->pn_stat & PNODE_DYING) == 0) {
        vattr_null(&va);
        error = dosetattr(vp, &va, FSCRED, SETATTR_CHSIZE |
                          (waitfor == MNT_NOWAIT ? 0 : SETATTR_ASYNC));
        if (error)
            return error;
    }

    /*
     * flush pages to avoid being overly dirty
     */
    vfsync(vp, waitfor, 0, NULL, NULL);

    return error;
}
Пример #7
0
int
adosfs_getattr(void *v)
{
	struct vop_getattr_args /* {
		struct vnode *a_vp;
		struct vattr *a_vap;
		kauth_cred_t a_cred;
	} */ *sp = v;
	struct vattr *vap;
	struct adosfsmount *amp;
	struct anode *ap;
	u_long fblks;

#ifdef ADOSFS_DIAGNOSTIC
	advopprint(sp);
#endif
	vap = sp->a_vap;
	ap = VTOA(sp->a_vp);
	amp = ap->amp;
	vattr_null(vap);
	vap->va_uid = ap->uid;
	vap->va_gid = ap->gid;
	vap->va_fsid = sp->a_vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
	vap->va_atime.tv_sec = vap->va_mtime.tv_sec = vap->va_ctime.tv_sec =
		ap->mtime.days * 24 * 60 * 60 + ap->mtime.mins * 60 +
		ap->mtime.ticks / 50 + (8 * 365 + 2) * 24 * 60 * 60;
	vap->va_atime.tv_nsec = vap->va_mtime.tv_nsec = vap->va_ctime.tv_nsec = 0;
	vap->va_gen = 0;
	vap->va_flags = 0;
	vap->va_rdev = NODEV;
	vap->va_fileid = ap->block;
	vap->va_type = sp->a_vp->v_type;
	vap->va_mode = adunixprot(ap->adprot) & amp->mask;
	if (sp->a_vp->v_type == VDIR) {
		vap->va_nlink = 1;	/* XXX bogus, oh well */
		vap->va_bytes = amp->bsize;
		vap->va_size = amp->bsize;
	} else {
		/*
		 * XXX actually we can track this if we were to walk the list
		 * of links if it exists.
		 * XXX for now, just set nlink to 2 if this is a hard link
		 * to a file, or a file with a hard link.
		 */
		vap->va_nlink = 1 + (ap->linkto != 0);
		/*
		 * round up to nearest blocks add number of file list
		 * blocks needed and mutiply by number of bytes per block.
		 */
		fblks = howmany(ap->fsize, amp->dbsize);
		fblks += howmany(fblks, ANODENDATBLKENT(ap));
		vap->va_bytes = fblks * amp->dbsize;
		vap->va_size = ap->fsize;

		vap->va_blocksize = amp->dbsize;
	}
#ifdef ADOSFS_DIAGNOSTIC
	printf(" 0)");
#endif
	return(0);
}
Пример #8
0
/*
 *	Routine:	macx_swapon
 *	Function:
 *		Syscall interface to add a file to backing store
 */
int
macx_swapon(
    char 	*filename,
    int	flags,
    long	size,
    long	priority)
{
    struct vnode		*vp = 0;
    struct nameidata 	nd, *ndp;
    struct proc		*p =  current_proc();
    pager_file_t		pf;
    register int		error;
    kern_return_t		kr;
    mach_port_t		backing_store;
    memory_object_default_t	default_pager;
    int			i;
    boolean_t		funnel_state;

    struct vattr	vattr;

    AUDIT_MACH_SYSCALL_ENTER(AUE_SWAPON);
    AUDIT_ARG(value, priority);

    funnel_state = thread_funnel_set(kernel_flock, TRUE);
    ndp = &nd;

    if ((error = suser(p->p_ucred, &p->p_acflag)))
        goto swapon_bailout;

    if(default_pager_init_flag == 0) {
        start_def_pager(NULL);
        default_pager_init_flag = 1;
    }

    /*
     * Get a vnode for the paging area.
     */
    NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1, UIO_USERSPACE,
           filename, p);

    if ((error = namei(ndp)))
        goto swapon_bailout;
    vp = ndp->ni_vp;

    if (vp->v_type != VREG) {
        error = EINVAL;
        VOP_UNLOCK(vp, 0, p);
        goto swapon_bailout;
    }
    UBCINFOCHECK("macx_swapon", vp);

    if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) {
        VOP_UNLOCK(vp, 0, p);
        goto swapon_bailout;
    }

    if (vattr.va_size < (u_quad_t)size) {
        vattr_null(&vattr);
        vattr.va_size = (u_quad_t)size;
        error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
        if (error) {
            VOP_UNLOCK(vp, 0, p);
            goto swapon_bailout;
        }
    }

    /* add new backing store to list */
    i = 0;
    while(bs_port_table[i].vp != 0) {
        if(i == MAX_BACKING_STORE)
            break;
        i++;
    }
    if(i == MAX_BACKING_STORE) {
        error = ENOMEM;
        VOP_UNLOCK(vp, 0, p);
        goto swapon_bailout;
    }

    /* remember the vnode. This vnode has namei() reference */
    bs_port_table[i].vp = vp;

    /*
     * Look to see if we are already paging to this file.
     */
    /* make certain the copy send of kernel call will work */
    default_pager = MEMORY_OBJECT_DEFAULT_NULL;
    kr = host_default_memory_manager(host_priv_self(), &default_pager, 0);
    if(kr != KERN_SUCCESS) {
        error = EAGAIN;
        VOP_UNLOCK(vp, 0, p);
        bs_port_table[i].vp = 0;
        goto swapon_bailout;
    }

    kr = default_pager_backing_store_create(default_pager,
                                            -1, /* default priority */
                                            0, /* default cluster size */
                                            &backing_store);
    memory_object_default_deallocate(default_pager);

    if(kr != KERN_SUCCESS) {
        error = ENOMEM;
        VOP_UNLOCK(vp, 0, p);
        bs_port_table[i].vp = 0;
        goto swapon_bailout;
    }

    /*
     * NOTE: we are able to supply PAGE_SIZE here instead of
     *	an actual record size or block number because:
     *	a: we do not support offsets from the beginning of the
     *		file (allowing for non page size/record modulo offsets.
     *	b: because allow paging will be done modulo page size
     */

    VOP_UNLOCK(vp, 0, p);
    kr = default_pager_add_file(backing_store, vp, PAGE_SIZE,
                                ((int)vattr.va_size)/PAGE_SIZE);
    if(kr != KERN_SUCCESS) {
        bs_port_table[i].vp = 0;
        if(kr == KERN_INVALID_ARGUMENT)
            error = EINVAL;
        else
            error = ENOMEM;
        goto swapon_bailout;
    }
    bs_port_table[i].bs = (void *)backing_store;
    error = 0;
    if (!ubc_hold(vp))
        panic("macx_swapon: hold");

    /* Mark this vnode as being used for swapfile */
    SET(vp->v_flag, VSWAP);

    ubc_setcred(vp, p);

    /*
     * take an extra reference on the vnode to keep
     * vnreclaim() away from this vnode.
     */
    VREF(vp);

    /* Hold on to the namei  reference to the paging file vnode */
    vp = 0;

swapon_bailout:
    if (vp) {
        vrele(vp);
    }
    (void) thread_funnel_set(kernel_flock, FALSE);
    AUDIT_MACH_SYSCALL_EXIT(error);
    return(error);
}
Пример #9
0
/*
 * Common code for vnode open operations.
 * Check permissions, and call the VOP_OPEN or VOP_CREATE routine.
 */
int
vn_open(struct nameidata *ndp, int fmode, int cmode)
{
	struct vnode *vp;
	struct lwp *l = curlwp;
	kauth_cred_t cred = l->l_cred;
	struct vattr va;
	int error;
	const char *pathstring;

	if ((fmode & (O_CREAT | O_DIRECTORY)) == (O_CREAT | O_DIRECTORY))
		return EINVAL;

	ndp->ni_cnd.cn_flags &= TRYEMULROOT | NOCHROOT;

	if (fmode & O_CREAT) {
		ndp->ni_cnd.cn_nameiop = CREATE;
		ndp->ni_cnd.cn_flags |= LOCKPARENT | LOCKLEAF;
		if ((fmode & O_EXCL) == 0 &&
		    ((fmode & O_NOFOLLOW) == 0))
			ndp->ni_cnd.cn_flags |= FOLLOW;
	} else {
		ndp->ni_cnd.cn_nameiop = LOOKUP;
		ndp->ni_cnd.cn_flags |= LOCKLEAF;
		if ((fmode & O_NOFOLLOW) == 0)
			ndp->ni_cnd.cn_flags |= FOLLOW;
	}

	pathstring = pathbuf_stringcopy_get(ndp->ni_pathbuf);
	if (pathstring == NULL) {
		return ENOMEM;
	}

	error = namei(ndp);
	if (error)
		goto out;

	vp = ndp->ni_vp;

#if NVERIEXEC > 0
	error = veriexec_openchk(l, ndp->ni_vp, pathstring, fmode);
	if (error)
		goto bad;
#endif /* NVERIEXEC > 0 */

	if (fmode & O_CREAT) {
		if (ndp->ni_vp == NULL) {
			vattr_null(&va);
			va.va_type = VREG;
			va.va_mode = cmode;
			if (fmode & O_EXCL)
				 va.va_vaflags |= VA_EXCLUSIVE;
			error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp,
					   &ndp->ni_cnd, &va);
			vput(ndp->ni_dvp);
			if (error)
				goto out;
			fmode &= ~O_TRUNC;
			vp = ndp->ni_vp;
			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
		} else {
			VOP_ABORTOP(ndp->ni_dvp, &ndp->ni_cnd);
			if (ndp->ni_dvp == ndp->ni_vp)
				vrele(ndp->ni_dvp);
			else
				vput(ndp->ni_dvp);
			ndp->ni_dvp = NULL;
			vp = ndp->ni_vp;
			if (fmode & O_EXCL) {
				error = EEXIST;
				goto bad;
			}
			fmode &= ~O_CREAT;
		}
	} else {
		vp = ndp->ni_vp;
	}
	if (vp->v_type == VSOCK) {
		error = EOPNOTSUPP;
		goto bad;
	}
	if (ndp->ni_vp->v_type == VLNK) {
		error = EFTYPE;
		goto bad;
	}

	if ((fmode & O_CREAT) == 0) {
		error = vn_openchk(vp, cred, fmode);
		if (error != 0)
			goto bad;
	}

	if (fmode & O_TRUNC) {
		vattr_null(&va);
		va.va_size = 0;
		error = VOP_SETATTR(vp, &va, cred);
		if (error != 0)
			goto bad;
	}
	if ((error = VOP_OPEN(vp, fmode, cred)) != 0)
		goto bad;
	if (fmode & FWRITE) {
		mutex_enter(vp->v_interlock);
		vp->v_writecount++;
		mutex_exit(vp->v_interlock);
	}

bad:
	if (error)
		vput(vp);
out:
	pathbuf_stringcopy_put(ndp->ni_pathbuf, pathstring);
	return (error);
}
Пример #10
0
/*
 * Dump core, into a file named "progname.core" or "core" (depending on the
 * value of shortcorename), unless the process was setuid/setgid.
 */
static int
coredump(struct lwp *l, const char *pattern)
{
	struct vnode		*vp;
	struct proc		*p;
	struct vmspace		*vm;
	kauth_cred_t		cred;
	struct pathbuf		*pb;
	struct nameidata	nd;
	struct vattr		vattr;
	struct coredump_iostate	io;
	struct plimit		*lim;
	int			error, error1;
	char			*name, *lastslash;

	name = PNBUF_GET();

	p = l->l_proc;
	vm = p->p_vmspace;

	mutex_enter(proc_lock);		/* p_session */
	mutex_enter(p->p_lock);

	/*
	 * Refuse to core if the data + stack + user size is larger than
	 * the core dump limit.  XXX THIS IS WRONG, because of mapped
	 * data.
	 */
	if (USPACE + ctob(vm->vm_dsize + vm->vm_ssize) >=
	    p->p_rlimit[RLIMIT_CORE].rlim_cur) {
		error = EFBIG;		/* better error code? */
		mutex_exit(p->p_lock);
		mutex_exit(proc_lock);
		goto done;
	}

	/*
	 * It may well not be curproc, so grab a reference to its current
	 * credentials.
	 */
	kauth_cred_hold(p->p_cred);
	cred = p->p_cred;

	/*
	 * Make sure the process has not set-id, to prevent data leaks,
	 * unless it was specifically requested to allow set-id coredumps.
	 */
	if (p->p_flag & PK_SUGID) {
		if (!security_setidcore_dump) {
			error = EPERM;
			mutex_exit(p->p_lock);
			mutex_exit(proc_lock);
			goto done;
		}
		pattern = security_setidcore_path;
	}

	/* Lock, as p_limit and pl_corename might change. */
	lim = p->p_limit;
	mutex_enter(&lim->pl_lock);
	if (pattern == NULL) {
		pattern = lim->pl_corename;
	}
	error = coredump_buildname(p, name, pattern, MAXPATHLEN);
	mutex_exit(&lim->pl_lock);

	if (error) {
		mutex_exit(p->p_lock);
		mutex_exit(proc_lock);
		goto done;
	}

	/*
	 * On a simple filename, see if the filesystem allow us to write
	 * core dumps there.
	 */
	lastslash = strrchr(name, '/');
	if (!lastslash) {
		vp = p->p_cwdi->cwdi_cdir;
		if (vp->v_mount == NULL ||
		    (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0)
			error = EPERM;
	}

	mutex_exit(p->p_lock);
	mutex_exit(proc_lock);
	if (error)
		goto done;

	/*
	 * On a complex filename, see if the filesystem allow us to write
	 * core dumps there.
	 *
	 * XXX: We should have an API that avoids double lookups
	 */
	if (lastslash) {
		char c[2];

		if (lastslash - name >= MAXPATHLEN - 2) {
			error = EPERM;
			goto done;
		}

		c[0] = lastslash[1];
		c[1] = lastslash[2];
		lastslash[1] = '.';
		lastslash[2] = '\0';
		error = namei_simple_kernel(name, NSM_FOLLOW_NOEMULROOT, &vp);
		if (error)
			goto done;
		if (vp->v_mount == NULL ||
		    (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0)
			error = EPERM;
		vrele(vp);
		if (error)
			goto done;
		lastslash[1] = c[0];
		lastslash[2] = c[1];
	}

	pb = pathbuf_create(name);
	if (pb == NULL) {
		error = ENOMEM;
		goto done;
	}
	NDINIT(&nd, LOOKUP, NOFOLLOW, pb);
	if ((error = vn_open(&nd, O_CREAT | O_NOFOLLOW | FWRITE,
	    S_IRUSR | S_IWUSR)) != 0) {
		pathbuf_destroy(pb);
		goto done;
	}
	vp = nd.ni_vp;
	pathbuf_destroy(pb);

	/*
	 * Don't dump to:
	 * 	- non-regular files
	 * 	- files with links
	 * 	- files we don't own
	 */
	if (vp->v_type != VREG ||
	    VOP_GETATTR(vp, &vattr, cred) || vattr.va_nlink != 1 ||
	    vattr.va_uid != kauth_cred_geteuid(cred)) {
		error = EACCES;
		goto out;
	}
	vattr_null(&vattr);
	vattr.va_size = 0;

	if ((p->p_flag & PK_SUGID) && security_setidcore_dump) {
		vattr.va_uid = security_setidcore_owner;
		vattr.va_gid = security_setidcore_group;
		vattr.va_mode = security_setidcore_mode;
	}

	VOP_SETATTR(vp, &vattr, cred);
	p->p_acflag |= ACORE;

	io.io_lwp = l;
	io.io_vp = vp;
	io.io_cred = cred;
	io.io_offset = 0;

	/* Now dump the actual core file. */
	error = (*p->p_execsw->es_coredump)(l, &io);
 out:
	VOP_UNLOCK(vp);
	error1 = vn_close(vp, FWRITE, cred);
	if (error == 0)
		error = error1;
done:
	if (name != NULL)
		PNBUF_PUT(name);
	return error;
}
Пример #11
0
/* Register a new filesystem type in the global table */
static int
vfs_register(struct vfsconf *vfc)
{
	struct sysctl_oid *oidp;
	struct vfsops *vfsops;
	static int once;

	if (!once) {
		vattr_null(&va_null);
		once = 1;
	}
	
	if (vfc->vfc_version != VFS_VERSION) {
		printf("ERROR: filesystem %s, unsupported ABI version %x\n",
		    vfc->vfc_name, vfc->vfc_version);
		return (EINVAL);
	}
	if (vfs_byname(vfc->vfc_name) != NULL)
		return EEXIST;

	vfc->vfc_typenum = maxvfsconf++;
	TAILQ_INSERT_TAIL(&vfsconf, vfc, vfc_list);

	/*
	 * If this filesystem has a sysctl node under vfs
	 * (i.e. vfs.xxfs), then change the oid number of that node to 
	 * match the filesystem's type number.  This allows user code
	 * which uses the type number to read sysctl variables defined
	 * by the filesystem to continue working. Since the oids are
	 * in a sorted list, we need to make sure the order is
	 * preserved by re-registering the oid after modifying its
	 * number.
	 */
	sysctl_lock();
	SLIST_FOREACH(oidp, &sysctl__vfs_children, oid_link)
		if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
			sysctl_unregister_oid(oidp);
			oidp->oid_number = vfc->vfc_typenum;
			sysctl_register_oid(oidp);
			break;
		}
	sysctl_unlock();

	/*
	 * Initialise unused ``struct vfsops'' fields, to use
	 * the vfs_std*() functions.  Note, we need the mount
	 * and unmount operations, at the least.  The check
	 * for vfsops available is just a debugging aid.
	 */
	KASSERT(vfc->vfc_vfsops != NULL,
	    ("Filesystem %s has no vfsops", vfc->vfc_name));
	/*
	 * Check the mount and unmount operations.
	 */
	vfsops = vfc->vfc_vfsops;
	KASSERT(vfsops->vfs_mount != NULL,
	    ("Filesystem %s has no mount op", vfc->vfc_name));
	KASSERT(vfsops->vfs_unmount != NULL,
	    ("Filesystem %s has no unmount op", vfc->vfc_name));

	if (vfsops->vfs_root == NULL)
		/* return file system's root vnode */
		vfsops->vfs_root =	vfs_stdroot;
	if (vfsops->vfs_quotactl == NULL)
		/* quota control */
		vfsops->vfs_quotactl =	vfs_stdquotactl;
	if (vfsops->vfs_statfs == NULL)
		/* return file system's status */
		vfsops->vfs_statfs =	vfs_stdstatfs;
	if (vfsops->vfs_sync == NULL)
		/*
		 * flush unwritten data (nosync)
		 * file systems can use vfs_stdsync
		 * explicitly by setting it in the
		 * vfsop vector.
		 */
		vfsops->vfs_sync =	vfs_stdnosync;
	if (vfsops->vfs_vget == NULL)
		/* convert an inode number to a vnode */
		vfsops->vfs_vget =	vfs_stdvget;
	if (vfsops->vfs_fhtovp == NULL)
		/* turn an NFS file handle into a vnode */
		vfsops->vfs_fhtovp =	vfs_stdfhtovp;
	if (vfsops->vfs_checkexp == NULL)
		/* check if file system is exported */
		vfsops->vfs_checkexp =	vfs_stdcheckexp;
	if (vfsops->vfs_init == NULL)
		/* file system specific initialisation */
		vfsops->vfs_init =	vfs_stdinit;
	if (vfsops->vfs_uninit == NULL)
		/* file system specific uninitialisation */
		vfsops->vfs_uninit =	vfs_stduninit;
	if (vfsops->vfs_extattrctl == NULL)
		/* extended attribute control */
		vfsops->vfs_extattrctl = vfs_stdextattrctl;
	if (vfsops->vfs_sysctl == NULL)
		vfsops->vfs_sysctl = vfs_stdsysctl;
	
	/*
	 * Call init function for this VFS...
	 */
	(*(vfc->vfc_vfsops->vfs_init))(vfc);

	return 0;
}