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. */
/*
* 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 unl_err_out;
goto err_out;
}
/* Compressed data stream. Handled in compress.c. */
/* Compressed data streams are 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);
......
......@@ -35,7 +35,7 @@
* @type: attribute type (see layout.h)
*
* This structure exists only to provide a small structure for the
* ntfs_iget()/ntfs_test_inode()/ntfs_init_locked_inode() mechanism.
* ntfs_{attr_}iget()/ntfs_test_inode()/ntfs_init_locked_inode() mechanism.
*
* NOTE: Elements are ordered by size to make the structure as compact as
* possible on all architectures.
......@@ -112,14 +112,21 @@ static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na)
ntfs_inode *ni = NTFS_I(vi);
vi->i_ino = na->mft_no;
ni->type = na->type;
if (na->type == AT_INDEX_ALLOCATION)
NInoSetMstProtected(ni);
ni->name = na->name;
ni->name_len = na->name_len;
/* If initializing a normal inode, we are done. */
if (likely(na->type == AT_UNUSED))
return 0;
/* It is a fake inode. */
NInoSetAttr(ni);
/*
* We have I30 global constant as an optimization as it is the name
* in >99.9% of named attributes! The other <0.1% incur a GFP_ATOMIC
......@@ -143,6 +150,7 @@ static int ntfs_init_locked_inode(struct inode *vi, ntfs_attr *na)
typedef int (*test_t)(struct inode *, void *);
typedef int (*set_t)(struct inode *, void *);
static void ntfs_read_locked_inode(struct inode *vi);
static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi);
/**
* ntfs_iget - obtain a struct inode corresponding to a specific normal inode
......@@ -197,6 +205,62 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no)
return vi;
}
/**
* ntfs_attr_iget - obtain a struct inode corresponding to an attribute
* @base_vi: vfs base inode containing the attribute
* @type: attribute type
* @name: Unicode name of the attribute (NULL if unnamed)
* @name_len: length of @name in Unicode characters (0 if unnamed)
*
* Obtain the (fake) struct inode corresponding to the attribute specified by
* @type, @name, and @name_len, which is present in the base mft record
* specified by the vfs inode @base_vi.
*
* If the attribute inode is in the cache, it is just returned with an
* increased reference count. Otherwise, a new struct inode is allocated and
* initialized, and finally ntfs_read_locked_attr_inode() is called to read the
* attribute and fill in the inode structure.
*
* Return the struct inode of the attribute inode on success. Check the return
* value with IS_ERR() and if true, the function failed and the error code is
* obtained from PTR_ERR().
*/
struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPES type,
uchar_t *name, u32 name_len)
{
struct inode *vi;
ntfs_attr na;
int err;
na.mft_no = base_vi->i_ino;
na.type = type;
na.name = name;
na.name_len = name_len;
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
(set_t)ntfs_init_locked_inode, &na);
if (!vi)
return ERR_PTR(-ENOMEM);
err = 0;
/* If this is a freshly allocated inode, need to read it now. */
if (vi->i_state & I_NEW) {
err = ntfs_read_locked_attr_inode(base_vi, vi);
unlock_new_inode(vi);
}
/*
* There is no point in keeping bad attribute inodes around. This also
* simplifies things in that we never need to check for bad attribute
* inodes elsewhere.
*/
if (err) {
iput(vi);
vi = ERR_PTR(-EIO);
}
return vi;
}
struct inode *ntfs_alloc_big_inode(struct super_block *sb)
{
ntfs_inode *ni;
......@@ -685,9 +749,8 @@ static void ntfs_read_locked_inode(struct inode *vi)
goto put_unm_err_out;
}
if (ir->type != AT_FILE_NAME) {
ntfs_error(vi->i_sb, __FUNCTION__ "(): Indexed "
"attribute is not $FILE_NAME. Not "
"allowed.");
ntfs_error(vi->i_sb, "Indexed attribute is not "
"$FILE_NAME. Not allowed.");
goto put_unm_err_out;
}
if (ir->collation_rule != COLLATION_FILE_NAME) {
......@@ -856,7 +919,7 @@ static void ntfs_read_locked_inode(struct inode *vi)
/* Setup the operations for this inode. */
vi->i_op = &ntfs_dir_inode_ops;
vi->i_fop = &ntfs_dir_ops;
vi->i_mapping->a_ops = &ntfs_dir_aops;
vi->i_mapping->a_ops = &ntfs_aops;
} else {
/* It is a file. */
reinit_attr_search_ctx(ctx);
......@@ -995,7 +1058,7 @@ static void ntfs_read_locked_inode(struct inode *vi)
/* Setup the operations for this inode. */
vi->i_op = &ntfs_file_inode_ops;
vi->i_fop = &ntfs_file_ops;
vi->i_mapping->a_ops = &ntfs_file_aops;
vi->i_mapping->a_ops = &ntfs_aops;
}
/*
* The number of 512-byte blocks used on disk (for stat). This is in so
......@@ -1033,6 +1096,245 @@ static void ntfs_read_locked_inode(struct inode *vi)
return;
}
/**
* ntfs_read_locked_attr_inode - read an attribute inode from its base inode
* @base_vi: base inode
* @vi: attribute inode to read
*
* ntfs_read_locked_attr_inode() is called from the ntfs_attr_iget() to read
* the attribute inode described by @vi into memory from the base mft record
* described by @base_ni.
*
* ntfs_read_locked_attr_inode() maps, pins and locks the base inode for
* reading and looks up the attribute described by @vi before setting up the
* necessary fields in @vi as well as initializing the ntfs inode.
*
* Q: What locks are held when the function is called?
* A: i_state has I_LOCK set, hence the inode is locked, also
* i_count is set to 1, so it is not going to go away
*/
static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
{
ntfs_volume *vol = NTFS_SB(vi->i_sb);
ntfs_inode *ni, *base_ni;
MFT_RECORD *m;
attr_search_context *ctx;
int err = 0;
ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino);
ntfs_init_big_inode(vi);
ni = NTFS_I(vi);
base_ni = NTFS_I(base_vi);
/* Just mirror the values from the base inode. */
vi->i_blksize = base_vi->i_blksize;
vi->i_version = base_vi->i_version;
vi->i_uid = base_vi->i_uid;
vi->i_gid = base_vi->i_gid;
vi->i_nlink = base_vi->i_nlink;
vi->i_mtime = base_vi->i_mtime;
vi->i_ctime = base_vi->i_ctime;
vi->i_atime = base_vi->i_atime;
ni->seq_no = base_ni->seq_no;
/* Set inode type to zero but preserve permissions. */
vi->i_mode = base_vi->i_mode & ~S_IFMT;
m = map_mft_record(READ, base_ni);
if (IS_ERR(m)) {
err = PTR_ERR(m);
goto err_out;
}
ctx = get_attr_search_ctx(base_ni, m);
if (!ctx) {
err = -ENOMEM;
goto unm_err_out;
}
/* Find the attribute. */
if (!lookup_attr(ni->type, ni->name, ni->name_len, IGNORE_CASE, 0,
NULL, 0, ctx))
goto unm_err_out;
if (!ctx->attr->non_resident) {
if (NInoMstProtected(ni) || ctx->attr->flags) {
ntfs_error(vi->i_sb, "Found mst protected attribute "
"or attribute with non-zero flags but "
"the attribute is resident (mft_no "
"0x%lx, type 0x%x, name_len %i). "
"Please report you saw this message "
"to linux-ntfs-dev@lists.sf.net",
vi->i_ino, ni->type, ni->name_len);
goto unm_err_out;
}
/*
* Resident attribute. Make all sizes equal for simplicity in
* read code paths.
*/
vi->i_size = ni->initialized_size = ni->allocated_size =
le32_to_cpu(ctx->attr->_ARA(value_length));
} else {
NInoSetNonResident(ni);
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is compressed (mft_no 0x%lx, "
"type 0x%x, name_len %i). "
"Please report you saw this "
"message to linux-ntfs-dev@"
"lists.sf.net", vi->i_ino,
ni->type, ni->name_len);
goto unm_err_out;
}
NInoSetCompressed(ni);
if ((ni->type != AT_DATA) || (ni->type == AT_DATA &&
ni->name_len)) {
ntfs_error(vi->i_sb, "Found compressed non-"
"data or named data attribute "
"(mft_no 0x%lx, type 0x%x, "
"name_len %i). Please report "
"you saw this message to "
"linux-ntfs-dev@lists.sf.net",
vi->i_ino, ni->type,
ni->name_len);
goto unm_err_out;
}
if (vol->cluster_size > 4096) {
ntfs_error(vi->i_sb, "Found "
"compressed attribute but "
"compression is disabled due "
"to cluster size (%i) > 4kiB.",
vol->cluster_size);
goto unm_err_out;
}
if ((ctx->attr->flags & ATTR_COMPRESSION_MASK)
!= ATTR_IS_COMPRESSED) {
ntfs_error(vi->i_sb, "Found unknown "
"compression method or "
"corrupt file.");
goto unm_err_out;
}
ni->_ICF(compression_block_clusters) = 1U <<
ctx->attr->_ANR(compression_unit);
if (ctx->attr->_ANR(compression_unit) != 4) {
ntfs_error(vi->i_sb, "Found "
"nonstandard compression unit "
"(%u instead of 4). Cannot "
"handle this. This might "
"indicate corruption so you "
"should run chkdsk.",
ctx->attr->_ANR(compression_unit));
err = -EOPNOTSUPP;
goto unm_err_out;
}
ni->_ICF(compression_block_size) = 1U << (
ctx->attr->_ANR(
compression_unit) +
vol->cluster_size_bits);
ni->_ICF(compression_block_size_bits) = ffs(
ni->_ICF(compression_block_size)) - 1;
}
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "Found encrypted "
"and compressed data.");
goto unm_err_out;
}
if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is encrypted (mft_no 0x%lx, "
"type 0x%x, name_len %i). "
"Please report you saw this "
"message to linux-ntfs-dev@"
"lists.sf.net", vi->i_ino,
ni->type, ni->name_len);
goto unm_err_out;
}
NInoSetEncrypted(ni);
}
if (ctx->attr->flags & ATTR_IS_SPARSE) {
if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is sparse (mft_no 0x%lx, "
"type 0x%x, name_len %i). "
"Please report you saw this "
"message to linux-ntfs-dev@"
"lists.sf.net", vi->i_ino,
ni->type, ni->name_len);
goto unm_err_out;
}
NInoSetSparse(ni);
}
if (ctx->attr->_ANR(lowest_vcn)) {
ntfs_error(vi->i_sb, "First extent of attribute has "
"non-zero lowest_vcn. Inode is "
"corrupt. You should run chkdsk.");
goto unm_err_out;
}
/* Setup all the sizes. */
vi->i_size = sle64_to_cpu(ctx->attr->_ANR(data_size));
ni->initialized_size = sle64_to_cpu(
ctx->attr->_ANR(initialized_size));
ni->allocated_size = sle64_to_cpu(
ctx->attr->_ANR(allocated_size));
if (NInoCompressed(ni)) {
ni->_ICF(compressed_size) = sle64_to_cpu(
ctx->attr->_ANR(compressed_size));
if (vi->i_size != ni->initialized_size)
ntfs_warning(vi->i_sb, "Compressed attribute "
"with data_size unequal to "
"initialized size found. This "
"will probably cause problems "
"when trying to access the "
"file. Please notify "
"linux-ntfs-dev@ lists.sf.net "
"that you saw this message.");
}
}
/* Setup the operations for this attribute inode. */
vi->i_op = NULL;
vi->i_fop = NULL;
vi->i_mapping->a_ops = &ntfs_aops;
if (!NInoCompressed(ni))
vi->i_blocks = ni->allocated_size >> 9;
else
vi->i_blocks = ni->_ICF(compressed_size) >> 9;
/*
* Make sure the base inode doesn't go away and attach it to the
* attribute inode.
*/
igrab(base_vi);
ni->_INE(base_ntfs_ino) = base_ni;
ni->nr_extents = -1;
put_attr_search_ctx(ctx);
unmap_mft_record(READ, ni);
ntfs_debug("Done.");
return 0;
unm_err_out:
if (!err)
err = -EIO;
if (ctx)
put_attr_search_ctx(ctx);
unmap_mft_record(READ, base_ni);
err_out:
ntfs_error(vi->i_sb, "Failed with error code %i while reading "
"attribute inode (mft_no 0x%lx, type 0x%x, name_len "
"%i.", -err, vi->i_ino, ni->type, ni->name_len);
make_bad_inode(vi);
return err;
}
/**
* ntfs_read_inode_mount - special read_inode for mount time use only
* @vi: inode to read
......@@ -1590,6 +1892,13 @@ void ntfs_clear_big_inode(struct inode *vi)
if (ni->_IDM(bmp_rl).rl)
ntfs_free(ni->_IDM(bmp_rl).rl);
up_write(&ni->_IDM(bmp_rl).lock);
} else if (NInoAttr(ni)) {
/* Release the base inode if we are holding it. */
if (ni->nr_extents == -1) {
iput(VFS_I(ni->_INE(base_ntfs_ino)));
ni->nr_extents = 0;
ni->_INE(base_ntfs_ino) = NULL;
}
}
return;
}
......
......@@ -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 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 inode_operations ntfs_dir_inode_ops;
extern struct address_space_operations ntfs_dir_aops;
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