try to get rid of races in hostfs open()
In case of mode mismatch, do *not* blindly close the descriptor
another openers might be using right now. Open the underlying
file with currently sufficient mode, then
* if current mode has grown so that it's sufficient for
us now, just close our new fd
* if current mode has grown and our fd is *not* enough
to cover it, close and repeat.
* otherwise, install our fd if the file hadn't been
opened at all or dup2() our fd over the current one (and close
our fd).
Critical section is protected by mutex; yes, system-wide. All
we do under it is a bunch of comparison and maybe an overwriting
dup2() on host.
Signed-off-by: Al Viro <[email protected]>
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c
index 8130ce9..dd1e555 100644
--- a/fs/hostfs/hostfs_kern.c
+++ b/fs/hostfs/hostfs_kern.c
@@ -302,27 +302,22 @@
int hostfs_file_open(struct inode *ino, struct file *file)
{
+ static DEFINE_MUTEX(open_mutex);
char *name;
fmode_t mode = 0;
+ int err;
int r = 0, w = 0, fd;
mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
if ((mode & HOSTFS_I(ino)->mode) == mode)
return 0;
- /*
- * The file may already have been opened, but with the wrong access,
- * so this resets things and reopens the file with the new access.
- */
- if (HOSTFS_I(ino)->fd != -1) {
- close_file(&HOSTFS_I(ino)->fd);
- HOSTFS_I(ino)->fd = -1;
- }
+ mode |= HOSTFS_I(ino)->mode;
- HOSTFS_I(ino)->mode |= mode;
- if (HOSTFS_I(ino)->mode & FMODE_READ)
+retry:
+ if (mode & FMODE_READ)
r = 1;
- if (HOSTFS_I(ino)->mode & FMODE_WRITE)
+ if (mode & FMODE_WRITE)
w = 1;
if (w)
r = 1;
@@ -335,7 +330,31 @@
__putname(name);
if (fd < 0)
return fd;
- FILE_HOSTFS_I(file)->fd = fd;
+
+ mutex_lock(&open_mutex);
+ /* somebody else had handled it first? */
+ if ((mode & HOSTFS_I(ino)->mode) == mode) {
+ mutex_unlock(&open_mutex);
+ return 0;
+ }
+ if ((mode | HOSTFS_I(ino)->mode) != mode) {
+ mode |= HOSTFS_I(ino)->mode;
+ mutex_unlock(&open_mutex);
+ close_file(&fd);
+ goto retry;
+ }
+ if (HOSTFS_I(ino)->fd == -1) {
+ HOSTFS_I(ino)->fd = fd;
+ } else {
+ err = replace_file(fd, HOSTFS_I(ino)->fd);
+ close_file(&fd);
+ if (err < 0) {
+ mutex_unlock(&open_mutex);
+ return err;
+ }
+ }
+ HOSTFS_I(ino)->mode = mode;
+ mutex_unlock(&open_mutex);
return 0;
}