1 2621 llai1 /* 2 2621 llai1 * CDDL HEADER START 3 2621 llai1 * 4 2621 llai1 * The contents of this file are subject to the terms of the 5 2621 llai1 * Common Development and Distribution License (the "License"). 6 2621 llai1 * You may not use this file except in compliance with the License. 7 2621 llai1 * 8 2621 llai1 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 2621 llai1 * or http://www.opensolaris.org/os/licensing. 10 2621 llai1 * See the License for the specific language governing permissions 11 2621 llai1 * and limitations under the License. 12 2621 llai1 * 13 2621 llai1 * When distributing Covered Code, include this CDDL HEADER in each 14 2621 llai1 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 2621 llai1 * If applicable, add the following below this CDDL HEADER, with the 16 2621 llai1 * fields enclosed by brackets "[]" replaced with your own identifying 17 2621 llai1 * information: Portions Copyright [yyyy] [name of copyright owner] 18 2621 llai1 * 19 2621 llai1 * CDDL HEADER END 20 2621 llai1 */ 21 2621 llai1 /* 22 9198 Jerry * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 2621 llai1 * Use is subject to license terms. 24 2621 llai1 */ 25 2621 llai1 26 2621 llai1 /* 27 2621 llai1 * vnode ops for the /dev filesystem 28 2621 llai1 * 29 2621 llai1 * - VDIR, VCHR, CBLK, and VLNK are considered must supported files 30 2621 llai1 * - VREG and VDOOR are used for some internal implementations in 31 2621 llai1 * the global zone, e.g. devname and devfsadm communication 32 2621 llai1 * - other file types are unusual in this namespace and 33 2621 llai1 * not supported for now 34 2621 llai1 */ 35 2621 llai1 36 2621 llai1 #include <sys/types.h> 37 2621 llai1 #include <sys/param.h> 38 2621 llai1 #include <sys/t_lock.h> 39 2621 llai1 #include <sys/systm.h> 40 2621 llai1 #include <sys/sysmacros.h> 41 2621 llai1 #include <sys/user.h> 42 2621 llai1 #include <sys/time.h> 43 2621 llai1 #include <sys/vfs.h> 44 2621 llai1 #include <sys/vnode.h> 45 3898 rsb #include <sys/vfs_opreg.h> 46 2621 llai1 #include <sys/file.h> 47 2621 llai1 #include <sys/fcntl.h> 48 2621 llai1 #include <sys/flock.h> 49 2621 llai1 #include <sys/kmem.h> 50 2621 llai1 #include <sys/uio.h> 51 2621 llai1 #include <sys/errno.h> 52 2621 llai1 #include <sys/stat.h> 53 2621 llai1 #include <sys/cred.h> 54 2621 llai1 #include <sys/dirent.h> 55 2621 llai1 #include <sys/pathname.h> 56 2621 llai1 #include <sys/cmn_err.h> 57 2621 llai1 #include <sys/debug.h> 58 2621 llai1 #include <sys/policy.h> 59 2621 llai1 #include <vm/hat.h> 60 2621 llai1 #include <vm/seg_vn.h> 61 2621 llai1 #include <vm/seg_map.h> 62 2621 llai1 #include <vm/seg.h> 63 2621 llai1 #include <vm/as.h> 64 2621 llai1 #include <vm/page.h> 65 2621 llai1 #include <sys/proc.h> 66 2621 llai1 #include <sys/mode.h> 67 2621 llai1 #include <sys/sunndi.h> 68 2621 llai1 #include <sys/ptms.h> 69 2621 llai1 #include <fs/fs_subr.h> 70 2621 llai1 #include <sys/fs/dv_node.h> 71 2621 llai1 #include <sys/fs/sdev_impl.h> 72 2621 llai1 73 2621 llai1 /*ARGSUSED*/ 74 2621 llai1 static int 75 5331 amw sdev_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct) 76 2621 llai1 { 77 2621 llai1 struct sdev_node *dv = VTOSDEV(*vpp); 78 2621 llai1 struct sdev_node *ddv = dv->sdev_dotdot; 79 2621 llai1 int error = 0; 80 2621 llai1 81 2621 llai1 if ((*vpp)->v_type == VDIR) 82 2621 llai1 return (0); 83 2621 llai1 84 2621 llai1 if (!SDEV_IS_GLOBAL(dv)) 85 2621 llai1 return (ENOTSUP); 86 2621 llai1 87 10588 Eric if ((*vpp)->v_type == VLNK) 88 10588 Eric return (ENOENT); 89 2621 llai1 ASSERT((*vpp)->v_type == VREG); 90 2621 llai1 if ((*vpp)->v_type != VREG) 91 2621 llai1 return (ENOTSUP); 92 2621 llai1 93 2621 llai1 ASSERT(ddv); 94 2621 llai1 rw_enter(&ddv->sdev_contents, RW_READER); 95 2621 llai1 if (dv->sdev_attrvp == NULL) { 96 2621 llai1 rw_exit(&ddv->sdev_contents); 97 2621 llai1 return (ENOENT); 98 2621 llai1 } 99 5331 amw error = VOP_OPEN(&(dv->sdev_attrvp), flag, cred, ct); 100 2621 llai1 rw_exit(&ddv->sdev_contents); 101 2621 llai1 return (error); 102 2621 llai1 } 103 2621 llai1 104 2621 llai1 /*ARGSUSED1*/ 105 2621 llai1 static int 106 2621 llai1 sdev_close(struct vnode *vp, int flag, int count, 107 5331 amw offset_t offset, struct cred *cred, caller_context_t *ct) 108 2621 llai1 { 109 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 110 2621 llai1 111 2621 llai1 if (vp->v_type == VDIR) { 112 2621 llai1 cleanlocks(vp, ttoproc(curthread)->p_pid, 0); 113 2621 llai1 cleanshares(vp, ttoproc(curthread)->p_pid); 114 2621 llai1 return (0); 115 2621 llai1 } 116 2621 llai1 117 2621 llai1 if (!SDEV_IS_GLOBAL(dv)) 118 2621 llai1 return (ENOTSUP); 119 2621 llai1 120 2621 llai1 ASSERT(vp->v_type == VREG); 121 2621 llai1 if (vp->v_type != VREG) 122 2621 llai1 return (ENOTSUP); 123 2621 llai1 124 2621 llai1 ASSERT(dv->sdev_attrvp); 125 5331 amw return (VOP_CLOSE(dv->sdev_attrvp, flag, count, offset, cred, ct)); 126 2621 llai1 } 127 2621 llai1 128 2621 llai1 /*ARGSUSED*/ 129 2621 llai1 static int 130 2621 llai1 sdev_read(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, 131 2621 llai1 struct caller_context *ct) 132 2621 llai1 { 133 2621 llai1 struct sdev_node *dv = (struct sdev_node *)VTOSDEV(vp); 134 2621 llai1 int error; 135 2621 llai1 136 2621 llai1 if (!SDEV_IS_GLOBAL(dv)) 137 2621 llai1 return (EINVAL); 138 2621 llai1 139 2621 llai1 if (vp->v_type == VDIR) 140 2621 llai1 return (EISDIR); 141 2621 llai1 142 2621 llai1 /* only supporting regular files in /dev */ 143 2621 llai1 ASSERT(vp->v_type == VREG); 144 2621 llai1 if (vp->v_type != VREG) 145 2621 llai1 return (EINVAL); 146 2621 llai1 147 2621 llai1 ASSERT(RW_READ_HELD(&VTOSDEV(vp)->sdev_contents)); 148 2621 llai1 ASSERT(dv->sdev_attrvp); 149 5331 amw (void) VOP_RWLOCK(dv->sdev_attrvp, 0, ct); 150 2621 llai1 error = VOP_READ(dv->sdev_attrvp, uio, ioflag, cred, ct); 151 5331 amw VOP_RWUNLOCK(dv->sdev_attrvp, 0, ct); 152 2621 llai1 return (error); 153 2621 llai1 } 154 2621 llai1 155 2621 llai1 /*ARGSUSED*/ 156 2621 llai1 static int 157 2621 llai1 sdev_write(struct vnode *vp, struct uio *uio, int ioflag, struct cred *cred, 158 2621 llai1 struct caller_context *ct) 159 2621 llai1 { 160 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 161 2621 llai1 int error = 0; 162 2621 llai1 163 2621 llai1 if (!SDEV_IS_GLOBAL(dv)) 164 2621 llai1 return (EINVAL); 165 2621 llai1 166 2621 llai1 if (vp->v_type == VDIR) 167 2621 llai1 return (EISDIR); 168 2621 llai1 169 2621 llai1 /* only supporting regular files in /dev */ 170 2621 llai1 ASSERT(vp->v_type == VREG); 171 2621 llai1 if (vp->v_type != VREG) 172 2621 llai1 return (EINVAL); 173 2621 llai1 174 2621 llai1 ASSERT(dv->sdev_attrvp); 175 2621 llai1 176 5331 amw (void) VOP_RWLOCK(dv->sdev_attrvp, 1, ct); 177 2621 llai1 error = VOP_WRITE(dv->sdev_attrvp, uio, ioflag, cred, ct); 178 5331 amw VOP_RWUNLOCK(dv->sdev_attrvp, 1, ct); 179 2621 llai1 if (error == 0) { 180 2621 llai1 sdev_update_timestamps(dv->sdev_attrvp, kcred, 181 2621 llai1 AT_MTIME); 182 2621 llai1 } 183 2621 llai1 return (error); 184 2621 llai1 } 185 2621 llai1 186 2621 llai1 /*ARGSUSED*/ 187 2621 llai1 static int 188 2621 llai1 sdev_ioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, 189 5331 amw struct cred *cred, int *rvalp, caller_context_t *ct) 190 2621 llai1 { 191 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 192 2621 llai1 193 2621 llai1 if (!SDEV_IS_GLOBAL(dv) || (vp->v_type == VDIR)) 194 2621 llai1 return (ENOTTY); 195 2621 llai1 196 2621 llai1 ASSERT(vp->v_type == VREG); 197 2621 llai1 if (vp->v_type != VREG) 198 2621 llai1 return (EINVAL); 199 2621 llai1 200 2621 llai1 ASSERT(dv->sdev_attrvp); 201 5331 amw return (VOP_IOCTL(dv->sdev_attrvp, cmd, arg, flag, cred, rvalp, ct)); 202 2621 llai1 } 203 2621 llai1 204 2621 llai1 static int 205 5331 amw sdev_getattr(struct vnode *vp, struct vattr *vap, int flags, 206 5331 amw struct cred *cr, caller_context_t *ct) 207 2621 llai1 { 208 2621 llai1 int error = 0; 209 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 210 2621 llai1 struct sdev_node *parent = dv->sdev_dotdot; 211 2621 llai1 212 2621 llai1 ASSERT(parent); 213 2621 llai1 214 2621 llai1 rw_enter(&parent->sdev_contents, RW_READER); 215 2621 llai1 ASSERT(dv->sdev_attr || dv->sdev_attrvp); 216 2621 llai1 217 2621 llai1 /* 218 2621 llai1 * search order: 219 2621 llai1 * - for persistent nodes (SDEV_PERSIST): backstore 220 2621 llai1 * - for non-persistent nodes: module ops if global, then memory 221 2621 llai1 */ 222 2621 llai1 if (dv->sdev_attrvp) { 223 2621 llai1 rw_exit(&parent->sdev_contents); 224 5331 amw error = VOP_GETATTR(dv->sdev_attrvp, vap, flags, cr, ct); 225 2621 llai1 sdev_vattr_merge(dv, vap); 226 2621 llai1 } else { 227 2621 llai1 ASSERT(dv->sdev_attr); 228 2621 llai1 *vap = *dv->sdev_attr; 229 2621 llai1 sdev_vattr_merge(dv, vap); 230 2621 llai1 rw_exit(&parent->sdev_contents); 231 2621 llai1 } 232 2621 llai1 233 2621 llai1 return (error); 234 2621 llai1 } 235 2621 llai1 236 3748 jg /*ARGSUSED4*/ 237 2621 llai1 static int 238 3748 jg sdev_setattr(struct vnode *vp, struct vattr *vap, int flags, 239 3748 jg struct cred *cred, caller_context_t *ctp) 240 2621 llai1 { 241 2621 llai1 return (devname_setattr_func(vp, vap, flags, cred, NULL, 0)); 242 2621 llai1 } 243 2621 llai1 244 2621 llai1 static int 245 2621 llai1 sdev_getsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, 246 5331 amw struct cred *cr, caller_context_t *ct) 247 2621 llai1 { 248 2621 llai1 int error; 249 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 250 2621 llai1 struct vnode *avp = dv->sdev_attrvp; 251 2621 llai1 252 2621 llai1 if (avp == NULL) { 253 2621 llai1 /* return fs_fab_acl() if flavor matches, else do nothing */ 254 2621 llai1 if ((SDEV_ACL_FLAVOR(vp) == _ACL_ACLENT_ENABLED && 255 2621 llai1 (vsap->vsa_mask & (VSA_ACLCNT | VSA_DFACLCNT))) || 256 2621 llai1 (SDEV_ACL_FLAVOR(vp) == _ACL_ACE_ENABLED && 257 2621 llai1 (vsap->vsa_mask & (VSA_ACECNT | VSA_ACE)))) 258 5331 amw return (fs_fab_acl(vp, vsap, flags, cr, ct)); 259 2621 llai1 260 2621 llai1 return (ENOSYS); 261 2621 llai1 } 262 2621 llai1 263 5331 amw (void) VOP_RWLOCK(avp, 1, ct); 264 5331 amw error = VOP_GETSECATTR(avp, vsap, flags, cr, ct); 265 5331 amw VOP_RWUNLOCK(avp, 1, ct); 266 2621 llai1 return (error); 267 2621 llai1 } 268 2621 llai1 269 2621 llai1 static int 270 2621 llai1 sdev_setsecattr(struct vnode *vp, struct vsecattr *vsap, int flags, 271 5331 amw struct cred *cr, caller_context_t *ct) 272 2621 llai1 { 273 2621 llai1 int error; 274 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 275 2621 llai1 struct vnode *avp = dv->sdev_attrvp; 276 2621 llai1 277 2621 llai1 if (dv->sdev_state == SDEV_ZOMBIE) 278 2621 llai1 return (0); 279 2621 llai1 280 2621 llai1 if (avp == NULL) { 281 2621 llai1 if (SDEV_IS_GLOBAL(dv) && !SDEV_IS_PERSIST(dv)) 282 2621 llai1 return (fs_nosys()); 283 2621 llai1 ASSERT(dv->sdev_attr); 284 2621 llai1 /* 285 2621 llai1 * if coming in directly, the acl system call will 286 2621 llai1 * have held the read-write lock via VOP_RWLOCK() 287 2621 llai1 * If coming in via specfs, specfs will have 288 2621 llai1 * held the rw lock on the realvp i.e. us. 289 2621 llai1 */ 290 2621 llai1 ASSERT(RW_WRITE_HELD(&dv->sdev_contents)); 291 2621 llai1 sdev_vattr_merge(dv, dv->sdev_attr); 292 5331 amw error = sdev_shadow_node(dv, cr); 293 2621 llai1 if (error) { 294 2621 llai1 return (fs_nosys()); 295 2621 llai1 } 296 2621 llai1 297 2621 llai1 ASSERT(dv->sdev_attrvp); 298 2621 llai1 /* clean out the memory copy if any */ 299 2621 llai1 if (dv->sdev_attr) { 300 2621 llai1 kmem_free(dv->sdev_attr, sizeof (struct vattr)); 301 2621 llai1 dv->sdev_attr = NULL; 302 2621 llai1 } 303 2621 llai1 avp = dv->sdev_attrvp; 304 2621 llai1 } 305 2621 llai1 ASSERT(avp); 306 2621 llai1 307 5331 amw (void) VOP_RWLOCK(avp, V_WRITELOCK_TRUE, ct); 308 5331 amw error = VOP_SETSECATTR(avp, vsap, flags, cr, ct); 309 5331 amw VOP_RWUNLOCK(avp, V_WRITELOCK_TRUE, ct); 310 2621 llai1 return (error); 311 2621 llai1 } 312 2621 llai1 313 2621 llai1 int 314 2621 llai1 sdev_unlocked_access(void *vdv, int mode, struct cred *cr) 315 2621 llai1 { 316 2621 llai1 struct sdev_node *dv = vdv; 317 2621 llai1 int shift = 0; 318 2621 llai1 uid_t owner = dv->sdev_attr->va_uid; 319 2621 llai1 320 2621 llai1 if (crgetuid(cr) != owner) { 321 2621 llai1 shift += 3; 322 2621 llai1 if (groupmember(dv->sdev_attr->va_gid, cr) == 0) 323 2621 llai1 shift += 3; 324 2621 llai1 } 325 2621 llai1 326 2621 llai1 mode &= ~(dv->sdev_attr->va_mode << shift); 327 2621 llai1 if (mode == 0) 328 2621 llai1 return (0); 329 2621 llai1 330 2621 llai1 return (secpolicy_vnode_access(cr, SDEVTOV(dv), owner, mode)); 331 2621 llai1 } 332 2621 llai1 333 2621 llai1 static int 334 5331 amw sdev_access(struct vnode *vp, int mode, int flags, struct cred *cr, 335 5331 amw caller_context_t *ct) 336 2621 llai1 { 337 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 338 2621 llai1 int ret = 0; 339 2621 llai1 340 2621 llai1 ASSERT(dv->sdev_attr || dv->sdev_attrvp); 341 2621 llai1 342 2621 llai1 if (dv->sdev_attrvp) { 343 5331 amw ret = VOP_ACCESS(dv->sdev_attrvp, mode, flags, cr, ct); 344 2621 llai1 } else if (dv->sdev_attr) { 345 2621 llai1 rw_enter(&dv->sdev_contents, RW_READER); 346 2621 llai1 ret = sdev_unlocked_access(dv, mode, cr); 347 2621 llai1 if (ret) 348 2621 llai1 ret = EACCES; 349 2621 llai1 rw_exit(&dv->sdev_contents); 350 2621 llai1 } 351 2621 llai1 352 2621 llai1 return (ret); 353 2621 llai1 } 354 2621 llai1 355 2621 llai1 /* 356 2621 llai1 * Lookup 357 2621 llai1 */ 358 2621 llai1 /*ARGSUSED3*/ 359 2621 llai1 static int 360 2621 llai1 sdev_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 361 5331 amw struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 362 5331 amw caller_context_t *ct, int *direntflags, pathname_t *realpnp) 363 2621 llai1 { 364 3843 jg struct sdev_node *parent; 365 3843 jg int error; 366 2621 llai1 367 2621 llai1 parent = VTOSDEV(dvp); 368 2621 llai1 ASSERT(parent); 369 3843 jg 370 3843 jg /* execute access is required to search the directory */ 371 5331 amw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 372 3843 jg return (error); 373 2621 llai1 374 2621 llai1 if (!SDEV_IS_GLOBAL(parent)) 375 2621 llai1 return (prof_lookup(dvp, nm, vpp, cred)); 376 2621 llai1 return (devname_lookup_func(parent, nm, vpp, cred, NULL, 0)); 377 2621 llai1 } 378 2621 llai1 379 2621 llai1 /*ARGSUSED2*/ 380 2621 llai1 static int 381 2621 llai1 sdev_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl, 382 5331 amw int mode, struct vnode **vpp, struct cred *cred, int flag, 383 5331 amw caller_context_t *ct, vsecattr_t *vsecp) 384 2621 llai1 { 385 2621 llai1 struct vnode *vp = NULL; 386 2621 llai1 struct vnode *avp; 387 2621 llai1 struct sdev_node *parent; 388 2621 llai1 struct sdev_node *self = NULL; 389 2621 llai1 int error = 0; 390 2621 llai1 vtype_t type = vap->va_type; 391 2621 llai1 392 2729 llai1 ASSERT(type != VNON && type != VBAD); 393 2621 llai1 394 2621 llai1 if ((type == VFIFO) || (type == VSOCK) || 395 2621 llai1 (type == VPROC) || (type == VPORT)) 396 2621 llai1 return (ENOTSUP); 397 2621 llai1 398 2621 llai1 parent = VTOSDEV(dvp); 399 2621 llai1 ASSERT(parent); 400 2621 llai1 401 2621 llai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 402 2621 llai1 if (parent->sdev_state == SDEV_ZOMBIE) { 403 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 404 2621 llai1 return (ENOENT); 405 2621 llai1 } 406 2621 llai1 407 2621 llai1 /* non-global do not allow pure node creation */ 408 2621 llai1 if (!SDEV_IS_GLOBAL(parent)) { 409 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 410 2621 llai1 return (prof_lookup(dvp, nm, vpp, cred)); 411 2621 llai1 } 412 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 413 2621 llai1 414 3843 jg /* execute access is required to search the directory */ 415 9198 Jerry if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 416 3843 jg return (error); 417 3843 jg 418 2621 llai1 /* check existing name */ 419 5331 amw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 420 5331 amw error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 421 2621 llai1 422 2621 llai1 /* name found */ 423 2621 llai1 if (error == 0) { 424 2621 llai1 ASSERT(vp); 425 2621 llai1 if (excl == EXCL) { 426 2621 llai1 error = EEXIST; 427 2621 llai1 } else if ((vp->v_type == VDIR) && (mode & VWRITE)) { 428 2621 llai1 /* allowing create/read-only an existing directory */ 429 2621 llai1 error = EISDIR; 430 2621 llai1 } else { 431 5686 jg error = VOP_ACCESS(vp, mode, 0, cred, ct); 432 2621 llai1 } 433 2621 llai1 434 2621 llai1 if (error) { 435 2621 llai1 VN_RELE(vp); 436 2621 llai1 return (error); 437 2621 llai1 } 438 2621 llai1 439 2621 llai1 /* truncation first */ 440 2621 llai1 if ((vp->v_type == VREG) && (vap->va_mask & AT_SIZE) && 441 2621 llai1 (vap->va_size == 0)) { 442 2621 llai1 ASSERT(parent->sdev_attrvp); 443 2621 llai1 error = VOP_CREATE(parent->sdev_attrvp, 444 5331 amw nm, vap, excl, mode, &avp, cred, flag, ct, vsecp); 445 2621 llai1 446 2621 llai1 if (error) { 447 2621 llai1 VN_RELE(vp); 448 2621 llai1 return (error); 449 2621 llai1 } 450 2621 llai1 } 451 2621 llai1 452 2621 llai1 sdev_update_timestamps(vp, kcred, 453 2621 llai1 AT_CTIME|AT_MTIME|AT_ATIME); 454 2621 llai1 *vpp = vp; 455 2621 llai1 return (0); 456 2621 llai1 } 457 2621 llai1 458 2621 llai1 /* bail out early */ 459 2621 llai1 if (error != ENOENT) 460 2621 llai1 return (error); 461 9198 Jerry 462 9198 Jerry /* verify write access - compliance specifies ENXIO */ 463 9198 Jerry if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred, ct)) != 0) { 464 9198 Jerry if (error == EACCES) 465 9198 Jerry error = ENXIO; 466 9198 Jerry return (error); 467 9198 Jerry } 468 2621 llai1 469 2621 llai1 /* 470 2621 llai1 * For memory-based (ROFS) directory: 471 2621 llai1 * - either disallow node creation; 472 2621 llai1 * - or implement VOP_CREATE of its own 473 2621 llai1 */ 474 2621 llai1 rw_enter(&parent->sdev_contents, RW_WRITER); 475 2621 llai1 if (!SDEV_IS_PERSIST(parent)) { 476 2621 llai1 rw_exit(&parent->sdev_contents); 477 2621 llai1 return (ENOTSUP); 478 2621 llai1 } 479 2621 llai1 ASSERT(parent->sdev_attrvp); 480 2621 llai1 error = sdev_mknode(parent, nm, &self, vap, NULL, NULL, 481 2621 llai1 cred, SDEV_READY); 482 2621 llai1 if (error) { 483 2621 llai1 rw_exit(&parent->sdev_contents); 484 2621 llai1 if (self) 485 2621 llai1 SDEV_RELE(self); 486 2621 llai1 return (error); 487 2621 llai1 } 488 2621 llai1 rw_exit(&parent->sdev_contents); 489 2621 llai1 490 2621 llai1 ASSERT(self); 491 2621 llai1 /* take care the timestamps for the node and its parent */ 492 2621 llai1 sdev_update_timestamps(SDEVTOV(self), kcred, 493 2621 llai1 AT_CTIME|AT_MTIME|AT_ATIME); 494 2621 llai1 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 495 2621 llai1 if (SDEV_IS_GLOBAL(parent)) 496 2621 llai1 atomic_inc_ulong(&parent->sdev_gdir_gen); 497 2621 llai1 498 2621 llai1 /* wake up other threads blocked on looking up this node */ 499 2621 llai1 mutex_enter(&self->sdev_lookup_lock); 500 2621 llai1 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 501 2621 llai1 mutex_exit(&self->sdev_lookup_lock); 502 2621 llai1 error = sdev_to_vp(self, vpp); 503 2621 llai1 return (error); 504 2621 llai1 } 505 2621 llai1 506 2621 llai1 static int 507 5331 amw sdev_remove(struct vnode *dvp, char *nm, struct cred *cred, 508 5331 amw caller_context_t *ct, int flags) 509 2621 llai1 { 510 2621 llai1 int error; 511 2621 llai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 512 2621 llai1 struct vnode *vp = NULL; 513 2621 llai1 struct sdev_node *dv = NULL; 514 2621 llai1 int len; 515 2621 llai1 int bkstore = 0; 516 2621 llai1 517 2621 llai1 /* bail out early */ 518 2621 llai1 len = strlen(nm); 519 2621 llai1 if (nm[0] == '.') { 520 2621 llai1 if (len == 1) { 521 2621 llai1 return (EINVAL); 522 2621 llai1 } else if (len == 2 && nm[1] == '.') { 523 2621 llai1 return (EEXIST); 524 2621 llai1 } 525 2621 llai1 } 526 2621 llai1 527 2621 llai1 ASSERT(parent); 528 2621 llai1 rw_enter(&parent->sdev_contents, RW_READER); 529 2621 llai1 if (!SDEV_IS_GLOBAL(parent)) { 530 2621 llai1 rw_exit(&parent->sdev_contents); 531 2621 llai1 return (ENOTSUP); 532 2621 llai1 } 533 2621 llai1 534 3843 jg /* execute access is required to search the directory */ 535 5331 amw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { 536 3843 jg rw_exit(&parent->sdev_contents); 537 3843 jg return (error); 538 3843 jg } 539 3843 jg 540 2621 llai1 /* check existence first */ 541 2621 llai1 dv = sdev_cache_lookup(parent, nm); 542 2621 llai1 if (dv == NULL) { 543 2621 llai1 rw_exit(&parent->sdev_contents); 544 2621 llai1 return (ENOENT); 545 2621 llai1 } 546 2621 llai1 547 2621 llai1 vp = SDEVTOV(dv); 548 2621 llai1 if ((dv->sdev_state == SDEV_INIT) || 549 2621 llai1 (dv->sdev_state == SDEV_ZOMBIE)) { 550 2621 llai1 rw_exit(&parent->sdev_contents); 551 2621 llai1 VN_RELE(vp); 552 2621 llai1 return (ENOENT); 553 3843 jg } 554 3843 jg 555 3843 jg /* write access is required to remove an entry */ 556 5331 amw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 557 3843 jg rw_exit(&parent->sdev_contents); 558 3843 jg VN_RELE(vp); 559 3843 jg return (error); 560 2621 llai1 } 561 2621 llai1 562 2621 llai1 /* 563 2621 llai1 * sdev_dirdelete does the real job of: 564 2621 llai1 * - make sure no open ref count 565 2621 llai1 * - destroying the sdev_node 566 2621 llai1 * - releasing the hold on attrvp 567 2621 llai1 */ 568 2621 llai1 bkstore = SDEV_IS_PERSIST(dv) ? 1 : 0; 569 2621 llai1 if (!rw_tryupgrade(&parent->sdev_contents)) { 570 2621 llai1 rw_exit(&parent->sdev_contents); 571 2621 llai1 rw_enter(&parent->sdev_contents, RW_WRITER); 572 2621 llai1 } 573 2621 llai1 error = sdev_cache_update(parent, &dv, nm, SDEV_CACHE_DELETE); 574 2621 llai1 rw_exit(&parent->sdev_contents); 575 2621 llai1 576 2621 llai1 sdcmn_err2(("sdev_remove: cache_update error %d\n", error)); 577 2621 llai1 if (error && (error != EBUSY)) { 578 2621 llai1 /* report errors other than EBUSY */ 579 2621 llai1 VN_RELE(vp); 580 2621 llai1 } else { 581 2621 llai1 sdcmn_err2(("sdev_remove: cleaning node %s from cache " 582 2621 llai1 " with error %d\n", nm, error)); 583 2621 llai1 584 2621 llai1 /* 585 2621 llai1 * best efforts clean up the backing store 586 2621 llai1 */ 587 2621 llai1 if (bkstore) { 588 2621 llai1 ASSERT(parent->sdev_attrvp); 589 5331 amw error = VOP_REMOVE(parent->sdev_attrvp, nm, cred, 590 5331 amw ct, flags); 591 2621 llai1 /* 592 2621 llai1 * do not report BUSY error 593 2621 llai1 * because the backing store ref count is released 594 2621 llai1 * when the last ref count on the sdev_node is 595 2621 llai1 * released. 596 2621 llai1 */ 597 2621 llai1 if (error == EBUSY) { 598 2621 llai1 sdcmn_err2(("sdev_remove: device %s is still on" 599 2621 llai1 "disk %s\n", nm, parent->sdev_path)); 600 2621 llai1 error = 0; 601 2621 llai1 } 602 2621 llai1 } 603 2621 llai1 604 2621 llai1 if (error == EBUSY) 605 2621 llai1 error = 0; 606 2621 llai1 } 607 2621 llai1 608 2621 llai1 return (error); 609 2621 llai1 } 610 2621 llai1 611 2621 llai1 /* 612 2621 llai1 * Some restrictions for this file system: 613 2621 llai1 * - both oldnm and newnm are in the scope of /dev file system, 614 2621 llai1 * to simply the namespace management model. 615 2621 llai1 */ 616 5331 amw /*ARGSUSED6*/ 617 2621 llai1 static int 618 2621 llai1 sdev_rename(struct vnode *odvp, char *onm, struct vnode *ndvp, char *nnm, 619 5331 amw struct cred *cred, caller_context_t *ct, int flags) 620 2621 llai1 { 621 2621 llai1 struct sdev_node *fromparent = NULL; 622 2621 llai1 struct vattr vattr; 623 2621 llai1 struct sdev_node *toparent; 624 2621 llai1 struct sdev_node *fromdv = NULL; /* source node */ 625 2729 llai1 struct vnode *ovp = NULL; /* source vnode */ 626 2621 llai1 struct sdev_node *todv = NULL; /* destination node */ 627 2729 llai1 struct vnode *nvp = NULL; /* destination vnode */ 628 2621 llai1 int samedir = 0; /* set if odvp == ndvp */ 629 2621 llai1 struct vnode *realvp; 630 2621 llai1 int error = 0; 631 2621 llai1 dev_t fsid; 632 2621 llai1 int bkstore = 0; 633 2729 llai1 vtype_t type; 634 2621 llai1 635 2621 llai1 /* prevent modifying "." and ".." */ 636 2621 llai1 if ((onm[0] == '.' && 637 2729 llai1 (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) || 638 2729 llai1 (nnm[0] == '.' && 639 2729 llai1 (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0')))) { 640 2621 llai1 return (EINVAL); 641 2621 llai1 } 642 2621 llai1 643 2621 llai1 fromparent = VTOSDEV(odvp); 644 2621 llai1 toparent = VTOSDEV(ndvp); 645 2621 llai1 646 2621 llai1 /* ZOMBIE parent doesn't allow new node creation */ 647 2621 llai1 rw_enter(&fromparent->sdev_dotdot->sdev_contents, RW_READER); 648 2621 llai1 if (fromparent->sdev_state == SDEV_ZOMBIE) { 649 2621 llai1 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 650 2621 llai1 return (ENOENT); 651 2621 llai1 } 652 2621 llai1 653 2621 llai1 /* renaming only supported for global device nodes */ 654 2621 llai1 if (!SDEV_IS_GLOBAL(fromparent)) { 655 2621 llai1 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 656 2621 llai1 return (ENOTSUP); 657 2621 llai1 } 658 2621 llai1 rw_exit(&fromparent->sdev_dotdot->sdev_contents); 659 2621 llai1 660 2621 llai1 rw_enter(&toparent->sdev_dotdot->sdev_contents, RW_READER); 661 2621 llai1 if (toparent->sdev_state == SDEV_ZOMBIE) { 662 2621 llai1 rw_exit(&toparent->sdev_dotdot->sdev_contents); 663 2621 llai1 return (ENOENT); 664 2621 llai1 } 665 2621 llai1 rw_exit(&toparent->sdev_dotdot->sdev_contents); 666 2621 llai1 667 2729 llai1 /* 668 3843 jg * acquire the global lock to prevent 669 2729 llai1 * mount/unmount/other rename activities. 670 2729 llai1 */ 671 2729 llai1 mutex_enter(&sdev_lock); 672 2729 llai1 673 2621 llai1 /* check existence of the source node */ 674 5331 amw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 675 5331 amw error = VOP_LOOKUP(odvp, onm, &ovp, NULL, 0, NULL, cred, ct, 676 5331 amw NULL, NULL); 677 2621 llai1 if (error) { 678 2621 llai1 sdcmn_err2(("sdev_rename: the source node %s exists\n", 679 2621 llai1 onm)); 680 2729 llai1 mutex_exit(&sdev_lock); 681 2621 llai1 return (error); 682 2621 llai1 } 683 2621 llai1 684 5331 amw if (VOP_REALVP(ovp, &realvp, ct) == 0) { 685 2621 llai1 VN_HOLD(realvp); 686 2621 llai1 VN_RELE(ovp); 687 2621 llai1 ovp = realvp; 688 2621 llai1 } 689 2621 llai1 690 2621 llai1 /* check existence of destination */ 691 5331 amw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 692 5331 amw error = VOP_LOOKUP(ndvp, nnm, &nvp, NULL, 0, NULL, cred, ct, 693 5331 amw NULL, NULL); 694 2621 llai1 if (error && (error != ENOENT)) { 695 2729 llai1 mutex_exit(&sdev_lock); 696 2621 llai1 VN_RELE(ovp); 697 2621 llai1 return (error); 698 2621 llai1 } 699 2621 llai1 700 5331 amw if (nvp && (VOP_REALVP(nvp, &realvp, ct) == 0)) { 701 2621 llai1 VN_HOLD(realvp); 702 2621 llai1 VN_RELE(nvp); 703 2621 llai1 nvp = realvp; 704 2621 llai1 } 705 2621 llai1 706 2621 llai1 /* 707 2729 llai1 * make sure the source and the destination are 708 2729 llai1 * in the same dev filesystem 709 2621 llai1 */ 710 2621 llai1 if (odvp != ndvp) { 711 2621 llai1 vattr.va_mask = AT_FSID; 712 5331 amw if (error = VOP_GETATTR(odvp, &vattr, 0, cred, ct)) { 713 2729 llai1 mutex_exit(&sdev_lock); 714 2621 llai1 VN_RELE(ovp); 715 2621 llai1 return (error); 716 2621 llai1 } 717 2621 llai1 fsid = vattr.va_fsid; 718 2621 llai1 vattr.va_mask = AT_FSID; 719 5331 amw if (error = VOP_GETATTR(ndvp, &vattr, 0, cred, ct)) { 720 2729 llai1 mutex_exit(&sdev_lock); 721 2621 llai1 VN_RELE(ovp); 722 2621 llai1 return (error); 723 2621 llai1 } 724 2621 llai1 if (fsid != vattr.va_fsid) { 725 2729 llai1 mutex_exit(&sdev_lock); 726 2621 llai1 VN_RELE(ovp); 727 2621 llai1 return (EXDEV); 728 2621 llai1 } 729 2621 llai1 } 730 2621 llai1 731 2621 llai1 /* make sure the old entry can be deleted */ 732 5331 amw error = VOP_ACCESS(odvp, VWRITE, 0, cred, ct); 733 2621 llai1 if (error) { 734 2729 llai1 mutex_exit(&sdev_lock); 735 2621 llai1 VN_RELE(ovp); 736 2621 llai1 return (error); 737 2621 llai1 } 738 2621 llai1 739 2621 llai1 /* make sure the destination allows creation */ 740 2621 llai1 samedir = (fromparent == toparent); 741 2621 llai1 if (!samedir) { 742 5331 amw error = VOP_ACCESS(ndvp, VEXEC|VWRITE, 0, cred, ct); 743 2621 llai1 if (error) { 744 2729 llai1 mutex_exit(&sdev_lock); 745 2621 llai1 VN_RELE(ovp); 746 2621 llai1 return (error); 747 2621 llai1 } 748 2621 llai1 } 749 2621 llai1 750 2621 llai1 fromdv = VTOSDEV(ovp); 751 2621 llai1 ASSERT(fromdv); 752 2621 llai1 753 2729 llai1 /* destination file exists */ 754 2621 llai1 if (nvp) { 755 2729 llai1 todv = VTOSDEV(nvp); 756 2729 llai1 ASSERT(todv); 757 2621 llai1 } 758 2621 llai1 759 2621 llai1 /* 760 2621 llai1 * link source to new target in the memory 761 2621 llai1 */ 762 2729 llai1 error = sdev_rnmnode(fromparent, fromdv, toparent, &todv, nnm, cred); 763 2621 llai1 if (error) { 764 2621 llai1 sdcmn_err2(("sdev_rename: renaming %s to %s failed " 765 2621 llai1 " with error %d\n", onm, nnm, error)); 766 2729 llai1 mutex_exit(&sdev_lock); 767 2729 llai1 if (nvp) 768 2729 llai1 VN_RELE(nvp); 769 2621 llai1 VN_RELE(ovp); 770 2729 llai1 return (error); 771 2729 llai1 } 772 2729 llai1 773 2729 llai1 /* 774 2729 llai1 * unlink from source 775 2729 llai1 */ 776 2729 llai1 rw_enter(&fromparent->sdev_contents, RW_READER); 777 2729 llai1 fromdv = sdev_cache_lookup(fromparent, onm); 778 2729 llai1 if (fromdv == NULL) { 779 2729 llai1 rw_exit(&fromparent->sdev_contents); 780 2729 llai1 mutex_exit(&sdev_lock); 781 2729 llai1 sdcmn_err2(("sdev_rename: the source is deleted already\n")); 782 2729 llai1 return (0); 783 2729 llai1 } 784 2729 llai1 785 2729 llai1 if (fromdv->sdev_state == SDEV_ZOMBIE) { 786 2729 llai1 rw_exit(&fromparent->sdev_contents); 787 2729 llai1 mutex_exit(&sdev_lock); 788 2729 llai1 VN_RELE(SDEVTOV(fromdv)); 789 2729 llai1 sdcmn_err2(("sdev_rename: the source is being deleted\n")); 790 2729 llai1 return (0); 791 2729 llai1 } 792 2729 llai1 rw_exit(&fromparent->sdev_contents); 793 2729 llai1 ASSERT(SDEVTOV(fromdv) == ovp); 794 2729 llai1 VN_RELE(ovp); 795 2729 llai1 796 2729 llai1 /* clean out the directory contents before it can be removed */ 797 2729 llai1 type = SDEVTOV(fromdv)->v_type; 798 2729 llai1 if (type == VDIR) { 799 2729 llai1 error = sdev_cleandir(fromdv, NULL, 0); 800 2729 llai1 sdcmn_err2(("sdev_rename: cleandir finished with %d\n", 801 2729 llai1 error)); 802 2729 llai1 if (error == EBUSY) 803 2729 llai1 error = 0; 804 2729 llai1 } 805 2729 llai1 806 2729 llai1 rw_enter(&fromparent->sdev_contents, RW_WRITER); 807 2729 llai1 bkstore = SDEV_IS_PERSIST(fromdv) ? 1 : 0; 808 2729 llai1 error = sdev_cache_update(fromparent, &fromdv, onm, 809 2729 llai1 SDEV_CACHE_DELETE); 810 2729 llai1 811 2729 llai1 /* best effforts clean up the backing store */ 812 2729 llai1 if (bkstore) { 813 2729 llai1 ASSERT(fromparent->sdev_attrvp); 814 2729 llai1 if (type != VDIR) { 815 5331 amw /* XXXci - We may need to translate the C-I flags on VOP_REMOVE */ 816 2729 llai1 error = VOP_REMOVE(fromparent->sdev_attrvp, 817 5331 amw onm, kcred, ct, 0); 818 2729 llai1 } else { 819 5331 amw /* XXXci - We may need to translate the C-I flags on VOP_RMDIR */ 820 2729 llai1 error = VOP_RMDIR(fromparent->sdev_attrvp, 821 5331 amw onm, fromparent->sdev_attrvp, kcred, ct, 0); 822 2729 llai1 } 823 2729 llai1 824 2729 llai1 if (error) { 825 2729 llai1 sdcmn_err2(("sdev_rename: device %s is " 826 2729 llai1 "still on disk %s\n", onm, 827 2729 llai1 fromparent->sdev_path)); 828 2729 llai1 error = 0; 829 2729 llai1 } 830 2729 llai1 } 831 2729 llai1 rw_exit(&fromparent->sdev_contents); 832 2729 llai1 mutex_exit(&sdev_lock); 833 2729 llai1 834 2729 llai1 /* once reached to this point, the rename is regarded successful */ 835 2729 llai1 return (0); 836 2621 llai1 } 837 2621 llai1 838 2621 llai1 /* 839 2621 llai1 * dev-fs version of "ln -s path dev-name" 840 2621 llai1 * tnm - path, e.g. /devices/... or /dev/... 841 2621 llai1 * lnm - dev_name 842 2621 llai1 */ 843 5331 amw /*ARGSUSED6*/ 844 2621 llai1 static int 845 2621 llai1 sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva, 846 5331 amw char *tnm, struct cred *cred, caller_context_t *ct, int flags) 847 2621 llai1 { 848 2621 llai1 int error; 849 2621 llai1 struct vnode *vp = NULL; 850 2621 llai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 851 2621 llai1 struct sdev_node *self = (struct sdev_node *)NULL; 852 2621 llai1 853 2621 llai1 ASSERT(parent); 854 2621 llai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 855 2621 llai1 if (parent->sdev_state == SDEV_ZOMBIE) { 856 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 857 2621 llai1 sdcmn_err2(("sdev_symlink: parent %s is ZOMBIED \n", 858 2621 llai1 parent->sdev_name)); 859 2621 llai1 return (ENOENT); 860 2621 llai1 } 861 2621 llai1 862 2621 llai1 if (!SDEV_IS_GLOBAL(parent)) { 863 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 864 2621 llai1 return (ENOTSUP); 865 2621 llai1 } 866 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 867 2621 llai1 868 3843 jg /* execute access is required to search a directory */ 869 5331 amw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 870 3843 jg return (error); 871 3843 jg 872 2621 llai1 /* find existing name */ 873 5331 amw /* XXXci - We may need to translate the C-I flags here */ 874 5331 amw error = VOP_LOOKUP(dvp, lnm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 875 2621 llai1 if (error == 0) { 876 2621 llai1 ASSERT(vp); 877 2621 llai1 VN_RELE(vp); 878 2621 llai1 sdcmn_err2(("sdev_symlink: node %s already exists\n", lnm)); 879 2621 llai1 return (EEXIST); 880 2621 llai1 } 881 3843 jg if (error != ENOENT) 882 3843 jg return (error); 883 2621 llai1 884 3843 jg /* write access is required to create a symlink */ 885 5331 amw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) 886 2621 llai1 return (error); 887 2621 llai1 888 2621 llai1 /* put it into memory cache */ 889 2621 llai1 rw_enter(&parent->sdev_contents, RW_WRITER); 890 2621 llai1 error = sdev_mknode(parent, lnm, &self, tva, NULL, (void *)tnm, 891 2621 llai1 cred, SDEV_READY); 892 2621 llai1 if (error) { 893 2621 llai1 rw_exit(&parent->sdev_contents); 894 2621 llai1 sdcmn_err2(("sdev_symlink: node %s creation failed\n", lnm)); 895 2621 llai1 if (self) 896 2621 llai1 SDEV_RELE(self); 897 2621 llai1 898 2621 llai1 return (error); 899 2621 llai1 } 900 2621 llai1 ASSERT(self && (self->sdev_state == SDEV_READY)); 901 2621 llai1 rw_exit(&parent->sdev_contents); 902 2621 llai1 903 2621 llai1 /* take care the timestamps for the node and its parent */ 904 2621 llai1 sdev_update_timestamps(SDEVTOV(self), kcred, 905 2621 llai1 AT_CTIME|AT_MTIME|AT_ATIME); 906 2621 llai1 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 907 2621 llai1 if (SDEV_IS_GLOBAL(parent)) 908 2621 llai1 atomic_inc_ulong(&parent->sdev_gdir_gen); 909 2621 llai1 910 2621 llai1 /* wake up other threads blocked on looking up this node */ 911 2621 llai1 mutex_enter(&self->sdev_lookup_lock); 912 2621 llai1 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 913 2621 llai1 mutex_exit(&self->sdev_lookup_lock); 914 2621 llai1 SDEV_RELE(self); /* don't return with vnode held */ 915 2621 llai1 return (0); 916 2621 llai1 } 917 2621 llai1 918 5331 amw /*ARGSUSED6*/ 919 2621 llai1 static int 920 2621 llai1 sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp, 921 5331 amw struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp) 922 2621 llai1 { 923 2621 llai1 int error; 924 2621 llai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 925 2621 llai1 struct sdev_node *self = NULL; 926 2621 llai1 struct vnode *vp = NULL; 927 2621 llai1 928 2621 llai1 ASSERT(parent && parent->sdev_dotdot); 929 2621 llai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 930 2621 llai1 if (parent->sdev_state == SDEV_ZOMBIE) { 931 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 932 2621 llai1 return (ENOENT); 933 2621 llai1 } 934 2621 llai1 935 2621 llai1 /* non-global do not allow pure directory creation */ 936 2621 llai1 if (!SDEV_IS_GLOBAL(parent)) { 937 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 938 2621 llai1 return (prof_lookup(dvp, nm, vpp, cred)); 939 2621 llai1 } 940 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 941 2621 llai1 942 3843 jg /* execute access is required to search the directory */ 943 5331 amw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) { 944 3843 jg return (error); 945 3843 jg } 946 3843 jg 947 2621 llai1 /* find existing name */ 948 5331 amw /* XXXci - We may need to translate the C-I flags on VOP_LOOKUP */ 949 5331 amw error = VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL, NULL); 950 2621 llai1 if (error == 0) { 951 2621 llai1 VN_RELE(vp); 952 2621 llai1 return (EEXIST); 953 2621 llai1 } 954 2621 llai1 if (error != ENOENT) 955 2621 llai1 return (error); 956 3843 jg 957 3843 jg /* require write access to create a directory */ 958 5331 amw if ((error = VOP_ACCESS(dvp, VWRITE, 0, cred, ct)) != 0) { 959 3843 jg return (error); 960 3843 jg } 961 2621 llai1 962 2621 llai1 /* put it into memory */ 963 2621 llai1 rw_enter(&parent->sdev_contents, RW_WRITER); 964 2621 llai1 error = sdev_mknode(parent, nm, &self, 965 2621 llai1 va, NULL, NULL, cred, SDEV_READY); 966 2621 llai1 if (error) { 967 2621 llai1 rw_exit(&parent->sdev_contents); 968 2621 llai1 if (self) 969 2621 llai1 SDEV_RELE(self); 970 2621 llai1 return (error); 971 2621 llai1 } 972 2621 llai1 ASSERT(self && (self->sdev_state == SDEV_READY)); 973 2621 llai1 rw_exit(&parent->sdev_contents); 974 2621 llai1 975 2621 llai1 /* take care the timestamps for the node and its parent */ 976 2621 llai1 sdev_update_timestamps(SDEVTOV(self), kcred, 977 2621 llai1 AT_CTIME|AT_MTIME|AT_ATIME); 978 2621 llai1 sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME); 979 2621 llai1 if (SDEV_IS_GLOBAL(parent)) 980 2621 llai1 atomic_inc_ulong(&parent->sdev_gdir_gen); 981 2621 llai1 982 2621 llai1 /* wake up other threads blocked on looking up this node */ 983 2621 llai1 mutex_enter(&self->sdev_lookup_lock); 984 2621 llai1 SDEV_UNBLOCK_OTHERS(self, SDEV_LOOKUP); 985 2621 llai1 mutex_exit(&self->sdev_lookup_lock); 986 2621 llai1 *vpp = SDEVTOV(self); 987 2621 llai1 return (0); 988 2621 llai1 } 989 2621 llai1 990 2621 llai1 /* 991 2621 llai1 * allowing removing an empty directory under /dev 992 2621 llai1 */ 993 2621 llai1 /*ARGSUSED*/ 994 2621 llai1 static int 995 5331 amw sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred, 996 5331 amw caller_context_t *ct, int flags) 997 2621 llai1 { 998 2621 llai1 int error = 0; 999 2621 llai1 struct sdev_node *parent = (struct sdev_node *)VTOSDEV(dvp); 1000 2621 llai1 struct sdev_node *self = NULL; 1001 2621 llai1 struct vnode *vp = NULL; 1002 2621 llai1 1003 2621 llai1 /* bail out early */ 1004 2621 llai1 if (strcmp(nm, ".") == 0) 1005 2621 llai1 return (EINVAL); 1006 2621 llai1 if (strcmp(nm, "..") == 0) 1007 2621 llai1 return (EEXIST); /* should be ENOTEMPTY */ 1008 2621 llai1 1009 2621 llai1 /* no destruction of non-global node */ 1010 2621 llai1 ASSERT(parent && parent->sdev_dotdot); 1011 2621 llai1 rw_enter(&parent->sdev_dotdot->sdev_contents, RW_READER); 1012 2621 llai1 if (!SDEV_IS_GLOBAL(parent)) { 1013 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 1014 2621 llai1 return (ENOTSUP); 1015 2621 llai1 } 1016 2621 llai1 rw_exit(&parent->sdev_dotdot->sdev_contents); 1017 2621 llai1 1018 3843 jg /* execute access is required to search the directory */ 1019 10588 Eric if ((error = VOP_ACCESS(dvp, VEXEC|VWRITE, 0, cred, ct)) != 0) 1020 3843 jg return (error); 1021 3843 jg 1022 2621 llai1 /* check existing name */ 1023 2621 llai1 rw_enter(&parent->sdev_contents, RW_WRITER); 1024 2621 llai1 self = sdev_cache_lookup(parent, nm); 1025 2621 llai1 if (self == NULL) { 1026 2621 llai1 rw_exit(&parent->sdev_contents); 1027 2621 llai1 return (ENOENT); 1028 2621 llai1 } 1029 2621 llai1 1030 2621 llai1 vp = SDEVTOV(self); 1031 2621 llai1 if ((self->sdev_state == SDEV_INIT) || 1032 2621 llai1 (self->sdev_state == SDEV_ZOMBIE)) { 1033 2621 llai1 rw_exit(&parent->sdev_contents); 1034 2621 llai1 VN_RELE(vp); 1035 2621 llai1 return (ENOENT); 1036 2621 llai1 } 1037 2621 llai1 1038 2621 llai1 /* some sanity checks */ 1039 2621 llai1 if (vp == dvp || vp == cdir) { 1040 2621 llai1 rw_exit(&parent->sdev_contents); 1041 2621 llai1 VN_RELE(vp); 1042 2621 llai1 return (EINVAL); 1043 2621 llai1 } 1044 2621 llai1 1045 2621 llai1 if (vp->v_type != VDIR) { 1046 2621 llai1 rw_exit(&parent->sdev_contents); 1047 2621 llai1 VN_RELE(vp); 1048 2621 llai1 return (ENOTDIR); 1049 2621 llai1 } 1050 2621 llai1 1051 2621 llai1 if (vn_vfswlock(vp)) { 1052 2621 llai1 rw_exit(&parent->sdev_contents); 1053 2621 llai1 VN_RELE(vp); 1054 2621 llai1 return (EBUSY); 1055 2621 llai1 } 1056 2621 llai1 1057 2621 llai1 if (vn_mountedvfs(vp) != NULL) { 1058 2621 llai1 rw_exit(&parent->sdev_contents); 1059 2621 llai1 vn_vfsunlock(vp); 1060 2621 llai1 VN_RELE(vp); 1061 2621 llai1 return (EBUSY); 1062 2621 llai1 } 1063 2621 llai1 1064 2621 llai1 self = VTOSDEV(vp); 1065 2621 llai1 /* bail out on a non-empty directory */ 1066 2621 llai1 rw_enter(&self->sdev_contents, RW_READER); 1067 2621 llai1 if (self->sdev_nlink > 2) { 1068 2621 llai1 rw_exit(&self->sdev_contents); 1069 2621 llai1 rw_exit(&parent->sdev_contents); 1070 2621 llai1 vn_vfsunlock(vp); 1071 2621 llai1 VN_RELE(vp); 1072 2621 llai1 return (ENOTEMPTY); 1073 2621 llai1 } 1074 2621 llai1 rw_exit(&self->sdev_contents); 1075 2621 llai1 1076 2621 llai1 /* unlink it from the directory cache */ 1077 2621 llai1 error = sdev_cache_update(parent, &self, nm, SDEV_CACHE_DELETE); 1078 2621 llai1 rw_exit(&parent->sdev_contents); 1079 2621 llai1 vn_vfsunlock(vp); 1080 2621 llai1 1081 2621 llai1 if (error && (error != EBUSY)) { 1082 2621 llai1 VN_RELE(vp); 1083 2621 llai1 } else { 1084 2621 llai1 sdcmn_err2(("sdev_rmdir: cleaning node %s from directory " 1085 2621 llai1 " cache with error %d\n", nm, error)); 1086 2621 llai1 1087 2621 llai1 /* best effort to clean up the backing store */ 1088 2621 llai1 if (SDEV_IS_PERSIST(parent)) { 1089 2621 llai1 ASSERT(parent->sdev_attrvp); 1090 2621 llai1 error = VOP_RMDIR(parent->sdev_attrvp, nm, 1091 5331 amw parent->sdev_attrvp, kcred, ct, flags); 1092 2621 llai1 sdcmn_err2(("sdev_rmdir: cleaning device %s is on" 1093 2621 llai1 " disk error %d\n", parent->sdev_path, error)); 1094 2621 llai1 } 1095 2621 llai1 1096 2621 llai1 if (error == EBUSY) 1097 2621 llai1 error = 0; 1098 2621 llai1 } 1099 2621 llai1 1100 2621 llai1 return (error); 1101 2621 llai1 } 1102 2621 llai1 1103 2621 llai1 /* 1104 2621 llai1 * read the contents of a symbolic link 1105 2621 llai1 */ 1106 2621 llai1 static int 1107 5331 amw sdev_readlink(struct vnode *vp, struct uio *uiop, struct cred *cred, 1108 5331 amw caller_context_t *ct) 1109 2621 llai1 { 1110 2621 llai1 struct sdev_node *dv; 1111 2621 llai1 int error = 0; 1112 2621 llai1 1113 2621 llai1 ASSERT(vp->v_type == VLNK); 1114 2621 llai1 1115 2621 llai1 dv = VTOSDEV(vp); 1116 2621 llai1 1117 2621 llai1 if (dv->sdev_attrvp) { 1118 2621 llai1 /* non-NULL attrvp implys a persisted node at READY state */ 1119 5331 amw return (VOP_READLINK(dv->sdev_attrvp, uiop, cred, ct)); 1120 2621 llai1 } else if (dv->sdev_symlink != NULL) { 1121 2621 llai1 /* memory nodes, e.g. local nodes */ 1122 2621 llai1 rw_enter(&dv->sdev_contents, RW_READER); 1123 2621 llai1 sdcmn_err2(("sdev_readlink link is %s\n", dv->sdev_symlink)); 1124 2621 llai1 error = uiomove(dv->sdev_symlink, strlen(dv->sdev_symlink), 1125 2621 llai1 UIO_READ, uiop); 1126 2621 llai1 rw_exit(&dv->sdev_contents); 1127 2621 llai1 return (error); 1128 2621 llai1 } 1129 2621 llai1 1130 2621 llai1 return (ENOENT); 1131 2621 llai1 } 1132 2621 llai1 1133 5331 amw /*ARGSUSED4*/ 1134 2621 llai1 static int 1135 5331 amw sdev_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, int *eofp, 1136 5331 amw caller_context_t *ct, int flags) 1137 2621 llai1 { 1138 2679 szhou struct sdev_node *parent = VTOSDEV(dvp); 1139 3843 jg int error; 1140 3843 jg 1141 3843 jg /* execute access is required to search the directory */ 1142 5331 amw if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0) 1143 3843 jg return (error); 1144 2679 szhou 1145 2679 szhou ASSERT(parent); 1146 2679 szhou if (!SDEV_IS_GLOBAL(parent)) 1147 2679 szhou prof_filldir(parent); 1148 2621 llai1 return (devname_readdir_func(dvp, uiop, cred, eofp, SDEV_BROWSE)); 1149 2621 llai1 } 1150 2621 llai1 1151 2621 llai1 /*ARGSUSED1*/ 1152 2621 llai1 static void 1153 5331 amw sdev_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct) 1154 2621 llai1 { 1155 5895 yz147064 devname_inactive_func(vp, cred, NULL); 1156 2621 llai1 } 1157 2621 llai1 1158 5331 amw /*ARGSUSED2*/ 1159 2621 llai1 static int 1160 5331 amw sdev_fid(struct vnode *vp, struct fid *fidp, caller_context_t *ct) 1161 2621 llai1 { 1162 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 1163 2621 llai1 struct sdev_fid *sdev_fid; 1164 2621 llai1 1165 2621 llai1 if (fidp->fid_len < (sizeof (struct sdev_fid) - sizeof (ushort_t))) { 1166 2621 llai1 fidp->fid_len = sizeof (struct sdev_fid) - sizeof (ushort_t); 1167 2621 llai1 return (ENOSPC); 1168 2621 llai1 } 1169 2621 llai1 1170 2621 llai1 sdev_fid = (struct sdev_fid *)fidp; 1171 2621 llai1 bzero(sdev_fid, sizeof (struct sdev_fid)); 1172 2621 llai1 sdev_fid->sdevfid_len = 1173 2621 llai1 (int)sizeof (struct sdev_fid) - sizeof (ushort_t); 1174 2621 llai1 sdev_fid->sdevfid_ino = dv->sdev_ino; 1175 2621 llai1 1176 2621 llai1 return (0); 1177 2621 llai1 } 1178 2621 llai1 1179 2621 llai1 /* 1180 2621 llai1 * This pair of routines bracket all VOP_READ, VOP_WRITE 1181 2621 llai1 * and VOP_READDIR requests. The contents lock stops things 1182 2621 llai1 * moving around while we're looking at them. 1183 2621 llai1 */ 1184 3748 jg /*ARGSUSED2*/ 1185 3748 jg static int 1186 3748 jg sdev_rwlock(struct vnode *vp, int write_flag, caller_context_t *ctp) 1187 2621 llai1 { 1188 3748 jg rw_enter(&VTOSDEV(vp)->sdev_contents, 1189 3748 jg write_flag ? RW_WRITER : RW_READER); 1190 3748 jg return (write_flag ? V_WRITELOCK_TRUE : V_WRITELOCK_FALSE); 1191 2621 llai1 } 1192 2621 llai1 1193 2621 llai1 /*ARGSUSED1*/ 1194 2621 llai1 static void 1195 3748 jg sdev_rwunlock(struct vnode *vp, int write_flag, caller_context_t *ctp) 1196 2621 llai1 { 1197 2621 llai1 rw_exit(&VTOSDEV(vp)->sdev_contents); 1198 2621 llai1 } 1199 2621 llai1 1200 2621 llai1 /*ARGSUSED1*/ 1201 2621 llai1 static int 1202 5331 amw sdev_seek(struct vnode *vp, offset_t ooff, offset_t *noffp, 1203 5331 amw caller_context_t *ct) 1204 2621 llai1 { 1205 2621 llai1 struct vnode *attrvp = VTOSDEV(vp)->sdev_attrvp; 1206 2621 llai1 1207 2621 llai1 ASSERT(vp->v_type != VCHR && 1208 2621 llai1 vp->v_type != VBLK && vp->v_type != VLNK); 1209 2621 llai1 1210 2621 llai1 if (vp->v_type == VDIR) 1211 5331 amw return (fs_seek(vp, ooff, noffp, ct)); 1212 2621 llai1 1213 2621 llai1 ASSERT(attrvp); 1214 5331 amw return (VOP_SEEK(attrvp, ooff, noffp, ct)); 1215 2621 llai1 } 1216 2621 llai1 1217 2621 llai1 /*ARGSUSED1*/ 1218 2621 llai1 static int 1219 2621 llai1 sdev_frlock(struct vnode *vp, int cmd, struct flock64 *bfp, int flag, 1220 5331 amw offset_t offset, struct flk_callback *flk_cbp, struct cred *cr, 1221 5331 amw caller_context_t *ct) 1222 2621 llai1 { 1223 2621 llai1 int error; 1224 2621 llai1 struct sdev_node *dv = VTOSDEV(vp); 1225 2621 llai1 1226 2621 llai1 ASSERT(dv); 1227 2621 llai1 ASSERT(dv->sdev_attrvp); 1228 2621 llai1 error = VOP_FRLOCK(dv->sdev_attrvp, cmd, bfp, flag, offset, 1229 5331 amw flk_cbp, cr, ct); 1230 2621 llai1 1231 2621 llai1 return (error); 1232 2621 llai1 } 1233 2621 llai1 1234 2621 llai1 static int 1235 5331 amw sdev_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, 1236 5331 amw caller_context_t *ct) 1237 2621 llai1 { 1238 2621 llai1 switch (cmd) { 1239 2621 llai1 case _PC_ACL_ENABLED: 1240 2621 llai1 *valp = SDEV_ACL_FLAVOR(vp); 1241 2621 llai1 return (0); 1242 2621 llai1 } 1243 2621 llai1 1244 5331 amw return (fs_pathconf(vp, cmd, valp, cr, ct)); 1245 2621 llai1 } 1246 2621 llai1 1247 2621 llai1 vnodeops_t *sdev_vnodeops; 1248 2621 llai1 1249 2621 llai1 const fs_operation_def_t sdev_vnodeops_tbl[] = { 1250 3898 rsb VOPNAME_OPEN, { .vop_open = sdev_open }, 1251 3898 rsb VOPNAME_CLOSE, { .vop_close = sdev_close }, 1252 3898 rsb VOPNAME_READ, { .vop_read = sdev_read }, 1253 3898 rsb VOPNAME_WRITE, { .vop_write = sdev_write }, 1254 3898 rsb VOPNAME_IOCTL, { .vop_ioctl = sdev_ioctl }, 1255 3898 rsb VOPNAME_GETATTR, { .vop_getattr = sdev_getattr }, 1256 3898 rsb VOPNAME_SETATTR, { .vop_setattr = sdev_setattr }, 1257 3898 rsb VOPNAME_ACCESS, { .vop_access = sdev_access }, 1258 3898 rsb VOPNAME_LOOKUP, { .vop_lookup = sdev_lookup }, 1259 3898 rsb VOPNAME_CREATE, { .vop_create = sdev_create }, 1260 3898 rsb VOPNAME_RENAME, { .vop_rename = sdev_rename }, 1261 3898 rsb VOPNAME_REMOVE, { .vop_remove = sdev_remove }, 1262 3898 rsb VOPNAME_MKDIR, { .vop_mkdir = sdev_mkdir }, 1263 3898 rsb VOPNAME_RMDIR, { .vop_rmdir = sdev_rmdir }, 1264 3898 rsb VOPNAME_READDIR, { .vop_readdir = sdev_readdir }, 1265 3898 rsb VOPNAME_SYMLINK, { .vop_symlink = sdev_symlink }, 1266 3898 rsb VOPNAME_READLINK, { .vop_readlink = sdev_readlink }, 1267 3898 rsb VOPNAME_INACTIVE, { .vop_inactive = sdev_inactive }, 1268 3898 rsb VOPNAME_FID, { .vop_fid = sdev_fid }, 1269 3898 rsb VOPNAME_RWLOCK, { .vop_rwlock = sdev_rwlock }, 1270 3898 rsb VOPNAME_RWUNLOCK, { .vop_rwunlock = sdev_rwunlock }, 1271 3898 rsb VOPNAME_SEEK, { .vop_seek = sdev_seek }, 1272 3898 rsb VOPNAME_FRLOCK, { .vop_frlock = sdev_frlock }, 1273 3898 rsb VOPNAME_PATHCONF, { .vop_pathconf = sdev_pathconf }, 1274 3898 rsb VOPNAME_SETSECATTR, { .vop_setsecattr = sdev_setsecattr }, 1275 3898 rsb VOPNAME_GETSECATTR, { .vop_getsecattr = sdev_getsecattr }, 1276 3898 rsb NULL, NULL 1277 2621 llai1 }; 1278 2621 llai1 1279 2621 llai1 int sdev_vnodeops_tbl_size = sizeof (sdev_vnodeops_tbl); 1280