Commit ebaf546a5584d0a75aa61e4872771008715b8639

Authored by Ronnie Sahlberg
Committed by Steve French
1 parent 6a54b2e002

SMB3: Clean up query symlink when reparse point

Two of the common symlink formats use reparse points
(unlike mfsymlinks and also unlike the SMB1 posix
extensions).  This is the first part of the fixes
to allow these reparse points (NFS style and Windows
symlinks) to be resolved properly as symlinks by the
client.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

Showing 4 changed files with 118 additions and 17 deletions Side-by-side Diff

... ... @@ -355,7 +355,8 @@
355 355 struct cifs_sb_info *);
356 356 /* query symlink target */
357 357 int (*query_symlink)(const unsigned int, struct cifs_tcon *,
358   - const char *, char **, struct cifs_sb_info *);
  358 + struct cifs_sb_info *, const char *,
  359 + char **, bool);
359 360 /* open a file for non-posix mounts */
360 361 int (*open)(const unsigned int, struct cifs_open_parms *,
361 362 __u32 *, FILE_ALL_INFO *);
... ... @@ -648,9 +648,16 @@
648 648 rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
649 649 &target_path);
650 650  
651   - if (rc != 0 && server->ops->query_symlink)
652   - rc = server->ops->query_symlink(xid, tcon, full_path,
653   - &target_path, cifs_sb);
  651 + if (rc != 0 && server->ops->query_symlink) {
  652 + struct cifsInodeInfo *cifsi = CIFS_I(inode);
  653 + bool reparse_point = false;
  654 +
  655 + if (cifsi->cifsAttrs & ATTR_REPARSE)
  656 + reparse_point = true;
  657 +
  658 + rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
  659 + &target_path, reparse_point);
  660 + }
654 661  
655 662 kfree(full_path);
656 663 free_xid(xid);
... ... @@ -950,8 +950,8 @@
950 950  
951 951 static int
952 952 cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
953   - const char *full_path, char **target_path,
954   - struct cifs_sb_info *cifs_sb)
  953 + struct cifs_sb_info *cifs_sb, const char *full_path,
  954 + char **target_path, bool is_reparse_point)
955 955 {
956 956 int rc;
957 957 int oplock = 0;
... ... @@ -959,6 +959,11 @@
959 959 struct cifs_open_parms oparms;
960 960  
961 961 cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
  962 +
  963 + if (is_reparse_point) {
  964 + cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n");
  965 + return -EOPNOTSUPP;
  966 + }
962 967  
963 968 /* Check for unix extensions */
964 969 if (cap_unix(tcon->ses)) {
... ... @@ -2390,46 +2390,129 @@
2390 2390  
2391 2391 static int
2392 2392 smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
2393   - const char *full_path, char **target_path,
2394   - struct cifs_sb_info *cifs_sb)
  2393 + struct cifs_sb_info *cifs_sb, const char *full_path,
  2394 + char **target_path, bool is_reparse_point)
2395 2395 {
2396 2396 int rc;
2397   - __le16 *utf16_path;
  2397 + __le16 *utf16_path = NULL;
2398 2398 __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
2399 2399 struct cifs_open_parms oparms;
2400 2400 struct cifs_fid fid;
2401 2401 struct kvec err_iov = {NULL, 0};
2402 2402 struct smb2_err_rsp *err_buf = NULL;
2403   - int resp_buftype;
2404 2403 struct smb2_symlink_err_rsp *symlink;
2405 2404 unsigned int sub_len;
2406 2405 unsigned int sub_offset;
2407 2406 unsigned int print_len;
2408 2407 unsigned int print_offset;
  2408 + int flags = 0;
  2409 + struct smb_rqst rqst[3];
  2410 + int resp_buftype[3];
  2411 + struct kvec rsp_iov[3];
  2412 + struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
  2413 + struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
  2414 + struct kvec close_iov[1];
  2415 + struct smb2_create_rsp *create_rsp;
  2416 + struct smb2_ioctl_rsp *ioctl_rsp;
  2417 + char *ioctl_buf;
  2418 + u32 plen;
2409 2419  
2410 2420 cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
2411 2421  
  2422 + if (smb3_encryption_required(tcon))
  2423 + flags |= CIFS_TRANSFORM_REQ;
  2424 +
  2425 + memset(rqst, 0, sizeof(rqst));
  2426 + resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
  2427 + memset(rsp_iov, 0, sizeof(rsp_iov));
  2428 +
2412 2429 utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
2413 2430 if (!utf16_path)
2414 2431 return -ENOMEM;
2415 2432  
  2433 + /* Open */
  2434 + memset(&open_iov, 0, sizeof(open_iov));
  2435 + rqst[0].rq_iov = open_iov;
  2436 + rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
  2437 +
  2438 + memset(&oparms, 0, sizeof(oparms));
2416 2439 oparms.tcon = tcon;
2417 2440 oparms.desired_access = FILE_READ_ATTRIBUTES;
2418 2441 oparms.disposition = FILE_OPEN;
  2442 +
2419 2443 if (backup_cred(cifs_sb))
2420 2444 oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
2421 2445 else
2422 2446 oparms.create_options = 0;
  2447 + if (is_reparse_point)
  2448 + oparms.create_options = OPEN_REPARSE_POINT;
  2449 +
2423 2450 oparms.fid = &fid;
2424 2451 oparms.reconnect = false;
2425 2452  
2426   - rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
2427   - &resp_buftype);
2428   - if (!rc)
2429   - SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
  2453 + rc = SMB2_open_init(tcon, &rqst[0], &oplock, &oparms, utf16_path);
  2454 + if (rc)
  2455 + goto querty_exit;
  2456 + smb2_set_next_command(tcon, &rqst[0]);
  2457 +
  2458 +
  2459 + /* IOCTL */
  2460 + memset(&io_iov, 0, sizeof(io_iov));
  2461 + rqst[1].rq_iov = io_iov;
  2462 + rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
  2463 +
  2464 + rc = SMB2_ioctl_init(tcon, &rqst[1], fid.persistent_fid,
  2465 + fid.volatile_fid, FSCTL_GET_REPARSE_POINT,
  2466 + true /* is_fctl */, NULL, 0, CIFSMaxBufSize);
  2467 + if (rc)
  2468 + goto querty_exit;
  2469 +
  2470 + smb2_set_next_command(tcon, &rqst[1]);
  2471 + smb2_set_related(&rqst[1]);
  2472 +
  2473 +
  2474 + /* Close */
  2475 + memset(&close_iov, 0, sizeof(close_iov));
  2476 + rqst[2].rq_iov = close_iov;
  2477 + rqst[2].rq_nvec = 1;
  2478 +
  2479 + rc = SMB2_close_init(tcon, &rqst[2], COMPOUND_FID, COMPOUND_FID);
  2480 + if (rc)
  2481 + goto querty_exit;
  2482 +
  2483 + smb2_set_related(&rqst[2]);
  2484 +
  2485 + rc = compound_send_recv(xid, tcon->ses, flags, 3, rqst,
  2486 + resp_buftype, rsp_iov);
  2487 +
  2488 + create_rsp = rsp_iov[0].iov_base;
  2489 + if (create_rsp && create_rsp->sync_hdr.Status)
  2490 + err_iov = rsp_iov[0];
  2491 + ioctl_rsp = rsp_iov[1].iov_base;
  2492 +
  2493 + /*
  2494 + * Open was successful and we got an ioctl response.
  2495 + */
  2496 + if ((rc == 0) && (is_reparse_point)) {
  2497 + /* See MS-FSCC 2.3.23 */
  2498 +
  2499 + ioctl_buf = (char *)ioctl_rsp + le32_to_cpu(ioctl_rsp->OutputOffset);
  2500 + plen = le32_to_cpu(ioctl_rsp->OutputCount);
  2501 +
  2502 + if (plen + le32_to_cpu(ioctl_rsp->OutputOffset) >
  2503 + rsp_iov[1].iov_len) {
  2504 + cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", plen);
  2505 + rc = -EIO;
  2506 + goto querty_exit;
  2507 + }
  2508 +
  2509 + /* Do stuff with ioctl_buf/plen */
  2510 + goto querty_exit;
  2511 + }
  2512 +
2430 2513 if (!rc || !err_iov.iov_base) {
2431 2514 rc = -ENOENT;
2432   - goto free_path;
  2515 + goto querty_exit;
2433 2516 }
2434 2517  
2435 2518 err_buf = err_iov.iov_base;
2436 2519  
... ... @@ -2469,9 +2552,14 @@
2469 2552 cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
2470 2553  
2471 2554 querty_exit:
2472   - free_rsp_buf(resp_buftype, err_buf);
2473   - free_path:
  2555 + cifs_dbg(FYI, "query symlink rc %d\n", rc);
2474 2556 kfree(utf16_path);
  2557 + SMB2_open_free(&rqst[0]);
  2558 + SMB2_ioctl_free(&rqst[1]);
  2559 + SMB2_close_free(&rqst[2]);
  2560 + free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
  2561 + free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
  2562 + free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
2475 2563 return rc;
2476 2564 }
2477 2565