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) <pc@manguebit.org>
Cc: David Howells <dhowells@redhat.com>
Cc: linux-cifs@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Paulo Alcantara 2026-04-07 19:51:35 -03:00 committed by Steve French
parent 3e7d63037a
commit 62e02084ab
2 changed files with 36 additions and 39 deletions

View File

@ -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;

View File

@ -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,