aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/smb2ops.c64
-rw-r--r--fs/cifs/smb2pdu.h14
2 files changed, 73 insertions, 5 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 3fdc6a41b304..9fd56b0acd7e 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2372,6 +2372,41 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
2372 kfree(dfs_rsp); 2372 kfree(dfs_rsp);
2373 return rc; 2373 return rc;
2374} 2374}
2375
2376static int
2377parse_reparse_symlink(struct reparse_symlink_data_buffer *symlink_buf,
2378 u32 plen, char **target_path,
2379 struct cifs_sb_info *cifs_sb)
2380{
2381 unsigned int sub_len;
2382 unsigned int sub_offset;
2383
2384 /* We only handle Symbolic Link : MS-FSCC 2.1.2.4 */
2385 if (le32_to_cpu(symlink_buf->ReparseTag) != IO_REPARSE_TAG_SYMLINK) {
2386 cifs_dbg(VFS, "srv returned invalid symlink buffer\n");
2387 return -EIO;
2388 }
2389
2390 sub_offset = le16_to_cpu(symlink_buf->SubstituteNameOffset);
2391 sub_len = le16_to_cpu(symlink_buf->SubstituteNameLength);
2392 if (sub_offset + 20 > plen ||
2393 sub_offset + sub_len + 20 > plen) {
2394 cifs_dbg(VFS, "srv returned malformed symlink buffer\n");
2395 return -EIO;
2396 }
2397
2398 *target_path = cifs_strndup_from_utf16(
2399 symlink_buf->PathBuffer + sub_offset,
2400 sub_len, true, cifs_sb->local_nls);
2401 if (!(*target_path))
2402 return -ENOMEM;
2403
2404 convert_delimiter(*target_path, '/');
2405 cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
2406
2407 return 0;
2408}
2409
2375#define SMB2_SYMLINK_STRUCT_SIZE \ 2410#define SMB2_SYMLINK_STRUCT_SIZE \
2376 (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) 2411 (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
2377 2412
@@ -2401,11 +2436,13 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
2401 struct kvec close_iov[1]; 2436 struct kvec close_iov[1];
2402 struct smb2_create_rsp *create_rsp; 2437 struct smb2_create_rsp *create_rsp;
2403 struct smb2_ioctl_rsp *ioctl_rsp; 2438 struct smb2_ioctl_rsp *ioctl_rsp;
2404 char *ioctl_buf; 2439 struct reparse_data_buffer *reparse_buf;
2405 u32 plen; 2440 u32 plen;
2406 2441
2407 cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); 2442 cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
2408 2443
2444 *target_path = NULL;
2445
2409 if (smb3_encryption_required(tcon)) 2446 if (smb3_encryption_required(tcon))
2410 flags |= CIFS_TRANSFORM_REQ; 2447 flags |= CIFS_TRANSFORM_REQ;
2411 2448
@@ -2483,17 +2520,36 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
2483 if ((rc == 0) && (is_reparse_point)) { 2520 if ((rc == 0) && (is_reparse_point)) {
2484 /* See MS-FSCC 2.3.23 */ 2521 /* See MS-FSCC 2.3.23 */
2485 2522
2486 ioctl_buf = (char *)ioctl_rsp + le32_to_cpu(ioctl_rsp->OutputOffset); 2523 reparse_buf = (struct reparse_data_buffer *)
2524 ((char *)ioctl_rsp +
2525 le32_to_cpu(ioctl_rsp->OutputOffset));
2487 plen = le32_to_cpu(ioctl_rsp->OutputCount); 2526 plen = le32_to_cpu(ioctl_rsp->OutputCount);
2488 2527
2489 if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) > 2528 if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
2490 rsp_iov[1].iov_len) { 2529 rsp_iov[1].iov_len) {
2491 cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", plen); 2530 cifs_dbg(VFS, "srv returned invalid ioctl len: %d\n",
2531 plen);
2532 rc = -EIO;
2533 goto querty_exit;
2534 }
2535
2536 if (plen < 8) {
2537 cifs_dbg(VFS, "reparse buffer is too small. Must be "
2538 "at least 8 bytes but was %d\n", plen);
2539 rc = -EIO;
2540 goto querty_exit;
2541 }
2542
2543 if (plen < le16_to_cpu(reparse_buf->ReparseDataLength) + 8) {
2544 cifs_dbg(VFS, "srv returned invalid reparse buf "
2545 "length: %d\n", plen);
2492 rc = -EIO; 2546 rc = -EIO;
2493 goto querty_exit; 2547 goto querty_exit;
2494 } 2548 }
2495 2549
2496 /* Do stuff with ioctl_buf/plen */ 2550 rc = parse_reparse_symlink(
2551 (struct reparse_symlink_data_buffer *)reparse_buf,
2552 plen, target_path, cifs_sb);
2497 goto querty_exit; 2553 goto querty_exit;
2498 } 2554 }
2499 2555
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index c7d5813bebd8..858353d20c39 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -914,7 +914,19 @@ struct reparse_mount_point_data_buffer {
914 __u8 PathBuffer[0]; /* Variable Length */ 914 __u8 PathBuffer[0]; /* Variable Length */
915} __packed; 915} __packed;
916 916
917/* See MS-FSCC 2.1.2.4 and cifspdu.h for struct reparse_symlink_data */ 917#define SYMLINK_FLAG_RELATIVE 0x00000001
918
919struct reparse_symlink_data_buffer {
920 __le32 ReparseTag;
921 __le16 ReparseDataLength;
922 __u16 Reserved;
923 __le16 SubstituteNameOffset;
924 __le16 SubstituteNameLength;
925 __le16 PrintNameOffset;
926 __le16 PrintNameLength;
927 __le32 Flags;
928 __u8 PathBuffer[0]; /* Variable Length */
929} __packed;
918 930
919/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */ 931/* See MS-FSCC 2.1.2.6 and cifspdu.h for struct reparse_posix_data */
920 932