Hi Namjae,
The compression unit is not only used for compressed files. It is also used for sparse files. This is from some internal documentation I wrote:
The compression unit expressed as the log to the base 2 of the number of clusters in a compression unit. 0 means not compressed. (This effectively limits the compression unit size to be a power of two clusters.) WinNT4 only uses a value of 4. Sparse files have this set to 0 on XPSP2. On Windows 7, sparse files have this set to 4 for cluster size <= 4096, to 3 for cluster size 8192, to 2 for cluster size 16384, to 1 for cluster size 32768 and to 0 for cluster size 65536, i.e. for cluster size >= 4096, the compression unit is set such that 2^compression unit * cluster_size = 65536 bytes, i.e. so that the compression block size is 65536 bytes thus for cluster size >= 4096, the compression unit can be calculated as compression_unit = log2(65536 / cluster_size). And the closest we we can get to approximating Windows behaviour is to use compression_unit 4 and only allow it for cluster size <= 4096 when NTFS version is < 3.0, use compression_unit 0 and only allow it for cluster size <= 4096 when NTFS version is = 3.0 and use the correct number as described above for Windows 7 when NTFS version is >= 3.1. This is not ideal as XP has NTFS version 3.1, too but XP is already out of support so it is better to be aligning ourselves with current Windows versions like 7 and 8. Finally, note that even Windows 7 disabled compressed files when cluster size > 4096 thus the new compression unit only applies to sparse files and nothing changes for compressed files (unless Windows 8 or later changes this behaviour which is as yet untested).
The correct check that is missing here is:
if (NInoSparse(ni) && a->data.non_resident.compression_unit && a->data.non_resident.compression_unit != vol->sparse_compression_unit) {
ntfs_error(vi->i_sb, "Found non-standard compression unit (%u instead of 0 or %d). Cannot handle this.",
a->data.non_resident.compression_unit, vol->sparse_compression_unit);
err = -EOPNOTSUPP;
goto unm_err_out;
}
And vol->sparse_compression_unit is set at mount time like this:
vol->sparse_compression_unit = 4;
if (vol->cluster_size > 4096) {
switch (vol->cluster_size) {
case 65536:
vol->sparse_compression_unit = 0;
break;
case 32768:
vol->sparse_compression_unit = 1;
break;
case 16384:
vol->sparse_compression_unit = 2;
break;
case 8192:
vol->sparse_compression_unit = 3;
break;
}
}
Best regards,
Anton
On 14 Aug 2023, at 02:02, Namjae Jeon <lin...@ke...> wrote:
2023-08-13 14:59 GMT+09:00, Manas Ghandat <gha...@gm...<mailto:gha...@gm...>>:
Hi,
Currently there is not check for ni->itype.compressed.block_size when
a->data.non_resident.compression_unit is present and NInoSparse(ni) is
true. Added the required check to calculation of block size.
Signed-off-by: Manas Ghandat <gha...@gm...>
Reported-by: syz...@sy...
Closes: https://syzkaller.appspot.com/bug?extid=4768a8f039aa677897d0
Fix-commit-ID: upstream f40ddce88593482919761f74910f42f4b84c004b
---
V3 -> V4: Fix description
V2 -> V3: Fix patching issue.
V1 -> V2: Cleaned up coding style.
fs/ntfs/inode.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 6c3f38d66579..a657322874ed 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1077,6 +1077,15 @@ static int ntfs_read_locked_inode(struct inode *vi)
goto unm_err_out;
}
if (a->data.non_resident.compression_unit) {
+ if (a->data.non_resident.compression_unit +
+ vol->cluster_size_bits > 32) {
+ ntfs_error(vi->i_sb,
+ "Found non-standard compression unit (%u). Cannot handle this.",
+ a->data.non_resident.compression_unit
+ );
+ err = -EOPNOTSUPP;
+ goto unm_err_out;
+ }
compression_unit seems to be used when the ntfs inode is compressed.
And it should be either 0 or 4 value. So, I think we can set related
compression block variables of ntfs inode only when ni is
NInoCompressed like this... Anton, Am I missing something ?
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index efe0602b4e51..e5a7d81d575b 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1076,7 +1076,8 @@ static int ntfs_read_locked_inode(struct inode *vi)
err = -EOPNOTSUPP;
goto unm_err_out;
}
- if (a->data.non_resident.compression_unit) {
+ if (NInoCompressed(ni) &&
+ a->data.non_resident.compression_unit) {
ni->itype.compressed.block_size = 1U <<
(a->data.non_resident.
compression_unit +
ni->itype.compressed.block_size = 1U <<
(a->data.non_resident.
compression_unit +
--
2.37.2
--
Anton Altaparmakov <anton at tuxera.com> (replace at with @)
Lead in File System Development, Tuxera Inc., http://www.tuxera.com/
Linux NTFS maintainer
|