mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
ntfs: fix resident conversion in ntfs_new_attr_flags
When setting sparse/compressed flags on a resident attribute, the function skipped the resident-to-non-resident conversion and terminated. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
5b676a77f3
commit
7ed0028126
94
fs/ntfs/ea.c
94
fs/ntfs/ea.c
|
|
@ -630,6 +630,7 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
|
|||
struct mft_record *m;
|
||||
struct attr_record *a;
|
||||
__le16 new_aflags;
|
||||
u16 old_name_ofs, old_mp_ofs;
|
||||
int mp_size, mp_ofs, name_ofs, arec_size, err;
|
||||
|
||||
m = map_mft_record(ni);
|
||||
|
|
@ -662,8 +663,10 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
|
|||
else
|
||||
new_aflags &= ~ATTR_IS_COMPRESSED;
|
||||
|
||||
if (new_aflags == a->flags)
|
||||
return 0;
|
||||
if (new_aflags == a->flags) {
|
||||
err = 0;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if ((new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) ==
|
||||
(ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) {
|
||||
|
|
@ -672,15 +675,40 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
|
|||
goto err_out;
|
||||
}
|
||||
|
||||
if (!a->non_resident)
|
||||
goto out;
|
||||
if (!a->non_resident) {
|
||||
if (!(new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)))
|
||||
return 0;
|
||||
|
||||
if (a->data.non_resident.data_size) {
|
||||
pr_err("Can't change sparsed/compressed for non-empty file\n");
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_out;
|
||||
if (le32_to_cpu(a->data.resident.value_length)) {
|
||||
pr_err("Can't change sparse/compressed for non-empty file");
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = ntfs_attr_make_non_resident(ni, 0);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
ntfs_attr_reinit_search_ctx(ctx);
|
||||
err = ntfs_attr_lookup(ni->type, ni->name,
|
||||
ni->name_len, CASE_SENSITIVE,
|
||||
0, NULL, 0, ctx);
|
||||
if (err) {
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
a = ctx->attr;
|
||||
} else {
|
||||
if (a->data.non_resident.data_size) {
|
||||
pr_err("Can't change sparsed/compressed for non-empty file");
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
old_name_ofs = le16_to_cpu(a->name_offset);
|
||||
old_mp_ofs = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
|
||||
|
||||
if (new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))
|
||||
name_ofs = (offsetof(struct attr_record,
|
||||
data.non_resident.compressed_size) +
|
||||
|
|
@ -703,6 +731,25 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
|
|||
if (unlikely(err))
|
||||
goto err_out;
|
||||
|
||||
/*
|
||||
* When compressed/sparse state changes, the non-resident header grows or
|
||||
* shrinks by the compressed_size field. Update the in-record payload layout
|
||||
* to match the new offsets before exposing the new mapping_pairs_offset.
|
||||
*/
|
||||
if (name_ofs > old_name_ofs) {
|
||||
if (mp_ofs != old_mp_ofs)
|
||||
memmove((u8 *)a + mp_ofs, (u8 *)a + old_mp_ofs, mp_size);
|
||||
if (a->name_length)
|
||||
memmove((u8 *)a + name_ofs, (u8 *)a + old_name_ofs,
|
||||
a->name_length * sizeof(__le16));
|
||||
} else {
|
||||
if (a->name_length && name_ofs != old_name_ofs)
|
||||
memmove((u8 *)a + name_ofs, (u8 *)a + old_name_ofs,
|
||||
a->name_length * sizeof(__le16));
|
||||
if (mp_ofs != old_mp_ofs)
|
||||
memmove((u8 *)a + mp_ofs, (u8 *)a + old_mp_ofs, mp_size);
|
||||
}
|
||||
|
||||
if (new_aflags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED)) {
|
||||
a->data.non_resident.compression_unit = 0;
|
||||
if (new_aflags & ATTR_IS_COMPRESSED || ni->vol->major_ver < 3)
|
||||
|
|
@ -723,28 +770,31 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
|
|||
ni->itype.compressed.block_size_bits = 0;
|
||||
ni->itype.compressed.block_clusters = 0;
|
||||
}
|
||||
|
||||
if (new_aflags & ATTR_IS_SPARSE) {
|
||||
NInoSetSparse(ni);
|
||||
ni->flags |= FILE_ATTR_SPARSE_FILE;
|
||||
}
|
||||
|
||||
if (new_aflags & ATTR_IS_COMPRESSED) {
|
||||
NInoSetCompressed(ni);
|
||||
ni->flags |= FILE_ATTR_COMPRESSED;
|
||||
}
|
||||
} else {
|
||||
ni->flags &= ~(FILE_ATTR_SPARSE_FILE | FILE_ATTR_COMPRESSED);
|
||||
a->data.non_resident.compression_unit = 0;
|
||||
NInoClearSparse(ni);
|
||||
NInoClearCompressed(ni);
|
||||
}
|
||||
|
||||
a->name_offset = cpu_to_le16(name_ofs);
|
||||
a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs);
|
||||
|
||||
out:
|
||||
a->flags = new_aflags;
|
||||
|
||||
if (new_aflags & ATTR_IS_SPARSE) {
|
||||
NInoSetSparse(ni);
|
||||
ni->flags |= FILE_ATTR_SPARSE_FILE;
|
||||
} else {
|
||||
NInoClearSparse(ni);
|
||||
ni->flags &= ~FILE_ATTR_SPARSE_FILE;
|
||||
}
|
||||
|
||||
if (new_aflags & ATTR_IS_COMPRESSED) {
|
||||
NInoSetCompressed(ni);
|
||||
ni->flags |= FILE_ATTR_COMPRESSED;
|
||||
} else {
|
||||
NInoClearCompressed(ni);
|
||||
ni->flags &= ~FILE_ATTR_COMPRESSED;
|
||||
}
|
||||
|
||||
mark_mft_record_dirty(ctx->ntfs_ino);
|
||||
err_out:
|
||||
if (ctx)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user