18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2001-2012 Anton Altaparmakov and Tuxera Inc. 68c2ecf20Sopenharmony_ci * Copyright (c) 2002 Richard Russon 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/swap.h> 138c2ecf20Sopenharmony_ci#include <linux/writeback.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "attrib.h" 168c2ecf20Sopenharmony_ci#include "debug.h" 178c2ecf20Sopenharmony_ci#include "layout.h" 188c2ecf20Sopenharmony_ci#include "lcnalloc.h" 198c2ecf20Sopenharmony_ci#include "malloc.h" 208c2ecf20Sopenharmony_ci#include "mft.h" 218c2ecf20Sopenharmony_ci#include "ntfs.h" 228c2ecf20Sopenharmony_ci#include "types.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/** 258c2ecf20Sopenharmony_ci * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode 268c2ecf20Sopenharmony_ci * @ni: ntfs inode for which to map (part of) a runlist 278c2ecf20Sopenharmony_ci * @vcn: map runlist part containing this vcn 288c2ecf20Sopenharmony_ci * @ctx: active attribute search context if present or NULL if not 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Map the part of a runlist containing the @vcn of the ntfs inode @ni. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * If @ctx is specified, it is an active search context of @ni and its base mft 338c2ecf20Sopenharmony_ci * record. This is needed when ntfs_map_runlist_nolock() encounters unmapped 348c2ecf20Sopenharmony_ci * runlist fragments and allows their mapping. If you do not have the mft 358c2ecf20Sopenharmony_ci * record mapped, you can specify @ctx as NULL and ntfs_map_runlist_nolock() 368c2ecf20Sopenharmony_ci * will perform the necessary mapping and unmapping. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Note, ntfs_map_runlist_nolock() saves the state of @ctx on entry and 398c2ecf20Sopenharmony_ci * restores it before returning. Thus, @ctx will be left pointing to the same 408c2ecf20Sopenharmony_ci * attribute on return as on entry. However, the actual pointers in @ctx may 418c2ecf20Sopenharmony_ci * point to different memory locations on return, so you must remember to reset 428c2ecf20Sopenharmony_ci * any cached pointers from the @ctx, i.e. after the call to 438c2ecf20Sopenharmony_ci * ntfs_map_runlist_nolock(), you will probably want to do: 448c2ecf20Sopenharmony_ci * m = ctx->mrec; 458c2ecf20Sopenharmony_ci * a = ctx->attr; 468c2ecf20Sopenharmony_ci * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that 478c2ecf20Sopenharmony_ci * you cache ctx->mrec in a variable @m of type MFT_RECORD *. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. There is one special error code 508c2ecf20Sopenharmony_ci * which is not an error as such. This is -ENOENT. It means that @vcn is out 518c2ecf20Sopenharmony_ci * of bounds of the runlist. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Note the runlist can be NULL after this function returns if @vcn is zero and 548c2ecf20Sopenharmony_ci * the attribute has zero allocated size, i.e. there simply is no runlist. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * WARNING: If @ctx is supplied, regardless of whether success or failure is 578c2ecf20Sopenharmony_ci * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx 588c2ecf20Sopenharmony_ci * is no longer valid, i.e. you need to either call 598c2ecf20Sopenharmony_ci * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it. 608c2ecf20Sopenharmony_ci * In that case PTR_ERR(@ctx->mrec) will give you the error code for 618c2ecf20Sopenharmony_ci * why the mapping of the old inode failed. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Locking: - The runlist described by @ni must be locked for writing on entry 648c2ecf20Sopenharmony_ci * and is locked on return. Note the runlist will be modified. 658c2ecf20Sopenharmony_ci * - If @ctx is NULL, the base mft record of @ni must not be mapped on 668c2ecf20Sopenharmony_ci * entry and it will be left unmapped on return. 678c2ecf20Sopenharmony_ci * - If @ctx is not NULL, the base mft record must be mapped on entry 688c2ecf20Sopenharmony_ci * and it will be left mapped on return. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ciint ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, ntfs_attr_search_ctx *ctx) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci VCN end_vcn; 738c2ecf20Sopenharmony_ci unsigned long flags; 748c2ecf20Sopenharmony_ci ntfs_inode *base_ni; 758c2ecf20Sopenharmony_ci MFT_RECORD *m; 768c2ecf20Sopenharmony_ci ATTR_RECORD *a; 778c2ecf20Sopenharmony_ci runlist_element *rl; 788c2ecf20Sopenharmony_ci struct page *put_this_page = NULL; 798c2ecf20Sopenharmony_ci int err = 0; 808c2ecf20Sopenharmony_ci bool ctx_is_temporary, ctx_needs_reset; 818c2ecf20Sopenharmony_ci ntfs_attr_search_ctx old_ctx = { NULL, }; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci ntfs_debug("Mapping runlist part containing vcn 0x%llx.", 848c2ecf20Sopenharmony_ci (unsigned long long)vcn); 858c2ecf20Sopenharmony_ci if (!NInoAttr(ni)) 868c2ecf20Sopenharmony_ci base_ni = ni; 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci base_ni = ni->ext.base_ntfs_ino; 898c2ecf20Sopenharmony_ci if (!ctx) { 908c2ecf20Sopenharmony_ci ctx_is_temporary = ctx_needs_reset = true; 918c2ecf20Sopenharmony_ci m = map_mft_record(base_ni); 928c2ecf20Sopenharmony_ci if (IS_ERR(m)) 938c2ecf20Sopenharmony_ci return PTR_ERR(m); 948c2ecf20Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(base_ni, m); 958c2ecf20Sopenharmony_ci if (unlikely(!ctx)) { 968c2ecf20Sopenharmony_ci err = -ENOMEM; 978c2ecf20Sopenharmony_ci goto err_out; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } else { 1008c2ecf20Sopenharmony_ci VCN allocated_size_vcn; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci BUG_ON(IS_ERR(ctx->mrec)); 1038c2ecf20Sopenharmony_ci a = ctx->attr; 1048c2ecf20Sopenharmony_ci BUG_ON(!a->non_resident); 1058c2ecf20Sopenharmony_ci ctx_is_temporary = false; 1068c2ecf20Sopenharmony_ci end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn); 1078c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 1088c2ecf20Sopenharmony_ci allocated_size_vcn = ni->allocated_size >> 1098c2ecf20Sopenharmony_ci ni->vol->cluster_size_bits; 1108c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 1118c2ecf20Sopenharmony_ci if (!a->data.non_resident.lowest_vcn && end_vcn <= 0) 1128c2ecf20Sopenharmony_ci end_vcn = allocated_size_vcn - 1; 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * If we already have the attribute extent containing @vcn in 1158c2ecf20Sopenharmony_ci * @ctx, no need to look it up again. We slightly cheat in 1168c2ecf20Sopenharmony_ci * that if vcn exceeds the allocated size, we will refuse to 1178c2ecf20Sopenharmony_ci * map the runlist below, so there is definitely no need to get 1188c2ecf20Sopenharmony_ci * the right attribute extent. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci if (vcn >= allocated_size_vcn || (a->type == ni->type && 1218c2ecf20Sopenharmony_ci a->name_length == ni->name_len && 1228c2ecf20Sopenharmony_ci !memcmp((u8*)a + le16_to_cpu(a->name_offset), 1238c2ecf20Sopenharmony_ci ni->name, ni->name_len) && 1248c2ecf20Sopenharmony_ci sle64_to_cpu(a->data.non_resident.lowest_vcn) 1258c2ecf20Sopenharmony_ci <= vcn && end_vcn >= vcn)) 1268c2ecf20Sopenharmony_ci ctx_needs_reset = false; 1278c2ecf20Sopenharmony_ci else { 1288c2ecf20Sopenharmony_ci /* Save the old search context. */ 1298c2ecf20Sopenharmony_ci old_ctx = *ctx; 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * If the currently mapped (extent) inode is not the 1328c2ecf20Sopenharmony_ci * base inode we will unmap it when we reinitialize the 1338c2ecf20Sopenharmony_ci * search context which means we need to get a 1348c2ecf20Sopenharmony_ci * reference to the page containing the mapped mft 1358c2ecf20Sopenharmony_ci * record so we do not accidentally drop changes to the 1368c2ecf20Sopenharmony_ci * mft record when it has not been marked dirty yet. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino != 1398c2ecf20Sopenharmony_ci old_ctx.base_ntfs_ino) { 1408c2ecf20Sopenharmony_ci put_this_page = old_ctx.ntfs_ino->page; 1418c2ecf20Sopenharmony_ci get_page(put_this_page); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * Reinitialize the search context so we can lookup the 1458c2ecf20Sopenharmony_ci * needed attribute extent. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 1488c2ecf20Sopenharmony_ci ctx_needs_reset = true; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci if (ctx_needs_reset) { 1528c2ecf20Sopenharmony_ci err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, 1538c2ecf20Sopenharmony_ci CASE_SENSITIVE, vcn, NULL, 0, ctx); 1548c2ecf20Sopenharmony_ci if (unlikely(err)) { 1558c2ecf20Sopenharmony_ci if (err == -ENOENT) 1568c2ecf20Sopenharmony_ci err = -EIO; 1578c2ecf20Sopenharmony_ci goto err_out; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci BUG_ON(!ctx->attr->non_resident); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci a = ctx->attr; 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * Only decompress the mapping pairs if @vcn is inside it. Otherwise 1648c2ecf20Sopenharmony_ci * we get into problems when we try to map an out of bounds vcn because 1658c2ecf20Sopenharmony_ci * we then try to map the already mapped runlist fragment and 1668c2ecf20Sopenharmony_ci * ntfs_mapping_pairs_decompress() fails. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1; 1698c2ecf20Sopenharmony_ci if (unlikely(vcn && vcn >= end_vcn)) { 1708c2ecf20Sopenharmony_ci err = -ENOENT; 1718c2ecf20Sopenharmony_ci goto err_out; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci rl = ntfs_mapping_pairs_decompress(ni->vol, a, ni->runlist.rl); 1748c2ecf20Sopenharmony_ci if (IS_ERR(rl)) 1758c2ecf20Sopenharmony_ci err = PTR_ERR(rl); 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci ni->runlist.rl = rl; 1788c2ecf20Sopenharmony_cierr_out: 1798c2ecf20Sopenharmony_ci if (ctx_is_temporary) { 1808c2ecf20Sopenharmony_ci if (likely(ctx)) 1818c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 1828c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 1838c2ecf20Sopenharmony_ci } else if (ctx_needs_reset) { 1848c2ecf20Sopenharmony_ci /* 1858c2ecf20Sopenharmony_ci * If there is no attribute list, restoring the search context 1868c2ecf20Sopenharmony_ci * is accomplished simply by copying the saved context back over 1878c2ecf20Sopenharmony_ci * the caller supplied context. If there is an attribute list, 1888c2ecf20Sopenharmony_ci * things are more complicated as we need to deal with mapping 1898c2ecf20Sopenharmony_ci * of mft records and resulting potential changes in pointers. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (NInoAttrList(base_ni)) { 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * If the currently mapped (extent) inode is not the 1948c2ecf20Sopenharmony_ci * one we had before, we need to unmap it and map the 1958c2ecf20Sopenharmony_ci * old one. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci if (ctx->ntfs_ino != old_ctx.ntfs_ino) { 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * If the currently mapped inode is not the 2008c2ecf20Sopenharmony_ci * base inode, unmap it. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci if (ctx->base_ntfs_ino && ctx->ntfs_ino != 2038c2ecf20Sopenharmony_ci ctx->base_ntfs_ino) { 2048c2ecf20Sopenharmony_ci unmap_extent_mft_record(ctx->ntfs_ino); 2058c2ecf20Sopenharmony_ci ctx->mrec = ctx->base_mrec; 2068c2ecf20Sopenharmony_ci BUG_ON(!ctx->mrec); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * If the old mapped inode is not the base 2108c2ecf20Sopenharmony_ci * inode, map it. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci if (old_ctx.base_ntfs_ino && 2138c2ecf20Sopenharmony_ci old_ctx.ntfs_ino != 2148c2ecf20Sopenharmony_ci old_ctx.base_ntfs_ino) { 2158c2ecf20Sopenharmony_ciretry_map: 2168c2ecf20Sopenharmony_ci ctx->mrec = map_mft_record( 2178c2ecf20Sopenharmony_ci old_ctx.ntfs_ino); 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * Something bad has happened. If out 2208c2ecf20Sopenharmony_ci * of memory retry till it succeeds. 2218c2ecf20Sopenharmony_ci * Any other errors are fatal and we 2228c2ecf20Sopenharmony_ci * return the error code in ctx->mrec. 2238c2ecf20Sopenharmony_ci * Let the caller deal with it... We 2248c2ecf20Sopenharmony_ci * just need to fudge things so the 2258c2ecf20Sopenharmony_ci * caller can reinit and/or put the 2268c2ecf20Sopenharmony_ci * search context safely. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci if (IS_ERR(ctx->mrec)) { 2298c2ecf20Sopenharmony_ci if (PTR_ERR(ctx->mrec) == 2308c2ecf20Sopenharmony_ci -ENOMEM) { 2318c2ecf20Sopenharmony_ci schedule(); 2328c2ecf20Sopenharmony_ci goto retry_map; 2338c2ecf20Sopenharmony_ci } else 2348c2ecf20Sopenharmony_ci old_ctx.ntfs_ino = 2358c2ecf20Sopenharmony_ci old_ctx. 2368c2ecf20Sopenharmony_ci base_ntfs_ino; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci /* Update the changed pointers in the saved context. */ 2418c2ecf20Sopenharmony_ci if (ctx->mrec != old_ctx.mrec) { 2428c2ecf20Sopenharmony_ci if (!IS_ERR(ctx->mrec)) 2438c2ecf20Sopenharmony_ci old_ctx.attr = (ATTR_RECORD*)( 2448c2ecf20Sopenharmony_ci (u8*)ctx->mrec + 2458c2ecf20Sopenharmony_ci ((u8*)old_ctx.attr - 2468c2ecf20Sopenharmony_ci (u8*)old_ctx.mrec)); 2478c2ecf20Sopenharmony_ci old_ctx.mrec = ctx->mrec; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci /* Restore the search context to the saved one. */ 2518c2ecf20Sopenharmony_ci *ctx = old_ctx; 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * We drop the reference on the page we took earlier. In the 2548c2ecf20Sopenharmony_ci * case that IS_ERR(ctx->mrec) is true this means we might lose 2558c2ecf20Sopenharmony_ci * some changes to the mft record that had been made between 2568c2ecf20Sopenharmony_ci * the last time it was marked dirty/written out and now. This 2578c2ecf20Sopenharmony_ci * at this stage is not a problem as the mapping error is fatal 2588c2ecf20Sopenharmony_ci * enough that the mft record cannot be written out anyway and 2598c2ecf20Sopenharmony_ci * the caller is very likely to shutdown the whole inode 2608c2ecf20Sopenharmony_ci * immediately and mark the volume dirty for chkdsk to pick up 2618c2ecf20Sopenharmony_ci * the pieces anyway. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci if (put_this_page) 2648c2ecf20Sopenharmony_ci put_page(put_this_page); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/** 2708c2ecf20Sopenharmony_ci * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode 2718c2ecf20Sopenharmony_ci * @ni: ntfs inode for which to map (part of) a runlist 2728c2ecf20Sopenharmony_ci * @vcn: map runlist part containing this vcn 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * Map the part of a runlist containing the @vcn of the ntfs inode @ni. 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. There is one special error code 2778c2ecf20Sopenharmony_ci * which is not an error as such. This is -ENOENT. It means that @vcn is out 2788c2ecf20Sopenharmony_ci * of bounds of the runlist. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Locking: - The runlist must be unlocked on entry and is unlocked on return. 2818c2ecf20Sopenharmony_ci * - This function takes the runlist lock for writing and may modify 2828c2ecf20Sopenharmony_ci * the runlist. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ciint ntfs_map_runlist(ntfs_inode *ni, VCN vcn) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci int err = 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci down_write(&ni->runlist.lock); 2898c2ecf20Sopenharmony_ci /* Make sure someone else didn't do the work while we were sleeping. */ 2908c2ecf20Sopenharmony_ci if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <= 2918c2ecf20Sopenharmony_ci LCN_RL_NOT_MAPPED)) 2928c2ecf20Sopenharmony_ci err = ntfs_map_runlist_nolock(ni, vcn, NULL); 2938c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 2948c2ecf20Sopenharmony_ci return err; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/** 2988c2ecf20Sopenharmony_ci * ntfs_attr_vcn_to_lcn_nolock - convert a vcn into a lcn given an ntfs inode 2998c2ecf20Sopenharmony_ci * @ni: ntfs inode of the attribute whose runlist to search 3008c2ecf20Sopenharmony_ci * @vcn: vcn to convert 3018c2ecf20Sopenharmony_ci * @write_locked: true if the runlist is locked for writing 3028c2ecf20Sopenharmony_ci * 3038c2ecf20Sopenharmony_ci * Find the virtual cluster number @vcn in the runlist of the ntfs attribute 3048c2ecf20Sopenharmony_ci * described by the ntfs inode @ni and return the corresponding logical cluster 3058c2ecf20Sopenharmony_ci * number (lcn). 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * If the @vcn is not mapped yet, the attempt is made to map the attribute 3088c2ecf20Sopenharmony_ci * extent containing the @vcn and the vcn to lcn conversion is retried. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * If @write_locked is true the caller has locked the runlist for writing and 3118c2ecf20Sopenharmony_ci * if false for reading. 3128c2ecf20Sopenharmony_ci * 3138c2ecf20Sopenharmony_ci * Since lcns must be >= 0, we use negative return codes with special meaning: 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * Return code Meaning / Description 3168c2ecf20Sopenharmony_ci * ========================================== 3178c2ecf20Sopenharmony_ci * LCN_HOLE Hole / not allocated on disk. 3188c2ecf20Sopenharmony_ci * LCN_ENOENT There is no such vcn in the runlist, i.e. @vcn is out of bounds. 3198c2ecf20Sopenharmony_ci * LCN_ENOMEM Not enough memory to map runlist. 3208c2ecf20Sopenharmony_ci * LCN_EIO Critical error (runlist/file is corrupt, i/o error, etc). 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * Locking: - The runlist must be locked on entry and is left locked on return. 3238c2ecf20Sopenharmony_ci * - If @write_locked is 'false', i.e. the runlist is locked for reading, 3248c2ecf20Sopenharmony_ci * the lock may be dropped inside the function so you cannot rely on 3258c2ecf20Sopenharmony_ci * the runlist still being the same when this function returns. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ciLCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn, 3288c2ecf20Sopenharmony_ci const bool write_locked) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci LCN lcn; 3318c2ecf20Sopenharmony_ci unsigned long flags; 3328c2ecf20Sopenharmony_ci bool is_retry = false; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci BUG_ON(!ni); 3358c2ecf20Sopenharmony_ci ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.", 3368c2ecf20Sopenharmony_ci ni->mft_no, (unsigned long long)vcn, 3378c2ecf20Sopenharmony_ci write_locked ? "write" : "read"); 3388c2ecf20Sopenharmony_ci BUG_ON(!NInoNonResident(ni)); 3398c2ecf20Sopenharmony_ci BUG_ON(vcn < 0); 3408c2ecf20Sopenharmony_ci if (!ni->runlist.rl) { 3418c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 3428c2ecf20Sopenharmony_ci if (!ni->allocated_size) { 3438c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 3448c2ecf20Sopenharmony_ci return LCN_ENOENT; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ciretry_remap: 3498c2ecf20Sopenharmony_ci /* Convert vcn to lcn. If that fails map the runlist and retry once. */ 3508c2ecf20Sopenharmony_ci lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn); 3518c2ecf20Sopenharmony_ci if (likely(lcn >= LCN_HOLE)) { 3528c2ecf20Sopenharmony_ci ntfs_debug("Done, lcn 0x%llx.", (long long)lcn); 3538c2ecf20Sopenharmony_ci return lcn; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci if (lcn != LCN_RL_NOT_MAPPED) { 3568c2ecf20Sopenharmony_ci if (lcn != LCN_ENOENT) 3578c2ecf20Sopenharmony_ci lcn = LCN_EIO; 3588c2ecf20Sopenharmony_ci } else if (!is_retry) { 3598c2ecf20Sopenharmony_ci int err; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (!write_locked) { 3628c2ecf20Sopenharmony_ci up_read(&ni->runlist.lock); 3638c2ecf20Sopenharmony_ci down_write(&ni->runlist.lock); 3648c2ecf20Sopenharmony_ci if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) != 3658c2ecf20Sopenharmony_ci LCN_RL_NOT_MAPPED)) { 3668c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 3678c2ecf20Sopenharmony_ci down_read(&ni->runlist.lock); 3688c2ecf20Sopenharmony_ci goto retry_remap; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci err = ntfs_map_runlist_nolock(ni, vcn, NULL); 3728c2ecf20Sopenharmony_ci if (!write_locked) { 3738c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 3748c2ecf20Sopenharmony_ci down_read(&ni->runlist.lock); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci if (likely(!err)) { 3778c2ecf20Sopenharmony_ci is_retry = true; 3788c2ecf20Sopenharmony_ci goto retry_remap; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci if (err == -ENOENT) 3818c2ecf20Sopenharmony_ci lcn = LCN_ENOENT; 3828c2ecf20Sopenharmony_ci else if (err == -ENOMEM) 3838c2ecf20Sopenharmony_ci lcn = LCN_ENOMEM; 3848c2ecf20Sopenharmony_ci else 3858c2ecf20Sopenharmony_ci lcn = LCN_EIO; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci if (lcn != LCN_ENOENT) 3888c2ecf20Sopenharmony_ci ntfs_error(ni->vol->sb, "Failed with error code %lli.", 3898c2ecf20Sopenharmony_ci (long long)lcn); 3908c2ecf20Sopenharmony_ci return lcn; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode 3958c2ecf20Sopenharmony_ci * @ni: ntfs inode describing the runlist to search 3968c2ecf20Sopenharmony_ci * @vcn: vcn to find 3978c2ecf20Sopenharmony_ci * @ctx: active attribute search context if present or NULL if not 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * Find the virtual cluster number @vcn in the runlist described by the ntfs 4008c2ecf20Sopenharmony_ci * inode @ni and return the address of the runlist element containing the @vcn. 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * If the @vcn is not mapped yet, the attempt is made to map the attribute 4038c2ecf20Sopenharmony_ci * extent containing the @vcn and the vcn to lcn conversion is retried. 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * If @ctx is specified, it is an active search context of @ni and its base mft 4068c2ecf20Sopenharmony_ci * record. This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped 4078c2ecf20Sopenharmony_ci * runlist fragments and allows their mapping. If you do not have the mft 4088c2ecf20Sopenharmony_ci * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock() 4098c2ecf20Sopenharmony_ci * will perform the necessary mapping and unmapping. 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and 4128c2ecf20Sopenharmony_ci * restores it before returning. Thus, @ctx will be left pointing to the same 4138c2ecf20Sopenharmony_ci * attribute on return as on entry. However, the actual pointers in @ctx may 4148c2ecf20Sopenharmony_ci * point to different memory locations on return, so you must remember to reset 4158c2ecf20Sopenharmony_ci * any cached pointers from the @ctx, i.e. after the call to 4168c2ecf20Sopenharmony_ci * ntfs_attr_find_vcn_nolock(), you will probably want to do: 4178c2ecf20Sopenharmony_ci * m = ctx->mrec; 4188c2ecf20Sopenharmony_ci * a = ctx->attr; 4198c2ecf20Sopenharmony_ci * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that 4208c2ecf20Sopenharmony_ci * you cache ctx->mrec in a variable @m of type MFT_RECORD *. 4218c2ecf20Sopenharmony_ci * Note you need to distinguish between the lcn of the returned runlist element 4228c2ecf20Sopenharmony_ci * being >= 0 and LCN_HOLE. In the later case you have to return zeroes on 4238c2ecf20Sopenharmony_ci * read and allocate clusters on write. 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * Return the runlist element containing the @vcn on success and 4268c2ecf20Sopenharmony_ci * ERR_PTR(-errno) on error. You need to test the return value with IS_ERR() 4278c2ecf20Sopenharmony_ci * to decide if the return is success or failure and PTR_ERR() to get to the 4288c2ecf20Sopenharmony_ci * error code if IS_ERR() is true. 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * The possible error return codes are: 4318c2ecf20Sopenharmony_ci * -ENOENT - No such vcn in the runlist, i.e. @vcn is out of bounds. 4328c2ecf20Sopenharmony_ci * -ENOMEM - Not enough memory to map runlist. 4338c2ecf20Sopenharmony_ci * -EIO - Critical error (runlist/file is corrupt, i/o error, etc). 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * WARNING: If @ctx is supplied, regardless of whether success or failure is 4368c2ecf20Sopenharmony_ci * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx 4378c2ecf20Sopenharmony_ci * is no longer valid, i.e. you need to either call 4388c2ecf20Sopenharmony_ci * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it. 4398c2ecf20Sopenharmony_ci * In that case PTR_ERR(@ctx->mrec) will give you the error code for 4408c2ecf20Sopenharmony_ci * why the mapping of the old inode failed. 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * Locking: - The runlist described by @ni must be locked for writing on entry 4438c2ecf20Sopenharmony_ci * and is locked on return. Note the runlist may be modified when 4448c2ecf20Sopenharmony_ci * needed runlist fragments need to be mapped. 4458c2ecf20Sopenharmony_ci * - If @ctx is NULL, the base mft record of @ni must not be mapped on 4468c2ecf20Sopenharmony_ci * entry and it will be left unmapped on return. 4478c2ecf20Sopenharmony_ci * - If @ctx is not NULL, the base mft record must be mapped on entry 4488c2ecf20Sopenharmony_ci * and it will be left mapped on return. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_cirunlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, 4518c2ecf20Sopenharmony_ci ntfs_attr_search_ctx *ctx) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci unsigned long flags; 4548c2ecf20Sopenharmony_ci runlist_element *rl; 4558c2ecf20Sopenharmony_ci int err = 0; 4568c2ecf20Sopenharmony_ci bool is_retry = false; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci BUG_ON(!ni); 4598c2ecf20Sopenharmony_ci ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, with%s ctx.", 4608c2ecf20Sopenharmony_ci ni->mft_no, (unsigned long long)vcn, ctx ? "" : "out"); 4618c2ecf20Sopenharmony_ci BUG_ON(!NInoNonResident(ni)); 4628c2ecf20Sopenharmony_ci BUG_ON(vcn < 0); 4638c2ecf20Sopenharmony_ci if (!ni->runlist.rl) { 4648c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 4658c2ecf20Sopenharmony_ci if (!ni->allocated_size) { 4668c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 4678c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ciretry_remap: 4728c2ecf20Sopenharmony_ci rl = ni->runlist.rl; 4738c2ecf20Sopenharmony_ci if (likely(rl && vcn >= rl[0].vcn)) { 4748c2ecf20Sopenharmony_ci while (likely(rl->length)) { 4758c2ecf20Sopenharmony_ci if (unlikely(vcn < rl[1].vcn)) { 4768c2ecf20Sopenharmony_ci if (likely(rl->lcn >= LCN_HOLE)) { 4778c2ecf20Sopenharmony_ci ntfs_debug("Done."); 4788c2ecf20Sopenharmony_ci return rl; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci rl++; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci if (likely(rl->lcn != LCN_RL_NOT_MAPPED)) { 4858c2ecf20Sopenharmony_ci if (likely(rl->lcn == LCN_ENOENT)) 4868c2ecf20Sopenharmony_ci err = -ENOENT; 4878c2ecf20Sopenharmony_ci else 4888c2ecf20Sopenharmony_ci err = -EIO; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci if (!err && !is_retry) { 4928c2ecf20Sopenharmony_ci /* 4938c2ecf20Sopenharmony_ci * If the search context is invalid we cannot map the unmapped 4948c2ecf20Sopenharmony_ci * region. 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci if (IS_ERR(ctx->mrec)) 4978c2ecf20Sopenharmony_ci err = PTR_ERR(ctx->mrec); 4988c2ecf20Sopenharmony_ci else { 4998c2ecf20Sopenharmony_ci /* 5008c2ecf20Sopenharmony_ci * The @vcn is in an unmapped region, map the runlist 5018c2ecf20Sopenharmony_ci * and retry. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci err = ntfs_map_runlist_nolock(ni, vcn, ctx); 5048c2ecf20Sopenharmony_ci if (likely(!err)) { 5058c2ecf20Sopenharmony_ci is_retry = true; 5068c2ecf20Sopenharmony_ci goto retry_remap; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci if (err == -EINVAL) 5108c2ecf20Sopenharmony_ci err = -EIO; 5118c2ecf20Sopenharmony_ci } else if (!err) 5128c2ecf20Sopenharmony_ci err = -EIO; 5138c2ecf20Sopenharmony_ci if (err != -ENOENT) 5148c2ecf20Sopenharmony_ci ntfs_error(ni->vol->sb, "Failed with error code %i.", err); 5158c2ecf20Sopenharmony_ci return ERR_PTR(err); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/** 5198c2ecf20Sopenharmony_ci * ntfs_attr_find - find (next) attribute in mft record 5208c2ecf20Sopenharmony_ci * @type: attribute type to find 5218c2ecf20Sopenharmony_ci * @name: attribute name to find (optional, i.e. NULL means don't care) 5228c2ecf20Sopenharmony_ci * @name_len: attribute name length (only needed if @name present) 5238c2ecf20Sopenharmony_ci * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) 5248c2ecf20Sopenharmony_ci * @val: attribute value to find (optional, resident attributes only) 5258c2ecf20Sopenharmony_ci * @val_len: attribute value length 5268c2ecf20Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 5278c2ecf20Sopenharmony_ci * 5288c2ecf20Sopenharmony_ci * You should not need to call this function directly. Use ntfs_attr_lookup() 5298c2ecf20Sopenharmony_ci * instead. 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * ntfs_attr_find() takes a search context @ctx as parameter and searches the 5328c2ecf20Sopenharmony_ci * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an 5338c2ecf20Sopenharmony_ci * attribute of @type, optionally @name and @val. 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * If the attribute is found, ntfs_attr_find() returns 0 and @ctx->attr will 5368c2ecf20Sopenharmony_ci * point to the found attribute. 5378c2ecf20Sopenharmony_ci * 5388c2ecf20Sopenharmony_ci * If the attribute is not found, ntfs_attr_find() returns -ENOENT and 5398c2ecf20Sopenharmony_ci * @ctx->attr will point to the attribute before which the attribute being 5408c2ecf20Sopenharmony_ci * searched for would need to be inserted if such an action were to be desired. 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * On actual error, ntfs_attr_find() returns -EIO. In this case @ctx->attr is 5438c2ecf20Sopenharmony_ci * undefined and in particular do not rely on it not changing. 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * If @ctx->is_first is 'true', the search begins with @ctx->attr itself. If it 5468c2ecf20Sopenharmony_ci * is 'false', the search begins after @ctx->attr. 5478c2ecf20Sopenharmony_ci * 5488c2ecf20Sopenharmony_ci * If @ic is IGNORE_CASE, the @name comparisson is not case sensitive and 5498c2ecf20Sopenharmony_ci * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record 5508c2ecf20Sopenharmony_ci * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at 5518c2ecf20Sopenharmony_ci * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case 5528c2ecf20Sopenharmony_ci * sensitive. When @name is present, @name_len is the @name length in Unicode 5538c2ecf20Sopenharmony_ci * characters. 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * If @name is not present (NULL), we assume that the unnamed attribute is 5568c2ecf20Sopenharmony_ci * being searched for. 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * Finally, the resident attribute value @val is looked for, if present. If 5598c2ecf20Sopenharmony_ci * @val is not present (NULL), @val_len is ignored. 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * ntfs_attr_find() only searches the specified mft record and it ignores the 5628c2ecf20Sopenharmony_ci * presence of an attribute list attribute (unless it is the one being searched 5638c2ecf20Sopenharmony_ci * for, obviously). If you need to take attribute lists into consideration, 5648c2ecf20Sopenharmony_ci * use ntfs_attr_lookup() instead (see below). This also means that you cannot 5658c2ecf20Sopenharmony_ci * use ntfs_attr_find() to search for extent records of non-resident 5668c2ecf20Sopenharmony_ci * attributes, as extents with lowest_vcn != 0 are usually described by the 5678c2ecf20Sopenharmony_ci * attribute list attribute only. - Note that it is possible that the first 5688c2ecf20Sopenharmony_ci * extent is only in the attribute list while the last extent is in the base 5698c2ecf20Sopenharmony_ci * mft record, so do not rely on being able to find the first extent in the 5708c2ecf20Sopenharmony_ci * base mft record. 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * Warning: Never use @val when looking for attribute types which can be 5738c2ecf20Sopenharmony_ci * non-resident as this most likely will result in a crash! 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_cistatic int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name, 5768c2ecf20Sopenharmony_ci const u32 name_len, const IGNORE_CASE_BOOL ic, 5778c2ecf20Sopenharmony_ci const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci ATTR_RECORD *a; 5808c2ecf20Sopenharmony_ci ntfs_volume *vol = ctx->ntfs_ino->vol; 5818c2ecf20Sopenharmony_ci ntfschar *upcase = vol->upcase; 5828c2ecf20Sopenharmony_ci u32 upcase_len = vol->upcase_len; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* 5858c2ecf20Sopenharmony_ci * Iterate over attributes in mft record starting at @ctx->attr, or the 5868c2ecf20Sopenharmony_ci * attribute following that, if @ctx->is_first is 'true'. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (ctx->is_first) { 5898c2ecf20Sopenharmony_ci a = ctx->attr; 5908c2ecf20Sopenharmony_ci ctx->is_first = false; 5918c2ecf20Sopenharmony_ci } else 5928c2ecf20Sopenharmony_ci a = (ATTR_RECORD*)((u8*)ctx->attr + 5938c2ecf20Sopenharmony_ci le32_to_cpu(ctx->attr->length)); 5948c2ecf20Sopenharmony_ci for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) { 5958c2ecf20Sopenharmony_ci u8 *mrec_end = (u8 *)ctx->mrec + 5968c2ecf20Sopenharmony_ci le32_to_cpu(ctx->mrec->bytes_allocated); 5978c2ecf20Sopenharmony_ci u8 *name_end; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* check whether ATTR_RECORD wrap */ 6008c2ecf20Sopenharmony_ci if ((u8 *)a < (u8 *)ctx->mrec) 6018c2ecf20Sopenharmony_ci break; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* check whether Attribute Record Header is within bounds */ 6048c2ecf20Sopenharmony_ci if ((u8 *)a > mrec_end || 6058c2ecf20Sopenharmony_ci (u8 *)a + sizeof(ATTR_RECORD) > mrec_end) 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* check whether ATTR_RECORD's name is within bounds */ 6098c2ecf20Sopenharmony_ci name_end = (u8 *)a + le16_to_cpu(a->name_offset) + 6108c2ecf20Sopenharmony_ci a->name_length * sizeof(ntfschar); 6118c2ecf20Sopenharmony_ci if (name_end > mrec_end) 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ctx->attr = a; 6158c2ecf20Sopenharmony_ci if (unlikely(le32_to_cpu(a->type) > le32_to_cpu(type) || 6168c2ecf20Sopenharmony_ci a->type == AT_END)) 6178c2ecf20Sopenharmony_ci return -ENOENT; 6188c2ecf20Sopenharmony_ci if (unlikely(!a->length)) 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* check whether ATTR_RECORD's length wrap */ 6228c2ecf20Sopenharmony_ci if ((u8 *)a + le32_to_cpu(a->length) < (u8 *)a) 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci /* check whether ATTR_RECORD's length is within bounds */ 6258c2ecf20Sopenharmony_ci if ((u8 *)a + le32_to_cpu(a->length) > mrec_end) 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (a->type != type) 6298c2ecf20Sopenharmony_ci continue; 6308c2ecf20Sopenharmony_ci /* 6318c2ecf20Sopenharmony_ci * If @name is present, compare the two names. If @name is 6328c2ecf20Sopenharmony_ci * missing, assume we want an unnamed attribute. 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_ci if (!name) { 6358c2ecf20Sopenharmony_ci /* The search failed if the found attribute is named. */ 6368c2ecf20Sopenharmony_ci if (a->name_length) 6378c2ecf20Sopenharmony_ci return -ENOENT; 6388c2ecf20Sopenharmony_ci } else if (!ntfs_are_names_equal(name, name_len, 6398c2ecf20Sopenharmony_ci (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), 6408c2ecf20Sopenharmony_ci a->name_length, ic, upcase, upcase_len)) { 6418c2ecf20Sopenharmony_ci register int rc; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci rc = ntfs_collate_names(name, name_len, 6448c2ecf20Sopenharmony_ci (ntfschar*)((u8*)a + 6458c2ecf20Sopenharmony_ci le16_to_cpu(a->name_offset)), 6468c2ecf20Sopenharmony_ci a->name_length, 1, IGNORE_CASE, 6478c2ecf20Sopenharmony_ci upcase, upcase_len); 6488c2ecf20Sopenharmony_ci /* 6498c2ecf20Sopenharmony_ci * If @name collates before a->name, there is no 6508c2ecf20Sopenharmony_ci * matching attribute. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_ci if (rc == -1) 6538c2ecf20Sopenharmony_ci return -ENOENT; 6548c2ecf20Sopenharmony_ci /* If the strings are not equal, continue search. */ 6558c2ecf20Sopenharmony_ci if (rc) 6568c2ecf20Sopenharmony_ci continue; 6578c2ecf20Sopenharmony_ci rc = ntfs_collate_names(name, name_len, 6588c2ecf20Sopenharmony_ci (ntfschar*)((u8*)a + 6598c2ecf20Sopenharmony_ci le16_to_cpu(a->name_offset)), 6608c2ecf20Sopenharmony_ci a->name_length, 1, CASE_SENSITIVE, 6618c2ecf20Sopenharmony_ci upcase, upcase_len); 6628c2ecf20Sopenharmony_ci if (rc == -1) 6638c2ecf20Sopenharmony_ci return -ENOENT; 6648c2ecf20Sopenharmony_ci if (rc) 6658c2ecf20Sopenharmony_ci continue; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci /* 6688c2ecf20Sopenharmony_ci * The names match or @name not present and attribute is 6698c2ecf20Sopenharmony_ci * unnamed. If no @val specified, we have found the attribute 6708c2ecf20Sopenharmony_ci * and are done. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci if (!val) 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci /* @val is present; compare values. */ 6758c2ecf20Sopenharmony_ci else { 6768c2ecf20Sopenharmony_ci register int rc; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci rc = memcmp(val, (u8*)a + le16_to_cpu( 6798c2ecf20Sopenharmony_ci a->data.resident.value_offset), 6808c2ecf20Sopenharmony_ci min_t(u32, val_len, le32_to_cpu( 6818c2ecf20Sopenharmony_ci a->data.resident.value_length))); 6828c2ecf20Sopenharmony_ci /* 6838c2ecf20Sopenharmony_ci * If @val collates before the current attribute's 6848c2ecf20Sopenharmony_ci * value, there is no matching attribute. 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci if (!rc) { 6878c2ecf20Sopenharmony_ci register u32 avl; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci avl = le32_to_cpu( 6908c2ecf20Sopenharmony_ci a->data.resident.value_length); 6918c2ecf20Sopenharmony_ci if (val_len == avl) 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci if (val_len < avl) 6948c2ecf20Sopenharmony_ci return -ENOENT; 6958c2ecf20Sopenharmony_ci } else if (rc < 0) 6968c2ecf20Sopenharmony_ci return -ENOENT; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk."); 7008c2ecf20Sopenharmony_ci NVolSetErrors(vol); 7018c2ecf20Sopenharmony_ci return -EIO; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/** 7058c2ecf20Sopenharmony_ci * load_attribute_list - load an attribute list into memory 7068c2ecf20Sopenharmony_ci * @vol: ntfs volume from which to read 7078c2ecf20Sopenharmony_ci * @runlist: runlist of the attribute list 7088c2ecf20Sopenharmony_ci * @al_start: destination buffer 7098c2ecf20Sopenharmony_ci * @size: size of the destination buffer in bytes 7108c2ecf20Sopenharmony_ci * @initialized_size: initialized size of the attribute list 7118c2ecf20Sopenharmony_ci * 7128c2ecf20Sopenharmony_ci * Walk the runlist @runlist and load all clusters from it copying them into 7138c2ecf20Sopenharmony_ci * the linear buffer @al. The maximum number of bytes copied to @al is @size 7148c2ecf20Sopenharmony_ci * bytes. Note, @size does not need to be a multiple of the cluster size. If 7158c2ecf20Sopenharmony_ci * @initialized_size is less than @size, the region in @al between 7168c2ecf20Sopenharmony_ci * @initialized_size and @size will be zeroed and not read from disk. 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * Return 0 on success or -errno on error. 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ciint load_attribute_list(ntfs_volume *vol, runlist *runlist, u8 *al_start, 7218c2ecf20Sopenharmony_ci const s64 size, const s64 initialized_size) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci LCN lcn; 7248c2ecf20Sopenharmony_ci u8 *al = al_start; 7258c2ecf20Sopenharmony_ci u8 *al_end = al + initialized_size; 7268c2ecf20Sopenharmony_ci runlist_element *rl; 7278c2ecf20Sopenharmony_ci struct buffer_head *bh; 7288c2ecf20Sopenharmony_ci struct super_block *sb; 7298c2ecf20Sopenharmony_ci unsigned long block_size; 7308c2ecf20Sopenharmony_ci unsigned long block, max_block; 7318c2ecf20Sopenharmony_ci int err = 0; 7328c2ecf20Sopenharmony_ci unsigned char block_size_bits; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ntfs_debug("Entering."); 7358c2ecf20Sopenharmony_ci if (!vol || !runlist || !al || size <= 0 || initialized_size < 0 || 7368c2ecf20Sopenharmony_ci initialized_size > size) 7378c2ecf20Sopenharmony_ci return -EINVAL; 7388c2ecf20Sopenharmony_ci if (!initialized_size) { 7398c2ecf20Sopenharmony_ci memset(al, 0, size); 7408c2ecf20Sopenharmony_ci return 0; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci sb = vol->sb; 7438c2ecf20Sopenharmony_ci block_size = sb->s_blocksize; 7448c2ecf20Sopenharmony_ci block_size_bits = sb->s_blocksize_bits; 7458c2ecf20Sopenharmony_ci down_read(&runlist->lock); 7468c2ecf20Sopenharmony_ci rl = runlist->rl; 7478c2ecf20Sopenharmony_ci if (!rl) { 7488c2ecf20Sopenharmony_ci ntfs_error(sb, "Cannot read attribute list since runlist is " 7498c2ecf20Sopenharmony_ci "missing."); 7508c2ecf20Sopenharmony_ci goto err_out; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci /* Read all clusters specified by the runlist one run at a time. */ 7538c2ecf20Sopenharmony_ci while (rl->length) { 7548c2ecf20Sopenharmony_ci lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn); 7558c2ecf20Sopenharmony_ci ntfs_debug("Reading vcn = 0x%llx, lcn = 0x%llx.", 7568c2ecf20Sopenharmony_ci (unsigned long long)rl->vcn, 7578c2ecf20Sopenharmony_ci (unsigned long long)lcn); 7588c2ecf20Sopenharmony_ci /* The attribute list cannot be sparse. */ 7598c2ecf20Sopenharmony_ci if (lcn < 0) { 7608c2ecf20Sopenharmony_ci ntfs_error(sb, "ntfs_rl_vcn_to_lcn() failed. Cannot " 7618c2ecf20Sopenharmony_ci "read attribute list."); 7628c2ecf20Sopenharmony_ci goto err_out; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci block = lcn << vol->cluster_size_bits >> block_size_bits; 7658c2ecf20Sopenharmony_ci /* Read the run from device in chunks of block_size bytes. */ 7668c2ecf20Sopenharmony_ci max_block = block + (rl->length << vol->cluster_size_bits >> 7678c2ecf20Sopenharmony_ci block_size_bits); 7688c2ecf20Sopenharmony_ci ntfs_debug("max_block = 0x%lx.", max_block); 7698c2ecf20Sopenharmony_ci do { 7708c2ecf20Sopenharmony_ci ntfs_debug("Reading block = 0x%lx.", block); 7718c2ecf20Sopenharmony_ci bh = sb_bread(sb, block); 7728c2ecf20Sopenharmony_ci if (!bh) { 7738c2ecf20Sopenharmony_ci ntfs_error(sb, "sb_bread() failed. Cannot " 7748c2ecf20Sopenharmony_ci "read attribute list."); 7758c2ecf20Sopenharmony_ci goto err_out; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci if (al + block_size >= al_end) 7788c2ecf20Sopenharmony_ci goto do_final; 7798c2ecf20Sopenharmony_ci memcpy(al, bh->b_data, block_size); 7808c2ecf20Sopenharmony_ci brelse(bh); 7818c2ecf20Sopenharmony_ci al += block_size; 7828c2ecf20Sopenharmony_ci } while (++block < max_block); 7838c2ecf20Sopenharmony_ci rl++; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci if (initialized_size < size) { 7868c2ecf20Sopenharmony_ciinitialize: 7878c2ecf20Sopenharmony_ci memset(al_start + initialized_size, 0, size - initialized_size); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_cidone: 7908c2ecf20Sopenharmony_ci up_read(&runlist->lock); 7918c2ecf20Sopenharmony_ci return err; 7928c2ecf20Sopenharmony_cido_final: 7938c2ecf20Sopenharmony_ci if (al < al_end) { 7948c2ecf20Sopenharmony_ci /* 7958c2ecf20Sopenharmony_ci * Partial block. 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * Note: The attribute list can be smaller than its allocation 7988c2ecf20Sopenharmony_ci * by multiple clusters. This has been encountered by at least 7998c2ecf20Sopenharmony_ci * two people running Windows XP, thus we cannot do any 8008c2ecf20Sopenharmony_ci * truncation sanity checking here. (AIA) 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ci memcpy(al, bh->b_data, al_end - al); 8038c2ecf20Sopenharmony_ci brelse(bh); 8048c2ecf20Sopenharmony_ci if (initialized_size < size) 8058c2ecf20Sopenharmony_ci goto initialize; 8068c2ecf20Sopenharmony_ci goto done; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci brelse(bh); 8098c2ecf20Sopenharmony_ci /* Real overflow! */ 8108c2ecf20Sopenharmony_ci ntfs_error(sb, "Attribute list buffer overflow. Read attribute list " 8118c2ecf20Sopenharmony_ci "is truncated."); 8128c2ecf20Sopenharmony_cierr_out: 8138c2ecf20Sopenharmony_ci err = -EIO; 8148c2ecf20Sopenharmony_ci goto done; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/** 8188c2ecf20Sopenharmony_ci * ntfs_external_attr_find - find an attribute in the attribute list of an inode 8198c2ecf20Sopenharmony_ci * @type: attribute type to find 8208c2ecf20Sopenharmony_ci * @name: attribute name to find (optional, i.e. NULL means don't care) 8218c2ecf20Sopenharmony_ci * @name_len: attribute name length (only needed if @name present) 8228c2ecf20Sopenharmony_ci * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) 8238c2ecf20Sopenharmony_ci * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) 8248c2ecf20Sopenharmony_ci * @val: attribute value to find (optional, resident attributes only) 8258c2ecf20Sopenharmony_ci * @val_len: attribute value length 8268c2ecf20Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 8278c2ecf20Sopenharmony_ci * 8288c2ecf20Sopenharmony_ci * You should not need to call this function directly. Use ntfs_attr_lookup() 8298c2ecf20Sopenharmony_ci * instead. 8308c2ecf20Sopenharmony_ci * 8318c2ecf20Sopenharmony_ci * Find an attribute by searching the attribute list for the corresponding 8328c2ecf20Sopenharmony_ci * attribute list entry. Having found the entry, map the mft record if the 8338c2ecf20Sopenharmony_ci * attribute is in a different mft record/inode, ntfs_attr_find() the attribute 8348c2ecf20Sopenharmony_ci * in there and return it. 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * On first search @ctx->ntfs_ino must be the base mft record and @ctx must 8378c2ecf20Sopenharmony_ci * have been obtained from a call to ntfs_attr_get_search_ctx(). On subsequent 8388c2ecf20Sopenharmony_ci * calls @ctx->ntfs_ino can be any extent inode, too (@ctx->base_ntfs_ino is 8398c2ecf20Sopenharmony_ci * then the base inode). 8408c2ecf20Sopenharmony_ci * 8418c2ecf20Sopenharmony_ci * After finishing with the attribute/mft record you need to call 8428c2ecf20Sopenharmony_ci * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any 8438c2ecf20Sopenharmony_ci * mapped inodes, etc). 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci * If the attribute is found, ntfs_external_attr_find() returns 0 and 8468c2ecf20Sopenharmony_ci * @ctx->attr will point to the found attribute. @ctx->mrec will point to the 8478c2ecf20Sopenharmony_ci * mft record in which @ctx->attr is located and @ctx->al_entry will point to 8488c2ecf20Sopenharmony_ci * the attribute list entry for the attribute. 8498c2ecf20Sopenharmony_ci * 8508c2ecf20Sopenharmony_ci * If the attribute is not found, ntfs_external_attr_find() returns -ENOENT and 8518c2ecf20Sopenharmony_ci * @ctx->attr will point to the attribute in the base mft record before which 8528c2ecf20Sopenharmony_ci * the attribute being searched for would need to be inserted if such an action 8538c2ecf20Sopenharmony_ci * were to be desired. @ctx->mrec will point to the mft record in which 8548c2ecf20Sopenharmony_ci * @ctx->attr is located and @ctx->al_entry will point to the attribute list 8558c2ecf20Sopenharmony_ci * entry of the attribute before which the attribute being searched for would 8568c2ecf20Sopenharmony_ci * need to be inserted if such an action were to be desired. 8578c2ecf20Sopenharmony_ci * 8588c2ecf20Sopenharmony_ci * Thus to insert the not found attribute, one wants to add the attribute to 8598c2ecf20Sopenharmony_ci * @ctx->mrec (the base mft record) and if there is not enough space, the 8608c2ecf20Sopenharmony_ci * attribute should be placed in a newly allocated extent mft record. The 8618c2ecf20Sopenharmony_ci * attribute list entry for the inserted attribute should be inserted in the 8628c2ecf20Sopenharmony_ci * attribute list attribute at @ctx->al_entry. 8638c2ecf20Sopenharmony_ci * 8648c2ecf20Sopenharmony_ci * On actual error, ntfs_external_attr_find() returns -EIO. In this case 8658c2ecf20Sopenharmony_ci * @ctx->attr is undefined and in particular do not rely on it not changing. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_cistatic int ntfs_external_attr_find(const ATTR_TYPE type, 8688c2ecf20Sopenharmony_ci const ntfschar *name, const u32 name_len, 8698c2ecf20Sopenharmony_ci const IGNORE_CASE_BOOL ic, const VCN lowest_vcn, 8708c2ecf20Sopenharmony_ci const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci ntfs_inode *base_ni, *ni; 8738c2ecf20Sopenharmony_ci ntfs_volume *vol; 8748c2ecf20Sopenharmony_ci ATTR_LIST_ENTRY *al_entry, *next_al_entry; 8758c2ecf20Sopenharmony_ci u8 *al_start, *al_end; 8768c2ecf20Sopenharmony_ci ATTR_RECORD *a; 8778c2ecf20Sopenharmony_ci ntfschar *al_name; 8788c2ecf20Sopenharmony_ci u32 al_name_len; 8798c2ecf20Sopenharmony_ci int err = 0; 8808c2ecf20Sopenharmony_ci static const char *es = " Unmount and run chkdsk."; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci ni = ctx->ntfs_ino; 8838c2ecf20Sopenharmony_ci base_ni = ctx->base_ntfs_ino; 8848c2ecf20Sopenharmony_ci ntfs_debug("Entering for inode 0x%lx, type 0x%x.", ni->mft_no, type); 8858c2ecf20Sopenharmony_ci if (!base_ni) { 8868c2ecf20Sopenharmony_ci /* First call happens with the base mft record. */ 8878c2ecf20Sopenharmony_ci base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; 8888c2ecf20Sopenharmony_ci ctx->base_mrec = ctx->mrec; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci if (ni == base_ni) 8918c2ecf20Sopenharmony_ci ctx->base_attr = ctx->attr; 8928c2ecf20Sopenharmony_ci if (type == AT_END) 8938c2ecf20Sopenharmony_ci goto not_found; 8948c2ecf20Sopenharmony_ci vol = base_ni->vol; 8958c2ecf20Sopenharmony_ci al_start = base_ni->attr_list; 8968c2ecf20Sopenharmony_ci al_end = al_start + base_ni->attr_list_size; 8978c2ecf20Sopenharmony_ci if (!ctx->al_entry) 8988c2ecf20Sopenharmony_ci ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; 8998c2ecf20Sopenharmony_ci /* 9008c2ecf20Sopenharmony_ci * Iterate over entries in attribute list starting at @ctx->al_entry, 9018c2ecf20Sopenharmony_ci * or the entry following that, if @ctx->is_first is 'true'. 9028c2ecf20Sopenharmony_ci */ 9038c2ecf20Sopenharmony_ci if (ctx->is_first) { 9048c2ecf20Sopenharmony_ci al_entry = ctx->al_entry; 9058c2ecf20Sopenharmony_ci ctx->is_first = false; 9068c2ecf20Sopenharmony_ci } else 9078c2ecf20Sopenharmony_ci al_entry = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + 9088c2ecf20Sopenharmony_ci le16_to_cpu(ctx->al_entry->length)); 9098c2ecf20Sopenharmony_ci for (;; al_entry = next_al_entry) { 9108c2ecf20Sopenharmony_ci /* Out of bounds check. */ 9118c2ecf20Sopenharmony_ci if ((u8*)al_entry < base_ni->attr_list || 9128c2ecf20Sopenharmony_ci (u8*)al_entry > al_end) 9138c2ecf20Sopenharmony_ci break; /* Inode is corrupt. */ 9148c2ecf20Sopenharmony_ci ctx->al_entry = al_entry; 9158c2ecf20Sopenharmony_ci /* Catch the end of the attribute list. */ 9168c2ecf20Sopenharmony_ci if ((u8*)al_entry == al_end) 9178c2ecf20Sopenharmony_ci goto not_found; 9188c2ecf20Sopenharmony_ci if (!al_entry->length) 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + 9218c2ecf20Sopenharmony_ci le16_to_cpu(al_entry->length) > al_end) 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + 9248c2ecf20Sopenharmony_ci le16_to_cpu(al_entry->length)); 9258c2ecf20Sopenharmony_ci if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) 9268c2ecf20Sopenharmony_ci goto not_found; 9278c2ecf20Sopenharmony_ci if (type != al_entry->type) 9288c2ecf20Sopenharmony_ci continue; 9298c2ecf20Sopenharmony_ci /* 9308c2ecf20Sopenharmony_ci * If @name is present, compare the two names. If @name is 9318c2ecf20Sopenharmony_ci * missing, assume we want an unnamed attribute. 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_ci al_name_len = al_entry->name_length; 9348c2ecf20Sopenharmony_ci al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); 9358c2ecf20Sopenharmony_ci if (!name) { 9368c2ecf20Sopenharmony_ci if (al_name_len) 9378c2ecf20Sopenharmony_ci goto not_found; 9388c2ecf20Sopenharmony_ci } else if (!ntfs_are_names_equal(al_name, al_name_len, name, 9398c2ecf20Sopenharmony_ci name_len, ic, vol->upcase, vol->upcase_len)) { 9408c2ecf20Sopenharmony_ci register int rc; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci rc = ntfs_collate_names(name, name_len, al_name, 9438c2ecf20Sopenharmony_ci al_name_len, 1, IGNORE_CASE, 9448c2ecf20Sopenharmony_ci vol->upcase, vol->upcase_len); 9458c2ecf20Sopenharmony_ci /* 9468c2ecf20Sopenharmony_ci * If @name collates before al_name, there is no 9478c2ecf20Sopenharmony_ci * matching attribute. 9488c2ecf20Sopenharmony_ci */ 9498c2ecf20Sopenharmony_ci if (rc == -1) 9508c2ecf20Sopenharmony_ci goto not_found; 9518c2ecf20Sopenharmony_ci /* If the strings are not equal, continue search. */ 9528c2ecf20Sopenharmony_ci if (rc) 9538c2ecf20Sopenharmony_ci continue; 9548c2ecf20Sopenharmony_ci /* 9558c2ecf20Sopenharmony_ci * FIXME: Reverse engineering showed 0, IGNORE_CASE but 9568c2ecf20Sopenharmony_ci * that is inconsistent with ntfs_attr_find(). The 9578c2ecf20Sopenharmony_ci * subsequent rc checks were also different. Perhaps I 9588c2ecf20Sopenharmony_ci * made a mistake in one of the two. Need to recheck 9598c2ecf20Sopenharmony_ci * which is correct or at least see what is going on... 9608c2ecf20Sopenharmony_ci * (AIA) 9618c2ecf20Sopenharmony_ci */ 9628c2ecf20Sopenharmony_ci rc = ntfs_collate_names(name, name_len, al_name, 9638c2ecf20Sopenharmony_ci al_name_len, 1, CASE_SENSITIVE, 9648c2ecf20Sopenharmony_ci vol->upcase, vol->upcase_len); 9658c2ecf20Sopenharmony_ci if (rc == -1) 9668c2ecf20Sopenharmony_ci goto not_found; 9678c2ecf20Sopenharmony_ci if (rc) 9688c2ecf20Sopenharmony_ci continue; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci /* 9718c2ecf20Sopenharmony_ci * The names match or @name not present and attribute is 9728c2ecf20Sopenharmony_ci * unnamed. Now check @lowest_vcn. Continue search if the 9738c2ecf20Sopenharmony_ci * next attribute list entry still fits @lowest_vcn. Otherwise 9748c2ecf20Sopenharmony_ci * we have reached the right one or the search has failed. 9758c2ecf20Sopenharmony_ci */ 9768c2ecf20Sopenharmony_ci if (lowest_vcn && (u8*)next_al_entry >= al_start && 9778c2ecf20Sopenharmony_ci (u8*)next_al_entry + 6 < al_end && 9788c2ecf20Sopenharmony_ci (u8*)next_al_entry + le16_to_cpu( 9798c2ecf20Sopenharmony_ci next_al_entry->length) <= al_end && 9808c2ecf20Sopenharmony_ci sle64_to_cpu(next_al_entry->lowest_vcn) <= 9818c2ecf20Sopenharmony_ci lowest_vcn && 9828c2ecf20Sopenharmony_ci next_al_entry->type == al_entry->type && 9838c2ecf20Sopenharmony_ci next_al_entry->name_length == al_name_len && 9848c2ecf20Sopenharmony_ci ntfs_are_names_equal((ntfschar*)((u8*) 9858c2ecf20Sopenharmony_ci next_al_entry + 9868c2ecf20Sopenharmony_ci next_al_entry->name_offset), 9878c2ecf20Sopenharmony_ci next_al_entry->name_length, 9888c2ecf20Sopenharmony_ci al_name, al_name_len, CASE_SENSITIVE, 9898c2ecf20Sopenharmony_ci vol->upcase, vol->upcase_len)) 9908c2ecf20Sopenharmony_ci continue; 9918c2ecf20Sopenharmony_ci if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { 9928c2ecf20Sopenharmony_ci if (MSEQNO_LE(al_entry->mft_reference) != ni->seq_no) { 9938c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Found stale mft " 9948c2ecf20Sopenharmony_ci "reference in attribute list " 9958c2ecf20Sopenharmony_ci "of base inode 0x%lx.%s", 9968c2ecf20Sopenharmony_ci base_ni->mft_no, es); 9978c2ecf20Sopenharmony_ci err = -EIO; 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci } else { /* Mft references do not match. */ 10018c2ecf20Sopenharmony_ci /* If there is a mapped record unmap it first. */ 10028c2ecf20Sopenharmony_ci if (ni != base_ni) 10038c2ecf20Sopenharmony_ci unmap_extent_mft_record(ni); 10048c2ecf20Sopenharmony_ci /* Do we want the base record back? */ 10058c2ecf20Sopenharmony_ci if (MREF_LE(al_entry->mft_reference) == 10068c2ecf20Sopenharmony_ci base_ni->mft_no) { 10078c2ecf20Sopenharmony_ci ni = ctx->ntfs_ino = base_ni; 10088c2ecf20Sopenharmony_ci ctx->mrec = ctx->base_mrec; 10098c2ecf20Sopenharmony_ci } else { 10108c2ecf20Sopenharmony_ci /* We want an extent record. */ 10118c2ecf20Sopenharmony_ci ctx->mrec = map_extent_mft_record(base_ni, 10128c2ecf20Sopenharmony_ci le64_to_cpu( 10138c2ecf20Sopenharmony_ci al_entry->mft_reference), &ni); 10148c2ecf20Sopenharmony_ci if (IS_ERR(ctx->mrec)) { 10158c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to map " 10168c2ecf20Sopenharmony_ci "extent mft record " 10178c2ecf20Sopenharmony_ci "0x%lx of base inode " 10188c2ecf20Sopenharmony_ci "0x%lx.%s", 10198c2ecf20Sopenharmony_ci MREF_LE(al_entry-> 10208c2ecf20Sopenharmony_ci mft_reference), 10218c2ecf20Sopenharmony_ci base_ni->mft_no, es); 10228c2ecf20Sopenharmony_ci err = PTR_ERR(ctx->mrec); 10238c2ecf20Sopenharmony_ci if (err == -ENOENT) 10248c2ecf20Sopenharmony_ci err = -EIO; 10258c2ecf20Sopenharmony_ci /* Cause @ctx to be sanitized below. */ 10268c2ecf20Sopenharmony_ci ni = NULL; 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci ctx->ntfs_ino = ni; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + 10328c2ecf20Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci /* 10358c2ecf20Sopenharmony_ci * ctx->vfs_ino, ctx->mrec, and ctx->attr now point to the 10368c2ecf20Sopenharmony_ci * mft record containing the attribute represented by the 10378c2ecf20Sopenharmony_ci * current al_entry. 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci /* 10408c2ecf20Sopenharmony_ci * We could call into ntfs_attr_find() to find the right 10418c2ecf20Sopenharmony_ci * attribute in this mft record but this would be less 10428c2ecf20Sopenharmony_ci * efficient and not quite accurate as ntfs_attr_find() ignores 10438c2ecf20Sopenharmony_ci * the attribute instance numbers for example which become 10448c2ecf20Sopenharmony_ci * important when one plays with attribute lists. Also, 10458c2ecf20Sopenharmony_ci * because a proper match has been found in the attribute list 10468c2ecf20Sopenharmony_ci * entry above, the comparison can now be optimized. So it is 10478c2ecf20Sopenharmony_ci * worth re-implementing a simplified ntfs_attr_find() here. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci a = ctx->attr; 10508c2ecf20Sopenharmony_ci /* 10518c2ecf20Sopenharmony_ci * Use a manual loop so we can still use break and continue 10528c2ecf20Sopenharmony_ci * with the same meanings as above. 10538c2ecf20Sopenharmony_ci */ 10548c2ecf20Sopenharmony_cido_next_attr_loop: 10558c2ecf20Sopenharmony_ci if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec + 10568c2ecf20Sopenharmony_ci le32_to_cpu(ctx->mrec->bytes_allocated)) 10578c2ecf20Sopenharmony_ci break; 10588c2ecf20Sopenharmony_ci if (a->type == AT_END) 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci if (!a->length) 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci if (al_entry->instance != a->instance) 10638c2ecf20Sopenharmony_ci goto do_next_attr; 10648c2ecf20Sopenharmony_ci /* 10658c2ecf20Sopenharmony_ci * If the type and/or the name are mismatched between the 10668c2ecf20Sopenharmony_ci * attribute list entry and the attribute record, there is 10678c2ecf20Sopenharmony_ci * corruption so we break and return error EIO. 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_ci if (al_entry->type != a->type) 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci if (!ntfs_are_names_equal((ntfschar*)((u8*)a + 10728c2ecf20Sopenharmony_ci le16_to_cpu(a->name_offset)), a->name_length, 10738c2ecf20Sopenharmony_ci al_name, al_name_len, CASE_SENSITIVE, 10748c2ecf20Sopenharmony_ci vol->upcase, vol->upcase_len)) 10758c2ecf20Sopenharmony_ci break; 10768c2ecf20Sopenharmony_ci ctx->attr = a; 10778c2ecf20Sopenharmony_ci /* 10788c2ecf20Sopenharmony_ci * If no @val specified or @val specified and it matches, we 10798c2ecf20Sopenharmony_ci * have found it! 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci if (!val || (!a->non_resident && le32_to_cpu( 10828c2ecf20Sopenharmony_ci a->data.resident.value_length) == val_len && 10838c2ecf20Sopenharmony_ci !memcmp((u8*)a + 10848c2ecf20Sopenharmony_ci le16_to_cpu(a->data.resident.value_offset), 10858c2ecf20Sopenharmony_ci val, val_len))) { 10868c2ecf20Sopenharmony_ci ntfs_debug("Done, found."); 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_cido_next_attr: 10908c2ecf20Sopenharmony_ci /* Proceed to the next attribute in the current mft record. */ 10918c2ecf20Sopenharmony_ci a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length)); 10928c2ecf20Sopenharmony_ci goto do_next_attr_loop; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci if (!err) { 10958c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Base inode 0x%lx contains corrupt " 10968c2ecf20Sopenharmony_ci "attribute list attribute.%s", base_ni->mft_no, 10978c2ecf20Sopenharmony_ci es); 10988c2ecf20Sopenharmony_ci err = -EIO; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci if (ni != base_ni) { 11018c2ecf20Sopenharmony_ci if (ni) 11028c2ecf20Sopenharmony_ci unmap_extent_mft_record(ni); 11038c2ecf20Sopenharmony_ci ctx->ntfs_ino = base_ni; 11048c2ecf20Sopenharmony_ci ctx->mrec = ctx->base_mrec; 11058c2ecf20Sopenharmony_ci ctx->attr = ctx->base_attr; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci if (err != -ENOMEM) 11088c2ecf20Sopenharmony_ci NVolSetErrors(vol); 11098c2ecf20Sopenharmony_ci return err; 11108c2ecf20Sopenharmony_cinot_found: 11118c2ecf20Sopenharmony_ci /* 11128c2ecf20Sopenharmony_ci * If we were looking for AT_END, we reset the search context @ctx and 11138c2ecf20Sopenharmony_ci * use ntfs_attr_find() to seek to the end of the base mft record. 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_ci if (type == AT_END) { 11168c2ecf20Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 11178c2ecf20Sopenharmony_ci return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, 11188c2ecf20Sopenharmony_ci ctx); 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci /* 11218c2ecf20Sopenharmony_ci * The attribute was not found. Before we return, we want to ensure 11228c2ecf20Sopenharmony_ci * @ctx->mrec and @ctx->attr indicate the position at which the 11238c2ecf20Sopenharmony_ci * attribute should be inserted in the base mft record. Since we also 11248c2ecf20Sopenharmony_ci * want to preserve @ctx->al_entry we cannot reinitialize the search 11258c2ecf20Sopenharmony_ci * context using ntfs_attr_reinit_search_ctx() as this would set 11268c2ecf20Sopenharmony_ci * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see 11278c2ecf20Sopenharmony_ci * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve 11288c2ecf20Sopenharmony_ci * @ctx->al_entry as the remaining fields (base_*) are identical to 11298c2ecf20Sopenharmony_ci * their non base_ counterparts and we cannot set @ctx->base_attr 11308c2ecf20Sopenharmony_ci * correctly yet as we do not know what @ctx->attr will be set to by 11318c2ecf20Sopenharmony_ci * the call to ntfs_attr_find() below. 11328c2ecf20Sopenharmony_ci */ 11338c2ecf20Sopenharmony_ci if (ni != base_ni) 11348c2ecf20Sopenharmony_ci unmap_extent_mft_record(ni); 11358c2ecf20Sopenharmony_ci ctx->mrec = ctx->base_mrec; 11368c2ecf20Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + 11378c2ecf20Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 11388c2ecf20Sopenharmony_ci ctx->is_first = true; 11398c2ecf20Sopenharmony_ci ctx->ntfs_ino = base_ni; 11408c2ecf20Sopenharmony_ci ctx->base_ntfs_ino = NULL; 11418c2ecf20Sopenharmony_ci ctx->base_mrec = NULL; 11428c2ecf20Sopenharmony_ci ctx->base_attr = NULL; 11438c2ecf20Sopenharmony_ci /* 11448c2ecf20Sopenharmony_ci * In case there are multiple matches in the base mft record, need to 11458c2ecf20Sopenharmony_ci * keep enumerating until we get an attribute not found response (or 11468c2ecf20Sopenharmony_ci * another error), otherwise we would keep returning the same attribute 11478c2ecf20Sopenharmony_ci * over and over again and all programs using us for enumeration would 11488c2ecf20Sopenharmony_ci * lock up in a tight loop. 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_ci do { 11518c2ecf20Sopenharmony_ci err = ntfs_attr_find(type, name, name_len, ic, val, val_len, 11528c2ecf20Sopenharmony_ci ctx); 11538c2ecf20Sopenharmony_ci } while (!err); 11548c2ecf20Sopenharmony_ci ntfs_debug("Done, not found."); 11558c2ecf20Sopenharmony_ci return err; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/** 11598c2ecf20Sopenharmony_ci * ntfs_attr_lookup - find an attribute in an ntfs inode 11608c2ecf20Sopenharmony_ci * @type: attribute type to find 11618c2ecf20Sopenharmony_ci * @name: attribute name to find (optional, i.e. NULL means don't care) 11628c2ecf20Sopenharmony_ci * @name_len: attribute name length (only needed if @name present) 11638c2ecf20Sopenharmony_ci * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) 11648c2ecf20Sopenharmony_ci * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) 11658c2ecf20Sopenharmony_ci * @val: attribute value to find (optional, resident attributes only) 11668c2ecf20Sopenharmony_ci * @val_len: attribute value length 11678c2ecf20Sopenharmony_ci * @ctx: search context with mft record and attribute to search from 11688c2ecf20Sopenharmony_ci * 11698c2ecf20Sopenharmony_ci * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must 11708c2ecf20Sopenharmony_ci * be the base mft record and @ctx must have been obtained from a call to 11718c2ecf20Sopenharmony_ci * ntfs_attr_get_search_ctx(). 11728c2ecf20Sopenharmony_ci * 11738c2ecf20Sopenharmony_ci * This function transparently handles attribute lists and @ctx is used to 11748c2ecf20Sopenharmony_ci * continue searches where they were left off at. 11758c2ecf20Sopenharmony_ci * 11768c2ecf20Sopenharmony_ci * After finishing with the attribute/mft record you need to call 11778c2ecf20Sopenharmony_ci * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any 11788c2ecf20Sopenharmony_ci * mapped inodes, etc). 11798c2ecf20Sopenharmony_ci * 11808c2ecf20Sopenharmony_ci * Return 0 if the search was successful and -errno if not. 11818c2ecf20Sopenharmony_ci * 11828c2ecf20Sopenharmony_ci * When 0, @ctx->attr is the found attribute and it is in mft record 11838c2ecf20Sopenharmony_ci * @ctx->mrec. If an attribute list attribute is present, @ctx->al_entry is 11848c2ecf20Sopenharmony_ci * the attribute list entry of the found attribute. 11858c2ecf20Sopenharmony_ci * 11868c2ecf20Sopenharmony_ci * When -ENOENT, @ctx->attr is the attribute which collates just after the 11878c2ecf20Sopenharmony_ci * attribute being searched for, i.e. if one wants to add the attribute to the 11888c2ecf20Sopenharmony_ci * mft record this is the correct place to insert it into. If an attribute 11898c2ecf20Sopenharmony_ci * list attribute is present, @ctx->al_entry is the attribute list entry which 11908c2ecf20Sopenharmony_ci * collates just after the attribute list entry of the attribute being searched 11918c2ecf20Sopenharmony_ci * for, i.e. if one wants to add the attribute to the mft record this is the 11928c2ecf20Sopenharmony_ci * correct place to insert its attribute list entry into. 11938c2ecf20Sopenharmony_ci * 11948c2ecf20Sopenharmony_ci * When -errno != -ENOENT, an error occurred during the lookup. @ctx->attr is 11958c2ecf20Sopenharmony_ci * then undefined and in particular you should not rely on it not changing. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ciint ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name, 11988c2ecf20Sopenharmony_ci const u32 name_len, const IGNORE_CASE_BOOL ic, 11998c2ecf20Sopenharmony_ci const VCN lowest_vcn, const u8 *val, const u32 val_len, 12008c2ecf20Sopenharmony_ci ntfs_attr_search_ctx *ctx) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci ntfs_inode *base_ni; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci ntfs_debug("Entering."); 12058c2ecf20Sopenharmony_ci BUG_ON(IS_ERR(ctx->mrec)); 12068c2ecf20Sopenharmony_ci if (ctx->base_ntfs_ino) 12078c2ecf20Sopenharmony_ci base_ni = ctx->base_ntfs_ino; 12088c2ecf20Sopenharmony_ci else 12098c2ecf20Sopenharmony_ci base_ni = ctx->ntfs_ino; 12108c2ecf20Sopenharmony_ci /* Sanity check, just for debugging really. */ 12118c2ecf20Sopenharmony_ci BUG_ON(!base_ni); 12128c2ecf20Sopenharmony_ci if (!NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) 12138c2ecf20Sopenharmony_ci return ntfs_attr_find(type, name, name_len, ic, val, val_len, 12148c2ecf20Sopenharmony_ci ctx); 12158c2ecf20Sopenharmony_ci return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn, 12168c2ecf20Sopenharmony_ci val, val_len, ctx); 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci/** 12208c2ecf20Sopenharmony_ci * ntfs_attr_init_search_ctx - initialize an attribute search context 12218c2ecf20Sopenharmony_ci * @ctx: attribute search context to initialize 12228c2ecf20Sopenharmony_ci * @ni: ntfs inode with which to initialize the search context 12238c2ecf20Sopenharmony_ci * @mrec: mft record with which to initialize the search context 12248c2ecf20Sopenharmony_ci * 12258c2ecf20Sopenharmony_ci * Initialize the attribute search context @ctx with @ni and @mrec. 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_cistatic inline void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, 12288c2ecf20Sopenharmony_ci ntfs_inode *ni, MFT_RECORD *mrec) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci *ctx = (ntfs_attr_search_ctx) { 12318c2ecf20Sopenharmony_ci .mrec = mrec, 12328c2ecf20Sopenharmony_ci /* Sanity checks are performed elsewhere. */ 12338c2ecf20Sopenharmony_ci .attr = (ATTR_RECORD*)((u8*)mrec + 12348c2ecf20Sopenharmony_ci le16_to_cpu(mrec->attrs_offset)), 12358c2ecf20Sopenharmony_ci .is_first = true, 12368c2ecf20Sopenharmony_ci .ntfs_ino = ni, 12378c2ecf20Sopenharmony_ci }; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci/** 12418c2ecf20Sopenharmony_ci * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context 12428c2ecf20Sopenharmony_ci * @ctx: attribute search context to reinitialize 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * Reinitialize the attribute search context @ctx, unmapping an associated 12458c2ecf20Sopenharmony_ci * extent mft record if present, and initialize the search context again. 12468c2ecf20Sopenharmony_ci * 12478c2ecf20Sopenharmony_ci * This is used when a search for a new attribute is being started to reset 12488c2ecf20Sopenharmony_ci * the search context to the beginning. 12498c2ecf20Sopenharmony_ci */ 12508c2ecf20Sopenharmony_civoid ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci if (likely(!ctx->base_ntfs_ino)) { 12538c2ecf20Sopenharmony_ci /* No attribute list. */ 12548c2ecf20Sopenharmony_ci ctx->is_first = true; 12558c2ecf20Sopenharmony_ci /* Sanity checks are performed elsewhere. */ 12568c2ecf20Sopenharmony_ci ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + 12578c2ecf20Sopenharmony_ci le16_to_cpu(ctx->mrec->attrs_offset)); 12588c2ecf20Sopenharmony_ci /* 12598c2ecf20Sopenharmony_ci * This needs resetting due to ntfs_external_attr_find() which 12608c2ecf20Sopenharmony_ci * can leave it set despite having zeroed ctx->base_ntfs_ino. 12618c2ecf20Sopenharmony_ci */ 12628c2ecf20Sopenharmony_ci ctx->al_entry = NULL; 12638c2ecf20Sopenharmony_ci return; 12648c2ecf20Sopenharmony_ci } /* Attribute list. */ 12658c2ecf20Sopenharmony_ci if (ctx->ntfs_ino != ctx->base_ntfs_ino) 12668c2ecf20Sopenharmony_ci unmap_extent_mft_record(ctx->ntfs_ino); 12678c2ecf20Sopenharmony_ci ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); 12688c2ecf20Sopenharmony_ci return; 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci/** 12728c2ecf20Sopenharmony_ci * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context 12738c2ecf20Sopenharmony_ci * @ni: ntfs inode with which to initialize the search context 12748c2ecf20Sopenharmony_ci * @mrec: mft record with which to initialize the search context 12758c2ecf20Sopenharmony_ci * 12768c2ecf20Sopenharmony_ci * Allocate a new attribute search context, initialize it with @ni and @mrec, 12778c2ecf20Sopenharmony_ci * and return it. Return NULL if allocation failed. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_cintfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci ntfs_attr_search_ctx *ctx; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci ctx = kmem_cache_alloc(ntfs_attr_ctx_cache, GFP_NOFS); 12848c2ecf20Sopenharmony_ci if (ctx) 12858c2ecf20Sopenharmony_ci ntfs_attr_init_search_ctx(ctx, ni, mrec); 12868c2ecf20Sopenharmony_ci return ctx; 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci/** 12908c2ecf20Sopenharmony_ci * ntfs_attr_put_search_ctx - release an attribute search context 12918c2ecf20Sopenharmony_ci * @ctx: attribute search context to free 12928c2ecf20Sopenharmony_ci * 12938c2ecf20Sopenharmony_ci * Release the attribute search context @ctx, unmapping an associated extent 12948c2ecf20Sopenharmony_ci * mft record if present. 12958c2ecf20Sopenharmony_ci */ 12968c2ecf20Sopenharmony_civoid ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci if (ctx->base_ntfs_ino && ctx->ntfs_ino != ctx->base_ntfs_ino) 12998c2ecf20Sopenharmony_ci unmap_extent_mft_record(ctx->ntfs_ino); 13008c2ecf20Sopenharmony_ci kmem_cache_free(ntfs_attr_ctx_cache, ctx); 13018c2ecf20Sopenharmony_ci return; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci#ifdef NTFS_RW 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci/** 13078c2ecf20Sopenharmony_ci * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file 13088c2ecf20Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 13098c2ecf20Sopenharmony_ci * @type: attribute type which to find 13108c2ecf20Sopenharmony_ci * 13118c2ecf20Sopenharmony_ci * Search for the attribute definition record corresponding to the attribute 13128c2ecf20Sopenharmony_ci * @type in the $AttrDef system file. 13138c2ecf20Sopenharmony_ci * 13148c2ecf20Sopenharmony_ci * Return the attribute type definition record if found and NULL if not found. 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_cistatic ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, 13178c2ecf20Sopenharmony_ci const ATTR_TYPE type) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci ATTR_DEF *ad; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci BUG_ON(!vol->attrdef); 13228c2ecf20Sopenharmony_ci BUG_ON(!type); 13238c2ecf20Sopenharmony_ci for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < 13248c2ecf20Sopenharmony_ci vol->attrdef_size && ad->type; ++ad) { 13258c2ecf20Sopenharmony_ci /* We have not found it yet, carry on searching. */ 13268c2ecf20Sopenharmony_ci if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type))) 13278c2ecf20Sopenharmony_ci continue; 13288c2ecf20Sopenharmony_ci /* We found the attribute; return it. */ 13298c2ecf20Sopenharmony_ci if (likely(ad->type == type)) 13308c2ecf20Sopenharmony_ci return ad; 13318c2ecf20Sopenharmony_ci /* We have gone too far already. No point in continuing. */ 13328c2ecf20Sopenharmony_ci break; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci /* Attribute not found. */ 13358c2ecf20Sopenharmony_ci ntfs_debug("Attribute type 0x%x not found in $AttrDef.", 13368c2ecf20Sopenharmony_ci le32_to_cpu(type)); 13378c2ecf20Sopenharmony_ci return NULL; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci/** 13418c2ecf20Sopenharmony_ci * ntfs_attr_size_bounds_check - check a size of an attribute type for validity 13428c2ecf20Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 13438c2ecf20Sopenharmony_ci * @type: attribute type which to check 13448c2ecf20Sopenharmony_ci * @size: size which to check 13458c2ecf20Sopenharmony_ci * 13468c2ecf20Sopenharmony_ci * Check whether the @size in bytes is valid for an attribute of @type on the 13478c2ecf20Sopenharmony_ci * ntfs volume @vol. This information is obtained from $AttrDef system file. 13488c2ecf20Sopenharmony_ci * 13498c2ecf20Sopenharmony_ci * Return 0 if valid, -ERANGE if not valid, or -ENOENT if the attribute is not 13508c2ecf20Sopenharmony_ci * listed in $AttrDef. 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_ciint ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type, 13538c2ecf20Sopenharmony_ci const s64 size) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci ATTR_DEF *ad; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci BUG_ON(size < 0); 13588c2ecf20Sopenharmony_ci /* 13598c2ecf20Sopenharmony_ci * $ATTRIBUTE_LIST has a maximum size of 256kiB, but this is not 13608c2ecf20Sopenharmony_ci * listed in $AttrDef. 13618c2ecf20Sopenharmony_ci */ 13628c2ecf20Sopenharmony_ci if (unlikely(type == AT_ATTRIBUTE_LIST && size > 256 * 1024)) 13638c2ecf20Sopenharmony_ci return -ERANGE; 13648c2ecf20Sopenharmony_ci /* Get the $AttrDef entry for the attribute @type. */ 13658c2ecf20Sopenharmony_ci ad = ntfs_attr_find_in_attrdef(vol, type); 13668c2ecf20Sopenharmony_ci if (unlikely(!ad)) 13678c2ecf20Sopenharmony_ci return -ENOENT; 13688c2ecf20Sopenharmony_ci /* Do the bounds check. */ 13698c2ecf20Sopenharmony_ci if (((sle64_to_cpu(ad->min_size) > 0) && 13708c2ecf20Sopenharmony_ci size < sle64_to_cpu(ad->min_size)) || 13718c2ecf20Sopenharmony_ci ((sle64_to_cpu(ad->max_size) > 0) && size > 13728c2ecf20Sopenharmony_ci sle64_to_cpu(ad->max_size))) 13738c2ecf20Sopenharmony_ci return -ERANGE; 13748c2ecf20Sopenharmony_ci return 0; 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci/** 13788c2ecf20Sopenharmony_ci * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident 13798c2ecf20Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 13808c2ecf20Sopenharmony_ci * @type: attribute type which to check 13818c2ecf20Sopenharmony_ci * 13828c2ecf20Sopenharmony_ci * Check whether the attribute of @type on the ntfs volume @vol is allowed to 13838c2ecf20Sopenharmony_ci * be non-resident. This information is obtained from $AttrDef system file. 13848c2ecf20Sopenharmony_ci * 13858c2ecf20Sopenharmony_ci * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, and 13868c2ecf20Sopenharmony_ci * -ENOENT if the attribute is not listed in $AttrDef. 13878c2ecf20Sopenharmony_ci */ 13888c2ecf20Sopenharmony_ciint ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci ATTR_DEF *ad; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* Find the attribute definition record in $AttrDef. */ 13938c2ecf20Sopenharmony_ci ad = ntfs_attr_find_in_attrdef(vol, type); 13948c2ecf20Sopenharmony_ci if (unlikely(!ad)) 13958c2ecf20Sopenharmony_ci return -ENOENT; 13968c2ecf20Sopenharmony_ci /* Check the flags and return the result. */ 13978c2ecf20Sopenharmony_ci if (ad->flags & ATTR_DEF_RESIDENT) 13988c2ecf20Sopenharmony_ci return -EPERM; 13998c2ecf20Sopenharmony_ci return 0; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci/** 14038c2ecf20Sopenharmony_ci * ntfs_attr_can_be_resident - check if an attribute can be resident 14048c2ecf20Sopenharmony_ci * @vol: ntfs volume to which the attribute belongs 14058c2ecf20Sopenharmony_ci * @type: attribute type which to check 14068c2ecf20Sopenharmony_ci * 14078c2ecf20Sopenharmony_ci * Check whether the attribute of @type on the ntfs volume @vol is allowed to 14088c2ecf20Sopenharmony_ci * be resident. This information is derived from our ntfs knowledge and may 14098c2ecf20Sopenharmony_ci * not be completely accurate, especially when user defined attributes are 14108c2ecf20Sopenharmony_ci * present. Basically we allow everything to be resident except for index 14118c2ecf20Sopenharmony_ci * allocation and $EA attributes. 14128c2ecf20Sopenharmony_ci * 14138c2ecf20Sopenharmony_ci * Return 0 if the attribute is allowed to be non-resident and -EPERM if not. 14148c2ecf20Sopenharmony_ci * 14158c2ecf20Sopenharmony_ci * Warning: In the system file $MFT the attribute $Bitmap must be non-resident 14168c2ecf20Sopenharmony_ci * otherwise windows will not boot (blue screen of death)! We cannot 14178c2ecf20Sopenharmony_ci * check for this here as we do not know which inode's $Bitmap is 14188c2ecf20Sopenharmony_ci * being asked about so the caller needs to special case this. 14198c2ecf20Sopenharmony_ci */ 14208c2ecf20Sopenharmony_ciint ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci if (type == AT_INDEX_ALLOCATION) 14238c2ecf20Sopenharmony_ci return -EPERM; 14248c2ecf20Sopenharmony_ci return 0; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci/** 14288c2ecf20Sopenharmony_ci * ntfs_attr_record_resize - resize an attribute record 14298c2ecf20Sopenharmony_ci * @m: mft record containing attribute record 14308c2ecf20Sopenharmony_ci * @a: attribute record to resize 14318c2ecf20Sopenharmony_ci * @new_size: new size in bytes to which to resize the attribute record @a 14328c2ecf20Sopenharmony_ci * 14338c2ecf20Sopenharmony_ci * Resize the attribute record @a, i.e. the resident part of the attribute, in 14348c2ecf20Sopenharmony_ci * the mft record @m to @new_size bytes. 14358c2ecf20Sopenharmony_ci * 14368c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. The following error codes are 14378c2ecf20Sopenharmony_ci * defined: 14388c2ecf20Sopenharmony_ci * -ENOSPC - Not enough space in the mft record @m to perform the resize. 14398c2ecf20Sopenharmony_ci * 14408c2ecf20Sopenharmony_ci * Note: On error, no modifications have been performed whatsoever. 14418c2ecf20Sopenharmony_ci * 14428c2ecf20Sopenharmony_ci * Warning: If you make a record smaller without having copied all the data you 14438c2ecf20Sopenharmony_ci * are interested in the data may be overwritten. 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_ciint ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) 14468c2ecf20Sopenharmony_ci{ 14478c2ecf20Sopenharmony_ci ntfs_debug("Entering for new_size %u.", new_size); 14488c2ecf20Sopenharmony_ci /* Align to 8 bytes if it is not already done. */ 14498c2ecf20Sopenharmony_ci if (new_size & 7) 14508c2ecf20Sopenharmony_ci new_size = (new_size + 7) & ~7; 14518c2ecf20Sopenharmony_ci /* If the actual attribute length has changed, move things around. */ 14528c2ecf20Sopenharmony_ci if (new_size != le32_to_cpu(a->length)) { 14538c2ecf20Sopenharmony_ci u32 new_muse = le32_to_cpu(m->bytes_in_use) - 14548c2ecf20Sopenharmony_ci le32_to_cpu(a->length) + new_size; 14558c2ecf20Sopenharmony_ci /* Not enough space in this mft record. */ 14568c2ecf20Sopenharmony_ci if (new_muse > le32_to_cpu(m->bytes_allocated)) 14578c2ecf20Sopenharmony_ci return -ENOSPC; 14588c2ecf20Sopenharmony_ci /* Move attributes following @a to their new location. */ 14598c2ecf20Sopenharmony_ci memmove((u8*)a + new_size, (u8*)a + le32_to_cpu(a->length), 14608c2ecf20Sopenharmony_ci le32_to_cpu(m->bytes_in_use) - ((u8*)a - 14618c2ecf20Sopenharmony_ci (u8*)m) - le32_to_cpu(a->length)); 14628c2ecf20Sopenharmony_ci /* Adjust @m to reflect the change in used space. */ 14638c2ecf20Sopenharmony_ci m->bytes_in_use = cpu_to_le32(new_muse); 14648c2ecf20Sopenharmony_ci /* Adjust @a to reflect the new size. */ 14658c2ecf20Sopenharmony_ci if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) 14668c2ecf20Sopenharmony_ci a->length = cpu_to_le32(new_size); 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci return 0; 14698c2ecf20Sopenharmony_ci} 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci/** 14728c2ecf20Sopenharmony_ci * ntfs_resident_attr_value_resize - resize the value of a resident attribute 14738c2ecf20Sopenharmony_ci * @m: mft record containing attribute record 14748c2ecf20Sopenharmony_ci * @a: attribute record whose value to resize 14758c2ecf20Sopenharmony_ci * @new_size: new size in bytes to which to resize the attribute value of @a 14768c2ecf20Sopenharmony_ci * 14778c2ecf20Sopenharmony_ci * Resize the value of the attribute @a in the mft record @m to @new_size bytes. 14788c2ecf20Sopenharmony_ci * If the value is made bigger, the newly allocated space is cleared. 14798c2ecf20Sopenharmony_ci * 14808c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. The following error codes are 14818c2ecf20Sopenharmony_ci * defined: 14828c2ecf20Sopenharmony_ci * -ENOSPC - Not enough space in the mft record @m to perform the resize. 14838c2ecf20Sopenharmony_ci * 14848c2ecf20Sopenharmony_ci * Note: On error, no modifications have been performed whatsoever. 14858c2ecf20Sopenharmony_ci * 14868c2ecf20Sopenharmony_ci * Warning: If you make a record smaller without having copied all the data you 14878c2ecf20Sopenharmony_ci * are interested in the data may be overwritten. 14888c2ecf20Sopenharmony_ci */ 14898c2ecf20Sopenharmony_ciint ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, 14908c2ecf20Sopenharmony_ci const u32 new_size) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci u32 old_size; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* Resize the resident part of the attribute record. */ 14958c2ecf20Sopenharmony_ci if (ntfs_attr_record_resize(m, a, 14968c2ecf20Sopenharmony_ci le16_to_cpu(a->data.resident.value_offset) + new_size)) 14978c2ecf20Sopenharmony_ci return -ENOSPC; 14988c2ecf20Sopenharmony_ci /* 14998c2ecf20Sopenharmony_ci * The resize succeeded! If we made the attribute value bigger, clear 15008c2ecf20Sopenharmony_ci * the area between the old size and @new_size. 15018c2ecf20Sopenharmony_ci */ 15028c2ecf20Sopenharmony_ci old_size = le32_to_cpu(a->data.resident.value_length); 15038c2ecf20Sopenharmony_ci if (new_size > old_size) 15048c2ecf20Sopenharmony_ci memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) + 15058c2ecf20Sopenharmony_ci old_size, 0, new_size - old_size); 15068c2ecf20Sopenharmony_ci /* Finally update the length of the attribute value. */ 15078c2ecf20Sopenharmony_ci a->data.resident.value_length = cpu_to_le32(new_size); 15088c2ecf20Sopenharmony_ci return 0; 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci/** 15128c2ecf20Sopenharmony_ci * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute 15138c2ecf20Sopenharmony_ci * @ni: ntfs inode describing the attribute to convert 15148c2ecf20Sopenharmony_ci * @data_size: size of the resident data to copy to the non-resident attribute 15158c2ecf20Sopenharmony_ci * 15168c2ecf20Sopenharmony_ci * Convert the resident ntfs attribute described by the ntfs inode @ni to a 15178c2ecf20Sopenharmony_ci * non-resident one. 15188c2ecf20Sopenharmony_ci * 15198c2ecf20Sopenharmony_ci * @data_size must be equal to the attribute value size. This is needed since 15208c2ecf20Sopenharmony_ci * we need to know the size before we can map the mft record and our callers 15218c2ecf20Sopenharmony_ci * always know it. The reason we cannot simply read the size from the vfs 15228c2ecf20Sopenharmony_ci * inode i_size is that this is not necessarily uptodate. This happens when 15238c2ecf20Sopenharmony_ci * ntfs_attr_make_non_resident() is called in the ->truncate call path(s). 15248c2ecf20Sopenharmony_ci * 15258c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. The following error return codes 15268c2ecf20Sopenharmony_ci * are defined: 15278c2ecf20Sopenharmony_ci * -EPERM - The attribute is not allowed to be non-resident. 15288c2ecf20Sopenharmony_ci * -ENOMEM - Not enough memory. 15298c2ecf20Sopenharmony_ci * -ENOSPC - Not enough disk space. 15308c2ecf20Sopenharmony_ci * -EINVAL - Attribute not defined on the volume. 15318c2ecf20Sopenharmony_ci * -EIO - I/o error or other error. 15328c2ecf20Sopenharmony_ci * Note that -ENOSPC is also returned in the case that there is not enough 15338c2ecf20Sopenharmony_ci * space in the mft record to do the conversion. This can happen when the mft 15348c2ecf20Sopenharmony_ci * record is already very full. The caller is responsible for trying to make 15358c2ecf20Sopenharmony_ci * space in the mft record and trying again. FIXME: Do we need a separate 15368c2ecf20Sopenharmony_ci * error return code for this kind of -ENOSPC or is it always worth trying 15378c2ecf20Sopenharmony_ci * again in case the attribute may then fit in a resident state so no need to 15388c2ecf20Sopenharmony_ci * make it non-resident at all? Ho-hum... (AIA) 15398c2ecf20Sopenharmony_ci * 15408c2ecf20Sopenharmony_ci * NOTE to self: No changes in the attribute list are required to move from 15418c2ecf20Sopenharmony_ci * a resident to a non-resident attribute. 15428c2ecf20Sopenharmony_ci * 15438c2ecf20Sopenharmony_ci * Locking: - The caller must hold i_mutex on the inode. 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_ciint ntfs_attr_make_non_resident(ntfs_inode *ni, const u32 data_size) 15468c2ecf20Sopenharmony_ci{ 15478c2ecf20Sopenharmony_ci s64 new_size; 15488c2ecf20Sopenharmony_ci struct inode *vi = VFS_I(ni); 15498c2ecf20Sopenharmony_ci ntfs_volume *vol = ni->vol; 15508c2ecf20Sopenharmony_ci ntfs_inode *base_ni; 15518c2ecf20Sopenharmony_ci MFT_RECORD *m; 15528c2ecf20Sopenharmony_ci ATTR_RECORD *a; 15538c2ecf20Sopenharmony_ci ntfs_attr_search_ctx *ctx; 15548c2ecf20Sopenharmony_ci struct page *page; 15558c2ecf20Sopenharmony_ci runlist_element *rl; 15568c2ecf20Sopenharmony_ci u8 *kaddr; 15578c2ecf20Sopenharmony_ci unsigned long flags; 15588c2ecf20Sopenharmony_ci int mp_size, mp_ofs, name_ofs, arec_size, err, err2; 15598c2ecf20Sopenharmony_ci u32 attr_size; 15608c2ecf20Sopenharmony_ci u8 old_res_attr_flags; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* Check that the attribute is allowed to be non-resident. */ 15638c2ecf20Sopenharmony_ci err = ntfs_attr_can_be_non_resident(vol, ni->type); 15648c2ecf20Sopenharmony_ci if (unlikely(err)) { 15658c2ecf20Sopenharmony_ci if (err == -EPERM) 15668c2ecf20Sopenharmony_ci ntfs_debug("Attribute is not allowed to be " 15678c2ecf20Sopenharmony_ci "non-resident."); 15688c2ecf20Sopenharmony_ci else 15698c2ecf20Sopenharmony_ci ntfs_debug("Attribute not defined on the NTFS " 15708c2ecf20Sopenharmony_ci "volume!"); 15718c2ecf20Sopenharmony_ci return err; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci /* 15748c2ecf20Sopenharmony_ci * FIXME: Compressed and encrypted attributes are not supported when 15758c2ecf20Sopenharmony_ci * writing and we should never have gotten here for them. 15768c2ecf20Sopenharmony_ci */ 15778c2ecf20Sopenharmony_ci BUG_ON(NInoCompressed(ni)); 15788c2ecf20Sopenharmony_ci BUG_ON(NInoEncrypted(ni)); 15798c2ecf20Sopenharmony_ci /* 15808c2ecf20Sopenharmony_ci * The size needs to be aligned to a cluster boundary for allocation 15818c2ecf20Sopenharmony_ci * purposes. 15828c2ecf20Sopenharmony_ci */ 15838c2ecf20Sopenharmony_ci new_size = (data_size + vol->cluster_size - 1) & 15848c2ecf20Sopenharmony_ci ~(vol->cluster_size - 1); 15858c2ecf20Sopenharmony_ci if (new_size > 0) { 15868c2ecf20Sopenharmony_ci /* 15878c2ecf20Sopenharmony_ci * Will need the page later and since the page lock nests 15888c2ecf20Sopenharmony_ci * outside all ntfs locks, we need to get the page now. 15898c2ecf20Sopenharmony_ci */ 15908c2ecf20Sopenharmony_ci page = find_or_create_page(vi->i_mapping, 0, 15918c2ecf20Sopenharmony_ci mapping_gfp_mask(vi->i_mapping)); 15928c2ecf20Sopenharmony_ci if (unlikely(!page)) 15938c2ecf20Sopenharmony_ci return -ENOMEM; 15948c2ecf20Sopenharmony_ci /* Start by allocating clusters to hold the attribute value. */ 15958c2ecf20Sopenharmony_ci rl = ntfs_cluster_alloc(vol, 0, new_size >> 15968c2ecf20Sopenharmony_ci vol->cluster_size_bits, -1, DATA_ZONE, true); 15978c2ecf20Sopenharmony_ci if (IS_ERR(rl)) { 15988c2ecf20Sopenharmony_ci err = PTR_ERR(rl); 15998c2ecf20Sopenharmony_ci ntfs_debug("Failed to allocate cluster%s, error code " 16008c2ecf20Sopenharmony_ci "%i.", (new_size >> 16018c2ecf20Sopenharmony_ci vol->cluster_size_bits) > 1 ? "s" : "", 16028c2ecf20Sopenharmony_ci err); 16038c2ecf20Sopenharmony_ci goto page_err_out; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci } else { 16068c2ecf20Sopenharmony_ci rl = NULL; 16078c2ecf20Sopenharmony_ci page = NULL; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci /* Determine the size of the mapping pairs array. */ 16108c2ecf20Sopenharmony_ci mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, -1); 16118c2ecf20Sopenharmony_ci if (unlikely(mp_size < 0)) { 16128c2ecf20Sopenharmony_ci err = mp_size; 16138c2ecf20Sopenharmony_ci ntfs_debug("Failed to get size for mapping pairs array, error " 16148c2ecf20Sopenharmony_ci "code %i.", err); 16158c2ecf20Sopenharmony_ci goto rl_err_out; 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci down_write(&ni->runlist.lock); 16188c2ecf20Sopenharmony_ci if (!NInoAttr(ni)) 16198c2ecf20Sopenharmony_ci base_ni = ni; 16208c2ecf20Sopenharmony_ci else 16218c2ecf20Sopenharmony_ci base_ni = ni->ext.base_ntfs_ino; 16228c2ecf20Sopenharmony_ci m = map_mft_record(base_ni); 16238c2ecf20Sopenharmony_ci if (IS_ERR(m)) { 16248c2ecf20Sopenharmony_ci err = PTR_ERR(m); 16258c2ecf20Sopenharmony_ci m = NULL; 16268c2ecf20Sopenharmony_ci ctx = NULL; 16278c2ecf20Sopenharmony_ci goto err_out; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(base_ni, m); 16308c2ecf20Sopenharmony_ci if (unlikely(!ctx)) { 16318c2ecf20Sopenharmony_ci err = -ENOMEM; 16328c2ecf20Sopenharmony_ci goto err_out; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, 16358c2ecf20Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx); 16368c2ecf20Sopenharmony_ci if (unlikely(err)) { 16378c2ecf20Sopenharmony_ci if (err == -ENOENT) 16388c2ecf20Sopenharmony_ci err = -EIO; 16398c2ecf20Sopenharmony_ci goto err_out; 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci m = ctx->mrec; 16428c2ecf20Sopenharmony_ci a = ctx->attr; 16438c2ecf20Sopenharmony_ci BUG_ON(NInoNonResident(ni)); 16448c2ecf20Sopenharmony_ci BUG_ON(a->non_resident); 16458c2ecf20Sopenharmony_ci /* 16468c2ecf20Sopenharmony_ci * Calculate new offsets for the name and the mapping pairs array. 16478c2ecf20Sopenharmony_ci */ 16488c2ecf20Sopenharmony_ci if (NInoSparse(ni) || NInoCompressed(ni)) 16498c2ecf20Sopenharmony_ci name_ofs = (offsetof(ATTR_REC, 16508c2ecf20Sopenharmony_ci data.non_resident.compressed_size) + 16518c2ecf20Sopenharmony_ci sizeof(a->data.non_resident.compressed_size) + 16528c2ecf20Sopenharmony_ci 7) & ~7; 16538c2ecf20Sopenharmony_ci else 16548c2ecf20Sopenharmony_ci name_ofs = (offsetof(ATTR_REC, 16558c2ecf20Sopenharmony_ci data.non_resident.compressed_size) + 7) & ~7; 16568c2ecf20Sopenharmony_ci mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; 16578c2ecf20Sopenharmony_ci /* 16588c2ecf20Sopenharmony_ci * Determine the size of the resident part of the now non-resident 16598c2ecf20Sopenharmony_ci * attribute record. 16608c2ecf20Sopenharmony_ci */ 16618c2ecf20Sopenharmony_ci arec_size = (mp_ofs + mp_size + 7) & ~7; 16628c2ecf20Sopenharmony_ci /* 16638c2ecf20Sopenharmony_ci * If the page is not uptodate bring it uptodate by copying from the 16648c2ecf20Sopenharmony_ci * attribute value. 16658c2ecf20Sopenharmony_ci */ 16668c2ecf20Sopenharmony_ci attr_size = le32_to_cpu(a->data.resident.value_length); 16678c2ecf20Sopenharmony_ci BUG_ON(attr_size != data_size); 16688c2ecf20Sopenharmony_ci if (page && !PageUptodate(page)) { 16698c2ecf20Sopenharmony_ci kaddr = kmap_atomic(page); 16708c2ecf20Sopenharmony_ci memcpy(kaddr, (u8*)a + 16718c2ecf20Sopenharmony_ci le16_to_cpu(a->data.resident.value_offset), 16728c2ecf20Sopenharmony_ci attr_size); 16738c2ecf20Sopenharmony_ci memset(kaddr + attr_size, 0, PAGE_SIZE - attr_size); 16748c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 16758c2ecf20Sopenharmony_ci flush_dcache_page(page); 16768c2ecf20Sopenharmony_ci SetPageUptodate(page); 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci /* Backup the attribute flag. */ 16798c2ecf20Sopenharmony_ci old_res_attr_flags = a->data.resident.flags; 16808c2ecf20Sopenharmony_ci /* Resize the resident part of the attribute record. */ 16818c2ecf20Sopenharmony_ci err = ntfs_attr_record_resize(m, a, arec_size); 16828c2ecf20Sopenharmony_ci if (unlikely(err)) 16838c2ecf20Sopenharmony_ci goto err_out; 16848c2ecf20Sopenharmony_ci /* 16858c2ecf20Sopenharmony_ci * Convert the resident part of the attribute record to describe a 16868c2ecf20Sopenharmony_ci * non-resident attribute. 16878c2ecf20Sopenharmony_ci */ 16888c2ecf20Sopenharmony_ci a->non_resident = 1; 16898c2ecf20Sopenharmony_ci /* Move the attribute name if it exists and update the offset. */ 16908c2ecf20Sopenharmony_ci if (a->name_length) 16918c2ecf20Sopenharmony_ci memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), 16928c2ecf20Sopenharmony_ci a->name_length * sizeof(ntfschar)); 16938c2ecf20Sopenharmony_ci a->name_offset = cpu_to_le16(name_ofs); 16948c2ecf20Sopenharmony_ci /* Setup the fields specific to non-resident attributes. */ 16958c2ecf20Sopenharmony_ci a->data.non_resident.lowest_vcn = 0; 16968c2ecf20Sopenharmony_ci a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >> 16978c2ecf20Sopenharmony_ci vol->cluster_size_bits); 16988c2ecf20Sopenharmony_ci a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs); 16998c2ecf20Sopenharmony_ci memset(&a->data.non_resident.reserved, 0, 17008c2ecf20Sopenharmony_ci sizeof(a->data.non_resident.reserved)); 17018c2ecf20Sopenharmony_ci a->data.non_resident.allocated_size = cpu_to_sle64(new_size); 17028c2ecf20Sopenharmony_ci a->data.non_resident.data_size = 17038c2ecf20Sopenharmony_ci a->data.non_resident.initialized_size = 17048c2ecf20Sopenharmony_ci cpu_to_sle64(attr_size); 17058c2ecf20Sopenharmony_ci if (NInoSparse(ni) || NInoCompressed(ni)) { 17068c2ecf20Sopenharmony_ci a->data.non_resident.compression_unit = 0; 17078c2ecf20Sopenharmony_ci if (NInoCompressed(ni) || vol->major_ver < 3) 17088c2ecf20Sopenharmony_ci a->data.non_resident.compression_unit = 4; 17098c2ecf20Sopenharmony_ci a->data.non_resident.compressed_size = 17108c2ecf20Sopenharmony_ci a->data.non_resident.allocated_size; 17118c2ecf20Sopenharmony_ci } else 17128c2ecf20Sopenharmony_ci a->data.non_resident.compression_unit = 0; 17138c2ecf20Sopenharmony_ci /* Generate the mapping pairs array into the attribute record. */ 17148c2ecf20Sopenharmony_ci err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, 17158c2ecf20Sopenharmony_ci arec_size - mp_ofs, rl, 0, -1, NULL); 17168c2ecf20Sopenharmony_ci if (unlikely(err)) { 17178c2ecf20Sopenharmony_ci ntfs_debug("Failed to build mapping pairs, error code %i.", 17188c2ecf20Sopenharmony_ci err); 17198c2ecf20Sopenharmony_ci goto undo_err_out; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci /* Setup the in-memory attribute structure to be non-resident. */ 17228c2ecf20Sopenharmony_ci ni->runlist.rl = rl; 17238c2ecf20Sopenharmony_ci write_lock_irqsave(&ni->size_lock, flags); 17248c2ecf20Sopenharmony_ci ni->allocated_size = new_size; 17258c2ecf20Sopenharmony_ci if (NInoSparse(ni) || NInoCompressed(ni)) { 17268c2ecf20Sopenharmony_ci ni->itype.compressed.size = ni->allocated_size; 17278c2ecf20Sopenharmony_ci if (a->data.non_resident.compression_unit) { 17288c2ecf20Sopenharmony_ci ni->itype.compressed.block_size = 1U << (a->data. 17298c2ecf20Sopenharmony_ci non_resident.compression_unit + 17308c2ecf20Sopenharmony_ci vol->cluster_size_bits); 17318c2ecf20Sopenharmony_ci ni->itype.compressed.block_size_bits = 17328c2ecf20Sopenharmony_ci ffs(ni->itype.compressed.block_size) - 17338c2ecf20Sopenharmony_ci 1; 17348c2ecf20Sopenharmony_ci ni->itype.compressed.block_clusters = 1U << 17358c2ecf20Sopenharmony_ci a->data.non_resident.compression_unit; 17368c2ecf20Sopenharmony_ci } else { 17378c2ecf20Sopenharmony_ci ni->itype.compressed.block_size = 0; 17388c2ecf20Sopenharmony_ci ni->itype.compressed.block_size_bits = 0; 17398c2ecf20Sopenharmony_ci ni->itype.compressed.block_clusters = 0; 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci vi->i_blocks = ni->itype.compressed.size >> 9; 17428c2ecf20Sopenharmony_ci } else 17438c2ecf20Sopenharmony_ci vi->i_blocks = ni->allocated_size >> 9; 17448c2ecf20Sopenharmony_ci write_unlock_irqrestore(&ni->size_lock, flags); 17458c2ecf20Sopenharmony_ci /* 17468c2ecf20Sopenharmony_ci * This needs to be last since the address space operations ->readpage 17478c2ecf20Sopenharmony_ci * and ->writepage can run concurrently with us as they are not 17488c2ecf20Sopenharmony_ci * serialized on i_mutex. Note, we are not allowed to fail once we flip 17498c2ecf20Sopenharmony_ci * this switch, which is another reason to do this last. 17508c2ecf20Sopenharmony_ci */ 17518c2ecf20Sopenharmony_ci NInoSetNonResident(ni); 17528c2ecf20Sopenharmony_ci /* Mark the mft record dirty, so it gets written back. */ 17538c2ecf20Sopenharmony_ci flush_dcache_mft_record_page(ctx->ntfs_ino); 17548c2ecf20Sopenharmony_ci mark_mft_record_dirty(ctx->ntfs_ino); 17558c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 17568c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 17578c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 17588c2ecf20Sopenharmony_ci if (page) { 17598c2ecf20Sopenharmony_ci set_page_dirty(page); 17608c2ecf20Sopenharmony_ci unlock_page(page); 17618c2ecf20Sopenharmony_ci put_page(page); 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci ntfs_debug("Done."); 17648c2ecf20Sopenharmony_ci return 0; 17658c2ecf20Sopenharmony_ciundo_err_out: 17668c2ecf20Sopenharmony_ci /* Convert the attribute back into a resident attribute. */ 17678c2ecf20Sopenharmony_ci a->non_resident = 0; 17688c2ecf20Sopenharmony_ci /* Move the attribute name if it exists and update the offset. */ 17698c2ecf20Sopenharmony_ci name_ofs = (offsetof(ATTR_RECORD, data.resident.reserved) + 17708c2ecf20Sopenharmony_ci sizeof(a->data.resident.reserved) + 7) & ~7; 17718c2ecf20Sopenharmony_ci if (a->name_length) 17728c2ecf20Sopenharmony_ci memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), 17738c2ecf20Sopenharmony_ci a->name_length * sizeof(ntfschar)); 17748c2ecf20Sopenharmony_ci mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; 17758c2ecf20Sopenharmony_ci a->name_offset = cpu_to_le16(name_ofs); 17768c2ecf20Sopenharmony_ci arec_size = (mp_ofs + attr_size + 7) & ~7; 17778c2ecf20Sopenharmony_ci /* Resize the resident part of the attribute record. */ 17788c2ecf20Sopenharmony_ci err2 = ntfs_attr_record_resize(m, a, arec_size); 17798c2ecf20Sopenharmony_ci if (unlikely(err2)) { 17808c2ecf20Sopenharmony_ci /* 17818c2ecf20Sopenharmony_ci * This cannot happen (well if memory corruption is at work it 17828c2ecf20Sopenharmony_ci * could happen in theory), but deal with it as well as we can. 17838c2ecf20Sopenharmony_ci * If the old size is too small, truncate the attribute, 17848c2ecf20Sopenharmony_ci * otherwise simply give it a larger allocated size. 17858c2ecf20Sopenharmony_ci * FIXME: Should check whether chkdsk complains when the 17868c2ecf20Sopenharmony_ci * allocated size is much bigger than the resident value size. 17878c2ecf20Sopenharmony_ci */ 17888c2ecf20Sopenharmony_ci arec_size = le32_to_cpu(a->length); 17898c2ecf20Sopenharmony_ci if ((mp_ofs + attr_size) > arec_size) { 17908c2ecf20Sopenharmony_ci err2 = attr_size; 17918c2ecf20Sopenharmony_ci attr_size = arec_size - mp_ofs; 17928c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to undo partial resident " 17938c2ecf20Sopenharmony_ci "to non-resident attribute " 17948c2ecf20Sopenharmony_ci "conversion. Truncating inode 0x%lx, " 17958c2ecf20Sopenharmony_ci "attribute type 0x%x from %i bytes to " 17968c2ecf20Sopenharmony_ci "%i bytes to maintain metadata " 17978c2ecf20Sopenharmony_ci "consistency. THIS MEANS YOU ARE " 17988c2ecf20Sopenharmony_ci "LOSING %i BYTES DATA FROM THIS %s.", 17998c2ecf20Sopenharmony_ci vi->i_ino, 18008c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), 18018c2ecf20Sopenharmony_ci err2, attr_size, err2 - attr_size, 18028c2ecf20Sopenharmony_ci ((ni->type == AT_DATA) && 18038c2ecf20Sopenharmony_ci !ni->name_len) ? "FILE": "ATTRIBUTE"); 18048c2ecf20Sopenharmony_ci write_lock_irqsave(&ni->size_lock, flags); 18058c2ecf20Sopenharmony_ci ni->initialized_size = attr_size; 18068c2ecf20Sopenharmony_ci i_size_write(vi, attr_size); 18078c2ecf20Sopenharmony_ci write_unlock_irqrestore(&ni->size_lock, flags); 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci /* Setup the fields specific to resident attributes. */ 18118c2ecf20Sopenharmony_ci a->data.resident.value_length = cpu_to_le32(attr_size); 18128c2ecf20Sopenharmony_ci a->data.resident.value_offset = cpu_to_le16(mp_ofs); 18138c2ecf20Sopenharmony_ci a->data.resident.flags = old_res_attr_flags; 18148c2ecf20Sopenharmony_ci memset(&a->data.resident.reserved, 0, 18158c2ecf20Sopenharmony_ci sizeof(a->data.resident.reserved)); 18168c2ecf20Sopenharmony_ci /* Copy the data from the page back to the attribute value. */ 18178c2ecf20Sopenharmony_ci if (page) { 18188c2ecf20Sopenharmony_ci kaddr = kmap_atomic(page); 18198c2ecf20Sopenharmony_ci memcpy((u8*)a + mp_ofs, kaddr, attr_size); 18208c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci /* Setup the allocated size in the ntfs inode in case it changed. */ 18238c2ecf20Sopenharmony_ci write_lock_irqsave(&ni->size_lock, flags); 18248c2ecf20Sopenharmony_ci ni->allocated_size = arec_size - mp_ofs; 18258c2ecf20Sopenharmony_ci write_unlock_irqrestore(&ni->size_lock, flags); 18268c2ecf20Sopenharmony_ci /* Mark the mft record dirty, so it gets written back. */ 18278c2ecf20Sopenharmony_ci flush_dcache_mft_record_page(ctx->ntfs_ino); 18288c2ecf20Sopenharmony_ci mark_mft_record_dirty(ctx->ntfs_ino); 18298c2ecf20Sopenharmony_cierr_out: 18308c2ecf20Sopenharmony_ci if (ctx) 18318c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 18328c2ecf20Sopenharmony_ci if (m) 18338c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 18348c2ecf20Sopenharmony_ci ni->runlist.rl = NULL; 18358c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 18368c2ecf20Sopenharmony_cirl_err_out: 18378c2ecf20Sopenharmony_ci if (rl) { 18388c2ecf20Sopenharmony_ci if (ntfs_cluster_free_from_rl(vol, rl) < 0) { 18398c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to release allocated " 18408c2ecf20Sopenharmony_ci "cluster(s) in error code path. Run " 18418c2ecf20Sopenharmony_ci "chkdsk to recover the lost " 18428c2ecf20Sopenharmony_ci "cluster(s)."); 18438c2ecf20Sopenharmony_ci NVolSetErrors(vol); 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci ntfs_free(rl); 18468c2ecf20Sopenharmony_cipage_err_out: 18478c2ecf20Sopenharmony_ci unlock_page(page); 18488c2ecf20Sopenharmony_ci put_page(page); 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci if (err == -EINVAL) 18518c2ecf20Sopenharmony_ci err = -EIO; 18528c2ecf20Sopenharmony_ci return err; 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci/** 18568c2ecf20Sopenharmony_ci * ntfs_attr_extend_allocation - extend the allocated space of an attribute 18578c2ecf20Sopenharmony_ci * @ni: ntfs inode of the attribute whose allocation to extend 18588c2ecf20Sopenharmony_ci * @new_alloc_size: new size in bytes to which to extend the allocation to 18598c2ecf20Sopenharmony_ci * @new_data_size: new size in bytes to which to extend the data to 18608c2ecf20Sopenharmony_ci * @data_start: beginning of region which is required to be non-sparse 18618c2ecf20Sopenharmony_ci * 18628c2ecf20Sopenharmony_ci * Extend the allocated space of an attribute described by the ntfs inode @ni 18638c2ecf20Sopenharmony_ci * to @new_alloc_size bytes. If @data_start is -1, the whole extension may be 18648c2ecf20Sopenharmony_ci * implemented as a hole in the file (as long as both the volume and the ntfs 18658c2ecf20Sopenharmony_ci * inode @ni have sparse support enabled). If @data_start is >= 0, then the 18668c2ecf20Sopenharmony_ci * region between the old allocated size and @data_start - 1 may be made sparse 18678c2ecf20Sopenharmony_ci * but the regions between @data_start and @new_alloc_size must be backed by 18688c2ecf20Sopenharmony_ci * actual clusters. 18698c2ecf20Sopenharmony_ci * 18708c2ecf20Sopenharmony_ci * If @new_data_size is -1, it is ignored. If it is >= 0, then the data size 18718c2ecf20Sopenharmony_ci * of the attribute is extended to @new_data_size. Note that the i_size of the 18728c2ecf20Sopenharmony_ci * vfs inode is not updated. Only the data size in the base attribute record 18738c2ecf20Sopenharmony_ci * is updated. The caller has to update i_size separately if this is required. 18748c2ecf20Sopenharmony_ci * WARNING: It is a BUG() for @new_data_size to be smaller than the old data 18758c2ecf20Sopenharmony_ci * size as well as for @new_data_size to be greater than @new_alloc_size. 18768c2ecf20Sopenharmony_ci * 18778c2ecf20Sopenharmony_ci * For resident attributes this involves resizing the attribute record and if 18788c2ecf20Sopenharmony_ci * necessary moving it and/or other attributes into extent mft records and/or 18798c2ecf20Sopenharmony_ci * converting the attribute to a non-resident attribute which in turn involves 18808c2ecf20Sopenharmony_ci * extending the allocation of a non-resident attribute as described below. 18818c2ecf20Sopenharmony_ci * 18828c2ecf20Sopenharmony_ci * For non-resident attributes this involves allocating clusters in the data 18838c2ecf20Sopenharmony_ci * zone on the volume (except for regions that are being made sparse) and 18848c2ecf20Sopenharmony_ci * extending the run list to describe the allocated clusters as well as 18858c2ecf20Sopenharmony_ci * updating the mapping pairs array of the attribute. This in turn involves 18868c2ecf20Sopenharmony_ci * resizing the attribute record and if necessary moving it and/or other 18878c2ecf20Sopenharmony_ci * attributes into extent mft records and/or splitting the attribute record 18888c2ecf20Sopenharmony_ci * into multiple extent attribute records. 18898c2ecf20Sopenharmony_ci * 18908c2ecf20Sopenharmony_ci * Also, the attribute list attribute is updated if present and in some of the 18918c2ecf20Sopenharmony_ci * above cases (the ones where extent mft records/attributes come into play), 18928c2ecf20Sopenharmony_ci * an attribute list attribute is created if not already present. 18938c2ecf20Sopenharmony_ci * 18948c2ecf20Sopenharmony_ci * Return the new allocated size on success and -errno on error. In the case 18958c2ecf20Sopenharmony_ci * that an error is encountered but a partial extension at least up to 18968c2ecf20Sopenharmony_ci * @data_start (if present) is possible, the allocation is partially extended 18978c2ecf20Sopenharmony_ci * and this is returned. This means the caller must check the returned size to 18988c2ecf20Sopenharmony_ci * determine if the extension was partial. If @data_start is -1 then partial 18998c2ecf20Sopenharmony_ci * allocations are not performed. 19008c2ecf20Sopenharmony_ci * 19018c2ecf20Sopenharmony_ci * WARNING: Do not call ntfs_attr_extend_allocation() for $MFT/$DATA. 19028c2ecf20Sopenharmony_ci * 19038c2ecf20Sopenharmony_ci * Locking: This function takes the runlist lock of @ni for writing as well as 19048c2ecf20Sopenharmony_ci * locking the mft record of the base ntfs inode. These locks are maintained 19058c2ecf20Sopenharmony_ci * throughout execution of the function. These locks are required so that the 19068c2ecf20Sopenharmony_ci * attribute can be resized safely and so that it can for example be converted 19078c2ecf20Sopenharmony_ci * from resident to non-resident safely. 19088c2ecf20Sopenharmony_ci * 19098c2ecf20Sopenharmony_ci * TODO: At present attribute list attribute handling is not implemented. 19108c2ecf20Sopenharmony_ci * 19118c2ecf20Sopenharmony_ci * TODO: At present it is not safe to call this function for anything other 19128c2ecf20Sopenharmony_ci * than the $DATA attribute(s) of an uncompressed and unencrypted file. 19138c2ecf20Sopenharmony_ci */ 19148c2ecf20Sopenharmony_cis64 ntfs_attr_extend_allocation(ntfs_inode *ni, s64 new_alloc_size, 19158c2ecf20Sopenharmony_ci const s64 new_data_size, const s64 data_start) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci VCN vcn; 19188c2ecf20Sopenharmony_ci s64 ll, allocated_size, start = data_start; 19198c2ecf20Sopenharmony_ci struct inode *vi = VFS_I(ni); 19208c2ecf20Sopenharmony_ci ntfs_volume *vol = ni->vol; 19218c2ecf20Sopenharmony_ci ntfs_inode *base_ni; 19228c2ecf20Sopenharmony_ci MFT_RECORD *m; 19238c2ecf20Sopenharmony_ci ATTR_RECORD *a; 19248c2ecf20Sopenharmony_ci ntfs_attr_search_ctx *ctx; 19258c2ecf20Sopenharmony_ci runlist_element *rl, *rl2; 19268c2ecf20Sopenharmony_ci unsigned long flags; 19278c2ecf20Sopenharmony_ci int err, mp_size; 19288c2ecf20Sopenharmony_ci u32 attr_len = 0; /* Silence stupid gcc warning. */ 19298c2ecf20Sopenharmony_ci bool mp_rebuilt; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci#ifdef DEBUG 19328c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 19338c2ecf20Sopenharmony_ci allocated_size = ni->allocated_size; 19348c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 19358c2ecf20Sopenharmony_ci ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, " 19368c2ecf20Sopenharmony_ci "old_allocated_size 0x%llx, " 19378c2ecf20Sopenharmony_ci "new_allocated_size 0x%llx, new_data_size 0x%llx, " 19388c2ecf20Sopenharmony_ci "data_start 0x%llx.", vi->i_ino, 19398c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), 19408c2ecf20Sopenharmony_ci (unsigned long long)allocated_size, 19418c2ecf20Sopenharmony_ci (unsigned long long)new_alloc_size, 19428c2ecf20Sopenharmony_ci (unsigned long long)new_data_size, 19438c2ecf20Sopenharmony_ci (unsigned long long)start); 19448c2ecf20Sopenharmony_ci#endif 19458c2ecf20Sopenharmony_ciretry_extend: 19468c2ecf20Sopenharmony_ci /* 19478c2ecf20Sopenharmony_ci * For non-resident attributes, @start and @new_size need to be aligned 19488c2ecf20Sopenharmony_ci * to cluster boundaries for allocation purposes. 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_ci if (NInoNonResident(ni)) { 19518c2ecf20Sopenharmony_ci if (start > 0) 19528c2ecf20Sopenharmony_ci start &= ~(s64)vol->cluster_size_mask; 19538c2ecf20Sopenharmony_ci new_alloc_size = (new_alloc_size + vol->cluster_size - 1) & 19548c2ecf20Sopenharmony_ci ~(s64)vol->cluster_size_mask; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci BUG_ON(new_data_size >= 0 && new_data_size > new_alloc_size); 19578c2ecf20Sopenharmony_ci /* Check if new size is allowed in $AttrDef. */ 19588c2ecf20Sopenharmony_ci err = ntfs_attr_size_bounds_check(vol, ni->type, new_alloc_size); 19598c2ecf20Sopenharmony_ci if (unlikely(err)) { 19608c2ecf20Sopenharmony_ci /* Only emit errors when the write will fail completely. */ 19618c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 19628c2ecf20Sopenharmony_ci allocated_size = ni->allocated_size; 19638c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 19648c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) { 19658c2ecf20Sopenharmony_ci if (err == -ERANGE) { 19668c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation " 19678c2ecf20Sopenharmony_ci "of inode 0x%lx, attribute " 19688c2ecf20Sopenharmony_ci "type 0x%x, because the new " 19698c2ecf20Sopenharmony_ci "allocation would exceed the " 19708c2ecf20Sopenharmony_ci "maximum allowed size for " 19718c2ecf20Sopenharmony_ci "this attribute type.", 19728c2ecf20Sopenharmony_ci vi->i_ino, (unsigned) 19738c2ecf20Sopenharmony_ci le32_to_cpu(ni->type)); 19748c2ecf20Sopenharmony_ci } else { 19758c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation " 19768c2ecf20Sopenharmony_ci "of inode 0x%lx, attribute " 19778c2ecf20Sopenharmony_ci "type 0x%x, because this " 19788c2ecf20Sopenharmony_ci "attribute type is not " 19798c2ecf20Sopenharmony_ci "defined on the NTFS volume. " 19808c2ecf20Sopenharmony_ci "Possible corruption! You " 19818c2ecf20Sopenharmony_ci "should run chkdsk!", 19828c2ecf20Sopenharmony_ci vi->i_ino, (unsigned) 19838c2ecf20Sopenharmony_ci le32_to_cpu(ni->type)); 19848c2ecf20Sopenharmony_ci } 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci /* Translate error code to be POSIX conformant for write(2). */ 19878c2ecf20Sopenharmony_ci if (err == -ERANGE) 19888c2ecf20Sopenharmony_ci err = -EFBIG; 19898c2ecf20Sopenharmony_ci else 19908c2ecf20Sopenharmony_ci err = -EIO; 19918c2ecf20Sopenharmony_ci return err; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci if (!NInoAttr(ni)) 19948c2ecf20Sopenharmony_ci base_ni = ni; 19958c2ecf20Sopenharmony_ci else 19968c2ecf20Sopenharmony_ci base_ni = ni->ext.base_ntfs_ino; 19978c2ecf20Sopenharmony_ci /* 19988c2ecf20Sopenharmony_ci * We will be modifying both the runlist (if non-resident) and the mft 19998c2ecf20Sopenharmony_ci * record so lock them both down. 20008c2ecf20Sopenharmony_ci */ 20018c2ecf20Sopenharmony_ci down_write(&ni->runlist.lock); 20028c2ecf20Sopenharmony_ci m = map_mft_record(base_ni); 20038c2ecf20Sopenharmony_ci if (IS_ERR(m)) { 20048c2ecf20Sopenharmony_ci err = PTR_ERR(m); 20058c2ecf20Sopenharmony_ci m = NULL; 20068c2ecf20Sopenharmony_ci ctx = NULL; 20078c2ecf20Sopenharmony_ci goto err_out; 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci ctx = ntfs_attr_get_search_ctx(base_ni, m); 20108c2ecf20Sopenharmony_ci if (unlikely(!ctx)) { 20118c2ecf20Sopenharmony_ci err = -ENOMEM; 20128c2ecf20Sopenharmony_ci goto err_out; 20138c2ecf20Sopenharmony_ci } 20148c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 20158c2ecf20Sopenharmony_ci allocated_size = ni->allocated_size; 20168c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 20178c2ecf20Sopenharmony_ci /* 20188c2ecf20Sopenharmony_ci * If non-resident, seek to the last extent. If resident, there is 20198c2ecf20Sopenharmony_ci * only one extent, so seek to that. 20208c2ecf20Sopenharmony_ci */ 20218c2ecf20Sopenharmony_ci vcn = NInoNonResident(ni) ? allocated_size >> vol->cluster_size_bits : 20228c2ecf20Sopenharmony_ci 0; 20238c2ecf20Sopenharmony_ci /* 20248c2ecf20Sopenharmony_ci * Abort if someone did the work whilst we waited for the locks. If we 20258c2ecf20Sopenharmony_ci * just converted the attribute from resident to non-resident it is 20268c2ecf20Sopenharmony_ci * likely that exactly this has happened already. We cannot quite 20278c2ecf20Sopenharmony_ci * abort if we need to update the data size. 20288c2ecf20Sopenharmony_ci */ 20298c2ecf20Sopenharmony_ci if (unlikely(new_alloc_size <= allocated_size)) { 20308c2ecf20Sopenharmony_ci ntfs_debug("Allocated size already exceeds requested size."); 20318c2ecf20Sopenharmony_ci new_alloc_size = allocated_size; 20328c2ecf20Sopenharmony_ci if (new_data_size < 0) 20338c2ecf20Sopenharmony_ci goto done; 20348c2ecf20Sopenharmony_ci /* 20358c2ecf20Sopenharmony_ci * We want the first attribute extent so that we can update the 20368c2ecf20Sopenharmony_ci * data size. 20378c2ecf20Sopenharmony_ci */ 20388c2ecf20Sopenharmony_ci vcn = 0; 20398c2ecf20Sopenharmony_ci } 20408c2ecf20Sopenharmony_ci err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, 20418c2ecf20Sopenharmony_ci CASE_SENSITIVE, vcn, NULL, 0, ctx); 20428c2ecf20Sopenharmony_ci if (unlikely(err)) { 20438c2ecf20Sopenharmony_ci if (err == -ENOENT) 20448c2ecf20Sopenharmony_ci err = -EIO; 20458c2ecf20Sopenharmony_ci goto err_out; 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci m = ctx->mrec; 20488c2ecf20Sopenharmony_ci a = ctx->attr; 20498c2ecf20Sopenharmony_ci /* Use goto to reduce indentation. */ 20508c2ecf20Sopenharmony_ci if (a->non_resident) 20518c2ecf20Sopenharmony_ci goto do_non_resident_extend; 20528c2ecf20Sopenharmony_ci BUG_ON(NInoNonResident(ni)); 20538c2ecf20Sopenharmony_ci /* The total length of the attribute value. */ 20548c2ecf20Sopenharmony_ci attr_len = le32_to_cpu(a->data.resident.value_length); 20558c2ecf20Sopenharmony_ci /* 20568c2ecf20Sopenharmony_ci * Extend the attribute record to be able to store the new attribute 20578c2ecf20Sopenharmony_ci * size. ntfs_attr_record_resize() will not do anything if the size is 20588c2ecf20Sopenharmony_ci * not changing. 20598c2ecf20Sopenharmony_ci */ 20608c2ecf20Sopenharmony_ci if (new_alloc_size < vol->mft_record_size && 20618c2ecf20Sopenharmony_ci !ntfs_attr_record_resize(m, a, 20628c2ecf20Sopenharmony_ci le16_to_cpu(a->data.resident.value_offset) + 20638c2ecf20Sopenharmony_ci new_alloc_size)) { 20648c2ecf20Sopenharmony_ci /* The resize succeeded! */ 20658c2ecf20Sopenharmony_ci write_lock_irqsave(&ni->size_lock, flags); 20668c2ecf20Sopenharmony_ci ni->allocated_size = le32_to_cpu(a->length) - 20678c2ecf20Sopenharmony_ci le16_to_cpu(a->data.resident.value_offset); 20688c2ecf20Sopenharmony_ci write_unlock_irqrestore(&ni->size_lock, flags); 20698c2ecf20Sopenharmony_ci if (new_data_size >= 0) { 20708c2ecf20Sopenharmony_ci BUG_ON(new_data_size < attr_len); 20718c2ecf20Sopenharmony_ci a->data.resident.value_length = 20728c2ecf20Sopenharmony_ci cpu_to_le32((u32)new_data_size); 20738c2ecf20Sopenharmony_ci } 20748c2ecf20Sopenharmony_ci goto flush_done; 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci /* 20778c2ecf20Sopenharmony_ci * We have to drop all the locks so we can call 20788c2ecf20Sopenharmony_ci * ntfs_attr_make_non_resident(). This could be optimised by try- 20798c2ecf20Sopenharmony_ci * locking the first page cache page and only if that fails dropping 20808c2ecf20Sopenharmony_ci * the locks, locking the page, and redoing all the locking and 20818c2ecf20Sopenharmony_ci * lookups. While this would be a huge optimisation, it is not worth 20828c2ecf20Sopenharmony_ci * it as this is definitely a slow code path. 20838c2ecf20Sopenharmony_ci */ 20848c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 20858c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 20868c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 20878c2ecf20Sopenharmony_ci /* 20888c2ecf20Sopenharmony_ci * Not enough space in the mft record, try to make the attribute 20898c2ecf20Sopenharmony_ci * non-resident and if successful restart the extension process. 20908c2ecf20Sopenharmony_ci */ 20918c2ecf20Sopenharmony_ci err = ntfs_attr_make_non_resident(ni, attr_len); 20928c2ecf20Sopenharmony_ci if (likely(!err)) 20938c2ecf20Sopenharmony_ci goto retry_extend; 20948c2ecf20Sopenharmony_ci /* 20958c2ecf20Sopenharmony_ci * Could not make non-resident. If this is due to this not being 20968c2ecf20Sopenharmony_ci * permitted for this attribute type or there not being enough space, 20978c2ecf20Sopenharmony_ci * try to make other attributes non-resident. Otherwise fail. 20988c2ecf20Sopenharmony_ci */ 20998c2ecf20Sopenharmony_ci if (unlikely(err != -EPERM && err != -ENOSPC)) { 21008c2ecf20Sopenharmony_ci /* Only emit errors when the write will fail completely. */ 21018c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 21028c2ecf20Sopenharmony_ci allocated_size = ni->allocated_size; 21038c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 21048c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 21058c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation of " 21068c2ecf20Sopenharmony_ci "inode 0x%lx, attribute type 0x%x, " 21078c2ecf20Sopenharmony_ci "because the conversion from resident " 21088c2ecf20Sopenharmony_ci "to non-resident attribute failed " 21098c2ecf20Sopenharmony_ci "with error code %i.", vi->i_ino, 21108c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), err); 21118c2ecf20Sopenharmony_ci if (err != -ENOMEM) 21128c2ecf20Sopenharmony_ci err = -EIO; 21138c2ecf20Sopenharmony_ci goto conv_err_out; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci /* TODO: Not implemented from here, abort. */ 21168c2ecf20Sopenharmony_ci read_lock_irqsave(&ni->size_lock, flags); 21178c2ecf20Sopenharmony_ci allocated_size = ni->allocated_size; 21188c2ecf20Sopenharmony_ci read_unlock_irqrestore(&ni->size_lock, flags); 21198c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) { 21208c2ecf20Sopenharmony_ci if (err == -ENOSPC) 21218c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Not enough space in the mft " 21228c2ecf20Sopenharmony_ci "record/on disk for the non-resident " 21238c2ecf20Sopenharmony_ci "attribute value. This case is not " 21248c2ecf20Sopenharmony_ci "implemented yet."); 21258c2ecf20Sopenharmony_ci else /* if (err == -EPERM) */ 21268c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "This attribute type may not be " 21278c2ecf20Sopenharmony_ci "non-resident. This case is not " 21288c2ecf20Sopenharmony_ci "implemented yet."); 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 21318c2ecf20Sopenharmony_ci goto conv_err_out; 21328c2ecf20Sopenharmony_ci#if 0 21338c2ecf20Sopenharmony_ci // TODO: Attempt to make other attributes non-resident. 21348c2ecf20Sopenharmony_ci if (!err) 21358c2ecf20Sopenharmony_ci goto do_resident_extend; 21368c2ecf20Sopenharmony_ci /* 21378c2ecf20Sopenharmony_ci * Both the attribute list attribute and the standard information 21388c2ecf20Sopenharmony_ci * attribute must remain in the base inode. Thus, if this is one of 21398c2ecf20Sopenharmony_ci * these attributes, we have to try to move other attributes out into 21408c2ecf20Sopenharmony_ci * extent mft records instead. 21418c2ecf20Sopenharmony_ci */ 21428c2ecf20Sopenharmony_ci if (ni->type == AT_ATTRIBUTE_LIST || 21438c2ecf20Sopenharmony_ci ni->type == AT_STANDARD_INFORMATION) { 21448c2ecf20Sopenharmony_ci // TODO: Attempt to move other attributes into extent mft 21458c2ecf20Sopenharmony_ci // records. 21468c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 21478c2ecf20Sopenharmony_ci if (!err) 21488c2ecf20Sopenharmony_ci goto do_resident_extend; 21498c2ecf20Sopenharmony_ci goto err_out; 21508c2ecf20Sopenharmony_ci } 21518c2ecf20Sopenharmony_ci // TODO: Attempt to move this attribute to an extent mft record, but 21528c2ecf20Sopenharmony_ci // only if it is not already the only attribute in an mft record in 21538c2ecf20Sopenharmony_ci // which case there would be nothing to gain. 21548c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 21558c2ecf20Sopenharmony_ci if (!err) 21568c2ecf20Sopenharmony_ci goto do_resident_extend; 21578c2ecf20Sopenharmony_ci /* There is nothing we can do to make enough space. )-: */ 21588c2ecf20Sopenharmony_ci goto err_out; 21598c2ecf20Sopenharmony_ci#endif 21608c2ecf20Sopenharmony_cido_non_resident_extend: 21618c2ecf20Sopenharmony_ci BUG_ON(!NInoNonResident(ni)); 21628c2ecf20Sopenharmony_ci if (new_alloc_size == allocated_size) { 21638c2ecf20Sopenharmony_ci BUG_ON(vcn); 21648c2ecf20Sopenharmony_ci goto alloc_done; 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci /* 21678c2ecf20Sopenharmony_ci * If the data starts after the end of the old allocation, this is a 21688c2ecf20Sopenharmony_ci * $DATA attribute and sparse attributes are enabled on the volume and 21698c2ecf20Sopenharmony_ci * for this inode, then create a sparse region between the old 21708c2ecf20Sopenharmony_ci * allocated size and the start of the data. Otherwise simply proceed 21718c2ecf20Sopenharmony_ci * with filling the whole space between the old allocated size and the 21728c2ecf20Sopenharmony_ci * new allocated size with clusters. 21738c2ecf20Sopenharmony_ci */ 21748c2ecf20Sopenharmony_ci if ((start >= 0 && start <= allocated_size) || ni->type != AT_DATA || 21758c2ecf20Sopenharmony_ci !NVolSparseEnabled(vol) || NInoSparseDisabled(ni)) 21768c2ecf20Sopenharmony_ci goto skip_sparse; 21778c2ecf20Sopenharmony_ci // TODO: This is not implemented yet. We just fill in with real 21788c2ecf20Sopenharmony_ci // clusters for now... 21798c2ecf20Sopenharmony_ci ntfs_debug("Inserting holes is not-implemented yet. Falling back to " 21808c2ecf20Sopenharmony_ci "allocating real clusters instead."); 21818c2ecf20Sopenharmony_ciskip_sparse: 21828c2ecf20Sopenharmony_ci rl = ni->runlist.rl; 21838c2ecf20Sopenharmony_ci if (likely(rl)) { 21848c2ecf20Sopenharmony_ci /* Seek to the end of the runlist. */ 21858c2ecf20Sopenharmony_ci while (rl->length) 21868c2ecf20Sopenharmony_ci rl++; 21878c2ecf20Sopenharmony_ci } 21888c2ecf20Sopenharmony_ci /* If this attribute extent is not mapped, map it now. */ 21898c2ecf20Sopenharmony_ci if (unlikely(!rl || rl->lcn == LCN_RL_NOT_MAPPED || 21908c2ecf20Sopenharmony_ci (rl->lcn == LCN_ENOENT && rl > ni->runlist.rl && 21918c2ecf20Sopenharmony_ci (rl-1)->lcn == LCN_RL_NOT_MAPPED))) { 21928c2ecf20Sopenharmony_ci if (!rl && !allocated_size) 21938c2ecf20Sopenharmony_ci goto first_alloc; 21948c2ecf20Sopenharmony_ci rl = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl); 21958c2ecf20Sopenharmony_ci if (IS_ERR(rl)) { 21968c2ecf20Sopenharmony_ci err = PTR_ERR(rl); 21978c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 21988c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation " 21998c2ecf20Sopenharmony_ci "of inode 0x%lx, attribute " 22008c2ecf20Sopenharmony_ci "type 0x%x, because the " 22018c2ecf20Sopenharmony_ci "mapping of a runlist " 22028c2ecf20Sopenharmony_ci "fragment failed with error " 22038c2ecf20Sopenharmony_ci "code %i.", vi->i_ino, 22048c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), 22058c2ecf20Sopenharmony_ci err); 22068c2ecf20Sopenharmony_ci if (err != -ENOMEM) 22078c2ecf20Sopenharmony_ci err = -EIO; 22088c2ecf20Sopenharmony_ci goto err_out; 22098c2ecf20Sopenharmony_ci } 22108c2ecf20Sopenharmony_ci ni->runlist.rl = rl; 22118c2ecf20Sopenharmony_ci /* Seek to the end of the runlist. */ 22128c2ecf20Sopenharmony_ci while (rl->length) 22138c2ecf20Sopenharmony_ci rl++; 22148c2ecf20Sopenharmony_ci } 22158c2ecf20Sopenharmony_ci /* 22168c2ecf20Sopenharmony_ci * We now know the runlist of the last extent is mapped and @rl is at 22178c2ecf20Sopenharmony_ci * the end of the runlist. We want to begin allocating clusters 22188c2ecf20Sopenharmony_ci * starting at the last allocated cluster to reduce fragmentation. If 22198c2ecf20Sopenharmony_ci * there are no valid LCNs in the attribute we let the cluster 22208c2ecf20Sopenharmony_ci * allocator choose the starting cluster. 22218c2ecf20Sopenharmony_ci */ 22228c2ecf20Sopenharmony_ci /* If the last LCN is a hole or simillar seek back to last real LCN. */ 22238c2ecf20Sopenharmony_ci while (rl->lcn < 0 && rl > ni->runlist.rl) 22248c2ecf20Sopenharmony_ci rl--; 22258c2ecf20Sopenharmony_cifirst_alloc: 22268c2ecf20Sopenharmony_ci // FIXME: Need to implement partial allocations so at least part of the 22278c2ecf20Sopenharmony_ci // write can be performed when start >= 0. (Needed for POSIX write(2) 22288c2ecf20Sopenharmony_ci // conformance.) 22298c2ecf20Sopenharmony_ci rl2 = ntfs_cluster_alloc(vol, allocated_size >> vol->cluster_size_bits, 22308c2ecf20Sopenharmony_ci (new_alloc_size - allocated_size) >> 22318c2ecf20Sopenharmony_ci vol->cluster_size_bits, (rl && (rl->lcn >= 0)) ? 22328c2ecf20Sopenharmony_ci rl->lcn + rl->length : -1, DATA_ZONE, true); 22338c2ecf20Sopenharmony_ci if (IS_ERR(rl2)) { 22348c2ecf20Sopenharmony_ci err = PTR_ERR(rl2); 22358c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 22368c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation of " 22378c2ecf20Sopenharmony_ci "inode 0x%lx, attribute type 0x%x, " 22388c2ecf20Sopenharmony_ci "because the allocation of clusters " 22398c2ecf20Sopenharmony_ci "failed with error code %i.", vi->i_ino, 22408c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), err); 22418c2ecf20Sopenharmony_ci if (err != -ENOMEM && err != -ENOSPC) 22428c2ecf20Sopenharmony_ci err = -EIO; 22438c2ecf20Sopenharmony_ci goto err_out; 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci rl = ntfs_runlists_merge(ni->runlist.rl, rl2); 22468c2ecf20Sopenharmony_ci if (IS_ERR(rl)) { 22478c2ecf20Sopenharmony_ci err = PTR_ERR(rl); 22488c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 22498c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation of " 22508c2ecf20Sopenharmony_ci "inode 0x%lx, attribute type 0x%x, " 22518c2ecf20Sopenharmony_ci "because the runlist merge failed " 22528c2ecf20Sopenharmony_ci "with error code %i.", vi->i_ino, 22538c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), err); 22548c2ecf20Sopenharmony_ci if (err != -ENOMEM) 22558c2ecf20Sopenharmony_ci err = -EIO; 22568c2ecf20Sopenharmony_ci if (ntfs_cluster_free_from_rl(vol, rl2)) { 22578c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to release allocated " 22588c2ecf20Sopenharmony_ci "cluster(s) in error code path. Run " 22598c2ecf20Sopenharmony_ci "chkdsk to recover the lost " 22608c2ecf20Sopenharmony_ci "cluster(s)."); 22618c2ecf20Sopenharmony_ci NVolSetErrors(vol); 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci ntfs_free(rl2); 22648c2ecf20Sopenharmony_ci goto err_out; 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci ni->runlist.rl = rl; 22678c2ecf20Sopenharmony_ci ntfs_debug("Allocated 0x%llx clusters.", (long long)(new_alloc_size - 22688c2ecf20Sopenharmony_ci allocated_size) >> vol->cluster_size_bits); 22698c2ecf20Sopenharmony_ci /* Find the runlist element with which the attribute extent starts. */ 22708c2ecf20Sopenharmony_ci ll = sle64_to_cpu(a->data.non_resident.lowest_vcn); 22718c2ecf20Sopenharmony_ci rl2 = ntfs_rl_find_vcn_nolock(rl, ll); 22728c2ecf20Sopenharmony_ci BUG_ON(!rl2); 22738c2ecf20Sopenharmony_ci BUG_ON(!rl2->length); 22748c2ecf20Sopenharmony_ci BUG_ON(rl2->lcn < LCN_HOLE); 22758c2ecf20Sopenharmony_ci mp_rebuilt = false; 22768c2ecf20Sopenharmony_ci /* Get the size for the new mapping pairs array for this extent. */ 22778c2ecf20Sopenharmony_ci mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1); 22788c2ecf20Sopenharmony_ci if (unlikely(mp_size <= 0)) { 22798c2ecf20Sopenharmony_ci err = mp_size; 22808c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 22818c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation of " 22828c2ecf20Sopenharmony_ci "inode 0x%lx, attribute type 0x%x, " 22838c2ecf20Sopenharmony_ci "because determining the size for the " 22848c2ecf20Sopenharmony_ci "mapping pairs failed with error code " 22858c2ecf20Sopenharmony_ci "%i.", vi->i_ino, 22868c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), err); 22878c2ecf20Sopenharmony_ci err = -EIO; 22888c2ecf20Sopenharmony_ci goto undo_alloc; 22898c2ecf20Sopenharmony_ci } 22908c2ecf20Sopenharmony_ci /* Extend the attribute record to fit the bigger mapping pairs array. */ 22918c2ecf20Sopenharmony_ci attr_len = le32_to_cpu(a->length); 22928c2ecf20Sopenharmony_ci err = ntfs_attr_record_resize(m, a, mp_size + 22938c2ecf20Sopenharmony_ci le16_to_cpu(a->data.non_resident.mapping_pairs_offset)); 22948c2ecf20Sopenharmony_ci if (unlikely(err)) { 22958c2ecf20Sopenharmony_ci BUG_ON(err != -ENOSPC); 22968c2ecf20Sopenharmony_ci // TODO: Deal with this by moving this extent to a new mft 22978c2ecf20Sopenharmony_ci // record or by starting a new extent in a new mft record, 22988c2ecf20Sopenharmony_ci // possibly by extending this extent partially and filling it 22998c2ecf20Sopenharmony_ci // and creating a new extent for the remainder, or by making 23008c2ecf20Sopenharmony_ci // other attributes non-resident and/or by moving other 23018c2ecf20Sopenharmony_ci // attributes out of this mft record. 23028c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 23038c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Not enough space in the mft " 23048c2ecf20Sopenharmony_ci "record for the extended attribute " 23058c2ecf20Sopenharmony_ci "record. This case is not " 23068c2ecf20Sopenharmony_ci "implemented yet."); 23078c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 23088c2ecf20Sopenharmony_ci goto undo_alloc; 23098c2ecf20Sopenharmony_ci } 23108c2ecf20Sopenharmony_ci mp_rebuilt = true; 23118c2ecf20Sopenharmony_ci /* Generate the mapping pairs array directly into the attr record. */ 23128c2ecf20Sopenharmony_ci err = ntfs_mapping_pairs_build(vol, (u8*)a + 23138c2ecf20Sopenharmony_ci le16_to_cpu(a->data.non_resident.mapping_pairs_offset), 23148c2ecf20Sopenharmony_ci mp_size, rl2, ll, -1, NULL); 23158c2ecf20Sopenharmony_ci if (unlikely(err)) { 23168c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 23178c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot extend allocation of " 23188c2ecf20Sopenharmony_ci "inode 0x%lx, attribute type 0x%x, " 23198c2ecf20Sopenharmony_ci "because building the mapping pairs " 23208c2ecf20Sopenharmony_ci "failed with error code %i.", vi->i_ino, 23218c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), err); 23228c2ecf20Sopenharmony_ci err = -EIO; 23238c2ecf20Sopenharmony_ci goto undo_alloc; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci /* Update the highest_vcn. */ 23268c2ecf20Sopenharmony_ci a->data.non_resident.highest_vcn = cpu_to_sle64((new_alloc_size >> 23278c2ecf20Sopenharmony_ci vol->cluster_size_bits) - 1); 23288c2ecf20Sopenharmony_ci /* 23298c2ecf20Sopenharmony_ci * We now have extended the allocated size of the attribute. Reflect 23308c2ecf20Sopenharmony_ci * this in the ntfs_inode structure and the attribute record. 23318c2ecf20Sopenharmony_ci */ 23328c2ecf20Sopenharmony_ci if (a->data.non_resident.lowest_vcn) { 23338c2ecf20Sopenharmony_ci /* 23348c2ecf20Sopenharmony_ci * We are not in the first attribute extent, switch to it, but 23358c2ecf20Sopenharmony_ci * first ensure the changes will make it to disk later. 23368c2ecf20Sopenharmony_ci */ 23378c2ecf20Sopenharmony_ci flush_dcache_mft_record_page(ctx->ntfs_ino); 23388c2ecf20Sopenharmony_ci mark_mft_record_dirty(ctx->ntfs_ino); 23398c2ecf20Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 23408c2ecf20Sopenharmony_ci err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len, 23418c2ecf20Sopenharmony_ci CASE_SENSITIVE, 0, NULL, 0, ctx); 23428c2ecf20Sopenharmony_ci if (unlikely(err)) 23438c2ecf20Sopenharmony_ci goto restore_undo_alloc; 23448c2ecf20Sopenharmony_ci /* @m is not used any more so no need to set it. */ 23458c2ecf20Sopenharmony_ci a = ctx->attr; 23468c2ecf20Sopenharmony_ci } 23478c2ecf20Sopenharmony_ci write_lock_irqsave(&ni->size_lock, flags); 23488c2ecf20Sopenharmony_ci ni->allocated_size = new_alloc_size; 23498c2ecf20Sopenharmony_ci a->data.non_resident.allocated_size = cpu_to_sle64(new_alloc_size); 23508c2ecf20Sopenharmony_ci /* 23518c2ecf20Sopenharmony_ci * FIXME: This would fail if @ni is a directory, $MFT, or an index, 23528c2ecf20Sopenharmony_ci * since those can have sparse/compressed set. For example can be 23538c2ecf20Sopenharmony_ci * set compressed even though it is not compressed itself and in that 23548c2ecf20Sopenharmony_ci * case the bit means that files are to be created compressed in the 23558c2ecf20Sopenharmony_ci * directory... At present this is ok as this code is only called for 23568c2ecf20Sopenharmony_ci * regular files, and only for their $DATA attribute(s). 23578c2ecf20Sopenharmony_ci * FIXME: The calculation is wrong if we created a hole above. For now 23588c2ecf20Sopenharmony_ci * it does not matter as we never create holes. 23598c2ecf20Sopenharmony_ci */ 23608c2ecf20Sopenharmony_ci if (NInoSparse(ni) || NInoCompressed(ni)) { 23618c2ecf20Sopenharmony_ci ni->itype.compressed.size += new_alloc_size - allocated_size; 23628c2ecf20Sopenharmony_ci a->data.non_resident.compressed_size = 23638c2ecf20Sopenharmony_ci cpu_to_sle64(ni->itype.compressed.size); 23648c2ecf20Sopenharmony_ci vi->i_blocks = ni->itype.compressed.size >> 9; 23658c2ecf20Sopenharmony_ci } else 23668c2ecf20Sopenharmony_ci vi->i_blocks = new_alloc_size >> 9; 23678c2ecf20Sopenharmony_ci write_unlock_irqrestore(&ni->size_lock, flags); 23688c2ecf20Sopenharmony_cialloc_done: 23698c2ecf20Sopenharmony_ci if (new_data_size >= 0) { 23708c2ecf20Sopenharmony_ci BUG_ON(new_data_size < 23718c2ecf20Sopenharmony_ci sle64_to_cpu(a->data.non_resident.data_size)); 23728c2ecf20Sopenharmony_ci a->data.non_resident.data_size = cpu_to_sle64(new_data_size); 23738c2ecf20Sopenharmony_ci } 23748c2ecf20Sopenharmony_ciflush_done: 23758c2ecf20Sopenharmony_ci /* Ensure the changes make it to disk. */ 23768c2ecf20Sopenharmony_ci flush_dcache_mft_record_page(ctx->ntfs_ino); 23778c2ecf20Sopenharmony_ci mark_mft_record_dirty(ctx->ntfs_ino); 23788c2ecf20Sopenharmony_cidone: 23798c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 23808c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 23818c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 23828c2ecf20Sopenharmony_ci ntfs_debug("Done, new_allocated_size 0x%llx.", 23838c2ecf20Sopenharmony_ci (unsigned long long)new_alloc_size); 23848c2ecf20Sopenharmony_ci return new_alloc_size; 23858c2ecf20Sopenharmony_cirestore_undo_alloc: 23868c2ecf20Sopenharmony_ci if (start < 0 || start >= allocated_size) 23878c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Cannot complete extension of allocation " 23888c2ecf20Sopenharmony_ci "of inode 0x%lx, attribute type 0x%x, because " 23898c2ecf20Sopenharmony_ci "lookup of first attribute extent failed with " 23908c2ecf20Sopenharmony_ci "error code %i.", vi->i_ino, 23918c2ecf20Sopenharmony_ci (unsigned)le32_to_cpu(ni->type), err); 23928c2ecf20Sopenharmony_ci if (err == -ENOENT) 23938c2ecf20Sopenharmony_ci err = -EIO; 23948c2ecf20Sopenharmony_ci ntfs_attr_reinit_search_ctx(ctx); 23958c2ecf20Sopenharmony_ci if (ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE, 23968c2ecf20Sopenharmony_ci allocated_size >> vol->cluster_size_bits, NULL, 0, 23978c2ecf20Sopenharmony_ci ctx)) { 23988c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to find last attribute extent of " 23998c2ecf20Sopenharmony_ci "attribute in error code path. Run chkdsk to " 24008c2ecf20Sopenharmony_ci "recover."); 24018c2ecf20Sopenharmony_ci write_lock_irqsave(&ni->size_lock, flags); 24028c2ecf20Sopenharmony_ci ni->allocated_size = new_alloc_size; 24038c2ecf20Sopenharmony_ci /* 24048c2ecf20Sopenharmony_ci * FIXME: This would fail if @ni is a directory... See above. 24058c2ecf20Sopenharmony_ci * FIXME: The calculation is wrong if we created a hole above. 24068c2ecf20Sopenharmony_ci * For now it does not matter as we never create holes. 24078c2ecf20Sopenharmony_ci */ 24088c2ecf20Sopenharmony_ci if (NInoSparse(ni) || NInoCompressed(ni)) { 24098c2ecf20Sopenharmony_ci ni->itype.compressed.size += new_alloc_size - 24108c2ecf20Sopenharmony_ci allocated_size; 24118c2ecf20Sopenharmony_ci vi->i_blocks = ni->itype.compressed.size >> 9; 24128c2ecf20Sopenharmony_ci } else 24138c2ecf20Sopenharmony_ci vi->i_blocks = new_alloc_size >> 9; 24148c2ecf20Sopenharmony_ci write_unlock_irqrestore(&ni->size_lock, flags); 24158c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 24168c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 24178c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 24188c2ecf20Sopenharmony_ci /* 24198c2ecf20Sopenharmony_ci * The only thing that is now wrong is the allocated size of the 24208c2ecf20Sopenharmony_ci * base attribute extent which chkdsk should be able to fix. 24218c2ecf20Sopenharmony_ci */ 24228c2ecf20Sopenharmony_ci NVolSetErrors(vol); 24238c2ecf20Sopenharmony_ci return err; 24248c2ecf20Sopenharmony_ci } 24258c2ecf20Sopenharmony_ci ctx->attr->data.non_resident.highest_vcn = cpu_to_sle64( 24268c2ecf20Sopenharmony_ci (allocated_size >> vol->cluster_size_bits) - 1); 24278c2ecf20Sopenharmony_ciundo_alloc: 24288c2ecf20Sopenharmony_ci ll = allocated_size >> vol->cluster_size_bits; 24298c2ecf20Sopenharmony_ci if (ntfs_cluster_free(ni, ll, -1, ctx) < 0) { 24308c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to release allocated cluster(s) " 24318c2ecf20Sopenharmony_ci "in error code path. Run chkdsk to recover " 24328c2ecf20Sopenharmony_ci "the lost cluster(s)."); 24338c2ecf20Sopenharmony_ci NVolSetErrors(vol); 24348c2ecf20Sopenharmony_ci } 24358c2ecf20Sopenharmony_ci m = ctx->mrec; 24368c2ecf20Sopenharmony_ci a = ctx->attr; 24378c2ecf20Sopenharmony_ci /* 24388c2ecf20Sopenharmony_ci * If the runlist truncation fails and/or the search context is no 24398c2ecf20Sopenharmony_ci * longer valid, we cannot resize the attribute record or build the 24408c2ecf20Sopenharmony_ci * mapping pairs array thus we mark the inode bad so that no access to 24418c2ecf20Sopenharmony_ci * the freed clusters can happen. 24428c2ecf20Sopenharmony_ci */ 24438c2ecf20Sopenharmony_ci if (ntfs_rl_truncate_nolock(vol, &ni->runlist, ll) || IS_ERR(m)) { 24448c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to %s in error code path. Run " 24458c2ecf20Sopenharmony_ci "chkdsk to recover.", IS_ERR(m) ? 24468c2ecf20Sopenharmony_ci "restore attribute search context" : 24478c2ecf20Sopenharmony_ci "truncate attribute runlist"); 24488c2ecf20Sopenharmony_ci NVolSetErrors(vol); 24498c2ecf20Sopenharmony_ci } else if (mp_rebuilt) { 24508c2ecf20Sopenharmony_ci if (ntfs_attr_record_resize(m, a, attr_len)) { 24518c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to restore attribute " 24528c2ecf20Sopenharmony_ci "record in error code path. Run " 24538c2ecf20Sopenharmony_ci "chkdsk to recover."); 24548c2ecf20Sopenharmony_ci NVolSetErrors(vol); 24558c2ecf20Sopenharmony_ci } else /* if (success) */ { 24568c2ecf20Sopenharmony_ci if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu( 24578c2ecf20Sopenharmony_ci a->data.non_resident. 24588c2ecf20Sopenharmony_ci mapping_pairs_offset), attr_len - 24598c2ecf20Sopenharmony_ci le16_to_cpu(a->data.non_resident. 24608c2ecf20Sopenharmony_ci mapping_pairs_offset), rl2, ll, -1, 24618c2ecf20Sopenharmony_ci NULL)) { 24628c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to restore " 24638c2ecf20Sopenharmony_ci "mapping pairs array in error " 24648c2ecf20Sopenharmony_ci "code path. Run chkdsk to " 24658c2ecf20Sopenharmony_ci "recover."); 24668c2ecf20Sopenharmony_ci NVolSetErrors(vol); 24678c2ecf20Sopenharmony_ci } 24688c2ecf20Sopenharmony_ci flush_dcache_mft_record_page(ctx->ntfs_ino); 24698c2ecf20Sopenharmony_ci mark_mft_record_dirty(ctx->ntfs_ino); 24708c2ecf20Sopenharmony_ci } 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_cierr_out: 24738c2ecf20Sopenharmony_ci if (ctx) 24748c2ecf20Sopenharmony_ci ntfs_attr_put_search_ctx(ctx); 24758c2ecf20Sopenharmony_ci if (m) 24768c2ecf20Sopenharmony_ci unmap_mft_record(base_ni); 24778c2ecf20Sopenharmony_ci up_write(&ni->runlist.lock); 24788c2ecf20Sopenharmony_ciconv_err_out: 24798c2ecf20Sopenharmony_ci ntfs_debug("Failed. Returning error code %i.", err); 24808c2ecf20Sopenharmony_ci return err; 24818c2ecf20Sopenharmony_ci} 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci/** 24848c2ecf20Sopenharmony_ci * ntfs_attr_set - fill (a part of) an attribute with a byte 24858c2ecf20Sopenharmony_ci * @ni: ntfs inode describing the attribute to fill 24868c2ecf20Sopenharmony_ci * @ofs: offset inside the attribute at which to start to fill 24878c2ecf20Sopenharmony_ci * @cnt: number of bytes to fill 24888c2ecf20Sopenharmony_ci * @val: the unsigned 8-bit value with which to fill the attribute 24898c2ecf20Sopenharmony_ci * 24908c2ecf20Sopenharmony_ci * Fill @cnt bytes of the attribute described by the ntfs inode @ni starting at 24918c2ecf20Sopenharmony_ci * byte offset @ofs inside the attribute with the constant byte @val. 24928c2ecf20Sopenharmony_ci * 24938c2ecf20Sopenharmony_ci * This function is effectively like memset() applied to an ntfs attribute. 24948c2ecf20Sopenharmony_ci * Note thie function actually only operates on the page cache pages belonging 24958c2ecf20Sopenharmony_ci * to the ntfs attribute and it marks them dirty after doing the memset(). 24968c2ecf20Sopenharmony_ci * Thus it relies on the vm dirty page write code paths to cause the modified 24978c2ecf20Sopenharmony_ci * pages to be written to the mft record/disk. 24988c2ecf20Sopenharmony_ci * 24998c2ecf20Sopenharmony_ci * Return 0 on success and -errno on error. An error code of -ESPIPE means 25008c2ecf20Sopenharmony_ci * that @ofs + @cnt were outside the end of the attribute and no write was 25018c2ecf20Sopenharmony_ci * performed. 25028c2ecf20Sopenharmony_ci */ 25038c2ecf20Sopenharmony_ciint ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val) 25048c2ecf20Sopenharmony_ci{ 25058c2ecf20Sopenharmony_ci ntfs_volume *vol = ni->vol; 25068c2ecf20Sopenharmony_ci struct address_space *mapping; 25078c2ecf20Sopenharmony_ci struct page *page; 25088c2ecf20Sopenharmony_ci u8 *kaddr; 25098c2ecf20Sopenharmony_ci pgoff_t idx, end; 25108c2ecf20Sopenharmony_ci unsigned start_ofs, end_ofs, size; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci ntfs_debug("Entering for ofs 0x%llx, cnt 0x%llx, val 0x%hx.", 25138c2ecf20Sopenharmony_ci (long long)ofs, (long long)cnt, val); 25148c2ecf20Sopenharmony_ci BUG_ON(ofs < 0); 25158c2ecf20Sopenharmony_ci BUG_ON(cnt < 0); 25168c2ecf20Sopenharmony_ci if (!cnt) 25178c2ecf20Sopenharmony_ci goto done; 25188c2ecf20Sopenharmony_ci /* 25198c2ecf20Sopenharmony_ci * FIXME: Compressed and encrypted attributes are not supported when 25208c2ecf20Sopenharmony_ci * writing and we should never have gotten here for them. 25218c2ecf20Sopenharmony_ci */ 25228c2ecf20Sopenharmony_ci BUG_ON(NInoCompressed(ni)); 25238c2ecf20Sopenharmony_ci BUG_ON(NInoEncrypted(ni)); 25248c2ecf20Sopenharmony_ci mapping = VFS_I(ni)->i_mapping; 25258c2ecf20Sopenharmony_ci /* Work out the starting index and page offset. */ 25268c2ecf20Sopenharmony_ci idx = ofs >> PAGE_SHIFT; 25278c2ecf20Sopenharmony_ci start_ofs = ofs & ~PAGE_MASK; 25288c2ecf20Sopenharmony_ci /* Work out the ending index and page offset. */ 25298c2ecf20Sopenharmony_ci end = ofs + cnt; 25308c2ecf20Sopenharmony_ci end_ofs = end & ~PAGE_MASK; 25318c2ecf20Sopenharmony_ci /* If the end is outside the inode size return -ESPIPE. */ 25328c2ecf20Sopenharmony_ci if (unlikely(end > i_size_read(VFS_I(ni)))) { 25338c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Request exceeds end of attribute."); 25348c2ecf20Sopenharmony_ci return -ESPIPE; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci end >>= PAGE_SHIFT; 25378c2ecf20Sopenharmony_ci /* If there is a first partial page, need to do it the slow way. */ 25388c2ecf20Sopenharmony_ci if (start_ofs) { 25398c2ecf20Sopenharmony_ci page = read_mapping_page(mapping, idx, NULL); 25408c2ecf20Sopenharmony_ci if (IS_ERR(page)) { 25418c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to read first partial " 25428c2ecf20Sopenharmony_ci "page (error, index 0x%lx).", idx); 25438c2ecf20Sopenharmony_ci return PTR_ERR(page); 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci /* 25468c2ecf20Sopenharmony_ci * If the last page is the same as the first page, need to 25478c2ecf20Sopenharmony_ci * limit the write to the end offset. 25488c2ecf20Sopenharmony_ci */ 25498c2ecf20Sopenharmony_ci size = PAGE_SIZE; 25508c2ecf20Sopenharmony_ci if (idx == end) 25518c2ecf20Sopenharmony_ci size = end_ofs; 25528c2ecf20Sopenharmony_ci kaddr = kmap_atomic(page); 25538c2ecf20Sopenharmony_ci memset(kaddr + start_ofs, val, size - start_ofs); 25548c2ecf20Sopenharmony_ci flush_dcache_page(page); 25558c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 25568c2ecf20Sopenharmony_ci set_page_dirty(page); 25578c2ecf20Sopenharmony_ci put_page(page); 25588c2ecf20Sopenharmony_ci balance_dirty_pages_ratelimited(mapping); 25598c2ecf20Sopenharmony_ci cond_resched(); 25608c2ecf20Sopenharmony_ci if (idx == end) 25618c2ecf20Sopenharmony_ci goto done; 25628c2ecf20Sopenharmony_ci idx++; 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci /* Do the whole pages the fast way. */ 25658c2ecf20Sopenharmony_ci for (; idx < end; idx++) { 25668c2ecf20Sopenharmony_ci /* Find or create the current page. (The page is locked.) */ 25678c2ecf20Sopenharmony_ci page = grab_cache_page(mapping, idx); 25688c2ecf20Sopenharmony_ci if (unlikely(!page)) { 25698c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Insufficient memory to grab " 25708c2ecf20Sopenharmony_ci "page (index 0x%lx).", idx); 25718c2ecf20Sopenharmony_ci return -ENOMEM; 25728c2ecf20Sopenharmony_ci } 25738c2ecf20Sopenharmony_ci kaddr = kmap_atomic(page); 25748c2ecf20Sopenharmony_ci memset(kaddr, val, PAGE_SIZE); 25758c2ecf20Sopenharmony_ci flush_dcache_page(page); 25768c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 25778c2ecf20Sopenharmony_ci /* 25788c2ecf20Sopenharmony_ci * If the page has buffers, mark them uptodate since buffer 25798c2ecf20Sopenharmony_ci * state and not page state is definitive in 2.6 kernels. 25808c2ecf20Sopenharmony_ci */ 25818c2ecf20Sopenharmony_ci if (page_has_buffers(page)) { 25828c2ecf20Sopenharmony_ci struct buffer_head *bh, *head; 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci bh = head = page_buffers(page); 25858c2ecf20Sopenharmony_ci do { 25868c2ecf20Sopenharmony_ci set_buffer_uptodate(bh); 25878c2ecf20Sopenharmony_ci } while ((bh = bh->b_this_page) != head); 25888c2ecf20Sopenharmony_ci } 25898c2ecf20Sopenharmony_ci /* Now that buffers are uptodate, set the page uptodate, too. */ 25908c2ecf20Sopenharmony_ci SetPageUptodate(page); 25918c2ecf20Sopenharmony_ci /* 25928c2ecf20Sopenharmony_ci * Set the page and all its buffers dirty and mark the inode 25938c2ecf20Sopenharmony_ci * dirty, too. The VM will write the page later on. 25948c2ecf20Sopenharmony_ci */ 25958c2ecf20Sopenharmony_ci set_page_dirty(page); 25968c2ecf20Sopenharmony_ci /* Finally unlock and release the page. */ 25978c2ecf20Sopenharmony_ci unlock_page(page); 25988c2ecf20Sopenharmony_ci put_page(page); 25998c2ecf20Sopenharmony_ci balance_dirty_pages_ratelimited(mapping); 26008c2ecf20Sopenharmony_ci cond_resched(); 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci /* If there is a last partial page, need to do it the slow way. */ 26038c2ecf20Sopenharmony_ci if (end_ofs) { 26048c2ecf20Sopenharmony_ci page = read_mapping_page(mapping, idx, NULL); 26058c2ecf20Sopenharmony_ci if (IS_ERR(page)) { 26068c2ecf20Sopenharmony_ci ntfs_error(vol->sb, "Failed to read last partial page " 26078c2ecf20Sopenharmony_ci "(error, index 0x%lx).", idx); 26088c2ecf20Sopenharmony_ci return PTR_ERR(page); 26098c2ecf20Sopenharmony_ci } 26108c2ecf20Sopenharmony_ci kaddr = kmap_atomic(page); 26118c2ecf20Sopenharmony_ci memset(kaddr, val, end_ofs); 26128c2ecf20Sopenharmony_ci flush_dcache_page(page); 26138c2ecf20Sopenharmony_ci kunmap_atomic(kaddr); 26148c2ecf20Sopenharmony_ci set_page_dirty(page); 26158c2ecf20Sopenharmony_ci put_page(page); 26168c2ecf20Sopenharmony_ci balance_dirty_pages_ratelimited(mapping); 26178c2ecf20Sopenharmony_ci cond_resched(); 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_cidone: 26208c2ecf20Sopenharmony_ci ntfs_debug("Done."); 26218c2ecf20Sopenharmony_ci return 0; 26228c2ecf20Sopenharmony_ci} 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci#endif /* NTFS_RW */ 2625