📄 inode.c
字号:
"compressed."); goto unm_err_out; } if (a->data.non_resident.lowest_vcn) { ntfs_error(vi->i_sb, "First extent of $INDEX_ALLOCATION " "attribute has non zero lowest_vcn."); goto unm_err_out; } vi->i_size = sle64_to_cpu(a->data.non_resident.data_size); ni->initialized_size = sle64_to_cpu( a->data.non_resident.initialized_size); ni->allocated_size = sle64_to_cpu(a->data.non_resident.allocated_size); /* * We are done with the mft record, so we release it. Otherwise * we would deadlock in ntfs_attr_iget(). */ ntfs_attr_put_search_ctx(ctx); unmap_mft_record(base_ni); m = NULL; ctx = NULL; /* Get the index bitmap attribute inode. */ bvi = ntfs_attr_iget(base_vi, AT_BITMAP, ni->name, ni->name_len); if (IS_ERR(bvi)) { ntfs_error(vi->i_sb, "Failed to get bitmap attribute."); err = PTR_ERR(bvi); goto unm_err_out; } bni = NTFS_I(bvi); if (NInoCompressed(bni) || NInoEncrypted(bni) || NInoSparse(bni)) { ntfs_error(vi->i_sb, "$BITMAP attribute is compressed and/or " "encrypted and/or sparse."); goto iput_unm_err_out; } /* Consistency check bitmap size vs. index allocation size. */ bvi_size = i_size_read(bvi); if ((bvi_size << 3) < (vi->i_size >> ni->itype.index.block_size_bits)) { ntfs_error(vi->i_sb, "Index bitmap too small (0x%llx) for " "index allocation (0x%llx).", bvi_size << 3, vi->i_size); goto iput_unm_err_out; } iput(bvi);skip_large_index_stuff: /* Setup the operations for this index inode. */ vi->i_op = NULL; vi->i_fop = NULL; vi->i_mapping->a_ops = &ntfs_mst_aops; vi->i_blocks = ni->allocated_size >> 9; /* * Make sure the base inode doesn't go away and attach it to the * index inode. */ igrab(base_vi); ni->ext.base_ntfs_ino = base_ni; ni->nr_extents = -1; ntfs_debug("Done."); return 0;iput_unm_err_out: iput(bvi);unm_err_out: if (!err) err = -EIO; if (ctx) ntfs_attr_put_search_ctx(ctx); if (m) unmap_mft_record(base_ni);err_out: ntfs_error(vi->i_sb, "Failed with error code %i while reading index " "inode (mft_no 0x%lx, name_len %i.", err, vi->i_ino, ni->name_len); make_bad_inode(vi); if (err != -EOPNOTSUPP && err != -ENOMEM) NVolSetErrors(vol); return err;}/* * The MFT inode has special locking, so teach the lock validator * about this by splitting off the locking rules of the MFT from * the locking rules of other inodes. The MFT inode can never be * accessed from the VFS side (or even internally), only by the * map_mft functions. */static struct lock_class_key mft_ni_runlist_lock_key, mft_ni_mrec_lock_key;/** * 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. cannot * load any attributes other than $ATTRIBUTE_LIST until $DATA is loaded, because * we do not 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 ntfs_attr_lookup($DATA) over all extents. As each * extent is found, we ntfs_mapping_pairs_decompress() including the implied * ntfs_runlists_merge(). 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... */int 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 *a; ntfs_attr_search_ctx *ctx; unsigned int i, nr_blocks; int err; ntfs_debug("Entering."); /* 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); NInoSetSparseDisabled(ni); ni->type = AT_DATA; ni->name = NULL; ni->name_len = 0; /* * This sets up our little cheat allowing us to reuse the async read 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. */ vi->i_generation = ni->seq_no = le16_to_cpu(m->sequence_number); /* Provides readpage() and sync_page() for map_mft_record(). */ vi->i_mapping->a_ops = &ntfs_mst_aops; ctx = ntfs_attr_get_search_ctx(ni, m); if (!ctx) { err = -ENOMEM; goto err_out; } /* Find the attribute list attribute if present. */ err = ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, 0, 0, NULL, 0, ctx); if (err) { if (unlikely(err != -ENOENT)) { ntfs_error(sb, "Failed to lookup attribute list " "attribute. You should run chkdsk."); goto put_err_out; } } else /* if (!err) */ { ATTR_LIST_ENTRY *al_entry, *next_al_entry; u8 *al_end; static const char *es = " Not allowed. $MFT is corrupt. " "You should run chkdsk."; ntfs_debug("Attribute list attribute found in $MFT."); NInoSetAttrList(ni); a = ctx->attr; if (a->flags & ATTR_COMPRESSION_MASK) { ntfs_error(sb, "Attribute list attribute is " "compressed.%s", es); goto put_err_out; } if (a->flags & ATTR_IS_ENCRYPTED || a->flags & ATTR_IS_SPARSE) { if (a->non_resident) { ntfs_error(sb, "Non-resident attribute list " "attribute is encrypted/" "sparse.%s", es); goto put_err_out; } ntfs_warning(sb, "Resident attribute list attribute " "in $MFT system file is marked " "encrypted/sparse which is not true. " "However, Windows allows this and " "chkdsk does not detect or correct it " "so we will just ignore the invalid " "flags and pretend they are not set."); } /* Now allocate memory for the attribute list. */ ni->attr_list_size = (u32)ntfs_attr_size(a); 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 (a->non_resident) { NInoSetAttrListNonResident(ni); if (a->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 runlist. */ ni->attr_list_rl.rl = ntfs_mapping_pairs_decompress(vol, a, 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(a->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*)a + le16_to_cpu( a->data.resident.value_offset) + le32_to_cpu( a->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*)a + le16_to_cpu( a->data.resident.value_offset), le32_to_cpu( a->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." "sourceforge.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; } } } ntfs_attr_reinit_search_ctx(ctx); /* Now load all attribute extents. */ a = NULL; next_vcn = last_vcn = highest_vcn = 0; while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, ctx))) { runlist_element *nrl; /* Cache the current attribute. */ a = ctx->attr; /* $MFT must be non-resident. */ if (!a->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 (a->flags & ATTR_COMPRESSION_MASK || a->flags & ATTR_IS_ENCRYPTED || a->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 runlist. 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 = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl); if (IS_ERR(nrl)) { ntfs_error(sb, "ntfs_mapping_pairs_decompress() " "failed with error code %ld. $MFT is " "corrupt.", PTR_ERR(nrl)); goto put_err_out; } ni->runlist.rl = nrl; /* Are we in the first extent? */ if (!next_vcn) { if (a->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(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -