xref: /illumos-gate/usr/src/uts/common/fs/ctfs/ctfs_root.c (revision 1ed6b69a5ca1ca3ee5e9a4931f74e2237c7e1c9f)
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  */
79 vnodeops_t *ctfs_ops_root;
80 vnodeops_t *ctfs_ops_adir;
81 vnodeops_t *ctfs_ops_sym;
82 vnodeops_t *ctfs_ops_tdir;
83 vnodeops_t *ctfs_ops_tmpl;
84 vnodeops_t *ctfs_ops_cdir;
85 vnodeops_t *ctfs_ops_ctl;
86 vnodeops_t *ctfs_ops_stat;
87 vnodeops_t *ctfs_ops_event;
88 vnodeops_t *ctfs_ops_bundle;
89 vnodeops_t *ctfs_ops_latest;
90 
91 static const fs_operation_def_t ctfs_vfstops[];
92 static gfs_opsvec_t ctfs_opsvec[];
93 
94 static int ctfs_init(int, char *);
95 
96 static ino64_t ctfs_root_do_inode(vnode_t *, int);
97 
98 
99 /*
100  * File system module linkage
101  */
102 static mntopts_t ctfs_mntopts = {
103 	0,
104 	NULL
105 };
106 
107 static vfsdef_t vfw = {
108 	VFSDEF_VERSION,
109 	"ctfs",
110 	ctfs_init,
111 	VSW_HASPROTO|VSW_ZMOUNT,
112 	&ctfs_mntopts,
113 };
114 
115 extern struct mod_ops mod_fsops;
116 
117 static struct modlfs modlfs = {
118 	&mod_fsops, "contract filesystem", &vfw
119 };
120 
121 static struct modlinkage modlinkage = {
122 	MODREV_1, (void *)&modlfs, NULL
123 };
124 
125 int
126 _init(void)
127 {
128 	return (mod_install(&modlinkage));
129 }
130 
131 int
132 _info(struct modinfo *modinfop)
133 {
134 	return (mod_info(&modlinkage, modinfop));
135 }
136 
137 int
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 
147 static int ctfs_fstype;
148 static major_t ctfs_major;
149 static minor_t ctfs_minor = 0;
150 
151 /*
152  * The ops vector vector.
153  */
154 static const fs_operation_def_t ctfs_tops_root[];
155 extern const fs_operation_def_t ctfs_tops_tmpl[];
156 extern const fs_operation_def_t ctfs_tops_ctl[];
157 extern const fs_operation_def_t ctfs_tops_adir[];
158 extern const fs_operation_def_t ctfs_tops_cdir[];
159 extern const fs_operation_def_t ctfs_tops_tdir[];
160 extern const fs_operation_def_t ctfs_tops_latest[];
161 extern const fs_operation_def_t ctfs_tops_stat[];
162 extern const fs_operation_def_t ctfs_tops_sym[];
163 extern const fs_operation_def_t ctfs_tops_event[];
164 extern const fs_operation_def_t ctfs_tops_bundle[];
165 static 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 */
188 static int
189 ctfs_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  */
216 static int
217 ctfs_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_inc_32_nv(&ctfs_minor) & 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  */
278 static int
279 ctfs_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  */
324 static int
325 ctfs_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  */
339 static int
340 ctfs_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 
363 static 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  */
377 void
378 ctfs_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 */
398 int
399 ctfs_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 */
413 int
414 ctfs_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 */
429 int
430 ctfs_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 */
447 int
448 ctfs_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 */
465 int
466 ctfs_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 */
483 static int
484 ctfs_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 */
504 static ino64_t
505 ctfs_root_do_inode(vnode_t *vp, int index)
506 {
507 	return (CTFS_INO_TYPE_DIR(index));
508 }
509 
510 static 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