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/ext2fs/ext2_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 /*-
    2  *  modified for EXT2FS support in Lites 1.1
    3  *
    4  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
    5  *  University of Utah, Department of Computer Science
    6  */
    7 /*-
    8  * Copyright (c) 1982, 1986, 1989, 1993
    9  *      The Regents of the University of California.  All rights reserved.
   10  * (c) UNIX System Laboratories, Inc.
   11  * All or some portions of this file are derived from material licensed
   12  * to the University of California by American Telephone and Telegraph
   13  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
   14  * the permission of UNIX System Laboratories, Inc.
   15  *
   16  * Redistribution and use in source and binary forms, with or without
   17  * modification, are permitted provided that the following conditions
   18  * are met:
   19  * 1. Redistributions of source code must retain the above copyright
   20  *    notice, this list of conditions and the following disclaimer.
   21  * 2. Redistributions in binary form must reproduce the above copyright
   22  *    notice, this list of conditions and the following disclaimer in the
   23  *    documentation and/or other materials provided with the distribution.
   24  * 4. Neither the name of the University nor the names of its contributors
   25  *    may be used to endorse or promote products derived from this software
   26  *    without specific prior written permission.
   27  *
   28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   38  * SUCH DAMAGE.
   39  *
   40  *      @(#)ufs_vnops.c 8.7 (Berkeley) 2/3/94
   41  *      @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
   42  * $FreeBSD: releng/9.0/sys/fs/ext2fs/ext2_vnops.c 221166 2011-04-28 14:27:17Z jhb $
   43  */
   44 
   45 #include "opt_suiddir.h"
   46 
   47 #include <sys/param.h>
   48 #include <sys/systm.h>
   49 #include <sys/kernel.h>
   50 #include <sys/fcntl.h>
   51 #include <sys/stat.h>
   52 #include <sys/bio.h>
   53 #include <sys/buf.h>
   54 #include <sys/endian.h>
   55 #include <sys/priv.h>
   56 #include <sys/mount.h>
   57 #include <sys/unistd.h>
   58 #include <sys/time.h>
   59 #include <sys/vnode.h>
   60 #include <sys/namei.h>
   61 #include <sys/lockf.h>
   62 #include <sys/event.h>
   63 #include <sys/conf.h>
   64 #include <sys/file.h>
   65 
   66 #include <vm/vm.h>
   67 #include <vm/vm_extern.h>
   68 #include <vm/vnode_pager.h>
   69 
   70 #include <fs/fifofs/fifo.h>
   71 
   72 #include <ufs/ufs/dir.h>
   73 
   74 #include <fs/ext2fs/fs.h>
   75 #include <fs/ext2fs/inode.h>
   76 #include <fs/ext2fs/ext2_extern.h>
   77 #include <fs/ext2fs/ext2fs.h>
   78 #include <fs/ext2fs/ext2_dinode.h>
   79 #include <fs/ext2fs/ext2_dir.h>
   80 #include <fs/ext2fs/ext2_mount.h>
   81 
   82 static int ext2_makeinode(int mode, struct vnode *, struct vnode **, struct componentname *);
   83 static void ext2_itimes_locked(struct vnode *);
   84 
   85 static vop_access_t     ext2_access;
   86 static int ext2_chmod(struct vnode *, int, struct ucred *, struct thread *);
   87 static int ext2_chown(struct vnode *, uid_t, gid_t, struct ucred *,
   88     struct thread *);
   89 static vop_close_t      ext2_close;
   90 static vop_create_t     ext2_create;
   91 static vop_fsync_t      ext2_fsync;
   92 static vop_getattr_t    ext2_getattr;
   93 static vop_link_t       ext2_link;
   94 static vop_mkdir_t      ext2_mkdir;
   95 static vop_mknod_t      ext2_mknod;
   96 static vop_open_t       ext2_open;
   97 static vop_pathconf_t   ext2_pathconf;
   98 static vop_print_t      ext2_print;
   99 static vop_read_t       ext2_read;
  100 static vop_readlink_t   ext2_readlink;
  101 static vop_remove_t     ext2_remove;
  102 static vop_rename_t     ext2_rename;
  103 static vop_rmdir_t      ext2_rmdir;
  104 static vop_setattr_t    ext2_setattr;
  105 static vop_strategy_t   ext2_strategy;
  106 static vop_symlink_t    ext2_symlink;
  107 static vop_write_t      ext2_write;
  108 static vop_vptofh_t     ext2_vptofh;
  109 static vop_close_t      ext2fifo_close;
  110 static vop_kqfilter_t   ext2fifo_kqfilter;
  111 
  112 /* Global vfs data structures for ext2. */
  113 struct vop_vector ext2_vnodeops = {
  114         .vop_default =          &default_vnodeops,
  115         .vop_access =           ext2_access,
  116         .vop_bmap =             ext2_bmap,
  117         .vop_cachedlookup =     ext2_lookup,
  118         .vop_close =            ext2_close,
  119         .vop_create =           ext2_create,
  120         .vop_fsync =            ext2_fsync,
  121         .vop_getattr =          ext2_getattr,
  122         .vop_inactive =         ext2_inactive,
  123         .vop_link =             ext2_link,
  124         .vop_lookup =           vfs_cache_lookup,
  125         .vop_mkdir =            ext2_mkdir,
  126         .vop_mknod =            ext2_mknod,
  127         .vop_open =             ext2_open,
  128         .vop_pathconf =         ext2_pathconf,
  129         .vop_poll =             vop_stdpoll,
  130         .vop_print =            ext2_print,
  131         .vop_read =             ext2_read,
  132         .vop_readdir =          ext2_readdir,
  133         .vop_readlink =         ext2_readlink,
  134         .vop_reallocblks =      ext2_reallocblks,
  135         .vop_reclaim =          ext2_reclaim,
  136         .vop_remove =           ext2_remove,
  137         .vop_rename =           ext2_rename,
  138         .vop_rmdir =            ext2_rmdir,
  139         .vop_setattr =          ext2_setattr,
  140         .vop_strategy =         ext2_strategy,
  141         .vop_symlink =          ext2_symlink,
  142         .vop_write =            ext2_write,
  143         .vop_vptofh =           ext2_vptofh,
  144 };
  145 
  146 struct vop_vector ext2_fifoops = {
  147         .vop_default =          &fifo_specops,
  148         .vop_access =           ext2_access,
  149         .vop_close =            ext2fifo_close,
  150         .vop_fsync =            ext2_fsync,
  151         .vop_getattr =          ext2_getattr,
  152         .vop_inactive =         ext2_inactive,
  153         .vop_kqfilter =         ext2fifo_kqfilter,
  154         .vop_print =            ext2_print,
  155         .vop_read =             VOP_PANIC,
  156         .vop_reclaim =          ext2_reclaim,
  157         .vop_setattr =          ext2_setattr,
  158         .vop_write =            VOP_PANIC,
  159         .vop_vptofh =           ext2_vptofh,
  160 };
  161 
  162 #include <fs/ext2fs/ext2_readwrite.c>
  163 
  164 /*
  165  * A virgin directory (no blushing please).
  166  * Note that the type and namlen fields are reversed relative to ext2.
  167  * Also, we don't use `struct odirtemplate', since it would just cause
  168  * endianness problems.
  169  */
  170 static struct dirtemplate mastertemplate = {
  171         0, 12, 1, EXT2_FT_DIR, ".",
  172         0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
  173 };
  174 static struct dirtemplate omastertemplate = {
  175         0, 12, 1, EXT2_FT_UNKNOWN, ".",
  176         0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".."
  177 };
  178 
  179 static void
  180 ext2_itimes_locked(struct vnode *vp)
  181 {
  182         struct inode *ip;
  183         struct timespec ts;
  184 
  185         ASSERT_VI_LOCKED(vp, __func__); 
  186 
  187         ip = VTOI(vp);
  188         if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
  189                 return;
  190         if ((vp->v_type == VBLK || vp->v_type == VCHR))
  191                 ip->i_flag |= IN_LAZYMOD;
  192         else
  193                 ip->i_flag |= IN_MODIFIED;
  194         if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
  195                 vfs_timestamp(&ts);
  196                 if (ip->i_flag & IN_ACCESS) {
  197                         ip->i_atime = ts.tv_sec;
  198                         ip->i_atimensec = ts.tv_nsec;
  199                 }
  200                 if (ip->i_flag & IN_UPDATE) {
  201                         ip->i_mtime = ts.tv_sec;
  202                         ip->i_mtimensec = ts.tv_nsec;
  203                         ip->i_modrev++;
  204                 }
  205                 if (ip->i_flag & IN_CHANGE) {
  206                         ip->i_ctime = ts.tv_sec;
  207                         ip->i_ctimensec = ts.tv_nsec;
  208                 }
  209         }
  210         ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
  211 }
  212 
  213 void
  214 ext2_itimes(struct vnode *vp)
  215 {
  216 
  217         VI_LOCK(vp);
  218         ext2_itimes_locked(vp);
  219         VI_UNLOCK(vp);
  220 }
  221 
  222 /*
  223  * Create a regular file
  224  */
  225 static int
  226 ext2_create(ap)
  227         struct vop_create_args /* {
  228                 struct vnode *a_dvp;
  229                 struct vnode **a_vpp;
  230                 struct componentname *a_cnp;
  231                 struct vattr *a_vap;
  232         } */ *ap;
  233 {
  234         int error;
  235 
  236         error =
  237             ext2_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
  238             ap->a_dvp, ap->a_vpp, ap->a_cnp);
  239         if (error)
  240                 return (error);
  241         return (0);
  242 }
  243 
  244 static int
  245 ext2_open(ap)
  246         struct vop_open_args /* {
  247                 struct vnode *a_vp;
  248                 int  a_mode;
  249                 struct ucred *a_cred;
  250                 struct thread *a_td;
  251         } */ *ap;
  252 {
  253 
  254         if (ap->a_vp->v_type == VBLK || ap->a_vp->v_type == VCHR)
  255                 return (EOPNOTSUPP);
  256 
  257         /*
  258          * Files marked append-only must be opened for appending.
  259          */
  260         if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
  261             (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
  262                 return (EPERM);
  263 
  264         vnode_create_vobject(ap->a_vp, VTOI(ap->a_vp)->i_size, ap->a_td);
  265 
  266         return (0);
  267 }
  268 
  269 /*
  270  * Close called.
  271  *
  272  * Update the times on the inode.
  273  */
  274 static int
  275 ext2_close(ap)
  276         struct vop_close_args /* {
  277                 struct vnode *a_vp;
  278                 int  a_fflag;
  279                 struct ucred *a_cred;
  280                 struct thread *a_td;
  281         } */ *ap;
  282 {
  283         struct vnode *vp = ap->a_vp;
  284 
  285         VI_LOCK(vp);
  286         if (vp->v_usecount > 1)
  287                 ext2_itimes_locked(vp);
  288         VI_UNLOCK(vp);
  289         return (0);
  290 }
  291 
  292 static int
  293 ext2_access(ap)
  294         struct vop_access_args /* {
  295                 struct vnode *a_vp;
  296                 accmode_t a_accmode;
  297                 struct ucred *a_cred;
  298                 struct thread *a_td;
  299         } */ *ap;
  300 {
  301         struct vnode *vp = ap->a_vp;
  302         struct inode *ip = VTOI(vp);
  303         accmode_t accmode = ap->a_accmode;
  304         int error;
  305 
  306         if (vp->v_type == VBLK || vp->v_type == VCHR)
  307                 return (EOPNOTSUPP);
  308 
  309         /*
  310          * Disallow write attempts on read-only file systems;
  311          * unless the file is a socket, fifo, or a block or
  312          * character device resident on the file system.
  313          */
  314         if (accmode & VWRITE) {
  315                 switch (vp->v_type) {
  316                 case VDIR:
  317                 case VLNK:
  318                 case VREG:
  319                         if (vp->v_mount->mnt_flag & MNT_RDONLY)
  320                                 return (EROFS);
  321                         break;
  322                 default:
  323                         break;
  324                 }
  325         }
  326 
  327         /* If immutable bit set, nobody gets to write it. */
  328         if ((accmode & VWRITE) && (ip->i_flags & (SF_IMMUTABLE | SF_SNAPSHOT)))
  329                 return (EPERM);
  330 
  331         error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
  332             ap->a_accmode, ap->a_cred, NULL);
  333         return (error);
  334 }
  335 
  336 static int
  337 ext2_getattr(ap)
  338         struct vop_getattr_args /* {
  339                 struct vnode *a_vp;
  340                 struct vattr *a_vap;
  341                 struct ucred *a_cred;
  342         } */ *ap;
  343 {
  344         struct vnode *vp = ap->a_vp;
  345         struct inode *ip = VTOI(vp);
  346         struct vattr *vap = ap->a_vap;
  347 
  348         ext2_itimes(vp);
  349         /*
  350          * Copy from inode table
  351          */
  352         vap->va_fsid = dev2udev(ip->i_devvp->v_rdev);
  353         vap->va_fileid = ip->i_number;
  354         vap->va_mode = ip->i_mode & ~IFMT;
  355         vap->va_nlink = ip->i_nlink;
  356         vap->va_uid = ip->i_uid;
  357         vap->va_gid = ip->i_gid;
  358         vap->va_rdev = ip->i_rdev;
  359         vap->va_size = ip->i_size;
  360         vap->va_atime.tv_sec = ip->i_atime;
  361         vap->va_atime.tv_nsec = ip->i_atimensec;
  362         vap->va_mtime.tv_sec = ip->i_mtime;
  363         vap->va_mtime.tv_nsec = ip->i_mtimensec;
  364         vap->va_ctime.tv_sec = ip->i_ctime;
  365         vap->va_ctime.tv_nsec = ip->i_ctimensec;
  366         vap->va_flags = ip->i_flags;
  367         vap->va_gen = ip->i_gen;
  368         vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
  369         vap->va_bytes = dbtob((u_quad_t)ip->i_blocks);
  370         vap->va_type = IFTOVT(ip->i_mode);
  371         vap->va_filerev = ip->i_modrev;
  372         return (0);
  373 }
  374 
  375 /*
  376  * Set attribute vnode op. called from several syscalls
  377  */
  378 static int
  379 ext2_setattr(ap)
  380         struct vop_setattr_args /* {
  381                 struct vnode *a_vp;
  382                 struct vattr *a_vap;
  383                 struct ucred *a_cred;
  384         } */ *ap;
  385 {
  386         struct vattr *vap = ap->a_vap;
  387         struct vnode *vp = ap->a_vp;
  388         struct inode *ip = VTOI(vp);
  389         struct ucred *cred = ap->a_cred;
  390         struct thread *td = curthread;
  391         int error;
  392 
  393         /*
  394          * Check for unsettable attributes.
  395          */
  396         if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
  397             (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
  398             (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
  399             ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
  400                 return (EINVAL);
  401         }
  402         if (vap->va_flags != VNOVAL) {
  403                 /* Disallow flags not supported by ext2fs. */
  404                 if(vap->va_flags & ~(SF_APPEND | SF_IMMUTABLE | UF_NODUMP))
  405                         return (EOPNOTSUPP);
  406 
  407                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
  408                         return (EROFS);
  409                 /*
  410                  * Callers may only modify the file flags on objects they
  411                  * have VADMIN rights for.
  412                  */
  413                 if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
  414                         return (error);
  415                 /*
  416                  * Unprivileged processes and privileged processes in
  417                  * jail() are not permitted to unset system flags, or
  418                  * modify flags if any system flags are set.
  419                  * Privileged non-jail processes may not modify system flags
  420                  * if securelevel > 0 and any existing system flags are set.
  421                  */
  422                 if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
  423                         if (ip->i_flags
  424                             & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
  425                                 error = securelevel_gt(cred, 0);
  426                                 if (error)
  427                                         return (error);
  428                         }
  429                         ip->i_flags = vap->va_flags;
  430                 } else {
  431                         if (ip->i_flags
  432                             & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
  433                             (vap->va_flags & UF_SETTABLE) != vap->va_flags)
  434                                 return (EPERM);
  435                         ip->i_flags &= SF_SETTABLE;
  436                         ip->i_flags |= (vap->va_flags & UF_SETTABLE);
  437                 }
  438                 ip->i_flag |= IN_CHANGE;
  439                 if (vap->va_flags & (IMMUTABLE | APPEND))
  440                         return (0);
  441         }
  442         if (ip->i_flags & (IMMUTABLE | APPEND))
  443                 return (EPERM);
  444         /*
  445          * Go through the fields and update iff not VNOVAL.
  446          */
  447         if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
  448                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
  449                         return (EROFS);
  450                 if ((error = ext2_chown(vp, vap->va_uid, vap->va_gid, cred,
  451                     td)) != 0)
  452                         return (error);
  453         }
  454         if (vap->va_size != VNOVAL) {
  455                 /*
  456                  * Disallow write attempts on read-only file systems;
  457                  * unless the file is a socket, fifo, or a block or
  458                  * character device resident on the file system.
  459                  */
  460                 switch (vp->v_type) {
  461                 case VDIR:
  462                         return (EISDIR);
  463                 case VLNK:
  464                 case VREG:
  465                         if (vp->v_mount->mnt_flag & MNT_RDONLY)
  466                                 return (EROFS);
  467                         break;
  468                 default:
  469                         break;
  470                 }
  471                 if ((error = ext2_truncate(vp, vap->va_size, 0, cred, td)) != 0)
  472                         return (error);
  473         }
  474         if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
  475                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
  476                         return (EROFS);
  477                 /*
  478                  * From utimes(2):
  479                  * If times is NULL, ... The caller must be the owner of
  480                  * the file, have permission to write the file, or be the
  481                  * super-user.
  482                  * If times is non-NULL, ... The caller must be the owner of
  483                  * the file or be the super-user.
  484                  */
  485                 if ((error = VOP_ACCESS(vp, VADMIN, cred, td)) &&
  486                     ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
  487                     (error = VOP_ACCESS(vp, VWRITE, cred, td))))
  488                         return (error);
  489                 if (vap->va_atime.tv_sec != VNOVAL)
  490                         ip->i_flag |= IN_ACCESS;
  491                 if (vap->va_mtime.tv_sec != VNOVAL)
  492                         ip->i_flag |= IN_CHANGE | IN_UPDATE;
  493                 ext2_itimes(vp);
  494                 if (vap->va_atime.tv_sec != VNOVAL) {
  495                         ip->i_atime = vap->va_atime.tv_sec;
  496                         ip->i_atimensec = vap->va_atime.tv_nsec;
  497                 }
  498                 if (vap->va_mtime.tv_sec != VNOVAL) {
  499                         ip->i_mtime = vap->va_mtime.tv_sec;
  500                         ip->i_mtimensec = vap->va_mtime.tv_nsec;
  501                 }
  502                 error = ext2_update(vp, 0);
  503                 if (error)
  504                         return (error);
  505         }
  506         error = 0;
  507         if (vap->va_mode != (mode_t)VNOVAL) {
  508                 if (vp->v_mount->mnt_flag & MNT_RDONLY)
  509                         return (EROFS);
  510                 error = ext2_chmod(vp, (int)vap->va_mode, cred, td);
  511         }
  512         return (error);
  513 }
  514 
  515 /*
  516  * Change the mode on a file.
  517  * Inode must be locked before calling.
  518  */
  519 static int
  520 ext2_chmod(vp, mode, cred, td)
  521         struct vnode *vp;
  522         int mode;
  523         struct ucred *cred;
  524         struct thread *td;
  525 {
  526         struct inode *ip = VTOI(vp);
  527         int error;
  528 
  529         /*
  530          * To modify the permissions on a file, must possess VADMIN
  531          * for that file.
  532          */
  533         if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
  534                 return (error);
  535         /*
  536          * Privileged processes may set the sticky bit on non-directories,
  537          * as well as set the setgid bit on a file with a group that the
  538          * process is not a member of.
  539          */
  540         if (vp->v_type != VDIR && (mode & S_ISTXT)) {
  541                 error = priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0);
  542                 if (error)
  543                         return (EFTYPE);
  544         }
  545         if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) {
  546                 error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
  547                 if (error)
  548                         return (error);
  549         }
  550         ip->i_mode &= ~ALLPERMS;
  551         ip->i_mode |= (mode & ALLPERMS);
  552         ip->i_flag |= IN_CHANGE;
  553         return (0);
  554 }
  555 
  556 /*
  557  * Perform chown operation on inode ip;
  558  * inode must be locked prior to call.
  559  */
  560 static int
  561 ext2_chown(vp, uid, gid, cred, td)
  562         struct vnode *vp;
  563         uid_t uid;
  564         gid_t gid;
  565         struct ucred *cred;
  566         struct thread *td;
  567 {
  568         struct inode *ip = VTOI(vp);
  569         uid_t ouid;
  570         gid_t ogid;
  571         int error = 0;
  572 
  573         if (uid == (uid_t)VNOVAL)
  574                 uid = ip->i_uid;
  575         if (gid == (gid_t)VNOVAL)
  576                 gid = ip->i_gid;
  577         /*
  578          * To modify the ownership of a file, must possess VADMIN
  579          * for that file.
  580          */
  581         if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
  582                 return (error);
  583         /*
  584          * To change the owner of a file, or change the group of a file
  585          * to a group of which we are not a member, the caller must
  586          * have privilege.
  587          */
  588         if (uid != ip->i_uid || (gid != ip->i_gid &&
  589             !groupmember(gid, cred))) {
  590                 error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
  591                 if (error)
  592                         return (error);
  593         }
  594         ogid = ip->i_gid;
  595         ouid = ip->i_uid;
  596         ip->i_gid = gid;
  597         ip->i_uid = uid;
  598         ip->i_flag |= IN_CHANGE;
  599         if ((ip->i_mode & (ISUID | ISGID)) && (ouid != uid || ogid != gid)) {
  600                 if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0) != 0)
  601                         ip->i_mode &= ~(ISUID | ISGID);
  602         }
  603         return (0);
  604 }
  605 
  606 /*
  607  * Synch an open file.
  608  */
  609 /* ARGSUSED */
  610 static int
  611 ext2_fsync(ap)
  612         struct vop_fsync_args /* {
  613                 struct vnode *a_vp;
  614                 struct ucred *a_cred;
  615                 int a_waitfor;
  616                 struct thread *a_td;
  617         } */ *ap;
  618 {
  619         /*
  620          * Flush all dirty buffers associated with a vnode.
  621          */
  622 
  623         vop_stdfsync(ap);
  624 
  625         return (ext2_update(ap->a_vp, ap->a_waitfor == MNT_WAIT));
  626 }
  627 
  628 /*
  629  * Mknod vnode call
  630  */
  631 /* ARGSUSED */
  632 static int
  633 ext2_mknod(ap)
  634         struct vop_mknod_args /* {
  635                 struct vnode *a_dvp;
  636                 struct vnode **a_vpp;
  637                 struct componentname *a_cnp;
  638                 struct vattr *a_vap;
  639         } */ *ap;
  640 {
  641         struct vattr *vap = ap->a_vap;
  642         struct vnode **vpp = ap->a_vpp;
  643         struct inode *ip;
  644         ino_t ino;
  645         int error;
  646 
  647         error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
  648             ap->a_dvp, vpp, ap->a_cnp);
  649         if (error)
  650                 return (error);
  651         ip = VTOI(*vpp);
  652         ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
  653         if (vap->va_rdev != VNOVAL) {
  654                 /*
  655                  * Want to be able to use this to make badblock
  656                  * inodes, so don't truncate the dev number.
  657                  */
  658                 ip->i_rdev = vap->va_rdev;
  659         }
  660         /*
  661          * Remove inode, then reload it through VFS_VGET so it is
  662          * checked to see if it is an alias of an existing entry in
  663          * the inode cache.      XXX I don't believe this is necessary now.
  664          */
  665         (*vpp)->v_type = VNON;
  666         ino = ip->i_number;     /* Save this before vgone() invalidates ip. */
  667         vgone(*vpp);
  668         vput(*vpp);
  669         error = VFS_VGET(ap->a_dvp->v_mount, ino, LK_EXCLUSIVE, vpp);
  670         if (error) {
  671                 *vpp = NULL;
  672                 return (error);
  673         }
  674         return (0);
  675 }
  676 
  677 static int
  678 ext2_remove(ap)
  679         struct vop_remove_args /* {
  680                 struct vnode *a_dvp;
  681                 struct vnode *a_vp;
  682                 struct componentname *a_cnp;
  683         } */ *ap;
  684 {
  685         struct inode *ip;
  686         struct vnode *vp = ap->a_vp;
  687         struct vnode *dvp = ap->a_dvp;
  688         int error;
  689 
  690         ip = VTOI(vp);
  691         if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
  692             (VTOI(dvp)->i_flags & APPEND)) {
  693                 error = EPERM;
  694                 goto out;
  695         }
  696         error = ext2_dirremove(dvp, ap->a_cnp);
  697         if (error == 0) {
  698                 ip->i_nlink--;
  699                 ip->i_flag |= IN_CHANGE;
  700         }
  701 out:
  702         return (error);
  703 }
  704 
  705 /*
  706  * link vnode call
  707  */
  708 static int
  709 ext2_link(ap)
  710         struct vop_link_args /* {
  711                 struct vnode *a_tdvp;
  712                 struct vnode *a_vp;
  713                 struct componentname *a_cnp;
  714         } */ *ap;
  715 {
  716         struct vnode *vp = ap->a_vp;
  717         struct vnode *tdvp = ap->a_tdvp;
  718         struct componentname *cnp = ap->a_cnp;
  719         struct inode *ip;
  720         int error;
  721 
  722 #ifdef DIAGNOSTIC
  723         if ((cnp->cn_flags & HASBUF) == 0)
  724                 panic("ext2_link: no name");
  725 #endif
  726         if (tdvp->v_mount != vp->v_mount) {
  727                 error = EXDEV;
  728                 goto out;
  729         }
  730         ip = VTOI(vp);
  731         if ((nlink_t)ip->i_nlink >= LINK_MAX) {
  732                 error = EMLINK;
  733                 goto out;
  734         }
  735         if (ip->i_flags & (IMMUTABLE | APPEND)) {
  736                 error = EPERM;
  737                 goto out;
  738         }
  739         ip->i_nlink++;
  740         ip->i_flag |= IN_CHANGE;
  741         error = ext2_update(vp, !DOINGASYNC(vp));
  742         if (!error)
  743                 error = ext2_direnter(ip, tdvp, cnp);
  744         if (error) {
  745                 ip->i_nlink--;
  746                 ip->i_flag |= IN_CHANGE;
  747         }
  748 out:
  749         return (error);
  750 }
  751 
  752 /*
  753  * Rename system call.
  754  *      rename("foo", "bar");
  755  * is essentially
  756  *      unlink("bar");
  757  *      link("foo", "bar");
  758  *      unlink("foo");
  759  * but ``atomically''.  Can't do full commit without saving state in the
  760  * inode on disk which isn't feasible at this time.  Best we can do is
  761  * always guarantee the target exists.
  762  *
  763  * Basic algorithm is:
  764  *
  765  * 1) Bump link count on source while we're linking it to the
  766  *    target.  This also ensure the inode won't be deleted out
  767  *    from underneath us while we work (it may be truncated by
  768  *    a concurrent `trunc' or `open' for creation).
  769  * 2) Link source to destination.  If destination already exists,
  770  *    delete it first.
  771  * 3) Unlink source reference to inode if still around. If a
  772  *    directory was moved and the parent of the destination
  773  *    is different from the source, patch the ".." entry in the
  774  *    directory.
  775  */
  776 static int
  777 ext2_rename(ap)
  778         struct vop_rename_args  /* {
  779                 struct vnode *a_fdvp;
  780                 struct vnode *a_fvp;
  781                 struct componentname *a_fcnp;
  782                 struct vnode *a_tdvp;
  783                 struct vnode *a_tvp;
  784                 struct componentname *a_tcnp;
  785         } */ *ap;
  786 {
  787         struct vnode *tvp = ap->a_tvp;
  788         struct vnode *tdvp = ap->a_tdvp;
  789         struct vnode *fvp = ap->a_fvp;
  790         struct vnode *fdvp = ap->a_fdvp;
  791         struct componentname *tcnp = ap->a_tcnp;
  792         struct componentname *fcnp = ap->a_fcnp;
  793         struct inode *ip, *xp, *dp;
  794         struct dirtemplate dirbuf;
  795         int doingdirectory = 0, oldparent = 0, newparent = 0;
  796         int error = 0;
  797         u_char namlen;
  798 
  799 #ifdef DIAGNOSTIC
  800         if ((tcnp->cn_flags & HASBUF) == 0 ||
  801             (fcnp->cn_flags & HASBUF) == 0)
  802                 panic("ext2_rename: no name");
  803 #endif
  804         /*
  805          * Check for cross-device rename.
  806          */
  807         if ((fvp->v_mount != tdvp->v_mount) ||
  808             (tvp && (fvp->v_mount != tvp->v_mount))) {
  809                 error = EXDEV;
  810 abortit:
  811                 if (tdvp == tvp)
  812                         vrele(tdvp);
  813                 else
  814                         vput(tdvp);
  815                 if (tvp)
  816                         vput(tvp);
  817                 vrele(fdvp);
  818                 vrele(fvp);
  819                 return (error);
  820         }
  821 
  822         if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
  823             (VTOI(tdvp)->i_flags & APPEND))) {
  824                 error = EPERM;
  825                 goto abortit;
  826         }
  827 
  828         /*
  829          * Renaming a file to itself has no effect.  The upper layers should
  830          * not call us in that case.  Temporarily just warn if they do.
  831          */
  832         if (fvp == tvp) {
  833                 printf("ext2_rename: fvp == tvp (can't happen)\n");
  834                 error = 0;
  835                 goto abortit;
  836         }
  837 
  838         if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
  839                 goto abortit;
  840         dp = VTOI(fdvp);
  841         ip = VTOI(fvp);
  842         if (ip->i_nlink >= LINK_MAX) {
  843                 VOP_UNLOCK(fvp, 0);
  844                 error = EMLINK;
  845                 goto abortit;
  846         }
  847         if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
  848             || (dp->i_flags & APPEND)) {
  849                 VOP_UNLOCK(fvp, 0);
  850                 error = EPERM;
  851                 goto abortit;
  852         }
  853         if ((ip->i_mode & IFMT) == IFDIR) {
  854                 /*
  855                  * Avoid ".", "..", and aliases of "." for obvious reasons.
  856                  */
  857                 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
  858                     dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT ||
  859                     (ip->i_flag & IN_RENAME)) {
  860                         VOP_UNLOCK(fvp, 0);
  861                         error = EINVAL;
  862                         goto abortit;
  863                 }
  864                 ip->i_flag |= IN_RENAME;
  865                 oldparent = dp->i_number;
  866                 doingdirectory++;
  867         }
  868         vrele(fdvp);
  869 
  870         /*
  871          * When the target exists, both the directory
  872          * and target vnodes are returned locked.
  873          */
  874         dp = VTOI(tdvp);
  875         xp = NULL;
  876         if (tvp)
  877                 xp = VTOI(tvp);
  878 
  879         /*
  880          * 1) Bump link count while we're moving stuff
  881          *    around.  If we crash somewhere before
  882          *    completing our work, the link count
  883          *    may be wrong, but correctable.
  884          */
  885         ip->i_nlink++;
  886         ip->i_flag |= IN_CHANGE;
  887         if ((error = ext2_update(fvp, !DOINGASYNC(fvp))) != 0) {
  888                 VOP_UNLOCK(fvp, 0);
  889                 goto bad;
  890         }
  891 
  892         /*
  893          * If ".." must be changed (ie the directory gets a new
  894          * parent) then the source directory must not be in the
  895          * directory hierarchy above the target, as this would
  896          * orphan everything below the source directory. Also
  897          * the user must have write permission in the source so
  898          * as to be able to change "..". We must repeat the call
  899          * to namei, as the parent directory is unlocked by the
  900          * call to checkpath().
  901          */
  902         error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
  903         VOP_UNLOCK(fvp, 0);
  904         if (oldparent != dp->i_number)
  905                 newparent = dp->i_number;
  906         if (doingdirectory && newparent) {
  907                 if (error)      /* write access check above */
  908                         goto bad;
  909                 if (xp != NULL)
  910                         vput(tvp);
  911                 error = ext2_checkpath(ip, dp, tcnp->cn_cred);
  912                 if (error)
  913                         goto out;
  914                 VREF(tdvp);
  915                 error = relookup(tdvp, &tvp, tcnp);
  916                 if (error)
  917                         goto out;
  918                 vrele(tdvp);
  919                 dp = VTOI(tdvp);
  920                 xp = NULL;
  921                 if (tvp)
  922                         xp = VTOI(tvp);
  923         }
  924         /*
  925          * 2) If target doesn't exist, link the target
  926          *    to the source and unlink the source.
  927          *    Otherwise, rewrite the target directory
  928          *    entry to reference the source inode and
  929          *    expunge the original entry's existence.
  930          */
  931         if (xp == NULL) {
  932                 if (dp->i_devvp != ip->i_devvp)
  933                         panic("ext2_rename: EXDEV");
  934                 /*
  935                  * Account for ".." in new directory.
  936                  * When source and destination have the same
  937                  * parent we don't fool with the link count.
  938                  */
  939                 if (doingdirectory && newparent) {
  940                         if ((nlink_t)dp->i_nlink >= LINK_MAX) {
  941                                 error = EMLINK;
  942                                 goto bad;
  943                         }
  944                         dp->i_nlink++;
  945                         dp->i_flag |= IN_CHANGE;
  946                         error = ext2_update(tdvp, !DOINGASYNC(tdvp));
  947                         if (error)
  948                                 goto bad;
  949                 }
  950                 error = ext2_direnter(ip, tdvp, tcnp);
  951                 if (error) {
  952                         if (doingdirectory && newparent) {
  953                                 dp->i_nlink--;
  954                                 dp->i_flag |= IN_CHANGE;
  955                                 (void)ext2_update(tdvp, 1);
  956                         }
  957                         goto bad;
  958                 }
  959                 vput(tdvp);
  960         } else {
  961                 if (xp->i_devvp != dp->i_devvp || xp->i_devvp != ip->i_devvp)
  962                        panic("ext2_rename: EXDEV");
  963                 /*
  964                  * Short circuit rename(foo, foo).
  965                  */
  966                 if (xp->i_number == ip->i_number)
  967                         panic("ext2_rename: same file");
  968                 /*
  969                  * If the parent directory is "sticky", then the user must
  970                  * own the parent directory, or the destination of the rename,
  971                  * otherwise the destination may not be changed (except by
  972                  * root). This implements append-only directories.
  973                  */
  974                 if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
  975                     tcnp->cn_cred->cr_uid != dp->i_uid &&
  976                     xp->i_uid != tcnp->cn_cred->cr_uid) {
  977                         error = EPERM;
  978                         goto bad;
  979                 }
  980                 /*
  981                  * Target must be empty if a directory and have no links
  982                  * to it. Also, ensure source and target are compatible
  983                  * (both directories, or both not directories).
  984                  */
  985                 if ((xp->i_mode&IFMT) == IFDIR) {
  986                         if (! ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) || 
  987                             xp->i_nlink > 2) {
  988                                 error = ENOTEMPTY;
  989                                 goto bad;
  990                         }
  991                         if (!doingdirectory) {
  992                                 error = ENOTDIR;
  993                                 goto bad;
  994                         }
  995                         cache_purge(tdvp);
  996                 } else if (doingdirectory) {
  997                         error = EISDIR;
  998                         goto bad;
  999                 }
 1000                 error = ext2_dirrewrite(dp, ip, tcnp);
 1001                 if (error)
 1002                         goto bad;
 1003                 /*
 1004                  * If the target directory is in the same
 1005                  * directory as the source directory,
 1006                  * decrement the link count on the parent
 1007                  * of the target directory.
 1008                  */
 1009                 if (doingdirectory && !newparent) {
 1010                        dp->i_nlink--;
 1011                        dp->i_flag |= IN_CHANGE;
 1012                 }
 1013                 vput(tdvp);
 1014                 /*
 1015                  * Adjust the link count of the target to
 1016                  * reflect the dirrewrite above.  If this is
 1017                  * a directory it is empty and there are
 1018                  * no links to it, so we can squash the inode and
 1019                  * any space associated with it.  We disallowed
 1020                  * renaming over top of a directory with links to
 1021                  * it above, as the remaining link would point to
 1022                  * a directory without "." or ".." entries.
 1023                  */
 1024                 xp->i_nlink--;
 1025                 if (doingdirectory) {
 1026                         if (--xp->i_nlink != 0)
 1027                                 panic("ext2_rename: linked directory");
 1028                         error = ext2_truncate(tvp, (off_t)0, IO_SYNC,
 1029                             tcnp->cn_cred, tcnp->cn_thread);
 1030                 }
 1031                 xp->i_flag |= IN_CHANGE;
 1032                 vput(tvp);
 1033                 xp = NULL;
 1034         }
 1035 
 1036         /*
 1037          * 3) Unlink the source.
 1038          */
 1039         fcnp->cn_flags &= ~MODMASK;
 1040         fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
 1041         VREF(fdvp);
 1042         error = relookup(fdvp, &fvp, fcnp);
 1043         if (error == 0)
 1044                 vrele(fdvp);
 1045         if (fvp != NULL) {
 1046                 xp = VTOI(fvp);
 1047                 dp = VTOI(fdvp);
 1048         } else {
 1049                 /*
 1050                  * From name has disappeared.
 1051                  */
 1052                 if (doingdirectory)
 1053                         panic("ext2_rename: lost dir entry");
 1054                 vrele(ap->a_fvp);
 1055                 return (0);
 1056         }
 1057         /*
 1058          * Ensure that the directory entry still exists and has not
 1059          * changed while the new name has been entered. If the source is
 1060          * a file then the entry may have been unlinked or renamed. In
 1061          * either case there is no further work to be done. If the source
 1062          * is a directory then it cannot have been rmdir'ed; its link
 1063          * count of three would cause a rmdir to fail with ENOTEMPTY.
 1064          * The IN_RENAME flag ensures that it cannot be moved by another
 1065          * rename.
 1066          */
 1067         if (xp != ip) {
 1068                 if (doingdirectory)
 1069                         panic("ext2_rename: lost dir entry");
 1070         } else {
 1071                 /*
 1072                  * If the source is a directory with a
 1073                  * new parent, the link count of the old
 1074                  * parent directory must be decremented
 1075                  * and ".." set to point to the new parent.
 1076                  */
 1077                 if (doingdirectory && newparent) {
 1078                         dp->i_nlink--;
 1079                         dp->i_flag |= IN_CHANGE;
 1080                         error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
 1081                                 sizeof (struct dirtemplate), (off_t)0,
 1082                                 UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
 1083                                 tcnp->cn_cred, NOCRED, NULL, NULL);
 1084                         if (error == 0) {
 1085                                 /* Like ufs little-endian: */
 1086                                 namlen = dirbuf.dotdot_type;
 1087                                 if (namlen != 2 ||
 1088                                     dirbuf.dotdot_name[0] != '.' ||
 1089                                     dirbuf.dotdot_name[1] != '.') {
 1090                                         ext2_dirbad(xp, (doff_t)12,
 1091                                             "rename: mangled dir");
 1092                                 } else {
 1093                                         dirbuf.dotdot_ino = newparent;
 1094                                         (void) vn_rdwr(UIO_WRITE, fvp,
 1095                                             (caddr_t)&dirbuf,
 1096                                             sizeof (struct dirtemplate),
 1097                                             (off_t)0, UIO_SYSSPACE,
 1098                                             IO_NODELOCKED | IO_SYNC |
 1099                                             IO_NOMACCHECK, tcnp->cn_cred,
 1100                                             NOCRED, NULL, NULL);
 1101                                         cache_purge(fdvp);
 1102                                 }
 1103                         }
 1104                 }
 1105                 error = ext2_dirremove(fdvp, fcnp);
 1106                 if (!error) {
 1107                         xp->i_nlink--;
 1108                         xp->i_flag |= IN_CHANGE;
 1109                 }
 1110                 xp->i_flag &= ~IN_RENAME;
 1111         }
 1112         if (dp)
 1113                 vput(fdvp);
 1114         if (xp)
 1115                 vput(fvp);
 1116         vrele(ap->a_fvp);
 1117         return (error);
 1118 
 1119 bad:
 1120         if (xp)
 1121                 vput(ITOV(xp));
 1122         vput(ITOV(dp));
 1123 out:
 1124         if (doingdirectory)
 1125                 ip->i_flag &= ~IN_RENAME;
 1126         if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
 1127                 ip->i_nlink--;
 1128                 ip->i_flag |= IN_CHANGE;
 1129                 ip->i_flag &= ~IN_RENAME;
 1130                 vput(fvp);
 1131         } else
 1132                 vrele(fvp);
 1133         return (error);
 1134 }
 1135 
 1136 /*
 1137  * Mkdir system call
 1138  */
 1139 static int
 1140 ext2_mkdir(ap)
 1141         struct vop_mkdir_args /* {
 1142                 struct vnode *a_dvp;
 1143                 struct vnode **a_vpp;
 1144                 struct componentname *a_cnp;
 1145                 struct vattr *a_vap;
 1146         } */ *ap;
 1147 {
 1148         struct vnode *dvp = ap->a_dvp;
 1149         struct vattr *vap = ap->a_vap;
 1150         struct componentname *cnp = ap->a_cnp;
 1151         struct inode *ip, *dp;
 1152         struct vnode *tvp;
 1153         struct dirtemplate dirtemplate, *dtp;
 1154         int error, dmode;
 1155 
 1156 #ifdef DIAGNOSTIC
 1157         if ((cnp->cn_flags & HASBUF) == 0)
 1158                 panic("ext2_mkdir: no name");
 1159 #endif
 1160         dp = VTOI(dvp);
 1161         if ((nlink_t)dp->i_nlink >= LINK_MAX) {
 1162                 error = EMLINK;
 1163                 goto out;
 1164         }
 1165         dmode = vap->va_mode & 0777;
 1166         dmode |= IFDIR;
 1167         /*
 1168          * Must simulate part of ext2_makeinode here to acquire the inode,
 1169          * but not have it entered in the parent directory. The entry is
 1170          * made later after writing "." and ".." entries.
 1171          */
 1172         error = ext2_valloc(dvp, dmode, cnp->cn_cred, &tvp);
 1173         if (error)
 1174                 goto out;
 1175         ip = VTOI(tvp);
 1176         ip->i_gid = dp->i_gid;
 1177 #ifdef SUIDDIR
 1178         {
 1179                 /*
 1180                  * if we are hacking owners here, (only do this where told to)
 1181                  * and we are not giving it TOO root, (would subvert quotas)
 1182                  * then go ahead and give it to the other user.
 1183                  * The new directory also inherits the SUID bit. 
 1184                  * If user's UID and dir UID are the same,
 1185                  * 'give it away' so that the SUID is still forced on.
 1186                  */
 1187                 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
 1188                    (dp->i_mode & ISUID) && dp->i_uid) {
 1189                         dmode |= ISUID;
 1190                         ip->i_uid = dp->i_uid;
 1191                 } else {
 1192                         ip->i_uid = cnp->cn_cred->cr_uid;
 1193                 }
 1194         }
 1195 #else
 1196         ip->i_uid = cnp->cn_cred->cr_uid;
 1197 #endif
 1198         ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
 1199         ip->i_mode = dmode;
 1200         tvp->v_type = VDIR;     /* Rest init'd in getnewvnode(). */
 1201         ip->i_nlink = 2;
 1202         if (cnp->cn_flags & ISWHITEOUT)
 1203                 ip->i_flags |= UF_OPAQUE;
 1204         error = ext2_update(tvp, 1);
 1205 
 1206         /*
 1207          * Bump link count in parent directory
 1208          * to reflect work done below.  Should
 1209          * be done before reference is created
 1210          * so reparation is possible if we crash.
 1211          */
 1212         dp->i_nlink++;
 1213         dp->i_flag |= IN_CHANGE;
 1214         error = ext2_update(dvp, !DOINGASYNC(dvp));
 1215         if (error)
 1216                 goto bad;
 1217 
 1218         /* Initialize directory with "." and ".." from static template. */
 1219         if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
 1220             EXT2F_INCOMPAT_FTYPE))
 1221                 dtp = &mastertemplate;
 1222         else
 1223                 dtp = &omastertemplate;
 1224         dirtemplate = *dtp;
 1225         dirtemplate.dot_ino = ip->i_number;
 1226         dirtemplate.dotdot_ino = dp->i_number;
 1227         /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE 
 1228          * so let's just redefine it - for this function only
 1229          */
 1230 #undef  DIRBLKSIZ 
 1231 #define DIRBLKSIZ  VTOI(dvp)->i_e2fs->e2fs_bsize
 1232         dirtemplate.dotdot_reclen = DIRBLKSIZ - 12;
 1233         error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
 1234             sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
 1235             IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, cnp->cn_cred, NOCRED,
 1236             NULL, NULL);
 1237         if (error) {
 1238                 dp->i_nlink--;
 1239                 dp->i_flag |= IN_CHANGE;
 1240                 goto bad;
 1241         }
 1242         if (DIRBLKSIZ > VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
 1243                 /* XXX should grow with balloc() */
 1244                 panic("ext2_mkdir: blksize");
 1245         else {
 1246                 ip->i_size = DIRBLKSIZ;
 1247                 ip->i_flag |= IN_CHANGE;
 1248         }
 1249 
 1250         /* Directory set up, now install its entry in the parent directory. */
 1251         error = ext2_direnter(ip, dvp, cnp);
 1252         if (error) {
 1253                 dp->i_nlink--;
 1254                 dp->i_flag |= IN_CHANGE;
 1255         }
 1256 bad:
 1257         /*
 1258          * No need to do an explicit VOP_TRUNCATE here, vrele will do this
 1259          * for us because we set the link count to 0.
 1260          */
 1261         if (error) {
 1262                 ip->i_nlink = 0;
 1263                 ip->i_flag |= IN_CHANGE;
 1264                 vput(tvp);
 1265         } else
 1266                 *ap->a_vpp = tvp;
 1267 out:
 1268         return (error);
 1269 #undef  DIRBLKSIZ
 1270 #define DIRBLKSIZ  DEV_BSIZE
 1271 }
 1272 
 1273 /*
 1274  * Rmdir system call.
 1275  */
 1276 static int
 1277 ext2_rmdir(ap)
 1278         struct vop_rmdir_args /* {
 1279                 struct vnode *a_dvp;
 1280                 struct vnode *a_vp;
 1281                 struct componentname *a_cnp;
 1282         } */ *ap;
 1283 {
 1284         struct vnode *vp = ap->a_vp;
 1285         struct vnode *dvp = ap->a_dvp;
 1286         struct componentname *cnp = ap->a_cnp;
 1287         struct inode *ip, *dp;
 1288         int error;
 1289 
 1290         ip = VTOI(vp);
 1291         dp = VTOI(dvp);
 1292 
 1293         /*
 1294          * Verify the directory is empty (and valid).
 1295          * (Rmdir ".." won't be valid since
 1296          *  ".." will contain a reference to
 1297          *  the current directory and thus be
 1298          *  non-empty.)
 1299          */
 1300         error = 0;
 1301         if (ip->i_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
 1302                 error = ENOTEMPTY;
 1303                 goto out;
 1304         }
 1305         if ((dp->i_flags & APPEND)
 1306             || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
 1307                 error = EPERM;
 1308                 goto out;
 1309         }
 1310         /*
 1311          * Delete reference to directory before purging
 1312          * inode.  If we crash in between, the directory
 1313          * will be reattached to lost+found,
 1314          */
 1315         error = ext2_dirremove(dvp, cnp);
 1316         if (error)
 1317                 goto out;
 1318         dp->i_nlink--;
 1319         dp->i_flag |= IN_CHANGE;
 1320         cache_purge(dvp);
 1321         VOP_UNLOCK(dvp, 0);
 1322         /*
 1323          * Truncate inode.  The only stuff left
 1324          * in the directory is "." and "..".  The
 1325          * "." reference is inconsequential since
 1326          * we're quashing it.  The ".." reference
 1327          * has already been adjusted above.  We've
 1328          * removed the "." reference and the reference
 1329          * in the parent directory, but there may be
 1330          * other hard links so decrement by 2 and
 1331          * worry about them later.
 1332          */
 1333         ip->i_nlink -= 2;
 1334         error = ext2_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
 1335             cnp->cn_thread);
 1336         cache_purge(ITOV(ip));
 1337         vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
 1338 out:
 1339         return (error);
 1340 }
 1341 
 1342 /*
 1343  * symlink -- make a symbolic link
 1344  */
 1345 static int
 1346 ext2_symlink(ap)
 1347         struct vop_symlink_args /* {
 1348                 struct vnode *a_dvp;
 1349                 struct vnode **a_vpp;
 1350                 struct componentname *a_cnp;
 1351                 struct vattr *a_vap;
 1352                 char *a_target;
 1353         } */ *ap;
 1354 {
 1355         struct vnode *vp, **vpp = ap->a_vpp;
 1356         struct inode *ip;
 1357         int len, error;
 1358 
 1359         error = ext2_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
 1360             vpp, ap->a_cnp);
 1361         if (error)
 1362                 return (error);
 1363         vp = *vpp;
 1364         len = strlen(ap->a_target);
 1365         if (len < vp->v_mount->mnt_maxsymlinklen) {
 1366                 ip = VTOI(vp);
 1367                 bcopy(ap->a_target, (char *)ip->i_shortlink, len);
 1368                 ip->i_size = len;
 1369                 ip->i_flag |= IN_CHANGE | IN_UPDATE;
 1370         } else
 1371                 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
 1372                     UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
 1373                     ap->a_cnp->cn_cred, NOCRED, NULL, NULL);
 1374         if (error)
 1375                 vput(vp);
 1376         return (error);
 1377 }
 1378 
 1379 /*
 1380  * Return target name of a symbolic link
 1381  */
 1382 static int
 1383 ext2_readlink(ap)
 1384         struct vop_readlink_args /* {
 1385                 struct vnode *a_vp;
 1386                 struct uio *a_uio;
 1387                 struct ucred *a_cred;
 1388         } */ *ap;
 1389 {
 1390         struct vnode *vp = ap->a_vp;
 1391         struct inode *ip = VTOI(vp);
 1392         int isize;
 1393 
 1394         isize = ip->i_size;
 1395         if (isize < vp->v_mount->mnt_maxsymlinklen) {
 1396                 uiomove((char *)ip->i_shortlink, isize, ap->a_uio);
 1397                 return (0);
 1398         }
 1399         return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
 1400 }
 1401 
 1402 /*
 1403  * Calculate the logical to physical mapping if not done already,
 1404  * then call the device strategy routine.
 1405  *
 1406  * In order to be able to swap to a file, the ext2_bmaparray() operation may not
 1407  * deadlock on memory.  See ext2_bmap() for details.
 1408  */
 1409 static int
 1410 ext2_strategy(ap)
 1411         struct vop_strategy_args /* {
 1412                 struct vnode *a_vp;
 1413                 struct buf *a_bp;
 1414         } */ *ap;
 1415 {
 1416         struct buf *bp = ap->a_bp;
 1417         struct vnode *vp = ap->a_vp;
 1418         struct inode *ip;
 1419         struct bufobj *bo;
 1420         int32_t blkno;
 1421         int error;
 1422 
 1423         ip = VTOI(vp);
 1424         if (vp->v_type == VBLK || vp->v_type == VCHR)
 1425                 panic("ext2_strategy: spec");
 1426         if (bp->b_blkno == bp->b_lblkno) {
 1427                 error = ext2_bmaparray(vp, bp->b_lblkno, &blkno, NULL, NULL);
 1428                 bp->b_blkno = blkno;
 1429                 if (error) {
 1430                         bp->b_error = error;
 1431                         bp->b_ioflags |= BIO_ERROR;
 1432                         bufdone(bp);
 1433                         return (0);
 1434                 }
 1435                 if ((long)bp->b_blkno == -1)
 1436                         vfs_bio_clrbuf(bp);
 1437         }
 1438         if ((long)bp->b_blkno == -1) {
 1439                 bufdone(bp);
 1440                 return (0);
 1441         }
 1442         bp->b_iooffset = dbtob(bp->b_blkno);
 1443         bo = VFSTOEXT2(vp->v_mount)->um_bo;
 1444         BO_STRATEGY(bo, bp);
 1445         return (0);
 1446 }
 1447 
 1448 /*
 1449  * Print out the contents of an inode.
 1450  */
 1451 static int
 1452 ext2_print(ap)
 1453         struct vop_print_args /* {
 1454                 struct vnode *a_vp;
 1455         } */ *ap;
 1456 {
 1457         struct vnode *vp = ap->a_vp;
 1458         struct inode *ip = VTOI(vp);
 1459 
 1460         vn_printf(ip->i_devvp, "\tino %lu", (u_long)ip->i_number);
 1461         if (vp->v_type == VFIFO)
 1462                 fifo_printinfo(vp);
 1463         printf("\n");
 1464         return (0);
 1465 }
 1466 
 1467 /*
 1468  * Close wrapper for fifos.
 1469  *
 1470  * Update the times on the inode then do device close.
 1471  */
 1472 static int
 1473 ext2fifo_close(ap)
 1474         struct vop_close_args /* {
 1475                 struct vnode *a_vp;
 1476                 int  a_fflag;
 1477                 struct ucred *a_cred;
 1478                 struct thread *a_td;
 1479         } */ *ap;
 1480 {
 1481         struct vnode *vp = ap->a_vp;
 1482 
 1483         VI_LOCK(vp);
 1484         if (vp->v_usecount > 1)
 1485                 ext2_itimes_locked(vp);
 1486         VI_UNLOCK(vp);
 1487         return (fifo_specops.vop_close(ap));
 1488 }
 1489 
 1490 /*
 1491  * Kqfilter wrapper for fifos.
 1492  *
 1493  * Fall through to ext2 kqfilter routines if needed 
 1494  */
 1495 static int
 1496 ext2fifo_kqfilter(ap)
 1497         struct vop_kqfilter_args *ap;
 1498 {
 1499         int error;
 1500 
 1501         error = fifo_specops.vop_kqfilter(ap);
 1502         if (error)
 1503                 error = vfs_kqfilter(ap);
 1504         return (error);
 1505 }
 1506 
 1507 /*
 1508  * Return POSIX pathconf information applicable to ext2 filesystems.
 1509  */
 1510 static int
 1511 ext2_pathconf(ap)
 1512         struct vop_pathconf_args /* {
 1513                 struct vnode *a_vp;
 1514                 int a_name;
 1515                 int *a_retval;
 1516         } */ *ap;
 1517 {
 1518 
 1519         switch (ap->a_name) {
 1520         case _PC_LINK_MAX:
 1521                 *ap->a_retval = LINK_MAX;
 1522                 return (0);
 1523         case _PC_NAME_MAX:
 1524                 *ap->a_retval = NAME_MAX;
 1525                 return (0);
 1526         case _PC_PATH_MAX:
 1527                 *ap->a_retval = PATH_MAX;
 1528                 return (0);
 1529         case _PC_PIPE_BUF:
 1530                 *ap->a_retval = PIPE_BUF;
 1531                 return (0);
 1532         case _PC_CHOWN_RESTRICTED:
 1533                 *ap->a_retval = 1;
 1534                 return (0);
 1535         case _PC_NO_TRUNC:
 1536                 *ap->a_retval = 1;
 1537                 return (0);
 1538         default:
 1539                 return (EINVAL);
 1540         }
 1541         /* NOTREACHED */
 1542 }
 1543 
 1544 /*
 1545  * Vnode pointer to File handle
 1546  */
 1547 /* ARGSUSED */
 1548 static int
 1549 ext2_vptofh(ap)
 1550         struct vop_vptofh_args /* {
 1551                 struct vnode *a_vp;
 1552                 struct fid *a_fhp;
 1553         } */ *ap;
 1554 {
 1555         struct inode *ip;
 1556         struct ufid *ufhp;
 1557 
 1558         ip = VTOI(ap->a_vp);
 1559         ufhp = (struct ufid *)ap->a_fhp;
 1560         ufhp->ufid_len = sizeof(struct ufid);
 1561         ufhp->ufid_ino = ip->i_number;
 1562         ufhp->ufid_gen = ip->i_gen;
 1563         return (0);
 1564 }
 1565 
 1566 /*
 1567  * Initialize the vnode associated with a new inode, handle aliased
 1568  * vnodes.
 1569  */
 1570 int
 1571 ext2_vinit(mntp, fifoops, vpp)
 1572         struct mount *mntp;
 1573         struct vop_vector *fifoops;
 1574         struct vnode **vpp;
 1575 {
 1576         struct inode *ip;
 1577         struct vnode *vp;
 1578 
 1579         vp = *vpp;
 1580         ip = VTOI(vp);
 1581         vp->v_type = IFTOVT(ip->i_mode);
 1582         if (vp->v_type == VFIFO)
 1583                 vp->v_op = fifoops;
 1584 
 1585         if (ip->i_number == EXT2_ROOTINO)
 1586                 vp->v_vflag |= VV_ROOT;
 1587         ip->i_modrev = init_va_filerev();
 1588         *vpp = vp;
 1589         return (0);
 1590 }
 1591 
 1592 /*
 1593  * Allocate a new inode.
 1594  */
 1595 static int
 1596 ext2_makeinode(mode, dvp, vpp, cnp)
 1597         int mode;
 1598         struct vnode *dvp;
 1599         struct vnode **vpp;
 1600         struct componentname *cnp;
 1601 {
 1602         struct inode *ip, *pdir;
 1603         struct vnode *tvp;
 1604         int error;
 1605 
 1606         pdir = VTOI(dvp);
 1607 #ifdef DIAGNOSTIC
 1608         if ((cnp->cn_flags & HASBUF) == 0)
 1609                 panic("ext2_makeinode: no name");
 1610 #endif
 1611         *vpp = NULL;
 1612         if ((mode & IFMT) == 0)
 1613                 mode |= IFREG;
 1614 
 1615         error = ext2_valloc(dvp, mode, cnp->cn_cred, &tvp);
 1616         if (error) {
 1617                 return (error);
 1618         }
 1619         ip = VTOI(tvp);
 1620         ip->i_gid = pdir->i_gid;
 1621 #ifdef SUIDDIR
 1622         {
 1623                 /*
 1624                  * if we are
 1625                  * not the owner of the directory,
 1626                  * and we are hacking owners here, (only do this where told to)
 1627                  * and we are not giving it TOO root, (would subvert quotas)
 1628                  * then go ahead and give it to the other user.
 1629                  * Note that this drops off the execute bits for security.
 1630                  */
 1631                 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
 1632                      (pdir->i_mode & ISUID) &&
 1633                      (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
 1634                         ip->i_uid = pdir->i_uid;
 1635                         mode &= ~07111;
 1636                 } else {
 1637                         ip->i_uid = cnp->cn_cred->cr_uid;
 1638                 }
 1639         }
 1640 #else
 1641         ip->i_uid = cnp->cn_cred->cr_uid;
 1642 #endif
 1643         ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
 1644         ip->i_mode = mode;
 1645         tvp->v_type = IFTOVT(mode);     /* Rest init'd in getnewvnode(). */
 1646         ip->i_nlink = 1;
 1647         if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred)) {
 1648                 if (priv_check_cred(cnp->cn_cred, PRIV_VFS_RETAINSUGID, 0))
 1649                         ip->i_mode &= ~ISGID;
 1650         }
 1651 
 1652         if (cnp->cn_flags & ISWHITEOUT)
 1653                 ip->i_flags |= UF_OPAQUE;
 1654 
 1655         /*
 1656          * Make sure inode goes to disk before directory entry.
 1657          */
 1658         error = ext2_update(tvp, !DOINGASYNC(tvp));
 1659         if (error)
 1660                 goto bad;
 1661         error = ext2_direnter(ip, dvp, cnp);
 1662         if (error)
 1663                 goto bad;
 1664 
 1665         *vpp = tvp;
 1666         return (0);
 1667 
 1668 bad:
 1669         /*
 1670          * Write error occurred trying to update the inode
 1671          * or the directory so must deallocate the inode.
 1672          */
 1673         ip->i_nlink = 0;
 1674         ip->i_flag |= IN_CHANGE;
 1675         vput(tvp);
 1676         return (error);
 1677 }

Cache object: 28a535bdf7bfc463923c79ba84bbea04


[ 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.