Skip to content

Commit a49da4e

Browse files
paliSteve French
authored andcommitted
cifs: Fix parsing native symlinks directory/file type
As SMB protocol distinguish between symlink to directory and symlink to file, add some mechanism to disallow resolving incompatible types. When SMB symlink is of the directory type, ensure that its target path ends with slash. This forces Linux to not allow resolving such symlink to file. And when SMB symlink is of the file type and its target path ends with slash then returns an error as such symlink is unresolvable. Such symlink always points to invalid location as file cannot end with slash. As POSIX server does not distinguish between symlinks to file and symlink directory, do not apply this change for symlinks from POSIX SMB server. For POSIX SMB servers, this change does nothing. This mimics Windows behavior of native SMB symlinks. Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 2008d8c commit a49da4e

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

fs/smb/client/inode.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,11 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
12161216
full_path,
12171217
iov, data);
12181218
}
1219+
1220+
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
1221+
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
1222+
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
1223+
}
12191224
break;
12201225
}
12211226

fs/smb/client/smb2file.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,52 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
6363
return sym;
6464
}
6565

66+
int smb2_fix_symlink_target_type(char **target, bool directory, struct cifs_sb_info *cifs_sb)
67+
{
68+
char *buf;
69+
int len;
70+
71+
/*
72+
* POSIX server does not distinguish between symlinks to file and
73+
* symlink directory. So nothing is needed to fix on the client side.
74+
*/
75+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
76+
return 0;
77+
78+
if (!*target)
79+
return -EIO;
80+
81+
len = strlen(*target);
82+
if (!len)
83+
return -EIO;
84+
85+
/*
86+
* If this is directory symlink and it does not have trailing slash then
87+
* append it. Trailing slash simulates Windows/SMB behavior which do not
88+
* allow resolving directory symlink to file.
89+
*/
90+
if (directory && (*target)[len-1] != '/') {
91+
buf = krealloc(*target, len+2, GFP_KERNEL);
92+
if (!buf)
93+
return -ENOMEM;
94+
buf[len] = '/';
95+
buf[len+1] = '\0';
96+
*target = buf;
97+
len++;
98+
}
99+
100+
/*
101+
* If this is a file (non-directory) symlink and it points to path name
102+
* with trailing slash then this is an invalid symlink because file name
103+
* cannot contain slash character. File name with slash is invalid on
104+
* both Windows and Linux systems. So return an error for such symlink.
105+
*/
106+
if (!directory && (*target)[len-1] == '/')
107+
return -EIO;
108+
109+
return 0;
110+
}
111+
66112
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov,
67113
const char *full_path, char **path)
68114
{
@@ -132,6 +178,11 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
132178
NULL, NULL, NULL);
133179
oparms->create_options &= ~OPEN_REPARSE_POINT;
134180
}
181+
if (!rc) {
182+
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
183+
rc = smb2_fix_symlink_target_type(&data->symlink_target,
184+
directory, oparms->cifs_sb);
185+
}
135186
}
136187
}
137188

fs/smb/client/smb2inode.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,11 @@ int smb2_query_path_info(const unsigned int xid,
10101010
else
10111011
rc = -EOPNOTSUPP;
10121012
}
1013+
1014+
if (data->reparse.tag == IO_REPARSE_TAG_SYMLINK && !rc) {
1015+
bool directory = le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY;
1016+
rc = smb2_fix_symlink_target_type(&data->symlink_target, directory, cifs_sb);
1017+
}
10131018
break;
10141019
case -EREMOTE:
10151020
break;

fs/smb/client/smb2proto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
111111
struct cifs_sb_info *cifs_sb,
112112
const unsigned char *path, char *pbuf,
113113
unsigned int *pbytes_read);
114+
int smb2_fix_symlink_target_type(char **target, bool directory, struct cifs_sb_info *cifs_sb);
114115
int smb2_parse_native_symlink(char **target, const char *buf, unsigned int len,
115116
bool relative,
116117
const char *full_path,

0 commit comments

Comments
 (0)