1 1.10 andvar /* $NetBSD: autofs_vnops.c,v 1.10 2024/12/15 21:40:05 andvar Exp $ */ 2 1.1 christos /*- 3 1.1 christos * Copyright (c) 2017 The NetBSD Foundation, Inc. 4 1.1 christos * Copyright (c) 2016 The DragonFly Project 5 1.1 christos * Copyright (c) 2014 The FreeBSD Foundation 6 1.1 christos * All rights reserved. 7 1.1 christos * 8 1.1 christos * This code is derived from software contributed to The NetBSD Foundation 9 1.1 christos * by Tomohiro Kusumi <kusumi.tomohiro (at) gmail.com>. 10 1.1 christos * 11 1.1 christos * This software was developed by Edward Tomasz Napierala under sponsorship 12 1.1 christos * from the FreeBSD Foundation. 13 1.1 christos * 14 1.1 christos * Redistribution and use in source and binary forms, with or without 15 1.1 christos * modification, are permitted provided that the following conditions 16 1.1 christos * are met: 17 1.1 christos * 1. Redistributions of source code must retain the above copyright 18 1.1 christos * notice, this list of conditions and the following disclaimer. 19 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 20 1.1 christos * notice, this list of conditions and the following disclaimer in the 21 1.1 christos * documentation and/or other materials provided with the distribution. 22 1.1 christos * 23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 1.1 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27 1.1 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 1.1 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 1.1 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 1.1 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 1.1 christos * SUCH DAMAGE. 34 1.1 christos * 35 1.1 christos */ 36 1.1 christos #include <sys/cdefs.h> 37 1.10 andvar __KERNEL_RCSID(0, "$NetBSD: autofs_vnops.c,v 1.10 2024/12/15 21:40:05 andvar Exp $"); 38 1.1 christos 39 1.1 christos #include "autofs.h" 40 1.1 christos 41 1.1 christos #include <sys/stat.h> 42 1.1 christos #include <sys/dirent.h> 43 1.1 christos #include <sys/namei.h> 44 1.1 christos #include <miscfs/genfs/genfs.h> 45 1.1 christos 46 1.1 christos static int autofs_trigger_vn(struct vnode *vp, const char *path, 47 1.1 christos int pathlen, struct vnode **newvp); 48 1.1 christos 49 1.1 christos static int 50 1.1 christos autofs_access(void *v) 51 1.1 christos { 52 1.1 christos struct vop_access_args /* { 53 1.1 christos struct vnode *a_vp; 54 1.1 christos int a_mode; 55 1.1 christos kauth_cred_t a_cred; 56 1.1 christos } */ *ap = v; 57 1.1 christos struct vnode *vp __diagused = ap->a_vp; 58 1.1 christos 59 1.1 christos KASSERT(VOP_ISLOCKED(vp)); 60 1.1 christos /* 61 1.1 christos * Nothing to do here; the only kind of access control 62 1.1 christos * needed is in autofs_mkdir(). 63 1.1 christos */ 64 1.1 christos return 0; 65 1.1 christos } 66 1.1 christos 67 1.1 christos static int 68 1.1 christos autofs_getattr(void *v) 69 1.1 christos { 70 1.1 christos struct vop_getattr_args /* { 71 1.1 christos struct vnode *a_vp; 72 1.1 christos struct vattr *a_vap; 73 1.1 christos kauth_cred_t a_cred; 74 1.1 christos } */ *ap = v; 75 1.1 christos struct vnode *vp = ap->a_vp; 76 1.1 christos struct vattr *vap = ap->a_vap; 77 1.1 christos struct autofs_node *anp = VTOI(vp); 78 1.1 christos 79 1.1 christos KASSERT(vp->v_type == VDIR); 80 1.1 christos 81 1.1 christos /* 82 1.1 christos * The reason we must do this is that some tree-walking software, 83 1.1 christos * namely fts(3), assumes that stat(".") results will not change 84 1.1 christos * between chdir("subdir") and chdir(".."), and fails with ENOENT 85 1.1 christos * otherwise. 86 1.1 christos */ 87 1.1 christos if (autofs_mount_on_stat && 88 1.1 christos autofs_cached(anp, NULL, 0) == false && 89 1.1 christos autofs_ignore_thread() == false) { 90 1.4 tkusumi struct vnode *newvp = NULLVP; 91 1.1 christos int error = autofs_trigger_vn(vp, "", 0, &newvp); 92 1.1 christos if (error) 93 1.1 christos return error; 94 1.1 christos /* 95 1.1 christos * Already mounted here. 96 1.1 christos */ 97 1.1 christos if (newvp) { 98 1.1 christos error = VOP_GETATTR(newvp, vap, ap->a_cred); 99 1.1 christos vput(newvp); 100 1.1 christos return error; 101 1.1 christos } 102 1.1 christos } 103 1.1 christos 104 1.1 christos vattr_null(vap); 105 1.1 christos 106 1.1 christos vap->va_type = VDIR; 107 1.1 christos vap->va_mode = 0755; 108 1.1 christos vap->va_nlink = 3; 109 1.1 christos vap->va_uid = 0; 110 1.1 christos vap->va_gid = 0; 111 1.1 christos vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; 112 1.1 christos vap->va_fileid = anp->an_ino; 113 1.1 christos vap->va_size = S_BLKSIZE; 114 1.1 christos vap->va_blocksize = S_BLKSIZE; 115 1.1 christos vap->va_mtime = anp->an_ctime; 116 1.1 christos vap->va_atime = anp->an_ctime; 117 1.1 christos vap->va_ctime = anp->an_ctime; 118 1.1 christos vap->va_birthtime = anp->an_ctime; 119 1.1 christos vap->va_gen = 0; 120 1.1 christos vap->va_flags = 0; 121 1.1 christos vap->va_rdev = 0; 122 1.1 christos vap->va_bytes = S_BLKSIZE; 123 1.1 christos vap->va_filerev = 0; 124 1.1 christos vap->va_vaflags = 0; 125 1.1 christos vap->va_spare = 0; 126 1.1 christos 127 1.1 christos return 0; 128 1.1 christos } 129 1.1 christos 130 1.1 christos /* 131 1.1 christos * Unlock the vnode, request automountd(8) action, and then lock it back. 132 1.1 christos * If anything got mounted on top of the vnode, return the new filesystem's 133 1.1 christos * root vnode in 'newvp', locked. A caller needs to vput() the 'newvp'. 134 1.1 christos */ 135 1.1 christos static int 136 1.1 christos autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen, 137 1.1 christos struct vnode **newvp) 138 1.1 christos { 139 1.1 christos struct autofs_node *anp; 140 1.1 christos int error, lock_flags; 141 1.1 christos 142 1.1 christos anp = vp->v_data; 143 1.1 christos 144 1.1 christos /* 145 1.10 andvar * Release the vnode lock, so that other operations, in particular 146 1.1 christos * mounting a filesystem on top of it, can proceed. Increase use 147 1.1 christos * count, to prevent the vnode from being deallocated and to prevent 148 1.1 christos * filesystem from being unmounted. 149 1.1 christos */ 150 1.1 christos lock_flags = VOP_ISLOCKED(vp); 151 1.1 christos vref(vp); 152 1.1 christos VOP_UNLOCK(vp); 153 1.1 christos 154 1.1 christos mutex_enter(&autofs_softc->sc_lock); 155 1.1 christos 156 1.1 christos /* 157 1.1 christos * Workaround for mounting the same thing multiple times; revisit. 158 1.1 christos */ 159 1.1 christos if (vp->v_mountedhere) { 160 1.1 christos error = 0; 161 1.1 christos goto mounted; 162 1.1 christos } 163 1.1 christos 164 1.1 christos error = autofs_trigger(anp, path, pathlen); 165 1.1 christos mounted: 166 1.1 christos mutex_exit(&autofs_softc->sc_lock); 167 1.1 christos vn_lock(vp, lock_flags | LK_RETRY); 168 1.1 christos vrele(vp); 169 1.1 christos 170 1.1 christos if (error) 171 1.1 christos return error; 172 1.1 christos 173 1.1 christos if (!vp->v_mountedhere) { 174 1.4 tkusumi *newvp = NULLVP; 175 1.1 christos return 0; 176 1.1 christos } else { 177 1.1 christos /* 178 1.1 christos * If the operation that succeeded was mount, then mark 179 1.1 christos * the node as non-cached. Otherwise, if someone unmounts 180 1.1 christos * the filesystem before the cache times out, we will fail 181 1.1 christos * to trigger. 182 1.1 christos */ 183 1.1 christos autofs_node_uncache(anp); 184 1.1 christos } 185 1.1 christos 186 1.5 ad error = VFS_ROOT(vp->v_mountedhere, LK_EXCLUSIVE, newvp); 187 1.1 christos if (error) { 188 1.1 christos AUTOFS_WARN("VFS_ROOT() failed with error %d", error); 189 1.1 christos return error; 190 1.1 christos } 191 1.1 christos 192 1.1 christos return 0; 193 1.1 christos } 194 1.1 christos 195 1.1 christos static int 196 1.1 christos autofs_lookup(void *v) 197 1.1 christos { 198 1.1 christos struct vop_lookup_v2_args /* { 199 1.1 christos struct vnode *a_dvp; 200 1.1 christos struct vnode **a_vpp; 201 1.1 christos struct componentname *a_cnp; 202 1.1 christos } */ *ap = v; 203 1.1 christos struct vnode *dvp = ap->a_dvp; 204 1.1 christos struct vnode **vpp = ap->a_vpp; 205 1.1 christos struct componentname *cnp = ap->a_cnp; 206 1.1 christos struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 207 1.1 christos struct autofs_node *anp, *child; 208 1.1 christos int cachefound; 209 1.1 christos int error; 210 1.1 christos const bool lastcn __diagused = (cnp->cn_flags & ISLASTCN) != 0; 211 1.1 christos 212 1.1 christos KASSERT(VOP_ISLOCKED(dvp)); 213 1.1 christos 214 1.1 christos anp = VTOI(dvp); 215 1.4 tkusumi *vpp = NULLVP; 216 1.1 christos 217 1.1 christos /* Check accessibility of directory. */ 218 1.1 christos KASSERT(!VOP_ACCESS(dvp, VEXEC, cnp->cn_cred)); 219 1.1 christos 220 1.1 christos /* 221 1.1 christos * Avoid doing a linear scan of the directory if the requested 222 1.1 christos * directory/name couple is already in the cache. 223 1.1 christos */ 224 1.1 christos cachefound = cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, 225 1.1 christos cnp->cn_nameiop, cnp->cn_flags, NULL, vpp); 226 1.1 christos if (cachefound && *vpp == NULLVP) { 227 1.1 christos /* Negative cache hit. */ 228 1.1 christos error = ENOENT; 229 1.1 christos goto out; 230 1.1 christos } else if (cachefound) { 231 1.1 christos error = 0; 232 1.1 christos goto out; 233 1.1 christos } 234 1.1 christos 235 1.1 christos if (cnp->cn_flags & ISDOTDOT) { 236 1.1 christos struct autofs_node *parent; 237 1.1 christos /* 238 1.1 christos * Lookup of ".." case. 239 1.1 christos */ 240 1.1 christos KASSERT(!(lastcn && cnp->cn_nameiop == RENAME)); 241 1.1 christos parent = anp->an_parent; 242 1.1 christos if (!parent) { 243 1.1 christos error = ENOENT; 244 1.1 christos goto out; 245 1.1 christos } 246 1.1 christos 247 1.1 christos error = vcache_get(dvp->v_mount, &parent, sizeof(parent), vpp); 248 1.1 christos goto out; 249 1.1 christos } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 250 1.1 christos /* 251 1.1 christos * Lookup of "." case. 252 1.1 christos */ 253 1.1 christos KASSERT(!(lastcn && cnp->cn_nameiop == RENAME)); 254 1.1 christos vref(dvp); 255 1.1 christos *vpp = dvp; 256 1.1 christos error = 0; 257 1.1 christos goto done; 258 1.1 christos } 259 1.1 christos 260 1.1 christos if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false && 261 1.1 christos autofs_ignore_thread() == false) { 262 1.4 tkusumi struct vnode *newvp = NULLVP; 263 1.1 christos error = autofs_trigger_vn(dvp, cnp->cn_nameptr, cnp->cn_namelen, 264 1.1 christos &newvp); 265 1.1 christos if (error) 266 1.1 christos return error; 267 1.1 christos /* 268 1.1 christos * Already mounted here. 269 1.1 christos */ 270 1.1 christos if (newvp) { 271 1.1 christos error = VOP_LOOKUP(newvp, vpp, cnp); 272 1.1 christos vput(newvp); 273 1.1 christos return error; 274 1.1 christos } 275 1.1 christos } 276 1.1 christos 277 1.1 christos mutex_enter(&->am_lock); 278 1.1 christos error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child); 279 1.1 christos if (error) { 280 1.1 christos if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) { 281 1.1 christos mutex_exit(&->am_lock); 282 1.1 christos error = EJUSTRETURN; 283 1.1 christos goto done; 284 1.1 christos } 285 1.1 christos 286 1.1 christos mutex_exit(&->am_lock); 287 1.1 christos error = ENOENT; 288 1.1 christos goto done; 289 1.1 christos } 290 1.1 christos 291 1.1 christos /* 292 1.1 christos * Dropping the node here is ok, because we never remove nodes. 293 1.1 christos */ 294 1.1 christos mutex_exit(&->am_lock); 295 1.1 christos 296 1.1 christos /* Get a vnode for the matching entry. */ 297 1.1 christos error = vcache_get(dvp->v_mount, &child, sizeof(child), vpp); 298 1.1 christos done: 299 1.1 christos /* 300 1.1 christos * Cache the result, unless request was for creation (as it does 301 1.1 christos * not improve the performance). 302 1.1 christos */ 303 1.1 christos if (cnp->cn_nameiop != CREATE) { 304 1.1 christos cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, 305 1.1 christos cnp->cn_flags); 306 1.1 christos } 307 1.1 christos out: 308 1.1 christos KASSERT(VOP_ISLOCKED(dvp)); 309 1.1 christos 310 1.1 christos return error; 311 1.1 christos } 312 1.1 christos 313 1.1 christos static int 314 1.1 christos autofs_open(void *v) 315 1.1 christos { 316 1.1 christos struct vop_open_args /* { 317 1.1 christos struct vnode *a_vp; 318 1.1 christos int a_mode; 319 1.1 christos kauth_cred_t a_cred; 320 1.1 christos } */ *ap = v; 321 1.1 christos struct vnode *vp __diagused = ap->a_vp; 322 1.1 christos 323 1.1 christos KASSERT(VOP_ISLOCKED(vp)); 324 1.1 christos return 0; 325 1.1 christos } 326 1.1 christos 327 1.1 christos static int 328 1.1 christos autofs_close(void *v) 329 1.1 christos { 330 1.1 christos struct vop_close_args /* { 331 1.1 christos struct vnode *a_vp; 332 1.1 christos int a_fflag; 333 1.1 christos kauth_cred_t a_cred; 334 1.1 christos } */ *ap = v; 335 1.1 christos struct vnode *vp __diagused = ap->a_vp; 336 1.1 christos 337 1.1 christos KASSERT(VOP_ISLOCKED(vp)); 338 1.1 christos return 0; 339 1.1 christos } 340 1.1 christos 341 1.1 christos static int 342 1.1 christos autofs_fsync(void *v) 343 1.1 christos { 344 1.1 christos struct vop_fsync_args /* { 345 1.1 christos struct vnode *a_vp; 346 1.1 christos kauth_cred_t a_cred; 347 1.1 christos int a_flags; 348 1.1 christos off_t a_offlo; 349 1.1 christos off_t a_offhi; 350 1.1 christos struct lwp *a_l; 351 1.1 christos } */ *ap = v; 352 1.1 christos struct vnode *vp __diagused = ap->a_vp; 353 1.1 christos 354 1.1 christos /* Nothing to do. Should be up to date. */ 355 1.1 christos KASSERT(VOP_ISLOCKED(vp)); 356 1.1 christos return 0; 357 1.1 christos } 358 1.1 christos 359 1.1 christos static int 360 1.1 christos autofs_mkdir(void *v) 361 1.1 christos { 362 1.1 christos struct vop_mkdir_v3_args /* { 363 1.1 christos struct vnode *a_dvp; 364 1.1 christos struct vnode **a_vpp; 365 1.1 christos struct componentname *a_cnp; 366 1.1 christos struct vattr *a_vap; 367 1.1 christos } */ *ap = v; 368 1.1 christos struct vnode *dvp = ap->a_dvp; 369 1.1 christos struct vnode **vpp = ap->a_vpp; 370 1.1 christos struct componentname *cnp = ap->a_cnp; 371 1.1 christos struct autofs_mount *amp = VFSTOAUTOFS(dvp->v_mount); 372 1.1 christos struct autofs_node *anp = VTOI(dvp); 373 1.1 christos struct autofs_node *child = NULL; 374 1.1 christos int error; 375 1.1 christos 376 1.1 christos KASSERT(ap->a_vap->va_type == VDIR); 377 1.1 christos 378 1.1 christos /* 379 1.1 christos * Do not allow mkdir() if the calling thread is not 380 1.1 christos * automountd(8) descendant. 381 1.1 christos */ 382 1.1 christos if (autofs_ignore_thread() == false) 383 1.1 christos return EPERM; 384 1.1 christos 385 1.1 christos mutex_enter(&->am_lock); 386 1.1 christos error = autofs_node_new(anp, amp, cnp->cn_nameptr, cnp->cn_namelen, 387 1.1 christos &child); 388 1.1 christos if (error) { 389 1.1 christos mutex_exit(&->am_lock); 390 1.1 christos return error; 391 1.1 christos } 392 1.1 christos mutex_exit(&->am_lock); 393 1.1 christos 394 1.1 christos return vcache_get(amp->am_mp, &child, sizeof(child), vpp); 395 1.1 christos } 396 1.1 christos 397 1.1 christos static int 398 1.1 christos autofs_print(void *v) 399 1.1 christos { 400 1.1 christos struct vop_print_args /* { 401 1.1 christos struct vnode *a_vp; 402 1.1 christos } */ *ap = v; 403 1.1 christos struct vnode *vp = ap->a_vp; 404 1.1 christos struct autofs_node *anp = VTOI(vp); 405 1.1 christos 406 1.1 christos printf("tag VT_AUTOFS, node %p, ino %jd, name %s, cached %d, " 407 1.1 christos "retries %d, wildcards %d", 408 1.1 christos anp, (intmax_t)anp->an_ino, anp->an_name, anp->an_cached, 409 1.1 christos anp->an_retries, anp->an_wildcards); 410 1.1 christos printf("\n"); 411 1.1 christos 412 1.1 christos return 0; 413 1.1 christos } 414 1.1 christos 415 1.1 christos static int 416 1.2 tkusumi autofs_readdir_one(struct uio *uio, const char *name, ino_t ino) 417 1.1 christos { 418 1.1 christos struct dirent dirent; 419 1.1 christos 420 1.1 christos dirent.d_fileno = ino; 421 1.1 christos dirent.d_type = DT_DIR; 422 1.1 christos strlcpy(dirent.d_name, name, sizeof(dirent.d_name)); 423 1.1 christos dirent.d_namlen = strlen(dirent.d_name); 424 1.1 christos dirent.d_reclen = _DIRENT_SIZE(&dirent); 425 1.1 christos 426 1.1 christos if (!uio) 427 1.1 christos return 0; 428 1.1 christos 429 1.1 christos if (uio->uio_resid < dirent.d_reclen) 430 1.1 christos return EINVAL; 431 1.1 christos 432 1.1 christos return uiomove(&dirent, dirent.d_reclen, uio); 433 1.1 christos } 434 1.1 christos 435 1.1 christos static size_t 436 1.1 christos autofs_dirent_reclen(const char *name) 437 1.1 christos { 438 1.2 tkusumi struct dirent dirent; 439 1.1 christos 440 1.2 tkusumi strlcpy(dirent.d_name, name, sizeof(dirent.d_name)); 441 1.2 tkusumi dirent.d_namlen = strlen(dirent.d_name); 442 1.1 christos 443 1.2 tkusumi return _DIRENT_SIZE(&dirent); 444 1.1 christos } 445 1.1 christos 446 1.1 christos static int 447 1.1 christos autofs_readdir(void *v) 448 1.1 christos { 449 1.1 christos struct vop_readdir_args /* { 450 1.1 christos struct vnode *a_vp; 451 1.1 christos struct uio *a_uio; 452 1.1 christos kauth_cred_t a_cred; 453 1.1 christos int *a_eofflag; 454 1.1 christos off_t **a_cookies; 455 1.1 christos int *ncookies; 456 1.1 christos } */ *ap = v; 457 1.1 christos struct vnode *vp = ap->a_vp; 458 1.1 christos struct uio *uio = ap->a_uio; 459 1.1 christos size_t initial_resid = ap->a_uio->uio_resid; 460 1.1 christos struct autofs_mount *amp = VFSTOAUTOFS(vp->v_mount); 461 1.1 christos struct autofs_node *anp = VTOI(vp); 462 1.1 christos struct autofs_node *child; 463 1.2 tkusumi size_t reclens; 464 1.1 christos int error; 465 1.1 christos 466 1.1 christos if (vp->v_type != VDIR) 467 1.1 christos return ENOTDIR; 468 1.1 christos 469 1.1 christos if (autofs_cached(anp, NULL, 0) == false && 470 1.1 christos autofs_ignore_thread() == false) { 471 1.4 tkusumi struct vnode *newvp = NULLVP; 472 1.1 christos error = autofs_trigger_vn(vp, "", 0, &newvp); 473 1.1 christos if (error) 474 1.1 christos return error; 475 1.1 christos /* 476 1.1 christos * Already mounted here. 477 1.1 christos */ 478 1.1 christos if (newvp) { 479 1.1 christos error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred, 480 1.1 christos ap->a_eofflag, ap->a_cookies, ap->a_ncookies); 481 1.1 christos vput(newvp); 482 1.1 christos return error; 483 1.1 christos } 484 1.1 christos } 485 1.1 christos 486 1.1 christos if (uio->uio_offset < 0) 487 1.1 christos return EINVAL; 488 1.1 christos 489 1.1 christos if (ap->a_eofflag) 490 1.1 christos *ap->a_eofflag = FALSE; 491 1.1 christos 492 1.1 christos /* 493 1.1 christos * Write out the directory entry for ".". 494 1.1 christos */ 495 1.1 christos if (uio->uio_offset == 0) { 496 1.2 tkusumi error = autofs_readdir_one(uio, ".", anp->an_ino); 497 1.1 christos if (error) 498 1.1 christos goto out; 499 1.1 christos } 500 1.1 christos reclens = autofs_dirent_reclen("."); 501 1.1 christos 502 1.1 christos /* 503 1.1 christos * Write out the directory entry for "..". 504 1.1 christos */ 505 1.1 christos if (uio->uio_offset <= reclens) { 506 1.1 christos if (uio->uio_offset != reclens) 507 1.1 christos return EINVAL; 508 1.1 christos error = autofs_readdir_one(uio, "..", 509 1.2 tkusumi anp->an_parent ? anp->an_parent->an_ino : anp->an_ino); 510 1.1 christos if (error) 511 1.1 christos goto out; 512 1.1 christos } 513 1.1 christos reclens += autofs_dirent_reclen(".."); 514 1.1 christos 515 1.1 christos /* 516 1.1 christos * Write out the directory entries for subdirectories. 517 1.1 christos */ 518 1.1 christos mutex_enter(&->am_lock); 519 1.1 christos RB_FOREACH(child, autofs_node_tree, &anp->an_children) { 520 1.1 christos /* 521 1.1 christos * Check the offset to skip entries returned by previous 522 1.1 christos * calls to getdents(). 523 1.1 christos */ 524 1.1 christos if (uio->uio_offset > reclens) { 525 1.1 christos reclens += autofs_dirent_reclen(child->an_name); 526 1.1 christos continue; 527 1.1 christos } 528 1.1 christos 529 1.1 christos /* 530 1.1 christos * Prevent seeking into the middle of dirent. 531 1.1 christos */ 532 1.1 christos if (uio->uio_offset != reclens) { 533 1.1 christos mutex_exit(&->am_lock); 534 1.1 christos return EINVAL; 535 1.1 christos } 536 1.1 christos 537 1.2 tkusumi error = autofs_readdir_one(uio, child->an_name, child->an_ino); 538 1.2 tkusumi reclens += autofs_dirent_reclen(child->an_name); 539 1.1 christos if (error) { 540 1.1 christos mutex_exit(&->am_lock); 541 1.1 christos goto out; 542 1.1 christos } 543 1.1 christos } 544 1.1 christos mutex_exit(&->am_lock); 545 1.1 christos 546 1.1 christos if (ap->a_eofflag) 547 1.1 christos *ap->a_eofflag = TRUE; 548 1.1 christos 549 1.1 christos return 0; 550 1.1 christos out: 551 1.1 christos /* 552 1.1 christos * Return error if the initial buffer was too small to do anything. 553 1.1 christos */ 554 1.1 christos if (uio->uio_resid == initial_resid) 555 1.1 christos return error; 556 1.1 christos 557 1.1 christos /* 558 1.1 christos * Don't return an error if we managed to copy out some entries. 559 1.1 christos */ 560 1.2 tkusumi if (uio->uio_resid < initial_resid) 561 1.1 christos return 0; 562 1.1 christos 563 1.1 christos return error; 564 1.1 christos } 565 1.1 christos 566 1.1 christos static int 567 1.1 christos autofs_reclaim(void *v) 568 1.1 christos { 569 1.1 christos struct vop_reclaim_v2_args /* { 570 1.1 christos struct vnode *a_vp; 571 1.1 christos } */ *ap = v; 572 1.1 christos struct vnode *vp = ap->a_vp; 573 1.1 christos struct autofs_node *anp = VTOI(vp); 574 1.1 christos 575 1.1 christos VOP_UNLOCK(vp); 576 1.1 christos 577 1.1 christos /* 578 1.1 christos * We do not free autofs_node here; instead we are 579 1.1 christos * destroying them in autofs_node_delete(). 580 1.1 christos */ 581 1.4 tkusumi anp->an_vnode = NULLVP; 582 1.1 christos vp->v_data = NULL; 583 1.1 christos 584 1.1 christos return 0; 585 1.1 christos } 586 1.1 christos 587 1.1 christos int (**autofs_vnodeop_p)(void *); 588 1.1 christos static const struct vnodeopv_entry_desc autofs_vnodeop_entries[] = { 589 1.1 christos { &vop_default_desc, vn_default_error }, 590 1.7 dholland { &vop_parsepath_desc, genfs_parsepath }, 591 1.1 christos { &vop_lookup_desc, autofs_lookup }, 592 1.1 christos { &vop_open_desc, autofs_open }, 593 1.1 christos { &vop_close_desc, autofs_close }, 594 1.1 christos { &vop_access_desc, autofs_access }, 595 1.6 christos { &vop_accessx_desc, genfs_accessx }, 596 1.1 christos { &vop_getattr_desc, autofs_getattr }, 597 1.1 christos { &vop_fsync_desc, autofs_fsync }, 598 1.9 mlelstv { &vop_seek_desc, genfs_seek }, 599 1.1 christos { &vop_mkdir_desc, autofs_mkdir }, 600 1.1 christos { &vop_readdir_desc, autofs_readdir }, 601 1.1 christos { &vop_reclaim_desc, autofs_reclaim }, 602 1.1 christos { &vop_lock_desc, genfs_lock }, 603 1.1 christos { &vop_unlock_desc, genfs_unlock }, 604 1.1 christos { &vop_print_desc, autofs_print }, 605 1.1 christos { &vop_islocked_desc, genfs_islocked }, 606 1.1 christos { &vop_getpages_desc, genfs_getpages }, 607 1.1 christos { &vop_putpages_desc, genfs_putpages }, 608 1.8 mlelstv { &vop_pathconf_desc, genfs_pathconf }, 609 1.1 christos { NULL, NULL } 610 1.1 christos }; 611 1.1 christos 612 1.1 christos const struct vnodeopv_desc autofs_vnodeop_opv_desc = { 613 1.1 christos &autofs_vnodeop_p, autofs_vnodeop_entries 614 1.1 christos }; 615 1.1 christos 616 1.1 christos int 617 1.1 christos autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp, 618 1.1 christos const char *name, int namelen, struct autofs_node **anpp) 619 1.1 christos { 620 1.1 christos struct autofs_node *anp; 621 1.1 christos 622 1.1 christos KASSERT(mutex_owned(&->am_lock)); 623 1.1 christos 624 1.1 christos if (parent) { 625 1.1 christos KASSERT(mutex_owned(&parent->an_mount->am_lock)); 626 1.1 christos KASSERT(autofs_node_find(parent, name, namelen, NULL) == 627 1.1 christos ENOENT); 628 1.1 christos } 629 1.1 christos 630 1.1 christos anp = pool_get(&autofs_node_pool, PR_WAITOK); 631 1.1 christos anp->an_name = autofs_strndup(name, namelen, KM_SLEEP); 632 1.1 christos anp->an_ino = amp->am_last_ino++; 633 1.1 christos callout_init(&anp->an_callout, 0); 634 1.1 christos getnanotime(&anp->an_ctime); 635 1.1 christos anp->an_parent = parent; 636 1.1 christos anp->an_mount = amp; 637 1.4 tkusumi anp->an_vnode = NULLVP; 638 1.1 christos anp->an_cached = false; 639 1.1 christos anp->an_wildcards = false; 640 1.1 christos anp->an_retries = 0; 641 1.1 christos if (parent) 642 1.1 christos RB_INSERT(autofs_node_tree, &parent->an_children, anp); 643 1.1 christos RB_INIT(&anp->an_children); 644 1.1 christos 645 1.1 christos *anpp = anp; 646 1.1 christos return 0; 647 1.1 christos } 648 1.1 christos 649 1.1 christos int 650 1.1 christos autofs_node_find(struct autofs_node *parent, const char *name, 651 1.1 christos int namelen, struct autofs_node **anpp) 652 1.1 christos { 653 1.1 christos struct autofs_node *anp, find; 654 1.1 christos int error; 655 1.1 christos 656 1.1 christos KASSERT(mutex_owned(&parent->an_mount->am_lock)); 657 1.1 christos 658 1.1 christos find.an_name = autofs_strndup(name, namelen, KM_SLEEP); 659 1.1 christos anp = RB_FIND(autofs_node_tree, &parent->an_children, &find); 660 1.1 christos if (anp) { 661 1.1 christos error = 0; 662 1.1 christos if (anpp) 663 1.1 christos *anpp = anp; 664 1.1 christos } else { 665 1.1 christos error = ENOENT; 666 1.1 christos } 667 1.1 christos 668 1.1 christos kmem_strfree(find.an_name); 669 1.1 christos 670 1.1 christos return error; 671 1.1 christos } 672 1.1 christos 673 1.1 christos void 674 1.1 christos autofs_node_delete(struct autofs_node *anp) 675 1.1 christos { 676 1.1 christos 677 1.1 christos KASSERT(mutex_owned(&anp->an_mount->am_lock)); 678 1.1 christos KASSERT(RB_EMPTY(&anp->an_children)); 679 1.1 christos 680 1.1 christos callout_halt(&anp->an_callout, NULL); 681 1.1 christos 682 1.1 christos if (anp->an_parent) 683 1.1 christos RB_REMOVE(autofs_node_tree, &anp->an_parent->an_children, anp); 684 1.1 christos 685 1.1 christos kmem_strfree(anp->an_name); 686 1.1 christos pool_put(&autofs_node_pool, anp); 687 1.1 christos } 688