Linux 內核源碼分析 -- chroot


chroot 在 內核中的實現

在 Linux 5.6 版本中 chroot 函數的系統調用對應的函數位於:./fs/open.c:539:SYSCALL_DEFINE1(chroot, const char __user *, filename)

via: https://elixir.bootlin.com/linux/v5.6/source/fs/open.c#L539

SYSCALL_DEFINE1(chroot, const char __user *, filename)
{
	return ksys_chroot(filename);
}

ksys_chroot

via: https://elixir.bootlin.com/linux/v5.6/source/fs/open.c#L506

int ksys_chroot(const char __user *filename)
{
	struct path path;
	int error;
	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
retry:
    // 根據文件名找到 path 結構
	error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
	if (error)
		goto out;

    // 解析 path 的 mm_root dentry 結構,再解析相應的 inode 結構,即 d_inode,就可找到掛載點相應的 inode 結構
	error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
	if (error)
		goto dput_and_out;

	error = -EPERM;
    // 判斷當前進程所有者是不是有執行 chroot 操作的權限
    // 這里是 namespace, cred 的內容了,不展開
	if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
		goto dput_and_out;
	error = security_path_chroot(&path);
	if (error)
		goto dput_and_out;

    // 主要操作就是這個函數
	set_fs_root(current->fs, &path);
	error = 0;
dput_and_out:
	path_put(&path);
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
out:
	return error;
}

flag 含義

#define LOOKUP_FOLLOW		0x0001	/* follow links at the end */
#define LOOKUP_DIRECTORY	0x0002	/* require a directory */

set_fs_root

主要函數,就是在這個函數里修改了程序的 “根目錄”

via: https://elixir.bootlin.com/linux/v5.6/source/fs/fs_struct.c#L15

先來看一下 fs_struct

struct fs_struct {
	int users;
	spinlock_t lock;
	seqcount_t seq;
	int umask;
	int in_exec;
	struct path root, pwd; 
    // root:根目錄的目錄項
	 // pwd:當前工作目錄的目錄項
} __randomize_layout;
/*
 * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
 * It can block.
 */
void set_fs_root(struct fs_struct *fs, const struct path *path)
{
	struct path old_root;

	path_get(path);
	spin_lock(&fs->lock); // 自旋鎖
	write_seqcount_begin(&fs->seq);
	old_root = fs->root; // 保存程序的 根目錄 的目錄項
	fs->root = *path; // 設置 根目錄 為 path 的目錄項
	write_seqcount_end(&fs->seq);
	spin_unlock(&fs->lock);
	if (old_root.dentry)
		path_put(&old_root);
}

struct path

via: https://elixir.bootlin.com/linux/v5.6/source/include/linux/path.h#L8

struct path {
	struct vfsmount *mnt;
	struct dentry *dentry;
} __randomize_layout;

struct vfsmount

描述獨立文件系統的掛載信息,每個不同的掛載點對應一個獨立的 vfsmount 結構,屬於同一文件系統的所有目錄和文件隸屬同一 vfsmountvfsmount 結構對應於該文件系統頂層目錄,即掛載目錄

via: https://elixir.bootlin.com/linux/v5.6/source/include/linux/mount.h#L68

struct vfsmount {
	struct dentry *mnt_root;	/* 上一層掛載點對應的 dentry */
	struct super_block *mnt_sb;	/* 指向超級塊 */
	int mnt_flags;
} __randomize_layout;

struct dentry

目錄項,是Linux文件系統中某個 索引節點(inode) 的鏈接

via:https://elixir.bootlin.com/linux/v5.6/source/include/linux/dcache.h#L89

struct dentry {
	/* RCU lookup touched fields */
	unsigned int d_flags;		/* protected by d_lock */
	seqcount_t d_seq;		/* per dentry seqlock */
	struct hlist_bl_node d_hash;	/* lookup hash list */
	struct dentry *d_parent;	/* 父目錄項指針 */
	struct qstr d_name; // 文件或者目錄的名稱
    // 目錄的 inode
	struct inode *d_inode;		/* Where the name belongs to - NULL is
					 * negative */
	unsigned char d_iname[DNAME_INLINE_LEN];	/* small names */

	/* Ref lookup also touches following */
	struct lockref d_lockref;	/* per-dentry lock and refcount */
	const struct dentry_operations *d_op;
	struct super_block *d_sb;	/* The root of the dentry tree */
	unsigned long d_time;		/* used by d_revalidate */
	void *d_fsdata;			/* fs-specific data */

	union {
		struct list_head d_lru;		/* LRU list */
		wait_queue_head_t *d_wait;	/* in-lookup ones only */
	};
	struct list_head d_child;	/* child of parent list */
	struct list_head d_subdirs;	/* our children */
	/*
	 * d_alias and d_rcu can share memory
	 */
	union {
		struct hlist_node d_alias;	/* inode alias list */
		struct hlist_bl_node d_in_lookup_hash;	/* only for in-lookup ones */
	 	struct rcu_head d_rcu;
	} d_u;
} __randomize_layout;

上面這兩個都是 文件系統 的東西,不在這里詳細分析

總結

其實 chroot 修改了進程的 root 目錄的核心操作就是修改了 進程 的 task_struct -> fs -> root

因為是 struct path root,所以

user_path_at(AT_FDCWD, filename, lookup_flags, &path);

就是通過文件名去解析 文件夾 對應的 path 結構,存在 path 變量里面

然后就是權限檢查

在然后把 path 傳進 set_fs_root 函數

fs->root = *path;

修改了 root

這樣進程就認為 filename 是根目錄,因為 fs->root 存的是 filename 目錄的 path 結構


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM