18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/export.h>
38c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
48c2ecf20Sopenharmony_ci#include <linux/sched/task.h>
58c2ecf20Sopenharmony_ci#include <linux/fs.h>
68c2ecf20Sopenharmony_ci#include <linux/path.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/fs_struct.h>
98c2ecf20Sopenharmony_ci#include "internal.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
138c2ecf20Sopenharmony_ci * It can block.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_civoid set_fs_root(struct fs_struct *fs, const struct path *path)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	struct path old_root;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	path_get(path);
208c2ecf20Sopenharmony_ci	spin_lock(&fs->lock);
218c2ecf20Sopenharmony_ci	write_seqcount_begin(&fs->seq);
228c2ecf20Sopenharmony_ci	old_root = fs->root;
238c2ecf20Sopenharmony_ci	fs->root = *path;
248c2ecf20Sopenharmony_ci	write_seqcount_end(&fs->seq);
258c2ecf20Sopenharmony_ci	spin_unlock(&fs->lock);
268c2ecf20Sopenharmony_ci	if (old_root.dentry)
278c2ecf20Sopenharmony_ci		path_put(&old_root);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
328c2ecf20Sopenharmony_ci * It can block.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_civoid set_fs_pwd(struct fs_struct *fs, const struct path *path)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct path old_pwd;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	path_get(path);
398c2ecf20Sopenharmony_ci	spin_lock(&fs->lock);
408c2ecf20Sopenharmony_ci	write_seqcount_begin(&fs->seq);
418c2ecf20Sopenharmony_ci	old_pwd = fs->pwd;
428c2ecf20Sopenharmony_ci	fs->pwd = *path;
438c2ecf20Sopenharmony_ci	write_seqcount_end(&fs->seq);
448c2ecf20Sopenharmony_ci	spin_unlock(&fs->lock);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (old_pwd.dentry)
478c2ecf20Sopenharmony_ci		path_put(&old_pwd);
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic inline int replace_path(struct path *p, const struct path *old, const struct path *new)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	if (likely(p->dentry != old->dentry || p->mnt != old->mnt))
538c2ecf20Sopenharmony_ci		return 0;
548c2ecf20Sopenharmony_ci	*p = *new;
558c2ecf20Sopenharmony_ci	return 1;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_civoid chroot_fs_refs(const struct path *old_root, const struct path *new_root)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct task_struct *g, *p;
618c2ecf20Sopenharmony_ci	struct fs_struct *fs;
628c2ecf20Sopenharmony_ci	int count = 0;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	read_lock(&tasklist_lock);
658c2ecf20Sopenharmony_ci	do_each_thread(g, p) {
668c2ecf20Sopenharmony_ci		task_lock(p);
678c2ecf20Sopenharmony_ci		fs = p->fs;
688c2ecf20Sopenharmony_ci		if (fs) {
698c2ecf20Sopenharmony_ci			int hits = 0;
708c2ecf20Sopenharmony_ci			spin_lock(&fs->lock);
718c2ecf20Sopenharmony_ci			write_seqcount_begin(&fs->seq);
728c2ecf20Sopenharmony_ci			hits += replace_path(&fs->root, old_root, new_root);
738c2ecf20Sopenharmony_ci			hits += replace_path(&fs->pwd, old_root, new_root);
748c2ecf20Sopenharmony_ci			write_seqcount_end(&fs->seq);
758c2ecf20Sopenharmony_ci			while (hits--) {
768c2ecf20Sopenharmony_ci				count++;
778c2ecf20Sopenharmony_ci				path_get(new_root);
788c2ecf20Sopenharmony_ci			}
798c2ecf20Sopenharmony_ci			spin_unlock(&fs->lock);
808c2ecf20Sopenharmony_ci		}
818c2ecf20Sopenharmony_ci		task_unlock(p);
828c2ecf20Sopenharmony_ci	} while_each_thread(g, p);
838c2ecf20Sopenharmony_ci	read_unlock(&tasklist_lock);
848c2ecf20Sopenharmony_ci	while (count--)
858c2ecf20Sopenharmony_ci		path_put(old_root);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid free_fs_struct(struct fs_struct *fs)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	path_put(&fs->root);
918c2ecf20Sopenharmony_ci	path_put(&fs->pwd);
928c2ecf20Sopenharmony_ci	kmem_cache_free(fs_cachep, fs);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_civoid exit_fs(struct task_struct *tsk)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct fs_struct *fs = tsk->fs;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (fs) {
1008c2ecf20Sopenharmony_ci		int kill;
1018c2ecf20Sopenharmony_ci		task_lock(tsk);
1028c2ecf20Sopenharmony_ci		spin_lock(&fs->lock);
1038c2ecf20Sopenharmony_ci		tsk->fs = NULL;
1048c2ecf20Sopenharmony_ci		kill = !--fs->users;
1058c2ecf20Sopenharmony_ci		spin_unlock(&fs->lock);
1068c2ecf20Sopenharmony_ci		task_unlock(tsk);
1078c2ecf20Sopenharmony_ci		if (kill)
1088c2ecf20Sopenharmony_ci			free_fs_struct(fs);
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistruct fs_struct *copy_fs_struct(struct fs_struct *old)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
1158c2ecf20Sopenharmony_ci	/* We don't need to lock fs - think why ;-) */
1168c2ecf20Sopenharmony_ci	if (fs) {
1178c2ecf20Sopenharmony_ci		fs->users = 1;
1188c2ecf20Sopenharmony_ci		fs->in_exec = 0;
1198c2ecf20Sopenharmony_ci		spin_lock_init(&fs->lock);
1208c2ecf20Sopenharmony_ci		seqcount_spinlock_init(&fs->seq, &fs->lock);
1218c2ecf20Sopenharmony_ci		fs->umask = old->umask;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		spin_lock(&old->lock);
1248c2ecf20Sopenharmony_ci		fs->root = old->root;
1258c2ecf20Sopenharmony_ci		path_get(&fs->root);
1268c2ecf20Sopenharmony_ci		fs->pwd = old->pwd;
1278c2ecf20Sopenharmony_ci		path_get(&fs->pwd);
1288c2ecf20Sopenharmony_ci		spin_unlock(&old->lock);
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	return fs;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciint unshare_fs_struct(void)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct fs_struct *fs = current->fs;
1368c2ecf20Sopenharmony_ci	struct fs_struct *new_fs = copy_fs_struct(fs);
1378c2ecf20Sopenharmony_ci	int kill;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (!new_fs)
1408c2ecf20Sopenharmony_ci		return -ENOMEM;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	task_lock(current);
1438c2ecf20Sopenharmony_ci	spin_lock(&fs->lock);
1448c2ecf20Sopenharmony_ci	kill = !--fs->users;
1458c2ecf20Sopenharmony_ci	current->fs = new_fs;
1468c2ecf20Sopenharmony_ci	spin_unlock(&fs->lock);
1478c2ecf20Sopenharmony_ci	task_unlock(current);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (kill)
1508c2ecf20Sopenharmony_ci		free_fs_struct(fs);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unshare_fs_struct);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciint current_umask(void)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	return current->fs->umask;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(current_umask);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* to be mentioned only in INIT_TASK */
1638c2ecf20Sopenharmony_cistruct fs_struct init_fs = {
1648c2ecf20Sopenharmony_ci	.users		= 1,
1658c2ecf20Sopenharmony_ci	.lock		= __SPIN_LOCK_UNLOCKED(init_fs.lock),
1668c2ecf20Sopenharmony_ci	.seq		= SEQCNT_SPINLOCK_ZERO(init_fs.seq, &init_fs.lock),
1678c2ecf20Sopenharmony_ci	.umask		= 0022,
1688c2ecf20Sopenharmony_ci};
169