diff options
author | Ronnie Sahlberg <lsahlber@redhat.com> | 2017-08-23 21:24:55 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2017-09-04 15:03:41 -0400 |
commit | 95907fea4fd8ccc736e0a428e52159b4d42b9958 (patch) | |
tree | e2f69feb6d0f5b2c85e8666502608584c22e14f7 | |
parent | 81a84ad3cb5711cec79f4dd53a4ce026b092c432 (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.c | 144 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 12 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 10 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 3 |
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 | ||
429 | static ssize_t | ||
430 | move_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 | |||
508 | out: | ||
509 | return (ssize_t)rc; | ||
510 | } | ||
511 | |||
512 | static ssize_t | ||
513 | smb2_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 | |||
429 | static bool | 561 | static bool |
430 | smb2_can_echo(struct TCP_Server_Info *server) | 562 | smb2_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 | ||
2148 | int 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 | |||
2148 | int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, | 2160 | int 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 | |||
1183 | struct 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); |
133 | extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, | 133 | extern 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); |
135 | extern 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); | ||
135 | extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon, | 138 | extern 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); |