[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