]> git.hungrycats.org Git - linux/commitdiff
NTFS: Add fs/ntfs/runlist.[hc]::ntfs_get_nr_significant_bytes(),
authorAnton Altaparmakov <aia21@cantab.net>
Thu, 30 Sep 2004 12:14:02 +0000 (13:14 +0100)
committerAnton Altaparmakov <aia21@cantab.net>
Thu, 30 Sep 2004 12:14:02 +0000 (13:14 +0100)
      ntfs_get_size_for_mapping_pairs(), ntfs_write_significant_bytes(),
      and ntfs_mapping_pairs_build(), adapted from libntfs.

Signed-off-by: Anton Altaparmakov <aia21@cantab.net>
fs/ntfs/ChangeLog
fs/ntfs/runlist.c
fs/ntfs/runlist.h

index 2b0306ece05bc5f7b5d7aceaf581d33d2c65537b..3518677ab23ef32c739e8fb37fb7361d249efbd7 100644 (file)
@@ -30,6 +30,9 @@ ToDo/Notes:
        - Rename init_runlist() to ntfs_init_runlist(), ntfs_vcn_to_lcn() to
          ntfs_rl_vcn_to_lcn(), decompress_mapping_pairs() to
          ntfs_mapping_pairs_decompress() and adapt all callers.
+       - Add fs/ntfs/runlist.[hc]::ntfs_get_nr_significant_bytes(),
+         ntfs_get_size_for_mapping_pairs(), ntfs_write_significant_bytes(),
+         and ntfs_mapping_pairs_build(), adapted from libntfs.
 
 2.1.19 - Many cleanups, improvements, and a minor bug fix.
 
index 523b5c0261d14ac3927109638e66fc5938aa9ca9..ac44caaae866a8706708461483a8645feac57d06 100644 (file)
@@ -984,3 +984,340 @@ LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
        /* Just in case... We could replace this with BUG() some day. */
        return (LCN)LCN_ENOENT;
 }
+
+/**
+ * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number
+ * @n:         number for which to get the number of bytes for
+ *
+ * Return the number of bytes required to store @n unambiguously as
+ * a signed number.
+ *
+ * This is used in the context of the mapping pairs array to determine how
+ * many bytes will be needed in the array to store a given logical cluster
+ * number (lcn) or a specific run length.
+ *
+ * Return the number of bytes written.  This function cannot fail.
+ */
+static inline int ntfs_get_nr_significant_bytes(const s64 n)
+{
+       s64 l = n;
+       int i;
+       s8 j;
+
+       i = 0;
+       do {
+               l >>= 8;
+               i++;
+       } while (l != 0 && l != -1);
+       j = (n >> 8 * (i - 1)) & 0xff;
+       /* If the sign bit is wrong, we need an extra byte. */
+       if ((n < 0 && j >= 0) || (n > 0 && j < 0))
+               i++;
+       return i;
+}
+
+/**
+ * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array
+ * @vol:       ntfs volume (needed for the ntfs version)
+ * @rl:                locked runlist to determine the size of the mapping pairs of
+ * @start_vcn: vcn at which to start the mapping pairs array
+ *
+ * Walk the locked runlist @rl and calculate the size in bytes of the mapping
+ * pairs array corresponding to the runlist @rl, starting at vcn @start_vcn.
+ * This for example allows us to allocate a buffer of the right size when
+ * building the mapping pairs array.
+ *
+ * If @rl is NULL, just return 1 (for the single terminator byte).
+ *
+ * Return the calculated size in bytes on success.  On error, return -errno.
+ * The following error codes are defined:
+ *     -EINVAL - Run list contains unmapped elements.  Make sure to only pass
+ *               fully mapped runlists to this function.
+ *     -EIO    - The runlist is corrupt.
+ *
+ * Locking: @rl must be locked on entry (either for reading or writing), it
+ *         remains locked throughout, and is left locked upon return.
+ */
+int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
+               const runlist_element *rl, const VCN start_vcn)
+{
+       LCN prev_lcn;
+       int rls;
+
+       BUG_ON(start_vcn < 0);
+       if (!rl) {
+               BUG_ON(start_vcn);
+               return 1;
+       }
+       /* Skip to runlist element containing @start_vcn. */
+       while (rl->length && start_vcn >= rl[1].vcn)
+               rl++;
+       if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn)
+               return -EINVAL;
+       prev_lcn = 0;
+       /* Always need the termining zero byte. */
+       rls = 1;
+       /* Do the first partial run if present. */
+       if (start_vcn > rl->vcn) {
+               s64 delta;
+
+               /* We know rl->length != 0 already. */
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               delta = start_vcn - rl->vcn;
+               /* Header byte + length. */
+               rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta);
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just store the lcn.
+                * Note: this assumes that on NTFS 1.2-, holes are stored with
+                * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       prev_lcn = rl->lcn;
+                       if (rl->lcn >= 0)
+                               prev_lcn += delta;
+                       /* Change in lcn. */
+                       rls += ntfs_get_nr_significant_bytes(prev_lcn);
+               }
+               /* Go to next runlist element. */
+               rl++;
+       }
+       /* Do the full runs. */
+       for (; rl->length; rl++) {
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               /* Header byte + length. */
+               rls += 1 + ntfs_get_nr_significant_bytes(rl->length);
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just store the lcn.
+                * Note: this assumes that on NTFS 1.2-, holes are stored with
+                * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       /* Change in lcn. */
+                       rls += ntfs_get_nr_significant_bytes(rl->lcn -
+                                       prev_lcn);
+                       prev_lcn = rl->lcn;
+               }
+       }
+       return rls;
+err_out:
+       if (rl->lcn == LCN_RL_NOT_MAPPED)
+               rls = -EINVAL;
+       else
+               rls = -EIO;
+       return rls;
+}
+
+/**
+ * ntfs_write_significant_bytes - write the significant bytes of a number
+ * @dst:       destination buffer to write to
+ * @dst_max:   pointer to last byte of destination buffer for bounds checking
+ * @n:         number whose significant bytes to write
+ *
+ * Store in @dst, the minimum bytes of the number @n which are required to
+ * identify @n unambiguously as a signed number, taking care not to exceed
+ * @dest_max, the maximum position within @dst to which we are allowed to
+ * write.
+ *
+ * This is used when building the mapping pairs array of a runlist to compress
+ * a given logical cluster number (lcn) or a specific run length to the minumum
+ * size possible.
+ *
+ * Return the number of bytes written on success.  On error, i.e. the
+ * destination buffer @dst is too small, return -ENOSPC.
+ */
+static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
+               const s64 n)
+{
+       s64 l = n;
+       int i;
+       s8 j;
+
+       i = 0;
+       do {
+               if (dst > dst_max)
+                       goto err_out;
+               *dst++ = l & 0xffll;
+               l >>= 8;
+               i++;
+       } while (l != 0 && l != -1);
+       j = (n >> 8 * (i - 1)) & 0xff;
+       /* If the sign bit is wrong, we need an extra byte. */
+       if (n < 0 && j >= 0) {
+               if (dst > dst_max)
+                       goto err_out;
+               i++;
+               *dst = (s8)-1;
+       } else if (n > 0 && j < 0) {
+               if (dst > dst_max)
+                       goto err_out;
+               i++;
+               *dst = (s8)0;
+       }
+       return i;
+err_out:
+       return -ENOSPC;
+}
+
+/**
+ * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist
+ * @vol:       ntfs volume (needed for the ntfs version)
+ * @dst:       destination buffer to which to write the mapping pairs array
+ * @dst_len:   size of destination buffer @dst in bytes
+ * @rl:                locked runlist for which to build the mapping pairs array
+ * @start_vcn: vcn at which to start the mapping pairs array
+ * @stop_vcn:  first vcn outside destination buffer on success or -ENOSPC
+ *
+ * Create the mapping pairs array from the locked runlist @rl, starting at vcn
+ * @start_vcn and save the array in @dst.  @dst_len is the size of @dst in
+ * bytes and it should be at least equal to the value obtained by calling
+ * ntfs_get_size_for_mapping_pairs().
+ *
+ * If @rl is NULL, just write a single terminator byte to @dst.
+ *
+ * On success or -ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to
+ * the first vcn outside the destination buffer.  Note that on error, @dst has
+ * been filled with all the mapping pairs that will fit, thus it can be treated
+ * as partial success, in that a new attribute extent needs to be created or
+ * the next extent has to be used and the mapping pairs build has to be
+ * continued with @start_vcn set to *@stop_vcn.
+ *
+ * Return 0 on success and -errno on error.  The following error codes are
+ * defined:
+ *     -EINVAL - Run list contains unmapped elements.  Make sure to only pass
+ *               fully mapped runlists to this function.
+ *     -EIO    - The runlist is corrupt.
+ *     -ENOSPC - The destination buffer is too small.
+ *
+ * Locking: @rl must be locked on entry (either for reading or writing), it
+ *         remains locked throughout, and is left locked upon return.
+ */
+int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
+               const int dst_len, const runlist_element *rl,
+               const VCN start_vcn, VCN *const stop_vcn)
+{
+       LCN prev_lcn;
+       s8 *dst_max, *dst_next;
+       int err = -ENOSPC;
+       s8 len_len, lcn_len;
+
+       BUG_ON(start_vcn < 0);
+       BUG_ON(dst_len < 1);
+       if (!rl) {
+               BUG_ON(start_vcn);
+               if (stop_vcn)
+                       *stop_vcn = 0;
+               /* Terminator byte. */
+               *dst = 0;
+               return 0;
+       }
+       /* Skip to runlist element containing @start_vcn. */
+       while (rl->length && start_vcn >= rl[1].vcn)
+               rl++;
+       if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn)
+               return -EINVAL;
+       /*
+        * @dst_max is used for bounds checking in
+        * ntfs_write_significant_bytes().
+        */
+       dst_max = dst + dst_len - 1;
+       prev_lcn = 0;
+       /* Do the first partial run if present. */
+       if (start_vcn > rl->vcn) {
+               s64 delta;
+
+               /* We know rl->length != 0 already. */
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               delta = start_vcn - rl->vcn;
+               /* Write length. */
+               len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
+                               rl->length - delta);
+               if (len_len < 0)
+                       goto size_err;
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just write the lcn
+                * change.  FIXME: Do we need to write the lcn change or just
+                * the lcn in that case?  Not sure as I have never seen this
+                * case on NT4. - We assume that we just need to write the lcn
+                * change until someone tells us otherwise... (AIA)
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       prev_lcn = rl->lcn;
+                       if (rl->lcn >= 0)
+                               prev_lcn += delta;
+                       /* Write change in lcn. */
+                       lcn_len = ntfs_write_significant_bytes(dst + 1 +
+                                       len_len, dst_max, prev_lcn);
+                       if (lcn_len < 0)
+                               goto size_err;
+               } else
+                       lcn_len = 0;
+               dst_next = dst + len_len + lcn_len + 1;
+               if (dst_next > dst_max)
+                       goto size_err;
+               /* Update header byte. */
+               *dst = lcn_len << 4 | len_len;
+               /* Position at next mapping pairs array element. */
+               dst = dst_next;
+               /* Go to next runlist element. */
+               rl++;
+       }
+       /* Do the full runs. */
+       for (; rl->length; rl++) {
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               /* Write length. */
+               len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
+                               rl->length);
+               if (len_len < 0)
+                       goto size_err;
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just write the lcn
+                * change.  FIXME: Do we need to write the lcn change or just
+                * the lcn in that case?  Not sure as I have never seen this
+                * case on NT4. - We assume that we just need to write the lcn
+                * change until someone tells us otherwise... (AIA)
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       /* Write change in lcn. */
+                       lcn_len = ntfs_write_significant_bytes(dst + 1 +
+                                       len_len, dst_max, rl->lcn - prev_lcn);
+                       if (lcn_len < 0)
+                               goto size_err;
+                       prev_lcn = rl->lcn;
+               } else
+                       lcn_len = 0;
+               dst_next = dst + len_len + lcn_len + 1;
+               if (dst_next > dst_max)
+                       goto size_err;
+               /* Update header byte. */
+               *dst = lcn_len << 4 | len_len;
+               /* Position at next mapping pairs array element. */
+               dst = dst_next;
+       }
+       /* Success. */
+       err = 0;
+size_err:
+       /* Set stop vcn. */
+       if (stop_vcn)
+               *stop_vcn = rl->vcn;
+       /* Add terminator byte. */
+       *dst = 0;
+       return err;
+err_out:
+       if (rl->lcn == LCN_RL_NOT_MAPPED)
+               err = -EINVAL;
+       else
+               err = -EIO;
+       return err;
+}
index 0a01315a4f13547649ddb5027f30f8dad5fe9a7f..cc230c8dec8cef83687577b4f0a3336a91746443 100644 (file)
@@ -45,4 +45,11 @@ extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
 
 extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
 
+extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
+               const runlist_element *rl, const VCN start_vcn);
+
+extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
+               const int dst_len, const runlist_element *rl,
+               const VCN start_vcn, VCN *const stop_vcn);
+
 #endif /* _LINUX_NTFS_RUNLIST_H */