four SMB3 client fixes, also for stable

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmdUyQMACgkQiiy9cAdy
 T1FAXAv+KNV1F2qdANnZc9sj9xSgL0uXYvUqamVeEJFmFQMb0I6C/AzBGdcxtWak
 gXhCrFmlhlvQSan5Y3RGNj3ggr4UmW/+tEOEANOaZVn52T0IFrYf+wIX1jdetGLx
 LL1mKodca0MfGJXQYIwqsIcAjpzCPK5Qogro9Ve8Btg+iTve350LRxDQGyCMB8uL
 8kFse7eVHnPchmXcMn1O3CVTu/Iu/ZyZq4gkuUDAhP/xNM3ShL036682J3WB2FHI
 VVEjIQYyIeUbxQH9IuGgRuL0NWxjdim02o5DST04klpJwYUuf5ogmNkALYfBSFrg
 aKXqpUZ1ds2LEnR9++azUwnoft8fSRB+TNvLRhN1K5PqEA4nB+XJn/ZZ9OTGn92y
 PuFxXk0tDxbrCuPpQYD0eFeBE2wKWCWiwtGGU6zfqRwqCVmnRdZg3bAtvGOfd9oR
 PUPZtwlsCpl7a3m/cPhmkAe0WNnb9HU03lMIUVfTgGNa8sycIuNGrk7MNNI3u3FJ
 2/jHA71o
 =71WB
 -----END PGP SIGNATURE-----

Merge tag '6.13-rc1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - DFS fix (for race with tree disconnect and dfs cache worker)

 - Four fixes for SMB3.1.1 posix extensions:
      - improve special file support e.g. to Samba, retrieving the file
        type earlier
      - reduce roundtrips (e.g. on ls -l, in some cases)

* tag '6.13-rc1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: fix potential race in cifs_put_tcon()
  smb3.1.1: fix posix mounts to older servers
  fs/smb/client: cifs_prime_dcache() for SMB3 POSIX reparse points
  fs/smb/client: Implement new SMB3 POSIX type
  fs/smb/client: avoid querying SMB2_OP_QUERY_WSL_EA for SMB3 POSIX
This commit is contained in:
Linus Torvalds 2024-12-07 17:27:25 -08:00
commit 62b5a46999
6 changed files with 178 additions and 68 deletions

View File

@ -669,6 +669,7 @@ int __cifs_sfu_make_node(unsigned int xid, struct inode *inode,
int cifs_sfu_make_node(unsigned int xid, struct inode *inode, int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon, struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev); const char *full_path, umode_t mode, dev_t dev);
umode_t wire_mode_to_posix(u32 wire, bool is_dir);
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,

View File

@ -2532,9 +2532,6 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
list_del_init(&tcon->tcon_list); list_del_init(&tcon->tcon_list);
tcon->status = TID_EXITING; tcon->status = TID_EXITING;
#ifdef CONFIG_CIFS_DFS_UPCALL
list_replace_init(&tcon->dfs_ses_list, &ses_list);
#endif
spin_unlock(&tcon->tc_lock); spin_unlock(&tcon->tc_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
@ -2542,6 +2539,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
cancel_delayed_work_sync(&tcon->query_interfaces); cancel_delayed_work_sync(&tcon->query_interfaces);
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
cancel_delayed_work_sync(&tcon->dfs_cache_work); cancel_delayed_work_sync(&tcon->dfs_cache_work);
list_replace_init(&tcon->dfs_ses_list, &ses_list);
#endif #endif
if (tcon->use_witness) { if (tcon->use_witness) {

View File

@ -746,6 +746,88 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
#endif #endif
} }
#define POSIX_TYPE_FILE 0
#define POSIX_TYPE_DIR 1
#define POSIX_TYPE_SYMLINK 2
#define POSIX_TYPE_CHARDEV 3
#define POSIX_TYPE_BLKDEV 4
#define POSIX_TYPE_FIFO 5
#define POSIX_TYPE_SOCKET 6
#define POSIX_X_OTH 0000001
#define POSIX_W_OTH 0000002
#define POSIX_R_OTH 0000004
#define POSIX_X_GRP 0000010
#define POSIX_W_GRP 0000020
#define POSIX_R_GRP 0000040
#define POSIX_X_USR 0000100
#define POSIX_W_USR 0000200
#define POSIX_R_USR 0000400
#define POSIX_STICKY 0001000
#define POSIX_SET_GID 0002000
#define POSIX_SET_UID 0004000
#define POSIX_OTH_MASK 0000007
#define POSIX_GRP_MASK 0000070
#define POSIX_USR_MASK 0000700
#define POSIX_PERM_MASK 0000777
#define POSIX_FILETYPE_MASK 0070000
#define POSIX_FILETYPE_SHIFT 12
static u32 wire_perms_to_posix(u32 wire)
{
u32 mode = 0;
mode |= (wire & POSIX_X_OTH) ? S_IXOTH : 0;
mode |= (wire & POSIX_W_OTH) ? S_IWOTH : 0;
mode |= (wire & POSIX_R_OTH) ? S_IROTH : 0;
mode |= (wire & POSIX_X_GRP) ? S_IXGRP : 0;
mode |= (wire & POSIX_W_GRP) ? S_IWGRP : 0;
mode |= (wire & POSIX_R_GRP) ? S_IRGRP : 0;
mode |= (wire & POSIX_X_USR) ? S_IXUSR : 0;
mode |= (wire & POSIX_W_USR) ? S_IWUSR : 0;
mode |= (wire & POSIX_R_USR) ? S_IRUSR : 0;
mode |= (wire & POSIX_STICKY) ? S_ISVTX : 0;
mode |= (wire & POSIX_SET_GID) ? S_ISGID : 0;
mode |= (wire & POSIX_SET_UID) ? S_ISUID : 0;
return mode;
}
static u32 posix_filetypes[] = {
S_IFREG,
S_IFDIR,
S_IFLNK,
S_IFCHR,
S_IFBLK,
S_IFIFO,
S_IFSOCK
};
static u32 wire_filetype_to_posix(u32 wire_type)
{
if (wire_type >= ARRAY_SIZE(posix_filetypes)) {
pr_warn("Unexpected type %u", wire_type);
return 0;
}
return posix_filetypes[wire_type];
}
umode_t wire_mode_to_posix(u32 wire, bool is_dir)
{
u32 wire_type;
u32 mode;
wire_type = (wire & POSIX_FILETYPE_MASK) >> POSIX_FILETYPE_SHIFT;
/* older servers do not set POSIX file type in the mode field in the response */
if ((wire_type == 0) && is_dir)
mode = wire_perms_to_posix(wire) | S_IFDIR;
else
mode = (wire_perms_to_posix(wire) | wire_filetype_to_posix(wire_type));
return (umode_t)mode;
}
/* Fill a cifs_fattr struct with info from POSIX info struct */ /* Fill a cifs_fattr struct with info from POSIX info struct */
static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_open_info_data *data, struct cifs_open_info_data *data,
@ -782,20 +864,14 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
fattr->cf_bytes = le64_to_cpu(info->AllocationSize); fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
fattr->cf_createtime = le64_to_cpu(info->CreationTime); fattr->cf_createtime = le64_to_cpu(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->HardLinks); fattr->cf_nlink = le32_to_cpu(info->HardLinks);
fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode),
fattr->cf_cifsattrs & ATTR_DIRECTORY);
if (cifs_open_data_reparse(data) && if (cifs_open_data_reparse(data) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, data)) cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
goto out_reparse; goto out_reparse;
fattr->cf_mode &= ~S_IFMT; fattr->cf_dtype = S_DT(fattr->cf_mode);
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode |= S_IFDIR;
fattr->cf_dtype = DT_DIR;
} else { /* file */
fattr->cf_mode |= S_IFREG;
fattr->cf_dtype = DT_REG;
}
out_reparse: out_reparse:
if (S_ISLNK(fattr->cf_mode)) { if (S_ISLNK(fattr->cf_mode)) {

View File

@ -71,6 +71,8 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
struct inode *inode; struct inode *inode;
struct super_block *sb = parent->d_sb; struct super_block *sb = parent->d_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
bool reparse_need_reval = false;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
int rc; int rc;
@ -85,7 +87,21 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
* this spares us an invalidation. * this spares us an invalidation.
*/ */
retry: retry:
if ((fattr->cf_cifsattrs & ATTR_REPARSE) || if (posix) {
switch (fattr->cf_mode & S_IFMT) {
case S_IFLNK:
case S_IFBLK:
case S_IFCHR:
reparse_need_reval = true;
break;
default:
break;
}
} else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
reparse_need_reval = true;
}
if (reparse_need_reval ||
(fattr->cf_flags & CIFS_FATTR_NEED_REVAL)) (fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
return; return;
@ -241,31 +257,29 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
fattr->cf_nlink = le32_to_cpu(info->HardLinks); fattr->cf_nlink = le32_to_cpu(info->HardLinks);
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
/* if (fattr->cf_cifsattrs & ATTR_REPARSE)
* Since we set the inode type below we need to mask off fattr->cf_cifstag = le32_to_cpu(info->ReparseTag);
* to avoid strange results if bits set above.
* XXX: why not make server&client use the type bits? /* The Mode field in the response can now include the file type as well */
*/ fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode),
fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; fattr->cf_cifsattrs & ATTR_DIRECTORY);
fattr->cf_dtype = S_DT(le32_to_cpu(info->Mode));
switch (fattr->cf_mode & S_IFMT) {
case S_IFLNK:
case S_IFBLK:
case S_IFCHR:
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
break;
default:
break;
}
cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n", cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n",
le32_to_cpu(info->DeviceId), le32_to_cpu(info->DeviceId),
le32_to_cpu(info->ReparseTag), le32_to_cpu(info->ReparseTag),
le32_to_cpu(info->Mode)); le32_to_cpu(info->Mode));
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode |= S_IFDIR;
fattr->cf_dtype = DT_DIR;
} else {
/*
* mark anything that is not a dir as regular
* file. special files should have the REPARSE
* attribute and will be marked as needing revaluation
*/
fattr->cf_mode |= S_IFREG;
fattr->cf_dtype = DT_REG;
}
sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
} }

View File

@ -803,26 +803,35 @@ out:
fattr->cf_dtype = S_DT(fattr->cf_mode); fattr->cf_dtype = S_DT(fattr->cf_mode);
} }
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, static bool posix_reparse_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr, struct cifs_fattr *fattr,
struct cifs_open_info_data *data) struct cifs_open_info_data *data)
{ {
struct reparse_posix_data *buf = data->reparse.posix; struct reparse_posix_data *buf = data->reparse.posix;
u32 tag = data->reparse.tag;
if (tag == IO_REPARSE_TAG_NFS && buf) {
if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType)) if (buf == NULL)
return true;
if (le16_to_cpu(buf->ReparseDataLength) < sizeof(buf->InodeType)) {
WARN_ON_ONCE(1);
return false; return false;
}
switch (le64_to_cpu(buf->InodeType)) { switch (le64_to_cpu(buf->InodeType)) {
case NFS_SPECFILE_CHR: case NFS_SPECFILE_CHR:
if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
WARN_ON_ONCE(1);
return false; return false;
}
fattr->cf_mode |= S_IFCHR; fattr->cf_mode |= S_IFCHR;
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer); fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break; break;
case NFS_SPECFILE_BLK: case NFS_SPECFILE_BLK:
if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) if (le16_to_cpu(buf->ReparseDataLength) != sizeof(buf->InodeType) + 8) {
WARN_ON_ONCE(1);
return false; return false;
}
fattr->cf_mode |= S_IFBLK; fattr->cf_mode |= S_IFBLK;
fattr->cf_rdev = reparse_mkdev(buf->DataBuffer); fattr->cf_rdev = reparse_mkdev(buf->DataBuffer);
break; break;
@ -839,9 +848,16 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
return false; return false;
} }
goto out; return true;
} }
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr,
struct cifs_open_info_data *data)
{
u32 tag = data->reparse.tag;
bool ok;
switch (tag) { switch (tag) {
case IO_REPARSE_TAG_INTERNAL: case IO_REPARSE_TAG_INTERNAL:
if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY)) if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY))
@ -860,15 +876,19 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
case IO_REPARSE_TAG_LX_BLK: case IO_REPARSE_TAG_LX_BLK:
wsl_to_fattr(data, cifs_sb, tag, fattr); wsl_to_fattr(data, cifs_sb, tag, fattr);
break; break;
case IO_REPARSE_TAG_NFS:
ok = posix_reparse_to_fattr(cifs_sb, fattr, data);
if (!ok)
return false;
break;
case 0: /* SMB1 symlink */ case 0: /* SMB1 symlink */
case IO_REPARSE_TAG_SYMLINK: case IO_REPARSE_TAG_SYMLINK:
case IO_REPARSE_TAG_NFS:
fattr->cf_mode |= S_IFLNK; fattr->cf_mode |= S_IFLNK;
break; break;
default: default:
return false; return false;
} }
out:
fattr->cf_dtype = S_DT(fattr->cf_mode); fattr->cf_dtype = S_DT(fattr->cf_mode);
return true; return true;
} }

View File

@ -943,6 +943,7 @@ int smb2_query_path_info(const unsigned int xid,
if (rc || !data->reparse_point) if (rc || !data->reparse_point)
goto out; goto out;
if (!tcon->posix_extensions)
cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA; cmds[num_cmds++] = SMB2_OP_QUERY_WSL_EA;
/* /*
* Skip SMB2_OP_GET_REPARSE if symlink already parsed in create * Skip SMB2_OP_GET_REPARSE if symlink already parsed in create