diff options
-rw-r--r-- | ufs/sizes.c | 719 |
1 files changed, 357 insertions, 362 deletions
diff --git a/ufs/sizes.c b/ufs/sizes.c index 0bc0e547..a349eb40 100644 --- a/ufs/sizes.c +++ b/ufs/sizes.c @@ -30,283 +30,236 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define MAY_CACHE 1 #endif -static void dindir_drop (struct node *); -static void sindir_drop (struct node *, int, int); -static void poke_pages (memory_object_t, vm_offset_t, vm_offset_t); - -/* Truncate node NP to be at most LENGTH bytes. */ -/* The inode must be locked, and we must have the conch. */ -/* This is a pain. Sigh. */ + +/* Implement the diskfs_truncate callback; sse <hurd/diskfs.h> for the + interface description. */ error_t diskfs_truncate (struct node *np, off_t length) { - daddr_t lastblock, olastblock, bn; - off_t osize; - int bsize, idx; - mach_port_t obj; - - osize = np->dn_stat.st_size; - if (length >= osize) + int offset; + daddr_t lastiblock[NIADDR], lastblock, bn; + struct dinode *di = dino (np->dn->number); + int blocksfreed = 0; + + if (length >= np->dn_stat.st_size) return 0; - /* Check to see if this is a kludged symlink. */ + assert (!diskfs_readonly); + + /* First check to see if this is a kludged symlink; if so + this is special. */ if (direct_symlink_extension && S_ISLNK (np->dn_stat.st_mode) - && osize < sblock->fs_maxsymlinklen) + && np->dn_stat.st_size < sblock->fs_maxsymlinklen) { error_t err; - - /* Prune it here */ - err = diskfs_catch_exception (); - if (err) + + if (err = diskfs_catch_exception ()) return err; - - bzero (dinodes[np->dn->number].di_shortlink + length, - osize - length); + bzero (di->di_shortlink + length, np->dn_stat.st_size - length); diskfs_end_catch_exception (); np->dn_stat.st_size = length; - np->dn_set_ctime = 1; - np->dn_set_mtime = 1; + np->dn_set_ctime = np->dn_set_mtime = 1; + return 0; + } + + /* If the file is not being trucated to a block boundary, + the zero the partial bit in the new last block. */ + offset = blkoff (sblock, length); + if (offset) + { + int bsize; /* size of new last block */ + int savesize = np->allocsize; + + np->allocsize = length; /* temporary */ + bsize = blksize (sblock, np, lbkno (sblock, length)); + np->allocsize = savesize; + diskfs_node_rdwr (np, zeroblock, length, bsize - offset, 1, 0, 0); + diskfs_file_update (np, 1); } - /* Calculate block number of last block */ - lastblock = lblkno (sblock, length + sblock->fs_bsize - 1) - 1; - olastblock = lblkno (sblock, osize + sblock->fs_bsize - 1) - 1; - - /* If the prune is not to a block boundary, zero the bit upto the - next block boundary. */ - if (blkoff (sblock, length)) - diskfs_node_rdwr (np, (void *) zeroblock, length, - (blksize (sblock, np, lastblock) - - blkoff (sblock, length)), - 1, 0, 0); - - /* We are going to throw away the block pointers for the blocks - olastblock+1 through lastblock. This will cause the underlying - data to become zeroes, because of the behavior of pager_read_page - (in ufs/pager.c). Consequently, we have to take action to force - the kernel to immediately undertake any delayed copies that - implicitly depend on the data we are flushing. We also have to - prevent any new delayed copies from being undertaken until we - have finished the flush. */ + rwlock_writer_lock (&np->allocptrlock); + + /* Now flush all the data past the new size from the kernel. + Also force any delayed copies of this data to take place + immediately. (We are changing the data implicitly to zeros + and doing it without the kernels immediate knowledge; + this forces us to help out the kernel thusly.) */ if (np->dn->fileinfo) { - pager_change_attributes (np->dn->fileinfo->p, MAY_CACHE, + pager_change_attributes (np->dn->fileinfo->p, MAY_CACHE, MEMORY_OBJECT_COPY_NONE, 1); obj = diskfs_get_filemap (np); - mach_port_insert_right (mach_task_self (), obj, obj, + mach_port_insert_right (mach_task_self (), obj, obj, MACH_MSG_TYPE_MAKE_SEND); - poke_pages (obj, round_page (length), round_page (osize)); + poke_pages (obj, round_page (length), round_page (np->allocsize)); mach_port_deallocate (mach_task_self (), obj); + pager_flush_some (np->fileinfo->p, round_page (length), + 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 + sblock->fs_bsize - 1) - 1; + lastiblock[INDIR_SINGLE] = lastblock - NDADDR; + lastiblock[INDIR_DOUBLE] = lastiblock[INDIR_SINGLE] - NINDIR (sblock); + lastiblock[INDIR_TRIPLE] = (lastiblock[INDIR_DOUBLE] + - NINDIR (sblock) * NINDIR (sblock)); - rwlock_writer_lock (&np->dn->datalock, np->dn); + /* Normalize to -1 indicating that this block will not be needed. */ + for (level = INDIR_TRIPLE; level >= INDIR_SINGLE; level--) + if (lastiblock[level] < 0) + lastiblock[level] = -1; - /* Update the size now. If we crash, fsck can finish freeing the - blocks. */ + /* Update the size on disk; fsck will finish freeing blocks if necessary + should we crash. */ np->dn_stat.st_size = length; - np->dn_stat_dirty = 1; + np->dn_set_mtime = 1; + np->dn_set_ctime = 1; + diskfs_node_update (np, 1); - /* Flush the old data. */ - if (np->dn->fileinfo) - pager_flush_some (np->dn->fileinfo->p, - (lastblock == -1 ? 0 : lastblock) * sblock->fs_bsize, - (olastblock - lastblock) * sblock->fs_bsize, 1); + /* Free the blocks. */ + + err = diskfs_catch_exception (); + if (err) + { + rwlock_writer_unlock (&np->allocptrlock); + return err; + } - /* Drop data blocks mapped by indirect blocks */ - if (olastblock >= NDADDR) + /* Indirect blocks first. */ + for (level = INDIR_TRIPLE; level >= INDIR_SINGLE; level--) + if (lastiblock[level] == -1 && 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--) { - daddr_t first2free; + long bsize; - mutex_lock (&sinmaplock); - if (!np->dn->sinloc) - sin_map (np); + bn = dn->di_db[i]; + if (bn == 0) + continue; - if (lastblock + 1 > NDADDR) - first2free = lastblock + 1; - else - first2free = NDADDR; - - for (idx = first2free; idx <= olastblock; idx ++) - { - assert (idx - NDADDR < np->dn->sinloclen); - if (np->dn->sinloc[idx - NDADDR]) - { - ffs_blkfree (np, np->dn->sinloc[idx - NDADDR], sblock->fs_bsize); - np->dn->sinloc[idx - NDADDR] = 0; - np->dn_stat.st_blocks -= sblock->fs_bsize / DEV_BSIZE; - np->dn_stat_dirty = 1; - } - } + bsize = blksize (sblock, np, i); + ffs_blkfree (sblock, bn, bsize); + blocksfreed += btodb (bsize); - /* Prune the block pointers handled by the sindir pager. This will - free all the indirect blocks and such as necessary. */ - sindir_drop (np, lblkno(sblock, - (first2free - NDADDR) * sizeof (daddr_t)), - lblkno (sblock, (olastblock - NDADDR) * sizeof (daddr_t))); - - if (!np->dn->fileinfo) - sin_unmap (np); - mutex_unlock (&sinmaplock); + dn->di_db[i] = 0; } - /* Prune the blocks mapped directly from the inode */ - for (idx = lastblock + 1; idx < NDADDR; idx++) + /* Finally, check to see if the new last direct block is + changing size; if so release any frags necessary. */ + if (lastblock >= 0 + && dn->di_db[lastblock]) { - bn = dinodes[np->dn->number].di_db[idx]; - if (bn) + bn = dn->di_db[lastblock]; + long oldspace, newspace; + + oldspace = blksize (sblock, np, lastblock); + np->allocsize = length; + newspace = blksize (sblock, np, lastblock); + + assert (newspace); + + if (oldspace - newspace) { - dinodes[np->dn->number].di_db[idx] = 0; - assert (idx <= olastblock); - if (idx == olastblock) - bsize = blksize (sblock, np, idx); - else - bsize = sblock->fs_bsize; - ffs_blkfree (np, bn, bsize); - np->dn_stat.st_blocks -= bsize / DEV_BSIZE; - np->dn_stat_dirty = 1; + bn += numfrags (sblock, newspace); + ffs_blkfree (np, bn, oldspace - newspace); + blocksfreed += btodb (oldspace - newspace) } } + else + np->allocsize = length; - if (lastblock >= 0 && lastblock < NDADDR) - { - /* Look for a change in the size of the last direct block */ - bn = dinodes[np->dn->number].di_db[lastblock]; - if (bn) - { - off_t oldspace, newspace; - - oldspace = blksize (sblock, np, lastblock); - newspace = fragroundup (sblock, blkoff (sblock, length));; - assert (newspace); - if (oldspace - newspace) - { - bn += numfrags (sblock, newspace); - ffs_blkfree (np, bn, oldspace - newspace); - np->dn_stat.st_blocks -= (oldspace - newspace) / DEV_BSIZE; - np->dn_stat_dirty = 1; - } - } - } + diskfs_end_catch_exception (); - if (lastblock < NDADDR) - np->allocsize = fragroundup (sblock, length); - else - np->allocsize = blkroundup (sblock, length); + np->dn_set_ctime = 1; + diskfs_node_update (np, 1); - rwlock_writer_unlock (&np->dn->datalock, np->dn); + rwlock_writer_unlock (&np->allocptrlock); - /* Now we can allow delayed copies again */ + /* 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); - - diskfs_file_update (np, 1); - return 0; -} - -/* Deallocate the double indirect block of the file NP. */ -static void -dindir_drop (struct node *np) -{ - rwlock_writer_lock (&np->dn->dinlock, np->dn); - pager_flush_some (dinpager->p, np->dn->number * sblock->fs_bsize, - sblock->fs_bsize, 1); - - if (dinodes[np->dn->number].di_ib[INDIR_DOUBLE]) - { - ffs_blkfree (np, dinodes[np->dn->number].di_ib[INDIR_DOUBLE], - sblock->fs_bsize); - dinodes[np->dn->number].di_ib[INDIR_DOUBLE] = 0; - np->dn_stat.st_blocks -= sblock->fs_bsize / DEV_BSIZE; - } - - rwlock_writer_unlock (&np->dn->dinlock, np->dn); + return 0; } - -/* Deallocate the single indirect blocks of file IP from - FIRST through LAST inclusive. */ -static void -sindir_drop (struct node *np, - int first, - int last) +/* 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) { - int idx; + int count = 0; + daddr_t *addrs; + int i; + struct indir_dirty *d, *prev, *nxt; - rwlock_writer_lock (&np->dn->sinlock, np->dn); + assert (bno); - pager_flush_some (np->dn->sininfo->p, first * sblock->fs_bsize, - (last - first + 1) * sblock->fs_bsize, 1); + addrs = indir_block (bno); + for (i = 0; i < NINDIR (sblock); i++) + if (addrs[i]) + { + if (level == INDIR_SINGLE) + { + ffs_blkfree (np, addrs[i], sblock->fs_bsize); + count += btodb (sblock->fs_bsize); + } + else + count += free_indir (addrs[i], level - 1); + } - /* Drop indirect blocks found in the double indirect block */ - if (last > 1) + /* Subtlety: this block is no longer necessary; the information + the kernel has cached corresponding to ADDRS is now unimportant. + Consider that if this block is allocated to a file, it will then + be double cached and the kernel might decide to write out + the disk_image version of the block. So we have to flush + the block from the kernel's memory, making sure we do it + synchronously--and BEFORE we attach it to the free list + with ffs_blkfree. */ + pager_flush_some (diskpager->p, fsaddr (bno), sblock->fs_bsize, 1); + + /* We should also take this block off the inode's list of + dirty indirect blocks if it's there. */ + prev = 0; + d = np->dirty; + while (d) { - mutex_lock (&dinmaplock); - if (!np->dn->dinloc) - din_map (np); - for (idx = first; idx = last; idx++) + next = d->next; + if (d->bno == bno) { - assert (idx - 1 < np->dn->dinloclen); - if (np->dn->dinloc[idx - 1]) - { - ffs_blkfree (np, np->dn->dinloc[idx - 1], sblock->fs_bsize); - np->dn->dinloc[idx - 1] = 0; - np->dn_stat.st_blocks -= sblock->fs_bsize / DEV_BSIZE; - } + if (prev) + prev->next = next; + else + np->dirty = next; + free (d); } - - /* If we no longer need the double indirect block, drop it. */ - if (first <= 1) - dindir_drop (np); - - mutex_lock (&dinmaplock); - if (!np->dn->sininfo) - din_unmap (np); - mutex_unlock (&dinmaplock); - } - - /* Drop the block from the inode if we don't need it any more */ - if (first == 0 && dinodes[np->dn->number].di_ib[INDIR_SINGLE]) - { - ffs_blkfree (np, dinodes[np->dn->number].di_ib[INDIR_SINGLE], - sblock->fs_bsize); - dinodes[np->dn->number].di_ib[INDIR_SINGLE] = 0; - np->dn_stat.st_blocks -= sblock->fs_bsize / DEV_BSIZE; - } - rwlock_writer_unlock (&np->dn->sinlock, np->dn); -} - -/* Write something to each page from START to END inclusive of memory - object OBJ, but make sure the data doesns't actually change. */ -static void -poke_pages (memory_object_t obj, - vm_offset_t start, - vm_offset_t end) -{ - vm_address_t addr, poke; - vm_size_t len; - error_t err; - - while (start < end) - { - len = 8 * vm_page_size; - if (len > end - start) - len = end - start; - addr = 0; - err = vm_map (mach_task_self (), &addr, len, 0, 1, obj, start, 0, - VM_PROT_WRITE|VM_PROT_READ, VM_PROT_READ|VM_PROT_WRITE, 0); - if (!err) + else { - for (poke = addr; poke < addr + len; poke += vm_page_size) - *(volatile int *)poke = *(volatile int *)poke; - vm_deallocate (mach_task_self (), addr, len); + prev = d; + next = d->next; } - start += len; + d = next; } + + /* Free designated block */ + ffs_blkfree (np, bno, sblock->fs_bsize); + count += btodb (sblock->fs_bsize); + + return count; } + /* Implement the diskfs_grow callback; see <hurd/diskfs.h> for the interface description. */ @@ -315,197 +268,239 @@ diskfs_grow (struct node *np, off_t end, struct protid *cred) { - daddr_t lbn, pbn, nb; - int osize, size; - int err; - volatile daddr_t dealloc_on_error = 0; - volatile int dealloc_size = 0; - volatile off_t zero_off1 = 0, zero_off2 = 0; - volatile int zero_len1 = 0, zero_len2 = 0; - volatile off_t poke_off1 = 0, poke_off2 = 0; - volatile off_t poke_len1 = 0, poke_len2 = 0; - vm_address_t zerobuf; + daddr_t lbn, olbn; + int size, osize; + error_t err; + struct dinode *di = dino (np->dn->number); + off_t poke_off; + size_t poke_len = 0; + + /* Zero an sblock->fs_bsize piece of disk starting at BNO, + synchronously. We do this on newly allocated indirect + blocks before setting the pointer to them to ensure that an + indirect block absolutely never points to garbage. */ + void zero_disk_block (int bno) + { + bzero (indir_block (bno), sblock->fs_bsize); + sync_disk_blocks (bno, sblock->fs_bsize, 1); + }; + /* Check to see if we don't actually have to do anything */ if (end <= np->allocsize) return 0; - - rwlock_writer_lock (&np->dn->datalock, np->dn); - - /* This deallocation works for the calls to alloc, but not for - realloccg. I'm not sure how to prune the fragment down, especially if - we grew a fragment and then couldn't allocate the piece later. - freeing it all up is a royal pain, largely punted right now... -mib. - */ - if (err = diskfs_catch_exception()) - { - if (dealloc_on_error) - ffs_blkfree (np, dealloc_on_error, dealloc_size); - goto out; - } - /* This is the logical block number of what will be the last block. */ + assert (!diskfs_readonly); + + /* The new last block of the file. */ lbn = lblkno (sblock, end + sblock->fs_bsize - 1) - 1; - /* This is the size to be of that block if it is in the NDADDR array. */ + /* This is the size of that block if it is in the NDADDR array. */ size = fragroundup (sblock, blkoff (sblock, end)); if (size == 0) size = sblock->fs_bsize; - /* if we are writing a new block, then an old one may need to be - reallocated into a full block. */ + rwlock_writer_lock (&np->dn->allocptrlock); - nb = lblkno (sblock, np->allocsize + sblock->fs_bsize - 1) - 1; - if (np->allocsize && nb < NDADDR && nb < lbn) + /* The old last block of the file. */ + olbn = lbkno (sblock, np->allocsize + sblock->fs_bsize - 1) - 1; + + /* This is the size of that block if it is in the NDADDR array. */ + osize = fragroundup (sblock, blkoff (sblock, np->allocsize)); + if (osize == 0) + osize = sblock->fs_bsize; + + /* If this end point is a new block and the file currently + has a fragment, then expand the fragment to a full block. */ + if (np->allocsize && olbn < NDADDR && olbn < lbn) { - osize = blksize (sblock, np, nb); - if (osize < sblock->fs_bsize && osize > 0) + if (osize < sblock->fs_bsize) { - daddr_t old_pbn; + daddr_t olb_pbn, bno; err = ffs_realloccg (np, nb, - ffs_blkpref (np, nb, (int)nb, - dinodes[np->dn->number].di_db), - osize, sblock->fs_bsize, &pbn, cred); + ffs_blkpref (np, lbn, lbn, di->di_db), + osize, sblock->fs_bsize, &bno, cred); if (err) goto out; - np->allocsize = (nb + 1) * sblock->fs_bsize; - old_pbn = dinodes[np->dn->number].di_db[nb]; - dinodes[np->dn->number].di_db[nb] = pbn; - - /* The new disk blocks should be zeros but might not be. - This is a sanity measure that I'm not sure is necessary. */ - zero_off1 = nb * sblock->fs_bsize + osize; - zero_len1 = nb * sblock->fs_bsize + sblock->fs_bsize - zero_off1; + old_pbn = di->di_db + di->di_db[nb] = bno; + np->dn_set_ctime = 1; + + dev_write_sync (fsbtodb (bno) + btodb (osize), + zeroblock, sblock->fs_bsize - osize); if (pbn != old_pbn) { - /* Make sure that the old contents get written out by - poking the pages. */ - poke_off1 = nb * sblock->fs_bsize; - poke_len1 = osize; + /* Make sure the old contents get written out + to the new address by poking the pages. */ + poke_off = nb * sblock->fs_bsize; + poke_len = osize; } } } - - /* allocate this block */ + if (lbn < NDADDR) { - nb = dinodes[np->dn->number].di_db[lbn]; - - if (nb != 0) + daddr_t bno, old_pbn = di->di_db[lbn]; + + if (old_pbn != 0) { - /* consider need to reallocate a fragment. */ - osize = blkoff (sblock, np->allocsize); - if (osize == 0) - osize = sblock->fs_bsize; - if (size > osize) - { - err = ffs_realloccg (np, lbn, - ffs_blkpref (np, lbn, lbn, - dinodes[np->dn->number].di_db), - osize, size, &pbn, cred); - if (err) - goto out; - dinodes[np->dn->number].di_db[lbn] = pbn; - - /* The new disk blocks should be zeros but might not be. - This is a sanity measure that I'm not sure is necessary. */ - zero_off2 = lbn * sblock->fs_bsize + osize; - zero_len2 = lbn * sblock->fs_bsize + size - zero_off2; + /* The last block is already allocated. Therefore we + must be expanding the fragment. Make sure that's really + what we're up to. */ + assert (size > osize); + assert (lbn == olbn); + + err = ffs_realloccg (np, lbn, + ffs_blkpref (np, lbn, lbn, di->di_db), + osize, size, &bno, cred); + if (err) + goto out; + + di->di_db[lbn] = bno; + np->dn_sat_ctime = 1; + + dev_write_sync (fsbtodb (bno) + btodb (osize), + zeroblock, size - osize); - if (pbn != nb) - { - /* Make sure that the old contents get written out by - poking the pages. */ - poke_off2 = lbn * sblock->fs_bsize; - poke_len2 = osize; - } + if (pbn != old_pbn) + { + assert (!poke_len); + + /* Make sure the old contents get written out to + the new address by poking the pages. */ + poke_off = lbn * sblock->fs_bsize; + poke_len = osize; } } else { - err = ffs_alloc (np, lbn, - ffs_blkpref (np, lbn, lbn, - dinodes[np->dn->number].di_db), - size, &pbn, cred); + /* Allocate a new last block. */ + err = ffs_alloc (np, lbn, + ffs_blkpref (np, lbn, lbn, di->di_db), + size, &bno, cred); if (err) goto out; - dealloc_on_error = pbn; - dealloc_size = size; - dinodes[np->dn->number].di_db[lbn] = pbn; + + di->di_db[lbn] = pbn; + np->dn_set_ctime = 1; + + dev_write_sync (fsbtodb (bno), zeroblock, size); } - np->allocsize = fragroundup (sblock, end); } else { - /* Make user the sindir area is mapped at the right size. */ - mutex_lock (&sinmaplock); - if (np->dn->sinloc) - { - sin_remap (np, end); - np->allocsize = blkroundup (sblock, end); - } - else - { - np->allocsize = blkroundup (sblock, end); - sin_map (np); - } + struct iblock_spec indirs[NINDIR + 1]; + int i; + daddr_t *siblock; + + /* Count the number of levels of indirection. */ + err = fetch_indir_spec (np, lbn, indirs); + if (err) + goto out; - lbn -= NDADDR; - assert (lbn < np->dn->sinloclen); - if (!np->dn->sinloc[lbn]) + /* Make sure we didn't miss the NDADDR case + above somehow. */ + assert (indirs[0].offset != -1); + + /* See if we need a triple indirect block; fail if so. */ + assert (indirs[1].offset == -1 || indirs[2].offset == -1); + + /* Check to see if this block is allocated. If it is + that's an error. */ + assert (indirs[0].bno == 0); + + /* We need to set SIBLOCK to the single indirect block + array; see if the single indirect block is allocated. */ + if (indirs[1].bno == 0) { - err = ffs_alloc (np, lbn, ffs_blkpref (np, lbn + NDADDR, lbn, - np->dn->sinloc), - sblock->fs_bsize, &pbn, cred); - if (err) - goto out; - dealloc_on_error = pbn; - dealloc_size = sblock->fs_bsize; - np->dn->sinloc[lbn] = pbn; + /* Allocate it. */ + if (indirs[1].offset == -1) + { + err = ffs_alloc (np, lbn, + ffs_blkpref (np, lbn, INDIR_SINGLE, di->di_ib), + sblock->fs_bsize, &bno, 0); + if (err) + goto out; + zero_disk_block (bno); + indirs[1].bno = di->di_ib[INDIR_SINGLE] = bno; + } + else + { + daddr_t *diblock; + + /* We need to set diblock to the double indirect block + array; see if the double indirect block is allocated. */ + if (indirs[2].bno == 0) + { + /* This assert because triple indirection is not + supported. */ + assert (indirs[2].offset == -1); + err = ffs_alloc (np, lbn + ffs_blkpref (np, lbn, + INDIR_DOUBLE, di->di_ib), + sblock->fs_bsize, &bno, 0); + if (err) + goto out; + zero_disk_block (bno); + indirs[2].bno = di->di_ib[INDIR_DOUBLE] = bno; + } + + diblock = indir_block (indirs[2].bno); + mark_indir_dirty (indirs[2].bno); + + /* Now we can allocate the single indirect block */ + err = ffs_alloc (np, lbn, + ffs_blkpref (np, lbn, + indirs[1].offset, diblock), + sblock->fs_bsize, &bno, 0); + if (err) + goto out; + zero_disk_block (bno); + indirs[1].bno = diblock[indirs[1].offset] = bno; + } } - if (!np->dn->fileinfo) - sin_unmap (np); - mutex_unlock (&sinmaplock); - } + + siblock = indir_block (indirs[1].bno); + mark_indir_dirty (np, indirs[1].bno); - if (np->conch.holder) - ioserver_put_shared_data (np->conch.holder); + /* Now we can allocate the data block. */ + err = ffs_alloc (np, lbn, + ffs_blkpref (np, lbn, indirs[0].offset, siblock), + sblock->fn_bsize, &bno, 0); + if (err) + goto out; + indirs[0].bno = siblock[indirs[0].offset] = bno; + dev_write_sync (fsbtodb (bno), zeroblock, sblock->fs_bsize); + } out: - diskfs_end_catch_exception (); - rwlock_writer_unlock (&np->dn->datalock, np->dn); - - /* Do the pokes and zeros that we requested before; they have to be - done here because we can't cause a page while holding datalock. */ - if (zero_len1 || zero_len2) + if (!err) { - vm_allocate (mach_task_self (), &zerobuf, - zero_len1 > zero_len2 ? zero_len1 : zero_len2, 1); - if (zero_len1) - diskfs_node_rdwr (np, (char *) zerobuf, zero_off1, - zero_len1, 1, cred, 0); - if (zero_len2) - diskfs_node_rdwr (np, (char *) zerobuf, zero_off2, - zero_len2, 1, cred, 0); - vm_deallocate (mach_task_self (), zerobuf, - zero_len1 > zero_len2 ? zero_len1 : zero_len2); + int newallocsize; + if (lbn < NDADDR) + newallocsize = (lbn - 1) * sblock->fs_bsize + size; + else + newallocsize = lbn * sblock->fs_bsize; + assert (newallocsize > np->allocsize); + np->allocsize = newallocsize; } - if (poke_len1 || poke_len2) + + rwlock_writer_unlock (&np->allocptrlock); + + /* If we expanded a fragment, then POKE_LEN will be set. + We need to poke the requested amount of the memory object + so that the kernel will write out the data to the new location + at a suitable time. */ + if (poke_len) { - mach_port_t obj; obj = diskfs_get_filemap (np); - mach_port_insert_right (mach_task_self (), obj, obj, + mach_port_insert_reght (mach_task_self (), obj, obj, MACH_MSG_TYPE_MAKE_SEND); - if (poke_len1) - poke_pages (obj, trunc_page (poke_off1), - round_page (poke_off1 + poke_len1)); - if (poke_len2) - poke_pages (obj, trunc_page (poke_off2), - round_page (poke_off2 + poke_len2)); + poke_pages (obj, trunc_page (poke_off), + round_page (poke_off + poke_len)); mach_port_deallocate (mach_task_self (), obj); } - diskfs_file_update (np, 0); - return err; -} +} + |