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