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 <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
f8d7e1877e
commit
f8ad850f11
3 changed files with 37 additions and 12 deletions
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue