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,
|
extern char *read_dir(void *stream, unsigned long long *pos,
|
||||||
unsigned long long *ino_out, int *len_out);
|
unsigned long long *ino_out, int *len_out);
|
||||||
extern void close_file(void *stream);
|
extern void close_file(void *stream);
|
||||||
|
extern int replace_file(int oldfd, int fd);
|
||||||
extern void close_dir(void *stream);
|
extern void close_dir(void *stream);
|
||||||
extern int read_file(int fd, unsigned long long *offset, char *buf, int len);
|
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,
|
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)
|
int hostfs_file_open(struct inode *ino, struct file *file)
|
||||||
{
|
{
|
||||||
|
static DEFINE_MUTEX(open_mutex);
|
||||||
char *name;
|
char *name;
|
||||||
fmode_t mode = 0;
|
fmode_t mode = 0;
|
||||||
|
int err;
|
||||||
int r = 0, w = 0, fd;
|
int r = 0, w = 0, fd;
|
||||||
|
|
||||||
mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
|
mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
|
||||||
if ((mode & HOSTFS_I(ino)->mode) == mode)
|
if ((mode & HOSTFS_I(ino)->mode) == mode)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
mode |= HOSTFS_I(ino)->mode;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
HOSTFS_I(ino)->mode |= mode;
|
retry:
|
||||||
if (HOSTFS_I(ino)->mode & FMODE_READ)
|
if (mode & FMODE_READ)
|
||||||
r = 1;
|
r = 1;
|
||||||
if (HOSTFS_I(ino)->mode & FMODE_WRITE)
|
if (mode & FMODE_WRITE)
|
||||||
w = 1;
|
w = 1;
|
||||||
if (w)
|
if (w)
|
||||||
r = 1;
|
r = 1;
|
||||||
|
@ -335,7 +330,31 @@ int hostfs_file_open(struct inode *ino, struct file *file)
|
||||||
__putname(name);
|
__putname(name);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return fd;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,11 @@ int fsync_file(int fd, int datasync)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int replace_file(int oldfd, int fd)
|
||||||
|
{
|
||||||
|
return dup2(oldfd, fd);
|
||||||
|
}
|
||||||
|
|
||||||
void close_file(void *stream)
|
void close_file(void *stream)
|
||||||
{
|
{
|
||||||
close(*((int *) stream));
|
close(*((int *) stream));
|
||||||
|
|
Loading…
Reference in a new issue