[PATCH 8/8] ntfs: support resident WOF decompression

Hyunchul Lee <[email protected]>
Newsgroups dev.linux.lists.ntfs
Message-ID <[email protected]>
Extend WOF decompression to support files where the  reparse named
data attribute or the compressed chunks themselves are resident. Retrieve
resident metadata using ntfs_attr_lookup() and copy compressed chunks
directly from the resident attribute payload.

Signed-off-by: Hyunchul Lee <[email protected]>
---
 fs/ntfs/wof.c | 114 +++++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 90 insertions(+), 24 deletions(-)

diff --git a/fs/ntfs/wof.c b/fs/ntfs/wof.c
index 4c2a5943ffaf..dd80b30cf123 100644
--- a/fs/ntfs/wof.c
+++ b/fs/ntfs/wof.c
@@ -99,7 +99,8 @@ static int parse_wof_chunk_table(struct ntfs_inode *ni, u64 chunk_idx, u64 chunk
 {
 	u8 bytes_per_off;
 	u8 *buf_aligned = NULL, *buf = NULL;
-	u64 off[2];
+	u64 addr[2] = {0, }, off[2];
+	u64 byte_off;
 	u64 chunk_data_size;
 	int ret = 0;
 
@@ -114,16 +115,15 @@ static int parse_wof_chunk_table(struct ntfs_inode *ni, u64 chunk_idx, u64 chunk
 		return -EINVAL;
 	chunk_data_size = ni->data_size - (chunk_count - 1) * bytes_per_off;
 
+	if (chunk_idx > 0)
+		byte_off = (chunk_idx - 1) * bytes_per_off;
+	else
+		byte_off = 0;
+
 	if (NInoNonResident(ni)) {
-		u64 byte_off;
 		sector_t start_sector;
 		u32 sector_off, sectors;
 
-		if (chunk_idx > 0)
-			byte_off = (chunk_idx - 1) * bytes_per_off;
-		else
-			byte_off = 0;
-
 		start_sector = byte_off >> 9;
 		sector_off = byte_off & ((1 << 9) - 1);
 		sectors = DIV_ROUND_UP(sector_off + 2 * bytes_per_off, 512);
@@ -139,21 +139,61 @@ static int parse_wof_chunk_table(struct ntfs_inode *ni, u64 chunk_idx, u64 chunk
 			goto out;
 		}
 		buf = buf_aligned + sector_off;
+	} else {
+		struct ntfs_attr_search_ctx *ctx = NULL;
+		u32 value_length;
+		u16 value_offset;
+		u8 *attr;
+
+		ctx = ntfs_attr_get_search_ctx(ni, NULL);
+		if (!ctx) {
+			ret = -ENOMEM;
+			goto out;
+		}
 
-		if (chunk_idx + 1 == chunk_count) {
-			if (bytes_per_off == sizeof(__le32))
-				((__le32 *)buf)[1] =
-					cpu_to_le32(ni->data_size -
-						    (chunk_count - 1) * bytes_per_off);
-			else
-				((__le64 *)buf)[1] =
-					cpu_to_le64(ni->data_size -
-						    (chunk_count - 1) * bytes_per_off);
+		ret = ntfs_attr_lookup(AT_DATA, (__le16 *)WOF_NAME,
+				       WOF_NAME_LEN, CASE_SENSITIVE, 0, NULL, 0,
+				       ctx);
+		if (ret) {
+			ntfs_attr_put_search_ctx(ctx);
+			goto out;
 		}
-	} else {
-		/* Resident WOF chunk table logic will be added in Commit 8 */
-		ret = -EOPNOTSUPP;
-		goto out;
+
+		value_length =
+			le32_to_cpu(ctx->attr->data.resident.value_length);
+		value_offset =
+			le16_to_cpu(ctx->attr->data.resident.value_offset);
+
+		if (byte_off + bytes_per_off > value_length ||
+		    (chunk_idx + 1 != chunk_count &&
+		     byte_off + 2 * bytes_per_off > value_length)) {
+			ntfs_attr_put_search_ctx(ctx);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		attr = (u8 *)ctx->attr + value_offset;
+
+		if (chunk_idx + 1 == chunk_count)
+			memcpy((u8 *)addr, attr + byte_off, bytes_per_off);
+		else
+			memcpy((u8 *)addr, attr + byte_off, 2 * bytes_per_off);
+
+		buf = (u8 *)addr;
+
+		ntfs_attr_put_search_ctx(ctx);
+	}
+
+	/* Last chunk has an implicit end offset derived from data_size. */
+	if (chunk_idx + 1 == chunk_count) {
+		if (bytes_per_off == sizeof(__le32))
+			((__le32 *)buf)[1] =
+				cpu_to_le32(ni->data_size -
+					    (chunk_count - 1) * bytes_per_off);
+		else
+			((__le64 *)buf)[1] =
+				cpu_to_le64(ni->data_size -
+					    (chunk_count - 1) * bytes_per_off);
 	}
 
 	if (bytes_per_off == sizeof(__le32)) {
@@ -243,7 +283,7 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
 	}
 
 	wof_ni = NTFS_I(wof_inode);
-	if (!wof_ni->runlist.rl) {
+	if (NInoNonResident(wof_ni) && !NInoFullyMapped(wof_ni)) {
 		err = ntfs_attr_map_whole_runlist(wof_ni);
 		if (err)
 			goto out_iput;
@@ -308,9 +348,35 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
 
 	/* Read compressed data from disk */
 	if (!NInoNonResident(wof_ni)) {
-		/* Resident WOF data read logic will be added in Commit 8 */
-		err = -EOPNOTSUPP;
-		goto out_free;
+		struct ntfs_attr_search_ctx *ctx = NULL;
+
+		ctx = ntfs_attr_get_search_ctx(wof_ni, NULL);
+		if (!ctx) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+
+		err = ntfs_attr_lookup(AT_DATA, (__le16 *)WOF_NAME,
+				       WOF_NAME_LEN, CASE_SENSITIVE,
+				       0, NULL, 0, ctx);
+		if (err) {
+			ntfs_attr_put_search_ctx(ctx);
+			goto out_free;
+		}
+
+		if (chunk_offset + chunk_size >
+		    le32_to_cpu(ctx->attr->data.resident.value_length)) {
+			ntfs_attr_put_search_ctx(ctx);
+			err = -EINVAL;
+			goto out_free;
+		}
+
+		memcpy(chunk_mem_aligned,
+		       (u8 *)ctx->attr +
+		       le16_to_cpu(ctx->attr->data.resident.value_offset) +
+		       chunk_offset, chunk_size);
+		ntfs_attr_put_search_ctx(ctx);
+		chunk_mem = chunk_mem_aligned;
 	} else {
 		err = ntfs_bdev_read_from_rl(vol, &wof_ni->runlist,
 					     chunk_offset >> 9,

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.