Commit db05cffc authored by Anton Altaparmakov's avatar Anton Altaparmakov

NTFS: 2.0.15 - Fake inodes based attribute i/o via the pagecache, fixes, cleanups.

- Fix silly bug in fs/ntfs/super.c::parse_options() which was causing
  remounts to fail when the partition had an entry in /etc/fstab and
  the entry specified the nls= option.
- Apply same macro magic used in fs/ntfs/inode.h to fs/ntfs/volume.h to
  expand all the helper functions NVolFoo(), NVolSetFoo(), and
  NVolClearFoo().
- Move copyright statement from driver initialisation message to
  module description (fs/super.c). This makes the initialisation
  message fit on one line and fits in better with rest of kernel.
- Update fs/ntfs/attrib.c::map_run_list() to work on both real and
  attribute inodes, and both for files and directories.
- Implement fake attribute inodes allowing all attribute i/o to go via
  the page cache and to use all the normal vfs/mm functionality:
  - Add ntfs_attr_iget() and its helper ntfs_read_locked_attr_inode()
    to fs/ntfs/inode.c.
  - Add needed cleanup code to ntfs_clear_big_inode().
- Merge address space operations for files and directories (aops.c),
  now just have ntfs_aops:
  - Rename:
        end_buffer_read_attr_async() -> ntfs_end_buffer_read_async(),
        ntfs_attr_read_block()       -> ntfs_read_block(),
        ntfs_file_read_page()        -> ntfs_readpage().
  - Rewrite fs/ntfs/aops.c::ntfs_readpage() to work on both real and
    attribute inodes, and both for files and directories.
  - Remove obsolete fs/ntfs/aops.c::ntfs_mst_readpage().
parent 5a597e77
......@@ -247,6 +247,11 @@ ChangeLog
Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
2.0.15:
- Bug fix in parsing of remount options.
- Internal changes implementing attribute (fake) inodes allowing all
attribute i/o to go via the page cache and to use all the normal
vfs/mm functionality.
2.0.14:
- Internal changes improving run list merging code and minor locking
change to not rely on BKL in ntfs_statfs().
......
......@@ -26,7 +26,34 @@ ToDo:
callers, i.e. ntfs_iget(), to pass that error code up instead of just
using -EIO.
- Enable NFS exporting of NTFS.
- Use fake inodes for address space i/o.
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
remounts to fail when the partition had an entry in /etc/fstab and
the entry specified the nls= option.
- Apply same macro magic used in fs/ntfs/inode.h to fs/ntfs/volume.h to
expand all the helper functions NVolFoo(), NVolSetFoo(), and
NVolClearFoo().
- Move copyright statement from driver initialisation message to
module description (fs/super.c). This makes the initialisation
message fit on one line and fits in better with rest of kernel.
- Update fs/ntfs/attrib.c::map_run_list() to work on both real and
attribute inodes, and both for files and directories.
- Implement fake attribute inodes allowing all attribute i/o to go via
the page cache and to use all the normal vfs/mm functionality:
- Add ntfs_attr_iget() and its helper ntfs_read_locked_attr_inode()
to fs/ntfs/inode.c.
- Add needed cleanup code to ntfs_clear_big_inode().
- Merge address space operations for files and directories (aops.c),
now just have ntfs_aops:
- Rename:
end_buffer_read_attr_async() -> ntfs_end_buffer_read_async(),
ntfs_attr_read_block() -> ntfs_read_block(),
ntfs_file_read_page() -> ntfs_readpage().
- Rewrite fs/ntfs/aops.c::ntfs_readpage() to work on both real and
attribute inodes, and both for files and directories.
- Remove obsolete fs/ntfs/aops.c::ntfs_mst_readpage().
2.0.14 - Run list merging code cleanup, minor locking changes, typo fixes.
......
......@@ -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 \
mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.14\"
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.15\"
ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
......
......@@ -30,7 +30,7 @@
#include "ntfs.h"
/**
* end_buffer_read_attr_async - async io completion for reading attributes
* ntfs_end_buffer_read_async - async io completion for reading attributes
* @bh: buffer head on which io is completed
* @uptodate: whether @bh is now uptodate or not
*
......@@ -45,7 +45,7 @@
* record size, and index_block_size_bits, to the log(base 2) of the ntfs
* record size.
*/
static void end_buffer_read_attr_async(struct buffer_head *bh, int uptodate)
static void ntfs_end_buffer_read_async(struct buffer_head *bh, int uptodate)
{
static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
unsigned long flags;
......@@ -143,12 +143,12 @@ static void end_buffer_read_attr_async(struct buffer_head *bh, int uptodate)
}
/**
* ntfs_attr_read_block - fill a @page of an address space with data
* ntfs_read_block - fill a @page of an address space with data
* @page: page cache page to fill with data
*
* 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
* completion handler ntfs_end_buffer_read_async(), if required, automatically
* applies the mst fixups to the page before finally marking it uptodate and
* unlocking it.
*
......@@ -156,7 +156,7 @@ static void end_buffer_read_attr_async(struct buffer_head *bh, int uptodate)
*
* Contains an adapted version of fs/buffer.c::block_read_full_page().
*/
static int ntfs_attr_read_block(struct page *page)
static int ntfs_read_block(struct page *page)
{
VCN vcn;
LCN lcn;
......@@ -267,7 +267,7 @@ static int ntfs_attr_read_block(struct page *page)
for (i = 0; i < nr; i++) {
struct buffer_head *tbh = arr[i];
lock_buffer(tbh);
tbh->b_end_io = end_buffer_read_attr_async;
tbh->b_end_io = ntfs_end_buffer_read_async;
set_buffer_async_read(tbh);
}
/* Finally, start i/o on the buffers. */
......@@ -285,27 +285,27 @@ static int ntfs_attr_read_block(struct page *page)
}
/**
* ntfs_file_readpage - fill a @page of a @file with data from the device
* ntfs_readpage - fill a @page of a @file with data from the device
* @file: open file to which the page @page belongs or NULL
* @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_readpage() fills the @page of the open
* file @file by calling the ntfs version of the generic block_read_full_page()
* function provided by the kernel, ntfs_attr_read_block(), which in turn
* creates and reads in the buffers associated with the page asynchronously.
* function, ntfs_read_block(), which in turn creates and reads in the buffers
* associated with the page asynchronously.
*
* 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)
* 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
* wait for it to be read in before we can do the copy.
* For resident attributes, OTOH, ntfs_readpage() fills @page by copying 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, 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.
*
* Return 0 on success or -errno on error.
* Return 0 on success and -errno on error.
*/
static int ntfs_file_readpage(struct file *file, struct page *page)
int ntfs_readpage(struct file *file, struct page *page)
{
s64 attr_pos;
ntfs_inode *ni;
ntfs_inode *ni, *base_ni;
char *addr;
attr_search_context *ctx;
MFT_RECORD *mrec;
......@@ -317,40 +317,45 @@ static int ntfs_file_readpage(struct file *file, struct page *page)
ni = NTFS_I(page->mapping->host);
/* Is the unnamed $DATA attribute resident? */
if (NInoNonResident(ni)) {
/* Attribute is not resident. */
/* If the file is encrypted, we deny access, just like NT4. */
if (NInoEncrypted(ni)) {
err = -EACCES;
goto unl_err_out;
/*
* Only unnamed $DATA attributes can be compressed or
* encrypted.
*/
if (ni->type == AT_DATA && !ni->name_len) {
/* If file is encrypted, deny access, just like NT4. */
if (NInoEncrypted(ni)) {
err = -EACCES;
goto err_out;
}
/* Compressed data streams are handled in compress.c. */
if (NInoCompressed(ni))
return ntfs_file_read_compressed_block(page);
}
/* Compressed data stream. Handled in compress.c. */
if (NInoCompressed(ni))
return ntfs_file_read_compressed_block(page);
/* Normal data stream. */
return ntfs_attr_read_block(page);
return ntfs_read_block(page);
}
/* Attribute is resident, implying it is not compressed or encrypted. */
if (!NInoAttr(ni))
base_ni = ni;
else
base_ni = ni->_INE(base_ntfs_ino);
/* Map, pin and lock the mft record for reading. */
mrec = map_mft_record(READ, ni);
mrec = map_mft_record(READ, base_ni);
if (unlikely(IS_ERR(mrec))) {
err = PTR_ERR(mrec);
goto unl_err_out;
goto err_out;
}
ctx = get_attr_search_ctx(ni, mrec);
ctx = get_attr_search_ctx(base_ni, mrec);
if (unlikely(!ctx)) {
err = -ENOMEM;
goto unm_unl_err_out;
goto unm_err_out;
}
/* Find the data attribute in the mft record. */
if (unlikely(!lookup_attr(AT_DATA, NULL, 0, 0, 0, NULL, 0, ctx))) {
if (unlikely(!lookup_attr(ni->type, ni->name, ni->name_len,
IGNORE_CASE, 0, NULL, 0, ctx))) {
err = -ENOENT;
goto put_unm_unl_err_out;
goto put_unm_err_out;
}
/* Starting position of the page within the attribute value. */
......@@ -377,34 +382,15 @@ static int ntfs_file_readpage(struct file *file, struct page *page)
kunmap(page);
SetPageUptodate(page);
put_unm_unl_err_out:
put_unm_err_out:
put_attr_search_ctx(ctx);
unm_unl_err_out:
unmap_mft_record(READ, ni);
unl_err_out:
unm_err_out:
unmap_mft_record(READ, base_ni);
err_out:
unlock_page(page);
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 -
*
......@@ -473,7 +459,7 @@ static void end_buffer_read_mftbmp_async(struct buffer_head *bh, int uptodate)
/**
* ntfs_mftbmp_readpage -
*
* Readpage for accessing mft bitmap. Adapted from ntfs_mst_readpage().
* Readpage for accessing mft bitmap.
*/
static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
{
......@@ -587,11 +573,11 @@ static int ntfs_mftbmp_readpage(ntfs_volume *vol, struct page *page)
}
/**
* ntfs_file_aops - address space operations for accessing normal file data
* ntfs_aops - general address space operations for inodes and attributes
*/
struct address_space_operations ntfs_file_aops = {
struct address_space_operations ntfs_aops = {
writepage: NULL, /* Write dirty page to disk. */
readpage: ntfs_file_readpage, /* Fill page with data. */
readpage: ntfs_readpage, /* Fill page with data. */
sync_page: block_sync_page, /* Currently, just unplugs the
disk request queue. */
prepare_write: NULL, /* . */
......@@ -613,20 +599,3 @@ struct address_space_operations ntfs_mftbmp_aops = {
commit_write: NULL, /* . */
};
/**
* ntfs_dir_aops -
*
* Address space operations for accessing normal directory data (i.e. index
* allocation attribute). We can't just use the same operations as for files
* because 1) the attribute is different and even more importantly 2) the index
* records have to be multi sector transfer deprotected (i.e. fixed-up).
*/
struct address_space_operations ntfs_dir_aops = {
writepage: NULL, /* Write dirty page to disk. */
readpage: ntfs_mst_readpage, /* Fill page with data. */
sync_page: block_sync_page, /* Currently, just unplugs the
disk request queue. */
prepare_write: NULL, /* . */
commit_write: NULL, /* . */
};
......@@ -935,78 +935,51 @@ run_list_element *decompress_mapping_pairs(const ntfs_volume *vol,
*/
int map_run_list(ntfs_inode *ni, VCN vcn)
{
ntfs_inode *base_ni;
attr_search_context *ctx;
MFT_RECORD *mrec;
const uchar_t *name;
u32 name_len;
ATTR_TYPES at;
int err = 0;
ntfs_debug("Mapping run list part containing vcn 0x%Lx.",
(long long)vcn);
/* Map, pin and lock the mft record for reading. */
mrec = map_mft_record(READ, ni);
if (!NInoAttr(ni))
base_ni = ni;
else
base_ni = ni->_INE(base_ntfs_ino);
mrec = map_mft_record(READ, base_ni);
if (IS_ERR(mrec))
return PTR_ERR(mrec);
ctx = get_attr_search_ctx(ni, mrec);
ctx = get_attr_search_ctx(base_ni, mrec);
if (!ctx) {
err = -ENOMEM;
goto unm_err_out;
}
/* The attribute type is determined from the inode type. */
if (S_ISDIR(VFS_I(ni)->i_mode)) {
at = AT_INDEX_ALLOCATION;
name = I30;
name_len = 4;
} else {
at = AT_DATA;
name = NULL;
name_len = 0;
goto err_out;
}
/* Find the attribute in the mft record. */
if (!lookup_attr(at, name, name_len, CASE_SENSITIVE, vcn, NULL, 0,
ctx)) {
if (!lookup_attr(ni->type, ni->name, ni->name_len, IGNORE_CASE, vcn,
NULL, 0, ctx)) {
put_attr_search_ctx(ctx);
err = -ENOENT;
goto unm_err_out;
goto err_out;
}
/* Lock the run list. */
down_write(&ni->run_list.lock);
/* Make sure someone else didn't do the work while we were spinning. */
if (likely(vcn_to_lcn(ni->run_list.rl, vcn) <= LCN_RL_NOT_MAPPED)) {
run_list_element *rl;
/* Decode the run list. */
rl = decompress_mapping_pairs(ni->vol, ctx->attr,
ni->run_list.rl);
/* Flag any errors or set the run list if successful. */
if (unlikely(IS_ERR(rl)))
err = PTR_ERR(rl);
else
ni->run_list.rl = rl;
}
/* Unlock the run list. */
up_write(&ni->run_list.lock);
put_attr_search_ctx(ctx);
/* Unlock, unpin and release the mft record. */
unmap_mft_record(READ, ni);
/* If an error occured, return it. */
ntfs_debug("Done.");
return err;
unm_err_out:
unmap_mft_record(READ, ni);
err_out:
unmap_mft_record(READ, base_ni);
return err;
}
......
......@@ -462,6 +462,11 @@ int ntfs_file_read_compressed_block(struct page *page)
ntfs_debug("Entering, page->index = 0x%lx, cb_size = 0x%x, nr_pages = "
"%i.", index, cb_size, nr_pages);
/*
* Bad things happen if we get here for anything that is not an
* unnamed $DATA attribute.
*/
BUG_ON(ni->type != AT_DATA || ni->name_len);
pages = kmalloc(nr_pages * sizeof(struct page *), GFP_NOFS);
......
This diff is collapsed.
......@@ -95,8 +95,10 @@ int format_mft_record(ntfs_inode *ni, MFT_RECORD *mft_rec)
return 0;
}
/* From fs/ntfs/aops.c */
extern int ntfs_mst_readpage(struct file *, struct page *);
/**
* From fs/ntfs/aops.c
*/
extern int ntfs_readpage(struct file *, struct page *);
/**
* ntfs_mft_aops - address space operations for access to $MFT
......@@ -106,7 +108,7 @@ extern int ntfs_mst_readpage(struct file *, struct page *);
*/
struct address_space_operations ntfs_mft_aops = {
writepage: NULL, /* Write dirty page to disk. */
readpage: ntfs_mst_readpage, /* Fill page with data. */
readpage: ntfs_readpage, /* Fill page with data. */
sync_page: block_sync_page, /* Currently, just unplugs the
disk request queue. */
prepare_write: NULL, /* . */
......@@ -214,11 +216,11 @@ static inline void unmap_mft_record_page(ntfs_inode *ni)
* necessary, increments the use count on the page so that it cannot disappear
* under us and returns a reference to the page cache page).
*
* If read_cache_page() invokes ntfs_mst_readpage() to load the page from disk,
* it sets PG_locked and clears PG_uptodate on the page. Once I/O has
* completed and the post-read mst fixups on each mft record in the page have
* been performed, the page gets PG_uptodate set and PG_locked cleared (this is
* done in our asynchronous I/O completion handler end_buffer_read_mft_async()).
* If read_cache_page() invokes ntfs_readpage() to load the page from disk, it
* sets PG_locked and clears PG_uptodate on the page. Once I/O has completed
* and the post-read mst fixups on each mft record in the page have been
* performed, the page gets PG_uptodate set and PG_locked cleared (this is done
* in our asynchronous I/O completion handler end_buffer_read_mft_async()).
* ntfs_map_page() waits for PG_locked to become clear and checks if
* PG_uptodate is set and returns an error code if not. This provides
* sufficient protection against races when reading/using the page.
......
......@@ -62,18 +62,21 @@ extern kmem_cache_t *ntfs_big_inode_cache;
extern kmem_cache_t *ntfs_attr_ctx_cache;
/* The various operations structs defined throughout the driver files. */
extern struct super_operations ntfs_mount_sops;
extern struct super_operations ntfs_sops;
extern struct file_operations ntfs_file_ops;
extern struct super_operations ntfs_mount_sops;
extern struct address_space_operations ntfs_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 inode_operations ntfs_file_inode_ops;
extern struct address_space_operations ntfs_file_aops;
extern struct file_operations ntfs_dir_ops;
extern struct file_operations ntfs_dir_ops;
extern struct inode_operations ntfs_dir_inode_ops;
extern struct address_space_operations ntfs_dir_aops;
extern struct file_operations ntfs_empty_file_ops;
extern struct file_operations ntfs_empty_file_ops;
extern struct inode_operations ntfs_empty_inode_ops;
extern struct address_space_operations ntfs_mft_aops;
extern struct address_space_operations ntfs_mftbmp_aops;
/* Generic macro to convert pointers to values for comparison purposes. */
#ifndef p2n
......
......@@ -135,6 +135,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
}
if (!opt || !*opt)
goto no_mount_options;
ntfs_debug("Entering with mount options string: %s", opt);
while ((p = strsep(&opt, ","))) {
if ((v = strchr(p, '=')))
*v++ = '\0';
......@@ -217,7 +218,7 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
}
}
if (nls_map) {
if (vol->nls_map) {
if (vol->nls_map && vol->nls_map != nls_map) {
ntfs_error(vol->sb, "Cannot change NLS character set "
"on remount.");
return FALSE;
......@@ -249,8 +250,8 @@ static BOOL parse_options(ntfs_volume *vol, char *opt)
mft_zone_multiplier = 1;
}
vol->mft_zone_multiplier = mft_zone_multiplier;
} if (!vol->mft_zone_multiplier)
/* Not specified and it is the first mount, so set default. */
}
if (!vol->mft_zone_multiplier)
vol->mft_zone_multiplier = 1;
if (on_errors != -1)
vol->on_errors = on_errors;
......@@ -304,7 +305,7 @@ static int ntfs_remount(struct super_block *sb, int *flags, char *opt)
{
ntfs_volume *vol = NTFS_SB(sb);
ntfs_debug("Entering.");
ntfs_debug("Entering with remount options string: %s", opt);
// FIXME/TODO: If left like this we will have problems with rw->ro and
// ro->rw, as well as with sync->async and vice versa remounts.
......@@ -1799,7 +1800,7 @@ static int __init init_ntfs_fs(void)
#ifdef MODULE
" MODULE"
#endif
"]. Copyright (c) 2001,2002 Anton Altaparmakov.\n");
"].\n");
ntfs_debug("Debug messages are enabled.");
......@@ -1899,7 +1900,7 @@ static void __exit exit_ntfs_fs(void)
}
MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>");
MODULE_DESCRIPTION("NTFS 1.2/3.x driver");
MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2002 Anton Altaparmakov");
MODULE_LICENSE("GPL");
#ifdef DEBUG
MODULE_PARM(debug_msgs, "i");
......
......@@ -26,31 +26,6 @@
#include "types.h"
/*
* Defined bits for the flags field in the ntfs_volume structure.
*/
typedef enum {
NV_ShowSystemFiles, /* 1: Return system files in ntfs_readdir(). */
NV_CaseSensitive, /* 1: Treat file names as case sensitive and
create filenames in the POSIX namespace.
Otherwise be case insensitive and create
file names in WIN32 namespace. */
} ntfs_volume_flags;
#define NVolShowSystemFiles(n_vol) test_bit(NV_ShowSystemFiles, \
&(n_vol)->flags)
#define NVolSetShowSystemFiles(n_vol) set_bit(NV_ShowSystemFiles, \
&(n_vol)->flags)
#define NVolClearShowSystemFiles(n_vol) clear_bit(NV_ShowSystemFiles, \
&(n_vol)->flags)
#define NVolCaseSensitive(n_vol) test_bit(NV_CaseSensitive, \
&(n_vol)->flags)
#define NVolSetCaseSensitive(n_vol) set_bit(NV_CaseSensitive, \
&(n_vol)->flags)
#define NVolClearCaseSensitive(n_vol) clear_bit(NV_CaseSensitive, \
&(n_vol)->flags)
/*
* The NTFS in memory super block structure.
*/
......@@ -124,5 +99,38 @@ typedef struct {
struct nls_table *nls_map;
} ntfs_volume;
/*
* Defined bits for the flags field in the ntfs_volume structure.
*/
typedef enum {
NV_ShowSystemFiles, /* 1: Return system files in ntfs_readdir(). */
NV_CaseSensitive, /* 1: Treat file names as case sensitive and
create filenames in the POSIX namespace.
Otherwise be case insensitive and create
file names in WIN32 namespace. */
} ntfs_volume_flags;
/*
* Macro tricks to expand the NVolFoo(), NVolSetFoo(), and NVolClearFoo()
* functions.
*/
#define NVOL_FNS(flag) \
static inline int NVol##flag(ntfs_volume *vol) \
{ \
return test_bit(NV_##flag, &(vol)->flags); \
} \
static inline void NVolSet##flag(ntfs_volume *vol) \
{ \
set_bit(NV_##flag, &(vol)->flags); \
} \
static inline void NVolClear##flag(ntfs_volume *vol) \
{ \
clear_bit(NV_##flag, &(vol)->flags); \
}
/* Emit the ntfs volume bitops functions. */
NVOL_FNS(ShowSystemFiles)
NVOL_FNS(CaseSensitive)
#endif /* _LINUX_NTFS_VOLUME_H */
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