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 <steve@digidescorp.com>
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Steven J. Magnani
2019-08-14 07:50:02 -05:00
committed by Jan Kara
parent 56db199169
commit ab9a3a7372
3 changed files with 29 additions and 2 deletions

View File

@@ -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);