LCOV - code coverage report
Current view: top level - ufs/ext2fs - ext2fs_vnops.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 647 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 21 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: ext2fs_vnops.c,v 1.85 2018/09/06 11:50:54 jsg Exp $   */
       2             : /*      $NetBSD: ext2fs_vnops.c,v 1.1 1997/06/11 09:34:09 bouyer Exp $  */
       3             : 
       4             : /*
       5             :  * Copyright (c) 1997 Manuel Bouyer.
       6             :  * Copyright (c) 1982, 1986, 1989, 1993
       7             :  *      The Regents of the University of California.  All rights reserved.
       8             :  * (c) UNIX System Laboratories, Inc.
       9             :  * All or some portions of this file are derived from material licensed
      10             :  * to the University of California by American Telephone and Telegraph
      11             :  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
      12             :  * the permission of UNIX System Laboratories, Inc.
      13             :  *
      14             :  * Redistribution and use in source and binary forms, with or without
      15             :  * modification, are permitted provided that the following conditions
      16             :  * are met:
      17             :  * 1. Redistributions of source code must retain the above copyright
      18             :  *    notice, this list of conditions and the following disclaimer.
      19             :  * 2. Redistributions in binary form must reproduce the above copyright
      20             :  *    notice, this list of conditions and the following disclaimer in the
      21             :  *    documentation and/or other materials provided with the distribution.
      22             :  * 3. Neither the name of the University nor the names of its contributors
      23             :  *    may be used to endorse or promote products derived from this software
      24             :  *    without specific prior written permission.
      25             :  *
      26             :  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
      27             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      28             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      29             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      30             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      31             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      32             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      33             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      34             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      35             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      36             :  * SUCH DAMAGE.
      37             :  *
      38             :  *      @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94
      39             :  * Modified for ext2fs by Manuel Bouyer.
      40             :  */
      41             : 
      42             : #include <sys/param.h>
      43             : #include <sys/systm.h>
      44             : #include <sys/resourcevar.h>
      45             : #include <sys/kernel.h>
      46             : #include <sys/fcntl.h>
      47             : #include <sys/stat.h>
      48             : #include <sys/proc.h>
      49             : #include <sys/conf.h>
      50             : #include <sys/mount.h>
      51             : #include <sys/namei.h>
      52             : #include <sys/vnode.h>
      53             : #include <sys/lockf.h>
      54             : #include <sys/malloc.h>
      55             : #include <sys/pool.h>
      56             : #include <sys/signalvar.h>
      57             : #include <sys/specdev.h>
      58             : #include <sys/unistd.h>
      59             : 
      60             : #include <miscfs/fifofs/fifo.h>
      61             : 
      62             : #include <ufs/ufs/quota.h>
      63             : #include <ufs/ufs/inode.h>
      64             : #include <ufs/ufs/ufs_extern.h>
      65             : #include <ufs/ufs/ufsmount.h>
      66             : 
      67             : #include <ufs/ext2fs/ext2fs.h>
      68             : #include <ufs/ext2fs/ext2fs_extern.h>
      69             : #include <ufs/ext2fs/ext2fs_dir.h>
      70             : 
      71             : #include <uvm/uvm_extern.h>
      72             : 
      73             : static int ext2fs_chmod(struct vnode *, mode_t, struct ucred *, struct proc *);
      74             : static int ext2fs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct proc *);
      75             : 
      76             : /*
      77             :  * Create a regular file
      78             :  */
      79             : int
      80           0 : ext2fs_create(void *v)
      81             : {
      82           0 :         struct vop_create_args *ap = v;
      83             : 
      84           0 :         return (ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type,
      85           0 :             ap->a_vap->va_mode), ap->a_dvp, ap->a_vpp, ap->a_cnp));
      86             : }
      87             : 
      88             : /*
      89             :  * Mknod vnode call
      90             :  */
      91             : /* ARGSUSED */
      92             : int
      93           0 : ext2fs_mknod(void *v)
      94             : {
      95           0 :         struct vop_mknod_args *ap = v;
      96           0 :         struct vattr *vap = ap->a_vap;
      97           0 :         struct vnode **vpp = ap->a_vpp;
      98             :         struct inode *ip;
      99             :         int error;
     100             : 
     101           0 :         error = ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
     102           0 :             ap->a_dvp, vpp, ap->a_cnp);
     103           0 :         if (error != 0)
     104           0 :                 return (error);
     105           0 :         ip = VTOI(*vpp);
     106           0 :         ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
     107           0 :         if (vap->va_rdev != VNOVAL) {
     108             :                 /*
     109             :                  * Want to be able to use this to make badblock
     110             :                  * inodes, so don't truncate the dev number.
     111             :                  */
     112           0 :                 ip->i_e2din->e2di_rdev = htole32(vap->va_rdev);
     113           0 :         }
     114             :         /*
     115             :          * Remove inode so that it will be reloaded by VFS_VGET and
     116             :          * checked to see if it is an alias of an existing entry in
     117             :          * the inode cache.
     118             :          */
     119           0 :         vput(*vpp);
     120           0 :         (*vpp)->v_type = VNON;
     121           0 :         vgone(*vpp);
     122           0 :         *vpp = NULL;
     123           0 :         return (0);
     124           0 : }
     125             : 
     126             : /*
     127             :  * Open called.
     128             :  *
     129             :  * Just check the APPEND flag.
     130             :  */
     131             : /* ARGSUSED */
     132             : int
     133           0 : ext2fs_open(void *v)
     134             : {
     135           0 :         struct vop_open_args *ap = v;
     136             : 
     137             :         /*
     138             :          * Files marked append-only must be opened for appending.
     139             :          */
     140           0 :         if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
     141           0 :                 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
     142           0 :                 return (EPERM);
     143           0 :         return (0);
     144           0 : }
     145             : 
     146             : int
     147           0 : ext2fs_access(void *v)
     148             : {
     149           0 :         struct vop_access_args *ap = v;
     150           0 :         struct vnode *vp = ap->a_vp;
     151           0 :         struct inode *ip = VTOI(vp);
     152           0 :         mode_t mode = ap->a_mode;
     153             : 
     154             :         /* If immutable bit set, nobody gets to write it. */
     155           0 :         if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
     156           0 :                 return (EPERM);
     157             : 
     158           0 :         return (vaccess(vp->v_type, ip->i_e2fs_mode, ip->i_e2fs_uid,
     159           0 :                         ip->i_e2fs_gid, mode, ap->a_cred));
     160           0 : }
     161             : 
     162             : /* ARGSUSED */
     163             : int
     164           0 : ext2fs_getattr(void *v)
     165             : {
     166           0 :         struct vop_getattr_args *ap = v;
     167           0 :         struct vnode *vp = ap->a_vp;
     168           0 :         struct inode *ip = VTOI(vp);
     169           0 :         struct vattr *vap = ap->a_vap;
     170             : 
     171           0 :         EXT2FS_ITIMES(ip);
     172             :         /*
     173             :          * Copy from inode table
     174             :          */
     175           0 :         vap->va_fsid = ip->i_dev;
     176           0 :         vap->va_fileid = ip->i_number;
     177           0 :         vap->va_mode = ip->i_e2fs_mode & ALLPERMS;
     178           0 :         vap->va_nlink = ip->i_e2fs_nlink;
     179           0 :         vap->va_uid = ip->i_e2fs_uid;
     180           0 :         vap->va_gid = ip->i_e2fs_gid;
     181           0 :         vap->va_rdev = (dev_t) letoh32(ip->i_e2din->e2di_rdev);
     182           0 :         vap->va_size = ext2fs_size(ip);
     183           0 :         vap->va_atime.tv_sec = ip->i_e2fs_atime;
     184           0 :         vap->va_atime.tv_nsec = 0;
     185           0 :         vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
     186           0 :         vap->va_mtime.tv_nsec = 0;
     187           0 :         vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
     188           0 :         vap->va_ctime.tv_nsec = 0;
     189             : #ifdef EXT2FS_SYSTEM_FLAGS
     190             :         vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
     191             :         vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
     192             : #else
     193           0 :         vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
     194           0 :         vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
     195             : #endif
     196           0 :         vap->va_gen = ip->i_e2fs_gen;
     197             :         /* this doesn't belong here */
     198           0 :         if (vp->v_type == VBLK)
     199           0 :                 vap->va_blocksize = BLKDEV_IOSIZE;
     200           0 :         else if (vp->v_type == VCHR)
     201           0 :                 vap->va_blocksize = MAXBSIZE;
     202             :         else
     203           0 :                 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
     204           0 :         vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock);
     205           0 :         vap->va_type = vp->v_type;
     206           0 :         vap->va_filerev = ip->i_modrev;
     207           0 :         return (0);
     208             : }
     209             : 
     210             : /*
     211             :  * Set attribute vnode op. called from several syscalls
     212             :  */
     213             : int
     214           0 : ext2fs_setattr(void *v)
     215             : {
     216           0 :         struct vop_setattr_args *ap = v;
     217           0 :         struct vattr *vap = ap->a_vap;
     218           0 :         struct vnode *vp = ap->a_vp;
     219           0 :         struct inode *ip = VTOI(vp);
     220           0 :         struct ucred *cred = ap->a_cred;
     221           0 :         struct proc *p = ap->a_p;
     222             :         int error;
     223             : 
     224             :         /*
     225             :          * Check for unsettable attributes.
     226             :          */
     227           0 :         if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
     228           0 :                 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
     229           0 :                 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
     230           0 :                 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
     231           0 :                 return (EINVAL);
     232             :         }
     233           0 :         if (vap->va_flags != VNOVAL) {
     234           0 :                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
     235           0 :                         return (EROFS);
     236           0 :                 if (cred->cr_uid != ip->i_e2fs_uid &&
     237           0 :                         (error = suser_ucred(cred)))
     238           0 :                         return (error);
     239             : #ifdef EXT2FS_SYSTEM_FLAGS
     240             :                 if (cred->cr_uid == 0) {
     241             :                         if ((ip->i_e2fs_flags &
     242             :                             (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0)
     243             :                                 return (EPERM);
     244             :                         ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
     245             :                         ip->i_e2fs_flags |=
     246             :                             (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
     247             :                             (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
     248             :                 } else {
     249             :                         return (EPERM);
     250             :                 }
     251             : #else
     252           0 :                 ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
     253           0 :                 ip->i_e2fs_flags |=
     254           0 :                     (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
     255           0 :                     (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
     256             : #endif
     257           0 :                 ip->i_flag |= IN_CHANGE;
     258           0 :                 if (vap->va_flags & (IMMUTABLE | APPEND))
     259           0 :                         return (0);
     260             :         }
     261           0 :         if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
     262           0 :                 return (EPERM);
     263             :         /*
     264             :          * Go through the fields and update iff not VNOVAL.
     265             :          */
     266           0 :         if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
     267           0 :                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
     268           0 :                         return (EROFS);
     269           0 :                 error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
     270           0 :                 if (error)
     271           0 :                         return (error);
     272             :         }
     273           0 :         if (vap->va_size != VNOVAL) {
     274             :                 /*
     275             :                  * Disallow write attempts on read-only file systems;
     276             :                  * unless the file is a socket, fifo, or a block or
     277             :                  * character device resident on the file system.
     278             :                  */
     279           0 :                 switch (vp->v_type) {
     280             :                 case VDIR:
     281           0 :                         return (EISDIR);
     282             :                 case VLNK:
     283             :                 case VREG:
     284           0 :                         if (vp->v_mount->mnt_flag & MNT_RDONLY)
     285           0 :                                 return (EROFS);
     286             :                 default:
     287             :                         break;
     288             :                 }
     289           0 :                 error = ext2fs_truncate(ip, vap->va_size, 0, cred);
     290           0 :                 if (error)
     291           0 :                         return (error);
     292             :         }
     293           0 :         if ((vap->va_vaflags & VA_UTIMES_CHANGE) ||
     294           0 :             vap->va_atime.tv_nsec != VNOVAL ||
     295           0 :             vap->va_mtime.tv_nsec != VNOVAL) {
     296           0 :                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
     297           0 :                         return (EROFS);
     298           0 :                 if (cred->cr_uid != ip->i_e2fs_uid &&
     299           0 :                         (error = suser_ucred(cred)) &&
     300           0 :                         ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
     301           0 :                         (error = VOP_ACCESS(vp, VWRITE, cred, p))))
     302           0 :                         return (error);
     303           0 :                 if (vap->va_mtime.tv_nsec != VNOVAL)
     304           0 :                         ip->i_flag |= IN_CHANGE | IN_UPDATE;
     305           0 :                 else if (vap->va_vaflags & VA_UTIMES_CHANGE)
     306           0 :                         ip->i_flag |= IN_CHANGE;
     307           0 :                 if (vap->va_atime.tv_nsec != VNOVAL) {
     308           0 :                         if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
     309           0 :                             (ip->i_flag & (IN_CHANGE | IN_UPDATE)))
     310           0 :                                 ip->i_flag |= IN_ACCESS;
     311             :                 }
     312           0 :                 EXT2FS_ITIMES(ip);
     313           0 :                 if (vap->va_mtime.tv_nsec != VNOVAL)
     314           0 :                         ip->i_e2fs_mtime = vap->va_mtime.tv_sec;
     315           0 :                 if (vap->va_atime.tv_nsec != VNOVAL)
     316           0 :                         ip->i_e2fs_atime = vap->va_atime.tv_sec;
     317           0 :                 error = ext2fs_update(ip, 1);
     318           0 :                 if (error)
     319           0 :                         return (error);
     320             :         }
     321             :         error = 0;
     322           0 :         if (vap->va_mode != (mode_t)VNOVAL) {
     323           0 :                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
     324           0 :                         return (EROFS);
     325           0 :                 error = ext2fs_chmod(vp, vap->va_mode, cred, p);
     326           0 :         }
     327           0 :         return (error);
     328           0 : }
     329             : 
     330             : /*
     331             :  * Change the mode on a file.
     332             :  * Inode must be locked before calling.
     333             :  */
     334             : static int
     335           0 : ext2fs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct proc *p)
     336             : {
     337           0 :         struct inode *ip = VTOI(vp);
     338             :         int error;
     339             : 
     340           0 :         if (cred->cr_uid != ip->i_e2fs_uid && (error = suser_ucred(cred)))
     341           0 :                 return (error);
     342           0 :         if (cred->cr_uid) {
     343           0 :                 if (vp->v_type != VDIR && (mode & S_ISTXT))
     344           0 :                         return (EFTYPE);
     345           0 :                 if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
     346           0 :                         return (EPERM);
     347             :         }
     348           0 :         ip->i_e2fs_mode &= ~ALLPERMS;
     349           0 :         ip->i_e2fs_mode |= (mode & ALLPERMS);
     350           0 :         ip->i_flag |= IN_CHANGE;
     351           0 :         if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
     352           0 :                 (void) uvm_vnp_uncache(vp);
     353           0 :         return (0);
     354           0 : }
     355             : 
     356             : /*
     357             :  * Perform chown operation on inode ip;
     358             :  * inode must be locked prior to call.
     359             :  */
     360             : static int
     361           0 : ext2fs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p)
     362             : {
     363           0 :         struct inode *ip = VTOI(vp);
     364             :         uid_t ouid;
     365             :         gid_t ogid;
     366             :         int error = 0;
     367             : 
     368           0 :         if (uid == (uid_t)VNOVAL)
     369           0 :                 uid = ip->i_e2fs_uid;
     370           0 :         if (gid == (gid_t)VNOVAL)
     371           0 :                 gid = ip->i_e2fs_gid;
     372             :         /*
     373             :          * If we don't own the file, are trying to change the owner
     374             :          * of the file, or are not a member of the target group,
     375             :          * the caller must be superuser or the call fails.
     376             :          */
     377           0 :         if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
     378           0 :                 (gid != ip->i_e2fs_gid && !groupmember(gid, cred))) &&
     379           0 :                 (error = suser_ucred(cred)))
     380           0 :                 return (error);
     381           0 :         ogid = ip->i_e2fs_gid;
     382           0 :         ouid = ip->i_e2fs_uid;
     383             : 
     384           0 :         ip->i_e2fs_gid = gid;
     385           0 :         ip->i_e2fs_uid = uid;
     386           0 :         if (ouid != uid || ogid != gid)
     387           0 :                 ip->i_flag |= IN_CHANGE;
     388           0 :         if (ouid != uid && cred->cr_uid != 0)
     389           0 :                 ip->i_e2fs_mode &= ~ISUID;
     390           0 :         if (ogid != gid && cred->cr_uid != 0)
     391           0 :                 ip->i_e2fs_mode &= ~ISGID;
     392           0 :         return (0);
     393           0 : }
     394             : 
     395             : int
     396           0 : ext2fs_remove(void *v)
     397             : {
     398           0 :         struct vop_remove_args *ap = v;
     399             :         struct inode *ip;
     400           0 :         struct vnode *vp = ap->a_vp;
     401           0 :         struct vnode *dvp = ap->a_dvp;
     402             :         int error;
     403             : 
     404           0 :         ip = VTOI(vp);
     405           0 :         if (vp->v_type == VDIR ||
     406           0 :                 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
     407           0 :                 (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
     408             :                 error = EPERM;
     409           0 :                 goto out;
     410             :         }
     411           0 :         error = ext2fs_dirremove(dvp, ap->a_cnp);
     412           0 :         if (error == 0) {
     413           0 :                 ip->i_e2fs_nlink--;
     414           0 :                 ip->i_flag |= IN_CHANGE;
     415           0 :         }
     416             : out:
     417           0 :         if (dvp == vp)
     418           0 :                 vrele(vp);
     419             :         else
     420           0 :                 vput(vp);
     421           0 :         vput(dvp);
     422           0 :         return (error);
     423             : }
     424             : 
     425             : /*
     426             :  * link vnode call
     427             :  */
     428             : int
     429           0 : ext2fs_link(void *v)
     430             : {
     431           0 :         struct vop_link_args *ap = v;
     432           0 :         struct vnode *dvp = ap->a_dvp;
     433           0 :         struct vnode *vp = ap->a_vp;
     434           0 :         struct componentname *cnp = ap->a_cnp;
     435             :         struct inode *ip;
     436             :         int error;
     437             : 
     438             : #ifdef DIAGNOSTIC
     439           0 :         if ((cnp->cn_flags & HASBUF) == 0)
     440           0 :                 panic("ext2fs_link: no name");
     441             : #endif
     442           0 :         if (vp->v_type == VDIR) {
     443           0 :                 VOP_ABORTOP(dvp, cnp);
     444             :                 error = EISDIR;
     445           0 :                 goto out2;
     446             :         }
     447           0 :         if (dvp->v_mount != vp->v_mount) {
     448           0 :                 VOP_ABORTOP(dvp, cnp);
     449             :                 error = EXDEV;
     450           0 :                 goto out2;
     451             :         }
     452           0 :         if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) {
     453           0 :                 VOP_ABORTOP(dvp, cnp);
     454           0 :                 goto out2;
     455             :         }
     456           0 :         ip = VTOI(vp);
     457           0 :         if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
     458           0 :                 VOP_ABORTOP(dvp, cnp);
     459             :                 error = EMLINK;
     460           0 :                 goto out1;
     461             :         }
     462           0 :         if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
     463           0 :                 VOP_ABORTOP(dvp, cnp);
     464             :                 error = EPERM;
     465           0 :                 goto out1;
     466             :         }
     467           0 :         ip->i_e2fs_nlink++;
     468           0 :         ip->i_flag |= IN_CHANGE;
     469           0 :         error = ext2fs_update(ip, 1);
     470           0 :         if (!error)
     471           0 :                 error = ext2fs_direnter(ip, dvp, cnp);
     472           0 :         if (error) {
     473           0 :                 ip->i_e2fs_nlink--;
     474           0 :                 ip->i_flag |= IN_CHANGE;
     475           0 :         }
     476           0 :         pool_put(&namei_pool, cnp->cn_pnbuf);
     477             : out1:
     478           0 :         if (dvp != vp)
     479           0 :                 VOP_UNLOCK(vp);
     480             : out2:
     481           0 :         vput(dvp);
     482           0 :         return (error);
     483             : }
     484             : 
     485             : /*
     486             :  * Rename system call.
     487             :  *      rename("foo", "bar");
     488             :  * is essentially
     489             :  *      unlink("bar");
     490             :  *      link("foo", "bar");
     491             :  *      unlink("foo");
     492             :  * but ``atomically''.  Can't do full commit without saving state in the
     493             :  * inode on disk which isn't feasible at this time.  Best we can do is
     494             :  * always guarantee the target exists.
     495             :  *
     496             :  * Basic algorithm is:
     497             :  *
     498             :  * 1) Bump link count on source while we're linking it to the
     499             :  *    target.  This also ensure the inode won't be deleted out
     500             :  *    from underneath us while we work (it may be truncated by
     501             :  *    a concurrent `trunc' or `open' for creation).
     502             :  * 2) Link source to destination.  If destination already exists,
     503             :  *    delete it first.
     504             :  * 3) Unlink source reference to inode if still around. If a
     505             :  *    directory was moved and the parent of the destination
     506             :  *    is different from the source, patch the ".." entry in the
     507             :  *    directory.
     508             :  */
     509             : int
     510           0 : ext2fs_rename(void *v)
     511             : {
     512           0 :         struct vop_rename_args  *ap = v;
     513           0 :         struct vnode *tvp = ap->a_tvp;
     514           0 :         struct vnode *tdvp = ap->a_tdvp;
     515           0 :         struct vnode *fvp = ap->a_fvp;
     516           0 :         struct vnode *fdvp = ap->a_fdvp;
     517           0 :         struct componentname *tcnp = ap->a_tcnp;
     518           0 :         struct componentname *fcnp = ap->a_fcnp;
     519             :         struct inode *ip, *xp, *dp;
     520           0 :         struct ext2fs_dirtemplate dirbuf;
     521             :         /* struct timespec ts; */
     522             :         int doingdirectory = 0, oldparent = 0, newparent = 0;
     523             :         int error = 0;
     524             :         u_char namlen;
     525             : 
     526             : #ifdef DIAGNOSTIC
     527           0 :         if ((tcnp->cn_flags & HASBUF) == 0 ||
     528           0 :             (fcnp->cn_flags & HASBUF) == 0)
     529           0 :                 panic("ext2fs_rename: no name");
     530             : #endif
     531             :         /*
     532             :          * Check for cross-device rename.
     533             :          */
     534           0 :         if ((fvp->v_mount != tdvp->v_mount) ||
     535           0 :             (tvp && (fvp->v_mount != tvp->v_mount))) {
     536           0 :                 error = EXDEV;
     537             : abortit:
     538           0 :                 VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
     539           0 :                 if (tdvp == tvp)
     540           0 :                         vrele(tdvp);
     541             :                 else
     542           0 :                         vput(tdvp);
     543           0 :                 if (tvp)
     544           0 :                         vput(tvp);
     545           0 :                 VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
     546           0 :                 vrele(fdvp);
     547           0 :                 vrele(fvp);
     548           0 :                 return (error);
     549             :         }
     550             : 
     551             :         /*
     552             :          * Check if just deleting a link name.
     553             :          */
     554           0 :         if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
     555           0 :             (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
     556             :                 error = EPERM;
     557           0 :                 goto abortit;
     558             :         }
     559           0 :         if (fvp == tvp) {
     560           0 :                 if (fvp->v_type == VDIR) {
     561             :                         error = EINVAL;
     562           0 :                         goto abortit;
     563             :                 }
     564             : 
     565             :                 /* Release destination completely. */
     566           0 :                 VOP_ABORTOP(tdvp, tcnp);
     567           0 :                 vput(tdvp);
     568           0 :                 vput(tvp);
     569             : 
     570             :                 /* Delete source. */
     571           0 :                 vrele(fdvp);
     572           0 :                 vrele(fvp);
     573           0 :                 fcnp->cn_flags &= ~MODMASK;
     574           0 :                 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
     575           0 :                 if ((fcnp->cn_flags & SAVESTART) == 0)
     576           0 :                         panic("ext2fs_rename: lost from startdir");
     577           0 :                 fcnp->cn_nameiop = DELETE;
     578           0 :                 (void) vfs_relookup(fdvp, &fvp, fcnp);
     579           0 :                 return (VOP_REMOVE(fdvp, fvp, fcnp));
     580             :         }
     581           0 :         if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
     582             :                 goto abortit;
     583           0 :         dp = VTOI(fdvp);
     584           0 :         ip = VTOI(fvp);
     585           0 :         if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
     586           0 :                 VOP_UNLOCK(fvp);
     587             :                 error = EMLINK;
     588           0 :                 goto abortit;
     589             :         }
     590           0 :         if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
     591           0 :                 (dp->i_e2fs_flags & EXT2_APPEND)) {
     592           0 :                 VOP_UNLOCK(fvp);
     593             :                 error = EPERM;
     594           0 :                 goto abortit;
     595             :         }
     596           0 :         if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
     597           0 :                 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
     598           0 :                 if (!error && tvp)
     599           0 :                         error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred,
     600           0 :                             tcnp->cn_proc);
     601           0 :                 if (error) {
     602           0 :                         VOP_UNLOCK(fvp);
     603             :                         error = EACCES;
     604           0 :                         goto abortit;
     605             :                 }
     606             :                 /*
     607             :                  * Avoid ".", "..", and aliases of "." for obvious reasons.
     608             :                  */
     609           0 :                 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
     610           0 :                     dp == ip ||
     611           0 :                         (fcnp->cn_flags&ISDOTDOT) ||
     612           0 :                         (tcnp->cn_flags & ISDOTDOT) ||
     613           0 :                     (ip->i_flag & IN_RENAME)) {
     614           0 :                         VOP_UNLOCK(fvp);
     615             :                         error = EINVAL;
     616           0 :                         goto abortit;
     617             :                 }
     618           0 :                 ip->i_flag |= IN_RENAME;
     619           0 :                 oldparent = dp->i_number;
     620             :                 doingdirectory++;
     621           0 :         }
     622           0 :         vrele(fdvp);
     623             : 
     624             :         /*
     625             :          * When the target exists, both the directory
     626             :          * and target vnodes are returned locked.
     627             :          */
     628           0 :         dp = VTOI(tdvp);
     629             :         xp = NULL;
     630           0 :         if (tvp)
     631           0 :                 xp = VTOI(tvp);
     632             : 
     633             :         /*
     634             :          * 1) Bump link count while we're moving stuff
     635             :          *    around.  If we crash somewhere before
     636             :          *    completing our work, the link count
     637             :          *    may be wrong, but correctable.
     638             :          */
     639           0 :         ip->i_e2fs_nlink++;
     640           0 :         ip->i_flag |= IN_CHANGE;
     641           0 :         if ((error = ext2fs_update(ip, 1)) != 0) {
     642           0 :                 VOP_UNLOCK(fvp);
     643           0 :                 goto bad;
     644             :         }
     645             : 
     646             :         /*
     647             :          * If ".." must be changed (ie the directory gets a new
     648             :          * parent) then the source directory must not be in the
     649             :          * directory hierarchy above the target, as this would
     650             :          * orphan everything below the source directory. Also
     651             :          * the user must have write permission in the source so
     652             :          * as to be able to change "..". We must repeat the call
     653             :          * to namei, as the parent directory is unlocked by the
     654             :          * call to checkpath().
     655             :          */
     656           0 :         error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
     657           0 :         VOP_UNLOCK(fvp);
     658           0 :         if (oldparent != dp->i_number)
     659           0 :                 newparent = dp->i_number;
     660           0 :         if (doingdirectory && newparent) {
     661           0 :                 if (error)      /* write access check above */
     662             :                         goto bad;
     663           0 :                 if (xp != NULL)
     664           0 :                         vput(tvp);
     665           0 :                 error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
     666           0 :                 if (error != 0)
     667             :                         goto out;
     668           0 :                 if ((tcnp->cn_flags & SAVESTART) == 0)
     669           0 :                         panic("ext2fs_rename: lost to startdir");
     670           0 :                 if ((error = vfs_relookup(tdvp, &tvp, tcnp)) != 0)
     671             :                         goto out;
     672           0 :                 dp = VTOI(tdvp);
     673             :                 xp = NULL;
     674           0 :                 if (tvp)
     675           0 :                         xp = VTOI(tvp);
     676             :         }
     677             :         /*
     678             :          * 2) If target doesn't exist, link the target
     679             :          *    to the source and unlink the source.
     680             :          *    Otherwise, rewrite the target directory
     681             :          *    entry to reference the source inode and
     682             :          *    expunge the original entry's existence.
     683             :          */
     684           0 :         if (xp == NULL) {
     685           0 :                 if (dp->i_dev != ip->i_dev)
     686           0 :                         panic("rename: EXDEV");
     687             :                 /*
     688             :                  * Account for ".." in new directory.
     689             :                  * When source and destination have the same
     690             :                  * parent we don't fool with the link count.
     691             :                  */
     692           0 :                 if (doingdirectory && newparent) {
     693           0 :                         if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
     694             :                                 error = EMLINK;
     695           0 :                                 goto bad;
     696             :                         }
     697           0 :                         dp->i_e2fs_nlink++;
     698           0 :                         dp->i_flag |= IN_CHANGE;
     699           0 :                         if ((error = ext2fs_update(dp, 1)) != 0)
     700             :                                 goto bad;
     701             :                 }
     702           0 :                 error = ext2fs_direnter(ip, tdvp, tcnp);
     703           0 :                 if (error != 0) {
     704           0 :                         if (doingdirectory && newparent) {
     705           0 :                                 dp->i_e2fs_nlink--;
     706           0 :                                 dp->i_flag |= IN_CHANGE;
     707           0 :                                 (void)ext2fs_update(dp, 1);
     708           0 :                         }
     709             :                         goto bad;
     710             :                 }
     711           0 :                 vput(tdvp);
     712           0 :         } else {
     713           0 :                 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
     714           0 :                         panic("rename: EXDEV");
     715             :                 /*
     716             :                  * Short circuit rename(foo, foo).
     717             :                  */
     718           0 :                 if (xp->i_number == ip->i_number)
     719           0 :                         panic("rename: same file");
     720             :                 /*
     721             :                  * If the parent directory is "sticky", then the user must
     722             :                  * own the parent directory, or the destination of the rename,
     723             :                  * otherwise the destination may not be changed (except by
     724             :                  * root). This implements append-only directories.
     725             :                  */
     726           0 :                 if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
     727           0 :                     tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
     728           0 :                     xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
     729             :                         error = EPERM;
     730           0 :                         goto bad;
     731             :                 }
     732             :                 /*
     733             :                  * Target must be empty if a directory and have no links
     734             :                  * to it. Also, ensure source and target are compatible
     735             :                  * (both directories, or both not directories).
     736             :                  */
     737           0 :                 if ((xp->i_e2fs_mode & IFMT) == IFDIR) {
     738           0 :                         if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
     739           0 :                                 xp->i_e2fs_nlink > 2) {
     740             :                                 error = ENOTEMPTY;
     741           0 :                                 goto bad;
     742             :                         }
     743           0 :                         if (!doingdirectory) {
     744             :                                 error = ENOTDIR;
     745           0 :                                 goto bad;
     746             :                         }
     747           0 :                         cache_purge(tdvp);
     748           0 :                 } else if (doingdirectory) {
     749             :                         error = EISDIR;
     750           0 :                         goto bad;
     751             :                 }
     752           0 :                 error = ext2fs_dirrewrite(dp, ip, tcnp);
     753           0 :                 if (error != 0)
     754             :                         goto bad;
     755             :                 /*
     756             :                  * If the target directory is in the same
     757             :                  * directory as the source directory,
     758             :                  * decrement the link count on the parent
     759             :                  * of the target directory.
     760             :                  */
     761           0 :                 if (doingdirectory && !newparent) {
     762           0 :                         dp->i_e2fs_nlink--;
     763           0 :                         dp->i_flag |= IN_CHANGE;
     764           0 :                 }
     765           0 :                 vput(tdvp);
     766             :                 /*
     767             :                  * Adjust the link count of the target to
     768             :                  * reflect the dirrewrite above.  If this is
     769             :                  * a directory it is empty and there are
     770             :                  * no links to it, so we can squash the inode and
     771             :                  * any space associated with it.  We disallowed
     772             :                  * renaming over top of a directory with links to
     773             :                  * it above, as the remaining link would point to
     774             :                  * a directory without "." or ".." entries.
     775             :                  */
     776           0 :                 xp->i_e2fs_nlink--;
     777           0 :                 if (doingdirectory) {
     778           0 :                         if (--xp->i_e2fs_nlink != 0)
     779           0 :                                 panic("rename: linked directory");
     780           0 :                         error = ext2fs_truncate(xp, (off_t)0, IO_SYNC,
     781           0 :                             tcnp->cn_cred);
     782           0 :                 }
     783           0 :                 xp->i_flag |= IN_CHANGE;
     784           0 :                 vput(tvp);
     785             :                 xp = NULL;
     786             :         }
     787             : 
     788             :         /*
     789             :          * 3) Unlink the source.
     790             :          */
     791           0 :         fcnp->cn_flags &= ~MODMASK;
     792           0 :         fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
     793           0 :         if ((fcnp->cn_flags & SAVESTART) == 0)
     794           0 :                 panic("ext2fs_rename: lost from startdir");
     795           0 :         (void) vfs_relookup(fdvp, &fvp, fcnp);
     796           0 :         if (fvp != NULL) {
     797           0 :                 xp = VTOI(fvp);
     798           0 :                 dp = VTOI(fdvp);
     799             :         } else {
     800             :                 /*
     801             :                  * From name has disappeared.
     802             :                  */
     803           0 :                 if (doingdirectory)
     804           0 :                         panic("ext2fs_rename: lost dir entry");
     805           0 :                 vrele(ap->a_fvp);
     806           0 :                 return (0);
     807             :         }
     808             :         /*
     809             :          * Ensure that the directory entry still exists and has not
     810             :          * changed while the new name has been entered. If the source is
     811             :          * a file then the entry may have been unlinked or renamed. In
     812             :          * either case there is no further work to be done. If the source
     813             :          * is a directory then it cannot have been rmdir'ed; its link
     814             :          * count of three would cause a rmdir to fail with ENOTEMPTY.
     815             :          * The IRENAME flag ensures that it cannot be moved by another
     816             :          * rename.
     817             :          */
     818           0 :         if (xp != ip) {
     819           0 :                 if (doingdirectory)
     820           0 :                         panic("ext2fs_rename: lost dir entry");
     821             :         } else {
     822             :                 /*
     823             :                  * If the source is a directory with a
     824             :                  * new parent, the link count of the old
     825             :                  * parent directory must be decremented
     826             :                  * and ".." set to point to the new parent.
     827             :                  */
     828           0 :                 if (doingdirectory && newparent) {
     829           0 :                         dp->i_e2fs_nlink--;
     830           0 :                         dp->i_flag |= IN_CHANGE;
     831           0 :                         error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
     832             :                                 sizeof (struct ext2fs_dirtemplate), (off_t)0,
     833             :                                 UIO_SYSSPACE, IO_NODELOCKED,
     834           0 :                                 tcnp->cn_cred, NULL, curproc);
     835           0 :                         if (error == 0) {
     836           0 :                                         namlen = dirbuf.dotdot_namlen;
     837           0 :                                 if (namlen != 2 ||
     838           0 :                                     dirbuf.dotdot_name[0] != '.' ||
     839           0 :                                     dirbuf.dotdot_name[1] != '.') {
     840           0 :                                         ufs_dirbad(xp, (doff_t)12,
     841             :                                             "ext2fs_rename: mangled dir");
     842           0 :                                 } else {
     843           0 :                                         dirbuf.dotdot_ino = htole32(newparent);
     844           0 :                                         (void) vn_rdwr(UIO_WRITE, fvp,
     845             :                                             (caddr_t)&dirbuf,
     846             :                                             sizeof (struct dirtemplate),
     847             :                                             (off_t)0, UIO_SYSSPACE,
     848             :                                             IO_NODELOCKED|IO_SYNC,
     849           0 :                                             tcnp->cn_cred, NULL, curproc);
     850           0 :                                         cache_purge(fdvp);
     851             :                                 }
     852             :                         }
     853             :                 }
     854           0 :                 error = ext2fs_dirremove(fdvp, fcnp);
     855           0 :                 if (!error) {
     856           0 :                         xp->i_e2fs_nlink--;
     857           0 :                         xp->i_flag |= IN_CHANGE;
     858           0 :                 }
     859           0 :                 xp->i_flag &= ~IN_RENAME;
     860             :         }
     861           0 :         if (dp)
     862           0 :                 vput(fdvp);
     863           0 :         if (xp)
     864           0 :                 vput(fvp);
     865           0 :         vrele(ap->a_fvp);
     866           0 :         return (error);
     867             : 
     868             : bad:
     869           0 :         if (xp)
     870           0 :                 vput(ITOV(xp));
     871           0 :         vput(ITOV(dp));
     872             : out:
     873           0 :         if (doingdirectory)
     874           0 :                 ip->i_flag &= ~IN_RENAME;
     875           0 :         if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
     876           0 :                 ip->i_e2fs_nlink--;
     877           0 :                 ip->i_flag |= IN_CHANGE;
     878           0 :                 vput(fvp);
     879           0 :         } else
     880           0 :                 vrele(fvp);
     881           0 :         return (error);
     882           0 : }
     883             : 
     884             : /*
     885             :  * Mkdir system call
     886             :  */
     887             : int
     888           0 : ext2fs_mkdir(void *v)
     889             : {
     890           0 :         struct vop_mkdir_args *ap = v;
     891           0 :         struct vnode *dvp = ap->a_dvp;
     892           0 :         struct vattr *vap = ap->a_vap;
     893           0 :         struct componentname *cnp = ap->a_cnp;
     894             :         struct inode *ip, *dp;
     895           0 :         struct vnode *tvp;
     896           0 :         struct ext2fs_dirtemplate dirtemplate;
     897             :         mode_t dmode;
     898             :         int error;
     899             : 
     900             : #ifdef DIAGNOSTIC
     901           0 :         if ((cnp->cn_flags & HASBUF) == 0)
     902           0 :                 panic("ext2fs_mkdir: no name");
     903             : #endif
     904           0 :         dp = VTOI(dvp);
     905           0 :         if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
     906             :                 error = EMLINK;
     907           0 :                 goto out;
     908             :         }
     909           0 :         dmode = vap->va_mode & ACCESSPERMS;
     910           0 :         dmode |= IFDIR;
     911             :         /*
     912             :          * Must simulate part of ext2fs_makeinode here to acquire the inode,
     913             :          * but not have it entered in the parent directory. The entry is
     914             :          * made later after writing "." and ".." entries.
     915             :          */
     916           0 :         if ((error = ext2fs_inode_alloc(dp, dmode, cnp->cn_cred, &tvp)) != 0)
     917             :                 goto out;
     918           0 :         ip = VTOI(tvp);
     919           0 :         ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
     920           0 :         ip->i_e2fs_gid = dp->i_e2fs_gid;
     921           0 :         ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
     922           0 :         ip->i_e2fs_mode = dmode;
     923           0 :         tvp->v_type = VDIR;  /* Rest init'd in getnewvnode(). */
     924           0 :         ip->i_e2fs_nlink = 2;
     925           0 :         error = ext2fs_update(ip, 1);
     926             : 
     927             :         /*
     928             :          * Bump link count in parent directory
     929             :          * to reflect work done below.  Should
     930             :          * be done before reference is created
     931             :          * so reparation is possible if we crash.
     932             :          */
     933           0 :         dp->i_e2fs_nlink++;
     934           0 :         dp->i_flag |= IN_CHANGE;
     935           0 :         if ((error = ext2fs_update(dp, 1)) != 0)
     936             :                 goto bad;
     937             : 
     938             :         /* Initialize directory with "." and ".." from static template. */
     939           0 :         memset(&dirtemplate, 0, sizeof(dirtemplate));
     940           0 :         dirtemplate.dot_ino = htole32(ip->i_number);
     941           0 :         dirtemplate.dot_reclen = htole16(12);
     942           0 :         dirtemplate.dot_namlen = 1;
     943           0 :         if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
     944           0 :             (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
     945           0 :                 dirtemplate.dot_type = EXT2_FT_DIR;
     946           0 :         }
     947           0 :         dirtemplate.dot_name[0] = '.';
     948           0 :         dirtemplate.dotdot_ino = htole32(dp->i_number);
     949           0 :         dirtemplate.dotdot_reclen = htole16(VTOI(dvp)->i_e2fs->e2fs_bsize - 12);
     950           0 :         dirtemplate.dotdot_namlen = 2;
     951           0 :         if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
     952           0 :             (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
     953           0 :                 dirtemplate.dotdot_type = EXT2_FT_DIR;
     954           0 :         }
     955           0 :         dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.';
     956           0 :         error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
     957             :             sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
     958           0 :             IO_NODELOCKED|IO_SYNC, cnp->cn_cred, NULL, curproc);
     959           0 :         if (error) {
     960           0 :                 dp->i_e2fs_nlink--;
     961           0 :                 dp->i_flag |= IN_CHANGE;
     962           0 :                 goto bad;
     963             :         }
     964           0 :         if (VTOI(dvp)->i_e2fs->e2fs_bsize >
     965           0 :                                                         VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
     966           0 :                 panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
     967             :         else {
     968           0 :                 error = ext2fs_setsize(ip, VTOI(dvp)->i_e2fs->e2fs_bsize);
     969           0 :                 if (error) {
     970           0 :                         dp->i_e2fs_nlink--;
     971           0 :                         dp->i_flag |= IN_CHANGE;
     972           0 :                         goto bad;
     973             :                 }
     974           0 :                 ip->i_flag |= IN_CHANGE;
     975             :         }
     976             : 
     977             :         /* Directory set up, now install its entry in the parent directory. */
     978           0 :         error = ext2fs_direnter(ip, dvp, cnp);
     979           0 :         if (error != 0) {
     980           0 :                 dp->i_e2fs_nlink--;
     981           0 :                 dp->i_flag |= IN_CHANGE;
     982           0 :         }
     983             : bad:
     984             :         /*
     985             :          * No need to do an explicit VOP_TRUNCATE here, vrele will do this
     986             :          * for us because we set the link count to 0.
     987             :          */
     988           0 :         if (error) {
     989           0 :                 ip->i_e2fs_nlink = 0;
     990           0 :                 ip->i_flag |= IN_CHANGE;
     991           0 :                 vput(tvp);
     992           0 :         } else
     993           0 :                 *ap->a_vpp = tvp;
     994             : out:
     995           0 :         pool_put(&namei_pool, cnp->cn_pnbuf);
     996           0 :         vput(dvp);
     997           0 :         return (error);
     998           0 : }
     999             : 
    1000             : /*
    1001             :  * Rmdir system call.
    1002             :  */
    1003             : int
    1004           0 : ext2fs_rmdir(void *v)
    1005             : {
    1006           0 :         struct vop_rmdir_args *ap = v;
    1007           0 :         struct vnode *vp = ap->a_vp;
    1008           0 :         struct vnode *dvp = ap->a_dvp;
    1009           0 :         struct componentname *cnp = ap->a_cnp;
    1010             :         struct inode *ip, *dp;
    1011             :         int error;
    1012             : 
    1013           0 :         ip = VTOI(vp);
    1014           0 :         dp = VTOI(dvp);
    1015             :         /*
    1016             :          * Verify the directory is empty (and valid).
    1017             :          * (Rmdir ".." won't be valid since
    1018             :          *  ".." will contain a reference to
    1019             :          *  the current directory and thus be
    1020             :          *  non-empty.)
    1021             :          */
    1022             :         error = 0;
    1023           0 :         if (ip->i_e2fs_nlink != 2 ||
    1024           0 :             !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
    1025             :                 error = ENOTEMPTY;
    1026           0 :                 goto out;
    1027             :         }
    1028           0 :         if ((dp->i_e2fs_flags & EXT2_APPEND) ||
    1029           0 :                                  (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
    1030             :                 error = EPERM;
    1031           0 :                 goto out;
    1032             :         }
    1033             :         /*
    1034             :          * Delete reference to directory before purging
    1035             :          * inode.  If we crash in between, the directory
    1036             :          * will be reattached to lost+found,
    1037             :          */
    1038           0 :         error = ext2fs_dirremove(dvp, cnp);
    1039           0 :         if (error != 0)
    1040             :                 goto out;
    1041           0 :         dp->i_e2fs_nlink--;
    1042           0 :         dp->i_flag |= IN_CHANGE;
    1043           0 :         cache_purge(dvp);
    1044           0 :         vput(dvp);
    1045             :         dvp = NULL;
    1046             :         /*
    1047             :          * Truncate inode.  The only stuff left
    1048             :          * in the directory is "." and "..".  The
    1049             :          * "." reference is inconsequential since
    1050             :          * we're quashing it.  The ".." reference
    1051             :          * has already been adjusted above.  We've
    1052             :          * removed the "." reference and the reference
    1053             :          * in the parent directory, but there may be
    1054             :          * other hard links so decrement by 2 and
    1055             :          * worry about them later.
    1056             :          */
    1057           0 :         ip->i_e2fs_nlink -= 2;
    1058           0 :         error = ext2fs_truncate(ip, (off_t)0, IO_SYNC, cnp->cn_cred);
    1059           0 :         cache_purge(ITOV(ip));
    1060             : out:
    1061           0 :         if (dvp)
    1062           0 :                 vput(dvp);
    1063           0 :         vput(vp);
    1064           0 :         return (error);
    1065             : }
    1066             : 
    1067             : /*
    1068             :  * symlink -- make a symbolic link
    1069             :  */
    1070             : int
    1071           0 : ext2fs_symlink(void *v)
    1072             : {
    1073           0 :         struct vop_symlink_args *ap = v;
    1074           0 :         struct vnode *vp, **vpp = ap->a_vpp;
    1075             :         struct inode *ip;
    1076             :         int len, error;
    1077             : 
    1078           0 :         error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
    1079           0 :                               vpp, ap->a_cnp);
    1080           0 :         vput(ap->a_dvp);
    1081           0 :         if (error)
    1082           0 :                 return (error);
    1083           0 :         vp = *vpp;
    1084           0 :         len = strlen(ap->a_target);
    1085           0 :         if (len < EXT2_MAXSYMLINKLEN) {
    1086           0 :                 ip = VTOI(vp);
    1087           0 :                 memcpy(ip->i_e2din->e2di_shortlink, ap->a_target, len);
    1088           0 :                 error = ext2fs_setsize(ip, len);
    1089           0 :                 if (error)
    1090             :                         goto bad;
    1091           0 :                 ip->i_flag |= IN_CHANGE | IN_UPDATE;
    1092           0 :         } else
    1093           0 :                 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
    1094           0 :                     UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL,
    1095           0 :                     curproc);
    1096             : bad:
    1097           0 :         vput(vp);
    1098           0 :         return (error);
    1099           0 : }
    1100             : 
    1101             : /*
    1102             :  * Return target name of a symbolic link
    1103             :  */
    1104             : int
    1105           0 : ext2fs_readlink(void *v)
    1106             : {
    1107           0 :         struct vop_readlink_args *ap = v;
    1108           0 :         struct vnode *vp = ap->a_vp;
    1109           0 :         struct inode *ip = VTOI(vp);
    1110             :         u_int64_t isize;
    1111             : 
    1112           0 :         isize = ext2fs_size(ip);
    1113           0 :         if (isize < EXT2_MAXSYMLINKLEN) {
    1114           0 :                 return (uiomove((char *)ip->i_e2din->e2di_shortlink, isize,
    1115           0 :                     ap->a_uio));
    1116             :         }
    1117           0 :         return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
    1118           0 : }
    1119             : 
    1120             : /*
    1121             :  * Return POSIX pathconf information applicable to ext2 filesystems.
    1122             :  */
    1123             : int
    1124           0 : ext2fs_pathconf(void *v)
    1125             : {
    1126           0 :         struct vop_pathconf_args *ap = v;
    1127             :         int error = 0;
    1128             : 
    1129           0 :         switch (ap->a_name) {
    1130             :         case _PC_TIMESTAMP_RESOLUTION:
    1131           0 :                 *ap->a_retval = 1000000000;  /* 1 billion nanoseconds */
    1132             :                 break;
    1133             :         default:
    1134           0 :                 return (ufs_pathconf(v));
    1135             :         }
    1136             : 
    1137           0 :         return (error);
    1138           0 : }
    1139             : 
    1140             : /*
    1141             :  * Advisory record locking support
    1142             :  */
    1143             : int
    1144           0 : ext2fs_advlock(void *v)
    1145             : {
    1146           0 :         struct vop_advlock_args *ap = v;
    1147           0 :         struct inode *ip = VTOI(ap->a_vp);
    1148             : 
    1149           0 :         return (lf_advlock(&ip->i_lockf, ext2fs_size(ip), ap->a_id, ap->a_op,
    1150           0 :             ap->a_fl, ap->a_flags));
    1151             : }
    1152             : 
    1153             : /*
    1154             :  * Allocate a new inode.
    1155             :  */
    1156             : int
    1157           0 : ext2fs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
    1158             :     struct componentname *cnp)
    1159             : {
    1160             :         struct inode *ip, *pdir;
    1161           0 :         struct vnode *tvp;
    1162             :         int error;
    1163             : 
    1164           0 :         pdir = VTOI(dvp);
    1165             : #ifdef DIAGNOSTIC
    1166           0 :         if ((cnp->cn_flags & HASBUF) == 0)
    1167           0 :                 panic("ext2fs_makeinode: no name");
    1168             : #endif
    1169           0 :         *vpp = NULL;
    1170           0 :         if ((mode & IFMT) == 0)
    1171           0 :                 mode |= IFREG;
    1172             : 
    1173           0 :         if ((error = ext2fs_inode_alloc(pdir, mode, cnp->cn_cred, &tvp))
    1174           0 :             != 0) {
    1175           0 :                 pool_put(&namei_pool, cnp->cn_pnbuf);
    1176           0 :                 return (error);
    1177             :         }
    1178           0 :         ip = VTOI(tvp);
    1179           0 :         ip->i_e2fs_gid = pdir->i_e2fs_gid;
    1180           0 :         ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
    1181           0 :         ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
    1182           0 :         ip->i_e2fs_mode = mode;
    1183           0 :         tvp->v_type = IFTOVT(mode);  /* Rest init'd in getnewvnode(). */
    1184           0 :         ip->i_e2fs_nlink = 1;
    1185           0 :         if ((ip->i_e2fs_mode & ISGID) &&
    1186           0 :                 !groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
    1187           0 :             suser_ucred(cnp->cn_cred))
    1188           0 :                 ip->i_e2fs_mode &= ~ISGID;
    1189             : 
    1190             :         /*
    1191             :          * Make sure inode goes to disk before directory entry.
    1192             :          */
    1193           0 :         if ((error = ext2fs_update(ip, 1)) != 0)
    1194             :                 goto bad;
    1195           0 :         error = ext2fs_direnter(ip, dvp, cnp);
    1196           0 :         if (error != 0)
    1197             :                 goto bad;
    1198           0 :         if ((cnp->cn_flags & SAVESTART) == 0)
    1199           0 :                 pool_put(&namei_pool, cnp->cn_pnbuf);
    1200           0 :         *vpp = tvp;
    1201           0 :         return (0);
    1202             : 
    1203             : bad:
    1204             :         /*
    1205             :          * Write error occurred trying to update the inode
    1206             :          * or the directory so must deallocate the inode.
    1207             :          */
    1208           0 :         pool_put(&namei_pool, cnp->cn_pnbuf);
    1209           0 :         ip->i_e2fs_nlink = 0;
    1210           0 :         ip->i_flag |= IN_CHANGE;
    1211           0 :         tvp->v_type = VNON;
    1212           0 :         vput(tvp);
    1213           0 :         return (error);
    1214           0 : }
    1215             : 
    1216             : /*
    1217             :  * Synch an open file.
    1218             :  */
    1219             : /* ARGSUSED */
    1220             : int
    1221           0 : ext2fs_fsync(void *v)
    1222             : {
    1223           0 :         struct vop_fsync_args *ap = v;
    1224           0 :         struct vnode *vp = ap->a_vp;
    1225             : 
    1226           0 :         vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
    1227           0 :         return (ext2fs_update(VTOI(ap->a_vp), ap->a_waitfor == MNT_WAIT));
    1228             : }
    1229             : 
    1230             : /*
    1231             :  * Reclaim an inode so that it can be used for other purposes.
    1232             :  */
    1233             : int
    1234           0 : ext2fs_reclaim(void *v)
    1235             : {
    1236           0 :         struct vop_reclaim_args *ap = v;
    1237           0 :         struct vnode *vp = ap->a_vp;
    1238             :         struct inode *ip;
    1239             : #ifdef DIAGNOSTIC
    1240             :         extern int prtactive;
    1241             : 
    1242           0 :         if (prtactive && vp->v_usecount != 0)
    1243           0 :                 vprint("ext2fs_reclaim: pushing active", vp);
    1244             : #endif
    1245             : 
    1246             :         /*
    1247             :          * Remove the inode from its hash chain.
    1248             :          */
    1249           0 :         ip = VTOI(vp);
    1250           0 :         ufs_ihashrem(ip);
    1251             : 
    1252             :         /*
    1253             :          * Purge old data structures associated with the inode.
    1254             :          */
    1255           0 :         cache_purge(vp);
    1256           0 :         if (ip->i_devvp)
    1257           0 :                 vrele(ip->i_devvp);
    1258             : 
    1259           0 :         if (ip->i_e2din != NULL)
    1260           0 :                 pool_put(&ext2fs_dinode_pool, ip->i_e2din);
    1261             : 
    1262           0 :         pool_put(&ext2fs_inode_pool, ip);
    1263             : 
    1264           0 :         vp->v_data = NULL;
    1265             : 
    1266           0 :         return (0);
    1267             : }
    1268             : 
    1269             : /* Global vfs data structures for ext2fs. */
    1270             : struct vops ext2fs_vops = {
    1271             :         .vop_lookup     = ext2fs_lookup,
    1272             :         .vop_create     = ext2fs_create,
    1273             :         .vop_mknod      = ext2fs_mknod,
    1274             :         .vop_open       = ext2fs_open,
    1275             :         .vop_close      = ufs_close,
    1276             :         .vop_access     = ext2fs_access,
    1277             :         .vop_getattr    = ext2fs_getattr,
    1278             :         .vop_setattr    = ext2fs_setattr,
    1279             :         .vop_read       = ext2fs_read,
    1280             :         .vop_write      = ext2fs_write,
    1281             :         .vop_ioctl      = ufs_ioctl,
    1282             :         .vop_poll       = ufs_poll,
    1283             :         .vop_kqfilter   = ufs_kqfilter,
    1284             :         .vop_fsync      = ext2fs_fsync,
    1285             :         .vop_remove     = ext2fs_remove,
    1286             :         .vop_link       = ext2fs_link,
    1287             :         .vop_rename     = ext2fs_rename,
    1288             :         .vop_mkdir      = ext2fs_mkdir,
    1289             :         .vop_rmdir      = ext2fs_rmdir,
    1290             :         .vop_symlink    = ext2fs_symlink,
    1291             :         .vop_readdir    = ext2fs_readdir,
    1292             :         .vop_readlink   = ext2fs_readlink,
    1293             :         .vop_abortop    = vop_generic_abortop,
    1294             :         .vop_inactive   = ext2fs_inactive,
    1295             :         .vop_reclaim    = ext2fs_reclaim,
    1296             :         .vop_lock       = ufs_lock,
    1297             :         .vop_unlock     = ufs_unlock,
    1298             :         .vop_bmap       = ext2fs_bmap,
    1299             :         .vop_strategy   = ufs_strategy,
    1300             :         .vop_print      = ufs_print,
    1301             :         .vop_islocked   = ufs_islocked,
    1302             :         .vop_pathconf   = ext2fs_pathconf,
    1303             :         .vop_advlock    = ext2fs_advlock,
    1304             :         .vop_bwrite     = vop_generic_bwrite
    1305             : };
    1306             : 
    1307             : struct vops ext2fs_specvops = {
    1308             :         .vop_close      = ufsspec_close,
    1309             :         .vop_access     = ext2fs_access,
    1310             :         .vop_getattr    = ext2fs_getattr,
    1311             :         .vop_setattr    = ext2fs_setattr,
    1312             :         .vop_read       = ufsspec_read,
    1313             :         .vop_write      = ufsspec_write,
    1314             :         .vop_fsync      = ext2fs_fsync,
    1315             :         .vop_inactive   = ext2fs_inactive,
    1316             :         .vop_reclaim    = ext2fs_reclaim,
    1317             :         .vop_lock       = ufs_lock,
    1318             :         .vop_unlock     = ufs_unlock,
    1319             :         .vop_print      = ufs_print,
    1320             :         .vop_islocked   = ufs_islocked,
    1321             : 
    1322             :         /* XXX: Keep in sync with spec_vops. */
    1323             :         .vop_lookup     = vop_generic_lookup,
    1324             :         .vop_create     = spec_badop,
    1325             :         .vop_mknod      = spec_badop,
    1326             :         .vop_open       = spec_open,
    1327             :         .vop_ioctl      = spec_ioctl,
    1328             :         .vop_poll       = spec_poll,
    1329             :         .vop_kqfilter   = spec_kqfilter,
    1330             :         .vop_revoke     = vop_generic_revoke,
    1331             :         .vop_remove     = spec_badop,
    1332             :         .vop_link       = spec_badop,
    1333             :         .vop_rename     = spec_badop,
    1334             :         .vop_mkdir      = spec_badop,
    1335             :         .vop_rmdir      = spec_badop,
    1336             :         .vop_symlink    = spec_badop,
    1337             :         .vop_readdir    = spec_badop,
    1338             :         .vop_readlink   = spec_badop,
    1339             :         .vop_abortop    = spec_badop,
    1340             :         .vop_bmap       = vop_generic_bmap,
    1341             :         .vop_strategy   = spec_strategy,
    1342             :         .vop_pathconf   = spec_pathconf,
    1343             :         .vop_advlock    = spec_advlock,
    1344             :         .vop_bwrite     = vop_generic_bwrite,
    1345             : };
    1346             : 
    1347             : #ifdef FIFO
    1348             : struct vops ext2fs_fifovops = {
    1349             :         .vop_close      = ufsfifo_close,
    1350             :         .vop_access     = ufsfifo_close,
    1351             :         .vop_getattr    = ext2fs_getattr,
    1352             :         .vop_setattr    = ext2fs_setattr,
    1353             :         .vop_read       = ufsfifo_read,
    1354             :         .vop_write      = ufsfifo_write,
    1355             :         .vop_fsync      = ext2fs_fsync,
    1356             :         .vop_inactive   = ext2fs_inactive,
    1357             :         .vop_reclaim    = ext2fsfifo_reclaim,
    1358             :         .vop_lock       = ufs_lock,
    1359             :         .vop_unlock     = ufs_unlock,
    1360             :         .vop_print      = ufs_print,
    1361             :         .vop_islocked   = ufs_islocked,
    1362             :         .vop_bwrite     = vop_generic_bwrite,
    1363             : 
    1364             :         /* XXX: Keep in sync with fifo_vops */
    1365             :         .vop_lookup     = vop_generic_lookup,
    1366             :         .vop_create     = fifo_badop,
    1367             :         .vop_mknod      = fifo_badop,
    1368             :         .vop_open       = fifo_open,
    1369             :         .vop_ioctl      = fifo_ioctl,
    1370             :         .vop_poll       = fifo_poll,
    1371             :         .vop_kqfilter   = fifo_kqfilter,
    1372             :         .vop_revoke     = vop_generic_revoke,
    1373             :         .vop_remove     = fifo_badop,
    1374             :         .vop_link       = fifo_badop,
    1375             :         .vop_rename     = fifo_badop,
    1376             :         .vop_mkdir      = fifo_badop,
    1377             :         .vop_rmdir      = fifo_badop,
    1378             :         .vop_symlink    = fifo_badop,
    1379             :         .vop_readdir    = fifo_badop,
    1380             :         .vop_readlink   = fifo_badop,
    1381             :         .vop_abortop    = fifo_badop,
    1382             :         .vop_bmap       = vop_generic_bmap,
    1383             :         .vop_strategy   = fifo_badop,
    1384             :         .vop_pathconf   = fifo_pathconf,
    1385             :         .vop_advlock    = fifo_advlock,
    1386             : };
    1387             : 
    1388             : int
    1389           0 : ext2fsfifo_reclaim(void *v)
    1390             : {
    1391           0 :         fifo_reclaim(v);
    1392           0 :         return (ext2fs_reclaim(v));
    1393             : }
    1394             : #endif /* FIFO */

Generated by: LCOV version 1.13