xref: /openbmc/linux/fs/fs_struct.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2630d9c47SPaul Gortmaker #include <linux/export.h>
33f07c014SIngo Molnar #include <linux/sched/signal.h>
429930025SIngo Molnar #include <linux/sched/task.h>
53e93cd67SAl Viro #include <linux/fs.h>
63e93cd67SAl Viro #include <linux/path.h>
73e93cd67SAl Viro #include <linux/slab.h>
85ad4e53bSAl Viro #include <linux/fs_struct.h>
9f03c6599SAl Viro #include "internal.h"
10f03c6599SAl Viro 
113e93cd67SAl Viro /*
123e93cd67SAl Viro  * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
133e93cd67SAl Viro  * It can block.
143e93cd67SAl Viro  */
set_fs_root(struct fs_struct * fs,const struct path * path)15dcf787f3SAl Viro void set_fs_root(struct fs_struct *fs, const struct path *path)
163e93cd67SAl Viro {
173e93cd67SAl Viro 	struct path old_root;
183e93cd67SAl Viro 
19f7a99c5bSAl Viro 	path_get(path);
202a4419b5SNick Piggin 	spin_lock(&fs->lock);
21c28cc364SNick Piggin 	write_seqcount_begin(&fs->seq);
223e93cd67SAl Viro 	old_root = fs->root;
233e93cd67SAl Viro 	fs->root = *path;
24c28cc364SNick Piggin 	write_seqcount_end(&fs->seq);
252a4419b5SNick Piggin 	spin_unlock(&fs->lock);
263e93cd67SAl Viro 	if (old_root.dentry)
27f7a99c5bSAl Viro 		path_put(&old_root);
283e93cd67SAl Viro }
293e93cd67SAl Viro 
303e93cd67SAl Viro /*
313e93cd67SAl Viro  * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
323e93cd67SAl Viro  * It can block.
333e93cd67SAl Viro  */
set_fs_pwd(struct fs_struct * fs,const struct path * path)34dcf787f3SAl Viro void set_fs_pwd(struct fs_struct *fs, const struct path *path)
353e93cd67SAl Viro {
363e93cd67SAl Viro 	struct path old_pwd;
373e93cd67SAl Viro 
38f7a99c5bSAl Viro 	path_get(path);
392a4419b5SNick Piggin 	spin_lock(&fs->lock);
40c28cc364SNick Piggin 	write_seqcount_begin(&fs->seq);
413e93cd67SAl Viro 	old_pwd = fs->pwd;
423e93cd67SAl Viro 	fs->pwd = *path;
43c28cc364SNick Piggin 	write_seqcount_end(&fs->seq);
442a4419b5SNick Piggin 	spin_unlock(&fs->lock);
453e93cd67SAl Viro 
463e93cd67SAl Viro 	if (old_pwd.dentry)
47f7a99c5bSAl Viro 		path_put(&old_pwd);
483e93cd67SAl Viro }
493e93cd67SAl Viro 
replace_path(struct path * p,const struct path * old,const struct path * new)5082234e61SAl Viro static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
5182234e61SAl Viro {
5282234e61SAl Viro 	if (likely(p->dentry != old->dentry || p->mnt != old->mnt))
5382234e61SAl Viro 		return 0;
5482234e61SAl Viro 	*p = *new;
5582234e61SAl Viro 	return 1;
5682234e61SAl Viro }
5782234e61SAl Viro 
chroot_fs_refs(const struct path * old_root,const struct path * new_root)58dcf787f3SAl Viro void chroot_fs_refs(const struct path *old_root, const struct path *new_root)
593e93cd67SAl Viro {
603e93cd67SAl Viro 	struct task_struct *g, *p;
613e93cd67SAl Viro 	struct fs_struct *fs;
623e93cd67SAl Viro 	int count = 0;
633e93cd67SAl Viro 
643e93cd67SAl Viro 	read_lock(&tasklist_lock);
65*5ffd2c37SOleg Nesterov 	for_each_process_thread(g, p) {
663e93cd67SAl Viro 		task_lock(p);
673e93cd67SAl Viro 		fs = p->fs;
683e93cd67SAl Viro 		if (fs) {
6982234e61SAl Viro 			int hits = 0;
702a4419b5SNick Piggin 			spin_lock(&fs->lock);
71c28cc364SNick Piggin 			write_seqcount_begin(&fs->seq);
7282234e61SAl Viro 			hits += replace_path(&fs->root, old_root, new_root);
7382234e61SAl Viro 			hits += replace_path(&fs->pwd, old_root, new_root);
74c28cc364SNick Piggin 			write_seqcount_end(&fs->seq);
7582234e61SAl Viro 			while (hits--) {
7682234e61SAl Viro 				count++;
77f7a99c5bSAl Viro 				path_get(new_root);
7882234e61SAl Viro 			}
792a4419b5SNick Piggin 			spin_unlock(&fs->lock);
803e93cd67SAl Viro 		}
813e93cd67SAl Viro 		task_unlock(p);
82*5ffd2c37SOleg Nesterov 	}
833e93cd67SAl Viro 	read_unlock(&tasklist_lock);
843e93cd67SAl Viro 	while (count--)
85f7a99c5bSAl Viro 		path_put(old_root);
863e93cd67SAl Viro }
873e93cd67SAl Viro 
free_fs_struct(struct fs_struct * fs)88498052bbSAl Viro void free_fs_struct(struct fs_struct *fs)
893e93cd67SAl Viro {
90f7a99c5bSAl Viro 	path_put(&fs->root);
91f7a99c5bSAl Viro 	path_put(&fs->pwd);
923e93cd67SAl Viro 	kmem_cache_free(fs_cachep, fs);
933e93cd67SAl Viro }
943e93cd67SAl Viro 
exit_fs(struct task_struct * tsk)953e93cd67SAl Viro void exit_fs(struct task_struct *tsk)
963e93cd67SAl Viro {
973e93cd67SAl Viro 	struct fs_struct *fs = tsk->fs;
983e93cd67SAl Viro 
993e93cd67SAl Viro 	if (fs) {
100498052bbSAl Viro 		int kill;
1013e93cd67SAl Viro 		task_lock(tsk);
1022a4419b5SNick Piggin 		spin_lock(&fs->lock);
1033e93cd67SAl Viro 		tsk->fs = NULL;
104498052bbSAl Viro 		kill = !--fs->users;
1052a4419b5SNick Piggin 		spin_unlock(&fs->lock);
1063e93cd67SAl Viro 		task_unlock(tsk);
107498052bbSAl Viro 		if (kill)
108498052bbSAl Viro 			free_fs_struct(fs);
1093e93cd67SAl Viro 	}
1103e93cd67SAl Viro }
1113e93cd67SAl Viro 
copy_fs_struct(struct fs_struct * old)1123e93cd67SAl Viro struct fs_struct *copy_fs_struct(struct fs_struct *old)
1133e93cd67SAl Viro {
1143e93cd67SAl Viro 	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
1153e93cd67SAl Viro 	/* We don't need to lock fs - think why ;-) */
1163e93cd67SAl Viro 	if (fs) {
117498052bbSAl Viro 		fs->users = 1;
118498052bbSAl Viro 		fs->in_exec = 0;
1192a4419b5SNick Piggin 		spin_lock_init(&fs->lock);
12026475371SAhmed S. Darwish 		seqcount_spinlock_init(&fs->seq, &fs->lock);
1213e93cd67SAl Viro 		fs->umask = old->umask;
122b3e19d92SNick Piggin 
123b3e19d92SNick Piggin 		spin_lock(&old->lock);
124b3e19d92SNick Piggin 		fs->root = old->root;
125f7a99c5bSAl Viro 		path_get(&fs->root);
126b3e19d92SNick Piggin 		fs->pwd = old->pwd;
127f7a99c5bSAl Viro 		path_get(&fs->pwd);
128b3e19d92SNick Piggin 		spin_unlock(&old->lock);
1293e93cd67SAl Viro 	}
1303e93cd67SAl Viro 	return fs;
1313e93cd67SAl Viro }
1323e93cd67SAl Viro 
unshare_fs_struct(void)1333e93cd67SAl Viro int unshare_fs_struct(void)
1343e93cd67SAl Viro {
135498052bbSAl Viro 	struct fs_struct *fs = current->fs;
136498052bbSAl Viro 	struct fs_struct *new_fs = copy_fs_struct(fs);
137498052bbSAl Viro 	int kill;
138498052bbSAl Viro 
139498052bbSAl Viro 	if (!new_fs)
1403e93cd67SAl Viro 		return -ENOMEM;
141498052bbSAl Viro 
142498052bbSAl Viro 	task_lock(current);
1432a4419b5SNick Piggin 	spin_lock(&fs->lock);
144498052bbSAl Viro 	kill = !--fs->users;
145498052bbSAl Viro 	current->fs = new_fs;
1462a4419b5SNick Piggin 	spin_unlock(&fs->lock);
147498052bbSAl Viro 	task_unlock(current);
148498052bbSAl Viro 
149498052bbSAl Viro 	if (kill)
150498052bbSAl Viro 		free_fs_struct(fs);
151498052bbSAl Viro 
1523e93cd67SAl Viro 	return 0;
1533e93cd67SAl Viro }
1543e93cd67SAl Viro EXPORT_SYMBOL_GPL(unshare_fs_struct);
1553e93cd67SAl Viro 
current_umask(void)156ce3b0f8dSAl Viro int current_umask(void)
157ce3b0f8dSAl Viro {
158ce3b0f8dSAl Viro 	return current->fs->umask;
159ce3b0f8dSAl Viro }
160ce3b0f8dSAl Viro EXPORT_SYMBOL(current_umask);
161ce3b0f8dSAl Viro 
1623e93cd67SAl Viro /* to be mentioned only in INIT_TASK */
1633e93cd67SAl Viro struct fs_struct init_fs = {
164498052bbSAl Viro 	.users		= 1,
1652a4419b5SNick Piggin 	.lock		= __SPIN_LOCK_UNLOCKED(init_fs.lock),
16626475371SAhmed S. Darwish 	.seq		= SEQCNT_SPINLOCK_ZERO(init_fs.seq, &init_fs.lock),
1673e93cd67SAl Viro 	.umask		= 0022,
1683e93cd67SAl Viro };
169