mirror of https://gitee.com/openkylin/linux.git
GFS2: Add atomic_open support
I've restricted atomic_open to only operate on regular files, although I still don't understand why atomic_open should not be possible also for directories on GFS2. That can always be added in later though, if it makes sense. The ->atomic_open function can be passed negative dentries, which in most cases means either ENOENT (->lookup) or a call to d_instantiate (->create). In the GFS2 case though, we need to actually perform the look up, since we do not know whether there has been a new inode created on another node. The look up calls d_splice_alias which then tries to rehash the dentry - so the solution here is to simply check for that in d_splice_alias. The same issue is likely to affect any other cluster filesystem implementing ->atomic_open Signed-off-by: Steven Whitehouse <swhiteho@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: "J. Bruce Fields" <bfields fieldses org> Cc: Jeff Layton <jlayton@redhat.com>
This commit is contained in:
parent
5a00f3cc97
commit
6d4ade986f
11
fs/dcache.c
11
fs/dcache.c
|
@ -1612,6 +1612,10 @@ EXPORT_SYMBOL(d_obtain_alias);
|
||||||
* If a dentry was found and moved, then it is returned. Otherwise NULL
|
* If a dentry was found and moved, then it is returned. Otherwise NULL
|
||||||
* is returned. This matches the expected return value of ->lookup.
|
* is returned. This matches the expected return value of ->lookup.
|
||||||
*
|
*
|
||||||
|
* Cluster filesystems may call this function with a negative, hashed dentry.
|
||||||
|
* In that case, we know that the inode will be a regular file, and also this
|
||||||
|
* will only occur during atomic_open. So we need to check for the dentry
|
||||||
|
* being already hashed only in the final case.
|
||||||
*/
|
*/
|
||||||
struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
|
struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
|
@ -1636,8 +1640,11 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
|
||||||
security_d_instantiate(dentry, inode);
|
security_d_instantiate(dentry, inode);
|
||||||
d_rehash(dentry);
|
d_rehash(dentry);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
d_add(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
|
if (d_unhashed(dentry))
|
||||||
|
d_rehash(dentry);
|
||||||
|
}
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(d_splice_alias);
|
EXPORT_SYMBOL(d_splice_alias);
|
||||||
|
|
|
@ -537,11 +537,52 @@ static int gfs2_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gfs2_open_common - This is common to open and atomic_open
|
||||||
|
* @inode: The inode being opened
|
||||||
|
* @file: The file being opened
|
||||||
|
*
|
||||||
|
* This maybe called under a glock or not depending upon how it has
|
||||||
|
* been called. We must always be called under a glock for regular
|
||||||
|
* files, however. For other file types, it does not matter whether
|
||||||
|
* we hold the glock or not.
|
||||||
|
*
|
||||||
|
* Returns: Error code or 0 for success
|
||||||
|
*/
|
||||||
|
|
||||||
|
int gfs2_open_common(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct gfs2_file *fp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (S_ISREG(inode->i_mode)) {
|
||||||
|
ret = generic_file_open(inode, file);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = kzalloc(sizeof(struct gfs2_file), GFP_NOFS);
|
||||||
|
if (!fp)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&fp->f_fl_mutex);
|
||||||
|
|
||||||
|
gfs2_assert_warn(GFS2_SB(inode), !file->private_data);
|
||||||
|
file->private_data = fp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gfs2_open - open a file
|
* gfs2_open - open a file
|
||||||
* @inode: the inode to open
|
* @inode: the inode to open
|
||||||
* @file: the struct file for this opening
|
* @file: the struct file for this opening
|
||||||
*
|
*
|
||||||
|
* After atomic_open, this function is only used for opening files
|
||||||
|
* which are already cached. We must still get the glock for regular
|
||||||
|
* files to ensure that we have the file size uptodate for the large
|
||||||
|
* file check which is in the common code. That is only an issue for
|
||||||
|
* regular files though.
|
||||||
|
*
|
||||||
* Returns: errno
|
* Returns: errno
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -549,40 +590,22 @@ static int gfs2_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct gfs2_inode *ip = GFS2_I(inode);
|
struct gfs2_inode *ip = GFS2_I(inode);
|
||||||
struct gfs2_holder i_gh;
|
struct gfs2_holder i_gh;
|
||||||
struct gfs2_file *fp;
|
|
||||||
int error;
|
int error;
|
||||||
|
bool need_unlock = false;
|
||||||
fp = kzalloc(sizeof(struct gfs2_file), GFP_KERNEL);
|
|
||||||
if (!fp)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
mutex_init(&fp->f_fl_mutex);
|
|
||||||
|
|
||||||
gfs2_assert_warn(GFS2_SB(inode), !file->private_data);
|
|
||||||
file->private_data = fp;
|
|
||||||
|
|
||||||
if (S_ISREG(ip->i_inode.i_mode)) {
|
if (S_ISREG(ip->i_inode.i_mode)) {
|
||||||
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY,
|
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY,
|
||||||
&i_gh);
|
&i_gh);
|
||||||
if (error)
|
if (error)
|
||||||
goto fail;
|
return error;
|
||||||
|
need_unlock = true;
|
||||||
if (!(file->f_flags & O_LARGEFILE) &&
|
|
||||||
i_size_read(inode) > MAX_NON_LFS) {
|
|
||||||
error = -EOVERFLOW;
|
|
||||||
goto fail_gunlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gfs2_glock_dq_uninit(&i_gh);
|
error = gfs2_open_common(inode, file);
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
if (need_unlock)
|
||||||
|
|
||||||
fail_gunlock:
|
|
||||||
gfs2_glock_dq_uninit(&i_gh);
|
gfs2_glock_dq_uninit(&i_gh);
|
||||||
fail:
|
|
||||||
file->private_data = NULL;
|
|
||||||
kfree(fp);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
109
fs/gfs2/inode.c
109
fs/gfs2/inode.c
|
@ -535,6 +535,7 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
|
||||||
* gfs2_create_inode - Create a new inode
|
* gfs2_create_inode - Create a new inode
|
||||||
* @dir: The parent directory
|
* @dir: The parent directory
|
||||||
* @dentry: The new dentry
|
* @dentry: The new dentry
|
||||||
|
* @file: If non-NULL, the file which is being opened
|
||||||
* @mode: The permissions on the new inode
|
* @mode: The permissions on the new inode
|
||||||
* @dev: For device nodes, this is the device number
|
* @dev: For device nodes, this is the device number
|
||||||
* @symname: For symlinks, this is the link destination
|
* @symname: For symlinks, this is the link destination
|
||||||
|
@ -544,8 +545,9 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||||
|
struct file *file,
|
||||||
umode_t mode, dev_t dev, const char *symname,
|
umode_t mode, dev_t dev, const char *symname,
|
||||||
unsigned int size, int excl)
|
unsigned int size, int excl, int *opened)
|
||||||
{
|
{
|
||||||
const struct qstr *name = &dentry->d_name;
|
const struct qstr *name = &dentry->d_name;
|
||||||
struct gfs2_holder ghs[2];
|
struct gfs2_holder ghs[2];
|
||||||
|
@ -553,6 +555,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||||
struct gfs2_inode *dip = GFS2_I(dir), *ip;
|
struct gfs2_inode *dip = GFS2_I(dir), *ip;
|
||||||
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
|
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
|
||||||
struct gfs2_glock *io_gl;
|
struct gfs2_glock *io_gl;
|
||||||
|
struct dentry *d;
|
||||||
int error;
|
int error;
|
||||||
u32 aflags = 0;
|
u32 aflags = 0;
|
||||||
int arq;
|
int arq;
|
||||||
|
@ -579,9 +582,20 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||||
inode = gfs2_dir_search(dir, &dentry->d_name, !S_ISREG(mode) || excl);
|
inode = gfs2_dir_search(dir, &dentry->d_name, !S_ISREG(mode) || excl);
|
||||||
error = PTR_ERR(inode);
|
error = PTR_ERR(inode);
|
||||||
if (!IS_ERR(inode)) {
|
if (!IS_ERR(inode)) {
|
||||||
|
d = d_splice_alias(inode, dentry);
|
||||||
|
error = 0;
|
||||||
|
if (file && !IS_ERR(d)) {
|
||||||
|
if (d == NULL)
|
||||||
|
d = dentry;
|
||||||
|
if (S_ISREG(inode->i_mode))
|
||||||
|
error = finish_open(file, d, gfs2_open_common, opened);
|
||||||
|
else
|
||||||
|
error = finish_no_open(file, d);
|
||||||
|
}
|
||||||
gfs2_glock_dq_uninit(ghs);
|
gfs2_glock_dq_uninit(ghs);
|
||||||
d_instantiate(dentry, inode);
|
if (IS_ERR(d))
|
||||||
return 0;
|
return PTR_RET(d);
|
||||||
|
return error;
|
||||||
} else if (error != -ENOENT) {
|
} else if (error != -ENOENT) {
|
||||||
goto fail_gunlock;
|
goto fail_gunlock;
|
||||||
}
|
}
|
||||||
|
@ -679,10 +693,12 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||||
goto fail_gunlock3;
|
goto fail_gunlock3;
|
||||||
|
|
||||||
mark_inode_dirty(inode);
|
mark_inode_dirty(inode);
|
||||||
|
d_instantiate(dentry, inode);
|
||||||
|
if (file)
|
||||||
|
error = finish_open(file, dentry, gfs2_open_common, opened);
|
||||||
gfs2_glock_dq_uninit(ghs);
|
gfs2_glock_dq_uninit(ghs);
|
||||||
gfs2_glock_dq_uninit(ghs + 1);
|
gfs2_glock_dq_uninit(ghs + 1);
|
||||||
d_instantiate(dentry, inode);
|
return error;
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail_gunlock3:
|
fail_gunlock3:
|
||||||
gfs2_glock_dq_uninit(ghs + 1);
|
gfs2_glock_dq_uninit(ghs + 1);
|
||||||
|
@ -722,36 +738,56 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
|
||||||
static int gfs2_create(struct inode *dir, struct dentry *dentry,
|
static int gfs2_create(struct inode *dir, struct dentry *dentry,
|
||||||
umode_t mode, bool excl)
|
umode_t mode, bool excl)
|
||||||
{
|
{
|
||||||
return gfs2_create_inode(dir, dentry, S_IFREG | mode, 0, NULL, 0, excl);
|
return gfs2_create_inode(dir, dentry, NULL, S_IFREG | mode, 0, NULL, 0, excl, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gfs2_lookup - Look up a filename in a directory and return its inode
|
* __gfs2_lookup - Look up a filename in a directory and return its inode
|
||||||
* @dir: The directory inode
|
* @dir: The directory inode
|
||||||
* @dentry: The dentry of the new inode
|
* @dentry: The dentry of the new inode
|
||||||
* @nd: passed from Linux VFS, ignored by us
|
* @file: File to be opened
|
||||||
|
* @opened: atomic_open flags
|
||||||
*
|
*
|
||||||
* Called by the VFS layer. Lock dir and call gfs2_lookupi()
|
|
||||||
*
|
*
|
||||||
* Returns: errno
|
* Returns: errno
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
|
static struct dentry *__gfs2_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
unsigned int flags)
|
struct file *file, int *opened)
|
||||||
{
|
{
|
||||||
struct inode *inode = gfs2_lookupi(dir, &dentry->d_name, 0);
|
struct inode *inode;
|
||||||
if (inode && !IS_ERR(inode)) {
|
struct dentry *d;
|
||||||
struct gfs2_glock *gl = GFS2_I(inode)->i_gl;
|
|
||||||
struct gfs2_holder gh;
|
struct gfs2_holder gh;
|
||||||
|
struct gfs2_glock *gl;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
|
||||||
|
if (!inode)
|
||||||
|
return NULL;
|
||||||
|
if (IS_ERR(inode))
|
||||||
|
return ERR_CAST(inode);
|
||||||
|
|
||||||
|
gl = GFS2_I(inode)->i_gl;
|
||||||
error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
|
error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
|
||||||
if (error) {
|
if (error) {
|
||||||
iput(inode);
|
iput(inode);
|
||||||
return ERR_PTR(error);
|
return ERR_PTR(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d = d_splice_alias(inode, dentry);
|
||||||
|
if (file && S_ISREG(inode->i_mode))
|
||||||
|
error = finish_open(file, dentry, gfs2_open_common, opened);
|
||||||
|
|
||||||
gfs2_glock_dq_uninit(&gh);
|
gfs2_glock_dq_uninit(&gh);
|
||||||
|
if (error)
|
||||||
|
return ERR_PTR(error);
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
return d_splice_alias(inode, dentry);
|
|
||||||
|
static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned flags)
|
||||||
|
{
|
||||||
|
return __gfs2_lookup(dir, dentry, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1069,7 +1105,7 @@ static int gfs2_symlink(struct inode *dir, struct dentry *dentry,
|
||||||
if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1)
|
if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1)
|
||||||
return -ENAMETOOLONG;
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
return gfs2_create_inode(dir, dentry, S_IFLNK | S_IRWXUGO, 0, symname, size, 0);
|
return gfs2_create_inode(dir, dentry, NULL, S_IFLNK | S_IRWXUGO, 0, symname, size, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1085,7 +1121,7 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
{
|
{
|
||||||
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
struct gfs2_sbd *sdp = GFS2_SB(dir);
|
||||||
unsigned dsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
|
unsigned dsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
|
||||||
return gfs2_create_inode(dir, dentry, S_IFDIR | mode, 0, NULL, dsize, 0);
|
return gfs2_create_inode(dir, dentry, NULL, S_IFDIR | mode, 0, NULL, dsize, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1100,7 +1136,43 @@ static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||||
static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||||
dev_t dev)
|
dev_t dev)
|
||||||
{
|
{
|
||||||
return gfs2_create_inode(dir, dentry, mode, dev, NULL, 0, 0);
|
return gfs2_create_inode(dir, dentry, NULL, mode, dev, NULL, 0, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gfs2_atomic_open - Atomically open a file
|
||||||
|
* @dir: The directory
|
||||||
|
* @dentry: The proposed new entry
|
||||||
|
* @file: The proposed new struct file
|
||||||
|
* @flags: open flags
|
||||||
|
* @mode: File mode
|
||||||
|
* @opened: Flag to say whether the file has been opened or not
|
||||||
|
*
|
||||||
|
* Returns: error code or 0 for success
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||||
|
struct file *file, unsigned flags,
|
||||||
|
umode_t mode, int *opened)
|
||||||
|
{
|
||||||
|
struct dentry *d;
|
||||||
|
bool excl = !!(flags & O_EXCL);
|
||||||
|
|
||||||
|
d = __gfs2_lookup(dir, dentry, file, opened);
|
||||||
|
if (IS_ERR(d))
|
||||||
|
return PTR_ERR(d);
|
||||||
|
if (d == NULL)
|
||||||
|
d = dentry;
|
||||||
|
if (d->d_inode) {
|
||||||
|
if (!(*opened & FILE_OPENED))
|
||||||
|
return finish_no_open(file, d);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & O_CREAT))
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
return gfs2_create_inode(dir, dentry, file, S_IFREG | mode, 0, NULL, 0, excl, opened);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1780,6 +1852,7 @@ const struct inode_operations gfs2_dir_iops = {
|
||||||
.removexattr = gfs2_removexattr,
|
.removexattr = gfs2_removexattr,
|
||||||
.fiemap = gfs2_fiemap,
|
.fiemap = gfs2_fiemap,
|
||||||
.get_acl = gfs2_get_acl,
|
.get_acl = gfs2_get_acl,
|
||||||
|
.atomic_open = gfs2_atomic_open,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations gfs2_symlink_iops = {
|
const struct inode_operations gfs2_symlink_iops = {
|
||||||
|
|
|
@ -109,6 +109,7 @@ extern int gfs2_permission(struct inode *inode, int mask);
|
||||||
extern int gfs2_setattr_simple(struct inode *inode, struct iattr *attr);
|
extern int gfs2_setattr_simple(struct inode *inode, struct iattr *attr);
|
||||||
extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
|
extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
|
||||||
extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
|
extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
|
||||||
|
extern int gfs2_open_common(struct inode *inode, struct file *file);
|
||||||
|
|
||||||
extern const struct inode_operations gfs2_file_iops;
|
extern const struct inode_operations gfs2_file_iops;
|
||||||
extern const struct inode_operations gfs2_dir_iops;
|
extern const struct inode_operations gfs2_dir_iops;
|
||||||
|
|
Loading…
Reference in New Issue