fs/ntfs3: fix symlinks cannot be handled correctly

The symlinks created in windows will be broken in linux by ntfs3,
the patch fixes it.

Signed-off-by: Rong Zhang <ulin0208@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
pull/1279/head
Rong Zhang 2025-05-07 15:34:38 +08:00 committed by Konstantin Komarov
parent 19272b37aa
commit 8df35e16a9
No known key found for this signature in database
GPG Key ID: A9B0331F832407B6
6 changed files with 43 additions and 33 deletions

View File

@ -329,8 +329,7 @@ static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi,
* It does additional locks/reads just to get the type of name.
* Should we use additional mount option to enable branch below?
*/
if (((fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) ||
fname->dup.ea_size) &&
if (fname->dup.extend_data &&
ino != ni->mi.rno) {
struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL);
if (!IS_ERR_OR_NULL(inode)) {

View File

@ -3119,11 +3119,21 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
}
}
/* TODO: Fill reparse info. */
dup->reparse = 0;
dup->ea_size = 0;
dup->extend_data = 0;
if (ni->ni_flags & NI_FLAG_EA) {
if (dup->fa & FILE_ATTRIBUTE_REPARSE_POINT) {
attr = ni_find_attr(ni, NULL, NULL, ATTR_REPARSE, NULL, 0, NULL,
NULL);
if (attr) {
const struct REPARSE_POINT *rp;
rp = resident_data_ex(attr, sizeof(struct REPARSE_POINT));
/* If ATTR_REPARSE exists 'rp' can't be NULL. */
if (rp)
dup->extend_data = rp->ReparseTag;
}
} else if (ni->ni_flags & NI_FLAG_EA) {
attr = ni_find_attr(ni, attr, &le, ATTR_EA_INFO, NULL, 0, NULL,
NULL);
if (attr) {
@ -3132,7 +3142,7 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
info = resident_data_ex(attr, sizeof(struct EA_INFO));
/* If ATTR_EA_INFO exists 'info' can't be NULL. */
if (info)
dup->ea_size = info->size_pack;
dup->extend_data = info->size;
}
}

View File

@ -1350,7 +1350,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
fname->dup.a_time = std5->cr_time;
fname->dup.alloc_size = fname->dup.data_size = 0;
fname->dup.fa = std5->fa;
fname->dup.ea_size = fname->dup.reparse = 0;
fname->dup.extend_data = S_ISLNK(mode) ? IO_REPARSE_TAG_SYMLINK : 0;
dsize = le16_to_cpu(new_de->key_size);
asize = ALIGN(SIZEOF_RESIDENT + dsize, 8);
@ -1590,27 +1590,29 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
inode->i_flags |= S_NOSEC;
}
/*
* ntfs_init_acl and ntfs_save_wsl_perm update extended attribute.
* The packed size of extended attribute is stored in direntry too.
* 'fname' here points to inside new_de.
*/
err = ntfs_save_wsl_perm(inode, &fname->dup.ea_size);
if (err)
goto out6;
if (!S_ISLNK(mode)) {
/*
* ntfs_init_acl and ntfs_save_wsl_perm update extended attribute.
* The packed size of extended attribute is stored in direntry too.
* 'fname' here points to inside new_de.
*/
err = ntfs_save_wsl_perm(inode, &fname->dup.extend_data);
if (err)
goto out6;
/*
* update ea_size in file_name attribute too.
* Use ni_find_attr cause layout of MFT record may be changed
* in ntfs_init_acl and ntfs_save_wsl_perm.
*/
attr = ni_find_attr(ni, NULL, NULL, ATTR_NAME, NULL, 0, NULL, NULL);
if (attr) {
struct ATTR_FILE_NAME *fn;
/*
* update ea_size in file_name attribute too.
* Use ni_find_attr cause layout of MFT record may be changed
* in ntfs_init_acl and ntfs_save_wsl_perm.
*/
attr = ni_find_attr(ni, NULL, NULL, ATTR_NAME, NULL, 0, NULL, NULL);
if (attr) {
struct ATTR_FILE_NAME *fn;
fn = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
if (fn)
fn->dup.ea_size = fname->dup.ea_size;
fn = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
if (fn)
fn->dup.extend_data = fname->dup.extend_data;
}
}
/* We do not need to update parent directory later */

View File

@ -561,8 +561,7 @@ struct NTFS_DUP_INFO {
__le64 alloc_size; // 0x20: Data attribute allocated size, multiple of cluster size.
__le64 data_size; // 0x28: Data attribute size <= Dataalloc_size.
enum FILE_ATTRIBUTE fa; // 0x30: Standard DOS attributes & more.
__le16 ea_size; // 0x34: Packed EAs.
__le16 reparse; // 0x36: Used by Reparse.
__le32 extend_data; // 0x34: Extended data.
}; // 0x38

View File

@ -874,7 +874,7 @@ int ntfs_acl_chmod(struct mnt_idmap *idmap, struct dentry *dentry);
ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
extern const struct xattr_handler *const ntfs_xattr_handlers[];
int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size);
int ntfs_save_wsl_perm(struct inode *inode, __le32 *ea_size);
void ntfs_get_wsl_perm(struct inode *inode);
/* globals from lznt.c */

View File

@ -313,7 +313,7 @@ out:
static noinline int ntfs_set_ea(struct inode *inode, const char *name,
size_t name_len, const void *value,
size_t val_size, int flags, bool locked,
__le16 *ea_size)
__le32 *ea_size)
{
struct ntfs_inode *ni = ntfs_i(inode);
struct ntfs_sb_info *sbi = ni->mi.sbi;
@ -522,7 +522,7 @@ update_ea:
if (ea_info.size_pack != size_pack)
ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
if (ea_size)
*ea_size = ea_info.size_pack;
*ea_size = ea_info.size;
mark_inode_dirty(&ni->vfs_inode);
out:
@ -950,7 +950,7 @@ out:
*
* save uid/gid/mode in xattr
*/
int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size)
int ntfs_save_wsl_perm(struct inode *inode, __le32 *ea_size)
{
int err;
__le32 value;