next-20260730/ntfs

This commit is contained in:
Mark Brown
2026-07-31 13:34:36 +01:00
16 changed files with 523 additions and 348 deletions
+1 -1
View File
@@ -19284,7 +19284,7 @@ F: drivers/ntb/hw/intel/
NTFS FILESYSTEM
M: Namjae Jeon <linkinjeon@kernel.org>
M: Hyunchul Lee <hyc.lee@gmail.com>
L: linux-fsdevel@vger.kernel.org
L: ntfs@lists.linux.dev
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs.git
F: Documentation/filesystems/ntfs.rst
+15
View File
@@ -697,6 +697,11 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
attr_len = le32_to_cpu(a->length);
min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
sizeof(a->data.non_resident.initialized_size);
/* Sparse and compressed attributes have the extra compressed_size field */
if (a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK))
min_len += sizeof(a->data.non_resident.compressed_size);
if (attr_len < min_len)
return false;
@@ -4293,6 +4298,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
ni->initialized_size = newsize;
ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize);
}
/*
* Drop any page-cache folios that now lie beyond the shrunk
* attribute. The clusters backing them have just been freed and the
* runlist truncated, so leaving stale dirty folios around makes a
* later writeback map a vcn past the new allocation, which fails with
* -ENOENT and loses the write.
*/
truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
/* Update data size in the index. */
if (ni->type == AT_DATA && ni->name == AT_UNNAMED)
NInoSetFileNameDirty(ni);
+210 -177
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -966,13 +966,14 @@ filldir:
*/
private = file->private_data;
kfree(private->key);
private->key = kmalloc(le16_to_cpu(next->key_length), GFP_KERNEL);
private->key = kmemdup(&next->key.file_name,
le16_to_cpu(next->key_length),
GFP_KERNEL);
if (!private->key) {
err = -ENOMEM;
goto out;
}
memcpy(private->key, &next->key.file_name, le16_to_cpu(next->key_length));
private->key_length = next->key_length;
break;
}
+165 -52
View File
@@ -196,6 +196,9 @@ static int ntfs_set_ea(struct inode *inode, const char *name, size_t name_len,
struct ea_attr *p_ea;
u32 ea_info_qsize = 0;
char *ea_buf = NULL;
char *new_ea_buf;
char *old_ea_buf = NULL;
struct ea_information old_ea_info;
size_t new_ea_size = ALIGN(struct_size(p_ea, ea_name, 1 + name_len + val_size), 4);
s64 ea_off, ea_info_size, all_ea_size, ea_size;
@@ -249,6 +252,22 @@ create_ea_info:
err = -EEXIST;
goto out;
}
if ((flags & XATTR_REPLACE) && !val_size) {
old_ea_info = *p_ea_info;
old_ea_buf = kvmemdup(ea_buf, all_ea_size, GFP_NOFS);
if (!old_ea_buf) {
err = -ENOMEM;
goto out;
}
}
/* Check the final $EA size before removing the old entry. */
if (val_size &&
ntfs_attr_size_bounds_check(ni->vol, AT_EA,
ea_info_qsize - ea_size + new_ea_size)) {
err = -EFBIG;
goto out;
}
p_ea = (struct ea_attr *)(ea_buf + ea_off);
@@ -267,17 +286,39 @@ create_ea_info:
ea_info_qsize -= ea_size;
p_ea_info->ea_query_length = cpu_to_le32(ea_info_qsize);
err = ntfs_write_ea(ni, AT_EA_INFORMATION, (char *)p_ea_info, 0,
sizeof(struct ea_information), false);
if (err)
goto out;
if ((flags & XATTR_REPLACE) && !val_size && !ea_info_qsize) {
err = ntfs_attr_remove(ni, AT_EA, AT_UNNAMED, 0);
if (err)
goto out;
err = ntfs_write_ea(ni, AT_EA, ea_buf, 0, ea_info_qsize, true);
if (err)
err = ntfs_attr_remove(ni, AT_EA_INFORMATION, AT_UNNAMED, 0);
if (err) {
/* Restore the original $EA if $EA_INFORMATION removal failed. */
ntfs_attr_add(ni, AT_EA, AT_UNNAMED, 0, old_ea_buf,
all_ea_size);
ea_info_qsize = le32_to_cpu(old_ea_info.ea_query_length);
}
goto out;
}
if ((flags & XATTR_REPLACE) && !val_size) {
/* Remove xattr. */
err = ntfs_write_ea(ni, AT_EA, ea_buf, 0, ea_info_qsize,
true);
if (err) {
ntfs_write_ea(ni, AT_EA, old_ea_buf, 0,
all_ea_size, false);
goto out;
}
err = ntfs_write_ea(ni, AT_EA_INFORMATION, (char *)p_ea_info,
0, sizeof(struct ea_information), false);
if (err) {
ntfs_write_ea(ni, AT_EA, old_ea_buf, 0,
all_ea_size, false);
ntfs_write_ea(ni, AT_EA_INFORMATION,
(char *)&old_ea_info, 0,
sizeof(old_ea_info), false);
}
goto out;
}
} else {
@@ -285,22 +326,30 @@ create_ea_info:
err = -ENODATA;
goto out;
}
}
kvfree(ea_buf);
if (ntfs_attr_size_bounds_check(ni->vol, AT_EA,
ea_info_qsize + new_ea_size)) {
err = -EFBIG;
goto out;
}
}
alloc_new_ea:
ea_buf = kzalloc(new_ea_size, GFP_NOFS);
if (!ea_buf) {
new_ea_buf = kvzalloc(ea_info_qsize + new_ea_size, GFP_NOFS);
if (!new_ea_buf) {
err = -ENOMEM;
goto out;
}
if (ea_info_qsize)
memcpy(new_ea_buf, ea_buf, ea_info_qsize);
kvfree(ea_buf);
ea_buf = new_ea_buf;
p_ea = (struct ea_attr *)(ea_buf + ea_info_qsize);
/*
* EA and REPARSE_POINT compatibility not checked any more,
* required by Windows 10, but having both may lead to
* problems with earlier versions.
*/
p_ea = (struct ea_attr *)ea_buf;
memcpy(p_ea->ea_name, name, name_len);
p_ea->ea_name_length = name_len;
p_ea->ea_name[name_len] = 0;
@@ -312,8 +361,7 @@ alloc_new_ea:
p_ea_info->ea_length = cpu_to_le16(ea_packed);
p_ea_info->ea_query_length = cpu_to_le32(ea_info_qsize + new_ea_size);
if (ea_packed > 0xffff ||
ntfs_attr_size_bounds_check(ni->vol, AT_EA, new_ea_size)) {
if (ea_packed > 0xffff) {
err = -EFBIG;
goto out;
}
@@ -322,13 +370,13 @@ alloc_new_ea:
* no EA or EA_INFORMATION : add them
*/
if (!ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0)) {
err = ntfs_attr_add(ni, AT_EA, AT_UNNAMED, 0, (char *)p_ea,
new_ea_size);
err = ntfs_attr_add(ni, AT_EA, AT_UNNAMED, 0, ea_buf,
ea_info_qsize + new_ea_size);
if (err)
goto out;
} else {
err = ntfs_write_ea(ni, AT_EA, (char *)p_ea, ea_info_qsize,
new_ea_size, false);
err = ntfs_write_ea(ni, AT_EA, ea_buf, 0,
ea_info_qsize + new_ea_size, true);
if (err)
goto out;
}
@@ -348,6 +396,7 @@ out:
NInoClearHasEA(ni);
kvfree(ea_buf);
kvfree(old_ea_buf);
kvfree(p_ea_info);
return err;
@@ -357,37 +406,35 @@ out:
* Check for the presence of an EA "$LXDEV" (used by WSL)
* and return its value as a device address
*/
int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags)
int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags,
bool *has_lxmod)
{
int err;
__le32 v;
*has_lxmod = false;
if (!(flags & NTFS_VOL_UID)) {
/* Load uid to lxuid EA */
err = ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &v,
sizeof(v));
if (err < 0)
return err;
if (err != sizeof(v))
return -EIO;
i_uid_write(inode, le32_to_cpu(v));
if (err == sizeof(v))
i_uid_write(inode, le32_to_cpu(v));
}
if (!(flags & NTFS_VOL_GID)) {
/* Load gid to lxgid EA */
err = ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &v,
sizeof(v));
if (err < 0)
return err;
if (err != sizeof(v))
return -EIO;
i_gid_write(inode, le32_to_cpu(v));
if (err == sizeof(v))
i_gid_write(inode, le32_to_cpu(v));
}
/* Load mode to lxmod EA */
err = ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &v, sizeof(v));
if (err == sizeof(v)) {
inode->i_mode = le32_to_cpu(v);
*has_lxmod = true;
} else {
/* Everyone gets all permissions. */
inode->i_mode |= 0777;
@@ -581,7 +628,8 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
struct mft_record *m;
struct attr_record *a;
__le16 new_aflags;
int mp_size, mp_ofs, name_ofs, arec_size, err;
u16 old_name_ofs, old_mp_ofs;
int mp_size, mp_ofs, name_ofs, old_arec_size, arec_size, err;
m = map_mft_record(ni);
if (IS_ERR(m))
@@ -613,8 +661,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)) {
@@ -623,15 +673,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) +
@@ -649,11 +724,37 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
mp_ofs = (name_ofs + a->name_length * sizeof(__le16) + 7) & ~7;
arec_size = (mp_ofs + mp_size + 7) & ~7;
old_arec_size = le32_to_cpu(a->length);
/*
* Move payloads before shrinking the record. Otherwise resizing moves
* the following attribute over the old payload before it can be copied.
*/
if (arec_size < old_arec_size) {
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);
}
err = ntfs_attr_record_resize(m, a, arec_size);
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 (arec_size > old_arec_size) {
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));
}
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)
@@ -674,28 +775,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)
@@ -704,6 +808,12 @@ err_out:
return err;
}
static bool ntfs_is_reserved_lxattr(const char *name)
{
return !strcmp(name, "$LXUID") || !strcmp(name, "$LXGID") ||
!strcmp(name, "$LXMOD") || !strcmp(name, "$LXDEV");
}
static int ntfs_setxattr(const struct xattr_handler *handler,
struct mnt_idmap *idmap, struct dentry *unused,
struct inode *inode, const char *name, const void *value,
@@ -716,6 +826,9 @@ static int ntfs_setxattr(const struct xattr_handler *handler,
if (NVolShutdown(ni->vol))
return -EIO;
if (ntfs_is_reserved_lxattr(name) && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (!strcmp(name, SYSTEM_DOS_ATTRIB)) {
if (sizeof(u8) != size) {
err = -EINVAL;
+2 -1
View File
@@ -10,7 +10,8 @@
extern const struct xattr_handler *const ntfs_xattr_handlers[];
int ntfs_ea_set_wsl_not_symlink(struct ntfs_inode *ni, mode_t mode, dev_t dev);
int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags);
int ntfs_ea_get_wsl_inode(struct inode *inode, dev_t *rdevp, unsigned int flags,
bool *has_lxmod);
int ntfs_ea_set_wsl_inode(struct inode *inode, dev_t rdev, __le16 *ea_size,
unsigned int flags);
ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
+55 -26
View File
@@ -268,23 +268,19 @@ static int ntfs_setattr_size(struct inode *vi, struct iattr *attr)
return err;
inode_dio_wait(vi);
truncate_setsize(vi, attr->ia_size);
if (attr->ia_size > old_size) {
truncate_pagecache(vi, old_size);
i_size_write(vi, attr->ia_size);
pagecache_isize_extended(vi, old_size, attr->ia_size);
} else
truncate_setsize(vi, attr->ia_size);
err = ntfs_truncate_vfs(vi, attr->ia_size, old_size);
if (err) {
i_size_write(vi, old_size);
return err;
}
if (NInoNonResident(ni) && attr->ia_size > old_size &&
old_size % PAGE_SIZE != 0) {
loff_t len = min_t(loff_t,
round_up(old_size, PAGE_SIZE) - old_size,
attr->ia_size - old_size);
err = iomap_zero_range(vi, old_size, len,
NULL, &ntfs_seek_iomap_ops,
&ntfs_iomap_folio_ops, NULL);
}
return err;
}
@@ -346,14 +342,12 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (ia_valid & ATTR_MODE)
flags |= NTFS_EA_MODE;
if (S_ISDIR(vi->i_mode))
vi->i_mode &= ~vol->dmask;
else
vi->i_mode &= ~vol->fmask;
mutex_lock(&ni->mrec_lock);
ntfs_ea_set_wsl_inode(vi, 0, NULL, flags);
err = ntfs_ea_set_wsl_inode(vi, 0, NULL, flags);
mutex_unlock(&ni->mrec_lock);
if (err)
goto out;
}
mark_inode_dirty(vi);
@@ -535,6 +529,31 @@ out:
return ret;
}
static int ntfs_expand_for_write(struct ntfs_inode *ni, loff_t end)
{
struct ntfs_volume *vol = ni->vol;
loff_t prealloc_size = 0;
int err;
if (end <= ni->data_size)
return 0;
if (NInoCompressed(ni)) {
if (end > ni->allocated_size)
prealloc_size = round_up(end,
ni->itype.compressed.block_size);
} else if (end > ni->allocated_size &&
end < ni->allocated_size + vol->preallocated_size) {
prealloc_size = ni->allocated_size + vol->preallocated_size;
}
mutex_lock(&ni->mrec_lock);
err = ntfs_attr_expand(ni, end, prealloc_size);
mutex_unlock(&ni->mrec_lock);
return err;
}
static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
@@ -543,7 +562,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct ntfs_volume *vol = ni->vol;
ssize_t ret;
ssize_t count;
loff_t pos;
loff_t pos, end;
int err;
loff_t old_data_size, old_init_size;
@@ -580,10 +599,24 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
pos = iocb->ki_pos;
count = ret;
end = pos + count;
old_data_size = ni->data_size;
old_init_size = ni->initialized_size;
if (end > old_data_size) {
ret = ntfs_expand_for_write(ni, end);
if (ret < 0)
goto out;
}
if (NInoNonResident(ni) && !NInoCompressed(ni) &&
end > old_init_size) {
ret = ntfs_extend_initialized_size(vi, pos, end);
if (ret < 0)
goto out;
}
if (NInoNonResident(ni) && NInoCompressed(ni)) {
ret = ntfs_compress_write(ni, pos, count, from);
if (ret > 0)
@@ -655,7 +688,7 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
from + desc->end - desc->start);
if (NTFS_I(inode)->initialized_size < to) {
err = ntfs_extend_initialized_size(inode, to, to, false);
err = ntfs_extend_initialized_size(inode, to, to);
if (err)
return err;
}
@@ -1126,13 +1159,9 @@ out:
filemap_invalidate_unlock(vi->i_mapping);
if (!err) {
if (mode == 0 && NInoNonResident(ni) &&
offset > old_size && old_size % PAGE_SIZE != 0) {
loff_t len = min_t(loff_t,
round_up(old_size, PAGE_SIZE) - old_size,
offset - old_size);
err = iomap_zero_range(vi, old_size, len, NULL,
&ntfs_seek_iomap_ops,
&ntfs_iomap_folio_ops, NULL);
offset > old_size) {
truncate_pagecache(vi, old_size);
pagecache_isize_extended(vi, old_size, offset);
}
NInoSetFileNameDirty(ni);
inode_set_mtime_to_ts(vi, inode_set_ctime_current(vi));
+11
View File
@@ -1112,6 +1112,7 @@ static struct index_block *ntfs_ir_to_ib(struct index_root *ir, s64 ib_vcn)
struct index_entry *ie_last;
char *ies_start, *ies_end;
int i;
u32 ib_cap;
ntfs_debug("Entering\n");
@@ -1127,6 +1128,16 @@ static struct index_block *ntfs_ir_to_ib(struct index_root *ir, s64 ib_vcn)
* as well, which can never have any data.
*/
i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length);
/* Entries must fit in the allocated index block */
ib_cap = le32_to_cpu(ib->index.allocated_size) -
le32_to_cpu(ib->index.entries_offset);
if ((u32)i > ib_cap) {
ntfs_error(NULL, "Entries (%d B) exceed IB capacity", i);
kvfree(ib);
return NULL;
}
memcpy(ntfs_ie_get_first(&ib->index), ies_start, i);
ib->index.flags = ir->index.flags;
+10 -11
View File
@@ -682,6 +682,7 @@ static int ntfs_read_locked_inode(struct inode *vi)
unsigned int name_len = 4, flags = 0;
int extend_sys = 0;
dev_t dev = 0;
bool has_lxmod = false;
bool vol_err = true;
ntfs_debug("Entering for i_ino 0x%llx.", ni->mft_no);
@@ -862,7 +863,7 @@ skip_attr_list_load:
err = ntfs_attr_lookup(AT_EA_INFORMATION, NULL, 0, 0, 0, NULL, 0, ctx);
if (!err) {
NInoSetHasEA(ni);
ntfs_ea_get_wsl_inode(vi, &dev, flags);
ntfs_ea_get_wsl_inode(vi, &dev, flags, &has_lxmod);
}
if (ni->flags & FILE_ATTR_REPARSE_POINT) {
@@ -886,16 +887,18 @@ skip_attr_list_load:
if (S_ISDIR(vi->i_mode)) {
/*
* Apply the directory permissions mask set in the mount
* options.
* Apply the directory permissions mask set in the mount options
* when no per-file WSL mode is present.
*/
vi->i_mode &= ~vol->dmask;
if (!has_lxmod)
vi->i_mode &= ~vol->dmask;
/* Things break without this kludge! */
if (vi->i_nlink > 1)
set_nlink(vi, 1);
} else {
/* Apply the file permissions mask set in the mount options. */
vi->i_mode &= ~vol->fmask;
/* Apply the file permissions mask when no WSL mode is present. */
if (!has_lxmod)
vi->i_mode &= ~vol->fmask;
}
/*
@@ -2401,7 +2404,7 @@ int ntfs_show_options(struct seq_file *sf, struct dentry *root)
}
int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
const loff_t new_size, bool bsync)
const loff_t new_size)
{
struct ntfs_inode *ni = NTFS_I(vi);
loff_t old_init_size;
@@ -2428,10 +2431,6 @@ int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
&ntfs_iomap_folio_ops, NULL);
if (err)
return err;
if (bsync)
err = filemap_write_and_wait_range(vi->i_mapping,
old_init_size,
offset - 1);
}
+1 -1
View File
@@ -352,7 +352,7 @@ static inline void ntfs_commit_inode(struct inode *vi)
int ntfs_inode_sync_filename(struct ntfs_inode *ni);
int ntfs_extend_initialized_size(struct inode *vi, const loff_t offset,
const loff_t new_size, bool bsync);
const loff_t new_size);
void ntfs_set_vfs_operations(struct inode *inode, mode_t mode, dev_t dev);
struct folio *ntfs_get_locked_folio(struct address_space *mapping,
pgoff_t index, pgoff_t end_index, struct file_ra_state *ra);
+1 -33
View File
@@ -675,21 +675,7 @@ static int ntfs_write_iomap_begin_non_resident(struct inode *inode, loff_t offse
loff_t length, unsigned int flags,
struct iomap *iomap, int ntfs_iomap_flags)
{
struct ntfs_inode *ni = NTFS_I(inode);
if (ntfs_iomap_flags & (NTFS_IOMAP_FLAGS_BEGIN | NTFS_IOMAP_FLAGS_DIO) &&
offset + length > ni->initialized_size) {
int ret;
ret = ntfs_extend_initialized_size(inode, offset,
offset + length,
ntfs_iomap_flags &
NTFS_IOMAP_FLAGS_DIO);
if (ret < 0)
return ret;
}
mutex_lock(&ni->mrec_lock);
mutex_lock(&NTFS_I(inode)->mrec_lock);
if (ntfs_iomap_flags & NTFS_IOMAP_FLAGS_BEGIN)
return ntfs_write_simple_iomap_begin_non_resident(inode, offset,
length, iomap);
@@ -705,28 +691,10 @@ static int __ntfs_write_iomap_begin(struct inode *inode, loff_t offset,
struct iomap *iomap, int ntfs_iomap_flags)
{
struct ntfs_inode *ni = NTFS_I(inode);
loff_t end = offset + length;
if (NVolShutdown(ni->vol))
return -EIO;
if (ntfs_iomap_flags & (NTFS_IOMAP_FLAGS_BEGIN | NTFS_IOMAP_FLAGS_DIO) &&
end > ni->data_size) {
struct ntfs_volume *vol = ni->vol;
int ret;
mutex_lock(&ni->mrec_lock);
if (end > ni->allocated_size &&
end < ni->allocated_size + vol->preallocated_size)
ret = ntfs_attr_expand(ni, end,
ni->allocated_size + vol->preallocated_size);
else
ret = ntfs_attr_expand(ni, end, 0);
mutex_unlock(&ni->mrec_lock);
if (ret)
return ret;
}
if (!NInoNonResident(ni)) {
mutex_lock(&ni->mrec_lock);
return ntfs_write_iomap_begin_resident(inode, offset, iomap);
+6 -1
View File
@@ -298,7 +298,12 @@ struct runlist_element *ntfs_cluster_alloc(struct ntfs_volume *vol, const s64 st
clusters = count;
rlpos = rlsize = 0;
mapping = lcnbmp_vi->i_mapping;
i_size = i_size_read(lcnbmp_vi);
/*
* lcn_empty_bits_per_page is sized from nr_clusters, but $Bitmap can
* cover more clusters than that; bound the scan by the array.
*/
i_size = min_t(s64, i_size_read(lcnbmp_vi),
((s64)vol->nr_clusters + 7) >> 3);
while (1) {
ntfs_debug("Start of outer while loop: done_zones 0x%x, search_zone %i, pass %i, zone_start 0x%llx, zone_end 0x%llx, bmp_initial_pos 0x%llx, bmp_pos 0x%llx, rlpos %i, rlsize %i.",
done_zones, search_zone, pass,
+1 -2
View File
@@ -2420,7 +2420,7 @@ mft_rec_already_initialized:
* record.
*/
(*ni)->mrec = kmalloc(vol->mft_record_size, GFP_NOFS);
(*ni)->mrec = kmemdup(m, vol->mft_record_size, GFP_NOFS);
if (!(*ni)->mrec) {
folio_unlock(folio);
kunmap_local(m);
@@ -2429,7 +2429,6 @@ mft_rec_already_initialized:
goto undo_mftbmp_alloc;
}
memcpy((*ni)->mrec, m, vol->mft_record_size);
post_read_mst_fixup((struct ntfs_record *)(*ni)->mrec, vol->mft_record_size);
ntfs_mft_mark_dirty(folio);
folio_unlock(folio);
+5 -8
View File
@@ -61,12 +61,12 @@ static int ntfs_check_bad_windows_name(struct ntfs_volume *vol,
const __le16 *wc,
unsigned int wc_len)
{
if (ntfs_check_bad_char(wc, wc_len))
return -EINVAL;
if (!NVolCheckWindowsNames(vol))
return 0;
if (ntfs_check_bad_char(wc, wc_len))
return -EINVAL;
/* Check for trailing space or dot. */
if (wc_len > 0 &&
(wc[wc_len - 1] == cpu_to_le16(' ') ||
@@ -424,8 +424,6 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
* directories, also setup the index values to the defaults.
*/
if (S_ISDIR(mode)) {
mode &= ~vol->dmask;
NInoSetMstProtected(ni);
ni->itype.index.block_size = 4096;
ni->itype.index.block_size_bits = ntfs_ffs(4096) - 1;
@@ -439,8 +437,6 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
ni->itype.index.vcn_size_bits =
vol->sector_size_bits;
}
} else {
mode &= ~vol->fmask;
}
if (IS_RDONLY(vi))
@@ -685,7 +681,8 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
mutex_unlock(&dir_ni->mrec_lock);
mutex_unlock(&ni->mrec_lock);
ni->flags = fn->file_attributes;
ni->flags = fn->file_attributes |
(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN);
/* Set the sequence number. */
vi->i_generation = ni->seq_no;
set_nlink(vi, 1);
+3 -10
View File
@@ -317,8 +317,7 @@ unsigned int ntfs_make_symlink(struct ntfs_inode *ni)
} else
ni->flags &= ~FILE_ATTR_REPARSE_POINT;
if (reparse_attr)
kvfree(reparse_attr);
kvfree(reparse_attr);
return mode;
}
@@ -358,8 +357,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr
}
}
if (reparse_attr)
kvfree(reparse_attr);
kvfree(reparse_attr);
iput(vi);
return dt_type;
@@ -894,12 +892,7 @@ int ntfs_reparse_set_native_symlink(struct ntfs_inode *ni,
err = ntfs_set_ntfs_reparse_data(ni, (char *)reparse, total_reparse_len);
if (!err) {
int len = strlen(sub_name);
for (i = 0; i < len; i++) {
if (sub_name[i] == '\\')
sub_name[i] = '/';
}
strreplace(sub_name, '\\', '/');
ni->target = sub_name;
sub_name = NULL;
if (prt_sub_shared)
+34 -23
View File
@@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst,
* On success, return a pointer to the newly allocated, or recycled, memory.
* On error, return -errno.
*/
struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
int old_size, int new_size)
static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl,
int old_size, int new_size, gfp_t gfp)
{
struct runlist_element *new_rl;
size_t new_bytes;
if (old_size < 0 || new_size < 0)
return ERR_PTR(-EINVAL);
old_size = old_size * sizeof(*rl);
new_size = new_size * sizeof(*rl);
if (old_size == new_size)
return rl;
new_rl = kvzalloc(new_size, GFP_NOFS);
if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes))
return ERR_PTR(-EINVAL);
new_rl = kvzalloc(new_bytes, gfp);
if (unlikely(!new_rl))
return ERR_PTR(-ENOMEM);
if (likely(rl != NULL)) {
if (unlikely(old_size > new_size))
old_size = new_size;
memcpy(new_rl, rl, old_size);
size_t old_bytes;
if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) {
kvfree(new_rl);
return ERR_PTR(-EINVAL);
}
if (unlikely(old_bytes > new_bytes))
old_bytes = new_bytes;
memcpy(new_rl, rl, old_bytes);
kvfree(rl);
}
return new_rl;
}
struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
int old_size, int new_size)
{
return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS);
}
/*
* ntfs_rl_realloc_nofail - Reallocate memory for runlists
* @rl: original runlist
@@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl,
int old_size, int new_size)
{
struct runlist_element *new_rl;
old_size = old_size * sizeof(*rl);
new_size = new_size * sizeof(*rl);
if (old_size == new_size)
return rl;
new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL);
if (likely(rl != NULL)) {
if (unlikely(old_size > new_size))
old_size = new_size;
memcpy(new_rl, rl, old_size);
kvfree(rl);
}
return new_rl;
return ntfs_rl_realloc_gfp(rl, old_size, new_size,
GFP_NOFS | __GFP_NOFAIL);
}
/*
@@ -880,6 +884,13 @@ struct runlist_element *ntfs_mapping_pairs_decompress(const struct ntfs_volume *
ntfs_error(vol->sb, "lcn == -1");
}
#endif
/* Check lcn is within the volume. */
if (unlikely(lcn >= (s64)vol->nr_clusters)) {
ntfs_error(vol->sb,
"LCN >= nr_clusters in mapping pairs array.");
goto err_out;
}
/* Check lcn is not below -1. */
if (unlikely(lcn < -1)) {
ntfs_error(vol->sb, "Invalid s64 < -1 in mapping pairs array.");