Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Rong Zhang and committed by
Konstantin Komarov
8df35e16 19272b37

+43 -33
+1 -2
fs/ntfs3/dir.c
··· 329 329 * It does additional locks/reads just to get the type of name. 330 330 * Should we use additional mount option to enable branch below? 331 331 */ 332 - if (((fname->dup.fa & FILE_ATTRIBUTE_REPARSE_POINT) || 333 - fname->dup.ea_size) && 332 + if (fname->dup.extend_data && 334 333 ino != ni->mi.rno) { 335 334 struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL); 336 335 if (!IS_ERR_OR_NULL(inode)) {
+15 -5
fs/ntfs3/frecord.c
··· 3119 3119 } 3120 3120 } 3121 3121 3122 - /* TODO: Fill reparse info. */ 3123 - dup->reparse = 0; 3124 - dup->ea_size = 0; 3122 + dup->extend_data = 0; 3125 3123 3126 - if (ni->ni_flags & NI_FLAG_EA) { 3124 + if (dup->fa & FILE_ATTRIBUTE_REPARSE_POINT) { 3125 + attr = ni_find_attr(ni, NULL, NULL, ATTR_REPARSE, NULL, 0, NULL, 3126 + NULL); 3127 + 3128 + if (attr) { 3129 + const struct REPARSE_POINT *rp; 3130 + 3131 + rp = resident_data_ex(attr, sizeof(struct REPARSE_POINT)); 3132 + /* If ATTR_REPARSE exists 'rp' can't be NULL. */ 3133 + if (rp) 3134 + dup->extend_data = rp->ReparseTag; 3135 + } 3136 + } else if (ni->ni_flags & NI_FLAG_EA) { 3127 3137 attr = ni_find_attr(ni, attr, &le, ATTR_EA_INFO, NULL, 0, NULL, 3128 3138 NULL); 3129 3139 if (attr) { ··· 3142 3132 info = resident_data_ex(attr, sizeof(struct EA_INFO)); 3143 3133 /* If ATTR_EA_INFO exists 'info' can't be NULL. */ 3144 3134 if (info) 3145 - dup->ea_size = info->size_pack; 3135 + dup->extend_data = info->size; 3146 3136 } 3147 3137 } 3148 3138
+22 -20
fs/ntfs3/inode.c
··· 1350 1350 fname->dup.a_time = std5->cr_time; 1351 1351 fname->dup.alloc_size = fname->dup.data_size = 0; 1352 1352 fname->dup.fa = std5->fa; 1353 - fname->dup.ea_size = fname->dup.reparse = 0; 1353 + fname->dup.extend_data = S_ISLNK(mode) ? IO_REPARSE_TAG_SYMLINK : 0; 1354 1354 1355 1355 dsize = le16_to_cpu(new_de->key_size); 1356 1356 asize = ALIGN(SIZEOF_RESIDENT + dsize, 8); ··· 1590 1590 inode->i_flags |= S_NOSEC; 1591 1591 } 1592 1592 1593 - /* 1594 - * ntfs_init_acl and ntfs_save_wsl_perm update extended attribute. 1595 - * The packed size of extended attribute is stored in direntry too. 1596 - * 'fname' here points to inside new_de. 1597 - */ 1598 - err = ntfs_save_wsl_perm(inode, &fname->dup.ea_size); 1599 - if (err) 1600 - goto out6; 1593 + if (!S_ISLNK(mode)) { 1594 + /* 1595 + * ntfs_init_acl and ntfs_save_wsl_perm update extended attribute. 1596 + * The packed size of extended attribute is stored in direntry too. 1597 + * 'fname' here points to inside new_de. 1598 + */ 1599 + err = ntfs_save_wsl_perm(inode, &fname->dup.extend_data); 1600 + if (err) 1601 + goto out6; 1601 1602 1602 - /* 1603 - * update ea_size in file_name attribute too. 1604 - * Use ni_find_attr cause layout of MFT record may be changed 1605 - * in ntfs_init_acl and ntfs_save_wsl_perm. 1606 - */ 1607 - attr = ni_find_attr(ni, NULL, NULL, ATTR_NAME, NULL, 0, NULL, NULL); 1608 - if (attr) { 1609 - struct ATTR_FILE_NAME *fn; 1603 + /* 1604 + * update ea_size in file_name attribute too. 1605 + * Use ni_find_attr cause layout of MFT record may be changed 1606 + * in ntfs_init_acl and ntfs_save_wsl_perm. 1607 + */ 1608 + attr = ni_find_attr(ni, NULL, NULL, ATTR_NAME, NULL, 0, NULL, NULL); 1609 + if (attr) { 1610 + struct ATTR_FILE_NAME *fn; 1610 1611 1611 - fn = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); 1612 - if (fn) 1613 - fn->dup.ea_size = fname->dup.ea_size; 1612 + fn = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME); 1613 + if (fn) 1614 + fn->dup.extend_data = fname->dup.extend_data; 1615 + } 1614 1616 } 1615 1617 1616 1618 /* We do not need to update parent directory later */
+1 -2
fs/ntfs3/ntfs.h
··· 561 561 __le64 alloc_size; // 0x20: Data attribute allocated size, multiple of cluster size. 562 562 __le64 data_size; // 0x28: Data attribute size <= Dataalloc_size. 563 563 enum FILE_ATTRIBUTE fa; // 0x30: Standard DOS attributes & more. 564 - __le16 ea_size; // 0x34: Packed EAs. 565 - __le16 reparse; // 0x36: Used by Reparse. 564 + __le32 extend_data; // 0x34: Extended data. 566 565 567 566 }; // 0x38 568 567
+1 -1
fs/ntfs3/ntfs_fs.h
··· 874 874 ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size); 875 875 extern const struct xattr_handler *const ntfs_xattr_handlers[]; 876 876 877 - int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size); 877 + int ntfs_save_wsl_perm(struct inode *inode, __le32 *ea_size); 878 878 void ntfs_get_wsl_perm(struct inode *inode); 879 879 880 880 /* globals from lznt.c */
+3 -3
fs/ntfs3/xattr.c
··· 313 313 static noinline int ntfs_set_ea(struct inode *inode, const char *name, 314 314 size_t name_len, const void *value, 315 315 size_t val_size, int flags, bool locked, 316 - __le16 *ea_size) 316 + __le32 *ea_size) 317 317 { 318 318 struct ntfs_inode *ni = ntfs_i(inode); 319 319 struct ntfs_sb_info *sbi = ni->mi.sbi; ··· 522 522 if (ea_info.size_pack != size_pack) 523 523 ni->ni_flags |= NI_FLAG_UPDATE_PARENT; 524 524 if (ea_size) 525 - *ea_size = ea_info.size_pack; 525 + *ea_size = ea_info.size; 526 526 mark_inode_dirty(&ni->vfs_inode); 527 527 528 528 out: ··· 950 950 * 951 951 * save uid/gid/mode in xattr 952 952 */ 953 - int ntfs_save_wsl_perm(struct inode *inode, __le16 *ea_size) 953 + int ntfs_save_wsl_perm(struct inode *inode, __le32 *ea_size) 954 954 { 955 955 int err; 956 956 __le32 value;