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
_init(void)126 _init(void)
127 {
128 return (mod_install(&modlinkage));
129 }
130
131 int
_info(struct modinfo * modinfop)132 _info(struct modinfo *modinfop)
133 {
134 return (mod_info(&modlinkage, modinfop));
135 }
136
137 int
_fini(void)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
ctfs_init(int fstype,char * name)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
ctfs_mount(vfs_t * vfsp,vnode_t * mvp,struct mounta * uap,cred_t * cr)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
ctfs_unmount(vfs_t * vfsp,int flag,struct cred * cr)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
ctfs_root(vfs_t * vfsp,vnode_t ** vpp)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
ctfs_statvfs(vfs_t * vfsp,statvfs64_t * sp)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
ctfs_common_getattr(vnode_t * vp,vattr_t * vap)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
ctfs_open(vnode_t ** vpp,int flag,cred_t * cr,caller_context_t * ct)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
ctfs_close(vnode_t * vp,int flag,int count,offset_t offset,cred_t * cr,caller_context_t * ct)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
ctfs_access_dir(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)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
ctfs_access_readonly(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)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
ctfs_access_readwrite(vnode_t * vp,int mode,int flags,cred_t * cr,caller_context_t * ct)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
ctfs_root_getattr(vnode_t * vp,vattr_t * vap,int flags,cred_t * cr,caller_context_t * ct)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
ctfs_root_do_inode(vnode_t * vp,int index)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