1171802Sdelphij/* $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $ */ 2170808Sdelphij 3182739Sdelphij/*- 4171802Sdelphij * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. 5170808Sdelphij * All rights reserved. 6170808Sdelphij * 7170808Sdelphij * This code is derived from software contributed to The NetBSD Foundation 8170808Sdelphij * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9170808Sdelphij * 2005 program. 10170808Sdelphij * 11170808Sdelphij * Redistribution and use in source and binary forms, with or without 12170808Sdelphij * modification, are permitted provided that the following conditions 13170808Sdelphij * are met: 14170808Sdelphij * 1. Redistributions of source code must retain the above copyright 15170808Sdelphij * notice, this list of conditions and the following disclaimer. 16170808Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 17170808Sdelphij * notice, this list of conditions and the following disclaimer in the 18170808Sdelphij * documentation and/or other materials provided with the distribution. 19170808Sdelphij * 20170808Sdelphij * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21170808Sdelphij * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22170808Sdelphij * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23170808Sdelphij * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24170808Sdelphij * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25170808Sdelphij * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26170808Sdelphij * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27170808Sdelphij * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28170808Sdelphij * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29170808Sdelphij * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30170808Sdelphij * POSSIBILITY OF SUCH DAMAGE. 31170808Sdelphij */ 32170808Sdelphij 33170808Sdelphij/* 34170808Sdelphij * tmpfs vnode interface. 35170808Sdelphij */ 36170808Sdelphij#include <sys/cdefs.h> 37170808Sdelphij__FBSDID("$FreeBSD: stable/10/sys/fs/tmpfs/tmpfs_vnops.c 313095 2017-02-02 13:39:11Z kib $"); 38170808Sdelphij 39170808Sdelphij#include <sys/param.h> 40170808Sdelphij#include <sys/fcntl.h> 41170808Sdelphij#include <sys/lockf.h> 42248084Sattilio#include <sys/lock.h> 43170808Sdelphij#include <sys/namei.h> 44170808Sdelphij#include <sys/priv.h> 45170808Sdelphij#include <sys/proc.h> 46248084Sattilio#include <sys/rwlock.h> 47197850Sdelphij#include <sys/sched.h> 48170808Sdelphij#include <sys/stat.h> 49170808Sdelphij#include <sys/systm.h> 50232960Sgleb#include <sys/sysctl.h> 51170808Sdelphij#include <sys/unistd.h> 52170808Sdelphij#include <sys/vnode.h> 53170808Sdelphij 54170808Sdelphij#include <vm/vm.h> 55239065Skib#include <vm/vm_param.h> 56170808Sdelphij#include <vm/vm_object.h> 57170808Sdelphij#include <vm/vm_page.h> 58170808Sdelphij#include <vm/vm_pager.h> 59188929Salc 60170808Sdelphij#include <fs/tmpfs/tmpfs_vnops.h> 61170808Sdelphij#include <fs/tmpfs/tmpfs.h> 62170808Sdelphij 63232960SglebSYSCTL_DECL(_vfs_tmpfs); 64232960Sgleb 65232960Sglebstatic volatile int tmpfs_rename_restarts; 66232960SglebSYSCTL_INT(_vfs_tmpfs, OID_AUTO, rename_restarts, CTLFLAG_RD, 67232960Sgleb __DEVOLATILE(int *, &tmpfs_rename_restarts), 0, 68232960Sgleb "Times rename had to restart due to lock contention"); 69232960Sgleb 70171069Sdelphijstatic int 71269173Skibtmpfs_vn_get_ino_alloc(struct mount *mp, void *arg, int lkflags, 72269173Skib struct vnode **rvp) 73269173Skib{ 74269173Skib 75269173Skib return (tmpfs_alloc_vp(mp, arg, lkflags, rvp)); 76269173Skib} 77269173Skib 78269173Skibstatic int 79313095Skibtmpfs_lookup1(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) 80170808Sdelphij{ 81170808Sdelphij struct tmpfs_dirent *de; 82313092Skib struct tmpfs_node *dnode, *pnode; 83313092Skib struct tmpfs_mount *tm; 84269173Skib int error; 85170808Sdelphij 86170808Sdelphij dnode = VP_TO_TMPFS_DIR(dvp); 87170808Sdelphij *vpp = NULLVP; 88170808Sdelphij 89170808Sdelphij /* Check accessibility of requested node as a first step. */ 90191990Sattilio error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_thread); 91170808Sdelphij if (error != 0) 92170808Sdelphij goto out; 93170808Sdelphij 94170808Sdelphij /* We cannot be requesting the parent directory of the root node. */ 95170808Sdelphij MPASS(IMPLIES(dnode->tn_type == VDIR && 96170808Sdelphij dnode->tn_dir.tn_parent == dnode, 97170808Sdelphij !(cnp->cn_flags & ISDOTDOT))); 98170808Sdelphij 99197953Sdelphij TMPFS_ASSERT_LOCKED(dnode); 100197953Sdelphij if (dnode->tn_dir.tn_parent == NULL) { 101197953Sdelphij error = ENOENT; 102197953Sdelphij goto out; 103197953Sdelphij } 104170808Sdelphij if (cnp->cn_flags & ISDOTDOT) { 105313092Skib tm = VFS_TO_TMPFS(dvp->v_mount); 106313092Skib pnode = dnode->tn_dir.tn_parent; 107313092Skib tmpfs_ref_node(pnode); 108269173Skib error = vn_vget_ino_gen(dvp, tmpfs_vn_get_ino_alloc, 109313092Skib pnode, cnp->cn_lkflags, vpp); 110313092Skib tmpfs_free_node(tm, pnode); 111269173Skib if (error != 0) 112269173Skib goto out; 113170808Sdelphij } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 114170808Sdelphij VREF(dvp); 115170808Sdelphij *vpp = dvp; 116170808Sdelphij error = 0; 117170808Sdelphij } else { 118188318Skib de = tmpfs_dir_lookup(dnode, NULL, cnp); 119211598Sed if (de != NULL && de->td_node == NULL) 120211598Sed cnp->cn_flags |= ISWHITEOUT; 121211598Sed if (de == NULL || de->td_node == NULL) { 122312803Skib /* 123312803Skib * The entry was not found in the directory. 124170808Sdelphij * This is OK if we are creating or renaming an 125170808Sdelphij * entry and are working on the last component of 126312803Skib * the path name. 127312803Skib */ 128170808Sdelphij if ((cnp->cn_flags & ISLASTCN) && 129170808Sdelphij (cnp->cn_nameiop == CREATE || \ 130211598Sed cnp->cn_nameiop == RENAME || 131211598Sed (cnp->cn_nameiop == DELETE && 132211598Sed cnp->cn_flags & DOWHITEOUT && 133211598Sed cnp->cn_flags & ISWHITEOUT))) { 134170808Sdelphij error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 135170808Sdelphij cnp->cn_thread); 136170808Sdelphij if (error != 0) 137170808Sdelphij goto out; 138170808Sdelphij 139312803Skib /* 140312803Skib * Keep the component name in the buffer for 141312803Skib * future uses. 142312803Skib */ 143170808Sdelphij cnp->cn_flags |= SAVENAME; 144170808Sdelphij 145170808Sdelphij error = EJUSTRETURN; 146170808Sdelphij } else 147170808Sdelphij error = ENOENT; 148170808Sdelphij } else { 149170808Sdelphij struct tmpfs_node *tnode; 150170808Sdelphij 151312803Skib /* 152312803Skib * The entry was found, so get its associated 153312803Skib * tmpfs_node. 154312803Skib */ 155170808Sdelphij tnode = de->td_node; 156170808Sdelphij 157312803Skib /* 158312803Skib * If we are not at the last path component and 159170808Sdelphij * found a non-directory or non-link entry (which 160170808Sdelphij * may itself be pointing to a directory), raise 161312803Skib * an error. 162312803Skib */ 163170808Sdelphij if ((tnode->tn_type != VDIR && 164170808Sdelphij tnode->tn_type != VLNK) && 165170808Sdelphij !(cnp->cn_flags & ISLASTCN)) { 166170808Sdelphij error = ENOTDIR; 167170808Sdelphij goto out; 168170808Sdelphij } 169170808Sdelphij 170312803Skib /* 171312803Skib * If we are deleting or renaming the entry, keep 172170808Sdelphij * track of its tmpfs_dirent so that it can be 173312803Skib * easily deleted later. 174312803Skib */ 175170808Sdelphij if ((cnp->cn_flags & ISLASTCN) && 176170808Sdelphij (cnp->cn_nameiop == DELETE || 177170808Sdelphij cnp->cn_nameiop == RENAME)) { 178170808Sdelphij error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 179170808Sdelphij cnp->cn_thread); 180170808Sdelphij if (error != 0) 181170808Sdelphij goto out; 182171070Sdelphij 183170808Sdelphij /* Allocate a new vnode on the matching entry. */ 184171799Sdelphij error = tmpfs_alloc_vp(dvp->v_mount, tnode, 185269172Skib cnp->cn_lkflags, vpp); 186170808Sdelphij if (error != 0) 187170808Sdelphij goto out; 188170808Sdelphij 189170808Sdelphij if ((dnode->tn_mode & S_ISTXT) && 190312803Skib VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, 191312803Skib cnp->cn_thread) && VOP_ACCESS(*vpp, VADMIN, 192312803Skib cnp->cn_cred, cnp->cn_thread)) { 193170808Sdelphij error = EPERM; 194170808Sdelphij vput(*vpp); 195170808Sdelphij *vpp = NULL; 196170808Sdelphij goto out; 197171070Sdelphij } 198170808Sdelphij cnp->cn_flags |= SAVENAME; 199171799Sdelphij } else { 200171799Sdelphij error = tmpfs_alloc_vp(dvp->v_mount, tnode, 201269176Skib cnp->cn_lkflags, vpp); 202269176Skib if (error != 0) 203269176Skib goto out; 204170808Sdelphij } 205170808Sdelphij } 206170808Sdelphij } 207170808Sdelphij 208312803Skib /* 209312803Skib * Store the result of this lookup in the cache. Avoid this if the 210170808Sdelphij * request was for creation, as it does not improve timings on 211312803Skib * emprical tests. 212312803Skib */ 213313095Skib if ((cnp->cn_flags & MAKEENTRY) != 0 && tmpfs_use_nc(dvp)) 214170808Sdelphij cache_enter(dvp, *vpp, cnp); 215170808Sdelphij 216170808Sdelphijout: 217312803Skib /* 218312803Skib * If there were no errors, *vpp cannot be null and it must be 219312803Skib * locked. 220312803Skib */ 221176559Sattilio MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp))); 222170808Sdelphij 223312803Skib return (error); 224170808Sdelphij} 225170808Sdelphij 226171069Sdelphijstatic int 227313095Skibtmpfs_cached_lookup(struct vop_cachedlookup_args *v) 228313095Skib{ 229313095Skib 230313095Skib return (tmpfs_lookup1(v->a_dvp, v->a_vpp, v->a_cnp)); 231313095Skib} 232313095Skib 233313095Skibstatic int 234313095Skibtmpfs_lookup(struct vop_lookup_args *v) 235313095Skib{ 236313095Skib 237313095Skib return (tmpfs_lookup1(v->a_dvp, v->a_vpp, v->a_cnp)); 238313095Skib} 239313095Skib 240313095Skibstatic int 241170808Sdelphijtmpfs_create(struct vop_create_args *v) 242170808Sdelphij{ 243170808Sdelphij struct vnode *dvp = v->a_dvp; 244170808Sdelphij struct vnode **vpp = v->a_vpp; 245170808Sdelphij struct componentname *cnp = v->a_cnp; 246170808Sdelphij struct vattr *vap = v->a_vap; 247276648Skib int error; 248170808Sdelphij 249170808Sdelphij MPASS(vap->va_type == VREG || vap->va_type == VSOCK); 250170808Sdelphij 251276648Skib error = tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 252313095Skib if (error == 0 && (cnp->cn_flags & MAKEENTRY) != 0 && tmpfs_use_nc(dvp)) 253276648Skib cache_enter(dvp, *vpp, cnp); 254276648Skib return (error); 255170808Sdelphij} 256170808Sdelphij 257171069Sdelphijstatic int 258170808Sdelphijtmpfs_mknod(struct vop_mknod_args *v) 259170808Sdelphij{ 260170808Sdelphij struct vnode *dvp = v->a_dvp; 261170808Sdelphij struct vnode **vpp = v->a_vpp; 262170808Sdelphij struct componentname *cnp = v->a_cnp; 263170808Sdelphij struct vattr *vap = v->a_vap; 264170808Sdelphij 265170808Sdelphij if (vap->va_type != VBLK && vap->va_type != VCHR && 266170808Sdelphij vap->va_type != VFIFO) 267170808Sdelphij return EINVAL; 268170808Sdelphij 269170808Sdelphij return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 270170808Sdelphij} 271170808Sdelphij 272171069Sdelphijstatic int 273170808Sdelphijtmpfs_open(struct vop_open_args *v) 274170808Sdelphij{ 275170808Sdelphij struct vnode *vp = v->a_vp; 276170808Sdelphij int mode = v->a_mode; 277170808Sdelphij 278170808Sdelphij int error; 279170808Sdelphij struct tmpfs_node *node; 280170808Sdelphij 281176559Sattilio MPASS(VOP_ISLOCKED(vp)); 282170808Sdelphij 283170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 284171070Sdelphij 285170808Sdelphij /* The file is still active but all its names have been removed 286170808Sdelphij * (e.g. by a "rmdir $(pwd)"). It cannot be opened any more as 287170808Sdelphij * it is about to die. */ 288170808Sdelphij if (node->tn_links < 1) 289170808Sdelphij return (ENOENT); 290170808Sdelphij 291170808Sdelphij /* If the file is marked append-only, deny write requests. */ 292170808Sdelphij if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE) 293170808Sdelphij error = EPERM; 294170808Sdelphij else { 295170808Sdelphij error = 0; 296250190Skib /* For regular files, the call below is nop. */ 297269168Skib KASSERT(vp->v_type != VREG || (node->tn_reg.tn_aobj->flags & 298269168Skib OBJ_DEAD) == 0, ("dead object")); 299171070Sdelphij vnode_create_vobject(vp, node->tn_size, v->a_td); 300170808Sdelphij } 301170808Sdelphij 302176559Sattilio MPASS(VOP_ISLOCKED(vp)); 303170808Sdelphij return error; 304170808Sdelphij} 305170808Sdelphij 306171069Sdelphijstatic int 307170808Sdelphijtmpfs_close(struct vop_close_args *v) 308170808Sdelphij{ 309170808Sdelphij struct vnode *vp = v->a_vp; 310170808Sdelphij 311218949Salc /* Update node times. */ 312218949Salc tmpfs_update(vp); 313170808Sdelphij 314218949Salc return (0); 315170808Sdelphij} 316170808Sdelphij 317170808Sdelphijint 318170808Sdelphijtmpfs_access(struct vop_access_args *v) 319170808Sdelphij{ 320170808Sdelphij struct vnode *vp = v->a_vp; 321184413Strasz accmode_t accmode = v->a_accmode; 322170808Sdelphij struct ucred *cred = v->a_cred; 323170808Sdelphij 324170808Sdelphij int error; 325170808Sdelphij struct tmpfs_node *node; 326170808Sdelphij 327176559Sattilio MPASS(VOP_ISLOCKED(vp)); 328170808Sdelphij 329170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 330170808Sdelphij 331170808Sdelphij switch (vp->v_type) { 332170808Sdelphij case VDIR: 333170808Sdelphij /* FALLTHROUGH */ 334170808Sdelphij case VLNK: 335170808Sdelphij /* FALLTHROUGH */ 336170808Sdelphij case VREG: 337184413Strasz if (accmode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) { 338170808Sdelphij error = EROFS; 339170808Sdelphij goto out; 340170808Sdelphij } 341170808Sdelphij break; 342170808Sdelphij 343170808Sdelphij case VBLK: 344170808Sdelphij /* FALLTHROUGH */ 345170808Sdelphij case VCHR: 346170808Sdelphij /* FALLTHROUGH */ 347170808Sdelphij case VSOCK: 348170808Sdelphij /* FALLTHROUGH */ 349170808Sdelphij case VFIFO: 350170808Sdelphij break; 351170808Sdelphij 352170808Sdelphij default: 353170808Sdelphij error = EINVAL; 354170808Sdelphij goto out; 355170808Sdelphij } 356170808Sdelphij 357184413Strasz if (accmode & VWRITE && node->tn_flags & IMMUTABLE) { 358170808Sdelphij error = EPERM; 359170808Sdelphij goto out; 360170808Sdelphij } 361170808Sdelphij 362170808Sdelphij error = vaccess(vp->v_type, node->tn_mode, node->tn_uid, 363184413Strasz node->tn_gid, accmode, cred, NULL); 364170808Sdelphij 365170808Sdelphijout: 366176559Sattilio MPASS(VOP_ISLOCKED(vp)); 367170808Sdelphij 368170808Sdelphij return error; 369170808Sdelphij} 370170808Sdelphij 371170808Sdelphijint 372170808Sdelphijtmpfs_getattr(struct vop_getattr_args *v) 373170808Sdelphij{ 374170808Sdelphij struct vnode *vp = v->a_vp; 375170808Sdelphij struct vattr *vap = v->a_vap; 376170808Sdelphij 377170808Sdelphij struct tmpfs_node *node; 378170808Sdelphij 379170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 380170808Sdelphij 381170808Sdelphij tmpfs_update(vp); 382170808Sdelphij 383170808Sdelphij vap->va_type = vp->v_type; 384170808Sdelphij vap->va_mode = node->tn_mode; 385170808Sdelphij vap->va_nlink = node->tn_links; 386170808Sdelphij vap->va_uid = node->tn_uid; 387170808Sdelphij vap->va_gid = node->tn_gid; 388170808Sdelphij vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 389170808Sdelphij vap->va_fileid = node->tn_id; 390170808Sdelphij vap->va_size = node->tn_size; 391170808Sdelphij vap->va_blocksize = PAGE_SIZE; 392170808Sdelphij vap->va_atime = node->tn_atime; 393170808Sdelphij vap->va_mtime = node->tn_mtime; 394170808Sdelphij vap->va_ctime = node->tn_ctime; 395170808Sdelphij vap->va_birthtime = node->tn_birthtime; 396170808Sdelphij vap->va_gen = node->tn_gen; 397170808Sdelphij vap->va_flags = node->tn_flags; 398170808Sdelphij vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ? 399183214Skib node->tn_rdev : NODEV; 400170808Sdelphij vap->va_bytes = round_page(node->tn_size); 401183212Skib vap->va_filerev = 0; 402170808Sdelphij 403170808Sdelphij return 0; 404170808Sdelphij} 405170808Sdelphij 406170808Sdelphijint 407170808Sdelphijtmpfs_setattr(struct vop_setattr_args *v) 408170808Sdelphij{ 409170808Sdelphij struct vnode *vp = v->a_vp; 410170808Sdelphij struct vattr *vap = v->a_vap; 411170808Sdelphij struct ucred *cred = v->a_cred; 412182371Sattilio struct thread *td = curthread; 413170808Sdelphij 414170808Sdelphij int error; 415170808Sdelphij 416176559Sattilio MPASS(VOP_ISLOCKED(vp)); 417170808Sdelphij 418170808Sdelphij error = 0; 419170808Sdelphij 420170808Sdelphij /* Abort if any unsettable attribute is given. */ 421170808Sdelphij if (vap->va_type != VNON || 422170808Sdelphij vap->va_nlink != VNOVAL || 423170808Sdelphij vap->va_fsid != VNOVAL || 424170808Sdelphij vap->va_fileid != VNOVAL || 425170808Sdelphij vap->va_blocksize != VNOVAL || 426170808Sdelphij vap->va_gen != VNOVAL || 427170808Sdelphij vap->va_rdev != VNOVAL || 428170808Sdelphij vap->va_bytes != VNOVAL) 429170808Sdelphij error = EINVAL; 430170808Sdelphij 431170808Sdelphij if (error == 0 && (vap->va_flags != VNOVAL)) 432182371Sattilio error = tmpfs_chflags(vp, vap->va_flags, cred, td); 433170808Sdelphij 434170808Sdelphij if (error == 0 && (vap->va_size != VNOVAL)) 435182371Sattilio error = tmpfs_chsize(vp, vap->va_size, cred, td); 436170808Sdelphij 437170808Sdelphij if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) 438182371Sattilio error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, td); 439170808Sdelphij 440170808Sdelphij if (error == 0 && (vap->va_mode != (mode_t)VNOVAL)) 441182371Sattilio error = tmpfs_chmod(vp, vap->va_mode, cred, td); 442170808Sdelphij 443170808Sdelphij if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL && 444170808Sdelphij vap->va_atime.tv_nsec != VNOVAL) || 445170808Sdelphij (vap->va_mtime.tv_sec != VNOVAL && 446170808Sdelphij vap->va_mtime.tv_nsec != VNOVAL) || 447170808Sdelphij (vap->va_birthtime.tv_sec != VNOVAL && 448170808Sdelphij vap->va_birthtime.tv_nsec != VNOVAL))) 449267816Skib error = tmpfs_chtimes(vp, vap, cred, td); 450170808Sdelphij 451170808Sdelphij /* Update the node times. We give preference to the error codes 452170808Sdelphij * generated by this function rather than the ones that may arise 453170808Sdelphij * from tmpfs_update. */ 454170808Sdelphij tmpfs_update(vp); 455170808Sdelphij 456176559Sattilio MPASS(VOP_ISLOCKED(vp)); 457170808Sdelphij 458170808Sdelphij return error; 459170808Sdelphij} 460170808Sdelphij 461197850Sdelphijstatic int 462170808Sdelphijtmpfs_read(struct vop_read_args *v) 463170808Sdelphij{ 464254601Skib struct vnode *vp; 465254601Skib struct uio *uio; 466170808Sdelphij struct tmpfs_node *node; 467170808Sdelphij 468254601Skib vp = v->a_vp; 469254601Skib if (vp->v_type != VREG) 470254601Skib return (EISDIR); 471254601Skib uio = v->a_uio; 472254601Skib if (uio->uio_offset < 0) 473254601Skib return (EINVAL); 474170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 475312069Skib tmpfs_set_status(node, TMPFS_NODE_ACCESSED); 476254601Skib return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio)); 477170808Sdelphij} 478170808Sdelphij 479171069Sdelphijstatic int 480170808Sdelphijtmpfs_write(struct vop_write_args *v) 481170808Sdelphij{ 482254601Skib struct vnode *vp; 483254601Skib struct uio *uio; 484254601Skib struct tmpfs_node *node; 485254601Skib off_t oldsize; 486254601Skib int error, ioflag; 487170808Sdelphij 488254601Skib vp = v->a_vp; 489254601Skib uio = v->a_uio; 490254601Skib ioflag = v->a_ioflag; 491254601Skib error = 0; 492170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 493170808Sdelphij oldsize = node->tn_size; 494170808Sdelphij 495254601Skib if (uio->uio_offset < 0 || vp->v_type != VREG) 496254601Skib return (EINVAL); 497254601Skib if (uio->uio_resid == 0) 498254601Skib return (0); 499170808Sdelphij if (ioflag & IO_APPEND) 500170808Sdelphij uio->uio_offset = node->tn_size; 501171070Sdelphij if (uio->uio_offset + uio->uio_resid > 502170808Sdelphij VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) 503170808Sdelphij return (EFBIG); 504207719Strasz if (vn_rlimit_fsize(vp, uio, uio->uio_td)) 505207662Strasz return (EFBIG); 506278571Skib if (uio->uio_offset + uio->uio_resid > node->tn_size) { 507230180Salc error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid, 508230180Salc FALSE); 509170808Sdelphij if (error != 0) 510170808Sdelphij goto out; 511170808Sdelphij } 512170808Sdelphij 513254601Skib error = uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio); 514170808Sdelphij node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | 515278571Skib TMPFS_NODE_CHANGED; 516170808Sdelphij if (node->tn_mode & (S_ISUID | S_ISGID)) { 517170808Sdelphij if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0)) 518170808Sdelphij node->tn_mode &= ~(S_ISUID | S_ISGID); 519170808Sdelphij } 520170808Sdelphij if (error != 0) 521230180Salc (void)tmpfs_reg_resize(vp, oldsize, TRUE); 522170808Sdelphij 523170808Sdelphijout: 524170808Sdelphij MPASS(IMPLIES(error == 0, uio->uio_resid == 0)); 525170808Sdelphij MPASS(IMPLIES(error != 0, oldsize == node->tn_size)); 526170808Sdelphij 527254601Skib return (error); 528170808Sdelphij} 529170808Sdelphij 530171069Sdelphijstatic int 531170808Sdelphijtmpfs_fsync(struct vop_fsync_args *v) 532170808Sdelphij{ 533170808Sdelphij struct vnode *vp = v->a_vp; 534170808Sdelphij 535176559Sattilio MPASS(VOP_ISLOCKED(vp)); 536170808Sdelphij 537278571Skib tmpfs_check_mtime(vp); 538170808Sdelphij tmpfs_update(vp); 539170808Sdelphij 540170808Sdelphij return 0; 541170808Sdelphij} 542170808Sdelphij 543171069Sdelphijstatic int 544170808Sdelphijtmpfs_remove(struct vop_remove_args *v) 545170808Sdelphij{ 546170808Sdelphij struct vnode *dvp = v->a_dvp; 547170808Sdelphij struct vnode *vp = v->a_vp; 548170808Sdelphij 549170808Sdelphij int error; 550170808Sdelphij struct tmpfs_dirent *de; 551170808Sdelphij struct tmpfs_mount *tmp; 552170808Sdelphij struct tmpfs_node *dnode; 553170808Sdelphij struct tmpfs_node *node; 554170808Sdelphij 555176559Sattilio MPASS(VOP_ISLOCKED(dvp)); 556176559Sattilio MPASS(VOP_ISLOCKED(vp)); 557170808Sdelphij 558170808Sdelphij if (vp->v_type == VDIR) { 559170808Sdelphij error = EISDIR; 560170808Sdelphij goto out; 561170808Sdelphij } 562170808Sdelphij 563170808Sdelphij dnode = VP_TO_TMPFS_DIR(dvp); 564170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 565170808Sdelphij tmp = VFS_TO_TMPFS(vp->v_mount); 566188318Skib de = tmpfs_dir_lookup(dnode, node, v->a_cnp); 567170808Sdelphij MPASS(de != NULL); 568170808Sdelphij 569170808Sdelphij /* Files marked as immutable or append-only cannot be deleted. */ 570170808Sdelphij if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) || 571170808Sdelphij (dnode->tn_flags & APPEND)) { 572170808Sdelphij error = EPERM; 573170808Sdelphij goto out; 574170808Sdelphij } 575170808Sdelphij 576170808Sdelphij /* Remove the entry from the directory; as it is a file, we do not 577170808Sdelphij * have to change the number of hard links of the directory. */ 578170808Sdelphij tmpfs_dir_detach(dvp, de); 579211598Sed if (v->a_cnp->cn_flags & DOWHITEOUT) 580211598Sed tmpfs_dir_whiteout_add(dvp, v->a_cnp); 581170808Sdelphij 582170808Sdelphij /* Free the directory entry we just deleted. Note that the node 583170808Sdelphij * referred by it will not be removed until the vnode is really 584170808Sdelphij * reclaimed. */ 585245115Sgleb tmpfs_free_dirent(tmp, de); 586170808Sdelphij 587218949Salc node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED; 588170808Sdelphij error = 0; 589170808Sdelphij 590170808Sdelphijout: 591170808Sdelphij 592170808Sdelphij return error; 593170808Sdelphij} 594170808Sdelphij 595171069Sdelphijstatic int 596170808Sdelphijtmpfs_link(struct vop_link_args *v) 597170808Sdelphij{ 598170808Sdelphij struct vnode *dvp = v->a_tdvp; 599170808Sdelphij struct vnode *vp = v->a_vp; 600170808Sdelphij struct componentname *cnp = v->a_cnp; 601170808Sdelphij 602170808Sdelphij int error; 603170808Sdelphij struct tmpfs_dirent *de; 604170808Sdelphij struct tmpfs_node *node; 605170808Sdelphij 606176559Sattilio MPASS(VOP_ISLOCKED(dvp)); 607170808Sdelphij MPASS(cnp->cn_flags & HASBUF); 608170808Sdelphij MPASS(dvp != vp); /* XXX When can this be false? */ 609269167Skib node = VP_TO_TMPFS_NODE(vp); 610269167Skib 611170808Sdelphij /* Ensure that we do not overflow the maximum number of links imposed 612170808Sdelphij * by the system. */ 613170808Sdelphij MPASS(node->tn_links <= LINK_MAX); 614170808Sdelphij if (node->tn_links == LINK_MAX) { 615170808Sdelphij error = EMLINK; 616170808Sdelphij goto out; 617170808Sdelphij } 618170808Sdelphij 619170808Sdelphij /* We cannot create links of files marked immutable or append-only. */ 620170808Sdelphij if (node->tn_flags & (IMMUTABLE | APPEND)) { 621170808Sdelphij error = EPERM; 622170808Sdelphij goto out; 623170808Sdelphij } 624170808Sdelphij 625170808Sdelphij /* Allocate a new directory entry to represent the node. */ 626170808Sdelphij error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node, 627170808Sdelphij cnp->cn_nameptr, cnp->cn_namelen, &de); 628170808Sdelphij if (error != 0) 629170808Sdelphij goto out; 630170808Sdelphij 631170808Sdelphij /* Insert the new directory entry into the appropriate directory. */ 632211598Sed if (cnp->cn_flags & ISWHITEOUT) 633211598Sed tmpfs_dir_whiteout_remove(dvp, cnp); 634170808Sdelphij tmpfs_dir_attach(dvp, de); 635170808Sdelphij 636170808Sdelphij /* vp link count has changed, so update node times. */ 637170808Sdelphij node->tn_status |= TMPFS_NODE_CHANGED; 638170808Sdelphij tmpfs_update(vp); 639170808Sdelphij 640170808Sdelphij error = 0; 641171070Sdelphij 642170808Sdelphijout: 643170808Sdelphij return error; 644170808Sdelphij} 645170808Sdelphij 646232960Sgleb/* 647232960Sgleb * We acquire all but fdvp locks using non-blocking acquisitions. If we 648232960Sgleb * fail to acquire any lock in the path we will drop all held locks, 649232960Sgleb * acquire the new lock in a blocking fashion, and then release it and 650232960Sgleb * restart the rename. This acquire/release step ensures that we do not 651232960Sgleb * spin on a lock waiting for release. On error release all vnode locks 652232960Sgleb * and decrement references the way tmpfs_rename() would do. 653232960Sgleb */ 654171069Sdelphijstatic int 655232960Sglebtmpfs_rename_relock(struct vnode *fdvp, struct vnode **fvpp, 656232960Sgleb struct vnode *tdvp, struct vnode **tvpp, 657232960Sgleb struct componentname *fcnp, struct componentname *tcnp) 658232960Sgleb{ 659232960Sgleb struct vnode *nvp; 660232960Sgleb struct mount *mp; 661232960Sgleb struct tmpfs_dirent *de; 662232960Sgleb int error, restarts = 0; 663232960Sgleb 664232960Sgleb VOP_UNLOCK(tdvp, 0); 665232960Sgleb if (*tvpp != NULL && *tvpp != tdvp) 666232960Sgleb VOP_UNLOCK(*tvpp, 0); 667232960Sgleb mp = fdvp->v_mount; 668232960Sgleb 669232960Sglebrelock: 670232960Sgleb restarts += 1; 671232960Sgleb error = vn_lock(fdvp, LK_EXCLUSIVE); 672232960Sgleb if (error) 673232960Sgleb goto releout; 674232960Sgleb if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) { 675232960Sgleb VOP_UNLOCK(fdvp, 0); 676232960Sgleb error = vn_lock(tdvp, LK_EXCLUSIVE); 677232960Sgleb if (error) 678232960Sgleb goto releout; 679232960Sgleb VOP_UNLOCK(tdvp, 0); 680232960Sgleb goto relock; 681232960Sgleb } 682232960Sgleb /* 683232960Sgleb * Re-resolve fvp to be certain it still exists and fetch the 684232960Sgleb * correct vnode. 685232960Sgleb */ 686232960Sgleb de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(fdvp), NULL, fcnp); 687232960Sgleb if (de == NULL) { 688232960Sgleb VOP_UNLOCK(fdvp, 0); 689232960Sgleb VOP_UNLOCK(tdvp, 0); 690232960Sgleb if ((fcnp->cn_flags & ISDOTDOT) != 0 || 691232960Sgleb (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')) 692232960Sgleb error = EINVAL; 693232960Sgleb else 694232960Sgleb error = ENOENT; 695232960Sgleb goto releout; 696232960Sgleb } 697232960Sgleb error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE | LK_NOWAIT, &nvp); 698232960Sgleb if (error != 0) { 699232960Sgleb VOP_UNLOCK(fdvp, 0); 700232960Sgleb VOP_UNLOCK(tdvp, 0); 701232960Sgleb if (error != EBUSY) 702232960Sgleb goto releout; 703232960Sgleb error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE, &nvp); 704232960Sgleb if (error != 0) 705232960Sgleb goto releout; 706232960Sgleb VOP_UNLOCK(nvp, 0); 707232960Sgleb /* 708232960Sgleb * Concurrent rename race. 709232960Sgleb */ 710232960Sgleb if (nvp == tdvp) { 711232960Sgleb vrele(nvp); 712232960Sgleb error = EINVAL; 713232960Sgleb goto releout; 714232960Sgleb } 715232960Sgleb vrele(*fvpp); 716232960Sgleb *fvpp = nvp; 717232960Sgleb goto relock; 718232960Sgleb } 719232960Sgleb vrele(*fvpp); 720232960Sgleb *fvpp = nvp; 721232960Sgleb VOP_UNLOCK(*fvpp, 0); 722232960Sgleb /* 723232960Sgleb * Re-resolve tvp and acquire the vnode lock if present. 724232960Sgleb */ 725232960Sgleb de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(tdvp), NULL, tcnp); 726232960Sgleb /* 727232960Sgleb * If tvp disappeared we just carry on. 728232960Sgleb */ 729232960Sgleb if (de == NULL && *tvpp != NULL) { 730232960Sgleb vrele(*tvpp); 731232960Sgleb *tvpp = NULL; 732232960Sgleb } 733232960Sgleb /* 734232960Sgleb * Get the tvp ino if the lookup succeeded. We may have to restart 735232960Sgleb * if the non-blocking acquire fails. 736232960Sgleb */ 737232960Sgleb if (de != NULL) { 738232960Sgleb nvp = NULL; 739232960Sgleb error = tmpfs_alloc_vp(mp, de->td_node, 740232960Sgleb LK_EXCLUSIVE | LK_NOWAIT, &nvp); 741232960Sgleb if (*tvpp != NULL) 742232960Sgleb vrele(*tvpp); 743232960Sgleb *tvpp = nvp; 744232960Sgleb if (error != 0) { 745232960Sgleb VOP_UNLOCK(fdvp, 0); 746232960Sgleb VOP_UNLOCK(tdvp, 0); 747232960Sgleb if (error != EBUSY) 748232960Sgleb goto releout; 749232960Sgleb error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE, 750232960Sgleb &nvp); 751232960Sgleb if (error != 0) 752232960Sgleb goto releout; 753232960Sgleb VOP_UNLOCK(nvp, 0); 754232960Sgleb /* 755232960Sgleb * fdvp contains fvp, thus tvp (=fdvp) is not empty. 756232960Sgleb */ 757232960Sgleb if (nvp == fdvp) { 758232960Sgleb error = ENOTEMPTY; 759232960Sgleb goto releout; 760232960Sgleb } 761232960Sgleb goto relock; 762232960Sgleb } 763232960Sgleb } 764232960Sgleb tmpfs_rename_restarts += restarts; 765232960Sgleb 766232960Sgleb return (0); 767232960Sgleb 768232960Sglebreleout: 769232960Sgleb vrele(fdvp); 770232960Sgleb vrele(*fvpp); 771232960Sgleb vrele(tdvp); 772232960Sgleb if (*tvpp != NULL) 773232960Sgleb vrele(*tvpp); 774232960Sgleb tmpfs_rename_restarts += restarts; 775232960Sgleb 776232960Sgleb return (error); 777232960Sgleb} 778232960Sgleb 779232960Sglebstatic int 780170808Sdelphijtmpfs_rename(struct vop_rename_args *v) 781170808Sdelphij{ 782170808Sdelphij struct vnode *fdvp = v->a_fdvp; 783170808Sdelphij struct vnode *fvp = v->a_fvp; 784170808Sdelphij struct componentname *fcnp = v->a_fcnp; 785170808Sdelphij struct vnode *tdvp = v->a_tdvp; 786170808Sdelphij struct vnode *tvp = v->a_tvp; 787170808Sdelphij struct componentname *tcnp = v->a_tcnp; 788232960Sgleb struct mount *mp = NULL; 789170808Sdelphij 790170808Sdelphij char *newname; 791170808Sdelphij int error; 792170808Sdelphij struct tmpfs_dirent *de; 793197953Sdelphij struct tmpfs_mount *tmp; 794170808Sdelphij struct tmpfs_node *fdnode; 795170808Sdelphij struct tmpfs_node *fnode; 796171799Sdelphij struct tmpfs_node *tnode; 797170808Sdelphij struct tmpfs_node *tdnode; 798170808Sdelphij 799176559Sattilio MPASS(VOP_ISLOCKED(tdvp)); 800176559Sattilio MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp))); 801170808Sdelphij MPASS(fcnp->cn_flags & HASBUF); 802170808Sdelphij MPASS(tcnp->cn_flags & HASBUF); 803170808Sdelphij 804170808Sdelphij /* Disallow cross-device renames. 805170808Sdelphij * XXX Why isn't this done by the caller? */ 806170808Sdelphij if (fvp->v_mount != tdvp->v_mount || 807170808Sdelphij (tvp != NULL && fvp->v_mount != tvp->v_mount)) { 808170808Sdelphij error = EXDEV; 809170808Sdelphij goto out; 810170808Sdelphij } 811170808Sdelphij 812170808Sdelphij /* If source and target are the same file, there is nothing to do. */ 813170808Sdelphij if (fvp == tvp) { 814170808Sdelphij error = 0; 815170808Sdelphij goto out; 816170808Sdelphij } 817170808Sdelphij 818173725Sdelphij /* If we need to move the directory between entries, lock the 819173725Sdelphij * source so that we can safely operate on it. */ 820232960Sgleb if (fdvp != tdvp && fdvp != tvp) { 821232960Sgleb if (vn_lock(fdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) { 822232960Sgleb mp = tdvp->v_mount; 823232960Sgleb error = vfs_busy(mp, 0); 824232960Sgleb if (error != 0) { 825232960Sgleb mp = NULL; 826232960Sgleb goto out; 827232960Sgleb } 828232960Sgleb error = tmpfs_rename_relock(fdvp, &fvp, tdvp, &tvp, 829232960Sgleb fcnp, tcnp); 830232960Sgleb if (error != 0) { 831232960Sgleb vfs_unbusy(mp); 832232960Sgleb return (error); 833232960Sgleb } 834232960Sgleb ASSERT_VOP_ELOCKED(fdvp, 835232960Sgleb "tmpfs_rename: fdvp not locked"); 836232960Sgleb ASSERT_VOP_ELOCKED(tdvp, 837232960Sgleb "tmpfs_rename: tdvp not locked"); 838232960Sgleb if (tvp != NULL) 839232960Sgleb ASSERT_VOP_ELOCKED(tvp, 840232960Sgleb "tmpfs_rename: tvp not locked"); 841232960Sgleb if (fvp == tvp) { 842232960Sgleb error = 0; 843232960Sgleb goto out_locked; 844232960Sgleb } 845232960Sgleb } 846232960Sgleb } 847232960Sgleb 848232960Sgleb tmp = VFS_TO_TMPFS(tdvp->v_mount); 849232960Sgleb tdnode = VP_TO_TMPFS_DIR(tdvp); 850232960Sgleb tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp); 851173725Sdelphij fdnode = VP_TO_TMPFS_DIR(fdvp); 852173725Sdelphij fnode = VP_TO_TMPFS_NODE(fvp); 853188318Skib de = tmpfs_dir_lookup(fdnode, fnode, fcnp); 854173725Sdelphij 855212305Sivoras /* Entry can disappear before we lock fdvp, 856212305Sivoras * also avoid manipulating '.' and '..' entries. */ 857170808Sdelphij if (de == NULL) { 858212305Sivoras if ((fcnp->cn_flags & ISDOTDOT) != 0 || 859212305Sivoras (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')) 860212305Sivoras error = EINVAL; 861212305Sivoras else 862212305Sivoras error = ENOENT; 863173725Sdelphij goto out_locked; 864170808Sdelphij } 865170808Sdelphij MPASS(de->td_node == fnode); 866170808Sdelphij 867171070Sdelphij /* If re-naming a directory to another preexisting directory 868170808Sdelphij * ensure that the target directory is empty so that its 869171070Sdelphij * removal causes no side effects. 870170808Sdelphij * Kern_rename gurantees the destination to be a directory 871170808Sdelphij * if the source is one. */ 872170808Sdelphij if (tvp != NULL) { 873171799Sdelphij MPASS(tnode != NULL); 874171070Sdelphij 875170808Sdelphij if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) || 876170808Sdelphij (tdnode->tn_flags & (APPEND | IMMUTABLE))) { 877170808Sdelphij error = EPERM; 878173725Sdelphij goto out_locked; 879170808Sdelphij } 880170808Sdelphij 881171799Sdelphij if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) { 882171799Sdelphij if (tnode->tn_size > 0) { 883171799Sdelphij error = ENOTEMPTY; 884173725Sdelphij goto out_locked; 885171799Sdelphij } 886171799Sdelphij } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) { 887171799Sdelphij error = ENOTDIR; 888173725Sdelphij goto out_locked; 889171799Sdelphij } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) { 890171799Sdelphij error = EISDIR; 891173725Sdelphij goto out_locked; 892171799Sdelphij } else { 893171799Sdelphij MPASS(fnode->tn_type != VDIR && 894171799Sdelphij tnode->tn_type != VDIR); 895170808Sdelphij } 896170808Sdelphij } 897170808Sdelphij 898170808Sdelphij if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) 899170808Sdelphij || (fdnode->tn_flags & (APPEND | IMMUTABLE))) { 900170808Sdelphij error = EPERM; 901170808Sdelphij goto out_locked; 902170808Sdelphij } 903170808Sdelphij 904170808Sdelphij /* Ensure that we have enough memory to hold the new name, if it 905170808Sdelphij * has to be changed. */ 906170808Sdelphij if (fcnp->cn_namelen != tcnp->cn_namelen || 907183299Sobrien bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) { 908171087Sdelphij newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK); 909170808Sdelphij } else 910170808Sdelphij newname = NULL; 911170808Sdelphij 912170808Sdelphij /* If the node is being moved to another directory, we have to do 913170808Sdelphij * the move. */ 914170808Sdelphij if (fdnode != tdnode) { 915170808Sdelphij /* In case we are moving a directory, we have to adjust its 916170808Sdelphij * parent to point to the new parent. */ 917170808Sdelphij if (de->td_node->tn_type == VDIR) { 918170808Sdelphij struct tmpfs_node *n; 919170808Sdelphij 920170808Sdelphij /* Ensure the target directory is not a child of the 921170808Sdelphij * directory being moved. Otherwise, we'd end up 922170808Sdelphij * with stale nodes. */ 923170808Sdelphij n = tdnode; 924197953Sdelphij /* TMPFS_LOCK garanties that no nodes are freed while 925197953Sdelphij * traversing the list. Nodes can only be marked as 926197953Sdelphij * removed: tn_parent == NULL. */ 927197953Sdelphij TMPFS_LOCK(tmp); 928197953Sdelphij TMPFS_NODE_LOCK(n); 929170808Sdelphij while (n != n->tn_dir.tn_parent) { 930197953Sdelphij struct tmpfs_node *parent; 931197953Sdelphij 932170808Sdelphij if (n == fnode) { 933197953Sdelphij TMPFS_NODE_UNLOCK(n); 934197953Sdelphij TMPFS_UNLOCK(tmp); 935170808Sdelphij error = EINVAL; 936170808Sdelphij if (newname != NULL) 937171087Sdelphij free(newname, M_TMPFSNAME); 938170808Sdelphij goto out_locked; 939170808Sdelphij } 940197953Sdelphij parent = n->tn_dir.tn_parent; 941197953Sdelphij TMPFS_NODE_UNLOCK(n); 942197953Sdelphij if (parent == NULL) { 943197953Sdelphij n = NULL; 944197953Sdelphij break; 945197953Sdelphij } 946197953Sdelphij TMPFS_NODE_LOCK(parent); 947197953Sdelphij if (parent->tn_dir.tn_parent == NULL) { 948197953Sdelphij TMPFS_NODE_UNLOCK(parent); 949197953Sdelphij n = NULL; 950197953Sdelphij break; 951197953Sdelphij } 952197953Sdelphij n = parent; 953170808Sdelphij } 954197953Sdelphij TMPFS_UNLOCK(tmp); 955197953Sdelphij if (n == NULL) { 956197953Sdelphij error = EINVAL; 957197953Sdelphij if (newname != NULL) 958197953Sdelphij free(newname, M_TMPFSNAME); 959197953Sdelphij goto out_locked; 960197953Sdelphij } 961197953Sdelphij TMPFS_NODE_UNLOCK(n); 962170808Sdelphij 963170808Sdelphij /* Adjust the parent pointer. */ 964170808Sdelphij TMPFS_VALIDATE_DIR(fnode); 965197953Sdelphij TMPFS_NODE_LOCK(de->td_node); 966170808Sdelphij de->td_node->tn_dir.tn_parent = tdnode; 967197953Sdelphij TMPFS_NODE_UNLOCK(de->td_node); 968170808Sdelphij 969170808Sdelphij /* As a result of changing the target of the '..' 970170808Sdelphij * entry, the link count of the source and target 971170808Sdelphij * directories has to be adjusted. */ 972197953Sdelphij TMPFS_NODE_LOCK(tdnode); 973197953Sdelphij TMPFS_ASSERT_LOCKED(tdnode); 974197953Sdelphij tdnode->tn_links++; 975197953Sdelphij TMPFS_NODE_UNLOCK(tdnode); 976197953Sdelphij 977197953Sdelphij TMPFS_NODE_LOCK(fdnode); 978197953Sdelphij TMPFS_ASSERT_LOCKED(fdnode); 979170808Sdelphij fdnode->tn_links--; 980197953Sdelphij TMPFS_NODE_UNLOCK(fdnode); 981170808Sdelphij } 982170808Sdelphij } 983170808Sdelphij 984245115Sgleb /* Do the move: just remove the entry from the source directory 985245115Sgleb * and insert it into the target one. */ 986245115Sgleb tmpfs_dir_detach(fdvp, de); 987245115Sgleb 988245115Sgleb if (fcnp->cn_flags & DOWHITEOUT) 989245115Sgleb tmpfs_dir_whiteout_add(fdvp, fcnp); 990245115Sgleb if (tcnp->cn_flags & ISWHITEOUT) 991245115Sgleb tmpfs_dir_whiteout_remove(tdvp, tcnp); 992245115Sgleb 993170808Sdelphij /* If the name has changed, we need to make it effective by changing 994170808Sdelphij * it in the directory entry. */ 995170808Sdelphij if (newname != NULL) { 996170808Sdelphij MPASS(tcnp->cn_namelen <= MAXNAMLEN); 997170808Sdelphij 998245115Sgleb free(de->ud.td_name, M_TMPFSNAME); 999245115Sgleb de->ud.td_name = newname; 1000245115Sgleb tmpfs_dirent_init(de, tcnp->cn_nameptr, tcnp->cn_namelen); 1001170808Sdelphij 1002170808Sdelphij fnode->tn_status |= TMPFS_NODE_CHANGED; 1003170808Sdelphij tdnode->tn_status |= TMPFS_NODE_MODIFIED; 1004170808Sdelphij } 1005170808Sdelphij 1006170808Sdelphij /* If we are overwriting an entry, we have to remove the old one 1007170808Sdelphij * from the target directory. */ 1008170808Sdelphij if (tvp != NULL) { 1009245115Sgleb struct tmpfs_dirent *tde; 1010245115Sgleb 1011170808Sdelphij /* Remove the old entry from the target directory. */ 1012245115Sgleb tde = tmpfs_dir_lookup(tdnode, tnode, tcnp); 1013245115Sgleb tmpfs_dir_detach(tdvp, tde); 1014170808Sdelphij 1015170808Sdelphij /* Free the directory entry we just deleted. Note that the 1016170808Sdelphij * node referred by it will not be removed until the vnode is 1017170808Sdelphij * really reclaimed. */ 1018245115Sgleb tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde); 1019170808Sdelphij } 1020245115Sgleb 1021245115Sgleb tmpfs_dir_attach(tdvp, de); 1022245115Sgleb 1023313095Skib if (tmpfs_use_nc(fvp)) { 1024313095Skib cache_purge(fvp); 1025313095Skib if (tvp != NULL) 1026313095Skib cache_purge(tvp); 1027313095Skib cache_purge_negative(tdvp); 1028313095Skib } 1029170808Sdelphij 1030170808Sdelphij error = 0; 1031170808Sdelphij 1032170808Sdelphijout_locked: 1033227822Sivoras if (fdvp != tdvp && fdvp != tvp) 1034175294Sattilio VOP_UNLOCK(fdvp, 0); 1035170808Sdelphij 1036170808Sdelphijout: 1037170808Sdelphij /* Release target nodes. */ 1038170808Sdelphij /* XXX: I don't understand when tdvp can be the same as tvp, but 1039170808Sdelphij * other code takes care of this... */ 1040170808Sdelphij if (tdvp == tvp) 1041170808Sdelphij vrele(tdvp); 1042170808Sdelphij else 1043170808Sdelphij vput(tdvp); 1044170808Sdelphij if (tvp != NULL) 1045170808Sdelphij vput(tvp); 1046170808Sdelphij 1047170808Sdelphij /* Release source nodes. */ 1048170808Sdelphij vrele(fdvp); 1049170808Sdelphij vrele(fvp); 1050170808Sdelphij 1051232960Sgleb if (mp != NULL) 1052232960Sgleb vfs_unbusy(mp); 1053232960Sgleb 1054170808Sdelphij return error; 1055170808Sdelphij} 1056170808Sdelphij 1057171069Sdelphijstatic int 1058170808Sdelphijtmpfs_mkdir(struct vop_mkdir_args *v) 1059170808Sdelphij{ 1060170808Sdelphij struct vnode *dvp = v->a_dvp; 1061170808Sdelphij struct vnode **vpp = v->a_vpp; 1062170808Sdelphij struct componentname *cnp = v->a_cnp; 1063170808Sdelphij struct vattr *vap = v->a_vap; 1064170808Sdelphij 1065170808Sdelphij MPASS(vap->va_type == VDIR); 1066170808Sdelphij 1067170808Sdelphij return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 1068170808Sdelphij} 1069170808Sdelphij 1070171069Sdelphijstatic int 1071170808Sdelphijtmpfs_rmdir(struct vop_rmdir_args *v) 1072170808Sdelphij{ 1073170808Sdelphij struct vnode *dvp = v->a_dvp; 1074170808Sdelphij struct vnode *vp = v->a_vp; 1075170808Sdelphij 1076170808Sdelphij int error; 1077170808Sdelphij struct tmpfs_dirent *de; 1078170808Sdelphij struct tmpfs_mount *tmp; 1079170808Sdelphij struct tmpfs_node *dnode; 1080170808Sdelphij struct tmpfs_node *node; 1081170808Sdelphij 1082176559Sattilio MPASS(VOP_ISLOCKED(dvp)); 1083176559Sattilio MPASS(VOP_ISLOCKED(vp)); 1084170808Sdelphij 1085170808Sdelphij tmp = VFS_TO_TMPFS(dvp->v_mount); 1086170808Sdelphij dnode = VP_TO_TMPFS_DIR(dvp); 1087170808Sdelphij node = VP_TO_TMPFS_DIR(vp); 1088170808Sdelphij 1089171070Sdelphij /* Directories with more than two entries ('.' and '..') cannot be 1090171070Sdelphij * removed. */ 1091171070Sdelphij if (node->tn_size > 0) { 1092171070Sdelphij error = ENOTEMPTY; 1093171070Sdelphij goto out; 1094171070Sdelphij } 1095170808Sdelphij 1096170808Sdelphij if ((dnode->tn_flags & APPEND) 1097170808Sdelphij || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { 1098170808Sdelphij error = EPERM; 1099170808Sdelphij goto out; 1100170808Sdelphij } 1101170808Sdelphij 1102171070Sdelphij /* This invariant holds only if we are not trying to remove "..". 1103171070Sdelphij * We checked for that above so this is safe now. */ 1104170808Sdelphij MPASS(node->tn_dir.tn_parent == dnode); 1105170808Sdelphij 1106170808Sdelphij /* Get the directory entry associated with node (vp). This was 1107170808Sdelphij * filled by tmpfs_lookup while looking up the entry. */ 1108188318Skib de = tmpfs_dir_lookup(dnode, node, v->a_cnp); 1109170808Sdelphij MPASS(TMPFS_DIRENT_MATCHES(de, 1110170808Sdelphij v->a_cnp->cn_nameptr, 1111170808Sdelphij v->a_cnp->cn_namelen)); 1112170808Sdelphij 1113170808Sdelphij /* Check flags to see if we are allowed to remove the directory. */ 1114312069Skib if ((dnode->tn_flags & APPEND) != 0 || 1115312069Skib (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) != 0) { 1116170808Sdelphij error = EPERM; 1117170808Sdelphij goto out; 1118170808Sdelphij } 1119170808Sdelphij 1120197953Sdelphij 1121170808Sdelphij /* Detach the directory entry from the directory (dnode). */ 1122170808Sdelphij tmpfs_dir_detach(dvp, de); 1123211598Sed if (v->a_cnp->cn_flags & DOWHITEOUT) 1124211598Sed tmpfs_dir_whiteout_add(dvp, v->a_cnp); 1125170808Sdelphij 1126197953Sdelphij /* No vnode should be allocated for this entry from this point */ 1127197953Sdelphij TMPFS_NODE_LOCK(node); 1128170808Sdelphij node->tn_links--; 1129197953Sdelphij node->tn_dir.tn_parent = NULL; 1130312069Skib node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | 1131170808Sdelphij TMPFS_NODE_MODIFIED; 1132197953Sdelphij 1133197953Sdelphij TMPFS_NODE_UNLOCK(node); 1134197953Sdelphij 1135197953Sdelphij TMPFS_NODE_LOCK(dnode); 1136197953Sdelphij dnode->tn_links--; 1137312069Skib dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | 1138312069Skib TMPFS_NODE_MODIFIED; 1139197953Sdelphij TMPFS_NODE_UNLOCK(dnode); 1140170808Sdelphij 1141313095Skib if (tmpfs_use_nc(dvp)) { 1142313095Skib cache_purge(dvp); 1143313095Skib cache_purge(vp); 1144313095Skib } 1145170808Sdelphij 1146170808Sdelphij /* Free the directory entry we just deleted. Note that the node 1147170808Sdelphij * referred by it will not be removed until the vnode is really 1148170808Sdelphij * reclaimed. */ 1149245115Sgleb tmpfs_free_dirent(tmp, de); 1150170808Sdelphij 1151170808Sdelphij /* Release the deleted vnode (will destroy the node, notify 1152170808Sdelphij * interested parties and clean it from the cache). */ 1153170808Sdelphij 1154170808Sdelphij dnode->tn_status |= TMPFS_NODE_CHANGED; 1155170808Sdelphij tmpfs_update(dvp); 1156170808Sdelphij 1157170808Sdelphij error = 0; 1158170808Sdelphij 1159170808Sdelphijout: 1160170808Sdelphij return error; 1161170808Sdelphij} 1162170808Sdelphij 1163171069Sdelphijstatic int 1164170808Sdelphijtmpfs_symlink(struct vop_symlink_args *v) 1165170808Sdelphij{ 1166170808Sdelphij struct vnode *dvp = v->a_dvp; 1167170808Sdelphij struct vnode **vpp = v->a_vpp; 1168170808Sdelphij struct componentname *cnp = v->a_cnp; 1169170808Sdelphij struct vattr *vap = v->a_vap; 1170170808Sdelphij char *target = v->a_target; 1171170808Sdelphij 1172170808Sdelphij#ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */ 1173170808Sdelphij MPASS(vap->va_type == VLNK); 1174170808Sdelphij#else 1175170808Sdelphij vap->va_type = VLNK; 1176170808Sdelphij#endif 1177170808Sdelphij 1178170808Sdelphij return tmpfs_alloc_file(dvp, vpp, vap, cnp, target); 1179170808Sdelphij} 1180170808Sdelphij 1181171069Sdelphijstatic int 1182170808Sdelphijtmpfs_readdir(struct vop_readdir_args *v) 1183170808Sdelphij{ 1184170808Sdelphij struct vnode *vp = v->a_vp; 1185170808Sdelphij struct uio *uio = v->a_uio; 1186170808Sdelphij int *eofflag = v->a_eofflag; 1187170808Sdelphij u_long **cookies = v->a_cookies; 1188170808Sdelphij int *ncookies = v->a_ncookies; 1189170808Sdelphij 1190170808Sdelphij int error; 1191245115Sgleb ssize_t startresid; 1192263946Sbdrewery int maxcookies; 1193170808Sdelphij struct tmpfs_node *node; 1194170808Sdelphij 1195170808Sdelphij /* This operation only makes sense on directory nodes. */ 1196171802Sdelphij if (vp->v_type != VDIR) 1197171802Sdelphij return ENOTDIR; 1198170808Sdelphij 1199263946Sbdrewery maxcookies = 0; 1200170808Sdelphij node = VP_TO_TMPFS_DIR(vp); 1201170808Sdelphij 1202245115Sgleb startresid = uio->uio_resid; 1203170808Sdelphij 1204263946Sbdrewery /* Allocate cookies for NFS and compat modules. */ 1205245115Sgleb if (cookies != NULL && ncookies != NULL) { 1206263946Sbdrewery maxcookies = howmany(node->tn_size, 1207263946Sbdrewery sizeof(struct tmpfs_dirent)) + 2; 1208263946Sbdrewery *cookies = malloc(maxcookies * sizeof(**cookies), M_TEMP, 1209263946Sbdrewery M_WAITOK); 1210245115Sgleb *ncookies = 0; 1211171862Sdelphij } 1212171862Sdelphij 1213263946Sbdrewery if (cookies == NULL) 1214245115Sgleb error = tmpfs_dir_getdents(node, uio, 0, NULL, NULL); 1215245115Sgleb else 1216263946Sbdrewery error = tmpfs_dir_getdents(node, uio, maxcookies, *cookies, 1217263946Sbdrewery ncookies); 1218170808Sdelphij 1219263946Sbdrewery /* Buffer was filled without hitting EOF. */ 1220245115Sgleb if (error == EJUSTRETURN) 1221245115Sgleb error = (uio->uio_resid != startresid) ? 0 : EINVAL; 1222171862Sdelphij 1223295897Smarkj if (error != 0 && cookies != NULL && ncookies != NULL) { 1224245115Sgleb free(*cookies, M_TEMP); 1225295897Smarkj *cookies = NULL; 1226295897Smarkj *ncookies = 0; 1227295897Smarkj } 1228171862Sdelphij 1229170808Sdelphij if (eofflag != NULL) 1230170808Sdelphij *eofflag = 1231170808Sdelphij (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); 1232170808Sdelphij 1233170808Sdelphij return error; 1234170808Sdelphij} 1235170808Sdelphij 1236171069Sdelphijstatic int 1237170808Sdelphijtmpfs_readlink(struct vop_readlink_args *v) 1238170808Sdelphij{ 1239170808Sdelphij struct vnode *vp = v->a_vp; 1240170808Sdelphij struct uio *uio = v->a_uio; 1241170808Sdelphij 1242170808Sdelphij int error; 1243170808Sdelphij struct tmpfs_node *node; 1244170808Sdelphij 1245170808Sdelphij MPASS(uio->uio_offset == 0); 1246170808Sdelphij MPASS(vp->v_type == VLNK); 1247170808Sdelphij 1248170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 1249170808Sdelphij 1250170808Sdelphij error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid), 1251170808Sdelphij uio); 1252312069Skib tmpfs_set_status(node, TMPFS_NODE_ACCESSED); 1253170808Sdelphij 1254312069Skib return (error); 1255170808Sdelphij} 1256170808Sdelphij 1257171069Sdelphijstatic int 1258170808Sdelphijtmpfs_inactive(struct vop_inactive_args *v) 1259170808Sdelphij{ 1260278571Skib struct vnode *vp; 1261170808Sdelphij struct tmpfs_node *node; 1262170808Sdelphij 1263278571Skib vp = v->a_vp; 1264170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 1265170808Sdelphij if (node->tn_links == 0) 1266234607Strasz vrecycle(vp); 1267278571Skib else 1268278571Skib tmpfs_check_mtime(vp); 1269278571Skib return (0); 1270170808Sdelphij} 1271170808Sdelphij 1272170808Sdelphijint 1273170808Sdelphijtmpfs_reclaim(struct vop_reclaim_args *v) 1274170808Sdelphij{ 1275170808Sdelphij struct vnode *vp = v->a_vp; 1276170808Sdelphij 1277170808Sdelphij struct tmpfs_mount *tmp; 1278170808Sdelphij struct tmpfs_node *node; 1279170808Sdelphij 1280170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 1281170808Sdelphij tmp = VFS_TO_TMPFS(vp->v_mount); 1282171070Sdelphij 1283250189Skib if (vp->v_type == VREG) 1284250189Skib tmpfs_destroy_vobject(vp, node->tn_reg.tn_aobj); 1285250190Skib else 1286250190Skib vnode_destroy_vobject(vp); 1287250030Skib vp->v_object = NULL; 1288313095Skib if (tmpfs_use_nc(vp)) 1289313095Skib cache_purge(vp); 1290197953Sdelphij 1291197953Sdelphij TMPFS_NODE_LOCK(node); 1292170808Sdelphij tmpfs_free_vp(vp); 1293170808Sdelphij 1294170808Sdelphij /* If the node referenced by this vnode was deleted by the user, 1295170808Sdelphij * we must free its associated data structures (now that the vnode 1296170808Sdelphij * is being reclaimed). */ 1297197953Sdelphij if (node->tn_links == 0 && 1298197953Sdelphij (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) { 1299197953Sdelphij node->tn_vpstate = TMPFS_VNODE_DOOMED; 1300197953Sdelphij TMPFS_NODE_UNLOCK(node); 1301170808Sdelphij tmpfs_free_node(tmp, node); 1302197953Sdelphij } else 1303197953Sdelphij TMPFS_NODE_UNLOCK(node); 1304170808Sdelphij 1305170808Sdelphij MPASS(vp->v_data == NULL); 1306170808Sdelphij return 0; 1307170808Sdelphij} 1308170808Sdelphij 1309171069Sdelphijstatic int 1310170808Sdelphijtmpfs_print(struct vop_print_args *v) 1311170808Sdelphij{ 1312170808Sdelphij struct vnode *vp = v->a_vp; 1313170808Sdelphij 1314170808Sdelphij struct tmpfs_node *node; 1315170808Sdelphij 1316170808Sdelphij node = VP_TO_TMPFS_NODE(vp); 1317170808Sdelphij 1318248610Spjd printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%lx, links %d\n", 1319170808Sdelphij node, node->tn_flags, node->tn_links); 1320231669Stijl printf("\tmode 0%o, owner %d, group %d, size %jd, status 0x%x\n", 1321170808Sdelphij node->tn_mode, node->tn_uid, node->tn_gid, 1322231669Stijl (intmax_t)node->tn_size, node->tn_status); 1323170808Sdelphij 1324170808Sdelphij if (vp->v_type == VFIFO) 1325170808Sdelphij fifo_printinfo(vp); 1326170808Sdelphij 1327170808Sdelphij printf("\n"); 1328170808Sdelphij 1329170808Sdelphij return 0; 1330170808Sdelphij} 1331170808Sdelphij 1332171069Sdelphijstatic int 1333170808Sdelphijtmpfs_pathconf(struct vop_pathconf_args *v) 1334170808Sdelphij{ 1335170808Sdelphij int name = v->a_name; 1336170808Sdelphij register_t *retval = v->a_retval; 1337170808Sdelphij 1338170808Sdelphij int error; 1339170808Sdelphij 1340170808Sdelphij error = 0; 1341170808Sdelphij 1342170808Sdelphij switch (name) { 1343170808Sdelphij case _PC_LINK_MAX: 1344170808Sdelphij *retval = LINK_MAX; 1345170808Sdelphij break; 1346170808Sdelphij 1347170808Sdelphij case _PC_NAME_MAX: 1348170808Sdelphij *retval = NAME_MAX; 1349170808Sdelphij break; 1350170808Sdelphij 1351170808Sdelphij case _PC_PATH_MAX: 1352170808Sdelphij *retval = PATH_MAX; 1353170808Sdelphij break; 1354170808Sdelphij 1355170808Sdelphij case _PC_PIPE_BUF: 1356170808Sdelphij *retval = PIPE_BUF; 1357170808Sdelphij break; 1358170808Sdelphij 1359170808Sdelphij case _PC_CHOWN_RESTRICTED: 1360170808Sdelphij *retval = 1; 1361170808Sdelphij break; 1362170808Sdelphij 1363170808Sdelphij case _PC_NO_TRUNC: 1364170808Sdelphij *retval = 1; 1365170808Sdelphij break; 1366170808Sdelphij 1367170808Sdelphij case _PC_SYNC_IO: 1368170808Sdelphij *retval = 1; 1369170808Sdelphij break; 1370170808Sdelphij 1371170808Sdelphij case _PC_FILESIZEBITS: 1372170808Sdelphij *retval = 0; /* XXX Don't know which value should I return. */ 1373170808Sdelphij break; 1374170808Sdelphij 1375170808Sdelphij default: 1376170808Sdelphij error = EINVAL; 1377170808Sdelphij } 1378170808Sdelphij 1379170808Sdelphij return error; 1380170808Sdelphij} 1381170808Sdelphij 1382171069Sdelphijstatic int 1383170808Sdelphijtmpfs_vptofh(struct vop_vptofh_args *ap) 1384170808Sdelphij{ 1385170808Sdelphij struct tmpfs_fid *tfhp; 1386170808Sdelphij struct tmpfs_node *node; 1387170808Sdelphij 1388170808Sdelphij tfhp = (struct tmpfs_fid *)ap->a_fhp; 1389170808Sdelphij node = VP_TO_TMPFS_NODE(ap->a_vp); 1390170808Sdelphij 1391170808Sdelphij tfhp->tf_len = sizeof(struct tmpfs_fid); 1392170808Sdelphij tfhp->tf_id = node->tn_id; 1393170808Sdelphij tfhp->tf_gen = node->tn_gen; 1394171070Sdelphij 1395170808Sdelphij return (0); 1396170808Sdelphij} 1397171069Sdelphij 1398211598Sedstatic int 1399211598Sedtmpfs_whiteout(struct vop_whiteout_args *ap) 1400211598Sed{ 1401211598Sed struct vnode *dvp = ap->a_dvp; 1402211598Sed struct componentname *cnp = ap->a_cnp; 1403211598Sed struct tmpfs_dirent *de; 1404211598Sed 1405211598Sed switch (ap->a_flags) { 1406211598Sed case LOOKUP: 1407211598Sed return (0); 1408211598Sed case CREATE: 1409211598Sed de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp); 1410211598Sed if (de != NULL) 1411211598Sed return (de->td_node == NULL ? 0 : EEXIST); 1412211598Sed return (tmpfs_dir_whiteout_add(dvp, cnp)); 1413211598Sed case DELETE: 1414211598Sed tmpfs_dir_whiteout_remove(dvp, cnp); 1415211598Sed return (0); 1416211598Sed default: 1417211598Sed panic("tmpfs_whiteout: unknown op"); 1418211598Sed } 1419211598Sed} 1420211598Sed 1421313094Skibstatic int 1422313094Skibtmpfs_vptocnp_dir(struct tmpfs_node *tn, struct tmpfs_node *tnp, 1423313094Skib struct tmpfs_dirent **pde) 1424313094Skib{ 1425313094Skib struct tmpfs_dir_cursor dc; 1426313094Skib struct tmpfs_dirent *de; 1427313094Skib 1428313094Skib for (de = tmpfs_dir_first(tnp, &dc); de != NULL; 1429313094Skib de = tmpfs_dir_next(tnp, &dc)) { 1430313094Skib if (de->td_node == tn) { 1431313094Skib *pde = de; 1432313094Skib return (0); 1433313094Skib } 1434313094Skib } 1435313094Skib return (ENOENT); 1436313094Skib} 1437313094Skib 1438313094Skibstatic int 1439313094Skibtmpfs_vptocnp_fill(struct vnode *vp, struct tmpfs_node *tn, 1440313094Skib struct tmpfs_node *tnp, char *buf, int *buflen, struct vnode **dvp) 1441313094Skib{ 1442313094Skib struct tmpfs_dirent *de; 1443313094Skib int error, i; 1444313094Skib 1445313094Skib error = vn_vget_ino_gen(vp, tmpfs_vn_get_ino_alloc, tnp, LK_SHARED, 1446313094Skib dvp); 1447313094Skib if (error != 0) 1448313094Skib return (error); 1449313094Skib error = tmpfs_vptocnp_dir(tn, tnp, &de); 1450313094Skib if (error == 0) { 1451313094Skib i = *buflen; 1452313094Skib i -= de->td_namelen; 1453313094Skib if (i < 0) { 1454313094Skib error = ENOMEM; 1455313094Skib } else { 1456313094Skib bcopy(de->ud.td_name, buf + i, de->td_namelen); 1457313094Skib *buflen = i; 1458313094Skib } 1459313094Skib } 1460313094Skib if (error == 0) { 1461313094Skib if (vp != *dvp) 1462313094Skib VOP_UNLOCK(*dvp, 0); 1463313094Skib } else { 1464313094Skib if (vp != *dvp) 1465313094Skib vput(*dvp); 1466313094Skib else 1467313094Skib vrele(vp); 1468313094Skib } 1469313094Skib return (error); 1470313094Skib} 1471313094Skib 1472313094Skibstatic int 1473313094Skibtmpfs_vptocnp(struct vop_vptocnp_args *ap) 1474313094Skib{ 1475313094Skib struct vnode *vp, **dvp; 1476313094Skib struct tmpfs_node *tn, *tnp, *tnp1; 1477313094Skib struct tmpfs_dirent *de; 1478313094Skib struct tmpfs_mount *tm; 1479313094Skib char *buf; 1480313094Skib int *buflen; 1481313094Skib int error; 1482313094Skib 1483313094Skib vp = ap->a_vp; 1484313094Skib dvp = ap->a_vpp; 1485313094Skib buf = ap->a_buf; 1486313094Skib buflen = ap->a_buflen; 1487313094Skib 1488313094Skib tm = VFS_TO_TMPFS(vp->v_mount); 1489313094Skib tn = VP_TO_TMPFS_NODE(vp); 1490313094Skib if (tn->tn_type == VDIR) { 1491313094Skib tnp = tn->tn_dir.tn_parent; 1492313094Skib if (tnp == NULL) 1493313094Skib return (ENOENT); 1494313094Skib tmpfs_ref_node(tnp); 1495313094Skib error = tmpfs_vptocnp_fill(vp, tn, tn->tn_dir.tn_parent, buf, 1496313094Skib buflen, dvp); 1497313094Skib tmpfs_free_node(tm, tnp); 1498313094Skib return (error); 1499313094Skib } 1500313094Skibrestart: 1501313094Skib TMPFS_LOCK(tm); 1502313094Skib LIST_FOREACH_SAFE(tnp, &tm->tm_nodes_used, tn_entries, tnp1) { 1503313094Skib if (tnp->tn_type != VDIR) 1504313094Skib continue; 1505313094Skib TMPFS_NODE_LOCK(tnp); 1506313094Skib tmpfs_ref_node_locked(tnp); 1507313094Skib 1508313094Skib /* 1509313094Skib * tn_vnode cannot be instantiated while we hold the 1510313094Skib * node lock, so the directory cannot be changed while 1511313094Skib * we iterate over it. Do this to avoid instantiating 1512313094Skib * vnode for directories which cannot point to our 1513313094Skib * node. 1514313094Skib */ 1515313094Skib error = tnp->tn_vnode == NULL ? tmpfs_vptocnp_dir(tn, tnp, 1516313094Skib &de) : 0; 1517313094Skib 1518313094Skib if (error == 0) { 1519313094Skib TMPFS_NODE_UNLOCK(tnp); 1520313094Skib TMPFS_UNLOCK(tm); 1521313094Skib error = tmpfs_vptocnp_fill(vp, tn, tnp, buf, buflen, 1522313094Skib dvp); 1523313094Skib if (error == 0) { 1524313094Skib tmpfs_free_node(tm, tnp); 1525313094Skib return (0); 1526313094Skib } 1527313094Skib if ((vp->v_iflag & VI_DOOMED) != 0) { 1528313094Skib tmpfs_free_node(tm, tnp); 1529313094Skib return (ENOENT); 1530313094Skib } 1531313094Skib TMPFS_LOCK(tm); 1532313094Skib TMPFS_NODE_LOCK(tnp); 1533313094Skib } 1534313094Skib if (tmpfs_free_node_locked(tm, tnp, false)) { 1535313094Skib goto restart; 1536313094Skib } else { 1537313094Skib KASSERT(tnp->tn_refcount > 0, 1538313094Skib ("node %p refcount zero", tnp)); 1539313094Skib tnp1 = LIST_NEXT(tnp, tn_entries); 1540313094Skib TMPFS_NODE_UNLOCK(tnp); 1541313094Skib } 1542313094Skib } 1543313094Skib TMPFS_UNLOCK(tm); 1544313094Skib return (ENOENT); 1545313094Skib} 1546313094Skib 1547171069Sdelphij/* 1548312803Skib * Vnode operations vector used for files stored in a tmpfs file system. 1549171069Sdelphij */ 1550171069Sdelphijstruct vop_vector tmpfs_vnodeop_entries = { 1551171069Sdelphij .vop_default = &default_vnodeops, 1552171069Sdelphij .vop_lookup = vfs_cache_lookup, 1553313095Skib .vop_cachedlookup = tmpfs_cached_lookup, 1554171069Sdelphij .vop_create = tmpfs_create, 1555171069Sdelphij .vop_mknod = tmpfs_mknod, 1556171069Sdelphij .vop_open = tmpfs_open, 1557171069Sdelphij .vop_close = tmpfs_close, 1558171069Sdelphij .vop_access = tmpfs_access, 1559171069Sdelphij .vop_getattr = tmpfs_getattr, 1560171069Sdelphij .vop_setattr = tmpfs_setattr, 1561171069Sdelphij .vop_read = tmpfs_read, 1562171069Sdelphij .vop_write = tmpfs_write, 1563171069Sdelphij .vop_fsync = tmpfs_fsync, 1564171069Sdelphij .vop_remove = tmpfs_remove, 1565171069Sdelphij .vop_link = tmpfs_link, 1566171069Sdelphij .vop_rename = tmpfs_rename, 1567171069Sdelphij .vop_mkdir = tmpfs_mkdir, 1568171069Sdelphij .vop_rmdir = tmpfs_rmdir, 1569171069Sdelphij .vop_symlink = tmpfs_symlink, 1570171069Sdelphij .vop_readdir = tmpfs_readdir, 1571171069Sdelphij .vop_readlink = tmpfs_readlink, 1572171069Sdelphij .vop_inactive = tmpfs_inactive, 1573171069Sdelphij .vop_reclaim = tmpfs_reclaim, 1574171069Sdelphij .vop_print = tmpfs_print, 1575171069Sdelphij .vop_pathconf = tmpfs_pathconf, 1576171069Sdelphij .vop_vptofh = tmpfs_vptofh, 1577211598Sed .vop_whiteout = tmpfs_whiteout, 1578171069Sdelphij .vop_bmap = VOP_EOPNOTSUPP, 1579313094Skib .vop_vptocnp = tmpfs_vptocnp, 1580171069Sdelphij}; 1581171069Sdelphij 1582313095Skib/* 1583313095Skib * Same vector for mounts which do not use namecache. 1584313095Skib */ 1585313095Skibstruct vop_vector tmpfs_vnodeop_nonc_entries = { 1586313095Skib .vop_default = &tmpfs_vnodeop_entries, 1587313095Skib .vop_lookup = tmpfs_lookup, 1588313095Skib}; 1589