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