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 + -
显示快捷键?