Commit aae109de authored by Richard Russon's avatar Richard Russon

Merge flatcap.org:/home/flatcap/backup/bk/ntfs-2.6

into flatcap.org:/home/flatcap/backup/bk/ntfs-2.6-devel
parents 3beb711b bf71a676
...@@ -317,8 +317,8 @@ prototypes: ...@@ -317,8 +317,8 @@ prototypes:
locking rules: locking rules:
called from interrupts. In other words, extreme care is needed here. called from interrupts. In other words, extreme care is needed here.
bh is locked, but that's all warranties we have here. Currently only RAID1, bh is locked, but that's all warranties we have here. Currently only RAID1,
highmem and fs/buffer.c are providing these. Block devices call this method highmem, fs/buffer.c, and fs/ntfs/aops.c are providing these. Block devices
upon the IO completion. call this method upon the IO completion.
--------------------------- block_device_operations ----------------------- --------------------------- block_device_operations -----------------------
prototypes: prototypes:
......
...@@ -258,10 +258,10 @@ Then you would use ldminfo in dump mode to obtain the necessary information: ...@@ -258,10 +258,10 @@ Then you would use ldminfo in dump mode to obtain the necessary information:
$ ./ldminfo --dump /dev/hda $ ./ldminfo --dump /dev/hda
This would dump the LDM database found on /dev/hda which describes all of your This would dump the LDM database found on /dev/hda which describes all of your
dinamic disks and all the volumes on them. At the bottom you will see the dynamic disks and all the volumes on them. At the bottom you will see the
VOLUME DEFINITIONS section which is all you really need. You may need to look VOLUME DEFINITIONS section which is all you really need. You may need to look
further above to determine which of the disks in the volume definitions is further above to determine which of the disks in the volume definitions is
which device in Linux. Hint: Run ldminfo on each of your dinamic disks and which device in Linux. Hint: Run ldminfo on each of your dynamic disks and
look at the Disk Id close to the top of the output for each (the PRIVATE HEADER look at the Disk Id close to the top of the output for each (the PRIVATE HEADER
section). You can then find these Disk Ids in the VBLK DATABASE section in the section). You can then find these Disk Ids in the VBLK DATABASE section in the
<Disk> components where you will get the LDM Name for the disk that is found in <Disk> components where you will get the LDM Name for the disk that is found in
......
...@@ -12,6 +12,10 @@ ToDo/Notes: ...@@ -12,6 +12,10 @@ ToDo/Notes:
OTOH, perhaps i_sem, which is held accross generic_file_write is OTOH, perhaps i_sem, which is held accross generic_file_write is
sufficient for synchronisation here. We then just need to make sure sufficient for synchronisation here. We then just need to make sure
ntfs_readpage/writepage/truncate interoperate properly with us. ntfs_readpage/writepage/truncate interoperate properly with us.
UPDATE: The above is all ok as it is due to i_sem held. The only
thing that needs to be checked is ntfs_writepage() which does not
hold i_sem. It cannot change i_size but it needs to cope with a
concurrent i_size change.
- Implement mft.c::sync_mft_mirror_umount(). We currently will just - Implement mft.c::sync_mft_mirror_umount(). We currently will just
leave the volume dirty on umount if the final iput(vol->mft_ino) leave the volume dirty on umount if the final iput(vol->mft_ino)
causes a write of any mirrored mft records due to the mft mirror causes a write of any mirrored mft records due to the mft mirror
...@@ -21,6 +25,67 @@ ToDo/Notes: ...@@ -21,6 +25,67 @@ ToDo/Notes:
- Enable the code for setting the NT4 compatibility flag when we start - Enable the code for setting the NT4 compatibility flag when we start
making NTFS 1.2 specific modifications. making NTFS 1.2 specific modifications.
2.1.22-WIP
- Improve error handling in fs/ntfs/inode.c::ntfs_truncate().
- Change fs/ntfs/inode.c::ntfs_truncate() to return an error code
instead of void and provide a helper ntfs_truncate_vfs() for the
vfs ->truncate method.
- Add a new ntfs inode flag NInoTruncateFailed() and modify
fs/ntfs/inode.c::ntfs_truncate() to set and clear it appropriately.
- Fix min_size and max_size definitions in ATTR_DEF structure in
fs/ntfs/layout.h to be signed.
- Add attribute definition handling helpers to fs/ntfs/attrib.[hc]:
ntfs_attr_size_bounds_check(), ntfs_attr_can_be_non_resident(), and
ntfs_attr_can_be_resident(), which in turn use the new private helper
ntfs_attr_find_in_attrdef().
- In fs/ntfs/aops.c::mark_ntfs_record_dirty(), take the
mapping->private_lock around the dirtying of the buffer heads
analagous to the way it is done in __set_page_dirty_buffers().
- Ensure the mft record size does not exceed the PAGE_CACHE_SIZE at
mount time as this cannot work with the current implementation.
- Check for location of attribute name and improve error handling in
general in fs/ntfs/inode.c::ntfs_read_locked_inode() and friends.
- In fs/ntfs/aops.c::ntfs_writepage(), if the page is fully outside
i_size, i.e. race with truncate, invalidate the buffers on the page
so that they become freeable and hence the page does not leak.
- Remove unused function fs/ntfs/runlist.c::ntfs_rl_merge(). (Adrian
Bunk)
- Fix stupid bug in fs/ntfs/attrib.c::ntfs_attr_find() that resulted in
a NULL pointer dereference in the error code path when a corrupt
attribute was found. (Thanks to Domen Puncer for the bug report.)
- Add MODULE_VERSION() to fs/ntfs/super.c.
- Make several functions and variables static. (Adrian Bunk)
- Modify fs/ntfs/aops.c::mark_ntfs_record_dirty() so it allocates
buffers for the page if they are not present and then marks the
buffers belonging to the ntfs record dirty. This causes the buffers
to become busy and hence they are safe from removal until the page
has been written out.
- Fix stupid bug in fs/ntfs/attrib.c::ntfs_external_attr_find() in the
error handling code path that resulted in a BUG() due to trying to
unmap an extent mft record when the mapping of it had failed and it
thus was not mapped. (Thanks to Ken MacFerrin for the bug report.)
- Drop the runlist lock after the vcn has been read in
fs/ntfs/lcnalloc.c::__ntfs_cluster_free().
- Rewrite handling of multi sector transfer errors. We now do not set
PageError() when such errors are detected in the async i/o handler
fs/ntfs/aops.c::ntfs_end_buffer_async_read(). All users of mst
protected attributes now check the magic of each ntfs record as they
use it and act appropriately. This has the effect of making errors
granular per ntfs record rather than per page which solves the case
where we cannot access any of the ntfs records in a page when a
single one of them had an mst error. (Thanks to Ken MacFerrin for
the bug report.)
- Fix error handling in fs/ntfs/quota.c::ntfs_mark_quotas_out_of_date()
where we failed to release i_sem on the $Quota/$Q attribute inode.
- Fix bug in handling of bad inodes in fs/ntfs/namei.c::ntfs_lookup().
- Add mapping of unmapped buffers to all remaining code paths, i.e.
fs/ntfs/aops.c::ntfs_write_mst_block(), mft.c::ntfs_sync_mft_mirror(),
and write_mft_record_nolock(). From now on we require that the
complete runlist for the mft mirror is always mapped into memory.
- Add creation of buffers to fs/ntfs/mft.c::ntfs_sync_mft_mirror().
- Improve error handling in fs/ntfs/aops.c::ntfs_{read,write}_block().
2.1.21 - Fix some races and bugs, rewrite mft write code, add mft allocator. 2.1.21 - Fix some races and bugs, rewrite mft write code, add mft allocator.
- Implement extent mft record deallocation - Implement extent mft record deallocation
......
...@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ ...@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \ index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
unistr.o upcase.o unistr.o upcase.o
EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.21\" EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.22-WIP\"
ifeq ($(CONFIG_NTFS_DEBUG),y) ifeq ($(CONFIG_NTFS_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
......
This diff is collapsed.
...@@ -55,6 +55,13 @@ static inline void ntfs_unmap_page(struct page *page) ...@@ -55,6 +55,13 @@ static inline void ntfs_unmap_page(struct page *page)
* method defined in the address space operations of @mapping and the page is * method defined in the address space operations of @mapping and the page is
* added to the page cache of @mapping in the process. * added to the page cache of @mapping in the process.
* *
* If the page belongs to an mst protected attribute and it is marked as such
* in its ntfs inode (NInoMstProtected()) the mst fixups are applied but no
* error checking is performed. This means the caller has to verify whether
* the ntfs record(s) contained in the page are valid or not using one of the
* ntfs_is_XXXX_record{,p}() macros, where XXXX is the record type you are
* expecting to see. (For details of the macros, see fs/ntfs/layout.h.)
*
* If the page is in high memory it is mapped into memory directly addressible * If the page is in high memory it is mapped into memory directly addressible
* by the kernel. * by the kernel.
* *
......
...@@ -24,8 +24,10 @@ ...@@ -24,8 +24,10 @@
#include "attrib.h" #include "attrib.h"
#include "debug.h" #include "debug.h"
#include "layout.h"
#include "mft.h" #include "mft.h"
#include "ntfs.h" #include "ntfs.h"
#include "types.h"
/** /**
* ntfs_map_runlist - map (a part of) a runlist of an ntfs inode * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode
...@@ -248,19 +250,10 @@ static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name, ...@@ -248,19 +250,10 @@ static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name,
const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx)
{ {
ATTR_RECORD *a; ATTR_RECORD *a;
ntfs_volume *vol; ntfs_volume *vol = ctx->ntfs_ino->vol;
ntfschar *upcase; ntfschar *upcase = vol->upcase;
u32 upcase_len; u32 upcase_len = vol->upcase_len;
if (ic == IGNORE_CASE) {
vol = ctx->ntfs_ino->vol;
upcase = vol->upcase;
upcase_len = vol->upcase_len;
} else {
vol = NULL;
upcase = NULL;
upcase_len = 0;
}
/* /*
* Iterate over attributes in mft record starting at @ctx->attr, or the * Iterate over attributes in mft record starting at @ctx->attr, or the
* attribute following that, if @ctx->is_first is TRUE. * attribute following that, if @ctx->is_first is TRUE.
...@@ -352,7 +345,7 @@ static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name, ...@@ -352,7 +345,7 @@ static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name,
return -ENOENT; return -ENOENT;
} }
} }
ntfs_error(NULL, "Inode is corrupt. Run chkdsk."); ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk.");
NVolSetErrors(vol); NVolSetErrors(vol);
return -EIO; return -EIO;
} }
...@@ -662,7 +655,6 @@ static int ntfs_external_attr_find(const ATTR_TYPE type, ...@@ -662,7 +655,6 @@ static int ntfs_external_attr_find(const ATTR_TYPE type,
ctx->mrec = map_extent_mft_record(base_ni, ctx->mrec = map_extent_mft_record(base_ni,
le64_to_cpu( le64_to_cpu(
al_entry->mft_reference), &ni); al_entry->mft_reference), &ni);
ctx->ntfs_ino = ni;
if (IS_ERR(ctx->mrec)) { if (IS_ERR(ctx->mrec)) {
ntfs_error(vol->sb, "Failed to map " ntfs_error(vol->sb, "Failed to map "
"extent mft record " "extent mft record "
...@@ -674,8 +666,11 @@ static int ntfs_external_attr_find(const ATTR_TYPE type, ...@@ -674,8 +666,11 @@ static int ntfs_external_attr_find(const ATTR_TYPE type,
err = PTR_ERR(ctx->mrec); err = PTR_ERR(ctx->mrec);
if (err == -ENOENT) if (err == -ENOENT)
err = -EIO; err = -EIO;
/* Cause @ctx to be sanitized below. */
ni = NULL;
break; break;
} }
ctx->ntfs_ino = ni;
} }
ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
le16_to_cpu(ctx->mrec->attrs_offset)); le16_to_cpu(ctx->mrec->attrs_offset));
...@@ -747,7 +742,8 @@ static int ntfs_external_attr_find(const ATTR_TYPE type, ...@@ -747,7 +742,8 @@ static int ntfs_external_attr_find(const ATTR_TYPE type,
err = -EIO; err = -EIO;
} }
if (ni != base_ni) { if (ni != base_ni) {
unmap_extent_mft_record(ni); if (ni)
unmap_extent_mft_record(ni);
ctx->ntfs_ino = base_ni; ctx->ntfs_ino = base_ni;
ctx->mrec = ctx->base_mrec; ctx->mrec = ctx->base_mrec;
ctx->attr = ctx->base_attr; ctx->attr = ctx->base_attr;
...@@ -949,6 +945,133 @@ void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) ...@@ -949,6 +945,133 @@ void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
return; return;
} }
/**
* ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
* @vol: ntfs volume to which the attribute belongs
* @type: attribute type which to find
*
* Search for the attribute definition record corresponding to the attribute
* @type in the $AttrDef system file.
*
* Return the attribute type definition record if found and NULL if not found.
*/
static ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
const ATTR_TYPE type)
{
ATTR_DEF *ad;
BUG_ON(!vol->attrdef);
BUG_ON(!type);
for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
vol->attrdef_size && ad->type; ++ad) {
/* We have not found it yet, carry on searching. */
if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type)))
continue;
/* We found the attribute; return it. */
if (likely(ad->type == type))
return ad;
/* We have gone too far already. No point in continuing. */
break;
}
/* Attribute not found. */
ntfs_debug("Attribute type 0x%x not found in $AttrDef.",
le32_to_cpu(type));
return NULL;
}
/**
* ntfs_attr_size_bounds_check - check a size of an attribute type for validity
* @vol: ntfs volume to which the attribute belongs
* @type: attribute type which to check
* @size: size which to check
*
* Check whether the @size in bytes is valid for an attribute of @type on the
* ntfs volume @vol. This information is obtained from $AttrDef system file.
*
* Return 0 if valid, -ERANGE if not valid, or -ENOENT if the attribute is not
* listed in $AttrDef.
*/
int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type,
const s64 size)
{
ATTR_DEF *ad;
BUG_ON(size < 0);
/*
* $ATTRIBUTE_LIST has a maximum size of 256kiB, but this is not
* listed in $AttrDef.
*/
if (unlikely(type == AT_ATTRIBUTE_LIST && size > 256 * 1024))
return -ERANGE;
/* Get the $AttrDef entry for the attribute @type. */
ad = ntfs_attr_find_in_attrdef(vol, type);
if (unlikely(!ad))
return -ENOENT;
/* Do the bounds check. */
if (((sle64_to_cpu(ad->min_size) > 0) &&
size < sle64_to_cpu(ad->min_size)) ||
((sle64_to_cpu(ad->max_size) > 0) && size >
sle64_to_cpu(ad->max_size)))
return -ERANGE;
return 0;
}
/**
* ntfs_attr_can_be_non_resident - check if an attribute can be non-resident
* @vol: ntfs volume to which the attribute belongs
* @type: attribute type which to check
*
* Check whether the attribute of @type on the ntfs volume @vol is allowed to
* be non-resident. This information is obtained from $AttrDef system file.
*
* Return 0 if the attribute is allowed to be non-resident, -EPERM if not, or
* -ENOENT if the attribute is not listed in $AttrDef.
*/
int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
{
ATTR_DEF *ad;
/*
* $DATA is always allowed to be non-resident even if $AttrDef does not
* specify this in the flags of the $DATA attribute definition record.
*/
if (type == AT_DATA)
return 0;
/* Find the attribute definition record in $AttrDef. */
ad = ntfs_attr_find_in_attrdef(vol, type);
if (unlikely(!ad))
return -ENOENT;
/* Check the flags and return the result. */
if (ad->flags & CAN_BE_NON_RESIDENT)
return 0;
return -EPERM;
}
/**
* ntfs_attr_can_be_resident - check if an attribute can be resident
* @vol: ntfs volume to which the attribute belongs
* @type: attribute type which to check
*
* Check whether the attribute of @type on the ntfs volume @vol is allowed to
* be resident. This information is derived from our ntfs knowledge and may
* not be completely accurate, especially when user defined attributes are
* present. Basically we allow everything to be resident except for index
* allocation and $EA attributes.
*
* Return 0 if the attribute is allowed to be non-resident and -EPERM if not.
*
* Warning: In the system file $MFT the attribute $Bitmap must be non-resident
* otherwise windows will not boot (blue screen of death)! We cannot
* check for this here as we do not know which inode's $Bitmap is
* being asked about so the caller needs to special case this.
*/
int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
{
if (type != AT_INDEX_ALLOCATION && type != AT_EA)
return 0;
return -EPERM;
}
/** /**
* ntfs_attr_record_resize - resize an attribute record * ntfs_attr_record_resize - resize an attribute record
* @m: mft record containing attribute record * @m: mft record containing attribute record
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "layout.h" #include "layout.h"
#include "inode.h" #include "inode.h"
#include "runlist.h" #include "runlist.h"
#include "volume.h"
/** /**
* ntfs_attr_search_ctx - used in attribute search functions * ntfs_attr_search_ctx - used in attribute search functions
...@@ -84,6 +85,13 @@ extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, ...@@ -84,6 +85,13 @@ extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
MFT_RECORD *mrec); MFT_RECORD *mrec);
extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx); extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
const ATTR_TYPE type, const s64 size);
extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol,
const ATTR_TYPE type);
extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
const ATTR_TYPE type);
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size); extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt,
......
...@@ -127,8 +127,8 @@ void __ntfs_debug (const char *file, int line, const char *function, ...@@ -127,8 +127,8 @@ void __ntfs_debug (const char *file, int line, const char *function,
va_start(args, fmt); va_start(args, fmt);
vsnprintf(err_buf, sizeof(err_buf), fmt, args); vsnprintf(err_buf, sizeof(err_buf), fmt, args);
va_end(args); va_end(args);
printk(KERN_DEBUG "NTFS-fs DEBUG (%s, %d): %s(): %s\n", printk(KERN_DEBUG "NTFS-fs DEBUG (%s, %d): %s(): %s\n", file, line,
file, line, flen ? function : "", err_buf); flen ? function : "", err_buf);
spin_unlock(&err_buf_lock); spin_unlock(&err_buf_lock);
} }
...@@ -141,8 +141,7 @@ void ntfs_debug_dump_runlist(const runlist_element *rl) ...@@ -141,8 +141,7 @@ void ntfs_debug_dump_runlist(const runlist_element *rl)
if (!debug_msgs) if (!debug_msgs)
return; return;
printk(KERN_DEBUG "NTFS-fs DEBUG: Dumping runlist (values " printk(KERN_DEBUG "NTFS-fs DEBUG: Dumping runlist (values in hex):\n");
"in hex):\n");
if (!rl) { if (!rl) {
printk(KERN_DEBUG "Run list not present.\n"); printk(KERN_DEBUG "Run list not present.\n");
return; return;
...@@ -157,14 +156,14 @@ void ntfs_debug_dump_runlist(const runlist_element *rl) ...@@ -157,14 +156,14 @@ void ntfs_debug_dump_runlist(const runlist_element *rl)
if (index > -LCN_ENOENT - 1) if (index > -LCN_ENOENT - 1)
index = 3; index = 3;
printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n", printk(KERN_DEBUG "%-16Lx %s %-16Lx%s\n",
(rl + i)->vcn, lcn_str[index], (rl + i)->vcn, lcn_str[index],
(rl + i)->length, (rl + i)->length ? (rl + i)->length, (rl + i)->length ?
"" : " (runlist end)"); "" : " (runlist end)");
} else } else
printk(KERN_DEBUG "%-16Lx %-16Lx %-16Lx%s\n", printk(KERN_DEBUG "%-16Lx %-16Lx %-16Lx%s\n",
(rl + i)->vcn, (rl + i)->lcn, (rl + i)->vcn, (rl + i)->lcn,
(rl + i)->length, (rl + i)->length ? (rl + i)->length, (rl + i)->length ?
"" : " (runlist end)"); "" : " (runlist end)");
if (!(rl + i)->length) if (!(rl + i)->length)
break; break;
} }
......
...@@ -300,7 +300,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -300,7 +300,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "No index allocation attribute but index entry " ntfs_error(sb, "No index allocation attribute but index entry "
"requires one. Directory inode 0x%lx is " "requires one. Directory inode 0x%lx is "
"corrupt or driver bug.", dir_ni->mft_no); "corrupt or driver bug.", dir_ni->mft_no);
err = -EIO;
goto err_out; goto err_out;
} }
/* Get the starting vcn of the index_block holding the child node. */ /* Get the starting vcn of the index_block holding the child node. */
...@@ -338,7 +337,13 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -338,7 +337,13 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) { if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) {
ntfs_error(sb, "Out of bounds check failed. Corrupt directory " ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
"inode 0x%lx or driver bug.", dir_ni->mft_no); "inode 0x%lx or driver bug.", dir_ni->mft_no);
err = -EIO; goto unm_err_out;
}
/* Catch multi sector transfer fixup errors. */
if (unlikely(!ntfs_is_indx_record(ia->magic))) {
ntfs_error(sb, "Directory index record with vcn 0x%llx is "
"corrupt. Corrupt inode 0x%lx. Run chkdsk.",
(unsigned long long)vcn, dir_ni->mft_no);
goto unm_err_out; goto unm_err_out;
} }
if (sle64_to_cpu(ia->index_block_vcn) != vcn) { if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
...@@ -348,7 +353,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -348,7 +353,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
"bug.", (unsigned long long) "bug.", (unsigned long long)
sle64_to_cpu(ia->index_block_vcn), sle64_to_cpu(ia->index_block_vcn),
(unsigned long long)vcn, dir_ni->mft_no); (unsigned long long)vcn, dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
...@@ -360,7 +364,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -360,7 +364,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
(unsigned long long)vcn, dir_ni->mft_no, (unsigned long long)vcn, dir_ni->mft_no,
le32_to_cpu(ia->index.allocated_size) + 0x18, le32_to_cpu(ia->index.allocated_size) + 0x18,
dir_ni->itype.index.block_size); dir_ni->itype.index.block_size);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
index_end = (u8*)ia + dir_ni->itype.index.block_size; index_end = (u8*)ia + dir_ni->itype.index.block_size;
...@@ -370,7 +373,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -370,7 +373,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
"Cannot access! This is probably a bug in the " "Cannot access! This is probably a bug in the "
"driver.", (unsigned long long)vcn, "driver.", (unsigned long long)vcn,
dir_ni->mft_no); dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
...@@ -378,7 +380,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -378,7 +380,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of directory " ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of directory "
"inode 0x%lx exceeds maximum size.", "inode 0x%lx exceeds maximum size.",
(unsigned long long)vcn, dir_ni->mft_no); (unsigned long long)vcn, dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* The first index entry. */ /* The first index entry. */
...@@ -398,7 +399,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -398,7 +399,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "Index entry out of bounds in " ntfs_error(sb, "Index entry out of bounds in "
"directory inode 0x%lx.", "directory inode 0x%lx.",
dir_ni->mft_no); dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* /*
...@@ -551,7 +551,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -551,7 +551,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "Index entry with child node found in " ntfs_error(sb, "Index entry with child node found in "
"a leaf node in directory inode 0x%lx.", "a leaf node in directory inode 0x%lx.",
dir_ni->mft_no); dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* Child node present, descend into it. */ /* Child node present, descend into it. */
...@@ -572,7 +571,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -572,7 +571,6 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
} }
ntfs_error(sb, "Negative child node vcn in directory inode " ntfs_error(sb, "Negative child node vcn in directory inode "
"0x%lx.", dir_ni->mft_no); "0x%lx.", dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* /*
...@@ -591,6 +589,8 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -591,6 +589,8 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
unlock_page(page); unlock_page(page);
ntfs_unmap_page(page); ntfs_unmap_page(page);
err_out: err_out:
if (!err)
err = -EIO;
if (ctx) if (ctx)
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
if (m) if (m)
...@@ -601,8 +601,7 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -601,8 +601,7 @@ MFT_REF ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
} }
return ERR_MREF(err); return ERR_MREF(err);
dir_err_out: dir_err_out:
ntfs_error(sb, "Corrupt directory. Aborting lookup."); ntfs_error(sb, "Corrupt directory. Aborting lookup.");
err = -EIO;
goto err_out; goto err_out;
} }
...@@ -780,7 +779,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -780,7 +779,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "No index allocation attribute but index entry " ntfs_error(sb, "No index allocation attribute but index entry "
"requires one. Directory inode 0x%lx is " "requires one. Directory inode 0x%lx is "
"corrupt or driver bug.", dir_ni->mft_no); "corrupt or driver bug.", dir_ni->mft_no);
err = -EIO;
goto err_out; goto err_out;
} }
/* Get the starting vcn of the index_block holding the child node. */ /* Get the starting vcn of the index_block holding the child node. */
...@@ -818,7 +816,13 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -818,7 +816,13 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) { if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) {
ntfs_error(sb, "Out of bounds check failed. Corrupt directory " ntfs_error(sb, "Out of bounds check failed. Corrupt directory "
"inode 0x%lx or driver bug.", dir_ni->mft_no); "inode 0x%lx or driver bug.", dir_ni->mft_no);
err = -EIO; goto unm_err_out;
}
/* Catch multi sector transfer fixup errors. */
if (unlikely(!ntfs_is_indx_record(ia->magic))) {
ntfs_error(sb, "Directory index record with vcn 0x%llx is "
"corrupt. Corrupt inode 0x%lx. Run chkdsk.",
(unsigned long long)vcn, dir_ni->mft_no);
goto unm_err_out; goto unm_err_out;
} }
if (sle64_to_cpu(ia->index_block_vcn) != vcn) { if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
...@@ -828,7 +832,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -828,7 +832,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
"bug.", (unsigned long long) "bug.", (unsigned long long)
sle64_to_cpu(ia->index_block_vcn), sle64_to_cpu(ia->index_block_vcn),
(unsigned long long)vcn, dir_ni->mft_no); (unsigned long long)vcn, dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
...@@ -840,7 +843,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -840,7 +843,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
(unsigned long long)vcn, dir_ni->mft_no, (unsigned long long)vcn, dir_ni->mft_no,
le32_to_cpu(ia->index.allocated_size) + 0x18, le32_to_cpu(ia->index.allocated_size) + 0x18,
dir_ni->itype.index.block_size); dir_ni->itype.index.block_size);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
index_end = (u8*)ia + dir_ni->itype.index.block_size; index_end = (u8*)ia + dir_ni->itype.index.block_size;
...@@ -850,7 +852,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -850,7 +852,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
"Cannot access! This is probably a bug in the " "Cannot access! This is probably a bug in the "
"driver.", (unsigned long long)vcn, "driver.", (unsigned long long)vcn,
dir_ni->mft_no); dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
...@@ -858,7 +859,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -858,7 +859,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of directory " ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of directory "
"inode 0x%lx exceeds maximum size.", "inode 0x%lx exceeds maximum size.",
(unsigned long long)vcn, dir_ni->mft_no); (unsigned long long)vcn, dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* The first index entry. */ /* The first index entry. */
...@@ -878,7 +878,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -878,7 +878,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "Index entry out of bounds in " ntfs_error(sb, "Index entry out of bounds in "
"directory inode 0x%lx.", "directory inode 0x%lx.",
dir_ni->mft_no); dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* /*
...@@ -962,7 +961,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -962,7 +961,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
ntfs_error(sb, "Index entry with child node found in " ntfs_error(sb, "Index entry with child node found in "
"a leaf node in directory inode 0x%lx.", "a leaf node in directory inode 0x%lx.",
dir_ni->mft_no); dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* Child node present, descend into it. */ /* Child node present, descend into it. */
...@@ -982,7 +980,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -982,7 +980,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
} }
ntfs_error(sb, "Negative child node vcn in directory inode " ntfs_error(sb, "Negative child node vcn in directory inode "
"0x%lx.", dir_ni->mft_no); "0x%lx.", dir_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* No child node, return -ENOENT. */ /* No child node, return -ENOENT. */
...@@ -992,6 +989,8 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -992,6 +989,8 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
unlock_page(page); unlock_page(page);
ntfs_unmap_page(page); ntfs_unmap_page(page);
err_out: err_out:
if (!err)
err = -EIO;
if (ctx) if (ctx)
ntfs_attr_put_search_ctx(ctx); ntfs_attr_put_search_ctx(ctx);
if (m) if (m)
...@@ -999,7 +998,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname, ...@@ -999,7 +998,6 @@ u64 ntfs_lookup_inode_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
return ERR_MREF(err); return ERR_MREF(err);
dir_err_out: dir_err_out:
ntfs_error(sb, "Corrupt directory. Aborting lookup."); ntfs_error(sb, "Corrupt directory. Aborting lookup.");
err = -EIO;
goto err_out; goto err_out;
} }
...@@ -1340,6 +1338,14 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -1340,6 +1338,14 @@ static int ntfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
"inode 0x%lx or driver bug.", vdir->i_ino); "inode 0x%lx or driver bug.", vdir->i_ino);
goto err_out; goto err_out;
} }
/* Catch multi sector transfer fixup errors. */
if (unlikely(!ntfs_is_indx_record(ia->magic))) {
ntfs_error(sb, "Directory index record with vcn 0x%llx is "
"corrupt. Corrupt inode 0x%lx. Run chkdsk.",
(unsigned long long)ia_pos >>
ndir->itype.index.vcn_size_bits, vdir->i_ino);
goto err_out;
}
if (unlikely(sle64_to_cpu(ia->index_block_vcn) != (ia_pos & if (unlikely(sle64_to_cpu(ia->index_block_vcn) != (ia_pos &
~(s64)(ndir->itype.index.block_size - 1)) >> ~(s64)(ndir->itype.index.block_size - 1)) >>
ndir->itype.index.vcn_size_bits)) { ndir->itype.index.vcn_size_bits)) {
......
...@@ -145,7 +145,7 @@ struct file_operations ntfs_file_ops = { ...@@ -145,7 +145,7 @@ struct file_operations ntfs_file_ops = {
struct inode_operations ntfs_file_inode_ops = { struct inode_operations ntfs_file_inode_ops = {
#ifdef NTFS_RW #ifdef NTFS_RW
.truncate = ntfs_truncate, .truncate = ntfs_truncate_vfs,
.setattr = ntfs_setattr, .setattr = ntfs_setattr,
#endif /* NTFS_RW */ #endif /* NTFS_RW */
}; };
......
...@@ -263,7 +263,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -263,7 +263,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
ntfs_error(sb, "No index allocation attribute but index entry " ntfs_error(sb, "No index allocation attribute but index entry "
"requires one. Inode 0x%lx is corrupt or " "requires one. Inode 0x%lx is corrupt or "
"driver bug.", idx_ni->mft_no); "driver bug.", idx_ni->mft_no);
err = -EIO;
goto err_out; goto err_out;
} }
/* Get the starting vcn of the index_block holding the child node. */ /* Get the starting vcn of the index_block holding the child node. */
...@@ -301,7 +300,13 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -301,7 +300,13 @@ int ntfs_index_lookup(const void *key, const int key_len,
if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) { if ((u8*)ia < kaddr || (u8*)ia > kaddr + PAGE_CACHE_SIZE) {
ntfs_error(sb, "Out of bounds check failed. Corrupt inode " ntfs_error(sb, "Out of bounds check failed. Corrupt inode "
"0x%lx or driver bug.", idx_ni->mft_no); "0x%lx or driver bug.", idx_ni->mft_no);
err = -EIO; goto unm_err_out;
}
/* Catch multi sector transfer fixup errors. */
if (unlikely(!ntfs_is_indx_record(ia->magic))) {
ntfs_error(sb, "Index record with vcn 0x%llx is corrupt. "
"Corrupt inode 0x%lx. Run chkdsk.",
(long long)vcn, idx_ni->mft_no);
goto unm_err_out; goto unm_err_out;
} }
if (sle64_to_cpu(ia->index_block_vcn) != vcn) { if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
...@@ -311,7 +316,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -311,7 +316,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
(unsigned long long) (unsigned long long)
sle64_to_cpu(ia->index_block_vcn), sle64_to_cpu(ia->index_block_vcn),
(unsigned long long)vcn, idx_ni->mft_no); (unsigned long long)vcn, idx_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != if (le32_to_cpu(ia->index.allocated_size) + 0x18 !=
...@@ -323,7 +327,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -323,7 +327,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
idx_ni->mft_no, idx_ni->mft_no,
le32_to_cpu(ia->index.allocated_size) + 0x18, le32_to_cpu(ia->index.allocated_size) + 0x18,
idx_ni->itype.index.block_size); idx_ni->itype.index.block_size);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
index_end = (u8*)ia + idx_ni->itype.index.block_size; index_end = (u8*)ia + idx_ni->itype.index.block_size;
...@@ -333,7 +336,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -333,7 +336,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
"access! This is probably a bug in the " "access! This is probably a bug in the "
"driver.", (unsigned long long)vcn, "driver.", (unsigned long long)vcn,
idx_ni->mft_no); idx_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length); index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
...@@ -341,7 +343,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -341,7 +343,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of inode " ntfs_error(sb, "Size of index buffer (VCN 0x%llx) of inode "
"0x%lx exceeds maximum size.", "0x%lx exceeds maximum size.",
(unsigned long long)vcn, idx_ni->mft_no); (unsigned long long)vcn, idx_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* The first index entry. */ /* The first index entry. */
...@@ -359,11 +360,10 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -359,11 +360,10 @@ int ntfs_index_lookup(const void *key, const int key_len,
(u8*)ie + le16_to_cpu(ie->length) > index_end) { (u8*)ie + le16_to_cpu(ie->length) > index_end) {
ntfs_error(sb, "Index entry out of bounds in inode " ntfs_error(sb, "Index entry out of bounds in inode "
"0x%lx.", idx_ni->mft_no); "0x%lx.", idx_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* /*
* The last entry cannot contain a ket. It can however contain * The last entry cannot contain a key. It can however contain
* a pointer to a child node in the B+tree so we just break out. * a pointer to a child node in the B+tree so we just break out.
*/ */
if (ie->flags & INDEX_ENTRY_END) if (ie->flags & INDEX_ENTRY_END)
...@@ -377,7 +377,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -377,7 +377,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
le16_to_cpu(ie->length)) { le16_to_cpu(ie->length)) {
ntfs_error(sb, "Index entry out of bounds in inode " ntfs_error(sb, "Index entry out of bounds in inode "
"0x%lx.", idx_ni->mft_no); "0x%lx.", idx_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* If the keys match perfectly, we setup @ictx and return 0. */ /* If the keys match perfectly, we setup @ictx and return 0. */
...@@ -424,7 +423,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -424,7 +423,6 @@ int ntfs_index_lookup(const void *key, const int key_len,
if ((ia->index.flags & NODE_MASK) == LEAF_NODE) { if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
ntfs_error(sb, "Index entry with child node found in a leaf " ntfs_error(sb, "Index entry with child node found in a leaf "
"node in inode 0x%lx.", idx_ni->mft_no); "node in inode 0x%lx.", idx_ni->mft_no);
err = -EIO;
goto unm_err_out; goto unm_err_out;
} }
/* Child node present, descend into it. */ /* Child node present, descend into it. */
...@@ -446,11 +444,12 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -446,11 +444,12 @@ int ntfs_index_lookup(const void *key, const int key_len,
} }
ntfs_error(sb, "Negative child node vcn in inode 0x%lx.", ntfs_error(sb, "Negative child node vcn in inode 0x%lx.",
idx_ni->mft_no); idx_ni->mft_no);
err = -EIO;
unm_err_out: unm_err_out:
unlock_page(page); unlock_page(page);
ntfs_unmap_page(page); ntfs_unmap_page(page);
err_out: err_out:
if (!err)
err = -EIO;
if (actx) if (actx)
ntfs_attr_put_search_ctx(actx); ntfs_attr_put_search_ctx(actx);
if (m) if (m)
...@@ -458,6 +457,5 @@ int ntfs_index_lookup(const void *key, const int key_len, ...@@ -458,6 +457,5 @@ int ntfs_index_lookup(const void *key, const int key_len,
return err; return err;
idx_err_out: idx_err_out:
ntfs_error(sb, "Corrupt index. Aborting lookup."); ntfs_error(sb, "Corrupt index. Aborting lookup.");
err = -EIO;
goto err_out; goto err_out;
} }
This diff is collapsed.
...@@ -165,6 +165,7 @@ typedef enum { ...@@ -165,6 +165,7 @@ typedef enum {
NI_Sparse, /* 1: Unnamed data attr is sparse (f). NI_Sparse, /* 1: Unnamed data attr is sparse (f).
1: Create sparse files by default (d). 1: Create sparse files by default (d).
1: Attribute is sparse (a). */ 1: Attribute is sparse (a). */
NI_TruncateFailed, /* 1: Last ntfs_truncate() call failed. */
} ntfs_inode_state_bits; } ntfs_inode_state_bits;
/* /*
...@@ -216,6 +217,7 @@ NINO_FNS(IndexAllocPresent) ...@@ -216,6 +217,7 @@ NINO_FNS(IndexAllocPresent)
NINO_FNS(Compressed) NINO_FNS(Compressed)
NINO_FNS(Encrypted) NINO_FNS(Encrypted)
NINO_FNS(Sparse) NINO_FNS(Sparse)
NINO_FNS(TruncateFailed)
/* /*
* The full structure containing a ntfs_inode and a vfs struct inode. Used for * The full structure containing a ntfs_inode and a vfs struct inode. Used for
...@@ -300,7 +302,8 @@ extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt); ...@@ -300,7 +302,8 @@ extern int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt);
#ifdef NTFS_RW #ifdef NTFS_RW
extern void ntfs_truncate(struct inode *vi); extern int ntfs_truncate(struct inode *vi);
extern void ntfs_truncate_vfs(struct inode *vi);
extern int ntfs_setattr(struct dentry *dentry, struct iattr *attr); extern int ntfs_setattr(struct dentry *dentry, struct iattr *attr);
......
...@@ -589,8 +589,8 @@ typedef struct { ...@@ -589,8 +589,8 @@ typedef struct {
FIXME: What does it mean? (AIA) */ FIXME: What does it mean? (AIA) */
/* 88*/ COLLATION_RULE collation_rule; /* Default collation rule. */ /* 88*/ COLLATION_RULE collation_rule; /* Default collation rule. */
/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */ /* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */
/* 90*/ le64 min_size; /* Optional minimum attribute size. */ /* 90*/ sle64 min_size; /* Optional minimum attribute size. */
/* 98*/ le64 max_size; /* Maximum size of attribute. */ /* 98*/ sle64 max_size; /* Maximum size of attribute. */
/* sizeof() = 0xa0 or 160 bytes */ /* sizeof() = 0xa0 or 160 bytes */
} __attribute__ ((__packed__)) ATTR_DEF; } __attribute__ ((__packed__)) ATTR_DEF;
......
...@@ -903,8 +903,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count, ...@@ -903,8 +903,8 @@ s64 __ntfs_cluster_free(struct inode *vi, const VCN start_vcn, s64 count,
* Attempt to map runlist, dropping runlist lock for * Attempt to map runlist, dropping runlist lock for
* the duration. * the duration.
*/ */
up_read(&ni->runlist.lock);
vcn = rl->vcn; vcn = rl->vcn;
up_read(&ni->runlist.lock);
err = ntfs_map_runlist(ni, vcn); err = ntfs_map_runlist(ni, vcn);
if (err) { if (err) {
if (!is_rollback) if (!is_rollback)
......
...@@ -68,20 +68,31 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni) ...@@ -68,20 +68,31 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
if (index > end_index || (mft_vi->i_size & ~PAGE_CACHE_MASK) < if (index > end_index || (mft_vi->i_size & ~PAGE_CACHE_MASK) <
ofs + vol->mft_record_size) { ofs + vol->mft_record_size) {
page = ERR_PTR(-ENOENT); page = ERR_PTR(-ENOENT);
ntfs_error(vol->sb, "Attemt to read mft record 0x%lx, "
"which is beyond the end of the mft. "
"This is probably a bug in the ntfs "
"driver.", ni->mft_no);
goto err_out; goto err_out;
} }
} }
/* Read, map, and pin the page. */ /* Read, map, and pin the page. */
page = ntfs_map_page(mft_vi->i_mapping, index); page = ntfs_map_page(mft_vi->i_mapping, index);
if (likely(!IS_ERR(page))) { if (likely(!IS_ERR(page))) {
ni->page = page; /* Catch multi sector transfer fixup errors. */
ni->page_ofs = ofs; if (likely(ntfs_is_mft_recordp((le32*)(page_address(page) +
return page_address(page) + ofs; ofs)))) {
ni->page = page;
ni->page_ofs = ofs;
return page_address(page) + ofs;
}
ntfs_error(vol->sb, "Mft record 0x%lx is corrupt. "
"Run chkdsk.", ni->mft_no);
ntfs_unmap_page(page);
page = ERR_PTR(-EIO);
} }
err_out: err_out:
ni->page = NULL; ni->page = NULL;
ni->page_ofs = 0; ni->page_ofs = 0;
ntfs_error(vol->sb, "Failed with error code %lu.", -PTR_ERR(page));
return (void*)page; return (void*)page;
} }
...@@ -455,8 +466,10 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, ...@@ -455,8 +466,10 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no,
struct buffer_head *bhs[max_bhs]; struct buffer_head *bhs[max_bhs];
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
u8 *kmirr; u8 *kmirr;
unsigned int block_start, block_end, m_start, m_end; runlist_element *rl;
unsigned int block_start, block_end, m_start, m_end, page_ofs;
int i_bhs, nr_bhs, err = 0; int i_bhs, nr_bhs, err = 0;
unsigned char blocksize_bits = vol->mftmirr_ino->i_blkbits;
ntfs_debug("Entering for inode 0x%lx.", mft_no); ntfs_debug("Entering for inode 0x%lx.", mft_no);
BUG_ON(!max_bhs); BUG_ON(!max_bhs);
...@@ -475,24 +488,32 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, ...@@ -475,24 +488,32 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no,
err = PTR_ERR(page); err = PTR_ERR(page);
goto err_out; goto err_out;
} }
/*
* Exclusion against other writers. This should never be a problem
* since the page in which the mft record @m resides is also locked and
* hence any other writers would be held up there but it is better to
* make sure no one is writing from elsewhere.
*/
lock_page(page); lock_page(page);
BUG_ON(!PageUptodate(page)); BUG_ON(!PageUptodate(page));
ClearPageUptodate(page); ClearPageUptodate(page);
/* Offset of the mft mirror record inside the page. */
page_ofs = (mft_no << vol->mft_record_size_bits) & ~PAGE_CACHE_MASK;
/* The address in the page of the mirror copy of the mft record @m. */ /* The address in the page of the mirror copy of the mft record @m. */
kmirr = page_address(page) + ((mft_no << vol->mft_record_size_bits) & kmirr = page_address(page) + page_ofs;
~PAGE_CACHE_MASK);
/* Copy the mst protected mft record to the mirror. */ /* Copy the mst protected mft record to the mirror. */
memcpy(kmirr, m, vol->mft_record_size); memcpy(kmirr, m, vol->mft_record_size);
/* Make sure we have mapped buffers. */ /* Create uptodate buffers if not present. */
BUG_ON(!page_has_buffers(page)); if (unlikely(!page_has_buffers(page))) {
struct buffer_head *tail;
bh = head = alloc_page_buffers(page, blocksize, 1);
do {
set_buffer_uptodate(bh);
tail = bh;
bh = bh->b_this_page;
} while (bh);
tail->b_this_page = head;
attach_page_buffers(page, head);
BUG_ON(!page_has_buffers(page));
}
bh = head = page_buffers(page); bh = head = page_buffers(page);
BUG_ON(!bh); BUG_ON(!bh);
rl = NULL;
nr_bhs = 0; nr_bhs = 0;
block_start = 0; block_start = 0;
m_start = kmirr - (u8*)page_address(page); m_start = kmirr - (u8*)page_address(page);
...@@ -500,15 +521,61 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, ...@@ -500,15 +521,61 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no,
do { do {
block_end = block_start + blocksize; block_end = block_start + blocksize;
/* If the buffer is outside the mft record, skip it. */ /* If the buffer is outside the mft record, skip it. */
if ((block_end <= m_start) || (block_start >= m_end)) if (block_end <= m_start)
continue; continue;
BUG_ON(!buffer_mapped(bh)); if (unlikely(block_start >= m_end))
break;
/* Need to map the buffer if it is not mapped already. */
if (unlikely(!buffer_mapped(bh))) {
VCN vcn;
LCN lcn;
unsigned int vcn_ofs;
/* Obtain the vcn and offset of the current block. */
vcn = ((VCN)mft_no << vol->mft_record_size_bits) +
(block_start - m_start);
vcn_ofs = vcn & vol->cluster_size_mask;
vcn >>= vol->cluster_size_bits;
if (!rl) {
down_read(&NTFS_I(vol->mftmirr_ino)->
runlist.lock);
rl = NTFS_I(vol->mftmirr_ino)->runlist.rl;
/*
* $MFTMirr always has the whole of its runlist
* in memory.
*/
BUG_ON(!rl);
}
/* Seek to element containing target vcn. */
while (rl->length && rl[1].vcn <= vcn)
rl++;
lcn = ntfs_rl_vcn_to_lcn(rl, vcn);
/* For $MFTMirr, only lcn >= 0 is a successful remap. */
if (likely(lcn >= 0)) {
/* Setup buffer head to correct block. */
bh->b_blocknr = ((lcn <<
vol->cluster_size_bits) +
vcn_ofs) >> blocksize_bits;
set_buffer_mapped(bh);
} else {
bh->b_blocknr = -1;
ntfs_error(vol->sb, "Cannot write mft mirror "
"record 0x%lx because its "
"location on disk could not "
"be determined (error code "
"%lli).", mft_no,
(long long)lcn);
err = -EIO;
}
}
BUG_ON(!buffer_uptodate(bh)); BUG_ON(!buffer_uptodate(bh));
BUG_ON(!nr_bhs && (m_start != block_start)); BUG_ON(!nr_bhs && (m_start != block_start));
BUG_ON(nr_bhs >= max_bhs); BUG_ON(nr_bhs >= max_bhs);
bhs[nr_bhs++] = bh; bhs[nr_bhs++] = bh;
BUG_ON((nr_bhs >= max_bhs) && (m_end != block_end)); BUG_ON((nr_bhs >= max_bhs) && (m_end != block_end));
} while (block_start = block_end, (bh = bh->b_this_page) != head); } while (block_start = block_end, (bh = bh->b_this_page) != head);
if (unlikely(rl))
up_read(&NTFS_I(vol->mftmirr_ino)->runlist.lock);
if (likely(!err)) { if (likely(!err)) {
/* Lock buffers and start synchronous write i/o on them. */ /* Lock buffers and start synchronous write i/o on them. */
for (i_bhs = 0; i_bhs < nr_bhs; i_bhs++) { for (i_bhs = 0; i_bhs < nr_bhs; i_bhs++) {
...@@ -517,8 +584,7 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, ...@@ -517,8 +584,7 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no,
if (unlikely(test_set_buffer_locked(tbh))) if (unlikely(test_set_buffer_locked(tbh)))
BUG(); BUG();
BUG_ON(!buffer_uptodate(tbh)); BUG_ON(!buffer_uptodate(tbh));
if (buffer_dirty(tbh)) clear_buffer_dirty(tbh);
clear_buffer_dirty(tbh);
get_bh(tbh); get_bh(tbh);
tbh->b_end_io = end_buffer_write_sync; tbh->b_end_io = end_buffer_write_sync;
submit_bh(WRITE, tbh); submit_bh(WRITE, tbh);
...@@ -602,13 +668,14 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync) ...@@ -602,13 +668,14 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync)
{ {
ntfs_volume *vol = ni->vol; ntfs_volume *vol = ni->vol;
struct page *page = ni->page; struct page *page = ni->page;
unsigned int blocksize = vol->sb->s_blocksize; unsigned char blocksize_bits = vol->mft_ino->i_blkbits;
unsigned int blocksize = 1 << blocksize_bits;
int max_bhs = vol->mft_record_size / blocksize; int max_bhs = vol->mft_record_size / blocksize;
struct buffer_head *bhs[max_bhs]; struct buffer_head *bhs[max_bhs];
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
runlist_element *rl;
unsigned int block_start, block_end, m_start, m_end; unsigned int block_start, block_end, m_start, m_end;
int i_bhs, nr_bhs, err = 0; int i_bhs, nr_bhs, err = 0;
BOOL rec_is_dirty = TRUE;
ntfs_debug("Entering for inode 0x%lx.", ni->mft_no); ntfs_debug("Entering for inode 0x%lx.", ni->mft_no);
BUG_ON(NInoAttr(ni)); BUG_ON(NInoAttr(ni));
...@@ -625,6 +692,7 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync) ...@@ -625,6 +692,7 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync)
BUG_ON(!page_has_buffers(page)); BUG_ON(!page_has_buffers(page));
bh = head = page_buffers(page); bh = head = page_buffers(page);
BUG_ON(!bh); BUG_ON(!bh);
rl = NULL;
nr_bhs = 0; nr_bhs = 0;
block_start = 0; block_start = 0;
m_start = ni->page_ofs; m_start = ni->page_ofs;
...@@ -636,31 +704,65 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync) ...@@ -636,31 +704,65 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync)
continue; continue;
if (unlikely(block_start >= m_end)) if (unlikely(block_start >= m_end))
break; break;
/*
* If this block is not the first one in the record, we ignore
* the buffer's dirty state because we could have raced with a
* parallel mark_ntfs_record_dirty().
*/
if (block_start == m_start) { if (block_start == m_start) {
/* This block is the first one in the record. */ /* This block is the first one in the record. */
if (!buffer_dirty(bh)) { if (!buffer_dirty(bh)) {
BUG_ON(nr_bhs);
/* Clean records are not written out. */ /* Clean records are not written out. */
rec_is_dirty = FALSE; break;
continue; }
}
/* Need to map the buffer if it is not mapped already. */
if (unlikely(!buffer_mapped(bh))) {
VCN vcn;
LCN lcn;
unsigned int vcn_ofs;
/* Obtain the vcn and offset of the current block. */
vcn = ((VCN)ni->mft_no << vol->mft_record_size_bits) +
(block_start - m_start);
vcn_ofs = vcn & vol->cluster_size_mask;
vcn >>= vol->cluster_size_bits;
if (!rl) {
down_read(&NTFS_I(vol->mft_ino)->runlist.lock);
rl = NTFS_I(vol->mft_ino)->runlist.rl;
BUG_ON(!rl);
}
/* Seek to element containing target vcn. */
while (rl->length && rl[1].vcn <= vcn)
rl++;
lcn = ntfs_rl_vcn_to_lcn(rl, vcn);
/* For $MFT, only lcn >= 0 is a successful remap. */
if (likely(lcn >= 0)) {
/* Setup buffer head to correct block. */
bh->b_blocknr = ((lcn <<
vol->cluster_size_bits) +
vcn_ofs) >> blocksize_bits;
set_buffer_mapped(bh);
} else {
bh->b_blocknr = -1;
ntfs_error(vol->sb, "Cannot write mft record "
"0x%lx because its location "
"on disk could not be "
"determined (error code %lli).",
ni->mft_no, (long long)lcn);
err = -EIO;
} }
rec_is_dirty = TRUE;
} else {
/*
* This block is not the first one in the record. We
* ignore the buffer's dirty state because we could
* have raced with a parallel mark_ntfs_record_dirty().
*/
if (!rec_is_dirty)
continue;
} }
BUG_ON(!buffer_mapped(bh));
BUG_ON(!buffer_uptodate(bh)); BUG_ON(!buffer_uptodate(bh));
BUG_ON(!nr_bhs && (m_start != block_start)); BUG_ON(!nr_bhs && (m_start != block_start));
BUG_ON(nr_bhs >= max_bhs); BUG_ON(nr_bhs >= max_bhs);
bhs[nr_bhs++] = bh; bhs[nr_bhs++] = bh;
BUG_ON((nr_bhs >= max_bhs) && (m_end != block_end)); BUG_ON((nr_bhs >= max_bhs) && (m_end != block_end));
} while (block_start = block_end, (bh = bh->b_this_page) != head); } while (block_start = block_end, (bh = bh->b_this_page) != head);
if (!rec_is_dirty) if (unlikely(rl))
up_read(&NTFS_I(vol->mft_ino)->runlist.lock);
if (!nr_bhs)
goto done; goto done;
if (unlikely(err)) if (unlikely(err))
goto cleanup_out; goto cleanup_out;
...@@ -734,7 +836,8 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync) ...@@ -734,7 +836,8 @@ int write_mft_record_nolock(ntfs_inode *ni, MFT_RECORD *m, int sync)
"Redirtying so the write is retried later."); "Redirtying so the write is retried later.");
mark_mft_record_dirty(ni); mark_mft_record_dirty(ni);
err = 0; err = 0;
} } else
NVolSetErrors(vol);
return err; return err;
} }
......
...@@ -127,15 +127,16 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -127,15 +127,16 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
dent_inode = ntfs_iget(vol->sb, dent_ino); dent_inode = ntfs_iget(vol->sb, dent_ino);
if (likely(!IS_ERR(dent_inode))) { if (likely(!IS_ERR(dent_inode))) {
/* Consistency check. */ /* Consistency check. */
if (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || if (is_bad_inode(dent_inode) || MSEQNO(mref) ==
NTFS_I(dent_inode)->seq_no ||
dent_ino == FILE_MFT) { dent_ino == FILE_MFT) {
/* Perfect WIN32/POSIX match. -- Case 1. */ /* Perfect WIN32/POSIX match. -- Case 1. */
if (!name) { if (!name) {
ntfs_debug("Done."); ntfs_debug("Done. (Case 1.)");
return d_splice_alias(dent_inode, dent); return d_splice_alias(dent_inode, dent);
} }
/* /*
* We are too indented. Handle imperfect * We are too indented. Handle imperfect
* matches and short file names further below. * matches and short file names further below.
*/ */
goto handle_name; goto handle_name;
...@@ -181,6 +182,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -181,6 +182,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
nls_name.name = NULL; nls_name.name = NULL;
if (name->type != FILE_NAME_DOS) { /* Case 2. */ if (name->type != FILE_NAME_DOS) { /* Case 2. */
ntfs_debug("Case 2.");
nls_name.len = (unsigned)ntfs_ucstonls(vol, nls_name.len = (unsigned)ntfs_ucstonls(vol,
(ntfschar*)&name->name, name->len, (ntfschar*)&name->name, name->len,
(unsigned char**)&nls_name.name, 0); (unsigned char**)&nls_name.name, 0);
...@@ -188,6 +190,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -188,6 +190,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
} else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */ } else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */
FILE_NAME_ATTR *fn; FILE_NAME_ATTR *fn;
ntfs_debug("Case 3.");
kfree(name); kfree(name);
/* Find the WIN32 name corresponding to the matched DOS name. */ /* Find the WIN32 name corresponding to the matched DOS name. */
...@@ -271,12 +274,17 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -271,12 +274,17 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
dput(real_dent); dput(real_dent);
else else
new_dent = real_dent; new_dent = real_dent;
ntfs_debug("Done. (Created new dentry.)");
return new_dent; return new_dent;
} }
kfree(nls_name.name); kfree(nls_name.name);
/* Matching dentry exists, check if it is negative. */ /* Matching dentry exists, check if it is negative. */
if (real_dent->d_inode) { if (real_dent->d_inode) {
BUG_ON(real_dent->d_inode != dent_inode); if (unlikely(real_dent->d_inode != dent_inode)) {
/* This can happen because bad inodes are unhashed. */
BUG_ON(!is_bad_inode(dent_inode));
BUG_ON(!is_bad_inode(real_dent->d_inode));
}
/* /*
* Already have the inode and the dentry attached, decrement * Already have the inode and the dentry attached, decrement
* the reference count to balance the ntfs_iget() we did * the reference count to balance the ntfs_iget() we did
...@@ -285,6 +293,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -285,6 +293,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
* about any NFS/disconnectedness issues here. * about any NFS/disconnectedness issues here.
*/ */
iput(dent_inode); iput(dent_inode);
ntfs_debug("Done. (Already had inode and dentry.)");
return real_dent; return real_dent;
} }
/* /*
...@@ -295,6 +304,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -295,6 +304,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
if (!S_ISDIR(dent_inode->i_mode)) { if (!S_ISDIR(dent_inode->i_mode)) {
/* Not a directory; everything is easy. */ /* Not a directory; everything is easy. */
d_instantiate(real_dent, dent_inode); d_instantiate(real_dent, dent_inode);
ntfs_debug("Done. (Already had negative file dentry.)");
return real_dent; return real_dent;
} }
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
...@@ -308,6 +318,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -308,6 +318,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
real_dent->d_inode = dent_inode; real_dent->d_inode = dent_inode;
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
security_d_instantiate(real_dent, dent_inode); security_d_instantiate(real_dent, dent_inode);
ntfs_debug("Done. (Already had negative directory dentry.)");
return real_dent; return real_dent;
} }
/* /*
...@@ -327,6 +338,8 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -327,6 +338,8 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
/* Throw away real_dent. */ /* Throw away real_dent. */
dput(real_dent); dput(real_dent);
/* Use new_dent as the actual dentry. */ /* Use new_dent as the actual dentry. */
ntfs_debug("Done. (Already had negative, disconnected directory "
"dentry.)");
return new_dent; return new_dent;
eio_err_out: eio_err_out:
...@@ -338,6 +351,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, ...@@ -338,6 +351,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
if (m) if (m)
unmap_mft_record(ni); unmap_mft_record(ni);
iput(dent_inode); iput(dent_inode);
ntfs_error(vol->sb, "Failed, returning error code %i.", err);
return ERR_PTR(err); return ERR_PTR(err);
} }
} }
......
...@@ -53,7 +53,6 @@ extern kmem_cache_t *ntfs_attr_ctx_cache; ...@@ -53,7 +53,6 @@ extern kmem_cache_t *ntfs_attr_ctx_cache;
extern kmem_cache_t *ntfs_index_ctx_cache; extern kmem_cache_t *ntfs_index_ctx_cache;
/* The various operations structs defined throughout the driver files. */ /* The various operations structs defined throughout the driver files. */
extern struct super_operations ntfs_sops;
extern struct address_space_operations ntfs_aops; extern struct address_space_operations ntfs_aops;
extern struct address_space_operations ntfs_mst_aops; extern struct address_space_operations ntfs_mst_aops;
...@@ -86,8 +85,6 @@ extern void free_compression_buffers(void); ...@@ -86,8 +85,6 @@ extern void free_compression_buffers(void);
/* From fs/ntfs/super.c */ /* From fs/ntfs/super.c */
#define default_upcase_len 0x10000 #define default_upcase_len 0x10000
extern ntfschar *default_upcase;
extern unsigned long ntfs_nr_upcase_users;
extern struct semaphore ntfs_lock; extern struct semaphore ntfs_lock;
typedef struct { typedef struct {
......
...@@ -52,7 +52,7 @@ BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol) ...@@ -52,7 +52,7 @@ BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol)
ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino)); ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino));
if (!ictx) { if (!ictx) {
ntfs_error(vol->sb, "Failed to get index context."); ntfs_error(vol->sb, "Failed to get index context.");
return FALSE; goto err_out;
} }
err = ntfs_index_lookup(&qid, sizeof(qid), ictx); err = ntfs_index_lookup(&qid, sizeof(qid), ictx);
if (err) { if (err) {
...@@ -108,7 +108,8 @@ BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol) ...@@ -108,7 +108,8 @@ BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol)
ntfs_debug("Done."); ntfs_debug("Done.");
return TRUE; return TRUE;
err_out: err_out:
ntfs_index_ctx_put(ictx); if (ictx)
ntfs_index_ctx_put(ictx);
up(&vol->quota_q_ino->i_sem); up(&vol->quota_q_ino->i_sem);
return FALSE; return FALSE;
} }
......
...@@ -139,30 +139,6 @@ static inline void __ntfs_rl_merge(runlist_element *dst, runlist_element *src) ...@@ -139,30 +139,6 @@ static inline void __ntfs_rl_merge(runlist_element *dst, runlist_element *src)
dst->length += src->length; dst->length += src->length;
} }
/**
* ntfs_rl_merge - test if two runlists can be joined together and merge them
* @dst: original, destination runlist
* @src: new runlist to merge with @dst
*
* Test if two runlists can be joined together. For this, their VCNs and LCNs
* must be adjacent. If they can be merged, perform the merge, writing into
* the destination runlist @dst.
*
* It is up to the caller to serialize access to the runlists @dst and @src.
*
* Return: TRUE Success, the runlists have been merged.
* FALSE Failure, the runlists cannot be merged and have not been
* modified.
*/
static inline BOOL ntfs_rl_merge(runlist_element *dst, runlist_element *src)
{
BOOL merge = ntfs_are_rl_mergeable(dst, src);
if (merge)
__ntfs_rl_merge(dst, src);
return merge;
}
/** /**
* ntfs_rl_append - append a runlist after a given element * ntfs_rl_append - append a runlist after a given element
* @dst: original runlist to be worked on * @dst: original runlist to be worked on
......
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
/* Number of mounted file systems which have compression enabled. */ /* Number of mounted file systems which have compression enabled. */
static unsigned long ntfs_nr_compression_users; static unsigned long ntfs_nr_compression_users;
/* A global default upcase table and a corresponding reference count. */
static ntfschar *default_upcase = NULL;
static unsigned long ntfs_nr_upcase_users = 0;
/* Error constants/strings used in inode.c::ntfs_show_options(). */ /* Error constants/strings used in inode.c::ntfs_show_options(). */
typedef enum { typedef enum {
/* One of these must be present, default is ON_ERRORS_CONTINUE. */ /* One of these must be present, default is ON_ERRORS_CONTINUE. */
...@@ -680,7 +684,7 @@ static struct buffer_head *read_ntfs_boot_sector(struct super_block *sb, ...@@ -680,7 +684,7 @@ static struct buffer_head *read_ntfs_boot_sector(struct super_block *sb,
* @b: boot sector to parse * @b: boot sector to parse
* *
* Parse the ntfs boot sector @b and store all imporant information therein in * Parse the ntfs boot sector @b and store all imporant information therein in
* the ntfs super block @vol. Return TRUE on success and FALSE on error. * the ntfs super block @vol. Return TRUE on success and FALSE on error.
*/ */
static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
{ {
...@@ -713,12 +717,12 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -713,12 +717,12 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
vol->cluster_size_bits, vol->cluster_size_bits); vol->cluster_size_bits, vol->cluster_size_bits);
if (vol->sector_size > vol->cluster_size) { if (vol->sector_size > vol->cluster_size) {
ntfs_error(vol->sb, "Sector sizes above the cluster size are " ntfs_error(vol->sb, "Sector sizes above the cluster size are "
"not supported. Sorry."); "not supported. Sorry.");
return FALSE; return FALSE;
} }
if (vol->sb->s_blocksize > vol->cluster_size) { if (vol->sb->s_blocksize > vol->cluster_size) {
ntfs_error(vol->sb, "Cluster sizes smaller than the device " ntfs_error(vol->sb, "Cluster sizes smaller than the device "
"sector size are not supported. Sorry."); "sector size are not supported. Sorry.");
return FALSE; return FALSE;
} }
clusters_per_mft_record = b->clusters_per_mft_record; clusters_per_mft_record = b->clusters_per_mft_record;
...@@ -742,6 +746,18 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -742,6 +746,18 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
vol->mft_record_size_mask); vol->mft_record_size_mask);
ntfs_debug("vol->mft_record_size_bits = %i (0x%x)", ntfs_debug("vol->mft_record_size_bits = %i (0x%x)",
vol->mft_record_size_bits, vol->mft_record_size_bits); vol->mft_record_size_bits, vol->mft_record_size_bits);
/*
* We cannot support mft record sizes above the PAGE_CACHE_SIZE since
* we store $MFT/$DATA, the table of mft records in the page cache.
*/
if (vol->mft_record_size > PAGE_CACHE_SIZE) {
ntfs_error(vol->sb, "Mft record size %i (0x%x) exceeds the "
"page cache size on your system %lu (0x%lx). "
"This is not supported. Sorry.",
vol->mft_record_size, vol->mft_record_size,
PAGE_CACHE_SIZE, PAGE_CACHE_SIZE);
return FALSE;
}
clusters_per_index_record = b->clusters_per_index_record; clusters_per_index_record = b->clusters_per_index_record;
ntfs_debug("clusters_per_index_record = %i (0x%x)", ntfs_debug("clusters_per_index_record = %i (0x%x)",
clusters_per_index_record, clusters_per_index_record); clusters_per_index_record, clusters_per_index_record);
...@@ -772,7 +788,7 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -772,7 +788,7 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
*/ */
ll = sle64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits; ll = sle64_to_cpu(b->number_of_sectors) >> sectors_per_cluster_bits;
if ((u64)ll >= 1ULL << 32) { if ((u64)ll >= 1ULL << 32) {
ntfs_error(vol->sb, "Cannot handle 64-bit clusters. Sorry."); ntfs_error(vol->sb, "Cannot handle 64-bit clusters. Sorry.");
return FALSE; return FALSE;
} }
vol->nr_clusters = ll; vol->nr_clusters = ll;
...@@ -785,8 +801,8 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -785,8 +801,8 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
if (sizeof(unsigned long) < 8) { if (sizeof(unsigned long) < 8) {
if ((ll << vol->cluster_size_bits) >= (1ULL << 41)) { if ((ll << vol->cluster_size_bits) >= (1ULL << 41)) {
ntfs_error(vol->sb, "Volume size (%lluTiB) is too " ntfs_error(vol->sb, "Volume size (%lluTiB) is too "
"large for this architecture. Maximum " "large for this architecture. "
"supported is 2TiB. Sorry.", "Maximum supported is 2TiB. Sorry.",
(unsigned long long)ll >> (40 - (unsigned long long)ll >> (40 -
vol->cluster_size_bits)); vol->cluster_size_bits));
return FALSE; return FALSE;
...@@ -794,14 +810,14 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b) ...@@ -794,14 +810,14 @@ static BOOL parse_ntfs_boot_sector(ntfs_volume *vol, const NTFS_BOOT_SECTOR *b)
} }
ll = sle64_to_cpu(b->mft_lcn); ll = sle64_to_cpu(b->mft_lcn);
if (ll >= vol->nr_clusters) { if (ll >= vol->nr_clusters) {
ntfs_error(vol->sb, "MFT LCN is beyond end of volume. Weird."); ntfs_error(vol->sb, "MFT LCN is beyond end of volume. Weird.");
return FALSE; return FALSE;
} }
vol->mft_lcn = ll; vol->mft_lcn = ll;
ntfs_debug("vol->mft_lcn = 0x%llx", (long long)vol->mft_lcn); ntfs_debug("vol->mft_lcn = 0x%llx", (long long)vol->mft_lcn);
ll = sle64_to_cpu(b->mftmirr_lcn); ll = sle64_to_cpu(b->mftmirr_lcn);
if (ll >= vol->nr_clusters) { if (ll >= vol->nr_clusters) {
ntfs_error(vol->sb, "MFTMirr LCN is beyond end of volume. " ntfs_error(vol->sb, "MFTMirr LCN is beyond end of volume. "
"Weird."); "Weird.");
return FALSE; return FALSE;
} }
...@@ -967,6 +983,10 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol) ...@@ -967,6 +983,10 @@ static BOOL load_and_init_mft_mirror(ntfs_volume *vol)
* @vol: ntfs super block describing device whose mft mirror to check * @vol: ntfs super block describing device whose mft mirror to check
* *
* Return TRUE on success or FALSE on error. * Return TRUE on success or FALSE on error.
*
* Note, this function also results in the mft mirror runlist being completely
* mapped into memory. The mft mirror write code requires this and will BUG()
* should it find an unmapped runlist element.
*/ */
static BOOL check_mft_mirror(ntfs_volume *vol) static BOOL check_mft_mirror(ntfs_volume *vol)
{ {
...@@ -2163,7 +2183,7 @@ static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs) ...@@ -2163,7 +2183,7 @@ static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs)
/** /**
* The complete super operations. * The complete super operations.
*/ */
struct super_operations ntfs_sops = { static struct super_operations ntfs_sops = {
.alloc_inode = ntfs_alloc_big_inode, /* VFS: Allocate new inode. */ .alloc_inode = ntfs_alloc_big_inode, /* VFS: Allocate new inode. */
.destroy_inode = ntfs_destroy_big_inode, /* VFS: Deallocate inode. */ .destroy_inode = ntfs_destroy_big_inode, /* VFS: Deallocate inode. */
.put_inode = ntfs_put_inode, /* VFS: Called just before .put_inode = ntfs_put_inode, /* VFS: Called just before
...@@ -2581,10 +2601,6 @@ static void ntfs_big_inode_init_once(void *foo, kmem_cache_t *cachep, ...@@ -2581,10 +2601,6 @@ static void ntfs_big_inode_init_once(void *foo, kmem_cache_t *cachep,
kmem_cache_t *ntfs_attr_ctx_cache; kmem_cache_t *ntfs_attr_ctx_cache;
kmem_cache_t *ntfs_index_ctx_cache; kmem_cache_t *ntfs_index_ctx_cache;
/* A global default upcase table and a corresponding reference count. */
ntfschar *default_upcase = NULL;
unsigned long ntfs_nr_upcase_users = 0;
/* Driver wide semaphore. */ /* Driver wide semaphore. */
DECLARE_MUTEX(ntfs_lock); DECLARE_MUTEX(ntfs_lock);
...@@ -2742,6 +2758,7 @@ static void __exit exit_ntfs_fs(void) ...@@ -2742,6 +2758,7 @@ static void __exit exit_ntfs_fs(void)
MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>"); MODULE_AUTHOR("Anton Altaparmakov <aia21@cantab.net>");
MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2004 Anton Altaparmakov"); MODULE_DESCRIPTION("NTFS 1.2/3.x driver - Copyright (c) 2001-2004 Anton Altaparmakov");
MODULE_VERSION(NTFS_VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#ifdef DEBUG #ifdef DEBUG
module_param(debug_msgs, bool, 0); module_param(debug_msgs, bool, 0);
......
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