* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* %sccs.include.redist.c%
* @(#)ufs_inode.c 7.40.1.1 (Berkeley) %G%
#if ((INOHSZ&(INOHSZ-1)) == 0)
#define INOHASH(dev,ino) (((dev)+(ino))&(INOHSZ-1))
#define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ)
struct inode
*ih_chain
[2];
int prtactive
; /* 1 => print out reclaim of active vnodes */
* Initialize hash links for inodes.
register union ihead
*ih
= ihead
;
if (VN_MAXPRIVATE
< sizeof(struct inode
))
panic("ihinit: too small");
for (i
= INOHSZ
; --i
>= 0; ih
++) {
* Look up a UFS dinode number to find its incore vnode.
* If it is not in core, read it in from the specified device.
* If it is in core, wait for the lock bit to clear, then
* return the inode locked. Detection and handling of mount
* points must be done by the calling routine.
struct mount
*mntp
= ITOV(xp
)->v_mount
;
register struct fs
*fs
= VFSTOUFS(mntp
)->um_fs
;
extern struct vnodeops ufs_vnodeops
, spec_inodeops
;
register struct inode
*ip
, *iq
;
register struct vnode
*vp
;
ih
= &ihead
[INOHASH(dev
, ino
)];
for (ip
= ih
->ih_chain
[0]; ip
!= (struct inode
*)ih
; ip
= ip
->i_forw
) {
if (ino
!= ip
->i_number
|| dev
!= ip
->i_dev
)
if ((ip
->i_flag
&ILOCKED
) != 0) {
sleep((caddr_t
)ip
, PINOD
);
if (error
= getnewvnode(VT_UFS
, mntp
, &ufs_vnodeops
, &nvp
)) {
for (i
= 0; i
< MAXQUOTAS
; i
++)
ip
->i_dquot
[i
] = NODQUOT
;
* Put it onto its hash chain and lock it so that other requests for
* this inode will block if they arrive while we are sleeping waiting
* for old data structures to be purged or for the contents of the
* disk portion of this inode to be read.
* Read in the disk contents for the inode.
if (error
= bread(VFSTOUFS(mntp
)->um_devvp
, fsbtodb(fs
, itod(fs
, ino
)),
(int)fs
->fs_bsize
, NOCRED
, &bp
)) {
* The inode does not contain anything useful, so it would
* be misleading to leave it on its hash chain.
* Iput() will take care of putting it back on the free list.
* Unlock and discard unneeded inode.
* Initialize the associated vnode
vp
->v_type
= IFTOVT(ip
->i_mode
);
if (vp
->v_type
== VFIFO
) {
extern struct vnodeops fifo_inodeops
;
vp
->v_op
= &fifo_inodeops
;
if (vp
->v_type
== VCHR
|| vp
->v_type
== VBLK
) {
vp
->v_op
= &spec_inodeops
;
if (nvp
= checkalias(vp
, ip
->i_rdev
, mntp
)) {
* Reinitialize aliased inode.
* Finish inode initialization.
ip
->i_devvp
= VFSTOUFS(mntp
)->um_devvp
;
* Set up a generation number for this inode if it does not
* already have one. This should only happen on old filesystems.
if (++nextgennumber
< (u_long
)time
.tv_sec
)
nextgennumber
= time
.tv_sec
;
ip
->i_gen
= nextgennumber
;
if ((vp
->v_mount
->mnt_flag
& MNT_RDONLY
) == 0)
* Unlock and decrement the reference count of an inode structure.
register struct inode
*ip
;
if ((ip
->i_flag
& ILOCKED
) == 0)
* Last reference to an inode, write the inode out and if necessary,
* truncate and deallocate the file.
register struct inode
*ip
= VTOI(vp
);
if (prtactive
&& vp
->v_usecount
!= 0)
vprint("ufs_inactive: pushing active", vp
);
* Get rid of inodes related to stale file handles.
if ((vp
->v_flag
& VXLOCK
) == 0)
if (ip
->i_nlink
<= 0 && (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) == 0) {
(void) chkiq(ip
, -1, NOCRED
, 0);
error
= itrunc(ip
, (u_long
)0, 0);
ifree(ip
, ip
->i_number
, mode
);
IUPDAT(ip
, &time
, &time
, 0);
* If we are done with the inode, reclaim it
* so that it can be reused immediately.
if (vp
->v_usecount
== 0 && ip
->i_mode
== 0)
* Reclaim an inode so that it can be used for other purposes.
register struct vnode
*vp
;
register struct inode
*ip
= VTOI(vp
);
if (prtactive
&& vp
->v_usecount
!= 0)
vprint("ufs_reclaim: pushing active", vp
);
* Remove the inode from its hash chain.
* Purge old data structures associated with the inode.
for (i
= 0; i
< MAXQUOTAS
; i
++) {
if (ip
->i_dquot
[i
] != NODQUOT
) {
dqrele(vp
, ip
->i_dquot
[i
]);
ip
->i_dquot
[i
] = NODQUOT
;
* Update the access, modified, and inode change times as specified
* by the IACC, IMOD, and ICHG flags respectively. The IUPD flag
* is used to specify that the inode needs to be updated but that
* the times have already been set. The access and modified times
* are taken from the second and third parameters; the inode change
* time is always taken from the current time. If waitfor is set,
* then wait for the disk write of the inode to complete.
iupdat(ip
, ta
, tm
, waitfor
)
register struct inode
*ip
;
struct vnode
*vp
= ITOV(ip
);
if ((ip
->i_flag
& (IUPD
|IACC
|ICHG
|IMOD
)) == 0)
if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
error
= bread(ip
->i_devvp
, fsbtodb(fs
, itod(fs
, ip
->i_number
)),
(int)fs
->fs_bsize
, NOCRED
, &bp
);
ip
->i_atime
= ta
->tv_sec
;
ip
->i_mtime
= tm
->tv_sec
;
ip
->i_ctime
= time
.tv_sec
;
ip
->i_flag
&= ~(IUPD
|IACC
|ICHG
|IMOD
);
dp
= bp
->b_un
.b_dino
+ itoo(fs
, ip
->i_number
);
#define SINGLE 0 /* index of single indirect block */
#define DOUBLE 1 /* index of double indirect block */
#define TRIPLE 2 /* index of triple indirect block */
* Truncate the inode ip to at most length size. Free affected disk
* blocks -- the blocks of the file are removed in reverse order.
* NB: triple indirect blocks are untested.
itrunc(oip
, length
, flags
)
register struct inode
*oip
;
register daddr_t lastblock
;
daddr_t bn
, lbn
, lastiblock
[NIADDR
];
register struct inode
*ip
;
int offset
, osize
, size
, level
;
long count
, nblocks
, blocksreleased
= 0;
int aflags
, error
, allerror
;
vnode_pager_setsize(ITOV(oip
), length
);
if (oip
->i_size
<= length
) {
oip
->i_flag
|= ICHG
|IUPD
;
error
= iupdat(oip
, &time
, &time
, 1);
* Calculate index into inode's block list of
* last direct and indirect blocks (if any)
* which we want to keep. Lastblock is -1 when
* the file is truncated to 0.
lastblock
= lblkno(fs
, length
+ fs
->fs_bsize
- 1) - 1;
lastiblock
[SINGLE
] = lastblock
- NDADDR
;
lastiblock
[DOUBLE
] = lastiblock
[SINGLE
] - NINDIR(fs
);
lastiblock
[TRIPLE
] = lastiblock
[DOUBLE
] - NINDIR(fs
) * NINDIR(fs
);
nblocks
= btodb(fs
->fs_bsize
);
* 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 (error
= getinoquota(oip
))
if (error
= balloc(oip
, lbn
, offset
, &bp
, aflags
))
size
= blksize(fs
, oip
, lbn
);
(void) vnode_pager_uncache(ITOV(oip
));
bzero(bp
->b_un
.b_addr
+ offset
, (unsigned)(size
- offset
));
* Update file and block pointers
* on disk before we start freeing blocks.
* If we crash before free'ing blocks below,
* the blocks will be returned to the free list.
* lastiblock values are also normalized to -1
* for calls to indirtrunc below.
for (level
= TRIPLE
; level
>= SINGLE
; level
--)
if (lastiblock
[level
] < 0) {
for (i
= NDADDR
- 1; i
> lastblock
; i
--)
oip
->i_flag
|= ICHG
|IUPD
;
vinvalbuf(ITOV(oip
), (length
> 0));
allerror
= iupdat(oip
, &time
, &time
, MNT_WAIT
);
for (level
= TRIPLE
; level
>= SINGLE
; level
--) {
error
= indirtrunc(ip
, bn
, lastiblock
[level
], level
,
if (lastiblock
[level
] < 0) {
blkfree(ip
, bn
, (off_t
)fs
->fs_bsize
);
blocksreleased
+= nblocks
;
if (lastiblock
[level
] >= 0)
* All whole direct blocks or frags.
for (i
= NDADDR
- 1; i
> lastblock
; i
--) {
bsize
= (off_t
)blksize(fs
, ip
, i
);
blocksreleased
+= btodb(bsize
);
* Finally, look for a change in size of the
* last direct block; release any frags.
bn
= ip
->i_db
[lastblock
];
off_t oldspace
, newspace
;
* Calculate amount of space we're giving
* back as old block size minus new block size.
oldspace
= blksize(fs
, ip
, lastblock
);
newspace
= blksize(fs
, ip
, lastblock
);
panic("itrunc: newspace");
if (oldspace
- newspace
> 0) {
* Block number of space to be free'd is
* the old block # plus the number of frags
* required for the storage we're keeping.
bn
+= numfrags(fs
, newspace
);
blkfree(ip
, bn
, oldspace
- newspace
);
blocksreleased
+= btodb(oldspace
- newspace
);
for (level
= SINGLE
; level
<= TRIPLE
; level
++)
if (ip
->i_ib
[level
] != oip
->i_ib
[level
])
for (i
= 0; i
< NDADDR
; i
++)
if (ip
->i_db
[i
] != oip
->i_db
[i
])
oip
->i_blocks
-= blocksreleased
;
if (oip
->i_blocks
< 0) /* sanity */
(void) chkdq(oip
, -blocksreleased
, NOCRED
, 0);
* Release blocks associated with the inode ip and
* stored in the indirect block bn. Blocks are free'd
* in LIFO order up to (but not including) lastbn. If
* level is greater than SINGLE, the block is an indirect
* block and recursive calls to indirtrunc must be used to
* cleanse other indirect blocks.
* NB: triple indirect blocks are untested.
indirtrunc(ip
, bn
, lastbn
, level
, countp
)
register struct inode
*ip
;
register struct fs
*fs
= ip
->i_fs
;
int nblocks
, blocksreleased
= 0;
* Calculate index in current block of last
* block to be kept. -1 indicates the entire
* block so we need not calculate the index.
for (i
= SINGLE
; i
< level
; i
++)
nblocks
= btodb(fs
->fs_bsize
);
* Get buffer of block pointers, zero those
* entries corresponding to blocks to be free'd,
* and update on disk copy first.
bp
= bread(ip
->i_dev
, fsbtodb(fs
, bn
), (int)fs
->fs_bsize
,
error
= bread(ip
->i_devvp
, fsbtodb(fs
, bn
), (int)fs
->fs_bsize
,
MALLOC(copy
, daddr_t
*, fs
->fs_bsize
, M_TEMP
, M_WAITOK
);
bcopy((caddr_t
)bap
, (caddr_t
)copy
, (u_int
)fs
->fs_bsize
);
bzero((caddr_t
)&bap
[last
+ 1],
(u_int
)(NINDIR(fs
) - (last
+ 1)) * sizeof (daddr_t
));
* Recursively free totally unused blocks.
for (i
= NINDIR(fs
) - 1; i
> last
; i
--) {
error
= indirtrunc(ip
, nb
, (daddr_t
)-1, level
- 1,
blocksreleased
+= blkcount
;
blkfree(ip
, nb
, (off_t
)fs
->fs_bsize
);
blocksreleased
+= nblocks
;
* Recursively free last partial block.
if (level
> SINGLE
&& lastbn
>= 0) {
error
= indirtrunc(ip
, nb
, last
, level
- 1, &blkcount
);
blocksreleased
+= blkcount
;
*countp
= blocksreleased
;
* Lock an inode. If its already locked, set the WANT bit and sleep.
register struct inode
*ip
;
while (ip
->i_flag
& ILOCKED
) {
if (ip
->i_spare0
== curproc
->p_pid
)
panic("locking against myself");
ip
->i_spare1
= curproc
->p_pid
;
(void) sleep((caddr_t
)ip
, PINOD
);
ip
->i_spare0
= curproc
->p_pid
;
* Unlock an inode. If WANT bit is on, wakeup.
register struct inode
*ip
;
if ((ip
->i_flag
& ILOCKED
) == 0)
vprint("iunlock: unlocked inode", ITOV(ip
));