Commit b30de928 authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.0.12 - Initial cleanup of address space operations following 2.0.11 changes.

- Merge fs/ntfs/aops.c::end_buffer_read_mst_async() and
  fs/ntfs/aops.c::end_buffer_read_file_async() into one function
  fs/ntfs/aops.c::end_buffer_read_attr_async() using NInoMstProtected()
  to determine whether to apply mst fixups or not.
- Above change allows merging fs/ntfs/aops.c::ntfs_file_read_block()
  and fs/ntfs/aops.c::ntfs_mst_readpage() into one function
  fs/ntfs/aops.c::ntfs_attr_read_block(). Also, create a tiny wrapper
  fs/ntfs/aops.c::ntfs_mst_readpage() to transform the parameters from
  the VFS readpage function prototype to the ntfs_attr_read_block()
  function prototype.
parent 6bb39cd9
...@@ -247,6 +247,9 @@ ChangeLog ...@@ -247,6 +247,9 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
2.0.12:
- Internal cleanups in address space operations made possible by the
changes introduced in the previous release.
2.0.11: 2.0.11:
- Internal updates and cleanups introducing the first step towards - Internal updates and cleanups introducing the first step towards
fake inode based attribute i/o. fake inode based attribute i/o.
......
...@@ -6,7 +6,7 @@ ToDo: ...@@ -6,7 +6,7 @@ ToDo:
user open()s a file with i_size > s_maxbytes? Should read_inode() user open()s a file with i_size > s_maxbytes? Should read_inode()
truncate the visible i_size? Will the user just get -E2BIG (or truncate the visible i_size? Will the user just get -E2BIG (or
whatever) on open()? Or will (s)he be able to open() but lseek() and whatever) on open()? Or will (s)he be able to open() but lseek() and
read() will fail when s_maxbytes is reached? -> Investigate this! read() will fail when s_maxbytes is reached? -> Investigate this.
- Implement/allow non-resident index bitmaps in dir.c::ntfs_readdir() - Implement/allow non-resident index bitmaps in dir.c::ntfs_readdir()
and then also consider initialized_size w.r.t. the bitmaps, etc. and then also consider initialized_size w.r.t. the bitmaps, etc.
- vcn_to_lcn() should somehow return the correct pointer within the - vcn_to_lcn() should somehow return the correct pointer within the
...@@ -17,14 +17,28 @@ ToDo: ...@@ -17,14 +17,28 @@ ToDo:
- Consider if ntfs_file_read_compressed_block() shouldn't be coping - Consider if ntfs_file_read_compressed_block() shouldn't be coping
with initialized_size < data_size. I don't think it can happen but with initialized_size < data_size. I don't think it can happen but
it requires more careful consideration. it requires more careful consideration.
- CLEANUP: Modularise and reuse code in aops.c. At the moment we have - CLEANUP: At the moment we have two copies of almost identical
several copies of almost identicall functions and the functions are functions in aops.c, can merge them once fake inode address space
quite big. Modularising them a bit, e.g. a-la get_block(), will make based attribute i/o is further developed.
them cleaner and make code reuse easier. - CLEANUP: Modularising code in aops.c a bit, e.g. a-la get_block(),
will be cleaner and make code reuse easier.
- Enable NFS exporting of NTFS. - Enable NFS exporting of NTFS.
- Use iget5_locked() and friends instead of conventional iget(). - Use iget5_locked() and friends instead of conventional iget().
- Use fake inodes for address space i/o. - Use fake inodes for address space i/o.
2.0.12 - Initial cleanup of address space operations following 2.0.11 changes.
- Merge fs/ntfs/aops.c::end_buffer_read_mst_async() and
fs/ntfs/aops.c::end_buffer_read_file_async() into one function
fs/ntfs/aops.c::end_buffer_read_attr_async() using NInoMstProtected()
to determine whether to apply mst fixups or not.
- Above change allows merging fs/ntfs/aops.c::ntfs_file_read_block()
and fs/ntfs/aops.c::ntfs_mst_readpage() into one function
fs/ntfs/aops.c::ntfs_attr_read_block(). Also, create a tiny wrapper
fs/ntfs/aops.c::ntfs_mst_readpage() to transform the parameters from
the VFS readpage function prototype to the ntfs_attr_read_block()
function prototype.
2.0.11 - Initial preparations for fake inode based attribute i/o. 2.0.11 - Initial preparations for fake inode based attribute i/o.
- Move definition of ntfs_inode_state_bits to fs/ntfs/inode.h and - Move definition of ntfs_inode_state_bits to fs/ntfs/inode.h and
......
...@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o ...@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o
ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \ ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \
mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.11\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.12\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* Part of the Linux-NTFS project. * Part of the Linux-NTFS project.
* *
* Copyright (c) 2001,2002 Anton Altaparmakov. * Copyright (c) 2001,2002 Anton Altaparmakov.
* Copyright (C) 2002 Richard Russon. * Copyright (c) 2002 Richard Russon.
* *
* This program/include file is free software; you can redistribute it and/or * This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published * modify it under the terms of the GNU General Public License as published
...@@ -30,31 +30,43 @@ ...@@ -30,31 +30,43 @@
#include "ntfs.h" #include "ntfs.h"
/** /**
* end_buffer_read_file_async - * end_buffer_read_attr_async - async io completion for reading attributes
* @bh: buffer head on which io is completed
* @uptodate: whether @bh is now uptodate or not
* *
* Async io completion handler for accessing files. Adapted from * Asynchronous I/O completion handler for reading pages belonging to the
* end_buffer_read_mst_async(). * attribute address space of an inode. The inodes can either be files or
* directories or they can be fake inodes describing some attribute.
*
* If NInoMstProtected(), perform the post read mst fixups when all IO on the
* page has been completed and mark the page uptodate or set the error bit on
* the page. To determine the size of the records that need fixing up, we cheat
* a little bit by setting the index_block_size in ntfs_inode to the ntfs
* record size, and index_block_size_bits, to the log(base 2) of the ntfs
* record size.
*/ */
static void end_buffer_read_file_async(struct buffer_head *bh, int uptodate) static void end_buffer_read_attr_async(struct buffer_head *bh, int uptodate)
{ {
static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED; static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
unsigned long flags; unsigned long flags;
struct buffer_head *tmp; struct buffer_head *tmp;
struct page *page; struct page *page;
ntfs_inode *ni;
if (uptodate) if (likely(uptodate))
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
else else
clear_buffer_uptodate(bh); clear_buffer_uptodate(bh);
page = bh->b_page; page = bh->b_page;
ni = NTFS_I(page->mapping->host);
if (likely(uptodate)) { if (likely(uptodate)) {
s64 file_ofs; s64 file_ofs;
ntfs_inode *ni = NTFS_I(page->mapping->host);
file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh); file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
/* Check for the current buffer head overflowing. */
if (file_ofs + bh->b_size > ni->initialized_size) { if (file_ofs + bh->b_size > ni->initialized_size) {
char *addr; char *addr;
int ofs = 0; int ofs = 0;
...@@ -82,10 +94,47 @@ static void end_buffer_read_file_async(struct buffer_head *bh, int uptodate) ...@@ -82,10 +94,47 @@ static void end_buffer_read_file_async(struct buffer_head *bh, int uptodate)
SetPageError(page); SetPageError(page);
tmp = tmp->b_this_page; tmp = tmp->b_this_page;
} }
spin_unlock_irqrestore(&page_uptodate_lock, flags); spin_unlock_irqrestore(&page_uptodate_lock, flags);
if (!PageError(page)) /*
SetPageUptodate(page); * If none of the buffers had errors then we can set the page uptodate,
* but we first have to perform the post read mst fixups, if the
* attribute is mst protected, i.e. if NInoMstProteced(ni) is true.
*/
if (!NInoMstProtected(ni)) {
if (likely(!PageError(page)))
SetPageUptodate(page);
unlock_page(page);
return;
} else {
char *addr;
unsigned int i, recs, nr_err;
u32 rec_size;
rec_size = ni->_IDM(index_block_size);
recs = PAGE_CACHE_SIZE / rec_size;
addr = kmap_atomic(page, KM_BIO_SRC_IRQ);
for (i = nr_err = 0; i < recs; i++) {
if (likely(!post_read_mst_fixup((NTFS_RECORD*)(addr +
i * rec_size), rec_size)))
continue;
nr_err++;
ntfs_error(ni->vol->sb, "post_read_mst_fixup() failed, "
"corrupt %s record 0x%Lx. Run chkdsk.",
ni->mft_no ? "index" : "mft",
(long long)(((s64)page->index <<
PAGE_CACHE_SHIFT >>
ni->_IDM(index_block_size_bits)) + i));
}
flush_dcache_page(page);
kunmap_atomic(addr, KM_BIO_SRC_IRQ);
if (likely(!nr_err && recs))
SetPageUptodate(page);
else {
ntfs_error(ni->vol->sb, "Setting page error, index "
"0x%lx.", page->index);
SetPageError(page);
}
}
unlock_page(page); unlock_page(page);
return; return;
still_busy: still_busy:
...@@ -94,11 +143,20 @@ static void end_buffer_read_file_async(struct buffer_head *bh, int uptodate) ...@@ -94,11 +143,20 @@ static void end_buffer_read_file_async(struct buffer_head *bh, int uptodate)
} }
/** /**
* ntfs_file_read_block - * ntfs_attr_read_block - fill a @page of an address space with data
* @page: page cache page to fill with data
* *
* NTFS version of block_read_full_page(). Adapted from ntfs_mst_readpage(). * Fill the page @page of the address space belonging to the @page->host inode.
* We read each buffer asynchronously and when all buffers are read in, our io
* completion handler end_buffer_read_attr_async(), if required, automatically
* applies the mst fixups to the page before finally marking it uptodate and
* unlocking it.
*
* Return 0 on success and -errno on error.
*
* Contains an adapted version of fs/buffer.c::block_read_full_page().
*/ */
static int ntfs_file_read_block(struct page *page) static int ntfs_attr_read_block(struct page *page)
{ {
VCN vcn; VCN vcn;
LCN lcn; LCN lcn;
...@@ -119,7 +177,7 @@ static int ntfs_file_read_block(struct page *page) ...@@ -119,7 +177,7 @@ static int ntfs_file_read_block(struct page *page)
if (!page_has_buffers(page)) if (!page_has_buffers(page))
create_empty_buffers(page, blocksize, 0); create_empty_buffers(page, blocksize, 0);
bh = head = page_buffers(page); bh = head = page_buffers(page);
if (!bh) if (unlikely(!bh))
return -ENOMEM; return -ENOMEM;
blocks = PAGE_CACHE_SIZE >> blocksize_bits; blocks = PAGE_CACHE_SIZE >> blocksize_bits;
...@@ -128,11 +186,9 @@ static int ntfs_file_read_block(struct page *page) ...@@ -128,11 +186,9 @@ static int ntfs_file_read_block(struct page *page)
zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits; zblock = (ni->initialized_size + blocksize - 1) >> blocksize_bits;
#ifdef DEBUG #ifdef DEBUG
if (unlikely(!ni->mft_no)) { if (unlikely(!ni->run_list.rl && !ni->mft_no))
ntfs_error(vol->sb, "NTFS: Attempt to access $MFT! This is a " panic("NTFS: $MFT/$DATA run list has been unmapped! This is a "
"very serious bug! Denying access..."); "very serious bug! Cannot continue...");
return -EACCES;
}
#endif #endif
/* Loop through all the buffers in the page. */ /* Loop through all the buffers in the page. */
...@@ -211,7 +267,7 @@ static int ntfs_file_read_block(struct page *page) ...@@ -211,7 +267,7 @@ static int ntfs_file_read_block(struct page *page)
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
struct buffer_head *tbh = arr[i]; struct buffer_head *tbh = arr[i];
lock_buffer(tbh); lock_buffer(tbh);
tbh->b_end_io = end_buffer_read_file_async; tbh->b_end_io = end_buffer_read_attr_async;
set_buffer_async_read(tbh); set_buffer_async_read(tbh);
} }
/* Finally, start i/o on the buffers. */ /* Finally, start i/o on the buffers. */
...@@ -220,7 +276,7 @@ static int ntfs_file_read_block(struct page *page) ...@@ -220,7 +276,7 @@ static int ntfs_file_read_block(struct page *page)
return 0; return 0;
} }
/* No i/o was scheduled on any of the buffers. */ /* No i/o was scheduled on any of the buffers. */
if (!PageError(page)) if (likely(!PageError(page)))
SetPageUptodate(page); SetPageUptodate(page);
else /* Signal synchronous i/o error. */ else /* Signal synchronous i/o error. */
nr = -EIO; nr = -EIO;
...@@ -234,17 +290,17 @@ static int ntfs_file_read_block(struct page *page) ...@@ -234,17 +290,17 @@ static int ntfs_file_read_block(struct page *page)
* @page: page cache page to fill with data * @page: page cache page to fill with data
* *
* For non-resident attributes, ntfs_file_readpage() fills the @page of the open * For non-resident attributes, ntfs_file_readpage() fills the @page of the open
* file @file by calling the generic block_read_full_page() function provided by * file @file by calling the ntfs version of the generic block_read_full_page()
* the kernel which in turn invokes our ntfs_file_get_block() callback in order * function provided by the kernel, ntfs_attr_read_block(), which in turn
* to create and read in the buffers associated with the page asynchronously. * creates and reads in the buffers associated with the page asynchronously.
* *
* For resident attributes, OTOH, ntfs_file_readpage() fills @page by copying * For resident attributes, OTOH, ntfs_file_readpage() fills @page by copying
* the data from the mft record (which at this stage is most likely in memory) * the data from the mft record (which at this stage is most likely in memory)
* and fills the remainder with zeroes. Thus, in this case I/O is synchronous, * and fills the remainder with zeroes. 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 * 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. * wait for it to be read in before we can do the copy.
* *
* Return zero on success or -errno on error. * Return 0 on success or -errno on error.
*/ */
static int ntfs_file_readpage(struct file *file, struct page *page) static int ntfs_file_readpage(struct file *file, struct page *page)
{ {
...@@ -256,43 +312,43 @@ static int ntfs_file_readpage(struct file *file, struct page *page) ...@@ -256,43 +312,43 @@ static int ntfs_file_readpage(struct file *file, struct page *page)
u32 attr_len; u32 attr_len;
int err = 0; int err = 0;
if (!PageLocked(page)) if (unlikely(!PageLocked(page)))
PAGE_BUG(page); PAGE_BUG(page);
ni = NTFS_I(page->mapping->host); ni = NTFS_I(page->mapping->host);
/* Is the unnamed $DATA attribute resident? */ /* Is the unnamed $DATA attribute resident? */
if (test_bit(NI_NonResident, &ni->state)) { if (NInoNonResident(ni)) {
/* Attribute is not resident. */ /* Attribute is not resident. */
/* If the file is encrypted, we deny access, just like NT4. */ /* If the file is encrypted, we deny access, just like NT4. */
if (test_bit(NI_Encrypted, &ni->state)) { if (NInoEncrypted(ni)) {
err = -EACCES; err = -EACCES;
goto unl_err_out; goto unl_err_out;
} }
/* Compressed data stream. Handled in compress.c. */ /* Compressed data stream. Handled in compress.c. */
if (test_bit(NI_Compressed, &ni->state)) if (NInoCompressed(ni))
return ntfs_file_read_compressed_block(page); return ntfs_file_read_compressed_block(page);
/* Normal data stream. */ /* Normal data stream. */
return ntfs_file_read_block(page); return ntfs_attr_read_block(page);
} }
/* Attribute is resident, implying it is not compressed or encrypted. */ /* Attribute is resident, implying it is not compressed or encrypted. */
/* Map, pin and lock the mft record for reading. */ /* Map, pin and lock the mft record for reading. */
mrec = map_mft_record(READ, ni); mrec = map_mft_record(READ, ni);
if (IS_ERR(mrec)) { if (unlikely(IS_ERR(mrec))) {
err = PTR_ERR(mrec); err = PTR_ERR(mrec);
goto unl_err_out; goto unl_err_out;
} }
ctx = get_attr_search_ctx(ni, mrec); ctx = get_attr_search_ctx(ni, mrec);
if (!ctx) { if (unlikely(!ctx)) {
err = -ENOMEM; err = -ENOMEM;
goto unm_unl_err_out; goto unm_unl_err_out;
} }
/* Find the data attribute in the mft record. */ /* Find the data attribute in the mft record. */
if (!lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx)) { if (unlikely(!lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx))) {
err = -ENOENT; err = -ENOENT;
goto put_unm_unl_err_out; goto put_unm_unl_err_out;
} }
...@@ -330,6 +386,25 @@ static int ntfs_file_readpage(struct file *file, struct page *page) ...@@ -330,6 +386,25 @@ static int ntfs_file_readpage(struct file *file, struct page *page)
return err; return err;
} }
/**
* ntfs_mst_readpage - fill a @page of the mft or a directory with data
* @file: open file/directory to which the @page belongs or NULL
* @page: page cache page to fill with data
*
* Readpage method for the VFS address space operations of directory inodes
* and the $MFT/$DATA attribute.
*
* We just call ntfs_attr_read_block() here, in fact we only need this wrapper
* because of the difference in function parameters.
*/
int ntfs_mst_readpage(struct file *file, struct page *page)
{
if (unlikely(!PageLocked(page)))
PAGE_BUG(page);
return ntfs_attr_read_block(page);
}
/** /**
* end_buffer_read_mftbmp_async - * end_buffer_read_mftbmp_async -
* *
...@@ -343,7 +418,7 @@ static void end_buffer_read_mftbmp_async(struct buffer_head *bh, int uptodate) ...@@ -343,7 +418,7 @@ static void end_buffer_read_mftbmp_async(struct buffer_head *bh, int uptodate)
struct buffer_head *tmp; struct buffer_head *tmp;
struct page *page; struct page *page;
if (uptodate) if (likely(uptodate))
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
else else
clear_buffer_uptodate(bh); clear_buffer_uptodate(bh);
...@@ -386,7 +461,7 @@ static void end_buffer_read_mftbmp_async(struct buffer_head *bh, int uptodate) ...@@ -386,7 +461,7 @@ static void end_buffer_read_mftbmp_async(struct buffer_head *bh, int uptodate)
} }
spin_unlock_irqrestore(&page_uptodate_lock, flags); spin_unlock_irqrestore(&page_uptodate_lock, flags);
if (!PageError(page)) if (likely(!PageError(page)))
SetPageUptodate(page); SetPageUptodate(page);
unlock_page(page); unlock_page(page);
return; return;
...@@ -410,7 +485,7 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page) ...@@ -410,7 +485,7 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
int nr, i; int nr, i;
unsigned char blocksize_bits; unsigned char blocksize_bits;
if (!PageLocked(page)) if (unlikely(!PageLocked(page)))
PAGE_BUG(page); PAGE_BUG(page);
blocksize = vol->sb->s_blocksize; blocksize = vol->sb->s_blocksize;
...@@ -419,7 +494,7 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page) ...@@ -419,7 +494,7 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
if (!page_has_buffers(page)) if (!page_has_buffers(page))
create_empty_buffers(page, blocksize, 0); create_empty_buffers(page, blocksize, 0);
bh = head = page_buffers(page); bh = head = page_buffers(page);
if (!bh) if (unlikely(!bh))
return -ENOMEM; return -ENOMEM;
blocks = PAGE_CACHE_SIZE >> blocksize_bits; blocks = PAGE_CACHE_SIZE >> blocksize_bits;
...@@ -503,264 +578,7 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page) ...@@ -503,264 +578,7 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
return 0; return 0;
} }
/* No i/o was scheduled on any of the buffers. */ /* No i/o was scheduled on any of the buffers. */
if (!PageError(page)) if (likely(!PageError(page)))
SetPageUptodate(page);
else /* Signal synchronous i/o error. */
nr = -EIO;
unlock_page(page);
return nr;
}
/**
* end_buffer_read_mst_async - async io completion for reading index records
* @bh: buffer head on which io is completed
* @uptodate: whether @bh is now uptodate or not
*
* Asynchronous I/O completion handler for reading pages belonging to the
* index allocation attribute address space of directory inodes.
*
* Perform the post read mst fixups when all IO on the page has been completed
* and marks the page uptodate or sets the error bit on the page.
*
* Adapted from fs/buffer.c.
*
* NOTE: We use this function as async io completion handler for reading pages
* belonging to the mft data attribute address space, too as this saves
* duplicating an almost identical function. We do this by cheating a little
* bit in setting the index_block_size in the mft ntfs_inode to the mft record
* size of the volume (vol->mft_record_size), and index_block_size_bits to
* mft_record_size_bits, respectively.
*/
static void end_buffer_read_mst_async(struct buffer_head *bh, int uptodate)
{
static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
unsigned long flags;
struct buffer_head *tmp;
struct page *page;
ntfs_inode *ni;
if (uptodate)
set_buffer_uptodate(bh);
else
clear_buffer_uptodate(bh);
page = bh->b_page;
ni = NTFS_I(page->mapping->host);
if (likely(uptodate)) {
s64 file_ofs;
file_ofs = (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;
int ofs = 0;
if (file_ofs < ni->initialized_size)
ofs = ni->initialized_size - file_ofs;
addr = kmap_atomic(page, KM_BIO_SRC_IRQ);
memset(addr + bh_offset(bh) + ofs, 0, bh->b_size - ofs);
flush_dcache_page(page);
kunmap_atomic(addr, KM_BIO_SRC_IRQ);
}
} else
SetPageError(page);
spin_lock_irqsave(&page_uptodate_lock, flags);
clear_buffer_async_read(bh);
unlock_buffer(bh);
tmp = bh->b_this_page;
while (tmp != bh) {
if (buffer_locked(tmp)) {
if (buffer_async_read(tmp))
goto still_busy;
} else if (!buffer_uptodate(tmp))
SetPageError(page);
tmp = tmp->b_this_page;
}
spin_unlock_irqrestore(&page_uptodate_lock, flags);
/*
* If none of the buffers had errors then we can set the page uptodate,
* but we first have to perform the post read mst fixups.
*/
if (!PageError(page)) {
char *addr;
unsigned int i, recs, nr_err = 0;
u32 rec_size;
rec_size = ni->_IDM(index_block_size);
recs = PAGE_CACHE_SIZE / rec_size;
addr = kmap_atomic(page, KM_BIO_SRC_IRQ);
for (i = 0; i < recs; i++) {
if (!post_read_mst_fixup((NTFS_RECORD*)(addr +
i * rec_size), rec_size))
continue;
nr_err++;
ntfs_error(ni->vol->sb, "post_read_mst_fixup() failed, "
"corrupt %s record 0x%Lx. Run chkdsk.",
ni->mft_no ? "index" : "mft",
(long long)((page->index <<
PAGE_CACHE_SHIFT >>
ni->_IDM(index_block_size_bits)) + i));
}
flush_dcache_page(page);
kunmap_atomic(addr, KM_BIO_SRC_IRQ);
if (likely(!nr_err && recs))
SetPageUptodate(page);
else {
ntfs_error(ni->vol->sb, "Setting page error, index "
"0x%lx.", page->index);
SetPageError(page);
}
}
unlock_page(page);
return;
still_busy:
spin_unlock_irqrestore(&page_uptodate_lock, flags);
return;
}
/**
* ntfs_mst_readpage - fill a @page of the mft or a directory with data
* @file: open file/directory to which the page @page belongs or NULL
* @page: page cache page to fill with data
*
* Readpage method for the VFS address space operations.
*
* Fill the page @page of the $MFT or the open directory @dir. We read each
* buffer asynchronously and when all buffers are read in our io completion
* handler end_buffer_read_mst_async() automatically applies the mst fixups to
* the page before finally marking it uptodate and unlocking it.
*
* Contains an adapted version of fs/buffer.c::block_read_full_page().
*/
int ntfs_mst_readpage(struct file *dir, struct page *page)
{
VCN vcn;
LCN lcn;
ntfs_inode *ni;
ntfs_volume *vol;
struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
sector_t iblock, lblock, zblock;
unsigned int blocksize, blocks, vcn_ofs;
int i, nr;
unsigned char blocksize_bits;
if (!PageLocked(page))
PAGE_BUG(page);
ni = NTFS_I(page->mapping->host);
vol = ni->vol;
blocksize_bits = VFS_I(ni)->i_blkbits;
blocksize = 1 << blocksize_bits;
if (!page_has_buffers(page))
create_empty_buffers(page, blocksize, 0);
bh = head = page_buffers(page);
if (!bh)
return -ENOMEM;
blocks = PAGE_CACHE_SIZE >> blocksize_bits;
iblock = 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))
panic("NTFS: $MFT/$DATA run list has been unmapped! This is a "
"very serious bug! Cannot continue...");
#endif
/* Loop through all the buffers in the page. */
nr = i = 0;
do {
if (unlikely(buffer_uptodate(bh)))
continue;
if (unlikely(buffer_mapped(bh))) {
arr[nr++] = bh;
continue;
}
bh->b_bdev = vol->sb->s_bdev;
/* Is the block within the allowed limits? */
if (iblock < lblock) {
BOOL is_retry = FALSE;
/* Convert iblock into corresponding vcn and offset. */
vcn = (VCN)iblock << blocksize_bits >>
vol->cluster_size_bits;
vcn_ofs = ((VCN)iblock << blocksize_bits) &
vol->cluster_size_mask;
retry_remap:
/* Convert the vcn to the corresponding lcn. */
down_read(&ni->run_list.lock);
lcn = vcn_to_lcn(ni->run_list.rl, vcn);
up_read(&ni->run_list.lock);
/* Successful remap. */
if (lcn >= 0) {
/* Setup buffer head to correct block. */
bh->b_blocknr = ((lcn << vol->cluster_size_bits)
+ vcn_ofs) >> blocksize_bits;
set_buffer_mapped(bh);
/* Only read initialized data blocks. */
if (iblock < zblock) {
arr[nr++] = bh;
continue;
}
/* Fully non-initialized data block, zero it. */
goto handle_zblock;
}
/* 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 (!is_retry && lcn == LCN_RL_NOT_MAPPED) {
is_retry = TRUE;
if (!map_run_list(ni, vcn))
goto retry_remap;
}
/* Hard error, zero out region. */
SetPageError(page);
ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%Lx) failed "
"with error code 0x%Lx%s.",
(long long)vcn, (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()
* returned error. Just zero that portion of the page and set
* the buffer uptodate.
*/
handle_hole:
bh->b_blocknr = -1UL;
clear_buffer_mapped(bh);
handle_zblock:
memset(kmap(page) + i * blocksize, 0, blocksize);
flush_dcache_page(page);
kunmap(page);
set_buffer_uptodate(bh);
} while (i++, iblock++, (bh = bh->b_this_page) != head);
/* Check we have at least one buffer ready for i/o. */
if (nr) {
/* Lock the buffers. */
for (i = 0; i < nr; i++) {
struct buffer_head *tbh = arr[i];
lock_buffer(tbh);
tbh->b_end_io = end_buffer_read_mst_async;
set_buffer_async_read(tbh);
}
/* Finally, start i/o on the buffers. */
for (i = 0; i < nr; i++)
submit_bh(READ, arr[i]);
return 0;
}
/* No i/o was scheduled on any of the buffers. */
if (!PageError(page))
SetPageUptodate(page); SetPageUptodate(page);
else /* Signal synchronous i/o error. */ else /* Signal synchronous i/o error. */
nr = -EIO; nr = -EIO;
......
...@@ -102,7 +102,7 @@ extern int ntfs_mst_readpage(struct file *, struct page *); ...@@ -102,7 +102,7 @@ extern int ntfs_mst_readpage(struct file *, struct page *);
* ntfs_mft_aops - address space operations for access to $MFT * ntfs_mft_aops - address space operations for access to $MFT
* *
* Address space operations for access to $MFT. This allows us to simply use * Address space operations for access to $MFT. This allows us to simply use
* read_cache_page() in map_mft_record(). * ntfs_map_page() in map_mft_record_page().
*/ */
struct address_space_operations ntfs_mft_aops = { struct address_space_operations ntfs_mft_aops = {
writepage: NULL, /* Write dirty page to disk. */ writepage: NULL, /* Write dirty page to disk. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment