📄 attrib.c
字号:
} /* * Scatter the data from the linear data buffer to the volume. Note, a * partial final vcn is taken care of by the @count capping of write * length. */ ofs = pos - (rl->vcn << vol->cluster_size_bits); for (hole = 0; count; rl++, ofs = 0, hole = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { if (errno == ENOENT) { errno = EIO; ntfs_log_perror("%s: Failed to find VCN" " #2", __FUNCTION__); } goto rl_err_out; } /* Needed for case when runs merged. */ ofs = pos + total - (rl->vcn << vol->cluster_size_bits); } if (!rl->length) { errno = EIO; ntfs_log_perror("%s: Zero run length", __FUNCTION__); goto rl_err_out; } if (rl->lcn < (LCN)0) { hole = rl->vcn + rl->length; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; ntfs_log_perror("%s: Unexpected LCN (%lld)", __FUNCTION__, (long long)rl->lcn); goto rl_err_out; } if (ntfs_attr_fill_hole(na, count, &ofs, &rl, &update_from)) goto err_out; } /* It is a real lcn, write it to the volume. */ to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs);retry: ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " "%lld.\n", to_write, rl->vcn, rl->lcn, ofs); if (!NVolReadOnly(vol)) { s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; u32 bsize = vol->cluster_size; s64 rounded = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; /* * Zero fill to cluster boundary if we're writing to an * ex-sparse cluster or we're at the end of the attribute. * This will cause the kernel not to seek and read disk * blocks during write(2) to fill the end of the buffer * which increases write speed by 2-10 fold typically. */ if ((rounded && (wend < (hole << vol->cluster_size_bits))) || (((to_write % bsize) && (ofs + to_write == na->initialized_size)))) { char *cb; rounded += to_write; cb = ntfs_malloc(rounded); if (!cb) goto err_out; memcpy(cb, b, to_write); memset(cb + to_write, 0, rounded - to_write); written = ntfs_pwrite(vol->dev, wpos, rounded, cb); if (written == rounded) written = to_write; free(cb); } else written = ntfs_pwrite(vol->dev, wpos, to_write, b); } else written = to_write; /* If everything ok, update progress counters and continue. */ if (written > 0) { total += written; count -= written; b = (const u8*)b + written; continue; } /* If the syscall was interrupted, try again. */ if (written == (s64)-1 && errno == EINTR) goto retry; if (!written) errno = EIO; goto rl_err_out; }done: if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ if (update_from != -1) if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { /* * FIXME: trying to recover by goto rl_err_out; * could cause driver hang by infinite looping. */ total = -1; goto out; }out: ntfs_log_leave("\n"); return total;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; ntfs_log_perror("%s", __FUNCTION__); 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. */ ntfs_log_perror("%s #1", __FUNCTION__); if (!i) return err; bk_cnt = i; break; } } /* Write the prepared data. */ written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); if (written <= 0) { ntfs_log_perror("%s: written=%lld", __FUNCTION__, (long long)written); } /* 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -