fs_struct.c 3.31 KB
Newer Older
1
#include <linux/export.h>
2
#include <linux/sched/signal.h>
3
#include <linux/sched/task.h>
4 5 6
#include <linux/fs.h>
#include <linux/path.h>
#include <linux/slab.h>
7
#include <linux/fs_struct.h>
8 9
#include "internal.h"

10 11 12 13
/*
 * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
 * It can block.
 */
14
void set_fs_root(struct fs_struct *fs, const struct path *path)
15 16 17
{
	struct path old_root;

Al Viro's avatar
Al Viro committed
18
	path_get(path);
19
	spin_lock(&fs->lock);
Nick Piggin's avatar
Nick Piggin committed
20
	write_seqcount_begin(&fs->seq);
21 22
	old_root = fs->root;
	fs->root = *path;
Nick Piggin's avatar
Nick Piggin committed
23
	write_seqcount_end(&fs->seq);
24
	spin_unlock(&fs->lock);
25
	if (old_root.dentry)
Al Viro's avatar
Al Viro committed
26
		path_put(&old_root);
27 28 29 30 31 32
}

/*
 * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
 * It can block.
 */
33
void set_fs_pwd(struct fs_struct *fs, const struct path *path)
34 35 36
{
	struct path old_pwd;

Al Viro's avatar
Al Viro committed
37
	path_get(path);
38
	spin_lock(&fs->lock);
Nick Piggin's avatar
Nick Piggin committed
39
	write_seqcount_begin(&fs->seq);
40 41
	old_pwd = fs->pwd;
	fs->pwd = *path;
Nick Piggin's avatar
Nick Piggin committed
42
	write_seqcount_end(&fs->seq);
43
	spin_unlock(&fs->lock);
44 45

	if (old_pwd.dentry)
Al Viro's avatar
Al Viro committed
46
		path_put(&old_pwd);
47 48
}

49 50 51 52 53 54 55 56
static inline int replace_path(struct path *p, const struct path *old, const struct path *new)
{
	if (likely(p->dentry != old->dentry || p->mnt != old->mnt))
		return 0;
	*p = *new;
	return 1;
}

57
void chroot_fs_refs(const struct path *old_root, const struct path *new_root)
58 59 60 61 62 63 64 65 66 67
{
	struct task_struct *g, *p;
	struct fs_struct *fs;
	int count = 0;

	read_lock(&tasklist_lock);
	do_each_thread(g, p) {
		task_lock(p);
		fs = p->fs;
		if (fs) {
68
			int hits = 0;
69
			spin_lock(&fs->lock);
Nick Piggin's avatar
Nick Piggin committed
70
			write_seqcount_begin(&fs->seq);
71 72 73 74
			hits += replace_path(&fs->root, old_root, new_root);
			hits += replace_path(&fs->pwd, old_root, new_root);
			write_seqcount_end(&fs->seq);
			while (hits--) {
75
				count++;
Al Viro's avatar
Al Viro committed
76
				path_get(new_root);
77
			}
78
			spin_unlock(&fs->lock);
79 80 81 82 83
		}
		task_unlock(p);
	} while_each_thread(g, p);
	read_unlock(&tasklist_lock);
	while (count--)
Al Viro's avatar
Al Viro committed
84
		path_put(old_root);
85 86
}

87
void free_fs_struct(struct fs_struct *fs)
88
{
Al Viro's avatar
Al Viro committed
89 90
	path_put(&fs->root);
	path_put(&fs->pwd);
91
	kmem_cache_free(fs_cachep, fs);
92 93 94 95
}

void exit_fs(struct task_struct *tsk)
{
96
	struct fs_struct *fs = tsk->fs;
97 98

	if (fs) {
99
		int kill;
100
		task_lock(tsk);
101
		spin_lock(&fs->lock);
102
		tsk->fs = NULL;
103
		kill = !--fs->users;
104
		spin_unlock(&fs->lock);
105
		task_unlock(tsk);
106 107
		if (kill)
			free_fs_struct(fs);
108 109 110 111 112 113 114 115
	}
}

struct fs_struct *copy_fs_struct(struct fs_struct *old)
{
	struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL);
	/* We don't need to lock fs - think why ;-) */
	if (fs) {
116 117
		fs->users = 1;
		fs->in_exec = 0;
118
		spin_lock_init(&fs->lock);
Nick Piggin's avatar
Nick Piggin committed
119
		seqcount_init(&fs->seq);
120
		fs->umask = old->umask;
Nick Piggin's avatar
Nick Piggin committed
121 122 123

		spin_lock(&old->lock);
		fs->root = old->root;
Al Viro's avatar
Al Viro committed
124
		path_get(&fs->root);
Nick Piggin's avatar
Nick Piggin committed
125
		fs->pwd = old->pwd;
Al Viro's avatar
Al Viro committed
126
		path_get(&fs->pwd);
Nick Piggin's avatar
Nick Piggin committed
127
		spin_unlock(&old->lock);
128 129 130 131 132 133
	}
	return fs;
}

int unshare_fs_struct(void)
{
134 135 136 137 138
	struct fs_struct *fs = current->fs;
	struct fs_struct *new_fs = copy_fs_struct(fs);
	int kill;

	if (!new_fs)
139
		return -ENOMEM;
140 141

	task_lock(current);
142
	spin_lock(&fs->lock);
143 144
	kill = !--fs->users;
	current->fs = new_fs;
145
	spin_unlock(&fs->lock);
146 147 148 149 150
	task_unlock(current);

	if (kill)
		free_fs_struct(fs);

151 152 153 154
	return 0;
}
EXPORT_SYMBOL_GPL(unshare_fs_struct);

Al Viro's avatar
Al Viro committed
155 156 157 158 159 160
int current_umask(void)
{
	return current->fs->umask;
}
EXPORT_SYMBOL(current_umask);

161 162
/* to be mentioned only in INIT_TASK */
struct fs_struct init_fs = {
163
	.users		= 1,
164
	.lock		= __SPIN_LOCK_UNLOCKED(init_fs.lock),
165
	.seq		= SEQCNT_ZERO(init_fs.seq),
166 167
	.umask		= 0022,
};