From: Paulo Alcantara Date: Tue, 7 Apr 2026 22:51:35 +0000 (-0300) Subject: smb: client: set ATTR_TEMPORARY with O_TMPFILE | O_EXCL X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=62e02084ab93c31c53dd38f149782ce8349a2d90;p=thirdparty%2Fkernel%2Flinux.git smb: client: set ATTR_TEMPORARY with O_TMPFILE | O_EXCL Set ATTR_TEMPORARY attribute on temporary delete-on-close files when O_EXCL is specified in conjunction with O_TMPFILE to let some servers cache as much data as possible and possibly never persist them into storage, thereby improving performance. Signed-off-by: Paulo Alcantara (Red Hat) Cc: David Howells Cc: linux-cifs@vger.kernel.org Signed-off-by: Steve French --- diff --git a/fs/smb/client/dir.c b/fs/smb/client/dir.c index 67f9f956c211e..bfc887d506b61 100644 --- a/fs/smb/client/dir.c +++ b/fs/smb/client/dir.c @@ -1014,28 +1014,21 @@ 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) +static int set_tmpfile_attr(const unsigned int xid, unsigned int oflags, + struct inode *inode, const char *full_path, + struct TCP_Server_Info *server) { - 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; + struct cifsInodeInfo *cinode = CIFS_I(inode); + FILE_BASIC_INFO fi; - 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; + cinode->cifsAttrs |= ATTR_HIDDEN; + if (oflags & O_EXCL) + cinode->cifsAttrs |= ATTR_TEMPORARY; + + fi = (FILE_BASIC_INFO) { + .Attributes = cpu_to_le32(cinode->cifsAttrs), + }; + return server->ops->set_file_info(inode, full_path, &fi, xid); } /* @@ -1049,6 +1042,8 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, { struct dentry *dentry = file->f_path.dentry; struct cifs_sb_info *cifs_sb = CIFS_SB(dir); + char *path __free(kfree) = NULL, *name; + unsigned int oflags = file->f_flags; size_t size = CIFS_TMPNAME_LEN + 1; int retries = 0, max_retries = 16; struct TCP_Server_Info *server; @@ -1059,7 +1054,6 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct cifs_tcon *tcon; unsigned int sbflags; struct inode *inode; - char *path, *name; unsigned int xid; __u32 oplock; int rc; @@ -1089,6 +1083,7 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, if (IS_ERR(path)) { cifs_del_pending_open(&open); rc = PTR_ERR(path); + path = NULL; goto out; } @@ -1098,9 +1093,8 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, 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); + rc = __cifs_do_create(dir, dentry, path, xid, tlink, oflags, + mode, &oplock, &fid, NULL, &inode); if (!rc) { set_nlink(inode, 0); mark_inode_dirty(inode); @@ -1110,7 +1104,6 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, } } while (unlikely(rc == -EEXIST) && ++retries < max_retries); - kfree(path); if (rc) { cifs_del_pending_open(&open); goto out; @@ -1134,7 +1127,7 @@ int cifs_tmpfile(struct mnt_idmap *idmap, struct inode *dir, goto err_open; } - rc = set_hidden_attr(xid, server, file); + rc = set_tmpfile_attr(xid, oflags, inode, path, server); if (rc) goto out; diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index 26d8b25f42f2d..c6dd282fc3a90 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -1237,6 +1237,20 @@ int smb2_rename_path(const unsigned int xid, return rc; } +static int clear_tmpfile_attr(const unsigned int xid, struct cifs_tcon *tcon, + struct inode *inode, const char *full_path) +{ + struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); + struct cifsInodeInfo *cinode = CIFS_I(inode); + FILE_BASIC_INFO fi; + + cinode->cifsAttrs &= ~(ATTR_TEMPORARY | ATTR_HIDDEN); + fi = (FILE_BASIC_INFO) { + .Attributes = cpu_to_le32(cinode->cifsAttrs), + }; + return server->ops->set_file_info(inode, full_path, &fi, xid); +} + int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, struct dentry *source_dentry, @@ -1246,24 +1260,14 @@ int smb2_create_hardlink(const unsigned int xid, struct inode *inode = source_dentry ? d_inode(source_dentry) : NULL; __u32 co = file_create_options(source_dentry); struct cifsFileInfo *cfile; + int rc; - 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 (inode && test_bit(CIFS_INO_TMPFILE, &CIFS_I(inode)->flags)) { + rc = clear_tmpfile_attr(xid, tcon, inode, from_name); 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,