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