NTFS: Fix handling of valid but empty mapping pairs array in
[powerpc.git] / fs / ntfs / runlist.c
index 758855b..e4c4716 100644 (file)
@@ -35,7 +35,7 @@ static inline void ntfs_rl_mm(runlist_element *base, int dst, int src,
                int size)
 {
        if (likely((dst != src) && (size > 0)))
-               memmove(base + dst, base + src, size * sizeof (*base));
+               memmove(base + dst, base + src, size * sizeof(*base));
 }
 
 /**
@@ -94,6 +94,51 @@ static inline runlist_element *ntfs_rl_realloc(runlist_element *rl,
        return new_rl;
 }
 
+/**
+ * ntfs_rl_realloc_nofail - Reallocate memory for runlists
+ * @rl:                original runlist
+ * @old_size:  number of runlist elements in the original runlist @rl
+ * @new_size:  number of runlist elements we need space for
+ *
+ * As the runlists grow, more memory will be required.  To prevent the
+ * kernel having to allocate and reallocate large numbers of small bits of
+ * memory, this function returns an entire page of memory.
+ *
+ * This function guarantees that the allocation will succeed.  It will sleep
+ * for as long as it takes to complete the allocation.
+ *
+ * It is up to the caller to serialize access to the runlist @rl.
+ *
+ * N.B.  If the new allocation doesn't require a different number of pages in
+ *       memory, the function will return the original pointer.
+ *
+ * On success, return a pointer to the newly allocated, or recycled, memory.
+ * On error, return -errno. The following error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_realloc_nofail(runlist_element *rl,
+               int old_size, int new_size)
+{
+       runlist_element *new_rl;
+
+       old_size = PAGE_ALIGN(old_size * sizeof(*rl));
+       new_size = PAGE_ALIGN(new_size * sizeof(*rl));
+       if (old_size == new_size)
+               return rl;
+
+       new_rl = ntfs_malloc_nofs_nofail(new_size);
+       BUG_ON(!new_rl);
+
+       if (likely(rl != NULL)) {
+               if (unlikely(old_size > new_size))
+                       old_size = new_size;
+               memcpy(new_rl, rl, old_size);
+               ntfs_free(rl);
+       }
+       return new_rl;
+}
+
 /**
  * ntfs_are_rl_mergeable - test if two runlists can be joined together
  * @dst:       original runlist
@@ -497,6 +542,7 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl,
                        /* Scan to the end of the source runlist. */
                        for (dend = 0; likely(drl[dend].length); dend++)
                                ;
+                       dend++;
                        drl = ntfs_rl_realloc(drl, dend, dend + 1);
                        if (IS_ERR(drl))
                                return drl;
@@ -566,8 +612,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl,
                 ((drl[dins].vcn + drl[dins].length) <=      /* End of hole   */
                  (srl[send - 1].vcn + srl[send - 1].length)));
 
-       /* Or we'll lose an end marker */
-       if (start && finish && (drl[dins].length == 0))
+       /* Or we will lose an end marker. */
+       if (finish && !drl[dins].length)
                ss++;
        if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn))
                finish = FALSE;
@@ -621,11 +667,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl,
                        if (drl[ds].lcn != LCN_RL_NOT_MAPPED) {
                                /* Add an unmapped runlist element. */
                                if (!slots) {
-                                       /* FIXME/TODO: We need to have the
-                                        * extra memory already! (AIA) */
-                                       drl = ntfs_rl_realloc(drl, ds, ds + 2);
-                                       if (!drl)
-                                               goto critical_error;
+                                       drl = ntfs_rl_realloc_nofail(drl, ds,
+                                                       ds + 2);
                                        slots = 2;
                                }
                                ds++;
@@ -640,13 +683,8 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl,
                        drl[ds].length = marker_vcn - drl[ds].vcn;
                        /* Finally add the ENOENT terminator. */
                        ds++;
-                       if (!slots) {
-                               /* FIXME/TODO: We need to have the extra
-                                * memory already! (AIA) */
-                               drl = ntfs_rl_realloc(drl, ds, ds + 1);
-                               if (!drl)
-                                       goto critical_error;
-                       }
+                       if (!slots)
+                               drl = ntfs_rl_realloc_nofail(drl, ds, ds + 1);
                        drl[ds].vcn = marker_vcn;
                        drl[ds].lcn = LCN_ENOENT;
                        drl[ds].length = (s64)0;
@@ -659,11 +697,6 @@ finished:
        ntfs_debug("Merged runlist:");
        ntfs_debug_dump_runlist(drl);
        return drl;
-
-critical_error:
-       /* Critical error! We cannot afford to fail here. */
-       ntfs_error(NULL, "Critical error! Not enough memory.");
-       panic("NTFS: Cannot continue.");
 }
 
 /**
@@ -727,6 +760,9 @@ runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
                ntfs_error(vol->sb, "Corrupt attribute.");
                return ERR_PTR(-EIO);
        }
+       /* If the mapping pairs array is valid but empty, nothing to do. */
+       if (!vcn && !*buf)
+               return old_rl;
        /* Current position in runlist array. */
        rlpos = 0;
        /* Allocate first page and set current runlist size to one page. */