tmpfs_vnops.c revision 173725
1178431Sscf/* $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $ */ 2178431Sscf 3178431Sscf/* 4178431Sscf * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. 5178431Sscf * All rights reserved. 6178431Sscf * 7178431Sscf * This code is derived from software contributed to The NetBSD Foundation 8178431Sscf * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9178431Sscf * 2005 program. 10178431Sscf * 11178431Sscf * Redistribution and use in source and binary forms, with or without 12178431Sscf * modification, are permitted provided that the following conditions 13178431Sscf * are met: 14178431Sscf * 1. Redistributions of source code must retain the above copyright 15178431Sscf * notice, this list of conditions and the following disclaimer. 16178431Sscf * 2. Redistributions in binary form must reproduce the above copyright 17178431Sscf * notice, this list of conditions and the following disclaimer in the 18178431Sscf * documentation and/or other materials provided with the distribution. 19178431Sscf * 3. All advertising materials mentioning features or use of this software 20178431Sscf * must display the following acknowledgement: 21178431Sscf * This product includes software developed by the NetBSD 22178431Sscf * Foundation, Inc. and its contributors. 23178431Sscf * 4. Neither the name of The NetBSD Foundation nor the names of its 24178431Sscf * contributors may be used to endorse or promote products derived 25178431Sscf * from this software without specific prior written permission. 26178431Sscf * 27178431Sscf * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28178431Sscf * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29178431Sscf * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30178431Sscf * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31228545Sbapt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32228545Sbapt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33184831Sscf * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34228545Sbapt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35228545Sbapt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36228545Sbapt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37178431Sscf * POSSIBILITY OF SUCH DAMAGE. 38178431Sscf */ 39184831Sscf 40228545Sbapt/* 41178431Sscf * tmpfs vnode interface. 42178431Sscf */ 43178431Sscf#include <sys/cdefs.h> 44178431Sscf__FBSDID("$FreeBSD: head/sys/fs/tmpfs/tmpfs_vnops.c 173725 2007-11-18 04:52:40Z delphij $"); 45228545Sbapt 46178431Sscf#include <sys/param.h> 47228545Sbapt#include <sys/fcntl.h> 48228545Sbapt#include <sys/lockf.h> 49228545Sbapt#include <sys/namei.h> 50228545Sbapt#include <sys/priv.h> 51228545Sbapt#include <sys/proc.h> 52247919Sdb#include <sys/resourcevar.h> 53248102Sdb#include <sys/stat.h> 54228545Sbapt#include <sys/systm.h> 55178431Sscf#include <sys/unistd.h> 56228545Sbapt#include <sys/vnode.h> 57228545Sbapt 58228545Sbapt#include <vm/vm.h> 59228545Sbapt#include <vm/vm_object.h> 60228545Sbapt#include <vm/vm_page.h> 61242319Sbapt#include <vm/vm_pager.h> 62228545Sbapt#include <sys/sched.h> 63228545Sbapt#include <sys/sf_buf.h> 64228545Sbapt#include <machine/_inttypes.h> 65228545Sbapt 66228545Sbapt#include <fs/fifofs/fifo.h> 67228545Sbapt#include <fs/tmpfs/tmpfs_vnops.h> 68228545Sbapt#include <fs/tmpfs/tmpfs.h> 69228545Sbapt 70228545Sbapt/* --------------------------------------------------------------------- */ 71228545Sbapt 72228545Sbaptstatic int 73228545Sbapttmpfs_lookup(struct vop_cachedlookup_args *v) 74228545Sbapt{ 75228545Sbapt struct vnode *dvp = v->a_dvp; 76228545Sbapt struct vnode **vpp = v->a_vpp; 77228545Sbapt struct componentname *cnp = v->a_cnp; 78228545Sbapt struct thread *td = cnp->cn_thread; 79228545Sbapt 80228545Sbapt int error; 81228545Sbapt struct tmpfs_dirent *de; 82228545Sbapt struct tmpfs_node *dnode; 83228545Sbapt 84228545Sbapt dnode = VP_TO_TMPFS_DIR(dvp); 85228545Sbapt *vpp = NULLVP; 86228545Sbapt 87242319Sbapt /* Check accessibility of requested node as a first step. */ 88228545Sbapt error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td); 89228545Sbapt if (error != 0) 90228545Sbapt goto out; 91228545Sbapt 92228545Sbapt /* We cannot be requesting the parent directory of the root node. */ 93228545Sbapt MPASS(IMPLIES(dnode->tn_type == VDIR && 94228545Sbapt dnode->tn_dir.tn_parent == dnode, 95228545Sbapt !(cnp->cn_flags & ISDOTDOT))); 96228545Sbapt 97228545Sbapt if (cnp->cn_flags & ISDOTDOT) { 98228545Sbapt int ltype = 0; 99228545Sbapt 100228545Sbapt ltype = VOP_ISLOCKED(dvp, td); 101228545Sbapt vhold(dvp); 102228545Sbapt VOP_UNLOCK(dvp, 0, td); 103228545Sbapt /* Allocate a new vnode on the matching entry. */ 104244744Sbapt error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent, 105244735Sbapt cnp->cn_lkflags, vpp, td); 106228545Sbapt 107228545Sbapt vn_lock(dvp, ltype | LK_RETRY, td); 108228545Sbapt vdrop(dvp); 109228545Sbapt } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { 110228545Sbapt VREF(dvp); 111228545Sbapt *vpp = dvp; 112228545Sbapt error = 0; 113228545Sbapt } else { 114228545Sbapt de = tmpfs_dir_lookup(dnode, cnp); 115228545Sbapt if (de == NULL) { 116228545Sbapt /* The entry was not found in the directory. 117228545Sbapt * This is OK if we are creating or renaming an 118228545Sbapt * entry and are working on the last component of 119228545Sbapt * the path name. */ 120228545Sbapt if ((cnp->cn_flags & ISLASTCN) && 121228545Sbapt (cnp->cn_nameiop == CREATE || \ 122228545Sbapt cnp->cn_nameiop == RENAME)) { 123228545Sbapt error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 124228545Sbapt cnp->cn_thread); 125228545Sbapt if (error != 0) 126228545Sbapt goto out; 127228545Sbapt 128228545Sbapt /* Keep the component name in the buffer for 129228545Sbapt * future uses. */ 130228545Sbapt cnp->cn_flags |= SAVENAME; 131228545Sbapt 132228545Sbapt error = EJUSTRETURN; 133228545Sbapt } else 134228545Sbapt error = ENOENT; 135228545Sbapt } else { 136228545Sbapt struct tmpfs_node *tnode; 137228545Sbapt 138228545Sbapt /* The entry was found, so get its associated 139228545Sbapt * tmpfs_node. */ 140228545Sbapt tnode = de->td_node; 141228545Sbapt 142228545Sbapt /* If we are not at the last path component and 143228545Sbapt * found a non-directory or non-link entry (which 144285050Sgarga * may itself be pointing to a directory), raise 145228545Sbapt * an error. */ 146228545Sbapt if ((tnode->tn_type != VDIR && 147228545Sbapt tnode->tn_type != VLNK) && 148228545Sbapt !(cnp->cn_flags & ISLASTCN)) { 149228545Sbapt error = ENOTDIR; 150228545Sbapt goto out; 151228545Sbapt } 152228545Sbapt 153228545Sbapt /* If we are deleting or renaming the entry, keep 154228545Sbapt * track of its tmpfs_dirent so that it can be 155228545Sbapt * easily deleted later. */ 156228545Sbapt if ((cnp->cn_flags & ISLASTCN) && 157228545Sbapt (cnp->cn_nameiop == DELETE || 158228545Sbapt cnp->cn_nameiop == RENAME)) { 159228545Sbapt error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, 160228545Sbapt cnp->cn_thread); 161228545Sbapt if (error != 0) 162228545Sbapt goto out; 163228545Sbapt 164228545Sbapt /* Allocate a new vnode on the matching entry. */ 165228545Sbapt error = tmpfs_alloc_vp(dvp->v_mount, tnode, 166228545Sbapt cnp->cn_lkflags, vpp, td); 167228545Sbapt if (error != 0) 168228545Sbapt goto out; 169228545Sbapt 170228545Sbapt if ((dnode->tn_mode & S_ISTXT) && 171228545Sbapt VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) && 172228545Sbapt VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) { 173273791Sbapt error = EPERM; 174273791Sbapt vput(*vpp); 175273791Sbapt *vpp = NULL; 176273791Sbapt goto out; 177273791Sbapt } 178228545Sbapt cnp->cn_flags |= SAVENAME; 179228545Sbapt } else { 180273791Sbapt error = tmpfs_alloc_vp(dvp->v_mount, tnode, 181273791Sbapt cnp->cn_lkflags, vpp, td); 182228545Sbapt } 183273791Sbapt } 184228545Sbapt } 185273791Sbapt 186273791Sbapt /* Store the result of this lookup in the cache. Avoid this if the 187273791Sbapt * request was for creation, as it does not improve timings on 188273791Sbapt * emprical tests. */ 189228545Sbapt if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) 190228545Sbapt cache_enter(dvp, *vpp, cnp); 191228545Sbapt 192228545Sbaptout: 193228545Sbapt /* If there were no errors, *vpp cannot be null and it must be 194228545Sbapt * locked. */ 195228545Sbapt MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp, td))); 196228545Sbapt 197228545Sbapt return error; 198228545Sbapt} 199228545Sbapt 200228545Sbapt/* --------------------------------------------------------------------- */ 201228545Sbapt 202228545Sbaptstatic int 203228545Sbapttmpfs_create(struct vop_create_args *v) 204228545Sbapt{ 205228545Sbapt struct vnode *dvp = v->a_dvp; 206228545Sbapt struct vnode **vpp = v->a_vpp; 207228545Sbapt struct componentname *cnp = v->a_cnp; 208228545Sbapt struct vattr *vap = v->a_vap; 209228545Sbapt 210228545Sbapt MPASS(vap->va_type == VREG || vap->va_type == VSOCK); 211228545Sbapt 212228545Sbapt return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 213228545Sbapt} 214228545Sbapt/* --------------------------------------------------------------------- */ 215228545Sbapt 216228545Sbaptstatic int 217228545Sbapttmpfs_mknod(struct vop_mknod_args *v) 218228545Sbapt{ 219228545Sbapt struct vnode *dvp = v->a_dvp; 220228545Sbapt struct vnode **vpp = v->a_vpp; 221228545Sbapt struct componentname *cnp = v->a_cnp; 222228545Sbapt struct vattr *vap = v->a_vap; 223228545Sbapt 224228545Sbapt if (vap->va_type != VBLK && vap->va_type != VCHR && 225228545Sbapt vap->va_type != VFIFO) 226228545Sbapt return EINVAL; 227228545Sbapt 228228545Sbapt return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 229228545Sbapt} 230228545Sbapt 231228545Sbapt/* --------------------------------------------------------------------- */ 232228545Sbapt 233228545Sbaptstatic int 234228545Sbapttmpfs_open(struct vop_open_args *v) 235228545Sbapt{ 236228545Sbapt struct vnode *vp = v->a_vp; 237228545Sbapt int mode = v->a_mode; 238228545Sbapt 239228545Sbapt int error; 240228545Sbapt struct tmpfs_node *node; 241228545Sbapt 242228545Sbapt MPASS(VOP_ISLOCKED(vp, v->a_td)); 243228545Sbapt 244228545Sbapt node = VP_TO_TMPFS_NODE(vp); 245228545Sbapt 246228545Sbapt /* The file is still active but all its names have been removed 247228545Sbapt * (e.g. by a "rmdir $(pwd)"). It cannot be opened any more as 248228545Sbapt * it is about to die. */ 249228545Sbapt if (node->tn_links < 1) 250228545Sbapt return (ENOENT); 251228545Sbapt 252228545Sbapt /* If the file is marked append-only, deny write requests. */ 253228545Sbapt if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE) 254228545Sbapt error = EPERM; 255228545Sbapt else { 256228545Sbapt error = 0; 257228545Sbapt vnode_create_vobject(vp, node->tn_size, v->a_td); 258228545Sbapt } 259228545Sbapt 260228545Sbapt MPASS(VOP_ISLOCKED(vp, v->a_td)); 261228545Sbapt return error; 262228545Sbapt} 263228545Sbapt 264228545Sbapt/* --------------------------------------------------------------------- */ 265228545Sbapt 266228545Sbaptstatic int 267228545Sbapttmpfs_close(struct vop_close_args *v) 268228545Sbapt{ 269228545Sbapt struct vnode *vp = v->a_vp; 270228545Sbapt 271228545Sbapt struct tmpfs_node *node; 272228545Sbapt 273228545Sbapt MPASS(VOP_ISLOCKED(vp, v->a_td)); 274228545Sbapt 275228545Sbapt node = VP_TO_TMPFS_NODE(vp); 276228545Sbapt 277228545Sbapt if (node->tn_links > 0) { 278228545Sbapt /* Update node times. No need to do it if the node has 279228545Sbapt * been deleted, because it will vanish after we return. */ 280228545Sbapt tmpfs_update(vp); 281228545Sbapt } 282228545Sbapt 283228545Sbapt return 0; 284228545Sbapt} 285228545Sbapt 286228545Sbapt/* --------------------------------------------------------------------- */ 287228545Sbapt 288228545Sbaptint 289228545Sbapttmpfs_access(struct vop_access_args *v) 290228545Sbapt{ 291228545Sbapt struct vnode *vp = v->a_vp; 292228545Sbapt int mode = v->a_mode; 293228545Sbapt struct ucred *cred = v->a_cred; 294228545Sbapt 295228545Sbapt int error; 296228545Sbapt struct tmpfs_node *node; 297228545Sbapt 298228545Sbapt MPASS(VOP_ISLOCKED(vp, v->a_td)); 299228545Sbapt 300228545Sbapt node = VP_TO_TMPFS_NODE(vp); 301228545Sbapt 302228545Sbapt switch (vp->v_type) { 303228545Sbapt case VDIR: 304228545Sbapt /* FALLTHROUGH */ 305228545Sbapt case VLNK: 306228545Sbapt /* FALLTHROUGH */ 307228545Sbapt case VREG: 308228545Sbapt if (mode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) { 309228545Sbapt error = EROFS; 310228545Sbapt goto out; 311228545Sbapt } 312228545Sbapt break; 313228545Sbapt 314228545Sbapt case VBLK: 315228545Sbapt /* FALLTHROUGH */ 316228545Sbapt case VCHR: 317228545Sbapt /* FALLTHROUGH */ 318228545Sbapt case VSOCK: 319228545Sbapt /* FALLTHROUGH */ 320228545Sbapt case VFIFO: 321285050Sgarga break; 322285050Sgarga 323243334Sbapt default: 324243334Sbapt error = EINVAL; 325243328Sbapt goto out; 326285050Sgarga } 327285050Sgarga 328285050Sgarga if (mode & VWRITE && node->tn_flags & IMMUTABLE) { 329285050Sgarga error = EPERM; 330285050Sgarga goto out; 331285050Sgarga } 332285050Sgarga 333285050Sgarga error = vaccess(vp->v_type, node->tn_mode, node->tn_uid, 334285050Sgarga node->tn_gid, mode, cred, NULL); 335285050Sgarga 336285050Sgargaout: 337285050Sgarga MPASS(VOP_ISLOCKED(vp, v->a_td)); 338285050Sgarga 339285050Sgarga return error; 340285050Sgarga} 341285050Sgarga 342285050Sgarga/* --------------------------------------------------------------------- */ 343228545Sbapt 344228545Sbaptint 345228545Sbapttmpfs_getattr(struct vop_getattr_args *v) 346245390Smjg{ 347228545Sbapt struct vnode *vp = v->a_vp; 348228545Sbapt struct vattr *vap = v->a_vap; 349228545Sbapt 350228545Sbapt struct tmpfs_node *node; 351228545Sbapt 352228545Sbapt node = VP_TO_TMPFS_NODE(vp); 353228545Sbapt 354228545Sbapt VATTR_NULL(vap); 355228545Sbapt 356228545Sbapt tmpfs_update(vp); 357228545Sbapt 358228545Sbapt vap->va_type = vp->v_type; 359228545Sbapt vap->va_mode = node->tn_mode; 360228545Sbapt vap->va_nlink = node->tn_links; 361228545Sbapt vap->va_uid = node->tn_uid; 362228545Sbapt vap->va_gid = node->tn_gid; 363228545Sbapt vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; 364228545Sbapt vap->va_fileid = node->tn_id; 365228545Sbapt vap->va_size = node->tn_size; 366228545Sbapt vap->va_blocksize = PAGE_SIZE; 367178431Sscf vap->va_atime = node->tn_atime; 368178431Sscf vap->va_mtime = node->tn_mtime; 369178431Sscf vap->va_ctime = node->tn_ctime; 370178431Sscf vap->va_birthtime = node->tn_birthtime; 371178431Sscf vap->va_gen = node->tn_gen; 372178431Sscf vap->va_flags = node->tn_flags; 373178431Sscf vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ? 374185237Sscf node->tn_rdev : VNOVAL; 375185237Sscf vap->va_bytes = round_page(node->tn_size); 376185237Sscf vap->va_filerev = VNOVAL; 377185237Sscf vap->va_vaflags = 0; 378185237Sscf vap->va_spare = VNOVAL; /* XXX */ 379185237Sscf 380185237Sscf return 0; 381185237Sscf} 382185237Sscf 383185237Sscf/* --------------------------------------------------------------------- */ 384185237Sscf 385185237Sscf/* XXX Should this operation be atomic? I think it should, but code in 386178431Sscf * XXX other places (e.g., ufs) doesn't seem to be... */ 387277669Smarkjint 388277669Smarkjtmpfs_setattr(struct vop_setattr_args *v) 389248102Sdb{ 390248102Sdb struct vnode *vp = v->a_vp; 391248102Sdb struct vattr *vap = v->a_vap; 392248102Sdb struct ucred *cred = v->a_cred; 393248102Sdb struct thread *l = v->a_td; 394248102Sdb 395248102Sdb int error; 396277669Smarkj 397277669Smarkj MPASS(VOP_ISLOCKED(vp, l)); 398248102Sdb 399248102Sdb error = 0; 400178431Sscf 401277669Smarkj /* Abort if any unsettable attribute is given. */ 402277669Smarkj if (vap->va_type != VNON || 403277669Smarkj vap->va_nlink != VNOVAL || 404277669Smarkj vap->va_fsid != VNOVAL || 405277669Smarkj vap->va_fileid != VNOVAL || 406277669Smarkj vap->va_blocksize != VNOVAL || 407178431Sscf vap->va_gen != VNOVAL || 408178431Sscf vap->va_rdev != VNOVAL || 409185237Sscf vap->va_bytes != VNOVAL) 410178431Sscf error = EINVAL; 411178431Sscf 412178431Sscf if (error == 0 && (vap->va_flags != VNOVAL)) 413178431Sscf error = tmpfs_chflags(vp, vap->va_flags, cred, l); 414178431Sscf 415178431Sscf if (error == 0 && (vap->va_size != VNOVAL)) 416178431Sscf error = tmpfs_chsize(vp, vap->va_size, cred, l); 417178431Sscf 418245386Smjg if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) 419245387Smjg error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, 420178431Sscf l); 421245387Smjg 422185237Sscf if (error == 0 && (vap->va_mode != (mode_t)VNOVAL)) 423178431Sscf error = tmpfs_chmod(vp, vap->va_mode, cred, l); 424178431Sscf 425178431Sscf if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL && 426185237Sscf vap->va_atime.tv_nsec != VNOVAL) || 427178431Sscf (vap->va_mtime.tv_sec != VNOVAL && 428185237Sscf vap->va_mtime.tv_nsec != VNOVAL) || 429185237Sscf (vap->va_birthtime.tv_sec != VNOVAL && 430185237Sscf vap->va_birthtime.tv_nsec != VNOVAL))) 431185237Sscf error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime, 432185237Sscf &vap->va_birthtime, vap->va_vaflags, cred, l); 433185237Sscf 434178431Sscf /* Update the node times. We give preference to the error codes 435178431Sscf * generated by this function rather than the ones that may arise 436245387Smjg * from tmpfs_update. */ 437178431Sscf tmpfs_update(vp); 438245387Smjg 439200423Sscf MPASS(VOP_ISLOCKED(vp, l)); 440245387Smjg 441245387Smjg return error; 442185237Sscf} 443245387Smjg 444245387Smjg/* --------------------------------------------------------------------- */ 445245387Smjg 446185237Sscfstatic int 447245387Smjgtmpfs_mappedread(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio) 448178431Sscf{ 449178431Sscf vm_pindex_t idx; 450178431Sscf vm_page_t m; 451178431Sscf struct sf_buf *sf; 452178431Sscf off_t offset, addr; 453178431Sscf size_t tlen; 454178431Sscf caddr_t va; 455178431Sscf int error; 456178431Sscf 457178431Sscf addr = uio->uio_offset; 458247919Sdb idx = OFF_TO_IDX(addr); 459247919Sdb offset = addr & PAGE_MASK; 460247919Sdb tlen = MIN(PAGE_SIZE - offset, len); 461247919Sdb 462247919Sdb if ((vobj == NULL) || (vobj->resident_page_count == 0)) 463247919Sdb goto nocache; 464247919Sdb 465247919Sdb VM_OBJECT_LOCK(vobj); 466248102Sdblookupvpg: 467184831Sscf if (((m = vm_page_lookup(vobj, idx)) != NULL) && 468185237Sscf vm_page_is_valid(m, offset, tlen)) { 469178431Sscf if (vm_page_sleep_if_busy(m, FALSE, "tmfsmr")) 470247919Sdb goto lookupvpg; 471247919Sdb vm_page_busy(m); 472178431Sscf VM_OBJECT_UNLOCK(vobj); 473248102Sdb sched_pin(); 474178431Sscf sf = sf_buf_alloc(m, SFB_CPUPRIVATE); 475248102Sdb va = (caddr_t)sf_buf_kva(sf); 476247919Sdb error = uiomove(va + offset, tlen, uio); 477247919Sdb sf_buf_free(sf); 478247919Sdb sched_unpin(); 479248102Sdb VM_OBJECT_LOCK(vobj); 480248102Sdb vm_page_wakeup(m); 481247919Sdb VM_OBJECT_UNLOCK(vobj); 482247919Sdb return (error); 483247919Sdb } 484247919Sdb VM_OBJECT_UNLOCK(vobj); 485247919Sdbnocache: 486247919Sdb VM_OBJECT_LOCK(tobj); 487247919Sdb vm_object_pip_add(tobj, 1); 488247919Sdb m = vm_page_grab(tobj, idx, VM_ALLOC_WIRED | 489247919Sdb VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY); 490247919Sdb if (m->valid != VM_PAGE_BITS_ALL) { 491247919Sdb int behind, ahead; 492247919Sdb if (vm_pager_has_page(tobj, idx, &behind, &ahead)) { 493247919Sdb error = vm_pager_get_pages(tobj, &m, 1, 0); 494247919Sdb if (error != 0) { 495247919Sdb printf("tmpfs get pages from pager error [read]\n"); 496247919Sdb goto out; 497247919Sdb } 498247919Sdb } else 499247919Sdb vm_page_zero_invalid(m, TRUE); 500247919Sdb } 501248102Sdb VM_OBJECT_UNLOCK(tobj); 502247919Sdb sched_pin(); 503247919Sdb sf = sf_buf_alloc(m, SFB_CPUPRIVATE); 504248102Sdb va = (caddr_t)sf_buf_kva(sf); 505247919Sdb error = uiomove(va + offset, tlen, uio); 506247919Sdb sf_buf_free(sf); 507248102Sdb sched_unpin(); 508247919Sdb VM_OBJECT_LOCK(tobj); 509248102Sdbout: 510248102Sdb vm_page_lock_queues(); 511248102Sdb vm_page_unwire(m, 0); 512248102Sdb vm_page_activate(m); 513248102Sdb vm_page_unlock_queues(); 514248102Sdb vm_page_wakeup(m); 515244742Sbapt vm_object_pip_subtract(tobj, 1); 516178431Sscf VM_OBJECT_UNLOCK(tobj); 517244742Sbapt 518244742Sbapt return (error); 519247919Sdb} 520244777Sbapt 521178431Sscfstatic int 522244742Sbapttmpfs_read(struct vop_read_args *v) 523244742Sbapt{ 524247919Sdb struct vnode *vp = v->a_vp; 525244777Sbapt struct uio *uio = v->a_uio; 526244742Sbapt 527248102Sdb struct tmpfs_node *node; 528248102Sdb vm_object_t uobj; 529248102Sdb size_t len; 530248102Sdb int resid; 531247919Sdb 532247919Sdb int error; 533178431Sscf 534248102Sdb node = VP_TO_TMPFS_NODE(vp); 535248102Sdb 536248102Sdb if (vp->v_type != VREG) { 537248102Sdb error = EISDIR; 538248102Sdb goto out; 539248102Sdb } 540248102Sdb 541248102Sdb if (uio->uio_offset < 0) { 542247919Sdb error = EINVAL; 543248102Sdb goto out; 544244742Sbapt } 545178431Sscf 546178431Sscf node->tn_status |= TMPFS_NODE_ACCESSED; 547178431Sscf 548247919Sdb uobj = node->tn_reg.tn_aobj; 549244736Sbapt while ((resid = uio->uio_resid) > 0) { 550247919Sdb error = 0; 551247919Sdb if (node->tn_size <= uio->uio_offset) 552244736Sbapt break; 553247919Sdb len = MIN(node->tn_size - uio->uio_offset, resid); 554247919Sdb if (len == 0) 555244736Sbapt break; 556247919Sdb error = tmpfs_mappedread(vp->v_object, uobj, len, uio); 557247919Sdb if ((error != 0) || (resid == uio->uio_resid)) 558247919Sdb break; 559247919Sdb } 560247919Sdb 561247919Sdbout: 562247919Sdb 563247919Sdb return error; 564248102Sdb} 565244736Sbapt 566248102Sdb/* --------------------------------------------------------------------- */ 567247919Sdb 568247919Sdbstatic int 569244736Sbapttmpfs_mappedwrite(vm_object_t vobj, vm_object_t tobj, size_t len, struct uio *uio) 570244736Sbapt{ 571247919Sdb vm_pindex_t idx; 572248102Sdb vm_page_t vpg, tpg; 573247919Sdb struct sf_buf *sf; 574248102Sdb off_t offset, addr; 575247919Sdb size_t tlen; 576248102Sdb caddr_t va; 577248102Sdb int error; 578248102Sdb 579248102Sdb addr = uio->uio_offset; 580247919Sdb idx = OFF_TO_IDX(addr); 581244736Sbapt offset = addr & PAGE_MASK; 582244736Sbapt tlen = MIN(PAGE_SIZE - offset, len); 583244736Sbapt 584178431Sscf if ((vobj == NULL) || (vobj->resident_page_count == 0)) { 585178431Sscf vpg = NULL; 586178431Sscf goto nocache; 587178431Sscf } 588178431Sscf 589178431Sscf VM_OBJECT_LOCK(vobj); 590178431Sscflookupvpg: 591178431Sscf if (((vpg = vm_page_lookup(vobj, idx)) != NULL) && 592178431Sscf vm_page_is_valid(vpg, offset, tlen)) { 593178431Sscf if (vm_page_sleep_if_busy(vpg, FALSE, "tmfsmw")) 594178431Sscf goto lookupvpg; 595178431Sscf vm_page_busy(vpg); 596178431Sscf vm_page_lock_queues(); 597178431Sscf vm_page_undirty(vpg); 598184831Sscf vm_page_unlock_queues(); 599184831Sscf VM_OBJECT_UNLOCK(vobj); 600178431Sscf sched_pin(); 601178431Sscf sf = sf_buf_alloc(vpg, SFB_CPUPRIVATE); 602178431Sscf va = (caddr_t)sf_buf_kva(sf); 603178431Sscf error = uiomove(va + offset, tlen, uio); 604178431Sscf sf_buf_free(sf); 605184831Sscf sched_unpin(); 606178431Sscf } else { 607178431Sscf VM_OBJECT_UNLOCK(vobj); 608178431Sscf vpg = NULL; 609178431Sscf } 610178431Sscfnocache: 611178431Sscf VM_OBJECT_LOCK(tobj); 612178431Sscf vm_object_pip_add(tobj, 1); 613185237Sscf tpg = vm_page_grab(tobj, idx, VM_ALLOC_WIRED | 614185237Sscf VM_ALLOC_ZERO | VM_ALLOC_NORMAL | VM_ALLOC_RETRY); 615185237Sscf if (tpg->valid != VM_PAGE_BITS_ALL) { 616185237Sscf int behind, ahead; 617185237Sscf if (vm_pager_has_page(tobj, idx, &behind, &ahead)) { 618185237Sscf error = vm_pager_get_pages(tobj, &tpg, 1, 0); 619185237Sscf if (error != 0) { 620185237Sscf printf("tmpfs get pages from pager error [write]\n"); 621178431Sscf goto out; 622178431Sscf } 623185237Sscf } else 624185237Sscf vm_page_zero_invalid(tpg, TRUE); 625178431Sscf } 626178431Sscf VM_OBJECT_UNLOCK(tobj); 627178431Sscf if (vpg == NULL) { 628178431Sscf sched_pin(); 629178431Sscf sf = sf_buf_alloc(tpg, SFB_CPUPRIVATE); 630178431Sscf va = (caddr_t)sf_buf_kva(sf); 631178431Sscf error = uiomove(va + offset, tlen, uio); 632178431Sscf sf_buf_free(sf); 633178431Sscf sched_unpin(); 634178431Sscf } else { 635184831Sscf KASSERT(vpg->valid == VM_PAGE_BITS_ALL, ("parts of vpg invalid")); 636185237Sscf pmap_copy_page(vpg, tpg); 637185237Sscf } 638178431Sscf VM_OBJECT_LOCK(tobj); 639185237Sscfout: 640178431Sscf if (vobj != NULL) 641185237Sscf VM_OBJECT_LOCK(vobj); 642185237Sscf vm_page_lock_queues(); 643178431Sscf if (error == 0) { 644178431Sscf vm_page_set_validclean(tpg, offset, tlen); 645185237Sscf vm_page_zero_invalid(tpg, TRUE); 646185237Sscf vm_page_dirty(tpg); 647178431Sscf } 648178431Sscf vm_page_unwire(tpg, 0); 649178431Sscf vm_page_activate(tpg); 650185237Sscf vm_page_unlock_queues(); 651178431Sscf vm_page_wakeup(tpg); 652 if (vpg != NULL) 653 vm_page_wakeup(vpg); 654 if (vobj != NULL) 655 VM_OBJECT_UNLOCK(vobj); 656 vm_object_pip_subtract(tobj, 1); 657 VM_OBJECT_UNLOCK(tobj); 658 659 return (error); 660} 661 662static int 663tmpfs_write(struct vop_write_args *v) 664{ 665 struct vnode *vp = v->a_vp; 666 struct uio *uio = v->a_uio; 667 int ioflag = v->a_ioflag; 668 struct thread *td = uio->uio_td; 669 670 boolean_t extended; 671 int error = 0; 672 off_t oldsize; 673 struct tmpfs_node *node; 674 vm_object_t uobj; 675 size_t len; 676 int resid; 677 678 node = VP_TO_TMPFS_NODE(vp); 679 oldsize = node->tn_size; 680 681 if (uio->uio_offset < 0 || vp->v_type != VREG) { 682 error = EINVAL; 683 goto out; 684 } 685 686 if (uio->uio_resid == 0) { 687 error = 0; 688 goto out; 689 } 690 691 if (ioflag & IO_APPEND) 692 uio->uio_offset = node->tn_size; 693 694 if (uio->uio_offset + uio->uio_resid > 695 VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) 696 return (EFBIG); 697 698 if (vp->v_type == VREG && td != NULL) { 699 PROC_LOCK(td->td_proc); 700 if (uio->uio_offset + uio->uio_resid > 701 lim_cur(td->td_proc, RLIMIT_FSIZE)) { 702 psignal(td->td_proc, SIGXFSZ); 703 PROC_UNLOCK(td->td_proc); 704 return (EFBIG); 705 } 706 PROC_UNLOCK(td->td_proc); 707 } 708 709 extended = uio->uio_offset + uio->uio_resid > node->tn_size; 710 if (extended) { 711 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid); 712 if (error != 0) 713 goto out; 714 } 715 716 uobj = node->tn_reg.tn_aobj; 717 while ((resid = uio->uio_resid) > 0) { 718 if (node->tn_size <= uio->uio_offset) 719 break; 720 len = MIN(node->tn_size - uio->uio_offset, resid); 721 if (len == 0) 722 break; 723 error = tmpfs_mappedwrite(vp->v_object, uobj, len, uio); 724 if ((error != 0) || (resid == uio->uio_resid)) 725 break; 726 } 727 728 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | 729 (extended ? TMPFS_NODE_CHANGED : 0); 730 731 if (node->tn_mode & (S_ISUID | S_ISGID)) { 732 if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0)) 733 node->tn_mode &= ~(S_ISUID | S_ISGID); 734 } 735 736 if (error != 0) 737 (void)tmpfs_reg_resize(vp, oldsize); 738 739out: 740 MPASS(IMPLIES(error == 0, uio->uio_resid == 0)); 741 MPASS(IMPLIES(error != 0, oldsize == node->tn_size)); 742 743 return error; 744} 745 746/* --------------------------------------------------------------------- */ 747 748static int 749tmpfs_fsync(struct vop_fsync_args *v) 750{ 751 struct vnode *vp = v->a_vp; 752 753 MPASS(VOP_ISLOCKED(vp, v->a_td)); 754 755 tmpfs_update(vp); 756 757 return 0; 758} 759 760/* --------------------------------------------------------------------- */ 761 762static int 763tmpfs_remove(struct vop_remove_args *v) 764{ 765 struct vnode *dvp = v->a_dvp; 766 struct vnode *vp = v->a_vp; 767 768 int error; 769 struct tmpfs_dirent *de; 770 struct tmpfs_mount *tmp; 771 struct tmpfs_node *dnode; 772 struct tmpfs_node *node; 773 774 MPASS(VOP_ISLOCKED(dvp, v->a_cnp->cn_thread)); 775 MPASS(VOP_ISLOCKED(vp, v->a_cnp->cn_thread)); 776 777 if (vp->v_type == VDIR) { 778 error = EISDIR; 779 goto out; 780 } 781 782 dnode = VP_TO_TMPFS_DIR(dvp); 783 node = VP_TO_TMPFS_NODE(vp); 784 tmp = VFS_TO_TMPFS(vp->v_mount); 785 de = tmpfs_dir_search(dnode, node); 786 MPASS(de != NULL); 787 788 /* Files marked as immutable or append-only cannot be deleted. */ 789 if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) || 790 (dnode->tn_flags & APPEND)) { 791 error = EPERM; 792 goto out; 793 } 794 795 /* Remove the entry from the directory; as it is a file, we do not 796 * have to change the number of hard links of the directory. */ 797 tmpfs_dir_detach(dvp, de); 798 799 /* Free the directory entry we just deleted. Note that the node 800 * referred by it will not be removed until the vnode is really 801 * reclaimed. */ 802 tmpfs_free_dirent(tmp, de, TRUE); 803 804 if (node->tn_links > 0) 805 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 806 TMPFS_NODE_MODIFIED; 807 error = 0; 808 809out: 810 811 return error; 812} 813 814/* --------------------------------------------------------------------- */ 815 816static int 817tmpfs_link(struct vop_link_args *v) 818{ 819 struct vnode *dvp = v->a_tdvp; 820 struct vnode *vp = v->a_vp; 821 struct componentname *cnp = v->a_cnp; 822 823 int error; 824 struct tmpfs_dirent *de; 825 struct tmpfs_node *node; 826 827 MPASS(VOP_ISLOCKED(dvp, cnp->cn_thread)); 828 MPASS(cnp->cn_flags & HASBUF); 829 MPASS(dvp != vp); /* XXX When can this be false? */ 830 831 node = VP_TO_TMPFS_NODE(vp); 832 833 /* XXX: Why aren't the following two tests done by the caller? */ 834 835 /* Hard links of directories are forbidden. */ 836 if (vp->v_type == VDIR) { 837 error = EPERM; 838 goto out; 839 } 840 841 /* Cannot create cross-device links. */ 842 if (dvp->v_mount != vp->v_mount) { 843 error = EXDEV; 844 goto out; 845 } 846 847 /* Ensure that we do not overflow the maximum number of links imposed 848 * by the system. */ 849 MPASS(node->tn_links <= LINK_MAX); 850 if (node->tn_links == LINK_MAX) { 851 error = EMLINK; 852 goto out; 853 } 854 855 /* We cannot create links of files marked immutable or append-only. */ 856 if (node->tn_flags & (IMMUTABLE | APPEND)) { 857 error = EPERM; 858 goto out; 859 } 860 861 /* Allocate a new directory entry to represent the node. */ 862 error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node, 863 cnp->cn_nameptr, cnp->cn_namelen, &de); 864 if (error != 0) 865 goto out; 866 867 /* Insert the new directory entry into the appropriate directory. */ 868 tmpfs_dir_attach(dvp, de); 869 870 /* vp link count has changed, so update node times. */ 871 node->tn_status |= TMPFS_NODE_CHANGED; 872 tmpfs_update(vp); 873 874 error = 0; 875 876out: 877 return error; 878} 879 880/* --------------------------------------------------------------------- */ 881 882static int 883tmpfs_rename(struct vop_rename_args *v) 884{ 885 struct vnode *fdvp = v->a_fdvp; 886 struct vnode *fvp = v->a_fvp; 887 struct componentname *fcnp = v->a_fcnp; 888 struct vnode *tdvp = v->a_tdvp; 889 struct vnode *tvp = v->a_tvp; 890 struct componentname *tcnp = v->a_tcnp; 891 892 char *newname; 893 int error; 894 struct tmpfs_dirent *de; 895 struct tmpfs_node *fdnode; 896 struct tmpfs_node *fnode; 897 struct tmpfs_node *tnode; 898 struct tmpfs_node *tdnode; 899 900 MPASS(VOP_ISLOCKED(tdvp, tcnp->cn_thread)); 901 MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp, tcnp->cn_thread))); 902 MPASS(fcnp->cn_flags & HASBUF); 903 MPASS(tcnp->cn_flags & HASBUF); 904 905 tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp); 906 907 /* Disallow cross-device renames. 908 * XXX Why isn't this done by the caller? */ 909 if (fvp->v_mount != tdvp->v_mount || 910 (tvp != NULL && fvp->v_mount != tvp->v_mount)) { 911 error = EXDEV; 912 goto out; 913 } 914 915 tdnode = VP_TO_TMPFS_DIR(tdvp); 916 917 /* If source and target are the same file, there is nothing to do. */ 918 if (fvp == tvp) { 919 error = 0; 920 goto out; 921 } 922 923 /* If we need to move the directory between entries, lock the 924 * source so that we can safely operate on it. */ 925 if (tdvp != fdvp) { 926 error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, tcnp->cn_thread); 927 if (error != 0) 928 goto out; 929 } 930 fdnode = VP_TO_TMPFS_DIR(fdvp); 931 fnode = VP_TO_TMPFS_NODE(fvp); 932 de = tmpfs_dir_search(fdnode, fnode); 933 934 /* Avoid manipulating '.' and '..' entries. */ 935 if (de == NULL) { 936 MPASS(fvp->v_type == VDIR); 937 error = EINVAL; 938 goto out_locked; 939 } 940 MPASS(de->td_node == fnode); 941 942 /* If re-naming a directory to another preexisting directory 943 * ensure that the target directory is empty so that its 944 * removal causes no side effects. 945 * Kern_rename gurantees the destination to be a directory 946 * if the source is one. */ 947 if (tvp != NULL) { 948 MPASS(tnode != NULL); 949 950 if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) || 951 (tdnode->tn_flags & (APPEND | IMMUTABLE))) { 952 error = EPERM; 953 goto out_locked; 954 } 955 956 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) { 957 if (tnode->tn_size > 0) { 958 error = ENOTEMPTY; 959 goto out_locked; 960 } 961 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) { 962 error = ENOTDIR; 963 goto out_locked; 964 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) { 965 error = EISDIR; 966 goto out_locked; 967 } else { 968 MPASS(fnode->tn_type != VDIR && 969 tnode->tn_type != VDIR); 970 } 971 } 972 973 if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) 974 || (fdnode->tn_flags & (APPEND | IMMUTABLE))) { 975 error = EPERM; 976 goto out_locked; 977 } 978 979 /* Ensure that we have enough memory to hold the new name, if it 980 * has to be changed. */ 981 if (fcnp->cn_namelen != tcnp->cn_namelen || 982 memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) { 983 newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK); 984 } else 985 newname = NULL; 986 987 /* If the node is being moved to another directory, we have to do 988 * the move. */ 989 if (fdnode != tdnode) { 990 /* In case we are moving a directory, we have to adjust its 991 * parent to point to the new parent. */ 992 if (de->td_node->tn_type == VDIR) { 993 struct tmpfs_node *n; 994 995 /* Ensure the target directory is not a child of the 996 * directory being moved. Otherwise, we'd end up 997 * with stale nodes. */ 998 n = tdnode; 999 while (n != n->tn_dir.tn_parent) { 1000 if (n == fnode) { 1001 error = EINVAL; 1002 if (newname != NULL) 1003 free(newname, M_TMPFSNAME); 1004 goto out_locked; 1005 } 1006 n = n->tn_dir.tn_parent; 1007 } 1008 1009 /* Adjust the parent pointer. */ 1010 TMPFS_VALIDATE_DIR(fnode); 1011 de->td_node->tn_dir.tn_parent = tdnode; 1012 1013 /* As a result of changing the target of the '..' 1014 * entry, the link count of the source and target 1015 * directories has to be adjusted. */ 1016 fdnode->tn_links--; 1017 tdnode->tn_links++; 1018 } 1019 1020 /* Do the move: just remove the entry from the source directory 1021 * and insert it into the target one. */ 1022 tmpfs_dir_detach(fdvp, de); 1023 tmpfs_dir_attach(tdvp, de); 1024 } 1025 1026 /* If the name has changed, we need to make it effective by changing 1027 * it in the directory entry. */ 1028 if (newname != NULL) { 1029 MPASS(tcnp->cn_namelen <= MAXNAMLEN); 1030 1031 free(de->td_name, M_TMPFSNAME); 1032 de->td_namelen = (uint16_t)tcnp->cn_namelen; 1033 memcpy(newname, tcnp->cn_nameptr, tcnp->cn_namelen); 1034 de->td_name = newname; 1035 1036 fnode->tn_status |= TMPFS_NODE_CHANGED; 1037 tdnode->tn_status |= TMPFS_NODE_MODIFIED; 1038 } 1039 1040 /* If we are overwriting an entry, we have to remove the old one 1041 * from the target directory. */ 1042 if (tvp != NULL) { 1043 /* Remove the old entry from the target directory. */ 1044 de = tmpfs_dir_search(tdnode, tnode); 1045 tmpfs_dir_detach(tdvp, de); 1046 1047 /* Free the directory entry we just deleted. Note that the 1048 * node referred by it will not be removed until the vnode is 1049 * really reclaimed. */ 1050 tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), de, TRUE); 1051 } 1052 1053 error = 0; 1054 1055out_locked: 1056 if (fdnode != tdnode) 1057 VOP_UNLOCK(fdvp, 0, tcnp->cn_thread); 1058 1059out: 1060 /* Release target nodes. */ 1061 /* XXX: I don't understand when tdvp can be the same as tvp, but 1062 * other code takes care of this... */ 1063 if (tdvp == tvp) 1064 vrele(tdvp); 1065 else 1066 vput(tdvp); 1067 if (tvp != NULL) 1068 vput(tvp); 1069 1070 /* Release source nodes. */ 1071 vrele(fdvp); 1072 vrele(fvp); 1073 1074 return error; 1075} 1076 1077/* --------------------------------------------------------------------- */ 1078 1079static int 1080tmpfs_mkdir(struct vop_mkdir_args *v) 1081{ 1082 struct vnode *dvp = v->a_dvp; 1083 struct vnode **vpp = v->a_vpp; 1084 struct componentname *cnp = v->a_cnp; 1085 struct vattr *vap = v->a_vap; 1086 1087 MPASS(vap->va_type == VDIR); 1088 1089 return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL); 1090} 1091 1092/* --------------------------------------------------------------------- */ 1093 1094static int 1095tmpfs_rmdir(struct vop_rmdir_args *v) 1096{ 1097 struct vnode *dvp = v->a_dvp; 1098 struct vnode *vp = v->a_vp; 1099 1100 int error; 1101 struct tmpfs_dirent *de; 1102 struct tmpfs_mount *tmp; 1103 struct tmpfs_node *dnode; 1104 struct tmpfs_node *node; 1105 1106 MPASS(VOP_ISLOCKED(dvp, v->a_cnp->cn_thread)); 1107 MPASS(VOP_ISLOCKED(vp, v->a_cnp->cn_thread)); 1108 1109 tmp = VFS_TO_TMPFS(dvp->v_mount); 1110 dnode = VP_TO_TMPFS_DIR(dvp); 1111 node = VP_TO_TMPFS_DIR(vp); 1112 1113 /* Directories with more than two entries ('.' and '..') cannot be 1114 * removed. */ 1115 if (node->tn_size > 0) { 1116 error = ENOTEMPTY; 1117 goto out; 1118 } 1119 1120 if ((dnode->tn_flags & APPEND) 1121 || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { 1122 error = EPERM; 1123 goto out; 1124 } 1125 1126 /* This invariant holds only if we are not trying to remove "..". 1127 * We checked for that above so this is safe now. */ 1128 MPASS(node->tn_dir.tn_parent == dnode); 1129 1130 /* Get the directory entry associated with node (vp). This was 1131 * filled by tmpfs_lookup while looking up the entry. */ 1132 de = tmpfs_dir_search(dnode, node); 1133 MPASS(TMPFS_DIRENT_MATCHES(de, 1134 v->a_cnp->cn_nameptr, 1135 v->a_cnp->cn_namelen)); 1136 1137 /* Check flags to see if we are allowed to remove the directory. */ 1138 if (dnode->tn_flags & APPEND 1139 || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) { 1140 error = EPERM; 1141 goto out; 1142 } 1143 1144 /* Detach the directory entry from the directory (dnode). */ 1145 tmpfs_dir_detach(dvp, de); 1146 1147 node->tn_links--; 1148 node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ 1149 TMPFS_NODE_MODIFIED; 1150 node->tn_dir.tn_parent->tn_links--; 1151 node->tn_dir.tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \ 1152 TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; 1153 1154 cache_purge(dvp); 1155 cache_purge(vp); 1156 1157 /* Free the directory entry we just deleted. Note that the node 1158 * referred by it will not be removed until the vnode is really 1159 * reclaimed. */ 1160 tmpfs_free_dirent(tmp, de, TRUE); 1161 1162 /* Release the deleted vnode (will destroy the node, notify 1163 * interested parties and clean it from the cache). */ 1164 1165 dnode->tn_status |= TMPFS_NODE_CHANGED; 1166 tmpfs_update(dvp); 1167 1168 error = 0; 1169 1170out: 1171 return error; 1172} 1173 1174/* --------------------------------------------------------------------- */ 1175 1176static int 1177tmpfs_symlink(struct vop_symlink_args *v) 1178{ 1179 struct vnode *dvp = v->a_dvp; 1180 struct vnode **vpp = v->a_vpp; 1181 struct componentname *cnp = v->a_cnp; 1182 struct vattr *vap = v->a_vap; 1183 char *target = v->a_target; 1184 1185#ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */ 1186 MPASS(vap->va_type == VLNK); 1187#else 1188 vap->va_type = VLNK; 1189#endif 1190 1191 return tmpfs_alloc_file(dvp, vpp, vap, cnp, target); 1192} 1193 1194/* --------------------------------------------------------------------- */ 1195 1196static int 1197tmpfs_readdir(struct vop_readdir_args *v) 1198{ 1199 struct vnode *vp = v->a_vp; 1200 struct uio *uio = v->a_uio; 1201 int *eofflag = v->a_eofflag; 1202 u_long **cookies = v->a_cookies; 1203 int *ncookies = v->a_ncookies; 1204 1205 int error; 1206 off_t startoff; 1207 off_t cnt = 0; 1208 struct tmpfs_node *node; 1209 1210 /* This operation only makes sense on directory nodes. */ 1211 if (vp->v_type != VDIR) 1212 return ENOTDIR; 1213 1214 node = VP_TO_TMPFS_DIR(vp); 1215 1216 startoff = uio->uio_offset; 1217 1218 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) { 1219 error = tmpfs_dir_getdotdent(node, uio); 1220 if (error != 0) 1221 goto outok; 1222 cnt++; 1223 } 1224 1225 if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { 1226 error = tmpfs_dir_getdotdotdent(node, uio); 1227 if (error != 0) 1228 goto outok; 1229 cnt++; 1230 } 1231 1232 error = tmpfs_dir_getdents(node, uio, &cnt); 1233 1234outok: 1235 MPASS(error >= -1); 1236 1237 if (error == -1) 1238 error = 0; 1239 1240 if (eofflag != NULL) 1241 *eofflag = 1242 (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF); 1243 1244 /* Update NFS-related variables. */ 1245 if (error == 0 && cookies != NULL && ncookies != NULL) { 1246 off_t i; 1247 off_t off = startoff; 1248 struct tmpfs_dirent *de = NULL; 1249 1250 *ncookies = cnt; 1251 *cookies = malloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK); 1252 1253 for (i = 0; i < cnt; i++) { 1254 MPASS(off != TMPFS_DIRCOOKIE_EOF); 1255 if (off == TMPFS_DIRCOOKIE_DOT) { 1256 off = TMPFS_DIRCOOKIE_DOTDOT; 1257 } else { 1258 if (off == TMPFS_DIRCOOKIE_DOTDOT) { 1259 de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); 1260 } else if (de != NULL) { 1261 de = TAILQ_NEXT(de, td_entries); 1262 } else { 1263 de = tmpfs_dir_lookupbycookie(node, 1264 off); 1265 MPASS(de != NULL); 1266 de = TAILQ_NEXT(de, td_entries); 1267 } 1268 if (de == NULL) 1269 off = TMPFS_DIRCOOKIE_EOF; 1270 else 1271 off = tmpfs_dircookie(de); 1272 } 1273 1274 (*cookies)[i] = off; 1275 } 1276 MPASS(uio->uio_offset == off); 1277 } 1278 1279 return error; 1280} 1281 1282/* --------------------------------------------------------------------- */ 1283 1284static int 1285tmpfs_readlink(struct vop_readlink_args *v) 1286{ 1287 struct vnode *vp = v->a_vp; 1288 struct uio *uio = v->a_uio; 1289 1290 int error; 1291 struct tmpfs_node *node; 1292 1293 MPASS(uio->uio_offset == 0); 1294 MPASS(vp->v_type == VLNK); 1295 1296 node = VP_TO_TMPFS_NODE(vp); 1297 1298 error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid), 1299 uio); 1300 node->tn_status |= TMPFS_NODE_ACCESSED; 1301 1302 return error; 1303} 1304 1305/* --------------------------------------------------------------------- */ 1306 1307static int 1308tmpfs_inactive(struct vop_inactive_args *v) 1309{ 1310 struct vnode *vp = v->a_vp; 1311 struct thread *l = v->a_td; 1312 1313 struct tmpfs_node *node; 1314 1315 MPASS(VOP_ISLOCKED(vp, l)); 1316 1317 node = VP_TO_TMPFS_NODE(vp); 1318 1319 if (node->tn_links == 0) 1320 vrecycle(vp, l); 1321 1322 return 0; 1323} 1324 1325/* --------------------------------------------------------------------- */ 1326 1327int 1328tmpfs_reclaim(struct vop_reclaim_args *v) 1329{ 1330 struct vnode *vp = v->a_vp; 1331 1332 struct tmpfs_mount *tmp; 1333 struct tmpfs_node *node; 1334 1335 node = VP_TO_TMPFS_NODE(vp); 1336 tmp = VFS_TO_TMPFS(vp->v_mount); 1337 1338 vnode_destroy_vobject(vp); 1339 cache_purge(vp); 1340 tmpfs_free_vp(vp); 1341 1342 /* If the node referenced by this vnode was deleted by the user, 1343 * we must free its associated data structures (now that the vnode 1344 * is being reclaimed). */ 1345 if (node->tn_links == 0) 1346 tmpfs_free_node(tmp, node); 1347 1348 MPASS(vp->v_data == NULL); 1349 return 0; 1350} 1351 1352/* --------------------------------------------------------------------- */ 1353 1354static int 1355tmpfs_print(struct vop_print_args *v) 1356{ 1357 struct vnode *vp = v->a_vp; 1358 1359 struct tmpfs_node *node; 1360 1361 node = VP_TO_TMPFS_NODE(vp); 1362 1363 printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n", 1364 node, node->tn_flags, node->tn_links); 1365 printf("\tmode 0%o, owner %d, group %d, size %" PRIdMAX 1366 ", status 0x%x\n", 1367 node->tn_mode, node->tn_uid, node->tn_gid, 1368 (uintmax_t)node->tn_size, node->tn_status); 1369 1370 if (vp->v_type == VFIFO) 1371 fifo_printinfo(vp); 1372 1373 printf("\n"); 1374 1375 return 0; 1376} 1377 1378/* --------------------------------------------------------------------- */ 1379 1380static int 1381tmpfs_pathconf(struct vop_pathconf_args *v) 1382{ 1383 int name = v->a_name; 1384 register_t *retval = v->a_retval; 1385 1386 int error; 1387 1388 error = 0; 1389 1390 switch (name) { 1391 case _PC_LINK_MAX: 1392 *retval = LINK_MAX; 1393 break; 1394 1395 case _PC_NAME_MAX: 1396 *retval = NAME_MAX; 1397 break; 1398 1399 case _PC_PATH_MAX: 1400 *retval = PATH_MAX; 1401 break; 1402 1403 case _PC_PIPE_BUF: 1404 *retval = PIPE_BUF; 1405 break; 1406 1407 case _PC_CHOWN_RESTRICTED: 1408 *retval = 1; 1409 break; 1410 1411 case _PC_NO_TRUNC: 1412 *retval = 1; 1413 break; 1414 1415 case _PC_SYNC_IO: 1416 *retval = 1; 1417 break; 1418 1419 case _PC_FILESIZEBITS: 1420 *retval = 0; /* XXX Don't know which value should I return. */ 1421 break; 1422 1423 default: 1424 error = EINVAL; 1425 } 1426 1427 return error; 1428} 1429 1430/* --------------------------------------------------------------------- */ 1431 1432static int 1433tmpfs_advlock(struct vop_advlock_args *v) 1434{ 1435 struct vnode *vp = v->a_vp; 1436 1437 struct tmpfs_node *node; 1438 1439 node = VP_TO_TMPFS_NODE(vp); 1440 1441 return lf_advlock(v, &node->tn_lockf, node->tn_size); 1442} 1443 1444/* --------------------------------------------------------------------- */ 1445 1446static int 1447tmpfs_vptofh(struct vop_vptofh_args *ap) 1448{ 1449 struct tmpfs_fid *tfhp; 1450 struct tmpfs_node *node; 1451 1452 tfhp = (struct tmpfs_fid *)ap->a_fhp; 1453 node = VP_TO_TMPFS_NODE(ap->a_vp); 1454 1455 tfhp->tf_len = sizeof(struct tmpfs_fid); 1456 tfhp->tf_id = node->tn_id; 1457 tfhp->tf_gen = node->tn_gen; 1458 1459 return (0); 1460} 1461 1462/* --------------------------------------------------------------------- */ 1463 1464/* 1465 * vnode operations vector used for files stored in a tmpfs file system. 1466 */ 1467struct vop_vector tmpfs_vnodeop_entries = { 1468 .vop_default = &default_vnodeops, 1469 .vop_lookup = vfs_cache_lookup, 1470 .vop_cachedlookup = tmpfs_lookup, 1471 .vop_create = tmpfs_create, 1472 .vop_mknod = tmpfs_mknod, 1473 .vop_open = tmpfs_open, 1474 .vop_close = tmpfs_close, 1475 .vop_access = tmpfs_access, 1476 .vop_getattr = tmpfs_getattr, 1477 .vop_setattr = tmpfs_setattr, 1478 .vop_read = tmpfs_read, 1479 .vop_write = tmpfs_write, 1480 .vop_fsync = tmpfs_fsync, 1481 .vop_remove = tmpfs_remove, 1482 .vop_link = tmpfs_link, 1483 .vop_rename = tmpfs_rename, 1484 .vop_mkdir = tmpfs_mkdir, 1485 .vop_rmdir = tmpfs_rmdir, 1486 .vop_symlink = tmpfs_symlink, 1487 .vop_readdir = tmpfs_readdir, 1488 .vop_readlink = tmpfs_readlink, 1489 .vop_inactive = tmpfs_inactive, 1490 .vop_reclaim = tmpfs_reclaim, 1491 .vop_print = tmpfs_print, 1492 .vop_pathconf = tmpfs_pathconf, 1493 .vop_advlock = tmpfs_advlock, 1494 .vop_vptofh = tmpfs_vptofh, 1495 .vop_bmap = VOP_EOPNOTSUPP, 1496}; 1497 1498