@@ -516,18 +516,18 @@ static int get_security_context(struct dentry *entry, umode_t mode,
}
/*
- * Atomic create+open operation
- *
- * If the filesystem doesn't support this, then fall back to separate
- * 'mknod' + 'open' requests.
+ * This is common function for initiating atomic operations into libfuse.
+ * Currently being used by Atomic create(atomic lookup + create) and
+ * Atomic open(atomic lookup + open).
*/
-static int fuse_create_open(struct inode *dir, struct dentry *entry,
- struct file *file, unsigned int flags,
- umode_t mode, uint32_t opcode)
+static int fuse_atomic_do_common(struct inode *dir, struct dentry *entry,
+ struct dentry **alias, struct file *file,
+ unsigned int flags, umode_t mode, uint32_t opcode)
{
int err;
struct inode *inode;
struct fuse_mount *fm = get_fuse_mount(dir);
+ struct fuse_conn *fc = get_fuse_conn(dir);
FUSE_ARGS(args);
struct fuse_forget_link *forget;
struct fuse_create_in inarg;
@@ -539,9 +539,13 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
void *security_ctx = NULL;
u32 security_ctxlen;
bool atomic_create = (opcode == FUSE_ATOMIC_CREATE ? true : false);
+ bool create_op = (opcode == FUSE_CREATE ||
+ opcode == FUSE_ATOMIC_CREATE) ? true : false;
+ if (alias)
+ *alias = NULL;
/* Userspace expects S_IFREG in create mode */
- BUG_ON((mode & S_IFMT) != S_IFREG);
+ BUG_ON(create_op && (mode & S_IFMT) != S_IFREG);
forget = fuse_alloc_forget();
err = -ENOMEM;
@@ -553,7 +557,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
if (!ff)
goto out_put_forget_req;
- if (!fm->fc->dont_mask)
+ if (!fc->dont_mask)
mode &= ~current_umask();
flags &= ~O_NOCTTY;
@@ -642,8 +646,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
err = PTR_ERR(res);
goto out_err;
}
- /* res is expected to be NULL since its REG file */
- WARN_ON(res);
+ entry = res;
+ if (alias)
+ *alias = res;
}
}
fuse_change_entry_timeout(entry, &outentry);
@@ -681,8 +686,8 @@ static int fuse_atomic_create(struct inode *dir, struct dentry *entry,
if (fc->no_atomic_create)
return -ENOSYS;
- err = fuse_create_open(dir, entry, file, flags, mode,
- FUSE_ATOMIC_CREATE);
+ err = fuse_atomic_do_common(dir, entry, NULL, file, flags, mode,
+ FUSE_ATOMIC_CREATE);
/* If atomic create is not implemented then indicate in fc so that next
* request falls back to normal create instead of going into libufse and
* returning with -ENOSYS.
@@ -694,6 +699,29 @@ static int fuse_atomic_create(struct inode *dir, struct dentry *entry,
return err;
}
+static int fuse_do_atomic_open(struct inode *dir, struct dentry *entry,
+ struct dentry **alias, struct file *file,
+ unsigned int flags, umode_t mode)
+{
+ int err;
+ struct fuse_conn *fc = get_fuse_conn(dir);
+
+ if (!fc->do_atomic_open)
+ return -ENOSYS;
+
+ err = fuse_atomic_do_common(dir, entry, alias, file, flags, mode,
+ FUSE_ATOMIC_OPEN);
+ /* Atomic open imply atomic trunc as well i.e truncate should be performed
+ * as part of atomic open call itself.
+ */
+ if (!fc->atomic_o_trunc) {
+ if (fc->do_atomic_open)
+ fc->atomic_o_trunc = 1;
+ }
+
+ return err;
+}
+
static int fuse_mknod(struct user_namespace *, struct inode *, struct dentry *,
umode_t, dev_t);
static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
@@ -702,12 +730,23 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
{
int err;
struct fuse_conn *fc = get_fuse_conn(dir);
- struct dentry *res = NULL;
+ struct dentry *res = NULL, *alias = NULL;
bool create = flags & O_CREAT ? true : false;
if (fuse_is_bad(dir))
return -EIO;
+ if (!create) {
+ err = fuse_do_atomic_open(dir, entry, &alias,
+ file, flags, mode);
+ res = alias;
+ if (!err || err != -ENOSYS)
+ goto out_dput;
+ }
+ /*
+ * ENOSYS fall back for open- user space does not have full
+ * atomic open.
+ */
if ((!create || fc->no_atomic_create) && d_in_lookup(entry)) {
res = fuse_lookup(dir, entry, 0);
if (IS_ERR(res))
@@ -730,9 +769,14 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
/* Libfuse/user space has not implemented atomic create, therefore
* fall back to normal create.
*/
- if (err == -ENOSYS)
- err = fuse_create_open(dir, entry, file, flags, mode,
- FUSE_CREATE);
+ /* Atomic create+open operation
+ * If the filesystem doesn't support this, then fall back to separate
+ * 'mknod' + 'open' requests.
+ */
+ if (err == -ENOSYS) {
+ err = fuse_atomic_do_common(dir, entry, NULL, file, flags,
+ mode, FUSE_CREATE);
+ }
if (err == -ENOSYS) {
fc->no_create = 1;
goto mknod;
@@ -669,6 +669,9 @@ struct fuse_conn {
/** Is open/release not implemented by fs? */
unsigned no_open:1;
+ /** Is atomic open implemented by fs ? */
+ unsigned do_atomic_open : 1;
+
/** Is atomic create not implemented by fs? */
unsigned no_atomic_create:1;
@@ -1190,6 +1190,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
fc->setxattr_ext = 1;
if (flags & FUSE_SECURITY_CTX)
fc->init_security = 1;
+ if (flags & FUSE_DO_ATOMIC_OPEN)
+ fc->do_atomic_open = 1;
} else {
ra_pages = fc->max_read / PAGE_SIZE;
fc->no_lock = 1;
@@ -1235,7 +1237,7 @@ void fuse_send_init(struct fuse_mount *fm)
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
- FUSE_SECURITY_CTX;
+ FUSE_SECURITY_CTX | FUSE_DO_ATOMIC_OPEN;
#ifdef CONFIG_FUSE_DAX
if (fm->fc->dax)
flags |= FUSE_MAP_ALIGNMENT;
@@ -391,6 +391,7 @@ struct fuse_file_lock {
/* bits 32..63 get shifted down 32 bits into the flags2 field */
#define FUSE_SECURITY_CTX (1ULL << 32)
#define FUSE_HAS_INODE_DAX (1ULL << 33)
+#define FUSE_DO_ATOMIC_OPEN (1ULL << 34)
/**
* CUSE INIT request/reply flags
@@ -540,6 +541,7 @@ enum fuse_opcode {
FUSE_REMOVEMAPPING = 49,
FUSE_SYNCFS = 50,
FUSE_ATOMIC_CREATE = 51,
+ FUSE_ATOMIC_OPEN = 52,
/* CUSE specific operations */
CUSE_INIT = 4096,