inode.c
来自「一个类似windows」· C语言 代码 · 共 2,026 行 · 第 1/5 页
C
2,026 行
* Maximum sanity checking as we are called on an inode that
* we suspect might be corrupt.
*/
p = (u8*)attr + le32_to_cpu(attr->length);
if (p < (u8*)ctx->mrec || (u8*)p > (u8*)ctx->mrec +
le32_to_cpu(ctx->mrec->bytes_in_use)) {
err_corrupt_attr:
ntfs_error(ctx->ntfs_ino->vol->sb, "Corrupt file name "
"attribute. You should run chkdsk.");
return -EIO;
}
if (attr->non_resident) {
ntfs_error(ctx->ntfs_ino->vol->sb, "Non-resident file "
"name. You should run chkdsk.");
return -EIO;
}
if (attr->flags) {
ntfs_error(ctx->ntfs_ino->vol->sb, "File name with "
"invalid flags. You should run "
"chkdsk.");
return -EIO;
}
if (!(attr->data.resident.flags & RESIDENT_ATTR_IS_INDEXED)) {
ntfs_error(ctx->ntfs_ino->vol->sb, "Unindexed file "
"name. You should run chkdsk.");
return -EIO;
}
file_name_attr = (FILE_NAME_ATTR*)((u8*)attr +
le16_to_cpu(attr->data.resident.value_offset));
p2 = (u8*)attr + le32_to_cpu(attr->data.resident.value_length);
if (p2 < (u8*)attr || p2 > p)
goto err_corrupt_attr;
/* This attribute is ok, but is it in the $Extend directory? */
if (MREF_LE(file_name_attr->parent_directory) == FILE_Extend)
return 1; /* YES, it's an extended system file. */
}
if (nr_links) {
ntfs_error(ctx->ntfs_ino->vol->sb, "Inode hard link count "
"doesn't match number of name attributes. You "
"should run chkdsk.");
return -EIO;
}
return 0; /* NO, it is not an extended system file. */
}
/**
* ntfs_read_locked_inode - read an inode from its device
* @vi: inode to read
*
* ntfs_read_locked_inode() is called from ntfs_iget() to read the inode
* described by @vi into memory from the device.
*
* The only fields in @vi that we need to/can look at when the function is
* called are i_sb, pointing to the mounted device's super block, and i_ino,
* the number of the inode to load. If this is a fake inode, i.e. NInoAttr(),
* then the fields type, name, and name_len are also valid, and describe the
* attribute which this fake inode represents.
*
* ntfs_read_locked_inode() maps, pins and locks the mft record number i_ino
* for reading and sets up the necessary @vi fields as well as initializing
* the ntfs inode.
*
* Q: What locks are held when the function is called?
* A: i_state has I_LOCK set, hence the inode is locked, also
* i_count is set to 1, so it is not going to go away
* i_flags is set to 0 and we have no business touching it. Only an ioctl()
* is allowed to write to them. We should of course be honouring them but
* we need to do that using the IS_* macros defined in include/linux/fs.h.
* In any case ntfs_read_locked_inode() has nothing to do with i_flags.
*
* Return 0 on success and -errno on error. In the error case, the inode will
* have had make_bad_inode() executed on it.
*/
static int ntfs_read_locked_inode(struct inode *vi)
{
ntfs_volume *vol = NTFS_SB(vi->i_sb);
ntfs_inode *ni;
MFT_RECORD *m;
STANDARD_INFORMATION *si;
attr_search_context *ctx;
int err = 0;
ntfs_debug("Entering for i_ino 0x%lx.", vi->i_ino);
/* Setup the generic vfs inode parts now. */
/* This is the optimal IO size (for stat), not the fs block size. */
vi->i_blksize = PAGE_CACHE_SIZE;
/*
* This is for checking whether an inode has changed w.r.t. a file so
* that the file can be updated if necessary (compare with f_version).
*/
vi->i_version = 1;
vi->i_uid = vol->uid;
vi->i_gid = vol->gid;
vi->i_mode = 0;
/*
* Initialize the ntfs specific part of @vi special casing
* FILE_MFT which we need to do at mount time.
*/
if (vi->i_ino != FILE_MFT)
ntfs_init_big_inode(vi);
ni = NTFS_I(vi);
m = map_mft_record(ni);
if (IS_ERR(m)) {
err = PTR_ERR(m);
goto err_out;
}
ctx = get_attr_search_ctx(ni, m);
if (!ctx) {
err = -ENOMEM;
goto unm_err_out;
}
if (!(m->flags & MFT_RECORD_IN_USE)) {
ntfs_error(vi->i_sb, "Inode is not in use! You should "
"run chkdsk.");
goto unm_err_out;
}
if (m->base_mft_record) {
ntfs_error(vi->i_sb, "Inode is an extent inode! You should "
"run chkdsk.");
goto unm_err_out;
}
/* Transfer information from mft record into vfs and ntfs inodes. */
ni->seq_no = le16_to_cpu(m->sequence_number);
/*
* FIXME: Keep in mind that link_count is two for files which have both
* a long file name and a short file name as separate entries, so if
* we are hiding short file names this will be too high. Either we need
* to account for the short file names by subtracting them or we need
* to make sure we delete files even though i_nlink is not zero which
* might be tricky due to vfs interactions. Need to think about this
* some more when implementing the unlink command.
*/
vi->i_nlink = le16_to_cpu(m->link_count);
/*
* FIXME: Reparse points can have the directory bit set even though
* they would be S_IFLNK. Need to deal with this further below when we
* implement reparse points / symbolic links but it will do for now.
* Also if not a directory, it could be something else, rather than
* a regular file. But again, will do for now.
*/
if (m->flags & MFT_RECORD_IS_DIRECTORY) {
vi->i_mode |= S_IFDIR;
/* Things break without this kludge! */
if (vi->i_nlink > 1)
vi->i_nlink = 1;
} else
vi->i_mode |= S_IFREG;
/*
* Find the standard information attribute in the mft record. At this
* stage we haven't setup the attribute list stuff yet, so this could
* in fact fail if the standard information is in an extent record, but
* I don't think this actually ever happens.
*/
if (!lookup_attr(AT_STANDARD_INFORMATION, NULL, 0, 0, 0, NULL, 0,
ctx)) {
/*
* TODO: We should be performing a hot fix here (if the recover
* mount option is set) by creating a new attribute.
*/
ntfs_error(vi->i_sb, "$STANDARD_INFORMATION attribute is "
"missing.");
goto unm_err_out;
}
/* Get the standard information attribute value. */
si = (STANDARD_INFORMATION*)((char*)ctx->attr +
le16_to_cpu(ctx->attr->data.resident.value_offset));
/* Transfer information from the standard information into vfs_ino. */
/*
* Note: The i_?times do not quite map perfectly onto the NTFS times,
* but they are close enough, and in the end it doesn't really matter
* that much...
*/
/*
* mtime is the last change of the data within the file. Not changed
* when only metadata is changed, e.g. a rename doesn't affect mtime.
*/
vi->i_mtime.tv_sec = ntfs2utc(si->last_data_change_time);
vi->i_mtime.tv_nsec = 0;
/*
* ctime is the last change of the metadata of the file. This obviously
* always changes, when mtime is changed. ctime can be changed on its
* own, mtime is then not changed, e.g. when a file is renamed.
*/
vi->i_ctime.tv_sec = ntfs2utc(si->last_mft_change_time);
vi->i_ctime.tv_nsec = 0;
/*
* Last access to the data within the file. Not changed during a rename
* for example but changed whenever the file is written to.
*/
vi->i_atime.tv_sec = ntfs2utc(si->last_access_time);
vi->i_atime.tv_nsec = 0;
/* Find the attribute list attribute if present. */
reinit_attr_search_ctx(ctx);
if (lookup_attr(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx)) {
if (vi->i_ino == FILE_MFT)
goto skip_attr_list_load;
ntfs_debug("Attribute list found in inode 0x%lx.", vi->i_ino);
NInoSetAttrList(ni);
if (ctx->attr->flags & ATTR_IS_ENCRYPTED ||
ctx->attr->flags & ATTR_COMPRESSION_MASK ||
ctx->attr->flags & ATTR_IS_SPARSE) {
ntfs_error(vi->i_sb, "Attribute list attribute is "
"compressed/encrypted/sparse. Not "
"allowed. Corrupt inode. You should "
"run chkdsk.");
goto unm_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(vi->i_sb, "Not enough memory to allocate "
"buffer for attribute list.");
err = -ENOMEM;
goto unm_err_out;
}
if (ctx->attr->non_resident) {
NInoSetAttrListNonResident(ni);
if (ctx->attr->data.non_resident.lowest_vcn) {
ntfs_error(vi->i_sb, "Attribute list has non "
"zero lowest_vcn. Inode is "
"corrupt. You should run "
"chkdsk.");
goto unm_err_out;
}
/*
* Setup the run list. No need for locking as we have
* exclusive access to the inode at this time.
*/
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(vi->i_sb, "Mapping pairs "
"decompression failed with "
"error code %i. Corrupt "
"attribute list in inode.",
-err);
goto unm_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(vi->i_sb, "Failed to load "
"attribute list attribute.");
goto unm_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(vi->i_sb, "Corrupt attribute list "
"in inode.");
goto unm_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));
}
}
skip_attr_list_load:
/*
* If an attribute list is present we now have the attribute list value
* in ntfs_ino->attr_list and it is ntfs_ino->attr_list_size bytes.
*/
if (S_ISDIR(vi->i_mode)) {
struct inode *bvi;
ntfs_inode *bni;
INDEX_ROOT *ir;
char *ir_end, *index_end;
/* It is a directory, find index root attribute. */
reinit_attr_search_ctx(ctx);
if (!lookup_attr(AT_INDEX_ROOT, I30, 4, CASE_SENSITIVE, 0,
NULL, 0, ctx)) {
// FIXME: File is corrupt! Hot-fix with empty index
// root attribute if recovery option is set.
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
"missing.");
goto unm_err_out;
}
/* Set up the state. */
if (ctx->attr->non_resident) {
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
"not resident. Not allowed.");
goto unm_err_out;
}
/*
* Compressed/encrypted index root just means that the newly
* created files in that directory should be created compressed/
* encrypted. However index root cannot be both compressed and
* encrypted.
*/
if (ctx->attr->flags & ATTR_COMPRESSION_MASK)
NInoSetCompressed(ni);
if (ctx->attr->flags & ATTR_IS_ENCRYPTED) {
if (ctx->attr->flags & ATTR_COMPRESSION_MASK) {
ntfs_error(vi->i_sb, "Found encrypted and "
"compressed attribute. Not "
"allowed.");
goto unm_err_out;
}
NInoSetEncrypted(ni);
}
if (ctx->attr->flags & ATTR_IS_SPARSE)
NInoSetSparse(ni);
ir = (INDEX_ROOT*)((char*)ctx->attr + le16_to_cpu(
ctx->attr->data.resident.value_offset));
ir_end = (char*)ir + le32_to_cpu(
ctx->attr->data.resident.value_length);
if (ir_end > (char*)ctx->mrec + vol->mft_record_size) {
ntfs_error(vi->i_sb, "$INDEX_ROOT attribute is "
"corrupt.");
goto unm_err_out;
}
index_end = (char*)&ir->index +
le32_to_cpu(ir->index.index_length);
if (index_end > ir_end) {
ntfs_error(vi->i_sb, "Directory index is corrupt.");
goto unm_err_out;
}
if (ir->type != AT_FILE_NAME) {
ntfs_error(vi->i_sb, "Indexed attribute is not "
"$FILE_NAME. Not allowed.");
goto unm_err_out;
}
if (ir->collation_rule != COLLATION_FILE_NAME) {
ntfs_error(vi->i_sb, "Index collation rule is not "
"COLLATION_FILE_NAME. Not allowed.");
goto unm_err_out;
}
ni->itype.index.block_size = le32_to_cpu(ir->index_block_size);
if (ni->itype.index.block_size &
(ni->itype.index.block_size - 1)) {
ntfs_error(vi->i_sb, "Index block size (%u) is not a "
"power of two.",
ni->itype.index.block_size);
goto unm_err_out;
}
if (ni->itype.index.block_size > PAGE_CACHE_SIZE) {
ntfs_error(vi->i_sb, "Index block size (%u) > "
"PAGE_CACHE_SIZE (%ld) is not "
"supported. Sorry.",
ni->itype.index.block_size,
PAGE_CACHE_SIZE);
err = -EOPNOTSUPP;
goto unm_err_out;
}
if (ni->itype.index.block_size < NTFS_BLOCK_SIZE) {
ntfs_error(vi->i_sb, "Index block size (%u) < "
"NTFS_BLOCK_SIZE (%i) is not "
"supported. Sorry.",
ni->itype.index.block_size,
NTFS_BLOCK_SIZE);
err = -EOPNOTSUPP;
goto unm_err_out;
}
ni->itype.index.block_size_bits =
ffs(ni->itype.index.block_size) - 1;
/* Determine the size of a vcn in the directory index. */
if (vol->cluster_size <= ni->itype.index.block_size) {
ni->itype.index.vcn_size = vol->cluster_size;
ni->itype.index.vcn_size_bits = vol->cluster_size_bits;
} else {
ni->itype.index.vcn_size = vol->sector_size;
ni->itype.index.vcn_size_bits = vol->sector_size_bits;
}
/* Setup the index allocation attribute, even if not present. */
NInoSetMstProtected(ni);
ni->type = AT_INDEX_ALLOCATION;
ni->name = I30;
ni->name_len = 4;
if (!(ir->index.flags & LARGE_INDEX)) {
/* No index allocation. */
vi->i_size = ni->initialized_size =
ni->allocated_size = 0;
/* We are done with the mft record, so we release it. */
put_attr_search_ctx(ctx);
unmap_mft_record(ni);
m = NULL;
ctx = NULL;
goto skip_large_dir_stuff;
} /* LARGE_INDEX: Index allocation present. Setup state. */
NInoSetIndexAllocPresent(ni);
/* Find index allocation attribute. */
reinit_attr_search_ctx(ctx);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?