📄 attrib.c
字号:
* Otherwise, match any attribute. */ if (name == AT_UNNAMED) { /* The search failed if the found attribute is named. */ if (a->name_length) { errno = ENOENT; return -1; } } else if (name && !ntfs_names_are_equal(name, name_len, (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), a->name_length, ic, upcase, upcase_len)) { register int rc; rc = ntfs_names_collate(name, name_len, (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), a->name_length, 1, IGNORE_CASE, upcase, upcase_len); /* * If @name collates before a->name, there is no * matching attribute. */ if (rc == -1) { errno = ENOENT; return -1; } /* If the strings are not equal, continue search. */ if (rc) continue; rc = ntfs_names_collate(name, name_len, (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), a->name_length, 1, CASE_SENSITIVE, upcase, upcase_len); if (rc == -1) { errno = ENOENT; return -1; } if (rc) continue; } /* * The names match or @name not present and attribute is * unnamed. If no @val specified, we have found the attribute * and are done. */ if (!val) return 0; /* @val is present; compare values. */ else { register int rc; rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), min(val_len, le32_to_cpu(a->value_length))); /* * If @val collates before the current attribute's * value, there is no matching attribute. */ if (!rc) { register u32 avl; avl = le32_to_cpu(a->value_length); if (val_len == avl) return 0; if (val_len < avl) { errno = ENOENT; return -1; } } else if (rc < 0) { errno = ENOENT; return -1; } } } ntfs_log_debug("ntfs_attr_find(): File is corrupt. Run chkdsk.\n"); errno = EIO; return -1;}void ntfs_attr_name_free(char **name){ if (*name) { free(*name); *name = NULL; }}char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len){ char *name = NULL; int name_len; name_len = ntfs_ucstombs(uname, uname_len, &name, 0); if (name_len < 0) { ntfs_log_perror("ntfs_ucstombs"); return NULL; } else if (name_len > 0) return name; ntfs_attr_name_free(&name); return NULL;}/** * ntfs_external_attr_find - find an attribute in the attribute list of an inode * @type: attribute type to find * @name: attribute name to find (optional, i.e. NULL means don't care) * @name_len: attribute name length (only needed if @name present) * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) * @val: attribute value to find (optional, resident attributes only) * @val_len: attribute value length * @ctx: search context with mft record and attribute to search from * * You shouldn't need to call this function directly. Use ntfs_attr_lookup() * instead. * * Find an attribute by searching the attribute list for the corresponding * attribute list entry. Having found the entry, map the mft record for read * if the attribute is in a different mft record/inode, find the attribute in * there and return it. * * If @type is AT_UNUSED, return the first found attribute, i.e. one can * enumerate all attributes by setting @type to AT_UNUSED and then calling * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to * ENOENT to indicate that there are no more entries. During the enumeration, * each successful call of ntfs_external_attr_find() will return the next * attribute described by the attribute list of the base mft record described * by the search context @ctx. * * If @type is AT_END, seek to the end of the base mft record ignoring the * attribute list completely and return -1 with errno set to ENOENT. AT_END is * not a valid attribute, its length is zero for example, thus it is safer to * return error instead of success in this case. * * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, * match both named and unnamed attributes. * * On first search @ctx->ntfs_ino must be the inode of the base mft record and * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too * (@ctx->base_ntfs_ino is then the base inode). * * After finishing with the attribute/mft record you need to call * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any * mapped extent inodes, etc). * * Return 0 if the search was successful and -1 if not, with errno set to the * error code. * * On success, @ctx->attr is the found attribute, it is in mft record * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this * attribute with @ctx->base_* being the base mft record to which @ctx->attr * belongs. * * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the * attribute which collates just after the attribute being searched for in the * base ntfs inode, i.e. if one wants to add the attribute to the mft record * this is the correct place to insert it into, and if there is not enough * space, the attribute should be placed in an extent mft record. * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list * at which the new attribute's attribute list entry should be inserted. The * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. * The only exception to this is when @type is AT_END, in which case * @ctx->al_entry is set to NULL also (see above). * * The following error codes are defined: * ENOENT Attribute not found, not an error as such. * EINVAL Invalid arguments. * EIO I/O error or corrupt data structures found. * ENOMEM Not enough memory to allocate necessary buffers. */static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, const u32 name_len, const IGNORE_CASE_BOOL ic, const VCN lowest_vcn, const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx){ ntfs_inode *base_ni, *ni; ntfs_volume *vol; ATTR_LIST_ENTRY *al_entry, *next_al_entry; u8 *al_start, *al_end; ATTR_RECORD *a; ntfschar *al_name; u32 al_name_len; BOOL is_first_search = FALSE; ni = ctx->ntfs_ino; base_ni = ctx->base_ntfs_ino; ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", (unsigned long long)ni->mft_no, type); if (!base_ni) { /* First call happens with the base mft record. */ base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; ctx->base_mrec = ctx->mrec; } if (ni == base_ni) ctx->base_attr = ctx->attr; if (type == AT_END) goto not_found; vol = base_ni->vol; al_start = base_ni->attr_list; al_end = al_start + base_ni->attr_list_size; if (!ctx->al_entry) { ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; is_first_search = TRUE; } /* * Iterate over entries in attribute list starting at @ctx->al_entry, * or the entry following that, if @ctx->is_first is TRUE. */ if (ctx->is_first) { al_entry = ctx->al_entry; ctx->is_first = FALSE; /* * If an enumeration and the first attribute is higher than * the attribute list itself, need to return the attribute list * attribute. */ if ((type == AT_UNUSED) && is_first_search && le32_to_cpu(al_entry->type) > le32_to_cpu(AT_ATTRIBUTE_LIST)) goto find_attr_list_attr; } else { al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + le16_to_cpu(ctx->al_entry->length)); /* * If this is an enumeration and the attribute list attribute * is the next one in the enumeration sequence, just return the * attribute list attribute from the base mft record as it is * not listed in the attribute list itself. */ if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < le32_to_cpu(AT_ATTRIBUTE_LIST) && le32_to_cpu(al_entry->type) > le32_to_cpu(AT_ATTRIBUTE_LIST)) { int rc;find_attr_list_attr: /* Check for bogus calls. */ if (name || name_len || val || val_len || lowest_vcn) { errno = EINVAL; return -1; } /* We want the base record. */ ctx->ntfs_ino = base_ni; ctx->mrec = ctx->base_mrec; ctx->is_first = TRUE; /* Sanity checks are performed elsewhere. */ ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + le16_to_cpu(ctx->mrec->attrs_offset)); /* Find the attribute list attribute. */ rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, IGNORE_CASE, NULL, 0, ctx); /* * Setup the search context so the correct * attribute is returned next time round. */ ctx->al_entry = al_entry; ctx->is_first = TRUE; /* Got it. Done. */ if (!rc) return 0; /* Error! If other than not found return it. */ if (errno != ENOENT) return rc; /* Not found?!? Absurd! Must be a bug... )-: */ ntfs_log_error("Extant attribute list wasn't found\n"); errno = EINVAL; return -1; } } for (;; al_entry = next_al_entry) { /* Out of bounds check. */ if ((u8*)al_entry < base_ni->attr_list || (u8*)al_entry > al_end) break; /* Inode is corrupt. */ ctx->al_entry = al_entry; /* Catch the end of the attribute list. */ if ((u8*)al_entry == al_end) goto not_found; if (!al_entry->length) break; if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + le16_to_cpu(al_entry->length) > al_end) break; next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + le16_to_cpu(al_entry->length)); if (type != AT_UNUSED) { if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) goto not_found; if (type != al_entry->type) continue; } al_name_len = al_entry->name_length; al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); /* * If !@type we want the attribute represented by this * attribute list entry. */ if (type == AT_UNUSED) goto is_enumeration; /* * If @name is AT_UNNAMED we want an unnamed attribute. * If @name is present, compare the two names. * Otherwise, match any attribute. */ if (name == AT_UNNAMED) { if (al_name_len) goto not_found; } else if (name && !ntfs_names_are_equal(al_name, al_name_len, name, name_len, ic, vol->upcase, vol->upcase_len)) { register int rc; rc = ntfs_names_collate(name, name_len, al_name, al_name_len, 1, IGNORE_CASE, vol->upcase, vol->upcase_len); /* * If @name collates before al_name, there is no * matching attribute. */ if (rc == -1) goto not_found; /* If the strings are not equal, continue search. */ if (rc) continue; /* * FIXME: Reverse engineering showed 0, IGNORE_CASE but * that is inconsistent with ntfs_attr_find(). The * subsequent rc checks were also different. Perhaps I * made a mistake in one of the two. Need to recheck * which is correct or at least see what is going * on... (AIA) */ rc = ntfs_names_collate(name, name_len, al_name, al_name_len, 1, CASE_SENSITIVE, vol->upcase, vol->upcase_len); if (rc == -1) goto not_found; if (rc) continue; } /* * The names match or @name not present and attribute is * unnamed. Now check @lowest_vcn. Continue search if the * next attribute list entry still fits @lowest_vcn. Otherwise * we have reached the right one or the search has failed. */ if (lowest_vcn && (u8*)next_al_entry >= al_start && (u8*)next_al_entry + 6 < al_end && (u8*)next_al_entry + le16_to_cpu( next_al_entry->length) <= al_end && sle64_to_cpu(next_al_entry->lowest_vcn) <= lowest_vcn && next_al_entry->type == al_entry->type && next_al_entry->name_length == al_name_len && ntfs_names_are_equal((ntfschar*)((char*) next_al_entry + next_al_entry->name_offset), next_al_entry->name_length, al_name, al_name_len, CASE_SENSITIVE, vol->upcase, vol->upcase_len)) continue;is_enumeration: if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { if (MSEQNO_LE(al_entry->mft_reference) != le16_to_cpu( ni->mrec->sequence_number)) { ntfs_log_debug("Found stale mft reference in " "attribute list!\n"); break; } } else { /* Mft references do not match. */ /* Do we want the base record back? */ if (MREF_LE(al_entry->mft_reference) == base_ni->mft_no) { ni = ctx->ntfs_ino = base_ni; ctx->mrec = ctx->base_mrec; } else { /* We want an extent record. */ ni = ntfs_extent_inode_open(base_ni, al_entry->mft_reference); if (!ni) { ntfs_log_perror("Failed to map extent inode"); break; } ctx->ntfs_ino = ni; ctx->mrec = ni->mrec; } } a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + le16_to_cpu(ctx->mrec->attrs_offset)); /* * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the * mft record containing the attribute represented by the * current al_entry. * * We could call into ntfs_attr_find() to find the right * attribute in this mft record but this would be less * efficient and not quite accurate as ntfs_attr_find() ignores * the attribute instance numbers for example which become * important when one plays with attribute lists. Also, because * a proper match has been found in the attribute list entry * above, the comparison can now be optimized. So it is worth * re-implementing a simplified ntfs_attr_find() here. * * Use a manual loop so we can still use break and continue * with the same meanings as above. */do_next_attr_loop: if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_allocated)) break; if (a->type == AT_END) continue; if (!a->length) break; if (al_entry->instance != a->instance) goto do_next_attr; /* * If the type and/or the name are/is mismatched b
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -