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