diff options
author | Steven J. Magnani <steve.magnani@digidescorp.com> | 2019-08-27 08:13:59 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2019-08-27 09:38:46 -0400 |
commit | c3367a1b47d590f97109cd4b5189e750fb26c0f1 (patch) | |
tree | 8f29eb87d3375f3ff048b3384bef9692bde86367 | |
parent | 8cbd9af9d208b1f015cf8a4645602f0a007270a8 (diff) |
udf: augment UDF permissions on new inodes
Windows presents files created within Linux as read-only, even when
permissions in Linux indicate the file should be writable.
UDF defines a slightly different set of basic file permissions than Linux.
Specifically, UDF has "delete" and "change attribute" permissions for each
access class (user/group/other). Linux has no equivalents for these.
When the Linux UDF driver creates a file (or directory), no UDF delete or
change attribute permissions are granted. The lack of delete permission
appears to cause Windows to mark an item read-only when its permissions
otherwise indicate that it should be read-write.
Fix this by having UDF delete permissions track Linux write permissions.
Also grant UDF change attribute permission to the owner when creating a
new inode.
Reported by: Ty Young
Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
Link: https://lore.kernel.org/r/20190827121359.9954-1-steve@digidescorp.com
Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r-- | fs/udf/file.c | 3 | ||||
-rw-r--r-- | fs/udf/ialloc.c | 3 | ||||
-rw-r--r-- | fs/udf/inode.c | 31 | ||||
-rw-r--r-- | fs/udf/udf_i.h | 1 | ||||
-rw-r--r-- | fs/udf/udfdecl.h | 1 |
5 files changed, 35 insertions, 4 deletions
diff --git a/fs/udf/file.c b/fs/udf/file.c index cd31e4f6d6da..628941a6b79a 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c | |||
@@ -280,6 +280,9 @@ static int udf_setattr(struct dentry *dentry, struct iattr *attr) | |||
280 | return error; | 280 | return error; |
281 | } | 281 | } |
282 | 282 | ||
283 | if (attr->ia_valid & ATTR_MODE) | ||
284 | udf_update_extra_perms(inode, attr->ia_mode); | ||
285 | |||
283 | setattr_copy(inode, attr); | 286 | setattr_copy(inode, attr); |
284 | mark_inode_dirty(inode); | 287 | mark_inode_dirty(inode); |
285 | return 0; | 288 | return 0; |
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index f8e5872f7cc2..0adb40718a5d 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c | |||
@@ -118,6 +118,9 @@ struct inode *udf_new_inode(struct inode *dir, umode_t mode) | |||
118 | iinfo->i_lenAlloc = 0; | 118 | iinfo->i_lenAlloc = 0; |
119 | iinfo->i_use = 0; | 119 | iinfo->i_use = 0; |
120 | iinfo->i_checkpoint = 1; | 120 | iinfo->i_checkpoint = 1; |
121 | iinfo->i_extraPerms = FE_PERM_U_CHATTR; | ||
122 | udf_update_extra_perms(inode, mode); | ||
123 | |||
121 | if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) | 124 | if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) |
122 | iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; | 125 | iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; |
123 | else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) | 126 | else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) |
diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 54eee39f2698..ea80036d7897 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c | |||
@@ -45,6 +45,13 @@ | |||
45 | 45 | ||
46 | #define EXTENT_MERGE_SIZE 5 | 46 | #define EXTENT_MERGE_SIZE 5 |
47 | 47 | ||
48 | #define FE_MAPPED_PERMS (FE_PERM_U_READ | FE_PERM_U_WRITE | FE_PERM_U_EXEC | \ | ||
49 | FE_PERM_G_READ | FE_PERM_G_WRITE | FE_PERM_G_EXEC | \ | ||
50 | FE_PERM_O_READ | FE_PERM_O_WRITE | FE_PERM_O_EXEC) | ||
51 | |||
52 | #define FE_DELETE_PERMS (FE_PERM_U_DELETE | FE_PERM_G_DELETE | \ | ||
53 | FE_PERM_O_DELETE) | ||
54 | |||
48 | static umode_t udf_convert_permissions(struct fileEntry *); | 55 | static umode_t udf_convert_permissions(struct fileEntry *); |
49 | static int udf_update_inode(struct inode *, int); | 56 | static int udf_update_inode(struct inode *, int); |
50 | static int udf_sync_inode(struct inode *inode); | 57 | static int udf_sync_inode(struct inode *inode); |
@@ -1458,6 +1465,8 @@ reread: | |||
1458 | else | 1465 | else |
1459 | inode->i_mode = udf_convert_permissions(fe); | 1466 | inode->i_mode = udf_convert_permissions(fe); |
1460 | inode->i_mode &= ~sbi->s_umask; | 1467 | inode->i_mode &= ~sbi->s_umask; |
1468 | iinfo->i_extraPerms = le32_to_cpu(fe->permissions) & ~FE_MAPPED_PERMS; | ||
1469 | |||
1461 | read_unlock(&sbi->s_cred_lock); | 1470 | read_unlock(&sbi->s_cred_lock); |
1462 | 1471 | ||
1463 | link_count = le16_to_cpu(fe->fileLinkCount); | 1472 | link_count = le16_to_cpu(fe->fileLinkCount); |
@@ -1631,6 +1640,23 @@ static umode_t udf_convert_permissions(struct fileEntry *fe) | |||
1631 | return mode; | 1640 | return mode; |
1632 | } | 1641 | } |
1633 | 1642 | ||
1643 | void udf_update_extra_perms(struct inode *inode, umode_t mode) | ||
1644 | { | ||
1645 | struct udf_inode_info *iinfo = UDF_I(inode); | ||
1646 | |||
1647 | /* | ||
1648 | * UDF 2.01 sec. 3.3.3.3 Note 2: | ||
1649 | * In Unix, delete permission tracks write | ||
1650 | */ | ||
1651 | iinfo->i_extraPerms &= ~FE_DELETE_PERMS; | ||
1652 | if (mode & 0200) | ||
1653 | iinfo->i_extraPerms |= FE_PERM_U_DELETE; | ||
1654 | if (mode & 0020) | ||
1655 | iinfo->i_extraPerms |= FE_PERM_G_DELETE; | ||
1656 | if (mode & 0002) | ||
1657 | iinfo->i_extraPerms |= FE_PERM_O_DELETE; | ||
1658 | } | ||
1659 | |||
1634 | int udf_write_inode(struct inode *inode, struct writeback_control *wbc) | 1660 | int udf_write_inode(struct inode *inode, struct writeback_control *wbc) |
1635 | { | 1661 | { |
1636 | return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL); | 1662 | return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL); |
@@ -1703,10 +1729,7 @@ static int udf_update_inode(struct inode *inode, int do_sync) | |||
1703 | ((inode->i_mode & 0070) << 2) | | 1729 | ((inode->i_mode & 0070) << 2) | |
1704 | ((inode->i_mode & 0700) << 4); | 1730 | ((inode->i_mode & 0700) << 4); |
1705 | 1731 | ||
1706 | udfperms |= (le32_to_cpu(fe->permissions) & | 1732 | udfperms |= iinfo->i_extraPerms; |
1707 | (FE_PERM_O_DELETE | FE_PERM_O_CHATTR | | ||
1708 | FE_PERM_G_DELETE | FE_PERM_G_CHATTR | | ||
1709 | FE_PERM_U_DELETE | FE_PERM_U_CHATTR)); | ||
1710 | fe->permissions = cpu_to_le32(udfperms); | 1733 | fe->permissions = cpu_to_le32(udfperms); |
1711 | 1734 | ||
1712 | if (S_ISDIR(inode->i_mode) && inode->i_nlink > 0) | 1735 | if (S_ISDIR(inode->i_mode) && inode->i_nlink > 0) |
diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index 00d773d1b7cf..4245d1f63258 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h | |||
@@ -38,6 +38,7 @@ struct udf_inode_info { | |||
38 | __u32 i_next_alloc_block; | 38 | __u32 i_next_alloc_block; |
39 | __u32 i_next_alloc_goal; | 39 | __u32 i_next_alloc_goal; |
40 | __u32 i_checkpoint; | 40 | __u32 i_checkpoint; |
41 | __u32 i_extraPerms; | ||
41 | unsigned i_alloc_type : 3; | 42 | unsigned i_alloc_type : 3; |
42 | unsigned i_efe : 1; /* extendedFileEntry */ | 43 | unsigned i_efe : 1; /* extendedFileEntry */ |
43 | unsigned i_use : 1; /* unallocSpaceEntry */ | 44 | unsigned i_use : 1; /* unallocSpaceEntry */ |
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 65e243ebeb9c..9dd0814f1077 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h | |||
@@ -170,6 +170,7 @@ extern int8_t udf_next_aext(struct inode *, struct extent_position *, | |||
170 | struct kernel_lb_addr *, uint32_t *, int); | 170 | struct kernel_lb_addr *, uint32_t *, int); |
171 | extern int8_t udf_current_aext(struct inode *, struct extent_position *, | 171 | extern int8_t udf_current_aext(struct inode *, struct extent_position *, |
172 | struct kernel_lb_addr *, uint32_t *, int); | 172 | struct kernel_lb_addr *, uint32_t *, int); |
173 | extern void udf_update_extra_perms(struct inode *inode, umode_t mode); | ||
173 | 174 | ||
174 | /* misc.c */ | 175 | /* misc.c */ |
175 | extern struct buffer_head *udf_tgetblk(struct super_block *sb, | 176 | extern struct buffer_head *udf_tgetblk(struct super_block *sb, |