📄 attrib.c
字号:
if (errno == ENOENT) errno = EIO; goto rl_err_out; } /* Needed for case when runs merged. */ ofs = pos + total - (rl->vcn << vol->cluster_size_bits); } if (!rl->length) goto rl_err_out; if (rl->lcn < (LCN)0) { if (rl->lcn != (LCN)LCN_HOLE) goto rl_err_out; /* It is a hole, just zero the matching @b range. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs); memset(b, 0, to_read); /* Update progress counters. */ total += to_read; count -= to_read; b = (u8*)b + to_read; continue; } /* It is a real lcn, read it into @dst. */ to_read = min(count, (rl->length << vol->cluster_size_bits) - ofs);retry: ntfs_log_trace("Reading 0x%llx bytes from vcn 0x%llx, lcn 0x%llx, " "ofs 0x%llx.\n", to_read, rl->vcn, rl->lcn, ofs); br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, to_read, b); /* If everything ok, update progress counters and continue. */ if (br > 0) { total += br; count -= br; b = (u8*)b + br; continue; } /* If the syscall was interrupted, try again. */ if (br == (s64)-1 && errno == EINTR) goto retry; if (total) return total; if (!br) errno = EIO; return -1; } /* Finally, return the number of bytes read. */ return total + total2;rl_err_out: if (total) return total; errno = EIO; return -1;}/** * ntfs_attr_pwrite - positioned write to an ntfs attribute * @na: ntfs attribute to write to * @pos: position in the attribute to write to * @count: number of bytes to write * @b: data buffer to write to disk * * This function will write @count bytes from data buffer @b to ntfs attribute * @na at position @pos. * * On success, return the number of successfully written bytes. If this number * is lower than @count 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 @count is 0). * * On error and nothing has been written, return -1 with errno set * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of * invalid arguments. */s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b){ s64 written, to_write, ofs, total, old_initialized_size, old_data_size; VCN update_from = -1; ntfs_volume *vol; ntfs_attr_search_ctx *ctx = NULL; runlist_element *rl; int eo; struct { unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; unsigned int update_mapping_pairs : 1; } need_to = { 0, 0, 0 }; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count " "0x%llx.\n", na->ni->mft_no, na->type, (long long)pos, (long long)count); if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { errno = EINVAL; return -1; } vol = na->ni->vol; /* * 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; } /* If this is a compressed attribute it needs special treatment. */ if (NAttrCompressed(na)) { // TODO: Implement writing compressed attributes! (AIA) // return ntfs_attr_pwrite_compressed(ntfs_attr *na, // const s64 pos, s64 count, void *b); errno = EOPNOTSUPP; return -1; } /* Update access and change times if needed. */ if (na->type == AT_DATA || na->type == AT_INDEX_ROOT || na->type == AT_INDEX_ALLOCATION) ntfs_inode_update_time(na->ni); if (!count) return 0; /* If the write reaches beyond the end, extend the attribute. */ old_data_size = na->data_size; if (pos + count > na->data_size) { if (ntfs_attr_truncate(na, pos + count)) { eo = errno; ntfs_log_trace("Attribute extend failed.\n"); errno = eo; return -1; } need_to.undo_data_size = 1; } old_initialized_size = na->initialized_size; /* If it is a resident attribute, write the data to the mft record. */ if (!NAttrNonResident(na)) { char *val; ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) goto err_out; if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, ctx)) goto err_out; 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; goto err_out; } memcpy(val + pos, b, count); if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec)) { /* * NOTE: We are in a bad state at this moment. We have * dirtied the mft record but we failed to commit it to * disk. Since we have read the mft record ok before, * it is unlikely to fail writing it, so is ok to just * return error here... (AIA) */ goto err_out; } ntfs_attr_put_search_ctx(ctx); return count; } total = 0; /* Handle writes beyond initialized_size. */ if (pos + count > na->initialized_size) { if (ntfs_attr_map_whole_runlist(na)) goto err_out; /* Set initialized_size to @pos + @count. */ ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) goto err_out; if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, ctx)) goto err_out; /* If write starts beyond initialized_size, zero the gap. */ if (pos > na->initialized_size) { char *buf; buf = ntfs_malloc(NTFS_BUF_SIZE); if (!buf) goto err_out; memset(buf, 0, NTFS_BUF_SIZE); ofs = na->initialized_size; while (ofs < pos) { to_write = min(pos - ofs, NTFS_BUF_SIZE); written = ntfs_rl_pwrite(vol, na->rl, ofs, to_write, buf); if (written <= 0) { ntfs_log_trace("Failed to zero space between " "initialized size and @pos.\n"); free(buf); goto err_out; } ofs += written; } free(buf); } ctx->attr->initialized_size = cpu_to_sle64(pos + count); if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec)) { /* * Undo the change in the in-memory copy and send it * back for writing. */ ctx->attr->initialized_size = cpu_to_sle64(old_initialized_size); ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec); goto err_out; } na->initialized_size = pos + count; ntfs_attr_put_search_ctx(ctx); ctx = NULL; /* * NOTE: At this point the initialized_size in the mft record * has been updated BUT there is random data on disk thus if * we decide to abort, we MUST change the initialized_size * again. */ need_to.undo_initialized_size = 1; } /* 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 write. * However, we already extended the size of the attribute, * so getting this here must be an error of some kind. */ if (errno == ENOENT) errno = EIO; goto err_out; } /* * 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 (; count; rl++, ofs = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { if (errno == ENOENT) errno = EIO; goto rl_err_out; } /* Needed for case when runs merged. */ ofs = pos + total - (rl->vcn << vol->cluster_size_bits); } if (!rl->length) { errno = EIO; goto rl_err_out; } if (rl->lcn < (LCN)0) { LCN lcn_seek_from = -1; runlist *rlc; VCN cur_vcn, from_vcn; if (rl->lcn != (LCN)LCN_HOLE) { errno = EIO; goto rl_err_out; } to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); /* Instantiate the hole. */ cur_vcn = rl->vcn; from_vcn = rl->vcn + (ofs >> vol->cluster_size_bits); ntfs_log_trace("Instantiate the hole with vcn 0x%llx.\n", cur_vcn); /* * Map whole runlist to be able update mapping pairs * later. */ if (ntfs_attr_map_whole_runlist(na)) goto err_out; /* * Restore @rl, it probably get lost during runlist * mapping. */ rl = ntfs_attr_find_vcn(na, cur_vcn); if (!rl) { ntfs_log_error("BUG! Failed to find run after " "mapping whole runlist. Please " "report to the %s.\n", NTFS_DEV_LIST); errno = EIO; goto err_out; } /* * Search backwards to find the best lcn to start * seek from. */ rlc = rl; while (rlc->vcn) { rlc--; if (rlc->lcn >= 0) { lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn); break; } } if (lcn_seek_from == -1) { /* Backwards search failed, search forwards. */ rlc = rl; while (rlc->length) { rlc++; if (rlc->lcn >= 0) { lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn); break; } } } /* Allocate clusters to instantiate the hole. */ rlc = ntfs_cluster_alloc(vol, from_vcn, ((ofs + to_write - 1) >> vol->cluster_size_bits) + 1 + rl->vcn - from_vcn, lcn_seek_from, DATA_ZONE); if (!rlc) { eo = errno; ntfs_log_trace("Failed to allocate clusters for hole " "instantiating.\n"); errno = eo; goto err_out; } /* Merge runlists. */ rl = ntfs_runlists_merge(na->rl, rlc); if (!rl) { eo = errno; ntfs_log_trace("Failed to merge runlists.\n"); if (ntfs_cluster_free_from_rl(vol, rlc)) { ntfs_log_trace("Failed to free just " "allocated clusters. Leaving " "inconstant metadata. Run chkdsk\n"); } errno = eo; goto err_out; } na->rl = rl; need_to.update_mapping_pairs = 1; if (update_from == -1) update_from = from_vcn; rl = ntfs_attr_find_vcn(na, cur_vcn); if (!rl) { /* * It's definitely a BUG, if we failed to find * @cur_vcn, because we missed it during * instantiating of the hole. */ ntfs_log_error("BUG! Failed to find run after " "instantiating. Please report " "to the %s.\n", NTFS_DEV_LIST); errno = EIO; goto err_out; } /* If leaved part of the hole go to the next run. */ if (rl->lcn < 0) rl++; /* Now LCN shoudn't be less than 0. */ if (rl->lcn < 0) { ntfs_log_error("BUG! LCN is lesser than 0. " "Please report to the %s.\n", NTFS_DEV_LIST); errno = EIO; goto err_out; } if (ofs) { /* * Need to clear region between start of * @cur_vcn cluster and @ofs. */ char *buf; buf = ntfs_malloc(ofs); if (!buf) goto err_out; memset(buf, 0, ofs); if (ntfs_rl_pwrite(vol, na->rl, cur_vcn << vol->cluster_size_bits, ofs, buf) < 0) { eo = errno; ntfs_log_trace("Failed to zero area.\n"); free(buf); errno = eo; goto err_out; } free(buf); } if (rl->vcn < cur_vcn) { /* * Clusters that replaced hole are merged with * previous run, so we need to update offset. */ ofs += (cur_vcn - rl->vcn) << vol->cluster_size_bits; } if (rl->vcn > cur_vcn) { /* * We left part of the hole, so update we need * to update offset */ ofs -= (rl->vcn - cur_vcn) << vol->cluster_size_bits; } } /* 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 0x%llx bytes to vcn 0x%llx, lcn 0x%llx," " ofs 0x%llx.\n", to_write, rl->vcn, rl->lcn, ofs); if (!NVolReadOnly(vol)) written = ntfs_pwrite(vol->dev, (rl->lcn << vol->cluster_size_bits) + ofs, 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -