From: Paulo Alcantara <pc@manguebit.org>
To: viro@zeniv.linux.org.uk, smfrench@gmail.com
Cc: "Paulo Alcantara (Red Hat)" <pc@manguebit.org>,
David Howells <dhowells@redhat.com>,
linux-fsdevel@vger.kernel.org, linux-cifs@vger.kernel.org
Subject: [PATCH 2/2] smb: client: add support for O_TMPFILE
Date: Sun, 5 Apr 2026 18:18:19 -0300 [thread overview]
Message-ID: <20260405211819.1251369-2-pc@manguebit.org> (raw)
In-Reply-To: <20260405211819.1251369-1-pc@manguebit.org>
Implement O_TMPFILE support for SMB2+ in the CIFS client.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
Cc: David Howells <dhowells@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-cifs@vger.kernel.org
---
fs/smb/client/cifsfs.c | 4 +
fs/smb/client/cifsfs.h | 19 ++
fs/smb/client/cifsglob.h | 23 ++-
fs/smb/client/cifsproto.h | 3 +-
fs/smb/client/dir.c | 294 ++++++++++++++++++++++-----
fs/smb/client/file.c | 56 ++++--
fs/smb/client/inode.c | 3 +-
fs/smb/client/link.c | 1 +
fs/smb/client/smb2inode.c | 403 ++++++++++++++------------------------
9 files changed, 477 insertions(+), 329 deletions(-)
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 32d0305a1239..f4bed8d4a072 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -124,6 +124,9 @@ MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory conten
/* Module-wide total cached dirents (in bytes) across all tcons */
atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
+atomic_t cifs_sillycounter;
+atomic_t cifs_tmpcounter;
+
/*
* Write-only module parameter to drop all cached directory entries across
* all CIFS mounts. Echo a non-zero value to trigger.
@@ -1199,6 +1202,7 @@ MODULE_ALIAS("smb3");
const struct inode_operations cifs_dir_inode_ops = {
.create = cifs_create,
.atomic_open = cifs_atomic_open,
+ .tmpfile = cifs_tmpfile,
.lookup = cifs_lookup,
.getattr = cifs_getattr,
.unlink = cifs_unlink,
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index e320d39b01f5..64c7a4c6ac83 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -13,6 +13,9 @@
#define ROOT_I 2
+extern atomic_t cifs_sillycounter;
+extern atomic_t cifs_tmpcounter;
+
/*
* ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
* so that it will fit. We use hash_64 to convert the value to 31 bits, and
@@ -53,6 +56,8 @@ int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
struct dentry *direntry, umode_t mode, bool excl);
int cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct file *file, unsigned int oflags, umode_t mode);
+int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+ struct file *file, umode_t mode);
struct dentry *cifs_lookup(struct inode *parent_dir_inode,
struct dentry *direntry, unsigned int flags);
int cifs_unlink(struct inode *dir, struct dentry *dentry);
@@ -142,6 +147,20 @@ struct smb3_fs_context;
struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type, int flags,
struct smb3_fs_context *old_ctx);
+char *cifs_silly_fullpath(struct dentry *dentry);
+
+#define CIFS_TMPNAME_PREFIX ".__smbfile_tmp"
+#define CIFS_TMPNAME_PREFIX_LEN ((int)sizeof(CIFS_TMPNAME_PREFIX) - 1)
+#define CIFS_TMPNAME_COUNTER_LEN ((int)sizeof(cifs_tmpcounter) * 2)
+#define CIFS_TMPNAME_LEN \
+ (CIFS_TMPNAME_PREFIX_LEN + CIFS_TMPNAME_COUNTER_LEN)
+
+#define CIFS_SILLYNAME_PREFIX ".__smbfile_silly"
+#define CIFS_SILLYNAME_PREFIX_LEN ((int)sizeof(CIFS_SILLYNAME_PREFIX) - 1)
+#define CIFS_SILLYNAME_COUNTER_LEN ((int)sizeof(cifs_sillycounter) * 2)
+#define CIFS_SILLYNAME_LEN \
+ (CIFS_SILLYNAME_PREFIX_LEN + CIFS_SILLYNAME_COUNTER_LEN)
+
#ifdef CONFIG_CIFS_NFSD_EXPORT
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 709e96e07791..ccfde157d3be 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1534,9 +1534,16 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
#define CIFS_CACHE_RW_FLG (CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
#define CIFS_CACHE_RHW_FLG (CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)
-/*
- * One of these for each file inode
- */
+enum cifs_inode_flags {
+ CIFS_INODE_PENDING_OPLOCK_BREAK, /* oplock break in progress */
+ CIFS_INODE_PENDING_WRITERS, /* Writes in progress */
+ CIFS_INODE_FLAG_UNUSED, /* Unused flag */
+ CIFS_INO_DELETE_PENDING, /* delete pending on server */
+ CIFS_INO_INVALID_MAPPING, /* pagecache is invalid */
+ CIFS_INO_LOCK, /* lock bit for synchronization */
+ CIFS_INO_TMPFILE, /* for O_TMPFILE inodes */
+ CIFS_INO_CLOSE_ON_LOCK, /* Not to defer the close when lock is set */
+};
struct cifsInodeInfo {
struct netfs_inode netfs; /* Netfslib context and vfs inode */
@@ -1554,13 +1561,6 @@ struct cifsInodeInfo {
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
unsigned int oplock; /* oplock/lease level we have */
__u16 epoch; /* used to track lease state changes */
-#define CIFS_INODE_PENDING_OPLOCK_BREAK (0) /* oplock break in progress */
-#define CIFS_INODE_PENDING_WRITERS (1) /* Writes in progress */
-#define CIFS_INODE_FLAG_UNUSED (2) /* Unused flag */
-#define CIFS_INO_DELETE_PENDING (3) /* delete pending on server */
-#define CIFS_INO_INVALID_MAPPING (4) /* pagecache is invalid */
-#define CIFS_INO_LOCK (5) /* lock bit for synchronization */
-#define CIFS_INO_CLOSE_ON_LOCK (7) /* Not to defer the close when lock is set */
unsigned long flags;
spinlock_t writers_lock;
unsigned int writers; /* Number of writers on this inode */
@@ -2259,6 +2259,7 @@ struct smb2_compound_vars {
struct kvec qi_iov;
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
+ struct kvec hl_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec unlink_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec rename_iov[SMB2_SET_INFO_IOV_SIZE];
struct kvec close_iov;
@@ -2383,6 +2384,8 @@ static inline int cifs_open_create_options(unsigned int oflags, int opts)
opts |= CREATE_WRITE_THROUGH;
if (oflags & O_DIRECT)
opts |= CREATE_NO_BUFFER;
+ if (oflags & O_TMPFILE)
+ opts |= CREATE_DELETE_ON_CLOSE;
return opts;
}
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 884bfa1cf0b4..c24c50d732e6 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -141,7 +141,8 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
unsigned int find_flags, unsigned int open_flags,
struct cifsFileInfo **ret_file);
-int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name, int flags,
+int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
+ struct inode *inode, int flags,
struct cifsFileInfo **ret_file);
struct cifsFileInfo *__find_readable_file(struct cifsInodeInfo *cifs_inode,
unsigned int find_flags,
diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c
index 6d2378eeb7f6..933d78998f84 100644
--- a/fs/smb/client/dir.c
+++ b/fs/smb/client/dir.c
@@ -172,20 +172,44 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
return 0;
}
+static char *alloc_parent_path(struct dentry *dentry, size_t namelen)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dentry);
+ void *page = alloc_dentry_path();
+ const char *path;
+ size_t size;
+ char *npath;
+
+ path = build_path_from_dentry(dentry->d_parent, page);
+ if (IS_ERR(path)) {
+ npath = ERR_CAST(path);
+ goto out;
+ }
+
+ size = strlen(path) + namelen + 2;
+ npath = kmalloc(size, GFP_KERNEL);
+ if (!npath)
+ npath = ERR_PTR(-ENOMEM);
+ else
+ scnprintf(npath, size, "%s%c", path, CIFS_DIR_SEP(cifs_sb));
+out:
+ free_dentry_path(page);
+ return npath;
+}
/* Inode operations in similar order to how they appear in Linux file fs.h */
-
-static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
- struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
- struct cifs_fid *fid, struct cifs_open_info_data *buf)
+static int __cifs_do_create(struct inode *dir, struct dentry *direntry,
+ const char *full_path, unsigned int xid,
+ struct tcon_link *tlink, unsigned int oflags,
+ umode_t mode, __u32 *oplock, struct cifs_fid *fid,
+ struct cifs_open_info_data *buf,
+ struct inode **inode)
{
int rc = -ENOENT;
int create_options = CREATE_NOT_DIR;
int desired_access;
- struct cifs_sb_info *cifs_sb = CIFS_SB(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir);
struct cifs_tcon *tcon = tlink_tcon(tlink);
- const char *full_path;
- void *page = alloc_dentry_path();
struct inode *newinode = NULL;
unsigned int sbflags = cifs_sb_flags(cifs_sb);
int disposition;
@@ -199,21 +223,15 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
if (tcon->ses->server->oplocks)
*oplock = REQ_OPLOCK;
- full_path = build_path_from_dentry(direntry, page);
- if (IS_ERR(full_path)) {
- rc = PTR_ERR(full_path);
- goto out;
- }
-
/* If we're caching, we need to be able to fill in around partial writes. */
- if (cifs_fscache_enabled(inode) && (oflags & O_ACCMODE) == O_WRONLY)
+ if (cifs_fscache_enabled(dir) && (oflags & O_ACCMODE) == O_WRONLY)
rdwr_for_fscache = 1;
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
- rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode,
+ rc = cifs_posix_open(full_path, &newinode, dir->i_sb, mode,
oflags, oplock, &fid->netfid, xid);
switch (rc) {
case 0:
@@ -225,8 +243,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
if (S_ISDIR(newinode->i_mode)) {
CIFSSMBClose(xid, tcon, fid->netfid);
iput(newinode);
- rc = -EISDIR;
- goto out;
+ return -EISDIR;
}
if (!S_ISREG(newinode->i_mode)) {
@@ -269,7 +286,7 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
break;
default:
- goto out;
+ return rc;
}
/*
* fallthrough to retry, using older open call, this is case
@@ -287,26 +304,30 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
desired_access |= GENERIC_WRITE;
if (rdwr_for_fscache == 1)
desired_access |= GENERIC_READ;
+ if (oflags & O_TMPFILE)
+ desired_access |= DELETE;
disposition = FILE_OVERWRITE_IF;
- if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ if (oflags & O_CREAT) {
+ if (oflags & O_EXCL)
+ disposition = FILE_CREATE;
+ else if (oflags & O_TRUNC)
+ disposition = FILE_OVERWRITE_IF;
+ else
+ disposition = FILE_OPEN_IF;
+ } else if (oflags & O_TMPFILE) {
disposition = FILE_CREATE;
- else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
- disposition = FILE_OVERWRITE_IF;
- else if ((oflags & O_CREAT) == O_CREAT)
- disposition = FILE_OPEN_IF;
- else
+ } else {
cifs_dbg(FYI, "Create flag not set in create function\n");
+ }
/*
* BB add processing to set equivalent of mode - e.g. via CreateX with
* ACLs
*/
- if (!server->ops->open) {
- rc = -ENOSYS;
- goto out;
- }
+ if (!server->ops->open)
+ return -EOPNOTSUPP;
create_options |= cifs_open_create_options(oflags, create_options);
/*
@@ -358,10 +379,10 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
rdwr_for_fscache = 2;
goto retry_open;
}
- goto out;
+ return rc;
}
if (rdwr_for_fscache == 2)
- cifs_invalidate_cache(inode, FSCACHE_INVAL_DIO_WRITE);
+ cifs_invalidate_cache(dir, FSCACHE_INVAL_DIO_WRITE);
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
/*
@@ -379,8 +400,8 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
if (sbflags & CIFS_MOUNT_SET_UID) {
args.uid = current_fsuid();
- if (inode->i_mode & S_ISGID)
- args.gid = inode->i_gid;
+ if (dir->i_mode & S_ISGID)
+ args.gid = dir->i_gid;
else
args.gid = current_fsgid();
} else {
@@ -402,14 +423,14 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
cifs_create_get_file_info:
/* server might mask mode so we have to query for it */
if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
+ rc = cifs_get_inode_info_unix(&newinode, full_path, dir->i_sb,
xid);
else {
#else
{
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
/* TODO: Add support for calling POSIX query info here, but passing in fid */
- rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
+ rc = cifs_get_inode_info(&newinode, full_path, buf, dir->i_sb, xid, fid);
if (newinode) {
if (server->ops->set_lease_key)
server->ops->set_lease_key(newinode, fid);
@@ -418,8 +439,8 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
newinode->i_mode = mode;
if (sbflags & CIFS_MOUNT_SET_UID) {
newinode->i_uid = current_fsuid();
- if (inode->i_mode & S_ISGID)
- newinode->i_gid = inode->i_gid;
+ if (dir->i_mode & S_ISGID)
+ newinode->i_gid = dir->i_gid;
else
newinode->i_gid = current_fsgid();
}
@@ -436,17 +457,13 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
goto out_err;
}
- if (newinode)
- if (S_ISDIR(newinode->i_mode)) {
- rc = -EISDIR;
- goto out_err;
- }
+ if (newinode && S_ISDIR(newinode->i_mode)) {
+ rc = -EISDIR;
+ goto out_err;
+ }
d_drop(direntry);
- d_add(direntry, newinode);
-
-out:
- free_dentry_path(page);
+ *inode = newinode;
return rc;
out_err:
@@ -454,7 +471,32 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
server->ops->close(xid, tcon, fid);
if (newinode)
iput(newinode);
- goto out;
+ return rc;
+}
+
+static int cifs_do_create(struct inode *dir, struct dentry *direntry,
+ unsigned int xid, struct tcon_link *tlink,
+ unsigned int oflags, umode_t mode,
+ __u32 *oplock, struct cifs_fid *fid,
+ struct cifs_open_info_data *buf)
+{
+ void *page = alloc_dentry_path();
+ const char *full_path;
+ struct inode *inode;
+ int rc;
+
+ full_path = build_path_from_dentry(direntry, page);
+ if (IS_ERR(full_path)) {
+ rc = PTR_ERR(full_path);
+ } else {
+ rc = __cifs_do_create(dir, direntry, full_path, xid,
+ tlink, oflags, mode, oplock,
+ fid, buf, &inode);
+ if (!rc)
+ d_add(direntry, inode);
+ }
+ free_dentry_path(page);
+ return rc;
}
int
@@ -959,6 +1001,166 @@ static int cifs_ci_compare(const struct dentry *dentry,
return 0;
}
+static int set_hidden_attr(const unsigned int xid,
+ struct TCP_Server_Info *server,
+ struct file *file)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct cifsInodeInfo *cinode = CIFS_I(d_inode(dentry));
+ FILE_BASIC_INFO fi = {
+ .Attributes = cpu_to_le32(cinode->cifsAttrs |
+ ATTR_HIDDEN),
+ };
+ void *page = alloc_dentry_path();
+ const char *full_path;
+ int rc;
+
+ full_path = build_path_from_dentry(dentry, page);
+ if (IS_ERR(full_path))
+ rc = PTR_ERR(full_path);
+ else
+ rc = server->ops->set_file_info(d_inode(dentry),
+ full_path, &fi, xid);
+ free_dentry_path(page);
+ return rc;
+}
+
+int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+ struct file *file, umode_t mode)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dir);
+ size_t size = CIFS_TMPNAME_LEN + 1;
+ int retries = 0, max_retries = 16;
+ struct TCP_Server_Info *server;
+ struct cifs_pending_open open;
+ struct cifsFileInfo *cfile;
+ struct cifs_fid fid = {};
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ unsigned int sbflags;
+ struct inode *inode;
+ char *path, *name;
+ unsigned int xid;
+ __u32 oplock;
+ int rc;
+
+ if (unlikely(cifs_forced_shutdown(cifs_sb)))
+ return smb_EIO(smb_eio_trace_forced_shutdown);
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ xid = get_xid();
+
+ if (server->vals->protocol_id < SMB20_PROT_ID) {
+ cifs_dbg(VFS | ONCE, "O_TMPFILE is supported only in SMB2+\n");
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (server->ops->new_lease_key)
+ server->ops->new_lease_key(&fid);
+ cifs_add_pending_open(&fid, tlink, &open);
+
+ path = alloc_parent_path(dentry, size - 1);
+ if (IS_ERR(path)) {
+ cifs_del_pending_open(&open);
+ rc = PTR_ERR(path);
+ goto out;
+ }
+
+ name = path + strlen(path);
+ do {
+ scnprintf(name, size,
+ CIFS_TMPNAME_PREFIX "%0*x",
+ CIFS_TMPNAME_COUNTER_LEN,
+ atomic_inc_return(&cifs_tmpcounter));
+ rc = __cifs_do_create(dir, dentry, path, xid, tlink,
+ file->f_flags, mode, &oplock,
+ &fid, NULL, &inode);
+ if (!rc) {
+ set_nlink(inode, 0);
+ mark_inode_dirty(inode);
+ d_mark_tmpfile_name(file, &QSTR_LEN(name, size - 1));
+ d_instantiate(dentry, inode);
+ break;
+ }
+ } while (unlikely(rc == -EEXIST) && ++retries < max_retries);
+
+ kfree(path);
+ if (rc) {
+ cifs_del_pending_open(&open);
+ goto out;
+ }
+
+ rc = finish_open(file, dentry, generic_file_open);
+ if (rc)
+ goto err_open;
+
+ sbflags = cifs_sb_flags(cifs_sb);
+ if ((file->f_flags & O_DIRECT) && (sbflags & CIFS_MOUNT_STRICT_IO)) {
+ if (sbflags & CIFS_MOUNT_NO_BRL)
+ file->f_op = &cifs_file_direct_nobrl_ops;
+ else
+ file->f_op = &cifs_file_direct_ops;
+ }
+
+ cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, NULL);
+ if (!cfile) {
+ rc = -ENOMEM;
+ goto err_open;
+ }
+
+ rc = set_hidden_attr(xid, server, file);
+ if (rc)
+ goto out;
+
+ fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
+ file->f_mode & FMODE_WRITE);
+out:
+ cifs_put_tlink(tlink);
+ free_xid(xid);
+ return rc;
+err_open:
+ cifs_del_pending_open(&open);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ goto out;
+}
+
+char *cifs_silly_fullpath(struct dentry *dentry)
+{
+ unsigned char name[CIFS_SILLYNAME_LEN + 1];
+ int retries = 0, max_retries = 16;
+ size_t namesize = sizeof(name);
+ struct dentry *sdentry = NULL;
+ char *path;
+
+ do {
+ dput(sdentry);
+ scnprintf(name, namesize,
+ CIFS_SILLYNAME_PREFIX "%0*x",
+ CIFS_SILLYNAME_COUNTER_LEN,
+ atomic_inc_return(&cifs_sillycounter));
+ sdentry = lookup_noperm(&QSTR(name), dentry->d_parent);
+ if (IS_ERR(sdentry))
+ return ERR_CAST(sdentry);
+ if (d_is_negative(sdentry)) {
+ dput(sdentry);
+ path = alloc_parent_path(dentry, CIFS_SILLYNAME_LEN);
+ if (!IS_ERR(path))
+ strcat(path, name);
+ return path;
+ }
+ } while (++retries < max_retries);
+ dput(sdentry);
+ return ERR_PTR(-EBUSY);
+}
+
const struct dentry_operations cifs_ci_dentry_ops = {
.d_revalidate = cifs_d_revalidate,
.d_hash = cifs_ci_hash,
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index a69e05f86d7e..49f61ed7277b 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -406,22 +406,29 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
*/
}
-static inline int cifs_convert_flags(unsigned int flags, int rdwr_for_fscache)
+static inline int cifs_convert_flags(unsigned int oflags, int rdwr_for_fscache)
{
- if ((flags & O_ACCMODE) == O_RDONLY)
- return GENERIC_READ;
- else if ((flags & O_ACCMODE) == O_WRONLY)
- return rdwr_for_fscache == 1 ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE;
- else if ((flags & O_ACCMODE) == O_RDWR) {
+ int flags = 0;
+
+ if (oflags & O_TMPFILE)
+ flags |= DELETE;
+
+ if ((oflags & O_ACCMODE) == O_RDONLY)
+ return flags | GENERIC_READ;
+ if ((oflags & O_ACCMODE) == O_WRONLY) {
+ return flags | (rdwr_for_fscache == 1 ?
+ (GENERIC_READ | GENERIC_WRITE) : GENERIC_WRITE);
+ }
+ if ((oflags & O_ACCMODE) == O_RDWR) {
/* GENERIC_ALL is too much permission to request
can cause unnecessary access denied on create */
/* return GENERIC_ALL; */
- return (GENERIC_READ | GENERIC_WRITE);
+ return flags | GENERIC_READ | GENERIC_WRITE;
}
- return (READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES |
- FILE_WRITE_EA | FILE_APPEND_DATA | FILE_WRITE_DATA |
- FILE_READ_DATA);
+ return flags | READ_CONTROL | FILE_WRITE_ATTRIBUTES |
+ FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA |
+ FILE_WRITE_DATA | FILE_READ_DATA;
}
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@@ -696,6 +703,7 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->f_flags = file->f_flags;
cfile->invalidHandle = false;
cfile->deferred_close_scheduled = false;
+ cfile->status_file_deleted = file->f_flags & O_TMPFILE;
cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
INIT_WORK(&cfile->put, cifsFileInfo_put_work);
@@ -727,6 +735,8 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
/* if readable file instance put first in list*/
spin_lock(&cinode->open_file_lock);
+ if (file->f_flags & O_TMPFILE)
+ set_bit(CIFS_INO_TMPFILE, &cinode->flags);
fid->purge_cache = false;
server->ops->set_fid(cfile, fid, oplock);
@@ -2578,13 +2588,12 @@ int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
struct cifsFileInfo **ret_file)
{
struct cifsFileInfo *open_file, *inv_file = NULL;
+ bool fsuid_only, with_delete;
struct cifs_sb_info *cifs_sb;
bool any_available = false;
- int rc = -EBADF;
unsigned int refind = 0;
- bool fsuid_only = find_flags & FIND_FSUID_ONLY;
- bool with_delete = find_flags & FIND_WITH_DELETE;
*ret_file = NULL;
+ int rc = -EBADF;
/*
* Having a null inode here (because mapping->host was set to zero by
@@ -2600,11 +2609,15 @@ int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
cifs_sb = CIFS_SB(cifs_inode);
+ spin_lock(&cifs_inode->open_file_lock);
+ if (test_bit(CIFS_INO_TMPFILE, &cifs_inode->flags))
+ find_flags = FIND_ANY;
+
+ with_delete = find_flags & FIND_WITH_DELETE;
+ fsuid_only = find_flags & FIND_FSUID_ONLY;
/* only filter by fsuid on multiuser mounts */
if (!(cifs_sb_flags(cifs_sb) & CIFS_MOUNT_MULTIUSER))
fsuid_only = false;
-
- spin_lock(&cifs_inode->open_file_lock);
refind_writable:
if (refind > MAX_REOPEN_ATT) {
spin_unlock(&cifs_inode->open_file_lock);
@@ -2683,16 +2696,19 @@ find_writable_file(struct cifsInodeInfo *cifs_inode, int flags)
return cfile;
}
-int
-cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
- int flags,
- struct cifsFileInfo **ret_file)
+int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,
+ struct inode *inode, int flags,
+ struct cifsFileInfo **ret_file)
{
struct cifsFileInfo *cfile;
- void *page = alloc_dentry_path();
+ void *page;
*ret_file = NULL;
+ if (inode)
+ return cifs_get_writable_file(CIFS_I(inode), flags, ret_file);
+
+ page = alloc_dentry_path();
spin_lock(&tcon->open_file_lock);
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
struct cifsInodeInfo *cinode;
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 888f9e35f14b..24040909d184 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -2690,7 +2690,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
struct cached_fid *cfid = NULL;
- if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags))
+ if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags) ||
+ test_bit(CIFS_INO_TMPFILE, &cifs_i->flags))
return false;
if (cifs_i->time == 0)
return true;
diff --git a/fs/smb/client/link.c b/fs/smb/client/link.c
index 434e8fe74080..dd127917a340 100644
--- a/fs/smb/client/link.c
+++ b/fs/smb/client/link.c
@@ -503,6 +503,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
if (d_really_is_positive(old_file)) {
cifsInode = CIFS_I(d_inode(old_file));
if (rc == 0) {
+ clear_bit(CIFS_INO_TMPFILE, &cifsInode->flags);
spin_lock(&d_inode(old_file)->i_lock);
inc_nlink(d_inode(old_file));
spin_unlock(&d_inode(old_file)->i_lock);
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 364bdcff9c9d..962d5863516b 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -164,6 +164,27 @@ static int check_wsl_eas(struct kvec *rsp_iov)
return 0;
}
+/*
+ * If @cfile is NULL, then need to account for trailing CLOSE request in the
+ * compound chain.
+ */
+static void set_next_compound(struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile,
+ int i, int num_cmds,
+ struct smb_rqst *rqst, int *num_rqst)
+{
+ int k = !cfile ? 1 : 0;
+
+ if (i + 1 < num_cmds + k)
+ smb2_set_next_command(tcon, &rqst[*num_rqst]);
+ if (i + k > 0)
+ smb2_set_related(&rqst[*num_rqst]);
+ (*num_rqst)++;
+}
+
+#define COMP_PID(cfile) ((cfile) ? (cfile)->fid.persistent_fid : COMPOUND_FID)
+#define COMP_VID(cfile) ((cfile) ? (cfile)->fid.volatile_fid : COMPOUND_FID)
+
/*
* note: If cfile is passed, the reference to it is dropped here.
* So make sure that you do not reuse cfile after return from this func.
@@ -284,32 +305,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
rqst[num_rqst].rq_iov = &vars->qi_iov;
rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FILE_ALL_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb2_file_all_info) +
- PATH_MAX * 2, 0, NULL);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- FILE_ALL_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb2_file_all_info) +
- PATH_MAX * 2, 0, NULL);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_query_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FILE_ALL_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb2_file_all_info) +
+ PATH_MAX * 2, 0, NULL);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_query_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -317,35 +322,18 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
rqst[num_rqst].rq_iov = &vars->qi_iov;
rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- /* TBD: fix following to allow for longer SIDs */
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- SMB_FIND_FILE_POSIX_INFO,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb311_posix_qinfo) +
- (PATH_MAX * 2) +
- (sizeof(struct smb_sid) * 2), 0, NULL);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- SMB_FIND_FILE_POSIX_INFO,
- SMB2_O_INFO_FILE, 0,
- sizeof(struct smb311_posix_qinfo) +
- (PATH_MAX * 2) +
- (sizeof(struct smb_sid) * 2), 0, NULL);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ /* TBD: fix following to allow for longer SIDs */
+ rc = SMB2_query_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ SMB_FIND_FILE_POSIX_INFO,
+ SMB2_O_INFO_FILE, 0,
+ sizeof(struct smb311_posix_qinfo) +
+ (PATH_MAX * 2) +
+ (sizeof(struct smb_sid) * 2), 0, NULL);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_posix_query_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -363,32 +351,15 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[0] = 1; /* sizeof __u8 See MS-FSCC section 2.4.11 */
data[0] = &delete_pending[0];
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid,
- FILE_DISPOSITION_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- current->tgid,
- FILE_DISPOSITION_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_DISPOSITION_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_unlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_EOF:
@@ -398,32 +369,15 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[0] = in_iov[i].iov_len;
data[0] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid,
- FILE_END_OF_FILE_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- current->tgid,
- FILE_END_OF_FILE_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_END_OF_FILE_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ data, size);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_set_eof_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_INFO:
@@ -433,28 +387,14 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[0] = in_iov[i].iov_len;
data[0] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid, current->tgid,
- FILE_BASIC_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_BASIC_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_BASIC_INFORMATION,
+ SMB2_O_INFO_FILE, 0, data, size);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_set_info_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -474,31 +414,19 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
size[1] = len + 2 /* null */;
data[1] = in_iov[i].iov_base;
- if (cfile) {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- current->tgid, FILE_RENAME_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- } else {
- rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- current->tgid, FILE_RENAME_INFORMATION,
- SMB2_O_INFO_FILE, 0, data, size);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_set_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_RENAME_INFORMATION,
+ SMB2_O_INFO_FILE, 0, data, size);
+
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_rename_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_HARDLINK:
- rqst[num_rqst].rq_iov = &vars->si_iov[0];
+ rqst[num_rqst].rq_iov = vars->hl_iov;
rqst[num_rqst].rq_nvec = 2;
len = in_iov[i].iov_len;
@@ -514,41 +442,27 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
data[1] = in_iov[i].iov_base;
rc = SMB2_set_info_init(tcon, server,
- &rqst[num_rqst], COMPOUND_FID,
- COMPOUND_FID, current->tgid,
- FILE_LINK_INFORMATION,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ current->tgid, FILE_LINK_INFORMATION,
SMB2_O_INFO_FILE, 0, data, size);
if (rc)
goto finished;
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst++]);
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_hardlink_enter(xid, tcon->tid, ses->Suid, full_path);
break;
case SMB2_OP_SET_REPARSE:
rqst[num_rqst].rq_iov = vars->io_iov;
rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
- if (cfile) {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FSCTL_SET_REPARSE_POINT,
- in_iov[i].iov_base,
- in_iov[i].iov_len, 0);
- } else {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- FSCTL_SET_REPARSE_POINT,
- in_iov[i].iov_base,
- in_iov[i].iov_len, 0);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FSCTL_SET_REPARSE_POINT,
+ in_iov[i].iov_base,
+ in_iov[i].iov_len, 0);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_set_reparse_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -556,25 +470,13 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
rqst[num_rqst].rq_iov = vars->io_iov;
rqst[num_rqst].rq_nvec = ARRAY_SIZE(vars->io_iov);
- if (cfile) {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FSCTL_GET_REPARSE_POINT,
- NULL, 0, CIFSMaxBufSize);
- } else {
- rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
- COMPOUND_FID, COMPOUND_FID,
- FSCTL_GET_REPARSE_POINT,
- NULL, 0, CIFSMaxBufSize);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_ioctl_init(tcon, server, &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FSCTL_GET_REPARSE_POINT,
+ NULL, 0, CIFSMaxBufSize);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_get_reparse_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -582,34 +484,17 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
rqst[num_rqst].rq_iov = &vars->ea_iov;
rqst[num_rqst].rq_nvec = 1;
- if (cfile) {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- cfile->fid.persistent_fid,
- cfile->fid.volatile_fid,
- FILE_FULL_EA_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
- sizeof(wsl_query_eas),
- (void *)wsl_query_eas);
- } else {
- rc = SMB2_query_info_init(tcon, server,
- &rqst[num_rqst],
- COMPOUND_FID,
- COMPOUND_FID,
- FILE_FULL_EA_INFORMATION,
- SMB2_O_INFO_FILE, 0,
- SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
- sizeof(wsl_query_eas),
- (void *)wsl_query_eas);
- }
- if (!rc && (!cfile || num_rqst > 1)) {
- smb2_set_next_command(tcon, &rqst[num_rqst]);
- smb2_set_related(&rqst[num_rqst]);
- } else if (rc) {
+ rc = SMB2_query_info_init(tcon, server,
+ &rqst[num_rqst],
+ COMP_PID(cfile), COMP_VID(cfile),
+ FILE_FULL_EA_INFORMATION,
+ SMB2_O_INFO_FILE, 0,
+ SMB2_WSL_MAX_QUERY_EA_RESP_SIZE,
+ sizeof(wsl_query_eas),
+ (void *)wsl_query_eas);
+ if (rc)
goto finished;
- }
- num_rqst++;
+ set_next_compound(tcon, cfile, i, num_cmds, rqst, &num_rqst);
trace_smb3_query_wsl_ea_compound_enter(xid, tcon->tid,
ses->Suid, full_path);
break;
@@ -1156,7 +1041,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
cifs_i = CIFS_I(inode);
dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
data.Attributes = cpu_to_le32(dosattrs);
- cifs_get_writable_path(tcon, name, FIND_ANY, &cfile);
+ cifs_get_writable_path(tcon, name, inode, FIND_ANY, &cfile);
oparms = CIFS_OPARMS(cifs_sb, tcon, name, FILE_WRITE_ATTRIBUTES,
FILE_CREATE, CREATE_NOT_FILE, ACL_NO_MODE);
tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
@@ -1332,17 +1217,20 @@ int smb2_rename_path(const unsigned int xid,
const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb)
{
+ struct inode *inode = source_dentry ? d_inode(source_dentry) : NULL;
struct cifsFileInfo *cfile;
__u32 co = file_create_options(source_dentry);
drop_cached_dir_by_name(xid, tcon, from_name, cifs_sb);
- cifs_get_writable_path(tcon, from_name, FIND_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, from_name, inode,
+ FIND_WITH_DELETE, &cfile);
int rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
co, DELETE, SMB2_OP_RENAME, cfile, source_dentry);
if (rc == -EINVAL) {
cifs_dbg(FYI, "invalid lease key, resending request without lease");
- cifs_get_writable_path(tcon, from_name, FIND_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, from_name, inode,
+ FIND_WITH_DELETE, &cfile);
rc = smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
co, DELETE, SMB2_OP_RENAME, cfile, NULL);
}
@@ -1355,11 +1243,35 @@ int smb2_create_hardlink(const unsigned int xid,
const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb)
{
+ struct inode *inode = source_dentry ? d_inode(source_dentry) : NULL;
__u32 co = file_create_options(source_dentry);
+ struct cifsFileInfo *cfile;
+ if (inode) {
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ FILE_BASIC_INFO fi;
+ __u32 attrs;
+ int rc;
+
+ scoped_guard(spinlock, &cinode->open_file_lock) {
+ if (!test_bit(CIFS_INO_TMPFILE, &CIFS_I(inode)->flags))
+ goto no_tmpfile;
+ attrs = cinode->cifsAttrs;
+ }
+ fi = (FILE_BASIC_INFO) {
+ .Attributes = cpu_to_le32(attrs & ~ATTR_HIDDEN),
+ };
+ rc = smb2_set_file_info(inode, from_name, &fi, xid);
+ if (rc)
+ return rc;
+ }
+
+no_tmpfile:
+ cifs_get_writable_path(tcon, from_name, inode,
+ FIND_WITH_DELETE, &cfile);
return smb2_set_path_attr(xid, tcon, from_name, to_name,
cifs_sb, co, FILE_READ_ATTRIBUTES,
- SMB2_OP_HARDLINK, NULL, NULL);
+ SMB2_OP_HARDLINK, cfile, NULL);
}
int
@@ -1368,15 +1280,16 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, bool set_alloc,
struct dentry *dentry)
{
- struct cifs_open_parms oparms;
- struct cifsFileInfo *cfile;
- struct kvec in_iov;
+ struct inode *inode = dentry ? d_inode(dentry) : NULL;
__le64 eof = cpu_to_le64(size);
+ struct cifs_open_parms oparms;
+ struct cifsFileInfo *cfile;
+ struct kvec in_iov;
int rc;
in_iov.iov_base = &eof;
in_iov.iov_len = sizeof(eof);
- cifs_get_writable_path(tcon, full_path, FIND_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path, inode, FIND_ANY, &cfile);
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA,
FILE_OPEN, 0, ACL_NO_MODE);
@@ -1386,7 +1299,8 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
cfile, NULL, NULL, dentry);
if (rc == -EINVAL) {
cifs_dbg(FYI, "invalid lease key, resending request without lease");
- cifs_get_writable_path(tcon, full_path, FIND_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path,
+ inode, FIND_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb,
full_path, &oparms, &in_iov,
&(int){SMB2_OP_SET_EOF}, 1,
@@ -1416,7 +1330,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
(buf->LastWriteTime == 0) && (buf->ChangeTime == 0)) {
if (buf->Attributes == 0)
goto out; /* would be a no op, no sense sending this */
- cifs_get_writable_path(tcon, full_path, FIND_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path,
+ inode, FIND_ANY, &cfile);
}
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_ATTRIBUTES,
@@ -1475,7 +1390,7 @@ struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
if (tcon->posix_extensions) {
cmds[1] = SMB2_OP_POSIX_QUERY_INFO;
- cifs_get_writable_path(tcon, full_path, FIND_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path, NULL, FIND_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
if (!rc) {
@@ -1484,7 +1399,7 @@ struct inode *smb2_create_reparse_inode(struct cifs_open_info_data *data,
}
} else {
cmds[1] = SMB2_OP_QUERY_INFO;
- cifs_get_writable_path(tcon, full_path, FIND_ANY, &cfile);
+ cifs_get_writable_path(tcon, full_path, NULL, FIND_ANY, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms,
in_iov, cmds, 2, cfile, out_iov, out_buftype, NULL);
if (!rc) {
@@ -1566,8 +1481,8 @@ int smb2_rename_pending_delete(const char *full_path,
struct dentry *dentry,
const unsigned int xid)
{
- struct cifs_sb_info *cifs_sb = CIFS_SB(d_inode(dentry)->i_sb);
struct cifsInodeInfo *cinode = CIFS_I(d_inode(dentry));
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dentry);
__le16 *utf16_path __free(kfree) = NULL;
__u32 co = file_create_options(dentry);
int cmds[] = {
@@ -1579,14 +1494,10 @@ int smb2_rename_pending_delete(const char *full_path,
char *to_name __free(kfree) = NULL;
__u32 attrs = cinode->cifsAttrs;
struct cifs_open_parms oparms;
- static atomic_t sillycounter;
struct cifsFileInfo *cfile;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
struct kvec iov[2];
- const char *ppath;
- void *page;
- size_t len;
int rc;
tlink = cifs_sb_tlink(cifs_sb);
@@ -1594,25 +1505,14 @@ int smb2_rename_pending_delete(const char *full_path,
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
- page = alloc_dentry_path();
-
- ppath = build_path_from_dentry(dentry->d_parent, page);
- if (IS_ERR(ppath)) {
- rc = PTR_ERR(ppath);
+ to_name = cifs_silly_fullpath(dentry);
+ if (IS_ERR(to_name)) {
+ rc = PTR_ERR(to_name);
+ to_name = NULL;
goto out;
}
- len = strlen(ppath) + strlen("/.__smb1234") + 1;
- to_name = kmalloc(len, GFP_KERNEL);
- if (!to_name) {
- rc = -ENOMEM;
- goto out;
- }
-
- scnprintf(to_name, len, "%s%c.__smb%04X", ppath, CIFS_DIR_SEP(cifs_sb),
- atomic_inc_return(&sillycounter) & 0xffff);
-
- utf16_path = utf16_smb2_path(cifs_sb, to_name, len);
+ utf16_path = utf16_smb2_path(cifs_sb, to_name, strlen(to_name));
if (!utf16_path) {
rc = -ENOMEM;
goto out;
@@ -1635,12 +1535,14 @@ int smb2_rename_pending_delete(const char *full_path,
iov[1].iov_base = utf16_path;
iov[1].iov_len = sizeof(*utf16_path) * UniStrlen((wchar_t *)utf16_path);
- cifs_get_writable_path(tcon, full_path, FIND_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, full_path, d_inode(dentry),
+ FIND_WITH_DELETE, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, iov,
cmds, num_cmds, cfile, NULL, NULL, dentry);
if (rc == -EINVAL) {
cifs_dbg(FYI, "invalid lease key, resending request without lease\n");
- cifs_get_writable_path(tcon, full_path, FIND_WITH_DELETE, &cfile);
+ cifs_get_writable_path(tcon, full_path, d_inode(dentry),
+ FIND_WITH_DELETE, &cfile);
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, &oparms, iov,
cmds, num_cmds, cfile, NULL, NULL, NULL);
}
@@ -1653,6 +1555,5 @@ int smb2_rename_pending_delete(const char *full_path,
}
out:
cifs_put_tlink(tlink);
- free_dentry_path(page);
return rc;
}
--
2.53.0
next prev parent reply other threads:[~2026-04-05 21:18 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-05 21:18 [PATCH 1/2] vfs: introduce d_mark_tmpfile_name() Paulo Alcantara
2026-04-05 21:18 ` Paulo Alcantara [this message]
2026-04-05 23:32 ` [PATCH 2/2] smb: client: add support for O_TMPFILE Al Viro
2026-04-05 23:53 ` NeilBrown
2026-04-07 1:28 ` Paulo Alcantara
2026-04-08 6:57 ` Al Viro
2026-04-08 13:48 ` Paulo Alcantara
2026-04-06 3:23 ` [PATCH 1/2] vfs: introduce d_mark_tmpfile_name() Matthew Wilcox
2026-04-07 1:29 ` Paulo Alcantara
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260405211819.1251369-2-pc@manguebit.org \
--to=pc@manguebit.org \
--cc=dhowells@redhat.com \
--cc=linux-cifs@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=smfrench@gmail.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.