|
@@ -1,7 +1,7 @@
|
|
/**
|
|
/**
|
|
* inode.c - NTFS kernel inode handling. Part of the Linux-NTFS project.
|
|
* inode.c - NTFS kernel inode handling. Part of the Linux-NTFS project.
|
|
*
|
|
*
|
|
- * Copyright (c) 2001-2004 Anton Altaparmakov
|
|
|
|
|
|
+ * Copyright (c) 2001-2005 Anton Altaparmakov
|
|
*
|
|
*
|
|
* This program/include file is free software; you can redistribute it and/or
|
|
* This program/include file is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as published
|
|
* modify it under the terms of the GNU General Public License as published
|
|
@@ -174,7 +174,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no)
|
|
|
|
|
|
vi = iget5_locked(sb, mft_no, (test_t)ntfs_test_inode,
|
|
vi = iget5_locked(sb, mft_no, (test_t)ntfs_test_inode,
|
|
(set_t)ntfs_init_locked_inode, &na);
|
|
(set_t)ntfs_init_locked_inode, &na);
|
|
- if (!vi)
|
|
|
|
|
|
+ if (unlikely(!vi))
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
err = 0;
|
|
err = 0;
|
|
@@ -188,7 +188,7 @@ struct inode *ntfs_iget(struct super_block *sb, unsigned long mft_no)
|
|
* There is no point in keeping bad inodes around if the failure was
|
|
* There is no point in keeping bad inodes around if the failure was
|
|
* due to ENOMEM. We want to be able to retry again later.
|
|
* due to ENOMEM. We want to be able to retry again later.
|
|
*/
|
|
*/
|
|
- if (err == -ENOMEM) {
|
|
|
|
|
|
+ if (unlikely(err == -ENOMEM)) {
|
|
iput(vi);
|
|
iput(vi);
|
|
vi = ERR_PTR(err);
|
|
vi = ERR_PTR(err);
|
|
}
|
|
}
|
|
@@ -235,7 +235,7 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type,
|
|
|
|
|
|
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
|
|
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
|
|
(set_t)ntfs_init_locked_inode, &na);
|
|
(set_t)ntfs_init_locked_inode, &na);
|
|
- if (!vi)
|
|
|
|
|
|
+ if (unlikely(!vi))
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
err = 0;
|
|
err = 0;
|
|
@@ -250,7 +250,7 @@ struct inode *ntfs_attr_iget(struct inode *base_vi, ATTR_TYPE type,
|
|
* simplifies things in that we never need to check for bad attribute
|
|
* simplifies things in that we never need to check for bad attribute
|
|
* inodes elsewhere.
|
|
* inodes elsewhere.
|
|
*/
|
|
*/
|
|
- if (err) {
|
|
|
|
|
|
+ if (unlikely(err)) {
|
|
iput(vi);
|
|
iput(vi);
|
|
vi = ERR_PTR(err);
|
|
vi = ERR_PTR(err);
|
|
}
|
|
}
|
|
@@ -290,7 +290,7 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name,
|
|
|
|
|
|
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
|
|
vi = iget5_locked(base_vi->i_sb, na.mft_no, (test_t)ntfs_test_inode,
|
|
(set_t)ntfs_init_locked_inode, &na);
|
|
(set_t)ntfs_init_locked_inode, &na);
|
|
- if (!vi)
|
|
|
|
|
|
+ if (unlikely(!vi))
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
err = 0;
|
|
err = 0;
|
|
@@ -305,7 +305,7 @@ struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name,
|
|
* simplifies things in that we never need to check for bad index
|
|
* simplifies things in that we never need to check for bad index
|
|
* inodes elsewhere.
|
|
* inodes elsewhere.
|
|
*/
|
|
*/
|
|
- if (err) {
|
|
|
|
|
|
+ if (unlikely(err)) {
|
|
iput(vi);
|
|
iput(vi);
|
|
vi = ERR_PTR(err);
|
|
vi = ERR_PTR(err);
|
|
}
|
|
}
|
|
@@ -317,8 +317,7 @@ struct inode *ntfs_alloc_big_inode(struct super_block *sb)
|
|
ntfs_inode *ni;
|
|
ntfs_inode *ni;
|
|
|
|
|
|
ntfs_debug("Entering.");
|
|
ntfs_debug("Entering.");
|
|
- ni = (ntfs_inode *)kmem_cache_alloc(ntfs_big_inode_cache,
|
|
|
|
- SLAB_NOFS);
|
|
|
|
|
|
+ ni = kmem_cache_alloc(ntfs_big_inode_cache, SLAB_NOFS);
|
|
if (likely(ni != NULL)) {
|
|
if (likely(ni != NULL)) {
|
|
ni->state = 0;
|
|
ni->state = 0;
|
|
return VFS_I(ni);
|
|
return VFS_I(ni);
|
|
@@ -343,7 +342,7 @@ static inline ntfs_inode *ntfs_alloc_extent_inode(void)
|
|
ntfs_inode *ni;
|
|
ntfs_inode *ni;
|
|
|
|
|
|
ntfs_debug("Entering.");
|
|
ntfs_debug("Entering.");
|
|
- ni = (ntfs_inode *)kmem_cache_alloc(ntfs_inode_cache, SLAB_NOFS);
|
|
|
|
|
|
+ ni = kmem_cache_alloc(ntfs_inode_cache, SLAB_NOFS);
|
|
if (likely(ni != NULL)) {
|
|
if (likely(ni != NULL)) {
|
|
ni->state = 0;
|
|
ni->state = 0;
|
|
return ni;
|
|
return ni;
|
|
@@ -376,6 +375,7 @@ static void ntfs_destroy_extent_inode(ntfs_inode *ni)
|
|
void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni)
|
|
void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni)
|
|
{
|
|
{
|
|
ntfs_debug("Entering.");
|
|
ntfs_debug("Entering.");
|
|
|
|
+ rwlock_init(&ni->size_lock);
|
|
ni->initialized_size = ni->allocated_size = 0;
|
|
ni->initialized_size = ni->allocated_size = 0;
|
|
ni->seq_no = 0;
|
|
ni->seq_no = 0;
|
|
atomic_set(&ni->count, 1);
|
|
atomic_set(&ni->count, 1);
|
|
@@ -524,6 +524,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|
ntfs_volume *vol = NTFS_SB(vi->i_sb);
|
|
ntfs_volume *vol = NTFS_SB(vi->i_sb);
|
|
ntfs_inode *ni;
|
|
ntfs_inode *ni;
|
|
MFT_RECORD *m;
|
|
MFT_RECORD *m;
|
|
|
|
+ ATTR_RECORD *a;
|
|
STANDARD_INFORMATION *si;
|
|
STANDARD_INFORMATION *si;
|
|
ntfs_attr_search_ctx *ctx;
|
|
ntfs_attr_search_ctx *ctx;
|
|
int err = 0;
|
|
int err = 0;
|
|
@@ -632,9 +633,10 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|
}
|
|
}
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
|
|
+ a = ctx->attr;
|
|
/* Get the standard information attribute value. */
|
|
/* Get the standard information attribute value. */
|
|
- si = (STANDARD_INFORMATION*)((char*)ctx->attr +
|
|
|
|
- le16_to_cpu(ctx->attr->data.resident.value_offset));
|
|
|
|
|
|
+ si = (STANDARD_INFORMATION*)((u8*)a +
|
|
|
|
+ le16_to_cpu(a->data.resident.value_offset));
|
|
|
|
|
|
/* Transfer information from the standard information into vi. */
|
|
/* Transfer information from the standard information into vi. */
|
|
/*
|
|
/*
|
|
@@ -673,15 +675,16 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|
goto skip_attr_list_load;
|
|
goto skip_attr_list_load;
|
|
ntfs_debug("Attribute list found in inode 0x%lx.", vi->i_ino);
|
|
ntfs_debug("Attribute list found in inode 0x%lx.", vi->i_ino);
|
|
NInoSetAttrList(ni);
|
|
NInoSetAttrList(ni);
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED ||
|
|
|
|
- ctx->attr->flags & ATTR_COMPRESSION_MASK ||
|
|
|
|
- ctx->attr->flags & ATTR_IS_SPARSE) {
|
|
|
|
|
|
+ a = ctx->attr;
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED ||
|
|
|
|
+ a->flags & ATTR_COMPRESSION_MASK ||
|
|
|
|
+ a->flags & ATTR_IS_SPARSE) {
|
|
ntfs_error(vi->i_sb, "Attribute list attribute is "
|
|
ntfs_error(vi->i_sb, "Attribute list attribute is "
|
|
"compressed/encrypted/sparse.");
|
|
"compressed/encrypted/sparse.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
/* Now allocate memory for the attribute list. */
|
|
/* Now allocate memory for the attribute list. */
|
|
- ni->attr_list_size = (u32)ntfs_attr_size(ctx->attr);
|
|
|
|
|
|
+ ni->attr_list_size = (u32)ntfs_attr_size(a);
|
|
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
|
|
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
|
|
if (!ni->attr_list) {
|
|
if (!ni->attr_list) {
|
|
ntfs_error(vi->i_sb, "Not enough memory to allocate "
|
|
ntfs_error(vi->i_sb, "Not enough memory to allocate "
|
|
@@ -689,9 +692,9 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|
err = -ENOMEM;
|
|
err = -ENOMEM;
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->non_resident) {
|
|
|
|
|
|
+ if (a->non_resident) {
|
|
NInoSetAttrListNonResident(ni);
|
|
NInoSetAttrListNonResident(ni);
|
|
- if (ctx->attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(vi->i_sb, "Attribute list has non "
|
|
ntfs_error(vi->i_sb, "Attribute list has non "
|
|
"zero lowest_vcn.");
|
|
"zero lowest_vcn.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
@@ -701,7 +704,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|
* exclusive access to the inode at this time.
|
|
* exclusive access to the inode at this time.
|
|
*/
|
|
*/
|
|
ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol,
|
|
ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol,
|
|
- ctx->attr, NULL);
|
|
|
|
|
|
+ a, NULL);
|
|
if (IS_ERR(ni->attr_list_rl.rl)) {
|
|
if (IS_ERR(ni->attr_list_rl.rl)) {
|
|
err = PTR_ERR(ni->attr_list_rl.rl);
|
|
err = PTR_ERR(ni->attr_list_rl.rl);
|
|
ni->attr_list_rl.rl = NULL;
|
|
ni->attr_list_rl.rl = NULL;
|
|
@@ -712,27 +715,26 @@ static int ntfs_read_locked_inode(struct inode *vi)
|
|
/* Now load the attribute list. */
|
|
/* Now load the attribute list. */
|
|
if ((err = load_attribute_list(vol, &ni->attr_list_rl,
|
|
if ((err = load_attribute_list(vol, &ni->attr_list_rl,
|
|
ni->attr_list, ni->attr_list_size,
|
|
ni->attr_list, ni->attr_list_size,
|
|
- sle64_to_cpu(ctx->attr->data.
|
|
|
|
- non_resident.initialized_size)))) {
|
|
|
|
|
|
+ sle64_to_cpu(a->data.non_resident.
|
|
|
|
+ initialized_size)))) {
|
|
ntfs_error(vi->i_sb, "Failed to load "
|
|
ntfs_error(vi->i_sb, "Failed to load "
|
|
"attribute list attribute.");
|
|
"attribute list attribute.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- } else /* if (!ctx.attr->non_resident) */ {
|
|
|
|
- if ((u8*)ctx->attr + le16_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_offset) +
|
|
|
|
- le32_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_length) >
|
|
|
|
|
|
+ } else /* if (!a->non_resident) */ {
|
|
|
|
+ if ((u8*)a + le16_to_cpu(a->data.resident.value_offset)
|
|
|
|
+ + le32_to_cpu(
|
|
|
|
+ a->data.resident.value_length) >
|
|
(u8*)ctx->mrec + vol->mft_record_size) {
|
|
(u8*)ctx->mrec + vol->mft_record_size) {
|
|
ntfs_error(vi->i_sb, "Corrupt attribute list "
|
|
ntfs_error(vi->i_sb, "Corrupt attribute list "
|
|
"in inode.");
|
|
"in inode.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
/* Now copy the attribute list. */
|
|
/* Now copy the attribute list. */
|
|
- memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_offset),
|
|
|
|
|
|
+ memcpy(ni->attr_list, (u8*)a + le16_to_cpu(
|
|
|
|
+ a->data.resident.value_offset),
|
|
le32_to_cpu(
|
|
le32_to_cpu(
|
|
- ctx->attr->data.resident.value_length));
|
|
|
|
|
|
+ a->data.resident.value_length));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
skip_attr_list_load:
|
|
skip_attr_list_load:
|
|
@@ -741,10 +743,11 @@ skip_attr_list_load:
|
|
* in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes.
|
|
* in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes.
|
|
*/
|
|
*/
|
|
if (S_ISDIR(vi->i_mode)) {
|
|
if (S_ISDIR(vi->i_mode)) {
|
|
|
|
+ loff_t bvi_size;
|
|
struct inode *bvi;
|
|
struct inode *bvi;
|
|
ntfs_inode *bni;
|
|
ntfs_inode *bni;
|
|
INDEX_ROOT *ir;
|
|
INDEX_ROOT *ir;
|
|
- char *ir_end, *index_end;
|
|
|
|
|
|
+ u8 *ir_end, *index_end;
|
|
|
|
|
|
/* It is a directory, find index root attribute. */
|
|
/* It is a directory, find index root attribute. */
|
|
ntfs_attr_reinit_search_ctx(ctx);
|
|
ntfs_attr_reinit_search_ctx(ctx);
|
|
@@ -760,17 +763,16 @@ skip_attr_list_load:
|
|
}
|
|
}
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
|
|
+ a = ctx->attr;
|
|
/* Set up the state. */
|
|
/* Set up the state. */
|
|
- if (unlikely(ctx->attr->non_resident)) {
|
|
|
|
|
|
+ if (unlikely(a->non_resident)) {
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute is not "
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute is not "
|
|
"resident.");
|
|
"resident.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
/* Ensure the attribute name is placed before the value. */
|
|
/* Ensure the attribute name is placed before the value. */
|
|
- if (unlikely(ctx->attr->name_length &&
|
|
|
|
- (le16_to_cpu(ctx->attr->name_offset) >=
|
|
|
|
- le16_to_cpu(ctx->attr->data.resident.
|
|
|
|
- value_offset)))) {
|
|
|
|
|
|
+ if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
|
|
|
|
+ le16_to_cpu(a->data.resident.value_offset)))) {
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute name is "
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute name is "
|
|
"placed after the attribute value.");
|
|
"placed after the attribute value.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
@@ -781,28 +783,27 @@ skip_attr_list_load:
|
|
* encrypted. However index root cannot be both compressed and
|
|
* encrypted. However index root cannot be both compressed and
|
|
* encrypted.
|
|
* encrypted.
|
|
*/
|
|
*/
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK)
|
|
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK)
|
|
NInoSetCompressed(ni);
|
|
NInoSetCompressed(ni);
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
ntfs_error(vi->i_sb, "Found encrypted and "
|
|
ntfs_error(vi->i_sb, "Found encrypted and "
|
|
"compressed attribute.");
|
|
"compressed attribute.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
NInoSetEncrypted(ni);
|
|
NInoSetEncrypted(ni);
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_SPARSE)
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_SPARSE)
|
|
NInoSetSparse(ni);
|
|
NInoSetSparse(ni);
|
|
- ir = (INDEX_ROOT*)((char*)ctx->attr + le16_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_offset));
|
|
|
|
- ir_end = (char*)ir + le32_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_length);
|
|
|
|
- if (ir_end > (char*)ctx->mrec + vol->mft_record_size) {
|
|
|
|
|
|
+ ir = (INDEX_ROOT*)((u8*)a +
|
|
|
|
+ le16_to_cpu(a->data.resident.value_offset));
|
|
|
|
+ ir_end = (u8*)ir + le32_to_cpu(a->data.resident.value_length);
|
|
|
|
+ if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
|
|
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
|
|
"corrupt.");
|
|
"corrupt.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- index_end = (char*)&ir->index +
|
|
|
|
|
|
+ index_end = (u8*)&ir->index +
|
|
le32_to_cpu(ir->index.index_length);
|
|
le32_to_cpu(ir->index.index_length);
|
|
if (index_end > ir_end) {
|
|
if (index_end > ir_end) {
|
|
ntfs_error(vi->i_sb, "Directory index is corrupt.");
|
|
ntfs_error(vi->i_sb, "Directory index is corrupt.");
|
|
@@ -889,7 +890,8 @@ skip_attr_list_load:
|
|
"attribute.");
|
|
"attribute.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (!ctx->attr->non_resident) {
|
|
|
|
|
|
+ a = ctx->attr;
|
|
|
|
+ if (!a->non_resident) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
"is resident.");
|
|
"is resident.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
@@ -898,42 +900,40 @@ skip_attr_list_load:
|
|
* Ensure the attribute name is placed before the mapping pairs
|
|
* Ensure the attribute name is placed before the mapping pairs
|
|
* array.
|
|
* array.
|
|
*/
|
|
*/
|
|
- if (unlikely(ctx->attr->name_length &&
|
|
|
|
- (le16_to_cpu(ctx->attr->name_offset) >=
|
|
|
|
- le16_to_cpu(ctx->attr->data.non_resident.
|
|
|
|
- mapping_pairs_offset)))) {
|
|
|
|
|
|
+ if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
|
|
|
|
+ le16_to_cpu(
|
|
|
|
+ a->data.non_resident.mapping_pairs_offset)))) {
|
|
ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name "
|
|
ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name "
|
|
"is placed after the mapping pairs "
|
|
"is placed after the mapping pairs "
|
|
"array.");
|
|
"array.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
"is encrypted.");
|
|
"is encrypted.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_SPARSE) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_SPARSE) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
"is sparse.");
|
|
"is sparse.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute "
|
|
"is compressed.");
|
|
"is compressed.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(vi->i_sb, "First extent of "
|
|
ntfs_error(vi->i_sb, "First extent of "
|
|
"$INDEX_ALLOCATION attribute has non "
|
|
"$INDEX_ALLOCATION attribute has non "
|
|
"zero lowest_vcn.");
|
|
"zero lowest_vcn.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- vi->i_size = sle64_to_cpu(
|
|
|
|
- ctx->attr->data.non_resident.data_size);
|
|
|
|
|
|
+ vi->i_size = sle64_to_cpu(a->data.non_resident.data_size);
|
|
ni->initialized_size = sle64_to_cpu(
|
|
ni->initialized_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.initialized_size);
|
|
|
|
|
|
+ a->data.non_resident.initialized_size);
|
|
ni->allocated_size = sle64_to_cpu(
|
|
ni->allocated_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.allocated_size);
|
|
|
|
|
|
+ a->data.non_resident.allocated_size);
|
|
/*
|
|
/*
|
|
* We are done with the mft record, so we release it. Otherwise
|
|
* We are done with the mft record, so we release it. Otherwise
|
|
* we would deadlock in ntfs_attr_iget().
|
|
* we would deadlock in ntfs_attr_iget().
|
|
@@ -958,11 +958,12 @@ skip_attr_list_load:
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
/* Consistency check bitmap size vs. index allocation size. */
|
|
/* Consistency check bitmap size vs. index allocation size. */
|
|
- if ((bvi->i_size << 3) < (vi->i_size >>
|
|
|
|
|
|
+ bvi_size = i_size_read(bvi);
|
|
|
|
+ if ((bvi_size << 3) < (vi->i_size >>
|
|
ni->itype.index.block_size_bits)) {
|
|
ni->itype.index.block_size_bits)) {
|
|
ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) "
|
|
ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) "
|
|
"for index allocation (0x%llx).",
|
|
"for index allocation (0x%llx).",
|
|
- bvi->i_size << 3, vi->i_size);
|
|
|
|
|
|
+ bvi_size << 3, vi->i_size);
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
skip_large_dir_stuff:
|
|
skip_large_dir_stuff:
|
|
@@ -1010,87 +1011,92 @@ skip_large_dir_stuff:
|
|
ntfs_error(vi->i_sb, "$DATA attribute is missing.");
|
|
ntfs_error(vi->i_sb, "$DATA attribute is missing.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
|
|
+ a = ctx->attr;
|
|
/* Setup the state. */
|
|
/* Setup the state. */
|
|
- if (ctx->attr->non_resident) {
|
|
|
|
|
|
+ if (a->non_resident) {
|
|
NInoSetNonResident(ni);
|
|
NInoSetNonResident(ni);
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
- NInoSetCompressed(ni);
|
|
|
|
- if (vol->cluster_size > 4096) {
|
|
|
|
- ntfs_error(vi->i_sb, "Found "
|
|
|
|
- "compressed data 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;
|
|
|
|
|
|
+ if (a->flags & (ATTR_COMPRESSION_MASK |
|
|
|
|
+ ATTR_IS_SPARSE)) {
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
+ NInoSetCompressed(ni);
|
|
|
|
+ if (vol->cluster_size > 4096) {
|
|
|
|
+ ntfs_error(vi->i_sb, "Found "
|
|
|
|
+ "compressed data but "
|
|
|
|
+ "compression is "
|
|
|
|
+ "disabled due to "
|
|
|
|
+ "cluster size (%i) > "
|
|
|
|
+ "4kiB.",
|
|
|
|
+ vol->cluster_size);
|
|
|
|
+ goto unm_err_out;
|
|
|
|
+ }
|
|
|
|
+ if ((a->flags & ATTR_COMPRESSION_MASK)
|
|
|
|
+ != ATTR_IS_COMPRESSED) {
|
|
|
|
+ ntfs_error(vi->i_sb, "Found "
|
|
|
|
+ "unknown compression "
|
|
|
|
+ "method or corrupt "
|
|
|
|
+ "file.");
|
|
|
|
+ goto unm_err_out;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- ni->itype.compressed.block_clusters = 1U <<
|
|
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- compression_unit;
|
|
|
|
- if (ctx->attr->data.non_resident.
|
|
|
|
- compression_unit != 4) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_SPARSE)
|
|
|
|
+ NInoSetSparse(ni);
|
|
|
|
+ if (a->data.non_resident.compression_unit !=
|
|
|
|
+ 4) {
|
|
ntfs_error(vi->i_sb, "Found "
|
|
ntfs_error(vi->i_sb, "Found "
|
|
"nonstandard compression unit "
|
|
"nonstandard compression unit "
|
|
"(%u instead of 4). Cannot "
|
|
"(%u instead of 4). Cannot "
|
|
"handle this.",
|
|
"handle this.",
|
|
- ctx->attr->data.non_resident.
|
|
|
|
|
|
+ a->data.non_resident.
|
|
compression_unit);
|
|
compression_unit);
|
|
err = -EOPNOTSUPP;
|
|
err = -EOPNOTSUPP;
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
|
|
+ ni->itype.compressed.block_clusters = 1U <<
|
|
|
|
+ a->data.non_resident.
|
|
|
|
+ compression_unit;
|
|
ni->itype.compressed.block_size = 1U << (
|
|
ni->itype.compressed.block_size = 1U << (
|
|
- ctx->attr->data.non_resident.
|
|
|
|
|
|
+ a->data.non_resident.
|
|
compression_unit +
|
|
compression_unit +
|
|
vol->cluster_size_bits);
|
|
vol->cluster_size_bits);
|
|
ni->itype.compressed.block_size_bits = ffs(
|
|
ni->itype.compressed.block_size_bits = ffs(
|
|
- ni->itype.compressed.block_size) - 1;
|
|
|
|
|
|
+ ni->itype.compressed.
|
|
|
|
+ block_size) - 1;
|
|
|
|
+ ni->itype.compressed.size = sle64_to_cpu(
|
|
|
|
+ a->data.non_resident.
|
|
|
|
+ compressed_size);
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
ntfs_error(vi->i_sb, "Found encrypted "
|
|
ntfs_error(vi->i_sb, "Found encrypted "
|
|
"and compressed data.");
|
|
"and compressed data.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
NInoSetEncrypted(ni);
|
|
NInoSetEncrypted(ni);
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_SPARSE)
|
|
|
|
- NInoSetSparse(ni);
|
|
|
|
- if (ctx->attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(vi->i_sb, "First extent of $DATA "
|
|
ntfs_error(vi->i_sb, "First extent of $DATA "
|
|
"attribute has non zero "
|
|
"attribute has non zero "
|
|
"lowest_vcn.");
|
|
"lowest_vcn.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- /* Setup all the sizes. */
|
|
|
|
vi->i_size = sle64_to_cpu(
|
|
vi->i_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.data_size);
|
|
|
|
|
|
+ a->data.non_resident.data_size);
|
|
ni->initialized_size = sle64_to_cpu(
|
|
ni->initialized_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- initialized_size);
|
|
|
|
|
|
+ a->data.non_resident.initialized_size);
|
|
ni->allocated_size = sle64_to_cpu(
|
|
ni->allocated_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- allocated_size);
|
|
|
|
- if (NInoCompressed(ni)) {
|
|
|
|
- ni->itype.compressed.size = sle64_to_cpu(
|
|
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- compressed_size);
|
|
|
|
- }
|
|
|
|
|
|
+ a->data.non_resident.allocated_size);
|
|
} else { /* Resident attribute. */
|
|
} else { /* Resident attribute. */
|
|
- /*
|
|
|
|
- * Make all sizes equal for simplicity in read code
|
|
|
|
- * paths. FIXME: Need to keep this in mind when
|
|
|
|
- * converting to non-resident attribute in write code
|
|
|
|
- * path. (Probably only affects truncate().)
|
|
|
|
- */
|
|
|
|
- vi->i_size = ni->initialized_size = ni->allocated_size =
|
|
|
|
- le32_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_length);
|
|
|
|
|
|
+ vi->i_size = ni->initialized_size = le32_to_cpu(
|
|
|
|
+ a->data.resident.value_length);
|
|
|
|
+ ni->allocated_size = le32_to_cpu(a->length) -
|
|
|
|
+ le16_to_cpu(
|
|
|
|
+ a->data.resident.value_offset);
|
|
|
|
+ if (vi->i_size > ni->allocated_size) {
|
|
|
|
+ ntfs_error(vi->i_sb, "Resident data attribute "
|
|
|
|
+ "is corrupt (size exceeds "
|
|
|
|
+ "allocation).");
|
|
|
|
+ goto unm_err_out;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
no_data_attr_special_case:
|
|
no_data_attr_special_case:
|
|
/* We are done with the mft record, so we release it. */
|
|
/* We are done with the mft record, so we release it. */
|
|
@@ -1117,11 +1123,10 @@ no_data_attr_special_case:
|
|
* sizes of all non-resident attributes present to give us the Linux
|
|
* sizes of all non-resident attributes present to give us the Linux
|
|
* correct size that should go into i_blocks (after division by 512).
|
|
* correct size that should go into i_blocks (after division by 512).
|
|
*/
|
|
*/
|
|
- if (S_ISDIR(vi->i_mode) || !NInoCompressed(ni))
|
|
|
|
- vi->i_blocks = ni->allocated_size >> 9;
|
|
|
|
- else
|
|
|
|
|
|
+ if (S_ISREG(vi->i_mode) && (NInoCompressed(ni) || NInoSparse(ni)))
|
|
vi->i_blocks = ni->itype.compressed.size >> 9;
|
|
vi->i_blocks = ni->itype.compressed.size >> 9;
|
|
-
|
|
|
|
|
|
+ else
|
|
|
|
+ vi->i_blocks = ni->allocated_size >> 9;
|
|
ntfs_debug("Done.");
|
|
ntfs_debug("Done.");
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -1166,6 +1171,7 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
|
|
ntfs_volume *vol = NTFS_SB(vi->i_sb);
|
|
ntfs_volume *vol = NTFS_SB(vi->i_sb);
|
|
ntfs_inode *ni, *base_ni;
|
|
ntfs_inode *ni, *base_ni;
|
|
MFT_RECORD *m;
|
|
MFT_RECORD *m;
|
|
|
|
+ ATTR_RECORD *a;
|
|
ntfs_attr_search_ctx *ctx;
|
|
ntfs_attr_search_ctx *ctx;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
|
|
@@ -1200,24 +1206,21 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
|
|
err = -ENOMEM;
|
|
err = -ENOMEM;
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
-
|
|
|
|
/* Find the attribute. */
|
|
/* Find the attribute. */
|
|
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
|
|
err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
|
|
CASE_SENSITIVE, 0, NULL, 0, ctx);
|
|
CASE_SENSITIVE, 0, NULL, 0, ctx);
|
|
if (unlikely(err))
|
|
if (unlikely(err))
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
-
|
|
|
|
- if (!ctx->attr->non_resident) {
|
|
|
|
|
|
+ a = ctx->attr;
|
|
|
|
+ if (!a->non_resident) {
|
|
/* Ensure the attribute name is placed before the value. */
|
|
/* Ensure the attribute name is placed before the value. */
|
|
- if (unlikely(ctx->attr->name_length &&
|
|
|
|
- (le16_to_cpu(ctx->attr->name_offset) >=
|
|
|
|
- le16_to_cpu(ctx->attr->data.resident.
|
|
|
|
- value_offset)))) {
|
|
|
|
|
|
+ if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
|
|
|
|
+ le16_to_cpu(a->data.resident.value_offset)))) {
|
|
ntfs_error(vol->sb, "Attribute name is placed after "
|
|
ntfs_error(vol->sb, "Attribute name is placed after "
|
|
"the attribute value.");
|
|
"the attribute value.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (NInoMstProtected(ni) || ctx->attr->flags) {
|
|
|
|
|
|
+ if (NInoMstProtected(ni) || a->flags) {
|
|
ntfs_error(vi->i_sb, "Found mst protected attribute "
|
|
ntfs_error(vi->i_sb, "Found mst protected attribute "
|
|
"or attribute with non-zero flags but "
|
|
"or attribute with non-zero flags but "
|
|
"the attribute is resident. Please "
|
|
"the attribute is resident. Please "
|
|
@@ -1225,85 +1228,95 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
|
|
"linux-ntfs-dev@lists.sourceforge.net");
|
|
"linux-ntfs-dev@lists.sourceforge.net");
|
|
goto unm_err_out;
|
|
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->data.resident.value_length);
|
|
|
|
|
|
+ vi->i_size = ni->initialized_size = le32_to_cpu(
|
|
|
|
+ a->data.resident.value_length);
|
|
|
|
+ ni->allocated_size = le32_to_cpu(a->length) -
|
|
|
|
+ le16_to_cpu(a->data.resident.value_offset);
|
|
|
|
+ if (vi->i_size > ni->allocated_size) {
|
|
|
|
+ ntfs_error(vi->i_sb, "Resident attribute is corrupt "
|
|
|
|
+ "(size exceeds allocation).");
|
|
|
|
+ goto unm_err_out;
|
|
|
|
+ }
|
|
} else {
|
|
} else {
|
|
NInoSetNonResident(ni);
|
|
NInoSetNonResident(ni);
|
|
/*
|
|
/*
|
|
* Ensure the attribute name is placed before the mapping pairs
|
|
* Ensure the attribute name is placed before the mapping pairs
|
|
* array.
|
|
* array.
|
|
*/
|
|
*/
|
|
- if (unlikely(ctx->attr->name_length &&
|
|
|
|
- (le16_to_cpu(ctx->attr->name_offset) >=
|
|
|
|
- le16_to_cpu(ctx->attr->data.non_resident.
|
|
|
|
- mapping_pairs_offset)))) {
|
|
|
|
|
|
+ if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
|
|
|
|
+ le16_to_cpu(
|
|
|
|
+ a->data.non_resident.mapping_pairs_offset)))) {
|
|
ntfs_error(vol->sb, "Attribute name is placed after "
|
|
ntfs_error(vol->sb, "Attribute name is placed after "
|
|
"the mapping pairs array.");
|
|
"the mapping pairs array.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
|
|
+ if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) {
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
+ 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. "
|
|
|
|
+ "Please report you "
|
|
|
|
+ "saw this message to "
|
|
|
|
+ "linux-ntfs-dev@lists."
|
|
|
|
+ "sourceforge.net");
|
|
|
|
+ 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 ((a->flags & ATTR_COMPRESSION_MASK) !=
|
|
|
|
+ ATTR_IS_COMPRESSED) {
|
|
|
|
+ ntfs_error(vi->i_sb, "Found unknown "
|
|
|
|
+ "compression method.");
|
|
|
|
+ goto unm_err_out;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
if (NInoMstProtected(ni)) {
|
|
if (NInoMstProtected(ni)) {
|
|
ntfs_error(vi->i_sb, "Found mst protected "
|
|
ntfs_error(vi->i_sb, "Found mst protected "
|
|
"attribute but the attribute "
|
|
"attribute but the attribute "
|
|
- "is compressed. Please report "
|
|
|
|
- "you saw this message to "
|
|
|
|
- "linux-ntfs-dev@lists."
|
|
|
|
- "sourceforge.net");
|
|
|
|
- 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. Please report "
|
|
|
|
- "you saw this message to "
|
|
|
|
|
|
+ "is %s. Please report you "
|
|
|
|
+ "saw this message to "
|
|
"linux-ntfs-dev@lists."
|
|
"linux-ntfs-dev@lists."
|
|
- "sourceforge.net");
|
|
|
|
- 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);
|
|
|
|
|
|
+ "sourceforge.net",
|
|
|
|
+ NInoCompressed(ni) ?
|
|
|
|
+ "compressed" : "sparse");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if ((ctx->attr->flags & ATTR_COMPRESSION_MASK)
|
|
|
|
- != ATTR_IS_COMPRESSED) {
|
|
|
|
- ntfs_error(vi->i_sb, "Found unknown "
|
|
|
|
- "compression method.");
|
|
|
|
- goto unm_err_out;
|
|
|
|
- }
|
|
|
|
- ni->itype.compressed.block_clusters = 1U <<
|
|
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- compression_unit;
|
|
|
|
- if (ctx->attr->data.non_resident.compression_unit !=
|
|
|
|
- 4) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_SPARSE)
|
|
|
|
+ NInoSetSparse(ni);
|
|
|
|
+ if (a->data.non_resident.compression_unit != 4) {
|
|
ntfs_error(vi->i_sb, "Found nonstandard "
|
|
ntfs_error(vi->i_sb, "Found nonstandard "
|
|
"compression unit (%u instead "
|
|
"compression unit (%u instead "
|
|
"of 4). Cannot handle this.",
|
|
"of 4). Cannot handle this.",
|
|
- ctx->attr->data.non_resident.
|
|
|
|
|
|
+ a->data.non_resident.
|
|
compression_unit);
|
|
compression_unit);
|
|
err = -EOPNOTSUPP;
|
|
err = -EOPNOTSUPP;
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
|
|
+ ni->itype.compressed.block_clusters = 1U <<
|
|
|
|
+ a->data.non_resident.compression_unit;
|
|
ni->itype.compressed.block_size = 1U << (
|
|
ni->itype.compressed.block_size = 1U << (
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- compression_unit +
|
|
|
|
|
|
+ a->data.non_resident.compression_unit +
|
|
vol->cluster_size_bits);
|
|
vol->cluster_size_bits);
|
|
ni->itype.compressed.block_size_bits = ffs(
|
|
ni->itype.compressed.block_size_bits = ffs(
|
|
- ni->itype.compressed.block_size) - 1;
|
|
|
|
|
|
+ ni->itype.compressed.block_size) - 1;
|
|
|
|
+ ni->itype.compressed.size = sle64_to_cpu(
|
|
|
|
+ a->data.non_resident.compressed_size);
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
- ntfs_error(vi->i_sb, "Found encrypted "
|
|
|
|
- "and compressed data.");
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
+ ntfs_error(vi->i_sb, "Found encrypted and "
|
|
|
|
+ "compressed data.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
if (NInoMstProtected(ni)) {
|
|
if (NInoMstProtected(ni)) {
|
|
@@ -1317,37 +1330,17 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
|
|
}
|
|
}
|
|
NInoSetEncrypted(ni);
|
|
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. Please report "
|
|
|
|
- "you saw this message to "
|
|
|
|
- "linux-ntfs-dev@lists."
|
|
|
|
- "sourceforge.net");
|
|
|
|
- goto unm_err_out;
|
|
|
|
- }
|
|
|
|
- NInoSetSparse(ni);
|
|
|
|
- }
|
|
|
|
- if (ctx->attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(vi->i_sb, "First extent of attribute has "
|
|
ntfs_error(vi->i_sb, "First extent of attribute has "
|
|
"non-zero lowest_vcn.");
|
|
"non-zero lowest_vcn.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- /* Setup all the sizes. */
|
|
|
|
- vi->i_size = sle64_to_cpu(
|
|
|
|
- ctx->attr->data.non_resident.data_size);
|
|
|
|
|
|
+ vi->i_size = sle64_to_cpu(a->data.non_resident.data_size);
|
|
ni->initialized_size = sle64_to_cpu(
|
|
ni->initialized_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.initialized_size);
|
|
|
|
|
|
+ a->data.non_resident.initialized_size);
|
|
ni->allocated_size = sle64_to_cpu(
|
|
ni->allocated_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.allocated_size);
|
|
|
|
- if (NInoCompressed(ni)) {
|
|
|
|
- ni->itype.compressed.size = sle64_to_cpu(
|
|
|
|
- ctx->attr->data.non_resident.
|
|
|
|
- compressed_size);
|
|
|
|
- }
|
|
|
|
|
|
+ a->data.non_resident.allocated_size);
|
|
}
|
|
}
|
|
-
|
|
|
|
/* Setup the operations for this attribute inode. */
|
|
/* Setup the operations for this attribute inode. */
|
|
vi->i_op = NULL;
|
|
vi->i_op = NULL;
|
|
vi->i_fop = NULL;
|
|
vi->i_fop = NULL;
|
|
@@ -1355,12 +1348,10 @@ static int ntfs_read_locked_attr_inode(struct inode *base_vi, struct inode *vi)
|
|
vi->i_mapping->a_ops = &ntfs_mst_aops;
|
|
vi->i_mapping->a_ops = &ntfs_mst_aops;
|
|
else
|
|
else
|
|
vi->i_mapping->a_ops = &ntfs_aops;
|
|
vi->i_mapping->a_ops = &ntfs_aops;
|
|
-
|
|
|
|
- if (!NInoCompressed(ni))
|
|
|
|
- vi->i_blocks = ni->allocated_size >> 9;
|
|
|
|
- else
|
|
|
|
|
|
+ if (NInoCompressed(ni) || NInoSparse(ni))
|
|
vi->i_blocks = ni->itype.compressed.size >> 9;
|
|
vi->i_blocks = ni->itype.compressed.size >> 9;
|
|
-
|
|
|
|
|
|
+ else
|
|
|
|
+ vi->i_blocks = ni->allocated_size >> 9;
|
|
/*
|
|
/*
|
|
* Make sure the base inode doesn't go away and attach it to the
|
|
* Make sure the base inode doesn't go away and attach it to the
|
|
* attribute inode.
|
|
* attribute inode.
|
|
@@ -1429,10 +1420,12 @@ err_out:
|
|
*/
|
|
*/
|
|
static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|
static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|
{
|
|
{
|
|
|
|
+ loff_t bvi_size;
|
|
ntfs_volume *vol = NTFS_SB(vi->i_sb);
|
|
ntfs_volume *vol = NTFS_SB(vi->i_sb);
|
|
ntfs_inode *ni, *base_ni, *bni;
|
|
ntfs_inode *ni, *base_ni, *bni;
|
|
struct inode *bvi;
|
|
struct inode *bvi;
|
|
MFT_RECORD *m;
|
|
MFT_RECORD *m;
|
|
|
|
+ ATTR_RECORD *a;
|
|
ntfs_attr_search_ctx *ctx;
|
|
ntfs_attr_search_ctx *ctx;
|
|
INDEX_ROOT *ir;
|
|
INDEX_ROOT *ir;
|
|
u8 *ir_end, *index_end;
|
|
u8 *ir_end, *index_end;
|
|
@@ -1474,30 +1467,28 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|
"missing.");
|
|
"missing.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
|
|
+ a = ctx->attr;
|
|
/* Set up the state. */
|
|
/* Set up the state. */
|
|
- if (unlikely(ctx->attr->non_resident)) {
|
|
|
|
|
|
+ if (unlikely(a->non_resident)) {
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute is not resident.");
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute is not resident.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
/* Ensure the attribute name is placed before the value. */
|
|
/* Ensure the attribute name is placed before the value. */
|
|
- if (unlikely(ctx->attr->name_length &&
|
|
|
|
- (le16_to_cpu(ctx->attr->name_offset) >=
|
|
|
|
- le16_to_cpu(ctx->attr->data.resident.
|
|
|
|
- value_offset)))) {
|
|
|
|
|
|
+ if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
|
|
|
|
+ le16_to_cpu(a->data.resident.value_offset)))) {
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute name is placed "
|
|
ntfs_error(vol->sb, "$INDEX_ROOT attribute name is placed "
|
|
"after the attribute value.");
|
|
"after the attribute value.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
/* Compressed/encrypted/sparse index root is not allowed. */
|
|
/* Compressed/encrypted/sparse index root is not allowed. */
|
|
- if (ctx->attr->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED |
|
|
|
|
|
|
+ if (a->flags & (ATTR_COMPRESSION_MASK | ATTR_IS_ENCRYPTED |
|
|
ATTR_IS_SPARSE)) {
|
|
ATTR_IS_SPARSE)) {
|
|
ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index "
|
|
ntfs_error(vi->i_sb, "Found compressed/encrypted/sparse index "
|
|
"root attribute.");
|
|
"root attribute.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- ir = (INDEX_ROOT*)((u8*)ctx->attr +
|
|
|
|
- le16_to_cpu(ctx->attr->data.resident.value_offset));
|
|
|
|
- ir_end = (u8*)ir + le32_to_cpu(ctx->attr->data.resident.value_length);
|
|
|
|
|
|
+ ir = (INDEX_ROOT*)((u8*)a + le16_to_cpu(a->data.resident.value_offset));
|
|
|
|
+ ir_end = (u8*)ir + le32_to_cpu(a->data.resident.value_length);
|
|
if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) {
|
|
if (ir_end > (u8*)ctx->mrec + vol->mft_record_size) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is corrupt.");
|
|
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is corrupt.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
@@ -1570,7 +1561,7 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|
"$INDEX_ALLOCATION attribute.");
|
|
"$INDEX_ALLOCATION attribute.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (!ctx->attr->non_resident) {
|
|
|
|
|
|
+ if (!a->non_resident) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
|
|
"resident.");
|
|
"resident.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
@@ -1578,37 +1569,36 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|
/*
|
|
/*
|
|
* Ensure the attribute name is placed before the mapping pairs array.
|
|
* Ensure the attribute name is placed before the mapping pairs array.
|
|
*/
|
|
*/
|
|
- if (unlikely(ctx->attr->name_length && (le16_to_cpu(
|
|
|
|
- ctx->attr->name_offset) >= le16_to_cpu(
|
|
|
|
- ctx->attr->data.non_resident.mapping_pairs_offset)))) {
|
|
|
|
|
|
+ if (unlikely(a->name_length && (le16_to_cpu(a->name_offset) >=
|
|
|
|
+ le16_to_cpu(
|
|
|
|
+ a->data.non_resident.mapping_pairs_offset)))) {
|
|
ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name is "
|
|
ntfs_error(vol->sb, "$INDEX_ALLOCATION attribute name is "
|
|
"placed after the mapping pairs array.");
|
|
"placed after the mapping pairs array.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
|
|
"encrypted.");
|
|
"encrypted.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_IS_SPARSE) {
|
|
|
|
|
|
+ if (a->flags & ATTR_IS_SPARSE) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse.");
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is sparse.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
|
|
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK) {
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
|
|
ntfs_error(vi->i_sb, "$INDEX_ALLOCATION attribute is "
|
|
"compressed.");
|
|
"compressed.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION "
|
|
ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION "
|
|
"attribute has non zero lowest_vcn.");
|
|
"attribute has non zero lowest_vcn.");
|
|
goto unm_err_out;
|
|
goto unm_err_out;
|
|
}
|
|
}
|
|
- vi->i_size = sle64_to_cpu(ctx->attr->data.non_resident.data_size);
|
|
|
|
|
|
+ vi->i_size = sle64_to_cpu(a->data.non_resident.data_size);
|
|
ni->initialized_size = sle64_to_cpu(
|
|
ni->initialized_size = sle64_to_cpu(
|
|
- ctx->attr->data.non_resident.initialized_size);
|
|
|
|
- ni->allocated_size = sle64_to_cpu(
|
|
|
|
- ctx->attr->data.non_resident.allocated_size);
|
|
|
|
|
|
+ a->data.non_resident.initialized_size);
|
|
|
|
+ ni->allocated_size = sle64_to_cpu(a->data.non_resident.allocated_size);
|
|
/*
|
|
/*
|
|
* We are done with the mft record, so we release it. Otherwise
|
|
* We are done with the mft record, so we release it. Otherwise
|
|
* we would deadlock in ntfs_attr_iget().
|
|
* we would deadlock in ntfs_attr_iget().
|
|
@@ -1632,10 +1622,10 @@ static int ntfs_read_locked_index_inode(struct inode *base_vi, struct inode *vi)
|
|
goto iput_unm_err_out;
|
|
goto iput_unm_err_out;
|
|
}
|
|
}
|
|
/* Consistency check bitmap size vs. index allocation size. */
|
|
/* Consistency check bitmap size vs. index allocation size. */
|
|
- if ((bvi->i_size << 3) < (vi->i_size >>
|
|
|
|
- ni->itype.index.block_size_bits)) {
|
|
|
|
|
|
+ bvi_size = i_size_read(bvi);
|
|
|
|
+ if ((bvi_size << 3) < (vi->i_size >> ni->itype.index.block_size_bits)) {
|
|
ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) for "
|
|
ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) for "
|
|
- "index allocation (0x%llx).", bvi->i_size << 3,
|
|
|
|
|
|
+ "index allocation (0x%llx).", bvi_size << 3,
|
|
vi->i_size);
|
|
vi->i_size);
|
|
goto iput_unm_err_out;
|
|
goto iput_unm_err_out;
|
|
}
|
|
}
|
|
@@ -1646,7 +1636,6 @@ skip_large_index_stuff:
|
|
vi->i_fop = NULL;
|
|
vi->i_fop = NULL;
|
|
vi->i_mapping->a_ops = &ntfs_mst_aops;
|
|
vi->i_mapping->a_ops = &ntfs_mst_aops;
|
|
vi->i_blocks = ni->allocated_size >> 9;
|
|
vi->i_blocks = ni->allocated_size >> 9;
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Make sure the base inode doesn't go away and attach it to the
|
|
* Make sure the base inode doesn't go away and attach it to the
|
|
* index inode.
|
|
* index inode.
|
|
@@ -1712,7 +1701,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
struct buffer_head *bh;
|
|
struct buffer_head *bh;
|
|
ntfs_inode *ni;
|
|
ntfs_inode *ni;
|
|
MFT_RECORD *m = NULL;
|
|
MFT_RECORD *m = NULL;
|
|
- ATTR_RECORD *attr;
|
|
|
|
|
|
+ ATTR_RECORD *a;
|
|
ntfs_attr_search_ctx *ctx;
|
|
ntfs_attr_search_ctx *ctx;
|
|
unsigned int i, nr_blocks;
|
|
unsigned int i, nr_blocks;
|
|
int err;
|
|
int err;
|
|
@@ -1727,10 +1716,10 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
/* Setup the data attribute. It is special as it is mst protected. */
|
|
/* Setup the data attribute. It is special as it is mst protected. */
|
|
NInoSetNonResident(ni);
|
|
NInoSetNonResident(ni);
|
|
NInoSetMstProtected(ni);
|
|
NInoSetMstProtected(ni);
|
|
|
|
+ NInoSetSparseDisabled(ni);
|
|
ni->type = AT_DATA;
|
|
ni->type = AT_DATA;
|
|
ni->name = NULL;
|
|
ni->name = NULL;
|
|
ni->name_len = 0;
|
|
ni->name_len = 0;
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* This sets up our little cheat allowing us to reuse the async read io
|
|
* This sets up our little cheat allowing us to reuse the async read io
|
|
* completion handler for directories.
|
|
* completion handler for directories.
|
|
@@ -1808,9 +1797,10 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
|
|
|
|
ntfs_debug("Attribute list attribute found in $MFT.");
|
|
ntfs_debug("Attribute list attribute found in $MFT.");
|
|
NInoSetAttrList(ni);
|
|
NInoSetAttrList(ni);
|
|
- if (ctx->attr->flags & ATTR_IS_ENCRYPTED ||
|
|
|
|
- ctx->attr->flags & ATTR_COMPRESSION_MASK ||
|
|
|
|
- ctx->attr->flags & ATTR_IS_SPARSE) {
|
|
|
|
|
|
+ a = ctx->attr;
|
|
|
|
+ if (a->flags & ATTR_IS_ENCRYPTED ||
|
|
|
|
+ a->flags & ATTR_COMPRESSION_MASK ||
|
|
|
|
+ a->flags & ATTR_IS_SPARSE) {
|
|
ntfs_error(sb, "Attribute list attribute is "
|
|
ntfs_error(sb, "Attribute list attribute is "
|
|
"compressed/encrypted/sparse. Not "
|
|
"compressed/encrypted/sparse. Not "
|
|
"allowed. $MFT is corrupt. You should "
|
|
"allowed. $MFT is corrupt. You should "
|
|
@@ -1818,16 +1808,16 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
}
|
|
}
|
|
/* Now allocate memory for the attribute list. */
|
|
/* Now allocate memory for the attribute list. */
|
|
- ni->attr_list_size = (u32)ntfs_attr_size(ctx->attr);
|
|
|
|
|
|
+ ni->attr_list_size = (u32)ntfs_attr_size(a);
|
|
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
|
|
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
|
|
if (!ni->attr_list) {
|
|
if (!ni->attr_list) {
|
|
ntfs_error(sb, "Not enough memory to allocate buffer "
|
|
ntfs_error(sb, "Not enough memory to allocate buffer "
|
|
"for attribute list.");
|
|
"for attribute list.");
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
}
|
|
}
|
|
- if (ctx->attr->non_resident) {
|
|
|
|
|
|
+ if (a->non_resident) {
|
|
NInoSetAttrListNonResident(ni);
|
|
NInoSetAttrListNonResident(ni);
|
|
- if (ctx->attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(sb, "Attribute list has non zero "
|
|
ntfs_error(sb, "Attribute list has non zero "
|
|
"lowest_vcn. $MFT is corrupt. "
|
|
"lowest_vcn. $MFT is corrupt. "
|
|
"You should run chkdsk.");
|
|
"You should run chkdsk.");
|
|
@@ -1835,7 +1825,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
}
|
|
}
|
|
/* Setup the runlist. */
|
|
/* Setup the runlist. */
|
|
ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol,
|
|
ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol,
|
|
- ctx->attr, NULL);
|
|
|
|
|
|
+ a, NULL);
|
|
if (IS_ERR(ni->attr_list_rl.rl)) {
|
|
if (IS_ERR(ni->attr_list_rl.rl)) {
|
|
err = PTR_ERR(ni->attr_list_rl.rl);
|
|
err = PTR_ERR(ni->attr_list_rl.rl);
|
|
ni->attr_list_rl.rl = NULL;
|
|
ni->attr_list_rl.rl = NULL;
|
|
@@ -1847,7 +1837,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
/* Now load the attribute list. */
|
|
/* Now load the attribute list. */
|
|
if ((err = load_attribute_list(vol, &ni->attr_list_rl,
|
|
if ((err = load_attribute_list(vol, &ni->attr_list_rl,
|
|
ni->attr_list, ni->attr_list_size,
|
|
ni->attr_list, ni->attr_list_size,
|
|
- sle64_to_cpu(ctx->attr->data.
|
|
|
|
|
|
+ sle64_to_cpu(a->data.
|
|
non_resident.initialized_size)))) {
|
|
non_resident.initialized_size)))) {
|
|
ntfs_error(sb, "Failed to load attribute list "
|
|
ntfs_error(sb, "Failed to load attribute list "
|
|
"attribute with error code %i.",
|
|
"attribute with error code %i.",
|
|
@@ -1855,20 +1845,20 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
}
|
|
}
|
|
} else /* if (!ctx.attr->non_resident) */ {
|
|
} else /* if (!ctx.attr->non_resident) */ {
|
|
- if ((u8*)ctx->attr + le16_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_offset) +
|
|
|
|
|
|
+ if ((u8*)a + le16_to_cpu(
|
|
|
|
+ a->data.resident.value_offset) +
|
|
le32_to_cpu(
|
|
le32_to_cpu(
|
|
- ctx->attr->data.resident.value_length) >
|
|
|
|
|
|
+ a->data.resident.value_length) >
|
|
(u8*)ctx->mrec + vol->mft_record_size) {
|
|
(u8*)ctx->mrec + vol->mft_record_size) {
|
|
ntfs_error(sb, "Corrupt attribute list "
|
|
ntfs_error(sb, "Corrupt attribute list "
|
|
"attribute.");
|
|
"attribute.");
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
}
|
|
}
|
|
/* Now copy the attribute list. */
|
|
/* Now copy the attribute list. */
|
|
- memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu(
|
|
|
|
- ctx->attr->data.resident.value_offset),
|
|
|
|
|
|
+ memcpy(ni->attr_list, (u8*)a + le16_to_cpu(
|
|
|
|
+ a->data.resident.value_offset),
|
|
le32_to_cpu(
|
|
le32_to_cpu(
|
|
- ctx->attr->data.resident.value_length));
|
|
|
|
|
|
+ a->data.resident.value_length));
|
|
}
|
|
}
|
|
/* The attribute list is now setup in memory. */
|
|
/* The attribute list is now setup in memory. */
|
|
/*
|
|
/*
|
|
@@ -1934,25 +1924,25 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
ntfs_attr_reinit_search_ctx(ctx);
|
|
ntfs_attr_reinit_search_ctx(ctx);
|
|
|
|
|
|
/* Now load all attribute extents. */
|
|
/* Now load all attribute extents. */
|
|
- attr = NULL;
|
|
|
|
|
|
+ a = NULL;
|
|
next_vcn = last_vcn = highest_vcn = 0;
|
|
next_vcn = last_vcn = highest_vcn = 0;
|
|
while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0,
|
|
while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0,
|
|
ctx))) {
|
|
ctx))) {
|
|
runlist_element *nrl;
|
|
runlist_element *nrl;
|
|
|
|
|
|
/* Cache the current attribute. */
|
|
/* Cache the current attribute. */
|
|
- attr = ctx->attr;
|
|
|
|
|
|
+ a = ctx->attr;
|
|
/* $MFT must be non-resident. */
|
|
/* $MFT must be non-resident. */
|
|
- if (!attr->non_resident) {
|
|
|
|
|
|
+ if (!a->non_resident) {
|
|
ntfs_error(sb, "$MFT must be non-resident but a "
|
|
ntfs_error(sb, "$MFT must be non-resident but a "
|
|
"resident extent was found. $MFT is "
|
|
"resident extent was found. $MFT is "
|
|
"corrupt. Run chkdsk.");
|
|
"corrupt. Run chkdsk.");
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
}
|
|
}
|
|
/* $MFT must be uncompressed and unencrypted. */
|
|
/* $MFT must be uncompressed and unencrypted. */
|
|
- if (attr->flags & ATTR_COMPRESSION_MASK ||
|
|
|
|
- attr->flags & ATTR_IS_ENCRYPTED ||
|
|
|
|
- attr->flags & ATTR_IS_SPARSE) {
|
|
|
|
|
|
+ if (a->flags & ATTR_COMPRESSION_MASK ||
|
|
|
|
+ a->flags & ATTR_IS_ENCRYPTED ||
|
|
|
|
+ a->flags & ATTR_IS_SPARSE) {
|
|
ntfs_error(sb, "$MFT must be uncompressed, "
|
|
ntfs_error(sb, "$MFT must be uncompressed, "
|
|
"non-sparse, and unencrypted but a "
|
|
"non-sparse, and unencrypted but a "
|
|
"compressed/sparse/encrypted extent "
|
|
"compressed/sparse/encrypted extent "
|
|
@@ -1966,7 +1956,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
* as we have exclusive access to the inode at this time and we
|
|
* as we have exclusive access to the inode at this time and we
|
|
* are a mount in progress task, too.
|
|
* are a mount in progress task, too.
|
|
*/
|
|
*/
|
|
- nrl = ntfs_mapping_pairs_decompress(vol, attr, ni->runlist.rl);
|
|
|
|
|
|
+ nrl = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl);
|
|
if (IS_ERR(nrl)) {
|
|
if (IS_ERR(nrl)) {
|
|
ntfs_error(sb, "ntfs_mapping_pairs_decompress() "
|
|
ntfs_error(sb, "ntfs_mapping_pairs_decompress() "
|
|
"failed with error code %ld. $MFT is "
|
|
"failed with error code %ld. $MFT is "
|
|
@@ -1977,7 +1967,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
|
|
|
|
/* Are we in the first extent? */
|
|
/* Are we in the first extent? */
|
|
if (!next_vcn) {
|
|
if (!next_vcn) {
|
|
- if (attr->data.non_resident.lowest_vcn) {
|
|
|
|
|
|
+ if (a->data.non_resident.lowest_vcn) {
|
|
ntfs_error(sb, "First extent of $DATA "
|
|
ntfs_error(sb, "First extent of $DATA "
|
|
"attribute has non zero "
|
|
"attribute has non zero "
|
|
"lowest_vcn. $MFT is corrupt. "
|
|
"lowest_vcn. $MFT is corrupt. "
|
|
@@ -1986,15 +1976,15 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
}
|
|
}
|
|
/* Get the last vcn in the $DATA attribute. */
|
|
/* Get the last vcn in the $DATA attribute. */
|
|
last_vcn = sle64_to_cpu(
|
|
last_vcn = sle64_to_cpu(
|
|
- attr->data.non_resident.allocated_size)
|
|
|
|
|
|
+ a->data.non_resident.allocated_size)
|
|
>> vol->cluster_size_bits;
|
|
>> vol->cluster_size_bits;
|
|
/* Fill in the inode size. */
|
|
/* Fill in the inode size. */
|
|
vi->i_size = sle64_to_cpu(
|
|
vi->i_size = sle64_to_cpu(
|
|
- attr->data.non_resident.data_size);
|
|
|
|
- ni->initialized_size = sle64_to_cpu(attr->data.
|
|
|
|
- non_resident.initialized_size);
|
|
|
|
|
|
+ a->data.non_resident.data_size);
|
|
|
|
+ ni->initialized_size = sle64_to_cpu(
|
|
|
|
+ a->data.non_resident.initialized_size);
|
|
ni->allocated_size = sle64_to_cpu(
|
|
ni->allocated_size = sle64_to_cpu(
|
|
- attr->data.non_resident.allocated_size);
|
|
|
|
|
|
+ a->data.non_resident.allocated_size);
|
|
/*
|
|
/*
|
|
* Verify the number of mft records does not exceed
|
|
* Verify the number of mft records does not exceed
|
|
* 2^32 - 1.
|
|
* 2^32 - 1.
|
|
@@ -2051,7 +2041,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
}
|
|
}
|
|
|
|
|
|
/* Get the lowest vcn for the next extent. */
|
|
/* Get the lowest vcn for the next extent. */
|
|
- highest_vcn = sle64_to_cpu(attr->data.non_resident.highest_vcn);
|
|
|
|
|
|
+ highest_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
|
|
next_vcn = highest_vcn + 1;
|
|
next_vcn = highest_vcn + 1;
|
|
|
|
|
|
/* Only one extent or error, which we catch below. */
|
|
/* Only one extent or error, which we catch below. */
|
|
@@ -2060,7 +2050,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
|
|
|
|
/* Avoid endless loops due to corruption. */
|
|
/* Avoid endless loops due to corruption. */
|
|
if (next_vcn < sle64_to_cpu(
|
|
if (next_vcn < sle64_to_cpu(
|
|
- attr->data.non_resident.lowest_vcn)) {
|
|
|
|
|
|
+ a->data.non_resident.lowest_vcn)) {
|
|
ntfs_error(sb, "$MFT has corrupt attribute list "
|
|
ntfs_error(sb, "$MFT has corrupt attribute list "
|
|
"attribute. Run chkdsk.");
|
|
"attribute. Run chkdsk.");
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
@@ -2071,7 +2061,7 @@ int ntfs_read_inode_mount(struct inode *vi)
|
|
"$MFT is corrupt. Run chkdsk.");
|
|
"$MFT is corrupt. Run chkdsk.");
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
}
|
|
}
|
|
- if (!attr) {
|
|
|
|
|
|
+ if (!a) {
|
|
ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is "
|
|
ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is "
|
|
"corrupt. Run chkdsk.");
|
|
"corrupt. Run chkdsk.");
|
|
goto put_err_out;
|
|
goto put_err_out;
|
|
@@ -2275,6 +2265,8 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
|
|
seq_printf(sf, ",case_sensitive");
|
|
seq_printf(sf, ",case_sensitive");
|
|
if (NVolShowSystemFiles(vol))
|
|
if (NVolShowSystemFiles(vol))
|
|
seq_printf(sf, ",show_sys_files");
|
|
seq_printf(sf, ",show_sys_files");
|
|
|
|
+ if (!NVolSparseEnabled(vol))
|
|
|
|
+ seq_printf(sf, ",disable_sparse");
|
|
for (i = 0; on_errors_arr[i].val; i++) {
|
|
for (i = 0; on_errors_arr[i].val; i++) {
|
|
if (on_errors_arr[i].val & vol->on_errors)
|
|
if (on_errors_arr[i].val & vol->on_errors)
|
|
seq_printf(sf, ",errors=%s", on_errors_arr[i].str);
|
|
seq_printf(sf, ",errors=%s", on_errors_arr[i].str);
|
|
@@ -2311,6 +2303,7 @@ int ntfs_truncate(struct inode *vi)
|
|
ntfs_volume *vol = ni->vol;
|
|
ntfs_volume *vol = ni->vol;
|
|
ntfs_attr_search_ctx *ctx;
|
|
ntfs_attr_search_ctx *ctx;
|
|
MFT_RECORD *m;
|
|
MFT_RECORD *m;
|
|
|
|
+ ATTR_RECORD *a;
|
|
const char *te = " Leaving file length out of sync with i_size.";
|
|
const char *te = " Leaving file length out of sync with i_size.";
|
|
int err;
|
|
int err;
|
|
|
|
|
|
@@ -2347,14 +2340,15 @@ int ntfs_truncate(struct inode *vi)
|
|
vi->i_ino, err);
|
|
vi->i_ino, err);
|
|
goto err_out;
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
+ a = ctx->attr;
|
|
/* If the size has not changed there is nothing to do. */
|
|
/* If the size has not changed there is nothing to do. */
|
|
- if (ntfs_attr_size(ctx->attr) == i_size_read(vi))
|
|
|
|
|
|
+ if (ntfs_attr_size(a) == i_size_read(vi))
|
|
goto done;
|
|
goto done;
|
|
// TODO: Implement the truncate...
|
|
// TODO: Implement the truncate...
|
|
ntfs_error(vi->i_sb, "Inode size has changed but this is not "
|
|
ntfs_error(vi->i_sb, "Inode size has changed but this is not "
|
|
"implemented yet. Resetting inode size to old value. "
|
|
"implemented yet. Resetting inode size to old value. "
|
|
" This is most likely a bug in the ntfs driver!");
|
|
" This is most likely a bug in the ntfs driver!");
|
|
- i_size_write(vi, ntfs_attr_size(ctx->attr));
|
|
|
|
|
|
+ i_size_write(vi, ntfs_attr_size(a));
|
|
done:
|
|
done:
|
|
ntfs_attr_put_search_ctx(ctx);
|
|
ntfs_attr_put_search_ctx(ctx);
|
|
unmap_mft_record(ni);
|
|
unmap_mft_record(ni);
|
|
@@ -2515,18 +2509,18 @@ int ntfs_write_inode(struct inode *vi, int sync)
|
|
nt = utc2ntfs(vi->i_mtime);
|
|
nt = utc2ntfs(vi->i_mtime);
|
|
if (si->last_data_change_time != nt) {
|
|
if (si->last_data_change_time != nt) {
|
|
ntfs_debug("Updating mtime for inode 0x%lx: old = 0x%llx, "
|
|
ntfs_debug("Updating mtime for inode 0x%lx: old = 0x%llx, "
|
|
- "new = 0x%llx", vi->i_ino,
|
|
|
|
|
|
+ "new = 0x%llx", vi->i_ino, (long long)
|
|
sle64_to_cpu(si->last_data_change_time),
|
|
sle64_to_cpu(si->last_data_change_time),
|
|
- sle64_to_cpu(nt));
|
|
|
|
|
|
+ (long long)sle64_to_cpu(nt));
|
|
si->last_data_change_time = nt;
|
|
si->last_data_change_time = nt;
|
|
modified = TRUE;
|
|
modified = TRUE;
|
|
}
|
|
}
|
|
nt = utc2ntfs(vi->i_ctime);
|
|
nt = utc2ntfs(vi->i_ctime);
|
|
if (si->last_mft_change_time != nt) {
|
|
if (si->last_mft_change_time != nt) {
|
|
ntfs_debug("Updating ctime for inode 0x%lx: old = 0x%llx, "
|
|
ntfs_debug("Updating ctime for inode 0x%lx: old = 0x%llx, "
|
|
- "new = 0x%llx", vi->i_ino,
|
|
|
|
|
|
+ "new = 0x%llx", vi->i_ino, (long long)
|
|
sle64_to_cpu(si->last_mft_change_time),
|
|
sle64_to_cpu(si->last_mft_change_time),
|
|
- sle64_to_cpu(nt));
|
|
|
|
|
|
+ (long long)sle64_to_cpu(nt));
|
|
si->last_mft_change_time = nt;
|
|
si->last_mft_change_time = nt;
|
|
modified = TRUE;
|
|
modified = TRUE;
|
|
}
|
|
}
|
|
@@ -2534,8 +2528,8 @@ int ntfs_write_inode(struct inode *vi, int sync)
|
|
if (si->last_access_time != nt) {
|
|
if (si->last_access_time != nt) {
|
|
ntfs_debug("Updating atime for inode 0x%lx: old = 0x%llx, "
|
|
ntfs_debug("Updating atime for inode 0x%lx: old = 0x%llx, "
|
|
"new = 0x%llx", vi->i_ino,
|
|
"new = 0x%llx", vi->i_ino,
|
|
- sle64_to_cpu(si->last_access_time),
|
|
|
|
- sle64_to_cpu(nt));
|
|
|
|
|
|
+ (long long)sle64_to_cpu(si->last_access_time),
|
|
|
|
+ (long long)sle64_to_cpu(nt));
|
|
si->last_access_time = nt;
|
|
si->last_access_time = nt;
|
|
modified = TRUE;
|
|
modified = TRUE;
|
|
}
|
|
}
|