aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/dev.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2005-09-09 16:10:39 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-09-09 17:03:48 -0400
commit7c352bdf048811b8128019ffc1e886161e09c11c (patch)
tree74c48298b67d37295433d9b2bb08d590a5f97a78 /fs/fuse/dev.c
parent8254798199332966e2ab647380c990193af7e854 (diff)
[PATCH] FUSE: don't allow restarting of system calls
This patch removes ability to interrupt and restart operations while there hasn't been any side-effect. The reason: applications. There are some apps it seems that generate signals at a fast rate. This means, that if the operation cannot make enough progress between two signals, it will be restarted for ever. This bug actually manifested itself with 'krusader' trying to open a file for writing under sshfs. Thanks to Eduard Czimbalmos for the report. The problem can be solved just by making open() uninterruptible, because in this case it was the truncate operation that slowed down the progress. But it's better to solve this by simply not allowing interrupts at all (except SIGKILL), because applications don't expect file operations to be interruptible anyway. As an added bonus the code is simplified somewhat. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r--fs/fuse/dev.c73
1 files changed, 13 insertions, 60 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index e4ada021d087..d4c869c6d01b 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -103,20 +103,9 @@ static struct fuse_req *do_get_request(struct fuse_conn *fc)
103 return req; 103 return req;
104} 104}
105 105
106/* This can return NULL, but only in case it's interrupted by a SIGKILL */
106struct fuse_req *fuse_get_request(struct fuse_conn *fc) 107struct fuse_req *fuse_get_request(struct fuse_conn *fc)
107{ 108{
108 if (down_interruptible(&fc->outstanding_sem))
109 return NULL;
110 return do_get_request(fc);
111}
112
113/*
114 * Non-interruptible version of the above function is for operations
115 * which can't legally return -ERESTART{SYS,NOINTR}. This can still
116 * return NULL, but only in case the signal is SIGKILL.
117 */
118struct fuse_req *fuse_get_request_nonint(struct fuse_conn *fc)
119{
120 int intr; 109 int intr;
121 sigset_t oldset; 110 sigset_t oldset;
122 111
@@ -241,43 +230,20 @@ static void background_request(struct fuse_conn *fc, struct fuse_req *req)
241 get_file(req->file); 230 get_file(req->file);
242} 231}
243 232
244static int request_wait_answer_nonint(struct fuse_req *req)
245{
246 int err;
247 sigset_t oldset;
248 block_sigs(&oldset);
249 err = wait_event_interruptible(req->waitq, req->finished);
250 restore_sigs(&oldset);
251 return err;
252}
253
254/* Called with fuse_lock held. Releases, and then reacquires it. */ 233/* Called with fuse_lock held. Releases, and then reacquires it. */
255static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req, 234static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
256 int interruptible)
257{ 235{
258 int intr; 236 sigset_t oldset;
259 237
260 spin_unlock(&fuse_lock); 238 spin_unlock(&fuse_lock);
261 if (interruptible) 239 block_sigs(&oldset);
262 intr = wait_event_interruptible(req->waitq, req->finished); 240 wait_event_interruptible(req->waitq, req->finished);
263 else 241 restore_sigs(&oldset);
264 intr = request_wait_answer_nonint(req);
265 spin_lock(&fuse_lock); 242 spin_lock(&fuse_lock);
266 if (intr && interruptible && req->sent) { 243 if (req->finished)
267 /* If request is already in userspace, only allow KILL
268 signal to interrupt */
269 spin_unlock(&fuse_lock);
270 intr = request_wait_answer_nonint(req);
271 spin_lock(&fuse_lock);
272 }
273 if (!intr)
274 return; 244 return;
275 245
276 if (!interruptible || req->sent) 246 req->out.h.error = -EINTR;
277 req->out.h.error = -EINTR;
278 else
279 req->out.h.error = -ERESTARTNOINTR;
280
281 req->interrupted = 1; 247 req->interrupted = 1;
282 if (req->locked) { 248 if (req->locked) {
283 /* This is uninterruptible sleep, because data is 249 /* This is uninterruptible sleep, because data is
@@ -330,8 +296,10 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
330 wake_up(&fc->waitq); 296 wake_up(&fc->waitq);
331} 297}
332 298
333static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, 299/*
334 int interruptible) 300 * This can only be interrupted by a SIGKILL
301 */
302void request_send(struct fuse_conn *fc, struct fuse_req *req)
335{ 303{
336 req->isreply = 1; 304 req->isreply = 1;
337 spin_lock(&fuse_lock); 305 spin_lock(&fuse_lock);
@@ -345,26 +313,11 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
345 after request_end() */ 313 after request_end() */
346 __fuse_get_request(req); 314 __fuse_get_request(req);
347 315
348 request_wait_answer(fc, req, interruptible); 316 request_wait_answer(fc, req);
349 } 317 }
350 spin_unlock(&fuse_lock); 318 spin_unlock(&fuse_lock);
351} 319}
352 320
353void request_send(struct fuse_conn *fc, struct fuse_req *req)
354{
355 request_send_wait(fc, req, 1);
356}
357
358/*
359 * Non-interruptible version of the above function is for operations
360 * which can't legally return -ERESTART{SYS,NOINTR}. This can still
361 * be interrupted but only with SIGKILL.
362 */
363void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
364{
365 request_send_wait(fc, req, 0);
366}
367
368static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) 321static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
369{ 322{
370 spin_lock(&fuse_lock); 323 spin_lock(&fuse_lock);