diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2009-03-30 07:20:30 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-03-31 23:00:26 -0400 |
commit | 498052bba55ecaff58db6a1436b0e25bfd75a7ff (patch) | |
tree | bd3644ac60737e3733995a203acebd70cfd1b21b /fs/fs_struct.c | |
parent | 3e93cd671813e204c258f1e6c797959920cf7772 (diff) |
New locking/refcounting for fs_struct
* all changes of current->fs are done under task_lock and write_lock of
old fs->lock
* refcount is not atomic anymore (same protection)
* its decrements are done when removing reference from current; at the
same time we decide whether to free it.
* put_fs_struct() is gone
* new field - ->in_exec. Set by check_unsafe_exec() if we are trying to do
execve() and only subthreads share fs_struct. Cleared when finishing exec
(success and failure alike). Makes CLONE_FS fail with -EAGAIN if set.
* check_unsafe_exec() may fail with -EAGAIN if another execve() from subthread
is in progress.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/fs_struct.c')
-rw-r--r-- | fs/fs_struct.c | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 36e0a123bbf3..41cff72b377b 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c | |||
@@ -72,25 +72,27 @@ void chroot_fs_refs(struct path *old_root, struct path *new_root) | |||
72 | path_put(old_root); | 72 | path_put(old_root); |
73 | } | 73 | } |
74 | 74 | ||
75 | void put_fs_struct(struct fs_struct *fs) | 75 | void free_fs_struct(struct fs_struct *fs) |
76 | { | 76 | { |
77 | /* No need to hold fs->lock if we are killing it */ | 77 | path_put(&fs->root); |
78 | if (atomic_dec_and_test(&fs->count)) { | 78 | path_put(&fs->pwd); |
79 | path_put(&fs->root); | 79 | kmem_cache_free(fs_cachep, fs); |
80 | path_put(&fs->pwd); | ||
81 | kmem_cache_free(fs_cachep, fs); | ||
82 | } | ||
83 | } | 80 | } |
84 | 81 | ||
85 | void exit_fs(struct task_struct *tsk) | 82 | void exit_fs(struct task_struct *tsk) |
86 | { | 83 | { |
87 | struct fs_struct * fs = tsk->fs; | 84 | struct fs_struct *fs = tsk->fs; |
88 | 85 | ||
89 | if (fs) { | 86 | if (fs) { |
87 | int kill; | ||
90 | task_lock(tsk); | 88 | task_lock(tsk); |
89 | write_lock(&fs->lock); | ||
91 | tsk->fs = NULL; | 90 | tsk->fs = NULL; |
91 | kill = !--fs->users; | ||
92 | write_unlock(&fs->lock); | ||
92 | task_unlock(tsk); | 93 | task_unlock(tsk); |
93 | put_fs_struct(fs); | 94 | if (kill) |
95 | free_fs_struct(fs); | ||
94 | } | 96 | } |
95 | } | 97 | } |
96 | 98 | ||
@@ -99,7 +101,8 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) | |||
99 | struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL); | 101 | struct fs_struct *fs = kmem_cache_alloc(fs_cachep, GFP_KERNEL); |
100 | /* We don't need to lock fs - think why ;-) */ | 102 | /* We don't need to lock fs - think why ;-) */ |
101 | if (fs) { | 103 | if (fs) { |
102 | atomic_set(&fs->count, 1); | 104 | fs->users = 1; |
105 | fs->in_exec = 0; | ||
103 | rwlock_init(&fs->lock); | 106 | rwlock_init(&fs->lock); |
104 | fs->umask = old->umask; | 107 | fs->umask = old->umask; |
105 | read_lock(&old->lock); | 108 | read_lock(&old->lock); |
@@ -114,28 +117,54 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) | |||
114 | 117 | ||
115 | int unshare_fs_struct(void) | 118 | int unshare_fs_struct(void) |
116 | { | 119 | { |
117 | struct fs_struct *fsp = copy_fs_struct(current->fs); | 120 | struct fs_struct *fs = current->fs; |
118 | if (!fsp) | 121 | struct fs_struct *new_fs = copy_fs_struct(fs); |
122 | int kill; | ||
123 | |||
124 | if (!new_fs) | ||
119 | return -ENOMEM; | 125 | return -ENOMEM; |
120 | exit_fs(current); | 126 | |
121 | current->fs = fsp; | 127 | task_lock(current); |
128 | write_lock(&fs->lock); | ||
129 | kill = !--fs->users; | ||
130 | current->fs = new_fs; | ||
131 | write_unlock(&fs->lock); | ||
132 | task_unlock(current); | ||
133 | |||
134 | if (kill) | ||
135 | free_fs_struct(fs); | ||
136 | |||
122 | return 0; | 137 | return 0; |
123 | } | 138 | } |
124 | EXPORT_SYMBOL_GPL(unshare_fs_struct); | 139 | EXPORT_SYMBOL_GPL(unshare_fs_struct); |
125 | 140 | ||
126 | /* to be mentioned only in INIT_TASK */ | 141 | /* to be mentioned only in INIT_TASK */ |
127 | struct fs_struct init_fs = { | 142 | struct fs_struct init_fs = { |
128 | .count = ATOMIC_INIT(1), | 143 | .users = 1, |
129 | .lock = __RW_LOCK_UNLOCKED(init_fs.lock), | 144 | .lock = __RW_LOCK_UNLOCKED(init_fs.lock), |
130 | .umask = 0022, | 145 | .umask = 0022, |
131 | }; | 146 | }; |
132 | 147 | ||
133 | void daemonize_fs_struct(void) | 148 | void daemonize_fs_struct(void) |
134 | { | 149 | { |
135 | struct fs_struct *fs; | 150 | struct fs_struct *fs = current->fs; |
151 | |||
152 | if (fs) { | ||
153 | int kill; | ||
154 | |||
155 | task_lock(current); | ||
136 | 156 | ||
137 | exit_fs(current); /* current->fs->count--; */ | 157 | write_lock(&init_fs.lock); |
138 | fs = &init_fs; | 158 | init_fs.users++; |
139 | current->fs = fs; | 159 | write_unlock(&init_fs.lock); |
140 | atomic_inc(&fs->count); | 160 | |
161 | write_lock(&fs->lock); | ||
162 | current->fs = &init_fs; | ||
163 | kill = !--fs->users; | ||
164 | write_unlock(&fs->lock); | ||
165 | |||
166 | task_unlock(current); | ||
167 | if (kill) | ||
168 | free_fs_struct(fs); | ||
169 | } | ||
141 | } | 170 | } |