The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/tmpfs/tmpfs_vnops.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $    */
    2 
    3 /*-
    4  * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
    9  * 2005 program.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   30  * POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 /*
   34  * tmpfs vnode interface.
   35  */
   36 #include <sys/cdefs.h>
   37 __FBSDID("$FreeBSD: releng/10.0/sys/fs/tmpfs/tmpfs_vnops.c 254601 2013-08-21 17:23:24Z kib $");
   38 
   39 #include <sys/param.h>
   40 #include <sys/fcntl.h>
   41 #include <sys/lockf.h>
   42 #include <sys/lock.h>
   43 #include <sys/namei.h>
   44 #include <sys/priv.h>
   45 #include <sys/proc.h>
   46 #include <sys/rwlock.h>
   47 #include <sys/sched.h>
   48 #include <sys/sf_buf.h>
   49 #include <sys/stat.h>
   50 #include <sys/systm.h>
   51 #include <sys/sysctl.h>
   52 #include <sys/unistd.h>
   53 #include <sys/vnode.h>
   54 
   55 #include <vm/vm.h>
   56 #include <vm/vm_param.h>
   57 #include <vm/vm_object.h>
   58 #include <vm/vm_page.h>
   59 #include <vm/vm_pager.h>
   60 
   61 #include <fs/tmpfs/tmpfs_vnops.h>
   62 #include <fs/tmpfs/tmpfs.h>
   63 
   64 SYSCTL_DECL(_vfs_tmpfs);
   65 
   66 static volatile int tmpfs_rename_restarts;
   67 SYSCTL_INT(_vfs_tmpfs, OID_AUTO, rename_restarts, CTLFLAG_RD,
   68     __DEVOLATILE(int *, &tmpfs_rename_restarts), 0,
   69     "Times rename had to restart due to lock contention");
   70 
   71 /* --------------------------------------------------------------------- */
   72 
   73 static int
   74 tmpfs_lookup(struct vop_cachedlookup_args *v)
   75 {
   76         struct vnode *dvp = v->a_dvp;
   77         struct vnode **vpp = v->a_vpp;
   78         struct componentname *cnp = v->a_cnp;
   79 
   80         int error;
   81         struct tmpfs_dirent *de;
   82         struct tmpfs_node *dnode;
   83 
   84         dnode = VP_TO_TMPFS_DIR(dvp);
   85         *vpp = NULLVP;
   86 
   87         /* Check accessibility of requested node as a first step. */
   88         error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_thread);
   89         if (error != 0)
   90                 goto out;
   91 
   92         /* We cannot be requesting the parent directory of the root node. */
   93         MPASS(IMPLIES(dnode->tn_type == VDIR &&
   94             dnode->tn_dir.tn_parent == dnode,
   95             !(cnp->cn_flags & ISDOTDOT)));
   96 
   97         TMPFS_ASSERT_LOCKED(dnode);
   98         if (dnode->tn_dir.tn_parent == NULL) {
   99                 error = ENOENT;
  100                 goto out;
  101         }
  102         if (cnp->cn_flags & ISDOTDOT) {
  103                 int ltype = 0;
  104 
  105                 ltype = VOP_ISLOCKED(dvp);
  106                 vhold(dvp);
  107                 VOP_UNLOCK(dvp, 0);
  108                 /* Allocate a new vnode on the matching entry. */
  109                 error = tmpfs_alloc_vp(dvp->v_mount, dnode->tn_dir.tn_parent,
  110                     cnp->cn_lkflags, vpp);
  111 
  112                 vn_lock(dvp, ltype | LK_RETRY);
  113                 vdrop(dvp);
  114         } else if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
  115                 VREF(dvp);
  116                 *vpp = dvp;
  117                 error = 0;
  118         } else {
  119                 de = tmpfs_dir_lookup(dnode, NULL, cnp);
  120                 if (de != NULL && de->td_node == NULL)
  121                         cnp->cn_flags |= ISWHITEOUT;
  122                 if (de == NULL || de->td_node == NULL) {
  123                         /* The entry was not found in the directory.
  124                          * This is OK if we are creating or renaming an
  125                          * entry and are working on the last component of
  126                          * the path name. */
  127                         if ((cnp->cn_flags & ISLASTCN) &&
  128                             (cnp->cn_nameiop == CREATE || \
  129                             cnp->cn_nameiop == RENAME ||
  130                             (cnp->cn_nameiop == DELETE &&
  131                             cnp->cn_flags & DOWHITEOUT &&
  132                             cnp->cn_flags & ISWHITEOUT))) {
  133                                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
  134                                     cnp->cn_thread);
  135                                 if (error != 0)
  136                                         goto out;
  137 
  138                                 /* Keep the component name in the buffer for
  139                                  * future uses. */
  140                                 cnp->cn_flags |= SAVENAME;
  141 
  142                                 error = EJUSTRETURN;
  143                         } else
  144                                 error = ENOENT;
  145                 } else {
  146                         struct tmpfs_node *tnode;
  147 
  148                         /* The entry was found, so get its associated
  149                          * tmpfs_node. */
  150                         tnode = de->td_node;
  151 
  152                         /* If we are not at the last path component and
  153                          * found a non-directory or non-link entry (which
  154                          * may itself be pointing to a directory), raise
  155                          * an error. */
  156                         if ((tnode->tn_type != VDIR &&
  157                             tnode->tn_type != VLNK) &&
  158                             !(cnp->cn_flags & ISLASTCN)) {
  159                                 error = ENOTDIR;
  160                                 goto out;
  161                         }
  162 
  163                         /* If we are deleting or renaming the entry, keep
  164                          * track of its tmpfs_dirent so that it can be
  165                          * easily deleted later. */
  166                         if ((cnp->cn_flags & ISLASTCN) &&
  167                             (cnp->cn_nameiop == DELETE ||
  168                             cnp->cn_nameiop == RENAME)) {
  169                                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred,
  170                                     cnp->cn_thread);
  171                                 if (error != 0)
  172                                         goto out;
  173 
  174                                 /* Allocate a new vnode on the matching entry. */
  175                                 error = tmpfs_alloc_vp(dvp->v_mount, tnode,
  176                                                 cnp->cn_lkflags, vpp);
  177                                 if (error != 0)
  178                                         goto out;
  179 
  180                                 if ((dnode->tn_mode & S_ISTXT) &&
  181                                   VOP_ACCESS(dvp, VADMIN, cnp->cn_cred, cnp->cn_thread) &&
  182                                   VOP_ACCESS(*vpp, VADMIN, cnp->cn_cred, cnp->cn_thread)) {
  183                                         error = EPERM;
  184                                         vput(*vpp);
  185                                         *vpp = NULL;
  186                                         goto out;
  187                                 }
  188                                 cnp->cn_flags |= SAVENAME;
  189                         } else {
  190                                 error = tmpfs_alloc_vp(dvp->v_mount, tnode,
  191                                                 cnp->cn_lkflags, vpp);
  192                         }
  193                 }
  194         }
  195 
  196         /* Store the result of this lookup in the cache.  Avoid this if the
  197          * request was for creation, as it does not improve timings on
  198          * emprical tests. */
  199         if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE)
  200                 cache_enter(dvp, *vpp, cnp);
  201 
  202 out:
  203         /* If there were no errors, *vpp cannot be null and it must be
  204          * locked. */
  205         MPASS(IFF(error == 0, *vpp != NULLVP && VOP_ISLOCKED(*vpp)));
  206 
  207         return error;
  208 }
  209 
  210 /* --------------------------------------------------------------------- */
  211 
  212 static int
  213 tmpfs_create(struct vop_create_args *v)
  214 {
  215         struct vnode *dvp = v->a_dvp;
  216         struct vnode **vpp = v->a_vpp;
  217         struct componentname *cnp = v->a_cnp;
  218         struct vattr *vap = v->a_vap;
  219 
  220         MPASS(vap->va_type == VREG || vap->va_type == VSOCK);
  221 
  222         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
  223 }
  224 /* --------------------------------------------------------------------- */
  225 
  226 static int
  227 tmpfs_mknod(struct vop_mknod_args *v)
  228 {
  229         struct vnode *dvp = v->a_dvp;
  230         struct vnode **vpp = v->a_vpp;
  231         struct componentname *cnp = v->a_cnp;
  232         struct vattr *vap = v->a_vap;
  233 
  234         if (vap->va_type != VBLK && vap->va_type != VCHR &&
  235             vap->va_type != VFIFO)
  236                 return EINVAL;
  237 
  238         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
  239 }
  240 
  241 /* --------------------------------------------------------------------- */
  242 
  243 static int
  244 tmpfs_open(struct vop_open_args *v)
  245 {
  246         struct vnode *vp = v->a_vp;
  247         int mode = v->a_mode;
  248 
  249         int error;
  250         struct tmpfs_node *node;
  251 
  252         MPASS(VOP_ISLOCKED(vp));
  253 
  254         node = VP_TO_TMPFS_NODE(vp);
  255 
  256         /* The file is still active but all its names have been removed
  257          * (e.g. by a "rmdir $(pwd)").  It cannot be opened any more as
  258          * it is about to die. */
  259         if (node->tn_links < 1)
  260                 return (ENOENT);
  261 
  262         /* If the file is marked append-only, deny write requests. */
  263         if (node->tn_flags & APPEND && (mode & (FWRITE | O_APPEND)) == FWRITE)
  264                 error = EPERM;
  265         else {
  266                 error = 0;
  267                 /* For regular files, the call below is nop. */
  268                 vnode_create_vobject(vp, node->tn_size, v->a_td);
  269         }
  270 
  271         MPASS(VOP_ISLOCKED(vp));
  272         return error;
  273 }
  274 
  275 /* --------------------------------------------------------------------- */
  276 
  277 static int
  278 tmpfs_close(struct vop_close_args *v)
  279 {
  280         struct vnode *vp = v->a_vp;
  281 
  282         /* Update node times. */
  283         tmpfs_update(vp);
  284 
  285         return (0);
  286 }
  287 
  288 /* --------------------------------------------------------------------- */
  289 
  290 int
  291 tmpfs_access(struct vop_access_args *v)
  292 {
  293         struct vnode *vp = v->a_vp;
  294         accmode_t accmode = v->a_accmode;
  295         struct ucred *cred = v->a_cred;
  296 
  297         int error;
  298         struct tmpfs_node *node;
  299 
  300         MPASS(VOP_ISLOCKED(vp));
  301 
  302         node = VP_TO_TMPFS_NODE(vp);
  303 
  304         switch (vp->v_type) {
  305         case VDIR:
  306                 /* FALLTHROUGH */
  307         case VLNK:
  308                 /* FALLTHROUGH */
  309         case VREG:
  310                 if (accmode & VWRITE && vp->v_mount->mnt_flag & MNT_RDONLY) {
  311                         error = EROFS;
  312                         goto out;
  313                 }
  314                 break;
  315 
  316         case VBLK:
  317                 /* FALLTHROUGH */
  318         case VCHR:
  319                 /* FALLTHROUGH */
  320         case VSOCK:
  321                 /* FALLTHROUGH */
  322         case VFIFO:
  323                 break;
  324 
  325         default:
  326                 error = EINVAL;
  327                 goto out;
  328         }
  329 
  330         if (accmode & VWRITE && node->tn_flags & IMMUTABLE) {
  331                 error = EPERM;
  332                 goto out;
  333         }
  334 
  335         error = vaccess(vp->v_type, node->tn_mode, node->tn_uid,
  336             node->tn_gid, accmode, cred, NULL);
  337 
  338 out:
  339         MPASS(VOP_ISLOCKED(vp));
  340 
  341         return error;
  342 }
  343 
  344 /* --------------------------------------------------------------------- */
  345 
  346 int
  347 tmpfs_getattr(struct vop_getattr_args *v)
  348 {
  349         struct vnode *vp = v->a_vp;
  350         struct vattr *vap = v->a_vap;
  351 
  352         struct tmpfs_node *node;
  353 
  354         node = VP_TO_TMPFS_NODE(vp);
  355 
  356         tmpfs_update(vp);
  357 
  358         vap->va_type = vp->v_type;
  359         vap->va_mode = node->tn_mode;
  360         vap->va_nlink = node->tn_links;
  361         vap->va_uid = node->tn_uid;
  362         vap->va_gid = node->tn_gid;
  363         vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
  364         vap->va_fileid = node->tn_id;
  365         vap->va_size = node->tn_size;
  366         vap->va_blocksize = PAGE_SIZE;
  367         vap->va_atime = node->tn_atime;
  368         vap->va_mtime = node->tn_mtime;
  369         vap->va_ctime = node->tn_ctime;
  370         vap->va_birthtime = node->tn_birthtime;
  371         vap->va_gen = node->tn_gen;
  372         vap->va_flags = node->tn_flags;
  373         vap->va_rdev = (vp->v_type == VBLK || vp->v_type == VCHR) ?
  374                 node->tn_rdev : NODEV;
  375         vap->va_bytes = round_page(node->tn_size);
  376         vap->va_filerev = 0;
  377 
  378         return 0;
  379 }
  380 
  381 /* --------------------------------------------------------------------- */
  382 
  383 /* XXX Should this operation be atomic?  I think it should, but code in
  384  * XXX other places (e.g., ufs) doesn't seem to be... */
  385 int
  386 tmpfs_setattr(struct vop_setattr_args *v)
  387 {
  388         struct vnode *vp = v->a_vp;
  389         struct vattr *vap = v->a_vap;
  390         struct ucred *cred = v->a_cred;
  391         struct thread *td = curthread;
  392 
  393         int error;
  394 
  395         MPASS(VOP_ISLOCKED(vp));
  396 
  397         error = 0;
  398 
  399         /* Abort if any unsettable attribute is given. */
  400         if (vap->va_type != VNON ||
  401             vap->va_nlink != VNOVAL ||
  402             vap->va_fsid != VNOVAL ||
  403             vap->va_fileid != VNOVAL ||
  404             vap->va_blocksize != VNOVAL ||
  405             vap->va_gen != VNOVAL ||
  406             vap->va_rdev != VNOVAL ||
  407             vap->va_bytes != VNOVAL)
  408                 error = EINVAL;
  409 
  410         if (error == 0 && (vap->va_flags != VNOVAL))
  411                 error = tmpfs_chflags(vp, vap->va_flags, cred, td);
  412 
  413         if (error == 0 && (vap->va_size != VNOVAL))
  414                 error = tmpfs_chsize(vp, vap->va_size, cred, td);
  415 
  416         if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL))
  417                 error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred, td);
  418 
  419         if (error == 0 && (vap->va_mode != (mode_t)VNOVAL))
  420                 error = tmpfs_chmod(vp, vap->va_mode, cred, td);
  421 
  422         if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
  423             vap->va_atime.tv_nsec != VNOVAL) ||
  424             (vap->va_mtime.tv_sec != VNOVAL &&
  425             vap->va_mtime.tv_nsec != VNOVAL) ||
  426             (vap->va_birthtime.tv_sec != VNOVAL &&
  427             vap->va_birthtime.tv_nsec != VNOVAL)))
  428                 error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
  429                         &vap->va_birthtime, vap->va_vaflags, cred, td);
  430 
  431         /* Update the node times.  We give preference to the error codes
  432          * generated by this function rather than the ones that may arise
  433          * from tmpfs_update. */
  434         tmpfs_update(vp);
  435 
  436         MPASS(VOP_ISLOCKED(vp));
  437 
  438         return error;
  439 }
  440 
  441 static int
  442 tmpfs_read(struct vop_read_args *v)
  443 {
  444         struct vnode *vp;
  445         struct uio *uio;
  446         struct tmpfs_node *node;
  447 
  448         vp = v->a_vp;
  449         if (vp->v_type != VREG)
  450                 return (EISDIR);
  451         uio = v->a_uio;
  452         if (uio->uio_offset < 0)
  453                 return (EINVAL);
  454         node = VP_TO_TMPFS_NODE(vp);
  455         node->tn_status |= TMPFS_NODE_ACCESSED;
  456         return (uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio));
  457 }
  458 
  459 static int
  460 tmpfs_write(struct vop_write_args *v)
  461 {
  462         struct vnode *vp;
  463         struct uio *uio;
  464         struct tmpfs_node *node;
  465         off_t oldsize;
  466         int error, ioflag;
  467         boolean_t extended;
  468 
  469         vp = v->a_vp;
  470         uio = v->a_uio;
  471         ioflag = v->a_ioflag;
  472         error = 0;
  473         node = VP_TO_TMPFS_NODE(vp);
  474         oldsize = node->tn_size;
  475 
  476         if (uio->uio_offset < 0 || vp->v_type != VREG)
  477                 return (EINVAL);
  478         if (uio->uio_resid == 0)
  479                 return (0);
  480         if (ioflag & IO_APPEND)
  481                 uio->uio_offset = node->tn_size;
  482         if (uio->uio_offset + uio->uio_resid >
  483           VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize)
  484                 return (EFBIG);
  485         if (vn_rlimit_fsize(vp, uio, uio->uio_td))
  486                 return (EFBIG);
  487         extended = uio->uio_offset + uio->uio_resid > node->tn_size;
  488         if (extended) {
  489                 error = tmpfs_reg_resize(vp, uio->uio_offset + uio->uio_resid,
  490                     FALSE);
  491                 if (error != 0)
  492                         goto out;
  493         }
  494 
  495         error = uiomove_object(node->tn_reg.tn_aobj, node->tn_size, uio);
  496         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED |
  497             (extended ? TMPFS_NODE_CHANGED : 0);
  498         if (node->tn_mode & (S_ISUID | S_ISGID)) {
  499                 if (priv_check_cred(v->a_cred, PRIV_VFS_RETAINSUGID, 0))
  500                         node->tn_mode &= ~(S_ISUID | S_ISGID);
  501         }
  502         if (error != 0)
  503                 (void)tmpfs_reg_resize(vp, oldsize, TRUE);
  504 
  505 out:
  506         MPASS(IMPLIES(error == 0, uio->uio_resid == 0));
  507         MPASS(IMPLIES(error != 0, oldsize == node->tn_size));
  508 
  509         return (error);
  510 }
  511 
  512 /* --------------------------------------------------------------------- */
  513 
  514 static int
  515 tmpfs_fsync(struct vop_fsync_args *v)
  516 {
  517         struct vnode *vp = v->a_vp;
  518 
  519         MPASS(VOP_ISLOCKED(vp));
  520 
  521         tmpfs_update(vp);
  522 
  523         return 0;
  524 }
  525 
  526 /* --------------------------------------------------------------------- */
  527 
  528 static int
  529 tmpfs_remove(struct vop_remove_args *v)
  530 {
  531         struct vnode *dvp = v->a_dvp;
  532         struct vnode *vp = v->a_vp;
  533 
  534         int error;
  535         struct tmpfs_dirent *de;
  536         struct tmpfs_mount *tmp;
  537         struct tmpfs_node *dnode;
  538         struct tmpfs_node *node;
  539 
  540         MPASS(VOP_ISLOCKED(dvp));
  541         MPASS(VOP_ISLOCKED(vp));
  542 
  543         if (vp->v_type == VDIR) {
  544                 error = EISDIR;
  545                 goto out;
  546         }
  547 
  548         dnode = VP_TO_TMPFS_DIR(dvp);
  549         node = VP_TO_TMPFS_NODE(vp);
  550         tmp = VFS_TO_TMPFS(vp->v_mount);
  551         de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
  552         MPASS(de != NULL);
  553 
  554         /* Files marked as immutable or append-only cannot be deleted. */
  555         if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
  556             (dnode->tn_flags & APPEND)) {
  557                 error = EPERM;
  558                 goto out;
  559         }
  560 
  561         /* Remove the entry from the directory; as it is a file, we do not
  562          * have to change the number of hard links of the directory. */
  563         tmpfs_dir_detach(dvp, de);
  564         if (v->a_cnp->cn_flags & DOWHITEOUT)
  565                 tmpfs_dir_whiteout_add(dvp, v->a_cnp);
  566 
  567         /* Free the directory entry we just deleted.  Note that the node
  568          * referred by it will not be removed until the vnode is really
  569          * reclaimed. */
  570         tmpfs_free_dirent(tmp, de);
  571 
  572         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED;
  573         error = 0;
  574 
  575 out:
  576 
  577         return error;
  578 }
  579 
  580 /* --------------------------------------------------------------------- */
  581 
  582 static int
  583 tmpfs_link(struct vop_link_args *v)
  584 {
  585         struct vnode *dvp = v->a_tdvp;
  586         struct vnode *vp = v->a_vp;
  587         struct componentname *cnp = v->a_cnp;
  588 
  589         int error;
  590         struct tmpfs_dirent *de;
  591         struct tmpfs_node *node;
  592 
  593         MPASS(VOP_ISLOCKED(dvp));
  594         MPASS(cnp->cn_flags & HASBUF);
  595         MPASS(dvp != vp); /* XXX When can this be false? */
  596 
  597         node = VP_TO_TMPFS_NODE(vp);
  598 
  599         /* XXX: Why aren't the following two tests done by the caller? */
  600 
  601         /* Hard links of directories are forbidden. */
  602         if (vp->v_type == VDIR) {
  603                 error = EPERM;
  604                 goto out;
  605         }
  606 
  607         /* Cannot create cross-device links. */
  608         if (dvp->v_mount != vp->v_mount) {
  609                 error = EXDEV;
  610                 goto out;
  611         }
  612 
  613         /* Ensure that we do not overflow the maximum number of links imposed
  614          * by the system. */
  615         MPASS(node->tn_links <= LINK_MAX);
  616         if (node->tn_links == LINK_MAX) {
  617                 error = EMLINK;
  618                 goto out;
  619         }
  620 
  621         /* We cannot create links of files marked immutable or append-only. */
  622         if (node->tn_flags & (IMMUTABLE | APPEND)) {
  623                 error = EPERM;
  624                 goto out;
  625         }
  626 
  627         /* Allocate a new directory entry to represent the node. */
  628         error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
  629             cnp->cn_nameptr, cnp->cn_namelen, &de);
  630         if (error != 0)
  631                 goto out;
  632 
  633         /* Insert the new directory entry into the appropriate directory. */
  634         if (cnp->cn_flags & ISWHITEOUT)
  635                 tmpfs_dir_whiteout_remove(dvp, cnp);
  636         tmpfs_dir_attach(dvp, de);
  637 
  638         /* vp link count has changed, so update node times. */
  639         node->tn_status |= TMPFS_NODE_CHANGED;
  640         tmpfs_update(vp);
  641 
  642         error = 0;
  643 
  644 out:
  645         return error;
  646 }
  647 
  648 /* --------------------------------------------------------------------- */
  649 
  650 /*
  651  * We acquire all but fdvp locks using non-blocking acquisitions.  If we
  652  * fail to acquire any lock in the path we will drop all held locks,
  653  * acquire the new lock in a blocking fashion, and then release it and
  654  * restart the rename.  This acquire/release step ensures that we do not
  655  * spin on a lock waiting for release.  On error release all vnode locks
  656  * and decrement references the way tmpfs_rename() would do.
  657  */
  658 static int
  659 tmpfs_rename_relock(struct vnode *fdvp, struct vnode **fvpp,
  660     struct vnode *tdvp, struct vnode **tvpp,
  661     struct componentname *fcnp, struct componentname *tcnp)
  662 {
  663         struct vnode *nvp;
  664         struct mount *mp;
  665         struct tmpfs_dirent *de;
  666         int error, restarts = 0;
  667 
  668         VOP_UNLOCK(tdvp, 0);
  669         if (*tvpp != NULL && *tvpp != tdvp)
  670                 VOP_UNLOCK(*tvpp, 0);
  671         mp = fdvp->v_mount;
  672 
  673 relock:
  674         restarts += 1;
  675         error = vn_lock(fdvp, LK_EXCLUSIVE);
  676         if (error)
  677                 goto releout;
  678         if (vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
  679                 VOP_UNLOCK(fdvp, 0);
  680                 error = vn_lock(tdvp, LK_EXCLUSIVE);
  681                 if (error)
  682                         goto releout;
  683                 VOP_UNLOCK(tdvp, 0);
  684                 goto relock;
  685         }
  686         /*
  687          * Re-resolve fvp to be certain it still exists and fetch the
  688          * correct vnode.
  689          */
  690         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(fdvp), NULL, fcnp);
  691         if (de == NULL) {
  692                 VOP_UNLOCK(fdvp, 0);
  693                 VOP_UNLOCK(tdvp, 0);
  694                 if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
  695                     (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
  696                         error = EINVAL;
  697                 else
  698                         error = ENOENT;
  699                 goto releout;
  700         }
  701         error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE | LK_NOWAIT, &nvp);
  702         if (error != 0) {
  703                 VOP_UNLOCK(fdvp, 0);
  704                 VOP_UNLOCK(tdvp, 0);
  705                 if (error != EBUSY)
  706                         goto releout;
  707                 error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE, &nvp);
  708                 if (error != 0)
  709                         goto releout;
  710                 VOP_UNLOCK(nvp, 0);
  711                 /*
  712                  * Concurrent rename race.
  713                  */
  714                 if (nvp == tdvp) {
  715                         vrele(nvp);
  716                         error = EINVAL;
  717                         goto releout;
  718                 }
  719                 vrele(*fvpp);
  720                 *fvpp = nvp;
  721                 goto relock;
  722         }
  723         vrele(*fvpp);
  724         *fvpp = nvp;
  725         VOP_UNLOCK(*fvpp, 0);
  726         /*
  727          * Re-resolve tvp and acquire the vnode lock if present.
  728          */
  729         de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(tdvp), NULL, tcnp);
  730         /*
  731          * If tvp disappeared we just carry on.
  732          */
  733         if (de == NULL && *tvpp != NULL) {
  734                 vrele(*tvpp);
  735                 *tvpp = NULL;
  736         }
  737         /*
  738          * Get the tvp ino if the lookup succeeded.  We may have to restart
  739          * if the non-blocking acquire fails.
  740          */
  741         if (de != NULL) {
  742                 nvp = NULL;
  743                 error = tmpfs_alloc_vp(mp, de->td_node,
  744                     LK_EXCLUSIVE | LK_NOWAIT, &nvp);
  745                 if (*tvpp != NULL)
  746                         vrele(*tvpp);
  747                 *tvpp = nvp;
  748                 if (error != 0) {
  749                         VOP_UNLOCK(fdvp, 0);
  750                         VOP_UNLOCK(tdvp, 0);
  751                         if (error != EBUSY)
  752                                 goto releout;
  753                         error = tmpfs_alloc_vp(mp, de->td_node, LK_EXCLUSIVE,
  754                             &nvp);
  755                         if (error != 0)
  756                                 goto releout;
  757                         VOP_UNLOCK(nvp, 0);
  758                         /*
  759                          * fdvp contains fvp, thus tvp (=fdvp) is not empty.
  760                          */
  761                         if (nvp == fdvp) {
  762                                 error = ENOTEMPTY;
  763                                 goto releout;
  764                         }
  765                         goto relock;
  766                 }
  767         }
  768         tmpfs_rename_restarts += restarts;
  769 
  770         return (0);
  771 
  772 releout:
  773         vrele(fdvp);
  774         vrele(*fvpp);
  775         vrele(tdvp);
  776         if (*tvpp != NULL)
  777                 vrele(*tvpp);
  778         tmpfs_rename_restarts += restarts;
  779 
  780         return (error);
  781 }
  782 
  783 static int
  784 tmpfs_rename(struct vop_rename_args *v)
  785 {
  786         struct vnode *fdvp = v->a_fdvp;
  787         struct vnode *fvp = v->a_fvp;
  788         struct componentname *fcnp = v->a_fcnp;
  789         struct vnode *tdvp = v->a_tdvp;
  790         struct vnode *tvp = v->a_tvp;
  791         struct componentname *tcnp = v->a_tcnp;
  792         struct mount *mp = NULL;
  793 
  794         char *newname;
  795         int error;
  796         struct tmpfs_dirent *de;
  797         struct tmpfs_mount *tmp;
  798         struct tmpfs_node *fdnode;
  799         struct tmpfs_node *fnode;
  800         struct tmpfs_node *tnode;
  801         struct tmpfs_node *tdnode;
  802 
  803         MPASS(VOP_ISLOCKED(tdvp));
  804         MPASS(IMPLIES(tvp != NULL, VOP_ISLOCKED(tvp)));
  805         MPASS(fcnp->cn_flags & HASBUF);
  806         MPASS(tcnp->cn_flags & HASBUF);
  807 
  808         /* Disallow cross-device renames.
  809          * XXX Why isn't this done by the caller? */
  810         if (fvp->v_mount != tdvp->v_mount ||
  811             (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
  812                 error = EXDEV;
  813                 goto out;
  814         }
  815 
  816         /* If source and target are the same file, there is nothing to do. */
  817         if (fvp == tvp) {
  818                 error = 0;
  819                 goto out;
  820         }
  821 
  822         /* If we need to move the directory between entries, lock the
  823          * source so that we can safely operate on it. */
  824         if (fdvp != tdvp && fdvp != tvp) {
  825                 if (vn_lock(fdvp, LK_EXCLUSIVE | LK_NOWAIT) != 0) {
  826                         mp = tdvp->v_mount;
  827                         error = vfs_busy(mp, 0);
  828                         if (error != 0) {
  829                                 mp = NULL;
  830                                 goto out;
  831                         }
  832                         error = tmpfs_rename_relock(fdvp, &fvp, tdvp, &tvp,
  833                             fcnp, tcnp);
  834                         if (error != 0) {
  835                                 vfs_unbusy(mp);
  836                                 return (error);
  837                         }
  838                         ASSERT_VOP_ELOCKED(fdvp,
  839                             "tmpfs_rename: fdvp not locked");
  840                         ASSERT_VOP_ELOCKED(tdvp,
  841                             "tmpfs_rename: tdvp not locked");
  842                         if (tvp != NULL)
  843                                 ASSERT_VOP_ELOCKED(tvp,
  844                                     "tmpfs_rename: tvp not locked");
  845                         if (fvp == tvp) {
  846                                 error = 0;
  847                                 goto out_locked;
  848                         }
  849                 }
  850         }
  851 
  852         tmp = VFS_TO_TMPFS(tdvp->v_mount);
  853         tdnode = VP_TO_TMPFS_DIR(tdvp);
  854         tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp);
  855         fdnode = VP_TO_TMPFS_DIR(fdvp);
  856         fnode = VP_TO_TMPFS_NODE(fvp);
  857         de = tmpfs_dir_lookup(fdnode, fnode, fcnp);
  858 
  859         /* Entry can disappear before we lock fdvp,
  860          * also avoid manipulating '.' and '..' entries. */
  861         if (de == NULL) {
  862                 if ((fcnp->cn_flags & ISDOTDOT) != 0 ||
  863                     (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.'))
  864                         error = EINVAL;
  865                 else
  866                         error = ENOENT;
  867                 goto out_locked;
  868         }
  869         MPASS(de->td_node == fnode);
  870 
  871         /* If re-naming a directory to another preexisting directory
  872          * ensure that the target directory is empty so that its
  873          * removal causes no side effects.
  874          * Kern_rename gurantees the destination to be a directory
  875          * if the source is one. */
  876         if (tvp != NULL) {
  877                 MPASS(tnode != NULL);
  878 
  879                 if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
  880                     (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
  881                         error = EPERM;
  882                         goto out_locked;
  883                 }
  884 
  885                 if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
  886                         if (tnode->tn_size > 0) {
  887                                 error = ENOTEMPTY;
  888                                 goto out_locked;
  889                         }
  890                 } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
  891                         error = ENOTDIR;
  892                         goto out_locked;
  893                 } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
  894                         error = EISDIR;
  895                         goto out_locked;
  896                 } else {
  897                         MPASS(fnode->tn_type != VDIR &&
  898                                 tnode->tn_type != VDIR);
  899                 }
  900         }
  901 
  902         if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
  903             || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
  904                 error = EPERM;
  905                 goto out_locked;
  906         }
  907 
  908         /* Ensure that we have enough memory to hold the new name, if it
  909          * has to be changed. */
  910         if (fcnp->cn_namelen != tcnp->cn_namelen ||
  911             bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
  912                 newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
  913         } else
  914                 newname = NULL;
  915 
  916         /* If the node is being moved to another directory, we have to do
  917          * the move. */
  918         if (fdnode != tdnode) {
  919                 /* In case we are moving a directory, we have to adjust its
  920                  * parent to point to the new parent. */
  921                 if (de->td_node->tn_type == VDIR) {
  922                         struct tmpfs_node *n;
  923 
  924                         /* Ensure the target directory is not a child of the
  925                          * directory being moved.  Otherwise, we'd end up
  926                          * with stale nodes. */
  927                         n = tdnode;
  928                         /* TMPFS_LOCK garanties that no nodes are freed while
  929                          * traversing the list. Nodes can only be marked as
  930                          * removed: tn_parent == NULL. */
  931                         TMPFS_LOCK(tmp);
  932                         TMPFS_NODE_LOCK(n);
  933                         while (n != n->tn_dir.tn_parent) {
  934                                 struct tmpfs_node *parent;
  935 
  936                                 if (n == fnode) {
  937                                         TMPFS_NODE_UNLOCK(n);
  938                                         TMPFS_UNLOCK(tmp);
  939                                         error = EINVAL;
  940                                         if (newname != NULL)
  941                                                     free(newname, M_TMPFSNAME);
  942                                         goto out_locked;
  943                                 }
  944                                 parent = n->tn_dir.tn_parent;
  945                                 TMPFS_NODE_UNLOCK(n);
  946                                 if (parent == NULL) {
  947                                         n = NULL;
  948                                         break;
  949                                 }
  950                                 TMPFS_NODE_LOCK(parent);
  951                                 if (parent->tn_dir.tn_parent == NULL) {
  952                                         TMPFS_NODE_UNLOCK(parent);
  953                                         n = NULL;
  954                                         break;
  955                                 }
  956                                 n = parent;
  957                         }
  958                         TMPFS_UNLOCK(tmp);
  959                         if (n == NULL) {
  960                                 error = EINVAL;
  961                                 if (newname != NULL)
  962                                             free(newname, M_TMPFSNAME);
  963                                 goto out_locked;
  964                         }
  965                         TMPFS_NODE_UNLOCK(n);
  966 
  967                         /* Adjust the parent pointer. */
  968                         TMPFS_VALIDATE_DIR(fnode);
  969                         TMPFS_NODE_LOCK(de->td_node);
  970                         de->td_node->tn_dir.tn_parent = tdnode;
  971                         TMPFS_NODE_UNLOCK(de->td_node);
  972 
  973                         /* As a result of changing the target of the '..'
  974                          * entry, the link count of the source and target
  975                          * directories has to be adjusted. */
  976                         TMPFS_NODE_LOCK(tdnode);
  977                         TMPFS_ASSERT_LOCKED(tdnode);
  978                         tdnode->tn_links++;
  979                         TMPFS_NODE_UNLOCK(tdnode);
  980 
  981                         TMPFS_NODE_LOCK(fdnode);
  982                         TMPFS_ASSERT_LOCKED(fdnode);
  983                         fdnode->tn_links--;
  984                         TMPFS_NODE_UNLOCK(fdnode);
  985                 }
  986         }
  987 
  988         /* Do the move: just remove the entry from the source directory
  989          * and insert it into the target one. */
  990         tmpfs_dir_detach(fdvp, de);
  991 
  992         if (fcnp->cn_flags & DOWHITEOUT)
  993                 tmpfs_dir_whiteout_add(fdvp, fcnp);
  994         if (tcnp->cn_flags & ISWHITEOUT)
  995                 tmpfs_dir_whiteout_remove(tdvp, tcnp);
  996 
  997         /* If the name has changed, we need to make it effective by changing
  998          * it in the directory entry. */
  999         if (newname != NULL) {
 1000                 MPASS(tcnp->cn_namelen <= MAXNAMLEN);
 1001 
 1002                 free(de->ud.td_name, M_TMPFSNAME);
 1003                 de->ud.td_name = newname;
 1004                 tmpfs_dirent_init(de, tcnp->cn_nameptr, tcnp->cn_namelen);
 1005 
 1006                 fnode->tn_status |= TMPFS_NODE_CHANGED;
 1007                 tdnode->tn_status |= TMPFS_NODE_MODIFIED;
 1008         }
 1009 
 1010         /* If we are overwriting an entry, we have to remove the old one
 1011          * from the target directory. */
 1012         if (tvp != NULL) {
 1013                 struct tmpfs_dirent *tde;
 1014 
 1015                 /* Remove the old entry from the target directory. */
 1016                 tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
 1017                 tmpfs_dir_detach(tdvp, tde);
 1018 
 1019                 /* Free the directory entry we just deleted.  Note that the
 1020                  * node referred by it will not be removed until the vnode is
 1021                  * really reclaimed. */
 1022                 tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde);
 1023         }
 1024 
 1025         tmpfs_dir_attach(tdvp, de);
 1026 
 1027         cache_purge(fvp);
 1028         if (tvp != NULL)
 1029                 cache_purge(tvp);
 1030         cache_purge_negative(tdvp);
 1031 
 1032         error = 0;
 1033 
 1034 out_locked:
 1035         if (fdvp != tdvp && fdvp != tvp)
 1036                 VOP_UNLOCK(fdvp, 0);
 1037 
 1038 out:
 1039         /* Release target nodes. */
 1040         /* XXX: I don't understand when tdvp can be the same as tvp, but
 1041          * other code takes care of this... */
 1042         if (tdvp == tvp)
 1043                 vrele(tdvp);
 1044         else
 1045                 vput(tdvp);
 1046         if (tvp != NULL)
 1047                 vput(tvp);
 1048 
 1049         /* Release source nodes. */
 1050         vrele(fdvp);
 1051         vrele(fvp);
 1052 
 1053         if (mp != NULL)
 1054                 vfs_unbusy(mp);
 1055 
 1056         return error;
 1057 }
 1058 
 1059 /* --------------------------------------------------------------------- */
 1060 
 1061 static int
 1062 tmpfs_mkdir(struct vop_mkdir_args *v)
 1063 {
 1064         struct vnode *dvp = v->a_dvp;
 1065         struct vnode **vpp = v->a_vpp;
 1066         struct componentname *cnp = v->a_cnp;
 1067         struct vattr *vap = v->a_vap;
 1068 
 1069         MPASS(vap->va_type == VDIR);
 1070 
 1071         return tmpfs_alloc_file(dvp, vpp, vap, cnp, NULL);
 1072 }
 1073 
 1074 /* --------------------------------------------------------------------- */
 1075 
 1076 static int
 1077 tmpfs_rmdir(struct vop_rmdir_args *v)
 1078 {
 1079         struct vnode *dvp = v->a_dvp;
 1080         struct vnode *vp = v->a_vp;
 1081 
 1082         int error;
 1083         struct tmpfs_dirent *de;
 1084         struct tmpfs_mount *tmp;
 1085         struct tmpfs_node *dnode;
 1086         struct tmpfs_node *node;
 1087 
 1088         MPASS(VOP_ISLOCKED(dvp));
 1089         MPASS(VOP_ISLOCKED(vp));
 1090 
 1091         tmp = VFS_TO_TMPFS(dvp->v_mount);
 1092         dnode = VP_TO_TMPFS_DIR(dvp);
 1093         node = VP_TO_TMPFS_DIR(vp);
 1094 
 1095         /* Directories with more than two entries ('.' and '..') cannot be
 1096          * removed. */
 1097          if (node->tn_size > 0) {
 1098                  error = ENOTEMPTY;
 1099                  goto out;
 1100          }
 1101 
 1102         if ((dnode->tn_flags & APPEND)
 1103             || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
 1104                 error = EPERM;
 1105                 goto out;
 1106         }
 1107 
 1108         /* This invariant holds only if we are not trying to remove "..".
 1109           * We checked for that above so this is safe now. */
 1110         MPASS(node->tn_dir.tn_parent == dnode);
 1111 
 1112         /* Get the directory entry associated with node (vp).  This was
 1113          * filled by tmpfs_lookup while looking up the entry. */
 1114         de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
 1115         MPASS(TMPFS_DIRENT_MATCHES(de,
 1116             v->a_cnp->cn_nameptr,
 1117             v->a_cnp->cn_namelen));
 1118 
 1119         /* Check flags to see if we are allowed to remove the directory. */
 1120         if (dnode->tn_flags & APPEND
 1121                 || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
 1122                 error = EPERM;
 1123                 goto out;
 1124         }
 1125 
 1126 
 1127         /* Detach the directory entry from the directory (dnode). */
 1128         tmpfs_dir_detach(dvp, de);
 1129         if (v->a_cnp->cn_flags & DOWHITEOUT)
 1130                 tmpfs_dir_whiteout_add(dvp, v->a_cnp);
 1131 
 1132         /* No vnode should be allocated for this entry from this point */
 1133         TMPFS_NODE_LOCK(node);
 1134         TMPFS_ASSERT_ELOCKED(node);
 1135         node->tn_links--;
 1136         node->tn_dir.tn_parent = NULL;
 1137         node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
 1138             TMPFS_NODE_MODIFIED;
 1139 
 1140         TMPFS_NODE_UNLOCK(node);
 1141 
 1142         TMPFS_NODE_LOCK(dnode);
 1143         TMPFS_ASSERT_ELOCKED(dnode);
 1144         dnode->tn_links--;
 1145         dnode->tn_status |= TMPFS_NODE_ACCESSED | \
 1146             TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
 1147         TMPFS_NODE_UNLOCK(dnode);
 1148 
 1149         cache_purge(dvp);
 1150         cache_purge(vp);
 1151 
 1152         /* Free the directory entry we just deleted.  Note that the node
 1153          * referred by it will not be removed until the vnode is really
 1154          * reclaimed. */
 1155         tmpfs_free_dirent(tmp, de);
 1156 
 1157         /* Release the deleted vnode (will destroy the node, notify
 1158          * interested parties and clean it from the cache). */
 1159 
 1160         dnode->tn_status |= TMPFS_NODE_CHANGED;
 1161         tmpfs_update(dvp);
 1162 
 1163         error = 0;
 1164 
 1165 out:
 1166         return error;
 1167 }
 1168 
 1169 /* --------------------------------------------------------------------- */
 1170 
 1171 static int
 1172 tmpfs_symlink(struct vop_symlink_args *v)
 1173 {
 1174         struct vnode *dvp = v->a_dvp;
 1175         struct vnode **vpp = v->a_vpp;
 1176         struct componentname *cnp = v->a_cnp;
 1177         struct vattr *vap = v->a_vap;
 1178         char *target = v->a_target;
 1179 
 1180 #ifdef notyet /* XXX FreeBSD BUG: kern_symlink is not setting VLNK */
 1181         MPASS(vap->va_type == VLNK);
 1182 #else
 1183         vap->va_type = VLNK;
 1184 #endif
 1185 
 1186         return tmpfs_alloc_file(dvp, vpp, vap, cnp, target);
 1187 }
 1188 
 1189 /* --------------------------------------------------------------------- */
 1190 
 1191 static int
 1192 tmpfs_readdir(struct vop_readdir_args *v)
 1193 {
 1194         struct vnode *vp = v->a_vp;
 1195         struct uio *uio = v->a_uio;
 1196         int *eofflag = v->a_eofflag;
 1197         u_long **cookies = v->a_cookies;
 1198         int *ncookies = v->a_ncookies;
 1199 
 1200         int error;
 1201         ssize_t startresid;
 1202         int cnt = 0;
 1203         struct tmpfs_node *node;
 1204 
 1205         /* This operation only makes sense on directory nodes. */
 1206         if (vp->v_type != VDIR)
 1207                 return ENOTDIR;
 1208 
 1209         node = VP_TO_TMPFS_DIR(vp);
 1210 
 1211         startresid = uio->uio_resid;
 1212 
 1213         if (cookies != NULL && ncookies != NULL) {
 1214                 cnt = howmany(node->tn_size, sizeof(struct tmpfs_dirent)) + 2;
 1215                 *cookies = malloc(cnt * sizeof(**cookies), M_TEMP, M_WAITOK);
 1216                 *ncookies = 0;
 1217         }
 1218 
 1219         if (cnt == 0)
 1220                 error = tmpfs_dir_getdents(node, uio, 0, NULL, NULL);
 1221         else
 1222                 error = tmpfs_dir_getdents(node, uio, cnt, *cookies, ncookies);
 1223 
 1224         if (error == EJUSTRETURN)
 1225                 error = (uio->uio_resid != startresid) ? 0 : EINVAL;
 1226 
 1227         if (error != 0 && cnt != 0)
 1228                 free(*cookies, M_TEMP);
 1229 
 1230         if (eofflag != NULL)
 1231                 *eofflag =
 1232                     (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
 1233 
 1234         return error;
 1235 }
 1236 
 1237 /* --------------------------------------------------------------------- */
 1238 
 1239 static int
 1240 tmpfs_readlink(struct vop_readlink_args *v)
 1241 {
 1242         struct vnode *vp = v->a_vp;
 1243         struct uio *uio = v->a_uio;
 1244 
 1245         int error;
 1246         struct tmpfs_node *node;
 1247 
 1248         MPASS(uio->uio_offset == 0);
 1249         MPASS(vp->v_type == VLNK);
 1250 
 1251         node = VP_TO_TMPFS_NODE(vp);
 1252 
 1253         error = uiomove(node->tn_link, MIN(node->tn_size, uio->uio_resid),
 1254             uio);
 1255         node->tn_status |= TMPFS_NODE_ACCESSED;
 1256 
 1257         return error;
 1258 }
 1259 
 1260 /* --------------------------------------------------------------------- */
 1261 
 1262 static int
 1263 tmpfs_inactive(struct vop_inactive_args *v)
 1264 {
 1265         struct vnode *vp = v->a_vp;
 1266 
 1267         struct tmpfs_node *node;
 1268 
 1269         node = VP_TO_TMPFS_NODE(vp);
 1270 
 1271         if (node->tn_links == 0)
 1272                 vrecycle(vp);
 1273 
 1274         return 0;
 1275 }
 1276 
 1277 /* --------------------------------------------------------------------- */
 1278 
 1279 int
 1280 tmpfs_reclaim(struct vop_reclaim_args *v)
 1281 {
 1282         struct vnode *vp = v->a_vp;
 1283 
 1284         struct tmpfs_mount *tmp;
 1285         struct tmpfs_node *node;
 1286 
 1287         node = VP_TO_TMPFS_NODE(vp);
 1288         tmp = VFS_TO_TMPFS(vp->v_mount);
 1289 
 1290         if (vp->v_type == VREG)
 1291                 tmpfs_destroy_vobject(vp, node->tn_reg.tn_aobj);
 1292         else
 1293                 vnode_destroy_vobject(vp);
 1294         vp->v_object = NULL;
 1295         cache_purge(vp);
 1296 
 1297         TMPFS_NODE_LOCK(node);
 1298         TMPFS_ASSERT_ELOCKED(node);
 1299         tmpfs_free_vp(vp);
 1300 
 1301         /* If the node referenced by this vnode was deleted by the user,
 1302          * we must free its associated data structures (now that the vnode
 1303          * is being reclaimed). */
 1304         if (node->tn_links == 0 &&
 1305             (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) {
 1306                 node->tn_vpstate = TMPFS_VNODE_DOOMED;
 1307                 TMPFS_NODE_UNLOCK(node);
 1308                 tmpfs_free_node(tmp, node);
 1309         } else
 1310                 TMPFS_NODE_UNLOCK(node);
 1311 
 1312         MPASS(vp->v_data == NULL);
 1313         return 0;
 1314 }
 1315 
 1316 /* --------------------------------------------------------------------- */
 1317 
 1318 static int
 1319 tmpfs_print(struct vop_print_args *v)
 1320 {
 1321         struct vnode *vp = v->a_vp;
 1322 
 1323         struct tmpfs_node *node;
 1324 
 1325         node = VP_TO_TMPFS_NODE(vp);
 1326 
 1327         printf("tag VT_TMPFS, tmpfs_node %p, flags 0x%lx, links %d\n",
 1328             node, node->tn_flags, node->tn_links);
 1329         printf("\tmode 0%o, owner %d, group %d, size %jd, status 0x%x\n",
 1330             node->tn_mode, node->tn_uid, node->tn_gid,
 1331             (intmax_t)node->tn_size, node->tn_status);
 1332 
 1333         if (vp->v_type == VFIFO)
 1334                 fifo_printinfo(vp);
 1335 
 1336         printf("\n");
 1337 
 1338         return 0;
 1339 }
 1340 
 1341 /* --------------------------------------------------------------------- */
 1342 
 1343 static int
 1344 tmpfs_pathconf(struct vop_pathconf_args *v)
 1345 {
 1346         int name = v->a_name;
 1347         register_t *retval = v->a_retval;
 1348 
 1349         int error;
 1350 
 1351         error = 0;
 1352 
 1353         switch (name) {
 1354         case _PC_LINK_MAX:
 1355                 *retval = LINK_MAX;
 1356                 break;
 1357 
 1358         case _PC_NAME_MAX:
 1359                 *retval = NAME_MAX;
 1360                 break;
 1361 
 1362         case _PC_PATH_MAX:
 1363                 *retval = PATH_MAX;
 1364                 break;
 1365 
 1366         case _PC_PIPE_BUF:
 1367                 *retval = PIPE_BUF;
 1368                 break;
 1369 
 1370         case _PC_CHOWN_RESTRICTED:
 1371                 *retval = 1;
 1372                 break;
 1373 
 1374         case _PC_NO_TRUNC:
 1375                 *retval = 1;
 1376                 break;
 1377 
 1378         case _PC_SYNC_IO:
 1379                 *retval = 1;
 1380                 break;
 1381 
 1382         case _PC_FILESIZEBITS:
 1383                 *retval = 0; /* XXX Don't know which value should I return. */
 1384                 break;
 1385 
 1386         default:
 1387                 error = EINVAL;
 1388         }
 1389 
 1390         return error;
 1391 }
 1392 
 1393 static int
 1394 tmpfs_vptofh(struct vop_vptofh_args *ap)
 1395 {
 1396         struct tmpfs_fid *tfhp;
 1397         struct tmpfs_node *node;
 1398 
 1399         tfhp = (struct tmpfs_fid *)ap->a_fhp;
 1400         node = VP_TO_TMPFS_NODE(ap->a_vp);
 1401 
 1402         tfhp->tf_len = sizeof(struct tmpfs_fid);
 1403         tfhp->tf_id = node->tn_id;
 1404         tfhp->tf_gen = node->tn_gen;
 1405 
 1406         return (0);
 1407 }
 1408 
 1409 static int
 1410 tmpfs_whiteout(struct vop_whiteout_args *ap)
 1411 {
 1412         struct vnode *dvp = ap->a_dvp;
 1413         struct componentname *cnp = ap->a_cnp;
 1414         struct tmpfs_dirent *de;
 1415 
 1416         switch (ap->a_flags) {
 1417         case LOOKUP:
 1418                 return (0);
 1419         case CREATE:
 1420                 de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
 1421                 if (de != NULL)
 1422                         return (de->td_node == NULL ? 0 : EEXIST);
 1423                 return (tmpfs_dir_whiteout_add(dvp, cnp));
 1424         case DELETE:
 1425                 tmpfs_dir_whiteout_remove(dvp, cnp);
 1426                 return (0);
 1427         default:
 1428                 panic("tmpfs_whiteout: unknown op");
 1429         }
 1430 }
 1431 
 1432 /* --------------------------------------------------------------------- */
 1433 
 1434 /*
 1435  * vnode operations vector used for files stored in a tmpfs file system.
 1436  */
 1437 struct vop_vector tmpfs_vnodeop_entries = {
 1438         .vop_default =                  &default_vnodeops,
 1439         .vop_lookup =                   vfs_cache_lookup,
 1440         .vop_cachedlookup =             tmpfs_lookup,
 1441         .vop_create =                   tmpfs_create,
 1442         .vop_mknod =                    tmpfs_mknod,
 1443         .vop_open =                     tmpfs_open,
 1444         .vop_close =                    tmpfs_close,
 1445         .vop_access =                   tmpfs_access,
 1446         .vop_getattr =                  tmpfs_getattr,
 1447         .vop_setattr =                  tmpfs_setattr,
 1448         .vop_read =                     tmpfs_read,
 1449         .vop_write =                    tmpfs_write,
 1450         .vop_fsync =                    tmpfs_fsync,
 1451         .vop_remove =                   tmpfs_remove,
 1452         .vop_link =                     tmpfs_link,
 1453         .vop_rename =                   tmpfs_rename,
 1454         .vop_mkdir =                    tmpfs_mkdir,
 1455         .vop_rmdir =                    tmpfs_rmdir,
 1456         .vop_symlink =                  tmpfs_symlink,
 1457         .vop_readdir =                  tmpfs_readdir,
 1458         .vop_readlink =                 tmpfs_readlink,
 1459         .vop_inactive =                 tmpfs_inactive,
 1460         .vop_reclaim =                  tmpfs_reclaim,
 1461         .vop_print =                    tmpfs_print,
 1462         .vop_pathconf =                 tmpfs_pathconf,
 1463         .vop_vptofh =                   tmpfs_vptofh,
 1464         .vop_whiteout =                 tmpfs_whiteout,
 1465         .vop_bmap =                     VOP_EOPNOTSUPP,
 1466 };
 1467 

Cache object: 76b6e93d2b855edfd73e53394b2d256b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.