forked from Minki/linux
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs fixes from Al Viro: "atomic_open-related fixes (Miklos' series, with EEXIST-related parts replaced with fix in fs/namei.c:atomic_open() instead of messing with the instances) + race fix in autofs + leak on failure exit in 9p" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: 9p: don't forget to destroy inode cache if fscache registration fails atomic_open: take care of EEXIST in no-open case with O_CREAT|O_EXCL in fs/namei.c vfs: don't set FILE_CREATED before calling ->atomic_open() nfs: set FILE_CREATED gfs2: set FILE_CREATED cifs: fix filp leak in cifs_atomic_open() vfs: improve i_op->atomic_open() documentation autofs4: close the races around autofs4_notify_daemon()
This commit is contained in:
commit
3fe03debfc
@ -359,11 +359,9 @@ struct inode_operations {
|
||||
ssize_t (*listxattr) (struct dentry *, char *, size_t);
|
||||
int (*removexattr) (struct dentry *, const char *);
|
||||
void (*update_time)(struct inode *, struct timespec *, int);
|
||||
int (*atomic_open)(struct inode *, struct dentry *,
|
||||
int (*atomic_open)(struct inode *, struct dentry *, struct file *,
|
||||
unsigned open_flag, umode_t create_mode, int *opened);
|
||||
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
|
||||
} ____cacheline_aligned;
|
||||
struct file *, unsigned open_flag,
|
||||
umode_t create_mode, int *opened);
|
||||
};
|
||||
|
||||
Again, all methods are called without any locks being held, unless
|
||||
@ -470,9 +468,11 @@ otherwise noted.
|
||||
method the filesystem can look up, possibly create and open the file in
|
||||
one atomic operation. If it cannot perform this (e.g. the file type
|
||||
turned out to be wrong) it may signal this by returning 1 instead of
|
||||
usual 0 or -ve . This method is only called if the last
|
||||
component is negative or needs lookup. Cached positive dentries are
|
||||
still handled by f_op->open().
|
||||
usual 0 or -ve . This method is only called if the last component is
|
||||
negative or needs lookup. Cached positive dentries are still handled by
|
||||
f_op->open(). If the file was created, the FILE_CREATED flag should be
|
||||
set in "opened". In case of O_EXCL the method must only succeed if the
|
||||
file didn't exist and hence FILE_CREATED shall always be set on success.
|
||||
|
||||
tmpfile: called in the end of O_TMPFILE open(). Optional, equivalent to
|
||||
atomically creating, opening and unlinking a file in given directory.
|
||||
|
@ -603,10 +603,11 @@ static int v9fs_cache_register(void)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_9P_FSCACHE
|
||||
return fscache_register_netfs(&v9fs_cache_netfs);
|
||||
#else
|
||||
return ret;
|
||||
ret = fscache_register_netfs(&v9fs_cache_netfs);
|
||||
if (ret < 0)
|
||||
v9fs_destroy_inode_cache();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void v9fs_cache_unregister(void)
|
||||
|
@ -267,14 +267,8 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
|
||||
}
|
||||
|
||||
/* Only creates */
|
||||
if (!(flags & O_CREAT))
|
||||
if (!(flags & O_CREAT) || dentry->d_inode)
|
||||
return finish_no_open(file, res);
|
||||
else if (dentry->d_inode) {
|
||||
if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
||||
return -EEXIST;
|
||||
else
|
||||
return finish_no_open(file, res);
|
||||
}
|
||||
|
||||
v9ses = v9fs_inode2v9ses(dir);
|
||||
|
||||
|
@ -109,13 +109,7 @@ static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
|
||||
|
||||
pkt.hdr.proto_version = sbi->version;
|
||||
pkt.hdr.type = type;
|
||||
mutex_lock(&sbi->wq_mutex);
|
||||
|
||||
/* Check if we have become catatonic */
|
||||
if (sbi->catatonic) {
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
/* Kernel protocol v4 missing and expire packets */
|
||||
case autofs_ptype_missing:
|
||||
@ -427,7 +421,6 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
|
||||
wq->tgid = current->tgid;
|
||||
wq->status = -EINTR; /* Status return if interrupted */
|
||||
wq->wait_ctr = 2;
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
|
||||
if (sbi->version < 5) {
|
||||
if (notify == NFY_MOUNT)
|
||||
@ -449,15 +442,15 @@ int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
|
||||
(unsigned long) wq->wait_queue_token, wq->name.len,
|
||||
wq->name.name, notify);
|
||||
|
||||
/* autofs4_notify_daemon() may block */
|
||||
/* autofs4_notify_daemon() may block; it will unlock ->wq_mutex */
|
||||
autofs4_notify_daemon(sbi, wq, type);
|
||||
} else {
|
||||
wq->wait_ctr++;
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
kfree(qstr.name);
|
||||
DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
|
||||
(unsigned long) wq->wait_queue_token, wq->name.len,
|
||||
wq->name.name, notify);
|
||||
mutex_unlock(&sbi->wq_mutex);
|
||||
kfree(qstr.name);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -500,6 +500,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
|
||||
if (server->ops->close)
|
||||
server->ops->close(xid, tcon, &fid);
|
||||
cifs_del_pending_open(&open);
|
||||
fput(file);
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -694,8 +694,10 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||
|
||||
mark_inode_dirty(inode);
|
||||
d_instantiate(dentry, inode);
|
||||
if (file)
|
||||
if (file) {
|
||||
*opened |= FILE_CREATED;
|
||||
error = finish_open(file, dentry, gfs2_open_common, opened);
|
||||
}
|
||||
gfs2_glock_dq_uninit(ghs);
|
||||
gfs2_glock_dq_uninit(ghs + 1);
|
||||
return error;
|
||||
|
34
fs/namei.c
34
fs/namei.c
@ -2656,6 +2656,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
||||
int acc_mode;
|
||||
int create_error = 0;
|
||||
struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
|
||||
bool excl;
|
||||
|
||||
BUG_ON(dentry->d_inode);
|
||||
|
||||
@ -2669,10 +2670,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
||||
if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
|
||||
mode &= ~current_umask();
|
||||
|
||||
if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
|
||||
excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
|
||||
if (excl)
|
||||
open_flag &= ~O_TRUNC;
|
||||
*opened |= FILE_CREATED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checking write permission is tricky, bacuse we don't know if we are
|
||||
@ -2725,12 +2725,6 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
||||
goto out;
|
||||
}
|
||||
|
||||
acc_mode = op->acc_mode;
|
||||
if (*opened & FILE_CREATED) {
|
||||
fsnotify_create(dir, dentry);
|
||||
acc_mode = MAY_OPEN;
|
||||
}
|
||||
|
||||
if (error) { /* returned 1, that is */
|
||||
if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
|
||||
error = -EIO;
|
||||
@ -2740,9 +2734,19 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
||||
dput(dentry);
|
||||
dentry = file->f_path.dentry;
|
||||
}
|
||||
if (create_error && dentry->d_inode == NULL) {
|
||||
error = create_error;
|
||||
goto out;
|
||||
if (*opened & FILE_CREATED)
|
||||
fsnotify_create(dir, dentry);
|
||||
if (!dentry->d_inode) {
|
||||
WARN_ON(*opened & FILE_CREATED);
|
||||
if (create_error) {
|
||||
error = create_error;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (excl && !(*opened & FILE_CREATED)) {
|
||||
error = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
goto looked_up;
|
||||
}
|
||||
@ -2751,6 +2755,12 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
|
||||
* We didn't have the inode before the open, so check open permission
|
||||
* here.
|
||||
*/
|
||||
acc_mode = op->acc_mode;
|
||||
if (*opened & FILE_CREATED) {
|
||||
WARN_ON(!(open_flag & O_CREAT));
|
||||
fsnotify_create(dir, dentry);
|
||||
acc_mode = MAY_OPEN;
|
||||
}
|
||||
error = may_open(&file->f_path, acc_mode, open_flag);
|
||||
if (error)
|
||||
fput(file);
|
||||
|
@ -1392,6 +1392,9 @@ static int nfs_finish_open(struct nfs_open_context *ctx,
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((open_flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
||||
*opened |= FILE_CREATED;
|
||||
|
||||
err = finish_open(file, dentry, do_open, opened);
|
||||
if (err)
|
||||
goto out;
|
||||
|
21
fs/open.c
21
fs/open.c
@ -744,14 +744,24 @@ cleanup_file:
|
||||
|
||||
/**
|
||||
* finish_open - finish opening a file
|
||||
* @od: opaque open data
|
||||
* @file: file pointer
|
||||
* @dentry: pointer to dentry
|
||||
* @open: open callback
|
||||
* @opened: state of open
|
||||
*
|
||||
* This can be used to finish opening a file passed to i_op->atomic_open().
|
||||
*
|
||||
* If the open callback is set to NULL, then the standard f_op->open()
|
||||
* filesystem callback is substituted.
|
||||
*
|
||||
* NB: the dentry reference is _not_ consumed. If, for example, the dentry is
|
||||
* the return value of d_splice_alias(), then the caller needs to perform dput()
|
||||
* on it after finish_open().
|
||||
*
|
||||
* On successful return @file is a fully instantiated open file. After this, if
|
||||
* an error occurs in ->atomic_open(), it needs to clean up with fput().
|
||||
*
|
||||
* Returns zero on success or -errno if the open failed.
|
||||
*/
|
||||
int finish_open(struct file *file, struct dentry *dentry,
|
||||
int (*open)(struct inode *, struct file *),
|
||||
@ -772,11 +782,16 @@ EXPORT_SYMBOL(finish_open);
|
||||
/**
|
||||
* finish_no_open - finish ->atomic_open() without opening the file
|
||||
*
|
||||
* @od: opaque open data
|
||||
* @file: file pointer
|
||||
* @dentry: dentry or NULL (as returned from ->lookup())
|
||||
*
|
||||
* This can be used to set the result of a successful lookup in ->atomic_open().
|
||||
* The filesystem's atomic_open() method shall return NULL after calling this.
|
||||
*
|
||||
* NB: unlike finish_open() this function does consume the dentry reference and
|
||||
* the caller need not dput() it.
|
||||
*
|
||||
* Returns "1" which must be the return value of ->atomic_open() after having
|
||||
* called this function.
|
||||
*/
|
||||
int finish_no_open(struct file *file, struct dentry *dentry)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user