]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb: client: add support for O_TMPFILE
authorPaulo Alcantara <pc@manguebit.org>
Tue, 7 Apr 2026 19:58:10 +0000 (16:58 -0300)
committerSteve French <stfrench@microsoft.com>
Fri, 10 Apr 2026 16:25:35 +0000 (11:25 -0500)
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
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsfs.c
fs/smb/client/cifsfs.h
fs/smb/client/cifsglob.h
fs/smb/client/cifsproto.h
fs/smb/client/dir.c
fs/smb/client/file.c
fs/smb/client/inode.c
fs/smb/client/link.c
fs/smb/client/smb2inode.c

index 4631cd6dfeab31a9a2bf17c384a97109a9970a1a..2025739f070ac17c7bdbbbf9f465f72ed897861b 100644 (file)
@@ -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,
index e320d39b01f5eca8033cc516f6f1a2f07276676c..64c7a4c6ac834d6659ab2a8a9abb59f4721b73b7 100644 (file)
@@ -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 */
index 709e96e077916da435e40105042a1a4faf33f2f7..ccfde157d3befa2e0ec0d6dfdd598a1126164efe 100644 (file)
@@ -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;
 }
 
index 884bfa1cf0b42349107155c93ad94dc784444766..c24c50d732e64858a8b628f09bb2392acdec8f43 100644 (file)
@@ -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,
index 6d2378eeb7f6818c60e15203638cdb0a1a246d30..67f9f956c211ee9903840878e7f82b4752e7dbab 100644 (file)
@@ -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;
 
-/* Inode operations in similar order to how they appear in Linux file fs.h */
+       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;
+}
 
-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)
+/* Inode operations in similar order to how they appear in Linux file fs.h */
+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 @@ retry_open:
                        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 @@ retry_open:
 
                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 @@ retry_open:
 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 @@ cifs_create_get_file_info:
                                        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 @@ cifs_create_set_dentry:
                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,9 +471,41 @@ out_err:
                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;
+}
+
+
+/*
+ * Look up, create and open a CIFS file.
+ *
+ * The initial dentry state is in-lookup or hashed-negative.  On success, dentry
+ * will become hashed-positive by calling d_drop() & d_add(), respectively.
+ */
 int
 cifs_atomic_open(struct inode *inode, struct dentry *direntry,
                 struct file *file, unsigned int oflags, umode_t mode)
@@ -569,6 +618,12 @@ out_free_xid:
        return rc;
 }
 
+/*
+ * Create a CIFS file.
+ *
+ * The initial dentry state is hashed-negative.  On success, dentry will become
+ * hashed-positive by calling d_drop() & d_add(), respectively.
+ */
 int cifs_create(struct mnt_idmap *idmap, struct inode *inode,
                struct dentry *direntry, umode_t mode, bool excl)
 {
@@ -959,6 +1014,172 @@ 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;
+}
+
+/*
+ * Create a hidden temporary CIFS file with delete-on-close bit set.
+ *
+ * The initial dentry state is unhashed-negative.  On success, dentry will
+ * become unhashed-positive by calling d_drop() & d_instantiate(), respectively.
+ */
+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,
index a69e05f86d7e2f9b0c00f44c017ffde1b7b1ef0d..5d5b49468aff9bd5718a5b36a589c5dc64f0bb3f 100644 (file)
@@ -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
@@ -2598,8 +2607,13 @@ int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode,
                return rc;
        }
 
+       if (test_bit(CIFS_INO_TMPFILE, &cifs_inode->flags))
+               find_flags = FIND_ANY;
+
        cifs_sb = CIFS_SB(cifs_inode);
 
+       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;
@@ -2683,16 +2697,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;
index 888f9e35f14b8add0809d40ad0918f37c0f3d983..24040909d1844b2e3c08f5ad748d4e0996a95b22 100644 (file)
@@ -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;
index 434e8fe74080bd8d0928bb7908d3ef4643e11095..dd127917a34053e2ce86a443e842c65e37d6289c 100644 (file)
@@ -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);
index fe1c9d7765806d1d30f8fe87192028b10101eff3..26d8b25f42f2d2dc4312275c1f72520fbe7be2ed 100644 (file)
@@ -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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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 @@ replay_again:
                        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,32 @@ 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;
+               __le32 attrs;
+               int rc;
 
+               if (!test_bit(CIFS_INO_TMPFILE, &CIFS_I(inode)->flags))
+                       goto out;
+
+               attrs = cpu_to_le32(cinode->cifsAttrs & ~ATTR_HIDDEN);
+               fi = (FILE_BASIC_INFO){ .Attributes = attrs, };
+               rc = smb2_set_file_info(inode, from_name, &fi, xid);
+               if (rc)
+                       return rc;
+       }
+
+out:
+       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 +1277,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 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;
-       __le64 eof = cpu_to_le64(size);
        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 +1296,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 +1327,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 +1387,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 +1396,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 +1478,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 +1491,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 +1502,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);
-               goto out;
-       }
-
-       len = strlen(ppath) + strlen("/.__smb1234") + 1;
-       to_name = kmalloc(len, GFP_KERNEL);
-       if (!to_name) {
-               rc = -ENOMEM;
+       to_name = cifs_silly_fullpath(dentry);
+       if (IS_ERR(to_name)) {
+               rc = PTR_ERR(to_name);
+               to_name = NULL;
                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 +1532,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 +1552,5 @@ int smb2_rename_pending_delete(const char *full_path,
        }
 out:
        cifs_put_tlink(tlink);
-       free_dentry_path(page);
        return rc;
 }