xref: /linux/fs/ntfs/wof.c (revision 97be98b94dc8e43a3e4dedccaf9683fb806e49aa)
12bef615eSHyunchul Lee // SPDX-License-Identifier: GPL-2.0-or-later
22bef615eSHyunchul Lee /*
32bef615eSHyunchul Lee  * Windows System Compression (WOF) decompression glue.
42bef615eSHyunchul Lee  *
52bef615eSHyunchul Lee  * Copyright (c) 2026 LG Electronics Co., Ltd.
62bef615eSHyunchul Lee  */
72bef615eSHyunchul Lee 
82bef615eSHyunchul Lee #include <linux/fs.h>
92bef615eSHyunchul Lee #include <linux/blkdev.h>
102bef615eSHyunchul Lee #include <linux/overflow.h>
112bef615eSHyunchul Lee #include <linux/pagemap.h>
122bef615eSHyunchul Lee #include <linux/sched/mm.h>
132bef615eSHyunchul Lee #include <linux/slab.h>
142bef615eSHyunchul Lee #include <linux/unaligned.h>
152bef615eSHyunchul Lee #include <linux/vmalloc.h>
162bef615eSHyunchul Lee 
172bef615eSHyunchul Lee #include "ntfs.h"
182bef615eSHyunchul Lee #include "inode.h"
192bef615eSHyunchul Lee #include "debug.h"
202bef615eSHyunchul Lee #include "ntfs_codec.h"
212bef615eSHyunchul Lee #include "attrib.h"
222bef615eSHyunchul Lee 
232bef615eSHyunchul Lee static const __le16 WOF_NAME[] = {
242bef615eSHyunchul Lee 	cpu_to_le16('W'), cpu_to_le16('o'), cpu_to_le16('f'),
252bef615eSHyunchul Lee 	cpu_to_le16('C'), cpu_to_le16('o'), cpu_to_le16('m'),
262bef615eSHyunchul Lee 	cpu_to_le16('p'), cpu_to_le16('r'), cpu_to_le16('e'),
272bef615eSHyunchul Lee 	cpu_to_le16('s'), cpu_to_le16('s'), cpu_to_le16('e'),
282bef615eSHyunchul Lee 	cpu_to_le16('d'), cpu_to_le16('D'), cpu_to_le16('a'),
292bef615eSHyunchul Lee 	cpu_to_le16('t'), cpu_to_le16('a'),
302bef615eSHyunchul Lee };
312bef615eSHyunchul Lee 
322bef615eSHyunchul Lee #define WOF_NAME_LEN 17
332bef615eSHyunchul Lee 
342bef615eSHyunchul Lee #define NTFS_WOF_MAX_COMP_UNIT (1U << 15)
352bef615eSHyunchul Lee #define NTFS_WOF_MAX_PAGES \
362bef615eSHyunchul Lee 	DIV_ROUND_UP(NTFS_WOF_MAX_COMP_UNIT + PAGE_SIZE - 1, PAGE_SIZE)
372bef615eSHyunchul Lee 
382bef615eSHyunchul Lee struct ntfs_wof_workspace {
392bef615eSHyunchul Lee 	struct mutex *lock;
402bef615eSHyunchul Lee 	const struct ntfs_codec_ops *codec;
412bef615eSHyunchul Lee 	u32 comp_unit;
422bef615eSHyunchul Lee 	void *output;
432bef615eSHyunchul Lee 	void *scratch;
442bef615eSHyunchul Lee };
452bef615eSHyunchul Lee 
462bef615eSHyunchul Lee static DEFINE_MUTEX(ntfs_wof_xpress4k_lock);
472bef615eSHyunchul Lee static DEFINE_MUTEX(ntfs_wof_xpress8k_lock);
482bef615eSHyunchul Lee static DEFINE_MUTEX(ntfs_wof_xpress16k_lock);
492bef615eSHyunchul Lee static DEFINE_MUTEX(ntfs_wof_lzx32k_lock);
502bef615eSHyunchul Lee 
512bef615eSHyunchul Lee static struct ntfs_wof_workspace ntfs_wof_xpress4k_workspace = {
522bef615eSHyunchul Lee 	.lock = &ntfs_wof_xpress4k_lock,
532bef615eSHyunchul Lee 	.codec = &ntfs_xpress4k_codec_ops,
542bef615eSHyunchul Lee 	.comp_unit = 1U << 12,
552bef615eSHyunchul Lee };
562bef615eSHyunchul Lee 
572bef615eSHyunchul Lee static struct ntfs_wof_workspace ntfs_wof_xpress8k_workspace = {
582bef615eSHyunchul Lee 	.lock = &ntfs_wof_xpress8k_lock,
592bef615eSHyunchul Lee 	.codec = &ntfs_xpress8k_codec_ops,
602bef615eSHyunchul Lee 	.comp_unit = 1U << 13,
612bef615eSHyunchul Lee };
622bef615eSHyunchul Lee 
632bef615eSHyunchul Lee static struct ntfs_wof_workspace ntfs_wof_xpress16k_workspace = {
642bef615eSHyunchul Lee 	.lock = &ntfs_wof_xpress16k_lock,
652bef615eSHyunchul Lee 	.codec = &ntfs_xpress16k_codec_ops,
662bef615eSHyunchul Lee 	.comp_unit = 1U << 14,
672bef615eSHyunchul Lee };
682bef615eSHyunchul Lee 
692bef615eSHyunchul Lee static struct ntfs_wof_workspace ntfs_wof_lzx32k_workspace = {
702bef615eSHyunchul Lee 	.lock = &ntfs_wof_lzx32k_lock,
712bef615eSHyunchul Lee 	.codec = &ntfs_lzx32k_codec_ops,
722bef615eSHyunchul Lee 	.comp_unit = 1U << 15,
732bef615eSHyunchul Lee };
742bef615eSHyunchul Lee 
752bef615eSHyunchul Lee static struct ntfs_wof_workspace *const ntfs_wof_workspaces[] = {
762bef615eSHyunchul Lee 	&ntfs_wof_xpress4k_workspace,
772bef615eSHyunchul Lee 	&ntfs_wof_xpress8k_workspace,
782bef615eSHyunchul Lee 	&ntfs_wof_xpress16k_workspace,
792bef615eSHyunchul Lee 	&ntfs_wof_lzx32k_workspace,
802bef615eSHyunchul Lee };
812bef615eSHyunchul Lee 
822bef615eSHyunchul Lee static struct ntfs_wof_workspace *ntfs_wof_workspace(u8 block_size_bits)
832bef615eSHyunchul Lee {
842bef615eSHyunchul Lee 	switch (block_size_bits) {
852bef615eSHyunchul Lee 	case 12:
862bef615eSHyunchul Lee 		return &ntfs_wof_xpress4k_workspace;
872bef615eSHyunchul Lee 	case 13:
882bef615eSHyunchul Lee 		return &ntfs_wof_xpress8k_workspace;
892bef615eSHyunchul Lee 	case 14:
902bef615eSHyunchul Lee 		return &ntfs_wof_xpress16k_workspace;
912bef615eSHyunchul Lee 	case 15:
922bef615eSHyunchul Lee 		return &ntfs_wof_lzx32k_workspace;
932bef615eSHyunchul Lee 	default:
942bef615eSHyunchul Lee 		return NULL;
952bef615eSHyunchul Lee 	}
962bef615eSHyunchul Lee }
972bef615eSHyunchul Lee 
98*41a52ba4SZhan Xusheng /*
99*41a52ba4SZhan Xusheng  * Size of the buffer a chunk is read into.  A chunk is read straight off the
100*41a52ba4SZhan Xusheng  * device, so the buffer has to hold @comp_unit bytes plus the leading partial
101*41a52ba4SZhan Xusheng  * sector.
102*41a52ba4SZhan Xusheng  */
103*41a52ba4SZhan Xusheng static size_t ntfs_wof_input_size(const struct ntfs_wof_workspace *ws)
104*41a52ba4SZhan Xusheng {
105*41a52ba4SZhan Xusheng 	return round_up((size_t)ws->comp_unit + 511, 512);
106*41a52ba4SZhan Xusheng }
107*41a52ba4SZhan Xusheng 
1082bef615eSHyunchul Lee static int ntfs_wof_workspace_prepare(struct ntfs_wof_workspace *ws)
1092bef615eSHyunchul Lee {
110*41a52ba4SZhan Xusheng 	void *output, *scratch;
1112bef615eSHyunchul Lee 	size_t scratch_size;
1122bef615eSHyunchul Lee 
113*41a52ba4SZhan Xusheng 	if (ws->output)
1142bef615eSHyunchul Lee 		return 0;
1152bef615eSHyunchul Lee 
1162bef615eSHyunchul Lee 	scratch_size = ws->codec->scratch_size(ws->comp_unit);
1172bef615eSHyunchul Lee 	if (!scratch_size)
1182bef615eSHyunchul Lee 		return -EINVAL;
1192bef615eSHyunchul Lee 
1202bef615eSHyunchul Lee 	output = kvmalloc(ws->comp_unit, GFP_NOFS);
1212bef615eSHyunchul Lee 	scratch = kvzalloc(scratch_size, GFP_NOFS);
122*41a52ba4SZhan Xusheng 	if (!output || !scratch) {
1232bef615eSHyunchul Lee 		kvfree(output);
1242bef615eSHyunchul Lee 		kvfree(scratch);
1252bef615eSHyunchul Lee 		return -ENOMEM;
1262bef615eSHyunchul Lee 	}
1272bef615eSHyunchul Lee 
1282bef615eSHyunchul Lee 	ws->output = output;
1292bef615eSHyunchul Lee 	ws->scratch = scratch;
1302bef615eSHyunchul Lee 	return 0;
1312bef615eSHyunchul Lee }
1322bef615eSHyunchul Lee 
1332bef615eSHyunchul Lee void ntfs_wof_free_workspaces(void)
1342bef615eSHyunchul Lee {
1352bef615eSHyunchul Lee 	unsigned int i;
1362bef615eSHyunchul Lee 
1372bef615eSHyunchul Lee 	for (i = 0; i < ARRAY_SIZE(ntfs_wof_workspaces); i++) {
1382bef615eSHyunchul Lee 		struct ntfs_wof_workspace *ws = ntfs_wof_workspaces[i];
1392bef615eSHyunchul Lee 
1402bef615eSHyunchul Lee 		mutex_lock(ws->lock);
1412bef615eSHyunchul Lee 		kvfree(ws->output);
1422bef615eSHyunchul Lee 		kvfree(ws->scratch);
1432bef615eSHyunchul Lee 		ws->output = NULL;
1442bef615eSHyunchul Lee 		ws->scratch = NULL;
1452bef615eSHyunchul Lee 		mutex_unlock(ws->lock);
1462bef615eSHyunchul Lee 	}
1472bef615eSHyunchul Lee }
1482bef615eSHyunchul Lee 
1492bef615eSHyunchul Lee static int ntfs_bdev_read_from_rl(struct ntfs_volume *vol,
1502bef615eSHyunchul Lee 				  struct runlist *runlist,
1512bef615eSHyunchul Lee 				  sector_t start_sector, u64 sector_count,
1522bef615eSHyunchul Lee 				  void *buf)
1532bef615eSHyunchul Lee {
1542bef615eSHyunchul Lee 	struct runlist_element *rl;
1552bef615eSHyunchul Lee 	u32 sec_per_clu_bits;
1562bef615eSHyunchul Lee 	s64 vcn;
1572bef615eSHyunchul Lee 	u64 sec_off;
1582bef615eSHyunchul Lee 	size_t buf_off = 0;
1592bef615eSHyunchul Lee 	unsigned int nofs_flags;
1602bef615eSHyunchul Lee 	int err;
1612bef615eSHyunchul Lee 
1622bef615eSHyunchul Lee 	if (vol->cluster_size_bits < 9)
1632bef615eSHyunchul Lee 		return -EINVAL;
1642bef615eSHyunchul Lee 	sec_per_clu_bits = vol->cluster_size_bits - 9;
1652bef615eSHyunchul Lee 	vcn = start_sector >> sec_per_clu_bits;
1662bef615eSHyunchul Lee 	sec_off = start_sector & ((1ULL << sec_per_clu_bits) - 1);
1672bef615eSHyunchul Lee 
1682bef615eSHyunchul Lee 	nofs_flags = memalloc_nofs_save();
1692bef615eSHyunchul Lee 	down_read(&runlist->lock);
1702bef615eSHyunchul Lee 	if (!runlist->rl) {
1712bef615eSHyunchul Lee 		err = -EINVAL;
1722bef615eSHyunchul Lee 		goto out_unlock;
1732bef615eSHyunchul Lee 	}
1742bef615eSHyunchul Lee 
1752bef615eSHyunchul Lee 	rl = __ntfs_attr_find_vcn_nolock(runlist, vcn);
1762bef615eSHyunchul Lee 	if (IS_ERR(rl)) {
1772bef615eSHyunchul Lee 		err = PTR_ERR(rl);
1782bef615eSHyunchul Lee 		goto out_unlock;
1792bef615eSHyunchul Lee 	}
1802bef615eSHyunchul Lee 
1812bef615eSHyunchul Lee 	while (sector_count > 0) {
1822bef615eSHyunchul Lee 		s64 lcn;
1832bef615eSHyunchul Lee 		s64 rl_end;
1842bef615eSHyunchul Lee 		u64 byte_off, byte_len, sectors, available;
1852bef615eSHyunchul Lee 
1862bef615eSHyunchul Lee 		if (rl->length <= 0 || vcn < rl->vcn) {
1872bef615eSHyunchul Lee 			err = -EINVAL;
1882bef615eSHyunchul Lee 			goto out_unlock;
1892bef615eSHyunchul Lee 		}
1902bef615eSHyunchul Lee 
1912bef615eSHyunchul Lee 		lcn = ntfs_rl_vcn_to_lcn(rl, vcn);
1922bef615eSHyunchul Lee 		if (lcn < 0 && lcn != LCN_HOLE) {
1932bef615eSHyunchul Lee 			err = -EINVAL;
1942bef615eSHyunchul Lee 			goto out_unlock;
1952bef615eSHyunchul Lee 		}
1962bef615eSHyunchul Lee 
1972bef615eSHyunchul Lee 		if (check_add_overflow(rl->vcn, rl->length, &rl_end) ||
1982bef615eSHyunchul Lee 		    rl_end <= vcn ||
1992bef615eSHyunchul Lee 		    (u64)(rl_end - vcn) > (U64_MAX >> sec_per_clu_bits)) {
2002bef615eSHyunchul Lee 			err = -EOVERFLOW;
2012bef615eSHyunchul Lee 			goto out_unlock;
2022bef615eSHyunchul Lee 		}
2032bef615eSHyunchul Lee 		available = (u64)(rl_end - vcn) << sec_per_clu_bits;
2042bef615eSHyunchul Lee 		if (available <= sec_off) {
2052bef615eSHyunchul Lee 			err = -EINVAL;
2062bef615eSHyunchul Lee 			goto out_unlock;
2072bef615eSHyunchul Lee 		}
2082bef615eSHyunchul Lee 		available -= sec_off;
2092bef615eSHyunchul Lee 		sectors = min_t(u64, sector_count, available);
2102bef615eSHyunchul Lee 		if (check_mul_overflow(sectors, (u64)SECTOR_SIZE, &byte_len) ||
2112bef615eSHyunchul Lee 		    byte_len > SIZE_MAX - buf_off) {
2122bef615eSHyunchul Lee 			err = -EOVERFLOW;
2132bef615eSHyunchul Lee 			goto out_unlock;
2142bef615eSHyunchul Lee 		}
2152bef615eSHyunchul Lee 
2162bef615eSHyunchul Lee 		if (lcn == LCN_HOLE) {
2172bef615eSHyunchul Lee 			memset((u8 *)buf + buf_off, 0, byte_len);
2182bef615eSHyunchul Lee 		} else {
2192bef615eSHyunchul Lee 			byte_off = ntfs_cluster_to_bytes(vol, lcn);
2202bef615eSHyunchul Lee 			if (check_add_overflow(byte_off, sec_off << 9,
2212bef615eSHyunchul Lee 					       &byte_off) ||
2222bef615eSHyunchul Lee 			    byte_off > S64_MAX) {
2232bef615eSHyunchul Lee 				err = -EOVERFLOW;
2242bef615eSHyunchul Lee 				goto out_unlock;
2252bef615eSHyunchul Lee 			}
2262bef615eSHyunchul Lee 			err = ntfs_bdev_read(vol->sb->s_bdev,
2272bef615eSHyunchul Lee 					     (char *)buf + buf_off,
2282bef615eSHyunchul Lee 					     (loff_t)byte_off, byte_len);
2292bef615eSHyunchul Lee 			if (err)
2302bef615eSHyunchul Lee 				goto out_unlock;
2312bef615eSHyunchul Lee 		}
2322bef615eSHyunchul Lee 
2332bef615eSHyunchul Lee 		buf_off += byte_len;
2342bef615eSHyunchul Lee 		sector_count -= sectors;
2352bef615eSHyunchul Lee 		rl++;
2362bef615eSHyunchul Lee 		vcn = rl->vcn;
2372bef615eSHyunchul Lee 		sec_off = 0;
2382bef615eSHyunchul Lee 	}
2392bef615eSHyunchul Lee 
2402bef615eSHyunchul Lee 	err = 0;
2412bef615eSHyunchul Lee out_unlock:
2422bef615eSHyunchul Lee 	up_read(&runlist->lock);
2432bef615eSHyunchul Lee 	memalloc_nofs_restore(nofs_flags);
2442bef615eSHyunchul Lee 	return err;
2452bef615eSHyunchul Lee }
2462bef615eSHyunchul Lee 
2472bef615eSHyunchul Lee static int parse_wof_chunk_table(struct ntfs_inode *base_ni,
2482bef615eSHyunchul Lee 				 struct ntfs_inode *ni, u64 chunk_idx,
249a7b842f1SHyunchul Lee 				 u64 chunk_count, u32 decomp_size,
250a7b842f1SHyunchul Lee 				 u64 *chunk_offset, u32 *chunk_size,
251a7b842f1SHyunchul Lee 				 void *table_buf, size_t table_buf_size)
2522bef615eSHyunchul Lee {
2532bef615eSHyunchul Lee 	u8 bytes_per_off;
2542bef615eSHyunchul Lee 	u8 *buf;
2552bef615eSHyunchul Lee 	u64 off[2];
2562bef615eSHyunchul Lee 	u64 byte_off, chunk_data_size, table_size;
2572bef615eSHyunchul Lee 	u32 bytes_to_read;
2582bef615eSHyunchul Lee 	int ret = 0;
2592bef615eSHyunchul Lee 
2602bef615eSHyunchul Lee 	if (i_size_read(VFS_I(base_ni)) < (1ULL << 32))
2612bef615eSHyunchul Lee 		bytes_per_off = sizeof(__le32);
2622bef615eSHyunchul Lee 	else
2632bef615eSHyunchul Lee 		bytes_per_off = sizeof(__le64);
2642bef615eSHyunchul Lee 
2652bef615eSHyunchul Lee 	if (!chunk_count || chunk_idx >= chunk_count)
2662bef615eSHyunchul Lee 		return -EINVAL;
2672bef615eSHyunchul Lee 
2682bef615eSHyunchul Lee 	table_size = (chunk_count - 1) * bytes_per_off;
2692bef615eSHyunchul Lee 	if (ni->data_size < 0 || (u64)ni->data_size < table_size)
2702bef615eSHyunchul Lee 		return -EINVAL;
2712bef615eSHyunchul Lee 	chunk_data_size = (u64)ni->data_size - table_size;
2722bef615eSHyunchul Lee 
2732bef615eSHyunchul Lee 	if (chunk_count == 1) {
274a7b842f1SHyunchul Lee 		if (chunk_data_size > decomp_size)
2752bef615eSHyunchul Lee 			return -EINVAL;
2762bef615eSHyunchul Lee 		*chunk_offset = 0;
2772bef615eSHyunchul Lee 		*chunk_size = chunk_data_size;
278a7b842f1SHyunchul Lee 		goto out;
2792bef615eSHyunchul Lee 	}
2802bef615eSHyunchul Lee 
2812bef615eSHyunchul Lee 	byte_off = chunk_idx ? (chunk_idx - 1) * bytes_per_off : 0;
2822bef615eSHyunchul Lee 	bytes_to_read = chunk_idx + 1 == chunk_count ?
2832bef615eSHyunchul Lee 				bytes_per_off :
2842bef615eSHyunchul Lee 				(chunk_idx ? 2 : 1) * bytes_per_off;
2852bef615eSHyunchul Lee 
286a7b842f1SHyunchul Lee 	if (NInoNonResident(ni)) {
2872bef615eSHyunchul Lee 		sector_t start_sector = byte_off >> 9;
2882bef615eSHyunchul Lee 		u32 sector_off = byte_off & ((1 << 9) - 1);
2892bef615eSHyunchul Lee 		u32 sectors = DIV_ROUND_UP(sector_off + bytes_to_read, 512);
2902bef615eSHyunchul Lee 
2912bef615eSHyunchul Lee 		if ((size_t)sectors << 9 > table_buf_size)
2922bef615eSHyunchul Lee 			return -EINVAL;
2932bef615eSHyunchul Lee 		buf = table_buf;
2942bef615eSHyunchul Lee 		ret = ntfs_bdev_read_from_rl(ni->vol, &ni->runlist,
2952bef615eSHyunchul Lee 					     start_sector, sectors, buf);
296a7b842f1SHyunchul Lee 		if (ret)
297a7b842f1SHyunchul Lee 			return -EIO;
2982bef615eSHyunchul Lee 		buf += sector_off;
299a7b842f1SHyunchul Lee 	} else {
300a7b842f1SHyunchul Lee 		struct ntfs_attr_search_ctx *ctx;
301a7b842f1SHyunchul Lee 		u32 value_length;
302a7b842f1SHyunchul Lee 		u16 value_offset;
303a7b842f1SHyunchul Lee 
304a7b842f1SHyunchul Lee 		if (bytes_to_read > table_buf_size)
305a7b842f1SHyunchul Lee 			return -EINVAL;
306a7b842f1SHyunchul Lee 
307a7b842f1SHyunchul Lee 		mutex_lock(&base_ni->mrec_lock);
308a7b842f1SHyunchul Lee 		ctx = ntfs_attr_get_search_ctx(base_ni, NULL);
309a7b842f1SHyunchul Lee 		if (!ctx) {
310a7b842f1SHyunchul Lee 			ret = -ENOMEM;
311a7b842f1SHyunchul Lee 			goto out_unlock_mrec;
312a7b842f1SHyunchul Lee 		}
313a7b842f1SHyunchul Lee 		ret = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
314a7b842f1SHyunchul Lee 				       CASE_SENSITIVE, 0, NULL, 0, ctx);
315a7b842f1SHyunchul Lee 		if (ret)
316a7b842f1SHyunchul Lee 			goto out_put_ctx;
317a7b842f1SHyunchul Lee 
318a7b842f1SHyunchul Lee 		value_length =
319a7b842f1SHyunchul Lee 			le32_to_cpu(ctx->attr->data.resident.value_length);
320a7b842f1SHyunchul Lee 		value_offset =
321a7b842f1SHyunchul Lee 			le16_to_cpu(ctx->attr->data.resident.value_offset);
322a7b842f1SHyunchul Lee 		if (byte_off + bytes_to_read > value_length) {
323a7b842f1SHyunchul Lee 			ret = -EINVAL;
324a7b842f1SHyunchul Lee 			goto out_put_ctx;
325a7b842f1SHyunchul Lee 		}
326a7b842f1SHyunchul Lee 		memcpy(table_buf, (u8 *)ctx->attr + value_offset + byte_off,
327a7b842f1SHyunchul Lee 		       bytes_to_read);
328a7b842f1SHyunchul Lee 		buf = table_buf;
329a7b842f1SHyunchul Lee out_put_ctx:
330a7b842f1SHyunchul Lee 		ntfs_attr_put_search_ctx(ctx);
331a7b842f1SHyunchul Lee out_unlock_mrec:
332a7b842f1SHyunchul Lee 		mutex_unlock(&base_ni->mrec_lock);
333a7b842f1SHyunchul Lee 		if (ret)
334a7b842f1SHyunchul Lee 			return ret;
3352bef615eSHyunchul Lee 	}
3362bef615eSHyunchul Lee 
3372bef615eSHyunchul Lee 	if (bytes_per_off == sizeof(__le32)) {
3382bef615eSHyunchul Lee 		off[0] = chunk_idx ? get_unaligned_le32(buf) : 0;
3392bef615eSHyunchul Lee 		if (chunk_idx + 1 == chunk_count)
3402bef615eSHyunchul Lee 			off[1] = chunk_data_size;
3412bef615eSHyunchul Lee 		else if (chunk_idx)
3422bef615eSHyunchul Lee 			off[1] = get_unaligned_le32(buf + bytes_per_off);
3432bef615eSHyunchul Lee 		else
3442bef615eSHyunchul Lee 			off[1] = get_unaligned_le32(buf);
3452bef615eSHyunchul Lee 	} else {
3462bef615eSHyunchul Lee 		off[0] = chunk_idx ? get_unaligned_le64(buf) : 0;
3472bef615eSHyunchul Lee 		if (chunk_idx + 1 == chunk_count)
3482bef615eSHyunchul Lee 			off[1] = chunk_data_size;
3492bef615eSHyunchul Lee 		else if (chunk_idx)
3502bef615eSHyunchul Lee 			off[1] = get_unaligned_le64(buf + bytes_per_off);
3512bef615eSHyunchul Lee 		else
3522bef615eSHyunchul Lee 			off[1] = get_unaligned_le64(buf);
3532bef615eSHyunchul Lee 	}
3542bef615eSHyunchul Lee 
3552bef615eSHyunchul Lee 	if (off[1] <= off[0] || off[1] > chunk_data_size ||
356a7b842f1SHyunchul Lee 	    off[1] - off[0] > decomp_size)
357a7b842f1SHyunchul Lee 		return -EINVAL;
3582bef615eSHyunchul Lee 
3592bef615eSHyunchul Lee 	*chunk_offset = table_size + off[0];
3602bef615eSHyunchul Lee 	*chunk_size = off[1] - off[0];
361a7b842f1SHyunchul Lee out:
362a7b842f1SHyunchul Lee 	if (!*chunk_size)
363a7b842f1SHyunchul Lee 		return -EINVAL;
364a7b842f1SHyunchul Lee 	return 0;
3652bef615eSHyunchul Lee }
3662bef615eSHyunchul Lee 
3672bef615eSHyunchul Lee static int ntfs_read_wof_chunk(struct ntfs_volume *vol,
3682bef615eSHyunchul Lee 			       struct ntfs_inode *wof_ni, u64 chunk_offset,
3692bef615eSHyunchul Lee 			       u32 chunk_size, void *input, size_t input_size,
3702bef615eSHyunchul Lee 			       char **chunk_mem)
3712bef615eSHyunchul Lee {
372a7b842f1SHyunchul Lee 	struct ntfs_inode *base_ni = wof_ni->ext.base_ntfs_ino;
373a7b842f1SHyunchul Lee 	struct ntfs_attr_search_ctx *ctx;
3742bef615eSHyunchul Lee 	u32 input_offset = chunk_offset & 511;
3752bef615eSHyunchul Lee 	u32 input_size_aligned;
376a7b842f1SHyunchul Lee 	u32 value_length;
377a7b842f1SHyunchul Lee 	u16 value_offset;
3782bef615eSHyunchul Lee 	int err;
3792bef615eSHyunchul Lee 
3802bef615eSHyunchul Lee 	input_size_aligned = round_up(chunk_size + input_offset, 512);
3812bef615eSHyunchul Lee 	if (input_size_aligned > input_size)
3822bef615eSHyunchul Lee 		return -EINVAL;
3832bef615eSHyunchul Lee 
384a7b842f1SHyunchul Lee 	if (NInoNonResident(wof_ni)) {
385a7b842f1SHyunchul Lee 		err = ntfs_bdev_read_from_rl(vol, &wof_ni->runlist,
386a7b842f1SHyunchul Lee 					     chunk_offset >> 9,
3872bef615eSHyunchul Lee 					     input_size_aligned >> 9, input);
3882bef615eSHyunchul Lee 		if (err)
3892bef615eSHyunchul Lee 			return err;
3902bef615eSHyunchul Lee 		*chunk_mem = (u8 *)input + input_offset;
3912bef615eSHyunchul Lee 		return 0;
3922bef615eSHyunchul Lee 	}
3932bef615eSHyunchul Lee 
394a7b842f1SHyunchul Lee 	mutex_lock(&base_ni->mrec_lock);
395a7b842f1SHyunchul Lee 	ctx = ntfs_attr_get_search_ctx(base_ni, NULL);
396a7b842f1SHyunchul Lee 	if (!ctx) {
397a7b842f1SHyunchul Lee 		err = -ENOMEM;
398a7b842f1SHyunchul Lee 		goto out_unlock_mrec;
399a7b842f1SHyunchul Lee 	}
400a7b842f1SHyunchul Lee 
401a7b842f1SHyunchul Lee 	err = ntfs_attr_lookup(wof_ni->type, wof_ni->name, wof_ni->name_len,
402a7b842f1SHyunchul Lee 			       CASE_SENSITIVE, 0, NULL, 0, ctx);
403a7b842f1SHyunchul Lee 	if (err)
404a7b842f1SHyunchul Lee 		goto out_put_ctx;
405a7b842f1SHyunchul Lee 
406a7b842f1SHyunchul Lee 	value_length = le32_to_cpu(ctx->attr->data.resident.value_length);
407a7b842f1SHyunchul Lee 	value_offset = le16_to_cpu(ctx->attr->data.resident.value_offset);
408a7b842f1SHyunchul Lee 	if (chunk_offset + chunk_size > value_length) {
409a7b842f1SHyunchul Lee 		err = -EINVAL;
410a7b842f1SHyunchul Lee 		goto out_put_ctx;
411a7b842f1SHyunchul Lee 	}
412a7b842f1SHyunchul Lee 	memcpy(input, (u8 *)ctx->attr + value_offset + chunk_offset,
413a7b842f1SHyunchul Lee 	       chunk_size);
414a7b842f1SHyunchul Lee 	*chunk_mem = input;
415a7b842f1SHyunchul Lee out_put_ctx:
416a7b842f1SHyunchul Lee 	ntfs_attr_put_search_ctx(ctx);
417a7b842f1SHyunchul Lee out_unlock_mrec:
418a7b842f1SHyunchul Lee 	mutex_unlock(&base_ni->mrec_lock);
419a7b842f1SHyunchul Lee 	return err;
420a7b842f1SHyunchul Lee }
421a7b842f1SHyunchul Lee 
4222bef615eSHyunchul Lee struct ntfs_wof_dest {
4232bef615eSHyunchul Lee 	struct folio *folios[NTFS_WOF_MAX_PAGES];
4242bef615eSHyunchul Lee 	struct page *pages[NTFS_WOF_MAX_PAGES];
4252bef615eSHyunchul Lee 	unsigned int nr_folios;
4262bef615eSHyunchul Lee 	unsigned int nr_pages;
4272bef615eSHyunchul Lee };
4282bef615eSHyunchul Lee 
4292bef615eSHyunchul Lee static void ntfs_wof_release_dest(struct ntfs_wof_dest *dest,
4302bef615eSHyunchul Lee 				  struct folio *target, bool success)
4312bef615eSHyunchul Lee {
4322bef615eSHyunchul Lee 	unsigned int i;
4332bef615eSHyunchul Lee 
4342bef615eSHyunchul Lee 	for (i = 0; i < dest->nr_folios; i++) {
4352bef615eSHyunchul Lee 		struct folio *folio = dest->folios[i];
4362bef615eSHyunchul Lee 
4372bef615eSHyunchul Lee 		if (folio == target)
4382bef615eSHyunchul Lee 			continue;
4392bef615eSHyunchul Lee 		if (success) {
4402bef615eSHyunchul Lee 			flush_dcache_folio(folio);
4412bef615eSHyunchul Lee 			folio_mark_uptodate(folio);
4422bef615eSHyunchul Lee 		} else {
4432bef615eSHyunchul Lee 			folio_clear_uptodate(folio);
4442bef615eSHyunchul Lee 		}
4452bef615eSHyunchul Lee 		folio_unlock(folio);
4462bef615eSHyunchul Lee 		folio_put(folio);
4472bef615eSHyunchul Lee 	}
4482bef615eSHyunchul Lee }
4492bef615eSHyunchul Lee 
4502bef615eSHyunchul Lee static int ntfs_wof_collect_dest(struct address_space *mapping,
4512bef615eSHyunchul Lee 				 struct folio *target, loff_t chunk_start,
4522bef615eSHyunchul Lee 				 loff_t chunk_end, struct ntfs_wof_dest *dest)
4532bef615eSHyunchul Lee {
4542bef615eSHyunchul Lee 	pgoff_t index, last, page_index;
4552bef615eSHyunchul Lee 	unsigned int i;
4562bef615eSHyunchul Lee 
4572bef615eSHyunchul Lee 	memset(dest, 0, sizeof(*dest));
4582bef615eSHyunchul Lee 	index = chunk_start >> PAGE_SHIFT;
4592bef615eSHyunchul Lee 	last = (chunk_end - 1) >> PAGE_SHIFT;
4602bef615eSHyunchul Lee 	while (index <= last) {
4612bef615eSHyunchul Lee 		struct folio *folio;
4622bef615eSHyunchul Lee 		pgoff_t next;
4632bef615eSHyunchul Lee 		bool is_target;
4642bef615eSHyunchul Lee 
4652bef615eSHyunchul Lee 		if (folio_contains(target, index)) {
4662bef615eSHyunchul Lee 			folio = target;
4672bef615eSHyunchul Lee 			is_target = true;
4682bef615eSHyunchul Lee 		} else {
4692bef615eSHyunchul Lee 			folio = __filemap_get_folio(
4702bef615eSHyunchul Lee 				mapping, index,
4712bef615eSHyunchul Lee 				FGP_LOCK | FGP_CREAT | FGP_NOFS | FGP_NOWAIT,
4722bef615eSHyunchul Lee 				GFP_NOFS);
4732bef615eSHyunchul Lee 			if (IS_ERR(folio))
4742bef615eSHyunchul Lee 				return PTR_ERR(folio);
4752bef615eSHyunchul Lee 			is_target = false;
4762bef615eSHyunchul Lee 			if (folio_pos(folio) < chunk_start ||
4772bef615eSHyunchul Lee 			    folio_next_pos(folio) > chunk_end) {
4782bef615eSHyunchul Lee 				folio_unlock(folio);
4792bef615eSHyunchul Lee 				folio_put(folio);
4802bef615eSHyunchul Lee 				return -EAGAIN;
4812bef615eSHyunchul Lee 			}
4822bef615eSHyunchul Lee 		}
4832bef615eSHyunchul Lee 
4842bef615eSHyunchul Lee 		if (dest->nr_folios == ARRAY_SIZE(dest->folios)) {
4852bef615eSHyunchul Lee 			if (!is_target) {
4862bef615eSHyunchul Lee 				folio_unlock(folio);
4872bef615eSHyunchul Lee 				folio_put(folio);
4882bef615eSHyunchul Lee 			}
4892bef615eSHyunchul Lee 			return -EINVAL;
4902bef615eSHyunchul Lee 		}
4912bef615eSHyunchul Lee 		dest->folios[dest->nr_folios++] = folio;
4922bef615eSHyunchul Lee 		next = folio->index + folio_nr_pages(folio);
4932bef615eSHyunchul Lee 		if (next <= index)
4942bef615eSHyunchul Lee 			return -EAGAIN;
4952bef615eSHyunchul Lee 		index = next;
4962bef615eSHyunchul Lee 	}
4972bef615eSHyunchul Lee 
4982bef615eSHyunchul Lee 	for (page_index = chunk_start >> PAGE_SHIFT; page_index <= last;
4992bef615eSHyunchul Lee 	     page_index++) {
5002bef615eSHyunchul Lee 		struct folio *folio = NULL;
5012bef615eSHyunchul Lee 
5022bef615eSHyunchul Lee 		for (i = 0; i < dest->nr_folios; i++) {
5032bef615eSHyunchul Lee 			if (folio_contains(dest->folios[i], page_index)) {
5042bef615eSHyunchul Lee 				folio = dest->folios[i];
5052bef615eSHyunchul Lee 				break;
5062bef615eSHyunchul Lee 			}
5072bef615eSHyunchul Lee 		}
5082bef615eSHyunchul Lee 		if (!folio || dest->nr_pages == ARRAY_SIZE(dest->pages))
5092bef615eSHyunchul Lee 			return -EAGAIN;
5102bef615eSHyunchul Lee 		dest->pages[dest->nr_pages++] =
5112bef615eSHyunchul Lee 			folio_page(folio, page_index - folio->index);
5122bef615eSHyunchul Lee 	}
5132bef615eSHyunchul Lee 	return 0;
5142bef615eSHyunchul Lee }
5152bef615eSHyunchul Lee 
5162bef615eSHyunchul Lee static int ntfs_wof_decode(struct ntfs_wof_workspace *ws, const void *src,
5172bef615eSHyunchul Lee 			   u32 src_len, void *dst, u32 dst_len)
5182bef615eSHyunchul Lee {
5192bef615eSHyunchul Lee 	if (src_len == dst_len) {
5202bef615eSHyunchul Lee 		memcpy(dst, src, dst_len);
5212bef615eSHyunchul Lee 		return 0;
5222bef615eSHyunchul Lee 	}
5232bef615eSHyunchul Lee 	return ws->codec->decompress_chunk(ws->scratch, src, src_len, dst,
5242bef615eSHyunchul Lee 					   dst_len, ws->comp_unit);
5252bef615eSHyunchul Lee }
5262bef615eSHyunchul Lee 
5272bef615eSHyunchul Lee static int ntfs_wof_decode_page_direct(struct ntfs_wof_workspace *ws,
5282bef615eSHyunchul Lee 				       struct folio *target, loff_t chunk_start,
5292bef615eSHyunchul Lee 				       const void *src, u32 src_len,
5302bef615eSHyunchul Lee 				       u32 dst_len)
5312bef615eSHyunchul Lee {
5322bef615eSHyunchul Lee 	unsigned int page_offset = offset_in_page(chunk_start);
5332bef615eSHyunchul Lee 	struct page *page;
5342bef615eSHyunchul Lee 	pgoff_t page_index;
5352bef615eSHyunchul Lee 	void *addr;
5362bef615eSHyunchul Lee 	int err;
5372bef615eSHyunchul Lee 
5382bef615eSHyunchul Lee 	page_index = chunk_start >> PAGE_SHIFT;
5392bef615eSHyunchul Lee 	if (!folio_contains(target, page_index))
5402bef615eSHyunchul Lee 		return -EAGAIN;
5412bef615eSHyunchul Lee 
5422bef615eSHyunchul Lee 	page = folio_page(target, page_index - target->index);
5432bef615eSHyunchul Lee 	addr = kmap_local_page(page);
5442bef615eSHyunchul Lee 	err = ntfs_wof_decode(ws, src, src_len, (u8 *)addr + page_offset,
5452bef615eSHyunchul Lee 			      dst_len);
5462bef615eSHyunchul Lee 	kunmap_local(addr);
5472bef615eSHyunchul Lee 	if (err)
5482bef615eSHyunchul Lee 		return -EINVAL;
5492bef615eSHyunchul Lee 	return 0;
5502bef615eSHyunchul Lee }
5512bef615eSHyunchul Lee 
5522bef615eSHyunchul Lee static int ntfs_wof_decode_folios_direct(struct ntfs_wof_workspace *ws,
5532bef615eSHyunchul Lee 					 struct address_space *mapping,
5542bef615eSHyunchul Lee 					 struct folio *target,
5552bef615eSHyunchul Lee 					 loff_t chunk_start, loff_t chunk_end,
5562bef615eSHyunchul Lee 					 const void *src, u32 src_len,
5572bef615eSHyunchul Lee 					 u32 dst_len)
5582bef615eSHyunchul Lee {
5592bef615eSHyunchul Lee 	unsigned int page_offset = offset_in_page(chunk_start);
5602bef615eSHyunchul Lee 	struct ntfs_wof_dest dest;
5612bef615eSHyunchul Lee 	void *addr;
5622bef615eSHyunchul Lee 	unsigned int nofs_flags;
5632bef615eSHyunchul Lee 	int err;
5642bef615eSHyunchul Lee 
5652bef615eSHyunchul Lee 	err = ntfs_wof_collect_dest(mapping, target, chunk_start, chunk_end,
5662bef615eSHyunchul Lee 				    &dest);
5672bef615eSHyunchul Lee 	if (err) {
5682bef615eSHyunchul Lee 		ntfs_wof_release_dest(&dest, target, false);
5692bef615eSHyunchul Lee 		return -EAGAIN;
5702bef615eSHyunchul Lee 	}
5712bef615eSHyunchul Lee 
5722bef615eSHyunchul Lee 	nofs_flags = memalloc_nofs_save();
5732bef615eSHyunchul Lee 	addr = vmap(dest.pages, dest.nr_pages, VM_MAP, PAGE_KERNEL);
5742bef615eSHyunchul Lee 	memalloc_nofs_restore(nofs_flags);
5752bef615eSHyunchul Lee 	if (!addr) {
5762bef615eSHyunchul Lee 		ntfs_wof_release_dest(&dest, target, false);
5772bef615eSHyunchul Lee 		return -EAGAIN;
5782bef615eSHyunchul Lee 	}
5792bef615eSHyunchul Lee 
5802bef615eSHyunchul Lee 	err = ntfs_wof_decode(ws, src, src_len, (u8 *)addr + page_offset,
5812bef615eSHyunchul Lee 			      dst_len);
5822bef615eSHyunchul Lee 	vunmap(addr);
5832bef615eSHyunchul Lee 	if (err) {
5842bef615eSHyunchul Lee 		ntfs_wof_release_dest(&dest, target, false);
5852bef615eSHyunchul Lee 		return -EINVAL;
5862bef615eSHyunchul Lee 	}
5872bef615eSHyunchul Lee 	ntfs_wof_release_dest(&dest, target, true);
5882bef615eSHyunchul Lee 	return 0;
5892bef615eSHyunchul Lee }
5902bef615eSHyunchul Lee 
5912bef615eSHyunchul Lee static int ntfs_wof_try_direct(struct ntfs_wof_workspace *ws,
5922bef615eSHyunchul Lee 			       struct address_space *mapping,
5932bef615eSHyunchul Lee 			       struct folio *target, loff_t chunk_start,
5942bef615eSHyunchul Lee 			       loff_t chunk_end, const void *src, u32 src_len,
5952bef615eSHyunchul Lee 			       u32 dst_len)
5962bef615eSHyunchul Lee {
5972bef615eSHyunchul Lee 	unsigned int page_offset = offset_in_page(chunk_start);
5982bef615eSHyunchul Lee 
5992bef615eSHyunchul Lee 	if (dst_len <= PAGE_SIZE - page_offset)
6002bef615eSHyunchul Lee 		return ntfs_wof_decode_page_direct(ws, target, chunk_start, src,
6012bef615eSHyunchul Lee 						   src_len, dst_len);
6022bef615eSHyunchul Lee 
6032bef615eSHyunchul Lee 	return ntfs_wof_decode_folios_direct(ws, mapping, target, chunk_start,
6042bef615eSHyunchul Lee 					     chunk_end, src, src_len, dst_len);
6052bef615eSHyunchul Lee }
6062bef615eSHyunchul Lee 
607*41a52ba4SZhan Xusheng /*
608*41a52ba4SZhan Xusheng  * Decompress one chunk into @folio.  Only this step needs the workspace, so it
609*41a52ba4SZhan Xusheng  * is the only step that takes the workspace lock.
610*41a52ba4SZhan Xusheng  */
611*41a52ba4SZhan Xusheng static int ntfs_wof_decompress_chunk(struct ntfs_wof_workspace *ws,
612*41a52ba4SZhan Xusheng 				     struct ntfs_volume *vol,
613*41a52ba4SZhan Xusheng 				     struct address_space *mapping,
614*41a52ba4SZhan Xusheng 				     struct folio *folio, loff_t folio_start,
615*41a52ba4SZhan Xusheng 				     loff_t folio_end, u64 chunk_file_offset,
616*41a52ba4SZhan Xusheng 				     char *chunk_mem, u32 chunk_size,
617*41a52ba4SZhan Xusheng 				     u32 decomp_size)
618*41a52ba4SZhan Xusheng {
619*41a52ba4SZhan Xusheng 	loff_t chunk_end = chunk_file_offset + decomp_size;
620*41a52ba4SZhan Xusheng 	loff_t copy_start, copy_end;
621*41a52ba4SZhan Xusheng 	int err;
622*41a52ba4SZhan Xusheng 
623*41a52ba4SZhan Xusheng 	mutex_lock(ws->lock);
624*41a52ba4SZhan Xusheng 	err = ntfs_wof_workspace_prepare(ws);
625*41a52ba4SZhan Xusheng 	if (err)
626*41a52ba4SZhan Xusheng 		goto out_unlock;
627*41a52ba4SZhan Xusheng 
628*41a52ba4SZhan Xusheng 	err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset,
629*41a52ba4SZhan Xusheng 				  chunk_end, chunk_mem, chunk_size,
630*41a52ba4SZhan Xusheng 				  decomp_size);
631*41a52ba4SZhan Xusheng 	if (err != -EAGAIN)
632*41a52ba4SZhan Xusheng 		goto out_unlock;
633*41a52ba4SZhan Xusheng 
634*41a52ba4SZhan Xusheng 	err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output,
635*41a52ba4SZhan Xusheng 			      decomp_size);
636*41a52ba4SZhan Xusheng 	if (err) {
637*41a52ba4SZhan Xusheng 		ntfs_error(vol->sb, "Decompression failed: %d", err);
638*41a52ba4SZhan Xusheng 		err = -EINVAL;
639*41a52ba4SZhan Xusheng 		goto out_unlock;
640*41a52ba4SZhan Xusheng 	}
641*41a52ba4SZhan Xusheng 
642*41a52ba4SZhan Xusheng 	copy_start = max_t(loff_t, folio_start, chunk_file_offset);
643*41a52ba4SZhan Xusheng 	copy_end = min_t(loff_t, folio_end, chunk_file_offset + decomp_size);
644*41a52ba4SZhan Xusheng 	memcpy_to_folio(folio, copy_start - folio_start,
645*41a52ba4SZhan Xusheng 			ws->output + copy_start - chunk_file_offset,
646*41a52ba4SZhan Xusheng 			copy_end - copy_start);
647*41a52ba4SZhan Xusheng out_unlock:
648*41a52ba4SZhan Xusheng 	mutex_unlock(ws->lock);
649*41a52ba4SZhan Xusheng 	return err;
650*41a52ba4SZhan Xusheng }
651*41a52ba4SZhan Xusheng 
6522bef615eSHyunchul Lee int ntfs_read_wof_compressed_block(struct folio *folio)
6532bef615eSHyunchul Lee {
6542bef615eSHyunchul Lee 	struct address_space *mapping = folio->mapping;
6552bef615eSHyunchul Lee 	struct ntfs_inode *ni = NTFS_I(mapping->host), *wof_ni;
6562bef615eSHyunchul Lee 	struct inode *wof_inode;
6572bef615eSHyunchul Lee 	struct ntfs_volume *vol = ni->vol;
6582bef615eSHyunchul Lee 	struct ntfs_wof_workspace *ws;
6592bef615eSHyunchul Lee 	loff_t i_size = i_size_read(VFS_I(ni));
6602bef615eSHyunchul Lee 	loff_t folio_start = folio_pos(folio);
6612bef615eSHyunchul Lee 	loff_t folio_end = folio_next_pos(folio);
6622bef615eSHyunchul Lee 	char *chunk_mem;
663*41a52ba4SZhan Xusheng 	void *input;
664*41a52ba4SZhan Xusheng 	size_t input_size;
6652bef615eSHyunchul Lee 	u32 decomp_size;
6662bef615eSHyunchul Lee 	u64 chunk_count, chunk_idx, last_chunk, chunk_offset;
6672bef615eSHyunchul Lee 	int err = 0;
6682bef615eSHyunchul Lee 
6692bef615eSHyunchul Lee 	ws = ntfs_wof_workspace(ni->itype.compressed.block_size_bits);
6702bef615eSHyunchul Lee 	if (!ws) {
6712bef615eSHyunchul Lee 		err = -EOPNOTSUPP;
6722bef615eSHyunchul Lee 		goto out;
6732bef615eSHyunchul Lee 	}
6742bef615eSHyunchul Lee 
6752bef615eSHyunchul Lee 	if (folio_start >= i_size) {
6762bef615eSHyunchul Lee 		folio_zero_segment(folio, 0, folio_size(folio));
6772bef615eSHyunchul Lee 		goto out;
6782bef615eSHyunchul Lee 	}
6792bef615eSHyunchul Lee 
6802bef615eSHyunchul Lee 	wof_inode = ntfs_attr_iget(VFS_I(ni), AT_DATA, (__le16 *)WOF_NAME,
6812bef615eSHyunchul Lee 				   WOF_NAME_LEN);
6822bef615eSHyunchul Lee 	if (IS_ERR(wof_inode)) {
6832bef615eSHyunchul Lee 		err = PTR_ERR(wof_inode);
6842bef615eSHyunchul Lee 		goto out;
6852bef615eSHyunchul Lee 	}
6862bef615eSHyunchul Lee 
6872bef615eSHyunchul Lee 	wof_ni = NTFS_I(wof_inode);
6882bef615eSHyunchul Lee 	if (wof_ni->initialized_size != wof_ni->data_size) {
6892bef615eSHyunchul Lee 		ntfs_error(vol->sb,
6902bef615eSHyunchul Lee 			   "WOF compressed stream is not fully initialized (init %lld, data %lld).",
6912bef615eSHyunchul Lee 			   wof_ni->initialized_size, wof_ni->data_size);
6922bef615eSHyunchul Lee 		err = -EIO;
6932bef615eSHyunchul Lee 		goto out_iput;
6942bef615eSHyunchul Lee 	}
695a7b842f1SHyunchul Lee 	if (NInoNonResident(wof_ni) && !NInoFullyMapped(wof_ni)) {
6962bef615eSHyunchul Lee 		down_write(&wof_ni->runlist.lock);
6972bef615eSHyunchul Lee 		if (!NInoFullyMapped(wof_ni))
6982bef615eSHyunchul Lee 			err = ntfs_attr_map_whole_runlist(wof_ni);
6992bef615eSHyunchul Lee 		up_write(&wof_ni->runlist.lock);
7002bef615eSHyunchul Lee 		if (err)
7012bef615eSHyunchul Lee 			goto out_iput;
7022bef615eSHyunchul Lee 	}
7032bef615eSHyunchul Lee 
704*41a52ba4SZhan Xusheng 	input_size = ntfs_wof_input_size(ws);
705*41a52ba4SZhan Xusheng 	input = kvmalloc(input_size, GFP_NOFS);
706*41a52ba4SZhan Xusheng 	if (!input) {
707*41a52ba4SZhan Xusheng 		err = -ENOMEM;
708*41a52ba4SZhan Xusheng 		goto out_iput;
709*41a52ba4SZhan Xusheng 	}
7102bef615eSHyunchul Lee 
7112bef615eSHyunchul Lee 	chunk_idx = div_u64(folio_start, ws->comp_unit);
7122bef615eSHyunchul Lee 	last_chunk =
7132bef615eSHyunchul Lee 		div_u64(min_t(loff_t, folio_end, i_size) - 1, ws->comp_unit);
7142bef615eSHyunchul Lee 	chunk_count = DIV_ROUND_UP_ULL(i_size, ws->comp_unit);
7152bef615eSHyunchul Lee 	for (; chunk_idx <= last_chunk; chunk_idx++) {
7162bef615eSHyunchul Lee 		u32 chunk_size;
7172bef615eSHyunchul Lee 
7182bef615eSHyunchul Lee 		decomp_size = chunk_idx + 1 == chunk_count ?
7192bef615eSHyunchul Lee 				      i_size - chunk_idx * ws->comp_unit :
7202bef615eSHyunchul Lee 				      ws->comp_unit;
721a7b842f1SHyunchul Lee 		err = parse_wof_chunk_table(ni, wof_ni, chunk_idx, chunk_count,
722a7b842f1SHyunchul Lee 					    decomp_size, &chunk_offset,
723*41a52ba4SZhan Xusheng 					    &chunk_size, input, input_size);
724a7b842f1SHyunchul Lee 		if (err)
725*41a52ba4SZhan Xusheng 			goto out_free_input;
7262bef615eSHyunchul Lee 
7272bef615eSHyunchul Lee 		err = ntfs_read_wof_chunk(vol, wof_ni, chunk_offset, chunk_size,
728*41a52ba4SZhan Xusheng 					  input, input_size, &chunk_mem);
7292bef615eSHyunchul Lee 		if (err)
730*41a52ba4SZhan Xusheng 			goto out_free_input;
7312bef615eSHyunchul Lee 
732*41a52ba4SZhan Xusheng 		err = ntfs_wof_decompress_chunk(ws, vol, mapping, folio,
733*41a52ba4SZhan Xusheng 						folio_start, folio_end,
734*41a52ba4SZhan Xusheng 						chunk_idx * ws->comp_unit,
735*41a52ba4SZhan Xusheng 						chunk_mem, chunk_size,
7362bef615eSHyunchul Lee 						decomp_size);
737*41a52ba4SZhan Xusheng 		if (err)
738*41a52ba4SZhan Xusheng 			goto out_free_input;
7392bef615eSHyunchul Lee 	}
7402bef615eSHyunchul Lee 
7412bef615eSHyunchul Lee 	if (folio_end > i_size)
7422bef615eSHyunchul Lee 		folio_zero_segment(folio, i_size - folio_start,
7432bef615eSHyunchul Lee 				   folio_size(folio));
744*41a52ba4SZhan Xusheng out_free_input:
745*41a52ba4SZhan Xusheng 	kvfree(input);
7462bef615eSHyunchul Lee out_iput:
7472bef615eSHyunchul Lee 	iput(wof_inode);
7482bef615eSHyunchul Lee out:
7492bef615eSHyunchul Lee 	if (!err) {
7502bef615eSHyunchul Lee 		flush_dcache_folio(folio);
7512bef615eSHyunchul Lee 		folio_mark_uptodate(folio);
7522bef615eSHyunchul Lee 	} else {
7532bef615eSHyunchul Lee 		folio_clear_uptodate(folio);
7542bef615eSHyunchul Lee 	}
7552bef615eSHyunchul Lee 	folio_unlock(folio);
7562bef615eSHyunchul Lee 	return err;
7572bef615eSHyunchul Lee }
758