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

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

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

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

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

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

48 49 50 51 52 53 54 55
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;
}

56
void chroot_fs_refs(const struct path *old_root, const struct path *new_root)
57 58 59 60 61 62 63 64 65 66
{
	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) {
67
			int hits = 0;
Nick Piggin's avatar
Nick Piggin committed
68
			spin_lock(&fs->lock);
Nick Piggin's avatar
Nick Piggin committed
69
			write_seqcount_begin(&fs->seq);
70 71 72 73
			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--) {
74
				count++;
Al Viro's avatar
Al Viro committed
75
				path_get(new_root);
76
			}
Nick Piggin's avatar
Nick Piggin committed
77
			spin_unlock(&fs->lock);
78 79 80 81 82
		}
		task_unlock(p);
	} while_each_thread(g, p);
	read_unlock(&tasklist_lock);
	while (count--)
Al Viro's avatar
Al Viro committed
83
		path_put(old_root);
84 85
}

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

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

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

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) {
115 116
		fs->users = 1;
		fs->in_exec = 0;
Nick Piggin's avatar
Nick Piggin committed
117
		spin_lock_init(&fs->lock);
Nick Piggin's avatar
Nick Piggin committed
118
		seqcount_init(&fs->seq);
119
		fs->umask = old->umask;
Nick Piggin's avatar
Nick Piggin committed
120 121 122

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

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

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

	task_lock(current);
Nick Piggin's avatar
Nick Piggin committed
141
	spin_lock(&fs->lock);
142 143
	kill = !--fs->users;
	current->fs = new_fs;
Nick Piggin's avatar
Nick Piggin committed
144
	spin_unlock(&fs->lock);
145 146 147 148 149
	task_unlock(current);

	if (kill)
		free_fs_struct(fs);

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

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

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