Home | History | Annotate | Download | only in ctfs
      1     0  stevel /*
      2     0  stevel  * CDDL HEADER START
      3     0  stevel  *
      4     0  stevel  * The contents of this file are subject to the terms of the
      5  3898     rsb  * Common Development and Distribution License (the "License").
      6  3898     rsb  * You may not use this file except in compliance with the License.
      7     0  stevel  *
      8     0  stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9     0  stevel  * or http://www.opensolaris.org/os/licensing.
     10     0  stevel  * See the License for the specific language governing permissions
     11     0  stevel  * and limitations under the License.
     12     0  stevel  *
     13     0  stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14     0  stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15     0  stevel  * If applicable, add the following below this CDDL HEADER, with the
     16     0  stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17     0  stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18     0  stevel  *
     19     0  stevel  * CDDL HEADER END
     20     0  stevel  */
     21     0  stevel /*
     22  3898     rsb  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23     0  stevel  * Use is subject to license terms.
     24     0  stevel  */
     25     0  stevel 
     26     0  stevel #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27     0  stevel 
     28     0  stevel #include <sys/modctl.h>
     29     0  stevel #include <sys/types.h>
     30     0  stevel #include <sys/param.h>
     31     0  stevel #include <sys/time.h>
     32     0  stevel #include <sys/cred.h>
     33     0  stevel #include <sys/vfs.h>
     34  3898     rsb #include <sys/vfs_opreg.h>
     35     0  stevel #include <sys/gfs.h>
     36     0  stevel #include <sys/vnode.h>
     37     0  stevel #include <sys/systm.h>
     38     0  stevel #include <sys/cmn_err.h>
     39     0  stevel #include <sys/errno.h>
     40     0  stevel #include <sys/sysmacros.h>
     41     0  stevel #include <sys/policy.h>
     42     0  stevel #include <sys/mount.h>
     43     0  stevel #include <sys/pathname.h>
     44     0  stevel #include <sys/dirent.h>
     45     0  stevel #include <fs/fs_subr.h>
     46     0  stevel #include <sys/contract.h>
     47     0  stevel #include <sys/contract_impl.h>
     48     0  stevel #include <sys/ctfs.h>
     49     0  stevel #include <sys/ctfs_impl.h>
     50     0  stevel #include <sys/uio.h>
     51     0  stevel #include <sys/file.h>
     52     0  stevel #include <sys/atomic.h>
     53     0  stevel #include <sys/sunddi.h>
     54     0  stevel 
     55     0  stevel /*
     56     0  stevel  * ctfs, the contract filesystem.
     57     0  stevel  *
     58  5331     amw  * Exposes the construct subsystem to userland.  The structure of the
     59     0  stevel  * filesytem is a public interface, but the behavior of the files is
     60     0  stevel  * private and unstable.  Contract consumers are expected to use
     61     0  stevel  * libcontract(3lib) to operate on ctfs file descriptors.
     62     0  stevel  *
     63     0  stevel  * We're trying something a little different here.  Rather than make
     64     0  stevel  * each vnode op itself call into a vector of file type operations, we
     65     0  stevel  * actually use different vnode types (gasp!), the implementations of
     66     0  stevel  * which may call into routines providing common functionality.  This
     67     0  stevel  * design should hopefully make it easier to factor and maintain the
     68     0  stevel  * code.  For the most part, there is a separate file for each vnode
     69     0  stevel  * type's implementation.  The exceptions to this are the ctl/stat
     70     0  stevel  * nodes, which are very similar, and the three event endpoint types.
     71     0  stevel  *
     72     0  stevel  * This file contains common routines used by some or all of the vnode
     73     0  stevel  * types, the filesystem's module linkage and VFS operations, and the
     74     0  stevel  * implementation of the root vnode.
     75     0  stevel  */
     76     0  stevel 
     77     0  stevel /*
     78     0  stevel  * Ops vectors for all the vnode types; they have to be defined
     79     0  stevel  * somewhere.  See gfs_make_opsvec for thoughts on how this could be
     80     0  stevel  * done differently.
     81     0  stevel  */
     82     0  stevel vnodeops_t *ctfs_ops_root;
     83     0  stevel vnodeops_t *ctfs_ops_adir;
     84     0  stevel vnodeops_t *ctfs_ops_sym;
     85     0  stevel vnodeops_t *ctfs_ops_tdir;
     86     0  stevel vnodeops_t *ctfs_ops_tmpl;
     87     0  stevel vnodeops_t *ctfs_ops_cdir;
     88     0  stevel vnodeops_t *ctfs_ops_ctl;
     89     0  stevel vnodeops_t *ctfs_ops_stat;
     90     0  stevel vnodeops_t *ctfs_ops_event;
     91     0  stevel vnodeops_t *ctfs_ops_bundle;
     92     0  stevel vnodeops_t *ctfs_ops_latest;
     93     0  stevel 
     94     0  stevel static const fs_operation_def_t ctfs_vfstops[];
     95     0  stevel static gfs_opsvec_t ctfs_opsvec[];
     96     0  stevel 
     97     0  stevel static int ctfs_init(int, char *);
     98     0  stevel 
     99     0  stevel static ino64_t ctfs_root_do_inode(vnode_t *, int);
    100     0  stevel 
    101     0  stevel 
    102     0  stevel /*
    103     0  stevel  * File system module linkage
    104     0  stevel  */
    105     0  stevel static mntopts_t ctfs_mntopts = {
    106     0  stevel 	0,
    107     0  stevel 	NULL
    108     0  stevel };
    109     0  stevel 
    110     0  stevel static vfsdef_t vfw = {
    111     0  stevel 	VFSDEF_VERSION,
    112     0  stevel 	"ctfs",
    113     0  stevel 	ctfs_init,
    114     0  stevel 	VSW_HASPROTO,
    115     0  stevel 	&ctfs_mntopts,
    116     0  stevel };
    117     0  stevel 
    118     0  stevel extern struct mod_ops mod_fsops;
    119     0  stevel 
    120     0  stevel static struct modlfs modlfs = {
    121     0  stevel 	&mod_fsops, "contract filesystem", &vfw
    122     0  stevel };
    123     0  stevel 
    124     0  stevel static struct modlinkage modlinkage = {
    125     0  stevel 	MODREV_1, (void *)&modlfs, NULL
    126     0  stevel };
    127     0  stevel 
    128     0  stevel int
    129     0  stevel _init(void)
    130     0  stevel {
    131     0  stevel 	return (mod_install(&modlinkage));
    132     0  stevel }
    133     0  stevel 
    134     0  stevel int
    135     0  stevel _info(struct modinfo *modinfop)
    136     0  stevel {
    137     0  stevel 	return (mod_info(&modlinkage, modinfop));
    138     0  stevel }
    139     0  stevel 
    140     0  stevel int
    141     0  stevel _fini(void)
    142     0  stevel {
    143     0  stevel 	/*
    144     0  stevel 	 * As unloading filesystem modules isn't completely safe, we
    145     0  stevel 	 * don't allow it.
    146     0  stevel 	 */
    147     0  stevel 	return (EBUSY);
    148     0  stevel }
    149     0  stevel 
    150     0  stevel static int ctfs_fstype;
    151     0  stevel static major_t ctfs_major;
    152     0  stevel static minor_t ctfs_minor = 0;
    153     0  stevel 
    154     0  stevel /*
    155     0  stevel  * The ops vector vector.
    156     0  stevel  */
    157     0  stevel static const fs_operation_def_t ctfs_tops_root[];
    158     0  stevel extern const fs_operation_def_t ctfs_tops_tmpl[];
    159     0  stevel extern const fs_operation_def_t ctfs_tops_ctl[];
    160     0  stevel extern const fs_operation_def_t ctfs_tops_adir[];
    161     0  stevel extern const fs_operation_def_t ctfs_tops_cdir[];
    162     0  stevel extern const fs_operation_def_t ctfs_tops_tdir[];
    163     0  stevel extern const fs_operation_def_t ctfs_tops_latest[];
    164     0  stevel extern const fs_operation_def_t ctfs_tops_stat[];
    165     0  stevel extern const fs_operation_def_t ctfs_tops_sym[];
    166     0  stevel extern const fs_operation_def_t ctfs_tops_event[];
    167     0  stevel extern const fs_operation_def_t ctfs_tops_bundle[];
    168     0  stevel static gfs_opsvec_t ctfs_opsvec[] = {
    169     0  stevel 	{ "ctfs root directory", ctfs_tops_root, &ctfs_ops_root },
    170     0  stevel 	{ "ctfs all directory", ctfs_tops_adir, &ctfs_ops_adir },
    171     0  stevel 	{ "ctfs all symlink", ctfs_tops_sym, &ctfs_ops_sym },
    172     0  stevel 	{ "ctfs template directory", ctfs_tops_tdir, &ctfs_ops_tdir },
    173     0  stevel 	{ "ctfs template file", ctfs_tops_tmpl, &ctfs_ops_tmpl },
    174     0  stevel 	{ "ctfs contract directory", ctfs_tops_cdir, &ctfs_ops_cdir },
    175     0  stevel 	{ "ctfs ctl file", ctfs_tops_ctl, &ctfs_ops_ctl },
    176     0  stevel 	{ "ctfs status file", ctfs_tops_stat, &ctfs_ops_stat },
    177     0  stevel 	{ "ctfs events file", ctfs_tops_event, &ctfs_ops_event },
    178     0  stevel 	{ "ctfs bundle file", ctfs_tops_bundle, &ctfs_ops_bundle },
    179     0  stevel 	{ "ctfs latest file", ctfs_tops_latest, &ctfs_ops_latest },
    180     0  stevel 	{ NULL }
    181     0  stevel };
    182     0  stevel 
    183     0  stevel 
    184     0  stevel /*
    185     0  stevel  * ctfs_init - the vfsdef_t init entry point
    186     0  stevel  *
    187     0  stevel  * Sets the VFS ops, builds all the vnode ops, and allocates a device
    188     0  stevel  * number.
    189     0  stevel  */
    190     0  stevel /* ARGSUSED */
    191     0  stevel static int
    192     0  stevel ctfs_init(int fstype, char *name)
    193     0  stevel {
    194     0  stevel 	vfsops_t *vfsops;
    195     0  stevel 	int error;
    196     0  stevel 
    197     0  stevel 	ctfs_fstype = fstype;
    198     0  stevel 	if (error = vfs_setfsops(fstype, ctfs_vfstops, &vfsops)) {
    199     0  stevel 		cmn_err(CE_WARN, "ctfs_init: bad vfs ops template");
    200     0  stevel 		return (error);
    201     0  stevel 	}
    202     0  stevel 
    203     0  stevel 	if (error = gfs_make_opsvec(ctfs_opsvec)) {
    204     0  stevel 		(void) vfs_freevfsops(vfsops);
    205     0  stevel 		return (error);
    206     0  stevel 	}
    207     0  stevel 
    208     0  stevel 	if ((ctfs_major = getudev()) == (major_t)-1) {
    209     0  stevel 		cmn_err(CE_WARN, "ctfs_init: can't get unique device number");
    210     0  stevel 		ctfs_major = 0;
    211     0  stevel 	}
    212     0  stevel 
    213     0  stevel 	return (0);
    214     0  stevel }
    215     0  stevel 
    216     0  stevel /*
    217     0  stevel  * ctfs_mount - the VFS_MOUNT entry point
    218     0  stevel  */
    219     0  stevel static int
    220     0  stevel ctfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
    221     0  stevel {
    222     0  stevel 	ctfs_vfs_t *data;
    223     0  stevel 	dev_t dev;
    224     0  stevel 	gfs_dirent_t *dirent;
    225     0  stevel 	int i;
    226     0  stevel 
    227     0  stevel 	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
    228     0  stevel 		return (EPERM);
    229     0  stevel 
    230     0  stevel 	if (mvp->v_type != VDIR)
    231     0  stevel 		return (ENOTDIR);
    232     0  stevel 
    233     0  stevel 	if ((uap->flags & MS_OVERLAY) == 0 &&
    234     0  stevel 	    (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
    235     0  stevel 		return (EBUSY);
    236     0  stevel 
    237     0  stevel 	data = kmem_alloc(sizeof (ctfs_vfs_t), KM_SLEEP);
    238     0  stevel 
    239     0  stevel 	/*
    240     0  stevel 	 * Initialize vfs fields not initialized by VFS_INIT/domount
    241     0  stevel 	 */
    242     0  stevel 	vfsp->vfs_bsize = DEV_BSIZE;
    243     0  stevel 	vfsp->vfs_fstype = ctfs_fstype;
    244     0  stevel 	do
    245     0  stevel 		dev = makedevice(ctfs_major,
    246     0  stevel 		    atomic_add_32_nv(&ctfs_minor, 1) & L_MAXMIN32);
    247     0  stevel 	while (vfs_devismounted(dev));
    248     0  stevel 	vfs_make_fsid(&vfsp->vfs_fsid, dev, ctfs_fstype);
    249     0  stevel 	vfsp->vfs_data = data;
    250     0  stevel 	vfsp->vfs_dev = dev;
    251     0  stevel 
    252     0  stevel 	/*
    253     0  stevel 	 * Dynamically create gfs_dirent_t array for the root directory.
    254     0  stevel 	 */
    255     0  stevel 	dirent = kmem_zalloc((ct_ntypes + 2) * sizeof (gfs_dirent_t), KM_SLEEP);
    256     0  stevel 	for (i = 0; i < ct_ntypes; i++) {
    257     0  stevel 		dirent[i].gfse_name = (char *)ct_types[i]->ct_type_name;
    258     0  stevel 		dirent[i].gfse_ctor = ctfs_create_tdirnode;
    259     0  stevel 		dirent[i].gfse_flags = GFS_CACHE_VNODE;
    260     0  stevel 	}
    261     0  stevel 	dirent[i].gfse_name = "all";
    262     0  stevel 	dirent[i].gfse_ctor = ctfs_create_adirnode;
    263     0  stevel 	dirent[i].gfse_flags = GFS_CACHE_VNODE;
    264     0  stevel 	dirent[i+1].gfse_name = NULL;
    265     0  stevel 
    266     0  stevel 	/*
    267     0  stevel 	 * Create root vnode
    268     0  stevel 	 */
    269     0  stevel 	data->ctvfs_root = gfs_root_create(sizeof (ctfs_rootnode_t),
    270     0  stevel 	    vfsp, ctfs_ops_root, CTFS_INO_ROOT, dirent, ctfs_root_do_inode,
    271     0  stevel 	    CTFS_NAME_MAX, NULL, NULL);
    272     0  stevel 
    273     0  stevel 	kmem_free(dirent, (ct_ntypes + 2) * sizeof (gfs_dirent_t));
    274     0  stevel 
    275     0  stevel 	return (0);
    276     0  stevel }
    277     0  stevel 
    278     0  stevel /*
    279     0  stevel  * ctfs_unmount - the VFS_UNMOUNT entry point
    280     0  stevel  */
    281     0  stevel static int
    282     0  stevel ctfs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
    283     0  stevel {
    284     0  stevel 	ctfs_vfs_t *data;
    285     0  stevel 
    286     0  stevel 	if (secpolicy_fs_unmount(cr, vfsp) != 0)
    287     0  stevel 		return (EPERM);
    288     0  stevel 
    289     0  stevel 	/*
    290     0  stevel 	 * Supporting forced unmounts would be nice to do at some
    291     0  stevel 	 * point.
    292     0  stevel 	 */
    293     0  stevel 	if (flag & MS_FORCE)
    294     0  stevel 		return (ENOTSUP);
    295     0  stevel 
    296     0  stevel 	/*
    297     0  stevel 	 * We should never have a reference count less than 2: one for
    298     0  stevel 	 * the caller, one for the root vnode.
    299     0  stevel 	 */
    300     0  stevel 	ASSERT(vfsp->vfs_count >= 2);
    301     0  stevel 
    302     0  stevel 	/*
    303     0  stevel 	 * If we have any active vnodes, they will (transitively) have
    304     0  stevel 	 * holds on the root vnode.
    305     0  stevel 	 */
    306     0  stevel 	data = vfsp->vfs_data;
    307     0  stevel 	if (data->ctvfs_root->v_count > 1)
    308     0  stevel 		return (EBUSY);
    309     0  stevel 
    310     0  stevel 	/*
    311     0  stevel 	 * Release the last hold on the root vnode.  It will, in turn,
    312     0  stevel 	 * release its hold on us.
    313     0  stevel 	 */
    314     0  stevel 	VN_RELE(data->ctvfs_root);
    315     0  stevel 
    316     0  stevel 	/*
    317     0  stevel 	 * Disappear.
    318     0  stevel 	 */
    319     0  stevel 	kmem_free(data, sizeof (ctfs_vfs_t));
    320     0  stevel 
    321     0  stevel 	return (0);
    322     0  stevel }
    323     0  stevel 
    324     0  stevel /*
    325     0  stevel  * ctfs_root - the VFS_ROOT entry point
    326     0  stevel  */
    327     0  stevel static int
    328     0  stevel ctfs_root(vfs_t *vfsp, vnode_t **vpp)
    329     0  stevel {
    330     0  stevel 	vnode_t *vp;
    331     0  stevel 
    332     0  stevel 	vp = ((ctfs_vfs_t *)vfsp->vfs_data)->ctvfs_root;
    333     0  stevel 	VN_HOLD(vp);
    334     0  stevel 	*vpp = vp;
    335     0  stevel 
    336     0  stevel 	return (0);
    337     0  stevel }
    338     0  stevel 
    339     0  stevel /*
    340     0  stevel  * ctfs_statvfs - the VFS_STATVFS entry point
    341     0  stevel  */
    342     0  stevel static int
    343     0  stevel ctfs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
    344     0  stevel {
    345     0  stevel 	dev32_t	d32;
    346     0  stevel 	int	total, i;
    347     0  stevel 
    348     0  stevel 	bzero(sp, sizeof (*sp));
    349     0  stevel 	sp->f_bsize = DEV_BSIZE;
    350     0  stevel 	sp->f_frsize = DEV_BSIZE;
    351     0  stevel 	for (i = 0, total = 0; i < ct_ntypes; i++)
    352     0  stevel 		total += contract_type_count(ct_types[i]);
    353     0  stevel 	sp->f_files = total;
    354     0  stevel 	sp->f_favail = sp->f_ffree = INT_MAX - total;
    355     0  stevel 	(void) cmpldev(&d32, vfsp->vfs_dev);
    356     0  stevel 	sp->f_fsid = d32;
    357     0  stevel 	(void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
    358     0  stevel 	    sizeof (sp->f_basetype));
    359     0  stevel 	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
    360     0  stevel 	sp->f_namemax = CTFS_NAME_MAX;
    361     0  stevel 	(void) strlcpy(sp->f_fstr, "contract", sizeof (sp->f_fstr));
    362     0  stevel 
    363     0  stevel 	return (0);
    364     0  stevel }
    365     0  stevel 
    366     0  stevel static const fs_operation_def_t ctfs_vfstops[] = {
    367  3898     rsb 	{ VFSNAME_MOUNT,	{ .vfs_mount = ctfs_mount } },
    368  3898     rsb 	{ VFSNAME_UNMOUNT,	{ .vfs_unmount = ctfs_unmount } },
    369  3898     rsb 	{ VFSNAME_ROOT,		{ .vfs_root = ctfs_root } },
    370  3898     rsb 	{ VFSNAME_STATVFS,	{ .vfs_statvfs = ctfs_statvfs } },
    371     0  stevel 	{ NULL, NULL }
    372     0  stevel };
    373     0  stevel 
    374     0  stevel /*
    375     0  stevel  * ctfs_common_getattr
    376     0  stevel  *
    377     0  stevel  * Implements functionality common to all ctfs VOP_GETATTR entry
    378     0  stevel  * points.  It assumes vap->va_size is set.
    379     0  stevel  */
    380     0  stevel void
    381     0  stevel ctfs_common_getattr(vnode_t *vp, vattr_t *vap)
    382     0  stevel {
    383     0  stevel 	vap->va_uid = 0;
    384     0  stevel 	vap->va_gid = 0;
    385     0  stevel 	vap->va_rdev = 0;
    386     0  stevel 	vap->va_blksize = DEV_BSIZE;
    387     0  stevel 	vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
    388     0  stevel 	vap->va_seq = 0;
    389     0  stevel 	vap->va_fsid = vp->v_vfsp->vfs_dev;
    390     0  stevel 	vap->va_nodeid = gfs_file_inode(vp);
    391     0  stevel }
    392     0  stevel 
    393     0  stevel /*
    394     0  stevel  * ctfs_open - common VOP_OPEN entry point
    395     0  stevel  *
    396     0  stevel  * Used by all ctfs directories; just verifies we are using large-file
    397     0  stevel  * aware interfaces and we aren't trying to open the directories
    398     0  stevel  * writable.
    399     0  stevel  */
    400     0  stevel /* ARGSUSED */
    401     0  stevel int
    402  5331     amw ctfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
    403     0  stevel {
    404     0  stevel 	if ((flag & (FOFFMAX | FWRITE)) != FOFFMAX)
    405     0  stevel 		return (EINVAL);
    406     0  stevel 
    407     0  stevel 	return (0);
    408     0  stevel }
    409     0  stevel 
    410     0  stevel /*
    411     0  stevel  * ctfs_close - common VOP_CLOSE entry point
    412     0  stevel  *
    413     0  stevel  * For all ctfs vnode types which have no close-time clean-up to do.
    414     0  stevel  */
    415     0  stevel /* ARGSUSED */
    416     0  stevel int
    417  5331     amw ctfs_close(
    418  5331     amw 	vnode_t *vp,
    419  5331     amw 	int flag,
    420  5331     amw 	int count,
    421  5331     amw 	offset_t offset,
    422  5331     amw 	cred_t *cr,
    423  5331     amw 	caller_context_t *ct)
    424     0  stevel {
    425     0  stevel 	return (0);
    426     0  stevel }
    427     0  stevel 
    428     0  stevel /*
    429     0  stevel  * ctfs_access_dir - common VOP_ACCESS entry point for directories
    430     0  stevel  */
    431     0  stevel /* ARGSUSED */
    432     0  stevel int
    433  5331     amw ctfs_access_dir(
    434  5331     amw 	vnode_t *vp,
    435  5331     amw 	int mode,
    436  5331     amw 	int flags,
    437  5331     amw 	cred_t *cr,
    438  5331     amw 	caller_context_t *ct)
    439     0  stevel {
    440     0  stevel 	if (mode & VWRITE)
    441     0  stevel 		return (EACCES);
    442     0  stevel 
    443     0  stevel 	return (0);
    444     0  stevel }
    445     0  stevel 
    446     0  stevel /*
    447     0  stevel  * ctfs_access_dir - common VOP_ACCESS entry point for read-only files
    448     0  stevel  */
    449     0  stevel /* ARGSUSED */
    450     0  stevel int
    451  5331     amw ctfs_access_readonly(
    452  5331     amw 	vnode_t *vp,
    453  5331     amw 	int mode,
    454  5331     amw 	int flags,
    455  5331     amw 	cred_t *cr,
    456  5331     amw 	caller_context_t *ct)
    457     0  stevel {
    458     0  stevel 	if (mode & (VWRITE | VEXEC))
    459     0  stevel 		return (EACCES);
    460     0  stevel 
    461     0  stevel 	return (0);
    462     0  stevel }
    463     0  stevel 
    464     0  stevel /*
    465     0  stevel  * ctfs_access_dir - common VOP_ACCESS entry point for read-write files
    466     0  stevel  */
    467     0  stevel /* ARGSUSED */
    468     0  stevel int
    469  5331     amw ctfs_access_readwrite(
    470  5331     amw 	vnode_t *vp,
    471  5331     amw 	int mode,
    472  5331     amw 	int flags,
    473  5331     amw 	cred_t *cr,
    474  5331     amw 	caller_context_t *ct)
    475     0  stevel {
    476     0  stevel 	if (mode & VEXEC)
    477     0  stevel 		return (EACCES);
    478     0  stevel 
    479     0  stevel 	return (0);
    480     0  stevel }
    481     0  stevel 
    482     0  stevel /*
    483     0  stevel  * ctfs_root_getattr - VOP_GETATTR entry point
    484     0  stevel  */
    485     0  stevel /* ARGSUSED */
    486     0  stevel static int
    487  5331     amw ctfs_root_getattr(
    488  5331     amw 	vnode_t *vp,
    489  5331     amw 	vattr_t *vap,
    490  5331     amw 	int flags,
    491  5331     amw 	cred_t *cr,
    492  5331     amw 	caller_context_t *ct)
    493     0  stevel {
    494     0  stevel 	vap->va_type = VDIR;
    495     0  stevel 	vap->va_mode = 0555;
    496     0  stevel 	vap->va_nlink = 2 + ct_ntypes + 1;
    497     0  stevel 	vap->va_size = vap->va_nlink;
    498     0  stevel 	vap->va_atime.tv_sec = vp->v_vfsp->vfs_mtime;
    499     0  stevel 	vap->va_atime.tv_nsec = 0;
    500     0  stevel 	vap->va_mtime = vap->va_ctime = vap->va_atime;
    501     0  stevel 	ctfs_common_getattr(vp, vap);
    502     0  stevel 
    503     0  stevel 	return (0);
    504     0  stevel }
    505     0  stevel 
    506     0  stevel /* ARGSUSED */
    507     0  stevel static ino64_t
    508     0  stevel ctfs_root_do_inode(vnode_t *vp, int index)
    509     0  stevel {
    510     0  stevel 	return (CTFS_INO_TYPE_DIR(index));
    511     0  stevel }
    512     0  stevel 
    513     0  stevel static const fs_operation_def_t ctfs_tops_root[] = {
    514  3898     rsb 	{ VOPNAME_OPEN,		{ .vop_open = ctfs_open } },
    515  3898     rsb 	{ VOPNAME_CLOSE,	{ .vop_close = ctfs_close } },
    516  3898     rsb 	{ VOPNAME_IOCTL,	{ .error = fs_inval } },
    517  3898     rsb 	{ VOPNAME_GETATTR,	{ .vop_getattr = ctfs_root_getattr } },
    518  3898     rsb 	{ VOPNAME_ACCESS,	{ .vop_access = ctfs_access_dir } },
    519  3898     rsb 	{ VOPNAME_READDIR,	{ .vop_readdir = gfs_vop_readdir } },
    520  3898     rsb 	{ VOPNAME_LOOKUP,	{ .vop_lookup = gfs_vop_lookup } },
    521  3898     rsb 	{ VOPNAME_SEEK,		{ .vop_seek = fs_seek } },
    522  3898     rsb 	{ VOPNAME_INACTIVE,	{ .vop_inactive = gfs_vop_inactive } },
    523     0  stevel 	{ NULL, NULL }
    524     0  stevel };
    525