- if (block_end <= from || block_start >= to) {
- if (PageUptodate(page)) {
- if (!buffer_uptodate(bh))
- set_buffer_uptodate(bh);
- }
- continue;
- }
- /*
- * @bh is at least partially being written to.
- * Make sure it is not marked as new.
- */
- //if (buffer_new(bh))
- // clear_buffer_new(bh);
-
- if (block >= ablock) {
- // TODO: block is above allocated_size, need to
- // allocate it. Best done in one go to accommodate not
- // only block but all above blocks up to and including:
- // ((page->index << PAGE_CACHE_SHIFT) + to + blocksize
- // - 1) >> blobksize_bits. Obviously will need to round
- // up to next cluster boundary, too. This should be
- // done with a helper function, so it can be reused.
- ntfs_error(vol->sb, "Writing beyond allocated size "
- "is not supported yet. Sorry.");
- err = -EOPNOTSUPP;
- goto err_out;
- // Need to update ablock.
- // Need to set_buffer_new() on all block bhs that are
- // newly allocated.
- }
- /*
- * Now we have enough allocated size to fulfill the whole
- * request, i.e. block < ablock is true.
- */
- if (unlikely((block >= iblock) &&
- (ni->initialized_size < vi->i_size))) {
- /*
- * If this page is fully outside initialized size, zero
- * out all pages between the current initialized size
- * and the current page. Just use ntfs_readpage() to do
- * the zeroing transparently.
- */
- if (block > iblock) {
- // TODO:
- // For each page do:
- // - read_cache_page()
- // Again for each page do:
- // - wait_on_page_locked()
- // - Check (PageUptodate(page) &&
- // !PageError(page))
- // Update initialized size in the attribute and
- // in the inode.
- // Again, for each page do:
- // __set_page_dirty_buffers();
- // page_cache_release()
- // We don't need to wait on the writes.
- // Update iblock.
- }
- /*
- * The current page straddles initialized size. Zero
- * all non-uptodate buffers and set them uptodate (and
- * dirty?). Note, there aren't any non-uptodate buffers
- * if the page is uptodate.
- * FIXME: For an uptodate page, the buffers may need to
- * be written out because they were not initialized on
- * disk before.
- */
- if (!PageUptodate(page)) {
- // TODO:
- // Zero any non-uptodate buffers up to i_size.
- // Set them uptodate and dirty.
- }
- // TODO:
- // Update initialized size in the attribute and in the
- // inode (up to i_size).
- // Update iblock.
- // FIXME: This is inefficient. Try to batch the two
- // size changes to happen in one go.
- ntfs_error(vol->sb, "Writing beyond initialized size "
- "is not supported yet. Sorry.");
- err = -EOPNOTSUPP;
- goto err_out;
- // Do NOT set_buffer_new() BUT DO clear buffer range
- // outside write request range.
- // set_buffer_uptodate() on complete buffers as well as
- // set_buffer_dirty().
- }
-
- /* Need to map unmapped buffers. */
- if (!buffer_mapped(bh)) {
- /* Unmapped buffer. Need to map it. */
- bh->b_bdev = vol->sb->s_bdev;
-
- /* Convert block into corresponding vcn and offset. */
- vcn = (VCN)block << blocksize_bits >>
- vol->cluster_size_bits;
- vcn_ofs = ((VCN)block << blocksize_bits) &
- vol->cluster_size_mask;
-
- is_retry = FALSE;
- if (!rl) {
-lock_retry_remap:
- down_read(&ni->run_list.lock);
- rl = ni->run_list.rl;
- }
- if (likely(rl != NULL)) {
- /* Seek to element containing target vcn. */
- while (rl->length && rl[1].vcn <= vcn)
- rl++;
- lcn = vcn_to_lcn(rl, vcn);
- } else
- lcn = (LCN)LCN_RL_NOT_MAPPED;
- if (unlikely(lcn < 0)) {
- /*
- * We extended the attribute allocation above.
- * If we hit an ENOENT here it means that the
- * allocation was insufficient which is a bug.
- */
- BUG_ON(lcn == LCN_ENOENT);
-
- /* It is a hole, need to instantiate it. */
- if (lcn == LCN_HOLE) {
- // TODO: Instantiate the hole.
- // clear_buffer_new(bh);
- // unmap_underlying_metadata(bh->b_bdev,
- // bh->b_blocknr);
- // For non-uptodate buffers, need to
- // zero out the region outside the
- // request in this bh or all bhs,
- // depending on what we implemented
- // above.
- // Need to flush_dcache_page().
- // Or could use set_buffer_new()
- // instead?
- ntfs_error(vol->sb, "Writing into "
- "sparse regions is "
- "not supported yet. "
- "Sorry.");
- err = -EOPNOTSUPP;
- goto err_out;
- } else if (!is_retry &&
- lcn == LCN_RL_NOT_MAPPED) {
- is_retry = TRUE;
- /*
- * Attempt to map run list, dropping
- * lock for the duration.
- */
- up_read(&ni->run_list.lock);
- err = map_run_list(ni, vcn);
- if (likely(!err))
- goto lock_retry_remap;
- rl = NULL;
- }
- /*
- * Failed to map the buffer, even after
- * retrying.
- */
- bh->b_blocknr = -1UL;
- ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%llx) "
- "failed with error code "
- "0x%llx%s.",
- (unsigned long long)vcn,
- (unsigned long long)-lcn,
- is_retry ? " even after "
- "retrying" : "");
- // FIXME: Depending on vol->on_errors, do
- // something.
- if (!err)
- err = -EIO;
- goto err_out;
- }
- /* We now have a successful remap, i.e. lcn >= 0. */
-
- /* Setup buffer head to correct block. */
- bh->b_blocknr = ((lcn << vol->cluster_size_bits)
- + vcn_ofs) >> blocksize_bits;
- set_buffer_mapped(bh);
-
- // FIXME: Something analogous to this is needed for
- // each newly allocated block, i.e. BH_New.
- // FIXME: Might need to take this out of the
- // if (!buffer_mapped(bh)) {}, depending on how we
- // implement things during the allocated_size and
- // initialized_size extension code above.
- if (buffer_new(bh)) {
- clear_buffer_new(bh);
- unmap_underlying_metadata(bh->b_bdev,
- bh->b_blocknr);
- if (PageUptodate(page)) {
- set_buffer_uptodate(bh);
- continue;
- }
- /*
- * Page is _not_ uptodate, zero surrounding
- * region. NOTE: This is how we decide if to
- * zero or not!
- */
- if (block_end > to || block_start < from) {
- void *kaddr;
-
- kaddr = kmap_atomic(page, KM_USER0);
- if (block_end > to)
- memset(kaddr + to, 0,
- block_end - to);
- if (block_start < from)
- memset(kaddr + block_start, 0,
- from -
- block_start);
- flush_dcache_page(page);
- kunmap_atomic(kaddr, KM_USER0);
- }
- continue;
- }
- }
- /* @bh is mapped, set it uptodate if the page is uptodate. */
- if (PageUptodate(page)) {
- if (!buffer_uptodate(bh))
- set_buffer_uptodate(bh);
- continue;
- }
- /*
- * The page is not uptodate. The buffer is mapped. If it is not
- * uptodate, and it is only partially being written to, we need
- * to read the buffer in before the write, i.e. right now.
- */
- if (!buffer_uptodate(bh) &&
- (block_start < from || block_end > to)) {
- ll_rw_block(READ, 1, &bh);
- *wait_bh++ = bh;
- }
- } while (block++, block_start = block_end,
- (bh = bh->b_this_page) != head);
-
- /* Release the lock if we took it. */
- if (rl) {
- up_read(&ni->run_list.lock);
- rl = NULL;
- }
-
- /* If we issued read requests, let them complete. */
- while (wait_bh > wait) {
- wait_on_buffer(*--wait_bh);
- if (!buffer_uptodate(*wait_bh))
- return -EIO;