📄 attrib.c
字号:
rl_err_out: eo = errno; if (total) { if (need_to.undo_initialized_size) { if (pos + total > na->initialized_size) goto done; /* * TODO: Need to try to change initialized_size. If it * succeeds goto done, otherwise goto err_out. (AIA) */ errno = EOPNOTSUPP; goto err_out; } goto done; } errno = eo;err_out: eo = errno; if (need_to.undo_initialized_size) { int err; err = 0; if (!ctx) { ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) err = 1; } else ntfs_attr_reinit_search_ctx(ctx); if (!err) { err = ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, ctx); if (!err) { na->initialized_size = old_initialized_size; ctx->attr->initialized_size = cpu_to_sle64( old_initialized_size); err = ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec); } } if (err) { /* * FIXME: At this stage could try to recover by filling * old_initialized_size -> new_initialized_size with * data or at least zeroes. (AIA) */ ntfs_log_error("Eeek! Failed to recover from error. " "Leaving metadata in inconsistent " "state! Run chkdsk!\n"); } } if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ if (update_from != -1) ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); /* Restore original data_size if needed. */ if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) ntfs_log_perror("Failed to restore data_size"); errno = eo;errno_set: total = -1; goto out;}/** * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read * @na: multi sector transfer protected ntfs attribute to read from * @pos: byte position in the attribute to begin reading from * @bk_cnt: number of mst protected blocks to read * @bk_size: size of each mst protected block in bytes * @dst: output data buffer * * This function will read @bk_cnt blocks of size @bk_size bytes each starting * at offset @pos from the ntfs attribute @na into the data buffer @b. * * On success, the multi sector transfer fixups are applied and the number of * read blocks is returned. If this number is lower than @bk_cnt this means * that the read has either reached end of attribute or that an error was * encountered during the read so that the read is partial. 0 means end of * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). * * On error and nothing has been read, return -1 with errno set appropriately * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid * arguments. * * NOTE: If an incomplete multi sector transfer is detected the magic is * changed to BAAD but no error is returned, i.e. it is possible that any of * the returned blocks have multi sector transfer errors. This should be * detected by the caller by checking each block with is_baad_recordp(&block). * The reasoning is that we want to fixup as many blocks as possible and we * want to return even bad ones to the caller so, e.g. in case of ntfsck, the * errors can be repaired. */s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, const u32 bk_size, void *dst){ s64 br; u8 *end; ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, (long long)pos); if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { errno = EINVAL; return -1; } br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); if (br <= 0) return br; br /= bk_size; for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + bk_size) ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size); /* Finally, return the number of blocks read. */ return br;}/** * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write * @na: multi sector transfer protected ntfs attribute to write to * @pos: position in the attribute to write to * @bk_cnt: number of mst protected blocks to write * @bk_size: size of each mst protected block in bytes * @src: data buffer to write to disk * * This function will write @bk_cnt blocks of size @bk_size bytes each from * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na * at position @pos. * * On success, return the number of successfully written blocks. If this number * is lower than @bk_cnt this means that an error was encountered during the * write so that the write is partial. 0 means nothing was written (also * return 0 when @bk_cnt or @bk_size are 0). * * On error and nothing has been written, return -1 with errno set * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case * of invalid arguments. * * NOTE: We mst protect the data, write it, then mst deprotect it using a quick * deprotect algorithm (no checking). This saves us from making a copy before * the write and at the same time causes the usn to be incremented in the * buffer. This conceptually fits in better with the idea that cached data is * always deprotected and protection is performed when the data is actually * going to hit the disk and the cache is immediately deprotected again * simulating an mst read on the written data. This way cache coherency is * achieved. */s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, const u32 bk_size, void *src){ s64 written, i; ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", (unsigned long long)na->ni->mft_no, na->type, (long long)pos); if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { errno = EINVAL; return -1; } if (!bk_cnt) return 0; /* Prepare data for writing. */ for (i = 0; i < bk_cnt; ++i) { int err; err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) ((u8*)src + i * bk_size), bk_size); if (err < 0) { /* Abort write at this position. */ if (!i) return err; bk_cnt = i; break; } } /* Write the prepared data. */ written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); /* Quickly deprotect the data again. */ for (i = 0; i < bk_cnt; ++i) ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * bk_size)); if (written <= 0) return written; /* Finally, return the number of complete blocks written. */ return written / bk_size;}/** * 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 shouldn't need to call this function directly. Use lookup_attr() 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 found, ntfs_attr_find() * returns 0 and @ctx->attr will point to the found attribute. * * If not found, ntfs_attr_find() returns -1, with errno set to 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 -1 with errno set to the error * code but not to ENOENT. 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 @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_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_attr_find() will return the next attribute in the * mft record @ctx->mrec. * * If @type is AT_END, seek to the end 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. This also allows us * to interoperate cleanly with ntfs_external_attr_find(). * * 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. * * If @ic is IGNORE_CASE, the @name comparison 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 don't * 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_TYPES 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; ntfschar *upcase; u32 upcase_len; ntfs_log_trace("attribute type 0x%x.\n", type); if (ctx->ntfs_ino) { vol = ctx->ntfs_ino->vol; upcase = vol->upcase; upcase_len = vol->upcase_len; } else { if (name && name != AT_UNNAMED) { errno = EINVAL; return -1; } vol = NULL; upcase = NULL; upcase_len = 0; } /* * 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*)((char*)ctx->attr + le32_to_cpu(ctx->attr->length)); for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + le32_to_cpu(ctx->mrec->bytes_allocated)) break; ctx->attr = a; if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > le32_to_cpu(type))) || (a->type == AT_END)) { errno = ENOENT; return -1; } if (!a->length) break; /* If this is an enumeration return this attribute. */ if (type == AT_UNUSED) return 0; if (a->type != type) continue; /* * 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) { /* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -