xref: /illumos-gate/usr/src/uts/common/fs/ctfs/ctfs_root.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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