Skip to content

Commit

Permalink
vfs: don't set FILE_CREATED before calling ->atomic_open()
Browse files Browse the repository at this point in the history
If O_CREAT|O_EXCL are passed to open, then we know that either

 - the file is successfully created, or
 - the operation fails in some way.

So previously we set FILE_CREATED before calling ->atomic_open() so the
filesystem doesn't have to.  This, however, led to bugs in the
implementation that went unnoticed when the filesystem didn't check for
existence, yet returned success.  To prevent this kind of bug, require
filesystems to always explicitly set FILE_CREATED on O_CREAT|O_EXCL and
verify this in the VFS.

Also added a couple more verifications for the result of atomic_open():

 - Warn if filesystem set FILE_CREATED despite the lack of O_CREAT.
 - Warn if filesystem set FILE_CREATED but gave a negative dentry.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Miklos Szeredi authored and Al Viro committed Sep 16, 2013
1 parent 01c919a commit 116cc02
Showing 1 changed file with 8 additions and 3 deletions.
11 changes: 8 additions & 3 deletions fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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
Expand Down Expand Up @@ -2726,7 +2726,11 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
}

acc_mode = op->acc_mode;
if (WARN_ON(excl && !(*opened & FILE_CREATED)))
*opened |= FILE_CREATED;

if (*opened & FILE_CREATED) {
WARN_ON(!(open_flag & O_CREAT));
fsnotify_create(dir, dentry);
acc_mode = MAY_OPEN;
}
Expand All @@ -2740,6 +2744,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
dput(dentry);
dentry = file->f_path.dentry;
}
WARN_ON(!dentry->d_inode && (*opened & FILE_CREATED));
if (create_error && dentry->d_inode == NULL) {
error = create_error;
goto out;
Expand Down

0 comments on commit 116cc02

Please sign in to comment.