From ab9a3a737284b3d9e1d2ba43a0ef31b3ef2e2417 Mon Sep 17 00:00:00 2001 From: "Steven J. Magnani" Date: Wed, 14 Aug 2019 07:50:02 -0500 Subject: udf: reduce leakage of blocks related to named streams Windows is capable of creating UDF files having named streams. One example is the "Zone.Identifier" stream attached automatically to files downloaded from a network. See: https://msdn.microsoft.com/en-us/library/dn392609.aspx Modification of a file having one or more named streams in Linux causes the stream directory to become detached from the file, essentially leaking all blocks pertaining to the file's streams. Fix by saving off information about an inode's streams when reading it, for later use when its on-disk data is updated. Link: https://lore.kernel.org/r/20190814125002.10869-1-steve@digidescorp.com Signed-off-by: Steven J. Magnani Signed-off-by: Jan Kara --- fs/udf/inode.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'fs/udf/inode.c') diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 9bb18311a22f..54eee39f2698 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1485,6 +1485,8 @@ reread: iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr); iinfo->i_lenAlloc = le32_to_cpu(fe->lengthAllocDescs); iinfo->i_checkpoint = le32_to_cpu(fe->checkpoint); + iinfo->i_streamdir = 0; + iinfo->i_lenStreams = 0; } else { inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); @@ -1498,6 +1500,16 @@ reread: iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr); iinfo->i_lenAlloc = le32_to_cpu(efe->lengthAllocDescs); iinfo->i_checkpoint = le32_to_cpu(efe->checkpoint); + + /* Named streams */ + iinfo->i_streamdir = (efe->streamDirectoryICB.extLength != 0); + iinfo->i_locStreamdir = + lelb_to_cpu(efe->streamDirectoryICB.extLocation); + iinfo->i_lenStreams = le64_to_cpu(efe->objectSize); + if (iinfo->i_lenStreams >= inode->i_size) + iinfo->i_lenStreams -= inode->i_size; + else + iinfo->i_lenStreams = 0; } inode->i_generation = iinfo->i_unique; @@ -1760,9 +1772,19 @@ static int udf_update_inode(struct inode *inode, int do_sync) iinfo->i_ext.i_data, inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); - efe->objectSize = cpu_to_le64(inode->i_size); + efe->objectSize = + cpu_to_le64(inode->i_size + iinfo->i_lenStreams); efe->logicalBlocksRecorded = cpu_to_le64(lb_recorded); + if (iinfo->i_streamdir) { + struct long_ad *icb_lad = &efe->streamDirectoryICB; + + icb_lad->extLocation = + cpu_to_lelb(iinfo->i_locStreamdir); + icb_lad->extLength = + cpu_to_le32(inode->i_sb->s_blocksize); + } + udf_adjust_time(iinfo, inode->i_atime); udf_adjust_time(iinfo, inode->i_mtime); udf_adjust_time(iinfo, inode->i_ctime); -- cgit v1.2.2 From c3367a1b47d590f97109cd4b5189e750fb26c0f1 Mon Sep 17 00:00:00 2001 From: "Steven J. Magnani" Date: Tue, 27 Aug 2019 07:13:59 -0500 Subject: 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 Link: https://lore.kernel.org/r/20190827121359.9954-1-steve@digidescorp.com Signed-off-by: Jan Kara --- fs/udf/inode.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'fs/udf/inode.c') 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 @@ #define EXTENT_MERGE_SIZE 5 +#define FE_MAPPED_PERMS (FE_PERM_U_READ | FE_PERM_U_WRITE | FE_PERM_U_EXEC | \ + FE_PERM_G_READ | FE_PERM_G_WRITE | FE_PERM_G_EXEC | \ + FE_PERM_O_READ | FE_PERM_O_WRITE | FE_PERM_O_EXEC) + +#define FE_DELETE_PERMS (FE_PERM_U_DELETE | FE_PERM_G_DELETE | \ + FE_PERM_O_DELETE) + static umode_t udf_convert_permissions(struct fileEntry *); static int udf_update_inode(struct inode *, int); static int udf_sync_inode(struct inode *inode); @@ -1458,6 +1465,8 @@ reread: else inode->i_mode = udf_convert_permissions(fe); inode->i_mode &= ~sbi->s_umask; + iinfo->i_extraPerms = le32_to_cpu(fe->permissions) & ~FE_MAPPED_PERMS; + read_unlock(&sbi->s_cred_lock); link_count = le16_to_cpu(fe->fileLinkCount); @@ -1631,6 +1640,23 @@ static umode_t udf_convert_permissions(struct fileEntry *fe) return mode; } +void udf_update_extra_perms(struct inode *inode, umode_t mode) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + + /* + * UDF 2.01 sec. 3.3.3.3 Note 2: + * In Unix, delete permission tracks write + */ + iinfo->i_extraPerms &= ~FE_DELETE_PERMS; + if (mode & 0200) + iinfo->i_extraPerms |= FE_PERM_U_DELETE; + if (mode & 0020) + iinfo->i_extraPerms |= FE_PERM_G_DELETE; + if (mode & 0002) + iinfo->i_extraPerms |= FE_PERM_O_DELETE; +} + int udf_write_inode(struct inode *inode, struct writeback_control *wbc) { 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) ((inode->i_mode & 0070) << 2) | ((inode->i_mode & 0700) << 4); - udfperms |= (le32_to_cpu(fe->permissions) & - (FE_PERM_O_DELETE | FE_PERM_O_CHATTR | - FE_PERM_G_DELETE | FE_PERM_G_CHATTR | - FE_PERM_U_DELETE | FE_PERM_U_CHATTR)); + udfperms |= iinfo->i_extraPerms; fe->permissions = cpu_to_le32(udfperms); if (S_ISDIR(inode->i_mode) && inode->i_nlink > 0) -- cgit v1.2.2