Skip to content

Commit

Permalink
try to get rid of races in hostfs open()
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
Al Viro committed Aug 9, 2010
1 parent f8d7e18 commit f8ad850
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 12 deletions.
1 change: 1 addition & 0 deletions fs/hostfs/hostfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
43 changes: 31 additions & 12 deletions fs/hostfs/hostfs_kern.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions fs/hostfs/hostfs_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down

0 comments on commit f8ad850

Please sign in to comment.