vserver 1.9.3
[linux-2.6.git] / fs / ntfs / aops.c
index 797846b..edcc9fb 100644 (file)
@@ -62,7 +62,8 @@ static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
 
                set_buffer_uptodate(bh);
 
-               file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
+               file_ofs = ((s64)page->index << PAGE_CACHE_SHIFT) +
+                               bh_offset(bh);
                /* Check for the current buffer head overflowing. */
                if (file_ofs + bh->b_size > ni->initialized_size) {
                        char *addr;
@@ -169,7 +170,7 @@ static int ntfs_read_block(struct page *page)
        LCN lcn;
        ntfs_inode *ni;
        ntfs_volume *vol;
-       run_list_element *rl;
+       runlist_element *rl;
        struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
        sector_t iblock, lblock, zblock;
        unsigned int blocksize, vcn_ofs;
@@ -190,13 +191,13 @@ static int ntfs_read_block(struct page *page)
                return -ENOMEM;
        }
 
-       iblock = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
+       iblock = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
        lblock = (ni->allocated_size + blocksize - 1) >> blocksize_bits;
        zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits;
 
 #ifdef DEBUG
-       if (unlikely(!ni->run_list.rl && !ni->mft_no && !NInoAttr(ni)))
-               panic("NTFS: $MFT/$DATA run list has been unmapped! This is a "
+       if (unlikely(!ni->runlist.rl && !ni->mft_no && !NInoAttr(ni)))
+               panic("NTFS: $MFT/$DATA runlist has been unmapped! This is a "
                                "very serious bug! Cannot continue...");
 #endif
 
@@ -204,6 +205,8 @@ static int ntfs_read_block(struct page *page)
        rl = NULL;
        nr = i = 0;
        do {
+               u8 *kaddr;
+
                if (unlikely(buffer_uptodate(bh)))
                        continue;
                if (unlikely(buffer_mapped(bh))) {
@@ -222,14 +225,14 @@ static int ntfs_read_block(struct page *page)
                                        vol->cluster_size_mask;
                        if (!rl) {
 lock_retry_remap:
-                               down_read(&ni->run_list.lock);
-                               rl = ni->run_list.rl;
+                               down_read(&ni->runlist.lock);
+                               rl = ni->runlist.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);
+                               lcn = ntfs_vcn_to_lcn(rl, vcn);
                        } else
                                lcn = (LCN)LCN_RL_NOT_MAPPED;
                        /* Successful remap. */
@@ -249,29 +252,29 @@ lock_retry_remap:
                        /* It is a hole, need to zero it. */
                        if (lcn == LCN_HOLE)
                                goto handle_hole;
-                       /* If first try and run list unmapped, map and retry. */
+                       /* If first try and runlist unmapped, map and retry. */
                        if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
                                is_retry = TRUE;
                                /*
-                                * Attempt to map run list, dropping lock for
+                                * Attempt to map runlist, dropping lock for
                                 * the duration.
                                 */
-                               up_read(&ni->run_list.lock);
-                               if (!map_run_list(ni, vcn))
+                               up_read(&ni->runlist.lock);
+                               if (!ntfs_map_runlist(ni, vcn))
                                        goto lock_retry_remap;
                                rl = NULL;
                        }
                        /* Hard error, zero out region. */
                        SetPageError(page);
-                       ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%llx) failed "
-                                       "with error code 0x%llx%s.",
+                       ntfs_error(vol->sb, "ntfs_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.
                }
                /*
-                * Either iblock was outside lblock limits or vcn_to_lcn()
+                * Either iblock was outside lblock limits or ntfs_vcn_to_lcn()
                 * returned error. Just zero that portion of the page and set
                 * the buffer uptodate.
                 */
@@ -279,15 +282,16 @@ handle_hole:
                bh->b_blocknr = -1UL;
                clear_buffer_mapped(bh);
 handle_zblock:
-               memset(kmap(page) + i * blocksize, 0, blocksize);
+               kaddr = kmap_atomic(page, KM_USER0);
+               memset(kaddr + i * blocksize, 0, blocksize);
                flush_dcache_page(page);
-               kunmap(page);
+               kunmap_atomic(kaddr, KM_USER0);
                set_buffer_uptodate(bh);
        } while (i++, iblock++, (bh = bh->b_this_page) != head);
 
        /* Release the lock if we took it. */
        if (rl)
-               up_read(&ni->run_list.lock);
+               up_read(&ni->runlist.lock);
 
        /* Check we have at least one buffer ready for i/o. */
        if (nr) {
@@ -343,8 +347,8 @@ int ntfs_readpage(struct file *file, struct page *page)
 {
        s64 attr_pos;
        ntfs_inode *ni, *base_ni;
-       char *addr;
-       attr_search_context *ctx;
+       u8 *kaddr;
+       ntfs_attr_search_ctx *ctx;
        MFT_RECORD *mrec;
        u32 attr_len;
        int err = 0;
@@ -362,6 +366,7 @@ int ntfs_readpage(struct file *file, struct page *page)
 
        ni = NTFS_I(page->mapping->host);
 
+       /* NInoNonResident() == NInoIndexAllocPresent() */
        if (NInoNonResident(ni)) {
                /*
                 * Only unnamed $DATA attributes can be compressed or
@@ -388,20 +393,19 @@ int ntfs_readpage(struct file *file, struct page *page)
 
        /* Map, pin, and lock the mft record. */
        mrec = map_mft_record(base_ni);
-       if (unlikely(IS_ERR(mrec))) {
+       if (IS_ERR(mrec)) {
                err = PTR_ERR(mrec);
                goto err_out;
        }
-       ctx = get_attr_search_ctx(base_ni, mrec);
+       ctx = ntfs_attr_get_search_ctx(base_ni, mrec);
        if (unlikely(!ctx)) {
                err = -ENOMEM;
                goto unm_err_out;
        }
-       if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len,
-                       IGNORE_CASE, 0, NULL, 0, ctx))) {
-               err = -ENOENT;
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err))
                goto put_unm_err_out;
-       }
 
        /* Starting position of the page within the attribute value. */
        attr_pos = page->index << PAGE_CACHE_SHIFT;
@@ -409,26 +413,26 @@ int ntfs_readpage(struct file *file, struct page *page)
        /* The total length of the attribute value. */
        attr_len = le32_to_cpu(ctx->attr->data.resident.value_length);
 
-       addr = kmap(page);
+       kaddr = kmap_atomic(page, KM_USER0);
        /* Copy over in bounds data, zeroing the remainder of the page. */
        if (attr_pos < attr_len) {
                u32 bytes = attr_len - attr_pos;
                if (bytes > PAGE_CACHE_SIZE)
                        bytes = PAGE_CACHE_SIZE;
                else if (bytes < PAGE_CACHE_SIZE)
-                       memset(addr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+                       memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
                /* Copy the data to the page. */
-               memcpy(addr, attr_pos + (char*)ctx->attr +
+               memcpy(kaddr, attr_pos + (char*)ctx->attr +
                                le16_to_cpu(
                                ctx->attr->data.resident.value_offset), bytes);
        } else
-               memset(addr, 0, PAGE_CACHE_SIZE);
+               memset(kaddr, 0, PAGE_CACHE_SIZE);
        flush_dcache_page(page);
-       kunmap(page);
+       kunmap_atomic(kaddr, KM_USER0);
 
        SetPageUptodate(page);
 put_unm_err_out:
-       put_attr_search_ctx(ctx);
+       ntfs_attr_put_search_ctx(ctx);
 unm_err_out:
        unmap_mft_record(base_ni);
 err_out:
@@ -440,6 +444,7 @@ err_out:
 
 /**
  * ntfs_write_block - write a @page to the backing store
+ * @wbc:       writeback control structure
  * @page:      page cache page to write out
  *
  * This function is for writing pages belonging to non-resident, non-mst
@@ -467,7 +472,7 @@ static int ntfs_write_block(struct writeback_control *wbc, struct page *page)
        struct inode *vi;
        ntfs_inode *ni;
        ntfs_volume *vol;
-       run_list_element *rl;
+       runlist_element *rl;
        struct buffer_head *bh, *head;
        unsigned int blocksize, vcn_ofs;
        int err;
@@ -478,8 +483,8 @@ static int ntfs_write_block(struct writeback_control *wbc, struct page *page)
        ni = NTFS_I(vi);
        vol = ni->vol;
 
-       ntfs_debug("Entering for inode %li, attribute type 0x%x, page index "
-                       "0x%lx.\n", vi->i_ino, ni->type, page->index);
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
+                       "0x%lx.", vi->i_ino, ni->type, page->index);
 
        BUG_ON(!NInoNonResident(ni));
        BUG_ON(NInoMstProtected(ni));
@@ -508,7 +513,7 @@ static int ntfs_write_block(struct writeback_control *wbc, struct page *page)
        /* NOTE: Different naming scheme to ntfs_read_block()! */
 
        /* The first block in the page. */
-       block = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
+       block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
 
        /* The first out of bounds block for the data size. */
        dblock = (vi->i_size + blocksize - 1) >> blocksize_bits;
@@ -625,14 +630,14 @@ static int ntfs_write_block(struct writeback_control *wbc, struct page *page)
                                vol->cluster_size_mask;
                if (!rl) {
 lock_retry_remap:
-                       down_read(&ni->run_list.lock);
-                       rl = ni->run_list.rl;
+                       down_read(&ni->runlist.lock);
+                       rl = ni->runlist.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);
+                       lcn = ntfs_vcn_to_lcn(rl, vcn);
                } else
                        lcn = (LCN)LCN_RL_NOT_MAPPED;
                /* Successful remap. */
@@ -653,22 +658,22 @@ lock_retry_remap:
                        err = -EOPNOTSUPP;
                        break;
                }
-               /* If first try and run list unmapped, map and retry. */
+               /* If first try and runlist unmapped, map and retry. */
                if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
                        is_retry = TRUE;
                        /*
-                        * Attempt to map run list, dropping lock for
+                        * Attempt to map runlist, dropping lock for
                         * the duration.
                         */
-                       up_read(&ni->run_list.lock);
-                       err = map_run_list(ni, vcn);
+                       up_read(&ni->runlist.lock);
+                       err = ntfs_map_runlist(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 "
+               ntfs_error(vol->sb, "ntfs_vcn_to_lcn(vcn = 0x%llx) failed "
                                "with error code 0x%llx%s.",
                                (unsigned long long)vcn,
                                (unsigned long long)-lcn,
@@ -681,7 +686,7 @@ lock_retry_remap:
 
        /* Release the lock if we took it. */
        if (rl)
-               up_read(&ni->run_list.lock);
+               up_read(&ni->runlist.lock);
 
        /* For the error case, need to reset bh to the beginning. */
        bh = head;
@@ -767,9 +772,240 @@ lock_retry_remap:
        return err;
 }
 
+static const char *ntfs_please_email = "Please email "
+               "linux-ntfs-dev@lists.sourceforge.net and say that you saw "
+               "this message.  Thank you.";
+
+/**
+ * ntfs_write_mst_block - write a @page to the backing store
+ * @wbc:       writeback control structure
+ * @page:      page cache page to write out
+ *
+ * This function is for writing pages belonging to non-resident, mst protected
+ * attributes to their backing store.  The only supported attribute is the
+ * index allocation attribute.  Both directory inodes and index inodes are
+ * supported.
+ *
+ * The page must remain locked for the duration of the write because we apply
+ * the mst fixups, write, and then undo the fixups, so if we were to unlock the
+ * page before undoing the fixups, any other user of the page will see the
+ * page contents as corrupt.
+ *
+ * Return 0 on success and -errno on error.
+ *
+ * Based on ntfs_write_block(), ntfs_mft_writepage(), and
+ * write_mft_record_nolock().
+ */
+static int ntfs_write_mst_block(struct writeback_control *wbc,
+               struct page *page)
+{
+       sector_t block, dblock, rec_block;
+       struct inode *vi = page->mapping->host;
+       ntfs_inode *ni = NTFS_I(vi);
+       ntfs_volume *vol = ni->vol;
+       u8 *kaddr;
+       unsigned int bh_size = 1 << vi->i_blkbits;
+       unsigned int rec_size;
+       struct buffer_head *bh, *head;
+       int max_bhs = PAGE_CACHE_SIZE / bh_size;
+       struct buffer_head *bhs[max_bhs];
+       int i, nr_recs, nr_bhs, bhs_per_rec, err;
+       unsigned char bh_size_bits;
+       BOOL rec_is_dirty;
+
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
+                       "0x%lx.", vi->i_ino, ni->type, page->index);
+       BUG_ON(!NInoNonResident(ni));
+       BUG_ON(!NInoMstProtected(ni));
+       BUG_ON(!(S_ISDIR(vi->i_mode) ||
+                       (NInoAttr(ni) && ni->type == AT_INDEX_ALLOCATION)));
+       BUG_ON(PageWriteback(page));
+       BUG_ON(!PageUptodate(page));
+       BUG_ON(!max_bhs);
+
+       /* Make sure we have mapped buffers. */
+       if (unlikely(!page_has_buffers(page))) {
+no_buffers_err_out:
+               ntfs_error(vol->sb, "Writing ntfs records without existing "
+                               "buffers is not implemented yet.  %s",
+                               ntfs_please_email);
+               err = -EOPNOTSUPP;
+               goto err_out;
+       }
+       bh = head = page_buffers(page);
+       if (unlikely(!bh))
+               goto no_buffers_err_out;
+
+       bh_size_bits = vi->i_blkbits;
+       rec_size = ni->itype.index.block_size;
+       nr_recs = PAGE_CACHE_SIZE / rec_size;
+       BUG_ON(!nr_recs);
+       bhs_per_rec = rec_size >> bh_size_bits;
+       BUG_ON(!bhs_per_rec);
+
+       /* The first block in the page. */
+       rec_block = block = (s64)page->index <<
+                       (PAGE_CACHE_SHIFT - bh_size_bits);
+
+       /* The first out of bounds block for the data size. */
+       dblock = (vi->i_size + bh_size - 1) >> bh_size_bits;
+
+       err = nr_bhs = 0;
+       /* Need this to silence a stupid gcc warning. */
+       rec_is_dirty = FALSE;
+       do {
+               if (unlikely(block >= dblock)) {
+                       /*
+                        * Mapped buffers outside i_size will occur, because
+                        * this page can be outside i_size when there is a
+                        * truncate in progress. The contents of such buffers
+                        * were zeroed by ntfs_writepage().
+                        *
+                        * FIXME: What about the small race window where
+                        * ntfs_writepage() has not done any clearing because
+                        * the page was within i_size but before we get here,
+                        * vmtruncate() modifies i_size?
+                        */
+                       clear_buffer_dirty(bh);
+                       continue;
+               }
+               if (rec_block == block) {
+                       /* This block is the first one in the record. */
+                       rec_block += rec_size >> bh_size_bits;
+                       if (!buffer_dirty(bh)) {
+                               /* Clean buffers are not written out. */
+                               rec_is_dirty = FALSE;
+                               continue;
+                       }
+                       rec_is_dirty = TRUE;
+               } else {
+                       /* This block is not the first one in the record. */
+                       if (!buffer_dirty(bh)) {
+                               /* Clean buffers are not written out. */
+                               BUG_ON(rec_is_dirty);
+                               continue;
+                       }
+                       BUG_ON(!rec_is_dirty);
+               }
+               /* Attempting to write outside the initialized size is a bug. */
+               BUG_ON(((block + 1) << bh_size_bits) > ni->initialized_size);
+               if (!buffer_mapped(bh)) {
+                       ntfs_error(vol->sb, "Writing ntfs records without "
+                                       "existing mapped buffers is not "
+                                       "implemented yet.  %s",
+                                       ntfs_please_email);
+                       clear_buffer_dirty(bh);
+                       err = -EOPNOTSUPP;
+                       goto cleanup_out;
+               }
+               if (!buffer_uptodate(bh)) {
+                       ntfs_error(vol->sb, "Writing ntfs records without "
+                                       "existing uptodate buffers is not "
+                                       "implemented yet.  %s",
+                                       ntfs_please_email);
+                       clear_buffer_dirty(bh);
+                       err = -EOPNOTSUPP;
+                       goto cleanup_out;
+               }
+               bhs[nr_bhs++] = bh;
+               BUG_ON(nr_bhs > max_bhs);
+       } while (block++, (bh = bh->b_this_page) != head);
+       /* If there were no dirty buffers, we are done. */
+       if (!nr_bhs)
+               goto done;
+       /* Apply the mst protection fixups. */
+       kaddr = page_address(page);
+       for (i = 0; i < nr_bhs; i++) {
+               if (!(i % bhs_per_rec)) {
+                       err = pre_write_mst_fixup((NTFS_RECORD*)(kaddr +
+                                       bh_offset(bhs[i])), rec_size);
+                       if (err) {
+                               ntfs_error(vol->sb, "Failed to apply mst "
+                                               "fixups (inode 0x%lx, "
+                                               "attribute type 0x%x, page "
+                                               "index 0x%lx)!  Umount and "
+                                               "run chkdsk.", vi->i_ino,
+                                               ni->type,
+                               page->index);
+                               nr_bhs = i;
+                               goto mst_cleanup_out;
+                       }
+               }
+       }
+       flush_dcache_page(page);
+       /* Lock buffers and start synchronous write i/o on them. */
+       for (i = 0; i < nr_bhs; i++) {
+               struct buffer_head *tbh = bhs[i];
+
+               if (unlikely(test_set_buffer_locked(tbh)))
+                       BUG();
+               if (unlikely(!test_clear_buffer_dirty(tbh))) {
+                       unlock_buffer(tbh);
+                       continue;
+               }
+               BUG_ON(!buffer_uptodate(tbh));
+               BUG_ON(!buffer_mapped(tbh));
+               get_bh(tbh);
+               tbh->b_end_io = end_buffer_write_sync;
+               submit_bh(WRITE, tbh);
+       }
+       /* Wait on i/o completion of buffers. */
+       for (i = 0; i < nr_bhs; i++) {
+               struct buffer_head *tbh = bhs[i];
+
+               wait_on_buffer(tbh);
+               if (unlikely(!buffer_uptodate(tbh))) {
+                       err = -EIO;
+                       /*
+                        * Set the buffer uptodate so the page & buffer states
+                        * don't become out of sync.
+                        */
+                       if (PageUptodate(page))
+                               set_buffer_uptodate(tbh);
+               }
+       }
+       /* Remove the mst protection fixups again. */
+       for (i = 0; i < nr_bhs; i++) {
+               if (!(i % bhs_per_rec))
+                       post_write_mst_fixup((NTFS_RECORD*)(kaddr +
+                                       bh_offset(bhs[i])));
+       }
+       flush_dcache_page(page);
+       if (unlikely(err)) {
+               /* I/O error during writing.  This is really bad! */
+               ntfs_error(vol->sb, "I/O error while writing ntfs record "
+                               "(inode 0x%lx, attribute type 0x%x, page "
+                               "index 0x%lx)!  Umount and run chkdsk.",
+                               vi->i_ino, ni->type, page->index);
+               goto err_out;
+       }
+done:
+       set_page_writeback(page);
+       unlock_page(page);
+       end_page_writeback(page);
+       if (!err)
+               ntfs_debug("Done.");
+       return err;
+mst_cleanup_out:
+       /* Remove the mst protection fixups again. */
+       for (i = 0; i < nr_bhs; i++) {
+               if (!(i % bhs_per_rec))
+                       post_write_mst_fixup((NTFS_RECORD*)(kaddr +
+                                       bh_offset(bhs[i])));
+       }
+cleanup_out:
+       /* Clean the buffers. */
+       for (i = 0; i < nr_bhs; i++)
+               clear_buffer_dirty(bhs[i]);
+err_out:
+       SetPageError(page);
+       goto done;
+}
+
 /**
  * ntfs_writepage - write a @page to the backing store
  * @page:      page cache page to write out
+ * @wbc:       writeback control structure
  *
  * For non-resident attributes, ntfs_writepage() writes the @page by calling
  * the ntfs version of the generic block_write_full_page() function,
@@ -778,9 +1014,8 @@ lock_retry_remap:
  *
  * For resident attributes, OTOH, ntfs_writepage() writes the @page by copying
  * the data to the mft record (which at this stage is most likely in memory).
- * Thus, in this case, I/O is synchronous, as even if the mft record is not
- * cached at this point in time, we need to wait for it to be read in before we
- * can do the copy.
+ * The mft record is then marked dirty and written out asynchronously via the
+ * vfs inode dirty code path.
  *
  * Note the caller clears the page dirty flag before calling ntfs_writepage().
  *
@@ -794,7 +1029,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
        struct inode *vi;
        ntfs_inode *ni, *base_ni;
        char *kaddr;
-       attr_search_context *ctx;
+       ntfs_attr_search_ctx *ctx;
        MFT_RECORD *m;
        u32 attr_len, bytes;
        int err;
@@ -813,6 +1048,7 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
 
        ni = NTFS_I(vi);
 
+       /* NInoNonResident() == NInoIndexAllocPresent() */
        if (NInoNonResident(ni)) {
                /*
                 * Only unnamed $DATA attributes can be compressed, encrypted,
@@ -844,7 +1080,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
                                return -EOPNOTSUPP;
                        }
                }
-
                /* We have to zero every time due to mmap-at-end-of-file. */
                if (page->index >= (vi->i_size >> PAGE_CACHE_SHIFT)) {
                        /* The page straddles i_size. */
@@ -854,16 +1089,9 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
                        flush_dcache_page(page);
                        kunmap_atomic(kaddr, KM_USER0);
                }
-
-               // TODO: Implement and remove this check.
-               if (NInoMstProtected(ni)) {
-                       unlock_page(page);
-                       ntfs_error(vi->i_sb, "Writing to MST protected "
-                                       "attributes is not supported yet. "
-                                       "Sorry.");
-                       return -EOPNOTSUPP;
-               }
-
+               /* Handle mst protected attributes. */
+               if (NInoMstProtected(ni))
+                       return ntfs_write_mst_block(wbc, page);
                /* Normal data stream. */
                return ntfs_write_block(wbc, page);
        }
@@ -875,16 +1103,6 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
        BUG_ON(page_has_buffers(page));
        BUG_ON(!PageUptodate(page));
 
-       // TODO: Consider using PageWriteback() + unlock_page() in 2.5 once the
-       // "VM fiddling has ended". Note, don't forget to replace all the
-       // unlock_page() calls further below with end_page_writeback() ones.
-       // FIXME: Make sure it is ok to SetPageError() on unlocked page under
-       // writeback before doing the change!
-#if 0
-       set_page_writeback(page);
-       unlock_page(page);
-#endif
-
        if (!NInoAttr(ni))
                base_ni = ni;
        else
@@ -892,22 +1110,21 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
 
        /* Map, pin, and lock the mft record. */
        m = map_mft_record(base_ni);
-       if (unlikely(IS_ERR(m))) {
+       if (IS_ERR(m)) {
                err = PTR_ERR(m);
                m = NULL;
                ctx = NULL;
                goto err_out;
        }
-       ctx = get_attr_search_ctx(base_ni, m);
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
        if (unlikely(!ctx)) {
                err = -ENOMEM;
                goto err_out;
        }
-       if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len,
-                       IGNORE_CASE, 0, NULL, 0, ctx))) {
-               err = -ENOENT;
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err))
                goto err_out;
-       }
 
        /* Starting position of the page within the attribute value. */
        attr_pos = page->index << PAGE_CACHE_SHIFT;
@@ -934,6 +1151,14 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
        if (unlikely(bytes > PAGE_CACHE_SIZE))
                bytes = PAGE_CACHE_SIZE;
 
+       /*
+        * Keep the VM happy.  This must be done otherwise the radix-tree tag
+        * PAGECACHE_TAG_DIRTY remains set even though the page is clean.
+        */
+       BUG_ON(PageWriteback(page));
+       set_page_writeback(page);
+       unlock_page(page);
+
        /*
         * Here, we don't need to zero the out of bounds area everytime because
         * the below memcpy() already takes care of the mmap-at-end-of-file
@@ -948,7 +1173,10 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
         * expose data to userspace/disk which should never have been exposed.
         *
         * FIXME: Ensure that i_size increases do the zeroing/overwriting and
-        * if we cannot guarantee that, then enable the zeroing below.
+        * if we cannot guarantee that, then enable the zeroing below.  If the
+        * zeroing below is enabled, we MUST move the unlock_page() from above
+        * to after the kunmap_atomic(), i.e. just before the
+        * end_page_writeback().
         */
 
        kaddr = kmap_atomic(page, KM_USER0);
@@ -966,13 +1194,12 @@ static int ntfs_writepage(struct page *page, struct writeback_control *wbc)
 #endif
        kunmap_atomic(kaddr, KM_USER0);
 
-       unlock_page(page);
+       end_page_writeback(page);
 
-       // TODO: Mark mft record dirty so it gets written back.
-       ntfs_error(vi->i_sb, "Writing to resident files is not supported yet. "
-                       "Wrote to memory only...");
+       /* Mark the mft record dirty, so it gets written back. */
+       mark_mft_record_dirty(ctx->ntfs_ino);
 
-       put_attr_search_ctx(ctx);
+       ntfs_attr_put_search_ctx(ctx);
        unmap_mft_record(base_ni);
        return 0;
 err_out:
@@ -992,7 +1219,7 @@ err_out:
        }
        unlock_page(page);
        if (ctx)
-               put_attr_search_ctx(ctx);
+               ntfs_attr_put_search_ctx(ctx);
        if (m)
                unmap_mft_record(base_ni);
        return err;
@@ -1011,7 +1238,7 @@ static int ntfs_prepare_nonresident_write(struct page *page,
        struct inode *vi;
        ntfs_inode *ni;
        ntfs_volume *vol;
-       run_list_element *rl;
+       runlist_element *rl;
        struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
        unsigned int vcn_ofs, block_start, block_end, blocksize;
        int err;
@@ -1022,7 +1249,7 @@ static int ntfs_prepare_nonresident_write(struct page *page,
        ni = NTFS_I(vi);
        vol = ni->vol;
 
-       ntfs_debug("Entering for inode %li, attribute type 0x%x, page index "
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
                        "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
                        page->index, from, to);
 
@@ -1043,7 +1270,7 @@ static int ntfs_prepare_nonresident_write(struct page *page,
                return -ENOMEM;
 
        /* The first block in the page. */
-       block = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
+       block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
 
        /*
         * The first out of bounds block for the allocated size. No need to
@@ -1168,14 +1395,14 @@ static int ntfs_prepare_nonresident_write(struct page *page,
                        is_retry = FALSE;
                        if (!rl) {
 lock_retry_remap:
-                               down_read(&ni->run_list.lock);
-                               rl = ni->run_list.rl;
+                               down_read(&ni->runlist.lock);
+                               rl = ni->runlist.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);
+                               lcn = ntfs_vcn_to_lcn(rl, vcn);
                        } else
                                lcn = (LCN)LCN_RL_NOT_MAPPED;
                        if (unlikely(lcn < 0)) {
@@ -1210,11 +1437,11 @@ lock_retry_remap:
                                                lcn == LCN_RL_NOT_MAPPED) {
                                        is_retry = TRUE;
                                        /*
-                                        * Attempt to map run list, dropping
+                                        * Attempt to map runlist, dropping
                                         * lock for the duration.
                                         */
-                                       up_read(&ni->run_list.lock);
-                                       err = map_run_list(ni, vcn);
+                                       up_read(&ni->runlist.lock);
+                                       err = ntfs_map_runlist(ni, vcn);
                                        if (likely(!err))
                                                goto lock_retry_remap;
                                        rl = NULL;
@@ -1224,9 +1451,9 @@ lock_retry_remap:
                                 * retrying.
                                 */
                                bh->b_blocknr = -1UL;
-                               ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%llx) "
-                                               "failed with error code "
-                                               "0x%llx%s.",
+                               ntfs_error(vol->sb, "ntfs_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 "
@@ -1301,7 +1528,7 @@ lock_retry_remap:
 
        /* Release the lock if we took it. */
        if (rl) {
-               up_read(&ni->run_list.lock);
+               up_read(&ni->runlist.lock);
                rl = NULL;
        }
 
@@ -1347,7 +1574,7 @@ err_out:
        if (is_retry)
                flush_dcache_page(page);
        if (rl)
-               up_read(&ni->run_list.lock);
+               up_read(&ni->runlist.lock);
        return err;
 }
 
@@ -1379,7 +1606,7 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
        struct inode *vi = page->mapping->host;
        ntfs_inode   *ni = NTFS_I(vi);
 
-       ntfs_debug("Entering for inode %li, attribute type 0x%x, page index "
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
                        "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
                        page->index, from, to);
 
@@ -1454,9 +1681,9 @@ static int ntfs_prepare_write(struct file *file, struct page *page,
         * We thus defer the uptodate bringing of the page region outside the
         * region written to to ntfs_commit_write(). The reason for doing this
         * is that we save one round of:
-        *      map_mft_record(), get_attr_search_ctx(), lookup_attr(),
-        *      kmap_atomic(), kunmap_atomic(), put_attr_search_ctx(),
-        *      unmap_mft_record().
+        *      map_mft_record(), ntfs_attr_get_search_ctx(),
+        *      ntfs_attr_lookup(), kmap_atomic(), kunmap_atomic(),
+        *      ntfs_attr_put_search_ctx(), unmap_mft_record().
         * Which is obviously a very worthwhile save.
         *
         * Thus we just return success now...
@@ -1487,7 +1714,7 @@ static int ntfs_commit_nonresident_write(struct page *page,
 
        vi = page->mapping->host;
 
-       ntfs_debug("Entering for inode %li, attribute type 0x%x, page index "
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
                        "0x%lx, from = %u, to = %u.", vi->i_ino,
                        NTFS_I(vi)->type, page->index, from, to);
 
@@ -1575,7 +1802,7 @@ static int ntfs_commit_write(struct file *file, struct page *page,
        struct inode *vi;
        ntfs_inode *ni, *base_ni;
        char *kaddr, *kattr;
-       attr_search_context *ctx;
+       ntfs_attr_search_ctx *ctx;
        MFT_RECORD *m;
        u32 attr_len, bytes;
        int err;
@@ -1583,7 +1810,7 @@ static int ntfs_commit_write(struct file *file, struct page *page,
        vi = page->mapping->host;
        ni = NTFS_I(vi);
 
-       ntfs_debug("Entering for inode %li, attribute type 0x%x, page index "
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
                        "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
                        page->index, from, to);
 
@@ -1656,22 +1883,21 @@ static int ntfs_commit_write(struct file *file, struct page *page,
 
        /* Map, pin, and lock the mft record. */
        m = map_mft_record(base_ni);
-       if (unlikely(IS_ERR(m))) {
+       if (IS_ERR(m)) {
                err = PTR_ERR(m);
                m = NULL;
                ctx = NULL;
                goto err_out;
        }
-       ctx = get_attr_search_ctx(base_ni, m);
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
        if (unlikely(!ctx)) {
                err = -ENOMEM;
                goto err_out;
        }
-       if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len,
-                       IGNORE_CASE, 0, NULL, 0, ctx))) {
-               err = -ENOENT;
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err))
                goto err_out;
-       }
 
        /* Starting position of the page within the attribute value. */
        attr_pos = page->index << PAGE_CACHE_SHIFT;
@@ -1734,11 +1960,10 @@ static int ntfs_commit_write(struct file *file, struct page *page,
        }
        kunmap_atomic(kaddr, KM_USER0);
 
-       // TODO: Mark mft record dirty so it gets written back.
-       ntfs_error(vi->i_sb, "Writing to resident files is not supported yet. "
-                       "Wrote to memory only...");
+       /* Mark the mft record dirty, so it gets written back. */
+       mark_mft_record_dirty(ctx->ntfs_ino);
 
-       put_attr_search_ctx(ctx);
+       ntfs_attr_put_search_ctx(ctx);
        unmap_mft_record(base_ni);
        ntfs_debug("Done.");
        return 0;
@@ -1765,7 +1990,7 @@ err_out:
                SetPageError(page);
        }
        if (ctx)
-               put_attr_search_ctx(ctx);
+               ntfs_attr_put_search_ctx(ctx);
        if (m)
                unmap_mft_record(base_ni);
        return err;
@@ -1785,6 +2010,21 @@ struct address_space_operations ntfs_aops = {
        .prepare_write  = ntfs_prepare_write,   /* Prepare page and buffers
                                                   ready to receive data. */
        .commit_write   = ntfs_commit_write,    /* Commit received data. */
-#endif
+#endif /* NTFS_RW */
 };
 
+/**
+ * ntfs_mst_aops - general address space operations for mst protecteed inodes
+ *                and attributes
+ */
+struct address_space_operations ntfs_mst_aops = {
+       .readpage       = ntfs_readpage,        /* Fill page with data. */
+       .sync_page      = block_sync_page,      /* Currently, just unplugs the
+                                                  disk request queue. */
+#ifdef NTFS_RW
+       .writepage      = ntfs_writepage,       /* Write dirty page to disk. */
+       .set_page_dirty = __set_page_dirty_nobuffers,   /* Set the page dirty
+                                                  without touching the buffers
+                                                  belonging to the page. */
+#endif /* NTFS_RW */
+};