aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRonnie Sahlberg <lsahlber@redhat.com>2017-08-23 21:24:55 -0400
committerSteve French <smfrench@gmail.com>2017-09-04 15:03:41 -0400
commit95907fea4fd8ccc736e0a428e52159b4d42b9958 (patch)
treee2f69feb6d0f5b2c85e8666502608584c22e14f7
parent81a84ad3cb5711cec79f4dd53a4ce026b092c432 (diff)
cifs: Add support for reading attributes on SMB2+
SMB1 already has support to read attributes. This adds similar support to SMB2+. With this patch, tools such as 'getfattr' will now work with SMB2+ shares. RH-bz: 1110709 Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
-rw-r--r--fs/cifs/smb2ops.c144
-rw-r--r--fs/cifs/smb2pdu.c12
-rw-r--r--fs/cifs/smb2pdu.h10
-rw-r--r--fs/cifs/smb2proto.h3
4 files changed, 169 insertions, 0 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index cfacf2c97e94..78516d3a133c 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -426,6 +426,138 @@ smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
426 return rc; 426 return rc;
427} 427}
428 428
429static ssize_t
430move_smb2_ea_to_cifs(char *dst, size_t dst_size,
431 struct smb2_file_full_ea_info *src, size_t src_size,
432 const unsigned char *ea_name)
433{
434 int rc = 0;
435 unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
436 char *name, *value;
437 size_t name_len, value_len, user_name_len;
438
439 while (src_size > 0) {
440 name = &src->ea_data[0];
441 name_len = (size_t)src->ea_name_length;
442 value = &src->ea_data[src->ea_name_length + 1];
443 value_len = (size_t)le16_to_cpu(src->ea_value_length);
444
445 if (name_len == 0) {
446 break;
447 }
448
449 if (src_size < 8 + name_len + 1 + value_len) {
450 cifs_dbg(FYI, "EA entry goes beyond length of list\n");
451 rc = -EIO;
452 goto out;
453 }
454
455 if (ea_name) {
456 if (ea_name_len == name_len &&
457 memcmp(ea_name, name, name_len) == 0) {
458 rc = value_len;
459 if (dst_size == 0)
460 goto out;
461 if (dst_size < value_len) {
462 rc = -ERANGE;
463 goto out;
464 }
465 memcpy(dst, value, value_len);
466 goto out;
467 }
468 } else {
469 /* 'user.' plus a terminating null */
470 user_name_len = 5 + 1 + name_len;
471
472 rc += user_name_len;
473
474 if (dst_size >= user_name_len) {
475 dst_size -= user_name_len;
476 memcpy(dst, "user.", 5);
477 dst += 5;
478 memcpy(dst, src->ea_data, name_len);
479 dst += name_len;
480 *dst = 0;
481 ++dst;
482 } else if (dst_size == 0) {
483 /* skip copy - calc size only */
484 } else {
485 /* stop before overrun buffer */
486 rc = -ERANGE;
487 break;
488 }
489 }
490
491 if (!src->next_entry_offset)
492 break;
493
494 if (src_size < le32_to_cpu(src->next_entry_offset)) {
495 /* stop before overrun buffer */
496 rc = -ERANGE;
497 break;
498 }
499 src_size -= le32_to_cpu(src->next_entry_offset);
500 src = (void *)((char *)src +
501 le32_to_cpu(src->next_entry_offset));
502 }
503
504 /* didn't find the named attribute */
505 if (ea_name)
506 rc = -ENODATA;
507
508out:
509 return (ssize_t)rc;
510}
511
512static ssize_t
513smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
514 const unsigned char *path, const unsigned char *ea_name,
515 char *ea_data, size_t buf_size,
516 struct cifs_sb_info *cifs_sb)
517{
518 int rc;
519 __le16 *utf16_path;
520 __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
521 struct cifs_open_parms oparms;
522 struct cifs_fid fid;
523 struct smb2_file_full_ea_info *smb2_data;
524
525 utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
526 if (!utf16_path)
527 return -ENOMEM;
528
529 oparms.tcon = tcon;
530 oparms.desired_access = FILE_READ_EA;
531 oparms.disposition = FILE_OPEN;
532 oparms.create_options = 0;
533 oparms.fid = &fid;
534 oparms.reconnect = false;
535
536 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
537 kfree(utf16_path);
538 if (rc) {
539 cifs_dbg(FYI, "open failed rc=%d\n", rc);
540 return rc;
541 }
542
543 smb2_data = kzalloc(SMB2_MAX_EA_BUF, GFP_KERNEL);
544 if (smb2_data == NULL) {
545 SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
546 return -ENOMEM;
547 }
548
549 rc = SMB2_query_eas(xid, tcon, fid.persistent_fid, fid.volatile_fid,
550 smb2_data);
551 SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
552
553 if (!rc)
554 rc = move_smb2_ea_to_cifs(ea_data, buf_size, smb2_data,
555 SMB2_MAX_EA_BUF, ea_name);
556
557 kfree(smb2_data);
558 return rc;
559}
560
429static bool 561static bool
430smb2_can_echo(struct TCP_Server_Info *server) 562smb2_can_echo(struct TCP_Server_Info *server)
431{ 563{
@@ -2572,6 +2704,9 @@ struct smb_version_operations smb20_operations = {
2572 .dir_needs_close = smb2_dir_needs_close, 2704 .dir_needs_close = smb2_dir_needs_close,
2573 .get_dfs_refer = smb2_get_dfs_refer, 2705 .get_dfs_refer = smb2_get_dfs_refer,
2574 .select_sectype = smb2_select_sectype, 2706 .select_sectype = smb2_select_sectype,
2707#ifdef CONFIG_CIFS_XATTR
2708 .query_all_EAs = smb2_query_eas,
2709#endif /* CIFS_XATTR */
2575#ifdef CONFIG_CIFS_ACL 2710#ifdef CONFIG_CIFS_ACL
2576 .get_acl = get_smb2_acl, 2711 .get_acl = get_smb2_acl,
2577 .get_acl_by_fid = get_smb2_acl_by_fid, 2712 .get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2662,6 +2797,9 @@ struct smb_version_operations smb21_operations = {
2662 .enum_snapshots = smb3_enum_snapshots, 2797 .enum_snapshots = smb3_enum_snapshots,
2663 .get_dfs_refer = smb2_get_dfs_refer, 2798 .get_dfs_refer = smb2_get_dfs_refer,
2664 .select_sectype = smb2_select_sectype, 2799 .select_sectype = smb2_select_sectype,
2800#ifdef CONFIG_CIFS_XATTR
2801 .query_all_EAs = smb2_query_eas,
2802#endif /* CIFS_XATTR */
2665#ifdef CONFIG_CIFS_ACL 2803#ifdef CONFIG_CIFS_ACL
2666 .get_acl = get_smb2_acl, 2804 .get_acl = get_smb2_acl,
2667 .get_acl_by_fid = get_smb2_acl_by_fid, 2805 .get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2762,6 +2900,9 @@ struct smb_version_operations smb30_operations = {
2762 .receive_transform = smb3_receive_transform, 2900 .receive_transform = smb3_receive_transform,
2763 .get_dfs_refer = smb2_get_dfs_refer, 2901 .get_dfs_refer = smb2_get_dfs_refer,
2764 .select_sectype = smb2_select_sectype, 2902 .select_sectype = smb2_select_sectype,
2903#ifdef CONFIG_CIFS_XATTR
2904 .query_all_EAs = smb2_query_eas,
2905#endif /* CIFS_XATTR */
2765#ifdef CONFIG_CIFS_ACL 2906#ifdef CONFIG_CIFS_ACL
2766 .get_acl = get_smb2_acl, 2907 .get_acl = get_smb2_acl,
2767 .get_acl_by_fid = get_smb2_acl_by_fid, 2908 .get_acl_by_fid = get_smb2_acl_by_fid,
@@ -2863,6 +3004,9 @@ struct smb_version_operations smb311_operations = {
2863 .receive_transform = smb3_receive_transform, 3004 .receive_transform = smb3_receive_transform,
2864 .get_dfs_refer = smb2_get_dfs_refer, 3005 .get_dfs_refer = smb2_get_dfs_refer,
2865 .select_sectype = smb2_select_sectype, 3006 .select_sectype = smb2_select_sectype,
3007#ifdef CONFIG_CIFS_XATTR
3008 .query_all_EAs = smb2_query_eas,
3009#endif /* CIFS_XATTR */
2866}; 3010};
2867#endif /* CIFS_SMB311 */ 3011#endif /* CIFS_SMB311 */
2868 3012
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 7aa67206f6da..bf0ba3c15b63 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -2145,6 +2145,18 @@ qinf_exit:
2145 return rc; 2145 return rc;
2146} 2146}
2147 2147
2148int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
2149 u64 persistent_fid, u64 volatile_fid,
2150 struct smb2_file_full_ea_info *data)
2151{
2152 return query_info(xid, tcon, persistent_fid, volatile_fid,
2153 FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE, 0,
2154 SMB2_MAX_EA_BUF,
2155 sizeof(struct smb2_file_full_ea_info),
2156 (void **)&data,
2157 NULL);
2158}
2159
2148int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, 2160int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
2149 u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data) 2161 u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
2150{ 2162{
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 2826882c81d1..393ed5f4e1b6 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -1178,6 +1178,16 @@ struct smb2_file_link_info { /* encoding of request for level 11 */
1178 char FileName[0]; /* Name to be assigned to new link */ 1178 char FileName[0]; /* Name to be assigned to new link */
1179} __packed; /* level 11 Set */ 1179} __packed; /* level 11 Set */
1180 1180
1181#define SMB2_MAX_EA_BUF 2048
1182
1183struct smb2_file_full_ea_info { /* encoding of response for level 15 */
1184 __le32 next_entry_offset;
1185 __u8 flags;
1186 __u8 ea_name_length;
1187 __le16 ea_value_length;
1188 char ea_data[0]; /* \0 terminated name plus value */
1189} __packed; /* level 15 Set */
1190
1181/* 1191/*
1182 * This level 18, although with struct with same name is different from cifs 1192 * This level 18, although with struct with same name is different from cifs
1183 * level 0x107. Level 0x107 has an extra u64 between AccessFlags and 1193 * level 0x107. Level 0x107 has an extra u64 between AccessFlags and
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 1cadaf9f3c58..183389bfc8f6 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -132,6 +132,9 @@ extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
132 u64 persistent_file_id, u64 volatile_file_id); 132 u64 persistent_file_id, u64 volatile_file_id);
133extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, 133extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
134 u64 persistent_file_id, u64 volatile_file_id); 134 u64 persistent_file_id, u64 volatile_file_id);
135extern int SMB2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
136 u64 persistent_file_id, u64 volatile_file_id,
137 struct smb2_file_full_ea_info *data);
135extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, 138extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
136 u64 persistent_file_id, u64 volatile_file_id, 139 u64 persistent_file_id, u64 volatile_file_id,
137 struct smb2_file_all_info *data); 140 struct smb2_file_all_info *data);