xref: /openbsd/sys/ufs/ext2fs/ext2fs_vnops.c (revision 07feb63c)
1*07feb63cScsapuntz /*	$OpenBSD: ext2fs_vnops.c,v 1.6 1997/11/06 05:59:16 csapuntz Exp $	*/
21f3ff51cSdownsj /*	$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.
225ac2d602Sdownsj  * 3. All advertising materials mentioning features or use of this software
235ac2d602Sdownsj  *    must display the following acknowledgement:
245ac2d602Sdownsj  *	This product includes software developed by the University of
255ac2d602Sdownsj  *	California, Berkeley and its contributors.
265ac2d602Sdownsj  * 4. Neither the name of the University nor the names of its contributors
275ac2d602Sdownsj  *    may be used to endorse or promote products derived from this software
285ac2d602Sdownsj  *    without specific prior written permission.
295ac2d602Sdownsj  *
305ac2d602Sdownsj  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
315ac2d602Sdownsj  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
325ac2d602Sdownsj  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
335ac2d602Sdownsj  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
345ac2d602Sdownsj  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
355ac2d602Sdownsj  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
365ac2d602Sdownsj  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
375ac2d602Sdownsj  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
385ac2d602Sdownsj  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
395ac2d602Sdownsj  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
405ac2d602Sdownsj  * SUCH DAMAGE.
415ac2d602Sdownsj  *
425ac2d602Sdownsj  *	@(#)ufs_vnops.c	8.14 (Berkeley) 10/26/94
431f3ff51cSdownsj  * Modified for ext2fs by Manuel Bouyer.
445ac2d602Sdownsj  */
455ac2d602Sdownsj 
465ac2d602Sdownsj #include <sys/param.h>
475ac2d602Sdownsj #include <sys/systm.h>
485ac2d602Sdownsj #include <sys/resourcevar.h>
495ac2d602Sdownsj #include <sys/kernel.h>
505ac2d602Sdownsj #include <sys/file.h>
515ac2d602Sdownsj #include <sys/stat.h>
525ac2d602Sdownsj #include <sys/buf.h>
535ac2d602Sdownsj #include <sys/proc.h>
545ac2d602Sdownsj #include <sys/conf.h>
555ac2d602Sdownsj #include <sys/mount.h>
565ac2d602Sdownsj #include <sys/namei.h>
575ac2d602Sdownsj #include <sys/vnode.h>
585ac2d602Sdownsj #include <sys/lockf.h>
595ac2d602Sdownsj #include <sys/malloc.h>
605ac2d602Sdownsj #include <sys/signalvar.h>
615ac2d602Sdownsj 
625ac2d602Sdownsj #include <vm/vm.h>
635ac2d602Sdownsj 
645ac2d602Sdownsj #include <miscfs/fifofs/fifo.h>
655ac2d602Sdownsj #include <miscfs/specfs/specdev.h>
665ac2d602Sdownsj 
675ac2d602Sdownsj #include <ufs/ufs/quota.h>
685ac2d602Sdownsj #include <ufs/ufs/inode.h>
695ac2d602Sdownsj #include <ufs/ufs/ufs_extern.h>
705ac2d602Sdownsj #include <ufs/ufs/ufsmount.h>
715ac2d602Sdownsj 
725ac2d602Sdownsj #include <ufs/ext2fs/ext2fs.h>
735ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_extern.h>
745ac2d602Sdownsj #include <ufs/ext2fs/ext2fs_dir.h>
755ac2d602Sdownsj 
765ac2d602Sdownsj 
775ac2d602Sdownsj static int ext2fs_chmod
785ac2d602Sdownsj 	__P((struct vnode *, int, struct ucred *, struct proc *));
795ac2d602Sdownsj static int ext2fs_chown
805ac2d602Sdownsj 	__P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *));
815ac2d602Sdownsj 
825ac2d602Sdownsj union _qcvt {
835ac2d602Sdownsj 	int64_t	qcvt;
845ac2d602Sdownsj 	int32_t val[2];
855ac2d602Sdownsj };
865ac2d602Sdownsj #define SETHIGH(q, h) { \
875ac2d602Sdownsj 	union _qcvt tmp; \
885ac2d602Sdownsj 	tmp.qcvt = (q); \
895ac2d602Sdownsj 	tmp.val[_QUAD_HIGHWORD] = (h); \
905ac2d602Sdownsj 	(q) = tmp.qcvt; \
915ac2d602Sdownsj }
925ac2d602Sdownsj #define SETLOW(q, l) { \
935ac2d602Sdownsj 	union _qcvt tmp; \
945ac2d602Sdownsj 	tmp.qcvt = (q); \
955ac2d602Sdownsj 	tmp.val[_QUAD_LOWWORD] = (l); \
965ac2d602Sdownsj 	(q) = tmp.qcvt; \
975ac2d602Sdownsj }
985ac2d602Sdownsj 
995ac2d602Sdownsj /*
1005ac2d602Sdownsj  * Create a regular file
1015ac2d602Sdownsj  */
1025ac2d602Sdownsj int
1035ac2d602Sdownsj ext2fs_create(v)
1045ac2d602Sdownsj 	void *v;
1055ac2d602Sdownsj {
1065ac2d602Sdownsj 	struct vop_create_args /* {
1075ac2d602Sdownsj 		struct vnode *a_dvp;
1085ac2d602Sdownsj 		struct vnode **a_vpp;
1095ac2d602Sdownsj 		struct componentname *a_cnp;
1105ac2d602Sdownsj 		struct vattr *a_vap;
1115ac2d602Sdownsj 	} */ *ap = v;
1125ac2d602Sdownsj 	return
1135ac2d602Sdownsj 		ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
1145ac2d602Sdownsj 			  ap->a_dvp, ap->a_vpp, ap->a_cnp);
1155ac2d602Sdownsj }
1165ac2d602Sdownsj 
1175ac2d602Sdownsj /*
1185ac2d602Sdownsj  * Mknod vnode call
1195ac2d602Sdownsj  */
1205ac2d602Sdownsj /* ARGSUSED */
1215ac2d602Sdownsj int
1225ac2d602Sdownsj ext2fs_mknod(v)
1235ac2d602Sdownsj 	void *v;
1245ac2d602Sdownsj {
1255ac2d602Sdownsj 	struct vop_mknod_args /* {
1265ac2d602Sdownsj 		struct vnode *a_dvp;
1275ac2d602Sdownsj 		struct vnode **a_vpp;
1285ac2d602Sdownsj 		struct componentname *a_cnp;
1295ac2d602Sdownsj 		struct vattr *a_vap;
1305ac2d602Sdownsj 	} */ *ap = v;
1315ac2d602Sdownsj 	register struct vattr *vap = ap->a_vap;
1325ac2d602Sdownsj 	register struct vnode **vpp = ap->a_vpp;
1335ac2d602Sdownsj 	register struct inode *ip;
1345ac2d602Sdownsj 	int error;
1355ac2d602Sdownsj 
1365ac2d602Sdownsj 	if ((error =
1375ac2d602Sdownsj 		ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
1385ac2d602Sdownsj 		ap->a_dvp, vpp, ap->a_cnp)) != 0)
1395ac2d602Sdownsj 		return (error);
1405ac2d602Sdownsj 	ip = VTOI(*vpp);
1415ac2d602Sdownsj 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1425ac2d602Sdownsj 	if (vap->va_rdev != VNOVAL) {
1435ac2d602Sdownsj 		/*
1445ac2d602Sdownsj 		 * Want to be able to use this to make badblock
1455ac2d602Sdownsj 		 * inodes, so don't truncate the dev number.
1465ac2d602Sdownsj 		 */
1475ac2d602Sdownsj 		ip->i_din.e2fs_din.e2di_rdev = vap->va_rdev;
1485ac2d602Sdownsj 	}
1495ac2d602Sdownsj 	/*
1505ac2d602Sdownsj 	 * Remove inode so that it will be reloaded by VFS_VGET and
1515ac2d602Sdownsj 	 * checked to see if it is an alias of an existing entry in
1525ac2d602Sdownsj 	 * the inode cache.
1535ac2d602Sdownsj 	 */
1545ac2d602Sdownsj 	vput(*vpp);
1555ac2d602Sdownsj 	(*vpp)->v_type = VNON;
1565ac2d602Sdownsj 	vgone(*vpp);
1575ac2d602Sdownsj 	*vpp = 0;
1585ac2d602Sdownsj 	return (0);
1595ac2d602Sdownsj }
1605ac2d602Sdownsj 
1615ac2d602Sdownsj /*
1625ac2d602Sdownsj  * Open called.
1635ac2d602Sdownsj  *
1645ac2d602Sdownsj  * Just check the APPEND flag.
1655ac2d602Sdownsj  */
1665ac2d602Sdownsj /* ARGSUSED */
1675ac2d602Sdownsj int
1685ac2d602Sdownsj ext2fs_open(v)
1695ac2d602Sdownsj 	void *v;
1705ac2d602Sdownsj {
1715ac2d602Sdownsj 	struct vop_open_args /* {
1725ac2d602Sdownsj 		struct vnode *a_vp;
1735ac2d602Sdownsj 		int  a_mode;
1745ac2d602Sdownsj 		struct ucred *a_cred;
1755ac2d602Sdownsj 		struct proc *a_p;
1765ac2d602Sdownsj 	} */ *ap = v;
1775ac2d602Sdownsj 
1785ac2d602Sdownsj 	/*
1795ac2d602Sdownsj 	 * Files marked append-only must be opened for appending.
1805ac2d602Sdownsj 	 */
1815ac2d602Sdownsj 	if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
1825ac2d602Sdownsj 		(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
1835ac2d602Sdownsj 		return (EPERM);
1845ac2d602Sdownsj 	return (0);
1855ac2d602Sdownsj }
1865ac2d602Sdownsj 
1875ac2d602Sdownsj int
1885ac2d602Sdownsj ext2fs_access(v)
1895ac2d602Sdownsj 	void *v;
1905ac2d602Sdownsj {
1915ac2d602Sdownsj 	struct vop_access_args /* {
1925ac2d602Sdownsj 		struct vnode *a_vp;
1935ac2d602Sdownsj 		int  a_mode;
1945ac2d602Sdownsj 		struct ucred *a_cred;
1955ac2d602Sdownsj 		struct proc *a_p;
1965ac2d602Sdownsj 	} */ *ap = v;
1975ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
1985ac2d602Sdownsj 	register struct inode *ip = VTOI(vp);
1995ac2d602Sdownsj 	mode_t mode = ap->a_mode;
2005ac2d602Sdownsj 
2015ac2d602Sdownsj #ifdef DIAGNOSTIC
2025ac2d602Sdownsj 	if (!VOP_ISLOCKED(vp)) {
2035ac2d602Sdownsj 		vprint("ext2fs_access: not locked", vp);
2045ac2d602Sdownsj 		panic("ext2fs_access: not locked");
2055ac2d602Sdownsj 	}
2065ac2d602Sdownsj #endif
2075ac2d602Sdownsj 
2085ac2d602Sdownsj 	/* If immutable bit set, nobody gets to write it. */
2095ac2d602Sdownsj 	if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
2105ac2d602Sdownsj 		return (EPERM);
2115ac2d602Sdownsj 
2125ed759e3Sdownsj 	return (vaccess(ip->i_e2fs_mode, ip->i_e2fs_uid, ip->i_e2fs_gid, mode,
2135ac2d602Sdownsj 			ap->a_cred));
2145ac2d602Sdownsj }
2155ac2d602Sdownsj 
2165ac2d602Sdownsj /* ARGSUSED */
2175ac2d602Sdownsj int
2185ac2d602Sdownsj ext2fs_getattr(v)
2195ac2d602Sdownsj 	void *v;
2205ac2d602Sdownsj {
2215ac2d602Sdownsj 	struct vop_getattr_args /* {
2225ac2d602Sdownsj 		struct vnode *a_vp;
2235ac2d602Sdownsj 		struct vattr *a_vap;
2245ac2d602Sdownsj 		struct ucred *a_cred;
2255ac2d602Sdownsj 		struct proc *a_p;
2265ac2d602Sdownsj 	} */ *ap = v;
2275ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
2285ac2d602Sdownsj 	register struct inode *ip = VTOI(vp);
2295ac2d602Sdownsj 	register struct vattr *vap = ap->a_vap;
2305ac2d602Sdownsj 
2317dc61945Sdownsj 	EXT2FS_ITIMES(ip, &time, &time);
2325ac2d602Sdownsj 	/*
2335ac2d602Sdownsj 	 * Copy from inode table
2345ac2d602Sdownsj 	 */
2355ac2d602Sdownsj 	vap->va_fsid = ip->i_dev;
2365ac2d602Sdownsj 	vap->va_fileid = ip->i_number;
2371f3ff51cSdownsj 	vap->va_mode = ip->i_e2fs_mode & ALLPERMS;
2385ac2d602Sdownsj 	vap->va_nlink = ip->i_e2fs_nlink;
2395ac2d602Sdownsj 	vap->va_uid = ip->i_e2fs_uid;
2405ac2d602Sdownsj 	vap->va_gid = ip->i_e2fs_gid;
2415ac2d602Sdownsj 	vap->va_rdev = (dev_t)ip->i_din.e2fs_din.e2di_rdev;
2425ac2d602Sdownsj 	vap->va_size = ip->i_e2fs_size;
2435ac2d602Sdownsj 	vap->va_atime.tv_sec = ip->i_e2fs_atime;
2445ac2d602Sdownsj 	vap->va_atime.tv_nsec = 0;
2455ac2d602Sdownsj 	vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
2465ac2d602Sdownsj 	vap->va_mtime.tv_nsec = 0;
2475ac2d602Sdownsj 	vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
2485ac2d602Sdownsj 	vap->va_ctime.tv_nsec = 0;
2495ac2d602Sdownsj #ifdef EXT2FS_SYSTEM_FLAGS
2505ac2d602Sdownsj 	vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
2515ac2d602Sdownsj 	vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
2525ac2d602Sdownsj #else
2535ac2d602Sdownsj 	vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
2545ac2d602Sdownsj 	vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
2555ac2d602Sdownsj #endif
2565ac2d602Sdownsj 	vap->va_gen = ip->i_e2fs_gen;
2575ac2d602Sdownsj 	/* this doesn't belong here */
2585ac2d602Sdownsj 	if (vp->v_type == VBLK)
2595ac2d602Sdownsj 		vap->va_blocksize = BLKDEV_IOSIZE;
2605ac2d602Sdownsj 	else if (vp->v_type == VCHR)
2615ac2d602Sdownsj 		vap->va_blocksize = MAXBSIZE;
2625ac2d602Sdownsj 	else
2635ac2d602Sdownsj 		vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
2645ac2d602Sdownsj 	vap->va_bytes = dbtob(ip->i_e2fs_nblock);
2655ac2d602Sdownsj 	vap->va_type = vp->v_type;
2665ac2d602Sdownsj 	vap->va_filerev = ip->i_modrev;
2675ac2d602Sdownsj 	return (0);
2685ac2d602Sdownsj }
2695ac2d602Sdownsj 
2705ac2d602Sdownsj /*
2715ac2d602Sdownsj  * Set attribute vnode op. called from several syscalls
2725ac2d602Sdownsj  */
2735ac2d602Sdownsj int
2745ac2d602Sdownsj ext2fs_setattr(v)
2755ac2d602Sdownsj 	void *v;
2765ac2d602Sdownsj {
2775ac2d602Sdownsj 	struct vop_setattr_args /* {
2785ac2d602Sdownsj 		struct vnode *a_vp;
2795ac2d602Sdownsj 		struct vattr *a_vap;
2805ac2d602Sdownsj 		struct ucred *a_cred;
2815ac2d602Sdownsj 		struct proc *a_p;
2825ac2d602Sdownsj 	} */ *ap = v;
2835ac2d602Sdownsj 	register struct vattr *vap = ap->a_vap;
2845ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
2855ac2d602Sdownsj 	register struct inode *ip = VTOI(vp);
2865ac2d602Sdownsj 	register struct ucred *cred = ap->a_cred;
2875ac2d602Sdownsj 	register struct proc *p = ap->a_p;
2885ac2d602Sdownsj 	int error;
2895ac2d602Sdownsj 
2905ac2d602Sdownsj 	/*
2915ac2d602Sdownsj 	 * Check for unsettable attributes.
2925ac2d602Sdownsj 	 */
2935ac2d602Sdownsj 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
2945ac2d602Sdownsj 		(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
2955ac2d602Sdownsj 		(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
2965ac2d602Sdownsj 		((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
2975ac2d602Sdownsj 		return (EINVAL);
2985ac2d602Sdownsj 	}
2995ac2d602Sdownsj 	if (vap->va_flags != VNOVAL) {
3005ac2d602Sdownsj 		if (cred->cr_uid != ip->i_e2fs_uid &&
3015ac2d602Sdownsj 			(error = suser(cred, &p->p_acflag)))
3025ac2d602Sdownsj 			return (error);
3035ac2d602Sdownsj #ifdef EXT2FS_SYSTEM_FLAGS
3045ac2d602Sdownsj 		if (cred->cr_uid == 0) {
3055ac2d602Sdownsj 			if ((ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE)) &&
3065ac2d602Sdownsj 				securelevel > 0)
3075ac2d602Sdownsj 				return (EPERM);
3085ac2d602Sdownsj 			ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
3095ac2d602Sdownsj 			ip->i_e2fs_flags |= (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
3105ac2d602Sdownsj 					(vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
3115ac2d602Sdownsj 		} else {
3125ac2d602Sdownsj 			return (EPERM);
3135ac2d602Sdownsj 		}
3145ac2d602Sdownsj #else
3155ac2d602Sdownsj 		ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
3165ac2d602Sdownsj 		ip->i_e2fs_flags |= (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
3175ac2d602Sdownsj 				(vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
3185ac2d602Sdownsj #endif
3195ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
3205ac2d602Sdownsj 		if (vap->va_flags & (IMMUTABLE | APPEND))
3215ac2d602Sdownsj 			return (0);
3225ac2d602Sdownsj 	}
3235ac2d602Sdownsj 	if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
3245ac2d602Sdownsj 		return (EPERM);
3255ac2d602Sdownsj 	/*
3265ac2d602Sdownsj 	 * Go through the fields and update iff not VNOVAL.
3275ac2d602Sdownsj 	 */
3285ac2d602Sdownsj 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
3295ac2d602Sdownsj 		error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
3305ac2d602Sdownsj 		if (error)
3315ac2d602Sdownsj 			return (error);
3325ac2d602Sdownsj 	}
3335ac2d602Sdownsj 	if (vap->va_size != VNOVAL) {
3345ac2d602Sdownsj 		if (vp->v_type == VDIR)
3355ac2d602Sdownsj 			return (EISDIR);
3365ac2d602Sdownsj 		error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p);
3375ac2d602Sdownsj 		if (error)
3385ac2d602Sdownsj 			return (error);
3395ac2d602Sdownsj 	}
3405ac2d602Sdownsj 	ip = VTOI(vp);
3415ac2d602Sdownsj 	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
3425ac2d602Sdownsj 		if (cred->cr_uid != ip->i_e2fs_uid &&
3435ac2d602Sdownsj 			(error = suser(cred, &p->p_acflag)) &&
3445ac2d602Sdownsj 			((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
3455ac2d602Sdownsj 			(error = VOP_ACCESS(vp, VWRITE, cred, p))))
3465ac2d602Sdownsj 			return (error);
3475ac2d602Sdownsj 		if (vap->va_atime.tv_sec != VNOVAL)
3485ac2d602Sdownsj 			if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
3495ac2d602Sdownsj 				ip->i_flag |= IN_ACCESS;
3505ac2d602Sdownsj 		if (vap->va_mtime.tv_sec != VNOVAL)
3515ac2d602Sdownsj 			ip->i_flag |= IN_CHANGE | IN_UPDATE;
3525ac2d602Sdownsj 		error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 1);
3535ac2d602Sdownsj 		if (error)
3545ac2d602Sdownsj 			return (error);
3555ac2d602Sdownsj 	}
3565ac2d602Sdownsj 	error = 0;
3575ac2d602Sdownsj 	if (vap->va_mode != (mode_t)VNOVAL)
3585ac2d602Sdownsj 		error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p);
3595ac2d602Sdownsj 	return (error);
3605ac2d602Sdownsj }
3615ac2d602Sdownsj 
3625ac2d602Sdownsj /*
3635ac2d602Sdownsj  * Change the mode on a file.
3645ac2d602Sdownsj  * Inode must be locked before calling.
3655ac2d602Sdownsj  */
3665ac2d602Sdownsj static int
3675ac2d602Sdownsj ext2fs_chmod(vp, mode, cred, p)
3685ac2d602Sdownsj 	register struct vnode *vp;
3695ac2d602Sdownsj 	register int mode;
3705ac2d602Sdownsj 	register struct ucred *cred;
3715ac2d602Sdownsj 	struct proc *p;
3725ac2d602Sdownsj {
3735ac2d602Sdownsj 	register struct inode *ip = VTOI(vp);
3745ac2d602Sdownsj 	int error;
3755ac2d602Sdownsj 
3765ac2d602Sdownsj 	if (cred->cr_uid != ip->i_e2fs_uid &&
3775ac2d602Sdownsj 		(error = suser(cred, &p->p_acflag)))
3785ac2d602Sdownsj 		return (error);
3795ac2d602Sdownsj 	if (cred->cr_uid) {
3805ac2d602Sdownsj 		if (vp->v_type != VDIR && (mode & S_ISTXT))
3815ac2d602Sdownsj 			return (EFTYPE);
3825ac2d602Sdownsj 		if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
3835ac2d602Sdownsj 			return (EPERM);
3845ac2d602Sdownsj 	}
3855ac2d602Sdownsj 	ip->i_e2fs_mode &= ~ALLPERMS;
3865ac2d602Sdownsj 	ip->i_e2fs_mode |= (mode & ALLPERMS);
3875ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
3885ac2d602Sdownsj 	if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
3895ac2d602Sdownsj 		(void) vnode_pager_uncache(vp);
3905ac2d602Sdownsj 	return (0);
3915ac2d602Sdownsj }
3925ac2d602Sdownsj 
3935ac2d602Sdownsj /*
3945ac2d602Sdownsj  * Perform chown operation on inode ip;
3955ac2d602Sdownsj  * inode must be locked prior to call.
3965ac2d602Sdownsj  */
3975ac2d602Sdownsj static int
3985ac2d602Sdownsj ext2fs_chown(vp, uid, gid, cred, p)
3995ac2d602Sdownsj 	register struct vnode *vp;
4005ac2d602Sdownsj 	uid_t uid;
4015ac2d602Sdownsj 	gid_t gid;
4025ac2d602Sdownsj 	struct ucred *cred;
4035ac2d602Sdownsj 	struct proc *p;
4045ac2d602Sdownsj {
4055ac2d602Sdownsj 	register struct inode *ip = VTOI(vp);
4065ac2d602Sdownsj 	uid_t ouid;
4075ac2d602Sdownsj 	gid_t ogid;
4085ac2d602Sdownsj 	int error = 0;
4095ac2d602Sdownsj 
4105ac2d602Sdownsj 	if (uid == (uid_t)VNOVAL)
4115ac2d602Sdownsj 		uid = ip->i_e2fs_uid;
4125ac2d602Sdownsj 	if (gid == (gid_t)VNOVAL)
4135ac2d602Sdownsj 		gid = ip->i_e2fs_gid;
4145ac2d602Sdownsj 	/*
4155ac2d602Sdownsj 	 * If we don't own the file, are trying to change the owner
4165ac2d602Sdownsj 	 * of the file, or are not a member of the target group,
4175ac2d602Sdownsj 	 * the caller must be superuser or the call fails.
4185ac2d602Sdownsj 	 */
4195ac2d602Sdownsj 	if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
4205ac2d602Sdownsj 		(gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
4215ac2d602Sdownsj 		(error = suser(cred, &p->p_acflag)))
4225ac2d602Sdownsj 		return (error);
4235ac2d602Sdownsj 	ogid = ip->i_e2fs_gid;
4245ac2d602Sdownsj 	ouid = ip->i_e2fs_uid;
4255ac2d602Sdownsj 
4265ac2d602Sdownsj 	ip->i_e2fs_gid = gid;
4275ac2d602Sdownsj 	ip->i_e2fs_uid = uid;
4285ac2d602Sdownsj 	if (ouid != uid || ogid != gid)
4295ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
4305ac2d602Sdownsj 	if (ouid != uid && cred->cr_uid != 0)
4315ac2d602Sdownsj 		ip->i_e2fs_mode &= ~ISUID;
4325ac2d602Sdownsj 	if (ogid != gid && cred->cr_uid != 0)
4335ac2d602Sdownsj 		ip->i_e2fs_mode &= ~ISGID;
4345ac2d602Sdownsj 	return (0);
4355ac2d602Sdownsj }
4365ac2d602Sdownsj 
4375ac2d602Sdownsj int
4385ac2d602Sdownsj ext2fs_remove(v)
4395ac2d602Sdownsj 	void *v;
4405ac2d602Sdownsj {
4415ac2d602Sdownsj 	struct vop_remove_args /* {
4425ac2d602Sdownsj 		struct vnode *a_dvp;
4435ac2d602Sdownsj 		struct vnode *a_vp;
4445ac2d602Sdownsj 		struct componentname *a_cnp;
4455ac2d602Sdownsj 	} */ *ap = v;
4465ac2d602Sdownsj 	register struct inode *ip;
4475ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
4485ac2d602Sdownsj 	register struct vnode *dvp = ap->a_dvp;
4495ac2d602Sdownsj 	int error;
4505ac2d602Sdownsj 
4515ac2d602Sdownsj 	ip = VTOI(vp);
452d56d3b9eSderaadt 	if (vp->v_type == VDIR ||
453d56d3b9eSderaadt 	    (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
4545ac2d602Sdownsj 	    (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
4555ac2d602Sdownsj 		error = EPERM;
4565ac2d602Sdownsj 		goto out;
4575ac2d602Sdownsj 	}
4585ac2d602Sdownsj 	error = ext2fs_dirremove(dvp, ap->a_cnp);
4595ac2d602Sdownsj 	if (error == 0) {
4605ac2d602Sdownsj 		ip->i_e2fs_nlink--;
4615ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
4625ac2d602Sdownsj 	}
4635ac2d602Sdownsj out:
4645ac2d602Sdownsj 	if (dvp == vp)
4655ac2d602Sdownsj 		vrele(vp);
4665ac2d602Sdownsj 	else
4675ac2d602Sdownsj 		vput(vp);
4685ac2d602Sdownsj 	vput(dvp);
4695ac2d602Sdownsj 	return (error);
4705ac2d602Sdownsj }
4715ac2d602Sdownsj 
4725ac2d602Sdownsj /*
4735ac2d602Sdownsj  * link vnode call
4745ac2d602Sdownsj  */
4755ac2d602Sdownsj int
4765ac2d602Sdownsj ext2fs_link(v)
4775ac2d602Sdownsj 	void *v;
4785ac2d602Sdownsj {
4795ac2d602Sdownsj 	struct vop_link_args /* {
4805ac2d602Sdownsj 		struct vnode *a_dvp;
4815ac2d602Sdownsj 		struct vnode *a_vp;
4825ac2d602Sdownsj 		struct componentname *a_cnp;
4835ac2d602Sdownsj 	} */ *ap = v;
4845ac2d602Sdownsj 	register struct vnode *dvp = ap->a_dvp;
4855ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
4865ac2d602Sdownsj 	register struct componentname *cnp = ap->a_cnp;
487*07feb63cScsapuntz 	struct proc *p = cnp->cn_proc;
4885ac2d602Sdownsj 	register struct inode *ip;
4895ac2d602Sdownsj 	struct timespec ts;
4905ac2d602Sdownsj 	int error;
4915ac2d602Sdownsj 
4925ac2d602Sdownsj #ifdef DIAGNOSTIC
4935ac2d602Sdownsj 	if ((cnp->cn_flags & HASBUF) == 0)
4945ac2d602Sdownsj 		panic("ext2fs_link: no name");
4955ac2d602Sdownsj #endif
4965ac2d602Sdownsj 	if (vp->v_type == VDIR) {
4975ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
4985ac2d602Sdownsj 		error = EISDIR;
4995ac2d602Sdownsj 		goto out2;
5005ac2d602Sdownsj 	}
5015ac2d602Sdownsj 	if (dvp->v_mount != vp->v_mount) {
5025ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
5035ac2d602Sdownsj 		error = EXDEV;
5045ac2d602Sdownsj 		goto out2;
5055ac2d602Sdownsj 	}
506*07feb63cScsapuntz 	if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p))) {
5075ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
5085ac2d602Sdownsj 		goto out2;
5095ac2d602Sdownsj 	}
5105ac2d602Sdownsj 	ip = VTOI(vp);
5115ac2d602Sdownsj 	if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
5125ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
5135ac2d602Sdownsj 		error = EMLINK;
5145ac2d602Sdownsj 		goto out1;
5155ac2d602Sdownsj 	}
5165ac2d602Sdownsj 	if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
5175ac2d602Sdownsj 		VOP_ABORTOP(dvp, cnp);
5185ac2d602Sdownsj 		error = EPERM;
5195ac2d602Sdownsj 		goto out1;
5205ac2d602Sdownsj 	}
5215ac2d602Sdownsj 	ip->i_e2fs_nlink++;
5225ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
5235ac2d602Sdownsj 	TIMEVAL_TO_TIMESPEC(&time, &ts);
5245ac2d602Sdownsj 	error = VOP_UPDATE(vp, &ts, &ts, 1);
5255ac2d602Sdownsj 	if (!error)
5265ac2d602Sdownsj 		error = ext2fs_direnter(ip, dvp, cnp);
5275ac2d602Sdownsj 	if (error) {
5285ac2d602Sdownsj 		ip->i_e2fs_nlink--;
5295ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
5305ac2d602Sdownsj 	}
5315ac2d602Sdownsj 	FREE(cnp->cn_pnbuf, M_NAMEI);
5325ac2d602Sdownsj out1:
5335ac2d602Sdownsj 	if (dvp != vp)
534*07feb63cScsapuntz 		VOP_UNLOCK(vp, 0, p);
5355ac2d602Sdownsj out2:
5365ac2d602Sdownsj 	vput(dvp);
5375ac2d602Sdownsj 	return (error);
5385ac2d602Sdownsj }
5395ac2d602Sdownsj 
5405ac2d602Sdownsj /*
5415ac2d602Sdownsj  * Rename system call.
5425ac2d602Sdownsj  * 	rename("foo", "bar");
5435ac2d602Sdownsj  * is essentially
5445ac2d602Sdownsj  *	unlink("bar");
5455ac2d602Sdownsj  *	link("foo", "bar");
5465ac2d602Sdownsj  *	unlink("foo");
5475ac2d602Sdownsj  * but ``atomically''.  Can't do full commit without saving state in the
5485ac2d602Sdownsj  * inode on disk which isn't feasible at this time.  Best we can do is
5495ac2d602Sdownsj  * always guarantee the target exists.
5505ac2d602Sdownsj  *
5515ac2d602Sdownsj  * Basic algorithm is:
5525ac2d602Sdownsj  *
5535ac2d602Sdownsj  * 1) Bump link count on source while we're linking it to the
5545ac2d602Sdownsj  *    target.  This also ensure the inode won't be deleted out
5555ac2d602Sdownsj  *    from underneath us while we work (it may be truncated by
5565ac2d602Sdownsj  *    a concurrent `trunc' or `open' for creation).
5575ac2d602Sdownsj  * 2) Link source to destination.  If destination already exists,
5585ac2d602Sdownsj  *    delete it first.
5595ac2d602Sdownsj  * 3) Unlink source reference to inode if still around. If a
5605ac2d602Sdownsj  *    directory was moved and the parent of the destination
5615ac2d602Sdownsj  *    is different from the source, patch the ".." entry in the
5625ac2d602Sdownsj  *    directory.
5635ac2d602Sdownsj  */
5645ac2d602Sdownsj int
5655ac2d602Sdownsj ext2fs_rename(v)
5665ac2d602Sdownsj 	void *v;
5675ac2d602Sdownsj {
5685ac2d602Sdownsj 	struct vop_rename_args  /* {
5695ac2d602Sdownsj 		struct vnode *a_fdvp;
5705ac2d602Sdownsj 		struct vnode *a_fvp;
5715ac2d602Sdownsj 		struct componentname *a_fcnp;
5725ac2d602Sdownsj 		struct vnode *a_tdvp;
5735ac2d602Sdownsj 		struct vnode *a_tvp;
5745ac2d602Sdownsj 		struct componentname *a_tcnp;
5755ac2d602Sdownsj 	} */ *ap = v;
5765ac2d602Sdownsj 	struct vnode *tvp = ap->a_tvp;
5775ac2d602Sdownsj 	register struct vnode *tdvp = ap->a_tdvp;
5785ac2d602Sdownsj 	struct vnode *fvp = ap->a_fvp;
5795ac2d602Sdownsj 	register struct vnode *fdvp = ap->a_fdvp;
5805ac2d602Sdownsj 	register struct componentname *tcnp = ap->a_tcnp;
5815ac2d602Sdownsj 	register struct componentname *fcnp = ap->a_fcnp;
5825ac2d602Sdownsj 	register struct inode *ip, *xp, *dp;
583*07feb63cScsapuntz 	struct proc *p = fcnp->cn_proc;
5845ac2d602Sdownsj 	struct ext2fs_dirtemplate dirbuf;
5855ac2d602Sdownsj 	struct timespec ts;
5865ac2d602Sdownsj 	int doingdirectory = 0, oldparent = 0, newparent = 0;
5875ac2d602Sdownsj 	int error = 0;
5885ac2d602Sdownsj 	u_char namlen;
5895ac2d602Sdownsj 
5905ac2d602Sdownsj #ifdef DIAGNOSTIC
5915ac2d602Sdownsj 	if ((tcnp->cn_flags & HASBUF) == 0 ||
5925ac2d602Sdownsj 	    (fcnp->cn_flags & HASBUF) == 0)
5935ac2d602Sdownsj 		panic("ext2fs_rename: no name");
5945ac2d602Sdownsj #endif
5955ac2d602Sdownsj 	/*
5965ac2d602Sdownsj 	 * Check for cross-device rename.
5975ac2d602Sdownsj 	 */
5985ac2d602Sdownsj 	if ((fvp->v_mount != tdvp->v_mount) ||
5995ac2d602Sdownsj 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
6005ac2d602Sdownsj 		error = EXDEV;
6015ac2d602Sdownsj abortit:
6025ac2d602Sdownsj 		VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
6035ac2d602Sdownsj 		if (tdvp == tvp)
6045ac2d602Sdownsj 			vrele(tdvp);
6055ac2d602Sdownsj 		else
6065ac2d602Sdownsj 			vput(tdvp);
6075ac2d602Sdownsj 		if (tvp)
6085ac2d602Sdownsj 			vput(tvp);
6095ac2d602Sdownsj 		VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
6105ac2d602Sdownsj 		vrele(fdvp);
6115ac2d602Sdownsj 		vrele(fvp);
6125ac2d602Sdownsj 		return (error);
6135ac2d602Sdownsj 	}
6145ac2d602Sdownsj 
6155ac2d602Sdownsj 	/*
6165ac2d602Sdownsj 	 * Check if just deleting a link name.
6175ac2d602Sdownsj 	 */
6185ac2d602Sdownsj 	if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
6195ac2d602Sdownsj 	    (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
6205ac2d602Sdownsj 		error = EPERM;
6215ac2d602Sdownsj 		goto abortit;
6225ac2d602Sdownsj 	}
6235ac2d602Sdownsj 	if (fvp == tvp) {
6245ac2d602Sdownsj 		if (fvp->v_type == VDIR) {
6255ac2d602Sdownsj 			error = EINVAL;
6265ac2d602Sdownsj 			goto abortit;
6275ac2d602Sdownsj 		}
6285ac2d602Sdownsj 
6295ac2d602Sdownsj 		/* Release destination completely. */
6305ac2d602Sdownsj 		VOP_ABORTOP(tdvp, tcnp);
6315ac2d602Sdownsj 		vput(tdvp);
6325ac2d602Sdownsj 		vput(tvp);
6335ac2d602Sdownsj 
6345ac2d602Sdownsj 		/* Delete source. */
6355ac2d602Sdownsj 		vrele(fdvp);
6365ac2d602Sdownsj 		vrele(fvp);
6375ac2d602Sdownsj 		fcnp->cn_flags &= ~MODMASK;
6385ac2d602Sdownsj 		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
6395ac2d602Sdownsj 		if ((fcnp->cn_flags & SAVESTART) == 0)
6405ac2d602Sdownsj 			panic("ext2fs_rename: lost from startdir");
6415ac2d602Sdownsj 		fcnp->cn_nameiop = DELETE;
6425ac2d602Sdownsj 		(void) relookup(fdvp, &fvp, fcnp);
6435ac2d602Sdownsj 		return (VOP_REMOVE(fdvp, fvp, fcnp));
6445ac2d602Sdownsj 	}
645*07feb63cScsapuntz 	if ((error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p)) != 0)
6465ac2d602Sdownsj 		goto abortit;
6475ac2d602Sdownsj 	dp = VTOI(fdvp);
6485ac2d602Sdownsj 	ip = VTOI(fvp);
6495ac2d602Sdownsj 	if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
6505ac2d602Sdownsj 		(dp->i_e2fs_flags & EXT2_APPEND)) {
651*07feb63cScsapuntz 		VOP_UNLOCK(fvp, 0, p);
6525ac2d602Sdownsj 		error = EPERM;
6535ac2d602Sdownsj 		goto abortit;
6545ac2d602Sdownsj 	}
6555ac2d602Sdownsj 	if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
6565ac2d602Sdownsj         error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
6575ac2d602Sdownsj         if (!error && tvp)
6585ac2d602Sdownsj                 error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
6595ac2d602Sdownsj         if (error) {
660*07feb63cScsapuntz                 VOP_UNLOCK(fvp, 0, p);
6615ac2d602Sdownsj                 error = EACCES;
6625ac2d602Sdownsj                 goto abortit;
6635ac2d602Sdownsj         }
6645ac2d602Sdownsj 		/*
6655ac2d602Sdownsj 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
6665ac2d602Sdownsj 		 */
6675ac2d602Sdownsj 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
668d56d3b9eSderaadt 		    dp == ip ||
669d56d3b9eSderaadt 			(fcnp->cn_flags&ISDOTDOT) ||
670d56d3b9eSderaadt 			(tcnp->cn_flags & ISDOTDOT) ||
6715ac2d602Sdownsj 		    (ip->i_flag & IN_RENAME)) {
672*07feb63cScsapuntz 			VOP_UNLOCK(fvp, 0, p);
6735ac2d602Sdownsj 			error = EINVAL;
6745ac2d602Sdownsj 			goto abortit;
6755ac2d602Sdownsj 		}
6765ac2d602Sdownsj 		ip->i_flag |= IN_RENAME;
6775ac2d602Sdownsj 		oldparent = dp->i_number;
6785ac2d602Sdownsj 		doingdirectory++;
6795ac2d602Sdownsj 	}
6805ac2d602Sdownsj 	vrele(fdvp);
6815ac2d602Sdownsj 
6825ac2d602Sdownsj 	/*
6835ac2d602Sdownsj 	 * When the target exists, both the directory
6845ac2d602Sdownsj 	 * and target vnodes are returned locked.
6855ac2d602Sdownsj 	 */
6865ac2d602Sdownsj 	dp = VTOI(tdvp);
6875ac2d602Sdownsj 	xp = NULL;
6885ac2d602Sdownsj 	if (tvp)
6895ac2d602Sdownsj 		xp = VTOI(tvp);
6905ac2d602Sdownsj 
6915ac2d602Sdownsj 	/*
6925ac2d602Sdownsj 	 * 1) Bump link count while we're moving stuff
6935ac2d602Sdownsj 	 *    around.  If we crash somewhere before
6945ac2d602Sdownsj 	 *    completing our work, the link count
6955ac2d602Sdownsj 	 *    may be wrong, but correctable.
6965ac2d602Sdownsj 	 */
6975ac2d602Sdownsj 	ip->i_e2fs_nlink++;
6985ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
6995ac2d602Sdownsj 	TIMEVAL_TO_TIMESPEC(&time, &ts);
7005ac2d602Sdownsj 	if ((error = VOP_UPDATE(fvp, &ts, &ts, 1)) != 0) {
701*07feb63cScsapuntz 		VOP_UNLOCK(fvp, 0, p);
7025ac2d602Sdownsj 		goto bad;
7035ac2d602Sdownsj 	}
7045ac2d602Sdownsj 
7055ac2d602Sdownsj 	/*
7065ac2d602Sdownsj 	 * If ".." must be changed (ie the directory gets a new
7075ac2d602Sdownsj 	 * parent) then the source directory must not be in the
7085ac2d602Sdownsj 	 * directory heirarchy above the target, as this would
7095ac2d602Sdownsj 	 * orphan everything below the source directory. Also
7105ac2d602Sdownsj 	 * the user must have write permission in the source so
7115ac2d602Sdownsj 	 * as to be able to change "..". We must repeat the call
7125ac2d602Sdownsj 	 * to namei, as the parent directory is unlocked by the
7135ac2d602Sdownsj 	 * call to checkpath().
7145ac2d602Sdownsj 	 */
7155ac2d602Sdownsj 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
716*07feb63cScsapuntz 	VOP_UNLOCK(fvp, 0, p);
7175ac2d602Sdownsj 	if (oldparent != dp->i_number)
7185ac2d602Sdownsj 		newparent = dp->i_number;
7195ac2d602Sdownsj 	if (doingdirectory && newparent) {
7205ac2d602Sdownsj 		if (error)	/* write access check above */
7215ac2d602Sdownsj 			goto bad;
7225ac2d602Sdownsj 		if (xp != NULL)
7235ac2d602Sdownsj 			vput(tvp);
7245ac2d602Sdownsj 		error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
7255ac2d602Sdownsj 		if (error != 0)
7265ac2d602Sdownsj 			goto out;
7275ac2d602Sdownsj 		if ((tcnp->cn_flags & SAVESTART) == 0)
7285ac2d602Sdownsj 			panic("ext2fs_rename: lost to startdir");
7295ac2d602Sdownsj 		if ((error = relookup(tdvp, &tvp, tcnp)) != 0)
7305ac2d602Sdownsj 			goto out;
7315ac2d602Sdownsj 		dp = VTOI(tdvp);
7325ac2d602Sdownsj 		xp = NULL;
7335ac2d602Sdownsj 		if (tvp)
7345ac2d602Sdownsj 			xp = VTOI(tvp);
7355ac2d602Sdownsj 	}
7365ac2d602Sdownsj 	/*
7375ac2d602Sdownsj 	 * 2) If target doesn't exist, link the target
7385ac2d602Sdownsj 	 *    to the source and unlink the source.
7395ac2d602Sdownsj 	 *    Otherwise, rewrite the target directory
7405ac2d602Sdownsj 	 *    entry to reference the source inode and
7415ac2d602Sdownsj 	 *    expunge the original entry's existence.
7425ac2d602Sdownsj 	 */
7435ac2d602Sdownsj 	if (xp == NULL) {
7445ac2d602Sdownsj 		if (dp->i_dev != ip->i_dev)
7455ac2d602Sdownsj 			panic("rename: EXDEV");
7465ac2d602Sdownsj 		/*
7475ac2d602Sdownsj 		 * Account for ".." in new directory.
7485ac2d602Sdownsj 		 * When source and destination have the same
7495ac2d602Sdownsj 		 * parent we don't fool with the link count.
7505ac2d602Sdownsj 		 */
7515ac2d602Sdownsj 		if (doingdirectory && newparent) {
7525ac2d602Sdownsj 			if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
7535ac2d602Sdownsj 				error = EMLINK;
7545ac2d602Sdownsj 				goto bad;
7555ac2d602Sdownsj 			}
7565ac2d602Sdownsj 			dp->i_e2fs_nlink++;
7575ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
7585ac2d602Sdownsj 			if ((error = VOP_UPDATE(tdvp, &ts, &ts, 1)) != 0)
7595ac2d602Sdownsj 				goto bad;
7605ac2d602Sdownsj 		}
7615ac2d602Sdownsj 		error = ext2fs_direnter(ip, tdvp, tcnp);
7625ac2d602Sdownsj 		if (error != 0) {
7635ac2d602Sdownsj 			if (doingdirectory && newparent) {
7645ac2d602Sdownsj 				dp->i_e2fs_nlink--;
7655ac2d602Sdownsj 				dp->i_flag |= IN_CHANGE;
7665ac2d602Sdownsj 				(void)VOP_UPDATE(tdvp, &ts, &ts, 1);
7675ac2d602Sdownsj 			}
7685ac2d602Sdownsj 			goto bad;
7695ac2d602Sdownsj 		}
7705ac2d602Sdownsj 		vput(tdvp);
7715ac2d602Sdownsj 	} else {
7725ac2d602Sdownsj 		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
7735ac2d602Sdownsj 			panic("rename: EXDEV");
7745ac2d602Sdownsj 		/*
7755ac2d602Sdownsj 		 * Short circuit rename(foo, foo).
7765ac2d602Sdownsj 		 */
7775ac2d602Sdownsj 		if (xp->i_number == ip->i_number)
7785ac2d602Sdownsj 			panic("rename: same file");
7795ac2d602Sdownsj 		/*
7805ac2d602Sdownsj 		 * If the parent directory is "sticky", then the user must
7815ac2d602Sdownsj 		 * own the parent directory, or the destination of the rename,
7825ac2d602Sdownsj 		 * otherwise the destination may not be changed (except by
7835ac2d602Sdownsj 		 * root). This implements append-only directories.
7845ac2d602Sdownsj 		 */
7855ac2d602Sdownsj 		if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
7865ac2d602Sdownsj 		    tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
7875ac2d602Sdownsj 		    xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
7885ac2d602Sdownsj 			error = EPERM;
7895ac2d602Sdownsj 			goto bad;
7905ac2d602Sdownsj 		}
7915ac2d602Sdownsj 		/*
7925ac2d602Sdownsj 		 * Target must be empty if a directory and have no links
7935ac2d602Sdownsj 		 * to it. Also, ensure source and target are compatible
7945ac2d602Sdownsj 		 * (both directories, or both not directories).
7955ac2d602Sdownsj 		 */
7965ac2d602Sdownsj 		if ((xp->i_e2fs_mode & IFMT) == IFDIR) {
7975ac2d602Sdownsj 			if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
7985ac2d602Sdownsj 				xp->i_e2fs_nlink > 2) {
7995ac2d602Sdownsj 				error = ENOTEMPTY;
8005ac2d602Sdownsj 				goto bad;
8015ac2d602Sdownsj 			}
8025ac2d602Sdownsj 			if (!doingdirectory) {
8035ac2d602Sdownsj 				error = ENOTDIR;
8045ac2d602Sdownsj 				goto bad;
8055ac2d602Sdownsj 			}
8065ac2d602Sdownsj 			cache_purge(tdvp);
8075ac2d602Sdownsj 		} else if (doingdirectory) {
8085ac2d602Sdownsj 			error = EISDIR;
8095ac2d602Sdownsj 			goto bad;
8105ac2d602Sdownsj 		}
8115ac2d602Sdownsj 		error = ext2fs_dirrewrite(dp, ip, tcnp);
8125ac2d602Sdownsj 		if (error != 0)
8135ac2d602Sdownsj 			goto bad;
8145ac2d602Sdownsj 		/*
8155ac2d602Sdownsj 		 * If the target directory is in the same
8165ac2d602Sdownsj 		 * directory as the source directory,
8175ac2d602Sdownsj 		 * decrement the link count on the parent
8185ac2d602Sdownsj 		 * of the target directory.
8195ac2d602Sdownsj 		 */
8205ac2d602Sdownsj 		 if (doingdirectory && !newparent) {
8215ac2d602Sdownsj 			dp->i_e2fs_nlink--;
8225ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
8235ac2d602Sdownsj 		}
8245ac2d602Sdownsj 		vput(tdvp);
8255ac2d602Sdownsj 		/*
8265ac2d602Sdownsj 		 * Adjust the link count of the target to
8275ac2d602Sdownsj 		 * reflect the dirrewrite above.  If this is
8285ac2d602Sdownsj 		 * a directory it is empty and there are
8295ac2d602Sdownsj 		 * no links to it, so we can squash the inode and
8305ac2d602Sdownsj 		 * any space associated with it.  We disallowed
8315ac2d602Sdownsj 		 * renaming over top of a directory with links to
8325ac2d602Sdownsj 		 * it above, as the remaining link would point to
8335ac2d602Sdownsj 		 * a directory without "." or ".." entries.
8345ac2d602Sdownsj 		 */
8355ac2d602Sdownsj 		xp->i_e2fs_nlink--;
8365ac2d602Sdownsj 		if (doingdirectory) {
8375ac2d602Sdownsj 			if (--xp->i_e2fs_nlink != 0)
8385ac2d602Sdownsj 				panic("rename: linked directory");
8395ac2d602Sdownsj 			error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC,
8405ac2d602Sdownsj 			    tcnp->cn_cred, tcnp->cn_proc);
8415ac2d602Sdownsj 		}
8425ac2d602Sdownsj 		xp->i_flag |= IN_CHANGE;
8435ac2d602Sdownsj 		vput(tvp);
8445ac2d602Sdownsj 		xp = NULL;
8455ac2d602Sdownsj 	}
8465ac2d602Sdownsj 
8475ac2d602Sdownsj 	/*
8485ac2d602Sdownsj 	 * 3) Unlink the source.
8495ac2d602Sdownsj 	 */
8505ac2d602Sdownsj 	fcnp->cn_flags &= ~MODMASK;
8515ac2d602Sdownsj 	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
8525ac2d602Sdownsj 	if ((fcnp->cn_flags & SAVESTART) == 0)
8535ac2d602Sdownsj 		panic("ext2fs_rename: lost from startdir");
8545ac2d602Sdownsj 	(void) relookup(fdvp, &fvp, fcnp);
8555ac2d602Sdownsj 	if (fvp != NULL) {
8565ac2d602Sdownsj 		xp = VTOI(fvp);
8575ac2d602Sdownsj 		dp = VTOI(fdvp);
8585ac2d602Sdownsj 	} else {
8595ac2d602Sdownsj 		/*
8605ac2d602Sdownsj 		 * From name has disappeared.
8615ac2d602Sdownsj 		 */
8625ac2d602Sdownsj 		if (doingdirectory)
8635ac2d602Sdownsj 			panic("ext2fs_rename: lost dir entry");
8645ac2d602Sdownsj 		vrele(ap->a_fvp);
8655ac2d602Sdownsj 		return (0);
8665ac2d602Sdownsj 	}
8675ac2d602Sdownsj 	/*
8685ac2d602Sdownsj 	 * Ensure that the directory entry still exists and has not
8695ac2d602Sdownsj 	 * changed while the new name has been entered. If the source is
8705ac2d602Sdownsj 	 * a file then the entry may have been unlinked or renamed. In
8715ac2d602Sdownsj 	 * either case there is no further work to be done. If the source
8725ac2d602Sdownsj 	 * is a directory then it cannot have been rmdir'ed; its link
8735ac2d602Sdownsj 	 * count of three would cause a rmdir to fail with ENOTEMPTY.
8745ac2d602Sdownsj 	 * The IRENAME flag ensures that it cannot be moved by another
8755ac2d602Sdownsj 	 * rename.
8765ac2d602Sdownsj 	 */
8775ac2d602Sdownsj 	if (xp != ip) {
8785ac2d602Sdownsj 		if (doingdirectory)
8795ac2d602Sdownsj 			panic("ext2fs_rename: lost dir entry");
8805ac2d602Sdownsj 	} else {
8815ac2d602Sdownsj 		/*
8825ac2d602Sdownsj 		 * If the source is a directory with a
8835ac2d602Sdownsj 		 * new parent, the link count of the old
8845ac2d602Sdownsj 		 * parent directory must be decremented
8855ac2d602Sdownsj 		 * and ".." set to point to the new parent.
8865ac2d602Sdownsj 		 */
8875ac2d602Sdownsj 		if (doingdirectory && newparent) {
8885ac2d602Sdownsj 			dp->i_e2fs_nlink--;
8895ac2d602Sdownsj 			dp->i_flag |= IN_CHANGE;
8905ac2d602Sdownsj 			error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
8915ac2d602Sdownsj 				sizeof (struct ext2fs_dirtemplate), (off_t)0,
8925ac2d602Sdownsj 				UIO_SYSSPACE, IO_NODELOCKED,
8935ac2d602Sdownsj 				tcnp->cn_cred, (int *)0, (struct proc *)0);
8945ac2d602Sdownsj 			if (error == 0) {
8955ac2d602Sdownsj 					namlen = dirbuf.dotdot_namlen;
8965ac2d602Sdownsj 				if (namlen != 2 ||
8975ac2d602Sdownsj 				    dirbuf.dotdot_name[0] != '.' ||
8985ac2d602Sdownsj 				    dirbuf.dotdot_name[1] != '.') {
8995ac2d602Sdownsj 					ufs_dirbad(xp, (doff_t)12,
9005ac2d602Sdownsj 					    "ext2fs_rename: mangled dir");
9015ac2d602Sdownsj 				} else {
9025ac2d602Sdownsj 					dirbuf.dotdot_ino = newparent;
9035ac2d602Sdownsj 					(void) vn_rdwr(UIO_WRITE, fvp,
9045ac2d602Sdownsj 					    (caddr_t)&dirbuf,
9055ac2d602Sdownsj 					    sizeof (struct dirtemplate),
9065ac2d602Sdownsj 					    (off_t)0, UIO_SYSSPACE,
9075ac2d602Sdownsj 					    IO_NODELOCKED|IO_SYNC,
9085ac2d602Sdownsj 					    tcnp->cn_cred, (int *)0,
9095ac2d602Sdownsj 					    (struct proc *)0);
9105ac2d602Sdownsj 					cache_purge(fdvp);
9115ac2d602Sdownsj 				}
9125ac2d602Sdownsj 			}
9135ac2d602Sdownsj 		}
9145ac2d602Sdownsj 		error = ext2fs_dirremove(fdvp, fcnp);
9155ac2d602Sdownsj 		if (!error) {
9165ac2d602Sdownsj 			xp->i_e2fs_nlink--;
9175ac2d602Sdownsj 			xp->i_flag |= IN_CHANGE;
9185ac2d602Sdownsj 		}
9195ac2d602Sdownsj 		xp->i_flag &= ~IN_RENAME;
9205ac2d602Sdownsj 	}
9215ac2d602Sdownsj 	if (dp)
9225ac2d602Sdownsj 		vput(fdvp);
9235ac2d602Sdownsj 	if (xp)
9245ac2d602Sdownsj 		vput(fvp);
9255ac2d602Sdownsj 	vrele(ap->a_fvp);
9265ac2d602Sdownsj 	return (error);
9275ac2d602Sdownsj 
9285ac2d602Sdownsj bad:
9295ac2d602Sdownsj 	if (xp)
9305ac2d602Sdownsj 		vput(ITOV(xp));
9315ac2d602Sdownsj 	vput(ITOV(dp));
9325ac2d602Sdownsj out:
9335ac2d602Sdownsj 	if (doingdirectory)
9345ac2d602Sdownsj 		ip->i_flag &= ~IN_RENAME;
935*07feb63cScsapuntz 	if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
9365ac2d602Sdownsj 		ip->i_e2fs_nlink--;
9375ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
9385ac2d602Sdownsj 		vput(fvp);
9395ac2d602Sdownsj 	} else
9405ac2d602Sdownsj 		vrele(fvp);
9415ac2d602Sdownsj 	return (error);
9425ac2d602Sdownsj }
9435ac2d602Sdownsj 
9445ac2d602Sdownsj /*
9455ac2d602Sdownsj  * A virgin directory (no blushing please).
9465ac2d602Sdownsj  */
9475ac2d602Sdownsj static struct ext2fs_dirtemplate mastertemplate = {
9485ac2d602Sdownsj 	0, 12, 1, ".",
9495ac2d602Sdownsj 	0, - 12, 2, ".." /* XXX -12 should be e2fs_bsize-12 */
9505ac2d602Sdownsj };
9515ac2d602Sdownsj 
9525ac2d602Sdownsj /*
9535ac2d602Sdownsj  * Mkdir system call
9545ac2d602Sdownsj  */
9555ac2d602Sdownsj int
9565ac2d602Sdownsj ext2fs_mkdir(v)
9575ac2d602Sdownsj 	void *v;
9585ac2d602Sdownsj {
9595ac2d602Sdownsj 	struct vop_mkdir_args /* {
9605ac2d602Sdownsj 		struct vnode *a_dvp;
9615ac2d602Sdownsj 		struct vnode **a_vpp;
9625ac2d602Sdownsj 		struct componentname *a_cnp;
9635ac2d602Sdownsj 		struct vattr *a_vap;
9645ac2d602Sdownsj 	} */ *ap = v;
9655ac2d602Sdownsj 	register struct vnode *dvp = ap->a_dvp;
9665ac2d602Sdownsj 	register struct vattr *vap = ap->a_vap;
9675ac2d602Sdownsj 	register struct componentname *cnp = ap->a_cnp;
9685ac2d602Sdownsj 	register struct inode *ip, *dp;
9695ac2d602Sdownsj 	struct vnode *tvp;
9705ac2d602Sdownsj 	struct ext2fs_dirtemplate dirtemplate, *dtp;
9715ac2d602Sdownsj 	struct timespec ts;
9725ac2d602Sdownsj 	int error, dmode;
9735ac2d602Sdownsj 
9745ac2d602Sdownsj #ifdef DIAGNOSTIC
9755ac2d602Sdownsj 	if ((cnp->cn_flags & HASBUF) == 0)
9765ac2d602Sdownsj 		panic("ext2fs_mkdir: no name");
9775ac2d602Sdownsj #endif
9785ac2d602Sdownsj 	dp = VTOI(dvp);
9795ac2d602Sdownsj 	if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
9805ac2d602Sdownsj 		error = EMLINK;
9815ac2d602Sdownsj 		goto out;
9825ac2d602Sdownsj 	}
9831f3ff51cSdownsj 	dmode = vap->va_mode & ACCESSPERMS;
9845ac2d602Sdownsj 	dmode |= IFDIR;
9855ac2d602Sdownsj 	/*
9865ac2d602Sdownsj 	 * Must simulate part of ext2fs_makeinode here to acquire the inode,
9875ac2d602Sdownsj 	 * but not have it entered in the parent directory. The entry is
9885ac2d602Sdownsj 	 * made later after writing "." and ".." entries.
9895ac2d602Sdownsj 	 */
9905ac2d602Sdownsj 	if ((error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) != 0)
9915ac2d602Sdownsj 		goto out;
9925ac2d602Sdownsj 	ip = VTOI(tvp);
9935ac2d602Sdownsj 	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
9945ac2d602Sdownsj 	ip->i_e2fs_gid = dp->i_e2fs_gid;
9955ac2d602Sdownsj 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
9965ac2d602Sdownsj 	ip->i_e2fs_mode = dmode;
9975ac2d602Sdownsj 	tvp->v_type = VDIR;	/* Rest init'd in getnewvnode(). */
9985ac2d602Sdownsj 	ip->i_e2fs_nlink = 2;
9995ac2d602Sdownsj 	TIMEVAL_TO_TIMESPEC(&time, &ts);
10005ac2d602Sdownsj 	error = VOP_UPDATE(tvp, &ts, &ts, 1);
10015ac2d602Sdownsj 
10025ac2d602Sdownsj 	/*
10035ac2d602Sdownsj 	 * Bump link count in parent directory
10045ac2d602Sdownsj 	 * to reflect work done below.  Should
10055ac2d602Sdownsj 	 * be done before reference is created
10065ac2d602Sdownsj 	 * so reparation is possible if we crash.
10075ac2d602Sdownsj 	 */
10085ac2d602Sdownsj 	dp->i_e2fs_nlink++;
10095ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE;
10105ac2d602Sdownsj 	if ((error = VOP_UPDATE(dvp, &ts, &ts, 1)) != 0)
10115ac2d602Sdownsj 		goto bad;
10125ac2d602Sdownsj 
10135ac2d602Sdownsj 	/* Initialize directory with "." and ".." from static template. */
10145ac2d602Sdownsj 	dtp = &mastertemplate;
10155ac2d602Sdownsj 	dirtemplate = *dtp;
10165ac2d602Sdownsj 	dirtemplate.dot_ino = ip->i_number;
10175ac2d602Sdownsj 	dirtemplate.dotdot_ino = dp->i_number;
10185ac2d602Sdownsj 	/* Correct reclen of second entry */
10195ac2d602Sdownsj     dirtemplate.dotdot_reclen = VTOI(dvp)->i_e2fs->e2fs_bsize - 12;
10205ac2d602Sdownsj 	error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
10215ac2d602Sdownsj 	    sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
10225ac2d602Sdownsj 	    IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0);
10235ac2d602Sdownsj 	if (error) {
10245ac2d602Sdownsj 		dp->i_e2fs_nlink--;
10255ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE;
10265ac2d602Sdownsj 		goto bad;
10275ac2d602Sdownsj 	}
10285ac2d602Sdownsj 	if (VTOI(dvp)->i_e2fs->e2fs_bsize >
10295ac2d602Sdownsj 							VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
10305ac2d602Sdownsj 		panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
10315ac2d602Sdownsj 	else {
10325ac2d602Sdownsj 		ip->i_e2fs_size = VTOI(dvp)->i_e2fs->e2fs_bsize;
10335ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
10345ac2d602Sdownsj 	}
10355ac2d602Sdownsj 
10365ac2d602Sdownsj 	/* Directory set up, now install it's entry in the parent directory. */
10375ac2d602Sdownsj 	error = ext2fs_direnter(ip, dvp, cnp);
10385ac2d602Sdownsj 	if (error != 0) {
10395ac2d602Sdownsj 		dp->i_e2fs_nlink--;
10405ac2d602Sdownsj 		dp->i_flag |= IN_CHANGE;
10415ac2d602Sdownsj 	}
10425ac2d602Sdownsj bad:
10435ac2d602Sdownsj 	/*
10445ac2d602Sdownsj 	 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
10455ac2d602Sdownsj 	 * for us because we set the link count to 0.
10465ac2d602Sdownsj 	 */
10475ac2d602Sdownsj 	if (error) {
10485ac2d602Sdownsj 		ip->i_e2fs_nlink = 0;
10495ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE;
10505ac2d602Sdownsj 		vput(tvp);
10515ac2d602Sdownsj 	} else
10525ac2d602Sdownsj 		*ap->a_vpp = tvp;
10535ac2d602Sdownsj out:
10545ac2d602Sdownsj 	FREE(cnp->cn_pnbuf, M_NAMEI);
10555ac2d602Sdownsj 	vput(dvp);
10565ac2d602Sdownsj 	return (error);
10575ac2d602Sdownsj }
10585ac2d602Sdownsj 
10595ac2d602Sdownsj /*
10605ac2d602Sdownsj  * Rmdir system call.
10615ac2d602Sdownsj  */
10625ac2d602Sdownsj int
10635ac2d602Sdownsj ext2fs_rmdir(v)
10645ac2d602Sdownsj 	void *v;
10655ac2d602Sdownsj {
10665ac2d602Sdownsj 	struct vop_rmdir_args /* {
10675ac2d602Sdownsj 		struct vnode *a_dvp;
10685ac2d602Sdownsj 		struct vnode *a_vp;
10695ac2d602Sdownsj 		struct componentname *a_cnp;
10705ac2d602Sdownsj 	} */ *ap = v;
10715ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
10725ac2d602Sdownsj 	register struct vnode *dvp = ap->a_dvp;
10735ac2d602Sdownsj 	register struct componentname *cnp = ap->a_cnp;
10745ac2d602Sdownsj 	register struct inode *ip, *dp;
10755ac2d602Sdownsj 	int error;
10765ac2d602Sdownsj 
10775ac2d602Sdownsj 	ip = VTOI(vp);
10785ac2d602Sdownsj 	dp = VTOI(dvp);
10795ac2d602Sdownsj 	/*
10805ac2d602Sdownsj 	 * No rmdir "." please.
10815ac2d602Sdownsj 	 */
10825ac2d602Sdownsj 	if (dp == ip) {
10835ac2d602Sdownsj 		vrele(dvp);
10845ac2d602Sdownsj 		vput(vp);
10855ac2d602Sdownsj 		return (EINVAL);
10865ac2d602Sdownsj 	}
10875ac2d602Sdownsj 	/*
10885ac2d602Sdownsj 	 * Verify the directory is empty (and valid).
10895ac2d602Sdownsj 	 * (Rmdir ".." won't be valid since
10905ac2d602Sdownsj 	 *  ".." will contain a reference to
10915ac2d602Sdownsj 	 *  the current directory and thus be
10925ac2d602Sdownsj 	 *  non-empty.)
10935ac2d602Sdownsj 	 */
10945ac2d602Sdownsj 	error = 0;
10955ac2d602Sdownsj 	if (ip->i_e2fs_nlink != 2 ||
10965ac2d602Sdownsj 	    !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
10975ac2d602Sdownsj 		error = ENOTEMPTY;
10985ac2d602Sdownsj 		goto out;
10995ac2d602Sdownsj 	}
11005ac2d602Sdownsj 	if ((dp->i_e2fs_flags & EXT2_APPEND) ||
11015ac2d602Sdownsj 				 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
11025ac2d602Sdownsj 		error = EPERM;
11035ac2d602Sdownsj 		goto out;
11045ac2d602Sdownsj 	}
11055ac2d602Sdownsj 	/*
11065ac2d602Sdownsj 	 * Delete reference to directory before purging
11075ac2d602Sdownsj 	 * inode.  If we crash in between, the directory
11085ac2d602Sdownsj 	 * will be reattached to lost+found,
11095ac2d602Sdownsj 	 */
11105ac2d602Sdownsj 	error = ext2fs_dirremove(dvp, cnp);
11115ac2d602Sdownsj 	if (error != 0)
11125ac2d602Sdownsj 		goto out;
11135ac2d602Sdownsj 	dp->i_e2fs_nlink--;
11145ac2d602Sdownsj 	dp->i_flag |= IN_CHANGE;
11155ac2d602Sdownsj 	cache_purge(dvp);
11165ac2d602Sdownsj 	vput(dvp);
11175ac2d602Sdownsj 	dvp = NULL;
11185ac2d602Sdownsj 	/*
11195ac2d602Sdownsj 	 * Truncate inode.  The only stuff left
11205ac2d602Sdownsj 	 * in the directory is "." and "..".  The
11215ac2d602Sdownsj 	 * "." reference is inconsequential since
11225ac2d602Sdownsj 	 * we're quashing it.  The ".." reference
11235ac2d602Sdownsj 	 * has already been adjusted above.  We've
11245ac2d602Sdownsj 	 * removed the "." reference and the reference
11255ac2d602Sdownsj 	 * in the parent directory, but there may be
11265ac2d602Sdownsj 	 * other hard links so decrement by 2 and
11275ac2d602Sdownsj 	 * worry about them later.
11285ac2d602Sdownsj 	 */
11295ac2d602Sdownsj 	ip->i_e2fs_nlink -= 2;
11305ac2d602Sdownsj 	error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
11315ac2d602Sdownsj 	    cnp->cn_proc);
11325ac2d602Sdownsj 	cache_purge(ITOV(ip));
11335ac2d602Sdownsj out:
11345ac2d602Sdownsj 	if (dvp)
11355ac2d602Sdownsj 		vput(dvp);
11365ac2d602Sdownsj 	vput(vp);
11375ac2d602Sdownsj 	return (error);
11385ac2d602Sdownsj }
11395ac2d602Sdownsj 
11405ac2d602Sdownsj /*
11415ac2d602Sdownsj  * symlink -- make a symbolic link
11425ac2d602Sdownsj  */
11435ac2d602Sdownsj int
11445ac2d602Sdownsj ext2fs_symlink(v)
11455ac2d602Sdownsj 	void *v;
11465ac2d602Sdownsj {
11475ac2d602Sdownsj 	struct vop_symlink_args /* {
11485ac2d602Sdownsj 		struct vnode *a_dvp;
11495ac2d602Sdownsj 		struct vnode **a_vpp;
11505ac2d602Sdownsj 		struct componentname *a_cnp;
11515ac2d602Sdownsj 		struct vattr *a_vap;
11525ac2d602Sdownsj 		char *a_target;
11535ac2d602Sdownsj 	} */ *ap = v;
11545ac2d602Sdownsj 	register struct vnode *vp, **vpp = ap->a_vpp;
11555ac2d602Sdownsj 	register struct inode *ip;
11565ac2d602Sdownsj 	int len, error;
11575ac2d602Sdownsj 
11585ac2d602Sdownsj 	error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
11595ac2d602Sdownsj 			      vpp, ap->a_cnp);
11605ac2d602Sdownsj 	if (error)
11615ac2d602Sdownsj 		return (error);
11625ac2d602Sdownsj 	vp = *vpp;
11635ac2d602Sdownsj 	len = strlen(ap->a_target);
11645ac2d602Sdownsj 	if (len < vp->v_mount->mnt_maxsymlinklen) {
11655ac2d602Sdownsj 		ip = VTOI(vp);
11665ac2d602Sdownsj 		bcopy(ap->a_target, (char *)ip->i_din.e2fs_din.e2di_shortlink, len);
11675ac2d602Sdownsj 		ip->i_e2fs_size = len;
11685ac2d602Sdownsj 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
11695ac2d602Sdownsj 	} else
11705ac2d602Sdownsj 		error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
11715ac2d602Sdownsj 		    UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
11725ac2d602Sdownsj 		    (struct proc *)0);
11735ac2d602Sdownsj 	vput(vp);
11745ac2d602Sdownsj 	return (error);
11755ac2d602Sdownsj }
11765ac2d602Sdownsj 
11775ac2d602Sdownsj /*
11785ac2d602Sdownsj  * Return target name of a symbolic link
11795ac2d602Sdownsj  */
11805ac2d602Sdownsj int
11815ac2d602Sdownsj ext2fs_readlink(v)
11825ac2d602Sdownsj 	void *v;
11835ac2d602Sdownsj {
11845ac2d602Sdownsj 	struct vop_readlink_args /* {
11855ac2d602Sdownsj 		struct vnode *a_vp;
11865ac2d602Sdownsj 		struct uio *a_uio;
11875ac2d602Sdownsj 		struct ucred *a_cred;
11885ac2d602Sdownsj 	} */ *ap = v;
11895ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
11905ac2d602Sdownsj 	register struct inode *ip = VTOI(vp);
11915ac2d602Sdownsj 	int isize;
11925ac2d602Sdownsj 
11935ac2d602Sdownsj 	isize = ip->i_e2fs_size;
11945ac2d602Sdownsj 	if (isize < vp->v_mount->mnt_maxsymlinklen ||
11955ac2d602Sdownsj 	    (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) {
11965ac2d602Sdownsj 		uiomove((char *)ip->i_din.e2fs_din.e2di_shortlink, isize, ap->a_uio);
11975ac2d602Sdownsj 		return (0);
11985ac2d602Sdownsj 	}
11995ac2d602Sdownsj 	return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
12005ac2d602Sdownsj }
12015ac2d602Sdownsj 
12025ac2d602Sdownsj /*
12035ac2d602Sdownsj  * Advisory record locking support
12045ac2d602Sdownsj  */
12055ac2d602Sdownsj int
12065ac2d602Sdownsj ext2fs_advlock(v)
12075ac2d602Sdownsj 	void *v;
12085ac2d602Sdownsj {
12095ac2d602Sdownsj 	struct vop_advlock_args /* {
12105ac2d602Sdownsj 		struct vnode *a_vp;
12115ac2d602Sdownsj 		caddr_t  a_id;
12125ac2d602Sdownsj 		int  a_op;
12135ac2d602Sdownsj 		struct flock *a_fl;
12145ac2d602Sdownsj 		int  a_flags;
12155ac2d602Sdownsj 	} */ *ap = v;
12165ac2d602Sdownsj 	register struct inode *ip = VTOI(ap->a_vp);
12175ac2d602Sdownsj 
12185ac2d602Sdownsj 	return (lf_advlock(&ip->i_lockf, ip->i_e2fs_size, ap->a_id, ap->a_op,
12195ac2d602Sdownsj 	    ap->a_fl, ap->a_flags));
12205ac2d602Sdownsj }
12215ac2d602Sdownsj 
12225ac2d602Sdownsj /*
12235ac2d602Sdownsj  * Initialize the vnode associated with a new inode, handle aliased
12245ac2d602Sdownsj  * vnodes.
12255ac2d602Sdownsj  */
12265ac2d602Sdownsj int
12275ac2d602Sdownsj ext2fs_vinit(mntp, specops, fifoops, vpp)
12285ac2d602Sdownsj 	struct mount *mntp;
12295ac2d602Sdownsj 	int (**specops) __P((void *));
12305ac2d602Sdownsj 	int (**fifoops) __P((void *));
12315ac2d602Sdownsj 	struct vnode **vpp;
12325ac2d602Sdownsj {
12335ac2d602Sdownsj 	struct inode *ip;
12345ac2d602Sdownsj 	struct vnode *vp, *nvp;
1235*07feb63cScsapuntz 	struct proc *p = curproc;
12365ac2d602Sdownsj 
12375ac2d602Sdownsj 	vp = *vpp;
12385ac2d602Sdownsj 	ip = VTOI(vp);
12395ac2d602Sdownsj 	switch(vp->v_type = IFTOVT(ip->i_e2fs_mode)) {
12405ac2d602Sdownsj 	case VCHR:
12415ac2d602Sdownsj 	case VBLK:
12425ac2d602Sdownsj 		vp->v_op = specops;
12435ac2d602Sdownsj 		if ((nvp = checkalias(vp, ip->i_din.e2fs_din.e2di_rdev, mntp))
12445ac2d602Sdownsj 			!= NULL) {
12455ac2d602Sdownsj 			/*
12465ac2d602Sdownsj 			 * Discard unneeded vnode, but save its inode.
12475ac2d602Sdownsj 			 */
12485ac2d602Sdownsj 			ufs_ihashrem(ip);
1249*07feb63cScsapuntz 			VOP_UNLOCK(vp, 0, p);
12505ac2d602Sdownsj 			nvp->v_data = vp->v_data;
12515ac2d602Sdownsj 			vp->v_data = NULL;
12525ac2d602Sdownsj 			vp->v_op = spec_vnodeop_p;
12535ac2d602Sdownsj 			vrele(vp);
12545ac2d602Sdownsj 			vgone(vp);
12555ac2d602Sdownsj 			/*
12565ac2d602Sdownsj 			 * Reinitialize aliased inode.
12575ac2d602Sdownsj 			 */
12585ac2d602Sdownsj 			vp = nvp;
12595ac2d602Sdownsj 			ip->i_vnode = vp;
12605ac2d602Sdownsj 			ufs_ihashins(ip);
12615ac2d602Sdownsj 		}
12625ac2d602Sdownsj 		break;
12635ac2d602Sdownsj 	case VFIFO:
12645ac2d602Sdownsj #ifdef FIFO
12655ac2d602Sdownsj 		vp->v_op = fifoops;
12665ac2d602Sdownsj 		break;
12675ac2d602Sdownsj #else
12685ac2d602Sdownsj 		return (EOPNOTSUPP);
12695ac2d602Sdownsj #endif
12705ac2d602Sdownsj 	case VNON:
12715ac2d602Sdownsj 	case VBAD:
12725ac2d602Sdownsj 	case VSOCK:
12735ac2d602Sdownsj 	case VLNK:
12745ac2d602Sdownsj 	case VDIR:
12755ac2d602Sdownsj 	case VREG:
12765ac2d602Sdownsj 		break;
12775ac2d602Sdownsj 	}
12785ac2d602Sdownsj 	if (ip->i_number == ROOTINO)
12795ac2d602Sdownsj                 vp->v_flag |= VROOT;
12805ac2d602Sdownsj 	/*
12815ac2d602Sdownsj 	 * Initialize modrev times
12825ac2d602Sdownsj 	 */
12835ac2d602Sdownsj 	SETHIGH(ip->i_modrev, mono_time.tv_sec);
12845ac2d602Sdownsj 	SETLOW(ip->i_modrev, mono_time.tv_usec * 4294);
12855ac2d602Sdownsj 	*vpp = vp;
12865ac2d602Sdownsj 	return (0);
12875ac2d602Sdownsj }
12885ac2d602Sdownsj 
12895ac2d602Sdownsj /*
12905ac2d602Sdownsj  * Allocate a new inode.
12915ac2d602Sdownsj  */
12925ac2d602Sdownsj int
12935ac2d602Sdownsj ext2fs_makeinode(mode, dvp, vpp, cnp)
12945ac2d602Sdownsj 	int mode;
12955ac2d602Sdownsj 	struct vnode *dvp;
12965ac2d602Sdownsj 	struct vnode **vpp;
12975ac2d602Sdownsj 	struct componentname *cnp;
12985ac2d602Sdownsj {
12995ac2d602Sdownsj 	register struct inode *ip, *pdir;
13005ac2d602Sdownsj 	struct timespec ts;
13015ac2d602Sdownsj 	struct vnode *tvp;
13025ac2d602Sdownsj 	int error;
13035ac2d602Sdownsj 
13045ac2d602Sdownsj 	pdir = VTOI(dvp);
13055ac2d602Sdownsj #ifdef DIAGNOSTIC
13065ac2d602Sdownsj 	if ((cnp->cn_flags & HASBUF) == 0)
13075ac2d602Sdownsj 		panic("ext2fs_makeinode: no name");
13085ac2d602Sdownsj #endif
13095ac2d602Sdownsj 	*vpp = NULL;
13105ac2d602Sdownsj 	if ((mode & IFMT) == 0)
13115ac2d602Sdownsj 		mode |= IFREG;
13125ac2d602Sdownsj 
13135ac2d602Sdownsj 	if ((error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) != 0) {
13145ac2d602Sdownsj 		free(cnp->cn_pnbuf, M_NAMEI);
13155ac2d602Sdownsj 		vput(dvp);
13165ac2d602Sdownsj 		return (error);
13175ac2d602Sdownsj 	}
13185ac2d602Sdownsj 	ip = VTOI(tvp);
13195ac2d602Sdownsj 	ip->i_e2fs_gid = pdir->i_e2fs_gid;
13205ac2d602Sdownsj 	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
13215ac2d602Sdownsj 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
13225ac2d602Sdownsj 	ip->i_e2fs_mode = mode;
13235ac2d602Sdownsj 	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
13245ac2d602Sdownsj 	ip->i_e2fs_nlink = 1;
13255ac2d602Sdownsj 	if ((ip->i_e2fs_mode & ISGID) &&
13265ac2d602Sdownsj 		!groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
13275ac2d602Sdownsj 	    suser(cnp->cn_cred, NULL))
13285ac2d602Sdownsj 		ip->i_e2fs_mode &= ~ISGID;
13295ac2d602Sdownsj 
13305ac2d602Sdownsj 	/*
13315ac2d602Sdownsj 	 * Make sure inode goes to disk before directory entry.
13325ac2d602Sdownsj 	 */
13335ac2d602Sdownsj 	TIMEVAL_TO_TIMESPEC(&time, &ts);
13345ac2d602Sdownsj 	if ((error = VOP_UPDATE(tvp, &ts, &ts, 1)) != 0)
13355ac2d602Sdownsj 		goto bad;
13365ac2d602Sdownsj 	error = ext2fs_direnter(ip, dvp, cnp);
13375ac2d602Sdownsj 	if (error != 0)
13385ac2d602Sdownsj 		goto bad;
13395ac2d602Sdownsj 	if ((cnp->cn_flags & SAVESTART) == 0)
13405ac2d602Sdownsj 		FREE(cnp->cn_pnbuf, M_NAMEI);
13415ac2d602Sdownsj 	vput(dvp);
13425ac2d602Sdownsj 	*vpp = tvp;
13435ac2d602Sdownsj 	return (0);
13445ac2d602Sdownsj 
13455ac2d602Sdownsj bad:
13465ac2d602Sdownsj 	/*
13475ac2d602Sdownsj 	 * Write error occurred trying to update the inode
13485ac2d602Sdownsj 	 * or the directory so must deallocate the inode.
13495ac2d602Sdownsj 	 */
13505ac2d602Sdownsj 	free(cnp->cn_pnbuf, M_NAMEI);
13515ac2d602Sdownsj 	vput(dvp);
13525ac2d602Sdownsj 	ip->i_e2fs_nlink = 0;
13535ac2d602Sdownsj 	ip->i_flag |= IN_CHANGE;
13545ac2d602Sdownsj 	vput(tvp);
13555ac2d602Sdownsj 	return (error);
13565ac2d602Sdownsj }
13575ac2d602Sdownsj 
13585ac2d602Sdownsj /*
13597dc61945Sdownsj  * Synch an open file.
13607dc61945Sdownsj  */
13617dc61945Sdownsj /* ARGSUSED */
13627dc61945Sdownsj int
13637dc61945Sdownsj ext2fs_fsync(v)
13647dc61945Sdownsj 	void *v;
13657dc61945Sdownsj {
13667dc61945Sdownsj 	struct vop_fsync_args /* {
13677dc61945Sdownsj 		struct vnode *a_vp;
13687dc61945Sdownsj 		struct ucred *a_cred;
13697dc61945Sdownsj 		int a_waitfor;
13707dc61945Sdownsj 		struct proc *a_p;
13717dc61945Sdownsj 	} */ *ap = v;
13727dc61945Sdownsj 	register struct vnode *vp = ap->a_vp;
13737dc61945Sdownsj 	struct timespec ts;
13747dc61945Sdownsj 
13757dc61945Sdownsj 	vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
13767dc61945Sdownsj 	TIMEVAL_TO_TIMESPEC(&time, &ts);
13777dc61945Sdownsj 	return (VOP_UPDATE(ap->a_vp, &ts, &ts, ap->a_waitfor == MNT_WAIT));
13787dc61945Sdownsj }
13797dc61945Sdownsj 
13807dc61945Sdownsj /*
13815ac2d602Sdownsj  * Reclaim an inode so that it can be used for other purposes.
13825ac2d602Sdownsj  */
13835ac2d602Sdownsj int
13845ac2d602Sdownsj ext2fs_reclaim(v)
13855ac2d602Sdownsj 	void *v;
13865ac2d602Sdownsj {
13875ac2d602Sdownsj 	struct vop_reclaim_args /* {
13885ac2d602Sdownsj 		struct vnode *a_vp;
13895ac2d602Sdownsj 	} */ *ap = v;
13905ac2d602Sdownsj 	register struct vnode *vp = ap->a_vp;
13915ac2d602Sdownsj 	struct inode *ip;
13925ac2d602Sdownsj 	extern int prtactive;
13935ac2d602Sdownsj 
13945ac2d602Sdownsj     if (prtactive && vp->v_usecount != 0)
13955ac2d602Sdownsj         vprint("ext2fs_reclaim: pushing active", vp);
13965ac2d602Sdownsj     /*
13975ac2d602Sdownsj      * Remove the inode from its hash chain.
13985ac2d602Sdownsj      */
13995ac2d602Sdownsj     ip = VTOI(vp);
14005ac2d602Sdownsj     ufs_ihashrem(ip);
14015ac2d602Sdownsj     /*
14025ac2d602Sdownsj      * Purge old data structures associated with the inode.
14035ac2d602Sdownsj      */
14045ac2d602Sdownsj     cache_purge(vp);
14055ac2d602Sdownsj     if (ip->i_devvp) {
14065ac2d602Sdownsj         vrele(ip->i_devvp);
14075ac2d602Sdownsj         ip->i_devvp = 0;
14085ac2d602Sdownsj     }
14095ac2d602Sdownsj 
14105ac2d602Sdownsj 	FREE(vp->v_data, M_EXT2FSNODE);
14115ac2d602Sdownsj 	vp->v_data = NULL;
14125ac2d602Sdownsj 	return (0);
14135ac2d602Sdownsj }
14145ac2d602Sdownsj 
14155ac2d602Sdownsj /* Global vfs data structures for ext2fs. */
14165ac2d602Sdownsj int (**ext2fs_vnodeop_p) __P((void *));
14175ac2d602Sdownsj struct vnodeopv_entry_desc ext2fs_vnodeop_entries[] = {
14185ac2d602Sdownsj 	{ &vop_default_desc, vn_default_error },
14195ac2d602Sdownsj 	{ &vop_lookup_desc, ext2fs_lookup },	/* lookup */
14205ac2d602Sdownsj 	{ &vop_create_desc, ext2fs_create },	/* create */
14215ac2d602Sdownsj 	{ &vop_mknod_desc, ext2fs_mknod },		/* mknod */
14225ac2d602Sdownsj 	{ &vop_open_desc, ext2fs_open },		/* open */
14235ac2d602Sdownsj 	{ &vop_close_desc, ufs_close },			/* close */
14245ac2d602Sdownsj 	{ &vop_access_desc, ext2fs_access },	/* access */
14255ac2d602Sdownsj 	{ &vop_getattr_desc, ext2fs_getattr },	/* getattr */
14265ac2d602Sdownsj 	{ &vop_setattr_desc, ext2fs_setattr },	/* setattr */
14275ac2d602Sdownsj 	{ &vop_read_desc, ext2fs_read },		/* read */
14285ac2d602Sdownsj 	{ &vop_write_desc, ext2fs_write },		/* write */
14295ac2d602Sdownsj 	{ &vop_lease_desc, ufs_lease_check },	/* lease */
14305ac2d602Sdownsj 	{ &vop_ioctl_desc, ufs_ioctl },			/* ioctl */
14317dc61945Sdownsj 	{ &vop_select_desc, ufs_select },		/* select */
14325ac2d602Sdownsj 	{ &vop_mmap_desc, ufs_mmap },			/* mmap */
14335ac2d602Sdownsj 	{ &vop_fsync_desc, ext2fs_fsync },		/* fsync */
14345ac2d602Sdownsj 	{ &vop_seek_desc, ufs_seek },			/* seek */
14355ac2d602Sdownsj 	{ &vop_remove_desc, ext2fs_remove },	/* remove */
14365ac2d602Sdownsj 	{ &vop_link_desc, ext2fs_link },		/* link */
14375ac2d602Sdownsj 	{ &vop_rename_desc, ext2fs_rename },	/* rename */
14385ac2d602Sdownsj 	{ &vop_mkdir_desc, ext2fs_mkdir },		/* mkdir */
14395ac2d602Sdownsj 	{ &vop_rmdir_desc, ext2fs_rmdir },		/* rmdir */
14405ac2d602Sdownsj 	{ &vop_symlink_desc, ext2fs_symlink },	/* symlink */
14415ac2d602Sdownsj 	{ &vop_readdir_desc, ext2fs_readdir },	/* readdir */
14425ac2d602Sdownsj 	{ &vop_readlink_desc, ext2fs_readlink },/* readlink */
14435ac2d602Sdownsj 	{ &vop_abortop_desc, ufs_abortop },		/* abortop */
14445ac2d602Sdownsj 	{ &vop_inactive_desc, ext2fs_inactive },/* inactive */
14455ac2d602Sdownsj 	{ &vop_reclaim_desc, ext2fs_reclaim },	/* reclaim */
14465ac2d602Sdownsj 	{ &vop_lock_desc, ufs_lock },			/* lock */
14475ac2d602Sdownsj 	{ &vop_unlock_desc, ufs_unlock },		/* unlock */
14485ac2d602Sdownsj 	{ &vop_bmap_desc, ext2fs_bmap },		/* bmap */
14495ac2d602Sdownsj 	{ &vop_strategy_desc, ufs_strategy },	/* strategy */
14505ac2d602Sdownsj 	{ &vop_print_desc, ufs_print },			/* print */
14515ac2d602Sdownsj 	{ &vop_islocked_desc, ufs_islocked },	/* islocked */
14525ac2d602Sdownsj 	{ &vop_pathconf_desc, ufs_pathconf },	/* pathconf */
14535ac2d602Sdownsj 	{ &vop_advlock_desc, ext2fs_advlock },	/* advlock */
14545ac2d602Sdownsj 	{ &vop_blkatoff_desc, ext2fs_blkatoff },/* blkatoff */
14555ac2d602Sdownsj 	{ &vop_valloc_desc, ext2fs_valloc },	/* valloc */
14565ac2d602Sdownsj 	{ &vop_vfree_desc, ext2fs_vfree },		/* vfree */
14575ac2d602Sdownsj 	{ &vop_truncate_desc, ext2fs_truncate },/* truncate */
14585ac2d602Sdownsj 	{ &vop_update_desc, ext2fs_update },	/* update */
14595ac2d602Sdownsj 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
14605ac2d602Sdownsj 	{ (struct vnodeop_desc*)NULL, (int(*) __P((void*)))NULL }
14615ac2d602Sdownsj };
14625ac2d602Sdownsj struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
14635ac2d602Sdownsj 	{ &ext2fs_vnodeop_p, ext2fs_vnodeop_entries };
14645ac2d602Sdownsj 
14655ac2d602Sdownsj int (**ext2fs_specop_p) __P((void *));
14665ac2d602Sdownsj struct vnodeopv_entry_desc ext2fs_specop_entries[] = {
14675ac2d602Sdownsj 	{ &vop_default_desc, vn_default_error },
14685ac2d602Sdownsj 	{ &vop_lookup_desc, spec_lookup },		/* lookup */
14695ac2d602Sdownsj 	{ &vop_create_desc, spec_create },		/* create */
14705ac2d602Sdownsj 	{ &vop_mknod_desc, spec_mknod },		/* mknod */
14715ac2d602Sdownsj 	{ &vop_open_desc, spec_open },			/* open */
14725ac2d602Sdownsj 	{ &vop_close_desc, ufsspec_close },		/* close */
14735ac2d602Sdownsj 	{ &vop_access_desc, ext2fs_access },	/* access */
14745ac2d602Sdownsj 	{ &vop_getattr_desc, ext2fs_getattr },	/* getattr */
14755ac2d602Sdownsj 	{ &vop_setattr_desc, ext2fs_setattr },	/* setattr */
14765ac2d602Sdownsj 	{ &vop_read_desc, ufsspec_read },		/* read */
14775ac2d602Sdownsj 	{ &vop_write_desc, ufsspec_write },		/* write */
14785ac2d602Sdownsj 	{ &vop_lease_desc, spec_lease_check },	/* lease */
14795ac2d602Sdownsj 	{ &vop_ioctl_desc, spec_ioctl },		/* ioctl */
14807dc61945Sdownsj 	{ &vop_select_desc, spec_select },		/* poll */
14815ac2d602Sdownsj 	{ &vop_mmap_desc, spec_mmap },			/* mmap */
14825ac2d602Sdownsj 	{ &vop_fsync_desc, ext2fs_fsync },		/* fsync */
14835ac2d602Sdownsj 	{ &vop_seek_desc, spec_seek },			/* seek */
14845ac2d602Sdownsj 	{ &vop_remove_desc, spec_remove },		/* remove */
14855ac2d602Sdownsj 	{ &vop_link_desc, spec_link },			/* link */
14865ac2d602Sdownsj 	{ &vop_rename_desc, spec_rename },		/* rename */
14875ac2d602Sdownsj 	{ &vop_mkdir_desc, spec_mkdir },		/* mkdir */
14885ac2d602Sdownsj 	{ &vop_rmdir_desc, spec_rmdir },		/* rmdir */
14895ac2d602Sdownsj 	{ &vop_symlink_desc, spec_symlink },	/* symlink */
14905ac2d602Sdownsj 	{ &vop_readdir_desc, spec_readdir },	/* readdir */
14915ac2d602Sdownsj 	{ &vop_readlink_desc, spec_readlink },	/* readlink */
14925ac2d602Sdownsj 	{ &vop_abortop_desc, spec_abortop },	/* abortop */
14935ac2d602Sdownsj 	{ &vop_inactive_desc, ext2fs_inactive },/* inactive */
14945ac2d602Sdownsj 	{ &vop_reclaim_desc, ext2fs_reclaim },	/* reclaim */
14955ac2d602Sdownsj 	{ &vop_lock_desc, ufs_lock },			/* lock */
14965ac2d602Sdownsj 	{ &vop_unlock_desc, ufs_unlock },		/* unlock */
14975ac2d602Sdownsj 	{ &vop_bmap_desc, spec_bmap },			/* bmap */
14985ac2d602Sdownsj 	{ &vop_strategy_desc, spec_strategy },	/* strategy */
14995ac2d602Sdownsj 	{ &vop_print_desc, ufs_print },			/* print */
15005ac2d602Sdownsj 	{ &vop_islocked_desc, ufs_islocked },	/* islocked */
15015ac2d602Sdownsj 	{ &vop_pathconf_desc, spec_pathconf },	/* pathconf */
15025ac2d602Sdownsj 	{ &vop_advlock_desc, spec_advlock },	/* advlock */
15035ac2d602Sdownsj 	{ &vop_blkatoff_desc, spec_blkatoff },	/* blkatoff */
15045ac2d602Sdownsj 	{ &vop_valloc_desc, spec_valloc },		/* valloc */
15055ac2d602Sdownsj 	{ &vop_vfree_desc, ext2fs_vfree },		/* vfree */
15065ac2d602Sdownsj 	{ &vop_truncate_desc, spec_truncate },	/* truncate */
15075ac2d602Sdownsj 	{ &vop_update_desc, ext2fs_update },	/* update */
15085ac2d602Sdownsj 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
15095ac2d602Sdownsj 	{ (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL }
15105ac2d602Sdownsj };
15115ac2d602Sdownsj struct vnodeopv_desc ext2fs_specop_opv_desc =
15125ac2d602Sdownsj 	{ &ext2fs_specop_p, ext2fs_specop_entries };
15135ac2d602Sdownsj 
15145ac2d602Sdownsj #ifdef FIFO
15155ac2d602Sdownsj int (**ext2fs_fifoop_p) __P((void *));
15165ac2d602Sdownsj struct vnodeopv_entry_desc ext2fs_fifoop_entries[] = {
15175ac2d602Sdownsj 	{ &vop_default_desc, vn_default_error },
15185ac2d602Sdownsj 	{ &vop_lookup_desc, fifo_lookup },		/* lookup */
15195ac2d602Sdownsj 	{ &vop_create_desc, fifo_create },		/* create */
15205ac2d602Sdownsj 	{ &vop_mknod_desc, fifo_mknod },		/* mknod */
15215ac2d602Sdownsj 	{ &vop_open_desc, fifo_open },			/* open */
15225ac2d602Sdownsj 	{ &vop_close_desc, ufsfifo_close },		/* close */
15235ac2d602Sdownsj 	{ &vop_access_desc, ext2fs_access },	/* access */
15245ac2d602Sdownsj 	{ &vop_getattr_desc, ext2fs_getattr },	/* getattr */
15255ac2d602Sdownsj 	{ &vop_setattr_desc, ext2fs_setattr },	/* setattr */
15265ac2d602Sdownsj 	{ &vop_read_desc, ufsfifo_read },		/* read */
15275ac2d602Sdownsj 	{ &vop_write_desc, ufsfifo_write },		/* write */
15285ac2d602Sdownsj 	{ &vop_lease_desc, fifo_lease_check },	/* lease */
15295ac2d602Sdownsj 	{ &vop_ioctl_desc, fifo_ioctl },		/* ioctl */
15307dc61945Sdownsj 	{ &vop_select_desc, fifo_select },		/* select */
15315ac2d602Sdownsj 	{ &vop_mmap_desc, fifo_mmap },			/* mmap */
15325ac2d602Sdownsj 	{ &vop_fsync_desc, ext2fs_fsync },		/* fsync */
15335ac2d602Sdownsj 	{ &vop_seek_desc, fifo_seek },			/* seek */
15345ac2d602Sdownsj 	{ &vop_remove_desc, fifo_remove },		/* remove */
15355ac2d602Sdownsj 	{ &vop_link_desc, fifo_link },			/* link */
15365ac2d602Sdownsj 	{ &vop_rename_desc, fifo_rename },		/* rename */
15375ac2d602Sdownsj 	{ &vop_mkdir_desc, fifo_mkdir },		/* mkdir */
15385ac2d602Sdownsj 	{ &vop_rmdir_desc, fifo_rmdir },		/* rmdir */
15395ac2d602Sdownsj 	{ &vop_symlink_desc, fifo_symlink },	/* symlink */
15405ac2d602Sdownsj 	{ &vop_readdir_desc, fifo_readdir },	/* readdir */
15415ac2d602Sdownsj 	{ &vop_readlink_desc, fifo_readlink },	/* readlink */
15425ac2d602Sdownsj 	{ &vop_abortop_desc, fifo_abortop },	/* abortop */
15435ac2d602Sdownsj 	{ &vop_inactive_desc, ext2fs_inactive },/* inactive */
15445ac2d602Sdownsj 	{ &vop_reclaim_desc, ext2fs_reclaim },	/* reclaim */
15455ac2d602Sdownsj 	{ &vop_lock_desc, ufs_lock },			/* lock */
15465ac2d602Sdownsj 	{ &vop_unlock_desc, ufs_unlock },		/* unlock */
15475ac2d602Sdownsj 	{ &vop_bmap_desc, fifo_bmap },			/* bmap */
15485ac2d602Sdownsj 	{ &vop_strategy_desc, fifo_strategy },	/* strategy */
15495ac2d602Sdownsj 	{ &vop_print_desc, ufs_print },			/* print */
15505ac2d602Sdownsj 	{ &vop_islocked_desc, ufs_islocked },	/* islocked */
15515ac2d602Sdownsj 	{ &vop_pathconf_desc, fifo_pathconf },	/* pathconf */
15525ac2d602Sdownsj 	{ &vop_advlock_desc, fifo_advlock },	/* advlock */
15535ac2d602Sdownsj 	{ &vop_blkatoff_desc, fifo_blkatoff },	/* blkatoff */
15545ac2d602Sdownsj 	{ &vop_valloc_desc, fifo_valloc },		/* valloc */
15555ac2d602Sdownsj 	{ &vop_vfree_desc, ext2fs_vfree },		/* vfree */
15565ac2d602Sdownsj 	{ &vop_truncate_desc, fifo_truncate },	/* truncate */
15575ac2d602Sdownsj 	{ &vop_update_desc, ext2fs_update },	/* update */
15585ac2d602Sdownsj 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
15595ac2d602Sdownsj 	{ (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL }
15605ac2d602Sdownsj };
15615ac2d602Sdownsj struct vnodeopv_desc ext2fs_fifoop_opv_desc =
15625ac2d602Sdownsj 	{ &ext2fs_fifoop_p, ext2fs_fifoop_entries };
15635ac2d602Sdownsj #endif /* FIFO */
1564