NTFS: Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
authorAnton Altaparmakov <aia21@cantab.net>
Thu, 8 Sep 2005 21:00:33 +0000 (22:00 +0100)
committerAnton Altaparmakov <aia21@cantab.net>
Thu, 8 Sep 2005 21:00:33 +0000 (22:00 +0100)
      where a concurrent truncate has truncated the runlist under our feet.

Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
fs/ntfs/ChangeLog
fs/ntfs/aops.c

index 10fc238..7f24cb1 100644 (file)
@@ -84,6 +84,8 @@ ToDo/Notes:
        - Optimize fs/ntfs/aops.c::ntfs_write_block() by extending the page
          lock protection over the buffer submission for i/o which allows the
          removal of the get_bh()/put_bh() pairs for each buffer.
+       - Fix fs/ntfs/aops.c::ntfs_{read,write}_block() to handle the case
+         where a concurrent truncate has truncated the runlist under our feet.
 
 2.1.23 - Implement extension of resident files and make writing safe as well as
         many bug fixes, cleanups, and enhancements...
index 821dad7..59389a8 100644 (file)
@@ -204,6 +204,7 @@ static int ntfs_read_block(struct page *page)
        nr = i = 0;
        do {
                u8 *kaddr;
+               int err;
 
                if (unlikely(buffer_uptodate(bh)))
                        continue;
@@ -211,6 +212,7 @@ static int ntfs_read_block(struct page *page)
                        arr[nr++] = bh;
                        continue;
                }
+               err = 0;
                bh->b_bdev = vol->sb->s_bdev;
                /* Is the block within the allowed limits? */
                if (iblock < lblock) {
@@ -252,7 +254,6 @@ lock_retry_remap:
                                goto handle_hole;
                        /* If first try and runlist unmapped, map and retry. */
                        if (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
-                               int err;
                                is_retry = TRUE;
                                /*
                                 * Attempt to map runlist, dropping lock for
@@ -263,20 +264,30 @@ lock_retry_remap:
                                if (likely(!err))
                                        goto lock_retry_remap;
                                rl = NULL;
-                               lcn = err;
                        } else if (!rl)
                                up_read(&ni->runlist.lock);
+                       /*
+                        * If buffer is outside the runlist, treat it as a
+                        * hole.  This can happen due to concurrent truncate
+                        * for example.
+                        */
+                       if (err == -ENOENT || lcn == LCN_ENOENT) {
+                               err = 0;
+                               goto handle_hole;
+                       }
                        /* Hard error, zero out region. */
+                       if (!err)
+                               err = -EIO;
                        bh->b_blocknr = -1;
                        SetPageError(page);
                        ntfs_error(vol->sb, "Failed to read from inode 0x%lx, "
                                        "attribute type 0x%x, vcn 0x%llx, "
                                        "offset 0x%x because its location on "
                                        "disk could not be determined%s "
-                                       "(error code %lli).", ni->mft_no,
+                                       "(error code %i).", ni->mft_no,
                                        ni->type, (unsigned long long)vcn,
                                        vcn_ofs, is_retry ? " even after "
-                                       "retrying" : "", (long long)lcn);
+                                       "retrying" : "", err);
                }
                /*
                 * Either iblock was outside lblock limits or
@@ -289,9 +300,10 @@ handle_hole:
 handle_zblock:
                kaddr = kmap_atomic(page, KM_USER0);
                memset(kaddr + i * blocksize, 0, blocksize);
-               flush_dcache_page(page);
                kunmap_atomic(kaddr, KM_USER0);
-               set_buffer_uptodate(bh);
+               flush_dcache_page(page);
+               if (likely(!err))
+                       set_buffer_uptodate(bh);
        } while (i++, iblock++, (bh = bh->b_this_page) != head);
 
        /* Release the lock if we took it. */
@@ -711,20 +723,37 @@ lock_retry_remap:
                        if (likely(!err))
                                goto lock_retry_remap;
                        rl = NULL;
-                       lcn = err;
                } else if (!rl)
                        up_read(&ni->runlist.lock);
+               /*
+                * If buffer is outside the runlist, truncate has cut it out
+                * of the runlist.  Just clean and clear the buffer and set it
+                * uptodate so it can get discarded by the VM.
+                */
+               if (err == -ENOENT || lcn == LCN_ENOENT) {
+                       u8 *kaddr;
+
+                       bh->b_blocknr = -1;
+                       clear_buffer_dirty(bh);
+                       kaddr = kmap_atomic(page, KM_USER0);
+                       memset(kaddr + bh_offset(bh), 0, blocksize);
+                       kunmap_atomic(kaddr, KM_USER0);
+                       flush_dcache_page(page);
+                       set_buffer_uptodate(bh);
+                       err = 0;
+                       continue;
+               }
                /* Failed to map the buffer, even after retrying. */
+               if (!err)
+                       err = -EIO;
                bh->b_blocknr = -1;
                ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
                                "attribute type 0x%x, vcn 0x%llx, offset 0x%x "
                                "because its location on disk could not be "
-                               "determined%s (error code %lli).", ni->mft_no,
+                               "determined%s (error code %i).", ni->mft_no,
                                ni->type, (unsigned long long)vcn,
                                vcn_ofs, is_retry ? " even after "
-                               "retrying" : "", (long long)lcn);
-               if (!err)
-                       err = -EIO;
+                               "retrying" : "", err);
                break;
        } while (block++, (bh = bh->b_this_page) != head);