* Copyright (c) 1986, 1989, 1991 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)lfs_inode.c 7.84 (Berkeley) %G%
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufsmount.h>
#include <ufs/ufs/ufs_extern.h>
#include <ufs/lfs/lfs_extern.h>
/* Search a block for a specific dinode. */
register struct dinode
*dip
;
register struct dinode
*ldip
;
for (cnt
= INOPB(fs
), ldip
= dip
+ (cnt
- 1); cnt
--; --ldip
)
if (ldip
->di_inumber
== ino
)
panic("lfs_ifind: dinode %u not found", ino
);
struct vop_update_args
/* {
struct vnode
*vp
= ap
->a_vp
;
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
if ((ip
->i_flag
& (IUPD
| IACC
| ICHG
| IMOD
)) == 0)
ip
->i_atime
.ts_sec
= ap
->a_ta
->tv_sec
;
ip
->i_mtime
.ts_sec
= ap
->a_tm
->tv_sec
;
ip
->i_ctime
.ts_sec
= time
.tv_sec
;
ip
->i_flag
&= ~(IUPD
|IACC
|ICHG
);
if (!(ip
->i_flag
& IMOD
))
++(VFSTOUFS(vp
->v_mount
)->um_lfs
->lfs_uinodes
);
/* If sync, push back the vnode and any dirty blocks it may have. */
return (ap
->a_waitfor
& LFS_SYNC
? lfs_vflush(vp
) : 0);
/* Update segment usage information when removing a block. */
LFS_SEGENTRY(sup, fs, lastseg, sup_bp); \
if ((num << fs->lfs_bshift) > sup->su_nbytes) \
panic("lfs_truncate: negative bytes in segment %d\n", \
sup->su_nbytes -= num << fs->lfs_bshift; \
e1 = VOP_BWRITE(sup_bp); \
if (lastseg != (seg = datosn(fs, daddr))) { \
* Truncate the inode ip to at most length size. Update segment usage
struct vop_truncate_args
/* {
register struct indir
*inp
;
register daddr_t
*daddrp
;
register struct vnode
*vp
= ap
->a_vp
;
off_t length
= ap
->a_length
;
struct indir a
[NIADDR
+ 2], a_end
[NIADDR
+ 2];
daddr_t daddr
, lastblock
, lbn
, olastblock
;
long off
, a_released
, blocksreleased
, i_released
;
int e1
, e2
, depth
, lastseg
, num
, offset
, seg
, size
;
if (vp
->v_type
== VLNK
&& vp
->v_mount
->mnt_maxsymlinklen
> 0) {
panic("lfs_truncate: partial truncate of symlink");
bzero((char *)&ip
->i_shortlink
, (u_int
)ip
->i_size
);
return (VOP_UPDATE(vp
, &tv
, &tv
, 0));
vnode_pager_setsize(vp
, (u_long
)length
);
/* If length is larger than the file, just update the times. */
if (ip
->i_size
<= length
) {
return (VOP_UPDATE(vp
, &tv
, &tv
, 0));
* Calculate index into inode's block list of last direct and indirect
* blocks (if any) which we want to keep. Lastblock is 0 when the
* file is truncated to 0.
lastblock
= lblkno(fs
, length
+ fs
->lfs_bsize
- 1);
olastblock
= lblkno(fs
, ip
->i_size
+ fs
->lfs_bsize
- 1) - 1;
* Update the size of the file. If the file is not being truncated to
* a block boundry, the contents of the partial block following the end
* of the file must be zero'ed in case it ever become accessable again
* because of subsequent file growth.
offset
= blkoff(fs
, length
);
lbn
= lblkno(fs
, length
);
if (e1
= getinoquota(ip
))
if (e1
= bread(vp
, lbn
, fs
->lfs_bsize
, NOCRED
, &bp
))
(void)vnode_pager_uncache(vp
);
bzero(bp
->b_un
.b_addr
+ offset
, (unsigned)(size
- offset
));
* Modify sup->su_nbyte counters for each deleted block; keep track
* of number of blocks removed for ip->i_blocks.
for (lbn
= olastblock
; lbn
>= lastblock
;) {
/* XXX use run length from bmap array to make this faster */
ufs_bmaparray(vp
, lbn
, &daddr
, a
, &depth
, NULL
);
for (i
= NIADDR
+ 2; i
--;)
case 0: /* Direct block. */
case 1: /* An indirect block. */
panic("lfs_truncate: ufs_bmaparray returned depth 1");
default: /* Chain of indirect blocks. */
if (inp
->in_off
> 0 && lbn
!= lastblock
) {
lbn
-= inp
->in_off
< lbn
- lastblock
?
inp
->in_off
: lbn
- lastblock
;
for (; depth
&& (inp
->in_off
== 0 || lbn
== lastblock
);
inp
->in_lbn
, fs
->lfs_bsize
, NOCRED
, &bp
))
panic("lfs_truncate: bread bno %d",
daddrp
= bp
->b_un
.b_daddr
+ inp
->in_off
;
i
++ <= a_end
[depth
].in_off
;) {
a_end
[depth
].in_off
= NINDIR(fs
) - 1;
bzero(bp
->b_un
.b_daddr
+ inp
->in_off
,
inp
->in_off
* sizeof(daddr_t
));
if (depth
== 0 && a
[1].in_off
== 0) {
if (lbn
== lastblock
|| lbn
<= NDADDR
)
/* If truncating the file to 0, update the version number. */
LFS_IENTRY(ifp
, fs
, ip
->i_number
, bp
);
if (ip
->i_blocks
< fsbtodb(fs
, blocksreleased
)) {
printf("lfs_truncate: block count < 0\n");
blocksreleased
= ip
->i_blocks
;
ip
->i_blocks
-= fsbtodb(fs
, blocksreleased
);
fs
->lfs_bfree
+= fsbtodb(fs
, blocksreleased
);
* Traverse dirty block list counting number of dirty buffers
* that are being deleted out of the cache, so that the lfs_avail
for (bp
= vp
->v_dirtyblkhd
.le_next
; bp
; bp
= bp
->b_vnbufs
.qe_next
)
if (bp
->b_flags
& B_LOCKED
) {
* When buffers are created in the cache, their block
* number is set equal to their logical block number.
* If that is still true, we are assuming that the
* blocks are new (not yet on disk) and weren't
* counted above. However, there is a slight chance
* that a block's disk address is equal to its logical
* block number in which case, we'll get an overcounting
if (bp
->b_blkno
== bp
->b_lblkno
)
blocksreleased
= fsbtodb(fs
, i_released
);
if (blocksreleased
> ip
->i_blocks
) {
printf("lfs_inode: Warning! %s\n",
"more blocks released from inode than are in inode");
blocksreleased
= ip
->i_blocks
;
fs
->lfs_bfree
+= blocksreleased
;
ip
->i_blocks
-= blocksreleased
;
if (length
== 0 && ip
->i_blocks
!= 0)
printf("lfs_inode: Warning! %s%d%s\n",
"Truncation to zero, but ", ip
->i_blocks
,
" blocks left on inode");
fs
->lfs_avail
+= fsbtodb(fs
, a_released
);
e1
= vinvalbuf(vp
, (length
> 0) ? V_SAVE
: 0, ap
->a_cred
, ap
->a_p
,
e2
= VOP_UPDATE(vp
, &tv
, &tv
, 0);
return (e1
? e1
: e2
? e2
: 0);