inode.c

来自「一个类似windows」· C语言 代码 · 共 2,026 行 · 第 1/5 页

C
2,026
字号
			 * Verify the number of mft records does not exceed
			 * 2^32 - 1.
			 */
			if (ll >= (1ULL << 32)) {
				ntfs_error(sb, "$MFT is too big! Aborting.");
				goto put_err_out;
			}
			vol->nr_mft_records = ll;
			/*
			 * We have got the first extent of the run_list for
			 * $MFT which means it is now relatively safe to call
			 * the normal ntfs_read_inode() function. Thus, take
			 * us out of the calling chain. Also we need to do this
			 * now because we need ntfs_read_inode() in place to
			 * get at subsequent extents.
			 */
			sb->s_op = &ntfs_sops;
			/*
			 * Complete reading the inode, this will actually
			 * re-read the mft record for $MFT, this time entering
			 * it into the page cache with which we complete the
			 * kick start of the volume. It should be safe to do
			 * this now as the first extent of $MFT/$DATA is
			 * already known and we would hope that we don't need
			 * further extents in order to find the other
			 * attributes belonging to $MFT. Only time will tell if
			 * this is really the case. If not we will have to play
			 * magic at this point, possibly duplicating a lot of
			 * ntfs_read_inode() at this point. We will need to
			 * ensure we do enough of its work to be able to call
			 * ntfs_read_inode() on extents of $MFT/$DATA. But lets
			 * hope this never happens...
			 */
			ntfs_read_locked_inode(vi);
			if (is_bad_inode(vi)) {
				ntfs_error(sb, "ntfs_read_inode() of $MFT "
						"failed. BUG or corrupt $MFT. "
						"Run chkdsk and if no errors "
						"are found, please report you "
						"saw this message to "
						"linux-ntfs-dev@lists.sf.net");
				put_attr_search_ctx(ctx);
				/* Revert to the safe super operations. */
				sb->s_op = &ntfs_mount_sops;
				goto out_now;
			}
			/*
			 * Re-initialize some specifics about $MFT's inode as
			 * ntfs_read_inode() will have set up the default ones.
			 */
			/* Set uid and gid to root. */
			vi->i_uid = vi->i_gid = 0;
			/* Regular file. No access for anyone. */
			vi->i_mode = S_IFREG;
			/* No VFS initiated operations allowed for $MFT. */
			vi->i_op = &ntfs_empty_inode_ops;
			vi->i_fop = &ntfs_empty_file_ops;
			/* Put back our special address space operations. */
			vi->i_mapping->a_ops = &ntfs_mft_aops;
		}

		/* Get the lowest vcn for the next extent. */
		highest_vcn = sle64_to_cpu(attr->data.non_resident.highest_vcn);
		next_vcn = highest_vcn + 1;

		/* Only one extent or error, which we catch below. */
		if (next_vcn <= 0)
			break;

		/* Avoid endless loops due to corruption. */
		if (next_vcn < sle64_to_cpu(
				attr->data.non_resident.lowest_vcn)) {
			ntfs_error(sb, "$MFT has corrupt attribute list "
					"attribute. Run chkdsk.");
			goto put_err_out;
		}
	}
	if (!attr) {
		ntfs_error(sb, "$MFT/$DATA attribute not found. $MFT is "
				"corrupt. Run chkdsk.");
		goto put_err_out;
	}
	if (highest_vcn && highest_vcn != last_vcn - 1) {
		ntfs_error(sb, "Failed to load the complete run list "
				"for $MFT/$DATA. Driver bug or "
				"corrupt $MFT. Run chkdsk.");
		ntfs_debug("highest_vcn = 0x%Lx, last_vcn - 1 = 0x%Lx",
				(long long)highest_vcn,
				(long long)last_vcn - 1);
		goto put_err_out;
	}
	put_attr_search_ctx(ctx);
	ntfs_debug("Done.");
out_now:
	ntfs_free(m);
	return;
em_put_err_out:
	ntfs_error(sb, "Couldn't find first extent of $DATA attribute in "
			"attribute list. $MFT is corrupt. Run chkdsk.");
put_err_out:
	put_attr_search_ctx(ctx);
err_out:
	/* Make sure we revert to the safe super operations. */
	sb->s_op = &ntfs_mount_sops;
	ntfs_error(sb, "Failed. Marking inode as bad.");
	make_bad_inode(vi);
	goto out_now;
}

/**
 * ntfs_dirty_inode - mark the inode's metadata dirty
 * @vi:		inode to mark dirty
 *
 * This is called from fs/inode.c::__mark_inode_dirty(), when the inode itself
 * is being marked dirty. An example is when update_atime() is invoked.
 *
 * We mark the inode dirty by setting both the page in which the mft record
 * resides and the buffer heads in that page which correspond to the mft record
 * dirty. This ensures that the changes will eventually be propagated to disk
 * when the inode is set dirty.
 *
 * FIXME: Can we do that with the buffer heads? I am not too sure. Because if we
 * do that we need to make sure that the kernel will not write out those buffer
 * heads or we are screwed as it will write corrupt data to disk. The only way
 * a mft record can be written correctly is by mst protecting it, writting it
 * synchronously and fast mst deprotecting it. During this period, obviously,
 * the mft record must be marked as not uptodate, be locked for writing or
 * whatever, so that nobody attempts anything stupid.
 *
 * FIXME: Do we need to check that the fs is not mounted read only? And what
 * about the inode? Anything else?
 *
 * FIXME: As we are only a read only driver it is safe to just return here for
 * the moment.
 */
void ntfs_dirty_inode(struct inode *vi)
{
	ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
	NInoSetDirty(NTFS_I(vi));
	return;
}

/**
 * ntfs_commit_inode - write out a dirty inode
 * @ni:		inode to write out
 *
 */
int ntfs_commit_inode(ntfs_inode *ni)
{
	ntfs_debug("Entering for inode 0x%lx.", ni->mft_no);
	NInoClearDirty(ni);
	return 0;
}

/**
 * ntfs_put_inode - handler for when the inode reference count is decremented
 * @vi:		vfs inode
 *
 * The VFS calls ntfs_put_inode() every time the inode reference count (i_count)
 * is about to be decremented (but before the decrement itself.
 *
 * If the inode @vi is a directory with a single reference, we need to put the
 * attribute inode for the directory index bitmap, if it is present, otherwise
 * the directory inode would remain pinned for ever (or rather until umount()
 * time.
 */
void ntfs_put_inode(struct inode *vi)
{
	if (S_ISDIR(vi->i_mode) && (atomic_read(&vi->i_count) == 2)) {
		ntfs_inode *ni;

		ni = NTFS_I(vi);
		if (NInoIndexAllocPresent(ni) && ni->itype.index.bmp_ino) {
			iput(ni->itype.index.bmp_ino);
			ni->itype.index.bmp_ino = NULL;
		}
	}
	return;
}

void __ntfs_clear_inode(ntfs_inode *ni)
{
	int err;

	ntfs_debug("Entering for inode 0x%lx.", ni->mft_no);
	if (NInoDirty(ni)) {
		err = ntfs_commit_inode(ni);
		if (err) {
			ntfs_error(ni->vol->sb, "Failed to commit dirty "
					"inode synchronously.");
			// FIXME: Do something!!!
		}
	}
	/* Synchronize with ntfs_commit_inode(). */
	down(&ni->mrec_lock);
	up(&ni->mrec_lock);
	if (NInoDirty(ni)) {
		ntfs_error(ni->vol->sb, "Failed to commit dirty inode "
				"asynchronously.");
		// FIXME: Do something!!!
	}
	/* No need to lock at this stage as no one else has a reference. */
	if (ni->nr_extents > 0) {
		int i;

		// FIXME: Handle dirty case for each extent inode!
		for (i = 0; i < ni->nr_extents; i++)
			ntfs_clear_extent_inode(ni->ext.extent_ntfs_inos[i]);
		kfree(ni->ext.extent_ntfs_inos);
	}
	/* Free all alocated memory. */
	down_write(&ni->run_list.lock);
	if (ni->run_list.rl) {
		ntfs_free(ni->run_list.rl);
		ni->run_list.rl = NULL;
	}
	up_write(&ni->run_list.lock);

	if (ni->attr_list) {
		ntfs_free(ni->attr_list);
		ni->attr_list = NULL;
	}

	down_write(&ni->attr_list_rl.lock);
	if (ni->attr_list_rl.rl) {
		ntfs_free(ni->attr_list_rl.rl);
		ni->attr_list_rl.rl = NULL;
	}
	up_write(&ni->attr_list_rl.lock);

	if (ni->name_len && ni->name != I30) {
		/* Catch bugs... */
		BUG_ON(!ni->name);
		kfree(ni->name);
	}
}

void ntfs_clear_extent_inode(ntfs_inode *ni)
{
	__ntfs_clear_inode(ni);

	/* Bye, bye... */
	ntfs_destroy_extent_inode(ni);
}

/**
 * ntfs_clear_big_inode - clean up the ntfs specific part of an inode
 * @vi:		vfs inode pending annihilation
 *
 * When the VFS is going to remove an inode from memory, ntfs_clear_big_inode()
 * is called, which deallocates all memory belonging to the NTFS specific part
 * of the inode and returns.
 *
 * If the MFT record is dirty, we commit it before doing anything else.
 */
void ntfs_clear_big_inode(struct inode *vi)
{
	ntfs_inode *ni = NTFS_I(vi);

	__ntfs_clear_inode(ni);

	if (NInoAttr(ni)) {
		/* Release the base inode if we are holding it. */
		if (ni->nr_extents == -1) {
			iput(VFS_I(ni->ext.base_ntfs_ino));
			ni->nr_extents = 0;
			ni->ext.base_ntfs_ino = NULL;
		}
	}
	return;
}

/**
 * ntfs_show_options - show mount options in /proc/mounts
 * @sf:		seq_file in which to write our mount options
 * @mnt:	vfs mount whose mount options to display
 *
 * Called by the VFS once for each mounted ntfs volume when someone reads
 * /proc/mounts in order to display the NTFS specific mount options of each
 * mount. The mount options of the vfs mount @mnt are written to the seq file
 * @sf and success is returned.
 */
int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
{
	ntfs_volume *vol = NTFS_SB(mnt->mnt_sb);
	int i;

	seq_printf(sf, ",uid=%i", vol->uid);
	seq_printf(sf, ",gid=%i", vol->gid);
	if (vol->fmask == vol->dmask)
		seq_printf(sf, ",umask=0%o", vol->fmask);
	else {
		seq_printf(sf, ",fmask=0%o", vol->fmask);
		seq_printf(sf, ",dmask=0%o", vol->dmask);
	}
	seq_printf(sf, ",nls=%s", vol->nls_map->charset);
	if (NVolCaseSensitive(vol))
		seq_printf(sf, ",case_sensitive");
	if (NVolShowSystemFiles(vol))
		seq_printf(sf, ",show_sys_files");
	for (i = 0; on_errors_arr[i].val; i++) {
		if (on_errors_arr[i].val & vol->on_errors)
			seq_printf(sf, ",errors=%s", on_errors_arr[i].str);
	}
	seq_printf(sf, ",mft_zone_multiplier=%i", vol->mft_zone_multiplier);
	return 0;
}

#ifdef NTFS_RW

/**
 * ntfs_truncate - called when the i_size of an ntfs inode is changed
 * @vi:		inode for which the i_size was changed
 *
 * We don't support i_size changes yet.
 *
 * Called with ->i_sem held.
 */
void ntfs_truncate(struct inode *vi)
{
	// TODO: Implement...
	ntfs_warning(vi->i_sb, "Eeek: i_size may have changed! If you see "
			"this right after a message from "
			"ntfs_{prepare,commit}_{,nonresident_}write() then "
			"just ignore it. Otherwise it is bad news.");
	// TODO: reset i_size now!
	return;
}

/**
 * ntfs_setattr - called from notify_change() when an attribute is being changed
 * @dentry:	dentry whose attributes to change
 * @attr:	structure describing the attributes and the changes
 *
 * We have to trap VFS attempts to truncate the file described by @dentry as
 * soon as possible, because we do not implement changes in i_size yet. So we
 * abort all i_size changes here.
 *
 * Called with ->i_sem held.
 *
 * Basically this is a copy of generic notify_change() and inode_setattr()
 * functionality, except we intercept and abort changes in i_size.
 */
int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
{
	struct inode *vi;
	int err;
	unsigned int ia_valid = attr->ia_valid;

	vi = dentry->d_inode;

	err = inode_change_ok(vi, attr);
	if (err)
		return err;

	if ((ia_valid & ATTR_UID && attr->ia_uid != vi->i_uid) ||
			(ia_valid & ATTR_GID && attr->ia_gid != vi->i_gid)) {
		err = DQUOT_TRANSFER(vi, attr) ? -EDQUOT : 0;
		if (err)
			return err;
	}

	lock_kernel();

	if (ia_valid & ATTR_SIZE) {
		ntfs_error(vi->i_sb, "Changes in i_size are not supported "
				"yet. Sorry.");
		// TODO: Implement...
		// err = vmtruncate(vi, attr->ia_size);
		err = -EOPNOTSUPP;
		if (err)
			goto trunc_err;
	}

	if (ia_valid & ATTR_UID)
		vi->i_uid = attr->ia_uid;
	if (ia_valid & ATTR_GID)
		vi->i_gid = attr->ia_gid;
	if (ia_valid & ATTR_ATIME)
		vi->i_atime = attr->ia_atime;
	if (ia_valid & ATTR_MTIME)
		vi->i_mtime = attr->ia_mtime;
	if (ia_valid & ATTR_CTIME)
		vi->i_ctime = attr->ia_ctime;
	if (ia_valid & ATTR_MODE) {
		vi->i_mode = attr->ia_mode;
		if (!in_group_p(vi->i_gid) &&
				!capable(CAP_FSETID))
			vi->i_mode &= ~S_ISGID;
	}
	mark_inode_dirty(vi);

trunc_err:

	unlock_kernel();

	return err;
}

#endif

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?