inode.c
来自「一个类似windows」· C语言 代码 · 共 2,026 行 · 第 1/5 页
C
2,026 行
}
if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is encrypted (mft_no 0x%lx, "
"type 0x%x, name_len %i). "
"Please report you saw this "
"message to linux-ntfs-dev@"
"lists.sf.net", vi->i_ino,
ni->type, ni->name_len);
goto unm_err_out;
}
NInoSetEncrypted(ni);
}
if (ctx->attr->flags & ATTR_IS_SPARSE) {
if (NInoMstProtected(ni)) {
ntfs_error(vi->i_sb, "Found mst protected "
"attribute but the attribute "
"is sparse (mft_no 0x%lx, "
"type 0x%x, name_len %i). "
"Please report you saw this "
"message to linux-ntfs-dev@"
"lists.sf.net", vi->i_ino,
ni->type, ni->name_len);
goto unm_err_out;
}
NInoSetSparse(ni);
}
if (ctx->attr->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "First extent of attribute has "
"non-zero lowest_vcn. Inode is "
"corrupt. You should run chkdsk.");
goto unm_err_out;
}
/* Setup all the sizes. */
vi->i_size = sle64_to_cpu(
ctx->attr->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu(
ctx->attr->data.non_resident.initialized_size);
ni->allocated_size = sle64_to_cpu(
ctx->attr->data.non_resident.allocated_size);
if (NInoCompressed(ni)) {
ni->itype.compressed.size = sle64_to_cpu(
ctx->attr->data.non_resident.
compressed_size);
}
}
/* Setup the operations for this attribute inode. */
vi->i_op = NULL;
vi->i_fop = NULL;
vi->i_mapping->a_ops = &ntfs_aops;
if (!NInoCompressed(ni))
vi->i_blocks = ni->allocated_size >> 9;
else
vi->i_blocks = ni->itype.compressed.size >> 9;
/*
* Make sure the base inode doesn't go away and attach it to the
* attribute inode.
*/
igrab(base_vi);
ni->ext.base_ntfs_ino = base_ni;
ni->nr_extents = -1;
put_attr_search_ctx(ctx);
unmap_mft_record(base_ni);
ntfs_debug("Done.");
return 0;
unm_err_out:
if (!err)
err = -EIO;
if (ctx)
put_attr_search_ctx(ctx);
unmap_mft_record(base_ni);
err_out:
ntfs_error(vi->i_sb, "Failed with error code %i while reading "
"attribute inode (mft_no 0x%lx, type 0x%x, name_len "
"%i.", -err, vi->i_ino, ni->type, ni->name_len);
make_bad_inode(vi);
return err;
}
/**
* ntfs_read_inode_mount - special read_inode for mount time use only
* @vi: inode to read
*
* Read inode FILE_MFT at mount time, only called with super_block lock
* held from within the read_super() code path.
*
* This function exists because when it is called the page cache for $MFT/$DATA
* is not initialized and hence we cannot get at the contents of mft records
* by calling map_mft_record*().
*
* Further it needs to cope with the circular references problem, i.e. can't
* load any attributes other than $ATTRIBUTE_LIST until $DATA is loaded, because
* we don't know where the other extent mft records are yet and again, because
* we cannot call map_mft_record*() yet. Obviously this applies only when an
* attribute list is actually present in $MFT inode.
*
* We solve these problems by starting with the $DATA attribute before anything
* else and iterating using lookup_attr($DATA) over all extents. As each extent
* is found, we decompress_mapping_pairs() including the implied
* merge_run_lists(). Each step of the iteration necessarily provides
* sufficient information for the next step to complete.
*
* This should work but there are two possible pit falls (see inline comments
* below), but only time will tell if they are real pits or just smoke...
*/
void ntfs_read_inode_mount(struct inode *vi)
{
VCN next_vcn, last_vcn, highest_vcn;
s64 block;
struct super_block *sb = vi->i_sb;
ntfs_volume *vol = NTFS_SB(sb);
struct buffer_head *bh;
ntfs_inode *ni;
MFT_RECORD *m = NULL;
ATTR_RECORD *attr;
attr_search_context *ctx;
unsigned int i, nr_blocks;
int err;
ntfs_debug("Entering.");
if (vi->i_ino != FILE_MFT) {
ntfs_error(sb, "Called for inode 0x%lx but only inode %d "
"allowed.", vi->i_ino, FILE_MFT);
goto err_out;
}
/* Initialize the ntfs specific part of @vi. */
ntfs_init_big_inode(vi);
ni = NTFS_I(vi);
/* Setup the data attribute. It is special as it is mst protected. */
NInoSetNonResident(ni);
NInoSetMstProtected(ni);
ni->type = AT_DATA;
ni->name = NULL;
ni->name_len = 0;
/*
* This sets up our little cheat allowing us to reuse the async io
* completion handler for directories.
*/
ni->itype.index.block_size = vol->mft_record_size;
ni->itype.index.block_size_bits = vol->mft_record_size_bits;
/* Very important! Needed to be able to call map_mft_record*(). */
vol->mft_ino = vi;
/* Allocate enough memory to read the first mft record. */
if (vol->mft_record_size > 64 * 1024) {
ntfs_error(sb, "Unsupported mft record size %i (max 64kiB).",
vol->mft_record_size);
goto err_out;
}
i = vol->mft_record_size;
if (i < sb->s_blocksize)
i = sb->s_blocksize;
m = (MFT_RECORD*)ntfs_malloc_nofs(i);
if (!m) {
ntfs_error(sb, "Failed to allocate buffer for $MFT record 0.");
goto err_out;
}
/* Determine the first block of the $MFT/$DATA attribute. */
block = vol->mft_lcn << vol->cluster_size_bits >>
sb->s_blocksize_bits;
nr_blocks = vol->mft_record_size >> sb->s_blocksize_bits;
if (!nr_blocks)
nr_blocks = 1;
/* Load $MFT/$DATA's first mft record. */
for (i = 0; i < nr_blocks; i++) {
bh = sb_bread(sb, block++);
if (!bh) {
ntfs_error(sb, "Device read failed.");
goto err_out;
}
memcpy((char*)m + (i << sb->s_blocksize_bits), bh->b_data,
sb->s_blocksize);
brelse(bh);
}
/* Apply the mst fixups. */
if (post_read_mst_fixup((NTFS_RECORD*)m, vol->mft_record_size)) {
/* FIXME: Try to use the $MFTMirr now. */
ntfs_error(sb, "MST fixup failed. $MFT is corrupt.");
goto err_out;
}
/* Need this to sanity check attribute list references to $MFT. */
ni->seq_no = le16_to_cpu(m->sequence_number);
/* Provides readpage() and sync_page() for map_mft_record(). */
vi->i_mapping->a_ops = &ntfs_mft_aops;
ctx = get_attr_search_ctx(ni, m);
if (!ctx) {
err = -ENOMEM;
goto err_out;
}
/* Find the attribute list attribute if present. */
if (lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) {
ATTR_LIST_ENTRY *al_entry, *next_al_entry;
u8 *al_end;
ntfs_debug("Attribute list attribute found in $MFT.");
NInoSetAttrList(ni);
if (ctx->attr->flags & ATTR_IS_ENCRYPTED ||
ctx->attr->flags & ATTR_COMPRESSION_MASK ||
ctx->attr->flags & ATTR_IS_SPARSE) {
ntfs_error(sb, "Attribute list attribute is "
"compressed/encrypted/sparse. Not "
"allowed. $MFT is corrupt. You should "
"run chkdsk.");
goto put_err_out;
}
/* Now allocate memory for the attribute list. */
ni->attr_list_size = (u32)attribute_value_length(ctx->attr);
ni->attr_list = ntfs_malloc_nofs(ni->attr_list_size);
if (!ni->attr_list) {
ntfs_error(sb, "Not enough memory to allocate buffer "
"for attribute list.");
goto put_err_out;
}
if (ctx->attr->non_resident) {
NInoSetAttrListNonResident(ni);
if (ctx->attr->data.non_resident.lowest_vcn) {
ntfs_error(sb, "Attribute list has non zero "
"lowest_vcn. $MFT is corrupt. "
"You should run chkdsk.");
goto put_err_out;
}
/* Setup the run list. */
ni->attr_list_rl.rl = decompress_mapping_pairs(vol,
ctx->attr, NULL);
if (IS_ERR(ni->attr_list_rl.rl)) {
err = PTR_ERR(ni->attr_list_rl.rl);
ni->attr_list_rl.rl = NULL;
ntfs_error(sb, "Mapping pairs decompression "
"failed with error code %i.",
-err);
goto put_err_out;
}
/* Now load the attribute list. */
if ((err = load_attribute_list(vol, &ni->attr_list_rl,
ni->attr_list, ni->attr_list_size,
sle64_to_cpu(ctx->attr->data.
non_resident.initialized_size)))) {
ntfs_error(sb, "Failed to load attribute list "
"attribute with error code %i.",
-err);
goto put_err_out;
}
} else /* if (!ctx.attr->non_resident) */ {
if ((u8*)ctx->attr + le16_to_cpu(
ctx->attr->data.resident.value_offset) +
le32_to_cpu(
ctx->attr->data.resident.value_length) >
(u8*)ctx->mrec + vol->mft_record_size) {
ntfs_error(sb, "Corrupt attribute list "
"attribute.");
goto put_err_out;
}
/* Now copy the attribute list. */
memcpy(ni->attr_list, (u8*)ctx->attr + le16_to_cpu(
ctx->attr->data.resident.value_offset),
le32_to_cpu(
ctx->attr->data.resident.value_length));
}
/* The attribute list is now setup in memory. */
/*
* FIXME: I don't know if this case is actually possible.
* According to logic it is not possible but I have seen too
* many weird things in MS software to rely on logic... Thus we
* perform a manual search and make sure the first $MFT/$DATA
* extent is in the base inode. If it is not we abort with an
* error and if we ever see a report of this error we will need
* to do some magic in order to have the necessary mft record
* loaded and in the right place in the page cache. But
* hopefully logic will prevail and this never happens...
*/
al_entry = (ATTR_LIST_ENTRY*)ni->attr_list;
al_end = (u8*)al_entry + ni->attr_list_size;
for (;; al_entry = next_al_entry) {
/* Out of bounds check. */
if ((u8*)al_entry < ni->attr_list ||
(u8*)al_entry > al_end)
goto em_put_err_out;
/* Catch the end of the attribute list. */
if ((u8*)al_entry == al_end)
goto em_put_err_out;
if (!al_entry->length)
goto em_put_err_out;
if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
le16_to_cpu(al_entry->length) > al_end)
goto em_put_err_out;
next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
le16_to_cpu(al_entry->length));
if (le32_to_cpu(al_entry->type) >
const_le32_to_cpu(AT_DATA))
goto em_put_err_out;
if (AT_DATA != al_entry->type)
continue;
/* We want an unnamed attribute. */
if (al_entry->name_length)
goto em_put_err_out;
/* Want the first entry, i.e. lowest_vcn == 0. */
if (al_entry->lowest_vcn)
goto em_put_err_out;
/* First entry has to be in the base mft record. */
if (MREF_LE(al_entry->mft_reference) != vi->i_ino) {
/* MFT references do not match, logic fails. */
ntfs_error(sb, "BUG: The first $DATA extent "
"of $MFT is not in the base "
"mft record. Please report "
"you saw this message to "
"linux-ntfs-dev@lists.sf.net");
goto put_err_out;
} else {
/* Sequence numbers must match. */
if (MSEQNO_LE(al_entry->mft_reference) !=
ni->seq_no)
goto em_put_err_out;
/* Got it. All is ok. We can stop now. */
break;
}
}
}
reinit_attr_search_ctx(ctx);
/* Now load all attribute extents. */
attr = NULL;
next_vcn = last_vcn = highest_vcn = 0;
while (lookup_attr(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, ctx)) {
run_list_element *nrl;
/* Cache the current attribute. */
attr = ctx->attr;
/* $MFT must be non-resident. */
if (!attr->non_resident) {
ntfs_error(sb, "$MFT must be non-resident but a "
"resident extent was found. $MFT is "
"corrupt. Run chkdsk.");
goto put_err_out;
}
/* $MFT must be uncompressed and unencrypted. */
if (attr->flags & ATTR_COMPRESSION_MASK ||
attr->flags & ATTR_IS_ENCRYPTED ||
attr->flags & ATTR_IS_SPARSE) {
ntfs_error(sb, "$MFT must be uncompressed, "
"non-sparse, and unencrypted but a "
"compressed/sparse/encrypted extent "
"was found. $MFT is corrupt. Run "
"chkdsk.");
goto put_err_out;
}
/*
* Decompress the mapping pairs array of this extent and merge
* the result into the existing run list. No need for locking
* as we have exclusive access to the inode at this time and we
* are a mount in progress task, too.
*/
nrl = decompress_mapping_pairs(vol, attr, ni->run_list.rl);
if (IS_ERR(nrl)) {
ntfs_error(sb, "decompress_mapping_pairs() failed with "
"error code %ld. $MFT is corrupt.",
PTR_ERR(nrl));
goto put_err_out;
}
ni->run_list.rl = nrl;
/* Are we in the first extent? */
if (!next_vcn) {
u64 ll;
if (attr->data.non_resident.lowest_vcn) {
ntfs_error(sb, "First extent of $DATA "
"attribute has non zero "
"lowest_vcn. $MFT is corrupt. "
"You should run chkdsk.");
goto put_err_out;
}
/* Get the last vcn in the $DATA attribute. */
last_vcn = sle64_to_cpu(
attr->data.non_resident.allocated_size)
>> vol->cluster_size_bits;
/* Fill in the inode size. */
vi->i_size = sle64_to_cpu(
attr->data.non_resident.data_size);
ni->initialized_size = sle64_to_cpu(attr->data.
non_resident.initialized_size);
ni->allocated_size = sle64_to_cpu(
attr->data.non_resident.allocated_size);
/* Set the number of mft records. */
ll = vi->i_size >> vol->mft_record_size_bits;
/*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?