📄 runlist.c
字号:
rl[rlpos].lcn = LCN_RL_NOT_MAPPED; /* Setup terminating runlist element. */ rl[rlpos].vcn = vcn; rl[rlpos].length = (s64)0; /* If no existing runlist was specified, we are done. */ if (!old_rl) { ntfs_debug("Mapping pairs array successfully decompressed:"); ntfs_debug_dump_runlist(rl); return rl; } /* Now combine the new and old runlists checking for overlaps. */ old_rl = ntfs_runlists_merge(old_rl, rl); if (likely(!IS_ERR(old_rl))) return old_rl; ntfs_free(rl); ntfs_error(vol->sb, "Failed to merge runlists."); return old_rl;io_error: ntfs_error(vol->sb, "Corrupt attribute.");err_out: ntfs_free(rl); return ERR_PTR(-EIO);}/** * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist * @rl: runlist to use for conversion * @vcn: vcn to convert * * Convert the virtual cluster number @vcn of an attribute into a logical * cluster number (lcn) of a device using the runlist @rl to map vcns to their * corresponding lcns. * * It is up to the caller to serialize access to the runlist @rl. * * Since lcns must be >= 0, we use negative return codes with special meaning: * * Return code Meaning / Description * ================================================== * LCN_HOLE Hole / not allocated on disk. * LCN_RL_NOT_MAPPED This is part of the runlist which has not been * inserted into the runlist yet. * LCN_ENOENT There is no such vcn in the attribute. * * Locking: - The caller must have locked the runlist (for reading or writing). * - This function does not touch the lock, nor does it modify the * runlist. */LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn){ int i; BUG_ON(vcn < 0); /* * If rl is NULL, assume that we have found an unmapped runlist. The * caller can then attempt to map it and fail appropriately if * necessary. */ if (unlikely(!rl)) return LCN_RL_NOT_MAPPED; /* Catch out of lower bounds vcn. */ if (unlikely(vcn < rl[0].vcn)) return LCN_ENOENT; for (i = 0; likely(rl[i].length); i++) { if (unlikely(vcn < rl[i+1].vcn)) { if (likely(rl[i].lcn >= (LCN)0)) return rl[i].lcn + (vcn - rl[i].vcn); return rl[i].lcn; } } /* * The terminator element is setup to the correct value, i.e. one of * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT. */ if (likely(rl[i].lcn < (LCN)0)) return rl[i].lcn; /* Just in case... We could replace this with BUG() some day. */ return LCN_ENOENT;}#ifdef NTFS_RW/** * ntfs_rl_find_vcn_nolock - find a vcn in a runlist * @rl: runlist to search * @vcn: vcn to find * * Find the virtual cluster number @vcn in the runlist @rl and return the * address of the runlist element containing the @vcn on success. * * Return NULL if @rl is NULL or @vcn is in an unmapped part/out of bounds of * the runlist. * * Locking: The runlist must be locked on entry. */runlist_element *ntfs_rl_find_vcn_nolock(runlist_element *rl, const VCN vcn){ BUG_ON(vcn < 0); if (unlikely(!rl || vcn < rl[0].vcn)) return NULL; while (likely(rl->length)) { if (unlikely(vcn < rl[1].vcn)) { if (likely(rl->lcn >= LCN_HOLE)) return rl; return NULL; } rl++; } if (likely(rl->lcn == LCN_ENOENT)) return rl; return NULL;}/** * 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 * @first_vcn: first vcn which to include in the mapping pairs array * @last_vcn: last vcn which to include in 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 @first_vcn and * finishing with vcn @last_vcn. * * A @last_vcn of -1 means end of runlist and in that case the size of the * mapping pairs array corresponding to the runlist starting at vcn @first_vcn * and finishing at the end of the runlist is determined. * * 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 first_vcn, const VCN last_vcn){ LCN prev_lcn; int rls; bool the_end = false; BUG_ON(first_vcn < 0); BUG_ON(last_vcn < -1); BUG_ON(last_vcn >= 0 && first_vcn > last_vcn); if (!rl) { BUG_ON(first_vcn); BUG_ON(last_vcn > 0); return 1; } /* Skip to runlist element containing @first_vcn. */ while (rl->length && first_vcn >= rl[1].vcn) rl++; if (unlikely((!rl->length && first_vcn > rl->vcn) || first_vcn < rl->vcn)) return -EINVAL; prev_lcn = 0; /* Always need the termining zero byte. */ rls = 1; /* Do the first partial run if present. */ if (first_vcn > rl->vcn) { s64 delta, length = rl->length; /* We know rl->length != 0 already. */ if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) goto err_out; /* * If @stop_vcn is given and finishes inside this run, cap the * run length. */ if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { s64 s1 = last_vcn + 1; if (unlikely(rl[1].vcn > s1)) length = s1 - rl->vcn; the_end = true; } delta = first_vcn - rl->vcn; /* Header byte + length. */ rls += 1 + ntfs_get_nr_significant_bytes(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 (likely(rl->lcn >= 0 || vol->major_ver < 3)) { prev_lcn = rl->lcn; if (likely(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 && !the_end; rl++) { s64 length = rl->length; if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) goto err_out; /* * If @stop_vcn is given and finishes inside this run, cap the * run length. */ if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { s64 s1 = last_vcn + 1; if (unlikely(rl[1].vcn > s1)) length = s1 - rl->vcn; the_end = true; } /* Header byte + length. */ rls += 1 + ntfs_get_nr_significant_bytes(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 (likely(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 (unlikely(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 (unlikely(dst > dst_max)) goto err_out; i++; *dst = (s8)-1; } else if (n > 0 && j < 0) { if (unlikely(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 * @first_vcn: first vcn which to include in the mapping pairs array * @last_vcn: last vcn which to include in 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 * @first_vcn and finishing with vcn @last_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(). * * A @last_vcn of -1 means end of runlist and in that case the mapping pairs * array corresponding to the runlist starting at vcn @first_vcn and finishing * at the end of the runlist is created. * * 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 @first_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 first_vcn, const VCN last_vcn, VCN *const stop_vcn){ LCN prev_lcn; s8 *dst_max, *dst_next; int err = -ENOSPC; bool the_end = false; s8 len_len, lcn_len; BUG_ON(first_vcn < 0); BUG_ON(last_vcn < -1); BUG_ON(last_vcn >= 0 && first_vcn > last_vcn); BUG_ON(dst_len < 1); if (!rl) { BUG_ON(first_vcn); BUG_ON(last_vcn > 0); if (stop_vcn) *stop_vcn = 0; /* Terminator byte. */ *dst = 0; return 0; } /* Skip to runlist element containing @first_vcn. */ while (rl->length && first_vcn >= rl[1].vcn) rl++; if (unlikely((!rl->length && first_vcn > rl->vcn) || first_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 (first_vcn > rl->vcn) { s64 delta, length = rl->length; /* We know rl->length != 0 already. */ if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) goto err_out; /* * If @stop_vcn is given and finishes inside this run, cap the * run length. */ if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { s64 s1 = last_vcn + 1; if (unlikely(rl[1].vcn > s1)) length = s1 - rl->vcn; the_end = true; } delta = first_vcn - rl->vcn; /* Write length. */ len_len = ntfs_write_significant_bytes(dst + 1, dst_max, length - delta); if (unlikely(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 (likely(rl->lcn >= 0 || vol->major_ver < 3)) { prev_lcn = rl->lcn; if (likely(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 (unlikely(lcn_len < 0)) goto size_err; } else lcn_len = 0; dst_next = dst + len_len + lcn_len + 1; if (unlikely(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 && !the_end; rl++) { s64 length = rl->length; if (unlikely(length < 0 || rl->lcn < LCN_HOLE)) goto err_out; /* * If @stop_vcn is given and finishes inside this run, cap the * run length. */ if (unlikely(last_vcn >= 0 && rl[1].vcn > last_vcn)) { s64 s1 = last_vcn + 1; if (unlikely(rl[1].vcn > s1)) length = s1 - rl->vcn; the_end = true; } /* Write length. */ len_len = ntfs_write_significant_bytes(dst + 1, dst_max, length); if (unlikely(len_len < 0)) goto size_err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -