]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fs/ntfs3: fix symlinks cannot be handled correctly
authorRong Zhang <ulin0208@gmail.com>
Wed, 7 May 2025 07:34:38 +0000 (15:34 +0800)
committerKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Mon, 23 Jun 2025 17:22:32 +0000 (19:22 +0200)
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>
fs/ntfs3/dir.c
fs/ntfs3/frecord.c
fs/ntfs3/inode.c
fs/ntfs3/ntfs.h
fs/ntfs3/ntfs_fs.h
fs/ntfs3/xattr.c

index b6da80c69ca634f61aeb8ee003175c85d2ce51af..4aab4d54b1715a52c822ba6ea8f9f5280a380dfd 100644 (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)) {
index 756e1306fe6c4942ae916a8f80e2fa8297b7bcdc..86ba90b4104f497c7270a8435f59de35875d91ec 100644 (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;
                }
        }
 
index 0f0d27d4644a9b5802abfa122d376d3f01bbb3f2..0a653ee7fe089d1f9b2eed47d8ace60f96359207 100644 (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 */
index 1ff13b6f96132628a4177c10015fa87368cb0fb3..552b97905813bc5d47440b084c78ee61e83a348b 100644 (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
 
index 36b8052660d52c0ceab07c28fac2e23ae5a6a72e..faf0f067879dc41c4b7c2f5414127c2260df8f78 100644 (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 */
index e0055dcf8fe38ef845e97b871b8da8ba3bf258b1..4bf14cff2683eb650287290ae7f81b4d6f103a6c 100644 (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;