1a1d312deSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2*aa0b42b7SRandy Dunlap /*
31da177e4SLinus Torvalds * runlist.c - NTFS runlist handling code. Part of the Linux-NTFS project.
41da177e4SLinus Torvalds *
5bfab36e8SAnton Altaparmakov * Copyright (c) 2001-2007 Anton Altaparmakov
65c9f6de3SAnton Altaparmakov * Copyright (c) 2002-2005 Richard Russon
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds
91da177e4SLinus Torvalds #include "debug.h"
101da177e4SLinus Torvalds #include "dir.h"
111da177e4SLinus Torvalds #include "endian.h"
121da177e4SLinus Torvalds #include "malloc.h"
131da177e4SLinus Torvalds #include "ntfs.h"
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds /**
161da177e4SLinus Torvalds * ntfs_rl_mm - runlist memmove
171da177e4SLinus Torvalds *
181da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlist @base.
191da177e4SLinus Torvalds */
ntfs_rl_mm(runlist_element * base,int dst,int src,int size)201da177e4SLinus Torvalds static inline void ntfs_rl_mm(runlist_element *base, int dst, int src,
211da177e4SLinus Torvalds int size)
221da177e4SLinus Torvalds {
231da177e4SLinus Torvalds if (likely((dst != src) && (size > 0)))
241da177e4SLinus Torvalds memmove(base + dst, base + src, size * sizeof(*base));
251da177e4SLinus Torvalds }
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds /**
281da177e4SLinus Torvalds * ntfs_rl_mc - runlist memory copy
291da177e4SLinus Torvalds *
301da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dstbase and
311da177e4SLinus Torvalds * @srcbase.
321da177e4SLinus Torvalds */
ntfs_rl_mc(runlist_element * dstbase,int dst,runlist_element * srcbase,int src,int size)331da177e4SLinus Torvalds static inline void ntfs_rl_mc(runlist_element *dstbase, int dst,
341da177e4SLinus Torvalds runlist_element *srcbase, int src, int size)
351da177e4SLinus Torvalds {
361da177e4SLinus Torvalds if (likely(size > 0))
371da177e4SLinus Torvalds memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase));
381da177e4SLinus Torvalds }
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds /**
411da177e4SLinus Torvalds * ntfs_rl_realloc - Reallocate memory for runlists
421da177e4SLinus Torvalds * @rl: original runlist
431da177e4SLinus Torvalds * @old_size: number of runlist elements in the original runlist @rl
441da177e4SLinus Torvalds * @new_size: number of runlist elements we need space for
451da177e4SLinus Torvalds *
461da177e4SLinus Torvalds * As the runlists grow, more memory will be required. To prevent the
471da177e4SLinus Torvalds * kernel having to allocate and reallocate large numbers of small bits of
481a0df15aSAnton Altaparmakov * memory, this function returns an entire page of memory.
491da177e4SLinus Torvalds *
501da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlist @rl.
511da177e4SLinus Torvalds *
521da177e4SLinus Torvalds * N.B. If the new allocation doesn't require a different number of pages in
531da177e4SLinus Torvalds * memory, the function will return the original pointer.
541da177e4SLinus Torvalds *
551da177e4SLinus Torvalds * On success, return a pointer to the newly allocated, or recycled, memory.
561da177e4SLinus Torvalds * On error, return -errno. The following error codes are defined:
571da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
581da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
591da177e4SLinus Torvalds */
ntfs_rl_realloc(runlist_element * rl,int old_size,int new_size)601da177e4SLinus Torvalds static inline runlist_element *ntfs_rl_realloc(runlist_element *rl,
611da177e4SLinus Torvalds int old_size, int new_size)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds runlist_element *new_rl;
641da177e4SLinus Torvalds
651da177e4SLinus Torvalds old_size = PAGE_ALIGN(old_size * sizeof(*rl));
661da177e4SLinus Torvalds new_size = PAGE_ALIGN(new_size * sizeof(*rl));
671da177e4SLinus Torvalds if (old_size == new_size)
681da177e4SLinus Torvalds return rl;
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds new_rl = ntfs_malloc_nofs(new_size);
711da177e4SLinus Torvalds if (unlikely(!new_rl))
721da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
731da177e4SLinus Torvalds
741da177e4SLinus Torvalds if (likely(rl != NULL)) {
751da177e4SLinus Torvalds if (unlikely(old_size > new_size))
761da177e4SLinus Torvalds old_size = new_size;
771da177e4SLinus Torvalds memcpy(new_rl, rl, old_size);
781da177e4SLinus Torvalds ntfs_free(rl);
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds return new_rl;
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds /**
849529d461SAnton Altaparmakov * ntfs_rl_realloc_nofail - Reallocate memory for runlists
859529d461SAnton Altaparmakov * @rl: original runlist
869529d461SAnton Altaparmakov * @old_size: number of runlist elements in the original runlist @rl
879529d461SAnton Altaparmakov * @new_size: number of runlist elements we need space for
889529d461SAnton Altaparmakov *
899529d461SAnton Altaparmakov * As the runlists grow, more memory will be required. To prevent the
909529d461SAnton Altaparmakov * kernel having to allocate and reallocate large numbers of small bits of
919529d461SAnton Altaparmakov * memory, this function returns an entire page of memory.
929529d461SAnton Altaparmakov *
939529d461SAnton Altaparmakov * This function guarantees that the allocation will succeed. It will sleep
949529d461SAnton Altaparmakov * for as long as it takes to complete the allocation.
959529d461SAnton Altaparmakov *
969529d461SAnton Altaparmakov * It is up to the caller to serialize access to the runlist @rl.
979529d461SAnton Altaparmakov *
989529d461SAnton Altaparmakov * N.B. If the new allocation doesn't require a different number of pages in
999529d461SAnton Altaparmakov * memory, the function will return the original pointer.
1009529d461SAnton Altaparmakov *
1019529d461SAnton Altaparmakov * On success, return a pointer to the newly allocated, or recycled, memory.
1029529d461SAnton Altaparmakov * On error, return -errno. The following error codes are defined:
1039529d461SAnton Altaparmakov * -ENOMEM - Not enough memory to allocate runlist array.
1049529d461SAnton Altaparmakov * -EINVAL - Invalid parameters were passed in.
1059529d461SAnton Altaparmakov */
ntfs_rl_realloc_nofail(runlist_element * rl,int old_size,int new_size)1069529d461SAnton Altaparmakov static inline runlist_element *ntfs_rl_realloc_nofail(runlist_element *rl,
1079529d461SAnton Altaparmakov int old_size, int new_size)
1089529d461SAnton Altaparmakov {
1099529d461SAnton Altaparmakov runlist_element *new_rl;
1109529d461SAnton Altaparmakov
1119529d461SAnton Altaparmakov old_size = PAGE_ALIGN(old_size * sizeof(*rl));
1129529d461SAnton Altaparmakov new_size = PAGE_ALIGN(new_size * sizeof(*rl));
1139529d461SAnton Altaparmakov if (old_size == new_size)
1149529d461SAnton Altaparmakov return rl;
1159529d461SAnton Altaparmakov
1169529d461SAnton Altaparmakov new_rl = ntfs_malloc_nofs_nofail(new_size);
1179529d461SAnton Altaparmakov BUG_ON(!new_rl);
1189529d461SAnton Altaparmakov
1199529d461SAnton Altaparmakov if (likely(rl != NULL)) {
1209529d461SAnton Altaparmakov if (unlikely(old_size > new_size))
1219529d461SAnton Altaparmakov old_size = new_size;
1229529d461SAnton Altaparmakov memcpy(new_rl, rl, old_size);
1239529d461SAnton Altaparmakov ntfs_free(rl);
1249529d461SAnton Altaparmakov }
1259529d461SAnton Altaparmakov return new_rl;
1269529d461SAnton Altaparmakov }
1279529d461SAnton Altaparmakov
1289529d461SAnton Altaparmakov /**
1291da177e4SLinus Torvalds * ntfs_are_rl_mergeable - test if two runlists can be joined together
1301da177e4SLinus Torvalds * @dst: original runlist
1311da177e4SLinus Torvalds * @src: new runlist to test for mergeability with @dst
1321da177e4SLinus Torvalds *
1331da177e4SLinus Torvalds * Test if two runlists can be joined together. For this, their VCNs and LCNs
1341da177e4SLinus Torvalds * must be adjacent.
1351da177e4SLinus Torvalds *
1361da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dst and @src.
1371da177e4SLinus Torvalds *
138c49c3111SRichard Knutsson * Return: true Success, the runlists can be merged.
139c49c3111SRichard Knutsson * false Failure, the runlists cannot be merged.
1401da177e4SLinus Torvalds */
ntfs_are_rl_mergeable(runlist_element * dst,runlist_element * src)141c49c3111SRichard Knutsson static inline bool ntfs_are_rl_mergeable(runlist_element *dst,
1421da177e4SLinus Torvalds runlist_element *src)
1431da177e4SLinus Torvalds {
1441da177e4SLinus Torvalds BUG_ON(!dst);
1451da177e4SLinus Torvalds BUG_ON(!src);
1461da177e4SLinus Torvalds
147eed8b2deSAnton Altaparmakov /* We can merge unmapped regions even if they are misaligned. */
148eed8b2deSAnton Altaparmakov if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED))
149c49c3111SRichard Knutsson return true;
150eed8b2deSAnton Altaparmakov /* If the runs are misaligned, we cannot merge them. */
151eed8b2deSAnton Altaparmakov if ((dst->vcn + dst->length) != src->vcn)
152c49c3111SRichard Knutsson return false;
153eed8b2deSAnton Altaparmakov /* If both runs are non-sparse and contiguous, we can merge them. */
154eed8b2deSAnton Altaparmakov if ((dst->lcn >= 0) && (src->lcn >= 0) &&
155eed8b2deSAnton Altaparmakov ((dst->lcn + dst->length) == src->lcn))
156c49c3111SRichard Knutsson return true;
157eed8b2deSAnton Altaparmakov /* If we are merging two holes, we can merge them. */
158eed8b2deSAnton Altaparmakov if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE))
159c49c3111SRichard Knutsson return true;
160eed8b2deSAnton Altaparmakov /* Cannot merge. */
161c49c3111SRichard Knutsson return false;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds /**
1651da177e4SLinus Torvalds * __ntfs_rl_merge - merge two runlists without testing if they can be merged
1661da177e4SLinus Torvalds * @dst: original, destination runlist
1671da177e4SLinus Torvalds * @src: new runlist to merge with @dst
1681da177e4SLinus Torvalds *
1691da177e4SLinus Torvalds * Merge the two runlists, writing into the destination runlist @dst. The
1701da177e4SLinus Torvalds * caller must make sure the runlists can be merged or this will corrupt the
1711da177e4SLinus Torvalds * destination runlist.
1721da177e4SLinus Torvalds *
1731da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dst and @src.
1741da177e4SLinus Torvalds */
__ntfs_rl_merge(runlist_element * dst,runlist_element * src)1751da177e4SLinus Torvalds static inline void __ntfs_rl_merge(runlist_element *dst, runlist_element *src)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds dst->length += src->length;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds /**
1811da177e4SLinus Torvalds * ntfs_rl_append - append a runlist after a given element
1821da177e4SLinus Torvalds * @dst: original runlist to be worked on
1831da177e4SLinus Torvalds * @dsize: number of elements in @dst (including end marker)
1841da177e4SLinus Torvalds * @src: runlist to be inserted into @dst
1851da177e4SLinus Torvalds * @ssize: number of elements in @src (excluding end marker)
1861da177e4SLinus Torvalds * @loc: append the new runlist @src after this element in @dst
1871da177e4SLinus Torvalds *
1881da177e4SLinus Torvalds * Append the runlist @src after element @loc in @dst. Merge the right end of
1891da177e4SLinus Torvalds * the new runlist, if necessary. Adjust the size of the hole before the
1901da177e4SLinus Torvalds * appended runlist.
1911da177e4SLinus Torvalds *
1921da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dst and @src.
1931da177e4SLinus Torvalds *
1941da177e4SLinus Torvalds * On success, return a pointer to the new, combined, runlist. Note, both
1951da177e4SLinus Torvalds * runlists @dst and @src are deallocated before returning so you cannot use
1961da177e4SLinus Torvalds * the pointers for anything any more. (Strictly speaking the returned runlist
1971da177e4SLinus Torvalds * may be the same as @dst but this is irrelevant.)
1981da177e4SLinus Torvalds *
1991da177e4SLinus Torvalds * On error, return -errno. Both runlists are left unmodified. The following
2001da177e4SLinus Torvalds * error codes are defined:
2011da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
2021da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
2031da177e4SLinus Torvalds */
ntfs_rl_append(runlist_element * dst,int dsize,runlist_element * src,int ssize,int loc)2041da177e4SLinus Torvalds static inline runlist_element *ntfs_rl_append(runlist_element *dst,
2051da177e4SLinus Torvalds int dsize, runlist_element *src, int ssize, int loc)
2061da177e4SLinus Torvalds {
207c49c3111SRichard Knutsson bool right = false; /* Right end of @src needs merging. */
2085c9f6de3SAnton Altaparmakov int marker; /* End of the inserted runs. */
2091da177e4SLinus Torvalds
2101da177e4SLinus Torvalds BUG_ON(!dst);
2111da177e4SLinus Torvalds BUG_ON(!src);
2121da177e4SLinus Torvalds
2131da177e4SLinus Torvalds /* First, check if the right hand end needs merging. */
214eed8b2deSAnton Altaparmakov if ((loc + 1) < dsize)
2151da177e4SLinus Torvalds right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1);
2161da177e4SLinus Torvalds
2171da177e4SLinus Torvalds /* Space required: @dst size + @src size, less one if we merged. */
2181da177e4SLinus Torvalds dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right);
2191da177e4SLinus Torvalds if (IS_ERR(dst))
2201da177e4SLinus Torvalds return dst;
2211da177e4SLinus Torvalds /*
2221da177e4SLinus Torvalds * We are guaranteed to succeed from here so can start modifying the
2231da177e4SLinus Torvalds * original runlists.
2241da177e4SLinus Torvalds */
2251da177e4SLinus Torvalds
2261da177e4SLinus Torvalds /* First, merge the right hand end, if necessary. */
2271da177e4SLinus Torvalds if (right)
2281da177e4SLinus Torvalds __ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
2291da177e4SLinus Torvalds
2305c9f6de3SAnton Altaparmakov /* First run after the @src runs that have been inserted. */
2315c9f6de3SAnton Altaparmakov marker = loc + ssize + 1;
2321da177e4SLinus Torvalds
2331da177e4SLinus Torvalds /* Move the tail of @dst out of the way, then copy in @src. */
2345c9f6de3SAnton Altaparmakov ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - (loc + 1 + right));
2351da177e4SLinus Torvalds ntfs_rl_mc(dst, loc + 1, src, 0, ssize);
2361da177e4SLinus Torvalds
2371da177e4SLinus Torvalds /* Adjust the size of the preceding hole. */
2381da177e4SLinus Torvalds dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn;
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds /* We may have changed the length of the file, so fix the end marker */
2415c9f6de3SAnton Altaparmakov if (dst[marker].lcn == LCN_ENOENT)
2425c9f6de3SAnton Altaparmakov dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length;
2431da177e4SLinus Torvalds
2441da177e4SLinus Torvalds return dst;
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds
2471da177e4SLinus Torvalds /**
2481da177e4SLinus Torvalds * ntfs_rl_insert - insert a runlist into another
2491da177e4SLinus Torvalds * @dst: original runlist to be worked on
2501da177e4SLinus Torvalds * @dsize: number of elements in @dst (including end marker)
2511da177e4SLinus Torvalds * @src: new runlist to be inserted
2521da177e4SLinus Torvalds * @ssize: number of elements in @src (excluding end marker)
2531da177e4SLinus Torvalds * @loc: insert the new runlist @src before this element in @dst
2541da177e4SLinus Torvalds *
2551da177e4SLinus Torvalds * Insert the runlist @src before element @loc in the runlist @dst. Merge the
2561da177e4SLinus Torvalds * left end of the new runlist, if necessary. Adjust the size of the hole
2571da177e4SLinus Torvalds * after the inserted runlist.
2581da177e4SLinus Torvalds *
2591da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dst and @src.
2601da177e4SLinus Torvalds *
2611da177e4SLinus Torvalds * On success, return a pointer to the new, combined, runlist. Note, both
2621da177e4SLinus Torvalds * runlists @dst and @src are deallocated before returning so you cannot use
2631da177e4SLinus Torvalds * the pointers for anything any more. (Strictly speaking the returned runlist
2641da177e4SLinus Torvalds * may be the same as @dst but this is irrelevant.)
2651da177e4SLinus Torvalds *
2661da177e4SLinus Torvalds * On error, return -errno. Both runlists are left unmodified. The following
2671da177e4SLinus Torvalds * error codes are defined:
2681da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
2691da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
2701da177e4SLinus Torvalds */
ntfs_rl_insert(runlist_element * dst,int dsize,runlist_element * src,int ssize,int loc)2711da177e4SLinus Torvalds static inline runlist_element *ntfs_rl_insert(runlist_element *dst,
2721da177e4SLinus Torvalds int dsize, runlist_element *src, int ssize, int loc)
2731da177e4SLinus Torvalds {
274c49c3111SRichard Knutsson bool left = false; /* Left end of @src needs merging. */
275c49c3111SRichard Knutsson bool disc = false; /* Discontinuity between @dst and @src. */
2765c9f6de3SAnton Altaparmakov int marker; /* End of the inserted runs. */
2771da177e4SLinus Torvalds
2781da177e4SLinus Torvalds BUG_ON(!dst);
2791da177e4SLinus Torvalds BUG_ON(!src);
2801da177e4SLinus Torvalds
2815c9f6de3SAnton Altaparmakov /*
2825c9f6de3SAnton Altaparmakov * disc => Discontinuity between the end of @dst and the start of @src.
2835c9f6de3SAnton Altaparmakov * This means we might need to insert a "not mapped" run.
2845c9f6de3SAnton Altaparmakov */
2851da177e4SLinus Torvalds if (loc == 0)
2861da177e4SLinus Torvalds disc = (src[0].vcn > 0);
2871da177e4SLinus Torvalds else {
2881da177e4SLinus Torvalds s64 merged_length;
2891da177e4SLinus Torvalds
2901da177e4SLinus Torvalds left = ntfs_are_rl_mergeable(dst + loc - 1, src);
2911da177e4SLinus Torvalds
2921da177e4SLinus Torvalds merged_length = dst[loc - 1].length;
2931da177e4SLinus Torvalds if (left)
2941da177e4SLinus Torvalds merged_length += src->length;
2951da177e4SLinus Torvalds
2961da177e4SLinus Torvalds disc = (src[0].vcn > dst[loc - 1].vcn + merged_length);
2971da177e4SLinus Torvalds }
2985c9f6de3SAnton Altaparmakov /*
2995c9f6de3SAnton Altaparmakov * Space required: @dst size + @src size, less one if we merged, plus
3005c9f6de3SAnton Altaparmakov * one if there was a discontinuity.
3015c9f6de3SAnton Altaparmakov */
3025c9f6de3SAnton Altaparmakov dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc);
3031da177e4SLinus Torvalds if (IS_ERR(dst))
3041da177e4SLinus Torvalds return dst;
3051da177e4SLinus Torvalds /*
3061da177e4SLinus Torvalds * We are guaranteed to succeed from here so can start modifying the
3071da177e4SLinus Torvalds * original runlist.
3081da177e4SLinus Torvalds */
3091da177e4SLinus Torvalds if (left)
3101da177e4SLinus Torvalds __ntfs_rl_merge(dst + loc - 1, src);
3115c9f6de3SAnton Altaparmakov /*
3125c9f6de3SAnton Altaparmakov * First run after the @src runs that have been inserted.
3135c9f6de3SAnton Altaparmakov * Nominally, @marker equals @loc + @ssize, i.e. location + number of
3145c9f6de3SAnton Altaparmakov * runs in @src. However, if @left, then the first run in @src has
3155c9f6de3SAnton Altaparmakov * been merged with one in @dst. And if @disc, then @dst and @src do
3165c9f6de3SAnton Altaparmakov * not meet and we need an extra run to fill the gap.
3175c9f6de3SAnton Altaparmakov */
3185c9f6de3SAnton Altaparmakov marker = loc + ssize - left + disc;
3191da177e4SLinus Torvalds
3201da177e4SLinus Torvalds /* Move the tail of @dst out of the way, then copy in @src. */
3215c9f6de3SAnton Altaparmakov ntfs_rl_mm(dst, marker, loc, dsize - loc);
3225c9f6de3SAnton Altaparmakov ntfs_rl_mc(dst, loc + disc, src, left, ssize - left);
3231da177e4SLinus Torvalds
3245c9f6de3SAnton Altaparmakov /* Adjust the VCN of the first run after the insertion... */
3255c9f6de3SAnton Altaparmakov dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length;
3261da177e4SLinus Torvalds /* ... and the length. */
3275c9f6de3SAnton Altaparmakov if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED)
3285c9f6de3SAnton Altaparmakov dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn;
3291da177e4SLinus Torvalds
3305c9f6de3SAnton Altaparmakov /* Writing beyond the end of the file and there is a discontinuity. */
3311da177e4SLinus Torvalds if (disc) {
3321da177e4SLinus Torvalds if (loc > 0) {
3335c9f6de3SAnton Altaparmakov dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length;
3345c9f6de3SAnton Altaparmakov dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn;
3351da177e4SLinus Torvalds } else {
3361da177e4SLinus Torvalds dst[loc].vcn = 0;
3371da177e4SLinus Torvalds dst[loc].length = dst[loc + 1].vcn;
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds dst[loc].lcn = LCN_RL_NOT_MAPPED;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds return dst;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds
3441da177e4SLinus Torvalds /**
3451da177e4SLinus Torvalds * ntfs_rl_replace - overwrite a runlist element with another runlist
3461da177e4SLinus Torvalds * @dst: original runlist to be worked on
3471da177e4SLinus Torvalds * @dsize: number of elements in @dst (including end marker)
3481da177e4SLinus Torvalds * @src: new runlist to be inserted
3491da177e4SLinus Torvalds * @ssize: number of elements in @src (excluding end marker)
3501da177e4SLinus Torvalds * @loc: index in runlist @dst to overwrite with @src
3511da177e4SLinus Torvalds *
3521da177e4SLinus Torvalds * Replace the runlist element @dst at @loc with @src. Merge the left and
3531da177e4SLinus Torvalds * right ends of the inserted runlist, if necessary.
3541da177e4SLinus Torvalds *
3551da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dst and @src.
3561da177e4SLinus Torvalds *
3571da177e4SLinus Torvalds * On success, return a pointer to the new, combined, runlist. Note, both
3581da177e4SLinus Torvalds * runlists @dst and @src are deallocated before returning so you cannot use
3591da177e4SLinus Torvalds * the pointers for anything any more. (Strictly speaking the returned runlist
3601da177e4SLinus Torvalds * may be the same as @dst but this is irrelevant.)
3611da177e4SLinus Torvalds *
3621da177e4SLinus Torvalds * On error, return -errno. Both runlists are left unmodified. The following
3631da177e4SLinus Torvalds * error codes are defined:
3641da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
3651da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
3661da177e4SLinus Torvalds */
ntfs_rl_replace(runlist_element * dst,int dsize,runlist_element * src,int ssize,int loc)3671da177e4SLinus Torvalds static inline runlist_element *ntfs_rl_replace(runlist_element *dst,
3681da177e4SLinus Torvalds int dsize, runlist_element *src, int ssize, int loc)
3691da177e4SLinus Torvalds {
37067b1dfe7SAnton Altaparmakov signed delta;
371c49c3111SRichard Knutsson bool left = false; /* Left end of @src needs merging. */
372c49c3111SRichard Knutsson bool right = false; /* Right end of @src needs merging. */
3735c9f6de3SAnton Altaparmakov int tail; /* Start of tail of @dst. */
3745c9f6de3SAnton Altaparmakov int marker; /* End of the inserted runs. */
3751da177e4SLinus Torvalds
3761da177e4SLinus Torvalds BUG_ON(!dst);
3771da177e4SLinus Torvalds BUG_ON(!src);
3781da177e4SLinus Torvalds
379eed8b2deSAnton Altaparmakov /* First, see if the left and right ends need merging. */
380eed8b2deSAnton Altaparmakov if ((loc + 1) < dsize)
3811da177e4SLinus Torvalds right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1);
3821da177e4SLinus Torvalds if (loc > 0)
3831da177e4SLinus Torvalds left = ntfs_are_rl_mergeable(dst + loc - 1, src);
3845c9f6de3SAnton Altaparmakov /*
3855c9f6de3SAnton Altaparmakov * Allocate some space. We will need less if the left, right, or both
38667b1dfe7SAnton Altaparmakov * ends get merged. The -1 accounts for the run being replaced.
3875c9f6de3SAnton Altaparmakov */
38867b1dfe7SAnton Altaparmakov delta = ssize - 1 - left - right;
38967b1dfe7SAnton Altaparmakov if (delta > 0) {
39067b1dfe7SAnton Altaparmakov dst = ntfs_rl_realloc(dst, dsize, dsize + delta);
3911da177e4SLinus Torvalds if (IS_ERR(dst))
3921da177e4SLinus Torvalds return dst;
39367b1dfe7SAnton Altaparmakov }
3941da177e4SLinus Torvalds /*
3951da177e4SLinus Torvalds * We are guaranteed to succeed from here so can start modifying the
3961da177e4SLinus Torvalds * original runlists.
3971da177e4SLinus Torvalds */
398eed8b2deSAnton Altaparmakov
399eed8b2deSAnton Altaparmakov /* First, merge the left and right ends, if necessary. */
4001da177e4SLinus Torvalds if (right)
4011da177e4SLinus Torvalds __ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
4021da177e4SLinus Torvalds if (left)
4031da177e4SLinus Torvalds __ntfs_rl_merge(dst + loc - 1, src);
4045c9f6de3SAnton Altaparmakov /*
405eed8b2deSAnton Altaparmakov * Offset of the tail of @dst. This needs to be moved out of the way
406eed8b2deSAnton Altaparmakov * to make space for the runs to be copied from @src, i.e. the first
407eed8b2deSAnton Altaparmakov * run of the tail of @dst.
408eed8b2deSAnton Altaparmakov * Nominally, @tail equals @loc + 1, i.e. location, skipping the
409eed8b2deSAnton Altaparmakov * replaced run. However, if @right, then one of @dst's runs is
410eed8b2deSAnton Altaparmakov * already merged into @src.
4115c9f6de3SAnton Altaparmakov */
4125c9f6de3SAnton Altaparmakov tail = loc + right + 1;
4135c9f6de3SAnton Altaparmakov /*
4145c9f6de3SAnton Altaparmakov * First run after the @src runs that have been inserted, i.e. where
4155c9f6de3SAnton Altaparmakov * the tail of @dst needs to be moved to.
416eed8b2deSAnton Altaparmakov * Nominally, @marker equals @loc + @ssize, i.e. location + number of
417eed8b2deSAnton Altaparmakov * runs in @src. However, if @left, then the first run in @src has
4185c9f6de3SAnton Altaparmakov * been merged with one in @dst.
4195c9f6de3SAnton Altaparmakov */
4205c9f6de3SAnton Altaparmakov marker = loc + ssize - left;
4211da177e4SLinus Torvalds
4221da177e4SLinus Torvalds /* Move the tail of @dst out of the way, then copy in @src. */
4235c9f6de3SAnton Altaparmakov ntfs_rl_mm(dst, marker, tail, dsize - tail);
4241da177e4SLinus Torvalds ntfs_rl_mc(dst, loc, src, left, ssize - left);
4251da177e4SLinus Torvalds
4265c9f6de3SAnton Altaparmakov /* We may have changed the length of the file, so fix the end marker. */
4275c9f6de3SAnton Altaparmakov if (dsize - tail > 0 && dst[marker].lcn == LCN_ENOENT)
4285c9f6de3SAnton Altaparmakov dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length;
4291da177e4SLinus Torvalds return dst;
4301da177e4SLinus Torvalds }
4311da177e4SLinus Torvalds
4321da177e4SLinus Torvalds /**
4331da177e4SLinus Torvalds * ntfs_rl_split - insert a runlist into the centre of a hole
4341da177e4SLinus Torvalds * @dst: original runlist to be worked on
4351da177e4SLinus Torvalds * @dsize: number of elements in @dst (including end marker)
4361da177e4SLinus Torvalds * @src: new runlist to be inserted
4371da177e4SLinus Torvalds * @ssize: number of elements in @src (excluding end marker)
4381da177e4SLinus Torvalds * @loc: index in runlist @dst at which to split and insert @src
4391da177e4SLinus Torvalds *
4401da177e4SLinus Torvalds * Split the runlist @dst at @loc into two and insert @new in between the two
4411da177e4SLinus Torvalds * fragments. No merging of runlists is necessary. Adjust the size of the
4421da177e4SLinus Torvalds * holes either side.
4431da177e4SLinus Torvalds *
4441da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @dst and @src.
4451da177e4SLinus Torvalds *
4461da177e4SLinus Torvalds * On success, return a pointer to the new, combined, runlist. Note, both
4471da177e4SLinus Torvalds * runlists @dst and @src are deallocated before returning so you cannot use
4481da177e4SLinus Torvalds * the pointers for anything any more. (Strictly speaking the returned runlist
4491da177e4SLinus Torvalds * may be the same as @dst but this is irrelevant.)
4501da177e4SLinus Torvalds *
4511da177e4SLinus Torvalds * On error, return -errno. Both runlists are left unmodified. The following
4521da177e4SLinus Torvalds * error codes are defined:
4531da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
4541da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
4551da177e4SLinus Torvalds */
ntfs_rl_split(runlist_element * dst,int dsize,runlist_element * src,int ssize,int loc)4561da177e4SLinus Torvalds static inline runlist_element *ntfs_rl_split(runlist_element *dst, int dsize,
4571da177e4SLinus Torvalds runlist_element *src, int ssize, int loc)
4581da177e4SLinus Torvalds {
4591da177e4SLinus Torvalds BUG_ON(!dst);
4601da177e4SLinus Torvalds BUG_ON(!src);
4611da177e4SLinus Torvalds
4621da177e4SLinus Torvalds /* Space required: @dst size + @src size + one new hole. */
4631da177e4SLinus Torvalds dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1);
4641da177e4SLinus Torvalds if (IS_ERR(dst))
4651da177e4SLinus Torvalds return dst;
4661da177e4SLinus Torvalds /*
4671da177e4SLinus Torvalds * We are guaranteed to succeed from here so can start modifying the
4681da177e4SLinus Torvalds * original runlists.
4691da177e4SLinus Torvalds */
4701da177e4SLinus Torvalds
4711da177e4SLinus Torvalds /* Move the tail of @dst out of the way, then copy in @src. */
4721da177e4SLinus Torvalds ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc);
4731da177e4SLinus Torvalds ntfs_rl_mc(dst, loc + 1, src, 0, ssize);
4741da177e4SLinus Torvalds
4751da177e4SLinus Torvalds /* Adjust the size of the holes either size of @src. */
4761da177e4SLinus Torvalds dst[loc].length = dst[loc+1].vcn - dst[loc].vcn;
4771da177e4SLinus Torvalds dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length;
4781da177e4SLinus Torvalds dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn;
4791da177e4SLinus Torvalds
4801da177e4SLinus Torvalds return dst;
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds
4831da177e4SLinus Torvalds /**
4841da177e4SLinus Torvalds * ntfs_runlists_merge - merge two runlists into one
4851da177e4SLinus Torvalds * @drl: original runlist to be worked on
4861da177e4SLinus Torvalds * @srl: new runlist to be merged into @drl
4871da177e4SLinus Torvalds *
4881da177e4SLinus Torvalds * First we sanity check the two runlists @srl and @drl to make sure that they
4891da177e4SLinus Torvalds * are sensible and can be merged. The runlist @srl must be either after the
4901da177e4SLinus Torvalds * runlist @drl or completely within a hole (or unmapped region) in @drl.
4911da177e4SLinus Torvalds *
4921da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlists @drl and @srl.
4931da177e4SLinus Torvalds *
4941da177e4SLinus Torvalds * Merging of runlists is necessary in two cases:
4951da177e4SLinus Torvalds * 1. When attribute lists are used and a further extent is being mapped.
4961da177e4SLinus Torvalds * 2. When new clusters are allocated to fill a hole or extend a file.
4971da177e4SLinus Torvalds *
4981da177e4SLinus Torvalds * There are four possible ways @srl can be merged. It can:
4991da177e4SLinus Torvalds * - be inserted at the beginning of a hole,
5001da177e4SLinus Torvalds * - split the hole in two and be inserted between the two fragments,
5011da177e4SLinus Torvalds * - be appended at the end of a hole, or it can
5021da177e4SLinus Torvalds * - replace the whole hole.
5031da177e4SLinus Torvalds * It can also be appended to the end of the runlist, which is just a variant
5041da177e4SLinus Torvalds * of the insert case.
5051da177e4SLinus Torvalds *
5061da177e4SLinus Torvalds * On success, return a pointer to the new, combined, runlist. Note, both
5071da177e4SLinus Torvalds * runlists @drl and @srl are deallocated before returning so you cannot use
5081da177e4SLinus Torvalds * the pointers for anything any more. (Strictly speaking the returned runlist
5091da177e4SLinus Torvalds * may be the same as @dst but this is irrelevant.)
5101da177e4SLinus Torvalds *
5111da177e4SLinus Torvalds * On error, return -errno. Both runlists are left unmodified. The following
5121da177e4SLinus Torvalds * error codes are defined:
5131da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
5141da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
5151da177e4SLinus Torvalds * -ERANGE - The runlists overlap and cannot be merged.
5161da177e4SLinus Torvalds */
ntfs_runlists_merge(runlist_element * drl,runlist_element * srl)5171da177e4SLinus Torvalds runlist_element *ntfs_runlists_merge(runlist_element *drl,
5181da177e4SLinus Torvalds runlist_element *srl)
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds int di, si; /* Current index into @[ds]rl. */
5211da177e4SLinus Torvalds int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */
5221da177e4SLinus Torvalds int dins; /* Index into @drl at which to insert @srl. */
5231da177e4SLinus Torvalds int dend, send; /* Last index into @[ds]rl. */
5241da177e4SLinus Torvalds int dfinal, sfinal; /* The last index into @[ds]rl with
5251da177e4SLinus Torvalds lcn >= LCN_HOLE. */
5261da177e4SLinus Torvalds int marker = 0;
5271da177e4SLinus Torvalds VCN marker_vcn = 0;
5281da177e4SLinus Torvalds
5291da177e4SLinus Torvalds #ifdef DEBUG
5301da177e4SLinus Torvalds ntfs_debug("dst:");
5311da177e4SLinus Torvalds ntfs_debug_dump_runlist(drl);
5321da177e4SLinus Torvalds ntfs_debug("src:");
5331da177e4SLinus Torvalds ntfs_debug_dump_runlist(srl);
5341da177e4SLinus Torvalds #endif
5351da177e4SLinus Torvalds
5361da177e4SLinus Torvalds /* Check for silly calling... */
5371da177e4SLinus Torvalds if (unlikely(!srl))
5381da177e4SLinus Torvalds return drl;
5391da177e4SLinus Torvalds if (IS_ERR(srl) || IS_ERR(drl))
5401da177e4SLinus Torvalds return ERR_PTR(-EINVAL);
5411da177e4SLinus Torvalds
5421da177e4SLinus Torvalds /* Check for the case where the first mapping is being done now. */
5431da177e4SLinus Torvalds if (unlikely(!drl)) {
5441da177e4SLinus Torvalds drl = srl;
5451da177e4SLinus Torvalds /* Complete the source runlist if necessary. */
5461da177e4SLinus Torvalds if (unlikely(drl[0].vcn)) {
5471da177e4SLinus Torvalds /* Scan to the end of the source runlist. */
5481da177e4SLinus Torvalds for (dend = 0; likely(drl[dend].length); dend++)
5491da177e4SLinus Torvalds ;
55084d6ebe6SAnton Altaparmakov dend++;
5511da177e4SLinus Torvalds drl = ntfs_rl_realloc(drl, dend, dend + 1);
5521da177e4SLinus Torvalds if (IS_ERR(drl))
5531da177e4SLinus Torvalds return drl;
5541da177e4SLinus Torvalds /* Insert start element at the front of the runlist. */
5551da177e4SLinus Torvalds ntfs_rl_mm(drl, 1, 0, dend);
5561da177e4SLinus Torvalds drl[0].vcn = 0;
5571da177e4SLinus Torvalds drl[0].lcn = LCN_RL_NOT_MAPPED;
5581da177e4SLinus Torvalds drl[0].length = drl[1].vcn;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds goto finished;
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds
5631da177e4SLinus Torvalds si = di = 0;
5641da177e4SLinus Torvalds
5651da177e4SLinus Torvalds /* Skip any unmapped start element(s) in the source runlist. */
5661da177e4SLinus Torvalds while (srl[si].length && srl[si].lcn < LCN_HOLE)
5671da177e4SLinus Torvalds si++;
5681da177e4SLinus Torvalds
5691da177e4SLinus Torvalds /* Can't have an entirely unmapped source runlist. */
5701da177e4SLinus Torvalds BUG_ON(!srl[si].length);
5711da177e4SLinus Torvalds
5721da177e4SLinus Torvalds /* Record the starting points. */
5731da177e4SLinus Torvalds sstart = si;
5741da177e4SLinus Torvalds
5751da177e4SLinus Torvalds /*
5761da177e4SLinus Torvalds * Skip forward in @drl until we reach the position where @srl needs to
5771da177e4SLinus Torvalds * be inserted. If we reach the end of @drl, @srl just needs to be
5781da177e4SLinus Torvalds * appended to @drl.
5791da177e4SLinus Torvalds */
5801da177e4SLinus Torvalds for (; drl[di].length; di++) {
5811da177e4SLinus Torvalds if (drl[di].vcn + drl[di].length > srl[sstart].vcn)
5821da177e4SLinus Torvalds break;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds dins = di;
5851da177e4SLinus Torvalds
5861da177e4SLinus Torvalds /* Sanity check for illegal overlaps. */
5871da177e4SLinus Torvalds if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) &&
5881da177e4SLinus Torvalds (srl[si].lcn >= 0)) {
5891da177e4SLinus Torvalds ntfs_error(NULL, "Run lists overlap. Cannot merge!");
5901da177e4SLinus Torvalds return ERR_PTR(-ERANGE);
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds
5931da177e4SLinus Torvalds /* Scan to the end of both runlists in order to know their sizes. */
5941da177e4SLinus Torvalds for (send = si; srl[send].length; send++)
5951da177e4SLinus Torvalds ;
5961da177e4SLinus Torvalds for (dend = di; drl[dend].length; dend++)
5971da177e4SLinus Torvalds ;
5981da177e4SLinus Torvalds
5991da177e4SLinus Torvalds if (srl[send].lcn == LCN_ENOENT)
6001da177e4SLinus Torvalds marker_vcn = srl[marker = send].vcn;
6011da177e4SLinus Torvalds
6021da177e4SLinus Torvalds /* Scan to the last element with lcn >= LCN_HOLE. */
6031da177e4SLinus Torvalds for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--)
6041da177e4SLinus Torvalds ;
6051da177e4SLinus Torvalds for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--)
6061da177e4SLinus Torvalds ;
6071da177e4SLinus Torvalds
6081da177e4SLinus Torvalds {
609c49c3111SRichard Knutsson bool start;
610c49c3111SRichard Knutsson bool finish;
6111da177e4SLinus Torvalds int ds = dend + 1; /* Number of elements in drl & srl */
6121da177e4SLinus Torvalds int ss = sfinal - sstart + 1;
6131da177e4SLinus Torvalds
6141da177e4SLinus Torvalds start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */
6151da177e4SLinus Torvalds (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */
6161da177e4SLinus Torvalds finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */
6171da177e4SLinus Torvalds ((drl[dins].vcn + drl[dins].length) <= /* End of hole */
6181da177e4SLinus Torvalds (srl[send - 1].vcn + srl[send - 1].length)));
6191da177e4SLinus Torvalds
62084d6ebe6SAnton Altaparmakov /* Or we will lose an end marker. */
62184d6ebe6SAnton Altaparmakov if (finish && !drl[dins].length)
6221da177e4SLinus Torvalds ss++;
6231da177e4SLinus Torvalds if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn))
624c49c3111SRichard Knutsson finish = false;
6251da177e4SLinus Torvalds #if 0
6261da177e4SLinus Torvalds ntfs_debug("dfinal = %i, dend = %i", dfinal, dend);
6271da177e4SLinus Torvalds ntfs_debug("sstart = %i, sfinal = %i, send = %i", sstart, sfinal, send);
6281da177e4SLinus Torvalds ntfs_debug("start = %i, finish = %i", start, finish);
6291da177e4SLinus Torvalds ntfs_debug("ds = %i, ss = %i, dins = %i", ds, ss, dins);
6301da177e4SLinus Torvalds #endif
6311da177e4SLinus Torvalds if (start) {
6321da177e4SLinus Torvalds if (finish)
6331da177e4SLinus Torvalds drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins);
6341da177e4SLinus Torvalds else
6351da177e4SLinus Torvalds drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins);
6361da177e4SLinus Torvalds } else {
6371da177e4SLinus Torvalds if (finish)
6381da177e4SLinus Torvalds drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins);
6391da177e4SLinus Torvalds else
6401da177e4SLinus Torvalds drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins);
6411da177e4SLinus Torvalds }
6421da177e4SLinus Torvalds if (IS_ERR(drl)) {
6431da177e4SLinus Torvalds ntfs_error(NULL, "Merge failed.");
6441da177e4SLinus Torvalds return drl;
6451da177e4SLinus Torvalds }
6461da177e4SLinus Torvalds ntfs_free(srl);
6471da177e4SLinus Torvalds if (marker) {
6481da177e4SLinus Torvalds ntfs_debug("Triggering marker code.");
6491da177e4SLinus Torvalds for (ds = dend; drl[ds].length; ds++)
6501da177e4SLinus Torvalds ;
6511da177e4SLinus Torvalds /* We only need to care if @srl ended after @drl. */
6521da177e4SLinus Torvalds if (drl[ds].vcn <= marker_vcn) {
6531da177e4SLinus Torvalds int slots = 0;
6541da177e4SLinus Torvalds
6551da177e4SLinus Torvalds if (drl[ds].vcn == marker_vcn) {
6561da177e4SLinus Torvalds ntfs_debug("Old marker = 0x%llx, replacing "
6571da177e4SLinus Torvalds "with LCN_ENOENT.",
6581da177e4SLinus Torvalds (unsigned long long)
6591da177e4SLinus Torvalds drl[ds].lcn);
6601da177e4SLinus Torvalds drl[ds].lcn = LCN_ENOENT;
6611da177e4SLinus Torvalds goto finished;
6621da177e4SLinus Torvalds }
6631da177e4SLinus Torvalds /*
6641da177e4SLinus Torvalds * We need to create an unmapped runlist element in
6651da177e4SLinus Torvalds * @drl or extend an existing one before adding the
6661da177e4SLinus Torvalds * ENOENT terminator.
6671da177e4SLinus Torvalds */
6681da177e4SLinus Torvalds if (drl[ds].lcn == LCN_ENOENT) {
6691da177e4SLinus Torvalds ds--;
6701da177e4SLinus Torvalds slots = 1;
6711da177e4SLinus Torvalds }
6721da177e4SLinus Torvalds if (drl[ds].lcn != LCN_RL_NOT_MAPPED) {
6731da177e4SLinus Torvalds /* Add an unmapped runlist element. */
6741da177e4SLinus Torvalds if (!slots) {
6759529d461SAnton Altaparmakov drl = ntfs_rl_realloc_nofail(drl, ds,
6769529d461SAnton Altaparmakov ds + 2);
6771da177e4SLinus Torvalds slots = 2;
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds ds++;
6801da177e4SLinus Torvalds /* Need to set vcn if it isn't set already. */
6811da177e4SLinus Torvalds if (slots != 1)
6821da177e4SLinus Torvalds drl[ds].vcn = drl[ds - 1].vcn +
6831da177e4SLinus Torvalds drl[ds - 1].length;
6841da177e4SLinus Torvalds drl[ds].lcn = LCN_RL_NOT_MAPPED;
6851da177e4SLinus Torvalds /* We now used up a slot. */
6861da177e4SLinus Torvalds slots--;
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds drl[ds].length = marker_vcn - drl[ds].vcn;
6891da177e4SLinus Torvalds /* Finally add the ENOENT terminator. */
6901da177e4SLinus Torvalds ds++;
6919529d461SAnton Altaparmakov if (!slots)
6929529d461SAnton Altaparmakov drl = ntfs_rl_realloc_nofail(drl, ds, ds + 1);
6931da177e4SLinus Torvalds drl[ds].vcn = marker_vcn;
6941da177e4SLinus Torvalds drl[ds].lcn = LCN_ENOENT;
6951da177e4SLinus Torvalds drl[ds].length = (s64)0;
6961da177e4SLinus Torvalds }
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds }
6991da177e4SLinus Torvalds
7001da177e4SLinus Torvalds finished:
7011da177e4SLinus Torvalds /* The merge was completed successfully. */
7021da177e4SLinus Torvalds ntfs_debug("Merged runlist:");
7031da177e4SLinus Torvalds ntfs_debug_dump_runlist(drl);
7041da177e4SLinus Torvalds return drl;
7051da177e4SLinus Torvalds }
7061da177e4SLinus Torvalds
7071da177e4SLinus Torvalds /**
7081da177e4SLinus Torvalds * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist
7091da177e4SLinus Torvalds * @vol: ntfs volume on which the attribute resides
7101da177e4SLinus Torvalds * @attr: attribute record whose mapping pairs array to decompress
7111da177e4SLinus Torvalds * @old_rl: optional runlist in which to insert @attr's runlist
7121da177e4SLinus Torvalds *
7131da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlist @old_rl.
7141da177e4SLinus Torvalds *
7151da177e4SLinus Torvalds * Decompress the attribute @attr's mapping pairs array into a runlist. On
7161da177e4SLinus Torvalds * success, return the decompressed runlist.
7171da177e4SLinus Torvalds *
7181da177e4SLinus Torvalds * If @old_rl is not NULL, decompressed runlist is inserted into the
7191da177e4SLinus Torvalds * appropriate place in @old_rl and the resultant, combined runlist is
7201da177e4SLinus Torvalds * returned. The original @old_rl is deallocated.
7211da177e4SLinus Torvalds *
7221da177e4SLinus Torvalds * On error, return -errno. @old_rl is left unmodified in that case.
7231da177e4SLinus Torvalds *
7241da177e4SLinus Torvalds * The following error codes are defined:
7251da177e4SLinus Torvalds * -ENOMEM - Not enough memory to allocate runlist array.
7261da177e4SLinus Torvalds * -EIO - Corrupt runlist.
7271da177e4SLinus Torvalds * -EINVAL - Invalid parameters were passed in.
7281da177e4SLinus Torvalds * -ERANGE - The two runlists overlap.
7291da177e4SLinus Torvalds *
7301da177e4SLinus Torvalds * FIXME: For now we take the conceptionally simplest approach of creating the
7311da177e4SLinus Torvalds * new runlist disregarding the already existing one and then splicing the
7321da177e4SLinus Torvalds * two into one, if that is possible (we check for overlap and discard the new
7331da177e4SLinus Torvalds * runlist if overlap present before returning ERR_PTR(-ERANGE)).
7341da177e4SLinus Torvalds */
ntfs_mapping_pairs_decompress(const ntfs_volume * vol,const ATTR_RECORD * attr,runlist_element * old_rl)7351da177e4SLinus Torvalds runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
7361da177e4SLinus Torvalds const ATTR_RECORD *attr, runlist_element *old_rl)
7371da177e4SLinus Torvalds {
7381da177e4SLinus Torvalds VCN vcn; /* Current vcn. */
7391da177e4SLinus Torvalds LCN lcn; /* Current lcn. */
7401da177e4SLinus Torvalds s64 deltaxcn; /* Change in [vl]cn. */
7411da177e4SLinus Torvalds runlist_element *rl; /* The output runlist. */
7421da177e4SLinus Torvalds u8 *buf; /* Current position in mapping pairs array. */
7431da177e4SLinus Torvalds u8 *attr_end; /* End of attribute. */
7441da177e4SLinus Torvalds int rlsize; /* Size of runlist buffer. */
7451da177e4SLinus Torvalds u16 rlpos; /* Current runlist position in units of
7461da177e4SLinus Torvalds runlist_elements. */
7471da177e4SLinus Torvalds u8 b; /* Current byte offset in buf. */
7481da177e4SLinus Torvalds
7491da177e4SLinus Torvalds #ifdef DEBUG
7501da177e4SLinus Torvalds /* Make sure attr exists and is non-resident. */
7511da177e4SLinus Torvalds if (!attr || !attr->non_resident || sle64_to_cpu(
7521da177e4SLinus Torvalds attr->data.non_resident.lowest_vcn) < (VCN)0) {
7531da177e4SLinus Torvalds ntfs_error(vol->sb, "Invalid arguments.");
7541da177e4SLinus Torvalds return ERR_PTR(-EINVAL);
7551da177e4SLinus Torvalds }
7561da177e4SLinus Torvalds #endif
7571da177e4SLinus Torvalds /* Start at vcn = lowest_vcn and lcn 0. */
7581da177e4SLinus Torvalds vcn = sle64_to_cpu(attr->data.non_resident.lowest_vcn);
7591da177e4SLinus Torvalds lcn = 0;
7601da177e4SLinus Torvalds /* Get start of the mapping pairs array. */
7611da177e4SLinus Torvalds buf = (u8*)attr + le16_to_cpu(
7621da177e4SLinus Torvalds attr->data.non_resident.mapping_pairs_offset);
7631da177e4SLinus Torvalds attr_end = (u8*)attr + le32_to_cpu(attr->length);
7641da177e4SLinus Torvalds if (unlikely(buf < (u8*)attr || buf > attr_end)) {
7651da177e4SLinus Torvalds ntfs_error(vol->sb, "Corrupt attribute.");
7661da177e4SLinus Torvalds return ERR_PTR(-EIO);
7671da177e4SLinus Torvalds }
7682b0ada2bSAnton Altaparmakov /* If the mapping pairs array is valid but empty, nothing to do. */
7692b0ada2bSAnton Altaparmakov if (!vcn && !*buf)
7702b0ada2bSAnton Altaparmakov return old_rl;
7711da177e4SLinus Torvalds /* Current position in runlist array. */
7721da177e4SLinus Torvalds rlpos = 0;
7731da177e4SLinus Torvalds /* Allocate first page and set current runlist size to one page. */
7741da177e4SLinus Torvalds rl = ntfs_malloc_nofs(rlsize = PAGE_SIZE);
7751da177e4SLinus Torvalds if (unlikely(!rl))
7761da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
7771da177e4SLinus Torvalds /* Insert unmapped starting element if necessary. */
7781da177e4SLinus Torvalds if (vcn) {
7791da177e4SLinus Torvalds rl->vcn = 0;
7801da177e4SLinus Torvalds rl->lcn = LCN_RL_NOT_MAPPED;
7811da177e4SLinus Torvalds rl->length = vcn;
7821da177e4SLinus Torvalds rlpos++;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds while (buf < attr_end && *buf) {
7851da177e4SLinus Torvalds /*
7861da177e4SLinus Torvalds * Allocate more memory if needed, including space for the
7871da177e4SLinus Torvalds * not-mapped and terminator elements. ntfs_malloc_nofs()
7881da177e4SLinus Torvalds * operates on whole pages only.
7891da177e4SLinus Torvalds */
7901da177e4SLinus Torvalds if (((rlpos + 3) * sizeof(*old_rl)) > rlsize) {
7911da177e4SLinus Torvalds runlist_element *rl2;
7921da177e4SLinus Torvalds
7931da177e4SLinus Torvalds rl2 = ntfs_malloc_nofs(rlsize + (int)PAGE_SIZE);
7941da177e4SLinus Torvalds if (unlikely(!rl2)) {
7951da177e4SLinus Torvalds ntfs_free(rl);
7961da177e4SLinus Torvalds return ERR_PTR(-ENOMEM);
7971da177e4SLinus Torvalds }
7981da177e4SLinus Torvalds memcpy(rl2, rl, rlsize);
7991da177e4SLinus Torvalds ntfs_free(rl);
8001da177e4SLinus Torvalds rl = rl2;
8011da177e4SLinus Torvalds rlsize += PAGE_SIZE;
8021da177e4SLinus Torvalds }
8031da177e4SLinus Torvalds /* Enter the current vcn into the current runlist element. */
8041da177e4SLinus Torvalds rl[rlpos].vcn = vcn;
8051da177e4SLinus Torvalds /*
8061da177e4SLinus Torvalds * Get the change in vcn, i.e. the run length in clusters.
8071da177e4SLinus Torvalds * Doing it this way ensures that we signextend negative values.
8081da177e4SLinus Torvalds * A negative run length doesn't make any sense, but hey, I
8091da177e4SLinus Torvalds * didn't make up the NTFS specs and Windows NT4 treats the run
8101da177e4SLinus Torvalds * length as a signed value so that's how it is...
8111da177e4SLinus Torvalds */
8121da177e4SLinus Torvalds b = *buf & 0xf;
8131da177e4SLinus Torvalds if (b) {
8141da177e4SLinus Torvalds if (unlikely(buf + b > attr_end))
8151da177e4SLinus Torvalds goto io_error;
8161da177e4SLinus Torvalds for (deltaxcn = (s8)buf[b--]; b; b--)
8171da177e4SLinus Torvalds deltaxcn = (deltaxcn << 8) + buf[b];
8181da177e4SLinus Torvalds } else { /* The length entry is compulsory. */
8191da177e4SLinus Torvalds ntfs_error(vol->sb, "Missing length entry in mapping "
8201da177e4SLinus Torvalds "pairs array.");
8211da177e4SLinus Torvalds deltaxcn = (s64)-1;
8221da177e4SLinus Torvalds }
8231da177e4SLinus Torvalds /*
8241da177e4SLinus Torvalds * Assume a negative length to indicate data corruption and
8251da177e4SLinus Torvalds * hence clean-up and return NULL.
8261da177e4SLinus Torvalds */
8271da177e4SLinus Torvalds if (unlikely(deltaxcn < 0)) {
8281da177e4SLinus Torvalds ntfs_error(vol->sb, "Invalid length in mapping pairs "
8291da177e4SLinus Torvalds "array.");
8301da177e4SLinus Torvalds goto err_out;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds /*
8331da177e4SLinus Torvalds * Enter the current run length into the current runlist
8341da177e4SLinus Torvalds * element.
8351da177e4SLinus Torvalds */
8361da177e4SLinus Torvalds rl[rlpos].length = deltaxcn;
8371da177e4SLinus Torvalds /* Increment the current vcn by the current run length. */
8381da177e4SLinus Torvalds vcn += deltaxcn;
8391da177e4SLinus Torvalds /*
8401da177e4SLinus Torvalds * There might be no lcn change at all, as is the case for
8411da177e4SLinus Torvalds * sparse clusters on NTFS 3.0+, in which case we set the lcn
8421da177e4SLinus Torvalds * to LCN_HOLE.
8431da177e4SLinus Torvalds */
8441da177e4SLinus Torvalds if (!(*buf & 0xf0))
8451da177e4SLinus Torvalds rl[rlpos].lcn = LCN_HOLE;
8461da177e4SLinus Torvalds else {
8471da177e4SLinus Torvalds /* Get the lcn change which really can be negative. */
8481da177e4SLinus Torvalds u8 b2 = *buf & 0xf;
8491da177e4SLinus Torvalds b = b2 + ((*buf >> 4) & 0xf);
8501da177e4SLinus Torvalds if (buf + b > attr_end)
8511da177e4SLinus Torvalds goto io_error;
8521da177e4SLinus Torvalds for (deltaxcn = (s8)buf[b--]; b > b2; b--)
8531da177e4SLinus Torvalds deltaxcn = (deltaxcn << 8) + buf[b];
8541da177e4SLinus Torvalds /* Change the current lcn to its new value. */
8551da177e4SLinus Torvalds lcn += deltaxcn;
8561da177e4SLinus Torvalds #ifdef DEBUG
8571da177e4SLinus Torvalds /*
8581da177e4SLinus Torvalds * On NTFS 1.2-, apparently can have lcn == -1 to
8591da177e4SLinus Torvalds * indicate a hole. But we haven't verified ourselves
8601da177e4SLinus Torvalds * whether it is really the lcn or the deltaxcn that is
8611da177e4SLinus Torvalds * -1. So if either is found give us a message so we
8621da177e4SLinus Torvalds * can investigate it further!
8631da177e4SLinus Torvalds */
8641da177e4SLinus Torvalds if (vol->major_ver < 3) {
8651da177e4SLinus Torvalds if (unlikely(deltaxcn == (LCN)-1))
8661da177e4SLinus Torvalds ntfs_error(vol->sb, "lcn delta == -1");
8671da177e4SLinus Torvalds if (unlikely(lcn == (LCN)-1))
8681da177e4SLinus Torvalds ntfs_error(vol->sb, "lcn == -1");
8691da177e4SLinus Torvalds }
8701da177e4SLinus Torvalds #endif
8711da177e4SLinus Torvalds /* Check lcn is not below -1. */
8721da177e4SLinus Torvalds if (unlikely(lcn < (LCN)-1)) {
8731da177e4SLinus Torvalds ntfs_error(vol->sb, "Invalid LCN < -1 in "
8741da177e4SLinus Torvalds "mapping pairs array.");
8751da177e4SLinus Torvalds goto err_out;
8761da177e4SLinus Torvalds }
8771da177e4SLinus Torvalds /* Enter the current lcn into the runlist element. */
8781da177e4SLinus Torvalds rl[rlpos].lcn = lcn;
8791da177e4SLinus Torvalds }
8801da177e4SLinus Torvalds /* Get to the next runlist element. */
8811da177e4SLinus Torvalds rlpos++;
8821da177e4SLinus Torvalds /* Increment the buffer position to the next mapping pair. */
8831da177e4SLinus Torvalds buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds if (unlikely(buf >= attr_end))
8861da177e4SLinus Torvalds goto io_error;
8871da177e4SLinus Torvalds /*
8881da177e4SLinus Torvalds * If there is a highest_vcn specified, it must be equal to the final
8891da177e4SLinus Torvalds * vcn in the runlist - 1, or something has gone badly wrong.
8901da177e4SLinus Torvalds */
8911da177e4SLinus Torvalds deltaxcn = sle64_to_cpu(attr->data.non_resident.highest_vcn);
8921da177e4SLinus Torvalds if (unlikely(deltaxcn && vcn - 1 != deltaxcn)) {
8931da177e4SLinus Torvalds mpa_err:
8941da177e4SLinus Torvalds ntfs_error(vol->sb, "Corrupt mapping pairs array in "
8951da177e4SLinus Torvalds "non-resident attribute.");
8961da177e4SLinus Torvalds goto err_out;
8971da177e4SLinus Torvalds }
8981da177e4SLinus Torvalds /* Setup not mapped runlist element if this is the base extent. */
8991da177e4SLinus Torvalds if (!attr->data.non_resident.lowest_vcn) {
9001da177e4SLinus Torvalds VCN max_cluster;
9011da177e4SLinus Torvalds
9021a0df15aSAnton Altaparmakov max_cluster = ((sle64_to_cpu(
9031da177e4SLinus Torvalds attr->data.non_resident.allocated_size) +
9041da177e4SLinus Torvalds vol->cluster_size - 1) >>
9051a0df15aSAnton Altaparmakov vol->cluster_size_bits) - 1;
9061da177e4SLinus Torvalds /*
9071a0df15aSAnton Altaparmakov * A highest_vcn of zero means this is a single extent
9081a0df15aSAnton Altaparmakov * attribute so simply terminate the runlist with LCN_ENOENT).
9091da177e4SLinus Torvalds */
9101a0df15aSAnton Altaparmakov if (deltaxcn) {
9111a0df15aSAnton Altaparmakov /*
9121a0df15aSAnton Altaparmakov * If there is a difference between the highest_vcn and
9131a0df15aSAnton Altaparmakov * the highest cluster, the runlist is either corrupt
9141a0df15aSAnton Altaparmakov * or, more likely, there are more extents following
9151a0df15aSAnton Altaparmakov * this one.
9161a0df15aSAnton Altaparmakov */
9171a0df15aSAnton Altaparmakov if (deltaxcn < max_cluster) {
9181a0df15aSAnton Altaparmakov ntfs_debug("More extents to follow; deltaxcn "
9191a0df15aSAnton Altaparmakov "= 0x%llx, max_cluster = "
9201a0df15aSAnton Altaparmakov "0x%llx",
9211da177e4SLinus Torvalds (unsigned long long)deltaxcn,
9221a0df15aSAnton Altaparmakov (unsigned long long)
9231a0df15aSAnton Altaparmakov max_cluster);
9241da177e4SLinus Torvalds rl[rlpos].vcn = vcn;
9251a0df15aSAnton Altaparmakov vcn += rl[rlpos].length = max_cluster -
9261a0df15aSAnton Altaparmakov deltaxcn;
9271da177e4SLinus Torvalds rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
9281da177e4SLinus Torvalds rlpos++;
9291da177e4SLinus Torvalds } else if (unlikely(deltaxcn > max_cluster)) {
9301a0df15aSAnton Altaparmakov ntfs_error(vol->sb, "Corrupt attribute. "
9311a0df15aSAnton Altaparmakov "deltaxcn = 0x%llx, "
9321a0df15aSAnton Altaparmakov "max_cluster = 0x%llx",
9331da177e4SLinus Torvalds (unsigned long long)deltaxcn,
9341a0df15aSAnton Altaparmakov (unsigned long long)
9351a0df15aSAnton Altaparmakov max_cluster);
9361da177e4SLinus Torvalds goto mpa_err;
9371da177e4SLinus Torvalds }
9381a0df15aSAnton Altaparmakov }
9391da177e4SLinus Torvalds rl[rlpos].lcn = LCN_ENOENT;
9401da177e4SLinus Torvalds } else /* Not the base extent. There may be more extents to follow. */
9411da177e4SLinus Torvalds rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
9421da177e4SLinus Torvalds
9431da177e4SLinus Torvalds /* Setup terminating runlist element. */
9441da177e4SLinus Torvalds rl[rlpos].vcn = vcn;
9451da177e4SLinus Torvalds rl[rlpos].length = (s64)0;
9461da177e4SLinus Torvalds /* If no existing runlist was specified, we are done. */
9471da177e4SLinus Torvalds if (!old_rl) {
9481da177e4SLinus Torvalds ntfs_debug("Mapping pairs array successfully decompressed:");
9491da177e4SLinus Torvalds ntfs_debug_dump_runlist(rl);
9501da177e4SLinus Torvalds return rl;
9511da177e4SLinus Torvalds }
9521da177e4SLinus Torvalds /* Now combine the new and old runlists checking for overlaps. */
9531da177e4SLinus Torvalds old_rl = ntfs_runlists_merge(old_rl, rl);
954cc22c800SDenis Efremov if (!IS_ERR(old_rl))
9551da177e4SLinus Torvalds return old_rl;
9561da177e4SLinus Torvalds ntfs_free(rl);
9571da177e4SLinus Torvalds ntfs_error(vol->sb, "Failed to merge runlists.");
9581da177e4SLinus Torvalds return old_rl;
9591da177e4SLinus Torvalds io_error:
9601da177e4SLinus Torvalds ntfs_error(vol->sb, "Corrupt attribute.");
9611da177e4SLinus Torvalds err_out:
9621da177e4SLinus Torvalds ntfs_free(rl);
9631da177e4SLinus Torvalds return ERR_PTR(-EIO);
9641da177e4SLinus Torvalds }
9651da177e4SLinus Torvalds
9661da177e4SLinus Torvalds /**
9671da177e4SLinus Torvalds * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist
9681da177e4SLinus Torvalds * @rl: runlist to use for conversion
9691da177e4SLinus Torvalds * @vcn: vcn to convert
9701da177e4SLinus Torvalds *
9711da177e4SLinus Torvalds * Convert the virtual cluster number @vcn of an attribute into a logical
9721da177e4SLinus Torvalds * cluster number (lcn) of a device using the runlist @rl to map vcns to their
9731da177e4SLinus Torvalds * corresponding lcns.
9741da177e4SLinus Torvalds *
9751da177e4SLinus Torvalds * It is up to the caller to serialize access to the runlist @rl.
9761da177e4SLinus Torvalds *
977c0c1cc0eSAnton Altaparmakov * Since lcns must be >= 0, we use negative return codes with special meaning:
9781da177e4SLinus Torvalds *
979c0c1cc0eSAnton Altaparmakov * Return code Meaning / Description
9801da177e4SLinus Torvalds * ==================================================
981c0c1cc0eSAnton Altaparmakov * LCN_HOLE Hole / not allocated on disk.
982c0c1cc0eSAnton Altaparmakov * LCN_RL_NOT_MAPPED This is part of the runlist which has not been
9831da177e4SLinus Torvalds * inserted into the runlist yet.
984c0c1cc0eSAnton Altaparmakov * LCN_ENOENT There is no such vcn in the attribute.
9851da177e4SLinus Torvalds *
9861da177e4SLinus Torvalds * Locking: - The caller must have locked the runlist (for reading or writing).
987c0c1cc0eSAnton Altaparmakov * - This function does not touch the lock, nor does it modify the
988c0c1cc0eSAnton Altaparmakov * runlist.
9891da177e4SLinus Torvalds */
ntfs_rl_vcn_to_lcn(const runlist_element * rl,const VCN vcn)9901da177e4SLinus Torvalds LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
9911da177e4SLinus Torvalds {
9921da177e4SLinus Torvalds int i;
9931da177e4SLinus Torvalds
9941da177e4SLinus Torvalds BUG_ON(vcn < 0);
9951da177e4SLinus Torvalds /*
9961da177e4SLinus Torvalds * If rl is NULL, assume that we have found an unmapped runlist. The
9971da177e4SLinus Torvalds * caller can then attempt to map it and fail appropriately if
9981da177e4SLinus Torvalds * necessary.
9991da177e4SLinus Torvalds */
10001da177e4SLinus Torvalds if (unlikely(!rl))
10011da177e4SLinus Torvalds return LCN_RL_NOT_MAPPED;
10021da177e4SLinus Torvalds
10031da177e4SLinus Torvalds /* Catch out of lower bounds vcn. */
10041da177e4SLinus Torvalds if (unlikely(vcn < rl[0].vcn))
10051da177e4SLinus Torvalds return LCN_ENOENT;
10061da177e4SLinus Torvalds
10071da177e4SLinus Torvalds for (i = 0; likely(rl[i].length); i++) {
10081da177e4SLinus Torvalds if (unlikely(vcn < rl[i+1].vcn)) {
10091da177e4SLinus Torvalds if (likely(rl[i].lcn >= (LCN)0))
10101da177e4SLinus Torvalds return rl[i].lcn + (vcn - rl[i].vcn);
10111da177e4SLinus Torvalds return rl[i].lcn;
10121da177e4SLinus Torvalds }
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds /*
10151da177e4SLinus Torvalds * The terminator element is setup to the correct value, i.e. one of
10161da177e4SLinus Torvalds * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT.
10171da177e4SLinus Torvalds */
10181da177e4SLinus Torvalds if (likely(rl[i].lcn < (LCN)0))
10191da177e4SLinus Torvalds return rl[i].lcn;
10201da177e4SLinus Torvalds /* Just in case... We could replace this with BUG() some day. */
10211da177e4SLinus Torvalds return LCN_ENOENT;
10221da177e4SLinus Torvalds }
10231da177e4SLinus Torvalds
102453d59aadSAnton Altaparmakov #ifdef NTFS_RW
102553d59aadSAnton Altaparmakov
102653d59aadSAnton Altaparmakov /**
102753d59aadSAnton Altaparmakov * ntfs_rl_find_vcn_nolock - find a vcn in a runlist
102853d59aadSAnton Altaparmakov * @rl: runlist to search
102953d59aadSAnton Altaparmakov * @vcn: vcn to find
103053d59aadSAnton Altaparmakov *
103153d59aadSAnton Altaparmakov * Find the virtual cluster number @vcn in the runlist @rl and return the
103253d59aadSAnton Altaparmakov * address of the runlist element containing the @vcn on success.
103353d59aadSAnton Altaparmakov *
103453d59aadSAnton Altaparmakov * Return NULL if @rl is NULL or @vcn is in an unmapped part/out of bounds of
103553d59aadSAnton Altaparmakov * the runlist.
103653d59aadSAnton Altaparmakov *
103753d59aadSAnton Altaparmakov * Locking: The runlist must be locked on entry.
103853d59aadSAnton Altaparmakov */
ntfs_rl_find_vcn_nolock(runlist_element * rl,const VCN vcn)103953d59aadSAnton Altaparmakov runlist_element *ntfs_rl_find_vcn_nolock(runlist_element *rl, const VCN vcn)
104053d59aadSAnton Altaparmakov {
104153d59aadSAnton Altaparmakov BUG_ON(vcn < 0);
104253d59aadSAnton Altaparmakov if (unlikely(!rl || vcn < rl[0].vcn))
104353d59aadSAnton Altaparmakov return NULL;
104453d59aadSAnton Altaparmakov while (likely(rl->length)) {
104553d59aadSAnton Altaparmakov if (unlikely(vcn < rl[1].vcn)) {
104653d59aadSAnton Altaparmakov if (likely(rl->lcn >= LCN_HOLE))
104753d59aadSAnton Altaparmakov return rl;
104853d59aadSAnton Altaparmakov return NULL;
104953d59aadSAnton Altaparmakov }
105053d59aadSAnton Altaparmakov rl++;
105153d59aadSAnton Altaparmakov }
105253d59aadSAnton Altaparmakov if (likely(rl->lcn == LCN_ENOENT))
105353d59aadSAnton Altaparmakov return rl;
105453d59aadSAnton Altaparmakov return NULL;
105553d59aadSAnton Altaparmakov }
105653d59aadSAnton Altaparmakov
10571da177e4SLinus Torvalds /**
10581da177e4SLinus Torvalds * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number
10591da177e4SLinus Torvalds * @n: number for which to get the number of bytes for
10601da177e4SLinus Torvalds *
10611da177e4SLinus Torvalds * Return the number of bytes required to store @n unambiguously as
10621da177e4SLinus Torvalds * a signed number.
10631da177e4SLinus Torvalds *
10641da177e4SLinus Torvalds * This is used in the context of the mapping pairs array to determine how
10651da177e4SLinus Torvalds * many bytes will be needed in the array to store a given logical cluster
10661da177e4SLinus Torvalds * number (lcn) or a specific run length.
10671da177e4SLinus Torvalds *
10681da177e4SLinus Torvalds * Return the number of bytes written. This function cannot fail.
10691da177e4SLinus Torvalds */
ntfs_get_nr_significant_bytes(const s64 n)10701da177e4SLinus Torvalds static inline int ntfs_get_nr_significant_bytes(const s64 n)
10711da177e4SLinus Torvalds {
10721da177e4SLinus Torvalds s64 l = n;
10731da177e4SLinus Torvalds int i;
10741da177e4SLinus Torvalds s8 j;
10751da177e4SLinus Torvalds
10761da177e4SLinus Torvalds i = 0;
10771da177e4SLinus Torvalds do {
10781da177e4SLinus Torvalds l >>= 8;
10791da177e4SLinus Torvalds i++;
10801da177e4SLinus Torvalds } while (l != 0 && l != -1);
10811da177e4SLinus Torvalds j = (n >> 8 * (i - 1)) & 0xff;
10821da177e4SLinus Torvalds /* If the sign bit is wrong, we need an extra byte. */
10831da177e4SLinus Torvalds if ((n < 0 && j >= 0) || (n > 0 && j < 0))
10841da177e4SLinus Torvalds i++;
10851da177e4SLinus Torvalds return i;
10861da177e4SLinus Torvalds }
10871da177e4SLinus Torvalds
10881da177e4SLinus Torvalds /**
10891da177e4SLinus Torvalds * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array
10901da177e4SLinus Torvalds * @vol: ntfs volume (needed for the ntfs version)
10911da177e4SLinus Torvalds * @rl: locked runlist to determine the size of the mapping pairs of
1092fa3be923SAnton Altaparmakov * @first_vcn: first vcn which to include in the mapping pairs array
1093fa3be923SAnton Altaparmakov * @last_vcn: last vcn which to include in the mapping pairs array
10941da177e4SLinus Torvalds *
10951da177e4SLinus Torvalds * Walk the locked runlist @rl and calculate the size in bytes of the mapping
1096fa3be923SAnton Altaparmakov * pairs array corresponding to the runlist @rl, starting at vcn @first_vcn and
1097fa3be923SAnton Altaparmakov * finishing with vcn @last_vcn.
1098fa3be923SAnton Altaparmakov *
1099fa3be923SAnton Altaparmakov * A @last_vcn of -1 means end of runlist and in that case the size of the
1100fa3be923SAnton Altaparmakov * mapping pairs array corresponding to the runlist starting at vcn @first_vcn
1101fa3be923SAnton Altaparmakov * and finishing at the end of the runlist is determined.
1102fa3be923SAnton Altaparmakov *
11031da177e4SLinus Torvalds * This for example allows us to allocate a buffer of the right size when
11041da177e4SLinus Torvalds * building the mapping pairs array.
11051da177e4SLinus Torvalds *
11061da177e4SLinus Torvalds * If @rl is NULL, just return 1 (for the single terminator byte).
11071da177e4SLinus Torvalds *
11081da177e4SLinus Torvalds * Return the calculated size in bytes on success. On error, return -errno.
11091da177e4SLinus Torvalds * The following error codes are defined:
11101da177e4SLinus Torvalds * -EINVAL - Run list contains unmapped elements. Make sure to only pass
11111da177e4SLinus Torvalds * fully mapped runlists to this function.
11121da177e4SLinus Torvalds * -EIO - The runlist is corrupt.
11131da177e4SLinus Torvalds *
11141da177e4SLinus Torvalds * Locking: @rl must be locked on entry (either for reading or writing), it
11151da177e4SLinus Torvalds * remains locked throughout, and is left locked upon return.
11161da177e4SLinus Torvalds */
ntfs_get_size_for_mapping_pairs(const ntfs_volume * vol,const runlist_element * rl,const VCN first_vcn,const VCN last_vcn)11171da177e4SLinus Torvalds int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
1118fa3be923SAnton Altaparmakov const runlist_element *rl, const VCN first_vcn,
1119fa3be923SAnton Altaparmakov const VCN last_vcn)
11201da177e4SLinus Torvalds {
11211da177e4SLinus Torvalds LCN prev_lcn;
11221da177e4SLinus Torvalds int rls;
1123c49c3111SRichard Knutsson bool the_end = false;
11241da177e4SLinus Torvalds
1125fa3be923SAnton Altaparmakov BUG_ON(first_vcn < 0);
1126fa3be923SAnton Altaparmakov BUG_ON(last_vcn < -1);
1127fa3be923SAnton Altaparmakov BUG_ON(last_vcn >= 0 && first_vcn > last_vcn);
11281da177e4SLinus Torvalds if (!rl) {
1129fa3be923SAnton Altaparmakov BUG_ON(first_vcn);
1130fa3be923SAnton Altaparmakov BUG_ON(last_vcn > 0);
11311da177e4SLinus Torvalds return 1;
11321da177e4SLinus Torvalds }
1133fa3be923SAnton Altaparmakov /* Skip to runlist element containing @first_vcn. */
1134fa3be923SAnton Altaparmakov while (rl->length && first_vcn >= rl[1].vcn)
11351da177e4SLinus Torvalds rl++;
1136fa3be923SAnton Altaparmakov if (unlikely((!rl->length && first_vcn > rl->vcn) ||
1137fa3be923SAnton Altaparmakov first_vcn < rl->vcn))
11381da177e4SLinus Torvalds return -EINVAL;
11391da177e4SLinus Torvalds prev_lcn = 0;
11401da177e4SLinus Torvalds /* Always need the termining zero byte. */
11411da177e4SLinus Torvalds rls = 1;
11421da177e4SLinus Torvalds /* Do the first partial run if present. */
1143fa3be923SAnton Altaparmakov if (first_vcn > rl->vcn) {
1144fa3be923SAnton Altaparmakov s64 delta, length = rl->length;
11451da177e4SLinus Torvalds
11461da177e4SLinus Torvalds /* We know rl->length != 0 already. */
1147fa3be923SAnton Altaparmakov if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
11481da177e4SLinus Torvalds goto err_out;
1149fa3be923SAnton Altaparmakov /*
1150fa3be923SAnton Altaparmakov * If @stop_vcn is given and finishes inside this run, cap the
1151fa3be923SAnton Altaparmakov * run length.
1152fa3be923SAnton Altaparmakov */
1153fa3be923SAnton Altaparmakov if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
1154fa3be923SAnton Altaparmakov s64 s1 = last_vcn + 1;
1155fa3be923SAnton Altaparmakov if (unlikely(rl[1].vcn > s1))
1156fa3be923SAnton Altaparmakov length = s1 - rl->vcn;
1157c49c3111SRichard Knutsson the_end = true;
1158fa3be923SAnton Altaparmakov }
1159fa3be923SAnton Altaparmakov delta = first_vcn - rl->vcn;
11601da177e4SLinus Torvalds /* Header byte + length. */
1161fa3be923SAnton Altaparmakov rls += 1 + ntfs_get_nr_significant_bytes(length - delta);
11621da177e4SLinus Torvalds /*
11631da177e4SLinus Torvalds * If the logical cluster number (lcn) denotes a hole and we
11641da177e4SLinus Torvalds * are on NTFS 3.0+, we don't store it at all, i.e. we need
11651da177e4SLinus Torvalds * zero space. On earlier NTFS versions we just store the lcn.
11661da177e4SLinus Torvalds * Note: this assumes that on NTFS 1.2-, holes are stored with
11671da177e4SLinus Torvalds * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
11681da177e4SLinus Torvalds */
1169fa3be923SAnton Altaparmakov if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
11701da177e4SLinus Torvalds prev_lcn = rl->lcn;
1171fa3be923SAnton Altaparmakov if (likely(rl->lcn >= 0))
11721da177e4SLinus Torvalds prev_lcn += delta;
11731da177e4SLinus Torvalds /* Change in lcn. */
11741da177e4SLinus Torvalds rls += ntfs_get_nr_significant_bytes(prev_lcn);
11751da177e4SLinus Torvalds }
11761da177e4SLinus Torvalds /* Go to next runlist element. */
11771da177e4SLinus Torvalds rl++;
11781da177e4SLinus Torvalds }
11791da177e4SLinus Torvalds /* Do the full runs. */
1180fa3be923SAnton Altaparmakov for (; rl->length && !the_end; rl++) {
1181fa3be923SAnton Altaparmakov s64 length = rl->length;
1182fa3be923SAnton Altaparmakov
1183fa3be923SAnton Altaparmakov if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
11841da177e4SLinus Torvalds goto err_out;
1185fa3be923SAnton Altaparmakov /*
1186fa3be923SAnton Altaparmakov * If @stop_vcn is given and finishes inside this run, cap the
1187fa3be923SAnton Altaparmakov * run length.
1188fa3be923SAnton Altaparmakov */
1189fa3be923SAnton Altaparmakov if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
1190fa3be923SAnton Altaparmakov s64 s1 = last_vcn + 1;
1191fa3be923SAnton Altaparmakov if (unlikely(rl[1].vcn > s1))
1192fa3be923SAnton Altaparmakov length = s1 - rl->vcn;
1193c49c3111SRichard Knutsson the_end = true;
1194fa3be923SAnton Altaparmakov }
11951da177e4SLinus Torvalds /* Header byte + length. */
1196fa3be923SAnton Altaparmakov rls += 1 + ntfs_get_nr_significant_bytes(length);
11971da177e4SLinus Torvalds /*
11981da177e4SLinus Torvalds * If the logical cluster number (lcn) denotes a hole and we
11991da177e4SLinus Torvalds * are on NTFS 3.0+, we don't store it at all, i.e. we need
12001da177e4SLinus Torvalds * zero space. On earlier NTFS versions we just store the lcn.
12011da177e4SLinus Torvalds * Note: this assumes that on NTFS 1.2-, holes are stored with
12021da177e4SLinus Torvalds * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
12031da177e4SLinus Torvalds */
1204fa3be923SAnton Altaparmakov if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
12051da177e4SLinus Torvalds /* Change in lcn. */
12061da177e4SLinus Torvalds rls += ntfs_get_nr_significant_bytes(rl->lcn -
12071da177e4SLinus Torvalds prev_lcn);
12081da177e4SLinus Torvalds prev_lcn = rl->lcn;
12091da177e4SLinus Torvalds }
12101da177e4SLinus Torvalds }
12111da177e4SLinus Torvalds return rls;
12121da177e4SLinus Torvalds err_out:
12131da177e4SLinus Torvalds if (rl->lcn == LCN_RL_NOT_MAPPED)
12141da177e4SLinus Torvalds rls = -EINVAL;
12151da177e4SLinus Torvalds else
12161da177e4SLinus Torvalds rls = -EIO;
12171da177e4SLinus Torvalds return rls;
12181da177e4SLinus Torvalds }
12191da177e4SLinus Torvalds
12201da177e4SLinus Torvalds /**
12211da177e4SLinus Torvalds * ntfs_write_significant_bytes - write the significant bytes of a number
12221da177e4SLinus Torvalds * @dst: destination buffer to write to
12231da177e4SLinus Torvalds * @dst_max: pointer to last byte of destination buffer for bounds checking
12241da177e4SLinus Torvalds * @n: number whose significant bytes to write
12251da177e4SLinus Torvalds *
12261da177e4SLinus Torvalds * Store in @dst, the minimum bytes of the number @n which are required to
12271da177e4SLinus Torvalds * identify @n unambiguously as a signed number, taking care not to exceed
12281da177e4SLinus Torvalds * @dest_max, the maximum position within @dst to which we are allowed to
12291da177e4SLinus Torvalds * write.
12301da177e4SLinus Torvalds *
12311da177e4SLinus Torvalds * This is used when building the mapping pairs array of a runlist to compress
123225985edcSLucas De Marchi * a given logical cluster number (lcn) or a specific run length to the minimum
12331da177e4SLinus Torvalds * size possible.
12341da177e4SLinus Torvalds *
12351da177e4SLinus Torvalds * Return the number of bytes written on success. On error, i.e. the
12361da177e4SLinus Torvalds * destination buffer @dst is too small, return -ENOSPC.
12371da177e4SLinus Torvalds */
ntfs_write_significant_bytes(s8 * dst,const s8 * dst_max,const s64 n)12381da177e4SLinus Torvalds static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
12391da177e4SLinus Torvalds const s64 n)
12401da177e4SLinus Torvalds {
12411da177e4SLinus Torvalds s64 l = n;
12421da177e4SLinus Torvalds int i;
12431da177e4SLinus Torvalds s8 j;
12441da177e4SLinus Torvalds
12451da177e4SLinus Torvalds i = 0;
12461da177e4SLinus Torvalds do {
1247fa3be923SAnton Altaparmakov if (unlikely(dst > dst_max))
12481da177e4SLinus Torvalds goto err_out;
12491da177e4SLinus Torvalds *dst++ = l & 0xffll;
12501da177e4SLinus Torvalds l >>= 8;
12511da177e4SLinus Torvalds i++;
12521da177e4SLinus Torvalds } while (l != 0 && l != -1);
12531da177e4SLinus Torvalds j = (n >> 8 * (i - 1)) & 0xff;
12541da177e4SLinus Torvalds /* If the sign bit is wrong, we need an extra byte. */
12551da177e4SLinus Torvalds if (n < 0 && j >= 0) {
1256fa3be923SAnton Altaparmakov if (unlikely(dst > dst_max))
12571da177e4SLinus Torvalds goto err_out;
12581da177e4SLinus Torvalds i++;
12591da177e4SLinus Torvalds *dst = (s8)-1;
12601da177e4SLinus Torvalds } else if (n > 0 && j < 0) {
1261fa3be923SAnton Altaparmakov if (unlikely(dst > dst_max))
12621da177e4SLinus Torvalds goto err_out;
12631da177e4SLinus Torvalds i++;
12641da177e4SLinus Torvalds *dst = (s8)0;
12651da177e4SLinus Torvalds }
12661da177e4SLinus Torvalds return i;
12671da177e4SLinus Torvalds err_out:
12681da177e4SLinus Torvalds return -ENOSPC;
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds
12711da177e4SLinus Torvalds /**
12721da177e4SLinus Torvalds * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist
12731da177e4SLinus Torvalds * @vol: ntfs volume (needed for the ntfs version)
12741da177e4SLinus Torvalds * @dst: destination buffer to which to write the mapping pairs array
12751da177e4SLinus Torvalds * @dst_len: size of destination buffer @dst in bytes
12761da177e4SLinus Torvalds * @rl: locked runlist for which to build the mapping pairs array
1277fa3be923SAnton Altaparmakov * @first_vcn: first vcn which to include in the mapping pairs array
1278fa3be923SAnton Altaparmakov * @last_vcn: last vcn which to include in the mapping pairs array
12791da177e4SLinus Torvalds * @stop_vcn: first vcn outside destination buffer on success or -ENOSPC
12801da177e4SLinus Torvalds *
12811da177e4SLinus Torvalds * Create the mapping pairs array from the locked runlist @rl, starting at vcn
1282fa3be923SAnton Altaparmakov * @first_vcn and finishing with vcn @last_vcn and save the array in @dst.
1283fa3be923SAnton Altaparmakov * @dst_len is the size of @dst in bytes and it should be at least equal to the
1284fa3be923SAnton Altaparmakov * value obtained by calling ntfs_get_size_for_mapping_pairs().
1285fa3be923SAnton Altaparmakov *
1286fa3be923SAnton Altaparmakov * A @last_vcn of -1 means end of runlist and in that case the mapping pairs
1287fa3be923SAnton Altaparmakov * array corresponding to the runlist starting at vcn @first_vcn and finishing
1288fa3be923SAnton Altaparmakov * at the end of the runlist is created.
12891da177e4SLinus Torvalds *
12901da177e4SLinus Torvalds * If @rl is NULL, just write a single terminator byte to @dst.
12911da177e4SLinus Torvalds *
12921da177e4SLinus Torvalds * On success or -ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to
12931da177e4SLinus Torvalds * the first vcn outside the destination buffer. Note that on error, @dst has
12941da177e4SLinus Torvalds * been filled with all the mapping pairs that will fit, thus it can be treated
12951da177e4SLinus Torvalds * as partial success, in that a new attribute extent needs to be created or
12961da177e4SLinus Torvalds * the next extent has to be used and the mapping pairs build has to be
1297fa3be923SAnton Altaparmakov * continued with @first_vcn set to *@stop_vcn.
12981da177e4SLinus Torvalds *
12991da177e4SLinus Torvalds * Return 0 on success and -errno on error. The following error codes are
13001da177e4SLinus Torvalds * defined:
13011da177e4SLinus Torvalds * -EINVAL - Run list contains unmapped elements. Make sure to only pass
13021da177e4SLinus Torvalds * fully mapped runlists to this function.
13031da177e4SLinus Torvalds * -EIO - The runlist is corrupt.
13041da177e4SLinus Torvalds * -ENOSPC - The destination buffer is too small.
13051da177e4SLinus Torvalds *
13061da177e4SLinus Torvalds * Locking: @rl must be locked on entry (either for reading or writing), it
13071da177e4SLinus Torvalds * remains locked throughout, and is left locked upon return.
13081da177e4SLinus Torvalds */
ntfs_mapping_pairs_build(const ntfs_volume * vol,s8 * dst,const int dst_len,const runlist_element * rl,const VCN first_vcn,const VCN last_vcn,VCN * const stop_vcn)13091da177e4SLinus Torvalds int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
13101da177e4SLinus Torvalds const int dst_len, const runlist_element *rl,
1311fa3be923SAnton Altaparmakov const VCN first_vcn, const VCN last_vcn, VCN *const stop_vcn)
13121da177e4SLinus Torvalds {
13131da177e4SLinus Torvalds LCN prev_lcn;
13141da177e4SLinus Torvalds s8 *dst_max, *dst_next;
13151da177e4SLinus Torvalds int err = -ENOSPC;
1316c49c3111SRichard Knutsson bool the_end = false;
13171da177e4SLinus Torvalds s8 len_len, lcn_len;
13181da177e4SLinus Torvalds
1319fa3be923SAnton Altaparmakov BUG_ON(first_vcn < 0);
1320fa3be923SAnton Altaparmakov BUG_ON(last_vcn < -1);
1321fa3be923SAnton Altaparmakov BUG_ON(last_vcn >= 0 && first_vcn > last_vcn);
13221da177e4SLinus Torvalds BUG_ON(dst_len < 1);
13231da177e4SLinus Torvalds if (!rl) {
1324fa3be923SAnton Altaparmakov BUG_ON(first_vcn);
1325fa3be923SAnton Altaparmakov BUG_ON(last_vcn > 0);
13261da177e4SLinus Torvalds if (stop_vcn)
13271da177e4SLinus Torvalds *stop_vcn = 0;
13281da177e4SLinus Torvalds /* Terminator byte. */
13291da177e4SLinus Torvalds *dst = 0;
13301da177e4SLinus Torvalds return 0;
13311da177e4SLinus Torvalds }
1332fa3be923SAnton Altaparmakov /* Skip to runlist element containing @first_vcn. */
1333fa3be923SAnton Altaparmakov while (rl->length && first_vcn >= rl[1].vcn)
13341da177e4SLinus Torvalds rl++;
1335fa3be923SAnton Altaparmakov if (unlikely((!rl->length && first_vcn > rl->vcn) ||
1336fa3be923SAnton Altaparmakov first_vcn < rl->vcn))
13371da177e4SLinus Torvalds return -EINVAL;
13381da177e4SLinus Torvalds /*
13391da177e4SLinus Torvalds * @dst_max is used for bounds checking in
13401da177e4SLinus Torvalds * ntfs_write_significant_bytes().
13411da177e4SLinus Torvalds */
13421da177e4SLinus Torvalds dst_max = dst + dst_len - 1;
13431da177e4SLinus Torvalds prev_lcn = 0;
13441da177e4SLinus Torvalds /* Do the first partial run if present. */
1345fa3be923SAnton Altaparmakov if (first_vcn > rl->vcn) {
1346fa3be923SAnton Altaparmakov s64 delta, length = rl->length;
13471da177e4SLinus Torvalds
13481da177e4SLinus Torvalds /* We know rl->length != 0 already. */
1349fa3be923SAnton Altaparmakov if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
13501da177e4SLinus Torvalds goto err_out;
1351fa3be923SAnton Altaparmakov /*
1352fa3be923SAnton Altaparmakov * If @stop_vcn is given and finishes inside this run, cap the
1353fa3be923SAnton Altaparmakov * run length.
1354fa3be923SAnton Altaparmakov */
1355fa3be923SAnton Altaparmakov if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
1356fa3be923SAnton Altaparmakov s64 s1 = last_vcn + 1;
1357fa3be923SAnton Altaparmakov if (unlikely(rl[1].vcn > s1))
1358fa3be923SAnton Altaparmakov length = s1 - rl->vcn;
1359c49c3111SRichard Knutsson the_end = true;
1360fa3be923SAnton Altaparmakov }
1361fa3be923SAnton Altaparmakov delta = first_vcn - rl->vcn;
13621da177e4SLinus Torvalds /* Write length. */
13631da177e4SLinus Torvalds len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
1364fa3be923SAnton Altaparmakov length - delta);
1365fa3be923SAnton Altaparmakov if (unlikely(len_len < 0))
13661da177e4SLinus Torvalds goto size_err;
13671da177e4SLinus Torvalds /*
13681da177e4SLinus Torvalds * If the logical cluster number (lcn) denotes a hole and we
13691da177e4SLinus Torvalds * are on NTFS 3.0+, we don't store it at all, i.e. we need
13701da177e4SLinus Torvalds * zero space. On earlier NTFS versions we just write the lcn
13711da177e4SLinus Torvalds * change. FIXME: Do we need to write the lcn change or just
13721da177e4SLinus Torvalds * the lcn in that case? Not sure as I have never seen this
13731da177e4SLinus Torvalds * case on NT4. - We assume that we just need to write the lcn
13741da177e4SLinus Torvalds * change until someone tells us otherwise... (AIA)
13751da177e4SLinus Torvalds */
1376fa3be923SAnton Altaparmakov if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
13771da177e4SLinus Torvalds prev_lcn = rl->lcn;
1378fa3be923SAnton Altaparmakov if (likely(rl->lcn >= 0))
13791da177e4SLinus Torvalds prev_lcn += delta;
13801da177e4SLinus Torvalds /* Write change in lcn. */
13811da177e4SLinus Torvalds lcn_len = ntfs_write_significant_bytes(dst + 1 +
13821da177e4SLinus Torvalds len_len, dst_max, prev_lcn);
1383fa3be923SAnton Altaparmakov if (unlikely(lcn_len < 0))
13841da177e4SLinus Torvalds goto size_err;
13851da177e4SLinus Torvalds } else
13861da177e4SLinus Torvalds lcn_len = 0;
13871da177e4SLinus Torvalds dst_next = dst + len_len + lcn_len + 1;
1388fa3be923SAnton Altaparmakov if (unlikely(dst_next > dst_max))
13891da177e4SLinus Torvalds goto size_err;
13901da177e4SLinus Torvalds /* Update header byte. */
13911da177e4SLinus Torvalds *dst = lcn_len << 4 | len_len;
13921da177e4SLinus Torvalds /* Position at next mapping pairs array element. */
13931da177e4SLinus Torvalds dst = dst_next;
13941da177e4SLinus Torvalds /* Go to next runlist element. */
13951da177e4SLinus Torvalds rl++;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds /* Do the full runs. */
1398fa3be923SAnton Altaparmakov for (; rl->length && !the_end; rl++) {
1399fa3be923SAnton Altaparmakov s64 length = rl->length;
1400fa3be923SAnton Altaparmakov
1401fa3be923SAnton Altaparmakov if (unlikely(length < 0 || rl->lcn < LCN_HOLE))
14021da177e4SLinus Torvalds goto err_out;
1403fa3be923SAnton Altaparmakov /*
1404fa3be923SAnton Altaparmakov * If @stop_vcn is given and finishes inside this run, cap the
1405fa3be923SAnton Altaparmakov * run length.
1406fa3be923SAnton Altaparmakov */
1407fa3be923SAnton Altaparmakov if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) {
1408fa3be923SAnton Altaparmakov s64 s1 = last_vcn + 1;
1409fa3be923SAnton Altaparmakov if (unlikely(rl[1].vcn > s1))
1410fa3be923SAnton Altaparmakov length = s1 - rl->vcn;
1411c49c3111SRichard Knutsson the_end = true;
1412fa3be923SAnton Altaparmakov }
14131da177e4SLinus Torvalds /* Write length. */
14141da177e4SLinus Torvalds len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
1415fa3be923SAnton Altaparmakov length);
1416fa3be923SAnton Altaparmakov if (unlikely(len_len < 0))
14171da177e4SLinus Torvalds goto size_err;
14181da177e4SLinus Torvalds /*
14191da177e4SLinus Torvalds * If the logical cluster number (lcn) denotes a hole and we
14201da177e4SLinus Torvalds * are on NTFS 3.0+, we don't store it at all, i.e. we need
14211da177e4SLinus Torvalds * zero space. On earlier NTFS versions we just write the lcn
14221da177e4SLinus Torvalds * change. FIXME: Do we need to write the lcn change or just
14231da177e4SLinus Torvalds * the lcn in that case? Not sure as I have never seen this
14241da177e4SLinus Torvalds * case on NT4. - We assume that we just need to write the lcn
14251da177e4SLinus Torvalds * change until someone tells us otherwise... (AIA)
14261da177e4SLinus Torvalds */
1427fa3be923SAnton Altaparmakov if (likely(rl->lcn >= 0 || vol->major_ver < 3)) {
14281da177e4SLinus Torvalds /* Write change in lcn. */
14291da177e4SLinus Torvalds lcn_len = ntfs_write_significant_bytes(dst + 1 +
14301da177e4SLinus Torvalds len_len, dst_max, rl->lcn - prev_lcn);
1431fa3be923SAnton Altaparmakov if (unlikely(lcn_len < 0))
14321da177e4SLinus Torvalds goto size_err;
14331da177e4SLinus Torvalds prev_lcn = rl->lcn;
14341da177e4SLinus Torvalds } else
14351da177e4SLinus Torvalds lcn_len = 0;
14361da177e4SLinus Torvalds dst_next = dst + len_len + lcn_len + 1;
1437fa3be923SAnton Altaparmakov if (unlikely(dst_next > dst_max))
14381da177e4SLinus Torvalds goto size_err;
14391da177e4SLinus Torvalds /* Update header byte. */
14401da177e4SLinus Torvalds *dst = lcn_len << 4 | len_len;
14411da177e4SLinus Torvalds /* Position at next mapping pairs array element. */
14421da177e4SLinus Torvalds dst = dst_next;
14431da177e4SLinus Torvalds }
14441da177e4SLinus Torvalds /* Success. */
14451da177e4SLinus Torvalds err = 0;
14461da177e4SLinus Torvalds size_err:
14471da177e4SLinus Torvalds /* Set stop vcn. */
14481da177e4SLinus Torvalds if (stop_vcn)
14491da177e4SLinus Torvalds *stop_vcn = rl->vcn;
14501da177e4SLinus Torvalds /* Add terminator byte. */
14511da177e4SLinus Torvalds *dst = 0;
14521da177e4SLinus Torvalds return err;
14531da177e4SLinus Torvalds err_out:
14541da177e4SLinus Torvalds if (rl->lcn == LCN_RL_NOT_MAPPED)
14551da177e4SLinus Torvalds err = -EINVAL;
14561da177e4SLinus Torvalds else
14571da177e4SLinus Torvalds err = -EIO;
14581da177e4SLinus Torvalds return err;
14591da177e4SLinus Torvalds }
14601da177e4SLinus Torvalds
14611da177e4SLinus Torvalds /**
14621da177e4SLinus Torvalds * ntfs_rl_truncate_nolock - truncate a runlist starting at a specified vcn
14633ffc5a44SAnton Altaparmakov * @vol: ntfs volume (needed for error output)
14641da177e4SLinus Torvalds * @runlist: runlist to truncate
14651da177e4SLinus Torvalds * @new_length: the new length of the runlist in VCNs
14661da177e4SLinus Torvalds *
14671da177e4SLinus Torvalds * Truncate the runlist described by @runlist as well as the memory buffer
14681da177e4SLinus Torvalds * holding the runlist elements to a length of @new_length VCNs.
14691da177e4SLinus Torvalds *
14701da177e4SLinus Torvalds * If @new_length lies within the runlist, the runlist elements with VCNs of
14713ffc5a44SAnton Altaparmakov * @new_length and above are discarded. As a special case if @new_length is
14723ffc5a44SAnton Altaparmakov * zero, the runlist is discarded and set to NULL.
14731da177e4SLinus Torvalds *
14741da177e4SLinus Torvalds * If @new_length lies beyond the runlist, a sparse runlist element is added to
14751da177e4SLinus Torvalds * the end of the runlist @runlist or if the last runlist element is a sparse
14761da177e4SLinus Torvalds * one already, this is extended.
14771da177e4SLinus Torvalds *
14783ffc5a44SAnton Altaparmakov * Note, no checking is done for unmapped runlist elements. It is assumed that
14793ffc5a44SAnton Altaparmakov * the caller has mapped any elements that need to be mapped already.
14803ffc5a44SAnton Altaparmakov *
14811da177e4SLinus Torvalds * Return 0 on success and -errno on error.
14821da177e4SLinus Torvalds *
14831da177e4SLinus Torvalds * Locking: The caller must hold @runlist->lock for writing.
14841da177e4SLinus Torvalds */
ntfs_rl_truncate_nolock(const ntfs_volume * vol,runlist * const runlist,const s64 new_length)14851da177e4SLinus Torvalds int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist,
14861da177e4SLinus Torvalds const s64 new_length)
14871da177e4SLinus Torvalds {
14881da177e4SLinus Torvalds runlist_element *rl;
14891da177e4SLinus Torvalds int old_size;
14901da177e4SLinus Torvalds
14911da177e4SLinus Torvalds ntfs_debug("Entering for new_length 0x%llx.", (long long)new_length);
14921da177e4SLinus Torvalds BUG_ON(!runlist);
14931da177e4SLinus Torvalds BUG_ON(new_length < 0);
14941da177e4SLinus Torvalds rl = runlist->rl;
14953ffc5a44SAnton Altaparmakov if (!new_length) {
14963ffc5a44SAnton Altaparmakov ntfs_debug("Freeing runlist.");
14973ffc5a44SAnton Altaparmakov runlist->rl = NULL;
14983ffc5a44SAnton Altaparmakov if (rl)
14993ffc5a44SAnton Altaparmakov ntfs_free(rl);
15003ffc5a44SAnton Altaparmakov return 0;
15013ffc5a44SAnton Altaparmakov }
15021da177e4SLinus Torvalds if (unlikely(!rl)) {
15031da177e4SLinus Torvalds /*
15041da177e4SLinus Torvalds * Create a runlist consisting of a sparse runlist element of
15051da177e4SLinus Torvalds * length @new_length followed by a terminator runlist element.
15061da177e4SLinus Torvalds */
15071da177e4SLinus Torvalds rl = ntfs_malloc_nofs(PAGE_SIZE);
15081da177e4SLinus Torvalds if (unlikely(!rl)) {
15091da177e4SLinus Torvalds ntfs_error(vol->sb, "Not enough memory to allocate "
15101da177e4SLinus Torvalds "runlist element buffer.");
15111da177e4SLinus Torvalds return -ENOMEM;
15121da177e4SLinus Torvalds }
15131da177e4SLinus Torvalds runlist->rl = rl;
15141da177e4SLinus Torvalds rl[1].length = rl->vcn = 0;
15151da177e4SLinus Torvalds rl->lcn = LCN_HOLE;
15161da177e4SLinus Torvalds rl[1].vcn = rl->length = new_length;
15171da177e4SLinus Torvalds rl[1].lcn = LCN_ENOENT;
15181da177e4SLinus Torvalds return 0;
15191da177e4SLinus Torvalds }
15201da177e4SLinus Torvalds BUG_ON(new_length < rl->vcn);
15211da177e4SLinus Torvalds /* Find @new_length in the runlist. */
15221da177e4SLinus Torvalds while (likely(rl->length && new_length >= rl[1].vcn))
15231da177e4SLinus Torvalds rl++;
15241da177e4SLinus Torvalds /*
15251da177e4SLinus Torvalds * If not at the end of the runlist we need to shrink it.
15261da177e4SLinus Torvalds * If at the end of the runlist we need to expand it.
15271da177e4SLinus Torvalds */
15281da177e4SLinus Torvalds if (rl->length) {
15291da177e4SLinus Torvalds runlist_element *trl;
1530c49c3111SRichard Knutsson bool is_end;
15311da177e4SLinus Torvalds
15321da177e4SLinus Torvalds ntfs_debug("Shrinking runlist.");
15331da177e4SLinus Torvalds /* Determine the runlist size. */
15341da177e4SLinus Torvalds trl = rl + 1;
15351da177e4SLinus Torvalds while (likely(trl->length))
15361da177e4SLinus Torvalds trl++;
15371da177e4SLinus Torvalds old_size = trl - runlist->rl + 1;
15381da177e4SLinus Torvalds /* Truncate the run. */
15391da177e4SLinus Torvalds rl->length = new_length - rl->vcn;
15401da177e4SLinus Torvalds /*
15411da177e4SLinus Torvalds * If a run was partially truncated, make the following runlist
15421da177e4SLinus Torvalds * element a terminator.
15431da177e4SLinus Torvalds */
1544c49c3111SRichard Knutsson is_end = false;
15451da177e4SLinus Torvalds if (rl->length) {
15461da177e4SLinus Torvalds rl++;
15471da177e4SLinus Torvalds if (!rl->length)
1548c49c3111SRichard Knutsson is_end = true;
15491da177e4SLinus Torvalds rl->vcn = new_length;
15501da177e4SLinus Torvalds rl->length = 0;
15511da177e4SLinus Torvalds }
15521da177e4SLinus Torvalds rl->lcn = LCN_ENOENT;
15531da177e4SLinus Torvalds /* Reallocate memory if necessary. */
15541da177e4SLinus Torvalds if (!is_end) {
15551da177e4SLinus Torvalds int new_size = rl - runlist->rl + 1;
15561da177e4SLinus Torvalds rl = ntfs_rl_realloc(runlist->rl, old_size, new_size);
15571da177e4SLinus Torvalds if (IS_ERR(rl))
15581da177e4SLinus Torvalds ntfs_warning(vol->sb, "Failed to shrink "
15591da177e4SLinus Torvalds "runlist buffer. This just "
15601da177e4SLinus Torvalds "wastes a bit of memory "
15611da177e4SLinus Torvalds "temporarily so we ignore it "
15621da177e4SLinus Torvalds "and return success.");
15631da177e4SLinus Torvalds else
15641da177e4SLinus Torvalds runlist->rl = rl;
15651da177e4SLinus Torvalds }
15661da177e4SLinus Torvalds } else if (likely(/* !rl->length && */ new_length > rl->vcn)) {
15671da177e4SLinus Torvalds ntfs_debug("Expanding runlist.");
15681da177e4SLinus Torvalds /*
15691da177e4SLinus Torvalds * If there is a previous runlist element and it is a sparse
15701da177e4SLinus Torvalds * one, extend it. Otherwise need to add a new, sparse runlist
15711da177e4SLinus Torvalds * element.
15721da177e4SLinus Torvalds */
15731da177e4SLinus Torvalds if ((rl > runlist->rl) && ((rl - 1)->lcn == LCN_HOLE))
15741da177e4SLinus Torvalds (rl - 1)->length = new_length - (rl - 1)->vcn;
15751da177e4SLinus Torvalds else {
15761da177e4SLinus Torvalds /* Determine the runlist size. */
15771da177e4SLinus Torvalds old_size = rl - runlist->rl + 1;
15781da177e4SLinus Torvalds /* Reallocate memory if necessary. */
15791da177e4SLinus Torvalds rl = ntfs_rl_realloc(runlist->rl, old_size,
15801da177e4SLinus Torvalds old_size + 1);
15811da177e4SLinus Torvalds if (IS_ERR(rl)) {
15821da177e4SLinus Torvalds ntfs_error(vol->sb, "Failed to expand runlist "
15831da177e4SLinus Torvalds "buffer, aborting.");
15841da177e4SLinus Torvalds return PTR_ERR(rl);
15851da177e4SLinus Torvalds }
15861da177e4SLinus Torvalds runlist->rl = rl;
15871da177e4SLinus Torvalds /*
15881da177e4SLinus Torvalds * Set @rl to the same runlist element in the new
15891da177e4SLinus Torvalds * runlist as before in the old runlist.
15901da177e4SLinus Torvalds */
15911da177e4SLinus Torvalds rl += old_size - 1;
15921da177e4SLinus Torvalds /* Add a new, sparse runlist element. */
15931da177e4SLinus Torvalds rl->lcn = LCN_HOLE;
15941da177e4SLinus Torvalds rl->length = new_length - rl->vcn;
15951da177e4SLinus Torvalds /* Add a new terminator runlist element. */
15961da177e4SLinus Torvalds rl++;
15971da177e4SLinus Torvalds rl->length = 0;
15981da177e4SLinus Torvalds }
15991da177e4SLinus Torvalds rl->vcn = new_length;
16001da177e4SLinus Torvalds rl->lcn = LCN_ENOENT;
16011da177e4SLinus Torvalds } else /* if (unlikely(!rl->length && new_length == rl->vcn)) */ {
16021da177e4SLinus Torvalds /* Runlist already has same size as requested. */
16031da177e4SLinus Torvalds rl->lcn = LCN_ENOENT;
16041da177e4SLinus Torvalds }
16051da177e4SLinus Torvalds ntfs_debug("Done.");
16061da177e4SLinus Torvalds return 0;
16071da177e4SLinus Torvalds }
160853d59aadSAnton Altaparmakov
16096e48321aSAnton Altaparmakov /**
16106e48321aSAnton Altaparmakov * ntfs_rl_punch_nolock - punch a hole into a runlist
16116e48321aSAnton Altaparmakov * @vol: ntfs volume (needed for error output)
16126e48321aSAnton Altaparmakov * @runlist: runlist to punch a hole into
16136e48321aSAnton Altaparmakov * @start: starting VCN of the hole to be created
16146e48321aSAnton Altaparmakov * @length: size of the hole to be created in units of clusters
16156e48321aSAnton Altaparmakov *
16166e48321aSAnton Altaparmakov * Punch a hole into the runlist @runlist starting at VCN @start and of size
16176e48321aSAnton Altaparmakov * @length clusters.
16186e48321aSAnton Altaparmakov *
16196e48321aSAnton Altaparmakov * Return 0 on success and -errno on error, in which case @runlist has not been
16206e48321aSAnton Altaparmakov * modified.
16216e48321aSAnton Altaparmakov *
16226e48321aSAnton Altaparmakov * If @start and/or @start + @length are outside the runlist return error code
16236e48321aSAnton Altaparmakov * -ENOENT.
16246e48321aSAnton Altaparmakov *
16256e48321aSAnton Altaparmakov * If the runlist contains unmapped or error elements between @start and @start
16266e48321aSAnton Altaparmakov * + @length return error code -EINVAL.
16276e48321aSAnton Altaparmakov *
16286e48321aSAnton Altaparmakov * Locking: The caller must hold @runlist->lock for writing.
16296e48321aSAnton Altaparmakov */
ntfs_rl_punch_nolock(const ntfs_volume * vol,runlist * const runlist,const VCN start,const s64 length)16306e48321aSAnton Altaparmakov int ntfs_rl_punch_nolock(const ntfs_volume *vol, runlist *const runlist,
16316e48321aSAnton Altaparmakov const VCN start, const s64 length)
16326e48321aSAnton Altaparmakov {
16336e48321aSAnton Altaparmakov const VCN end = start + length;
16346e48321aSAnton Altaparmakov s64 delta;
16356e48321aSAnton Altaparmakov runlist_element *rl, *rl_end, *rl_real_end, *trl;
16366e48321aSAnton Altaparmakov int old_size;
1637c49c3111SRichard Knutsson bool lcn_fixup = false;
16386e48321aSAnton Altaparmakov
16396e48321aSAnton Altaparmakov ntfs_debug("Entering for start 0x%llx, length 0x%llx.",
16406e48321aSAnton Altaparmakov (long long)start, (long long)length);
16416e48321aSAnton Altaparmakov BUG_ON(!runlist);
16426e48321aSAnton Altaparmakov BUG_ON(start < 0);
16436e48321aSAnton Altaparmakov BUG_ON(length < 0);
16446e48321aSAnton Altaparmakov BUG_ON(end < 0);
16456e48321aSAnton Altaparmakov rl = runlist->rl;
16466e48321aSAnton Altaparmakov if (unlikely(!rl)) {
16476e48321aSAnton Altaparmakov if (likely(!start && !length))
16486e48321aSAnton Altaparmakov return 0;
16496e48321aSAnton Altaparmakov return -ENOENT;
16506e48321aSAnton Altaparmakov }
16516e48321aSAnton Altaparmakov /* Find @start in the runlist. */
16526e48321aSAnton Altaparmakov while (likely(rl->length && start >= rl[1].vcn))
16536e48321aSAnton Altaparmakov rl++;
16546e48321aSAnton Altaparmakov rl_end = rl;
16556e48321aSAnton Altaparmakov /* Find @end in the runlist. */
16566e48321aSAnton Altaparmakov while (likely(rl_end->length && end >= rl_end[1].vcn)) {
16576e48321aSAnton Altaparmakov /* Verify there are no unmapped or error elements. */
16586e48321aSAnton Altaparmakov if (unlikely(rl_end->lcn < LCN_HOLE))
16596e48321aSAnton Altaparmakov return -EINVAL;
16606e48321aSAnton Altaparmakov rl_end++;
16616e48321aSAnton Altaparmakov }
16626e48321aSAnton Altaparmakov /* Check the last element. */
16636e48321aSAnton Altaparmakov if (unlikely(rl_end->length && rl_end->lcn < LCN_HOLE))
16646e48321aSAnton Altaparmakov return -EINVAL;
16656e48321aSAnton Altaparmakov /* This covers @start being out of bounds, too. */
16666e48321aSAnton Altaparmakov if (!rl_end->length && end > rl_end->vcn)
16676e48321aSAnton Altaparmakov return -ENOENT;
16686e48321aSAnton Altaparmakov if (!length)
16696e48321aSAnton Altaparmakov return 0;
16706e48321aSAnton Altaparmakov if (!rl->length)
16716e48321aSAnton Altaparmakov return -ENOENT;
16726e48321aSAnton Altaparmakov rl_real_end = rl_end;
16736e48321aSAnton Altaparmakov /* Determine the runlist size. */
16746e48321aSAnton Altaparmakov while (likely(rl_real_end->length))
16756e48321aSAnton Altaparmakov rl_real_end++;
16766e48321aSAnton Altaparmakov old_size = rl_real_end - runlist->rl + 1;
16776e48321aSAnton Altaparmakov /* If @start is in a hole simply extend the hole. */
16786e48321aSAnton Altaparmakov if (rl->lcn == LCN_HOLE) {
16796e48321aSAnton Altaparmakov /*
16806e48321aSAnton Altaparmakov * If both @start and @end are in the same sparse run, we are
16816e48321aSAnton Altaparmakov * done.
16826e48321aSAnton Altaparmakov */
16836e48321aSAnton Altaparmakov if (end <= rl[1].vcn) {
16846e48321aSAnton Altaparmakov ntfs_debug("Done (requested hole is already sparse).");
16856e48321aSAnton Altaparmakov return 0;
16866e48321aSAnton Altaparmakov }
16876e48321aSAnton Altaparmakov extend_hole:
16886e48321aSAnton Altaparmakov /* Extend the hole. */
16896e48321aSAnton Altaparmakov rl->length = end - rl->vcn;
16906e48321aSAnton Altaparmakov /* If @end is in a hole, merge it with the current one. */
16916e48321aSAnton Altaparmakov if (rl_end->lcn == LCN_HOLE) {
16926e48321aSAnton Altaparmakov rl_end++;
16936e48321aSAnton Altaparmakov rl->length = rl_end->vcn - rl->vcn;
16946e48321aSAnton Altaparmakov }
16956e48321aSAnton Altaparmakov /* We have done the hole. Now deal with the remaining tail. */
16966e48321aSAnton Altaparmakov rl++;
16976e48321aSAnton Altaparmakov /* Cut out all runlist elements up to @end. */
16986e48321aSAnton Altaparmakov if (rl < rl_end)
16996e48321aSAnton Altaparmakov memmove(rl, rl_end, (rl_real_end - rl_end + 1) *
17006e48321aSAnton Altaparmakov sizeof(*rl));
17016e48321aSAnton Altaparmakov /* Adjust the beginning of the tail if necessary. */
17026e48321aSAnton Altaparmakov if (end > rl->vcn) {
1703bfab36e8SAnton Altaparmakov delta = end - rl->vcn;
17046e48321aSAnton Altaparmakov rl->vcn = end;
17056e48321aSAnton Altaparmakov rl->length -= delta;
17066e48321aSAnton Altaparmakov /* Only adjust the lcn if it is real. */
17076e48321aSAnton Altaparmakov if (rl->lcn >= 0)
17086e48321aSAnton Altaparmakov rl->lcn += delta;
17096e48321aSAnton Altaparmakov }
17106e48321aSAnton Altaparmakov shrink_allocation:
17116e48321aSAnton Altaparmakov /* Reallocate memory if the allocation changed. */
17126e48321aSAnton Altaparmakov if (rl < rl_end) {
17136e48321aSAnton Altaparmakov rl = ntfs_rl_realloc(runlist->rl, old_size,
17146e48321aSAnton Altaparmakov old_size - (rl_end - rl));
17156e48321aSAnton Altaparmakov if (IS_ERR(rl))
17166e48321aSAnton Altaparmakov ntfs_warning(vol->sb, "Failed to shrink "
17176e48321aSAnton Altaparmakov "runlist buffer. This just "
17186e48321aSAnton Altaparmakov "wastes a bit of memory "
17196e48321aSAnton Altaparmakov "temporarily so we ignore it "
17206e48321aSAnton Altaparmakov "and return success.");
17216e48321aSAnton Altaparmakov else
17226e48321aSAnton Altaparmakov runlist->rl = rl;
17236e48321aSAnton Altaparmakov }
17246e48321aSAnton Altaparmakov ntfs_debug("Done (extend hole).");
17256e48321aSAnton Altaparmakov return 0;
17266e48321aSAnton Altaparmakov }
17276e48321aSAnton Altaparmakov /*
17286e48321aSAnton Altaparmakov * If @start is at the beginning of a run things are easier as there is
17296e48321aSAnton Altaparmakov * no need to split the first run.
17306e48321aSAnton Altaparmakov */
17316e48321aSAnton Altaparmakov if (start == rl->vcn) {
17326e48321aSAnton Altaparmakov /*
17336e48321aSAnton Altaparmakov * @start is at the beginning of a run.
17346e48321aSAnton Altaparmakov *
17356e48321aSAnton Altaparmakov * If the previous run is sparse, extend its hole.
17366e48321aSAnton Altaparmakov *
17376e48321aSAnton Altaparmakov * If @end is not in the same run, switch the run to be sparse
17386e48321aSAnton Altaparmakov * and extend the newly created hole.
17396e48321aSAnton Altaparmakov *
17406e48321aSAnton Altaparmakov * Thus both of these cases reduce the problem to the above
17416e48321aSAnton Altaparmakov * case of "@start is in a hole".
17426e48321aSAnton Altaparmakov */
17436e48321aSAnton Altaparmakov if (rl > runlist->rl && (rl - 1)->lcn == LCN_HOLE) {
17446e48321aSAnton Altaparmakov rl--;
17456e48321aSAnton Altaparmakov goto extend_hole;
17466e48321aSAnton Altaparmakov }
17476e48321aSAnton Altaparmakov if (end >= rl[1].vcn) {
17486e48321aSAnton Altaparmakov rl->lcn = LCN_HOLE;
17496e48321aSAnton Altaparmakov goto extend_hole;
17506e48321aSAnton Altaparmakov }
17516e48321aSAnton Altaparmakov /*
17526e48321aSAnton Altaparmakov * The final case is when @end is in the same run as @start.
17536e48321aSAnton Altaparmakov * For this need to split the run into two. One run for the
17546e48321aSAnton Altaparmakov * sparse region between the beginning of the old run, i.e.
17556e48321aSAnton Altaparmakov * @start, and @end and one for the remaining non-sparse
17566e48321aSAnton Altaparmakov * region, i.e. between @end and the end of the old run.
17576e48321aSAnton Altaparmakov */
17586e48321aSAnton Altaparmakov trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 1);
17596e48321aSAnton Altaparmakov if (IS_ERR(trl))
17606e48321aSAnton Altaparmakov goto enomem_out;
17616e48321aSAnton Altaparmakov old_size++;
17626e48321aSAnton Altaparmakov if (runlist->rl != trl) {
17636e48321aSAnton Altaparmakov rl = trl + (rl - runlist->rl);
17646e48321aSAnton Altaparmakov rl_end = trl + (rl_end - runlist->rl);
17656e48321aSAnton Altaparmakov rl_real_end = trl + (rl_real_end - runlist->rl);
17666e48321aSAnton Altaparmakov runlist->rl = trl;
17676e48321aSAnton Altaparmakov }
17686e48321aSAnton Altaparmakov split_end:
17696e48321aSAnton Altaparmakov /* Shift all the runs up by one. */
17706e48321aSAnton Altaparmakov memmove(rl + 1, rl, (rl_real_end - rl + 1) * sizeof(*rl));
17716e48321aSAnton Altaparmakov /* Finally, setup the two split runs. */
17726e48321aSAnton Altaparmakov rl->lcn = LCN_HOLE;
17736e48321aSAnton Altaparmakov rl->length = length;
17746e48321aSAnton Altaparmakov rl++;
17756e48321aSAnton Altaparmakov rl->vcn += length;
17766e48321aSAnton Altaparmakov /* Only adjust the lcn if it is real. */
17776e48321aSAnton Altaparmakov if (rl->lcn >= 0 || lcn_fixup)
17786e48321aSAnton Altaparmakov rl->lcn += length;
17796e48321aSAnton Altaparmakov rl->length -= length;
17806e48321aSAnton Altaparmakov ntfs_debug("Done (split one).");
17816e48321aSAnton Altaparmakov return 0;
17826e48321aSAnton Altaparmakov }
17836e48321aSAnton Altaparmakov /*
17846e48321aSAnton Altaparmakov * @start is neither in a hole nor at the beginning of a run.
17856e48321aSAnton Altaparmakov *
17866e48321aSAnton Altaparmakov * If @end is in a hole, things are easier as simply truncating the run
17876e48321aSAnton Altaparmakov * @start is in to end at @start - 1, deleting all runs after that up
17886e48321aSAnton Altaparmakov * to @end, and finally extending the beginning of the run @end is in
17896e48321aSAnton Altaparmakov * to be @start is all that is needed.
17906e48321aSAnton Altaparmakov */
17916e48321aSAnton Altaparmakov if (rl_end->lcn == LCN_HOLE) {
17926e48321aSAnton Altaparmakov /* Truncate the run containing @start. */
17936e48321aSAnton Altaparmakov rl->length = start - rl->vcn;
17946e48321aSAnton Altaparmakov rl++;
17956e48321aSAnton Altaparmakov /* Cut out all runlist elements up to @end. */
17966e48321aSAnton Altaparmakov if (rl < rl_end)
17976e48321aSAnton Altaparmakov memmove(rl, rl_end, (rl_real_end - rl_end + 1) *
17986e48321aSAnton Altaparmakov sizeof(*rl));
17996e48321aSAnton Altaparmakov /* Extend the beginning of the run @end is in to be @start. */
18006e48321aSAnton Altaparmakov rl->vcn = start;
18016e48321aSAnton Altaparmakov rl->length = rl[1].vcn - start;
18026e48321aSAnton Altaparmakov goto shrink_allocation;
18036e48321aSAnton Altaparmakov }
18046e48321aSAnton Altaparmakov /*
18056e48321aSAnton Altaparmakov * If @end is not in a hole there are still two cases to distinguish.
18066e48321aSAnton Altaparmakov * Either @end is or is not in the same run as @start.
18076e48321aSAnton Altaparmakov *
18086e48321aSAnton Altaparmakov * The second case is easier as it can be reduced to an already solved
18096e48321aSAnton Altaparmakov * problem by truncating the run @start is in to end at @start - 1.
18106e48321aSAnton Altaparmakov * Then, if @end is in the next run need to split the run into a sparse
18116e48321aSAnton Altaparmakov * run followed by a non-sparse run (already covered above) and if @end
18126e48321aSAnton Altaparmakov * is not in the next run switching it to be sparse, again reduces the
18136e48321aSAnton Altaparmakov * problem to the already covered case of "@start is in a hole".
18146e48321aSAnton Altaparmakov */
18156e48321aSAnton Altaparmakov if (end >= rl[1].vcn) {
18166e48321aSAnton Altaparmakov /*
18176e48321aSAnton Altaparmakov * If @end is not in the next run, reduce the problem to the
18186e48321aSAnton Altaparmakov * case of "@start is in a hole".
18196e48321aSAnton Altaparmakov */
18206e48321aSAnton Altaparmakov if (rl[1].length && end >= rl[2].vcn) {
18216e48321aSAnton Altaparmakov /* Truncate the run containing @start. */
18226e48321aSAnton Altaparmakov rl->length = start - rl->vcn;
18236e48321aSAnton Altaparmakov rl++;
18246e48321aSAnton Altaparmakov rl->vcn = start;
18256e48321aSAnton Altaparmakov rl->lcn = LCN_HOLE;
18266e48321aSAnton Altaparmakov goto extend_hole;
18276e48321aSAnton Altaparmakov }
18286e48321aSAnton Altaparmakov trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 1);
18296e48321aSAnton Altaparmakov if (IS_ERR(trl))
18306e48321aSAnton Altaparmakov goto enomem_out;
18316e48321aSAnton Altaparmakov old_size++;
18326e48321aSAnton Altaparmakov if (runlist->rl != trl) {
18336e48321aSAnton Altaparmakov rl = trl + (rl - runlist->rl);
18346e48321aSAnton Altaparmakov rl_end = trl + (rl_end - runlist->rl);
18356e48321aSAnton Altaparmakov rl_real_end = trl + (rl_real_end - runlist->rl);
18366e48321aSAnton Altaparmakov runlist->rl = trl;
18376e48321aSAnton Altaparmakov }
18386e48321aSAnton Altaparmakov /* Truncate the run containing @start. */
18396e48321aSAnton Altaparmakov rl->length = start - rl->vcn;
18406e48321aSAnton Altaparmakov rl++;
18416e48321aSAnton Altaparmakov /*
18426e48321aSAnton Altaparmakov * @end is in the next run, reduce the problem to the case
18436e48321aSAnton Altaparmakov * where "@start is at the beginning of a run and @end is in
18446e48321aSAnton Altaparmakov * the same run as @start".
18456e48321aSAnton Altaparmakov */
18466e48321aSAnton Altaparmakov delta = rl->vcn - start;
18476e48321aSAnton Altaparmakov rl->vcn = start;
18486e48321aSAnton Altaparmakov if (rl->lcn >= 0) {
18496e48321aSAnton Altaparmakov rl->lcn -= delta;
18506e48321aSAnton Altaparmakov /* Need this in case the lcn just became negative. */
1851c49c3111SRichard Knutsson lcn_fixup = true;
18526e48321aSAnton Altaparmakov }
18536e48321aSAnton Altaparmakov rl->length += delta;
18546e48321aSAnton Altaparmakov goto split_end;
18556e48321aSAnton Altaparmakov }
18566e48321aSAnton Altaparmakov /*
18576e48321aSAnton Altaparmakov * The first case from above, i.e. @end is in the same run as @start.
18586e48321aSAnton Altaparmakov * We need to split the run into three. One run for the non-sparse
18596e48321aSAnton Altaparmakov * region between the beginning of the old run and @start, one for the
18606e48321aSAnton Altaparmakov * sparse region between @start and @end, and one for the remaining
18616e48321aSAnton Altaparmakov * non-sparse region, i.e. between @end and the end of the old run.
18626e48321aSAnton Altaparmakov */
18636e48321aSAnton Altaparmakov trl = ntfs_rl_realloc(runlist->rl, old_size, old_size + 2);
18646e48321aSAnton Altaparmakov if (IS_ERR(trl))
18656e48321aSAnton Altaparmakov goto enomem_out;
18666e48321aSAnton Altaparmakov old_size += 2;
18676e48321aSAnton Altaparmakov if (runlist->rl != trl) {
18686e48321aSAnton Altaparmakov rl = trl + (rl - runlist->rl);
18696e48321aSAnton Altaparmakov rl_end = trl + (rl_end - runlist->rl);
18706e48321aSAnton Altaparmakov rl_real_end = trl + (rl_real_end - runlist->rl);
18716e48321aSAnton Altaparmakov runlist->rl = trl;
18726e48321aSAnton Altaparmakov }
18736e48321aSAnton Altaparmakov /* Shift all the runs up by two. */
18746e48321aSAnton Altaparmakov memmove(rl + 2, rl, (rl_real_end - rl + 1) * sizeof(*rl));
18756e48321aSAnton Altaparmakov /* Finally, setup the three split runs. */
18766e48321aSAnton Altaparmakov rl->length = start - rl->vcn;
18776e48321aSAnton Altaparmakov rl++;
18786e48321aSAnton Altaparmakov rl->vcn = start;
18796e48321aSAnton Altaparmakov rl->lcn = LCN_HOLE;
18806e48321aSAnton Altaparmakov rl->length = length;
18816e48321aSAnton Altaparmakov rl++;
18826e48321aSAnton Altaparmakov delta = end - rl->vcn;
18836e48321aSAnton Altaparmakov rl->vcn = end;
18846e48321aSAnton Altaparmakov rl->lcn += delta;
18856e48321aSAnton Altaparmakov rl->length -= delta;
18866e48321aSAnton Altaparmakov ntfs_debug("Done (split both).");
18876e48321aSAnton Altaparmakov return 0;
18886e48321aSAnton Altaparmakov enomem_out:
18896e48321aSAnton Altaparmakov ntfs_error(vol->sb, "Not enough memory to extend runlist buffer.");
18906e48321aSAnton Altaparmakov return -ENOMEM;
18916e48321aSAnton Altaparmakov }
18926e48321aSAnton Altaparmakov
189353d59aadSAnton Altaparmakov #endif /* NTFS_RW */
1894