xref: /openbsd/sys/ufs/ext2fs/ext2fs_vnops.c (revision 669a8cbb)
1*669a8cbbSmiod /*	$OpenBSD: ext2fs_vnops.c,v 1.69 2014/12/29 05:29:28 miod Exp $	*/
21414b0faSart /*	$NetBSD: ext2fs_vnops.c,v 1.1 1997/06/11 09:34:09 bouyer Exp $	*/
35ac2d602Sdownsj 
45ac2d602Sdownsj /*
51f3ff51cSdownsj  * Copyright (c) 1997 Manuel Bouyer.
65ac2d602Sdownsj  * Copyright (c) 1982, 1986, 1989, 1993
75ac2d602Sdownsj  *	The Regents of the University of California.  All rights reserved.
85ac2d602Sdownsj  * (c) UNIX System Laboratories, Inc.
95ac2d602Sdownsj  * All or some portions of this file are derived from material licensed
105ac2d602Sdownsj  * to the University of California by American Telephone and Telegraph
115ac2d602Sdownsj  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
125ac2d602Sdownsj  * the permission of UNIX System Laboratories, Inc.
135ac2d602Sdownsj  *
145ac2d602Sdownsj  * Redistribution and use in source and binary forms, with or without
155ac2d602Sdownsj  * modification, are permitted provided that the following conditions
165ac2d602Sdownsj  * are met:
175ac2d602Sdownsj  * 1. Redistributions of source code must retain the above copyright
185ac2d602Sdownsj  *    notice, this list of conditions and the following disclaimer.
195ac2d602Sdownsj  * 2. Redistributions in binary form must reproduce the above copyright
205ac2d602Sdownsj  *    notice, this list of conditions and the following disclaimer in the
215ac2d602Sdownsj  *    documentation and/or other materials provided with the distribution.
2229295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
235ac2d602Sdownsj  *    may be used to endorse or promote products derived from this software
245ac2d602Sdownsj  *    without specific prior written permission.
255ac2d602Sdownsj  *
265ac2d602Sdownsj  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
275ac2d602Sdownsj  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
285ac2d602Sdownsj  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
295ac2d602Sdownsj  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
305ac2d602Sdownsj  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
315ac2d602Sdownsj  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
325ac2d602Sdownsj  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
335ac2d602Sdownsj  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
345ac2d602Sdownsj  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
355ac2d602Sdownsj  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
365ac2d602Sdownsj  * SUCH DAMAGE.
375ac2d602Sdownsj  *
385ac2d602Sdownsj  *	@(#)ufs_vnops.c	8.14 (Berkeley) 10/26/94
391f3ff51cSdownsj  * Modified for ext2fs by Manuel Bouyer.
405ac2d602Sdownsj  */
415ac2d602Sdownsj 
425ac2d602Sdownsj #include <sys/param.h>
435ac2d602Sdownsj #include <sys/systm.h>
445ac2d602Sdownsj #include <sys/resourcevar.h>
455ac2d602Sdownsj #include <sys/kernel.h>
465ac2d602Sdownsj #include <sys/file.h>
475ac2d602Sdownsj #include <sys/stat.h>
485ac2d602Sdownsj #include <sys/buf.h>
495ac2d602Sdownsj #include <sys/proc.h>
505ac2d602Sdownsj #include <sys/conf.h>
515ac2d602Sdownsj #include <sys/mount.h>
525ac2d602Sdownsj #include <sys/namei.h>
535ac2d602Sdownsj #include <sys/vnode.h>
545ac2d602Sdownsj #include <sys/lockf.h>
555ac2d602Sdownsj #include <sys/malloc.h>
56f5ee6277Sjasoni #include <sys/pool.h>
575ac2d602Sdownsj #include <sys/signalvar.h>
58544451c3Sderaadt #include <sys/specdev.h>
59782ebdf8Stedu #include <sys/unistd.h>
605ac2d602Sdownsj 
615ac2d602Sdownsj #include <miscfs/fifofs/fifo.h>
625ac2d602Sdownsj 
635ac2d602Sdownsj #include <ufs/ufs/quota.h>
645ac2d602Sdownsj #include <ufs/ufs/inode.h>
655ac2d602Sdownsj #include <ufs/ufs/ufs_extern.h>
665ac2d602Sdownsj #include <ufs/ufs/ufsmount.h>
675ac2d602Sdownsj 
685ac2d602Sdownsj #include <ufs/ext2fs/ext2fs.h>
695ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_extern.h>
705ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_dir.h>
715ac2d602Sdownsj 
72*669a8cbbSmiod #include <uvm/uvm_extern.h>
73*669a8cbbSmiod 
74cc2fc615Smillert static int ext2fs_chmod(struct vnode *, mode_t, struct ucred *, struct proc *);
75c4071fd1Smillert static int ext2fs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct proc *);
765ac2d602Sdownsj 
775ac2d602Sdownsj /*
785ac2d602Sdownsj  * Create a regular file
795ac2d602Sdownsj  */
805ac2d602Sdownsj int
815f64cd9cSjasper ext2fs_create(void *v)
825ac2d602Sdownsj {
8399bc9d31Sderaadt 	struct vop_create_args *ap = v;
84f5ee6277Sjasoni 	return ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type,
85f5ee6277Sjasoni 					  ap->a_vap->va_mode),
865ac2d602Sdownsj 			  	ap->a_dvp, ap->a_vpp, ap->a_cnp);
875ac2d602Sdownsj }
885ac2d602Sdownsj 
895ac2d602Sdownsj /*
905ac2d602Sdownsj  * Mknod vnode call
915ac2d602Sdownsj  */
925ac2d602Sdownsj /* ARGSUSED */
935ac2d602Sdownsj int
945f64cd9cSjasper ext2fs_mknod(void *v)
955ac2d602Sdownsj {
9699bc9d31Sderaadt 	struct vop_mknod_args *ap = v;
975f64cd9cSjasper 	struct vattr *vap = ap->a_vap;
985f64cd9cSjasper 	struct vnode **vpp = ap->a_vpp;
995f64cd9cSjasper 	struct inode *ip;
1005ac2d602Sdownsj 	int error;
1015ac2d602Sdownsj 
1025ac2d602Sdownsj 	if ((error =
1035ac2d602Sdownsj 		ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
1045ac2d602Sdownsj 		ap->a_dvp, vpp, ap->a_cnp)) != 0)
1055ac2d602Sdownsj 		return (error);
1065ac2d602Sdownsj 	ip = VTOI(*vpp);
1075ac2d602Sdownsj 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1085ac2d602Sdownsj 	if (vap->va_rdev != VNOVAL) {
1095ac2d602Sdownsj 		/*
1105ac2d602Sdownsj 		 * Want to be able to use this to make badblock
1115ac2d602Sdownsj 		 * inodes, so don't truncate the dev number.
1125ac2d602Sdownsj 		 */
113f7dbefaaSpelikan 		ip->i_e2din->e2di_rdev = htole32(vap->va_rdev);
1145ac2d602Sdownsj 	}
1155ac2d602Sdownsj 	/*
1165ac2d602Sdownsj 	 * Remove inode so that it will be reloaded by VFS_VGET and
1175ac2d602Sdownsj 	 * checked to see if it is an alias of an existing entry in
1185ac2d602Sdownsj 	 * the inode cache.
1195ac2d602Sdownsj 	 */
1205ac2d602Sdownsj 	vput(*vpp);
1215ac2d602Sdownsj 	(*vpp)->v_type = VNON;
1225ac2d602Sdownsj 	vgone(*vpp);
123dfc6b8b2Stedu 	*vpp = NULL;
1245ac2d602Sdownsj 	return (0);
1255ac2d602Sdownsj }
1265ac2d602Sdownsj 
1275ac2d602Sdownsj /*
1285ac2d602Sdownsj  * Open called.
1295ac2d602Sdownsj  *
1305ac2d602Sdownsj  * Just check the APPEND flag.
1315ac2d602Sdownsj  */
1325ac2d602Sdownsj /* ARGSUSED */
1335ac2d602Sdownsj int
1345f64cd9cSjasper ext2fs_open(void *v)
1355ac2d602Sdownsj {
13699bc9d31Sderaadt 	struct vop_open_args *ap = v;
1375ac2d602Sdownsj 
1385ac2d602Sdownsj 	/*
1395ac2d602Sdownsj 	 * Files marked append-only must be opened for appending.
1405ac2d602Sdownsj 	 */
1415ac2d602Sdownsj 	if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
1425ac2d602Sdownsj 		(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
1435ac2d602Sdownsj 		return (EPERM);
1445ac2d602Sdownsj 	return (0);
1455ac2d602Sdownsj }
1465ac2d602Sdownsj 
1475ac2d602Sdownsj int
1485f64cd9cSjasper ext2fs_access(void *v)
1495ac2d602Sdownsj {
15099bc9d31Sderaadt 	struct vop_access_args *ap = v;
1515f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
1525f64cd9cSjasper 	struct inode *ip = VTOI(vp);
1535ac2d602Sdownsj 	mode_t mode = ap->a_mode;
1545ac2d602Sdownsj 
1555ac2d602Sdownsj 	/* If immutable bit set, nobody gets to write it. */
1565ac2d602Sdownsj 	if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
1575ac2d602Sdownsj 		return (EPERM);
1585ac2d602Sdownsj 
159141c07a8Smillert 	return (vaccess(vp->v_type, ip->i_e2fs_mode, ip->i_e2fs_uid,
160141c07a8Smillert 			ip->i_e2fs_gid, mode, ap->a_cred));
1615ac2d602Sdownsj }
1625ac2d602Sdownsj 
1635ac2d602Sdownsj /* ARGSUSED */
1645ac2d602Sdownsj int
1655f64cd9cSjasper ext2fs_getattr(void *v)
1665ac2d602Sdownsj {
16799bc9d31Sderaadt 	struct vop_getattr_args *ap = v;
1685f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
1695f64cd9cSjasper 	struct inode *ip = VTOI(vp);
1705f64cd9cSjasper 	struct vattr *vap = ap->a_vap;
1715ac2d602Sdownsj 
172d4fc1c49Sguenther 	EXT2FS_ITIMES(ip);
1735ac2d602Sdownsj 	/*
1745ac2d602Sdownsj 	 * Copy from inode table
1755ac2d602Sdownsj 	 */
1765ac2d602Sdownsj 	vap->va_fsid = ip->i_dev;
1775ac2d602Sdownsj 	vap->va_fileid = ip->i_number;
1781f3ff51cSdownsj 	vap->va_mode = ip->i_e2fs_mode & ALLPERMS;
1795ac2d602Sdownsj 	vap->va_nlink = ip->i_e2fs_nlink;
1805ac2d602Sdownsj 	vap->va_uid = ip->i_e2fs_uid;
1815ac2d602Sdownsj 	vap->va_gid = ip->i_e2fs_gid;
182f7dbefaaSpelikan 	vap->va_rdev = (dev_t) letoh32(ip->i_e2din->e2di_rdev);
1834dcbbbb0Sniallo 	vap->va_size = ext2fs_size(ip);
1845ac2d602Sdownsj 	vap->va_atime.tv_sec = ip->i_e2fs_atime;
1855ac2d602Sdownsj 	vap->va_atime.tv_nsec = 0;
1865ac2d602Sdownsj 	vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
1875ac2d602Sdownsj 	vap->va_mtime.tv_nsec = 0;
1885ac2d602Sdownsj 	vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
1895ac2d602Sdownsj 	vap->va_ctime.tv_nsec = 0;
1905ac2d602Sdownsj #ifdef EXT2FS_SYSTEM_FLAGS
1915ac2d602Sdownsj 	vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
1925ac2d602Sdownsj 	vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
1935ac2d602Sdownsj #else
1945ac2d602Sdownsj 	vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
1955ac2d602Sdownsj 	vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
1965ac2d602Sdownsj #endif
1975ac2d602Sdownsj 	vap->va_gen = ip->i_e2fs_gen;
1985ac2d602Sdownsj 	/* this doesn't belong here */
1995ac2d602Sdownsj 	if (vp->v_type == VBLK)
2005ac2d602Sdownsj 		vap->va_blocksize = BLKDEV_IOSIZE;
2015ac2d602Sdownsj 	else if (vp->v_type == VCHR)
2025ac2d602Sdownsj 		vap->va_blocksize = MAXBSIZE;
2035ac2d602Sdownsj 	else
2045ac2d602Sdownsj 		vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
205f5ee6277Sjasoni 	vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock);
2065ac2d602Sdownsj 	vap->va_type = vp->v_type;
2075ac2d602Sdownsj 	vap->va_filerev = ip->i_modrev;
2085ac2d602Sdownsj 	return (0);
2095ac2d602Sdownsj }
2105ac2d602Sdownsj 
2115ac2d602Sdownsj /*
2125ac2d602Sdownsj  * Set attribute vnode op. called from several syscalls
2135ac2d602Sdownsj  */
2145ac2d602Sdownsj int
2155f64cd9cSjasper ext2fs_setattr(void *v)
2165ac2d602Sdownsj {
21799bc9d31Sderaadt 	struct vop_setattr_args *ap = v;
2185f64cd9cSjasper 	struct vattr *vap = ap->a_vap;
2195f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
2205f64cd9cSjasper 	struct inode *ip = VTOI(vp);
2215f64cd9cSjasper 	struct ucred *cred = ap->a_cred;
2225f64cd9cSjasper 	struct proc *p = ap->a_p;
2235ac2d602Sdownsj 	int error;
2245ac2d602Sdownsj 
2255ac2d602Sdownsj 	/*
2265ac2d602Sdownsj 	 * Check for unsettable attributes.
2275ac2d602Sdownsj 	 */
2285ac2d602Sdownsj 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
2295ac2d602Sdownsj 		(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
2305ac2d602Sdownsj 		(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
2315ac2d602Sdownsj 		((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
2325ac2d602Sdownsj 		return (EINVAL);
2335ac2d602Sdownsj 	}
2345ac2d602Sdownsj 	if (vap->va_flags != VNOVAL) {
235f5ee6277Sjasoni 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
236f5ee6277Sjasoni 			return (EROFS);
2375ac2d602Sdownsj 		if (cred->cr_uid != ip->i_e2fs_uid &&
238ad392958Stedu 			(error = suser_ucred(cred)))
2395ac2d602Sdownsj 			return (error);
2405ac2d602Sdownsj #ifdef EXT2FS_SYSTEM_FLAGS
2415ac2d602Sdownsj 		if (cred->cr_uid == 0) {
242f5ee6277Sjasoni 			if ((ip->i_e2fs_flags &
243f5ee6277Sjasoni 			    (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0)
2445ac2d602Sdownsj 				return (EPERM);
2455ac2d602Sdownsj 			ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
246f5ee6277Sjasoni 			ip->i_e2fs_flags |=
247f5ee6277Sjasoni 			    (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
2485ac2d602Sdownsj 			    (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
2495ac2d602Sdownsj 		} else {
2505ac2d602Sdownsj 			return (EPERM);
2515ac2d602Sdownsj 		}
2525ac2d602Sdownsj #else
2535ac2d602Sdownsj 		ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
254f5ee6277Sjasoni 		ip->i_e2fs_flags |=
255f5ee6277Sjasoni 		    (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
2565ac2d602Sdownsj 		    (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
2575ac2d602Sdownsj #endif
2585ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
2595ac2d602Sdownsj 		if (vap->va_flags & (IMMUTABLE | APPEND))
2605ac2d602Sdownsj 			return (0);
2615ac2d602Sdownsj 	}
2625ac2d602Sdownsj 	if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
2635ac2d602Sdownsj 		return (EPERM);
2645ac2d602Sdownsj 	/*
2655ac2d602Sdownsj 	 * Go through the fields and update iff not VNOVAL.
2665ac2d602Sdownsj 	 */
2675ac2d602Sdownsj 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
268f5ee6277Sjasoni 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
269f5ee6277Sjasoni 			return (EROFS);
2705ac2d602Sdownsj 		error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
2715ac2d602Sdownsj 		if (error)
2725ac2d602Sdownsj 			return (error);
2735ac2d602Sdownsj 	}
2745ac2d602Sdownsj 	if (vap->va_size != VNOVAL) {
275f5ee6277Sjasoni 		/*
276f5ee6277Sjasoni 		 * Disallow write attempts on read-only file systems;
277f5ee6277Sjasoni 		 * unless the file is a socket, fifo, or a block or
278f5ee6277Sjasoni 		 * character device resident on the file system.
279f5ee6277Sjasoni 		 */
280f5ee6277Sjasoni 		switch (vp->v_type) {
281f5ee6277Sjasoni 		case VDIR:
2825ac2d602Sdownsj 			return (EISDIR);
283f5ee6277Sjasoni 		case VLNK:
284f5ee6277Sjasoni 		case VREG:
285f5ee6277Sjasoni 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
286f5ee6277Sjasoni 				return (EROFS);
287f5ee6277Sjasoni 		default:
288f5ee6277Sjasoni 			break;
289f5ee6277Sjasoni 		}
290b080ad39Scsapuntz 		error = ext2fs_truncate(ip, vap->va_size, 0, cred);
2915ac2d602Sdownsj 		if (error)
2925ac2d602Sdownsj 			return (error);
2935ac2d602Sdownsj 	}
2945ac2d602Sdownsj 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
295f5ee6277Sjasoni 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
296f5ee6277Sjasoni 			return (EROFS);
2975ac2d602Sdownsj 		if (cred->cr_uid != ip->i_e2fs_uid &&
298ad392958Stedu 			(error = suser_ucred(cred)) &&
2995ac2d602Sdownsj 			((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
3005ac2d602Sdownsj 			(error = VOP_ACCESS(vp, VWRITE, cred, p))))
3015ac2d602Sdownsj 			return (error);
3025ac2d602Sdownsj 		if (vap->va_mtime.tv_sec != VNOVAL)
3035ac2d602Sdownsj 			ip->i_flag |= IN_CHANGE | IN_UPDATE;
3045fdfb110Smillert 		if (vap->va_atime.tv_sec != VNOVAL) {
3055fdfb110Smillert 			if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
3065fdfb110Smillert 			    (ip->i_flag & (IN_CHANGE | IN_UPDATE)))
3075fdfb110Smillert 				ip->i_flag |= IN_ACCESS;
3085fdfb110Smillert 		}
309d4fc1c49Sguenther 		EXT2FS_ITIMES(ip);
310d4fc1c49Sguenther 		if (vap->va_mtime.tv_sec != VNOVAL)
311d4fc1c49Sguenther 			ip->i_e2fs_mtime = vap->va_mtime.tv_sec;
312d4fc1c49Sguenther 		if (vap->va_atime.tv_sec != VNOVAL)
313d4fc1c49Sguenther 			ip->i_e2fs_atime = vap->va_atime.tv_sec;
314d4fc1c49Sguenther 		error = ext2fs_update(ip, 1);
3155ac2d602Sdownsj 		if (error)
3165ac2d602Sdownsj 			return (error);
3175ac2d602Sdownsj 	}
3185ac2d602Sdownsj 	error = 0;
319f5ee6277Sjasoni 	if (vap->va_mode != (mode_t)VNOVAL) {
320f5ee6277Sjasoni 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
321f5ee6277Sjasoni 			return (EROFS);
3225ac2d602Sdownsj 		error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p);
323f5ee6277Sjasoni 	}
3245ac2d602Sdownsj 	return (error);
3255ac2d602Sdownsj }
3265ac2d602Sdownsj 
3275ac2d602Sdownsj /*
3285ac2d602Sdownsj  * Change the mode on a file.
3295ac2d602Sdownsj  * Inode must be locked before calling.
3305ac2d602Sdownsj  */
3315ac2d602Sdownsj static int
3325f64cd9cSjasper ext2fs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred, struct proc *p)
3335ac2d602Sdownsj {
3345f64cd9cSjasper 	struct inode *ip = VTOI(vp);
3355ac2d602Sdownsj 	int error;
3365ac2d602Sdownsj 
337ad392958Stedu 	if (cred->cr_uid != ip->i_e2fs_uid && (error = suser_ucred(cred)))
3385ac2d602Sdownsj 		return (error);
3395ac2d602Sdownsj 	if (cred->cr_uid) {
3405ac2d602Sdownsj 		if (vp->v_type != VDIR && (mode & S_ISTXT))
3415ac2d602Sdownsj 			return (EFTYPE);
3425ac2d602Sdownsj 		if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
3435ac2d602Sdownsj 			return (EPERM);
3445ac2d602Sdownsj 	}
3455ac2d602Sdownsj 	ip->i_e2fs_mode &= ~ALLPERMS;
3465ac2d602Sdownsj 	ip->i_e2fs_mode |= (mode & ALLPERMS);
3475ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
3481414b0faSart 	if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
3491414b0faSart 		(void) uvm_vnp_uncache(vp);
3505ac2d602Sdownsj 	return (0);
3515ac2d602Sdownsj }
3525ac2d602Sdownsj 
3535ac2d602Sdownsj /*
3545ac2d602Sdownsj  * Perform chown operation on inode ip;
3555ac2d602Sdownsj  * inode must be locked prior to call.
3565ac2d602Sdownsj  */
3575ac2d602Sdownsj static int
3585f64cd9cSjasper ext2fs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p)
3595ac2d602Sdownsj {
3605f64cd9cSjasper 	struct inode *ip = VTOI(vp);
3615ac2d602Sdownsj 	uid_t ouid;
3625ac2d602Sdownsj 	gid_t ogid;
3635ac2d602Sdownsj 	int error = 0;
3645ac2d602Sdownsj 
3655ac2d602Sdownsj 	if (uid == (uid_t)VNOVAL)
3665ac2d602Sdownsj 		uid = ip->i_e2fs_uid;
3675ac2d602Sdownsj 	if (gid == (gid_t)VNOVAL)
3685ac2d602Sdownsj 		gid = ip->i_e2fs_gid;
3695ac2d602Sdownsj 	/*
3705ac2d602Sdownsj 	 * If we don't own the file, are trying to change the owner
3715ac2d602Sdownsj 	 * of the file, or are not a member of the target group,
3725ac2d602Sdownsj 	 * the caller must be superuser or the call fails.
3735ac2d602Sdownsj 	 */
3745ac2d602Sdownsj 	if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
3755ac2d602Sdownsj 		(gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
376ad392958Stedu 		(error = suser_ucred(cred)))
3775ac2d602Sdownsj 		return (error);
3785ac2d602Sdownsj 	ogid = ip->i_e2fs_gid;
3795ac2d602Sdownsj 	ouid = ip->i_e2fs_uid;
3805ac2d602Sdownsj 
3815ac2d602Sdownsj 	ip->i_e2fs_gid = gid;
3825ac2d602Sdownsj 	ip->i_e2fs_uid = uid;
3835ac2d602Sdownsj 	if (ouid != uid || ogid != gid)
3845ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
3855ac2d602Sdownsj 	if (ouid != uid && cred->cr_uid != 0)
3865ac2d602Sdownsj 		ip->i_e2fs_mode &= ~ISUID;
3875ac2d602Sdownsj 	if (ogid != gid && cred->cr_uid != 0)
3885ac2d602Sdownsj 		ip->i_e2fs_mode &= ~ISGID;
3895ac2d602Sdownsj 	return (0);
3905ac2d602Sdownsj }
3915ac2d602Sdownsj 
3925ac2d602Sdownsj int
3935f64cd9cSjasper ext2fs_remove(void *v)
3945ac2d602Sdownsj {
39599bc9d31Sderaadt 	struct vop_remove_args *ap = v;
3965f64cd9cSjasper 	struct inode *ip;
3975f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
3985f64cd9cSjasper 	struct vnode *dvp = ap->a_dvp;
3995ac2d602Sdownsj 	int error;
4005ac2d602Sdownsj 
4015ac2d602Sdownsj 	ip = VTOI(vp);
402d56d3b9eSderaadt 	if (vp->v_type == VDIR ||
403d56d3b9eSderaadt 		(ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
4045ac2d602Sdownsj 	    	(VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
4055ac2d602Sdownsj 		error = EPERM;
4065ac2d602Sdownsj 		goto out;
4075ac2d602Sdownsj 	}
4085ac2d602Sdownsj 	error = ext2fs_dirremove(dvp, ap->a_cnp);
4095ac2d602Sdownsj 	if (error == 0) {
4105ac2d602Sdownsj 		ip->i_e2fs_nlink--;
4115ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
4125ac2d602Sdownsj 	}
4135ac2d602Sdownsj out:
4145ac2d602Sdownsj 	if (dvp == vp)
4155ac2d602Sdownsj 		vrele(vp);
4165ac2d602Sdownsj 	else
4175ac2d602Sdownsj 		vput(vp);
4185ac2d602Sdownsj 	vput(dvp);
4195ac2d602Sdownsj 	return (error);
4205ac2d602Sdownsj }
4215ac2d602Sdownsj 
4225ac2d602Sdownsj /*
4235ac2d602Sdownsj  * link vnode call
4245ac2d602Sdownsj  */
4255ac2d602Sdownsj int
4265f64cd9cSjasper ext2fs_link(void *v)
4275ac2d602Sdownsj {
42899bc9d31Sderaadt 	struct vop_link_args *ap = v;
4295f64cd9cSjasper 	struct vnode *dvp = ap->a_dvp;
4305f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
4315f64cd9cSjasper 	struct componentname *cnp = ap->a_cnp;
43207feb63cScsapuntz 	struct proc *p = cnp->cn_proc;
4335f64cd9cSjasper 	struct inode *ip;
4345ac2d602Sdownsj 	int error;
4355ac2d602Sdownsj 
4365ac2d602Sdownsj #ifdef DIAGNOSTIC
4375ac2d602Sdownsj 	if ((cnp->cn_flags & HASBUF) == 0)
4385ac2d602Sdownsj 		panic("ext2fs_link: no name");
4395ac2d602Sdownsj #endif
4405ac2d602Sdownsj 	if (vp->v_type == VDIR) {
4415ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
4425ac2d602Sdownsj 		error = EISDIR;
4435ac2d602Sdownsj 		goto out2;
4445ac2d602Sdownsj 	}
4455ac2d602Sdownsj 	if (dvp->v_mount != vp->v_mount) {
4465ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
4475ac2d602Sdownsj 		error = EXDEV;
4485ac2d602Sdownsj 		goto out2;
4495ac2d602Sdownsj 	}
450f5ee6277Sjasoni 	if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
4515ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
4525ac2d602Sdownsj 		goto out2;
4535ac2d602Sdownsj 	}
4545ac2d602Sdownsj 	ip = VTOI(vp);
4555ac2d602Sdownsj 	if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
4565ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
4575ac2d602Sdownsj 		error = EMLINK;
4585ac2d602Sdownsj 		goto out1;
4595ac2d602Sdownsj 	}
4605ac2d602Sdownsj 	if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
4615ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
4625ac2d602Sdownsj 		error = EPERM;
4635ac2d602Sdownsj 		goto out1;
4645ac2d602Sdownsj 	}
4655ac2d602Sdownsj 	ip->i_e2fs_nlink++;
4665ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
467d4fc1c49Sguenther 	error = ext2fs_update(ip, 1);
4685ac2d602Sdownsj 	if (!error)
4695ac2d602Sdownsj 		error = ext2fs_direnter(ip, dvp, cnp);
4705ac2d602Sdownsj 	if (error) {
4715ac2d602Sdownsj 		ip->i_e2fs_nlink--;
4725ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
4735ac2d602Sdownsj 	}
474dbe27ba0Stedu 	pool_put(&namei_pool, cnp->cn_pnbuf);
4755ac2d602Sdownsj out1:
4765ac2d602Sdownsj 	if (dvp != vp)
47707feb63cScsapuntz 		VOP_UNLOCK(vp, 0, p);
4785ac2d602Sdownsj out2:
4795ac2d602Sdownsj 	vput(dvp);
4805ac2d602Sdownsj 	return (error);
4815ac2d602Sdownsj }
4825ac2d602Sdownsj 
4835ac2d602Sdownsj /*
4845ac2d602Sdownsj  * Rename system call.
4855ac2d602Sdownsj  * 	rename("foo", "bar");
4865ac2d602Sdownsj  * is essentially
4875ac2d602Sdownsj  *	unlink("bar");
4885ac2d602Sdownsj  *	link("foo", "bar");
4895ac2d602Sdownsj  *	unlink("foo");
4905ac2d602Sdownsj  * but ``atomically''.  Can't do full commit without saving state in the
4915ac2d602Sdownsj  * inode on disk which isn't feasible at this time.  Best we can do is
4925ac2d602Sdownsj  * always guarantee the target exists.
4935ac2d602Sdownsj  *
4945ac2d602Sdownsj  * Basic algorithm is:
4955ac2d602Sdownsj  *
4965ac2d602Sdownsj  * 1) Bump link count on source while we're linking it to the
4975ac2d602Sdownsj  *    target.  This also ensure the inode won't be deleted out
4985ac2d602Sdownsj  *    from underneath us while we work (it may be truncated by
4995ac2d602Sdownsj  *    a concurrent `trunc' or `open' for creation).
5005ac2d602Sdownsj  * 2) Link source to destination.  If destination already exists,
5015ac2d602Sdownsj  *    delete it first.
5025ac2d602Sdownsj  * 3) Unlink source reference to inode if still around. If a
5035ac2d602Sdownsj  *    directory was moved and the parent of the destination
5045ac2d602Sdownsj  *    is different from the source, patch the ".." entry in the
5055ac2d602Sdownsj  *    directory.
5065ac2d602Sdownsj  */
5075ac2d602Sdownsj int
5085f64cd9cSjasper ext2fs_rename(void *v)
5095ac2d602Sdownsj {
51099bc9d31Sderaadt 	struct vop_rename_args  *ap = v;
5115ac2d602Sdownsj 	struct vnode *tvp = ap->a_tvp;
5125f64cd9cSjasper 	struct vnode *tdvp = ap->a_tdvp;
5135ac2d602Sdownsj 	struct vnode *fvp = ap->a_fvp;
5145f64cd9cSjasper 	struct vnode *fdvp = ap->a_fdvp;
5155f64cd9cSjasper 	struct componentname *tcnp = ap->a_tcnp;
5165f64cd9cSjasper 	struct componentname *fcnp = ap->a_fcnp;
5175f64cd9cSjasper 	struct inode *ip, *xp, *dp;
51807feb63cScsapuntz 	struct proc *p = fcnp->cn_proc;
5195ac2d602Sdownsj 	struct ext2fs_dirtemplate dirbuf;
5205f64cd9cSjasper 	/* struct timespec ts; */
5215ac2d602Sdownsj 	int doingdirectory = 0, oldparent = 0, newparent = 0;
5225ac2d602Sdownsj 	int error = 0;
5235ac2d602Sdownsj 	u_char namlen;
5245ac2d602Sdownsj 
5255ac2d602Sdownsj #ifdef DIAGNOSTIC
5265ac2d602Sdownsj 	if ((tcnp->cn_flags & HASBUF) == 0 ||
5275ac2d602Sdownsj 	    (fcnp->cn_flags & HASBUF) == 0)
5285ac2d602Sdownsj 		panic("ext2fs_rename: no name");
5295ac2d602Sdownsj #endif
5305ac2d602Sdownsj 	/*
5315ac2d602Sdownsj 	 * Check for cross-device rename.
5325ac2d602Sdownsj 	 */
5335ac2d602Sdownsj 	if ((fvp->v_mount != tdvp->v_mount) ||
5345ac2d602Sdownsj 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
5355ac2d602Sdownsj 		error = EXDEV;
5365ac2d602Sdownsj abortit:
5375ac2d602Sdownsj 		VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
5385ac2d602Sdownsj 		if (tdvp == tvp)
5395ac2d602Sdownsj 			vrele(tdvp);
5405ac2d602Sdownsj 		else
5415ac2d602Sdownsj 			vput(tdvp);
5425ac2d602Sdownsj 		if (tvp)
5435ac2d602Sdownsj 			vput(tvp);
5445ac2d602Sdownsj 		VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
5455ac2d602Sdownsj 		vrele(fdvp);
5465ac2d602Sdownsj 		vrele(fvp);
5475ac2d602Sdownsj 		return (error);
5485ac2d602Sdownsj 	}
5495ac2d602Sdownsj 
5505ac2d602Sdownsj 	/*
5515ac2d602Sdownsj 	 * Check if just deleting a link name.
5525ac2d602Sdownsj 	 */
5535ac2d602Sdownsj 	if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
5545ac2d602Sdownsj 	    (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
5555ac2d602Sdownsj 		error = EPERM;
5565ac2d602Sdownsj 		goto abortit;
5575ac2d602Sdownsj 	}
5585ac2d602Sdownsj 	if (fvp == tvp) {
5595ac2d602Sdownsj 		if (fvp->v_type == VDIR) {
5605ac2d602Sdownsj 			error = EINVAL;
5615ac2d602Sdownsj 			goto abortit;
5625ac2d602Sdownsj 		}
5635ac2d602Sdownsj 
5645ac2d602Sdownsj 		/* Release destination completely. */
5655ac2d602Sdownsj 		VOP_ABORTOP(tdvp, tcnp);
5665ac2d602Sdownsj 		vput(tdvp);
5675ac2d602Sdownsj 		vput(tvp);
5685ac2d602Sdownsj 
5695ac2d602Sdownsj 		/* Delete source. */
5705ac2d602Sdownsj 		vrele(fdvp);
5715ac2d602Sdownsj 		vrele(fvp);
5725ac2d602Sdownsj 		fcnp->cn_flags &= ~MODMASK;
5735ac2d602Sdownsj 		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
5745ac2d602Sdownsj 		if ((fcnp->cn_flags & SAVESTART) == 0)
5755ac2d602Sdownsj 			panic("ext2fs_rename: lost from startdir");
5765ac2d602Sdownsj 		fcnp->cn_nameiop = DELETE;
5779baecc3aSthib 		(void) vfs_relookup(fdvp, &fvp, fcnp);
5785ac2d602Sdownsj 		return (VOP_REMOVE(fdvp, fvp, fcnp));
5795ac2d602Sdownsj 	}
580f5ee6277Sjasoni 	if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
5815ac2d602Sdownsj 		goto abortit;
5825ac2d602Sdownsj 	dp = VTOI(fdvp);
5835ac2d602Sdownsj 	ip = VTOI(fvp);
5844592dbefSmillert 	if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
5854592dbefSmillert 		VOP_UNLOCK(fvp, 0, p);
5864592dbefSmillert 		error = EMLINK;
5874592dbefSmillert 		goto abortit;
5884592dbefSmillert 	}
5895ac2d602Sdownsj 	if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
5905ac2d602Sdownsj 		(dp->i_e2fs_flags & EXT2_APPEND)) {
59107feb63cScsapuntz 		VOP_UNLOCK(fvp, 0, p);
5925ac2d602Sdownsj 		error = EPERM;
5935ac2d602Sdownsj 		goto abortit;
5945ac2d602Sdownsj 	}
5955ac2d602Sdownsj 	if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
5965ac2d602Sdownsj         	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
5975ac2d602Sdownsj         	if (!error && tvp)
598f5ee6277Sjasoni                 	error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred,
599f5ee6277Sjasoni 			    tcnp->cn_proc);
6005ac2d602Sdownsj         	if (error) {
60107feb63cScsapuntz                 	VOP_UNLOCK(fvp, 0, p);
6025ac2d602Sdownsj                 	error = EACCES;
6035ac2d602Sdownsj                 	goto abortit;
6045ac2d602Sdownsj         	}
6055ac2d602Sdownsj 		/*
6065ac2d602Sdownsj 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
6075ac2d602Sdownsj 		 */
6085ac2d602Sdownsj 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
609d56d3b9eSderaadt 		    dp == ip ||
610d56d3b9eSderaadt 			(fcnp->cn_flags&ISDOTDOT) ||
611d56d3b9eSderaadt 			(tcnp->cn_flags & ISDOTDOT) ||
6125ac2d602Sdownsj 		    (ip->i_flag & IN_RENAME)) {
61307feb63cScsapuntz 			VOP_UNLOCK(fvp, 0, p);
6145ac2d602Sdownsj 			error = EINVAL;
6155ac2d602Sdownsj 			goto abortit;
6165ac2d602Sdownsj 		}
6175ac2d602Sdownsj 		ip->i_flag |= IN_RENAME;
6185ac2d602Sdownsj 		oldparent = dp->i_number;
6195ac2d602Sdownsj 		doingdirectory++;
6205ac2d602Sdownsj 	}
6215ac2d602Sdownsj 	vrele(fdvp);
6225ac2d602Sdownsj 
6235ac2d602Sdownsj 	/*
6245ac2d602Sdownsj 	 * When the target exists, both the directory
6255ac2d602Sdownsj 	 * and target vnodes are returned locked.
6265ac2d602Sdownsj 	 */
6275ac2d602Sdownsj 	dp = VTOI(tdvp);
6285ac2d602Sdownsj 	xp = NULL;
6295ac2d602Sdownsj 	if (tvp)
6305ac2d602Sdownsj 		xp = VTOI(tvp);
6315ac2d602Sdownsj 
6325ac2d602Sdownsj 	/*
6335ac2d602Sdownsj 	 * 1) Bump link count while we're moving stuff
6345ac2d602Sdownsj 	 *    around.  If we crash somewhere before
6355ac2d602Sdownsj 	 *    completing our work, the link count
6365ac2d602Sdownsj 	 *    may be wrong, but correctable.
6375ac2d602Sdownsj 	 */
6385ac2d602Sdownsj 	ip->i_e2fs_nlink++;
6395ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
640d4fc1c49Sguenther 	if ((error = ext2fs_update(ip, 1)) != 0) {
64107feb63cScsapuntz 		VOP_UNLOCK(fvp, 0, p);
6425ac2d602Sdownsj 		goto bad;
6435ac2d602Sdownsj 	}
6445ac2d602Sdownsj 
6455ac2d602Sdownsj 	/*
6465ac2d602Sdownsj 	 * If ".." must be changed (ie the directory gets a new
6475ac2d602Sdownsj 	 * parent) then the source directory must not be in the
6482f4b598dStedu 	 * directory hierarchy above the target, as this would
6495ac2d602Sdownsj 	 * orphan everything below the source directory. Also
6505ac2d602Sdownsj 	 * the user must have write permission in the source so
6515ac2d602Sdownsj 	 * as to be able to change "..". We must repeat the call
6525ac2d602Sdownsj 	 * to namei, as the parent directory is unlocked by the
6535ac2d602Sdownsj 	 * call to checkpath().
6545ac2d602Sdownsj 	 */
6555ac2d602Sdownsj 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
65607feb63cScsapuntz 	VOP_UNLOCK(fvp, 0, p);
6575ac2d602Sdownsj 	if (oldparent != dp->i_number)
6585ac2d602Sdownsj 		newparent = dp->i_number;
6595ac2d602Sdownsj 	if (doingdirectory && newparent) {
6605ac2d602Sdownsj 		if (error)	/* write access check above */
6615ac2d602Sdownsj 			goto bad;
6625ac2d602Sdownsj 		if (xp != NULL)
6635ac2d602Sdownsj 			vput(tvp);
6645ac2d602Sdownsj 		error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
6655ac2d602Sdownsj 		if (error != 0)
6665ac2d602Sdownsj 			goto out;
6675ac2d602Sdownsj 		if ((tcnp->cn_flags & SAVESTART) == 0)
6685ac2d602Sdownsj 			panic("ext2fs_rename: lost to startdir");
6699baecc3aSthib 		if ((error = vfs_relookup(tdvp, &tvp, tcnp)) != 0)
6705ac2d602Sdownsj 			goto out;
6715ac2d602Sdownsj 		dp = VTOI(tdvp);
6725ac2d602Sdownsj 		xp = NULL;
6735ac2d602Sdownsj 		if (tvp)
6745ac2d602Sdownsj 			xp = VTOI(tvp);
6755ac2d602Sdownsj 	}
6765ac2d602Sdownsj 	/*
6775ac2d602Sdownsj 	 * 2) If target doesn't exist, link the target
6785ac2d602Sdownsj 	 *    to the source and unlink the source.
6795ac2d602Sdownsj 	 *    Otherwise, rewrite the target directory
6805ac2d602Sdownsj 	 *    entry to reference the source inode and
6815ac2d602Sdownsj 	 *    expunge the original entry's existence.
6825ac2d602Sdownsj 	 */
6835ac2d602Sdownsj 	if (xp == NULL) {
6845ac2d602Sdownsj 		if (dp->i_dev != ip->i_dev)
6855ac2d602Sdownsj 			panic("rename: EXDEV");
6865ac2d602Sdownsj 		/*
6875ac2d602Sdownsj 		 * Account for ".." in new directory.
6885ac2d602Sdownsj 		 * When source and destination have the same
6895ac2d602Sdownsj 		 * parent we don't fool with the link count.
6905ac2d602Sdownsj 		 */
6915ac2d602Sdownsj 		if (doingdirectory && newparent) {
6925ac2d602Sdownsj 			if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
6935ac2d602Sdownsj 				error = EMLINK;
6945ac2d602Sdownsj 				goto bad;
6955ac2d602Sdownsj 			}
6965ac2d602Sdownsj 			dp->i_e2fs_nlink++;
6975ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
698d4fc1c49Sguenther 			if ((error = ext2fs_update(dp, 1)) != 0)
6995ac2d602Sdownsj 				goto bad;
7005ac2d602Sdownsj 		}
7015ac2d602Sdownsj 		error = ext2fs_direnter(ip, tdvp, tcnp);
7025ac2d602Sdownsj 		if (error != 0) {
7035ac2d602Sdownsj 			if (doingdirectory && newparent) {
7045ac2d602Sdownsj 				dp->i_e2fs_nlink--;
7055ac2d602Sdownsj 				dp->i_flag |= IN_CHANGE;
706d4fc1c49Sguenther 				(void)ext2fs_update(dp, 1);
7075ac2d602Sdownsj 			}
7085ac2d602Sdownsj 			goto bad;
7095ac2d602Sdownsj 		}
7105ac2d602Sdownsj 		vput(tdvp);
7115ac2d602Sdownsj 	} else {
7125ac2d602Sdownsj 		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
7135ac2d602Sdownsj 			panic("rename: EXDEV");
7145ac2d602Sdownsj 		/*
7155ac2d602Sdownsj 		 * Short circuit rename(foo, foo).
7165ac2d602Sdownsj 		 */
7175ac2d602Sdownsj 		if (xp->i_number == ip->i_number)
7185ac2d602Sdownsj 			panic("rename: same file");
7195ac2d602Sdownsj 		/*
7205ac2d602Sdownsj 		 * If the parent directory is "sticky", then the user must
7215ac2d602Sdownsj 		 * own the parent directory, or the destination of the rename,
7225ac2d602Sdownsj 		 * otherwise the destination may not be changed (except by
7235ac2d602Sdownsj 		 * root). This implements append-only directories.
7245ac2d602Sdownsj 		 */
7255ac2d602Sdownsj 		if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
7265ac2d602Sdownsj 		    tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
7275ac2d602Sdownsj 		    xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
7285ac2d602Sdownsj 			error = EPERM;
7295ac2d602Sdownsj 			goto bad;
7305ac2d602Sdownsj 		}
7315ac2d602Sdownsj 		/*
7325ac2d602Sdownsj 		 * Target must be empty if a directory and have no links
7335ac2d602Sdownsj 		 * to it. Also, ensure source and target are compatible
7345ac2d602Sdownsj 		 * (both directories, or both not directories).
7355ac2d602Sdownsj 		 */
7365ac2d602Sdownsj 		if ((xp->i_e2fs_mode & IFMT) == IFDIR) {
7375ac2d602Sdownsj 			if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
7385ac2d602Sdownsj 				xp->i_e2fs_nlink > 2) {
7395ac2d602Sdownsj 				error = ENOTEMPTY;
7405ac2d602Sdownsj 				goto bad;
7415ac2d602Sdownsj 			}
7425ac2d602Sdownsj 			if (!doingdirectory) {
7435ac2d602Sdownsj 				error = ENOTDIR;
7445ac2d602Sdownsj 				goto bad;
7455ac2d602Sdownsj 			}
7465ac2d602Sdownsj 			cache_purge(tdvp);
7475ac2d602Sdownsj 		} else if (doingdirectory) {
7485ac2d602Sdownsj 			error = EISDIR;
7495ac2d602Sdownsj 			goto bad;
7505ac2d602Sdownsj 		}
7515ac2d602Sdownsj 		error = ext2fs_dirrewrite(dp, ip, tcnp);
7525ac2d602Sdownsj 		if (error != 0)
7535ac2d602Sdownsj 			goto bad;
7545ac2d602Sdownsj 		/*
7555ac2d602Sdownsj 		 * If the target directory is in the same
7565ac2d602Sdownsj 		 * directory as the source directory,
7575ac2d602Sdownsj 		 * decrement the link count on the parent
7585ac2d602Sdownsj 		 * of the target directory.
7595ac2d602Sdownsj 		 */
7605ac2d602Sdownsj 		 if (doingdirectory && !newparent) {
7615ac2d602Sdownsj 			dp->i_e2fs_nlink--;
7625ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
7635ac2d602Sdownsj 		}
7645ac2d602Sdownsj 		vput(tdvp);
7655ac2d602Sdownsj 		/*
7665ac2d602Sdownsj 		 * Adjust the link count of the target to
7675ac2d602Sdownsj 		 * reflect the dirrewrite above.  If this is
7685ac2d602Sdownsj 		 * a directory it is empty and there are
7695ac2d602Sdownsj 		 * no links to it, so we can squash the inode and
7705ac2d602Sdownsj 		 * any space associated with it.  We disallowed
7715ac2d602Sdownsj 		 * renaming over top of a directory with links to
7725ac2d602Sdownsj 		 * it above, as the remaining link would point to
7735ac2d602Sdownsj 		 * a directory without "." or ".." entries.
7745ac2d602Sdownsj 		 */
7755ac2d602Sdownsj 		xp->i_e2fs_nlink--;
7765ac2d602Sdownsj 		if (doingdirectory) {
7775ac2d602Sdownsj 			if (--xp->i_e2fs_nlink != 0)
7785ac2d602Sdownsj 				panic("rename: linked directory");
779b080ad39Scsapuntz 			error = ext2fs_truncate(xp, (off_t)0, IO_SYNC,
780b080ad39Scsapuntz 			    tcnp->cn_cred);
7815ac2d602Sdownsj 		}
7825ac2d602Sdownsj 		xp->i_flag |= IN_CHANGE;
7835ac2d602Sdownsj 		vput(tvp);
7845ac2d602Sdownsj 		xp = NULL;
7855ac2d602Sdownsj 	}
7865ac2d602Sdownsj 
7875ac2d602Sdownsj 	/*
7885ac2d602Sdownsj 	 * 3) Unlink the source.
7895ac2d602Sdownsj 	 */
7905ac2d602Sdownsj 	fcnp->cn_flags &= ~MODMASK;
7915ac2d602Sdownsj 	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
7925ac2d602Sdownsj 	if ((fcnp->cn_flags & SAVESTART) == 0)
7935ac2d602Sdownsj 		panic("ext2fs_rename: lost from startdir");
7949baecc3aSthib 	(void) vfs_relookup(fdvp, &fvp, fcnp);
7955ac2d602Sdownsj 	if (fvp != NULL) {
7965ac2d602Sdownsj 		xp = VTOI(fvp);
7975ac2d602Sdownsj 		dp = VTOI(fdvp);
7985ac2d602Sdownsj 	} else {
7995ac2d602Sdownsj 		/*
8005ac2d602Sdownsj 		 * From name has disappeared.
8015ac2d602Sdownsj 		 */
8025ac2d602Sdownsj 		if (doingdirectory)
8035ac2d602Sdownsj 			panic("ext2fs_rename: lost dir entry");
8045ac2d602Sdownsj 		vrele(ap->a_fvp);
8055ac2d602Sdownsj 		return (0);
8065ac2d602Sdownsj 	}
8075ac2d602Sdownsj 	/*
8085ac2d602Sdownsj 	 * Ensure that the directory entry still exists and has not
8095ac2d602Sdownsj 	 * changed while the new name has been entered. If the source is
8105ac2d602Sdownsj 	 * a file then the entry may have been unlinked or renamed. In
8115ac2d602Sdownsj 	 * either case there is no further work to be done. If the source
8125ac2d602Sdownsj 	 * is a directory then it cannot have been rmdir'ed; its link
8135ac2d602Sdownsj 	 * count of three would cause a rmdir to fail with ENOTEMPTY.
8145ac2d602Sdownsj 	 * The IRENAME flag ensures that it cannot be moved by another
8155ac2d602Sdownsj 	 * rename.
8165ac2d602Sdownsj 	 */
8175ac2d602Sdownsj 	if (xp != ip) {
8185ac2d602Sdownsj 		if (doingdirectory)
8195ac2d602Sdownsj 			panic("ext2fs_rename: lost dir entry");
8205ac2d602Sdownsj 	} else {
8215ac2d602Sdownsj 		/*
8225ac2d602Sdownsj 		 * If the source is a directory with a
8235ac2d602Sdownsj 		 * new parent, the link count of the old
8245ac2d602Sdownsj 		 * parent directory must be decremented
8255ac2d602Sdownsj 		 * and ".." set to point to the new parent.
8265ac2d602Sdownsj 		 */
8275ac2d602Sdownsj 		if (doingdirectory && newparent) {
8285ac2d602Sdownsj 			dp->i_e2fs_nlink--;
8295ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
8305ac2d602Sdownsj 			error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
8315ac2d602Sdownsj 				sizeof (struct ext2fs_dirtemplate), (off_t)0,
8325ac2d602Sdownsj 				UIO_SYSSPACE, IO_NODELOCKED,
833717ea6e5Soga 				tcnp->cn_cred, NULL, curproc);
8345ac2d602Sdownsj 			if (error == 0) {
8355ac2d602Sdownsj 					namlen = dirbuf.dotdot_namlen;
8365ac2d602Sdownsj 				if (namlen != 2 ||
8375ac2d602Sdownsj 				    dirbuf.dotdot_name[0] != '.' ||
8385ac2d602Sdownsj 				    dirbuf.dotdot_name[1] != '.') {
8395ac2d602Sdownsj 					ufs_dirbad(xp, (doff_t)12,
8405ac2d602Sdownsj 					    "ext2fs_rename: mangled dir");
8415ac2d602Sdownsj 				} else {
842f7dbefaaSpelikan 					dirbuf.dotdot_ino = htole32(newparent);
8435ac2d602Sdownsj 					(void) vn_rdwr(UIO_WRITE, fvp,
8445ac2d602Sdownsj 					    (caddr_t)&dirbuf,
8455ac2d602Sdownsj 					    sizeof (struct dirtemplate),
8465ac2d602Sdownsj 					    (off_t)0, UIO_SYSSPACE,
8475ac2d602Sdownsj 					    IO_NODELOCKED|IO_SYNC,
848717ea6e5Soga 					    tcnp->cn_cred, NULL, curproc);
8495ac2d602Sdownsj 					cache_purge(fdvp);
8505ac2d602Sdownsj 				}
8515ac2d602Sdownsj 			}
8525ac2d602Sdownsj 		}
8535ac2d602Sdownsj 		error = ext2fs_dirremove(fdvp, fcnp);
8545ac2d602Sdownsj 		if (!error) {
8555ac2d602Sdownsj 			xp->i_e2fs_nlink--;
8565ac2d602Sdownsj 			xp->i_flag |= IN_CHANGE;
8575ac2d602Sdownsj 		}
8585ac2d602Sdownsj 		xp->i_flag &= ~IN_RENAME;
8595ac2d602Sdownsj 	}
8605ac2d602Sdownsj 	if (dp)
8615ac2d602Sdownsj 		vput(fdvp);
8625ac2d602Sdownsj 	if (xp)
8635ac2d602Sdownsj 		vput(fvp);
8645ac2d602Sdownsj 	vrele(ap->a_fvp);
8655ac2d602Sdownsj 	return (error);
8665ac2d602Sdownsj 
8675ac2d602Sdownsj bad:
8685ac2d602Sdownsj 	if (xp)
8695ac2d602Sdownsj 		vput(ITOV(xp));
8705ac2d602Sdownsj 	vput(ITOV(dp));
8715ac2d602Sdownsj out:
8725ac2d602Sdownsj 	if (doingdirectory)
8735ac2d602Sdownsj 		ip->i_flag &= ~IN_RENAME;
874f5ee6277Sjasoni 	if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {
8755ac2d602Sdownsj 		ip->i_e2fs_nlink--;
8765ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
8775ac2d602Sdownsj 		vput(fvp);
8785ac2d602Sdownsj 	} else
8795ac2d602Sdownsj 		vrele(fvp);
8805ac2d602Sdownsj 	return (error);
8815ac2d602Sdownsj }
8825ac2d602Sdownsj 
8835ac2d602Sdownsj /*
8845ac2d602Sdownsj  * Mkdir system call
8855ac2d602Sdownsj  */
8865ac2d602Sdownsj int
8875f64cd9cSjasper ext2fs_mkdir(void *v)
8885ac2d602Sdownsj {
88999bc9d31Sderaadt 	struct vop_mkdir_args *ap = v;
8905f64cd9cSjasper 	struct vnode *dvp = ap->a_dvp;
8915f64cd9cSjasper 	struct vattr *vap = ap->a_vap;
8925f64cd9cSjasper 	struct componentname *cnp = ap->a_cnp;
8935f64cd9cSjasper 	struct inode *ip, *dp;
8945ac2d602Sdownsj 	struct vnode *tvp;
895f5ee6277Sjasoni 	struct ext2fs_dirtemplate dirtemplate;
896cc2fc615Smillert 	mode_t dmode;
897cc2fc615Smillert 	int error;
8985ac2d602Sdownsj 
8995ac2d602Sdownsj #ifdef DIAGNOSTIC
9005ac2d602Sdownsj 	if ((cnp->cn_flags & HASBUF) == 0)
9015ac2d602Sdownsj 		panic("ext2fs_mkdir: no name");
9025ac2d602Sdownsj #endif
9035ac2d602Sdownsj 	dp = VTOI(dvp);
9045ac2d602Sdownsj 	if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
9055ac2d602Sdownsj 		error = EMLINK;
9065ac2d602Sdownsj 		goto out;
9075ac2d602Sdownsj 	}
9081f3ff51cSdownsj 	dmode = vap->va_mode & ACCESSPERMS;
9095ac2d602Sdownsj 	dmode |= IFDIR;
9105ac2d602Sdownsj 	/*
9115ac2d602Sdownsj 	 * Must simulate part of ext2fs_makeinode here to acquire the inode,
9125ac2d602Sdownsj 	 * but not have it entered in the parent directory. The entry is
9135ac2d602Sdownsj 	 * made later after writing "." and ".." entries.
9145ac2d602Sdownsj 	 */
915b080ad39Scsapuntz 	if ((error = ext2fs_inode_alloc(dp, dmode, cnp->cn_cred, &tvp)) != 0)
9165ac2d602Sdownsj 		goto out;
9175ac2d602Sdownsj 	ip = VTOI(tvp);
9185ac2d602Sdownsj 	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
9195ac2d602Sdownsj 	ip->i_e2fs_gid = dp->i_e2fs_gid;
9205ac2d602Sdownsj 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
9215ac2d602Sdownsj 	ip->i_e2fs_mode = dmode;
9225ac2d602Sdownsj 	tvp->v_type = VDIR;	/* Rest init'd in getnewvnode(). */
9235ac2d602Sdownsj 	ip->i_e2fs_nlink = 2;
924d4fc1c49Sguenther 	error = ext2fs_update(ip, 1);
9255ac2d602Sdownsj 
9265ac2d602Sdownsj 	/*
9275ac2d602Sdownsj 	 * Bump link count in parent directory
9285ac2d602Sdownsj 	 * to reflect work done below.  Should
9295ac2d602Sdownsj 	 * be done before reference is created
9305ac2d602Sdownsj 	 * so reparation is possible if we crash.
9315ac2d602Sdownsj 	 */
9325ac2d602Sdownsj 	dp->i_e2fs_nlink++;
9335ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE;
934d4fc1c49Sguenther 	if ((error = ext2fs_update(dp, 1)) != 0)
9355ac2d602Sdownsj 		goto bad;
9365ac2d602Sdownsj 
9375ac2d602Sdownsj 	/* Initialize directory with "." and ".." from static template. */
9380f5c6c8bStedu 	memset(&dirtemplate, 0, sizeof(dirtemplate));
939f7dbefaaSpelikan 	dirtemplate.dot_ino = htole32(ip->i_number);
940f7dbefaaSpelikan 	dirtemplate.dot_reclen = htole16(12);
941f5ee6277Sjasoni 	dirtemplate.dot_namlen = 1;
942f5ee6277Sjasoni 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
943f5ee6277Sjasoni 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
944f5ee6277Sjasoni 		dirtemplate.dot_type = EXT2_FT_DIR;
945f5ee6277Sjasoni 	}
946f5ee6277Sjasoni 	dirtemplate.dot_name[0] = '.';
947f7dbefaaSpelikan 	dirtemplate.dotdot_ino = htole32(dp->i_number);
948f7dbefaaSpelikan 	dirtemplate.dotdot_reclen = htole16(VTOI(dvp)->i_e2fs->e2fs_bsize - 12);
949f5ee6277Sjasoni 	dirtemplate.dotdot_namlen = 2;
950f5ee6277Sjasoni 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
951f5ee6277Sjasoni 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
952f5ee6277Sjasoni 		dirtemplate.dotdot_type = EXT2_FT_DIR;
953f5ee6277Sjasoni 	}
954f5ee6277Sjasoni 	dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.';
9555ac2d602Sdownsj 	error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
9565ac2d602Sdownsj 	    sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
957717ea6e5Soga 	    IO_NODELOCKED|IO_SYNC, cnp->cn_cred, NULL, curproc);
9585ac2d602Sdownsj 	if (error) {
9595ac2d602Sdownsj 		dp->i_e2fs_nlink--;
9605ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE;
9615ac2d602Sdownsj 		goto bad;
9625ac2d602Sdownsj 	}
9635ac2d602Sdownsj 	if (VTOI(dvp)->i_e2fs->e2fs_bsize >
9645ac2d602Sdownsj 							VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
9655ac2d602Sdownsj 		panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
9665ac2d602Sdownsj 	else {
9674dcbbbb0Sniallo 		error = ext2fs_setsize(ip, VTOI(dvp)->i_e2fs->e2fs_bsize);
9684dcbbbb0Sniallo   	        if (error) {
9694dcbbbb0Sniallo   	        	dp->i_e2fs_nlink--;
9704dcbbbb0Sniallo   	        	dp->i_flag |= IN_CHANGE;
9714dcbbbb0Sniallo   	        	goto bad;
9724dcbbbb0Sniallo   	        }
9735ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
9745ac2d602Sdownsj 	}
9755ac2d602Sdownsj 
9760f1683a6Smiod 	/* Directory set up, now install its entry in the parent directory. */
9775ac2d602Sdownsj 	error = ext2fs_direnter(ip, dvp, cnp);
9785ac2d602Sdownsj 	if (error != 0) {
9795ac2d602Sdownsj 		dp->i_e2fs_nlink--;
9805ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE;
9815ac2d602Sdownsj 	}
9825ac2d602Sdownsj bad:
9835ac2d602Sdownsj 	/*
9845ac2d602Sdownsj 	 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
9855ac2d602Sdownsj 	 * for us because we set the link count to 0.
9865ac2d602Sdownsj 	 */
9875ac2d602Sdownsj 	if (error) {
9885ac2d602Sdownsj 		ip->i_e2fs_nlink = 0;
9895ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
9905ac2d602Sdownsj 		vput(tvp);
9915ac2d602Sdownsj 	} else
9925ac2d602Sdownsj 		*ap->a_vpp = tvp;
9935ac2d602Sdownsj out:
994dbe27ba0Stedu 	pool_put(&namei_pool, cnp->cn_pnbuf);
9955ac2d602Sdownsj 	vput(dvp);
9965ac2d602Sdownsj 	return (error);
9975ac2d602Sdownsj }
9985ac2d602Sdownsj 
9995ac2d602Sdownsj /*
10005ac2d602Sdownsj  * Rmdir system call.
10015ac2d602Sdownsj  */
10025ac2d602Sdownsj int
10035f64cd9cSjasper ext2fs_rmdir(void *v)
10045ac2d602Sdownsj {
100599bc9d31Sderaadt 	struct vop_rmdir_args *ap = v;
10065f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
10075f64cd9cSjasper 	struct vnode *dvp = ap->a_dvp;
10085f64cd9cSjasper 	struct componentname *cnp = ap->a_cnp;
10095f64cd9cSjasper 	struct inode *ip, *dp;
10105ac2d602Sdownsj 	int error;
10115ac2d602Sdownsj 
10125ac2d602Sdownsj 	ip = VTOI(vp);
10135ac2d602Sdownsj 	dp = VTOI(dvp);
10145ac2d602Sdownsj 	/*
10155ac2d602Sdownsj 	 * No rmdir "." please.
10165ac2d602Sdownsj 	 */
10175ac2d602Sdownsj 	if (dp == ip) {
10185ac2d602Sdownsj 		vrele(dvp);
10195ac2d602Sdownsj 		vput(vp);
10205ac2d602Sdownsj 		return (EINVAL);
10215ac2d602Sdownsj 	}
10225ac2d602Sdownsj 	/*
10235ac2d602Sdownsj 	 * Verify the directory is empty (and valid).
10245ac2d602Sdownsj 	 * (Rmdir ".." won't be valid since
10255ac2d602Sdownsj 	 *  ".." will contain a reference to
10265ac2d602Sdownsj 	 *  the current directory and thus be
10275ac2d602Sdownsj 	 *  non-empty.)
10285ac2d602Sdownsj 	 */
10295ac2d602Sdownsj 	error = 0;
10305ac2d602Sdownsj 	if (ip->i_e2fs_nlink != 2 ||
10315ac2d602Sdownsj 	    !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
10325ac2d602Sdownsj 		error = ENOTEMPTY;
10335ac2d602Sdownsj 		goto out;
10345ac2d602Sdownsj 	}
10355ac2d602Sdownsj 	if ((dp->i_e2fs_flags & EXT2_APPEND) ||
10365ac2d602Sdownsj 				 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
10375ac2d602Sdownsj 		error = EPERM;
10385ac2d602Sdownsj 		goto out;
10395ac2d602Sdownsj 	}
10405ac2d602Sdownsj 	/*
10415ac2d602Sdownsj 	 * Delete reference to directory before purging
10425ac2d602Sdownsj 	 * inode.  If we crash in between, the directory
10435ac2d602Sdownsj 	 * will be reattached to lost+found,
10445ac2d602Sdownsj 	 */
10455ac2d602Sdownsj 	error = ext2fs_dirremove(dvp, cnp);
10465ac2d602Sdownsj 	if (error != 0)
10475ac2d602Sdownsj 		goto out;
10485ac2d602Sdownsj 	dp->i_e2fs_nlink--;
10495ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE;
10505ac2d602Sdownsj 	cache_purge(dvp);
10515ac2d602Sdownsj 	vput(dvp);
10525ac2d602Sdownsj 	dvp = NULL;
10535ac2d602Sdownsj 	/*
10545ac2d602Sdownsj 	 * Truncate inode.  The only stuff left
10555ac2d602Sdownsj 	 * in the directory is "." and "..".  The
10565ac2d602Sdownsj 	 * "." reference is inconsequential since
10575ac2d602Sdownsj 	 * we're quashing it.  The ".." reference
10585ac2d602Sdownsj 	 * has already been adjusted above.  We've
10595ac2d602Sdownsj 	 * removed the "." reference and the reference
10605ac2d602Sdownsj 	 * in the parent directory, but there may be
10615ac2d602Sdownsj 	 * other hard links so decrement by 2 and
10625ac2d602Sdownsj 	 * worry about them later.
10635ac2d602Sdownsj 	 */
10645ac2d602Sdownsj 	ip->i_e2fs_nlink -= 2;
1065b080ad39Scsapuntz 	error = ext2fs_truncate(ip, (off_t)0, IO_SYNC, cnp->cn_cred);
10665ac2d602Sdownsj 	cache_purge(ITOV(ip));
10675ac2d602Sdownsj out:
10685ac2d602Sdownsj 	if (dvp)
10695ac2d602Sdownsj 		vput(dvp);
10705ac2d602Sdownsj 	vput(vp);
10715ac2d602Sdownsj 	return (error);
10725ac2d602Sdownsj }
10735ac2d602Sdownsj 
10745ac2d602Sdownsj /*
10755ac2d602Sdownsj  * symlink -- make a symbolic link
10765ac2d602Sdownsj  */
10775ac2d602Sdownsj int
10785f64cd9cSjasper ext2fs_symlink(void *v)
10795ac2d602Sdownsj {
108099bc9d31Sderaadt 	struct vop_symlink_args *ap = v;
10815f64cd9cSjasper 	struct vnode *vp, **vpp = ap->a_vpp;
10825f64cd9cSjasper 	struct inode *ip;
10835ac2d602Sdownsj 	int len, error;
10845ac2d602Sdownsj 
10855ac2d602Sdownsj 	error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
10865ac2d602Sdownsj 			      vpp, ap->a_cnp);
10875ac2d602Sdownsj 	if (error)
10885ac2d602Sdownsj 		return (error);
10895ac2d602Sdownsj 	vp = *vpp;
10905ac2d602Sdownsj 	len = strlen(ap->a_target);
10915ac2d602Sdownsj 	if (len < vp->v_mount->mnt_maxsymlinklen) {
10925ac2d602Sdownsj 		ip = VTOI(vp);
10930f5c6c8bStedu 		memcpy(ip->i_e2din->e2di_shortlink, ap->a_target, len);
10944dcbbbb0Sniallo 		error = ext2fs_setsize(ip, len);
10954dcbbbb0Sniallo 		if (error)
10964dcbbbb0Sniallo 			goto bad;
10975ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
10985ac2d602Sdownsj 	} else
10995ac2d602Sdownsj 		error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
11009b355cb2Smillert 		    UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL,
1101717ea6e5Soga 		    curproc);
11024dcbbbb0Sniallo bad:
11035ac2d602Sdownsj 	vput(vp);
11045ac2d602Sdownsj 	return (error);
11055ac2d602Sdownsj }
11065ac2d602Sdownsj 
11075ac2d602Sdownsj /*
11085ac2d602Sdownsj  * Return target name of a symbolic link
11095ac2d602Sdownsj  */
11105ac2d602Sdownsj int
11115f64cd9cSjasper ext2fs_readlink(void *v)
11125ac2d602Sdownsj {
111399bc9d31Sderaadt 	struct vop_readlink_args *ap = v;
11145f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
11155f64cd9cSjasper 	struct inode *ip = VTOI(vp);
11165ac2d602Sdownsj 	int isize;
11175ac2d602Sdownsj 
11184dcbbbb0Sniallo 	isize = ext2fs_size(ip);
11195ac2d602Sdownsj 	if (isize < vp->v_mount->mnt_maxsymlinklen ||
11205ac2d602Sdownsj 	    (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) {
11211330d409Smatthew 		return (uiomove((char *)ip->i_e2din->e2di_shortlink, isize,
11221330d409Smatthew 		    ap->a_uio));
11235ac2d602Sdownsj 	}
11245ac2d602Sdownsj 	return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
11255ac2d602Sdownsj }
11265ac2d602Sdownsj 
11275ac2d602Sdownsj /*
1128d4648cd6Sguenther  * Return POSIX pathconf information applicable to ext2 filesystems.
1129d4648cd6Sguenther  */
1130d4648cd6Sguenther int
1131d4648cd6Sguenther ext2fs_pathconf(void *v)
1132d4648cd6Sguenther {
1133d4648cd6Sguenther 	struct vop_pathconf_args *ap = v;
1134d4648cd6Sguenther 	int error = 0;
1135d4648cd6Sguenther 
1136d4648cd6Sguenther 	switch (ap->a_name) {
1137d4648cd6Sguenther 	case _PC_TIMESTAMP_RESOLUTION:
1138d4648cd6Sguenther 		*ap->a_retval = 1000000000;	/* 1 billion nanoseconds */
1139d4648cd6Sguenther 		break;
1140d4648cd6Sguenther 	default:
1141d4648cd6Sguenther 		return (ufs_pathconf(v));
1142d4648cd6Sguenther 	}
1143d4648cd6Sguenther 
1144d4648cd6Sguenther 	return (error);
1145d4648cd6Sguenther }
1146d4648cd6Sguenther 
1147d4648cd6Sguenther /*
11485ac2d602Sdownsj  * Advisory record locking support
11495ac2d602Sdownsj  */
11505ac2d602Sdownsj int
11515f64cd9cSjasper ext2fs_advlock(void *v)
11525ac2d602Sdownsj {
115399bc9d31Sderaadt 	struct vop_advlock_args *ap = v;
11545f64cd9cSjasper 	struct inode *ip = VTOI(ap->a_vp);
11555ac2d602Sdownsj 
11564dcbbbb0Sniallo 	return (lf_advlock(&ip->i_lockf, ext2fs_size(ip), ap->a_id, ap->a_op,
11575ac2d602Sdownsj 	    ap->a_fl, ap->a_flags));
11585ac2d602Sdownsj }
11595ac2d602Sdownsj 
11605ac2d602Sdownsj /*
11615ac2d602Sdownsj  * Allocate a new inode.
11625ac2d602Sdownsj  */
11635ac2d602Sdownsj int
11645f64cd9cSjasper ext2fs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
11655f64cd9cSjasper     struct componentname *cnp)
11665ac2d602Sdownsj {
11675f64cd9cSjasper 	struct inode *ip, *pdir;
11685ac2d602Sdownsj 	struct vnode *tvp;
11695ac2d602Sdownsj 	int error;
11705ac2d602Sdownsj 
11715ac2d602Sdownsj 	pdir = VTOI(dvp);
11725ac2d602Sdownsj #ifdef DIAGNOSTIC
11735ac2d602Sdownsj 	if ((cnp->cn_flags & HASBUF) == 0)
11745ac2d602Sdownsj 		panic("ext2fs_makeinode: no name");
11755ac2d602Sdownsj #endif
11765ac2d602Sdownsj 	*vpp = NULL;
11775ac2d602Sdownsj 	if ((mode & IFMT) == 0)
11785ac2d602Sdownsj 		mode |= IFREG;
11795ac2d602Sdownsj 
1180b080ad39Scsapuntz 	if ((error = ext2fs_inode_alloc(pdir, mode, cnp->cn_cred, &tvp))
1181b080ad39Scsapuntz 	    != 0) {
1182dbe27ba0Stedu 		pool_put(&namei_pool, cnp->cn_pnbuf);
11835ac2d602Sdownsj 		vput(dvp);
11845ac2d602Sdownsj 		return (error);
11855ac2d602Sdownsj 	}
11865ac2d602Sdownsj 	ip = VTOI(tvp);
11875ac2d602Sdownsj 	ip->i_e2fs_gid = pdir->i_e2fs_gid;
11885ac2d602Sdownsj 	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
11895ac2d602Sdownsj 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
11905ac2d602Sdownsj 	ip->i_e2fs_mode = mode;
11915ac2d602Sdownsj 	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
11925ac2d602Sdownsj 	ip->i_e2fs_nlink = 1;
11935ac2d602Sdownsj 	if ((ip->i_e2fs_mode & ISGID) &&
11945ac2d602Sdownsj 		!groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
1195ad392958Stedu 	    suser_ucred(cnp->cn_cred))
11965ac2d602Sdownsj 		ip->i_e2fs_mode &= ~ISGID;
11975ac2d602Sdownsj 
11985ac2d602Sdownsj 	/*
11995ac2d602Sdownsj 	 * Make sure inode goes to disk before directory entry.
12005ac2d602Sdownsj 	 */
1201d4fc1c49Sguenther 	if ((error = ext2fs_update(ip, 1)) != 0)
12025ac2d602Sdownsj 		goto bad;
12035ac2d602Sdownsj 	error = ext2fs_direnter(ip, dvp, cnp);
12045ac2d602Sdownsj 	if (error != 0)
12055ac2d602Sdownsj 		goto bad;
12065ac2d602Sdownsj 	if ((cnp->cn_flags & SAVESTART) == 0)
1207dbe27ba0Stedu 		pool_put(&namei_pool, cnp->cn_pnbuf);
12085ac2d602Sdownsj 	vput(dvp);
12095ac2d602Sdownsj 	*vpp = tvp;
12105ac2d602Sdownsj 	return (0);
12115ac2d602Sdownsj 
12125ac2d602Sdownsj bad:
12135ac2d602Sdownsj 	/*
12145ac2d602Sdownsj 	 * Write error occurred trying to update the inode
12155ac2d602Sdownsj 	 * or the directory so must deallocate the inode.
12165ac2d602Sdownsj 	 */
1217dbe27ba0Stedu 	pool_put(&namei_pool, cnp->cn_pnbuf);
12185ac2d602Sdownsj 	vput(dvp);
12195ac2d602Sdownsj 	ip->i_e2fs_nlink = 0;
12205ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
1221e318f6c7Stedu 	tvp->v_type = VNON;
12225ac2d602Sdownsj 	vput(tvp);
12235ac2d602Sdownsj 	return (error);
12245ac2d602Sdownsj }
12255ac2d602Sdownsj 
12265ac2d602Sdownsj /*
12277dc61945Sdownsj  * Synch an open file.
12287dc61945Sdownsj  */
12297dc61945Sdownsj /* ARGSUSED */
12307dc61945Sdownsj int
12315f64cd9cSjasper ext2fs_fsync(void *v)
12327dc61945Sdownsj {
123399bc9d31Sderaadt 	struct vop_fsync_args *ap = v;
12345f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
12357dc61945Sdownsj 
12367dc61945Sdownsj 	vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
1237d4fc1c49Sguenther 	return (ext2fs_update(VTOI(ap->a_vp), ap->a_waitfor == MNT_WAIT));
12387dc61945Sdownsj }
12397dc61945Sdownsj 
12407dc61945Sdownsj /*
12415ac2d602Sdownsj  * Reclaim an inode so that it can be used for other purposes.
12425ac2d602Sdownsj  */
12435ac2d602Sdownsj int
12445f64cd9cSjasper ext2fs_reclaim(void *v)
12455ac2d602Sdownsj {
124699bc9d31Sderaadt 	struct vop_reclaim_args *ap = v;
12475f64cd9cSjasper 	struct vnode *vp = ap->a_vp;
12485ac2d602Sdownsj 	struct inode *ip;
12494df17c52Spedro #ifdef DIAGNOSTIC
12505ac2d602Sdownsj 	extern int prtactive;
12515ac2d602Sdownsj 
12525ac2d602Sdownsj 	if (prtactive && vp->v_usecount != 0)
12535ac2d602Sdownsj 		vprint("ext2fs_reclaim: pushing active", vp);
12544df17c52Spedro #endif
1255d2e0ba60Spedro 
12565ac2d602Sdownsj 	/*
12575ac2d602Sdownsj 	 * Remove the inode from its hash chain.
12585ac2d602Sdownsj 	 */
12595ac2d602Sdownsj 	ip = VTOI(vp);
12605ac2d602Sdownsj 	ufs_ihashrem(ip);
1261d2e0ba60Spedro 
12625ac2d602Sdownsj 	/*
12635ac2d602Sdownsj 	 * Purge old data structures associated with the inode.
12645ac2d602Sdownsj 	 */
12655ac2d602Sdownsj 	cache_purge(vp);
1266d2e0ba60Spedro 	if (ip->i_devvp)
12675ac2d602Sdownsj 		vrele(ip->i_devvp);
12685ac2d602Sdownsj 
12692374824dSpedro 	if (ip->i_e2din != NULL)
12702374824dSpedro 		pool_put(&ext2fs_dinode_pool, ip->i_e2din);
12712374824dSpedro 
1272b7ade7a9Spedro 	pool_put(&ext2fs_inode_pool, ip);
12732374824dSpedro 
12745ac2d602Sdownsj 	vp->v_data = NULL;
1275d2e0ba60Spedro 
12765ac2d602Sdownsj 	return (0);
12775ac2d602Sdownsj }
12785ac2d602Sdownsj 
12795ac2d602Sdownsj /* Global vfs data structures for ext2fs. */
1280dc81e71aSthib struct vops ext2fs_vops = {
1281dc81e71aSthib         .vop_lookup     = ext2fs_lookup,
1282dc81e71aSthib         .vop_create     = ext2fs_create,
1283dc81e71aSthib         .vop_mknod      = ext2fs_mknod,
1284dc81e71aSthib         .vop_open       = ext2fs_open,
1285dc81e71aSthib         .vop_close      = ufs_close,
1286dc81e71aSthib         .vop_access     = ext2fs_access,
1287dc81e71aSthib         .vop_getattr    = ext2fs_getattr,
1288dc81e71aSthib         .vop_setattr    = ext2fs_setattr,
1289dc81e71aSthib         .vop_read       = ext2fs_read,
1290dc81e71aSthib         .vop_write      = ext2fs_write,
1291dc81e71aSthib         .vop_ioctl      = ufs_ioctl,
1292dc81e71aSthib         .vop_poll       = ufs_poll,
1293dc81e71aSthib         .vop_kqfilter   = vop_generic_kqfilter,
1294dc81e71aSthib         .vop_fsync      = ext2fs_fsync,
1295dc81e71aSthib         .vop_remove     = ext2fs_remove,
1296dc81e71aSthib         .vop_link       = ext2fs_link,
1297dc81e71aSthib         .vop_rename     = ext2fs_rename,
1298dc81e71aSthib         .vop_mkdir      = ext2fs_mkdir,
1299dc81e71aSthib         .vop_rmdir      = ext2fs_rmdir,
1300dc81e71aSthib         .vop_symlink    = ext2fs_symlink,
1301dc81e71aSthib         .vop_readdir    = ext2fs_readdir,
1302dc81e71aSthib         .vop_readlink   = ext2fs_readlink,
1303dc81e71aSthib         .vop_abortop    = vop_generic_abortop,
1304dc81e71aSthib         .vop_inactive   = ext2fs_inactive,
1305dc81e71aSthib         .vop_reclaim    = ext2fs_reclaim,
1306dc81e71aSthib         .vop_lock       = ufs_lock,
1307dc81e71aSthib         .vop_unlock     = ufs_unlock,
1308dc81e71aSthib         .vop_bmap       = ext2fs_bmap,
1309dc81e71aSthib         .vop_strategy   = ufs_strategy,
1310dc81e71aSthib         .vop_print      = ufs_print,
1311dc81e71aSthib         .vop_islocked   = ufs_islocked,
1312d4648cd6Sguenther         .vop_pathconf   = ext2fs_pathconf,
1313dc81e71aSthib         .vop_advlock    = ext2fs_advlock,
1314dc81e71aSthib         .vop_bwrite     = vop_generic_bwrite
13155ac2d602Sdownsj };
13165ac2d602Sdownsj 
1317dc81e71aSthib struct vops ext2fs_specvops = {
1318dc81e71aSthib         .vop_close      = ufsspec_close,
1319dc81e71aSthib         .vop_access     = ext2fs_access,
1320dc81e71aSthib         .vop_getattr    = ext2fs_getattr,
1321dc81e71aSthib         .vop_setattr    = ext2fs_setattr,
1322dc81e71aSthib         .vop_read       = ufsspec_read,
1323dc81e71aSthib         .vop_write      = ufsspec_write,
1324dc81e71aSthib         .vop_fsync      = ext2fs_fsync,
1325dc81e71aSthib         .vop_inactive   = ext2fs_inactive,
1326dc81e71aSthib         .vop_reclaim    = ext2fs_reclaim,
1327dc81e71aSthib         .vop_lock       = ufs_lock,
1328dc81e71aSthib         .vop_unlock     = ufs_unlock,
1329dc81e71aSthib         .vop_print      = ufs_print,
1330dc81e71aSthib         .vop_islocked   = ufs_islocked,
1331dc81e71aSthib 
1332dc81e71aSthib         /* XXX: Keep in sync with spec_vops. */
1333dc81e71aSthib 	.vop_lookup	= vop_generic_lookup,
1334dc81e71aSthib 	.vop_create	= spec_badop,
1335dc81e71aSthib 	.vop_mknod	= spec_badop,
1336dc81e71aSthib 	.vop_open	= spec_open,
1337dc81e71aSthib 	.vop_ioctl	= spec_ioctl,
1338dc81e71aSthib 	.vop_poll	= spec_poll,
1339dc81e71aSthib 	.vop_kqfilter	= spec_kqfilter,
1340dc81e71aSthib 	.vop_revoke	= vop_generic_revoke,
1341dc81e71aSthib 	.vop_remove	= spec_badop,
1342dc81e71aSthib 	.vop_link	= spec_badop,
1343dc81e71aSthib 	.vop_rename	= spec_badop,
1344dc81e71aSthib 	.vop_mkdir	= spec_badop,
1345dc81e71aSthib 	.vop_rmdir	= spec_badop,
1346dc81e71aSthib 	.vop_symlink	= spec_badop,
1347dc81e71aSthib 	.vop_readdir	= spec_badop,
1348dc81e71aSthib 	.vop_readlink	= spec_badop,
1349dc81e71aSthib 	.vop_abortop	= spec_badop,
1350dc81e71aSthib 	.vop_bmap	= vop_generic_bmap,
1351dc81e71aSthib 	.vop_strategy	= spec_strategy,
1352dc81e71aSthib 	.vop_pathconf	= spec_pathconf,
1353dc81e71aSthib 	.vop_advlock	= spec_advlock,
1354dc81e71aSthib 	.vop_bwrite	= vop_generic_bwrite,
13555ac2d602Sdownsj };
13565ac2d602Sdownsj 
13575ac2d602Sdownsj #ifdef FIFO
1358dc81e71aSthib struct vops ext2fs_fifovops = {
1359dc81e71aSthib         .vop_close      = ufsfifo_close,
1360dc81e71aSthib         .vop_access     = ufsfifo_close,
1361dc81e71aSthib         .vop_getattr    = ext2fs_getattr,
1362dc81e71aSthib         .vop_setattr    = ext2fs_setattr,
1363dc81e71aSthib         .vop_read       = ufsfifo_read,
1364dc81e71aSthib         .vop_write      = ufsfifo_write,
1365dc81e71aSthib         .vop_fsync      = ext2fs_fsync,
1366dc81e71aSthib         .vop_inactive   = ext2fs_inactive,
1367dc81e71aSthib         .vop_reclaim    = ext2fsfifo_reclaim,
1368dc81e71aSthib         .vop_lock       = ufs_lock,
1369dc81e71aSthib         .vop_unlock     = ufs_unlock,
1370dc81e71aSthib         .vop_print      = ufs_print,
1371dc81e71aSthib         .vop_islocked   = ufs_islocked,
1372dc81e71aSthib         .vop_bwrite     = vop_generic_bwrite,
1373dc81e71aSthib 
1374dc81e71aSthib         /* XXX: Keep in sync with fifo_vops */
1375dc81e71aSthib 	.vop_lookup	= vop_generic_lookup,
1376dc81e71aSthib 	.vop_create	= fifo_badop,
1377dc81e71aSthib 	.vop_mknod	= fifo_badop,
1378dc81e71aSthib 	.vop_open	= fifo_open,
1379dc81e71aSthib 	.vop_ioctl	= fifo_ioctl,
1380dc81e71aSthib 	.vop_poll	= fifo_poll,
1381dc81e71aSthib 	.vop_kqfilter	= fifo_kqfilter,
1382dc81e71aSthib 	.vop_revoke	= vop_generic_revoke,
1383dc81e71aSthib 	.vop_remove	= fifo_badop,
1384dc81e71aSthib 	.vop_link	= fifo_badop,
1385dc81e71aSthib 	.vop_rename	= fifo_badop,
1386dc81e71aSthib 	.vop_mkdir	= fifo_badop,
1387dc81e71aSthib 	.vop_rmdir	= fifo_badop,
1388dc81e71aSthib 	.vop_symlink	= fifo_badop,
1389dc81e71aSthib 	.vop_readdir	= fifo_badop,
1390dc81e71aSthib 	.vop_readlink	= fifo_badop,
1391dc81e71aSthib 	.vop_abortop	= fifo_badop,
1392dc81e71aSthib 	.vop_bmap	= vop_generic_bmap,
1393dc81e71aSthib 	.vop_strategy	= fifo_badop,
1394dc81e71aSthib 	.vop_pathconf	= fifo_pathconf,
1395dc81e71aSthib 	.vop_advlock	= fifo_advlock,
13965ac2d602Sdownsj };
13971816238dStedu 
13981816238dStedu int
13991816238dStedu ext2fsfifo_reclaim(void *v)
14001816238dStedu {
14011816238dStedu 	fifo_reclaim(v);
14021816238dStedu 	return (ext2fs_reclaim(v));
14031816238dStedu }
14045ac2d602Sdownsj #endif /* FIFO */
1405