📄 runlist.c
字号:
* -1. So if either is found give us a message so we * can investigate it further! */ if (vol->major_ver < 3) { if (deltaxcn == (LCN)-1) ntfs_log_debug("lcn delta == -1\n"); if (lcn == (LCN)-1) ntfs_log_debug("lcn == -1\n"); }#endif /* Check lcn is not below -1. */ if (lcn < (LCN)-1) { ntfs_log_debug("Invalid LCN < -1 in mapping pairs " "array.\n"); goto err_out; } /* Enter the current lcn into the runlist element. */ rl[rlpos].lcn = lcn; } /* Get to the next runlist element. */ rlpos++; /* Increment the buffer position to the next mapping pair. */ buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1; } if (buf >= attr_end) goto io_error; /* * If there is a highest_vcn specified, it must be equal to the final * vcn in the runlist - 1, or something has gone badly wrong. */ deltaxcn = sle64_to_cpu(attr->highest_vcn); if (deltaxcn && vcn - 1 != deltaxcn) {mpa_err: ntfs_log_debug("Corrupt mapping pairs array in non-resident " "attribute.\n"); goto err_out; } /* Setup not mapped runlist element if this is the base extent. */ if (!attr->lowest_vcn) { VCN max_cluster; max_cluster = ((sle64_to_cpu(attr->allocated_size) + vol->cluster_size - 1) >> vol->cluster_size_bits) - 1; /* * A highest_vcn of zero means this is a single extent * attribute so simply terminate the runlist with LCN_ENOENT). */ if (deltaxcn) { /* * If there is a difference between the highest_vcn and * the highest cluster, the runlist is either corrupt * or, more likely, there are more extents following * this one. */ if (deltaxcn < max_cluster) { ntfs_log_debug("More extents to follow; deltaxcn = " "0x%llx, max_cluster = 0x%llx\n", (long long)deltaxcn, (long long)max_cluster); rl[rlpos].vcn = vcn; vcn += rl[rlpos].length = max_cluster - deltaxcn; rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED; rlpos++; } else if (deltaxcn > max_cluster) { ntfs_log_debug("Corrupt attribute. deltaxcn = " "0x%llx, max_cluster = 0x%llx\n", (long long)deltaxcn, (long long)max_cluster); goto mpa_err; } } rl[rlpos].lcn = (LCN)LCN_ENOENT; } else /* Not the base extent. There may be more extents to follow. */ rl[rlpos].lcn = (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_log_debug("Mapping pairs array successfully decompressed:\n"); ntfs_debug_runlist_dump(rl); return rl; } /* Now combine the new and old runlists checking for overlaps. */ old_rl = ntfs_runlists_merge(old_rl, rl); if (old_rl) return old_rl; err = errno; free(rl); ntfs_log_debug("Failed to merge runlists.\n"); errno = err; return NULL;io_error: ntfs_log_debug("Corrupt attribute.\n");err_out: free(rl); errno = EIO; return NULL;}/** * 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. * * Since lcns must be >= 0, we use negative return values with special meaning: * * Return value Meaning / Description * ================================================== * -1 = LCN_HOLE Hole / not allocated on disk. * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been * inserted into the runlist yet. * -3 = LCN_ENOENT There is no such vcn in the attribute. * -4 = LCN_EINVAL Input parameter error. */LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn){ int i; if (vcn < (VCN)0) return (LCN)LCN_EINVAL; /* * 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 (!rl) return (LCN)LCN_RL_NOT_MAPPED; /* Catch out of lower bounds vcn. */ if (vcn < rl[0].vcn) return (LCN)LCN_ENOENT; for (i = 0; rl[i].length; i++) { if (vcn < rl[i+1].vcn) { if (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 (rl[i].lcn < (LCN)0) return rl[i].lcn; /* Just in case... We could replace this with BUG() some day. */ return (LCN)LCN_ENOENT;}/** * ntfs_rl_pread - gather read from disk * @vol: ntfs volume to read from * @rl: runlist specifying where to read the data from * @pos: byte position within runlist @rl at which to begin the read * @count: number of bytes to read * @b: data buffer into which to read from disk * * This function will read @count bytes from the volume @vol to the data buffer * @b gathering the data as specified by the runlist @rl. The read begins at * offset @pos into the runlist @rl. * * On success, return the number of successfully read bytes. If this number is * lower than @count this means that the read reached end of file or that an * error was encountered during the read so that the read is partial. 0 means * nothing was read (also return 0 when @count is 0). * * On error and nothing has been read, return -1 with errno set appropriately * to the return code of ntfs_pread(), or to EINVAL in case of invalid * arguments. * * NOTE: If we encounter EOF while reading we return EIO because we assume that * the run list must point to valid locations within the ntfs volume. */s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl, const s64 pos, s64 count, void *b){ s64 bytes_read, to_read, ofs, total; int err = EIO; if (!vol || !rl || pos < 0 || count < 0) { errno = EINVAL; return -1; } if (!count) return count; /* Seek in @rl to the run containing @pos. */ for (ofs = 0; rl->length && (ofs + (rl->length << vol->cluster_size_bits) <= pos); rl++) ofs += (rl->length << vol->cluster_size_bits); /* Offset in the run at which to begin reading. */ ofs = pos - ofs; for (total = 0LL; count; rl++, ofs = 0) { if (!rl->length) goto rl_err_out; if (rl->lcn < (LCN)0) { if (rl->lcn != (LCN)LCN_HOLE) goto rl_err_out; /* It is a hole. Just fill buffer @b with zeroes. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); memset(b, 0, to_read); /* Update counters and proceed with next run. */ total += to_read; count -= to_read; b = (u8*)b + to_read; continue; } /* It is a real lcn, read it from the volume. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs);retry: bytes_read = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, to_read, b); /* If everything ok, update progress counters and continue. */ if (bytes_read > 0) { total += bytes_read; count -= bytes_read; b = (u8*)b + bytes_read; continue; } /* If the syscall was interrupted, try again. */ if (bytes_read == (s64)-1 && errno == EINTR) goto retry; if (bytes_read == (s64)-1) err = errno; goto rl_err_out; } /* Finally, return the number of bytes read. */ return total;rl_err_out: if (total) return total; errno = err; return -1;}/** * ntfs_rl_pwrite - scatter write to disk * @vol: ntfs volume to write to * @rl: runlist specifying where to write the data to * @pos: byte position within runlist @rl at which to begin the write * @count: number of bytes to write * @b: data buffer to write to disk * * This function will write @count bytes from data buffer @b to the volume @vol * scattering the data as specified by the runlist @rl. The write begins at * offset @pos into the runlist @rl. * * On success, return the number of successfully written bytes. If this number * is lower than @count this means that the write has been interrupted in * flight or that an error was encountered during the write so that the write * is partial. 0 means nothing was written (also return 0 when @count is 0). * * On error and nothing has been written, return -1 with errno set * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case * of invalid arguments. */s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl, const s64 pos, s64 count, void *b){ s64 written, to_write, ofs, total; int err = EIO; if (!vol || !rl || pos < 0 || count < 0) { errno = EINVAL; return -1; } if (!count) return count; /* Seek in @rl to the run containing @pos. */ for (ofs = 0; rl->length && (ofs + (rl->length << vol->cluster_size_bits) <= pos); rl++) ofs += (rl->length << vol->cluster_size_bits); /* Offset in the run at which to begin writing. */ ofs = pos - ofs; for (total = 0LL; count; rl++, ofs = 0) { if (!rl->length) goto rl_err_out; if (rl->lcn < (LCN)0) { s64 t; int cnt; if (rl->lcn != (LCN)LCN_HOLE) goto rl_err_out; /* * It is a hole. Check if the buffer is zero in this * region and if not abort with error. */ to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); written = to_write / sizeof(unsigned long); for (t = 0; t < written; t++) { if (((unsigned long*)b)[t]) goto rl_err_out; } cnt = to_write & (sizeof(unsigned long) - 1); if (cnt) { int i; u8 *b2; b2 = (u8*)b + (to_write & ~(sizeof(unsigned long) - 1)); for (i = 0; i < cnt; i++) { if (b2[i]) goto rl_err_out; } } /* * The buffer region is zero, update progress counters * and proceed with next run. */ total += to_write; count -= to_write; b = (u8*)b + to_write; continue; } /* It is a real lcn, write it to the volume. */ to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs);retry: if (!NVolReadOnly(vol)) written = ntfs_pwrite(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, to_write, b); else written = to_write; /* If everything ok, update progress counters and continue. */ if (written > 0) { total += written; count -= written; b = (u8*)b + written; continue; } /* If the syscall was interrupted, try again. */ if (written == (s64)-1 && errno == EINTR) goto retry; if (written == (s64)-1) err = errno; goto rl_err_out; } /* Finally, return the number of bytes written. */ return total;rl_err_out: if (total) return total; errno = err; return -1;}/** * 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. */__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 != 0LL && l != -1LL); j = (n >> 8 * (i - 1)) & 0xff; /* If the sign bit is wrong, we need an extra byte. */ if ((n < 0LL && j >= 0) || (n > 0LL && 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: runlist for which to determine the size of the mapping pairs * @start_vcn: vcn at which to start the mapping pairs array * * Walk the 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 -1 with * errno set to the error code. The following error codes are defined: * EINVAL - Run list contains unmapped elements. Make sure to only pass * fully mapped runlists to this function. * - @start_vcn is invalid. * EIO - The runlist is corrupt. */int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol, const runlist_element *rl, const VCN start_vcn){ LCN prev_lcn; int rls; if (start_vcn < 0) { ntfs_log_trace("start_vcn %lld (should be >= 0)\n", (long long) start_vcn); errno = EINVAL; return -1; } if (!rl) { if (start_vcn) { ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n", (long long) start_vcn); errno = EINVAL; return -1; } return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -