]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
try to get rid of races in hostfs open()
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 7 Jun 2010 03:49:18 +0000 (23:49 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 9 Aug 2010 20:48:15 +0000 (16:48 -0400)
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 <viro@zeniv.linux.org.uk>
fs/hostfs/hostfs.h
fs/hostfs/hostfs_kern.c
fs/hostfs/hostfs_user.c

index ea87e224ed971425cc20815f6f8bbeb213bd5963..6bbd75c5589bc90c2a882c7e48a2fba9a35502ef 100644 (file)
@@ -74,6 +74,7 @@ extern void *open_dir(char *path, int *err_out);
 extern char *read_dir(void *stream, unsigned long long *pos,
                      unsigned long long *ino_out, int *len_out);
 extern void close_file(void *stream);
+extern int replace_file(int oldfd, int fd);
 extern void close_dir(void *stream);
 extern int read_file(int fd, unsigned long long *offset, char *buf, int len);
 extern int write_file(int fd, unsigned long long *offset, const char *buf,
index 8130ce93a06a7fd25e9a962eca7bb43a17e3f51a..dd1e55535a4e8a65e4405a2419d1a9bcde389f8c 100644 (file)
@@ -302,27 +302,22 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir)
 
 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 @@ int hostfs_file_open(struct inode *ino, struct file *file)
        __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;
 }
index 91ebfcefa409a50699cfb94d50596aa6bb0b4160..6777aa06ce2cc894de1885f65c4d145e9bc8f0a8 100644 (file)
@@ -160,6 +160,11 @@ int fsync_file(int fd, int datasync)
        return 0;
 }
 
+int replace_file(int oldfd, int fd)
+{
+       return dup2(oldfd, fd);
+}
+
 void close_file(void *stream)
 {
        close(*((int *) stream));