[PATCH v2 4/8] 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 | 97 ++++++++++++++++++++++++++++++++++++++++++++++++------- fs/ntfs/reparse.h | 2 +- 4 files changed, 94 insertions(+), 14 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 0d3988992119..5c2771e82c65 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,26 +280,32 @@ 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 (reparse_attr && - valid_reparse_data(ni, reparse_attr, attr_size)) { - err = -EINVAL; + if (!reparse_attr) { + ntfs_error(ni->vol->sb, "Failed to read reparse point."); + return -EIO; + } + 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: @@ -306,20 +353,48 @@ unsigned int ntfs_make_symlink(struct ntfs_inode *ni) } 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 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; + } + 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); + *mode = ntfs_reparse_tag_mode( + reparse_attr->reparse_tag); ni->reparse_tag = reparse_attr->reparse_tag; } - } else - ni->flags &= ~FILE_ATTR_REPARSE_POINT; +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