1a1d312deSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27e0af978SYang Li /*
31da177e4SLinus Torvalds * attrib.c - NTFS attribute operations. Part of the Linux-NTFS project.
41da177e4SLinus Torvalds *
545d95bcdSAnton Altaparmakov * Copyright (c) 2001-2012 Anton Altaparmakov and Tuxera Inc.
61da177e4SLinus Torvalds * Copyright (c) 2002 Richard Russon
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds
91da177e4SLinus Torvalds #include <linux/buffer_head.h>
1029b89905SAnton Altaparmakov #include <linux/sched.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
121ef334d3SAnton Altaparmakov #include <linux/swap.h>
1329b89905SAnton Altaparmakov #include <linux/writeback.h>
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds #include "attrib.h"
161da177e4SLinus Torvalds #include "debug.h"
171da177e4SLinus Torvalds #include "layout.h"
182bfb4fffSAnton Altaparmakov #include "lcnalloc.h"
192bfb4fffSAnton Altaparmakov #include "malloc.h"
201da177e4SLinus Torvalds #include "mft.h"
211da177e4SLinus Torvalds #include "ntfs.h"
221da177e4SLinus Torvalds #include "types.h"
231da177e4SLinus Torvalds
241da177e4SLinus Torvalds /**
25b6ad6c52SAnton Altaparmakov * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode
261da177e4SLinus Torvalds * @ni: ntfs inode for which to map (part of) a runlist
271da177e4SLinus Torvalds * @vcn: map runlist part containing this vcn
28fd9d6367SAnton Altaparmakov * @ctx: active attribute search context if present or NULL if not
291da177e4SLinus Torvalds *
301da177e4SLinus Torvalds * Map the part of a runlist containing the @vcn of the ntfs inode @ni.
311da177e4SLinus Torvalds *
32fd9d6367SAnton Altaparmakov * If @ctx is specified, it is an active search context of @ni and its base mft
33fd9d6367SAnton Altaparmakov * record. This is needed when ntfs_map_runlist_nolock() encounters unmapped
34fd9d6367SAnton Altaparmakov * runlist fragments and allows their mapping. If you do not have the mft
35fd9d6367SAnton Altaparmakov * record mapped, you can specify @ctx as NULL and ntfs_map_runlist_nolock()
36fd9d6367SAnton Altaparmakov * will perform the necessary mapping and unmapping.
37fd9d6367SAnton Altaparmakov *
38fd9d6367SAnton Altaparmakov * Note, ntfs_map_runlist_nolock() saves the state of @ctx on entry and
39fd9d6367SAnton Altaparmakov * restores it before returning. Thus, @ctx will be left pointing to the same
40fd9d6367SAnton Altaparmakov * attribute on return as on entry. However, the actual pointers in @ctx may
41fd9d6367SAnton Altaparmakov * point to different memory locations on return, so you must remember to reset
42fd9d6367SAnton Altaparmakov * any cached pointers from the @ctx, i.e. after the call to
43fd9d6367SAnton Altaparmakov * ntfs_map_runlist_nolock(), you will probably want to do:
44fd9d6367SAnton Altaparmakov * m = ctx->mrec;
45fd9d6367SAnton Altaparmakov * a = ctx->attr;
46fd9d6367SAnton Altaparmakov * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
47fd9d6367SAnton Altaparmakov * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
48fd9d6367SAnton Altaparmakov *
494757d7dfSAnton Altaparmakov * Return 0 on success and -errno on error. There is one special error code
504757d7dfSAnton Altaparmakov * which is not an error as such. This is -ENOENT. It means that @vcn is out
514757d7dfSAnton Altaparmakov * of bounds of the runlist.
521da177e4SLinus Torvalds *
532983d1bdSAnton Altaparmakov * Note the runlist can be NULL after this function returns if @vcn is zero and
542983d1bdSAnton Altaparmakov * the attribute has zero allocated size, i.e. there simply is no runlist.
552983d1bdSAnton Altaparmakov *
56fd9d6367SAnton Altaparmakov * WARNING: If @ctx is supplied, regardless of whether success or failure is
57c49c3111SRichard Knutsson * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx
58fd9d6367SAnton Altaparmakov * is no longer valid, i.e. you need to either call
59fd9d6367SAnton Altaparmakov * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
60fd9d6367SAnton Altaparmakov * In that case PTR_ERR(@ctx->mrec) will give you the error code for
61fd9d6367SAnton Altaparmakov * why the mapping of the old inode failed.
62fd9d6367SAnton Altaparmakov *
63fd9d6367SAnton Altaparmakov * Locking: - The runlist described by @ni must be locked for writing on entry
64fd9d6367SAnton Altaparmakov * and is locked on return. Note the runlist will be modified.
65fd9d6367SAnton Altaparmakov * - If @ctx is NULL, the base mft record of @ni must not be mapped on
66fd9d6367SAnton Altaparmakov * entry and it will be left unmapped on return.
67fd9d6367SAnton Altaparmakov * - If @ctx is not NULL, the base mft record must be mapped on entry
68fd9d6367SAnton Altaparmakov * and it will be left mapped on return.
691da177e4SLinus Torvalds */
ntfs_map_runlist_nolock(ntfs_inode * ni,VCN vcn,ntfs_attr_search_ctx * ctx)70fd9d6367SAnton Altaparmakov int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, ntfs_attr_search_ctx *ctx)
711da177e4SLinus Torvalds {
724757d7dfSAnton Altaparmakov VCN end_vcn;
73fd9d6367SAnton Altaparmakov unsigned long flags;
741da177e4SLinus Torvalds ntfs_inode *base_ni;
754757d7dfSAnton Altaparmakov MFT_RECORD *m;
764757d7dfSAnton Altaparmakov ATTR_RECORD *a;
77b6ad6c52SAnton Altaparmakov runlist_element *rl;
78fd9d6367SAnton Altaparmakov struct page *put_this_page = NULL;
791da177e4SLinus Torvalds int err = 0;
80c49c3111SRichard Knutsson bool ctx_is_temporary, ctx_needs_reset;
81dda65b94SAnton Altaparmakov ntfs_attr_search_ctx old_ctx = { NULL, };
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
841da177e4SLinus Torvalds (unsigned long long)vcn);
851da177e4SLinus Torvalds if (!NInoAttr(ni))
861da177e4SLinus Torvalds base_ni = ni;
871da177e4SLinus Torvalds else
881da177e4SLinus Torvalds base_ni = ni->ext.base_ntfs_ino;
89fd9d6367SAnton Altaparmakov if (!ctx) {
90c49c3111SRichard Knutsson ctx_is_temporary = ctx_needs_reset = true;
914757d7dfSAnton Altaparmakov m = map_mft_record(base_ni);
924757d7dfSAnton Altaparmakov if (IS_ERR(m))
934757d7dfSAnton Altaparmakov return PTR_ERR(m);
944757d7dfSAnton Altaparmakov ctx = ntfs_attr_get_search_ctx(base_ni, m);
951da177e4SLinus Torvalds if (unlikely(!ctx)) {
961da177e4SLinus Torvalds err = -ENOMEM;
971da177e4SLinus Torvalds goto err_out;
981da177e4SLinus Torvalds }
99fd9d6367SAnton Altaparmakov } else {
100fd9d6367SAnton Altaparmakov VCN allocated_size_vcn;
101fd9d6367SAnton Altaparmakov
102fd9d6367SAnton Altaparmakov BUG_ON(IS_ERR(ctx->mrec));
103fd9d6367SAnton Altaparmakov a = ctx->attr;
104fd9d6367SAnton Altaparmakov BUG_ON(!a->non_resident);
105c49c3111SRichard Knutsson ctx_is_temporary = false;
106fd9d6367SAnton Altaparmakov end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
107fd9d6367SAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
108fd9d6367SAnton Altaparmakov allocated_size_vcn = ni->allocated_size >>
109fd9d6367SAnton Altaparmakov ni->vol->cluster_size_bits;
110fd9d6367SAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
111fd9d6367SAnton Altaparmakov if (!a->data.non_resident.lowest_vcn && end_vcn <= 0)
112fd9d6367SAnton Altaparmakov end_vcn = allocated_size_vcn - 1;
113fd9d6367SAnton Altaparmakov /*
114fd9d6367SAnton Altaparmakov * If we already have the attribute extent containing @vcn in
115fd9d6367SAnton Altaparmakov * @ctx, no need to look it up again. We slightly cheat in
116fd9d6367SAnton Altaparmakov * that if vcn exceeds the allocated size, we will refuse to
117fd9d6367SAnton Altaparmakov * map the runlist below, so there is definitely no need to get
118fd9d6367SAnton Altaparmakov * the right attribute extent.
119fd9d6367SAnton Altaparmakov */
120fd9d6367SAnton Altaparmakov if (vcn >= allocated_size_vcn || (a->type == ni->type &&
121fd9d6367SAnton Altaparmakov a->name_length == ni->name_len &&
122fd9d6367SAnton Altaparmakov !memcmp((u8*)a + le16_to_cpu(a->name_offset),
123fd9d6367SAnton Altaparmakov ni->name, ni->name_len) &&
124fd9d6367SAnton Altaparmakov sle64_to_cpu(a->data.non_resident.lowest_vcn)
125fd9d6367SAnton Altaparmakov <= vcn && end_vcn >= vcn))
126c49c3111SRichard Knutsson ctx_needs_reset = false;
127fd9d6367SAnton Altaparmakov else {
128fd9d6367SAnton Altaparmakov /* Save the old search context. */
129fd9d6367SAnton Altaparmakov old_ctx = *ctx;
130fd9d6367SAnton Altaparmakov /*
131fd9d6367SAnton Altaparmakov * If the currently mapped (extent) inode is not the
132fd9d6367SAnton Altaparmakov * base inode we will unmap it when we reinitialize the
133fd9d6367SAnton Altaparmakov * search context which means we need to get a
134fd9d6367SAnton Altaparmakov * reference to the page containing the mapped mft
135fd9d6367SAnton Altaparmakov * record so we do not accidentally drop changes to the
136fd9d6367SAnton Altaparmakov * mft record when it has not been marked dirty yet.
137fd9d6367SAnton Altaparmakov */
138fd9d6367SAnton Altaparmakov if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino !=
139fd9d6367SAnton Altaparmakov old_ctx.base_ntfs_ino) {
140fd9d6367SAnton Altaparmakov put_this_page = old_ctx.ntfs_ino->page;
14109cbfeafSKirill A. Shutemov get_page(put_this_page);
142fd9d6367SAnton Altaparmakov }
143fd9d6367SAnton Altaparmakov /*
144fd9d6367SAnton Altaparmakov * Reinitialize the search context so we can lookup the
145fd9d6367SAnton Altaparmakov * needed attribute extent.
146fd9d6367SAnton Altaparmakov */
147fd9d6367SAnton Altaparmakov ntfs_attr_reinit_search_ctx(ctx);
148c49c3111SRichard Knutsson ctx_needs_reset = true;
149fd9d6367SAnton Altaparmakov }
150fd9d6367SAnton Altaparmakov }
151fd9d6367SAnton Altaparmakov if (ctx_needs_reset) {
1521da177e4SLinus Torvalds err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
1531da177e4SLinus Torvalds CASE_SENSITIVE, vcn, NULL, 0, ctx);
1544757d7dfSAnton Altaparmakov if (unlikely(err)) {
1554757d7dfSAnton Altaparmakov if (err == -ENOENT)
1564757d7dfSAnton Altaparmakov err = -EIO;
1574757d7dfSAnton Altaparmakov goto err_out;
1584757d7dfSAnton Altaparmakov }
159fd9d6367SAnton Altaparmakov BUG_ON(!ctx->attr->non_resident);
160fd9d6367SAnton Altaparmakov }
1614757d7dfSAnton Altaparmakov a = ctx->attr;
1624757d7dfSAnton Altaparmakov /*
1634757d7dfSAnton Altaparmakov * Only decompress the mapping pairs if @vcn is inside it. Otherwise
1644757d7dfSAnton Altaparmakov * we get into problems when we try to map an out of bounds vcn because
1654757d7dfSAnton Altaparmakov * we then try to map the already mapped runlist fragment and
1664757d7dfSAnton Altaparmakov * ntfs_mapping_pairs_decompress() fails.
1674757d7dfSAnton Altaparmakov */
1684757d7dfSAnton Altaparmakov end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1;
169ebab8990SAnton Altaparmakov if (unlikely(vcn && vcn >= end_vcn)) {
1704757d7dfSAnton Altaparmakov err = -ENOENT;
1714757d7dfSAnton Altaparmakov goto err_out;
1724757d7dfSAnton Altaparmakov }
1734757d7dfSAnton Altaparmakov rl = ntfs_mapping_pairs_decompress(ni->vol, a, ni->runlist.rl);
1741da177e4SLinus Torvalds if (IS_ERR(rl))
1751da177e4SLinus Torvalds err = PTR_ERR(rl);
1761da177e4SLinus Torvalds else
1771da177e4SLinus Torvalds ni->runlist.rl = rl;
1781da177e4SLinus Torvalds err_out:
179fd9d6367SAnton Altaparmakov if (ctx_is_temporary) {
1804757d7dfSAnton Altaparmakov if (likely(ctx))
1814757d7dfSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
1821da177e4SLinus Torvalds unmap_mft_record(base_ni);
183fd9d6367SAnton Altaparmakov } else if (ctx_needs_reset) {
184fd9d6367SAnton Altaparmakov /*
185fd9d6367SAnton Altaparmakov * If there is no attribute list, restoring the search context
18625985edcSLucas De Marchi * is accomplished simply by copying the saved context back over
187fd9d6367SAnton Altaparmakov * the caller supplied context. If there is an attribute list,
188fd9d6367SAnton Altaparmakov * things are more complicated as we need to deal with mapping
189fd9d6367SAnton Altaparmakov * of mft records and resulting potential changes in pointers.
190fd9d6367SAnton Altaparmakov */
191fd9d6367SAnton Altaparmakov if (NInoAttrList(base_ni)) {
192fd9d6367SAnton Altaparmakov /*
193fd9d6367SAnton Altaparmakov * If the currently mapped (extent) inode is not the
194fd9d6367SAnton Altaparmakov * one we had before, we need to unmap it and map the
195fd9d6367SAnton Altaparmakov * old one.
196fd9d6367SAnton Altaparmakov */
197fd9d6367SAnton Altaparmakov if (ctx->ntfs_ino != old_ctx.ntfs_ino) {
198fd9d6367SAnton Altaparmakov /*
199fd9d6367SAnton Altaparmakov * If the currently mapped inode is not the
200fd9d6367SAnton Altaparmakov * base inode, unmap it.
201fd9d6367SAnton Altaparmakov */
202fd9d6367SAnton Altaparmakov if (ctx->base_ntfs_ino && ctx->ntfs_ino !=
203fd9d6367SAnton Altaparmakov ctx->base_ntfs_ino) {
204fd9d6367SAnton Altaparmakov unmap_extent_mft_record(ctx->ntfs_ino);
205fd9d6367SAnton Altaparmakov ctx->mrec = ctx->base_mrec;
206fd9d6367SAnton Altaparmakov BUG_ON(!ctx->mrec);
207fd9d6367SAnton Altaparmakov }
208fd9d6367SAnton Altaparmakov /*
209fd9d6367SAnton Altaparmakov * If the old mapped inode is not the base
210fd9d6367SAnton Altaparmakov * inode, map it.
211fd9d6367SAnton Altaparmakov */
212fd9d6367SAnton Altaparmakov if (old_ctx.base_ntfs_ino &&
213fd9d6367SAnton Altaparmakov old_ctx.ntfs_ino !=
214fd9d6367SAnton Altaparmakov old_ctx.base_ntfs_ino) {
215fd9d6367SAnton Altaparmakov retry_map:
216fd9d6367SAnton Altaparmakov ctx->mrec = map_mft_record(
217fd9d6367SAnton Altaparmakov old_ctx.ntfs_ino);
218fd9d6367SAnton Altaparmakov /*
219fd9d6367SAnton Altaparmakov * Something bad has happened. If out
220fd9d6367SAnton Altaparmakov * of memory retry till it succeeds.
221fd9d6367SAnton Altaparmakov * Any other errors are fatal and we
222fd9d6367SAnton Altaparmakov * return the error code in ctx->mrec.
223fd9d6367SAnton Altaparmakov * Let the caller deal with it... We
224fd9d6367SAnton Altaparmakov * just need to fudge things so the
225fd9d6367SAnton Altaparmakov * caller can reinit and/or put the
226fd9d6367SAnton Altaparmakov * search context safely.
227fd9d6367SAnton Altaparmakov */
228fd9d6367SAnton Altaparmakov if (IS_ERR(ctx->mrec)) {
229fd9d6367SAnton Altaparmakov if (PTR_ERR(ctx->mrec) ==
230fd9d6367SAnton Altaparmakov -ENOMEM) {
231fd9d6367SAnton Altaparmakov schedule();
232fd9d6367SAnton Altaparmakov goto retry_map;
233fd9d6367SAnton Altaparmakov } else
234fd9d6367SAnton Altaparmakov old_ctx.ntfs_ino =
235fd9d6367SAnton Altaparmakov old_ctx.
236fd9d6367SAnton Altaparmakov base_ntfs_ino;
237fd9d6367SAnton Altaparmakov }
238fd9d6367SAnton Altaparmakov }
239fd9d6367SAnton Altaparmakov }
240fd9d6367SAnton Altaparmakov /* Update the changed pointers in the saved context. */
241fd9d6367SAnton Altaparmakov if (ctx->mrec != old_ctx.mrec) {
242fd9d6367SAnton Altaparmakov if (!IS_ERR(ctx->mrec))
243fd9d6367SAnton Altaparmakov old_ctx.attr = (ATTR_RECORD*)(
244fd9d6367SAnton Altaparmakov (u8*)ctx->mrec +
245fd9d6367SAnton Altaparmakov ((u8*)old_ctx.attr -
246fd9d6367SAnton Altaparmakov (u8*)old_ctx.mrec));
247fd9d6367SAnton Altaparmakov old_ctx.mrec = ctx->mrec;
248fd9d6367SAnton Altaparmakov }
249fd9d6367SAnton Altaparmakov }
250fd9d6367SAnton Altaparmakov /* Restore the search context to the saved one. */
251fd9d6367SAnton Altaparmakov *ctx = old_ctx;
252fd9d6367SAnton Altaparmakov /*
253fd9d6367SAnton Altaparmakov * We drop the reference on the page we took earlier. In the
254fd9d6367SAnton Altaparmakov * case that IS_ERR(ctx->mrec) is true this means we might lose
255fd9d6367SAnton Altaparmakov * some changes to the mft record that had been made between
256fd9d6367SAnton Altaparmakov * the last time it was marked dirty/written out and now. This
257fd9d6367SAnton Altaparmakov * at this stage is not a problem as the mapping error is fatal
258fd9d6367SAnton Altaparmakov * enough that the mft record cannot be written out anyway and
259fd9d6367SAnton Altaparmakov * the caller is very likely to shutdown the whole inode
260fd9d6367SAnton Altaparmakov * immediately and mark the volume dirty for chkdsk to pick up
261fd9d6367SAnton Altaparmakov * the pieces anyway.
262fd9d6367SAnton Altaparmakov */
263fd9d6367SAnton Altaparmakov if (put_this_page)
26409cbfeafSKirill A. Shutemov put_page(put_this_page);
265fd9d6367SAnton Altaparmakov }
2661da177e4SLinus Torvalds return err;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds
2691da177e4SLinus Torvalds /**
270b6ad6c52SAnton Altaparmakov * ntfs_map_runlist - map (a part of) a runlist of an ntfs inode
271b6ad6c52SAnton Altaparmakov * @ni: ntfs inode for which to map (part of) a runlist
272b6ad6c52SAnton Altaparmakov * @vcn: map runlist part containing this vcn
273b6ad6c52SAnton Altaparmakov *
274b6ad6c52SAnton Altaparmakov * Map the part of a runlist containing the @vcn of the ntfs inode @ni.
275b6ad6c52SAnton Altaparmakov *
2764757d7dfSAnton Altaparmakov * Return 0 on success and -errno on error. There is one special error code
2774757d7dfSAnton Altaparmakov * which is not an error as such. This is -ENOENT. It means that @vcn is out
2784757d7dfSAnton Altaparmakov * of bounds of the runlist.
279b6ad6c52SAnton Altaparmakov *
280b6ad6c52SAnton Altaparmakov * Locking: - The runlist must be unlocked on entry and is unlocked on return.
281fd9d6367SAnton Altaparmakov * - This function takes the runlist lock for writing and may modify
282fd9d6367SAnton Altaparmakov * the runlist.
283b6ad6c52SAnton Altaparmakov */
ntfs_map_runlist(ntfs_inode * ni,VCN vcn)284b6ad6c52SAnton Altaparmakov int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
285b6ad6c52SAnton Altaparmakov {
286b6ad6c52SAnton Altaparmakov int err = 0;
287b6ad6c52SAnton Altaparmakov
288b6ad6c52SAnton Altaparmakov down_write(&ni->runlist.lock);
289b6ad6c52SAnton Altaparmakov /* Make sure someone else didn't do the work while we were sleeping. */
290b6ad6c52SAnton Altaparmakov if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <=
291b6ad6c52SAnton Altaparmakov LCN_RL_NOT_MAPPED))
292fd9d6367SAnton Altaparmakov err = ntfs_map_runlist_nolock(ni, vcn, NULL);
293b6ad6c52SAnton Altaparmakov up_write(&ni->runlist.lock);
294b6ad6c52SAnton Altaparmakov return err;
295b6ad6c52SAnton Altaparmakov }
296b6ad6c52SAnton Altaparmakov
297b6ad6c52SAnton Altaparmakov /**
298271849a9SAnton Altaparmakov * ntfs_attr_vcn_to_lcn_nolock - convert a vcn into a lcn given an ntfs inode
299271849a9SAnton Altaparmakov * @ni: ntfs inode of the attribute whose runlist to search
300271849a9SAnton Altaparmakov * @vcn: vcn to convert
301271849a9SAnton Altaparmakov * @write_locked: true if the runlist is locked for writing
302271849a9SAnton Altaparmakov *
303271849a9SAnton Altaparmakov * Find the virtual cluster number @vcn in the runlist of the ntfs attribute
304271849a9SAnton Altaparmakov * described by the ntfs inode @ni and return the corresponding logical cluster
305271849a9SAnton Altaparmakov * number (lcn).
306271849a9SAnton Altaparmakov *
307271849a9SAnton Altaparmakov * If the @vcn is not mapped yet, the attempt is made to map the attribute
308271849a9SAnton Altaparmakov * extent containing the @vcn and the vcn to lcn conversion is retried.
309271849a9SAnton Altaparmakov *
310271849a9SAnton Altaparmakov * If @write_locked is true the caller has locked the runlist for writing and
311271849a9SAnton Altaparmakov * if false for reading.
312271849a9SAnton Altaparmakov *
313271849a9SAnton Altaparmakov * Since lcns must be >= 0, we use negative return codes with special meaning:
314271849a9SAnton Altaparmakov *
315271849a9SAnton Altaparmakov * Return code Meaning / Description
316271849a9SAnton Altaparmakov * ==========================================
317271849a9SAnton Altaparmakov * LCN_HOLE Hole / not allocated on disk.
318271849a9SAnton Altaparmakov * LCN_ENOENT There is no such vcn in the runlist, i.e. @vcn is out of bounds.
319271849a9SAnton Altaparmakov * LCN_ENOMEM Not enough memory to map runlist.
320271849a9SAnton Altaparmakov * LCN_EIO Critical error (runlist/file is corrupt, i/o error, etc).
321271849a9SAnton Altaparmakov *
322271849a9SAnton Altaparmakov * Locking: - The runlist must be locked on entry and is left locked on return.
323c49c3111SRichard Knutsson * - If @write_locked is 'false', i.e. the runlist is locked for reading,
324271849a9SAnton Altaparmakov * the lock may be dropped inside the function so you cannot rely on
325271849a9SAnton Altaparmakov * the runlist still being the same when this function returns.
326271849a9SAnton Altaparmakov */
ntfs_attr_vcn_to_lcn_nolock(ntfs_inode * ni,const VCN vcn,const bool write_locked)327271849a9SAnton Altaparmakov LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn,
328c49c3111SRichard Knutsson const bool write_locked)
329271849a9SAnton Altaparmakov {
330271849a9SAnton Altaparmakov LCN lcn;
3312983d1bdSAnton Altaparmakov unsigned long flags;
332c49c3111SRichard Knutsson bool is_retry = false;
333271849a9SAnton Altaparmakov
33445d95bcdSAnton Altaparmakov BUG_ON(!ni);
335271849a9SAnton Altaparmakov ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.",
336271849a9SAnton Altaparmakov ni->mft_no, (unsigned long long)vcn,
337271849a9SAnton Altaparmakov write_locked ? "write" : "read");
338271849a9SAnton Altaparmakov BUG_ON(!NInoNonResident(ni));
339271849a9SAnton Altaparmakov BUG_ON(vcn < 0);
3402983d1bdSAnton Altaparmakov if (!ni->runlist.rl) {
3412983d1bdSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
3422983d1bdSAnton Altaparmakov if (!ni->allocated_size) {
3432983d1bdSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
3442983d1bdSAnton Altaparmakov return LCN_ENOENT;
3452983d1bdSAnton Altaparmakov }
3462983d1bdSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
3472983d1bdSAnton Altaparmakov }
348271849a9SAnton Altaparmakov retry_remap:
349271849a9SAnton Altaparmakov /* Convert vcn to lcn. If that fails map the runlist and retry once. */
350271849a9SAnton Altaparmakov lcn = ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn);
351271849a9SAnton Altaparmakov if (likely(lcn >= LCN_HOLE)) {
352271849a9SAnton Altaparmakov ntfs_debug("Done, lcn 0x%llx.", (long long)lcn);
353271849a9SAnton Altaparmakov return lcn;
354271849a9SAnton Altaparmakov }
355271849a9SAnton Altaparmakov if (lcn != LCN_RL_NOT_MAPPED) {
356271849a9SAnton Altaparmakov if (lcn != LCN_ENOENT)
357271849a9SAnton Altaparmakov lcn = LCN_EIO;
358271849a9SAnton Altaparmakov } else if (!is_retry) {
359271849a9SAnton Altaparmakov int err;
360271849a9SAnton Altaparmakov
361271849a9SAnton Altaparmakov if (!write_locked) {
362271849a9SAnton Altaparmakov up_read(&ni->runlist.lock);
363271849a9SAnton Altaparmakov down_write(&ni->runlist.lock);
364271849a9SAnton Altaparmakov if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) !=
365271849a9SAnton Altaparmakov LCN_RL_NOT_MAPPED)) {
366271849a9SAnton Altaparmakov up_write(&ni->runlist.lock);
367271849a9SAnton Altaparmakov down_read(&ni->runlist.lock);
368271849a9SAnton Altaparmakov goto retry_remap;
369271849a9SAnton Altaparmakov }
370271849a9SAnton Altaparmakov }
371fd9d6367SAnton Altaparmakov err = ntfs_map_runlist_nolock(ni, vcn, NULL);
372271849a9SAnton Altaparmakov if (!write_locked) {
373271849a9SAnton Altaparmakov up_write(&ni->runlist.lock);
374271849a9SAnton Altaparmakov down_read(&ni->runlist.lock);
375271849a9SAnton Altaparmakov }
376271849a9SAnton Altaparmakov if (likely(!err)) {
377c49c3111SRichard Knutsson is_retry = true;
378271849a9SAnton Altaparmakov goto retry_remap;
379271849a9SAnton Altaparmakov }
380271849a9SAnton Altaparmakov if (err == -ENOENT)
381271849a9SAnton Altaparmakov lcn = LCN_ENOENT;
382271849a9SAnton Altaparmakov else if (err == -ENOMEM)
383271849a9SAnton Altaparmakov lcn = LCN_ENOMEM;
384271849a9SAnton Altaparmakov else
385271849a9SAnton Altaparmakov lcn = LCN_EIO;
386271849a9SAnton Altaparmakov }
387271849a9SAnton Altaparmakov if (lcn != LCN_ENOENT)
388271849a9SAnton Altaparmakov ntfs_error(ni->vol->sb, "Failed with error code %lli.",
389271849a9SAnton Altaparmakov (long long)lcn);
390271849a9SAnton Altaparmakov return lcn;
391271849a9SAnton Altaparmakov }
392271849a9SAnton Altaparmakov
393271849a9SAnton Altaparmakov /**
394c0c1cc0eSAnton Altaparmakov * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode
3951da177e4SLinus Torvalds * @ni: ntfs inode describing the runlist to search
3961da177e4SLinus Torvalds * @vcn: vcn to find
39769b41e3cSAnton Altaparmakov * @ctx: active attribute search context if present or NULL if not
3981da177e4SLinus Torvalds *
3991da177e4SLinus Torvalds * Find the virtual cluster number @vcn in the runlist described by the ntfs
4001da177e4SLinus Torvalds * inode @ni and return the address of the runlist element containing the @vcn.
401b6ad6c52SAnton Altaparmakov *
402c0c1cc0eSAnton Altaparmakov * If the @vcn is not mapped yet, the attempt is made to map the attribute
403c0c1cc0eSAnton Altaparmakov * extent containing the @vcn and the vcn to lcn conversion is retried.
404c0c1cc0eSAnton Altaparmakov *
40569b41e3cSAnton Altaparmakov * If @ctx is specified, it is an active search context of @ni and its base mft
40669b41e3cSAnton Altaparmakov * record. This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped
40769b41e3cSAnton Altaparmakov * runlist fragments and allows their mapping. If you do not have the mft
40869b41e3cSAnton Altaparmakov * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock()
40969b41e3cSAnton Altaparmakov * will perform the necessary mapping and unmapping.
4101da177e4SLinus Torvalds *
41169b41e3cSAnton Altaparmakov * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and
41269b41e3cSAnton Altaparmakov * restores it before returning. Thus, @ctx will be left pointing to the same
41369b41e3cSAnton Altaparmakov * attribute on return as on entry. However, the actual pointers in @ctx may
41469b41e3cSAnton Altaparmakov * point to different memory locations on return, so you must remember to reset
41569b41e3cSAnton Altaparmakov * any cached pointers from the @ctx, i.e. after the call to
41669b41e3cSAnton Altaparmakov * ntfs_attr_find_vcn_nolock(), you will probably want to do:
41769b41e3cSAnton Altaparmakov * m = ctx->mrec;
41869b41e3cSAnton Altaparmakov * a = ctx->attr;
41969b41e3cSAnton Altaparmakov * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
42069b41e3cSAnton Altaparmakov * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
4211da177e4SLinus Torvalds * Note you need to distinguish between the lcn of the returned runlist element
4221da177e4SLinus Torvalds * being >= 0 and LCN_HOLE. In the later case you have to return zeroes on
4231da177e4SLinus Torvalds * read and allocate clusters on write.
4241da177e4SLinus Torvalds *
4251da177e4SLinus Torvalds * Return the runlist element containing the @vcn on success and
4261da177e4SLinus Torvalds * ERR_PTR(-errno) on error. You need to test the return value with IS_ERR()
4271da177e4SLinus Torvalds * to decide if the return is success or failure and PTR_ERR() to get to the
4281da177e4SLinus Torvalds * error code if IS_ERR() is true.
4291da177e4SLinus Torvalds *
4301da177e4SLinus Torvalds * The possible error return codes are:
4311da177e4SLinus Torvalds * -ENOENT - No such vcn in the runlist, i.e. @vcn is out of bounds.
4321da177e4SLinus Torvalds * -ENOMEM - Not enough memory to map runlist.
4331da177e4SLinus Torvalds * -EIO - Critical error (runlist/file is corrupt, i/o error, etc).
4341da177e4SLinus Torvalds *
43569b41e3cSAnton Altaparmakov * WARNING: If @ctx is supplied, regardless of whether success or failure is
436c49c3111SRichard Knutsson * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx
43769b41e3cSAnton Altaparmakov * is no longer valid, i.e. you need to either call
43869b41e3cSAnton Altaparmakov * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
43969b41e3cSAnton Altaparmakov * In that case PTR_ERR(@ctx->mrec) will give you the error code for
44069b41e3cSAnton Altaparmakov * why the mapping of the old inode failed.
44169b41e3cSAnton Altaparmakov *
44269b41e3cSAnton Altaparmakov * Locking: - The runlist described by @ni must be locked for writing on entry
44369b41e3cSAnton Altaparmakov * and is locked on return. Note the runlist may be modified when
44469b41e3cSAnton Altaparmakov * needed runlist fragments need to be mapped.
44569b41e3cSAnton Altaparmakov * - If @ctx is NULL, the base mft record of @ni must not be mapped on
44669b41e3cSAnton Altaparmakov * entry and it will be left unmapped on return.
44769b41e3cSAnton Altaparmakov * - If @ctx is not NULL, the base mft record must be mapped on entry
44869b41e3cSAnton Altaparmakov * and it will be left mapped on return.
4491da177e4SLinus Torvalds */
ntfs_attr_find_vcn_nolock(ntfs_inode * ni,const VCN vcn,ntfs_attr_search_ctx * ctx)450c0c1cc0eSAnton Altaparmakov runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn,
45169b41e3cSAnton Altaparmakov ntfs_attr_search_ctx *ctx)
4521da177e4SLinus Torvalds {
4532983d1bdSAnton Altaparmakov unsigned long flags;
4541da177e4SLinus Torvalds runlist_element *rl;
4551da177e4SLinus Torvalds int err = 0;
456c49c3111SRichard Knutsson bool is_retry = false;
4571da177e4SLinus Torvalds
45845d95bcdSAnton Altaparmakov BUG_ON(!ni);
45969b41e3cSAnton Altaparmakov ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, with%s ctx.",
46069b41e3cSAnton Altaparmakov ni->mft_no, (unsigned long long)vcn, ctx ? "" : "out");
4611da177e4SLinus Torvalds BUG_ON(!NInoNonResident(ni));
4621da177e4SLinus Torvalds BUG_ON(vcn < 0);
4632983d1bdSAnton Altaparmakov if (!ni->runlist.rl) {
4642983d1bdSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
4652983d1bdSAnton Altaparmakov if (!ni->allocated_size) {
4662983d1bdSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
4672983d1bdSAnton Altaparmakov return ERR_PTR(-ENOENT);
4682983d1bdSAnton Altaparmakov }
4692983d1bdSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
4702983d1bdSAnton Altaparmakov }
471b6ad6c52SAnton Altaparmakov retry_remap:
4721da177e4SLinus Torvalds rl = ni->runlist.rl;
4731da177e4SLinus Torvalds if (likely(rl && vcn >= rl[0].vcn)) {
4741da177e4SLinus Torvalds while (likely(rl->length)) {
475b6ad6c52SAnton Altaparmakov if (unlikely(vcn < rl[1].vcn)) {
4761da177e4SLinus Torvalds if (likely(rl->lcn >= LCN_HOLE)) {
4771da177e4SLinus Torvalds ntfs_debug("Done.");
4781da177e4SLinus Torvalds return rl;
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds break;
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds rl++;
4831da177e4SLinus Torvalds }
4841da177e4SLinus Torvalds if (likely(rl->lcn != LCN_RL_NOT_MAPPED)) {
4851da177e4SLinus Torvalds if (likely(rl->lcn == LCN_ENOENT))
4861da177e4SLinus Torvalds err = -ENOENT;
4871da177e4SLinus Torvalds else
4881da177e4SLinus Torvalds err = -EIO;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds if (!err && !is_retry) {
4921da177e4SLinus Torvalds /*
49369b41e3cSAnton Altaparmakov * If the search context is invalid we cannot map the unmapped
49469b41e3cSAnton Altaparmakov * region.
4951da177e4SLinus Torvalds */
49669b41e3cSAnton Altaparmakov if (IS_ERR(ctx->mrec))
49769b41e3cSAnton Altaparmakov err = PTR_ERR(ctx->mrec);
49869b41e3cSAnton Altaparmakov else {
49969b41e3cSAnton Altaparmakov /*
50069b41e3cSAnton Altaparmakov * The @vcn is in an unmapped region, map the runlist
50169b41e3cSAnton Altaparmakov * and retry.
50269b41e3cSAnton Altaparmakov */
50369b41e3cSAnton Altaparmakov err = ntfs_map_runlist_nolock(ni, vcn, ctx);
5041da177e4SLinus Torvalds if (likely(!err)) {
505c49c3111SRichard Knutsson is_retry = true;
506b6ad6c52SAnton Altaparmakov goto retry_remap;
5071da177e4SLinus Torvalds }
50869b41e3cSAnton Altaparmakov }
5094757d7dfSAnton Altaparmakov if (err == -EINVAL)
5101da177e4SLinus Torvalds err = -EIO;
5111da177e4SLinus Torvalds } else if (!err)
5121da177e4SLinus Torvalds err = -EIO;
513b6ad6c52SAnton Altaparmakov if (err != -ENOENT)
5141da177e4SLinus Torvalds ntfs_error(ni->vol->sb, "Failed with error code %i.", err);
5151da177e4SLinus Torvalds return ERR_PTR(err);
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds /**
5191da177e4SLinus Torvalds * ntfs_attr_find - find (next) attribute in mft record
5201da177e4SLinus Torvalds * @type: attribute type to find
5211da177e4SLinus Torvalds * @name: attribute name to find (optional, i.e. NULL means don't care)
5221da177e4SLinus Torvalds * @name_len: attribute name length (only needed if @name present)
5231da177e4SLinus Torvalds * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
5241da177e4SLinus Torvalds * @val: attribute value to find (optional, resident attributes only)
5251da177e4SLinus Torvalds * @val_len: attribute value length
5261da177e4SLinus Torvalds * @ctx: search context with mft record and attribute to search from
5271da177e4SLinus Torvalds *
5281da177e4SLinus Torvalds * You should not need to call this function directly. Use ntfs_attr_lookup()
5291da177e4SLinus Torvalds * instead.
5301da177e4SLinus Torvalds *
5311da177e4SLinus Torvalds * ntfs_attr_find() takes a search context @ctx as parameter and searches the
5321da177e4SLinus Torvalds * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an
5331da177e4SLinus Torvalds * attribute of @type, optionally @name and @val.
5341da177e4SLinus Torvalds *
5351da177e4SLinus Torvalds * If the attribute is found, ntfs_attr_find() returns 0 and @ctx->attr will
5361da177e4SLinus Torvalds * point to the found attribute.
5371da177e4SLinus Torvalds *
5381da177e4SLinus Torvalds * If the attribute is not found, ntfs_attr_find() returns -ENOENT and
5391da177e4SLinus Torvalds * @ctx->attr will point to the attribute before which the attribute being
5401da177e4SLinus Torvalds * searched for would need to be inserted if such an action were to be desired.
5411da177e4SLinus Torvalds *
5421da177e4SLinus Torvalds * On actual error, ntfs_attr_find() returns -EIO. In this case @ctx->attr is
5431da177e4SLinus Torvalds * undefined and in particular do not rely on it not changing.
5441da177e4SLinus Torvalds *
545c49c3111SRichard Knutsson * If @ctx->is_first is 'true', the search begins with @ctx->attr itself. If it
546c49c3111SRichard Knutsson * is 'false', the search begins after @ctx->attr.
5471da177e4SLinus Torvalds *
5481da177e4SLinus Torvalds * If @ic is IGNORE_CASE, the @name comparisson is not case sensitive and
5491da177e4SLinus Torvalds * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record
5501da177e4SLinus Torvalds * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at
5511da177e4SLinus Torvalds * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case
5521da177e4SLinus Torvalds * sensitive. When @name is present, @name_len is the @name length in Unicode
5531da177e4SLinus Torvalds * characters.
5541da177e4SLinus Torvalds *
5551da177e4SLinus Torvalds * If @name is not present (NULL), we assume that the unnamed attribute is
5561da177e4SLinus Torvalds * being searched for.
5571da177e4SLinus Torvalds *
5581da177e4SLinus Torvalds * Finally, the resident attribute value @val is looked for, if present. If
5591da177e4SLinus Torvalds * @val is not present (NULL), @val_len is ignored.
5601da177e4SLinus Torvalds *
5611da177e4SLinus Torvalds * ntfs_attr_find() only searches the specified mft record and it ignores the
5621da177e4SLinus Torvalds * presence of an attribute list attribute (unless it is the one being searched
5631da177e4SLinus Torvalds * for, obviously). If you need to take attribute lists into consideration,
5641da177e4SLinus Torvalds * use ntfs_attr_lookup() instead (see below). This also means that you cannot
5651da177e4SLinus Torvalds * use ntfs_attr_find() to search for extent records of non-resident
5661da177e4SLinus Torvalds * attributes, as extents with lowest_vcn != 0 are usually described by the
5671da177e4SLinus Torvalds * attribute list attribute only. - Note that it is possible that the first
5681da177e4SLinus Torvalds * extent is only in the attribute list while the last extent is in the base
5691da177e4SLinus Torvalds * mft record, so do not rely on being able to find the first extent in the
5701da177e4SLinus Torvalds * base mft record.
5711da177e4SLinus Torvalds *
5721da177e4SLinus Torvalds * Warning: Never use @val when looking for attribute types which can be
5731da177e4SLinus Torvalds * non-resident as this most likely will result in a crash!
5741da177e4SLinus Torvalds */
ntfs_attr_find(const ATTR_TYPE type,const ntfschar * name,const u32 name_len,const IGNORE_CASE_BOOL ic,const u8 * val,const u32 val_len,ntfs_attr_search_ctx * ctx)5751da177e4SLinus Torvalds static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name,
5761da177e4SLinus Torvalds const u32 name_len, const IGNORE_CASE_BOOL ic,
5771da177e4SLinus Torvalds const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx)
5781da177e4SLinus Torvalds {
5791da177e4SLinus Torvalds ATTR_RECORD *a;
5801da177e4SLinus Torvalds ntfs_volume *vol = ctx->ntfs_ino->vol;
5811da177e4SLinus Torvalds ntfschar *upcase = vol->upcase;
5821da177e4SLinus Torvalds u32 upcase_len = vol->upcase_len;
5831da177e4SLinus Torvalds
5841da177e4SLinus Torvalds /*
5851da177e4SLinus Torvalds * Iterate over attributes in mft record starting at @ctx->attr, or the
586c49c3111SRichard Knutsson * attribute following that, if @ctx->is_first is 'true'.
5871da177e4SLinus Torvalds */
5881da177e4SLinus Torvalds if (ctx->is_first) {
5891da177e4SLinus Torvalds a = ctx->attr;
590c49c3111SRichard Knutsson ctx->is_first = false;
5911da177e4SLinus Torvalds } else
5921da177e4SLinus Torvalds a = (ATTR_RECORD*)((u8*)ctx->attr +
5931da177e4SLinus Torvalds le32_to_cpu(ctx->attr->length));
5941da177e4SLinus Torvalds for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) {
59538c9c22aSChenXiaoSong u8 *mrec_end = (u8 *)ctx->mrec +
59638c9c22aSChenXiaoSong le32_to_cpu(ctx->mrec->bytes_allocated);
59736a4d82dSHawkins Jiawei u8 *name_end;
59836a4d82dSHawkins Jiawei
59936a4d82dSHawkins Jiawei /* check whether ATTR_RECORD wrap */
60036a4d82dSHawkins Jiawei if ((u8 *)a < (u8 *)ctx->mrec)
6011da177e4SLinus Torvalds break;
60236a4d82dSHawkins Jiawei
60336a4d82dSHawkins Jiawei /* check whether Attribute Record Header is within bounds */
60436a4d82dSHawkins Jiawei if ((u8 *)a > mrec_end ||
60536a4d82dSHawkins Jiawei (u8 *)a + sizeof(ATTR_RECORD) > mrec_end)
60636a4d82dSHawkins Jiawei break;
60736a4d82dSHawkins Jiawei
60836a4d82dSHawkins Jiawei /* check whether ATTR_RECORD's name is within bounds */
60936a4d82dSHawkins Jiawei name_end = (u8 *)a + le16_to_cpu(a->name_offset) +
61036a4d82dSHawkins Jiawei a->name_length * sizeof(ntfschar);
61136a4d82dSHawkins Jiawei if (name_end > mrec_end)
61236a4d82dSHawkins Jiawei break;
61336a4d82dSHawkins Jiawei
6141da177e4SLinus Torvalds ctx->attr = a;
6151da177e4SLinus Torvalds if (unlikely(le32_to_cpu(a->type) > le32_to_cpu(type) ||
6161da177e4SLinus Torvalds a->type == AT_END))
6171da177e4SLinus Torvalds return -ENOENT;
6181da177e4SLinus Torvalds if (unlikely(!a->length))
6191da177e4SLinus Torvalds break;
62063095f4fSHawkins Jiawei
62163095f4fSHawkins Jiawei /* check whether ATTR_RECORD's length wrap */
62263095f4fSHawkins Jiawei if ((u8 *)a + le32_to_cpu(a->length) < (u8 *)a)
62363095f4fSHawkins Jiawei break;
62463095f4fSHawkins Jiawei /* check whether ATTR_RECORD's length is within bounds */
62563095f4fSHawkins Jiawei if ((u8 *)a + le32_to_cpu(a->length) > mrec_end)
62663095f4fSHawkins Jiawei break;
62763095f4fSHawkins Jiawei
6281da177e4SLinus Torvalds if (a->type != type)
6291da177e4SLinus Torvalds continue;
6301da177e4SLinus Torvalds /*
6311da177e4SLinus Torvalds * If @name is present, compare the two names. If @name is
6321da177e4SLinus Torvalds * missing, assume we want an unnamed attribute.
6331da177e4SLinus Torvalds */
6341da177e4SLinus Torvalds if (!name) {
6351da177e4SLinus Torvalds /* The search failed if the found attribute is named. */
6361da177e4SLinus Torvalds if (a->name_length)
6371da177e4SLinus Torvalds return -ENOENT;
6381da177e4SLinus Torvalds } else if (!ntfs_are_names_equal(name, name_len,
6391da177e4SLinus Torvalds (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)),
6401da177e4SLinus Torvalds a->name_length, ic, upcase, upcase_len)) {
6411da177e4SLinus Torvalds register int rc;
6421da177e4SLinus Torvalds
6431da177e4SLinus Torvalds rc = ntfs_collate_names(name, name_len,
6441da177e4SLinus Torvalds (ntfschar*)((u8*)a +
6451da177e4SLinus Torvalds le16_to_cpu(a->name_offset)),
6461da177e4SLinus Torvalds a->name_length, 1, IGNORE_CASE,
6471da177e4SLinus Torvalds upcase, upcase_len);
6481da177e4SLinus Torvalds /*
6491da177e4SLinus Torvalds * If @name collates before a->name, there is no
6501da177e4SLinus Torvalds * matching attribute.
6511da177e4SLinus Torvalds */
6521da177e4SLinus Torvalds if (rc == -1)
6531da177e4SLinus Torvalds return -ENOENT;
6541da177e4SLinus Torvalds /* If the strings are not equal, continue search. */
6551da177e4SLinus Torvalds if (rc)
6561da177e4SLinus Torvalds continue;
6571da177e4SLinus Torvalds rc = ntfs_collate_names(name, name_len,
6581da177e4SLinus Torvalds (ntfschar*)((u8*)a +
6591da177e4SLinus Torvalds le16_to_cpu(a->name_offset)),
6601da177e4SLinus Torvalds a->name_length, 1, CASE_SENSITIVE,
6611da177e4SLinus Torvalds upcase, upcase_len);
6621da177e4SLinus Torvalds if (rc == -1)
6631da177e4SLinus Torvalds return -ENOENT;
6641da177e4SLinus Torvalds if (rc)
6651da177e4SLinus Torvalds continue;
6661da177e4SLinus Torvalds }
6671da177e4SLinus Torvalds /*
6681da177e4SLinus Torvalds * The names match or @name not present and attribute is
6691da177e4SLinus Torvalds * unnamed. If no @val specified, we have found the attribute
6701da177e4SLinus Torvalds * and are done.
6711da177e4SLinus Torvalds */
6721da177e4SLinus Torvalds if (!val)
6731da177e4SLinus Torvalds return 0;
6741da177e4SLinus Torvalds /* @val is present; compare values. */
6751da177e4SLinus Torvalds else {
6761da177e4SLinus Torvalds register int rc;
6771da177e4SLinus Torvalds
6781da177e4SLinus Torvalds rc = memcmp(val, (u8*)a + le16_to_cpu(
6791da177e4SLinus Torvalds a->data.resident.value_offset),
6801da177e4SLinus Torvalds min_t(u32, val_len, le32_to_cpu(
6811da177e4SLinus Torvalds a->data.resident.value_length)));
6821da177e4SLinus Torvalds /*
6831da177e4SLinus Torvalds * If @val collates before the current attribute's
6841da177e4SLinus Torvalds * value, there is no matching attribute.
6851da177e4SLinus Torvalds */
6861da177e4SLinus Torvalds if (!rc) {
6871da177e4SLinus Torvalds register u32 avl;
6881da177e4SLinus Torvalds
6891da177e4SLinus Torvalds avl = le32_to_cpu(
6901da177e4SLinus Torvalds a->data.resident.value_length);
6911da177e4SLinus Torvalds if (val_len == avl)
6921da177e4SLinus Torvalds return 0;
6931da177e4SLinus Torvalds if (val_len < avl)
6941da177e4SLinus Torvalds return -ENOENT;
6951da177e4SLinus Torvalds } else if (rc < 0)
6961da177e4SLinus Torvalds return -ENOENT;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds }
6991da177e4SLinus Torvalds ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk.");
7001da177e4SLinus Torvalds NVolSetErrors(vol);
7011da177e4SLinus Torvalds return -EIO;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds
7041da177e4SLinus Torvalds /**
7051da177e4SLinus Torvalds * load_attribute_list - load an attribute list into memory
7061da177e4SLinus Torvalds * @vol: ntfs volume from which to read
7071da177e4SLinus Torvalds * @runlist: runlist of the attribute list
7081da177e4SLinus Torvalds * @al_start: destination buffer
7091da177e4SLinus Torvalds * @size: size of the destination buffer in bytes
7101da177e4SLinus Torvalds * @initialized_size: initialized size of the attribute list
7111da177e4SLinus Torvalds *
7121da177e4SLinus Torvalds * Walk the runlist @runlist and load all clusters from it copying them into
7131da177e4SLinus Torvalds * the linear buffer @al. The maximum number of bytes copied to @al is @size
7141da177e4SLinus Torvalds * bytes. Note, @size does not need to be a multiple of the cluster size. If
7151da177e4SLinus Torvalds * @initialized_size is less than @size, the region in @al between
7161da177e4SLinus Torvalds * @initialized_size and @size will be zeroed and not read from disk.
7171da177e4SLinus Torvalds *
7181da177e4SLinus Torvalds * Return 0 on success or -errno on error.
7191da177e4SLinus Torvalds */
load_attribute_list(ntfs_volume * vol,runlist * runlist,u8 * al_start,const s64 size,const s64 initialized_size)7201da177e4SLinus Torvalds int load_attribute_list(ntfs_volume *vol, runlist *runlist, u8 *al_start,
7211da177e4SLinus Torvalds const s64 size, const s64 initialized_size)
7221da177e4SLinus Torvalds {
7231da177e4SLinus Torvalds LCN lcn;
7241da177e4SLinus Torvalds u8 *al = al_start;
7251da177e4SLinus Torvalds u8 *al_end = al + initialized_size;
7261da177e4SLinus Torvalds runlist_element *rl;
7271da177e4SLinus Torvalds struct buffer_head *bh;
7281da177e4SLinus Torvalds struct super_block *sb;
7291da177e4SLinus Torvalds unsigned long block_size;
7301da177e4SLinus Torvalds unsigned long block, max_block;
7311da177e4SLinus Torvalds int err = 0;
7321da177e4SLinus Torvalds unsigned char block_size_bits;
7331da177e4SLinus Torvalds
7341da177e4SLinus Torvalds ntfs_debug("Entering.");
7351da177e4SLinus Torvalds if (!vol || !runlist || !al || size <= 0 || initialized_size < 0 ||
7361da177e4SLinus Torvalds initialized_size > size)
7371da177e4SLinus Torvalds return -EINVAL;
7381da177e4SLinus Torvalds if (!initialized_size) {
7391da177e4SLinus Torvalds memset(al, 0, size);
7401da177e4SLinus Torvalds return 0;
7411da177e4SLinus Torvalds }
7421da177e4SLinus Torvalds sb = vol->sb;
7431da177e4SLinus Torvalds block_size = sb->s_blocksize;
7441da177e4SLinus Torvalds block_size_bits = sb->s_blocksize_bits;
7451da177e4SLinus Torvalds down_read(&runlist->lock);
7461da177e4SLinus Torvalds rl = runlist->rl;
7472983d1bdSAnton Altaparmakov if (!rl) {
7482983d1bdSAnton Altaparmakov ntfs_error(sb, "Cannot read attribute list since runlist is "
7492983d1bdSAnton Altaparmakov "missing.");
7502983d1bdSAnton Altaparmakov goto err_out;
7512983d1bdSAnton Altaparmakov }
7521da177e4SLinus Torvalds /* Read all clusters specified by the runlist one run at a time. */
7531da177e4SLinus Torvalds while (rl->length) {
7541da177e4SLinus Torvalds lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn);
7551da177e4SLinus Torvalds ntfs_debug("Reading vcn = 0x%llx, lcn = 0x%llx.",
7561da177e4SLinus Torvalds (unsigned long long)rl->vcn,
7571da177e4SLinus Torvalds (unsigned long long)lcn);
7581da177e4SLinus Torvalds /* The attribute list cannot be sparse. */
7591da177e4SLinus Torvalds if (lcn < 0) {
7601da177e4SLinus Torvalds ntfs_error(sb, "ntfs_rl_vcn_to_lcn() failed. Cannot "
7611da177e4SLinus Torvalds "read attribute list.");
7621da177e4SLinus Torvalds goto err_out;
7631da177e4SLinus Torvalds }
7641da177e4SLinus Torvalds block = lcn << vol->cluster_size_bits >> block_size_bits;
7651da177e4SLinus Torvalds /* Read the run from device in chunks of block_size bytes. */
7661da177e4SLinus Torvalds max_block = block + (rl->length << vol->cluster_size_bits >>
7671da177e4SLinus Torvalds block_size_bits);
7681da177e4SLinus Torvalds ntfs_debug("max_block = 0x%lx.", max_block);
7691da177e4SLinus Torvalds do {
7701da177e4SLinus Torvalds ntfs_debug("Reading block = 0x%lx.", block);
7711da177e4SLinus Torvalds bh = sb_bread(sb, block);
7721da177e4SLinus Torvalds if (!bh) {
7731da177e4SLinus Torvalds ntfs_error(sb, "sb_bread() failed. Cannot "
7741da177e4SLinus Torvalds "read attribute list.");
7751da177e4SLinus Torvalds goto err_out;
7761da177e4SLinus Torvalds }
7771da177e4SLinus Torvalds if (al + block_size >= al_end)
7781da177e4SLinus Torvalds goto do_final;
7791da177e4SLinus Torvalds memcpy(al, bh->b_data, block_size);
7801da177e4SLinus Torvalds brelse(bh);
7811da177e4SLinus Torvalds al += block_size;
7821da177e4SLinus Torvalds } while (++block < max_block);
7831da177e4SLinus Torvalds rl++;
7841da177e4SLinus Torvalds }
7851da177e4SLinus Torvalds if (initialized_size < size) {
7861da177e4SLinus Torvalds initialize:
7871da177e4SLinus Torvalds memset(al_start + initialized_size, 0, size - initialized_size);
7881da177e4SLinus Torvalds }
7891da177e4SLinus Torvalds done:
7901da177e4SLinus Torvalds up_read(&runlist->lock);
7911da177e4SLinus Torvalds return err;
7921da177e4SLinus Torvalds do_final:
7931da177e4SLinus Torvalds if (al < al_end) {
7941da177e4SLinus Torvalds /*
7951da177e4SLinus Torvalds * Partial block.
7961da177e4SLinus Torvalds *
7971da177e4SLinus Torvalds * Note: The attribute list can be smaller than its allocation
7981da177e4SLinus Torvalds * by multiple clusters. This has been encountered by at least
7991da177e4SLinus Torvalds * two people running Windows XP, thus we cannot do any
8001da177e4SLinus Torvalds * truncation sanity checking here. (AIA)
8011da177e4SLinus Torvalds */
8021da177e4SLinus Torvalds memcpy(al, bh->b_data, al_end - al);
8031da177e4SLinus Torvalds brelse(bh);
8041da177e4SLinus Torvalds if (initialized_size < size)
8051da177e4SLinus Torvalds goto initialize;
8061da177e4SLinus Torvalds goto done;
8071da177e4SLinus Torvalds }
8081da177e4SLinus Torvalds brelse(bh);
8091da177e4SLinus Torvalds /* Real overflow! */
8101da177e4SLinus Torvalds ntfs_error(sb, "Attribute list buffer overflow. Read attribute list "
8111da177e4SLinus Torvalds "is truncated.");
8121da177e4SLinus Torvalds err_out:
8131da177e4SLinus Torvalds err = -EIO;
8141da177e4SLinus Torvalds goto done;
8151da177e4SLinus Torvalds }
8161da177e4SLinus Torvalds
8171da177e4SLinus Torvalds /**
8181da177e4SLinus Torvalds * ntfs_external_attr_find - find an attribute in the attribute list of an inode
8191da177e4SLinus Torvalds * @type: attribute type to find
8201da177e4SLinus Torvalds * @name: attribute name to find (optional, i.e. NULL means don't care)
8211da177e4SLinus Torvalds * @name_len: attribute name length (only needed if @name present)
8221da177e4SLinus Torvalds * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
8231da177e4SLinus Torvalds * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only)
8241da177e4SLinus Torvalds * @val: attribute value to find (optional, resident attributes only)
8251da177e4SLinus Torvalds * @val_len: attribute value length
8261da177e4SLinus Torvalds * @ctx: search context with mft record and attribute to search from
8271da177e4SLinus Torvalds *
8281da177e4SLinus Torvalds * You should not need to call this function directly. Use ntfs_attr_lookup()
8291da177e4SLinus Torvalds * instead.
8301da177e4SLinus Torvalds *
8311da177e4SLinus Torvalds * Find an attribute by searching the attribute list for the corresponding
8321da177e4SLinus Torvalds * attribute list entry. Having found the entry, map the mft record if the
8331da177e4SLinus Torvalds * attribute is in a different mft record/inode, ntfs_attr_find() the attribute
8341da177e4SLinus Torvalds * in there and return it.
8351da177e4SLinus Torvalds *
8361da177e4SLinus Torvalds * On first search @ctx->ntfs_ino must be the base mft record and @ctx must
8371da177e4SLinus Torvalds * have been obtained from a call to ntfs_attr_get_search_ctx(). On subsequent
8381da177e4SLinus Torvalds * calls @ctx->ntfs_ino can be any extent inode, too (@ctx->base_ntfs_ino is
8391da177e4SLinus Torvalds * then the base inode).
8401da177e4SLinus Torvalds *
8411da177e4SLinus Torvalds * After finishing with the attribute/mft record you need to call
8421da177e4SLinus Torvalds * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
8431da177e4SLinus Torvalds * mapped inodes, etc).
8441da177e4SLinus Torvalds *
8451da177e4SLinus Torvalds * If the attribute is found, ntfs_external_attr_find() returns 0 and
8461da177e4SLinus Torvalds * @ctx->attr will point to the found attribute. @ctx->mrec will point to the
8471da177e4SLinus Torvalds * mft record in which @ctx->attr is located and @ctx->al_entry will point to
8481da177e4SLinus Torvalds * the attribute list entry for the attribute.
8491da177e4SLinus Torvalds *
8501da177e4SLinus Torvalds * If the attribute is not found, ntfs_external_attr_find() returns -ENOENT and
8511da177e4SLinus Torvalds * @ctx->attr will point to the attribute in the base mft record before which
8521da177e4SLinus Torvalds * the attribute being searched for would need to be inserted if such an action
8531da177e4SLinus Torvalds * were to be desired. @ctx->mrec will point to the mft record in which
8541da177e4SLinus Torvalds * @ctx->attr is located and @ctx->al_entry will point to the attribute list
8551da177e4SLinus Torvalds * entry of the attribute before which the attribute being searched for would
8561da177e4SLinus Torvalds * need to be inserted if such an action were to be desired.
8571da177e4SLinus Torvalds *
8581da177e4SLinus Torvalds * Thus to insert the not found attribute, one wants to add the attribute to
8591da177e4SLinus Torvalds * @ctx->mrec (the base mft record) and if there is not enough space, the
8601da177e4SLinus Torvalds * attribute should be placed in a newly allocated extent mft record. The
8611da177e4SLinus Torvalds * attribute list entry for the inserted attribute should be inserted in the
8621da177e4SLinus Torvalds * attribute list attribute at @ctx->al_entry.
8631da177e4SLinus Torvalds *
8641da177e4SLinus Torvalds * On actual error, ntfs_external_attr_find() returns -EIO. In this case
8651da177e4SLinus Torvalds * @ctx->attr is undefined and in particular do not rely on it not changing.
8661da177e4SLinus Torvalds */
ntfs_external_attr_find(const ATTR_TYPE type,const ntfschar * name,const u32 name_len,const IGNORE_CASE_BOOL ic,const VCN lowest_vcn,const u8 * val,const u32 val_len,ntfs_attr_search_ctx * ctx)8671da177e4SLinus Torvalds static int ntfs_external_attr_find(const ATTR_TYPE type,
8681da177e4SLinus Torvalds const ntfschar *name, const u32 name_len,
8691da177e4SLinus Torvalds const IGNORE_CASE_BOOL ic, const VCN lowest_vcn,
8701da177e4SLinus Torvalds const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx)
8711da177e4SLinus Torvalds {
8721da177e4SLinus Torvalds ntfs_inode *base_ni, *ni;
8731da177e4SLinus Torvalds ntfs_volume *vol;
8741da177e4SLinus Torvalds ATTR_LIST_ENTRY *al_entry, *next_al_entry;
8751da177e4SLinus Torvalds u8 *al_start, *al_end;
8761da177e4SLinus Torvalds ATTR_RECORD *a;
8771da177e4SLinus Torvalds ntfschar *al_name;
8781da177e4SLinus Torvalds u32 al_name_len;
8791da177e4SLinus Torvalds int err = 0;
8801da177e4SLinus Torvalds static const char *es = " Unmount and run chkdsk.";
8811da177e4SLinus Torvalds
8821da177e4SLinus Torvalds ni = ctx->ntfs_ino;
8831da177e4SLinus Torvalds base_ni = ctx->base_ntfs_ino;
8841da177e4SLinus Torvalds ntfs_debug("Entering for inode 0x%lx, type 0x%x.", ni->mft_no, type);
8851da177e4SLinus Torvalds if (!base_ni) {
8861da177e4SLinus Torvalds /* First call happens with the base mft record. */
8871da177e4SLinus Torvalds base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino;
8881da177e4SLinus Torvalds ctx->base_mrec = ctx->mrec;
8891da177e4SLinus Torvalds }
8901da177e4SLinus Torvalds if (ni == base_ni)
8911da177e4SLinus Torvalds ctx->base_attr = ctx->attr;
8921da177e4SLinus Torvalds if (type == AT_END)
8931da177e4SLinus Torvalds goto not_found;
8941da177e4SLinus Torvalds vol = base_ni->vol;
8951da177e4SLinus Torvalds al_start = base_ni->attr_list;
8961da177e4SLinus Torvalds al_end = al_start + base_ni->attr_list_size;
8971da177e4SLinus Torvalds if (!ctx->al_entry)
8981da177e4SLinus Torvalds ctx->al_entry = (ATTR_LIST_ENTRY*)al_start;
8991da177e4SLinus Torvalds /*
9001da177e4SLinus Torvalds * Iterate over entries in attribute list starting at @ctx->al_entry,
901c49c3111SRichard Knutsson * or the entry following that, if @ctx->is_first is 'true'.
9021da177e4SLinus Torvalds */
9031da177e4SLinus Torvalds if (ctx->is_first) {
9041da177e4SLinus Torvalds al_entry = ctx->al_entry;
905c49c3111SRichard Knutsson ctx->is_first = false;
9061da177e4SLinus Torvalds } else
9071da177e4SLinus Torvalds al_entry = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
9081da177e4SLinus Torvalds le16_to_cpu(ctx->al_entry->length));
9091da177e4SLinus Torvalds for (;; al_entry = next_al_entry) {
9101da177e4SLinus Torvalds /* Out of bounds check. */
9111da177e4SLinus Torvalds if ((u8*)al_entry < base_ni->attr_list ||
9121da177e4SLinus Torvalds (u8*)al_entry > al_end)
9131da177e4SLinus Torvalds break; /* Inode is corrupt. */
9141da177e4SLinus Torvalds ctx->al_entry = al_entry;
9151da177e4SLinus Torvalds /* Catch the end of the attribute list. */
9161da177e4SLinus Torvalds if ((u8*)al_entry == al_end)
9171da177e4SLinus Torvalds goto not_found;
9181da177e4SLinus Torvalds if (!al_entry->length)
9191da177e4SLinus Torvalds break;
9201da177e4SLinus Torvalds if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
9211da177e4SLinus Torvalds le16_to_cpu(al_entry->length) > al_end)
9221da177e4SLinus Torvalds break;
9231da177e4SLinus Torvalds next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
9241da177e4SLinus Torvalds le16_to_cpu(al_entry->length));
9251da177e4SLinus Torvalds if (le32_to_cpu(al_entry->type) > le32_to_cpu(type))
9261da177e4SLinus Torvalds goto not_found;
9271da177e4SLinus Torvalds if (type != al_entry->type)
9281da177e4SLinus Torvalds continue;
9291da177e4SLinus Torvalds /*
9301da177e4SLinus Torvalds * If @name is present, compare the two names. If @name is
9311da177e4SLinus Torvalds * missing, assume we want an unnamed attribute.
9321da177e4SLinus Torvalds */
9331da177e4SLinus Torvalds al_name_len = al_entry->name_length;
9341da177e4SLinus Torvalds al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset);
9351da177e4SLinus Torvalds if (!name) {
9361da177e4SLinus Torvalds if (al_name_len)
9371da177e4SLinus Torvalds goto not_found;
9381da177e4SLinus Torvalds } else if (!ntfs_are_names_equal(al_name, al_name_len, name,
9391da177e4SLinus Torvalds name_len, ic, vol->upcase, vol->upcase_len)) {
9401da177e4SLinus Torvalds register int rc;
9411da177e4SLinus Torvalds
9421da177e4SLinus Torvalds rc = ntfs_collate_names(name, name_len, al_name,
9431da177e4SLinus Torvalds al_name_len, 1, IGNORE_CASE,
9441da177e4SLinus Torvalds vol->upcase, vol->upcase_len);
9451da177e4SLinus Torvalds /*
9461da177e4SLinus Torvalds * If @name collates before al_name, there is no
9471da177e4SLinus Torvalds * matching attribute.
9481da177e4SLinus Torvalds */
9491da177e4SLinus Torvalds if (rc == -1)
9501da177e4SLinus Torvalds goto not_found;
9511da177e4SLinus Torvalds /* If the strings are not equal, continue search. */
9521da177e4SLinus Torvalds if (rc)
9531da177e4SLinus Torvalds continue;
9541da177e4SLinus Torvalds /*
9551da177e4SLinus Torvalds * FIXME: Reverse engineering showed 0, IGNORE_CASE but
9561da177e4SLinus Torvalds * that is inconsistent with ntfs_attr_find(). The
9571da177e4SLinus Torvalds * subsequent rc checks were also different. Perhaps I
9581da177e4SLinus Torvalds * made a mistake in one of the two. Need to recheck
9591da177e4SLinus Torvalds * which is correct or at least see what is going on...
9601da177e4SLinus Torvalds * (AIA)
9611da177e4SLinus Torvalds */
9621da177e4SLinus Torvalds rc = ntfs_collate_names(name, name_len, al_name,
9631da177e4SLinus Torvalds al_name_len, 1, CASE_SENSITIVE,
9641da177e4SLinus Torvalds vol->upcase, vol->upcase_len);
9651da177e4SLinus Torvalds if (rc == -1)
9661da177e4SLinus Torvalds goto not_found;
9671da177e4SLinus Torvalds if (rc)
9681da177e4SLinus Torvalds continue;
9691da177e4SLinus Torvalds }
9701da177e4SLinus Torvalds /*
9711da177e4SLinus Torvalds * The names match or @name not present and attribute is
9721da177e4SLinus Torvalds * unnamed. Now check @lowest_vcn. Continue search if the
9731da177e4SLinus Torvalds * next attribute list entry still fits @lowest_vcn. Otherwise
9741da177e4SLinus Torvalds * we have reached the right one or the search has failed.
9751da177e4SLinus Torvalds */
9761da177e4SLinus Torvalds if (lowest_vcn && (u8*)next_al_entry >= al_start &&
9771da177e4SLinus Torvalds (u8*)next_al_entry + 6 < al_end &&
9781da177e4SLinus Torvalds (u8*)next_al_entry + le16_to_cpu(
9791da177e4SLinus Torvalds next_al_entry->length) <= al_end &&
9801da177e4SLinus Torvalds sle64_to_cpu(next_al_entry->lowest_vcn) <=
9811da177e4SLinus Torvalds lowest_vcn &&
9821da177e4SLinus Torvalds next_al_entry->type == al_entry->type &&
9831da177e4SLinus Torvalds next_al_entry->name_length == al_name_len &&
9841da177e4SLinus Torvalds ntfs_are_names_equal((ntfschar*)((u8*)
9851da177e4SLinus Torvalds next_al_entry +
9861da177e4SLinus Torvalds next_al_entry->name_offset),
9871da177e4SLinus Torvalds next_al_entry->name_length,
9881da177e4SLinus Torvalds al_name, al_name_len, CASE_SENSITIVE,
9891da177e4SLinus Torvalds vol->upcase, vol->upcase_len))
9901da177e4SLinus Torvalds continue;
9911da177e4SLinus Torvalds if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {
9921da177e4SLinus Torvalds if (MSEQNO_LE(al_entry->mft_reference) != ni->seq_no) {
9931da177e4SLinus Torvalds ntfs_error(vol->sb, "Found stale mft "
9941da177e4SLinus Torvalds "reference in attribute list "
9951da177e4SLinus Torvalds "of base inode 0x%lx.%s",
9961da177e4SLinus Torvalds base_ni->mft_no, es);
9971da177e4SLinus Torvalds err = -EIO;
9981da177e4SLinus Torvalds break;
9991da177e4SLinus Torvalds }
10001da177e4SLinus Torvalds } else { /* Mft references do not match. */
10011da177e4SLinus Torvalds /* If there is a mapped record unmap it first. */
10021da177e4SLinus Torvalds if (ni != base_ni)
10031da177e4SLinus Torvalds unmap_extent_mft_record(ni);
10041da177e4SLinus Torvalds /* Do we want the base record back? */
10051da177e4SLinus Torvalds if (MREF_LE(al_entry->mft_reference) ==
10061da177e4SLinus Torvalds base_ni->mft_no) {
10071da177e4SLinus Torvalds ni = ctx->ntfs_ino = base_ni;
10081da177e4SLinus Torvalds ctx->mrec = ctx->base_mrec;
10091da177e4SLinus Torvalds } else {
10101da177e4SLinus Torvalds /* We want an extent record. */
10111da177e4SLinus Torvalds ctx->mrec = map_extent_mft_record(base_ni,
10121da177e4SLinus Torvalds le64_to_cpu(
10131da177e4SLinus Torvalds al_entry->mft_reference), &ni);
10141da177e4SLinus Torvalds if (IS_ERR(ctx->mrec)) {
10151da177e4SLinus Torvalds ntfs_error(vol->sb, "Failed to map "
10161da177e4SLinus Torvalds "extent mft record "
10171da177e4SLinus Torvalds "0x%lx of base inode "
10181da177e4SLinus Torvalds "0x%lx.%s",
10191da177e4SLinus Torvalds MREF_LE(al_entry->
10201da177e4SLinus Torvalds mft_reference),
10211da177e4SLinus Torvalds base_ni->mft_no, es);
10221da177e4SLinus Torvalds err = PTR_ERR(ctx->mrec);
10231da177e4SLinus Torvalds if (err == -ENOENT)
10241da177e4SLinus Torvalds err = -EIO;
10251da177e4SLinus Torvalds /* Cause @ctx to be sanitized below. */
10261da177e4SLinus Torvalds ni = NULL;
10271da177e4SLinus Torvalds break;
10281da177e4SLinus Torvalds }
10291da177e4SLinus Torvalds ctx->ntfs_ino = ni;
10301da177e4SLinus Torvalds }
10311da177e4SLinus Torvalds ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
10321da177e4SLinus Torvalds le16_to_cpu(ctx->mrec->attrs_offset));
10331da177e4SLinus Torvalds }
10341da177e4SLinus Torvalds /*
10351da177e4SLinus Torvalds * ctx->vfs_ino, ctx->mrec, and ctx->attr now point to the
10361da177e4SLinus Torvalds * mft record containing the attribute represented by the
10371da177e4SLinus Torvalds * current al_entry.
10381da177e4SLinus Torvalds */
10391da177e4SLinus Torvalds /*
10401da177e4SLinus Torvalds * We could call into ntfs_attr_find() to find the right
10411da177e4SLinus Torvalds * attribute in this mft record but this would be less
10421da177e4SLinus Torvalds * efficient and not quite accurate as ntfs_attr_find() ignores
10431da177e4SLinus Torvalds * the attribute instance numbers for example which become
10441da177e4SLinus Torvalds * important when one plays with attribute lists. Also,
10451da177e4SLinus Torvalds * because a proper match has been found in the attribute list
10461da177e4SLinus Torvalds * entry above, the comparison can now be optimized. So it is
10471da177e4SLinus Torvalds * worth re-implementing a simplified ntfs_attr_find() here.
10481da177e4SLinus Torvalds */
10491da177e4SLinus Torvalds a = ctx->attr;
10501da177e4SLinus Torvalds /*
10511da177e4SLinus Torvalds * Use a manual loop so we can still use break and continue
10521da177e4SLinus Torvalds * with the same meanings as above.
10531da177e4SLinus Torvalds */
10541da177e4SLinus Torvalds do_next_attr_loop:
10551da177e4SLinus Torvalds if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec +
10561da177e4SLinus Torvalds le32_to_cpu(ctx->mrec->bytes_allocated))
10571da177e4SLinus Torvalds break;
10581da177e4SLinus Torvalds if (a->type == AT_END)
10593ccc7384SAnton Altaparmakov break;
10601da177e4SLinus Torvalds if (!a->length)
10611da177e4SLinus Torvalds break;
10621da177e4SLinus Torvalds if (al_entry->instance != a->instance)
10631da177e4SLinus Torvalds goto do_next_attr;
10641da177e4SLinus Torvalds /*
10651da177e4SLinus Torvalds * If the type and/or the name are mismatched between the
10661da177e4SLinus Torvalds * attribute list entry and the attribute record, there is
10671da177e4SLinus Torvalds * corruption so we break and return error EIO.
10681da177e4SLinus Torvalds */
10691da177e4SLinus Torvalds if (al_entry->type != a->type)
10701da177e4SLinus Torvalds break;
10711da177e4SLinus Torvalds if (!ntfs_are_names_equal((ntfschar*)((u8*)a +
10721da177e4SLinus Torvalds le16_to_cpu(a->name_offset)), a->name_length,
10731da177e4SLinus Torvalds al_name, al_name_len, CASE_SENSITIVE,
10741da177e4SLinus Torvalds vol->upcase, vol->upcase_len))
10751da177e4SLinus Torvalds break;
10761da177e4SLinus Torvalds ctx->attr = a;
10771da177e4SLinus Torvalds /*
10781da177e4SLinus Torvalds * If no @val specified or @val specified and it matches, we
10791da177e4SLinus Torvalds * have found it!
10801da177e4SLinus Torvalds */
10811da177e4SLinus Torvalds if (!val || (!a->non_resident && le32_to_cpu(
10821da177e4SLinus Torvalds a->data.resident.value_length) == val_len &&
10831da177e4SLinus Torvalds !memcmp((u8*)a +
10841da177e4SLinus Torvalds le16_to_cpu(a->data.resident.value_offset),
10851da177e4SLinus Torvalds val, val_len))) {
10861da177e4SLinus Torvalds ntfs_debug("Done, found.");
10871da177e4SLinus Torvalds return 0;
10881da177e4SLinus Torvalds }
10891da177e4SLinus Torvalds do_next_attr:
10901da177e4SLinus Torvalds /* Proceed to the next attribute in the current mft record. */
10911da177e4SLinus Torvalds a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length));
10921da177e4SLinus Torvalds goto do_next_attr_loop;
10931da177e4SLinus Torvalds }
10941da177e4SLinus Torvalds if (!err) {
10951da177e4SLinus Torvalds ntfs_error(vol->sb, "Base inode 0x%lx contains corrupt "
10961da177e4SLinus Torvalds "attribute list attribute.%s", base_ni->mft_no,
10971da177e4SLinus Torvalds es);
10981da177e4SLinus Torvalds err = -EIO;
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds if (ni != base_ni) {
11011da177e4SLinus Torvalds if (ni)
11021da177e4SLinus Torvalds unmap_extent_mft_record(ni);
11031da177e4SLinus Torvalds ctx->ntfs_ino = base_ni;
11041da177e4SLinus Torvalds ctx->mrec = ctx->base_mrec;
11051da177e4SLinus Torvalds ctx->attr = ctx->base_attr;
11061da177e4SLinus Torvalds }
11071da177e4SLinus Torvalds if (err != -ENOMEM)
11081da177e4SLinus Torvalds NVolSetErrors(vol);
11091da177e4SLinus Torvalds return err;
11101da177e4SLinus Torvalds not_found:
11111da177e4SLinus Torvalds /*
11121da177e4SLinus Torvalds * If we were looking for AT_END, we reset the search context @ctx and
11131da177e4SLinus Torvalds * use ntfs_attr_find() to seek to the end of the base mft record.
11141da177e4SLinus Torvalds */
11151da177e4SLinus Torvalds if (type == AT_END) {
11161da177e4SLinus Torvalds ntfs_attr_reinit_search_ctx(ctx);
11171da177e4SLinus Torvalds return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len,
11181da177e4SLinus Torvalds ctx);
11191da177e4SLinus Torvalds }
11201da177e4SLinus Torvalds /*
11211da177e4SLinus Torvalds * The attribute was not found. Before we return, we want to ensure
11221da177e4SLinus Torvalds * @ctx->mrec and @ctx->attr indicate the position at which the
11231da177e4SLinus Torvalds * attribute should be inserted in the base mft record. Since we also
11241da177e4SLinus Torvalds * want to preserve @ctx->al_entry we cannot reinitialize the search
11251da177e4SLinus Torvalds * context using ntfs_attr_reinit_search_ctx() as this would set
11261da177e4SLinus Torvalds * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see
11271da177e4SLinus Torvalds * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve
11281da177e4SLinus Torvalds * @ctx->al_entry as the remaining fields (base_*) are identical to
11291da177e4SLinus Torvalds * their non base_ counterparts and we cannot set @ctx->base_attr
11301da177e4SLinus Torvalds * correctly yet as we do not know what @ctx->attr will be set to by
11311da177e4SLinus Torvalds * the call to ntfs_attr_find() below.
11321da177e4SLinus Torvalds */
11331da177e4SLinus Torvalds if (ni != base_ni)
11341da177e4SLinus Torvalds unmap_extent_mft_record(ni);
11351da177e4SLinus Torvalds ctx->mrec = ctx->base_mrec;
11361da177e4SLinus Torvalds ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
11371da177e4SLinus Torvalds le16_to_cpu(ctx->mrec->attrs_offset));
1138c49c3111SRichard Knutsson ctx->is_first = true;
11391da177e4SLinus Torvalds ctx->ntfs_ino = base_ni;
11401da177e4SLinus Torvalds ctx->base_ntfs_ino = NULL;
11411da177e4SLinus Torvalds ctx->base_mrec = NULL;
11421da177e4SLinus Torvalds ctx->base_attr = NULL;
11431da177e4SLinus Torvalds /*
11441da177e4SLinus Torvalds * In case there are multiple matches in the base mft record, need to
11451da177e4SLinus Torvalds * keep enumerating until we get an attribute not found response (or
11461da177e4SLinus Torvalds * another error), otherwise we would keep returning the same attribute
11471da177e4SLinus Torvalds * over and over again and all programs using us for enumeration would
11481da177e4SLinus Torvalds * lock up in a tight loop.
11491da177e4SLinus Torvalds */
11501da177e4SLinus Torvalds do {
11511da177e4SLinus Torvalds err = ntfs_attr_find(type, name, name_len, ic, val, val_len,
11521da177e4SLinus Torvalds ctx);
11531da177e4SLinus Torvalds } while (!err);
11541da177e4SLinus Torvalds ntfs_debug("Done, not found.");
11551da177e4SLinus Torvalds return err;
11561da177e4SLinus Torvalds }
11571da177e4SLinus Torvalds
11581da177e4SLinus Torvalds /**
11591da177e4SLinus Torvalds * ntfs_attr_lookup - find an attribute in an ntfs inode
11601da177e4SLinus Torvalds * @type: attribute type to find
11611da177e4SLinus Torvalds * @name: attribute name to find (optional, i.e. NULL means don't care)
11621da177e4SLinus Torvalds * @name_len: attribute name length (only needed if @name present)
11631da177e4SLinus Torvalds * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present)
11641da177e4SLinus Torvalds * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only)
11651da177e4SLinus Torvalds * @val: attribute value to find (optional, resident attributes only)
11661da177e4SLinus Torvalds * @val_len: attribute value length
11671da177e4SLinus Torvalds * @ctx: search context with mft record and attribute to search from
11681da177e4SLinus Torvalds *
11691da177e4SLinus Torvalds * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must
11701da177e4SLinus Torvalds * be the base mft record and @ctx must have been obtained from a call to
11711da177e4SLinus Torvalds * ntfs_attr_get_search_ctx().
11721da177e4SLinus Torvalds *
11731da177e4SLinus Torvalds * This function transparently handles attribute lists and @ctx is used to
11741da177e4SLinus Torvalds * continue searches where they were left off at.
11751da177e4SLinus Torvalds *
11761da177e4SLinus Torvalds * After finishing with the attribute/mft record you need to call
11771da177e4SLinus Torvalds * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
11781da177e4SLinus Torvalds * mapped inodes, etc).
11791da177e4SLinus Torvalds *
11801da177e4SLinus Torvalds * Return 0 if the search was successful and -errno if not.
11811da177e4SLinus Torvalds *
11821da177e4SLinus Torvalds * When 0, @ctx->attr is the found attribute and it is in mft record
11831da177e4SLinus Torvalds * @ctx->mrec. If an attribute list attribute is present, @ctx->al_entry is
11841da177e4SLinus Torvalds * the attribute list entry of the found attribute.
11851da177e4SLinus Torvalds *
11861da177e4SLinus Torvalds * When -ENOENT, @ctx->attr is the attribute which collates just after the
11871da177e4SLinus Torvalds * attribute being searched for, i.e. if one wants to add the attribute to the
11881da177e4SLinus Torvalds * mft record this is the correct place to insert it into. If an attribute
11891da177e4SLinus Torvalds * list attribute is present, @ctx->al_entry is the attribute list entry which
11901da177e4SLinus Torvalds * collates just after the attribute list entry of the attribute being searched
11911da177e4SLinus Torvalds * for, i.e. if one wants to add the attribute to the mft record this is the
11921da177e4SLinus Torvalds * correct place to insert its attribute list entry into.
11931da177e4SLinus Torvalds *
119425985edcSLucas De Marchi * When -errno != -ENOENT, an error occurred during the lookup. @ctx->attr is
11951da177e4SLinus Torvalds * then undefined and in particular you should not rely on it not changing.
11961da177e4SLinus Torvalds */
ntfs_attr_lookup(const ATTR_TYPE type,const ntfschar * name,const u32 name_len,const IGNORE_CASE_BOOL ic,const VCN lowest_vcn,const u8 * val,const u32 val_len,ntfs_attr_search_ctx * ctx)11971da177e4SLinus Torvalds int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
11981da177e4SLinus Torvalds const u32 name_len, const IGNORE_CASE_BOOL ic,
11991da177e4SLinus Torvalds const VCN lowest_vcn, const u8 *val, const u32 val_len,
12001da177e4SLinus Torvalds ntfs_attr_search_ctx *ctx)
12011da177e4SLinus Torvalds {
12021da177e4SLinus Torvalds ntfs_inode *base_ni;
12031da177e4SLinus Torvalds
12041da177e4SLinus Torvalds ntfs_debug("Entering.");
120569b41e3cSAnton Altaparmakov BUG_ON(IS_ERR(ctx->mrec));
12061da177e4SLinus Torvalds if (ctx->base_ntfs_ino)
12071da177e4SLinus Torvalds base_ni = ctx->base_ntfs_ino;
12081da177e4SLinus Torvalds else
12091da177e4SLinus Torvalds base_ni = ctx->ntfs_ino;
12101da177e4SLinus Torvalds /* Sanity check, just for debugging really. */
12111da177e4SLinus Torvalds BUG_ON(!base_ni);
12121da177e4SLinus Torvalds if (!NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST)
12131da177e4SLinus Torvalds return ntfs_attr_find(type, name, name_len, ic, val, val_len,
12141da177e4SLinus Torvalds ctx);
12151da177e4SLinus Torvalds return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn,
12161da177e4SLinus Torvalds val, val_len, ctx);
12171da177e4SLinus Torvalds }
12181da177e4SLinus Torvalds
12191da177e4SLinus Torvalds /**
12201da177e4SLinus Torvalds * ntfs_attr_init_search_ctx - initialize an attribute search context
12211da177e4SLinus Torvalds * @ctx: attribute search context to initialize
12221da177e4SLinus Torvalds * @ni: ntfs inode with which to initialize the search context
12231da177e4SLinus Torvalds * @mrec: mft record with which to initialize the search context
12241da177e4SLinus Torvalds *
12251da177e4SLinus Torvalds * Initialize the attribute search context @ctx with @ni and @mrec.
12261da177e4SLinus Torvalds */
ntfs_attr_init_search_ctx(ntfs_attr_search_ctx * ctx,ntfs_inode * ni,MFT_RECORD * mrec)12271da177e4SLinus Torvalds static inline void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx,
12281da177e4SLinus Torvalds ntfs_inode *ni, MFT_RECORD *mrec)
12291da177e4SLinus Torvalds {
1230442d207eSAnton Altaparmakov *ctx = (ntfs_attr_search_ctx) {
1231442d207eSAnton Altaparmakov .mrec = mrec,
12321da177e4SLinus Torvalds /* Sanity checks are performed elsewhere. */
1233442d207eSAnton Altaparmakov .attr = (ATTR_RECORD*)((u8*)mrec +
1234442d207eSAnton Altaparmakov le16_to_cpu(mrec->attrs_offset)),
1235c49c3111SRichard Knutsson .is_first = true,
1236442d207eSAnton Altaparmakov .ntfs_ino = ni,
1237442d207eSAnton Altaparmakov };
12381da177e4SLinus Torvalds }
12391da177e4SLinus Torvalds
12401da177e4SLinus Torvalds /**
12411da177e4SLinus Torvalds * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context
12421da177e4SLinus Torvalds * @ctx: attribute search context to reinitialize
12431da177e4SLinus Torvalds *
12441da177e4SLinus Torvalds * Reinitialize the attribute search context @ctx, unmapping an associated
12451da177e4SLinus Torvalds * extent mft record if present, and initialize the search context again.
12461da177e4SLinus Torvalds *
12471da177e4SLinus Torvalds * This is used when a search for a new attribute is being started to reset
12481da177e4SLinus Torvalds * the search context to the beginning.
12491da177e4SLinus Torvalds */
ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx * ctx)12501da177e4SLinus Torvalds void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx)
12511da177e4SLinus Torvalds {
12521da177e4SLinus Torvalds if (likely(!ctx->base_ntfs_ino)) {
12531da177e4SLinus Torvalds /* No attribute list. */
1254c49c3111SRichard Knutsson ctx->is_first = true;
12551da177e4SLinus Torvalds /* Sanity checks are performed elsewhere. */
12561da177e4SLinus Torvalds ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
12571da177e4SLinus Torvalds le16_to_cpu(ctx->mrec->attrs_offset));
12581da177e4SLinus Torvalds /*
12591da177e4SLinus Torvalds * This needs resetting due to ntfs_external_attr_find() which
12601da177e4SLinus Torvalds * can leave it set despite having zeroed ctx->base_ntfs_ino.
12611da177e4SLinus Torvalds */
12621da177e4SLinus Torvalds ctx->al_entry = NULL;
12631da177e4SLinus Torvalds return;
12641da177e4SLinus Torvalds } /* Attribute list. */
12651da177e4SLinus Torvalds if (ctx->ntfs_ino != ctx->base_ntfs_ino)
12661da177e4SLinus Torvalds unmap_extent_mft_record(ctx->ntfs_ino);
12671da177e4SLinus Torvalds ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec);
12681da177e4SLinus Torvalds return;
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds
12711da177e4SLinus Torvalds /**
12721da177e4SLinus Torvalds * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context
12731da177e4SLinus Torvalds * @ni: ntfs inode with which to initialize the search context
12741da177e4SLinus Torvalds * @mrec: mft record with which to initialize the search context
12751da177e4SLinus Torvalds *
12761da177e4SLinus Torvalds * Allocate a new attribute search context, initialize it with @ni and @mrec,
12771da177e4SLinus Torvalds * and return it. Return NULL if allocation failed.
12781da177e4SLinus Torvalds */
ntfs_attr_get_search_ctx(ntfs_inode * ni,MFT_RECORD * mrec)12791da177e4SLinus Torvalds ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec)
12801da177e4SLinus Torvalds {
12811da177e4SLinus Torvalds ntfs_attr_search_ctx *ctx;
12821da177e4SLinus Torvalds
1283e6b4f8daSChristoph Lameter ctx = kmem_cache_alloc(ntfs_attr_ctx_cache, GFP_NOFS);
12841da177e4SLinus Torvalds if (ctx)
12851da177e4SLinus Torvalds ntfs_attr_init_search_ctx(ctx, ni, mrec);
12861da177e4SLinus Torvalds return ctx;
12871da177e4SLinus Torvalds }
12881da177e4SLinus Torvalds
12891da177e4SLinus Torvalds /**
12901da177e4SLinus Torvalds * ntfs_attr_put_search_ctx - release an attribute search context
12911da177e4SLinus Torvalds * @ctx: attribute search context to free
12921da177e4SLinus Torvalds *
12931da177e4SLinus Torvalds * Release the attribute search context @ctx, unmapping an associated extent
12941da177e4SLinus Torvalds * mft record if present.
12951da177e4SLinus Torvalds */
ntfs_attr_put_search_ctx(ntfs_attr_search_ctx * ctx)12961da177e4SLinus Torvalds void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
12971da177e4SLinus Torvalds {
12981da177e4SLinus Torvalds if (ctx->base_ntfs_ino && ctx->ntfs_ino != ctx->base_ntfs_ino)
12991da177e4SLinus Torvalds unmap_extent_mft_record(ctx->ntfs_ino);
13001da177e4SLinus Torvalds kmem_cache_free(ntfs_attr_ctx_cache, ctx);
13011da177e4SLinus Torvalds return;
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds
130453d59aadSAnton Altaparmakov #ifdef NTFS_RW
130553d59aadSAnton Altaparmakov
13061da177e4SLinus Torvalds /**
13071da177e4SLinus Torvalds * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
13081da177e4SLinus Torvalds * @vol: ntfs volume to which the attribute belongs
13091da177e4SLinus Torvalds * @type: attribute type which to find
13101da177e4SLinus Torvalds *
13111da177e4SLinus Torvalds * Search for the attribute definition record corresponding to the attribute
13121da177e4SLinus Torvalds * @type in the $AttrDef system file.
13131da177e4SLinus Torvalds *
13141da177e4SLinus Torvalds * Return the attribute type definition record if found and NULL if not found.
13151da177e4SLinus Torvalds */
ntfs_attr_find_in_attrdef(const ntfs_volume * vol,const ATTR_TYPE type)13161da177e4SLinus Torvalds static ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
13171da177e4SLinus Torvalds const ATTR_TYPE type)
13181da177e4SLinus Torvalds {
13191da177e4SLinus Torvalds ATTR_DEF *ad;
13201da177e4SLinus Torvalds
13211da177e4SLinus Torvalds BUG_ON(!vol->attrdef);
13221da177e4SLinus Torvalds BUG_ON(!type);
13231da177e4SLinus Torvalds for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
13241da177e4SLinus Torvalds vol->attrdef_size && ad->type; ++ad) {
13251da177e4SLinus Torvalds /* We have not found it yet, carry on searching. */
13261da177e4SLinus Torvalds if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type)))
13271da177e4SLinus Torvalds continue;
13281da177e4SLinus Torvalds /* We found the attribute; return it. */
13291da177e4SLinus Torvalds if (likely(ad->type == type))
13301da177e4SLinus Torvalds return ad;
13311da177e4SLinus Torvalds /* We have gone too far already. No point in continuing. */
13321da177e4SLinus Torvalds break;
13331da177e4SLinus Torvalds }
13341da177e4SLinus Torvalds /* Attribute not found. */
13351da177e4SLinus Torvalds ntfs_debug("Attribute type 0x%x not found in $AttrDef.",
13361da177e4SLinus Torvalds le32_to_cpu(type));
13371da177e4SLinus Torvalds return NULL;
13381da177e4SLinus Torvalds }
13391da177e4SLinus Torvalds
13401da177e4SLinus Torvalds /**
13411da177e4SLinus Torvalds * ntfs_attr_size_bounds_check - check a size of an attribute type for validity
13421da177e4SLinus Torvalds * @vol: ntfs volume to which the attribute belongs
13431da177e4SLinus Torvalds * @type: attribute type which to check
13441da177e4SLinus Torvalds * @size: size which to check
13451da177e4SLinus Torvalds *
13461da177e4SLinus Torvalds * Check whether the @size in bytes is valid for an attribute of @type on the
13471da177e4SLinus Torvalds * ntfs volume @vol. This information is obtained from $AttrDef system file.
13481da177e4SLinus Torvalds *
13491da177e4SLinus Torvalds * Return 0 if valid, -ERANGE if not valid, or -ENOENT if the attribute is not
13501da177e4SLinus Torvalds * listed in $AttrDef.
13511da177e4SLinus Torvalds */
ntfs_attr_size_bounds_check(const ntfs_volume * vol,const ATTR_TYPE type,const s64 size)13521da177e4SLinus Torvalds int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPE type,
13531da177e4SLinus Torvalds const s64 size)
13541da177e4SLinus Torvalds {
13551da177e4SLinus Torvalds ATTR_DEF *ad;
13561da177e4SLinus Torvalds
13571da177e4SLinus Torvalds BUG_ON(size < 0);
13581da177e4SLinus Torvalds /*
13591da177e4SLinus Torvalds * $ATTRIBUTE_LIST has a maximum size of 256kiB, but this is not
13601da177e4SLinus Torvalds * listed in $AttrDef.
13611da177e4SLinus Torvalds */
13621da177e4SLinus Torvalds if (unlikely(type == AT_ATTRIBUTE_LIST && size > 256 * 1024))
13631da177e4SLinus Torvalds return -ERANGE;
13641da177e4SLinus Torvalds /* Get the $AttrDef entry for the attribute @type. */
13651da177e4SLinus Torvalds ad = ntfs_attr_find_in_attrdef(vol, type);
13661da177e4SLinus Torvalds if (unlikely(!ad))
13671da177e4SLinus Torvalds return -ENOENT;
13681da177e4SLinus Torvalds /* Do the bounds check. */
13691da177e4SLinus Torvalds if (((sle64_to_cpu(ad->min_size) > 0) &&
13701da177e4SLinus Torvalds size < sle64_to_cpu(ad->min_size)) ||
13711da177e4SLinus Torvalds ((sle64_to_cpu(ad->max_size) > 0) && size >
13721da177e4SLinus Torvalds sle64_to_cpu(ad->max_size)))
13731da177e4SLinus Torvalds return -ERANGE;
13741da177e4SLinus Torvalds return 0;
13751da177e4SLinus Torvalds }
13761da177e4SLinus Torvalds
13771da177e4SLinus Torvalds /**
13781da177e4SLinus Torvalds * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident
13791da177e4SLinus Torvalds * @vol: ntfs volume to which the attribute belongs
13801da177e4SLinus Torvalds * @type: attribute type which to check
13811da177e4SLinus Torvalds *
13821da177e4SLinus Torvalds * Check whether the attribute of @type on the ntfs volume @vol is allowed to
13831da177e4SLinus Torvalds * be non-resident. This information is obtained from $AttrDef system file.
13841da177e4SLinus Torvalds *
1385bb3cf335SAnton Altaparmakov * Return 0 if the attribute is allowed to be non-resident, -EPERM if not, and
13861da177e4SLinus Torvalds * -ENOENT if the attribute is not listed in $AttrDef.
13871da177e4SLinus Torvalds */
ntfs_attr_can_be_non_resident(const ntfs_volume * vol,const ATTR_TYPE type)13881da177e4SLinus Torvalds int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
13891da177e4SLinus Torvalds {
13901da177e4SLinus Torvalds ATTR_DEF *ad;
13911da177e4SLinus Torvalds
13921da177e4SLinus Torvalds /* Find the attribute definition record in $AttrDef. */
13931da177e4SLinus Torvalds ad = ntfs_attr_find_in_attrdef(vol, type);
13941da177e4SLinus Torvalds if (unlikely(!ad))
13951da177e4SLinus Torvalds return -ENOENT;
13961da177e4SLinus Torvalds /* Check the flags and return the result. */
1397bb3cf335SAnton Altaparmakov if (ad->flags & ATTR_DEF_RESIDENT)
13981da177e4SLinus Torvalds return -EPERM;
1399bb3cf335SAnton Altaparmakov return 0;
14001da177e4SLinus Torvalds }
14011da177e4SLinus Torvalds
14021da177e4SLinus Torvalds /**
14031da177e4SLinus Torvalds * ntfs_attr_can_be_resident - check if an attribute can be resident
14041da177e4SLinus Torvalds * @vol: ntfs volume to which the attribute belongs
14051da177e4SLinus Torvalds * @type: attribute type which to check
14061da177e4SLinus Torvalds *
14071da177e4SLinus Torvalds * Check whether the attribute of @type on the ntfs volume @vol is allowed to
14081da177e4SLinus Torvalds * be resident. This information is derived from our ntfs knowledge and may
14091da177e4SLinus Torvalds * not be completely accurate, especially when user defined attributes are
14101da177e4SLinus Torvalds * present. Basically we allow everything to be resident except for index
14111da177e4SLinus Torvalds * allocation and $EA attributes.
14121da177e4SLinus Torvalds *
14131da177e4SLinus Torvalds * Return 0 if the attribute is allowed to be non-resident and -EPERM if not.
14141da177e4SLinus Torvalds *
14151da177e4SLinus Torvalds * Warning: In the system file $MFT the attribute $Bitmap must be non-resident
14161da177e4SLinus Torvalds * otherwise windows will not boot (blue screen of death)! We cannot
14171da177e4SLinus Torvalds * check for this here as we do not know which inode's $Bitmap is
14181da177e4SLinus Torvalds * being asked about so the caller needs to special case this.
14191da177e4SLinus Torvalds */
ntfs_attr_can_be_resident(const ntfs_volume * vol,const ATTR_TYPE type)14201da177e4SLinus Torvalds int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
14211da177e4SLinus Torvalds {
14227d0ffdb2SAnton Altaparmakov if (type == AT_INDEX_ALLOCATION)
14231da177e4SLinus Torvalds return -EPERM;
1424bb3cf335SAnton Altaparmakov return 0;
14251da177e4SLinus Torvalds }
14261da177e4SLinus Torvalds
14271da177e4SLinus Torvalds /**
14281da177e4SLinus Torvalds * ntfs_attr_record_resize - resize an attribute record
14291da177e4SLinus Torvalds * @m: mft record containing attribute record
14301da177e4SLinus Torvalds * @a: attribute record to resize
14311da177e4SLinus Torvalds * @new_size: new size in bytes to which to resize the attribute record @a
14321da177e4SLinus Torvalds *
14331da177e4SLinus Torvalds * Resize the attribute record @a, i.e. the resident part of the attribute, in
14341da177e4SLinus Torvalds * the mft record @m to @new_size bytes.
14351da177e4SLinus Torvalds *
14361da177e4SLinus Torvalds * Return 0 on success and -errno on error. The following error codes are
14371da177e4SLinus Torvalds * defined:
14381da177e4SLinus Torvalds * -ENOSPC - Not enough space in the mft record @m to perform the resize.
14391da177e4SLinus Torvalds *
14401da177e4SLinus Torvalds * Note: On error, no modifications have been performed whatsoever.
14411da177e4SLinus Torvalds *
14421da177e4SLinus Torvalds * Warning: If you make a record smaller without having copied all the data you
14431da177e4SLinus Torvalds * are interested in the data may be overwritten.
14441da177e4SLinus Torvalds */
ntfs_attr_record_resize(MFT_RECORD * m,ATTR_RECORD * a,u32 new_size)14451da177e4SLinus Torvalds int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
14461da177e4SLinus Torvalds {
14471da177e4SLinus Torvalds ntfs_debug("Entering for new_size %u.", new_size);
14481da177e4SLinus Torvalds /* Align to 8 bytes if it is not already done. */
14491da177e4SLinus Torvalds if (new_size & 7)
14501da177e4SLinus Torvalds new_size = (new_size + 7) & ~7;
14511da177e4SLinus Torvalds /* If the actual attribute length has changed, move things around. */
14521da177e4SLinus Torvalds if (new_size != le32_to_cpu(a->length)) {
14531da177e4SLinus Torvalds u32 new_muse = le32_to_cpu(m->bytes_in_use) -
14541da177e4SLinus Torvalds le32_to_cpu(a->length) + new_size;
14551da177e4SLinus Torvalds /* Not enough space in this mft record. */
14561da177e4SLinus Torvalds if (new_muse > le32_to_cpu(m->bytes_allocated))
14571da177e4SLinus Torvalds return -ENOSPC;
14581da177e4SLinus Torvalds /* Move attributes following @a to their new location. */
14591da177e4SLinus Torvalds memmove((u8*)a + new_size, (u8*)a + le32_to_cpu(a->length),
14601da177e4SLinus Torvalds le32_to_cpu(m->bytes_in_use) - ((u8*)a -
14611da177e4SLinus Torvalds (u8*)m) - le32_to_cpu(a->length));
14621da177e4SLinus Torvalds /* Adjust @m to reflect the change in used space. */
14631da177e4SLinus Torvalds m->bytes_in_use = cpu_to_le32(new_muse);
14641da177e4SLinus Torvalds /* Adjust @a to reflect the new size. */
14651da177e4SLinus Torvalds if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length))
14661da177e4SLinus Torvalds a->length = cpu_to_le32(new_size);
14671da177e4SLinus Torvalds }
14681da177e4SLinus Torvalds return 0;
14691da177e4SLinus Torvalds }
14701da177e4SLinus Torvalds
14711da177e4SLinus Torvalds /**
14720aacceacSAnton Altaparmakov * ntfs_resident_attr_value_resize - resize the value of a resident attribute
14730aacceacSAnton Altaparmakov * @m: mft record containing attribute record
14740aacceacSAnton Altaparmakov * @a: attribute record whose value to resize
14750aacceacSAnton Altaparmakov * @new_size: new size in bytes to which to resize the attribute value of @a
14760aacceacSAnton Altaparmakov *
14770aacceacSAnton Altaparmakov * Resize the value of the attribute @a in the mft record @m to @new_size bytes.
14780aacceacSAnton Altaparmakov * If the value is made bigger, the newly allocated space is cleared.
14790aacceacSAnton Altaparmakov *
14800aacceacSAnton Altaparmakov * Return 0 on success and -errno on error. The following error codes are
14810aacceacSAnton Altaparmakov * defined:
14820aacceacSAnton Altaparmakov * -ENOSPC - Not enough space in the mft record @m to perform the resize.
14830aacceacSAnton Altaparmakov *
14840aacceacSAnton Altaparmakov * Note: On error, no modifications have been performed whatsoever.
14850aacceacSAnton Altaparmakov *
14860aacceacSAnton Altaparmakov * Warning: If you make a record smaller without having copied all the data you
14870aacceacSAnton Altaparmakov * are interested in the data may be overwritten.
14880aacceacSAnton Altaparmakov */
ntfs_resident_attr_value_resize(MFT_RECORD * m,ATTR_RECORD * a,const u32 new_size)14890aacceacSAnton Altaparmakov int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
14900aacceacSAnton Altaparmakov const u32 new_size)
14910aacceacSAnton Altaparmakov {
14920aacceacSAnton Altaparmakov u32 old_size;
14930aacceacSAnton Altaparmakov
14940aacceacSAnton Altaparmakov /* Resize the resident part of the attribute record. */
14950aacceacSAnton Altaparmakov if (ntfs_attr_record_resize(m, a,
14960aacceacSAnton Altaparmakov le16_to_cpu(a->data.resident.value_offset) + new_size))
14970aacceacSAnton Altaparmakov return -ENOSPC;
14980aacceacSAnton Altaparmakov /*
14990aacceacSAnton Altaparmakov * The resize succeeded! If we made the attribute value bigger, clear
15000aacceacSAnton Altaparmakov * the area between the old size and @new_size.
15010aacceacSAnton Altaparmakov */
15020aacceacSAnton Altaparmakov old_size = le32_to_cpu(a->data.resident.value_length);
15030aacceacSAnton Altaparmakov if (new_size > old_size)
15040aacceacSAnton Altaparmakov memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) +
15050aacceacSAnton Altaparmakov old_size, 0, new_size - old_size);
15060aacceacSAnton Altaparmakov /* Finally update the length of the attribute value. */
15070aacceacSAnton Altaparmakov a->data.resident.value_length = cpu_to_le32(new_size);
15080aacceacSAnton Altaparmakov return 0;
15090aacceacSAnton Altaparmakov }
15100aacceacSAnton Altaparmakov
15110aacceacSAnton Altaparmakov /**
15122bfb4fffSAnton Altaparmakov * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
15132bfb4fffSAnton Altaparmakov * @ni: ntfs inode describing the attribute to convert
15148925d4f0SAnton Altaparmakov * @data_size: size of the resident data to copy to the non-resident attribute
15152bfb4fffSAnton Altaparmakov *
15162bfb4fffSAnton Altaparmakov * Convert the resident ntfs attribute described by the ntfs inode @ni to a
15172bfb4fffSAnton Altaparmakov * non-resident one.
15182bfb4fffSAnton Altaparmakov *
15198925d4f0SAnton Altaparmakov * @data_size must be equal to the attribute value size. This is needed since
15208925d4f0SAnton Altaparmakov * we need to know the size before we can map the mft record and our callers
15218925d4f0SAnton Altaparmakov * always know it. The reason we cannot simply read the size from the vfs
15228925d4f0SAnton Altaparmakov * inode i_size is that this is not necessarily uptodate. This happens when
15238925d4f0SAnton Altaparmakov * ntfs_attr_make_non_resident() is called in the ->truncate call path(s).
15248925d4f0SAnton Altaparmakov *
15252bfb4fffSAnton Altaparmakov * Return 0 on success and -errno on error. The following error return codes
15262bfb4fffSAnton Altaparmakov * are defined:
15272bfb4fffSAnton Altaparmakov * -EPERM - The attribute is not allowed to be non-resident.
15282bfb4fffSAnton Altaparmakov * -ENOMEM - Not enough memory.
15292bfb4fffSAnton Altaparmakov * -ENOSPC - Not enough disk space.
15302bfb4fffSAnton Altaparmakov * -EINVAL - Attribute not defined on the volume.
15312bfb4fffSAnton Altaparmakov * -EIO - I/o error or other error.
153253d59aadSAnton Altaparmakov * Note that -ENOSPC is also returned in the case that there is not enough
153353d59aadSAnton Altaparmakov * space in the mft record to do the conversion. This can happen when the mft
153453d59aadSAnton Altaparmakov * record is already very full. The caller is responsible for trying to make
153553d59aadSAnton Altaparmakov * space in the mft record and trying again. FIXME: Do we need a separate
153653d59aadSAnton Altaparmakov * error return code for this kind of -ENOSPC or is it always worth trying
153753d59aadSAnton Altaparmakov * again in case the attribute may then fit in a resident state so no need to
153853d59aadSAnton Altaparmakov * make it non-resident at all? Ho-hum... (AIA)
15392bfb4fffSAnton Altaparmakov *
15402bfb4fffSAnton Altaparmakov * NOTE to self: No changes in the attribute list are required to move from
15412bfb4fffSAnton Altaparmakov * a resident to a non-resident attribute.
15422bfb4fffSAnton Altaparmakov *
15431b1dcc1bSJes Sorensen * Locking: - The caller must hold i_mutex on the inode.
15442bfb4fffSAnton Altaparmakov */
ntfs_attr_make_non_resident(ntfs_inode * ni,const u32 data_size)15458925d4f0SAnton Altaparmakov int ntfs_attr_make_non_resident(ntfs_inode *ni, const u32 data_size)
15462bfb4fffSAnton Altaparmakov {
15472bfb4fffSAnton Altaparmakov s64 new_size;
15482bfb4fffSAnton Altaparmakov struct inode *vi = VFS_I(ni);
15492bfb4fffSAnton Altaparmakov ntfs_volume *vol = ni->vol;
15502bfb4fffSAnton Altaparmakov ntfs_inode *base_ni;
15512bfb4fffSAnton Altaparmakov MFT_RECORD *m;
15522bfb4fffSAnton Altaparmakov ATTR_RECORD *a;
15532bfb4fffSAnton Altaparmakov ntfs_attr_search_ctx *ctx;
15542bfb4fffSAnton Altaparmakov struct page *page;
15552bfb4fffSAnton Altaparmakov runlist_element *rl;
15562bfb4fffSAnton Altaparmakov u8 *kaddr;
15572bfb4fffSAnton Altaparmakov unsigned long flags;
15582bfb4fffSAnton Altaparmakov int mp_size, mp_ofs, name_ofs, arec_size, err, err2;
15592bfb4fffSAnton Altaparmakov u32 attr_size;
15602bfb4fffSAnton Altaparmakov u8 old_res_attr_flags;
15612bfb4fffSAnton Altaparmakov
15622bfb4fffSAnton Altaparmakov /* Check that the attribute is allowed to be non-resident. */
15632bfb4fffSAnton Altaparmakov err = ntfs_attr_can_be_non_resident(vol, ni->type);
15642bfb4fffSAnton Altaparmakov if (unlikely(err)) {
15652bfb4fffSAnton Altaparmakov if (err == -EPERM)
15662bfb4fffSAnton Altaparmakov ntfs_debug("Attribute is not allowed to be "
15672bfb4fffSAnton Altaparmakov "non-resident.");
15682bfb4fffSAnton Altaparmakov else
15692bfb4fffSAnton Altaparmakov ntfs_debug("Attribute not defined on the NTFS "
15702bfb4fffSAnton Altaparmakov "volume!");
15712bfb4fffSAnton Altaparmakov return err;
15722bfb4fffSAnton Altaparmakov }
15732bfb4fffSAnton Altaparmakov /*
1574807c453dSAnton Altaparmakov * FIXME: Compressed and encrypted attributes are not supported when
1575807c453dSAnton Altaparmakov * writing and we should never have gotten here for them.
1576807c453dSAnton Altaparmakov */
1577807c453dSAnton Altaparmakov BUG_ON(NInoCompressed(ni));
1578807c453dSAnton Altaparmakov BUG_ON(NInoEncrypted(ni));
1579807c453dSAnton Altaparmakov /*
15802bfb4fffSAnton Altaparmakov * The size needs to be aligned to a cluster boundary for allocation
15812bfb4fffSAnton Altaparmakov * purposes.
15822bfb4fffSAnton Altaparmakov */
15838925d4f0SAnton Altaparmakov new_size = (data_size + vol->cluster_size - 1) &
15842bfb4fffSAnton Altaparmakov ~(vol->cluster_size - 1);
15852bfb4fffSAnton Altaparmakov if (new_size > 0) {
15862bfb4fffSAnton Altaparmakov /*
15872bfb4fffSAnton Altaparmakov * Will need the page later and since the page lock nests
15882bfb4fffSAnton Altaparmakov * outside all ntfs locks, we need to get the page now.
15892bfb4fffSAnton Altaparmakov */
15902bfb4fffSAnton Altaparmakov page = find_or_create_page(vi->i_mapping, 0,
15912bfb4fffSAnton Altaparmakov mapping_gfp_mask(vi->i_mapping));
15922bfb4fffSAnton Altaparmakov if (unlikely(!page))
15932bfb4fffSAnton Altaparmakov return -ENOMEM;
15942bfb4fffSAnton Altaparmakov /* Start by allocating clusters to hold the attribute value. */
15952bfb4fffSAnton Altaparmakov rl = ntfs_cluster_alloc(vol, 0, new_size >>
1596c49c3111SRichard Knutsson vol->cluster_size_bits, -1, DATA_ZONE, true);
15972bfb4fffSAnton Altaparmakov if (IS_ERR(rl)) {
15982bfb4fffSAnton Altaparmakov err = PTR_ERR(rl);
15992bfb4fffSAnton Altaparmakov ntfs_debug("Failed to allocate cluster%s, error code "
1600af859a42SAnton Altaparmakov "%i.", (new_size >>
16012bfb4fffSAnton Altaparmakov vol->cluster_size_bits) > 1 ? "s" : "",
16022bfb4fffSAnton Altaparmakov err);
16032bfb4fffSAnton Altaparmakov goto page_err_out;
16042bfb4fffSAnton Altaparmakov }
16052bfb4fffSAnton Altaparmakov } else {
16062bfb4fffSAnton Altaparmakov rl = NULL;
16072bfb4fffSAnton Altaparmakov page = NULL;
16082bfb4fffSAnton Altaparmakov }
16092bfb4fffSAnton Altaparmakov /* Determine the size of the mapping pairs array. */
1610fa3be923SAnton Altaparmakov mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, -1);
16112bfb4fffSAnton Altaparmakov if (unlikely(mp_size < 0)) {
16122bfb4fffSAnton Altaparmakov err = mp_size;
16132bfb4fffSAnton Altaparmakov ntfs_debug("Failed to get size for mapping pairs array, error "
16142bfb4fffSAnton Altaparmakov "code %i.", err);
16152bfb4fffSAnton Altaparmakov goto rl_err_out;
16162bfb4fffSAnton Altaparmakov }
16172bfb4fffSAnton Altaparmakov down_write(&ni->runlist.lock);
16182bfb4fffSAnton Altaparmakov if (!NInoAttr(ni))
16192bfb4fffSAnton Altaparmakov base_ni = ni;
16202bfb4fffSAnton Altaparmakov else
16212bfb4fffSAnton Altaparmakov base_ni = ni->ext.base_ntfs_ino;
16222bfb4fffSAnton Altaparmakov m = map_mft_record(base_ni);
16232bfb4fffSAnton Altaparmakov if (IS_ERR(m)) {
16242bfb4fffSAnton Altaparmakov err = PTR_ERR(m);
16252bfb4fffSAnton Altaparmakov m = NULL;
16262bfb4fffSAnton Altaparmakov ctx = NULL;
16272bfb4fffSAnton Altaparmakov goto err_out;
16282bfb4fffSAnton Altaparmakov }
16292bfb4fffSAnton Altaparmakov ctx = ntfs_attr_get_search_ctx(base_ni, m);
16302bfb4fffSAnton Altaparmakov if (unlikely(!ctx)) {
16312bfb4fffSAnton Altaparmakov err = -ENOMEM;
16322bfb4fffSAnton Altaparmakov goto err_out;
16332bfb4fffSAnton Altaparmakov }
16342bfb4fffSAnton Altaparmakov err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
16352bfb4fffSAnton Altaparmakov CASE_SENSITIVE, 0, NULL, 0, ctx);
16362bfb4fffSAnton Altaparmakov if (unlikely(err)) {
16372bfb4fffSAnton Altaparmakov if (err == -ENOENT)
16382bfb4fffSAnton Altaparmakov err = -EIO;
16392bfb4fffSAnton Altaparmakov goto err_out;
16402bfb4fffSAnton Altaparmakov }
16412bfb4fffSAnton Altaparmakov m = ctx->mrec;
16422bfb4fffSAnton Altaparmakov a = ctx->attr;
16432bfb4fffSAnton Altaparmakov BUG_ON(NInoNonResident(ni));
16442bfb4fffSAnton Altaparmakov BUG_ON(a->non_resident);
16452bfb4fffSAnton Altaparmakov /*
16462bfb4fffSAnton Altaparmakov * Calculate new offsets for the name and the mapping pairs array.
16472bfb4fffSAnton Altaparmakov */
1648807c453dSAnton Altaparmakov if (NInoSparse(ni) || NInoCompressed(ni))
1649807c453dSAnton Altaparmakov name_ofs = (offsetof(ATTR_REC,
1650807c453dSAnton Altaparmakov data.non_resident.compressed_size) +
1651807c453dSAnton Altaparmakov sizeof(a->data.non_resident.compressed_size) +
1652807c453dSAnton Altaparmakov 7) & ~7;
1653807c453dSAnton Altaparmakov else
16542bfb4fffSAnton Altaparmakov name_ofs = (offsetof(ATTR_REC,
16552bfb4fffSAnton Altaparmakov data.non_resident.compressed_size) + 7) & ~7;
16562bfb4fffSAnton Altaparmakov mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
16572bfb4fffSAnton Altaparmakov /*
16582bfb4fffSAnton Altaparmakov * Determine the size of the resident part of the now non-resident
16592bfb4fffSAnton Altaparmakov * attribute record.
16602bfb4fffSAnton Altaparmakov */
16612bfb4fffSAnton Altaparmakov arec_size = (mp_ofs + mp_size + 7) & ~7;
16622bfb4fffSAnton Altaparmakov /*
16632bfb4fffSAnton Altaparmakov * If the page is not uptodate bring it uptodate by copying from the
16642bfb4fffSAnton Altaparmakov * attribute value.
16652bfb4fffSAnton Altaparmakov */
16662bfb4fffSAnton Altaparmakov attr_size = le32_to_cpu(a->data.resident.value_length);
16678925d4f0SAnton Altaparmakov BUG_ON(attr_size != data_size);
16682bfb4fffSAnton Altaparmakov if (page && !PageUptodate(page)) {
1669a3ac1414SCong Wang kaddr = kmap_atomic(page);
16702bfb4fffSAnton Altaparmakov memcpy(kaddr, (u8*)a +
16712bfb4fffSAnton Altaparmakov le16_to_cpu(a->data.resident.value_offset),
16722bfb4fffSAnton Altaparmakov attr_size);
167309cbfeafSKirill A. Shutemov memset(kaddr + attr_size, 0, PAGE_SIZE - attr_size);
1674a3ac1414SCong Wang kunmap_atomic(kaddr);
16752bfb4fffSAnton Altaparmakov flush_dcache_page(page);
16762bfb4fffSAnton Altaparmakov SetPageUptodate(page);
16772bfb4fffSAnton Altaparmakov }
16782bfb4fffSAnton Altaparmakov /* Backup the attribute flag. */
16792bfb4fffSAnton Altaparmakov old_res_attr_flags = a->data.resident.flags;
16802bfb4fffSAnton Altaparmakov /* Resize the resident part of the attribute record. */
16812bfb4fffSAnton Altaparmakov err = ntfs_attr_record_resize(m, a, arec_size);
16822bfb4fffSAnton Altaparmakov if (unlikely(err))
16832bfb4fffSAnton Altaparmakov goto err_out;
16842bfb4fffSAnton Altaparmakov /*
16852bfb4fffSAnton Altaparmakov * Convert the resident part of the attribute record to describe a
16862bfb4fffSAnton Altaparmakov * non-resident attribute.
16872bfb4fffSAnton Altaparmakov */
16882bfb4fffSAnton Altaparmakov a->non_resident = 1;
16892bfb4fffSAnton Altaparmakov /* Move the attribute name if it exists and update the offset. */
16902bfb4fffSAnton Altaparmakov if (a->name_length)
16912bfb4fffSAnton Altaparmakov memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
16922bfb4fffSAnton Altaparmakov a->name_length * sizeof(ntfschar));
16932bfb4fffSAnton Altaparmakov a->name_offset = cpu_to_le16(name_ofs);
16942bfb4fffSAnton Altaparmakov /* Setup the fields specific to non-resident attributes. */
16952bfb4fffSAnton Altaparmakov a->data.non_resident.lowest_vcn = 0;
16962bfb4fffSAnton Altaparmakov a->data.non_resident.highest_vcn = cpu_to_sle64((new_size - 1) >>
16972bfb4fffSAnton Altaparmakov vol->cluster_size_bits);
16982bfb4fffSAnton Altaparmakov a->data.non_resident.mapping_pairs_offset = cpu_to_le16(mp_ofs);
16992bfb4fffSAnton Altaparmakov memset(&a->data.non_resident.reserved, 0,
17002bfb4fffSAnton Altaparmakov sizeof(a->data.non_resident.reserved));
17012bfb4fffSAnton Altaparmakov a->data.non_resident.allocated_size = cpu_to_sle64(new_size);
17022bfb4fffSAnton Altaparmakov a->data.non_resident.data_size =
17032bfb4fffSAnton Altaparmakov a->data.non_resident.initialized_size =
17042bfb4fffSAnton Altaparmakov cpu_to_sle64(attr_size);
1705807c453dSAnton Altaparmakov if (NInoSparse(ni) || NInoCompressed(ni)) {
1706a0646a1fSAnton Altaparmakov a->data.non_resident.compression_unit = 0;
1707a0646a1fSAnton Altaparmakov if (NInoCompressed(ni) || vol->major_ver < 3)
1708807c453dSAnton Altaparmakov a->data.non_resident.compression_unit = 4;
1709807c453dSAnton Altaparmakov a->data.non_resident.compressed_size =
1710807c453dSAnton Altaparmakov a->data.non_resident.allocated_size;
1711807c453dSAnton Altaparmakov } else
1712807c453dSAnton Altaparmakov a->data.non_resident.compression_unit = 0;
17132bfb4fffSAnton Altaparmakov /* Generate the mapping pairs array into the attribute record. */
17142bfb4fffSAnton Altaparmakov err = ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs,
1715fa3be923SAnton Altaparmakov arec_size - mp_ofs, rl, 0, -1, NULL);
17162bfb4fffSAnton Altaparmakov if (unlikely(err)) {
17172bfb4fffSAnton Altaparmakov ntfs_debug("Failed to build mapping pairs, error code %i.",
17182bfb4fffSAnton Altaparmakov err);
17192bfb4fffSAnton Altaparmakov goto undo_err_out;
17202bfb4fffSAnton Altaparmakov }
1721905685f6SAnton Altaparmakov /* Setup the in-memory attribute structure to be non-resident. */
1722905685f6SAnton Altaparmakov ni->runlist.rl = rl;
1723905685f6SAnton Altaparmakov write_lock_irqsave(&ni->size_lock, flags);
1724905685f6SAnton Altaparmakov ni->allocated_size = new_size;
1725807c453dSAnton Altaparmakov if (NInoSparse(ni) || NInoCompressed(ni)) {
1726807c453dSAnton Altaparmakov ni->itype.compressed.size = ni->allocated_size;
1727a0646a1fSAnton Altaparmakov if (a->data.non_resident.compression_unit) {
1728a0646a1fSAnton Altaparmakov ni->itype.compressed.block_size = 1U << (a->data.
1729a0646a1fSAnton Altaparmakov non_resident.compression_unit +
1730807c453dSAnton Altaparmakov vol->cluster_size_bits);
1731807c453dSAnton Altaparmakov ni->itype.compressed.block_size_bits =
1732a0646a1fSAnton Altaparmakov ffs(ni->itype.compressed.block_size) -
1733a0646a1fSAnton Altaparmakov 1;
1734807c453dSAnton Altaparmakov ni->itype.compressed.block_clusters = 1U <<
1735807c453dSAnton Altaparmakov a->data.non_resident.compression_unit;
1736a0646a1fSAnton Altaparmakov } else {
1737a0646a1fSAnton Altaparmakov ni->itype.compressed.block_size = 0;
1738a0646a1fSAnton Altaparmakov ni->itype.compressed.block_size_bits = 0;
1739a0646a1fSAnton Altaparmakov ni->itype.compressed.block_clusters = 0;
1740a0646a1fSAnton Altaparmakov }
17412a6fc4e1SAnton Altaparmakov vi->i_blocks = ni->itype.compressed.size >> 9;
17422a6fc4e1SAnton Altaparmakov } else
17432a6fc4e1SAnton Altaparmakov vi->i_blocks = ni->allocated_size >> 9;
1744905685f6SAnton Altaparmakov write_unlock_irqrestore(&ni->size_lock, flags);
1745905685f6SAnton Altaparmakov /*
1746933906f8SMatthew Wilcox (Oracle) * This needs to be last since the address space operations ->read_folio
1747905685f6SAnton Altaparmakov * and ->writepage can run concurrently with us as they are not
17481b1dcc1bSJes Sorensen * serialized on i_mutex. Note, we are not allowed to fail once we flip
1749905685f6SAnton Altaparmakov * this switch, which is another reason to do this last.
1750905685f6SAnton Altaparmakov */
1751905685f6SAnton Altaparmakov NInoSetNonResident(ni);
17522bfb4fffSAnton Altaparmakov /* Mark the mft record dirty, so it gets written back. */
17532bfb4fffSAnton Altaparmakov flush_dcache_mft_record_page(ctx->ntfs_ino);
17542bfb4fffSAnton Altaparmakov mark_mft_record_dirty(ctx->ntfs_ino);
17552bfb4fffSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
17562bfb4fffSAnton Altaparmakov unmap_mft_record(base_ni);
17572bfb4fffSAnton Altaparmakov up_write(&ni->runlist.lock);
17582bfb4fffSAnton Altaparmakov if (page) {
17592bfb4fffSAnton Altaparmakov set_page_dirty(page);
17602bfb4fffSAnton Altaparmakov unlock_page(page);
176109cbfeafSKirill A. Shutemov put_page(page);
17622bfb4fffSAnton Altaparmakov }
17632bfb4fffSAnton Altaparmakov ntfs_debug("Done.");
17642bfb4fffSAnton Altaparmakov return 0;
17652bfb4fffSAnton Altaparmakov undo_err_out:
17662bfb4fffSAnton Altaparmakov /* Convert the attribute back into a resident attribute. */
17672bfb4fffSAnton Altaparmakov a->non_resident = 0;
17682bfb4fffSAnton Altaparmakov /* Move the attribute name if it exists and update the offset. */
17692bfb4fffSAnton Altaparmakov name_ofs = (offsetof(ATTR_RECORD, data.resident.reserved) +
17702bfb4fffSAnton Altaparmakov sizeof(a->data.resident.reserved) + 7) & ~7;
17712bfb4fffSAnton Altaparmakov if (a->name_length)
17722bfb4fffSAnton Altaparmakov memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
17732bfb4fffSAnton Altaparmakov a->name_length * sizeof(ntfschar));
17742bfb4fffSAnton Altaparmakov mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
17752bfb4fffSAnton Altaparmakov a->name_offset = cpu_to_le16(name_ofs);
17762bfb4fffSAnton Altaparmakov arec_size = (mp_ofs + attr_size + 7) & ~7;
17772bfb4fffSAnton Altaparmakov /* Resize the resident part of the attribute record. */
17782bfb4fffSAnton Altaparmakov err2 = ntfs_attr_record_resize(m, a, arec_size);
17792bfb4fffSAnton Altaparmakov if (unlikely(err2)) {
17802bfb4fffSAnton Altaparmakov /*
17812bfb4fffSAnton Altaparmakov * This cannot happen (well if memory corruption is at work it
17822bfb4fffSAnton Altaparmakov * could happen in theory), but deal with it as well as we can.
17832bfb4fffSAnton Altaparmakov * If the old size is too small, truncate the attribute,
17842bfb4fffSAnton Altaparmakov * otherwise simply give it a larger allocated size.
17852bfb4fffSAnton Altaparmakov * FIXME: Should check whether chkdsk complains when the
17862bfb4fffSAnton Altaparmakov * allocated size is much bigger than the resident value size.
17872bfb4fffSAnton Altaparmakov */
17882bfb4fffSAnton Altaparmakov arec_size = le32_to_cpu(a->length);
17892bfb4fffSAnton Altaparmakov if ((mp_ofs + attr_size) > arec_size) {
17902bfb4fffSAnton Altaparmakov err2 = attr_size;
17912bfb4fffSAnton Altaparmakov attr_size = arec_size - mp_ofs;
17922bfb4fffSAnton Altaparmakov ntfs_error(vol->sb, "Failed to undo partial resident "
17932bfb4fffSAnton Altaparmakov "to non-resident attribute "
17942bfb4fffSAnton Altaparmakov "conversion. Truncating inode 0x%lx, "
17952bfb4fffSAnton Altaparmakov "attribute type 0x%x from %i bytes to "
17962bfb4fffSAnton Altaparmakov "%i bytes to maintain metadata "
17972bfb4fffSAnton Altaparmakov "consistency. THIS MEANS YOU ARE "
17982bfb4fffSAnton Altaparmakov "LOSING %i BYTES DATA FROM THIS %s.",
17992bfb4fffSAnton Altaparmakov vi->i_ino,
18002bfb4fffSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type),
18012bfb4fffSAnton Altaparmakov err2, attr_size, err2 - attr_size,
18022bfb4fffSAnton Altaparmakov ((ni->type == AT_DATA) &&
18032bfb4fffSAnton Altaparmakov !ni->name_len) ? "FILE": "ATTRIBUTE");
18042bfb4fffSAnton Altaparmakov write_lock_irqsave(&ni->size_lock, flags);
18052bfb4fffSAnton Altaparmakov ni->initialized_size = attr_size;
18062bfb4fffSAnton Altaparmakov i_size_write(vi, attr_size);
18072bfb4fffSAnton Altaparmakov write_unlock_irqrestore(&ni->size_lock, flags);
18082bfb4fffSAnton Altaparmakov }
18092bfb4fffSAnton Altaparmakov }
18102bfb4fffSAnton Altaparmakov /* Setup the fields specific to resident attributes. */
18112bfb4fffSAnton Altaparmakov a->data.resident.value_length = cpu_to_le32(attr_size);
18122bfb4fffSAnton Altaparmakov a->data.resident.value_offset = cpu_to_le16(mp_ofs);
18132bfb4fffSAnton Altaparmakov a->data.resident.flags = old_res_attr_flags;
18142bfb4fffSAnton Altaparmakov memset(&a->data.resident.reserved, 0,
18152bfb4fffSAnton Altaparmakov sizeof(a->data.resident.reserved));
18162bfb4fffSAnton Altaparmakov /* Copy the data from the page back to the attribute value. */
18172bfb4fffSAnton Altaparmakov if (page) {
1818a3ac1414SCong Wang kaddr = kmap_atomic(page);
18192bfb4fffSAnton Altaparmakov memcpy((u8*)a + mp_ofs, kaddr, attr_size);
1820a3ac1414SCong Wang kunmap_atomic(kaddr);
18212bfb4fffSAnton Altaparmakov }
1822905685f6SAnton Altaparmakov /* Setup the allocated size in the ntfs inode in case it changed. */
18232bfb4fffSAnton Altaparmakov write_lock_irqsave(&ni->size_lock, flags);
18242bfb4fffSAnton Altaparmakov ni->allocated_size = arec_size - mp_ofs;
18252bfb4fffSAnton Altaparmakov write_unlock_irqrestore(&ni->size_lock, flags);
18262bfb4fffSAnton Altaparmakov /* Mark the mft record dirty, so it gets written back. */
18272bfb4fffSAnton Altaparmakov flush_dcache_mft_record_page(ctx->ntfs_ino);
18282bfb4fffSAnton Altaparmakov mark_mft_record_dirty(ctx->ntfs_ino);
18292bfb4fffSAnton Altaparmakov err_out:
18302bfb4fffSAnton Altaparmakov if (ctx)
18312bfb4fffSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
18322bfb4fffSAnton Altaparmakov if (m)
18332bfb4fffSAnton Altaparmakov unmap_mft_record(base_ni);
18342bfb4fffSAnton Altaparmakov ni->runlist.rl = NULL;
18352bfb4fffSAnton Altaparmakov up_write(&ni->runlist.lock);
18362bfb4fffSAnton Altaparmakov rl_err_out:
18372bfb4fffSAnton Altaparmakov if (rl) {
18382bfb4fffSAnton Altaparmakov if (ntfs_cluster_free_from_rl(vol, rl) < 0) {
18392bfb4fffSAnton Altaparmakov ntfs_error(vol->sb, "Failed to release allocated "
18402bfb4fffSAnton Altaparmakov "cluster(s) in error code path. Run "
18412bfb4fffSAnton Altaparmakov "chkdsk to recover the lost "
18422bfb4fffSAnton Altaparmakov "cluster(s).");
18432bfb4fffSAnton Altaparmakov NVolSetErrors(vol);
18442bfb4fffSAnton Altaparmakov }
184553d59aadSAnton Altaparmakov ntfs_free(rl);
18462bfb4fffSAnton Altaparmakov page_err_out:
18472bfb4fffSAnton Altaparmakov unlock_page(page);
184809cbfeafSKirill A. Shutemov put_page(page);
18492bfb4fffSAnton Altaparmakov }
18502bfb4fffSAnton Altaparmakov if (err == -EINVAL)
18512bfb4fffSAnton Altaparmakov err = -EIO;
18522bfb4fffSAnton Altaparmakov return err;
18532bfb4fffSAnton Altaparmakov }
18542bfb4fffSAnton Altaparmakov
18552bfb4fffSAnton Altaparmakov /**
18562d86829bSAnton Altaparmakov * ntfs_attr_extend_allocation - extend the allocated space of an attribute
18572d86829bSAnton Altaparmakov * @ni: ntfs inode of the attribute whose allocation to extend
18582d86829bSAnton Altaparmakov * @new_alloc_size: new size in bytes to which to extend the allocation to
18592d86829bSAnton Altaparmakov * @new_data_size: new size in bytes to which to extend the data to
18602d86829bSAnton Altaparmakov * @data_start: beginning of region which is required to be non-sparse
18612d86829bSAnton Altaparmakov *
18622d86829bSAnton Altaparmakov * Extend the allocated space of an attribute described by the ntfs inode @ni
18632d86829bSAnton Altaparmakov * to @new_alloc_size bytes. If @data_start is -1, the whole extension may be
18642d86829bSAnton Altaparmakov * implemented as a hole in the file (as long as both the volume and the ntfs
18652d86829bSAnton Altaparmakov * inode @ni have sparse support enabled). If @data_start is >= 0, then the
18662d86829bSAnton Altaparmakov * region between the old allocated size and @data_start - 1 may be made sparse
18672d86829bSAnton Altaparmakov * but the regions between @data_start and @new_alloc_size must be backed by
18682d86829bSAnton Altaparmakov * actual clusters.
18692d86829bSAnton Altaparmakov *
18702d86829bSAnton Altaparmakov * If @new_data_size is -1, it is ignored. If it is >= 0, then the data size
18712d86829bSAnton Altaparmakov * of the attribute is extended to @new_data_size. Note that the i_size of the
18722d86829bSAnton Altaparmakov * vfs inode is not updated. Only the data size in the base attribute record
18732d86829bSAnton Altaparmakov * is updated. The caller has to update i_size separately if this is required.
18742d86829bSAnton Altaparmakov * WARNING: It is a BUG() for @new_data_size to be smaller than the old data
18752d86829bSAnton Altaparmakov * size as well as for @new_data_size to be greater than @new_alloc_size.
18762d86829bSAnton Altaparmakov *
18772d86829bSAnton Altaparmakov * For resident attributes this involves resizing the attribute record and if
18782d86829bSAnton Altaparmakov * necessary moving it and/or other attributes into extent mft records and/or
18792d86829bSAnton Altaparmakov * converting the attribute to a non-resident attribute which in turn involves
18802d86829bSAnton Altaparmakov * extending the allocation of a non-resident attribute as described below.
18812d86829bSAnton Altaparmakov *
18822d86829bSAnton Altaparmakov * For non-resident attributes this involves allocating clusters in the data
18832d86829bSAnton Altaparmakov * zone on the volume (except for regions that are being made sparse) and
18842d86829bSAnton Altaparmakov * extending the run list to describe the allocated clusters as well as
18852d86829bSAnton Altaparmakov * updating the mapping pairs array of the attribute. This in turn involves
18862d86829bSAnton Altaparmakov * resizing the attribute record and if necessary moving it and/or other
18872d86829bSAnton Altaparmakov * attributes into extent mft records and/or splitting the attribute record
18882d86829bSAnton Altaparmakov * into multiple extent attribute records.
18892d86829bSAnton Altaparmakov *
18902d86829bSAnton Altaparmakov * Also, the attribute list attribute is updated if present and in some of the
18912d86829bSAnton Altaparmakov * above cases (the ones where extent mft records/attributes come into play),
18922d86829bSAnton Altaparmakov * an attribute list attribute is created if not already present.
18932d86829bSAnton Altaparmakov *
18942d86829bSAnton Altaparmakov * Return the new allocated size on success and -errno on error. In the case
18952d86829bSAnton Altaparmakov * that an error is encountered but a partial extension at least up to
18962d86829bSAnton Altaparmakov * @data_start (if present) is possible, the allocation is partially extended
18972d86829bSAnton Altaparmakov * and this is returned. This means the caller must check the returned size to
18982d86829bSAnton Altaparmakov * determine if the extension was partial. If @data_start is -1 then partial
18992d86829bSAnton Altaparmakov * allocations are not performed.
19002d86829bSAnton Altaparmakov *
19012d86829bSAnton Altaparmakov * WARNING: Do not call ntfs_attr_extend_allocation() for $MFT/$DATA.
19022d86829bSAnton Altaparmakov *
19032d86829bSAnton Altaparmakov * Locking: This function takes the runlist lock of @ni for writing as well as
19042d86829bSAnton Altaparmakov * locking the mft record of the base ntfs inode. These locks are maintained
19052d86829bSAnton Altaparmakov * throughout execution of the function. These locks are required so that the
19062d86829bSAnton Altaparmakov * attribute can be resized safely and so that it can for example be converted
19072d86829bSAnton Altaparmakov * from resident to non-resident safely.
19082d86829bSAnton Altaparmakov *
19092d86829bSAnton Altaparmakov * TODO: At present attribute list attribute handling is not implemented.
19102d86829bSAnton Altaparmakov *
19112d86829bSAnton Altaparmakov * TODO: At present it is not safe to call this function for anything other
19122d86829bSAnton Altaparmakov * than the $DATA attribute(s) of an uncompressed and unencrypted file.
19132d86829bSAnton Altaparmakov */
ntfs_attr_extend_allocation(ntfs_inode * ni,s64 new_alloc_size,const s64 new_data_size,const s64 data_start)19142d86829bSAnton Altaparmakov s64 ntfs_attr_extend_allocation(ntfs_inode *ni, s64 new_alloc_size,
19152d86829bSAnton Altaparmakov const s64 new_data_size, const s64 data_start)
19162d86829bSAnton Altaparmakov {
19172d86829bSAnton Altaparmakov VCN vcn;
19182d86829bSAnton Altaparmakov s64 ll, allocated_size, start = data_start;
19192d86829bSAnton Altaparmakov struct inode *vi = VFS_I(ni);
19202d86829bSAnton Altaparmakov ntfs_volume *vol = ni->vol;
19212d86829bSAnton Altaparmakov ntfs_inode *base_ni;
19222d86829bSAnton Altaparmakov MFT_RECORD *m;
19232d86829bSAnton Altaparmakov ATTR_RECORD *a;
19242d86829bSAnton Altaparmakov ntfs_attr_search_ctx *ctx;
19252d86829bSAnton Altaparmakov runlist_element *rl, *rl2;
19262d86829bSAnton Altaparmakov unsigned long flags;
19272d86829bSAnton Altaparmakov int err, mp_size;
19282d86829bSAnton Altaparmakov u32 attr_len = 0; /* Silence stupid gcc warning. */
1929c49c3111SRichard Knutsson bool mp_rebuilt;
19302d86829bSAnton Altaparmakov
19315c3bd438SRobert P. J. Day #ifdef DEBUG
19322d86829bSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
19332d86829bSAnton Altaparmakov allocated_size = ni->allocated_size;
19342d86829bSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
19352d86829bSAnton Altaparmakov ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
19362d86829bSAnton Altaparmakov "old_allocated_size 0x%llx, "
19372d86829bSAnton Altaparmakov "new_allocated_size 0x%llx, new_data_size 0x%llx, "
19382d86829bSAnton Altaparmakov "data_start 0x%llx.", vi->i_ino,
19392d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type),
19402d86829bSAnton Altaparmakov (unsigned long long)allocated_size,
19412d86829bSAnton Altaparmakov (unsigned long long)new_alloc_size,
19422d86829bSAnton Altaparmakov (unsigned long long)new_data_size,
19432d86829bSAnton Altaparmakov (unsigned long long)start);
19442d86829bSAnton Altaparmakov #endif
19452d86829bSAnton Altaparmakov retry_extend:
19462d86829bSAnton Altaparmakov /*
19472d86829bSAnton Altaparmakov * For non-resident attributes, @start and @new_size need to be aligned
19482d86829bSAnton Altaparmakov * to cluster boundaries for allocation purposes.
19492d86829bSAnton Altaparmakov */
19502d86829bSAnton Altaparmakov if (NInoNonResident(ni)) {
19512d86829bSAnton Altaparmakov if (start > 0)
19522d86829bSAnton Altaparmakov start &= ~(s64)vol->cluster_size_mask;
19532d86829bSAnton Altaparmakov new_alloc_size = (new_alloc_size + vol->cluster_size - 1) &
19542d86829bSAnton Altaparmakov ~(s64)vol->cluster_size_mask;
19552d86829bSAnton Altaparmakov }
19562d86829bSAnton Altaparmakov BUG_ON(new_data_size >= 0 && new_data_size > new_alloc_size);
19572d86829bSAnton Altaparmakov /* Check if new size is allowed in $AttrDef. */
19582d86829bSAnton Altaparmakov err = ntfs_attr_size_bounds_check(vol, ni->type, new_alloc_size);
19592d86829bSAnton Altaparmakov if (unlikely(err)) {
19602d86829bSAnton Altaparmakov /* Only emit errors when the write will fail completely. */
19612d86829bSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
19622d86829bSAnton Altaparmakov allocated_size = ni->allocated_size;
19632d86829bSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
19642d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size) {
19652d86829bSAnton Altaparmakov if (err == -ERANGE) {
19662d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation "
19672d86829bSAnton Altaparmakov "of inode 0x%lx, attribute "
19682d86829bSAnton Altaparmakov "type 0x%x, because the new "
19692d86829bSAnton Altaparmakov "allocation would exceed the "
19702d86829bSAnton Altaparmakov "maximum allowed size for "
19712d86829bSAnton Altaparmakov "this attribute type.",
19722d86829bSAnton Altaparmakov vi->i_ino, (unsigned)
19732d86829bSAnton Altaparmakov le32_to_cpu(ni->type));
19742d86829bSAnton Altaparmakov } else {
19752d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation "
19762d86829bSAnton Altaparmakov "of inode 0x%lx, attribute "
19772d86829bSAnton Altaparmakov "type 0x%x, because this "
19782d86829bSAnton Altaparmakov "attribute type is not "
19792d86829bSAnton Altaparmakov "defined on the NTFS volume. "
19802d86829bSAnton Altaparmakov "Possible corruption! You "
19812d86829bSAnton Altaparmakov "should run chkdsk!",
19822d86829bSAnton Altaparmakov vi->i_ino, (unsigned)
19832d86829bSAnton Altaparmakov le32_to_cpu(ni->type));
19842d86829bSAnton Altaparmakov }
19852d86829bSAnton Altaparmakov }
19862d86829bSAnton Altaparmakov /* Translate error code to be POSIX conformant for write(2). */
19872d86829bSAnton Altaparmakov if (err == -ERANGE)
19882d86829bSAnton Altaparmakov err = -EFBIG;
19892d86829bSAnton Altaparmakov else
19902d86829bSAnton Altaparmakov err = -EIO;
19912d86829bSAnton Altaparmakov return err;
19922d86829bSAnton Altaparmakov }
19932d86829bSAnton Altaparmakov if (!NInoAttr(ni))
19942d86829bSAnton Altaparmakov base_ni = ni;
19952d86829bSAnton Altaparmakov else
19962d86829bSAnton Altaparmakov base_ni = ni->ext.base_ntfs_ino;
19972d86829bSAnton Altaparmakov /*
19982d86829bSAnton Altaparmakov * We will be modifying both the runlist (if non-resident) and the mft
19992d86829bSAnton Altaparmakov * record so lock them both down.
20002d86829bSAnton Altaparmakov */
20012d86829bSAnton Altaparmakov down_write(&ni->runlist.lock);
20022d86829bSAnton Altaparmakov m = map_mft_record(base_ni);
20032d86829bSAnton Altaparmakov if (IS_ERR(m)) {
20042d86829bSAnton Altaparmakov err = PTR_ERR(m);
20052d86829bSAnton Altaparmakov m = NULL;
20062d86829bSAnton Altaparmakov ctx = NULL;
20072d86829bSAnton Altaparmakov goto err_out;
20082d86829bSAnton Altaparmakov }
20092d86829bSAnton Altaparmakov ctx = ntfs_attr_get_search_ctx(base_ni, m);
20102d86829bSAnton Altaparmakov if (unlikely(!ctx)) {
20112d86829bSAnton Altaparmakov err = -ENOMEM;
20122d86829bSAnton Altaparmakov goto err_out;
20132d86829bSAnton Altaparmakov }
20142d86829bSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
20152d86829bSAnton Altaparmakov allocated_size = ni->allocated_size;
20162d86829bSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
20172d86829bSAnton Altaparmakov /*
20182d86829bSAnton Altaparmakov * If non-resident, seek to the last extent. If resident, there is
20192d86829bSAnton Altaparmakov * only one extent, so seek to that.
20202d86829bSAnton Altaparmakov */
20212d86829bSAnton Altaparmakov vcn = NInoNonResident(ni) ? allocated_size >> vol->cluster_size_bits :
20222d86829bSAnton Altaparmakov 0;
20232d86829bSAnton Altaparmakov /*
20242d86829bSAnton Altaparmakov * Abort if someone did the work whilst we waited for the locks. If we
20252d86829bSAnton Altaparmakov * just converted the attribute from resident to non-resident it is
20262d86829bSAnton Altaparmakov * likely that exactly this has happened already. We cannot quite
20272d86829bSAnton Altaparmakov * abort if we need to update the data size.
20282d86829bSAnton Altaparmakov */
20292d86829bSAnton Altaparmakov if (unlikely(new_alloc_size <= allocated_size)) {
20302d86829bSAnton Altaparmakov ntfs_debug("Allocated size already exceeds requested size.");
20312d86829bSAnton Altaparmakov new_alloc_size = allocated_size;
20322d86829bSAnton Altaparmakov if (new_data_size < 0)
20332d86829bSAnton Altaparmakov goto done;
20342d86829bSAnton Altaparmakov /*
20352d86829bSAnton Altaparmakov * We want the first attribute extent so that we can update the
20362d86829bSAnton Altaparmakov * data size.
20372d86829bSAnton Altaparmakov */
20382d86829bSAnton Altaparmakov vcn = 0;
20392d86829bSAnton Altaparmakov }
20402d86829bSAnton Altaparmakov err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
20412d86829bSAnton Altaparmakov CASE_SENSITIVE, vcn, NULL, 0, ctx);
20422d86829bSAnton Altaparmakov if (unlikely(err)) {
20432d86829bSAnton Altaparmakov if (err == -ENOENT)
20442d86829bSAnton Altaparmakov err = -EIO;
20452d86829bSAnton Altaparmakov goto err_out;
20462d86829bSAnton Altaparmakov }
20472d86829bSAnton Altaparmakov m = ctx->mrec;
20482d86829bSAnton Altaparmakov a = ctx->attr;
20492d86829bSAnton Altaparmakov /* Use goto to reduce indentation. */
20502d86829bSAnton Altaparmakov if (a->non_resident)
20512d86829bSAnton Altaparmakov goto do_non_resident_extend;
20522d86829bSAnton Altaparmakov BUG_ON(NInoNonResident(ni));
20532d86829bSAnton Altaparmakov /* The total length of the attribute value. */
20542d86829bSAnton Altaparmakov attr_len = le32_to_cpu(a->data.resident.value_length);
20552d86829bSAnton Altaparmakov /*
20562d86829bSAnton Altaparmakov * Extend the attribute record to be able to store the new attribute
20572d86829bSAnton Altaparmakov * size. ntfs_attr_record_resize() will not do anything if the size is
20582d86829bSAnton Altaparmakov * not changing.
20592d86829bSAnton Altaparmakov */
20602d86829bSAnton Altaparmakov if (new_alloc_size < vol->mft_record_size &&
20612d86829bSAnton Altaparmakov !ntfs_attr_record_resize(m, a,
20622d86829bSAnton Altaparmakov le16_to_cpu(a->data.resident.value_offset) +
20632d86829bSAnton Altaparmakov new_alloc_size)) {
20642d86829bSAnton Altaparmakov /* The resize succeeded! */
20652d86829bSAnton Altaparmakov write_lock_irqsave(&ni->size_lock, flags);
20662d86829bSAnton Altaparmakov ni->allocated_size = le32_to_cpu(a->length) -
20672d86829bSAnton Altaparmakov le16_to_cpu(a->data.resident.value_offset);
20682d86829bSAnton Altaparmakov write_unlock_irqrestore(&ni->size_lock, flags);
20692d86829bSAnton Altaparmakov if (new_data_size >= 0) {
20702d86829bSAnton Altaparmakov BUG_ON(new_data_size < attr_len);
20712d86829bSAnton Altaparmakov a->data.resident.value_length =
20722d86829bSAnton Altaparmakov cpu_to_le32((u32)new_data_size);
20732d86829bSAnton Altaparmakov }
20742d86829bSAnton Altaparmakov goto flush_done;
20752d86829bSAnton Altaparmakov }
20762d86829bSAnton Altaparmakov /*
20772d86829bSAnton Altaparmakov * We have to drop all the locks so we can call
20782d86829bSAnton Altaparmakov * ntfs_attr_make_non_resident(). This could be optimised by try-
20792d86829bSAnton Altaparmakov * locking the first page cache page and only if that fails dropping
20802d86829bSAnton Altaparmakov * the locks, locking the page, and redoing all the locking and
20812d86829bSAnton Altaparmakov * lookups. While this would be a huge optimisation, it is not worth
20822d86829bSAnton Altaparmakov * it as this is definitely a slow code path.
20832d86829bSAnton Altaparmakov */
20842d86829bSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
20852d86829bSAnton Altaparmakov unmap_mft_record(base_ni);
20862d86829bSAnton Altaparmakov up_write(&ni->runlist.lock);
20872d86829bSAnton Altaparmakov /*
20882d86829bSAnton Altaparmakov * Not enough space in the mft record, try to make the attribute
20892d86829bSAnton Altaparmakov * non-resident and if successful restart the extension process.
20902d86829bSAnton Altaparmakov */
20912d86829bSAnton Altaparmakov err = ntfs_attr_make_non_resident(ni, attr_len);
20922d86829bSAnton Altaparmakov if (likely(!err))
20932d86829bSAnton Altaparmakov goto retry_extend;
20942d86829bSAnton Altaparmakov /*
20952d86829bSAnton Altaparmakov * Could not make non-resident. If this is due to this not being
20962d86829bSAnton Altaparmakov * permitted for this attribute type or there not being enough space,
20972d86829bSAnton Altaparmakov * try to make other attributes non-resident. Otherwise fail.
20982d86829bSAnton Altaparmakov */
20992d86829bSAnton Altaparmakov if (unlikely(err != -EPERM && err != -ENOSPC)) {
21002d86829bSAnton Altaparmakov /* Only emit errors when the write will fail completely. */
21012d86829bSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
21022d86829bSAnton Altaparmakov allocated_size = ni->allocated_size;
21032d86829bSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
21042d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
21052d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation of "
21062d86829bSAnton Altaparmakov "inode 0x%lx, attribute type 0x%x, "
21072d86829bSAnton Altaparmakov "because the conversion from resident "
21082d86829bSAnton Altaparmakov "to non-resident attribute failed "
21092d86829bSAnton Altaparmakov "with error code %i.", vi->i_ino,
21102d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type), err);
21112d86829bSAnton Altaparmakov if (err != -ENOMEM)
21122d86829bSAnton Altaparmakov err = -EIO;
21132d86829bSAnton Altaparmakov goto conv_err_out;
21142d86829bSAnton Altaparmakov }
21152d86829bSAnton Altaparmakov /* TODO: Not implemented from here, abort. */
21162d86829bSAnton Altaparmakov read_lock_irqsave(&ni->size_lock, flags);
21172d86829bSAnton Altaparmakov allocated_size = ni->allocated_size;
21182d86829bSAnton Altaparmakov read_unlock_irqrestore(&ni->size_lock, flags);
21192d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size) {
21202d86829bSAnton Altaparmakov if (err == -ENOSPC)
21212d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Not enough space in the mft "
21222d86829bSAnton Altaparmakov "record/on disk for the non-resident "
21232d86829bSAnton Altaparmakov "attribute value. This case is not "
21242d86829bSAnton Altaparmakov "implemented yet.");
21252d86829bSAnton Altaparmakov else /* if (err == -EPERM) */
21262d86829bSAnton Altaparmakov ntfs_error(vol->sb, "This attribute type may not be "
21272d86829bSAnton Altaparmakov "non-resident. This case is not "
21282d86829bSAnton Altaparmakov "implemented yet.");
21292d86829bSAnton Altaparmakov }
21302d86829bSAnton Altaparmakov err = -EOPNOTSUPP;
21312d86829bSAnton Altaparmakov goto conv_err_out;
21322d86829bSAnton Altaparmakov #if 0
21332d86829bSAnton Altaparmakov // TODO: Attempt to make other attributes non-resident.
21342d86829bSAnton Altaparmakov if (!err)
21352d86829bSAnton Altaparmakov goto do_resident_extend;
21362d86829bSAnton Altaparmakov /*
21372d86829bSAnton Altaparmakov * Both the attribute list attribute and the standard information
21382d86829bSAnton Altaparmakov * attribute must remain in the base inode. Thus, if this is one of
21392d86829bSAnton Altaparmakov * these attributes, we have to try to move other attributes out into
21402d86829bSAnton Altaparmakov * extent mft records instead.
21412d86829bSAnton Altaparmakov */
21422d86829bSAnton Altaparmakov if (ni->type == AT_ATTRIBUTE_LIST ||
21432d86829bSAnton Altaparmakov ni->type == AT_STANDARD_INFORMATION) {
21442d86829bSAnton Altaparmakov // TODO: Attempt to move other attributes into extent mft
21452d86829bSAnton Altaparmakov // records.
21462d86829bSAnton Altaparmakov err = -EOPNOTSUPP;
21472d86829bSAnton Altaparmakov if (!err)
21482d86829bSAnton Altaparmakov goto do_resident_extend;
21492d86829bSAnton Altaparmakov goto err_out;
21502d86829bSAnton Altaparmakov }
21512d86829bSAnton Altaparmakov // TODO: Attempt to move this attribute to an extent mft record, but
21522d86829bSAnton Altaparmakov // only if it is not already the only attribute in an mft record in
21532d86829bSAnton Altaparmakov // which case there would be nothing to gain.
21542d86829bSAnton Altaparmakov err = -EOPNOTSUPP;
21552d86829bSAnton Altaparmakov if (!err)
21562d86829bSAnton Altaparmakov goto do_resident_extend;
21572d86829bSAnton Altaparmakov /* There is nothing we can do to make enough space. )-: */
21582d86829bSAnton Altaparmakov goto err_out;
21592d86829bSAnton Altaparmakov #endif
21602d86829bSAnton Altaparmakov do_non_resident_extend:
21612d86829bSAnton Altaparmakov BUG_ON(!NInoNonResident(ni));
21622d86829bSAnton Altaparmakov if (new_alloc_size == allocated_size) {
21632d86829bSAnton Altaparmakov BUG_ON(vcn);
21642d86829bSAnton Altaparmakov goto alloc_done;
21652d86829bSAnton Altaparmakov }
21662d86829bSAnton Altaparmakov /*
21672d86829bSAnton Altaparmakov * If the data starts after the end of the old allocation, this is a
21682d86829bSAnton Altaparmakov * $DATA attribute and sparse attributes are enabled on the volume and
21692d86829bSAnton Altaparmakov * for this inode, then create a sparse region between the old
21702d86829bSAnton Altaparmakov * allocated size and the start of the data. Otherwise simply proceed
21712d86829bSAnton Altaparmakov * with filling the whole space between the old allocated size and the
21722d86829bSAnton Altaparmakov * new allocated size with clusters.
21732d86829bSAnton Altaparmakov */
21742d86829bSAnton Altaparmakov if ((start >= 0 && start <= allocated_size) || ni->type != AT_DATA ||
21752d86829bSAnton Altaparmakov !NVolSparseEnabled(vol) || NInoSparseDisabled(ni))
21762d86829bSAnton Altaparmakov goto skip_sparse;
21772d86829bSAnton Altaparmakov // TODO: This is not implemented yet. We just fill in with real
21782d86829bSAnton Altaparmakov // clusters for now...
21792d86829bSAnton Altaparmakov ntfs_debug("Inserting holes is not-implemented yet. Falling back to "
21802d86829bSAnton Altaparmakov "allocating real clusters instead.");
21812d86829bSAnton Altaparmakov skip_sparse:
21822d86829bSAnton Altaparmakov rl = ni->runlist.rl;
21832d86829bSAnton Altaparmakov if (likely(rl)) {
21842d86829bSAnton Altaparmakov /* Seek to the end of the runlist. */
21852d86829bSAnton Altaparmakov while (rl->length)
21862d86829bSAnton Altaparmakov rl++;
21872d86829bSAnton Altaparmakov }
21882d86829bSAnton Altaparmakov /* If this attribute extent is not mapped, map it now. */
21892d86829bSAnton Altaparmakov if (unlikely(!rl || rl->lcn == LCN_RL_NOT_MAPPED ||
21902d86829bSAnton Altaparmakov (rl->lcn == LCN_ENOENT && rl > ni->runlist.rl &&
21912d86829bSAnton Altaparmakov (rl-1)->lcn == LCN_RL_NOT_MAPPED))) {
21922d86829bSAnton Altaparmakov if (!rl && !allocated_size)
21932d86829bSAnton Altaparmakov goto first_alloc;
21942d86829bSAnton Altaparmakov rl = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl);
21952d86829bSAnton Altaparmakov if (IS_ERR(rl)) {
21962d86829bSAnton Altaparmakov err = PTR_ERR(rl);
21972d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
21982d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation "
21992d86829bSAnton Altaparmakov "of inode 0x%lx, attribute "
22002d86829bSAnton Altaparmakov "type 0x%x, because the "
22012d86829bSAnton Altaparmakov "mapping of a runlist "
22022d86829bSAnton Altaparmakov "fragment failed with error "
22032d86829bSAnton Altaparmakov "code %i.", vi->i_ino,
22042d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type),
22052d86829bSAnton Altaparmakov err);
22062d86829bSAnton Altaparmakov if (err != -ENOMEM)
22072d86829bSAnton Altaparmakov err = -EIO;
22082d86829bSAnton Altaparmakov goto err_out;
22092d86829bSAnton Altaparmakov }
22102d86829bSAnton Altaparmakov ni->runlist.rl = rl;
22112d86829bSAnton Altaparmakov /* Seek to the end of the runlist. */
22122d86829bSAnton Altaparmakov while (rl->length)
22132d86829bSAnton Altaparmakov rl++;
22142d86829bSAnton Altaparmakov }
22152d86829bSAnton Altaparmakov /*
22162d86829bSAnton Altaparmakov * We now know the runlist of the last extent is mapped and @rl is at
22172d86829bSAnton Altaparmakov * the end of the runlist. We want to begin allocating clusters
22182d86829bSAnton Altaparmakov * starting at the last allocated cluster to reduce fragmentation. If
22192d86829bSAnton Altaparmakov * there are no valid LCNs in the attribute we let the cluster
22202d86829bSAnton Altaparmakov * allocator choose the starting cluster.
22212d86829bSAnton Altaparmakov */
22222d86829bSAnton Altaparmakov /* If the last LCN is a hole or simillar seek back to last real LCN. */
22232d86829bSAnton Altaparmakov while (rl->lcn < 0 && rl > ni->runlist.rl)
22242d86829bSAnton Altaparmakov rl--;
22252d86829bSAnton Altaparmakov first_alloc:
22262d86829bSAnton Altaparmakov // FIXME: Need to implement partial allocations so at least part of the
22272d86829bSAnton Altaparmakov // write can be performed when start >= 0. (Needed for POSIX write(2)
22282d86829bSAnton Altaparmakov // conformance.)
22292d86829bSAnton Altaparmakov rl2 = ntfs_cluster_alloc(vol, allocated_size >> vol->cluster_size_bits,
22302d86829bSAnton Altaparmakov (new_alloc_size - allocated_size) >>
22312d86829bSAnton Altaparmakov vol->cluster_size_bits, (rl && (rl->lcn >= 0)) ?
2232c49c3111SRichard Knutsson rl->lcn + rl->length : -1, DATA_ZONE, true);
22332d86829bSAnton Altaparmakov if (IS_ERR(rl2)) {
22342d86829bSAnton Altaparmakov err = PTR_ERR(rl2);
22352d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
22362d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation of "
22372d86829bSAnton Altaparmakov "inode 0x%lx, attribute type 0x%x, "
22382d86829bSAnton Altaparmakov "because the allocation of clusters "
22392d86829bSAnton Altaparmakov "failed with error code %i.", vi->i_ino,
22402d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type), err);
22412d86829bSAnton Altaparmakov if (err != -ENOMEM && err != -ENOSPC)
22422d86829bSAnton Altaparmakov err = -EIO;
22432d86829bSAnton Altaparmakov goto err_out;
22442d86829bSAnton Altaparmakov }
22452d86829bSAnton Altaparmakov rl = ntfs_runlists_merge(ni->runlist.rl, rl2);
22462d86829bSAnton Altaparmakov if (IS_ERR(rl)) {
22472d86829bSAnton Altaparmakov err = PTR_ERR(rl);
22482d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
22492d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation of "
22502d86829bSAnton Altaparmakov "inode 0x%lx, attribute type 0x%x, "
22512d86829bSAnton Altaparmakov "because the runlist merge failed "
22522d86829bSAnton Altaparmakov "with error code %i.", vi->i_ino,
22532d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type), err);
22542d86829bSAnton Altaparmakov if (err != -ENOMEM)
22552d86829bSAnton Altaparmakov err = -EIO;
22562d86829bSAnton Altaparmakov if (ntfs_cluster_free_from_rl(vol, rl2)) {
22572d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Failed to release allocated "
22582d86829bSAnton Altaparmakov "cluster(s) in error code path. Run "
22592d86829bSAnton Altaparmakov "chkdsk to recover the lost "
22602d86829bSAnton Altaparmakov "cluster(s).");
22612d86829bSAnton Altaparmakov NVolSetErrors(vol);
22622d86829bSAnton Altaparmakov }
22632d86829bSAnton Altaparmakov ntfs_free(rl2);
22642d86829bSAnton Altaparmakov goto err_out;
22652d86829bSAnton Altaparmakov }
22662d86829bSAnton Altaparmakov ni->runlist.rl = rl;
22672d86829bSAnton Altaparmakov ntfs_debug("Allocated 0x%llx clusters.", (long long)(new_alloc_size -
22682d86829bSAnton Altaparmakov allocated_size) >> vol->cluster_size_bits);
22692d86829bSAnton Altaparmakov /* Find the runlist element with which the attribute extent starts. */
22702d86829bSAnton Altaparmakov ll = sle64_to_cpu(a->data.non_resident.lowest_vcn);
22712d86829bSAnton Altaparmakov rl2 = ntfs_rl_find_vcn_nolock(rl, ll);
22722d86829bSAnton Altaparmakov BUG_ON(!rl2);
22732d86829bSAnton Altaparmakov BUG_ON(!rl2->length);
22742d86829bSAnton Altaparmakov BUG_ON(rl2->lcn < LCN_HOLE);
2275c49c3111SRichard Knutsson mp_rebuilt = false;
22762d86829bSAnton Altaparmakov /* Get the size for the new mapping pairs array for this extent. */
22772d86829bSAnton Altaparmakov mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
22782d86829bSAnton Altaparmakov if (unlikely(mp_size <= 0)) {
22792d86829bSAnton Altaparmakov err = mp_size;
22802d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
22812d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation of "
22822d86829bSAnton Altaparmakov "inode 0x%lx, attribute type 0x%x, "
22832d86829bSAnton Altaparmakov "because determining the size for the "
22842d86829bSAnton Altaparmakov "mapping pairs failed with error code "
22852d86829bSAnton Altaparmakov "%i.", vi->i_ino,
22862d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type), err);
22872d86829bSAnton Altaparmakov err = -EIO;
22882d86829bSAnton Altaparmakov goto undo_alloc;
22892d86829bSAnton Altaparmakov }
22902d86829bSAnton Altaparmakov /* Extend the attribute record to fit the bigger mapping pairs array. */
22912d86829bSAnton Altaparmakov attr_len = le32_to_cpu(a->length);
22922d86829bSAnton Altaparmakov err = ntfs_attr_record_resize(m, a, mp_size +
22932d86829bSAnton Altaparmakov le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
22942d86829bSAnton Altaparmakov if (unlikely(err)) {
22952d86829bSAnton Altaparmakov BUG_ON(err != -ENOSPC);
22962d86829bSAnton Altaparmakov // TODO: Deal with this by moving this extent to a new mft
22972d86829bSAnton Altaparmakov // record or by starting a new extent in a new mft record,
22982d86829bSAnton Altaparmakov // possibly by extending this extent partially and filling it
22992d86829bSAnton Altaparmakov // and creating a new extent for the remainder, or by making
23002d86829bSAnton Altaparmakov // other attributes non-resident and/or by moving other
23012d86829bSAnton Altaparmakov // attributes out of this mft record.
23022d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
23032d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Not enough space in the mft "
23042d86829bSAnton Altaparmakov "record for the extended attribute "
23052d86829bSAnton Altaparmakov "record. This case is not "
23062d86829bSAnton Altaparmakov "implemented yet.");
23072d86829bSAnton Altaparmakov err = -EOPNOTSUPP;
23082d86829bSAnton Altaparmakov goto undo_alloc;
23092d86829bSAnton Altaparmakov }
2310c49c3111SRichard Knutsson mp_rebuilt = true;
23112d86829bSAnton Altaparmakov /* Generate the mapping pairs array directly into the attr record. */
23122d86829bSAnton Altaparmakov err = ntfs_mapping_pairs_build(vol, (u8*)a +
23132d86829bSAnton Altaparmakov le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
23142d86829bSAnton Altaparmakov mp_size, rl2, ll, -1, NULL);
23152d86829bSAnton Altaparmakov if (unlikely(err)) {
23162d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
23172d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot extend allocation of "
23182d86829bSAnton Altaparmakov "inode 0x%lx, attribute type 0x%x, "
23192d86829bSAnton Altaparmakov "because building the mapping pairs "
23202d86829bSAnton Altaparmakov "failed with error code %i.", vi->i_ino,
23212d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type), err);
23222d86829bSAnton Altaparmakov err = -EIO;
23232d86829bSAnton Altaparmakov goto undo_alloc;
23242d86829bSAnton Altaparmakov }
23252d86829bSAnton Altaparmakov /* Update the highest_vcn. */
23262d86829bSAnton Altaparmakov a->data.non_resident.highest_vcn = cpu_to_sle64((new_alloc_size >>
23272d86829bSAnton Altaparmakov vol->cluster_size_bits) - 1);
23282d86829bSAnton Altaparmakov /*
23292d86829bSAnton Altaparmakov * We now have extended the allocated size of the attribute. Reflect
23302d86829bSAnton Altaparmakov * this in the ntfs_inode structure and the attribute record.
23312d86829bSAnton Altaparmakov */
23322d86829bSAnton Altaparmakov if (a->data.non_resident.lowest_vcn) {
23332d86829bSAnton Altaparmakov /*
23342d86829bSAnton Altaparmakov * We are not in the first attribute extent, switch to it, but
23352d86829bSAnton Altaparmakov * first ensure the changes will make it to disk later.
23362d86829bSAnton Altaparmakov */
23372d86829bSAnton Altaparmakov flush_dcache_mft_record_page(ctx->ntfs_ino);
23382d86829bSAnton Altaparmakov mark_mft_record_dirty(ctx->ntfs_ino);
23392d86829bSAnton Altaparmakov ntfs_attr_reinit_search_ctx(ctx);
23402d86829bSAnton Altaparmakov err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
23412d86829bSAnton Altaparmakov CASE_SENSITIVE, 0, NULL, 0, ctx);
23422d86829bSAnton Altaparmakov if (unlikely(err))
23432d86829bSAnton Altaparmakov goto restore_undo_alloc;
23442d86829bSAnton Altaparmakov /* @m is not used any more so no need to set it. */
23452d86829bSAnton Altaparmakov a = ctx->attr;
23462d86829bSAnton Altaparmakov }
23472d86829bSAnton Altaparmakov write_lock_irqsave(&ni->size_lock, flags);
23482d86829bSAnton Altaparmakov ni->allocated_size = new_alloc_size;
23492d86829bSAnton Altaparmakov a->data.non_resident.allocated_size = cpu_to_sle64(new_alloc_size);
23502d86829bSAnton Altaparmakov /*
23512d86829bSAnton Altaparmakov * FIXME: This would fail if @ni is a directory, $MFT, or an index,
23522d86829bSAnton Altaparmakov * since those can have sparse/compressed set. For example can be
23532d86829bSAnton Altaparmakov * set compressed even though it is not compressed itself and in that
23542d86829bSAnton Altaparmakov * case the bit means that files are to be created compressed in the
23552d86829bSAnton Altaparmakov * directory... At present this is ok as this code is only called for
23562d86829bSAnton Altaparmakov * regular files, and only for their $DATA attribute(s).
23572d86829bSAnton Altaparmakov * FIXME: The calculation is wrong if we created a hole above. For now
23582d86829bSAnton Altaparmakov * it does not matter as we never create holes.
23592d86829bSAnton Altaparmakov */
23602d86829bSAnton Altaparmakov if (NInoSparse(ni) || NInoCompressed(ni)) {
23612d86829bSAnton Altaparmakov ni->itype.compressed.size += new_alloc_size - allocated_size;
23622d86829bSAnton Altaparmakov a->data.non_resident.compressed_size =
23632d86829bSAnton Altaparmakov cpu_to_sle64(ni->itype.compressed.size);
23642d86829bSAnton Altaparmakov vi->i_blocks = ni->itype.compressed.size >> 9;
23652d86829bSAnton Altaparmakov } else
23662d86829bSAnton Altaparmakov vi->i_blocks = new_alloc_size >> 9;
23672d86829bSAnton Altaparmakov write_unlock_irqrestore(&ni->size_lock, flags);
23682d86829bSAnton Altaparmakov alloc_done:
23692d86829bSAnton Altaparmakov if (new_data_size >= 0) {
23702d86829bSAnton Altaparmakov BUG_ON(new_data_size <
23712d86829bSAnton Altaparmakov sle64_to_cpu(a->data.non_resident.data_size));
23722d86829bSAnton Altaparmakov a->data.non_resident.data_size = cpu_to_sle64(new_data_size);
23732d86829bSAnton Altaparmakov }
23742d86829bSAnton Altaparmakov flush_done:
23752d86829bSAnton Altaparmakov /* Ensure the changes make it to disk. */
23762d86829bSAnton Altaparmakov flush_dcache_mft_record_page(ctx->ntfs_ino);
23772d86829bSAnton Altaparmakov mark_mft_record_dirty(ctx->ntfs_ino);
23782d86829bSAnton Altaparmakov done:
23792d86829bSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
23802d86829bSAnton Altaparmakov unmap_mft_record(base_ni);
23812d86829bSAnton Altaparmakov up_write(&ni->runlist.lock);
23822d86829bSAnton Altaparmakov ntfs_debug("Done, new_allocated_size 0x%llx.",
23832d86829bSAnton Altaparmakov (unsigned long long)new_alloc_size);
23842d86829bSAnton Altaparmakov return new_alloc_size;
23852d86829bSAnton Altaparmakov restore_undo_alloc:
23862d86829bSAnton Altaparmakov if (start < 0 || start >= allocated_size)
23872d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Cannot complete extension of allocation "
23882d86829bSAnton Altaparmakov "of inode 0x%lx, attribute type 0x%x, because "
23892d86829bSAnton Altaparmakov "lookup of first attribute extent failed with "
23902d86829bSAnton Altaparmakov "error code %i.", vi->i_ino,
23912d86829bSAnton Altaparmakov (unsigned)le32_to_cpu(ni->type), err);
23922d86829bSAnton Altaparmakov if (err == -ENOENT)
23932d86829bSAnton Altaparmakov err = -EIO;
23942d86829bSAnton Altaparmakov ntfs_attr_reinit_search_ctx(ctx);
23952d86829bSAnton Altaparmakov if (ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE,
23962d86829bSAnton Altaparmakov allocated_size >> vol->cluster_size_bits, NULL, 0,
23972d86829bSAnton Altaparmakov ctx)) {
23982d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Failed to find last attribute extent of "
23992d86829bSAnton Altaparmakov "attribute in error code path. Run chkdsk to "
24002d86829bSAnton Altaparmakov "recover.");
24012d86829bSAnton Altaparmakov write_lock_irqsave(&ni->size_lock, flags);
24022d86829bSAnton Altaparmakov ni->allocated_size = new_alloc_size;
24032d86829bSAnton Altaparmakov /*
24042d86829bSAnton Altaparmakov * FIXME: This would fail if @ni is a directory... See above.
24052d86829bSAnton Altaparmakov * FIXME: The calculation is wrong if we created a hole above.
24062d86829bSAnton Altaparmakov * For now it does not matter as we never create holes.
24072d86829bSAnton Altaparmakov */
24082d86829bSAnton Altaparmakov if (NInoSparse(ni) || NInoCompressed(ni)) {
24092d86829bSAnton Altaparmakov ni->itype.compressed.size += new_alloc_size -
24102d86829bSAnton Altaparmakov allocated_size;
24112d86829bSAnton Altaparmakov vi->i_blocks = ni->itype.compressed.size >> 9;
24122d86829bSAnton Altaparmakov } else
24132d86829bSAnton Altaparmakov vi->i_blocks = new_alloc_size >> 9;
24142d86829bSAnton Altaparmakov write_unlock_irqrestore(&ni->size_lock, flags);
24152d86829bSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
24162d86829bSAnton Altaparmakov unmap_mft_record(base_ni);
24172d86829bSAnton Altaparmakov up_write(&ni->runlist.lock);
24182d86829bSAnton Altaparmakov /*
24192d86829bSAnton Altaparmakov * The only thing that is now wrong is the allocated size of the
24202d86829bSAnton Altaparmakov * base attribute extent which chkdsk should be able to fix.
24212d86829bSAnton Altaparmakov */
24222d86829bSAnton Altaparmakov NVolSetErrors(vol);
24232d86829bSAnton Altaparmakov return err;
24242d86829bSAnton Altaparmakov }
24252d86829bSAnton Altaparmakov ctx->attr->data.non_resident.highest_vcn = cpu_to_sle64(
24262d86829bSAnton Altaparmakov (allocated_size >> vol->cluster_size_bits) - 1);
24272d86829bSAnton Altaparmakov undo_alloc:
24282d86829bSAnton Altaparmakov ll = allocated_size >> vol->cluster_size_bits;
24292d86829bSAnton Altaparmakov if (ntfs_cluster_free(ni, ll, -1, ctx) < 0) {
24302d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Failed to release allocated cluster(s) "
24312d86829bSAnton Altaparmakov "in error code path. Run chkdsk to recover "
24322d86829bSAnton Altaparmakov "the lost cluster(s).");
24332d86829bSAnton Altaparmakov NVolSetErrors(vol);
24342d86829bSAnton Altaparmakov }
24352d86829bSAnton Altaparmakov m = ctx->mrec;
24362d86829bSAnton Altaparmakov a = ctx->attr;
24372d86829bSAnton Altaparmakov /*
24382d86829bSAnton Altaparmakov * If the runlist truncation fails and/or the search context is no
24392d86829bSAnton Altaparmakov * longer valid, we cannot resize the attribute record or build the
24402d86829bSAnton Altaparmakov * mapping pairs array thus we mark the inode bad so that no access to
24412d86829bSAnton Altaparmakov * the freed clusters can happen.
24422d86829bSAnton Altaparmakov */
24432d86829bSAnton Altaparmakov if (ntfs_rl_truncate_nolock(vol, &ni->runlist, ll) || IS_ERR(m)) {
24442d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Failed to %s in error code path. Run "
24452d86829bSAnton Altaparmakov "chkdsk to recover.", IS_ERR(m) ?
24462d86829bSAnton Altaparmakov "restore attribute search context" :
24472d86829bSAnton Altaparmakov "truncate attribute runlist");
24482d86829bSAnton Altaparmakov NVolSetErrors(vol);
24492d86829bSAnton Altaparmakov } else if (mp_rebuilt) {
24502d86829bSAnton Altaparmakov if (ntfs_attr_record_resize(m, a, attr_len)) {
24512d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Failed to restore attribute "
24522d86829bSAnton Altaparmakov "record in error code path. Run "
24532d86829bSAnton Altaparmakov "chkdsk to recover.");
24542d86829bSAnton Altaparmakov NVolSetErrors(vol);
24552d86829bSAnton Altaparmakov } else /* if (success) */ {
24562d86829bSAnton Altaparmakov if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
24572d86829bSAnton Altaparmakov a->data.non_resident.
24582d86829bSAnton Altaparmakov mapping_pairs_offset), attr_len -
24592d86829bSAnton Altaparmakov le16_to_cpu(a->data.non_resident.
24602d86829bSAnton Altaparmakov mapping_pairs_offset), rl2, ll, -1,
24612d86829bSAnton Altaparmakov NULL)) {
24622d86829bSAnton Altaparmakov ntfs_error(vol->sb, "Failed to restore "
24632d86829bSAnton Altaparmakov "mapping pairs array in error "
24642d86829bSAnton Altaparmakov "code path. Run chkdsk to "
24652d86829bSAnton Altaparmakov "recover.");
24662d86829bSAnton Altaparmakov NVolSetErrors(vol);
24672d86829bSAnton Altaparmakov }
24682d86829bSAnton Altaparmakov flush_dcache_mft_record_page(ctx->ntfs_ino);
24692d86829bSAnton Altaparmakov mark_mft_record_dirty(ctx->ntfs_ino);
24702d86829bSAnton Altaparmakov }
24712d86829bSAnton Altaparmakov }
24722d86829bSAnton Altaparmakov err_out:
24732d86829bSAnton Altaparmakov if (ctx)
24742d86829bSAnton Altaparmakov ntfs_attr_put_search_ctx(ctx);
24752d86829bSAnton Altaparmakov if (m)
24762d86829bSAnton Altaparmakov unmap_mft_record(base_ni);
24772d86829bSAnton Altaparmakov up_write(&ni->runlist.lock);
24782d86829bSAnton Altaparmakov conv_err_out:
24792d86829bSAnton Altaparmakov ntfs_debug("Failed. Returning error code %i.", err);
24802d86829bSAnton Altaparmakov return err;
24812d86829bSAnton Altaparmakov }
24822d86829bSAnton Altaparmakov
24832d86829bSAnton Altaparmakov /**
24841da177e4SLinus Torvalds * ntfs_attr_set - fill (a part of) an attribute with a byte
24851da177e4SLinus Torvalds * @ni: ntfs inode describing the attribute to fill
24861da177e4SLinus Torvalds * @ofs: offset inside the attribute at which to start to fill
24871da177e4SLinus Torvalds * @cnt: number of bytes to fill
24881da177e4SLinus Torvalds * @val: the unsigned 8-bit value with which to fill the attribute
24891da177e4SLinus Torvalds *
24901da177e4SLinus Torvalds * Fill @cnt bytes of the attribute described by the ntfs inode @ni starting at
24911da177e4SLinus Torvalds * byte offset @ofs inside the attribute with the constant byte @val.
24921da177e4SLinus Torvalds *
24931da177e4SLinus Torvalds * This function is effectively like memset() applied to an ntfs attribute.
2494*253f3137SDeming Wang * Note this function actually only operates on the page cache pages belonging
2495da28438cSAnton Altaparmakov * to the ntfs attribute and it marks them dirty after doing the memset().
2496da28438cSAnton Altaparmakov * Thus it relies on the vm dirty page write code paths to cause the modified
2497da28438cSAnton Altaparmakov * pages to be written to the mft record/disk.
24981da177e4SLinus Torvalds *
24991da177e4SLinus Torvalds * Return 0 on success and -errno on error. An error code of -ESPIPE means
25001da177e4SLinus Torvalds * that @ofs + @cnt were outside the end of the attribute and no write was
25011da177e4SLinus Torvalds * performed.
25021da177e4SLinus Torvalds */
ntfs_attr_set(ntfs_inode * ni,const s64 ofs,const s64 cnt,const u8 val)25031da177e4SLinus Torvalds int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
25041da177e4SLinus Torvalds {
25051da177e4SLinus Torvalds ntfs_volume *vol = ni->vol;
25061da177e4SLinus Torvalds struct address_space *mapping;
25071da177e4SLinus Torvalds struct page *page;
25081da177e4SLinus Torvalds u8 *kaddr;
25091da177e4SLinus Torvalds pgoff_t idx, end;
2510bfab36e8SAnton Altaparmakov unsigned start_ofs, end_ofs, size;
25111da177e4SLinus Torvalds
25121da177e4SLinus Torvalds ntfs_debug("Entering for ofs 0x%llx, cnt 0x%llx, val 0x%hx.",
25131da177e4SLinus Torvalds (long long)ofs, (long long)cnt, val);
25141da177e4SLinus Torvalds BUG_ON(ofs < 0);
25151da177e4SLinus Torvalds BUG_ON(cnt < 0);
25161da177e4SLinus Torvalds if (!cnt)
25171da177e4SLinus Torvalds goto done;
2518807c453dSAnton Altaparmakov /*
2519807c453dSAnton Altaparmakov * FIXME: Compressed and encrypted attributes are not supported when
2520807c453dSAnton Altaparmakov * writing and we should never have gotten here for them.
2521807c453dSAnton Altaparmakov */
2522807c453dSAnton Altaparmakov BUG_ON(NInoCompressed(ni));
2523807c453dSAnton Altaparmakov BUG_ON(NInoEncrypted(ni));
25241da177e4SLinus Torvalds mapping = VFS_I(ni)->i_mapping;
25251da177e4SLinus Torvalds /* Work out the starting index and page offset. */
252609cbfeafSKirill A. Shutemov idx = ofs >> PAGE_SHIFT;
252709cbfeafSKirill A. Shutemov start_ofs = ofs & ~PAGE_MASK;
25281da177e4SLinus Torvalds /* Work out the ending index and page offset. */
25291da177e4SLinus Torvalds end = ofs + cnt;
253009cbfeafSKirill A. Shutemov end_ofs = end & ~PAGE_MASK;
25311da177e4SLinus Torvalds /* If the end is outside the inode size return -ESPIPE. */
2532da28438cSAnton Altaparmakov if (unlikely(end > i_size_read(VFS_I(ni)))) {
25331da177e4SLinus Torvalds ntfs_error(vol->sb, "Request exceeds end of attribute.");
25341da177e4SLinus Torvalds return -ESPIPE;
25351da177e4SLinus Torvalds }
253609cbfeafSKirill A. Shutemov end >>= PAGE_SHIFT;
25371da177e4SLinus Torvalds /* If there is a first partial page, need to do it the slow way. */
25381da177e4SLinus Torvalds if (start_ofs) {
2539090d2b18SPekka Enberg page = read_mapping_page(mapping, idx, NULL);
25401da177e4SLinus Torvalds if (IS_ERR(page)) {
25411da177e4SLinus Torvalds ntfs_error(vol->sb, "Failed to read first partial "
25426fe6900eSNick Piggin "page (error, index 0x%lx).", idx);
25431da177e4SLinus Torvalds return PTR_ERR(page);
25441da177e4SLinus Torvalds }
25451da177e4SLinus Torvalds /*
25461da177e4SLinus Torvalds * If the last page is the same as the first page, need to
25471da177e4SLinus Torvalds * limit the write to the end offset.
25481da177e4SLinus Torvalds */
254909cbfeafSKirill A. Shutemov size = PAGE_SIZE;
25501da177e4SLinus Torvalds if (idx == end)
25511da177e4SLinus Torvalds size = end_ofs;
2552a3ac1414SCong Wang kaddr = kmap_atomic(page);
25531da177e4SLinus Torvalds memset(kaddr + start_ofs, val, size - start_ofs);
25541da177e4SLinus Torvalds flush_dcache_page(page);
2555a3ac1414SCong Wang kunmap_atomic(kaddr);
25561da177e4SLinus Torvalds set_page_dirty(page);
255709cbfeafSKirill A. Shutemov put_page(page);
2558bfab36e8SAnton Altaparmakov balance_dirty_pages_ratelimited(mapping);
2559bfab36e8SAnton Altaparmakov cond_resched();
25601da177e4SLinus Torvalds if (idx == end)
25611da177e4SLinus Torvalds goto done;
25621da177e4SLinus Torvalds idx++;
25631da177e4SLinus Torvalds }
25641da177e4SLinus Torvalds /* Do the whole pages the fast way. */
25651da177e4SLinus Torvalds for (; idx < end; idx++) {
25661da177e4SLinus Torvalds /* Find or create the current page. (The page is locked.) */
25671da177e4SLinus Torvalds page = grab_cache_page(mapping, idx);
25681da177e4SLinus Torvalds if (unlikely(!page)) {
25691da177e4SLinus Torvalds ntfs_error(vol->sb, "Insufficient memory to grab "
25701da177e4SLinus Torvalds "page (index 0x%lx).", idx);
25711da177e4SLinus Torvalds return -ENOMEM;
25721da177e4SLinus Torvalds }
2573a3ac1414SCong Wang kaddr = kmap_atomic(page);
257409cbfeafSKirill A. Shutemov memset(kaddr, val, PAGE_SIZE);
25751da177e4SLinus Torvalds flush_dcache_page(page);
2576a3ac1414SCong Wang kunmap_atomic(kaddr);
25771da177e4SLinus Torvalds /*
25781da177e4SLinus Torvalds * If the page has buffers, mark them uptodate since buffer
25791da177e4SLinus Torvalds * state and not page state is definitive in 2.6 kernels.
25801da177e4SLinus Torvalds */
25811da177e4SLinus Torvalds if (page_has_buffers(page)) {
25821da177e4SLinus Torvalds struct buffer_head *bh, *head;
25831da177e4SLinus Torvalds
25841da177e4SLinus Torvalds bh = head = page_buffers(page);
25851da177e4SLinus Torvalds do {
25861da177e4SLinus Torvalds set_buffer_uptodate(bh);
25871da177e4SLinus Torvalds } while ((bh = bh->b_this_page) != head);
25881da177e4SLinus Torvalds }
25891da177e4SLinus Torvalds /* Now that buffers are uptodate, set the page uptodate, too. */
25901da177e4SLinus Torvalds SetPageUptodate(page);
25911da177e4SLinus Torvalds /*
25921da177e4SLinus Torvalds * Set the page and all its buffers dirty and mark the inode
25931da177e4SLinus Torvalds * dirty, too. The VM will write the page later on.
25941da177e4SLinus Torvalds */
25951da177e4SLinus Torvalds set_page_dirty(page);
25961da177e4SLinus Torvalds /* Finally unlock and release the page. */
25971da177e4SLinus Torvalds unlock_page(page);
259809cbfeafSKirill A. Shutemov put_page(page);
259929b89905SAnton Altaparmakov balance_dirty_pages_ratelimited(mapping);
260029b89905SAnton Altaparmakov cond_resched();
26011da177e4SLinus Torvalds }
26021da177e4SLinus Torvalds /* If there is a last partial page, need to do it the slow way. */
26031da177e4SLinus Torvalds if (end_ofs) {
2604090d2b18SPekka Enberg page = read_mapping_page(mapping, idx, NULL);
26051da177e4SLinus Torvalds if (IS_ERR(page)) {
26061da177e4SLinus Torvalds ntfs_error(vol->sb, "Failed to read last partial page "
26076fe6900eSNick Piggin "(error, index 0x%lx).", idx);
26081da177e4SLinus Torvalds return PTR_ERR(page);
26091da177e4SLinus Torvalds }
2610a3ac1414SCong Wang kaddr = kmap_atomic(page);
26111da177e4SLinus Torvalds memset(kaddr, val, end_ofs);
26121da177e4SLinus Torvalds flush_dcache_page(page);
2613a3ac1414SCong Wang kunmap_atomic(kaddr);
26141da177e4SLinus Torvalds set_page_dirty(page);
261509cbfeafSKirill A. Shutemov put_page(page);
2616bfab36e8SAnton Altaparmakov balance_dirty_pages_ratelimited(mapping);
2617bfab36e8SAnton Altaparmakov cond_resched();
26181da177e4SLinus Torvalds }
26191da177e4SLinus Torvalds done:
26201da177e4SLinus Torvalds ntfs_debug("Done.");
26211da177e4SLinus Torvalds return 0;
26221da177e4SLinus Torvalds }
262353d59aadSAnton Altaparmakov
262453d59aadSAnton Altaparmakov #endif /* NTFS_RW */
2625