diff options
author | Michael I. Bushnell <mib@gnu.org> | 1994-10-03 21:18:00 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1994-10-03 21:18:00 +0000 |
commit | 4e3d37a5f964e023b5273b95be637dc7c01f1ce5 (patch) | |
tree | bfaab53e864d0bd421a40eb90282dacd9c7b3106 /ufs/sizes.c | |
parent | b1e1ec7f1ba2dd942424e9337c1e808b1cf12f18 (diff) |
Formerly sizes.c.~23~
Diffstat (limited to 'ufs/sizes.c')
-rw-r--r-- | ufs/sizes.c | 181 |
1 files changed, 126 insertions, 55 deletions
diff --git a/ufs/sizes.c b/ufs/sizes.c index 0f0fd427..8f6174af 100644 --- a/ufs/sizes.c +++ b/ufs/sizes.c @@ -28,7 +28,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define MAY_CACHE 1 #endif -static int free_indir (struct node *np, daddr_t bno, int level); +static int indir_release (struct node *np, daddr_t bno, int level); static void poke_pages (memory_object_t, vm_offset_t, vm_offset_t); /* Implement the diskfs_truncate callback; sse <hurd/diskfs.h> for the @@ -38,12 +38,12 @@ diskfs_truncate (struct node *np, off_t length) { int offset; - daddr_t lastiblock[NIADDR], lastblock, bn; struct dinode *di = dino (np->dn->number); int blocksfreed = 0; error_t err; - int level; int i; + struct iblock_spec indirs[NIADDR + 1]; + daddr_t lbn; if (length >= np->dn_stat.st_size) return 0; @@ -104,17 +104,6 @@ diskfs_truncate (struct node *np, np->allocsize - length, 1); } - /* Calculate index into node's block list of direct - and indirect blocks which we want to keep. Lastblock - is -1 when the file is truncated to 0. */ - lastblock = lblkno (sblock, length - 1); - lastiblock[INDIR_SINGLE] = lastblock - NDADDR; - lastiblock[INDIR_DOUBLE] = lastiblock[INDIR_SINGLE] - NINDIR (sblock); - lastiblock[INDIR_TRIPLE] = (lastiblock[INDIR_DOUBLE] - - NINDIR (sblock) * NINDIR (sblock)); - - /* lastiblock will now be negative for elements that we should free. */ - /* Update the size on disk; fsck will finish freeing blocks if necessary should we crash. */ np->dn_stat.st_size = length; @@ -122,52 +111,98 @@ diskfs_truncate (struct node *np, np->dn_set_ctime = 1; diskfs_node_update (np, 1); - /* Free the blocks. */ + /* Find out the location information for the last block to + be retained */ + lbn = lblkno (sblock, length - 1); + err = fetch_indir_spec (np, lbn, indirs); + /* err XXX */ + + /* We don't support triple indirs */ + assert (indirs[0].offset == -1 + || indirs[1].offset == -1 + || indirs[2].offset == -1); + + /* BSD carefully finds out how far to clear; it's vastly simpler + to just clear everything after the new last block. */ - err = diskfs_catch_exception (); - if (err) + /* Free direct blocks */ + if (indirs[0].offset < 0) { - rwlock_writer_unlock (&np->dn->allocptrlock); - return err; + /* ...mapped from the inode. */ + for (i = lbn + 1; i < NDADDR; i++) + if (di->di_db[i]) + { + long bsize = blksize (sblock, np, i); + ffs_blkfree (np, di->di_db[i], bsize); + di->di_db[i] = 0; + blocksfreed += btodb (bsize); + } } - - /* Indirect blocks first. */ - for (level = INDIR_TRIPLE; level >= INDIR_SINGLE; level--) - if (lastiblock[level] < 0 && di->di_ib[level]) - { - int count; - count = free_indir (np, di->di_ib[level], level); - blocksfreed += count; - di->di_ib[level] = 0; - } - - /* Whole direct blocks or frags */ - for (i = NDADDR - 1; i > lastblock; i--) + else { - long bsize; - - bn = di->di_db[i]; - if (bn == 0) - continue; - - bsize = blksize (sblock, np, i); - ffs_blkfree (np, bn, bsize); - blocksfreed += btodb (bsize); - - di->di_db[i] = 0; + /* ... or mapped from sindir */ + if (indirs[1].bno) + { + daddr_t *sindir = indir_block (indirs[1].bno); + for (i = indirs[0].offset + 1; i < NINDIR (sblock); i++) + { + ffs_blkfree (np, sindir[i], sblock->fs_bsize); + sindir[i] = 0; + blocksfreed += btodb (sblock->fs_bsize); + } + record_poke (sindir, sblock->fs_bsize); + } } - + + /* Free single indirect blocks */ + if (indirs[1].offset < 0) + { + /* ...mapped from the inode */ + if (di->di_ib[INDIR_SINGLE] && indirs[1].offset == -2) + { + blocksfreed += indir_release (np, di->di_ib[INDIR_SINGLE], + INDIR_SINGLE); + di->di_ib[INDIR_SINGLE] = 0; + } + } + else + { + /* ...or mapped from dindir */ + if (indirs[2].bno) + { + daddr_t *dindir = indir_block (indirs[2].bno); + for (i = indirs[1].offset + 1; i < NINDIR (sblock); i++) + { + blocksfreed += indir_release (np, dindir[i], INDIR_SINGLE); + dindir[i] = 0; + } + record_poke (dindir, sblock->fs_bsize); + } + } + + /* Free double indirect block */ + assert (indirs[2].offset < 0); /* which must be mapped from the inode */ + if (indirs[2].offset == -2) + { + if (di->di_ib[INDIR_DOUBLE]) + { + blocksfreed += indir_release (np, di->di_ib[INDIR_DOUBLE], + INDIR_DOUBLE); + di->di_ib[INDIR_DOUBLE] = 0; + } + } + /* Finally, check to see if the new last direct block is changing size; if so release any frags necessary. */ - if (lastblock >= 0 - && di->di_db[lastblock]) + if (lbn >= 0 && lbn < NDADDR && di->di_db[lbn]) { long oldspace, newspace; + daddr_t bn; - bn = di->di_db[lastblock]; - oldspace = blksize (sblock, np, lastblock); + bn = di->di_db[lbn]; + oldspace = blksize (sblock, np, lbn); np->allocsize = fragroundup (sblock, length); - newspace = blksize (sblock, np, lastblock); + newspace = blksize (sblock, np, lbn); assert (newspace); @@ -179,28 +214,56 @@ diskfs_truncate (struct node *np, } } else - np->allocsize = fragroundup (sblock, length); - - diskfs_end_catch_exception (); + { + if (lbn > NDADDR) + np->allocsize = blkroundup (sblock, length); + else + np->allocsize = fragroundup (sblock, length); + } + + record_poke (di, sizeof (struct dinode)); + np->dn_stat.st_blocks -= blocksfreed; np->dn_set_ctime = 1; diskfs_node_update (np, 1); rwlock_writer_unlock (&np->dn->allocptrlock); + /* At this point the last block (as defined by np->allocsize) + might not be allocated. We need to allocate it to maintain + the rule that the last block of a file is always allocated. */ + + if (np->allocsize && indirs[0].bno == 0) + { + /* The strategy is to reduce LBN until we get one that's allocated; + then reduce allocsize accordingly, then call diskfs_grow. */ + + do + err = fetch_indir_spec (np, --lbn, indirs); + /* err XXX */ + while (indirs[0].bno == 0 && lbn >= 0); + + assert ((lbn + 1) * sblock->fs_bsize < np->allocsize); + np->allocsize = (lbn + 1) * sblock->fs_bsize; + + diskfs_grow (np, length, 0); + } + + diskfs_end_catch_exception (); + /* Now we can permit delayed copies again. */ if (np->dn->fileinfo) pager_change_attributes (np->dn->fileinfo->p, MAY_CACHE, MEMORY_OBJECT_COPY_DELAY, 0); - return 0; + return err; } /* Free indirect block BNO of level LEVEL; recursing if necessary to free other indirect blocks. Return the number of disk blocks freed. */ static int -free_indir (struct node *np, daddr_t bno, int level) +indir_release (struct node *np, daddr_t bno, int level) { int count = 0; daddr_t *addrs; @@ -219,7 +282,7 @@ free_indir (struct node *np, daddr_t bno, int level) count += btodb (sblock->fs_bsize); } else - count += free_indir (np, addrs[i], level - 1); + count += indir_release (np, addrs[i], level - 1); } /* Subtlety: this block is no longer necessary; the information @@ -263,6 +326,7 @@ free_indir (struct node *np, daddr_t bno, int level) } + /* Implement the diskfs_grow callback; see <hurd/diskfs.h> for the interface description. */ @@ -326,6 +390,7 @@ diskfs_grow (struct node *np, goto out; old_pbn = di->di_db[olbn]; di->di_db[olbn] = bno; + record_poke (di, sizeof (struct dinode)); np->dn_set_ctime = 1; dev_write_sync (fsbtodb (sblock, bno) + btodb (osize), @@ -360,6 +425,7 @@ diskfs_grow (struct node *np, goto out; di->di_db[lbn] = bno; + record_poke (di, sizeof (struct dinode)); np->dn_set_ctime = 1; dev_write_sync (fsbtodb (sblock, bno) + btodb (osize), @@ -385,6 +451,7 @@ diskfs_grow (struct node *np, goto out; di->di_db[lbn] = bno; + record_poke (di, sizeof (struct dinode)); np->dn_set_ctime = 1; dev_write_sync (fsbtodb (sblock, bno), zeroblock, size); @@ -426,6 +493,7 @@ diskfs_grow (struct node *np, goto out; zero_disk_block (bno); indirs[1].bno = di->di_ib[INDIR_SINGLE] = bno; + record_poke (di, sizeof (struct dinode)); } else { @@ -446,6 +514,7 @@ diskfs_grow (struct node *np, goto out; zero_disk_block (bno); indirs[2].bno = di->di_ib[INDIR_DOUBLE] = bno; + record_poke (di, sizeof (struct dinode)); } diblock = indir_block (indirs[2].bno); @@ -460,6 +529,7 @@ diskfs_grow (struct node *np, goto out; zero_disk_block (bno); indirs[1].bno = diblock[indirs[1].offset] = bno; + record_poke (diblock, sblock->fs_bsize); } } @@ -473,6 +543,7 @@ diskfs_grow (struct node *np, if (err) goto out; indirs[0].bno = siblock[indirs[0].offset] = bno; + record_poke (siblock, sblock->fs_bsize); dev_write_sync (fsbtodb (sblock, bno), zeroblock, sblock->fs_bsize); } |