xref: /minix3/sys/ufs/ext2fs/ext2fs_inode.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: ext2fs_inode.c,v 1.82 2015/03/28 19:24:04 maxv Exp $	*/
2d65f6f70SBen Gras 
3d65f6f70SBen Gras /*
4d65f6f70SBen Gras  * Copyright (c) 1982, 1986, 1989, 1993
5d65f6f70SBen Gras  *	The Regents of the University of California.  All rights reserved.
6d65f6f70SBen Gras  *
7d65f6f70SBen Gras  * Redistribution and use in source and binary forms, with or without
8d65f6f70SBen Gras  * modification, are permitted provided that the following conditions
9d65f6f70SBen Gras  * are met:
10d65f6f70SBen Gras  * 1. Redistributions of source code must retain the above copyright
11d65f6f70SBen Gras  *    notice, this list of conditions and the following disclaimer.
12d65f6f70SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
13d65f6f70SBen Gras  *    notice, this list of conditions and the following disclaimer in the
14d65f6f70SBen Gras  *    documentation and/or other materials provided with the distribution.
15d65f6f70SBen Gras  * 3. Neither the name of the University nor the names of its contributors
16d65f6f70SBen Gras  *    may be used to endorse or promote products derived from this software
17d65f6f70SBen Gras  *    without specific prior written permission.
18d65f6f70SBen Gras  *
19d65f6f70SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20d65f6f70SBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21d65f6f70SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22d65f6f70SBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23d65f6f70SBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24d65f6f70SBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25d65f6f70SBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26d65f6f70SBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27d65f6f70SBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28d65f6f70SBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29d65f6f70SBen Gras  * SUCH DAMAGE.
30d65f6f70SBen Gras  *
31d65f6f70SBen Gras  *	@(#)ffs_inode.c	8.8 (Berkeley) 10/19/94
32d65f6f70SBen Gras  * Modified for ext2fs by Manuel Bouyer.
33d65f6f70SBen Gras  */
34d65f6f70SBen Gras 
35d65f6f70SBen Gras /*
36d65f6f70SBen Gras  * Copyright (c) 1997 Manuel Bouyer.
37d65f6f70SBen Gras  *
38d65f6f70SBen Gras  * Redistribution and use in source and binary forms, with or without
39d65f6f70SBen Gras  * modification, are permitted provided that the following conditions
40d65f6f70SBen Gras  * are met:
41d65f6f70SBen Gras  * 1. Redistributions of source code must retain the above copyright
42d65f6f70SBen Gras  *    notice, this list of conditions and the following disclaimer.
43d65f6f70SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
44d65f6f70SBen Gras  *    notice, this list of conditions and the following disclaimer in the
45d65f6f70SBen Gras  *    documentation and/or other materials provided with the distribution.
46d65f6f70SBen Gras  *
47d65f6f70SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48d65f6f70SBen Gras  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49d65f6f70SBen Gras  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50d65f6f70SBen Gras  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51d65f6f70SBen Gras  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52d65f6f70SBen Gras  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53d65f6f70SBen Gras  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54d65f6f70SBen Gras  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55d65f6f70SBen Gras  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56d65f6f70SBen Gras  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57d65f6f70SBen Gras  *
58d65f6f70SBen Gras  *	@(#)ffs_inode.c	8.8 (Berkeley) 10/19/94
59d65f6f70SBen Gras  * Modified for ext2fs by Manuel Bouyer.
60d65f6f70SBen Gras  */
61d65f6f70SBen Gras 
62d65f6f70SBen Gras #include <sys/cdefs.h>
63*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: ext2fs_inode.c,v 1.82 2015/03/28 19:24:04 maxv Exp $");
64d65f6f70SBen Gras 
65d65f6f70SBen Gras #include <sys/param.h>
66d65f6f70SBen Gras #include <sys/systm.h>
67d65f6f70SBen Gras #include <sys/mount.h>
68d65f6f70SBen Gras #include <sys/proc.h>
69d65f6f70SBen Gras #include <sys/file.h>
70d65f6f70SBen Gras #include <sys/buf.h>
71d65f6f70SBen Gras #include <sys/vnode.h>
72d65f6f70SBen Gras #include <sys/kernel.h>
7384d9c625SLionel Sambuc #include <sys/kmem.h>
74d65f6f70SBen Gras #include <sys/trace.h>
75d65f6f70SBen Gras #include <sys/resourcevar.h>
76d65f6f70SBen Gras #include <sys/kauth.h>
77d65f6f70SBen Gras 
78d65f6f70SBen Gras #include <ufs/ufs/inode.h>
79d65f6f70SBen Gras #include <ufs/ufs/ufsmount.h>
80d65f6f70SBen Gras #include <ufs/ufs/ufs_extern.h>
81d65f6f70SBen Gras 
82d65f6f70SBen Gras #include <ufs/ext2fs/ext2fs.h>
83d65f6f70SBen Gras #include <ufs/ext2fs/ext2fs_extern.h>
84d65f6f70SBen Gras 
85d65f6f70SBen Gras extern int prtactive;
86d65f6f70SBen Gras 
87d65f6f70SBen Gras static int ext2fs_indirtrunc(struct inode *, daddr_t, daddr_t,
88d65f6f70SBen Gras 				  daddr_t, int, long *);
89d65f6f70SBen Gras 
90d65f6f70SBen Gras /*
9184d9c625SLionel Sambuc  * These are fortunately the same values; it is likely that there is
9284d9c625SLionel Sambuc  * code that assumes they're equal. In any event, neither ought to
9384d9c625SLionel Sambuc  * ever change because it's a property of the on-disk formats.
9484d9c625SLionel Sambuc  */
9584d9c625SLionel Sambuc CTASSERT(EXT2FS_NDADDR == UFS_NDADDR);
9684d9c625SLionel Sambuc CTASSERT(EXT2FS_NIADDR == UFS_NIADDR);
9784d9c625SLionel Sambuc 
9884d9c625SLionel Sambuc /*
99d65f6f70SBen Gras  * Get the size of an inode.
100d65f6f70SBen Gras  */
101d65f6f70SBen Gras uint64_t
ext2fs_size(struct inode * ip)102d65f6f70SBen Gras ext2fs_size(struct inode *ip)
103d65f6f70SBen Gras {
104d65f6f70SBen Gras 	uint64_t size = ip->i_e2fs_size;
105d65f6f70SBen Gras 
106d65f6f70SBen Gras 	if ((ip->i_e2fs_mode & IFMT) == IFREG)
107d65f6f70SBen Gras 		size |= (uint64_t)ip->i_e2fs_dacl << 32;
108d65f6f70SBen Gras 	return size;
109d65f6f70SBen Gras }
110d65f6f70SBen Gras 
111d65f6f70SBen Gras int
ext2fs_setsize(struct inode * ip,uint64_t size)112d65f6f70SBen Gras ext2fs_setsize(struct inode *ip, uint64_t size)
113d65f6f70SBen Gras {
114d65f6f70SBen Gras 	if ((ip->i_e2fs_mode & IFMT) == IFREG ||
115d65f6f70SBen Gras 	    ip->i_e2fs_mode == 0) {
116d65f6f70SBen Gras 		ip->i_e2fs_dacl = size >> 32;
117d65f6f70SBen Gras 		if (size >= 0x80000000U) {
118d65f6f70SBen Gras 			struct m_ext2fs *fs = ip->i_e2fs;
119d65f6f70SBen Gras 
120d65f6f70SBen Gras 			if (fs->e2fs.e2fs_rev <= E2FS_REV0) {
121d65f6f70SBen Gras 				/* Linux automagically upgrades to REV1 here! */
122d65f6f70SBen Gras 				return EFBIG;
123d65f6f70SBen Gras 			}
124d65f6f70SBen Gras 			if (!(fs->e2fs.e2fs_features_rocompat
125d65f6f70SBen Gras 			    & EXT2F_ROCOMPAT_LARGEFILE)) {
126d65f6f70SBen Gras 				fs->e2fs.e2fs_features_rocompat |=
127d65f6f70SBen Gras 				    EXT2F_ROCOMPAT_LARGEFILE;
128d65f6f70SBen Gras 				fs->e2fs_fmod = 1;
129d65f6f70SBen Gras 			}
130d65f6f70SBen Gras 		}
131d65f6f70SBen Gras 	} else if (size >= 0x80000000U)
132d65f6f70SBen Gras 		return EFBIG;
133d65f6f70SBen Gras 
134d65f6f70SBen Gras 	ip->i_e2fs_size = size;
135d65f6f70SBen Gras 
136d65f6f70SBen Gras 	return 0;
137d65f6f70SBen Gras }
138d65f6f70SBen Gras 
13984d9c625SLionel Sambuc uint64_t
ext2fs_nblock(struct inode * ip)14084d9c625SLionel Sambuc ext2fs_nblock(struct inode *ip)
14184d9c625SLionel Sambuc {
14284d9c625SLionel Sambuc 	uint64_t nblock = ip->i_e2fs_nblock;
14384d9c625SLionel Sambuc 	struct m_ext2fs * const fs = ip->i_e2fs;
14484d9c625SLionel Sambuc 
14584d9c625SLionel Sambuc 	if (fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_HUGE_FILE) {
14684d9c625SLionel Sambuc 		nblock |= (uint64_t)ip->i_e2fs_nblock_high << 32;
14784d9c625SLionel Sambuc 
14884d9c625SLionel Sambuc 		if ((ip->i_e2fs_flags & EXT2_HUGE_FILE)) {
14984d9c625SLionel Sambuc 			nblock = EXT2_FSBTODB(fs, nblock);
15084d9c625SLionel Sambuc 		}
15184d9c625SLionel Sambuc 	}
15284d9c625SLionel Sambuc 
15384d9c625SLionel Sambuc 	return nblock;
15484d9c625SLionel Sambuc }
15584d9c625SLionel Sambuc 
15684d9c625SLionel Sambuc int
ext2fs_setnblock(struct inode * ip,uint64_t nblock)15784d9c625SLionel Sambuc ext2fs_setnblock(struct inode *ip, uint64_t nblock)
15884d9c625SLionel Sambuc {
15984d9c625SLionel Sambuc 	struct m_ext2fs * const fs = ip->i_e2fs;
16084d9c625SLionel Sambuc 
16184d9c625SLionel Sambuc 	if (nblock <= 0xffffffffULL) {
16284d9c625SLionel Sambuc 		CLR(ip->i_e2fs_flags, EXT2_HUGE_FILE);
16384d9c625SLionel Sambuc 		ip->i_e2fs_nblock = nblock;
16484d9c625SLionel Sambuc 		return 0;
16584d9c625SLionel Sambuc 	}
16684d9c625SLionel Sambuc 
16784d9c625SLionel Sambuc 	if (!ISSET(fs->e2fs.e2fs_features_rocompat, EXT2F_ROCOMPAT_HUGE_FILE))
16884d9c625SLionel Sambuc 		return EFBIG;
16984d9c625SLionel Sambuc 
17084d9c625SLionel Sambuc 	if (nblock <= 0xffffffffffffULL) {
17184d9c625SLionel Sambuc 		CLR(ip->i_e2fs_flags, EXT2_HUGE_FILE);
17284d9c625SLionel Sambuc 		ip->i_e2fs_nblock = nblock & 0xffffffff;
17384d9c625SLionel Sambuc 		ip->i_e2fs_nblock_high = (nblock >> 32) & 0xffff;
17484d9c625SLionel Sambuc 		return 0;
17584d9c625SLionel Sambuc 	}
17684d9c625SLionel Sambuc 
17784d9c625SLionel Sambuc 	if (EXT2_DBTOFSB(fs, nblock) <= 0xffffffffffffULL) {
17884d9c625SLionel Sambuc 		SET(ip->i_e2fs_flags, EXT2_HUGE_FILE);
17984d9c625SLionel Sambuc 		ip->i_e2fs_nblock = EXT2_DBTOFSB(fs, nblock) & 0xffffffff;
18084d9c625SLionel Sambuc 		ip->i_e2fs_nblock_high = (EXT2_DBTOFSB(fs, nblock) >> 32) & 0xffff;
18184d9c625SLionel Sambuc 		return 0;
18284d9c625SLionel Sambuc 	}
18384d9c625SLionel Sambuc 
18484d9c625SLionel Sambuc 	return EFBIG;
18584d9c625SLionel Sambuc }
18684d9c625SLionel Sambuc 
187d65f6f70SBen Gras /*
188d65f6f70SBen Gras  * Last reference to an inode.  If necessary, write or delete it.
189d65f6f70SBen Gras  */
190d65f6f70SBen Gras int
ext2fs_inactive(void * v)191d65f6f70SBen Gras ext2fs_inactive(void *v)
192d65f6f70SBen Gras {
193d65f6f70SBen Gras 	struct vop_inactive_args /* {
194d65f6f70SBen Gras 		struct vnode *a_vp;
195d65f6f70SBen Gras 		bool *a_recycle;
196d65f6f70SBen Gras 	} */ *ap = v;
197d65f6f70SBen Gras 	struct vnode *vp = ap->a_vp;
198d65f6f70SBen Gras 	struct inode *ip = VTOI(vp);
199d65f6f70SBen Gras 	int error = 0;
200d65f6f70SBen Gras 
201d65f6f70SBen Gras 	if (prtactive && vp->v_usecount != 0)
202d65f6f70SBen Gras 		vprint("ext2fs_inactive: pushing active", vp);
203d65f6f70SBen Gras 	/* Get rid of inodes related to stale file handles. */
204d65f6f70SBen Gras 	if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0)
205d65f6f70SBen Gras 		goto out;
206d65f6f70SBen Gras 
207d65f6f70SBen Gras 	error = 0;
208d65f6f70SBen Gras 	if (ip->i_e2fs_nlink == 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
209d65f6f70SBen Gras 		/* Defer final inode free and update to reclaim.*/
210d65f6f70SBen Gras 		if (ext2fs_size(ip) != 0) {
211d65f6f70SBen Gras 			error = ext2fs_truncate(vp, (off_t)0, 0, NOCRED);
212d65f6f70SBen Gras 		}
213d65f6f70SBen Gras 		ip->i_e2fs_dtime = time_second;
214d65f6f70SBen Gras 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
215d65f6f70SBen Gras 		ip->i_omode = 1;
216d65f6f70SBen Gras 	}
217d65f6f70SBen Gras 	if (ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) {
218d65f6f70SBen Gras 		ext2fs_update(vp, NULL, NULL, 0);
219d65f6f70SBen Gras 	}
220d65f6f70SBen Gras out:
221d65f6f70SBen Gras 	/*
222d65f6f70SBen Gras 	 * If we are done with the inode, reclaim it
223d65f6f70SBen Gras 	 * so that it can be reused immediately.
224d65f6f70SBen Gras 	 */
225d65f6f70SBen Gras 	*ap->a_recycle = (ip->i_e2fs_dtime != 0);
226d65f6f70SBen Gras 	VOP_UNLOCK(vp);
227d65f6f70SBen Gras 	return (error);
228d65f6f70SBen Gras }
229d65f6f70SBen Gras 
230d65f6f70SBen Gras 
231d65f6f70SBen Gras /*
232d65f6f70SBen Gras  * Update the access, modified, and inode change times as specified by the
233d65f6f70SBen Gras  * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
234d65f6f70SBen Gras  * used to specify that the inode needs to be updated but that the times have
235d65f6f70SBen Gras  * already been set. The access and modified times are taken from the second
236d65f6f70SBen Gras  * and third parameters; the inode change time is always taken from the current
237d65f6f70SBen Gras  * time. If UPDATE_WAIT or UPDATE_DIROP is set, then wait for the disk
238d65f6f70SBen Gras  * write of the inode to complete.
239d65f6f70SBen Gras  */
240d65f6f70SBen Gras int
ext2fs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int updflags)241d65f6f70SBen Gras ext2fs_update(struct vnode *vp, const struct timespec *acc,
242d65f6f70SBen Gras     const struct timespec *mod, int updflags)
243d65f6f70SBen Gras {
244d65f6f70SBen Gras 	struct m_ext2fs *fs;
245d65f6f70SBen Gras 	struct buf *bp;
246d65f6f70SBen Gras 	struct inode *ip;
247d65f6f70SBen Gras 	int error;
248d65f6f70SBen Gras 	void *cp;
249d65f6f70SBen Gras 	int flags;
250d65f6f70SBen Gras 
251d65f6f70SBen Gras 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
252d65f6f70SBen Gras 		return (0);
253d65f6f70SBen Gras 	ip = VTOI(vp);
254d65f6f70SBen Gras 	EXT2FS_ITIMES(ip, acc, mod, NULL);
255d65f6f70SBen Gras 	if (updflags & UPDATE_CLOSE)
256d65f6f70SBen Gras 		flags = ip->i_flag & (IN_MODIFIED | IN_ACCESSED);
257d65f6f70SBen Gras 	else
258d65f6f70SBen Gras 		flags = ip->i_flag & IN_MODIFIED;
259d65f6f70SBen Gras 	if (flags == 0)
260d65f6f70SBen Gras 		return (0);
261d65f6f70SBen Gras 	fs = ip->i_e2fs;
262d65f6f70SBen Gras 
263d65f6f70SBen Gras 	error = bread(ip->i_devvp,
26484d9c625SLionel Sambuc 			  EXT2_FSBTODB(fs, ino_to_fsba(fs, ip->i_number)),
265*0a6a1f1dSLionel Sambuc 			  (int)fs->e2fs_bsize, B_MODIFY, &bp);
266d65f6f70SBen Gras 	if (error) {
267d65f6f70SBen Gras 		return (error);
268d65f6f70SBen Gras 	}
269d65f6f70SBen Gras 	ip->i_flag &= ~(IN_MODIFIED | IN_ACCESSED);
270d65f6f70SBen Gras 	cp = (char *)bp->b_data +
271d65f6f70SBen Gras 	    (ino_to_fsbo(fs, ip->i_number) * EXT2_DINODE_SIZE(fs));
272d65f6f70SBen Gras 	e2fs_isave(ip->i_din.e2fs_din, (struct ext2fs_dinode *)cp);
273d65f6f70SBen Gras 	if ((updflags & (UPDATE_WAIT|UPDATE_DIROP)) != 0 &&
274d65f6f70SBen Gras 	    (flags & IN_MODIFIED) != 0 &&
275d65f6f70SBen Gras 	    (vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
276d65f6f70SBen Gras 		return (bwrite(bp));
277d65f6f70SBen Gras 	else {
278d65f6f70SBen Gras 		bdwrite(bp);
279d65f6f70SBen Gras 		return (0);
280d65f6f70SBen Gras 	}
281d65f6f70SBen Gras }
282d65f6f70SBen Gras 
283d65f6f70SBen Gras #define	SINGLE	0	/* index of single indirect block */
284d65f6f70SBen Gras #define	DOUBLE	1	/* index of double indirect block */
285d65f6f70SBen Gras #define	TRIPLE	2	/* index of triple indirect block */
286d65f6f70SBen Gras /*
287d65f6f70SBen Gras  * Truncate the inode oip to at most length size, freeing the
288d65f6f70SBen Gras  * disk blocks.
289d65f6f70SBen Gras  */
290d65f6f70SBen Gras int
ext2fs_truncate(struct vnode * ovp,off_t length,int ioflag,kauth_cred_t cred)291d65f6f70SBen Gras ext2fs_truncate(struct vnode *ovp, off_t length, int ioflag,
292d65f6f70SBen Gras     kauth_cred_t cred)
293d65f6f70SBen Gras {
294d65f6f70SBen Gras 	daddr_t lastblock;
295d65f6f70SBen Gras 	struct inode *oip = VTOI(ovp);
29684d9c625SLionel Sambuc 	daddr_t bn, lastiblock[EXT2FS_NIADDR], indir_lbn[EXT2FS_NIADDR];
297d65f6f70SBen Gras 	/* XXX ondisk32 */
29884d9c625SLionel Sambuc 	int32_t oldblks[EXT2FS_NDADDR + EXT2FS_NIADDR], newblks[EXT2FS_NDADDR + EXT2FS_NIADDR];
299d65f6f70SBen Gras 	struct m_ext2fs *fs;
300d65f6f70SBen Gras 	int offset, size, level;
301d65f6f70SBen Gras 	long count, blocksreleased = 0;
302d65f6f70SBen Gras 	int i, nblocks;
303d65f6f70SBen Gras 	int error, allerror = 0;
304d65f6f70SBen Gras 	off_t osize;
305d65f6f70SBen Gras 	int sync;
306d65f6f70SBen Gras 	struct ufsmount *ump = oip->i_ump;
307d65f6f70SBen Gras 
308d65f6f70SBen Gras 	if (ovp->v_type == VCHR || ovp->v_type == VBLK ||
309d65f6f70SBen Gras 	    ovp->v_type == VFIFO || ovp->v_type == VSOCK) {
310d65f6f70SBen Gras 		return 0;
311d65f6f70SBen Gras 	}
312d65f6f70SBen Gras 
313d65f6f70SBen Gras 	if (length < 0)
314d65f6f70SBen Gras 		return (EINVAL);
315d65f6f70SBen Gras 
316d65f6f70SBen Gras 	if (ovp->v_type == VLNK &&
317d65f6f70SBen Gras 	    (ext2fs_size(oip) < ump->um_maxsymlinklen ||
31884d9c625SLionel Sambuc 	     (ump->um_maxsymlinklen == 0 && ext2fs_nblock(oip) == 0))) {
319d65f6f70SBen Gras 		KDASSERT(length == 0);
320d65f6f70SBen Gras 		memset((char *)&oip->i_din.e2fs_din->e2di_shortlink, 0,
321d65f6f70SBen Gras 			(u_int)ext2fs_size(oip));
322d65f6f70SBen Gras 		(void)ext2fs_setsize(oip, 0);
323d65f6f70SBen Gras 		oip->i_flag |= IN_CHANGE | IN_UPDATE;
324d65f6f70SBen Gras 		return (ext2fs_update(ovp, NULL, NULL, 0));
325d65f6f70SBen Gras 	}
326d65f6f70SBen Gras 	if (ext2fs_size(oip) == length) {
327d65f6f70SBen Gras 		/* still do a uvm_vnp_setsize() as writesize may be larger */
328d65f6f70SBen Gras 		uvm_vnp_setsize(ovp, length);
329d65f6f70SBen Gras 		oip->i_flag |= IN_CHANGE | IN_UPDATE;
330d65f6f70SBen Gras 		return (ext2fs_update(ovp, NULL, NULL, 0));
331d65f6f70SBen Gras 	}
332d65f6f70SBen Gras 	fs = oip->i_e2fs;
333d65f6f70SBen Gras 	if (length > ump->um_maxfilesize)
334d65f6f70SBen Gras 		return (EFBIG);
335d65f6f70SBen Gras 
336d65f6f70SBen Gras 	osize = ext2fs_size(oip);
337d65f6f70SBen Gras 
338d65f6f70SBen Gras 	/*
339d65f6f70SBen Gras 	 * Lengthen the size of the file. We must ensure that the
340d65f6f70SBen Gras 	 * last byte of the file is allocated. Since the smallest
341d65f6f70SBen Gras 	 * value of osize is 0, length will be at least 1.
342d65f6f70SBen Gras 	 */
343d65f6f70SBen Gras 	if (osize < length) {
344d65f6f70SBen Gras 		uvm_vnp_setwritesize(ovp, length);
345d65f6f70SBen Gras 		error = ufs_balloc_range(ovp, length - 1, 1, cred,
346d65f6f70SBen Gras 		    ioflag & IO_SYNC ? B_SYNC : 0);
347d65f6f70SBen Gras 		if (error) {
348d65f6f70SBen Gras 			(void) ext2fs_truncate(ovp, osize, ioflag & IO_SYNC,
349d65f6f70SBen Gras 			    cred);
350d65f6f70SBen Gras 			return (error);
351d65f6f70SBen Gras 		}
352d65f6f70SBen Gras 		uvm_vnp_setsize(ovp, length);
353d65f6f70SBen Gras 		oip->i_flag |= IN_CHANGE | IN_UPDATE;
354d65f6f70SBen Gras 		KASSERT(error  || ovp->v_size == ext2fs_size(oip));
355d65f6f70SBen Gras 		return (ext2fs_update(ovp, NULL, NULL, 0));
356d65f6f70SBen Gras 	}
357d65f6f70SBen Gras 	/*
358d65f6f70SBen Gras 	 * Shorten the size of the file. If the file is not being
359d65f6f70SBen Gras 	 * truncated to a block boundry, the contents of the
360d65f6f70SBen Gras 	 * partial block following the end of the file must be
361d65f6f70SBen Gras 	 * zero'ed in case it ever become accessible again because
362d65f6f70SBen Gras 	 * of subsequent file growth.
363d65f6f70SBen Gras 	 */
36484d9c625SLionel Sambuc 	offset = ext2_blkoff(fs, length);
365d65f6f70SBen Gras 	if (offset != 0) {
366d65f6f70SBen Gras 		size = fs->e2fs_bsize;
367d65f6f70SBen Gras 
368d65f6f70SBen Gras 		/* XXXUBC we should handle more than just VREG */
369d65f6f70SBen Gras 		ubc_zerorange(&ovp->v_uobj, length, size - offset,
370d65f6f70SBen Gras 		    UBC_UNMAP_FLAG(ovp));
371d65f6f70SBen Gras 	}
372d65f6f70SBen Gras 	(void)ext2fs_setsize(oip, length);
373d65f6f70SBen Gras 	uvm_vnp_setsize(ovp, length);
374d65f6f70SBen Gras 	/*
375d65f6f70SBen Gras 	 * Calculate index into inode's block list of
376d65f6f70SBen Gras 	 * last direct and indirect blocks (if any)
377d65f6f70SBen Gras 	 * which we want to keep.  Lastblock is -1 when
378d65f6f70SBen Gras 	 * the file is truncated to 0.
379d65f6f70SBen Gras 	 */
38084d9c625SLionel Sambuc 	lastblock = ext2_lblkno(fs, length + fs->e2fs_bsize - 1) - 1;
38184d9c625SLionel Sambuc 	lastiblock[SINGLE] = lastblock - EXT2FS_NDADDR;
38284d9c625SLionel Sambuc 	lastiblock[DOUBLE] = lastiblock[SINGLE] - EXT2_NINDIR(fs);
38384d9c625SLionel Sambuc 	lastiblock[TRIPLE] = lastiblock[DOUBLE] - EXT2_NINDIR(fs) * EXT2_NINDIR(fs);
384d65f6f70SBen Gras 	nblocks = btodb(fs->e2fs_bsize);
385d65f6f70SBen Gras 	/*
386d65f6f70SBen Gras 	 * Update file and block pointers on disk before we start freeing
387d65f6f70SBen Gras 	 * blocks.  If we crash before free'ing blocks below, the blocks
388d65f6f70SBen Gras 	 * will be returned to the free list.  lastiblock values are also
389d65f6f70SBen Gras 	 * normalized to -1 for calls to ext2fs_indirtrunc below.
390d65f6f70SBen Gras 	 */
391d65f6f70SBen Gras 	memcpy((void *)oldblks, (void *)&oip->i_e2fs_blocks[0], sizeof oldblks);
392d65f6f70SBen Gras 	sync = 0;
393d65f6f70SBen Gras 	for (level = TRIPLE; level >= SINGLE; level--) {
39484d9c625SLionel Sambuc 		if (lastiblock[level] < 0 && oldblks[EXT2FS_NDADDR + level] != 0) {
395d65f6f70SBen Gras 			sync = 1;
39684d9c625SLionel Sambuc 			oip->i_e2fs_blocks[EXT2FS_NDADDR + level] = 0;
397d65f6f70SBen Gras 			lastiblock[level] = -1;
398d65f6f70SBen Gras 		}
399d65f6f70SBen Gras 	}
40084d9c625SLionel Sambuc 	for (i = 0; i < EXT2FS_NDADDR; i++) {
401d65f6f70SBen Gras 		if (i > lastblock && oldblks[i] != 0) {
402d65f6f70SBen Gras 			sync = 1;
403d65f6f70SBen Gras 			oip->i_e2fs_blocks[i] = 0;
404d65f6f70SBen Gras 		}
405d65f6f70SBen Gras 	}
406d65f6f70SBen Gras 	oip->i_flag |= IN_CHANGE | IN_UPDATE;
407d65f6f70SBen Gras 	if (sync) {
408d65f6f70SBen Gras 		error = ext2fs_update(ovp, NULL, NULL, UPDATE_WAIT);
409d65f6f70SBen Gras 		if (error && !allerror)
410d65f6f70SBen Gras 			allerror = error;
411d65f6f70SBen Gras 	}
412d65f6f70SBen Gras 
413d65f6f70SBen Gras 	/*
414d65f6f70SBen Gras 	 * Having written the new inode to disk, save its new configuration
415d65f6f70SBen Gras 	 * and put back the old block pointers long enough to process them.
416d65f6f70SBen Gras 	 * Note that we save the new block configuration so we can check it
417d65f6f70SBen Gras 	 * when we are done.
418d65f6f70SBen Gras 	 */
419d65f6f70SBen Gras 	memcpy((void *)newblks, (void *)&oip->i_e2fs_blocks[0], sizeof newblks);
420d65f6f70SBen Gras 	memcpy((void *)&oip->i_e2fs_blocks[0], (void *)oldblks, sizeof oldblks);
421d65f6f70SBen Gras 
422d65f6f70SBen Gras 	(void)ext2fs_setsize(oip, osize);
423d65f6f70SBen Gras 	error = vtruncbuf(ovp, lastblock + 1, 0, 0);
424d65f6f70SBen Gras 	if (error && !allerror)
425d65f6f70SBen Gras 		allerror = error;
426d65f6f70SBen Gras 
427d65f6f70SBen Gras 	/*
428d65f6f70SBen Gras 	 * Indirect blocks first.
429d65f6f70SBen Gras 	 */
43084d9c625SLionel Sambuc 	indir_lbn[SINGLE] = -EXT2FS_NDADDR;
43184d9c625SLionel Sambuc 	indir_lbn[DOUBLE] = indir_lbn[SINGLE] - EXT2_NINDIR(fs) -1;
43284d9c625SLionel Sambuc 	indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - EXT2_NINDIR(fs) * EXT2_NINDIR(fs) - 1;
433d65f6f70SBen Gras 	for (level = TRIPLE; level >= SINGLE; level--) {
434d65f6f70SBen Gras 		/* XXX ondisk32 */
43584d9c625SLionel Sambuc 		bn = fs2h32(oip->i_e2fs_blocks[EXT2FS_NDADDR + level]);
436d65f6f70SBen Gras 		if (bn != 0) {
437d65f6f70SBen Gras 			error = ext2fs_indirtrunc(oip, indir_lbn[level],
43884d9c625SLionel Sambuc 			    EXT2_FSBTODB(fs, bn), lastiblock[level], level, &count);
439d65f6f70SBen Gras 			if (error)
440d65f6f70SBen Gras 				allerror = error;
441d65f6f70SBen Gras 			blocksreleased += count;
442d65f6f70SBen Gras 			if (lastiblock[level] < 0) {
44384d9c625SLionel Sambuc 				oip->i_e2fs_blocks[EXT2FS_NDADDR + level] = 0;
444d65f6f70SBen Gras 				ext2fs_blkfree(oip, bn);
445d65f6f70SBen Gras 				blocksreleased += nblocks;
446d65f6f70SBen Gras 			}
447d65f6f70SBen Gras 		}
448d65f6f70SBen Gras 		if (lastiblock[level] >= 0)
449d65f6f70SBen Gras 			goto done;
450d65f6f70SBen Gras 	}
451d65f6f70SBen Gras 
452d65f6f70SBen Gras 	/*
453d65f6f70SBen Gras 	 * All whole direct blocks or frags.
454d65f6f70SBen Gras 	 */
45584d9c625SLionel Sambuc 	for (i = EXT2FS_NDADDR - 1; i > lastblock; i--) {
456d65f6f70SBen Gras 		/* XXX ondisk32 */
457d65f6f70SBen Gras 		bn = fs2h32(oip->i_e2fs_blocks[i]);
458d65f6f70SBen Gras 		if (bn == 0)
459d65f6f70SBen Gras 			continue;
460d65f6f70SBen Gras 		oip->i_e2fs_blocks[i] = 0;
461d65f6f70SBen Gras 		ext2fs_blkfree(oip, bn);
462d65f6f70SBen Gras 		blocksreleased += btodb(fs->e2fs_bsize);
463d65f6f70SBen Gras 	}
464d65f6f70SBen Gras 
465d65f6f70SBen Gras done:
466d65f6f70SBen Gras #ifdef DIAGNOSTIC
467d65f6f70SBen Gras 	for (level = SINGLE; level <= TRIPLE; level++)
46884d9c625SLionel Sambuc 		if (newblks[EXT2FS_NDADDR + level] !=
46984d9c625SLionel Sambuc 		    oip->i_e2fs_blocks[EXT2FS_NDADDR + level])
470d65f6f70SBen Gras 			panic("ext2fs_truncate1");
47184d9c625SLionel Sambuc 	for (i = 0; i < EXT2FS_NDADDR; i++)
472d65f6f70SBen Gras 		if (newblks[i] != oip->i_e2fs_blocks[i])
473d65f6f70SBen Gras 			panic("ext2fs_truncate2");
474d65f6f70SBen Gras 	if (length == 0 &&
475d65f6f70SBen Gras 	    (!LIST_EMPTY(&ovp->v_cleanblkhd) ||
476d65f6f70SBen Gras 	     !LIST_EMPTY(&ovp->v_dirtyblkhd)))
477d65f6f70SBen Gras 		panic("ext2fs_truncate3");
478d65f6f70SBen Gras #endif /* DIAGNOSTIC */
479d65f6f70SBen Gras 	/*
480d65f6f70SBen Gras 	 * Put back the real size.
481d65f6f70SBen Gras 	 */
482d65f6f70SBen Gras 	(void)ext2fs_setsize(oip, length);
48384d9c625SLionel Sambuc 	error = ext2fs_setnblock(oip, ext2fs_nblock(oip) - blocksreleased);
48484d9c625SLionel Sambuc 	if (error != 0)
48584d9c625SLionel Sambuc 		allerror = error;
486d65f6f70SBen Gras 	oip->i_flag |= IN_CHANGE;
487d65f6f70SBen Gras 	KASSERT(ovp->v_type != VREG || ovp->v_size == ext2fs_size(oip));
488d65f6f70SBen Gras 	return (allerror);
489d65f6f70SBen Gras }
490d65f6f70SBen Gras 
491d65f6f70SBen Gras /*
492d65f6f70SBen Gras  * Release blocks associated with the inode ip and stored in the indirect
493d65f6f70SBen Gras  * block bn.  Blocks are free'd in LIFO order up to (but not including)
494d65f6f70SBen Gras  * lastbn.  If level is greater than SINGLE, the block is an indirect block
495d65f6f70SBen Gras  * and recursive calls to indirtrunc must be used to cleanse other indirect
496d65f6f70SBen Gras  * blocks.
497d65f6f70SBen Gras  *
498d65f6f70SBen Gras  * NB: triple indirect blocks are untested.
499d65f6f70SBen Gras  */
500d65f6f70SBen Gras static int
ext2fs_indirtrunc(struct inode * ip,daddr_t lbn,daddr_t dbn,daddr_t lastbn,int level,long * countp)501d65f6f70SBen Gras ext2fs_indirtrunc(struct inode *ip, daddr_t lbn, daddr_t dbn, daddr_t lastbn,
502d65f6f70SBen Gras 		int level, long *countp)
503d65f6f70SBen Gras {
504d65f6f70SBen Gras 	int i;
505d65f6f70SBen Gras 	struct buf *bp;
506d65f6f70SBen Gras 	struct m_ext2fs *fs = ip->i_e2fs;
507d65f6f70SBen Gras 	int32_t *bap;	/* XXX ondisk32 */
508d65f6f70SBen Gras 	struct vnode *vp;
509d65f6f70SBen Gras 	daddr_t nb, nlbn, last;
510d65f6f70SBen Gras 	int32_t *copy = NULL;	/* XXX ondisk32 */
511d65f6f70SBen Gras 	long blkcount, factor;
512d65f6f70SBen Gras 	int nblocks, blocksreleased = 0;
513d65f6f70SBen Gras 	int error = 0, allerror = 0;
514d65f6f70SBen Gras 
515d65f6f70SBen Gras 	/*
516d65f6f70SBen Gras 	 * Calculate index in current block of last
517d65f6f70SBen Gras 	 * block to be kept.  -1 indicates the entire
518d65f6f70SBen Gras 	 * block so we need not calculate the index.
519d65f6f70SBen Gras 	 */
520d65f6f70SBen Gras 	factor = 1;
521d65f6f70SBen Gras 	for (i = SINGLE; i < level; i++)
52284d9c625SLionel Sambuc 		factor *= EXT2_NINDIR(fs);
523d65f6f70SBen Gras 	last = lastbn;
524d65f6f70SBen Gras 	if (lastbn > 0)
525d65f6f70SBen Gras 		last /= factor;
526d65f6f70SBen Gras 	nblocks = btodb(fs->e2fs_bsize);
527d65f6f70SBen Gras 	/*
528d65f6f70SBen Gras 	 * Get buffer of block pointers, zero those entries corresponding
529d65f6f70SBen Gras 	 * to blocks to be free'd, and update on disk copy first.  Since
530d65f6f70SBen Gras 	 * double(triple) indirect before single(double) indirect, calls
531d65f6f70SBen Gras 	 * to bmap on these blocks will fail.  However, we already have
532d65f6f70SBen Gras 	 * the on disk address, so we have to set the b_blkno field
533d65f6f70SBen Gras 	 * explicitly instead of letting bread do everything for us.
534d65f6f70SBen Gras 	 */
535d65f6f70SBen Gras 	vp = ITOV(ip);
536d65f6f70SBen Gras 	bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0);
537d65f6f70SBen Gras 	if (bp->b_oflags & (BO_DONE | BO_DELWRI)) {
538d65f6f70SBen Gras 		/* Braces must be here in case trace evaluates to nothing. */
539d65f6f70SBen Gras 		trace(TR_BREADHIT, pack(vp, fs->e2fs_bsize), lbn);
540d65f6f70SBen Gras 	} else {
541d65f6f70SBen Gras 		trace(TR_BREADMISS, pack(vp, fs->e2fs_bsize), lbn);
542d65f6f70SBen Gras 		curlwp->l_ru.ru_inblock++;	/* pay for read */
543d65f6f70SBen Gras 		bp->b_flags |= B_READ;
544d65f6f70SBen Gras 		if (bp->b_bcount > bp->b_bufsize)
545d65f6f70SBen Gras 			panic("ext2fs_indirtrunc: bad buffer size");
546d65f6f70SBen Gras 		bp->b_blkno = dbn;
547d65f6f70SBen Gras 		VOP_STRATEGY(vp, bp);
548d65f6f70SBen Gras 		error = biowait(bp);
549d65f6f70SBen Gras 	}
550d65f6f70SBen Gras 	if (error) {
551d65f6f70SBen Gras 		brelse(bp, 0);
552d65f6f70SBen Gras 		*countp = 0;
553d65f6f70SBen Gras 		return (error);
554d65f6f70SBen Gras 	}
555d65f6f70SBen Gras 
556d65f6f70SBen Gras 	bap = (int32_t *)bp->b_data;	/* XXX ondisk32 */
557d65f6f70SBen Gras 	if (lastbn >= 0) {
558d65f6f70SBen Gras 		/* XXX ondisk32 */
55984d9c625SLionel Sambuc 		copy = kmem_alloc(fs->e2fs_bsize, KM_SLEEP);
560d65f6f70SBen Gras 		memcpy((void *)copy, (void *)bap, (u_int)fs->e2fs_bsize);
561d65f6f70SBen Gras 		memset((void *)&bap[last + 1], 0,
56284d9c625SLionel Sambuc 			(u_int)(EXT2_NINDIR(fs) - (last + 1)) * sizeof (uint32_t));
563d65f6f70SBen Gras 		error = bwrite(bp);
564d65f6f70SBen Gras 		if (error)
565d65f6f70SBen Gras 			allerror = error;
566d65f6f70SBen Gras 		bap = copy;
567d65f6f70SBen Gras 	}
568d65f6f70SBen Gras 
569d65f6f70SBen Gras 	/*
570d65f6f70SBen Gras 	 * Recursively free totally unused blocks.
571d65f6f70SBen Gras 	 */
57284d9c625SLionel Sambuc 	for (i = EXT2_NINDIR(fs) - 1,
573d65f6f70SBen Gras 		nlbn = lbn + 1 - i * factor; i > last;
574d65f6f70SBen Gras 		i--, nlbn += factor) {
575d65f6f70SBen Gras 		/* XXX ondisk32 */
576d65f6f70SBen Gras 		nb = fs2h32(bap[i]);
577d65f6f70SBen Gras 		if (nb == 0)
578d65f6f70SBen Gras 			continue;
579d65f6f70SBen Gras 		if (level > SINGLE) {
58084d9c625SLionel Sambuc 			error = ext2fs_indirtrunc(ip, nlbn, EXT2_FSBTODB(fs, nb),
581d65f6f70SBen Gras 						   (daddr_t)-1, level - 1,
582d65f6f70SBen Gras 						   &blkcount);
583d65f6f70SBen Gras 			if (error)
584d65f6f70SBen Gras 				allerror = error;
585d65f6f70SBen Gras 			blocksreleased += blkcount;
586d65f6f70SBen Gras 		}
587d65f6f70SBen Gras 		ext2fs_blkfree(ip, nb);
588d65f6f70SBen Gras 		blocksreleased += nblocks;
589d65f6f70SBen Gras 	}
590d65f6f70SBen Gras 
591d65f6f70SBen Gras 	/*
592d65f6f70SBen Gras 	 * Recursively free last partial block.
593d65f6f70SBen Gras 	 */
594d65f6f70SBen Gras 	if (level > SINGLE && lastbn >= 0) {
595d65f6f70SBen Gras 		last = lastbn % factor;
596d65f6f70SBen Gras 		/* XXX ondisk32 */
597d65f6f70SBen Gras 		nb = fs2h32(bap[i]);
598d65f6f70SBen Gras 		if (nb != 0) {
59984d9c625SLionel Sambuc 			error = ext2fs_indirtrunc(ip, nlbn, EXT2_FSBTODB(fs, nb),
600d65f6f70SBen Gras 						   last, level - 1, &blkcount);
601d65f6f70SBen Gras 			if (error)
602d65f6f70SBen Gras 				allerror = error;
603d65f6f70SBen Gras 			blocksreleased += blkcount;
604d65f6f70SBen Gras 		}
605d65f6f70SBen Gras 	}
606d65f6f70SBen Gras 
607d65f6f70SBen Gras 	if (copy != NULL) {
60884d9c625SLionel Sambuc 		kmem_free(copy, fs->e2fs_bsize);
609d65f6f70SBen Gras 	} else {
610d65f6f70SBen Gras 		brelse(bp, BC_INVAL);
611d65f6f70SBen Gras 	}
612d65f6f70SBen Gras 
613d65f6f70SBen Gras 	*countp = blocksreleased;
614d65f6f70SBen Gras 	return (allerror);
615d65f6f70SBen Gras }
616