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 constract 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) 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(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr) 418 { 419 return (0); 420 } 421 422 /* 423 * ctfs_access_dir - common VOP_ACCESS entry point for directories 424 */ 425 /* ARGSUSED */ 426 int 427 ctfs_access_dir(vnode_t *vp, int mode, int flags, cred_t *cr) 428 { 429 if (mode & VWRITE) 430 return (EACCES); 431 432 return (0); 433 } 434 435 /* 436 * ctfs_access_dir - common VOP_ACCESS entry point for read-only files 437 */ 438 /* ARGSUSED */ 439 int 440 ctfs_access_readonly(vnode_t *vp, int mode, int flags, cred_t *cr) 441 { 442 if (mode & (VWRITE | VEXEC)) 443 return (EACCES); 444 445 return (0); 446 } 447 448 /* 449 * ctfs_access_dir - common VOP_ACCESS entry point for read-write files 450 */ 451 /* ARGSUSED */ 452 int 453 ctfs_access_readwrite(vnode_t *vp, int mode, int flags, cred_t *cr) 454 { 455 if (mode & VEXEC) 456 return (EACCES); 457 458 return (0); 459 } 460 461 /* 462 * ctfs_root_getattr - VOP_GETATTR entry point 463 */ 464 /* ARGSUSED */ 465 static int 466 ctfs_root_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr) 467 { 468 vap->va_type = VDIR; 469 vap->va_mode = 0555; 470 vap->va_nlink = 2 + ct_ntypes + 1; 471 vap->va_size = vap->va_nlink; 472 vap->va_atime.tv_sec = vp->v_vfsp->vfs_mtime; 473 vap->va_atime.tv_nsec = 0; 474 vap->va_mtime = vap->va_ctime = vap->va_atime; 475 ctfs_common_getattr(vp, vap); 476 477 return (0); 478 } 479 480 /* ARGSUSED */ 481 static ino64_t 482 ctfs_root_do_inode(vnode_t *vp, int index) 483 { 484 return (CTFS_INO_TYPE_DIR(index)); 485 } 486 487 static const fs_operation_def_t ctfs_tops_root[] = { 488 { VOPNAME_OPEN, { .vop_open = ctfs_open } }, 489 { VOPNAME_CLOSE, { .vop_close = ctfs_close } }, 490 { VOPNAME_IOCTL, { .error = fs_inval } }, 491 { VOPNAME_GETATTR, { .vop_getattr = ctfs_root_getattr } }, 492 { VOPNAME_ACCESS, { .vop_access = ctfs_access_dir } }, 493 { VOPNAME_READDIR, { .vop_readdir = gfs_vop_readdir } }, 494 { VOPNAME_LOOKUP, { .vop_lookup = gfs_vop_lookup } }, 495 { VOPNAME_SEEK, { .vop_seek = fs_seek } }, 496 { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } }, 497 { NULL, NULL } 498 }; 499