📄 attrib.c
字号:
up_read(&ni->runlist.lock); down_write(&ni->runlist.lock); if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) != LCN_RL_NOT_MAPPED)) { up_write(&ni->runlist.lock); down_read(&ni->runlist.lock); goto retry_remap; } } err = ntfs_map_runlist_nolock(ni, vcn, NULL); if (!write_locked) { up_write(&ni->runlist.lock); down_read(&ni->runlist.lock); } if (likely(!err)) { is_retry = true; goto retry_remap; } if (err == -ENOENT) lcn = LCN_ENOENT; else if (err == -ENOMEM) lcn = LCN_ENOMEM; else lcn = LCN_EIO; } if (lcn != LCN_ENOENT) ntfs_error(ni->vol->sb, "Failed with error code %lli.", (long long)lcn); return lcn;}/** * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode * @ni: ntfs inode describing the runlist to search * @vcn: vcn to find * @ctx: active attribute search context if present or NULL if not * * Find the virtual cluster number @vcn in the runlist described by the ntfs * inode @ni and return the address of the runlist element containing the @vcn. * * If the @vcn is not mapped yet, the attempt is made to map the attribute * extent containing the @vcn and the vcn to lcn conversion is retried. * * If @ctx is specified, it is an active search context of @ni and its base mft * record. This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped * runlist fragments and allows their mapping. If you do not have the mft * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock() * will perform the necessary mapping and unmapping. * * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and * restores it before returning. Thus, @ctx will be left pointing to the same * attribute on return as on entry. However, the actual pointers in @ctx may * point to different memory locations on return, so you must remember to reset * any cached pointers from the @ctx, i.e. after the call to * ntfs_attr_find_vcn_nolock(), you will probably want to do: * m = ctx->mrec; * a = ctx->attr; * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that * you cache ctx->mrec in a variable @m of type MFT_RECORD *. * 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. * * Return the runlist element containing the @vcn on success and * ERR_PTR(-errno) on error. You need to test the return value with IS_ERR() * to decide if the return is success or failure and PTR_ERR() to get to the * error code if IS_ERR() is true. * * The possible error return codes are: * -ENOENT - No such vcn in the runlist, i.e. @vcn is out of bounds. * -ENOMEM - Not enough memory to map runlist. * -EIO - Critical error (runlist/file is corrupt, i/o error, etc). * * WARNING: If @ctx is supplied, regardless of whether success or failure is * returned, you need to check IS_ERR(@ctx->mrec) and if 'true' the @ctx * is no longer valid, i.e. you need to either call * ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it. * In that case PTR_ERR(@ctx->mrec) will give you the error code for * why the mapping of the old inode failed. * * Locking: - The runlist described by @ni must be locked for writing on entry * and is locked on return. Note the runlist may be modified when * needed runlist fragments need to be mapped. * - If @ctx is NULL, the base mft record of @ni must not be mapped on * entry and it will be left unmapped on return. * - If @ctx is not NULL, the base mft record must be mapped on entry * and it will be left mapped on return. */runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn, ntfs_attr_search_ctx *ctx){ unsigned long flags; runlist_element *rl; int err = 0; bool is_retry = false; ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, with%s ctx.", ni->mft_no, (unsigned long long)vcn, ctx ? "" : "out"); BUG_ON(!ni); BUG_ON(!NInoNonResident(ni)); BUG_ON(vcn < 0); if (!ni->runlist.rl) { read_lock_irqsave(&ni->size_lock, flags); if (!ni->allocated_size) { read_unlock_irqrestore(&ni->size_lock, flags); return ERR_PTR(-ENOENT); } read_unlock_irqrestore(&ni->size_lock, flags); }retry_remap: rl = ni->runlist.rl; if (likely(rl && vcn >= rl[0].vcn)) { while (likely(rl->length)) { if (unlikely(vcn < rl[1].vcn)) { if (likely(rl->lcn >= LCN_HOLE)) { ntfs_debug("Done."); return rl; } break; } rl++; } if (likely(rl->lcn != LCN_RL_NOT_MAPPED)) { if (likely(rl->lcn == LCN_ENOENT)) err = -ENOENT; else err = -EIO; } } if (!err && !is_retry) { /* * If the search context is invalid we cannot map the unmapped * region. */ if (IS_ERR(ctx->mrec)) err = PTR_ERR(ctx->mrec); else { /* * The @vcn is in an unmapped region, map the runlist * and retry. */ err = ntfs_map_runlist_nolock(ni, vcn, ctx); if (likely(!err)) { is_retry = true; goto retry_remap; } } if (err == -EINVAL) err = -EIO; } else if (!err) err = -EIO; if (err != -ENOENT) ntfs_error(ni->vol->sb, "Failed with error code %i.", err); return ERR_PTR(err);}/** * ntfs_attr_find - find (next) attribute in mft record * @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) * @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 should not need to call this function directly. Use ntfs_attr_lookup() * instead. * * ntfs_attr_find() takes a search context @ctx as parameter and searches the * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an * attribute of @type, optionally @name and @val. * * If the attribute is found, ntfs_attr_find() returns 0 and @ctx->attr will * point to the found attribute. * * If the attribute is not found, ntfs_attr_find() returns -ENOENT and * @ctx->attr will point to the attribute before which the attribute being * searched for would need to be inserted if such an action were to be desired. * * On actual error, ntfs_attr_find() returns -EIO. In this case @ctx->attr is * undefined and in particular do not rely on it not changing. * * If @ctx->is_first is 'true', the search begins with @ctx->attr itself. If it * is 'false', the search begins after @ctx->attr. * * If @ic is IGNORE_CASE, the @name comparisson is not case sensitive and * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case * sensitive. When @name is present, @name_len is the @name length in Unicode * characters. * * If @name is not present (NULL), we assume that the unnamed attribute is * being searched for. * * Finally, the resident attribute value @val is looked for, if present. If * @val is not present (NULL), @val_len is ignored. * * ntfs_attr_find() only searches the specified mft record and it ignores the * presence of an attribute list attribute (unless it is the one being searched * for, obviously). If you need to take attribute lists into consideration, * use ntfs_attr_lookup() instead (see below). This also means that you cannot * use ntfs_attr_find() to search for extent records of non-resident * attributes, as extents with lowest_vcn != 0 are usually described by the * attribute list attribute only. - Note that it is possible that the first * extent is only in the attribute list while the last extent is in the base * mft record, so do not rely on being able to find the first extent in the * base mft record. * * Warning: Never use @val when looking for attribute types which can be * non-resident as this most likely will result in a crash! */static int ntfs_attr_find(const ATTR_TYPE type, const ntfschar *name, const u32 name_len, const IGNORE_CASE_BOOL ic, const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx){ ATTR_RECORD *a; ntfs_volume *vol = ctx->ntfs_ino->vol; ntfschar *upcase = vol->upcase; u32 upcase_len = vol->upcase_len; /* * Iterate over attributes in mft record starting at @ctx->attr, or the * attribute following that, if @ctx->is_first is 'true'. */ if (ctx->is_first) { a = ctx->attr; ctx->is_first = false; } else a = (ATTR_RECORD*)((u8*)ctx->attr + le32_to_cpu(ctx->attr->length)); for (;; a = (ATTR_RECORD*)((u8*)a + le32_to_cpu(a->length))) { if ((u8*)a < (u8*)ctx->mrec || (u8*)a > (u8*)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_allocated)) break; ctx->attr = a; if (unlikely(le32_to_cpu(a->type) > le32_to_cpu(type) || a->type == AT_END)) return -ENOENT; if (unlikely(!a->length)) break; if (a->type != type) continue; /* * If @name is present, compare the two names. If @name is * missing, assume we want an unnamed attribute. */ if (!name) { /* The search failed if the found attribute is named. */ if (a->name_length) return -ENOENT; } else if (!ntfs_are_names_equal(name, name_len, (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), a->name_length, ic, upcase, upcase_len)) { register int rc; rc = ntfs_collate_names(name, name_len, (ntfschar*)((u8*)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) return -ENOENT; /* If the strings are not equal, continue search. */ if (rc) continue; rc = ntfs_collate_names(name, name_len, (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), a->name_length, 1, CASE_SENSITIVE, upcase, upcase_len); if (rc == -1) return -ENOENT; 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, (u8*)a + le16_to_cpu( a->data.resident.value_offset), min_t(u32, val_len, le32_to_cpu( a->data.resident.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->data.resident.value_length); if (val_len == avl) return 0; if (val_len < avl) return -ENOENT; } else if (rc < 0) return -ENOENT; } } ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk."); NVolSetErrors(vol); return -EIO;}/** * load_attribute_list - load an attribute list into memory * @vol: ntfs volume from which to read * @runlist: runlist of the attribute list * @al_start: destination buffer * @size: size of the destination buffer in bytes * @initialized_size: initialized size of the attribute list * * Walk the runlist @runlist and load all clusters from it copying them into * the linear buffer @al. The maximum number of bytes copied to @al is @size * bytes. Note, @size does not need to be a multiple of the cluster size. If * @initialized_size is less than @size, the region in @al between * @initialized_size and @size will be zeroed and not read from disk. * * Return 0 on success or -errno on error. */int load_attribute_list(ntfs_volume *vol, runlist *runlist, u8 *al_start, const s64 size, const s64 initialized_size){ LCN lcn; u8 *al = al_start; u8 *al_end = al + initialized_size; runlist_element *rl; struct buffer_head *bh; struct super_block *sb; unsigned long block_size; unsigned long block, max_block; int err = 0; unsigned char block_size_bits; ntfs_debug("Entering."); if (!vol || !runlist || !al || size <= 0 || initialized_size < 0 || initialized_size > size) return -EINVAL; if (!initialized_size) { memset(al, 0, size); return 0; } sb = vol->sb; block_size = sb->s_blocksize; block_size_bits = sb->s_blocksize_bits; down_read(&runlist->lock); rl = runlist->rl; if (!rl) { ntfs_error(sb, "Cannot read attribute list since runlist is " "missing."); goto err_out; } /* Read all clusters specified by the runlist one run at a time. */ while (rl->length) { lcn = ntfs_rl_vcn_to_lcn(rl, rl->vcn); ntfs_debug("Reading vcn = 0x%llx, lcn = 0x%llx.", (unsigned long long)rl->vcn, (unsigned long long)lcn); /* The attribute list cannot be sparse. */ if (lcn < 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -