Commit 600a1382 authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.0.16 - Convert access to $MFT/$BITMAP to attribute inode API.

- Fix a stupid bug introduced in 2.0.15 where we were unmapping the
  wrong inode in fs/ntfs/inode.c::ntfs_attr_iget().
- Convert $MFT/$BITMAP access to attribute inode API and remove all
  remnants of the ugly mftbmp address space and operations hack. This
  means we finally have only one readpage function as well as only one
  async io completion handler. Yey! The mft bitmap is now just an
  attribute inode and is accessed from vol->mftbmp_ino just as if it
  were a normal file. Fake inodes rule. (-:
parent db05cffc
...@@ -247,6 +247,10 @@ ChangeLog ...@@ -247,6 +247,10 @@ 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.16:
- Fix stupid bug introduced in 2.0.15 in new attribute inode API.
- Big internal cleanup replacing the mftbmp access hacks by using the
new attribute inode API instead.
2.0.15: 2.0.15:
- Bug fix in parsing of remount options. - Bug fix in parsing of remount options.
- Internal changes implementing attribute (fake) inodes allowing all - Internal changes implementing attribute (fake) inodes allowing all
......
...@@ -17,9 +17,6 @@ ToDo: ...@@ -17,9 +17,6 @@ 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: At the moment we have two copies of almost identical
functions in aops.c, can merge them once fake inode address space
based attribute i/o is further developed.
- CLEANUP: Modularising code in aops.c a bit, e.g. a-la get_block(), - CLEANUP: Modularising code in aops.c a bit, e.g. a-la get_block(),
will be cleaner and make code reuse easier. will be cleaner and make code reuse easier.
- Modify ntfs_read_locked_inode() to return an error code and update - Modify ntfs_read_locked_inode() to return an error code and update
...@@ -27,6 +24,17 @@ ToDo: ...@@ -27,6 +24,17 @@ ToDo:
using -EIO. using -EIO.
- Enable NFS exporting of NTFS. - Enable NFS exporting of NTFS.
2.0.16 - Convert access to $MFT/$BITMAP to attribute inode API.
- Fix a stupid bug introduced in 2.0.15 where we were unmapping the
wrong inode in fs/ntfs/inode.c::ntfs_attr_iget().
- Convert $MFT/$BITMAP access to attribute inode API and remove all
remnants of the ugly mftbmp address space and operations hack. This
means we finally have only one readpage function as well as only one
async io completion handler. Yey! The mft bitmap is now just an
attribute inode and is accessed from vol->mftbmp_ino just as if it
were a normal file. Fake inodes rule. (-:
2.0.15 - Fake inodes based attribute i/o via the pagecache, fixes and cleanups. 2.0.15 - Fake inodes based attribute i/o via the pagecache, fixes and cleanups.
- Fix silly bug in fs/ntfs/super.c::parse_options() which was causing - Fix silly bug in fs/ntfs/super.c::parse_options() which was causing
......
...@@ -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.15\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.16\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
......
...@@ -391,187 +391,6 @@ int ntfs_readpage(struct file *file, struct page *page) ...@@ -391,187 +391,6 @@ int ntfs_readpage(struct file *file, struct page *page)
return err; return err;
} }
/**
* end_buffer_read_mftbmp_async -
*
* Async io completion handler for accessing mft bitmap. Adapted from
* end_buffer_read_mst_async().
*/
static void end_buffer_read_mftbmp_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;
if (likely(uptodate))
set_buffer_uptodate(bh);
else
clear_buffer_uptodate(bh);
page = bh->b_page;
if (likely(uptodate)) {
s64 file_ofs;
/* Host is the ntfs volume. Our mft bitmap access kludge... */
ntfs_volume *vol = (ntfs_volume*)page->mapping->host;
file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
if (file_ofs + bh->b_size > vol->mftbmp_initialized_size) {
char *addr;
int ofs = 0;
if (file_ofs < vol->mftbmp_initialized_size)
ofs = vol->mftbmp_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 (likely(!PageError(page)))
SetPageUptodate(page);
unlock_page(page);
return;
still_busy:
spin_unlock_irqrestore(&page_uptodate_lock, flags);
return;
}
/**
* ntfs_mftbmp_readpage -
*
* Readpage for accessing mft bitmap.
*/
static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
{
VCN vcn;
LCN lcn;
struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
sector_t iblock, lblock, zblock;
unsigned int blocksize, blocks, vcn_ofs;
int nr, i;
unsigned char blocksize_bits;
if (unlikely(!PageLocked(page)))
PAGE_BUG(page);
blocksize = vol->sb->s_blocksize;
blocksize_bits = vol->sb->s_blocksize_bits;
if (!page_has_buffers(page))
create_empty_buffers(page, blocksize, 0);
bh = head = page_buffers(page);
if (unlikely(!bh))
return -ENOMEM;
blocks = PAGE_CACHE_SIZE >> blocksize_bits;
iblock = page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
lblock = (vol->mftbmp_allocated_size + blocksize - 1) >> blocksize_bits;
zblock = (vol->mftbmp_initialized_size + blocksize - 1) >>
blocksize_bits;
/* 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) {
/* 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;
/* Convert the vcn to the corresponding lcn. */
down_read(&vol->mftbmp_rl.lock);
lcn = vcn_to_lcn(vol->mftbmp_rl.rl, vcn);
up_read(&vol->mftbmp_rl.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;
}
if (lcn != LCN_HOLE) {
/* Hard error, zero out region. */
SetPageError(page);
ntfs_error(vol->sb, "vcn_to_lcn(vcn = 0x%Lx) "
"failed with error code "
"0x%Lx.", (long long)vcn,
(long long)-lcn);
// 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.
*/
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_mftbmp_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 (likely(!PageError(page)))
SetPageUptodate(page);
else /* Signal synchronous i/o error. */
nr = -EIO;
unlock_page(page);
return nr;
}
/** /**
* ntfs_aops - general address space operations for inodes and attributes * ntfs_aops - general address space operations for inodes and attributes
*/ */
...@@ -584,18 +403,3 @@ struct address_space_operations ntfs_aops = { ...@@ -584,18 +403,3 @@ struct address_space_operations ntfs_aops = {
commit_write: NULL, /* . */ commit_write: NULL, /* . */
}; };
typedef int readpage_t(struct file *, struct page *);
/**
* ntfs_mftbmp_aops - address space operations for accessing mftbmp
*/
struct address_space_operations ntfs_mftbmp_aops = {
writepage: NULL, /* Write dirty page to disk. */
readpage: (readpage_t*)ntfs_mftbmp_readpage, /* Fill page with
data. */
sync_page: block_sync_page, /* Currently, just unplugs the
disk request queue. */
prepare_write: NULL, /* . */
commit_write: NULL, /* . */
};
...@@ -1316,7 +1316,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi) ...@@ -1316,7 +1316,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
ni->nr_extents = -1; ni->nr_extents = -1;
put_attr_search_ctx(ctx); put_attr_search_ctx(ctx);
unmap_mft_record(READ, ni); unmap_mft_record(READ, base_ni);
ntfs_debug("Done."); ntfs_debug("Done.");
return 0; return 0;
......
...@@ -232,6 +232,8 @@ static inline struct inode *VFS_I(ntfs_inode *ni) ...@@ -232,6 +232,8 @@ static inline struct inode *VFS_I(ntfs_inode *ni)
} }
extern struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no); extern struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no);
extern struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPES type,
uchar_t *name, u32 name_len);
extern struct inode *ntfs_alloc_big_inode(struct super_block *sb); extern struct inode *ntfs_alloc_big_inode(struct super_block *sb);
extern void ntfs_destroy_big_inode(struct inode *inode); extern void ntfs_destroy_big_inode(struct inode *inode);
......
...@@ -67,7 +67,6 @@ extern struct super_operations ntfs_mount_sops; ...@@ -67,7 +67,6 @@ extern struct super_operations ntfs_mount_sops;
extern struct address_space_operations ntfs_aops; extern struct address_space_operations ntfs_aops;
extern struct address_space_operations ntfs_mft_aops; extern struct address_space_operations ntfs_mft_aops;
extern struct address_space_operations ntfs_mftbmp_aops;
extern struct file_operations ntfs_file_ops; extern struct file_operations ntfs_file_ops;
extern struct inode_operations ntfs_file_inode_ops; extern struct inode_operations ntfs_file_inode_ops;
......
...@@ -775,141 +775,20 @@ static BOOL load_and_init_upcase(ntfs_volume *vol) ...@@ -775,141 +775,20 @@ static BOOL load_and_init_upcase(ntfs_volume *vol)
*/ */
static BOOL load_system_files(ntfs_volume *vol) static BOOL load_system_files(ntfs_volume *vol)
{ {
VCN next_vcn, last_vcn, highest_vcn;
struct super_block *sb = vol->sb; struct super_block *sb = vol->sb;
struct inode *tmp_ino; struct inode *tmp_ino;
MFT_RECORD *m; MFT_RECORD *m;
ATTR_RECORD *attr;
VOLUME_INFORMATION *vi; VOLUME_INFORMATION *vi;
attr_search_context *ctx; attr_search_context *ctx;
run_list_element *rl;
ntfs_debug("Entering."); ntfs_debug("Entering.");
/*
* We have $MFT already (vol->mft_ino) but we need to setup access to
* the $MFT/$BITMAP attribute.
*/
m = map_mft_record(READ, NTFS_I(vol->mft_ino));
if (IS_ERR(m)) {
ntfs_error(sb, "Failed to map $MFT.");
return FALSE;
}
if (!(ctx = get_attr_search_ctx(NTFS_I(vol->mft_ino), m))) {
ntfs_error(sb, "Failed to get attribute search context.");
goto unmap_err_out;
}
/* Load all attribute extents. */
attr = NULL;
rl = NULL;
next_vcn = last_vcn = highest_vcn = 0;
while (lookup_attr(AT_BITMAP, NULL, 0, 0, next_vcn, NULL, 0, ctx)) {
run_list_element *nrl;
/* Cache the current attribute extent. */
attr = ctx->attr;
/* $MFT/$BITMAP must be non-resident. */
if (!attr->non_resident) {
ntfs_error(sb, "$MFT/$BITMAP must be non-resident but "
"a resident extent was found. $MFT is "
"corrupt. Run chkdsk.");
goto put_err_out;
}
/* $MFT/$BITMAP must be uncompressed and unencrypted. */
if (attr->flags & ATTR_COMPRESSION_MASK ||
attr->flags & ATTR_IS_ENCRYPTED) {
ntfs_error(sb, "$MFT/$BITMAP must be uncompressed and "
"unencrypted but a compressed/"
"encrypted extent was found. $MFT is "
"corrupt. Run chkdsk.");
goto put_err_out;
}
/*
* Decompress the mapping pairs array of this extent
* and merge the result into the existing run list. Note we
* don't need any locking at this stage as we are already
* running exclusively as we are mount in progress task.
*/
nrl = decompress_mapping_pairs(vol, attr, rl);
if (IS_ERR(nrl)) {
ntfs_error(sb, "decompress_mapping_pairs() failed with "
"error code %ld. $MFT is corrupt.",
PTR_ERR(nrl));
goto put_err_out;
}
rl = nrl;
/* Are we in the first extent? */
if (!next_vcn) {
/* Get the last vcn in the $BITMAP attribute. */
last_vcn = sle64_to_cpu(attr->_ANR(allocated_size)) >>
vol->cluster_size_bits;
vol->mftbmp_size = sle64_to_cpu(attr->_ANR(data_size));
vol->mftbmp_initialized_size =
sle64_to_cpu(attr->_ANR(initialized_size));
vol->mftbmp_allocated_size =
sle64_to_cpu(attr->_ANR(allocated_size));
/* Consistency check. */
if (vol->mftbmp_size < (vol->nr_mft_records + 7) >> 3) {
ntfs_error(sb, "$MFT/$BITMAP is too short to "
"contain a complete mft "
"bitmap: impossible. $MFT is "
"corrupt. Run chkdsk.");
goto put_err_out;
}
}
/* Get the lowest vcn for the next extent. */ /* Get mft bitmap attribute inode. */
highest_vcn = sle64_to_cpu(attr->_ANR(highest_vcn)); vol->mftbmp_ino = ntfs_attr_iget(vol->mft_ino, AT_BITMAP, NULL, 0);
next_vcn = highest_vcn + 1; if (IS_ERR(vol->mftbmp_ino)) {
ntfs_error(sb, "Failed to load $MFT/$BITMAP attribute.");
/* Only one extent or error, which we catch below. */
if (next_vcn <= 0)
break;
/* Avoid endless loops due to corruption. */
if (next_vcn < sle64_to_cpu(attr->_ANR(lowest_vcn))) {
ntfs_error(sb, "$MFT/$BITMAP has corrupt attribute "
"list attribute. Run chkdsk.");
goto put_err_out;
}
}
if (!attr) {
ntfs_error(sb, "Missing or invalid $BITMAP attribute in file "
"$MFT. $MFT is corrupt. Run chkdsk.");
put_err_out:
put_attr_search_ctx(ctx);
unmap_err_out:
unmap_mft_record(READ, NTFS_I(vol->mft_ino));
return FALSE; return FALSE;
} }
/* We are finished with $MFT/$BITMAP. */
put_attr_search_ctx(ctx);
unmap_mft_record(READ, NTFS_I(vol->mft_ino));
/* Catch errors. */
if (highest_vcn && highest_vcn != last_vcn - 1) {
ntfs_error(sb, "Failed to load the complete run list for "
"$MFT/$BITMAP. Driver bug or corrupt $MFT. "
"Run chkdsk.");
ntfs_debug("highest_vcn = 0x%Lx, last_vcn - 1 = 0x%Lx",
(long long)highest_vcn,
(long long)last_vcn - 1);
return FALSE;;
}
/* Setup the run list and the address space in the volume structure. */
vol->mftbmp_rl.rl = rl;
vol->mftbmp_mapping.a_ops = &ntfs_mftbmp_aops;
/*
* Not inode data, set to volume. Our mft bitmap access kludge...
* We can only pray this is not going to cause problems... If it does
* cause problems we will need a fake inode for this.
*/
vol->mftbmp_mapping.host = (struct inode*)vol;
// FIXME: If mounting read-only, it would be ok to ignore errors when // FIXME: If mounting read-only, it would be ok to ignore errors when
// loading the mftbmp but we then need to make sure nobody remounts the // loading the mftbmp but we then need to make sure nobody remounts the
// volume read-write... // volume read-write...
...@@ -920,7 +799,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -920,7 +799,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(vol->mftmirr_ino)) if (!IS_ERR(vol->mftmirr_ino))
iput(vol->mftmirr_ino); iput(vol->mftmirr_ino);
ntfs_error(sb, "Failed to load $MFTMirr."); ntfs_error(sb, "Failed to load $MFTMirr.");
return FALSE; goto iput_mftbmp_err_out;
} }
// FIXME: Compare mftmirr with mft and repair if appropriate and not // FIXME: Compare mftmirr with mft and repair if appropriate and not
// a read-only mount. // a read-only mount.
...@@ -955,7 +834,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -955,7 +834,7 @@ static BOOL load_system_files(ntfs_volume *vol)
iput(vol->vol_ino); iput(vol->vol_ino);
volume_failed: volume_failed:
ntfs_error(sb, "Failed to load $Volume."); ntfs_error(sb, "Failed to load $Volume.");
goto iput_bmp_mirr_err_out; goto iput_lcnbmp_err_out;
} }
m = map_mft_record(READ, NTFS_I(vol->vol_ino)); m = map_mft_record(READ, NTFS_I(vol->vol_ino));
if (IS_ERR(m)) { if (IS_ERR(m)) {
...@@ -1001,7 +880,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1001,7 +880,7 @@ static BOOL load_system_files(ntfs_volume *vol)
ntfs_error(sb, "Failed to load $LogFile."); ntfs_error(sb, "Failed to load $LogFile.");
// FIMXE: We only want to empty the thing so pointless bailing // FIMXE: We only want to empty the thing so pointless bailing
// out. Can recover/ignore. // out. Can recover/ignore.
goto iput_vol_bmp_mirr_err_out; goto iput_vol_err_out;
} }
// FIXME: Empty the logfile, but only if not read-only. // FIXME: Empty the logfile, but only if not read-only.
// FIXME: What happens if someone remounts rw? We need to empty the file // FIXME: What happens if someone remounts rw? We need to empty the file
...@@ -1016,7 +895,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1016,7 +895,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(tmp_ino)) if (!IS_ERR(tmp_ino))
iput(tmp_ino); iput(tmp_ino);
ntfs_error(sb, "Failed to load $AttrDef."); ntfs_error(sb, "Failed to load $AttrDef.");
goto iput_vol_bmp_mirr_err_out; goto iput_vol_err_out;
} }
// FIXME: Parse the attribute definitions. // FIXME: Parse the attribute definitions.
iput(tmp_ino); iput(tmp_ino);
...@@ -1026,7 +905,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1026,7 +905,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(vol->root_ino)) if (!IS_ERR(vol->root_ino))
iput(vol->root_ino); iput(vol->root_ino);
ntfs_error(sb, "Failed to load root directory."); ntfs_error(sb, "Failed to load root directory.");
goto iput_vol_bmp_mirr_err_out; goto iput_vol_err_out;
} }
/* If on NTFS versions before 3.0, we are done. */ /* If on NTFS versions before 3.0, we are done. */
if (vol->major_ver < 3) if (vol->major_ver < 3)
...@@ -1038,7 +917,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1038,7 +917,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(vol->secure_ino)) if (!IS_ERR(vol->secure_ino))
iput(vol->secure_ino); iput(vol->secure_ino);
ntfs_error(sb, "Failed to load $Secure."); ntfs_error(sb, "Failed to load $Secure.");
goto iput_root_vol_bmp_mirr_err_out; goto iput_root_err_out;
} }
// FIXME: Initialize security. // FIXME: Initialize security.
/* Get the extended system files' directory inode. */ /* Get the extended system files' directory inode. */
...@@ -1047,7 +926,7 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1047,7 +926,7 @@ static BOOL load_system_files(ntfs_volume *vol)
if (!IS_ERR(tmp_ino)) if (!IS_ERR(tmp_ino))
iput(tmp_ino); iput(tmp_ino);
ntfs_error(sb, "Failed to load $Extend."); ntfs_error(sb, "Failed to load $Extend.");
goto iput_sec_root_vol_bmp_mirr_err_out; goto iput_sec_err_out;
} }
// FIXME: Do something. E.g. want to delete the $UsnJrnl if exists. // FIXME: Do something. E.g. want to delete the $UsnJrnl if exists.
// Note we might be doing this at the wrong level; we might want to // Note we might be doing this at the wrong level; we might want to
...@@ -1056,16 +935,18 @@ static BOOL load_system_files(ntfs_volume *vol) ...@@ -1056,16 +935,18 @@ static BOOL load_system_files(ntfs_volume *vol)
// for the files in $Extend directory. // for the files in $Extend directory.
iput(tmp_ino); iput(tmp_ino);
return TRUE; return TRUE;
iput_sec_root_vol_bmp_mirr_err_out: iput_sec_err_out:
iput(vol->secure_ino); iput(vol->secure_ino);
iput_root_vol_bmp_mirr_err_out: iput_root_err_out:
iput(vol->root_ino); iput(vol->root_ino);
iput_vol_bmp_mirr_err_out: iput_vol_err_out:
iput(vol->vol_ino); iput(vol->vol_ino);
iput_bmp_mirr_err_out: iput_lcnbmp_err_out:
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
iput_mirr_err_out: iput_mirr_err_out:
iput(vol->mftmirr_ino); iput(vol->mftmirr_ino);
iput_mftbmp_err_out:
iput(vol->mftbmp_ino);
return FALSE; return FALSE;
} }
...@@ -1083,8 +964,10 @@ static void ntfs_put_super(struct super_block *vfs_sb) ...@@ -1083,8 +964,10 @@ static void ntfs_put_super(struct super_block *vfs_sb)
ntfs_volume *vol = NTFS_SB(vfs_sb); ntfs_volume *vol = NTFS_SB(vfs_sb);
ntfs_debug("Entering."); ntfs_debug("Entering.");
iput(vol->vol_ino); iput(vol->vol_ino);
vol->vol_ino = NULL; vol->vol_ino = NULL;
/* NTFS 3.0+ specific clean up. */ /* NTFS 3.0+ specific clean up. */
if (vol->major_ver >= 3) { if (vol->major_ver >= 3) {
if (vol->secure_ino) { if (vol->secure_ino) {
...@@ -1092,29 +975,26 @@ static void ntfs_put_super(struct super_block *vfs_sb) ...@@ -1092,29 +975,26 @@ static void ntfs_put_super(struct super_block *vfs_sb)
vol->secure_ino = NULL; vol->secure_ino = NULL;
} }
} }
iput(vol->root_ino); iput(vol->root_ino);
vol->root_ino = NULL; vol->root_ino = NULL;
down_write(&vol->lcnbmp_lock); down_write(&vol->lcnbmp_lock);
iput(vol->lcnbmp_ino); iput(vol->lcnbmp_ino);
vol->lcnbmp_ino = NULL; vol->lcnbmp_ino = NULL;
up_write(&vol->lcnbmp_lock); up_write(&vol->lcnbmp_lock);
iput(vol->mftmirr_ino); iput(vol->mftmirr_ino);
vol->mftmirr_ino = NULL; vol->mftmirr_ino = NULL;
iput(vol->mft_ino);
vol->mft_ino = NULL;
down_write(&vol->mftbmp_lock); down_write(&vol->mftbmp_lock);
/* iput(vol->mftbmp_ino);
* Clean up mft bitmap address space. Ignore the _inode_ bit in the vol->mftbmp_ino = NULL;
* name of the function... FIXME: This destroys dirty pages!!! (AIA)
*/
truncate_inode_pages(&vol->mftbmp_mapping, 0);
vol->mftbmp_mapping.a_ops = NULL;
vol->mftbmp_mapping.host = NULL;
up_write(&vol->mftbmp_lock); up_write(&vol->mftbmp_lock);
down_write(&vol->mftbmp_rl.lock);
ntfs_free(vol->mftbmp_rl.rl); iput(vol->mft_ino);
vol->mftbmp_rl.rl = NULL; vol->mft_ino = NULL;
up_write(&vol->mftbmp_rl.lock);
vol->upcase_len = 0; vol->upcase_len = 0;
/* /*
* Decrease the number of mounts and destroy the global default upcase * Decrease the number of mounts and destroy the global default upcase
...@@ -1242,60 +1122,41 @@ static s64 get_nr_free_clusters(ntfs_volume *vol) ...@@ -1242,60 +1122,41 @@ static s64 get_nr_free_clusters(ntfs_volume *vol)
static unsigned long __get_nr_free_mft_records(ntfs_volume *vol) static unsigned long __get_nr_free_mft_records(ntfs_volume *vol)
{ {
struct address_space *mapping; struct address_space *mapping;
filler_t *readpage;
struct page *page; struct page *page;
unsigned long index, max_index, nr_free = 0; unsigned long index, max_index, nr_free = 0;
unsigned int max_size, i; unsigned int max_size, i;
u32 *b; u32 *b;
ntfs_debug("Entering."); mapping = vol->mftbmp_ino->i_mapping;
/* Serialize accesses to the inode bitmap. */
mapping = &vol->mftbmp_mapping;
readpage = (filler_t*)mapping->a_ops->readpage;
/* /*
* Convert the number of bits into bytes rounded up, then convert into * Convert the number of bits into bytes rounded up to a multiple of 8
* multiples of PAGE_CACHE_SIZE. * bytes, then convert into multiples of PAGE_CACHE_SIZE.
*/ */
max_index = (vol->nr_mft_records + 7) >> (3 + PAGE_CACHE_SHIFT); max_index = (((vol->nr_mft_records + 7) >> 3) + 7) >> PAGE_CACHE_SHIFT;
/* Use multiples of 4 bytes. */ /* Use multiples of 4 bytes. */
max_size = PAGE_CACHE_SIZE >> 2; max_size = PAGE_CACHE_SIZE >> 2;
ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = " ntfs_debug("Reading $MFT/$BITMAP, max_index = 0x%lx, max_size = "
"0x%x.", max_index, max_size); "0x%x.", max_index, max_size);
for (index = 0UL; index < max_index;) { for (index = 0UL; index < max_index;) {
handle_partial_page: handle_partial_page:
/* page = ntfs_map_page(mapping, index++);
* Read the page from page cache, getting it from backing store
* if necessary, and increment the use count.
*/
page = read_cache_page(mapping, index++, (filler_t*)readpage,
vol);
/* Ignore pages which errored synchronously. */
if (IS_ERR(page)) { if (IS_ERR(page)) {
ntfs_debug("Sync read_cache_page() error. Skipping " ntfs_debug("ntfs_map_page() error. Skipping page "
"page (index 0x%lx).", index - 1); "(index 0x%lx).", index - 1);
continue; continue;
} }
wait_on_page_locked(page); b = (u32*)page_address(page);
if (!PageUptodate(page)) {
ntfs_debug("Async read_cache_page() error. Skipping "
"page (index 0x%lx).", index - 1);
/* Ignore pages which errored asynchronously. */
page_cache_release(page);
continue;
}
b = (u32*)kmap(page);
/* For each 4 bytes, add up the number of zero bits. */ /* For each 4 bytes, add up the number of zero bits. */
for (i = 0; i < max_size; i++) for (i = 0; i < max_size; i++)
nr_free += 32 - hweight32(b[i]); nr_free += 32 - hweight32(b[i]);
kunmap(page); ntfs_unmap_page(page);
page_cache_release(page);
} }
if (index == max_index) { if (index == max_index) {
/* /*
* Get the multiples of 4 bytes in use in the final partial * Get the multiples of 4 bytes in use in the final partial
* page. * page.
*/ */
max_size = ((((vol->nr_mft_records + 7) >> 3) & max_size = ((((((vol->nr_mft_records + 7) >> 3) + 7) & ~7) &
~PAGE_CACHE_MASK) + 3) >> 2; ~PAGE_CACHE_MASK) + 3) >> 2;
/* If there is a partial page go back and do it. */ /* If there is a partial page go back and do it. */
if (max_size) { if (max_size) {
...@@ -1309,7 +1170,6 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol) ...@@ -1309,7 +1170,6 @@ static unsigned long __get_nr_free_mft_records(ntfs_volume *vol)
} }
ntfs_debug("Finished reading $MFT/$BITMAP, last index = 0x%lx", ntfs_debug("Finished reading $MFT/$BITMAP, last index = 0x%lx",
index - 1); index - 1);
ntfs_debug("Exiting.");
return nr_free; return nr_free;
} }
...@@ -1355,6 +1215,7 @@ static int ntfs_statfs(struct super_block *sb, struct statfs *sfs) ...@@ -1355,6 +1215,7 @@ static int ntfs_statfs(struct super_block *sb, struct statfs *sfs)
size = 0LL; size = 0LL;
/* Free blocks avail to non-superuser, same as above on NTFS. */ /* Free blocks avail to non-superuser, same as above on NTFS. */
sfs->f_bavail = sfs->f_bfree = size; sfs->f_bavail = sfs->f_bfree = size;
/* Serialize accesses to the inode bitmap. */
down_read(&vol->mftbmp_lock); down_read(&vol->mftbmp_lock);
/* Total file nodes in file system (at this moment in time). */ /* Total file nodes in file system (at this moment in time). */
sfs->f_files = vol->mft_ino->i_size >> vol->mft_record_size_bits; sfs->f_files = vol->mft_ino->i_size >> vol->mft_record_size_bits;
...@@ -1459,6 +1320,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1459,6 +1320,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
vol->sb = sb; vol->sb = sb;
vol->upcase = NULL; vol->upcase = NULL;
vol->mft_ino = NULL; vol->mft_ino = NULL;
vol->mftbmp_ino = NULL;
init_rwsem(&vol->mftbmp_lock);
vol->mftmirr_ino = NULL; vol->mftmirr_ino = NULL;
vol->lcnbmp_ino = NULL; vol->lcnbmp_ino = NULL;
init_rwsem(&vol->lcnbmp_lock); init_rwsem(&vol->lcnbmp_lock);
...@@ -1470,37 +1333,10 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1470,37 +1333,10 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
vol->on_errors = 0; vol->on_errors = 0;
vol->mft_zone_multiplier = 0; vol->mft_zone_multiplier = 0;
vol->nls_map = NULL; vol->nls_map = NULL;
init_rwsem(&vol->mftbmp_lock);
init_run_list(&vol->mftbmp_rl);
/* Initialize the mftbmp address space mapping. */
INIT_RADIX_TREE(&vol->mftbmp_mapping.page_tree, GFP_ATOMIC);
rwlock_init(&vol->mftbmp_mapping.page_lock);
INIT_LIST_HEAD(&vol->mftbmp_mapping.clean_pages);
INIT_LIST_HEAD(&vol->mftbmp_mapping.dirty_pages);
INIT_LIST_HEAD(&vol->mftbmp_mapping.locked_pages);
INIT_LIST_HEAD(&vol->mftbmp_mapping.io_pages);
vol->mftbmp_mapping.nrpages = 0;
vol->mftbmp_mapping.a_ops = NULL;
vol->mftbmp_mapping.host = NULL;
INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap);
INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap_shared);
spin_lock_init(&vol->mftbmp_mapping.i_shared_lock);
/*
* private_lock and private_list are unused by ntfs. But they
* are available.
*/
spin_lock_init(&vol->mftbmp_mapping.private_lock);
INIT_LIST_HEAD(&vol->mftbmp_mapping.private_list);
vol->mftbmp_mapping.assoc_mapping = NULL;
vol->mftbmp_mapping.dirtied_when = 0;
vol->mftbmp_mapping.gfp_mask = GFP_HIGHUSER;
vol->mftbmp_mapping.backing_dev_info =
sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
/* /*
* Default is group and other don't have any access to files or * Default is group and other don't have any access to files or
* directories while owner has full access. Further files by default * directories while owner has full access. Further, files by default
* are not executable but directories are of course browseable. * are not executable but directories are of course browseable.
*/ */
vol->fmask = 0177; vol->fmask = 0177;
...@@ -1509,7 +1345,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1509,7 +1345,7 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
/* Important to get the mount options dealt with now. */ /* Important to get the mount options dealt with now. */
if (!parse_options(vol, (char*)opt)) if (!parse_options(vol, (char*)opt))
goto err_out_now; goto err_out_now;
/* We are just a read-only fs at the moment. */ /* We are just a read-only fs at the moment. */
sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME; sb->s_flags |= MS_RDONLY | MS_NOATIME | MS_NODIRATIME;
...@@ -1667,11 +1503,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1667,11 +1503,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
vol->lcnbmp_ino = NULL; vol->lcnbmp_ino = NULL;
iput(vol->mftmirr_ino); iput(vol->mftmirr_ino);
vol->mftmirr_ino = NULL; vol->mftmirr_ino = NULL;
truncate_inode_pages(&vol->mftbmp_mapping, 0); iput(vol->mftbmp_ino);
vol->mftbmp_mapping.a_ops = NULL; vol->mftbmp_ino = NULL;
vol->mftbmp_mapping.host = NULL;
ntfs_free(vol->mftbmp_rl.rl);
vol->mftbmp_rl.rl = NULL;
vol->upcase_len = 0; vol->upcase_len = 0;
if (vol->upcase != default_upcase) if (vol->upcase != default_upcase)
ntfs_free(vol->upcase); ntfs_free(vol->upcase);
...@@ -1708,7 +1541,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent) ...@@ -1708,7 +1541,8 @@ static int ntfs_fill_super(struct super_block *sb, void *opt, const int silent)
* inode we have ever called ntfs_iget()/iput() on, otherwise we A) * inode we have ever called ntfs_iget()/iput() on, otherwise we A)
* leak resources and B) a subsequent mount fails automatically due to * leak resources and B) a subsequent mount fails automatically due to
* ntfs_iget() never calling down into our ntfs_read_locked_inode() * ntfs_iget() never calling down into our ntfs_read_locked_inode()
* method again... * method again... FIXME: Do we need to do this twice now because of
* attribute inodes? I think not, so leave as is for now... (AIA)
*/ */
if (invalidate_inodes(sb)) { if (invalidate_inodes(sb)) {
ntfs_error(sb, "Busy inodes left. This is most likely a NTFS " ntfs_error(sb, "Busy inodes left. This is most likely a NTFS "
......
...@@ -75,15 +75,13 @@ typedef struct { ...@@ -75,15 +75,13 @@ typedef struct {
LCN mft_zone_start; /* First cluster of the mft zone. */ LCN mft_zone_start; /* First cluster of the mft zone. */
LCN mft_zone_end; /* First cluster beyond the mft zone. */ LCN mft_zone_end; /* First cluster beyond the mft zone. */
struct inode *mft_ino; /* The VFS inode of $MFT. */ struct inode *mft_ino; /* The VFS inode of $MFT. */
struct inode *mftbmp_ino; /* Attribute inode for $MFT/$BITMAP. */
struct rw_semaphore mftbmp_lock; /* Lock for serializing accesses to the struct rw_semaphore mftbmp_lock; /* Lock for serializing accesses to the
mft record bitmap ($MFT/$BITMAP). */ mft record bitmap ($MFT/$BITMAP). */
unsigned long nr_mft_records; /* Number of mft records == number of unsigned long nr_mft_records; /* Number of mft records == number of
bits in mft bitmap. */ bits in mft bitmap. */
struct address_space mftbmp_mapping; /* Page cache for $MFT/$BITMAP. */
run_list mftbmp_rl; /* Run list for $MFT/$BITMAP. */
s64 mftbmp_size; /* Data size of $MFT/$BITMAP. */
s64 mftbmp_initialized_size; /* Initialized size of $MFT/$BITMAP. */
s64 mftbmp_allocated_size; /* Allocated size of $MFT/$BITMAP. */
struct inode *mftmirr_ino; /* The VFS inode of $MFTMirr. */ struct inode *mftmirr_ino; /* The VFS inode of $MFTMirr. */
struct inode *lcnbmp_ino; /* The VFS inode of $Bitmap. */ struct inode *lcnbmp_ino; /* The VFS inode of $Bitmap. */
struct rw_semaphore lcnbmp_lock; /* Lock for serializing accesses to the struct rw_semaphore lcnbmp_lock; /* Lock for serializing accesses to the
......
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