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