Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

smb: client: handle lack of FSCTL_GET_REPARSE_POINT support

As per MS-FSA 2.1.5.10.14, support for FSCTL_GET_REPARSE_POINT is
optional and if the server doesn't support it,
STATUS_INVALID_DEVICE_REQUEST must be returned for the operation.

If we find files with reparse points and we can't read them due to
lack of client or server support, just ignore it and then treat them
as regular files or junctions.

Fixes: 5f71ebc41294 ("smb: client: parse reparse point flag in create response")
Reported-by: Sebastian Steinbeisser <Sebastian.Steinbeisser@lrz.de>
Tested-by: Sebastian Steinbeisser <Sebastian.Steinbeisser@lrz.de>
Acked-by: Tom Talpey <tom@talpey.com>
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paulo Alcantara and committed by
Steve French
4b96024e 8400291e

+38 -4
+15 -2
fs/smb/client/inode.c
··· 1042 1042 } 1043 1043 1044 1044 rc = -EOPNOTSUPP; 1045 - switch ((data->reparse.tag = tag)) { 1046 - case 0: /* SMB1 symlink */ 1045 + data->reparse.tag = tag; 1046 + if (!data->reparse.tag) { 1047 1047 if (server->ops->query_symlink) { 1048 1048 rc = server->ops->query_symlink(xid, tcon, 1049 1049 cifs_sb, full_path, 1050 1050 &data->symlink_target); 1051 + } 1052 + if (rc == -EOPNOTSUPP) 1053 + data->reparse.tag = IO_REPARSE_TAG_INTERNAL; 1054 + } 1055 + 1056 + switch (data->reparse.tag) { 1057 + case 0: /* SMB1 symlink */ 1058 + break; 1059 + case IO_REPARSE_TAG_INTERNAL: 1060 + rc = 0; 1061 + if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) { 1062 + cifs_create_junction_fattr(fattr, sb); 1063 + goto out; 1051 1064 } 1052 1065 break; 1053 1066 case IO_REPARSE_TAG_MOUNT_POINT:
+4
fs/smb/client/reparse.c
··· 505 505 } 506 506 507 507 switch (tag) { 508 + case IO_REPARSE_TAG_INTERNAL: 509 + if (!(fattr->cf_cifsattrs & ATTR_DIRECTORY)) 510 + return false; 511 + fallthrough; 508 512 case IO_REPARSE_TAG_DFS: 509 513 case IO_REPARSE_TAG_DFSR: 510 514 case IO_REPARSE_TAG_MOUNT_POINT:
+17 -2
fs/smb/client/reparse.h
··· 12 12 #include "fs_context.h" 13 13 #include "cifsglob.h" 14 14 15 + /* 16 + * Used only by cifs.ko to ignore reparse points from files when client or 17 + * server doesn't support FSCTL_GET_REPARSE_POINT. 18 + */ 19 + #define IO_REPARSE_TAG_INTERNAL ((__u32)~0U) 20 + 15 21 static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf) 16 22 { 17 23 u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); ··· 84 78 static inline bool reparse_inode_match(struct inode *inode, 85 79 struct cifs_fattr *fattr) 86 80 { 81 + struct cifsInodeInfo *cinode = CIFS_I(inode); 87 82 struct timespec64 ctime = inode_get_ctime(inode); 88 83 89 - return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) && 90 - CIFS_I(inode)->reparse_tag == fattr->cf_cifstag && 84 + /* 85 + * Do not match reparse tags when client or server doesn't support 86 + * FSCTL_GET_REPARSE_POINT. @fattr->cf_cifstag should contain correct 87 + * reparse tag from query dir response but the client won't be able to 88 + * read the reparse point data anyway. This spares us a revalidation. 89 + */ 90 + if (cinode->reparse_tag != IO_REPARSE_TAG_INTERNAL && 91 + cinode->reparse_tag != fattr->cf_cifstag) 92 + return false; 93 + return (cinode->cifsAttrs & ATTR_REPARSE) && 91 94 timespec64_equal(&ctime, &fattr->cf_ctime); 92 95 } 93 96
+2
fs/smb/client/smb2inode.c
··· 930 930 931 931 switch (rc) { 932 932 case 0: 933 + rc = parse_create_response(data, cifs_sb, &out_iov[0]); 934 + break; 933 935 case -EOPNOTSUPP: 934 936 /* 935 937 * BB TODO: When support for special files added to Samba