/* * Called for ftruncate() and from sfs_reclaim. */ static int sfs_truncate(struct vnode *v, off_t len) { /* * I/O buffer for handling the indirect block. * * Note: in real life (and when you've done the fs assignment) * you would get space from the disk buffer cache for this, * not use a static area. */ static u_int32_t idbuf[SFS_DBPERIDB]; struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data; /* Length in blocks (divide rounding up) */ u_int32_t blocklen = DIVROUNDUP(len, SFS_BLOCKSIZE); u_int32_t i, j, block; u_int32_t idblock, baseblock, highblock; int result; int hasnonzero, iddirty; assert(sizeof(idbuf)==SFS_BLOCKSIZE); /* * Go through the direct blocks. Discard any that are * past the limit we're truncating to. */ for (i=0; i<SFS_NDIRECT; i++) { block = sv->sv_i.sfi_direct[i]; if (i >= blocklen && block != 0) { sfs_bfree(sfs, block); sv->sv_i.sfi_direct[i] = 0; sv->sv_dirty = 1; } } /* Indirect block number */ idblock = sv->sv_i.sfi_indirect; /* The lowest block in the indirect block */ baseblock = SFS_NDIRECT; /* The highest block in the indirect block */ highblock = baseblock + SFS_DBPERIDB - 1; if (blocklen < highblock && idblock != 0) { /* We're past the proposed EOF; may need to free stuff */ /* Read the indirect block */ result = sfs_rblock(sfs, idbuf, idblock); if (result) { return result; } hasnonzero = 0; iddirty = 0; for (j=0; j<SFS_DBPERIDB; j++) { /* Discard any blocks that are past the new EOF */ if (blocklen < baseblock+j && idbuf[j] != 0) { sfs_bfree(sfs, idbuf[j]); idbuf[j] = 0; iddirty = 1; } /* Remember if we see any nonzero blocks in here */ if (idbuf[j]!=0) { hasnonzero=1; } } if (!hasnonzero) { /* The whole indirect block is empty now; free it */ sfs_bfree(sfs, idblock); sv->sv_i.sfi_indirect = 0; sv->sv_dirty = 1; } else if (iddirty) { /* The indirect block is dirty; write it back */ result = sfs_wblock(sfs, idbuf, idblock); if (result) { return result; } } } /* Set the file size */ sv->sv_i.sfi_size = len; /* Mark the inode dirty */ sv->sv_dirty = 1; return 0; }
/* * Called when the vnode refcount (in-memory usage count) hits zero. * * This function should try to avoid returning errors other than EBUSY. */ static int sfs_reclaim(struct vnode *v) { struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = v->vn_fs->fs_data; int ix, i, num, result; lock_acquire(sfs->sfs_vnodes_lock); /* * Make sure someone else hasn't picked up the vnode since the * decision was made to reclaim it. (You must also synchronize * this with sfs_loadvnode.) */ lock_acquire(v->vn_countlock); if (v->vn_refcount != 1) { /* consume the reference VOP_DECREF gave us */ assert(v->vn_refcount>1); v->vn_refcount--; lock_release(v->vn_countlock); lock_release(sfs->sfs_vnodes_lock); return EBUSY; } lock_release(v->vn_countlock); /* If there are no on-disk references to the file either, erase it. */ if (sv->sv_i.sfi_linkcount==0 && sv->sv_i.sfi_type==SFS_TYPE_FILE) { /* * VOP_TRUNCATE doesn't work on directories, which is why I added * the second requirement to the above if statement. */ result = VOP_TRUNCATE(&sv->sv_v, 0); if (result) { lock_release(sfs->sfs_vnodes_lock); return result; } } /* Sync the inode to disk */ result = sfs_sync_inode(sv); if (result) { lock_release(sfs->sfs_vnodes_lock); return result; } /* If there are no on-disk references, discard the inode */ if (sv->sv_i.sfi_linkcount==0) { sfs_bfree(sfs, sv->sv_ino); } /* Remove the vnode structure from the table in the struct sfs_fs. */ ix = -1; num = array_getnum(sfs->sfs_vnodes); for (i=0; i<num; i++) { struct sfs_vnode *sv2 = array_getguy(sfs->sfs_vnodes, i); if (sv2==sv) { ix = i; break; } } if (ix<0) { panic("sfs: reclaim vnode %u not in vnode pool\n", sv->sv_ino); } array_remove(sfs->sfs_vnodes, ix); VOP_KILL(&sv->sv_v); /* Release the storage for the vnode structure itself. */ kfree(sv); lock_release(sfs->sfs_vnodes_lock); /* Done */ return 0; }
/* * Called when the vnode refcount (in-memory usage count) hits zero. * * This function should try to avoid returning errors other than EBUSY. */ static int sfs_reclaim(struct vnode *v) { struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = v->vn_fs->fs_data; unsigned ix, i, num; int result; vfs_biglock_acquire(); /* * Make sure someone else hasn't picked up the vnode since the * decision was made to reclaim it. (You must also synchronize * this with sfs_loadvnode.) */ if (v->vn_refcount != 1) { /* consume the reference VOP_DECREF gave us */ KASSERT(v->vn_refcount>1); v->vn_refcount--; vfs_biglock_release(); return EBUSY; } /* If there are no on-disk references to the file either, erase it. */ if (sv->sv_i.sfi_linkcount==0) { result = VOP_TRUNCATE(&sv->sv_v, 0); if (result) { vfs_biglock_release(); return result; } } /* Sync the inode to disk */ result = sfs_sync_inode(sv); if (result) { vfs_biglock_release(); return result; } /* If there are no on-disk references, discard the inode */ if (sv->sv_i.sfi_linkcount==0) { sfs_bfree(sfs, sv->sv_ino); } /* Remove the vnode structure from the table in the struct sfs_fs. */ num = vnodearray_num(sfs->sfs_vnodes); ix = num; for (i=0; i<num; i++) { struct vnode *v2 = vnodearray_get(sfs->sfs_vnodes, i); struct sfs_vnode *sv2 = v2->vn_data; if (sv2 == sv) { ix = i; break; } } if (ix == num) { panic("sfs: reclaim vnode %u not in vnode pool\n", sv->sv_ino); } vnodearray_remove(sfs->sfs_vnodes, ix); VOP_CLEANUP(&sv->sv_v); vfs_biglock_release(); /* Release the storage for the vnode structure itself. */ kfree(sv); /* Done */ return 0; }