/* ufs_inode.c 4.36 83/06/11 */
#if ((INOHSZ&(INOHSZ-1)) == 0)
#define INOHASH(dev,ino) (((dev)+(ino))&(INOHSZ-1))
#define INOHASH(dev,ino) (((unsigned)((dev)+(ino)))%INOHSZ)
union ihead
{ /* inode LRU cache, Chris Maltby */
struct inode
*ih_chain
[2];
struct inode
*ifreeh
, **ifreet
;
* Initialize hash links for inodes
* and build inode free list.
register struct inode
*ip
= inode
;
register union ihead
*ih
= ihead
;
for (i
= INOHSZ
; --i
>= 0; ih
++) {
for (i
= ninode
; --i
> 0; ) {
* Find an inode if it is incore.
* This is the equivalent, for inodes,
* of ``incore'' in bio.c or ``pfind'' in subr.c.
register struct inode
*ip
;
register union ihead
*ih
;
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
)
return ((struct inode
*)0);
* Look up an inode by device,inumber.
* If it is in core (in the inode structure),
* honor the locking protocol.
* If it is not in core, read it in from the
* If the inode is mounted on, perform
* the indicated indirection.
* In all cases, a pointer to a locked
* inode structure is returned.
* panic: no imt -- if the mounted file
* system is not in the mount table.
register struct inode
*ip
;
register union ihead
*ih
;
register struct mount
*mp
;
register struct dinode
*dp
;
register struct inode
*iq
;
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 ((ip
->i_flag
&IMOUNT
) != 0) {
for (mp
= &mount
[0]; mp
< &mount
[NMOUNT
]; mp
++)
fs
= mp
->m_bufp
->b_un
.b_fs
;
if (ip
->i_count
== 0) { /* ino on free list */
iq
->i_freeb
= ip
->i_freeb
;
if ((ip
= ifreeh
) == NULL
) {
* Now to take inode off the hash chain it was on
* (initially, or after an iflush, it is on a "hash chain"
* consisting entirely of itself, and pointed to by no-one,
* but that doesn't matter), and put it on the chain for
* its new (ino, dev) pair
bp
= bread(dev
, fsbtodb(fs
, itod(fs
, ino
)), (int)fs
->fs_bsize
);
if ((bp
->b_flags
&B_ERROR
) != 0) {
* the inode doesn't 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.
* we also loose its inumber, just in case (as iput
* doesn't do that any more) - but as it isn't on its
* hash chain, I doubt if this is really necessary .. kre
* (probably the two methods are interchangable)
ip
->i_dquot
= inoquota(ip
);
* Decrement reference count of
* write the inode out and if necessary,
* truncate and deallocate the file.
register struct inode
*ip
;
if ((ip
->i_flag
& ILOCKED
) == 0)
register struct inode
*ip
;
ifree(ip
, ip
->i_number
, mode
);
(void) chkiq(ip
->i_dev
, ip
, ip
->i_uid
, 0);
IUPDAT(ip
, &time
, &time
, 0);
* Put the inode on the end of the free list.
* Possibly in some cases it would be better to
* put the inode at the head of the free list,
* (eg: where i_mode == 0 || i_number == 0)
* but I will think about that later .. kre
* (i_number is rarely 0 - only after an i/o error in iget,
* where i_mode == 0, the inode will probably be wanted
* again soon for an ialloc, so possibly we should keep it)
* Check accessed and update flags on
* If any is on, update the inode
* If waitfor is given, then must insure
* i/o order so wait for write to complete.
iupdat(ip
, ta
, tm
, waitfor
)
register struct inode
*ip
;
if ((ip
->i_flag
& (IUPD
|IACC
|ICHG
)) != 0) {
bp
= bread(ip
->i_dev
, fsbtodb(fp
, itod(fp
, ip
->i_number
)),
if (bp
->b_flags
& B_ERROR
) {
ip
->i_atime
= ta
->tv_sec
;
ip
->i_mtime
= tm
->tv_sec
;
ip
->i_ctime
= time
.tv_sec
;
ip
->i_flag
&= ~(IUPD
|IACC
|ICHG
);
dp
= bp
->b_un
.b_dino
+ itoo(fp
, 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.
register daddr_t lastblock
;
daddr_t bn
, lastiblock
[NIADDR
];
register struct inode
*ip
;
long blocksreleased
= 0, nblocks
;
if (oip
->i_size
<= length
) {
oip
->i_flag
|= ICHG
|IUPD
;
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 size of 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.
* (? fsck doesn't check validity of pointers in indirect blocks)
for (level
= TRIPLE
; level
>= SINGLE
; level
--)
if (lastiblock
[level
] < 0) {
for (i
= NDADDR
- 1; i
> lastblock
; i
--)
oip
->i_flag
|= ICHG
|IUPD
;
iupdat(oip
, &time
, &time
, 1);
for (level
= TRIPLE
; level
>= SINGLE
; level
--) {
indirtrunc(ip
, bn
, lastiblock
[level
], level
);
if (lastiblock
[level
] < 0) {
free(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
--) {
size
= (off_t
)blksize(fs
, ip
, i
);
blocksreleased
+= btodb(size
);
* Finally, look for a change in size of the
* last direct block; release any frags.
bn
= ip
->i_db
[lastblock
];
* 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
);
free(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
, 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
)
register struct inode
*ip
;
register struct fs
*fs
= ip
->i_fs
;
int blocksreleased
= 0, nblocks
;
* 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.
copy
= geteblk((int)fs
->fs_bsize
);
bp
= bread(ip
->i_dev
, fsbtodb(fs
, bn
), (int)fs
->fs_bsize
);
if (bp
->b_flags
&B_ERROR
) {
bcopy((caddr_t
)bap
, (caddr_t
)copy
->b_un
.b_daddr
, (u_int
)fs
->fs_bsize
);
bzero((caddr_t
)&bap
[last
+ 1],
(u_int
)(NINDIR(fs
) - (last
+ 1)) * sizeof (daddr_t
));
bp
= copy
, bap
= bp
->b_un
.b_daddr
;
* Recursively free totally unused blocks.
for (i
= NINDIR(fs
) - 1; i
> last
; i
--) {
indirtrunc(ip
, nb
, (daddr_t
)-1, level
- 1);
free(ip
, nb
, (int)fs
->fs_bsize
);
blocksreleased
+= nblocks
;
* Recursively free last partial block.
if (level
> SINGLE
&& lastbn
>= 0) {
blocksreleased
+= indirtrunc(ip
, nb
, last
, level
- 1);
* remove any inodes in the inode cache belonging to dev
* There should not be any active ones, return error if any are found
* (nb: this is a user error, not a system err)
* Also, count the references to dev by block devices - this really
* has nothing to do with the object of the procedure, but as we have
* to scan the inode table here anyway, we might as well get the
* this is called from sumount()/sys3.c when dev is being unmounted
register struct inode
*ip
;
for (ip
= inode
; ip
< inodeNINODE
; ip
++) {
if (ip
!= iq
&& ip
->i_dev
== dev
)
* as i_count == 0, the inode was on the free
* list already, just leave it there, it will
* fall off the bottom eventually. We could
* perhaps move it to the head of the free
* list, but as umounts are done so
* infrequently, we would gain very little,
* while making the code bigger.
else if (ip
->i_count
&& (ip
->i_mode
&IFMT
)==IFBLK
&&
* Lock an inode. If its already locked, set the WANT bit and sleep.
register struct inode
*ip
;
* Unlock an inode. If WANT bit is on, wakeup.
register struct inode
*ip
;