[PATCH v4 05/10] ntfs: parse REPARSE_TAG_WOF

Hyunchul Lee <[email protected]>
Newsgroups dev.linux.lists.ntfs
Message-ID <[email protected]>
Introduce parsing support for REPARSE_TAG_WOF reparse points. Rename
ntfs_make_symlink() to ntfs_parse_reparse() since it now handles both
symlinks and WOF reparse tags. Introduce NI_WofCompressed flag to
indicate files compressed via Windows System Compression (WOF), and
configure compressed block size accordingly (12 to 15 bits based on the
format).

Signed-off-by: Hyunchul Lee <[email protected]>
---
 fs/ntfs/inode.c   |   7 +-
 fs/ntfs/inode.h   |   2 +
 fs/ntfs/reparse.c | 197 +++++++++++++++++++++++++++++++++++++-----------------
 fs/ntfs/reparse.h |   2 +-
 4 files changed, 144 insertions(+), 64 deletions(-)

diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 46be4514b60f..45e013eb3541 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -869,7 +869,9 @@ static int ntfs_read_locked_inode(struct inode *vi)
 	if (ni->flags & FILE_ATTR_REPARSE_POINT) {
 		unsigned int mode;
 
-		mode = ntfs_make_symlink(ni);
+		err = ntfs_parse_reparse(ni, &mode);
+		if (err)
+			goto unm_err_out;
 		if (mode)
 			vi->i_mode |= mode;
 		else {
@@ -1214,7 +1216,8 @@ static int ntfs_read_locked_inode(struct inode *vi)
 	 * sizes of all non-resident attributes present to give us the Linux
 	 * correct size that should go into i_blocks (after division by 512).
 	 */
-	if (S_ISREG(vi->i_mode) && (NInoCompressed(ni) || NInoSparse(ni)))
+	if (S_ISREG(vi->i_mode) &&
+	    (NInoCompressed(ni) || (NInoSparse(ni) && !NInoWofCompressed(ni))))
 		vi->i_blocks = ni->itype.compressed.size >> 9;
 	else
 		vi->i_blocks = ni->allocated_size >> 9;
diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h
index aee1c1fdb609..ff61bd402df0 100644
--- a/fs/ntfs/inode.h
+++ b/fs/ntfs/inode.h
@@ -189,6 +189,7 @@ enum {
 	NI_NonResident,
 	NI_IndexAllocPresent,
 	NI_Compressed,
+	NI_WofCompressed,
 	NI_Encrypted,
 	NI_Sparse,
 	NI_SparseDisabled,
@@ -248,6 +249,7 @@ NINO_FNS(MstProtected)
 NINO_FNS(NonResident)
 NINO_FNS(IndexAllocPresent)
 NINO_FNS(Compressed)
+NINO_FNS(WofCompressed)
 NINO_FNS(Encrypted)
 NINO_FNS(Sparse)
 NINO_FNS(SparseDisabled)
diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c
index b8d601f5500f..0e3ed9f520dc 100644
--- a/fs/ntfs/reparse.c
+++ b/fs/ntfs/reparse.c
@@ -24,6 +24,25 @@ struct wsl_link_reparse_data {
 	char	link[];
 };
 
+struct wof_reparse_data {
+	__le32 version;
+	__le32 provider;
+	__le32 provider_version;
+	__le32 compression_format;
+} __packed;
+
+#define WOF_CURRENT_VERSION			cpu_to_le32(1)
+
+#define WOF_PROVIDER_WIM			cpu_to_le32(1)
+#define WOF_PROVIDER_FILE			cpu_to_le32(2)
+
+#define WOF_PROVIDER_CURRENT_VERSION		cpu_to_le32(1)
+
+#define WOF_COMPRESSION_XPRESS4K		cpu_to_le32(0)
+#define WOF_COMPRESSION_LZX			cpu_to_le32(1)
+#define WOF_COMPRESSION_XPRESS8K		cpu_to_le32(2)
+#define WOF_COMPRESSION_XPRESS16K		cpu_to_le32(3)
+
 static bool reparse_name_is_valid(size_t size, size_t name_off, u16 len)
 {
 	if ((name_off | len) & 1)
@@ -203,6 +222,28 @@ static bool valid_reparse_data(struct ntfs_inode *ni,
 		    !(ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN))
 			return false;
 		break;
+	case IO_REPARSE_TAG_WOF: {
+		const struct wof_reparse_data *wof_data =
+			(const struct wof_reparse_data *)reparse_attr->reparse_data;
+
+		if (!valid_reparse_buffer(ni, reparse_attr, size,
+					  sizeof(struct wof_reparse_data)))
+			return false;
+
+		if (le16_to_cpu(reparse_attr->reparse_data_length) <
+		    sizeof(struct wof_reparse_data) ||
+		    wof_data->version != WOF_CURRENT_VERSION ||
+		    wof_data->provider != WOF_PROVIDER_FILE ||
+		    wof_data->provider_version !=
+		    WOF_PROVIDER_CURRENT_VERSION)
+			return false;
+		if (wof_data->compression_format != WOF_COMPRESSION_XPRESS4K &&
+		    wof_data->compression_format != WOF_COMPRESSION_XPRESS8K &&
+		    wof_data->compression_format != WOF_COMPRESSION_XPRESS16K &&
+		    wof_data->compression_format != WOF_COMPRESSION_LZX)
+			return false;
+		break;
+	}
 	default:
 		if (!valid_reparse_buffer(ni, reparse_attr, size, 0))
 			return false;
@@ -239,89 +280,123 @@ static unsigned int ntfs_reparse_tag_mode(__le32 reparse_tag)
 }
 
 /*
- * Get the target for symbolic link
+ * Parse reparse point data and initialize its in-memory representation.
  */
-unsigned int ntfs_make_symlink(struct ntfs_inode *ni)
+int ntfs_parse_reparse(struct ntfs_inode *ni, unsigned int *mode)
 {
 	s64 attr_size = 0;
-	int err;
+	int err = -EINVAL;
 	unsigned int lth;
 	struct reparse_point *reparse_attr;
-	unsigned int mode = 0;
 
 	kvfree(ni->target);
 	ni->target = NULL;
 	ni->reparse_tag = 0;
 	ni->reparse_flags = 0;
+	*mode = 0;
 
 	reparse_attr = ntfs_attr_readall(ni, AT_REPARSE_POINT, NULL, 0,
 					 &attr_size);
-	if (IS_ERR(reparse_attr))
-		reparse_attr = NULL;
-	if (reparse_attr &&
-	    valid_reparse_data(ni, reparse_attr, attr_size)) {
-		err = -EINVAL;
+	if (IS_ERR(reparse_attr)) {
+		err = PTR_ERR(reparse_attr);
+		ntfs_error(ni->vol->sb,
+			   "Failed to read reparse point: %d.", err);
+		return err;
+	}
+	if (!valid_reparse_data(ni, reparse_attr, attr_size)) {
+		ntfs_error(ni->vol->sb, "Invalid reparse point.");
+		err = -EFSCORRUPTED;
+		goto out;
+	}
 
-		switch (reparse_attr->reparse_tag) {
-		case IO_REPARSE_TAG_MOUNT_POINT:
-		{
-			struct mount_point_reparse_data *data =
-				(struct mount_point_reparse_data *)reparse_attr->reparse_data;
-			const __le16 *name = (const __le16 *)((u8 *)data->path_buffer +
-					      le16_to_cpu(data->substitute_name_offset));
-
-			err = ntfs_reparse_target_to_nls(ni->vol,
-							 name,
-							 le16_to_cpu(data->substitute_name_length),
-							 &ni->target);
-			break;
+	switch (reparse_attr->reparse_tag) {
+	case IO_REPARSE_TAG_MOUNT_POINT:
+	{
+		struct mount_point_reparse_data *data =
+			(struct mount_point_reparse_data *)reparse_attr->reparse_data;
+		const __le16 *name = (const __le16 *)((u8 *)data->path_buffer +
+				      le16_to_cpu(data->substitute_name_offset));
+
+		err = ntfs_reparse_target_to_nls(ni->vol,
+						 name,
+						 le16_to_cpu(data->substitute_name_length),
+						 &ni->target);
+		break;
+	}
+	case IO_REPARSE_TAG_SYMLINK:
+	{
+		struct symlink_reparse_data *data =
+			(struct symlink_reparse_data *)reparse_attr->reparse_data;
+		const __le16 *name = (const __le16 *)((u8 *)data->path_buffer +
+						le16_to_cpu(data->substitute_name_offset));
+
+		err = ntfs_reparse_target_to_nls(ni->vol,
+						 name,
+						 le16_to_cpu(data->substitute_name_length),
+						 &ni->target);
+		if (!err)
+			ni->reparse_flags = data->flags;
+		break;
+	}
+	case IO_REPARSE_TAG_LX_SYMLINK:
+	{
+		struct wsl_link_reparse_data *wsl_link_data =
+			(struct wsl_link_reparse_data *)reparse_attr->reparse_data;
+
+		if (wsl_link_data->type == cpu_to_le32(2)) {
+			lth = le16_to_cpu(reparse_attr->reparse_data_length) -
+				  sizeof(wsl_link_data->type);
+			ni->target = kvzalloc(lth + 1, GFP_NOFS);
+			if (ni->target) {
+				memcpy(ni->target, wsl_link_data->link, lth);
+				ni->target[lth] = 0;
+				err = 0;
+			}
 		}
-		case IO_REPARSE_TAG_SYMLINK:
-		{
-			struct symlink_reparse_data *data =
-				(struct symlink_reparse_data *)reparse_attr->reparse_data;
-			const __le16 *name = (const __le16 *)((u8 *)data->path_buffer +
-							le16_to_cpu(data->substitute_name_offset));
-
-			err = ntfs_reparse_target_to_nls(ni->vol,
-							 name,
-							 le16_to_cpu(data->substitute_name_length),
-							 &ni->target);
-			if (!err)
-				ni->reparse_flags = data->flags;
+		break;
+	}
+	case IO_REPARSE_TAG_WOF:
+	{
+#ifdef CONFIG_NTFS_FS_WOF_COMPRESSION
+		const struct wof_reparse_data *wof_data =
+			(const struct wof_reparse_data *)reparse_attr->reparse_data;
+
+		switch (wof_data->compression_format) {
+		case WOF_COMPRESSION_XPRESS4K:
+			ni->itype.compressed.block_size_bits = 12;
 			break;
-		}
-		case IO_REPARSE_TAG_LX_SYMLINK:
-		{
-			struct wsl_link_reparse_data *wsl_link_data =
-				(struct wsl_link_reparse_data *)reparse_attr->reparse_data;
-
-			if (wsl_link_data->type == cpu_to_le32(2)) {
-				lth = le16_to_cpu(reparse_attr->reparse_data_length) -
-					  sizeof(wsl_link_data->type);
-				ni->target = kvzalloc(lth + 1, GFP_NOFS);
-				if (ni->target) {
-					memcpy(ni->target, wsl_link_data->link, lth);
-					ni->target[lth] = 0;
-					err = 0;
-				}
-			}
+		case WOF_COMPRESSION_XPRESS8K:
+			ni->itype.compressed.block_size_bits = 13;
+			break;
+		case WOF_COMPRESSION_XPRESS16K:
+			ni->itype.compressed.block_size_bits = 14;
+			break;
+		case WOF_COMPRESSION_LZX:
+			ni->itype.compressed.block_size_bits = 15;
 			break;
 		}
-		default:
-			err = 0;
-		}
+		ni->itype.compressed.block_size =
+			1 << ni->itype.compressed.block_size_bits;
+#endif
+		NInoSetWofCompressed(ni);
+		VFS_I(ni)->i_mode &= ~0222;
+		err = 0;
+		break;
+	}
+	default:
+		err = 0;
+	}
 
-		if (!err) {
-			mode = ntfs_reparse_tag_mode(reparse_attr->reparse_tag);
-			ni->reparse_tag = reparse_attr->reparse_tag;
-		}
-	} else
-		ni->flags &= ~FILE_ATTR_REPARSE_POINT;
+	if (!err) {
+		*mode = ntfs_reparse_tag_mode(
+			reparse_attr->reparse_tag);
+		ni->reparse_tag = reparse_attr->reparse_tag;
+	}
 
+out:
 	kvfree(reparse_attr);
 
-	return mode;
+	return err;
 }
 
 unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref)
diff --git a/fs/ntfs/reparse.h b/fs/ntfs/reparse.h
index c11a5bb7e6a5..b6360b0452e6 100644
--- a/fs/ntfs/reparse.h
+++ b/fs/ntfs/reparse.h
@@ -9,7 +9,7 @@
 
 extern __le16 reparse_index_name[];
 
-unsigned int ntfs_make_symlink(struct ntfs_inode *ni);
+int ntfs_parse_reparse(struct ntfs_inode *ni, unsigned int *mode);
 unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mref);
 int ntfs_translate_symlink_path(struct dentry *dentry, const char *target,
 				char **translated);

-- 
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.