📄 attrib.c
字号:
(!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { errno = EIO; ntfs_log_perror("Inode %lld has corrupt attribute flags " "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, a->flags, na->ni->flags); goto put_err_out; } if (a->non_resident) { if ((a->flags & ATTR_IS_COMPRESSED) && !a->compression_unit) { errno = EIO; ntfs_log_perror("Compressed inode %lld attr 0x%x has " "no compression unit", (unsigned long long)ni->mft_no, type); goto put_err_out; } ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED, a->flags & ATTR_IS_ENCRYPTED, a->flags & ATTR_IS_SPARSE, sle64_to_cpu(a->allocated_size), sle64_to_cpu(a->data_size), sle64_to_cpu(a->initialized_size), cs ? sle64_to_cpu(a->compressed_size) : 0, cs ? a->compression_unit : 0); } else { s64 l = le32_to_cpu(a->value_length); ntfs_attr_init(na, FALSE, a->flags & ATTR_IS_COMPRESSED, a->flags & ATTR_IS_ENCRYPTED, a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, cs ? (l + 7) & ~7 : 0, 0); } ntfs_attr_put_search_ctx(ctx);out: ntfs_log_leave("\n"); return na;put_err_out: ntfs_attr_put_search_ctx(ctx);err_out: free(na); na = NULL; goto out;}/** * 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("First extent of inode %llu " "attribute has non-zero lowest_vcn", (unsigned long long)na->ni->mft_no); 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 %llu has corrupt attribute list", (unsigned long long)na->ni->mft_no); 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("Failed to load full runlist: inode: %llu " "highest_vcn: 0x%llx last_vcn: 0x%llx", (unsigned long long)na->ni->mft_no, (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_i - see description at ntfs_attr_pread() */ static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b){ s64 br, to_read, ofs, total, total2; ntfs_volume *vol; runlist_element *rl; /* Sanity checking arguments is done in ntfs_attr_pread(). */ 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; 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; ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); 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; ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__); } 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -