2 * modified for Lites 1.1
4 * Aug 1995, Godmar Back (gback@cs.utah.edu)
5 * University of Utah, Department of Computer Science
8 * Copyright (c) 1982, 1986, 1989, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * @(#)ext2_inode.c 8.5 (Berkeley) 12/30/93
40 * $FreeBSD: src/sys/gnu/ext2fs/ext2_inode.c,v 1.24.2.1 2000/08/03 00:52:57 peter Exp $
43 #include "opt_quota.h"
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/mount.h>
49 #include <sys/vnode.h>
50 #include <sys/malloc.h>
53 #include <vm/vm_extern.h>
57 #include "ext2mount.h"
60 #include "ext2_fs_sb.h"
62 #include "ext2_extern.h"
64 static int ext2_indirtrunc (struct inode
*, daddr_t
, off_t
, daddr_t
,
68 * Update the access, modified, and inode change times as specified by the
69 * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively. Write the inode
70 * to disk if the IN_MODIFIED flag is set (it may be set initially, or by
71 * the timestamp update). The IN_LAZYMOD flag is set to force a write
72 * later if not now. If we write now, then clear both IN_MODIFIED and
73 * IN_LAZYMOD to reflect the presumably successful write, and if waitfor is
74 * set, then wait for the write to complete.
77 ext2_update(struct vnode
*vp
, int waitfor
)
79 struct ext2_sb_info
*fs
;
86 if ((ip
->i_flag
& IN_MODIFIED
) == 0)
88 ip
->i_flag
&= ~(IN_LAZYMOD
| IN_MODIFIED
);
89 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
92 error
= bread(ip
->i_devvp
,
93 fsbtodoff(fs
, ino_to_fsba(fs
, ip
->i_number
)),
94 (int)fs
->s_blocksize
, &bp
);
99 ext2_di2ei( &ip
->i_din
, (struct ext2_inode
*) ((char *)bp
->b_data
+ EXT2_INODE_SIZE(fs
) *
100 ino_to_fsbo(fs
, ip
->i_number
)));
102 if (waitfor && (vp->v_mount->mnt_flag & MNT_ASYNC) == 0)
113 #define SINGLE 0 /* index of single indirect block */
114 #define DOUBLE 1 /* index of double indirect block */
115 #define TRIPLE 2 /* index of triple indirect block */
117 * Truncate the inode oip to at most length size, freeing the
121 ext2_truncate(struct vnode
*vp
, off_t length
, int flags
, struct ucred
*cred
)
123 struct vnode
*ovp
= vp
;
126 daddr_t bn
, lbn
, lastiblock
[NIADDR
], indir_lbn
[NIADDR
];
127 daddr_t oldblks
[NDADDR
+ NIADDR
], newblks
[NDADDR
+ NIADDR
];
128 struct ext2_sb_info
*fs
;
130 int offset
, size
, level
;
131 long count
, nblocks
, blocksreleased
= 0;
133 int aflags
, error
, allerror
;
136 kprintf("ext2_truncate called %d to %d\n", VTOI(ovp)->i_number, length);
138 * negative file sizes will totally break the code below and
139 * are not meaningful anyways.
145 if (ovp
->v_type
== VLNK
&&
146 oip
->i_size
< ovp
->v_mount
->mnt_maxsymlinklen
) {
149 panic("ext2_truncate: partial truncate of symlink");
151 bzero((char *)&oip
->i_shortlink
, (u_int
)oip
->i_size
);
153 oip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
154 return (EXT2_UPDATE(ovp
, 1));
156 if (oip
->i_size
== length
) {
157 oip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
158 return (EXT2_UPDATE(ovp
, 0));
161 if ((error
= ext2_getinoquota(oip
)) != 0)
166 ext2_discard_prealloc(oip
);
168 * Lengthen the size of the file. We must ensure that the
169 * last byte of the file is allocated. Since the smallest
170 * value of osize is 0, length will be at least 1.
172 if (osize
< length
) {
173 offset
= blkoff(fs
, length
- 1);
174 lbn
= lblkno(fs
, length
- 1);
178 vnode_pager_setsize(ovp
, length
);
179 error
= ext2_balloc(oip
, lbn
, offset
+ 1, cred
, &bp
, aflags
);
181 vnode_pager_setsize(ovp
, osize
);
184 oip
->i_size
= length
;
185 if (aflags
& IO_SYNC
)
189 oip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
190 return (EXT2_UPDATE(ovp
, 1));
193 * Shorten the size of the file. If the file is not being
194 * truncated to a block boundry, the contents of the
195 * partial block following the end of the file must be
196 * zero'ed in case it ever become accessable again because
197 * of subsequent file growth.
199 /* I don't understand the comment above */
200 offset
= blkoff(fs
, length
);
202 oip
->i_size
= length
;
204 lbn
= lblkno(fs
, length
);
208 error
= ext2_balloc(oip
, lbn
, offset
, cred
, &bp
, aflags
);
211 oip
->i_size
= length
;
212 size
= blksize(fs
, oip
, lbn
);
213 bzero((char *)bp
->b_data
+ offset
, (u_int
)(size
- offset
));
215 if (aflags
& IO_SYNC
)
221 * Calculate index into inode's block list of
222 * last direct and indirect blocks (if any)
223 * which we want to keep. Lastblock is -1 when
224 * the file is truncated to 0.
226 lastblock
= lblkno(fs
, length
+ fs
->s_blocksize
- 1) - 1;
227 lastiblock
[SINGLE
] = lastblock
- NDADDR
;
228 lastiblock
[DOUBLE
] = lastiblock
[SINGLE
] - NINDIR(fs
);
229 lastiblock
[TRIPLE
] = lastiblock
[DOUBLE
] - NINDIR(fs
) * NINDIR(fs
);
230 nblocks
= btodb(fs
->s_blocksize
);
232 * Update file and block pointers on disk before we start freeing
233 * blocks. If we crash before free'ing blocks below, the blocks
234 * will be returned to the free list. lastiblock values are also
235 * normalized to -1 for calls to ext2_indirtrunc below.
237 bcopy((caddr_t
)&oip
->i_db
[0], (caddr_t
)oldblks
, sizeof oldblks
);
238 for (level
= TRIPLE
; level
>= SINGLE
; level
--)
239 if (lastiblock
[level
] < 0) {
240 oip
->i_ib
[level
] = 0;
241 lastiblock
[level
] = -1;
243 for (i
= NDADDR
- 1; i
> lastblock
; i
--)
245 oip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
246 allerror
= EXT2_UPDATE(ovp
, 1);
249 * Having written the new inode to disk, save its new configuration
250 * and put back the old block pointers long enough to process them.
251 * Note that we save the new block configuration so we can check it
254 bcopy((caddr_t
)&oip
->i_db
[0], (caddr_t
)newblks
, sizeof newblks
);
255 bcopy((caddr_t
)oldblks
, (caddr_t
)&oip
->i_db
[0], sizeof oldblks
);
257 error
= vtruncbuf(ovp
, length
, (int)fs
->s_blocksize
);
258 if (error
&& (allerror
== 0))
262 * Indirect blocks first.
264 indir_lbn
[SINGLE
] = -NDADDR
;
265 indir_lbn
[DOUBLE
] = indir_lbn
[SINGLE
] - NINDIR(fs
) - 1;
266 indir_lbn
[TRIPLE
] = indir_lbn
[DOUBLE
] - NINDIR(fs
) * NINDIR(fs
) - 1;
267 for (level
= TRIPLE
; level
>= SINGLE
; level
--) {
268 bn
= oip
->i_ib
[level
];
270 error
= ext2_indirtrunc(oip
, indir_lbn
[level
],
271 fsbtodoff(fs
, bn
), lastiblock
[level
], level
, &count
);
274 blocksreleased
+= count
;
275 if (lastiblock
[level
] < 0) {
276 oip
->i_ib
[level
] = 0;
277 ext2_blkfree(oip
, bn
, fs
->s_frag_size
);
278 blocksreleased
+= nblocks
;
281 if (lastiblock
[level
] >= 0)
286 * All whole direct blocks or frags.
288 for (i
= NDADDR
- 1; i
> lastblock
; i
--) {
295 bsize
= blksize(fs
, oip
, i
);
296 ext2_blkfree(oip
, bn
, bsize
);
297 blocksreleased
+= btodb(bsize
);
303 * Finally, look for a change in size of the
304 * last direct block; release any frags.
306 bn
= oip
->i_db
[lastblock
];
308 long oldspace
, newspace
;
311 * Calculate amount of space we're giving
312 * back as old block size minus new block size.
314 oldspace
= blksize(fs
, oip
, lastblock
);
315 oip
->i_size
= length
;
316 newspace
= blksize(fs
, oip
, lastblock
);
318 panic("itrunc: newspace");
319 if (oldspace
- newspace
> 0) {
321 * Block number of space to be free'd is
322 * the old block # plus the number of frags
323 * required for the storage we're keeping.
325 bn
+= numfrags(fs
, newspace
);
326 ext2_blkfree(oip
, bn
, oldspace
- newspace
);
327 blocksreleased
+= btodb(oldspace
- newspace
);
332 for (level
= SINGLE
; level
<= TRIPLE
; level
++)
333 if (newblks
[NDADDR
+ level
] != oip
->i_ib
[level
])
335 for (i
= 0; i
< NDADDR
; i
++)
336 if (newblks
[i
] != oip
->i_db
[i
])
338 if (length
== 0 && (!RB_EMPTY(&ovp
->v_rbdirty_tree
) ||
339 !RB_EMPTY(&ovp
->v_rbclean_tree
)))
341 #endif /* DIAGNOSTIC */
343 * Put back the real size.
345 oip
->i_size
= length
;
346 oip
->i_blocks
-= blocksreleased
;
347 if (oip
->i_blocks
< 0) /* sanity */
349 oip
->i_flag
|= IN_CHANGE
;
350 vnode_pager_setsize(ovp
, length
);
352 ext2_chkdq(oip
, -blocksreleased
, NOCRED
, 0);
358 * Release blocks associated with the inode ip and stored in the indirect
359 * block bn. Blocks are free'd in LIFO order up to (but not including)
360 * lastbn. If level is greater than SINGLE, the block is an indirect block
361 * and recursive calls to indirtrunc must be used to cleanse other indirect
364 * NB: triple indirect blocks are untested.
368 ext2_indirtrunc(struct inode
*ip
, daddr_t lbn
, off_t doffset
, daddr_t lastbn
,
369 int level
, long *countp
)
373 struct ext2_sb_info
*fs
= ip
->i_e2fs
;
376 daddr_t
*copy
, nb
, nlbn
, last
;
377 long blkcount
, factor
;
378 int nblocks
, blocksreleased
= 0;
379 int error
= 0, allerror
= 0;
382 * Calculate index in current block of last
383 * block to be kept. -1 indicates the entire
384 * block so we need not calculate the index.
387 for (i
= SINGLE
; i
< level
; i
++)
388 factor
*= NINDIR(fs
);
392 nblocks
= btodb(fs
->s_blocksize
);
394 * Get buffer of block pointers, zero those entries corresponding
395 * to blocks to be free'd, and update on disk copy first. Since
396 * double(triple) indirect before single(double) indirect, calls
397 * to bmap on these blocks will fail. However, we already have
398 * the on disk address, so we have to set the bio_offset field
399 * explicitly instead of letting bread do everything for us.
402 bp
= getblk(vp
, lblktodoff(fs
, lbn
), (int)fs
->s_blocksize
, 0, 0);
403 if ((bp
->b_flags
& B_CACHE
) == 0) {
404 bp
->b_flags
&= ~(B_ERROR
| B_INVAL
);
405 bp
->b_cmd
= BUF_CMD_READ
;
406 if (bp
->b_bcount
> bp
->b_bufsize
)
407 panic("ext2_indirtrunc: bad buffer size");
408 bp
->b_bio2
.bio_offset
= doffset
;
409 bp
->b_bio1
.bio_done
= biodone_sync
;
410 bp
->b_bio1
.bio_flags
|= BIO_SYNC
;
411 vfs_busy_pages(bp
->b_vp
, bp
);
412 vn_strategy(vp
, &bp
->b_bio1
);
413 error
= biowait(&bp
->b_bio1
, "biord");
421 bap
= (daddr_t
*)bp
->b_data
;
422 MALLOC(copy
, daddr_t
*, fs
->s_blocksize
, M_TEMP
, M_WAITOK
);
423 bcopy((caddr_t
)bap
, (caddr_t
)copy
, (u_int
)fs
->s_blocksize
);
424 bzero((caddr_t
)&bap
[last
+ 1],
425 (u_int
)(NINDIR(fs
) - (last
+ 1)) * sizeof (daddr_t
));
427 bp
->b_flags
|= B_INVAL
;
434 * Recursively free totally unused blocks.
436 for (i
= NINDIR(fs
) - 1, nlbn
= lbn
+ 1 - i
* factor
; i
> last
;
437 i
--, nlbn
+= factor
) {
441 if (level
> SINGLE
) {
442 if ((error
= ext2_indirtrunc(ip
, nlbn
,
443 fsbtodoff(fs
, nb
), (daddr_t
)-1, level
- 1, &blkcount
)) != 0)
445 blocksreleased
+= blkcount
;
447 ext2_blkfree(ip
, nb
, fs
->s_blocksize
);
448 blocksreleased
+= nblocks
;
452 * Recursively free last partial block.
454 if (level
> SINGLE
&& lastbn
>= 0) {
455 last
= lastbn
% factor
;
458 error
= ext2_indirtrunc(ip
, nlbn
, fsbtodoff(fs
, nb
),
459 last
, level
- 1, &blkcount
);
462 blocksreleased
+= blkcount
;
466 *countp
= blocksreleased
;
471 * Last reference to an inode. If necessary, write or delete it.
473 * ext2_inactive(struct vnode *a_vp)
476 ext2_inactive(struct vop_inactive_args
*ap
)
478 struct vnode
*vp
= ap
->a_vp
;
479 struct inode
*ip
= VTOI(vp
);
482 ext2_discard_prealloc(ip
);
483 if (prtactive
&& vp
->v_sysref
.refcnt
> 1)
484 vprint("ext2_inactive: pushing active", vp
);
487 * Ignore inodes related to stale file handles.
489 if (ip
== NULL
|| ip
->i_mode
== 0)
491 if (ip
->i_nlink
<= 0 && (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) == 0) {
493 if (!ext2_getinoquota(ip
))
494 (void)ext2_chkiq(ip
, -1, NOCRED
, FORCE
);
496 error
= EXT2_TRUNCATE(vp
, (off_t
)0, 0, NOCRED
);
500 ip
->i_flag
|= IN_CHANGE
| IN_UPDATE
;
501 EXT2_VFREE(vp
, ip
->i_number
, mode
);
503 if (ip
->i_flag
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
))
507 * If we are done with the inode, reclaim it
508 * so that it can be reused immediately.
510 if (ip
== NULL
|| ip
->i_mode
== 0)
516 * Reclaim an inode so that it can be used for other purposes.
518 * ext2_reclaim(struct vnode *a_vp)
521 ext2_reclaim(struct vop_reclaim_args
*ap
)
524 struct vnode
*vp
= ap
->a_vp
;
529 if (prtactive
&& vp
->v_sysref
.refcnt
> 1)
530 vprint("ext2_reclaim: pushing active", vp
);
537 if (ip
->i_flag
& IN_LAZYMOD
) {
538 ip
->i_flag
|= IN_MODIFIED
;
543 if (ip
&& (ip
->i_flag
& (IN_ACCESS
| IN_CHANGE
| IN_MODIFIED
| IN_UPDATE
))) {
544 kprintf("WARNING: INODE %ld flags %08x: modified inode being released!\n", (long)ip
->i_number
, (int)ip
->i_flag
);
545 ip
->i_flag
|= IN_MODIFIED
;
550 * Remove the inode from its hash chain and purge namecache
551 * data associated with the vnode.
561 for (i
= 0; i
< MAXQUOTAS
; i
++) {
562 if (ip
->i_dquot
[i
] != NODQUOT
) {
563 ext2_dqrele(vp
, ip
->i_dquot
[i
]);
564 ip
->i_dquot
[i
] = NODQUOT
;
569 if (ip
->i_dirhash
!= NULL
)
570 ext2dirhash_free(ip
);
572 kfree(ip
, VFSTOEXT2(vp
->v_mount
)->um_malloctype
);