mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
next-20260730/ntfs
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.");
|
||||
|
||||
Reference in New Issue
Block a user