aboutsummaryrefslogtreecommitdiffstats
path: root/fs/open.c
diff options
context:
space:
mode:
authorErnie Petrides <petrides@redhat.com>2006-09-29 05:00:13 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-29 12:18:13 -0400
commitee731f4f7880b09ca147008ab46ad4e5f72cb8bf (patch)
treea61545ec0a04c7699727dc9e5c5c9a30b1d75ab0 /fs/open.c
parent7bbab9166a82d15442357cfd63ec530b5b5fb62e (diff)
[PATCH] fix wrong error code on interrupted close syscalls
The problem is that close() syscalls can call a file system's flush handler, which in turn might sleep interruptibly and ultimately pass back an -ERESTARTSYS return value. This happens for files backed by an interruptible NFS mount under nfs_file_flush() when a large file has just been written and nfs_wait_bit_interruptible() detects that there is a signal pending. I have a test case where the "strace" command is used to attach to a process sleeping in such a close(). Since the SIGSTOP is forced onto the victim process (removing it from the thread's "blocked" mask in force_sig_info()), the RPC wait is interrupted and the close() is terminated early. But the file table entry has already been cleared before the flush handler was called. Thus, when the syscall is restarted, the file descriptor appears closed and an EBADF error is returned (which is wrong). What's worse, there is the hypothetical case where another thread of a multi-threaded application might have reused the file descriptor, in which case that file would be mistakenly closed. The bottom line is that close() syscalls are not restartable, and thus -ERESTARTSYS return values should be mapped to -EINTR. This is consistent with the close(2) manual page. The fix is below. Signed-off-by: Ernie Petrides <petrides@redhat.com> Cc: Roland McGrath <roland@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/open.c')
-rw-r--r--fs/open.c12
1 files changed, 11 insertions, 1 deletions
diff --git a/fs/open.c b/fs/open.c
index 1574d8fe4909..304c1c7814cb 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1173,6 +1173,7 @@ asmlinkage long sys_close(unsigned int fd)
1173 struct file * filp; 1173 struct file * filp;
1174 struct files_struct *files = current->files; 1174 struct files_struct *files = current->files;
1175 struct fdtable *fdt; 1175 struct fdtable *fdt;
1176 int retval;
1176 1177
1177 spin_lock(&files->file_lock); 1178 spin_lock(&files->file_lock);
1178 fdt = files_fdtable(files); 1179 fdt = files_fdtable(files);
@@ -1185,7 +1186,16 @@ asmlinkage long sys_close(unsigned int fd)
1185 FD_CLR(fd, fdt->close_on_exec); 1186 FD_CLR(fd, fdt->close_on_exec);
1186 __put_unused_fd(files, fd); 1187 __put_unused_fd(files, fd);
1187 spin_unlock(&files->file_lock); 1188 spin_unlock(&files->file_lock);
1188 return filp_close(filp, files); 1189 retval = filp_close(filp, files);
1190
1191 /* can't restart close syscall because file table entry was cleared */
1192 if (unlikely(retval == -ERESTARTSYS ||
1193 retval == -ERESTARTNOINTR ||
1194 retval == -ERESTARTNOHAND ||
1195 retval == -ERESTART_RESTARTBLOCK))
1196 retval = -EINTR;
1197
1198 return retval;
1189 1199
1190out_unlock: 1200out_unlock:
1191 spin_unlock(&files->file_lock); 1201 spin_unlock(&files->file_lock);