📄 attrib.c
字号:
/** * ntfs_attr_close - free an ntfs attribute structure * @na: ntfs attribute structure to free * * Release all memory associated with the ntfs attribute @na and then release * @na itself. */void ntfs_attr_close(ntfs_attr *na){ if (!na) return; if (NAttrNonResident(na) && na->rl) free(na->rl); /* Don't release if using an internal constant. */ if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30) free(na->name); free(na);}/** * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute * @na: ntfs attribute for which to map (part of) a runlist * @vcn: map runlist part containing this vcn * * Map the part of a runlist containing the @vcn of an the ntfs attribute @na. * * Return 0 on success and -1 on error with errno set to the error code. */int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn){ LCN lcn; ntfs_attr_search_ctx *ctx; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, (long long)vcn); lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) return 0; ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) return -1; /* Find the attribute in the mft record. */ if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, vcn, NULL, 0, ctx)) { runlist_element *rl; /* Decode the runlist. */ rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, na->rl); if (rl) { na->rl = rl; ntfs_attr_put_search_ctx(ctx); return 0; } } ntfs_attr_put_search_ctx(ctx); return -1;}/** * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute * @na: ntfs attribute for which to map the runlist * * Map the whole runlist of an the ntfs attribute @na. For an attribute made * up of only one attribute extent this is the same as calling * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this * will map the runlist fragments from each of the extents thus giving access * to the entirety of the disk allocation of an attribute. * * Return 0 on success and -1 on error with errno set to the error code. */int ntfs_attr_map_whole_runlist(ntfs_attr *na){ VCN next_vcn, last_vcn, highest_vcn; ntfs_attr_search_ctx *ctx; ntfs_volume *vol = na->ni->vol; ATTR_RECORD *a; int err; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long long)na->ni->mft_no, na->type); ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) return -1; /* Map all attribute extents one by one. */ next_vcn = last_vcn = highest_vcn = 0; a = NULL; while (1) { runlist_element *rl; int not_mapped = 0; if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) not_mapped = 1; if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) break; a = ctx->attr; if (not_mapped) { /* Decode the runlist. */ rl = ntfs_mapping_pairs_decompress(na->ni->vol, a, na->rl); if (!rl) goto err_out; na->rl = rl; } /* Are we in the first extent? */ if (!next_vcn) { if (a->lowest_vcn) { errno = EIO; ntfs_log_perror("Attribute first extent has " "non-zero lowest_vcn"); goto err_out; } /* Get the last vcn in the attribute. */ last_vcn = sle64_to_cpu(a->allocated_size) >> vol->cluster_size_bits; } /* Get the lowest vcn for the next extent. */ highest_vcn = sle64_to_cpu(a->highest_vcn); next_vcn = highest_vcn + 1; /* Only one extent or error, which we catch below. */ if (next_vcn <= 0) { errno = ENOENT; break; } /* Avoid endless loops due to corruption. */ if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { errno = EIO; ntfs_log_perror("Inode has corrupt attribute list"); goto err_out; } } if (!a) { ntfs_log_perror("Couldn't find attribute for runlist mapping"); goto err_out; } if (highest_vcn && highest_vcn != last_vcn - 1) { errno = EIO; ntfs_log_perror("Couldn't load full runlist: " "highest_vcn = 0x%llx, last_vcn = 0x%llx", (long long)highest_vcn, (long long)last_vcn); goto err_out; } err = errno; ntfs_attr_put_search_ctx(ctx); if (err == ENOENT) return 0;out_now: errno = err; return -1;err_out: err = errno; ntfs_attr_put_search_ctx(ctx); goto out_now;}/** * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute * @na: ntfs attribute whose 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 @na->rl to map vcns to * their corresponding lcns. * * If the @vcn is not mapped yet, attempt to map the attribute extent * containing the @vcn and retry the vcn to lcn conversion. * * 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. * -3 = LCN_ENOENT There is no such vcn in the attribute. * -4 = LCN_EINVAL Input parameter error. * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. */LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn){ LCN lcn; BOOL is_retry = FALSE; if (!na || !NAttrNonResident(na) || vcn < 0) return (LCN)LCN_EINVAL; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long long)na->ni->mft_no, na->type);retry: /* Convert vcn to lcn. If that fails map the runlist and retry once. */ lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); if (lcn >= 0) return lcn; if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { is_retry = TRUE; goto retry; } /* * If the attempt to map the runlist failed, or we are getting * LCN_RL_NOT_MAPPED despite having mapped the attribute extent * successfully, something is really badly wrong... */ if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) return (LCN)LCN_EIO; /* lcn contains the appropriate error code. */ return lcn;}/** * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute * @na: ntfs attribute whose runlist to search * @vcn: vcn to find * * Find the virtual cluster number @vcn in the runlist of the ntfs attribute * @na and return the the address of the runlist element containing the @vcn. * * Note you need to distinguish between the lcn of the returned runlist * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes * on read and allocate clusters on write. You need to update the runlist, the * attribute itself as well as write the modified mft record to disk. * * If there is an error return NULL with errno set to the error code. The * following error codes are defined: * EINVAL Input parameter error. * ENOENT There is no such vcn in the runlist. * ENOMEM Not enough memory. * EIO I/O error or corrupt metadata. */runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn){ runlist_element *rl; BOOL is_retry = FALSE; if (!na || !NAttrNonResident(na) || vcn < 0) { errno = EINVAL; return NULL; } ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", (unsigned long long)na->ni->mft_no, na->type, (long long)vcn);retry: rl = na->rl; if (!rl) goto map_rl; if (vcn < rl[0].vcn) goto map_rl; while (rl->length) { if (vcn < rl[1].vcn) { if (rl->lcn >= (LCN)LCN_HOLE) return rl; break; } rl++; } switch (rl->lcn) { case (LCN)LCN_RL_NOT_MAPPED: goto map_rl; case (LCN)LCN_ENOENT: errno = ENOENT; break; case (LCN)LCN_EINVAL: errno = EINVAL; break; default: errno = EIO; break; } return NULL;map_rl: /* The @vcn is in an unmapped region, map the runlist and retry. */ if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { is_retry = TRUE; goto retry; } /* * If we already retried or the mapping attempt failed something has * gone badly wrong. EINVAL and ENOENT coming from a failed mapping * attempt are equivalent to errors for us as they should not happen * in our code paths. */ if (is_retry || errno == EINVAL || errno == ENOENT) errno = EIO; return NULL;}/** * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure * @na: ntfs attribute to read from * @pos: byte position in the attribute to begin reading from * @count: number of bytes to read * @b: output data buffer * * This function will read @count bytes starting at offset @pos from the ntfs * attribute @na into the data buffer @b. * * 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 * end of file or 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. */s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b){ s64 br, to_read, ofs, total, total2; ntfs_volume *vol; runlist_element *rl; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " "0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, (long long)pos, (long long)count); if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; return -1; } /* * If this is a compressed attribute it needs special treatment, but * only if it is non-resident. */ if (NAttrCompressed(na) && NAttrNonResident(na)) return ntfs_compressed_attr_pread(na, pos, count, b); /* * Encrypted non-resident attributes are not supported. We return * access denied, which is what Windows NT4 does, too. */ if (NAttrEncrypted(na) && NAttrNonResident(na)) { errno = EACCES; return -1; } vol = na->ni->vol; /* Update access time if needed. */ if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || na->type == AT_INDEX_ALLOCATION) ntfs_inode_update_atime(na->ni); if (!count) return 0; /* Truncate reads beyond end of attribute. */ if (pos + count > na->data_size) { if (pos >= na->data_size) return 0; count = na->data_size - pos; } /* If it is a resident attribute, get the value from the mft record. */ if (!NAttrNonResident(na)) { ntfs_attr_search_ctx *ctx; char *val; ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) return -1; if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, ctx)) {res_err_out: ntfs_attr_put_search_ctx(ctx); return -1; } val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); if (val < (char*)ctx->attr || val + le32_to_cpu(ctx->attr->value_length) > (char*)ctx->mrec + vol->mft_record_size) { errno = EIO; goto res_err_out; } memcpy(b, val + pos, count); ntfs_attr_put_search_ctx(ctx); return count; } total = total2 = 0; /* Zero out reads beyond initialized size. */ if (pos + count > na->initialized_size) { if (pos >= na->initialized_size) { memset(b, 0, count); return count; } total2 = pos + count - na->initialized_size; count -= total2; memset((u8*)b + count, 0, total2); } /* Find the runlist element containing the vcn. */ rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); if (!rl) { /* * If the vcn is not present it is an out of bounds read. * However, we already truncated the read to the data_size, * so getting this here is an error. */ if (errno == ENOENT) errno = EIO; return -1; } /* * Gather the requested data into the linear destination buffer. Note, * a partial final vcn is taken care of by the @count capping of read * length. */ ofs = pos - (rl->vcn << vol->cluster_size_bits); for (; count; rl++, ofs = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { if (errno == ENOENT) errno = EIO; goto rl_err_out; } /* Needed for case when runs merged. */ ofs = pos + total - (rl->vcn << vol->cluster_size_bits); } if (!rl->length) goto rl_err_out; if (rl->lcn < (LCN)0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -