1facf4a8dSllai1 /* 2facf4a8dSllai1 * CDDL HEADER START 3facf4a8dSllai1 * 4facf4a8dSllai1 * The contents of this file are subject to the terms of the 5facf4a8dSllai1 * Common Development and Distribution License (the "License"). 6facf4a8dSllai1 * You may not use this file except in compliance with the License. 7facf4a8dSllai1 * 8facf4a8dSllai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9facf4a8dSllai1 * or http://www.opensolaris.org/os/licensing. 10facf4a8dSllai1 * See the License for the specific language governing permissions 11facf4a8dSllai1 * and limitations under the License. 12facf4a8dSllai1 * 13facf4a8dSllai1 * When distributing Covered Code, include this CDDL HEADER in each 14facf4a8dSllai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15facf4a8dSllai1 * If applicable, add the following below this CDDL HEADER, with the 16facf4a8dSllai1 * fields enclosed by brackets "[]" replaced with your own identifying 17facf4a8dSllai1 * information: Portions Copyright [yyyy] [name of copyright owner] 18facf4a8dSllai1 * 19facf4a8dSllai1 * CDDL HEADER END 20facf4a8dSllai1 */ 21facf4a8dSllai1 /* 22aac43a5fSjg * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23facf4a8dSllai1 * Use is subject to license terms. 24facf4a8dSllai1 */ 25facf4a8dSllai1 26facf4a8dSllai1 /* 27facf4a8dSllai1 * vnode ops for the /dev/pts directory 28facf4a8dSllai1 * The lookup is based on the internal pty table. We also 29facf4a8dSllai1 * override readdir in order to delete pts nodes no longer 30facf4a8dSllai1 * in use. 31facf4a8dSllai1 */ 32facf4a8dSllai1 33facf4a8dSllai1 #include <sys/types.h> 34facf4a8dSllai1 #include <sys/param.h> 35facf4a8dSllai1 #include <sys/sysmacros.h> 36facf4a8dSllai1 #include <sys/sunndi.h> 37facf4a8dSllai1 #include <fs/fs_subr.h> 38facf4a8dSllai1 #include <sys/fs/dv_node.h> 39facf4a8dSllai1 #include <sys/fs/sdev_impl.h> 40facf4a8dSllai1 #include <sys/policy.h> 41facf4a8dSllai1 #include <sys/ptms.h> 42facf4a8dSllai1 #include <sys/stat.h> 43aa59c4cbSrsb #include <sys/vfs_opreg.h> 44facf4a8dSllai1 45facf4a8dSllai1 #define DEVPTS_UID_DEFAULT 0 46facf4a8dSllai1 #define DEVPTS_GID_DEFAULT 3 47facf4a8dSllai1 #define DEVPTS_DEVMODE_DEFAULT (0620) 48facf4a8dSllai1 49facf4a8dSllai1 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 50facf4a8dSllai1 51facf4a8dSllai1 static vattr_t devpts_vattr = { 52facf4a8dSllai1 AT_TYPE|AT_MODE|AT_UID|AT_GID, /* va_mask */ 53facf4a8dSllai1 VCHR, /* va_type */ 54facf4a8dSllai1 S_IFCHR | DEVPTS_DEVMODE_DEFAULT, /* va_mode */ 55facf4a8dSllai1 DEVPTS_UID_DEFAULT, /* va_uid */ 56facf4a8dSllai1 DEVPTS_GID_DEFAULT, /* va_gid */ 57facf4a8dSllai1 0 /* 0 hereafter */ 58facf4a8dSllai1 }; 59facf4a8dSllai1 60facf4a8dSllai1 struct vnodeops *devpts_vnodeops; 61facf4a8dSllai1 62facf4a8dSllai1 struct vnodeops * 63facf4a8dSllai1 devpts_getvnodeops(void) 64facf4a8dSllai1 { 65facf4a8dSllai1 return (devpts_vnodeops); 66facf4a8dSllai1 } 67facf4a8dSllai1 68facf4a8dSllai1 /* 69facf4a8dSllai1 * Convert string to minor number. Some care must be taken 70facf4a8dSllai1 * as we are processing user input. Catch cases like 71facf4a8dSllai1 * /dev/pts/4foo and /dev/pts/-1 72facf4a8dSllai1 */ 73facf4a8dSllai1 static int 74facf4a8dSllai1 devpts_strtol(const char *nm, minor_t *mp) 75facf4a8dSllai1 { 76facf4a8dSllai1 long uminor = 0; 77facf4a8dSllai1 char *endptr = NULL; 78facf4a8dSllai1 79facf4a8dSllai1 if (nm == NULL || !isdigit(*nm)) 80facf4a8dSllai1 return (EINVAL); 81facf4a8dSllai1 82facf4a8dSllai1 *mp = 0; 83facf4a8dSllai1 if (ddi_strtol(nm, &endptr, 10, &uminor) != 0 || 84facf4a8dSllai1 *endptr != '\0' || uminor < 0) { 85facf4a8dSllai1 return (EINVAL); 86facf4a8dSllai1 } 87facf4a8dSllai1 88bc1009abSjg *mp = (minor_t)uminor; 89facf4a8dSllai1 return (0); 90facf4a8dSllai1 } 91facf4a8dSllai1 92facf4a8dSllai1 /* 93facf4a8dSllai1 * Check if a pts sdev_node is still valid - i.e. it represents a current pty. 94facf4a8dSllai1 * This serves two purposes 95facf4a8dSllai1 * - only valid pts nodes are returned during lookup() and readdir(). 96facf4a8dSllai1 * - since pts sdev_nodes are not actively destroyed when a pty goes 97facf4a8dSllai1 * away, we use the validator to do deferred cleanup i.e. when such 98facf4a8dSllai1 * nodes are encountered during subsequent lookup() and readdir(). 99facf4a8dSllai1 */ 100facf4a8dSllai1 /*ARGSUSED*/ 101facf4a8dSllai1 int 102facf4a8dSllai1 devpts_validate(struct sdev_node *dv) 103facf4a8dSllai1 { 104facf4a8dSllai1 minor_t min; 105facf4a8dSllai1 uid_t uid; 106facf4a8dSllai1 gid_t gid; 107facf4a8dSllai1 timestruc_t now; 108facf4a8dSllai1 char *nm = dv->sdev_name; 109facf4a8dSllai1 110facf4a8dSllai1 ASSERT(dv->sdev_state == SDEV_READY); 111facf4a8dSllai1 112facf4a8dSllai1 /* validate only READY nodes */ 113facf4a8dSllai1 if (dv->sdev_state != SDEV_READY) { 114facf4a8dSllai1 sdcmn_err(("dev fs: skipping: node not ready %s(%p)", 115facf4a8dSllai1 nm, (void *)dv)); 116facf4a8dSllai1 return (SDEV_VTOR_SKIP); 117facf4a8dSllai1 } 118facf4a8dSllai1 119facf4a8dSllai1 if (devpts_strtol(nm, &min) != 0) { 120facf4a8dSllai1 sdcmn_err7(("devpts_validate: not a valid minor: %s\n", nm)); 121facf4a8dSllai1 return (SDEV_VTOR_INVALID); 122facf4a8dSllai1 } 123facf4a8dSllai1 124facf4a8dSllai1 /* 125facf4a8dSllai1 * Check if pts driver is attached 126facf4a8dSllai1 */ 127facf4a8dSllai1 if (ptms_slave_attached() == (major_t)-1) { 128facf4a8dSllai1 sdcmn_err7(("devpts_validate: slave not attached\n")); 129facf4a8dSllai1 return (SDEV_VTOR_INVALID); 130facf4a8dSllai1 } 131facf4a8dSllai1 132facf4a8dSllai1 if (ptms_minor_valid(min, &uid, &gid) == 0) { 133facf4a8dSllai1 if (ptms_minor_exists(min)) { 134facf4a8dSllai1 sdcmn_err7(("devpts_validate: valid in different zone " 135facf4a8dSllai1 "%s\n", nm)); 136facf4a8dSllai1 return (SDEV_VTOR_SKIP); 137facf4a8dSllai1 } else { 138facf4a8dSllai1 sdcmn_err7(("devpts_validate: %s not valid pty\n", 139facf4a8dSllai1 nm)); 140facf4a8dSllai1 return (SDEV_VTOR_INVALID); 141facf4a8dSllai1 } 142facf4a8dSllai1 } 143facf4a8dSllai1 144facf4a8dSllai1 ASSERT(dv->sdev_attr); 145facf4a8dSllai1 if (dv->sdev_attr->va_uid != uid || dv->sdev_attr->va_gid != gid) { 146facf4a8dSllai1 dv->sdev_attr->va_uid = uid; 147facf4a8dSllai1 dv->sdev_attr->va_gid = gid; 148facf4a8dSllai1 gethrestime(&now); 149facf4a8dSllai1 dv->sdev_attr->va_atime = now; 150facf4a8dSllai1 dv->sdev_attr->va_mtime = now; 151facf4a8dSllai1 dv->sdev_attr->va_ctime = now; 152facf4a8dSllai1 sdcmn_err7(("devpts_validate: update uid/gid/times%s\n", nm)); 153facf4a8dSllai1 } 154facf4a8dSllai1 155facf4a8dSllai1 return (SDEV_VTOR_VALID); 156facf4a8dSllai1 } 157facf4a8dSllai1 158facf4a8dSllai1 /* 159facf4a8dSllai1 * This callback is invoked from devname_lookup_func() to create 160facf4a8dSllai1 * a pts entry when the node is not found in the cache. 161facf4a8dSllai1 */ 162facf4a8dSllai1 /*ARGSUSED*/ 163facf4a8dSllai1 static int 164facf4a8dSllai1 devpts_create_rvp(struct sdev_node *ddv, char *nm, 165facf4a8dSllai1 void **arg, cred_t *cred, void *whatever, char *whichever) 166facf4a8dSllai1 { 167facf4a8dSllai1 minor_t min; 168facf4a8dSllai1 major_t maj; 169facf4a8dSllai1 uid_t uid; 170facf4a8dSllai1 gid_t gid; 171facf4a8dSllai1 timestruc_t now; 172facf4a8dSllai1 struct vattr *vap = (struct vattr *)arg; 173facf4a8dSllai1 174facf4a8dSllai1 if (devpts_strtol(nm, &min) != 0) { 175facf4a8dSllai1 sdcmn_err7(("devpts_create_rvp: not a valid minor: %s\n", nm)); 176facf4a8dSllai1 return (-1); 177facf4a8dSllai1 } 178facf4a8dSllai1 179facf4a8dSllai1 /* 180facf4a8dSllai1 * Check if pts driver is attached and if it is 181facf4a8dSllai1 * get the major number. 182facf4a8dSllai1 */ 183facf4a8dSllai1 maj = ptms_slave_attached(); 184facf4a8dSllai1 if (maj == (major_t)-1) { 185facf4a8dSllai1 sdcmn_err7(("devpts_create_rvp: slave not attached\n")); 186facf4a8dSllai1 return (-1); 187facf4a8dSllai1 } 188facf4a8dSllai1 189facf4a8dSllai1 /* 190facf4a8dSllai1 * Only allow creation of ptys allocated to our zone 191facf4a8dSllai1 */ 192facf4a8dSllai1 if (!ptms_minor_valid(min, &uid, &gid)) { 193facf4a8dSllai1 sdcmn_err7(("devpts_create_rvp: %s not valid pty" 194facf4a8dSllai1 "or not valid in this zone\n", nm)); 195facf4a8dSllai1 return (-1); 196facf4a8dSllai1 } 197facf4a8dSllai1 198facf4a8dSllai1 199facf4a8dSllai1 /* 200facf4a8dSllai1 * This is a valid pty (at least at this point in time). 201facf4a8dSllai1 * Create the node by setting the attribute. The rest 202facf4a8dSllai1 * is taken care of by devname_lookup_func(). 203facf4a8dSllai1 */ 204facf4a8dSllai1 *vap = devpts_vattr; 205facf4a8dSllai1 vap->va_rdev = makedevice(maj, min); 206facf4a8dSllai1 vap->va_uid = uid; 207facf4a8dSllai1 vap->va_gid = gid; 208facf4a8dSllai1 gethrestime(&now); 209facf4a8dSllai1 vap->va_atime = now; 210facf4a8dSllai1 vap->va_mtime = now; 211facf4a8dSllai1 vap->va_ctime = now; 212facf4a8dSllai1 213facf4a8dSllai1 return (0); 214facf4a8dSllai1 } 215facf4a8dSllai1 216facf4a8dSllai1 /* 217facf4a8dSllai1 * Clean pts sdev_nodes that are no longer valid. 218facf4a8dSllai1 */ 219facf4a8dSllai1 static void 220facf4a8dSllai1 devpts_prunedir(struct sdev_node *ddv) 221facf4a8dSllai1 { 222facf4a8dSllai1 struct vnode *vp; 223facf4a8dSllai1 struct sdev_node *dv, *next = NULL; 224facf4a8dSllai1 int (*vtor)(struct sdev_node *) = NULL; 225facf4a8dSllai1 226facf4a8dSllai1 ASSERT(ddv->sdev_flags & SDEV_VTOR); 227facf4a8dSllai1 228facf4a8dSllai1 vtor = (int (*)(struct sdev_node *))sdev_get_vtor(ddv); 229facf4a8dSllai1 ASSERT(vtor); 230facf4a8dSllai1 231facf4a8dSllai1 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 232facf4a8dSllai1 rw_exit(&ddv->sdev_contents); 233facf4a8dSllai1 rw_enter(&ddv->sdev_contents, RW_WRITER); 234facf4a8dSllai1 } 235facf4a8dSllai1 236aac43a5fSjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 237aac43a5fSjg next = SDEV_NEXT_ENTRY(ddv, dv); 238facf4a8dSllai1 239facf4a8dSllai1 /* validate and prune only ready nodes */ 240facf4a8dSllai1 if (dv->sdev_state != SDEV_READY) 241facf4a8dSllai1 continue; 242facf4a8dSllai1 243facf4a8dSllai1 switch (vtor(dv)) { 244facf4a8dSllai1 case SDEV_VTOR_VALID: 245facf4a8dSllai1 case SDEV_VTOR_SKIP: 246facf4a8dSllai1 continue; 247facf4a8dSllai1 case SDEV_VTOR_INVALID: 248b127ac41SPhilip Kirk case SDEV_VTOR_STALE: 249facf4a8dSllai1 sdcmn_err7(("prunedir: destroy invalid " 250facf4a8dSllai1 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 251facf4a8dSllai1 break; 252facf4a8dSllai1 } 253facf4a8dSllai1 vp = SDEVTOV(dv); 254facf4a8dSllai1 if (vp->v_count > 0) 255facf4a8dSllai1 continue; 256facf4a8dSllai1 SDEV_HOLD(dv); 257facf4a8dSllai1 /* remove the cache node */ 258facf4a8dSllai1 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 259facf4a8dSllai1 SDEV_CACHE_DELETE); 260*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv); 261facf4a8dSllai1 } 262facf4a8dSllai1 rw_downgrade(&ddv->sdev_contents); 263facf4a8dSllai1 } 264facf4a8dSllai1 265facf4a8dSllai1 /* 266facf4a8dSllai1 * Lookup for /dev/pts directory 267facf4a8dSllai1 * If the entry does not exist, the devpts_create_rvp() callback 268facf4a8dSllai1 * is invoked to create it. Nodes do not persist across reboot. 26949e92448Svikram * 27049e92448Svikram * There is a potential denial of service here via 27149e92448Svikram * fattach on top of a /dev/pts node - any permission changes 27249e92448Svikram * applied to the node, apply to the fattached file and not 27349e92448Svikram * to the underlying pts node. As a result when the previous 27449e92448Svikram * user fdetaches, the pts node is still owned by the previous 27549e92448Svikram * owner. To prevent this we don't allow fattach() on top of a pts 27649e92448Svikram * node. This is done by a modification in the namefs filesystem 27749e92448Svikram * where we check if the underlying node has the /dev/pts vnodeops. 27849e92448Svikram * We do this via VOP_REALVP() on the underlying specfs node. 27949e92448Svikram * sdev_nodes currently don't have a realvp. If a realvp is ever 28049e92448Svikram * created for sdev_nodes, then VOP_REALVP() will return the 28149e92448Svikram * actual realvp (possibly a ufs vnode). This will defeat the check 28249e92448Svikram * in namefs code which checks if VOP_REALVP() returns a devpts 28349e92448Svikram * node. We add an ASSERT here in /dev/pts lookup() to check for 28449e92448Svikram * this condition. If sdev_nodes ever get a VOP_REALVP() entry point, 28549e92448Svikram * change the code in the namefs filesystem code (in nm_mount()) to 28649e92448Svikram * access the realvp of the specfs node directly instead of using 28749e92448Svikram * VOP_REALVP(). 288facf4a8dSllai1 */ 289facf4a8dSllai1 /*ARGSUSED3*/ 290facf4a8dSllai1 static int 291facf4a8dSllai1 devpts_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 292da6c28aaSamw struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 293da6c28aaSamw caller_context_t *ct, int *direntflags, pathname_t *realpnp) 294facf4a8dSllai1 { 295facf4a8dSllai1 struct sdev_node *sdvp = VTOSDEV(dvp); 296facf4a8dSllai1 struct sdev_node *dv; 29749e92448Svikram struct vnode *rvp = NULL; 298facf4a8dSllai1 int error; 299facf4a8dSllai1 300facf4a8dSllai1 error = devname_lookup_func(sdvp, nm, vpp, cred, devpts_create_rvp, 301facf4a8dSllai1 SDEV_VATTR); 302facf4a8dSllai1 303facf4a8dSllai1 if (error == 0) { 304facf4a8dSllai1 switch ((*vpp)->v_type) { 305facf4a8dSllai1 case VCHR: 306facf4a8dSllai1 dv = VTOSDEV(VTOS(*vpp)->s_realvp); 307da6c28aaSamw ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS); 308facf4a8dSllai1 break; 309facf4a8dSllai1 case VDIR: 310facf4a8dSllai1 dv = VTOSDEV(*vpp); 311facf4a8dSllai1 break; 312facf4a8dSllai1 default: 313facf4a8dSllai1 cmn_err(CE_PANIC, "devpts_lookup: Unsupported node " 314facf4a8dSllai1 "type: %p: %d", (void *)(*vpp), (*vpp)->v_type); 315facf4a8dSllai1 break; 316facf4a8dSllai1 } 317facf4a8dSllai1 ASSERT(SDEV_HELD(dv)); 318facf4a8dSllai1 } 319facf4a8dSllai1 320facf4a8dSllai1 return (error); 321facf4a8dSllai1 } 322facf4a8dSllai1 323facf4a8dSllai1 /* 324facf4a8dSllai1 * We allow create to find existing nodes 325facf4a8dSllai1 * - if the node doesn't exist - EROFS 326facf4a8dSllai1 * - creating an existing dir read-only succeeds, otherwise EISDIR 327facf4a8dSllai1 * - exclusive creates fail - EEXIST 328facf4a8dSllai1 */ 329facf4a8dSllai1 /*ARGSUSED2*/ 330facf4a8dSllai1 static int 331facf4a8dSllai1 devpts_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 332da6c28aaSamw int mode, struct vnode **vpp, struct cred *cred, int flag, 333da6c28aaSamw caller_context_t *ct, vsecattr_t *vsecp) 334facf4a8dSllai1 { 335facf4a8dSllai1 int error; 336facf4a8dSllai1 struct vnode *vp; 337facf4a8dSllai1 338facf4a8dSllai1 *vpp = NULL; 339facf4a8dSllai1 340da6c28aaSamw error = devpts_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, 341da6c28aaSamw NULL); 342facf4a8dSllai1 if (error == 0) { 343facf4a8dSllai1 if (excl == EXCL) 344facf4a8dSllai1 error = EEXIST; 345facf4a8dSllai1 else if (vp->v_type == VDIR && (mode & VWRITE)) 346facf4a8dSllai1 error = EISDIR; 347facf4a8dSllai1 else 348da6c28aaSamw error = VOP_ACCESS(vp, mode, 0, cred, ct); 349facf4a8dSllai1 350facf4a8dSllai1 if (error) { 351facf4a8dSllai1 VN_RELE(vp); 352facf4a8dSllai1 } else 353facf4a8dSllai1 *vpp = vp; 354facf4a8dSllai1 } else if (error == ENOENT) { 355facf4a8dSllai1 error = EROFS; 356facf4a8dSllai1 } 357facf4a8dSllai1 358facf4a8dSllai1 return (error); 359facf4a8dSllai1 } 360facf4a8dSllai1 361facf4a8dSllai1 /* 362facf4a8dSllai1 * Display all instantiated pts (slave) device nodes. 363facf4a8dSllai1 * A /dev/pts entry will be created only after the first lookup of the slave 364facf4a8dSllai1 * device succeeds. 365facf4a8dSllai1 */ 366da6c28aaSamw /*ARGSUSED4*/ 367facf4a8dSllai1 static int 368facf4a8dSllai1 devpts_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 369da6c28aaSamw int *eofp, caller_context_t *ct, int flags) 370facf4a8dSllai1 { 371facf4a8dSllai1 struct sdev_node *sdvp = VTOSDEV(dvp); 372facf4a8dSllai1 if (uiop->uio_offset == 0) { 373facf4a8dSllai1 devpts_prunedir(sdvp); 374facf4a8dSllai1 } 375facf4a8dSllai1 376facf4a8dSllai1 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 377facf4a8dSllai1 } 378facf4a8dSllai1 379facf4a8dSllai1 380facf4a8dSllai1 static int 381facf4a8dSllai1 devpts_set_id(struct sdev_node *dv, struct vattr *vap, int protocol) 382facf4a8dSllai1 { 383facf4a8dSllai1 ASSERT((protocol & AT_UID) || (protocol & AT_GID)); 384facf4a8dSllai1 ptms_set_owner(getminor(SDEVTOV(dv)->v_rdev), 385facf4a8dSllai1 vap->va_uid, vap->va_gid); 386facf4a8dSllai1 return (0); 387facf4a8dSllai1 388facf4a8dSllai1 } 389facf4a8dSllai1 390cbcfaf83Sjg /*ARGSUSED4*/ 391facf4a8dSllai1 static int 392facf4a8dSllai1 devpts_setattr(struct vnode *vp, struct vattr *vap, int flags, 393cbcfaf83Sjg struct cred *cred, caller_context_t *ctp) 394facf4a8dSllai1 { 395facf4a8dSllai1 ASSERT((vp->v_type == VCHR) || (vp->v_type == VDIR)); 396facf4a8dSllai1 return (devname_setattr_func(vp, vap, flags, cred, 397facf4a8dSllai1 devpts_set_id, AT_UID|AT_GID)); 398facf4a8dSllai1 } 399facf4a8dSllai1 40049e92448Svikram 401facf4a8dSllai1 /* 402facf4a8dSllai1 * We override lookup and readdir to build entries based on the 403facf4a8dSllai1 * in kernel pty table. Also override setattr/setsecattr to 404facf4a8dSllai1 * avoid persisting permissions. 405facf4a8dSllai1 */ 406facf4a8dSllai1 const fs_operation_def_t devpts_vnodeops_tbl[] = { 407aa59c4cbSrsb VOPNAME_READDIR, { .vop_readdir = devpts_readdir }, 408aa59c4cbSrsb VOPNAME_LOOKUP, { .vop_lookup = devpts_lookup }, 409aa59c4cbSrsb VOPNAME_CREATE, { .vop_create = devpts_create }, 410aa59c4cbSrsb VOPNAME_SETATTR, { .vop_setattr = devpts_setattr }, 411aa59c4cbSrsb VOPNAME_REMOVE, { .error = fs_nosys }, 412aa59c4cbSrsb VOPNAME_MKDIR, { .error = fs_nosys }, 413aa59c4cbSrsb VOPNAME_RMDIR, { .error = fs_nosys }, 414aa59c4cbSrsb VOPNAME_SYMLINK, { .error = fs_nosys }, 415aa59c4cbSrsb VOPNAME_SETSECATTR, { .error = fs_nosys }, 416facf4a8dSllai1 NULL, NULL 417facf4a8dSllai1 }; 418