diff options
-rw-r--r-- | fs/cifs/cifspdu.h | 16 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 205 |
2 files changed, 139 insertions, 82 deletions
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index c43bf4b7a55..93d5ee02a25 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h | |||
@@ -1906,17 +1906,15 @@ typedef struct smb_com_transaction2_get_dfs_refer_req { | |||
1906 | 1906 | ||
1907 | typedef struct dfs_referral_level_3 { | 1907 | typedef struct dfs_referral_level_3 { |
1908 | __le16 VersionNumber; | 1908 | __le16 VersionNumber; |
1909 | __le16 ReferralSize; | 1909 | __le16 Size; |
1910 | __le16 ServerType; /* 0x0001 = CIFS server */ | 1910 | __le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */ |
1911 | __le16 ReferralFlags; /* or proximity - not clear which since it is | 1911 | __le16 ReferralEntryFlags; /* 0x0200 bit set only for domain |
1912 | always set to zero - SNIA spec says 0x01 | 1912 | or DC referral responce */ |
1913 | means strip off PathConsumed chars before | 1913 | __le32 TimeToLive; |
1914 | submitting RequestFileName to remote node */ | ||
1915 | __le16 TimeToLive; | ||
1916 | __le16 Proximity; | ||
1917 | __le16 DfsPathOffset; | 1914 | __le16 DfsPathOffset; |
1918 | __le16 DfsAlternatePathOffset; | 1915 | __le16 DfsAlternatePathOffset; |
1919 | __le16 NetworkAddressOffset; | 1916 | __le16 NetworkAddressOffset; /* offset of the link target */ |
1917 | __le16 ServiceSiteGuid; | ||
1920 | } __attribute__((packed)) REFERRAL3; | 1918 | } __attribute__((packed)) REFERRAL3; |
1921 | 1919 | ||
1922 | typedef struct smb_com_transaction_get_dfs_refer_rsp { | 1920 | typedef struct smb_com_transaction_get_dfs_refer_rsp { |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index fc297383cb0..6f8ed93a4ae 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -81,6 +81,39 @@ static struct { | |||
81 | #endif /* CONFIG_CIFS_WEAK_PW_HASH */ | 81 | #endif /* CONFIG_CIFS_WEAK_PW_HASH */ |
82 | #endif /* CIFS_POSIX */ | 82 | #endif /* CIFS_POSIX */ |
83 | 83 | ||
84 | /* Allocates buffer into dst and copies smb string from src to it. | ||
85 | * caller is responsible for freeing dst if function returned 0. | ||
86 | * returns: | ||
87 | * on success - 0 | ||
88 | * on failure - errno | ||
89 | */ | ||
90 | static int | ||
91 | cifs_strncpy_to_host(char **dst, const char *src, const int maxlen, | ||
92 | const bool is_unicode, const struct nls_table *nls_codepage) | ||
93 | { | ||
94 | int plen; | ||
95 | |||
96 | if (is_unicode) { | ||
97 | plen = UniStrnlen((wchar_t *)src, maxlen); | ||
98 | *dst = kmalloc(plen + 2, GFP_KERNEL); | ||
99 | if (!*dst) | ||
100 | goto cifs_strncpy_to_host_ErrExit; | ||
101 | cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage); | ||
102 | } else { | ||
103 | plen = strnlen(src, maxlen); | ||
104 | *dst = kmalloc(plen + 2, GFP_KERNEL); | ||
105 | if (!*dst) | ||
106 | goto cifs_strncpy_to_host_ErrExit; | ||
107 | strncpy(*dst, src, plen); | ||
108 | } | ||
109 | (*dst)[plen] = 0; | ||
110 | return 0; | ||
111 | |||
112 | cifs_strncpy_to_host_ErrExit: | ||
113 | cERROR(1, ("Failed to allocate buffer for string\n")); | ||
114 | return -ENOMEM; | ||
115 | } | ||
116 | |||
84 | 117 | ||
85 | /* Mark as invalid, all open files on tree connections since they | 118 | /* Mark as invalid, all open files on tree connections since they |
86 | were closed when session to server was lost */ | 119 | were closed when session to server was lost */ |
@@ -3867,6 +3900,96 @@ GetInodeNumOut: | |||
3867 | return rc; | 3900 | return rc; |
3868 | } | 3901 | } |
3869 | 3902 | ||
3903 | /* parses DFS refferal V3 structure | ||
3904 | * caller is responsible for freeing target_nodes | ||
3905 | * returns: | ||
3906 | * on success - 0 | ||
3907 | * on failure - errno | ||
3908 | */ | ||
3909 | static int | ||
3910 | parse_DFS_REFERRALS(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr, | ||
3911 | unsigned int *num_of_nodes, | ||
3912 | struct dfs_info3_param **target_nodes, | ||
3913 | const struct nls_table *nls_codepage) | ||
3914 | { | ||
3915 | int i, rc = 0; | ||
3916 | char *data_end; | ||
3917 | bool is_unicode; | ||
3918 | struct dfs_referral_level_3 *ref; | ||
3919 | |||
3920 | is_unicode = pSMBr->hdr.Flags2 & SMBFLG2_UNICODE; | ||
3921 | *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals); | ||
3922 | |||
3923 | if (*num_of_nodes < 1) { | ||
3924 | cERROR(1, ("num_referrals: must be at least > 0," | ||
3925 | "but we get num_referrals = %d\n", *num_of_nodes)); | ||
3926 | rc = -EINVAL; | ||
3927 | goto parse_DFS_REFERRALS_exit; | ||
3928 | } | ||
3929 | |||
3930 | ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals); | ||
3931 | if (ref->VersionNumber != 3) { | ||
3932 | cERROR(1, ("Referrals of V%d version are not supported," | ||
3933 | "should be V3", ref->VersionNumber)); | ||
3934 | rc = -EINVAL; | ||
3935 | goto parse_DFS_REFERRALS_exit; | ||
3936 | } | ||
3937 | |||
3938 | /* get the upper boundary of the resp buffer */ | ||
3939 | data_end = (char *)(&(pSMBr->PathConsumed)) + | ||
3940 | le16_to_cpu(pSMBr->t2.DataCount); | ||
3941 | |||
3942 | cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n", | ||
3943 | *num_of_nodes, | ||
3944 | le16_to_cpu(pSMBr->DFSFlags))); | ||
3945 | |||
3946 | *target_nodes = kzalloc(sizeof(struct dfs_info3_param) * | ||
3947 | *num_of_nodes, GFP_KERNEL); | ||
3948 | if (*target_nodes == NULL) { | ||
3949 | cERROR(1, ("Failed to allocate buffer for target_nodes\n")); | ||
3950 | rc = -ENOMEM; | ||
3951 | goto parse_DFS_REFERRALS_exit; | ||
3952 | } | ||
3953 | |||
3954 | /* collect neccessary data from referrals */ | ||
3955 | for (i = 0; i < *num_of_nodes; i++) { | ||
3956 | char *temp; | ||
3957 | int max_len; | ||
3958 | struct dfs_info3_param *node = (*target_nodes)+i; | ||
3959 | |||
3960 | node->flags = le16_to_cpu(pSMBr->DFSFlags); | ||
3961 | node->path_consumed = le16_to_cpu(pSMBr->PathConsumed); | ||
3962 | node->server_type = le16_to_cpu(ref->ServerType); | ||
3963 | node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags); | ||
3964 | |||
3965 | /* copy DfsPath */ | ||
3966 | temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset); | ||
3967 | max_len = data_end - temp; | ||
3968 | rc = cifs_strncpy_to_host(&(node->path_name), temp, | ||
3969 | max_len, is_unicode, nls_codepage); | ||
3970 | if (rc) | ||
3971 | goto parse_DFS_REFERRALS_exit; | ||
3972 | |||
3973 | /* copy link target UNC */ | ||
3974 | temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset); | ||
3975 | max_len = data_end - temp; | ||
3976 | rc = cifs_strncpy_to_host(&(node->node_name), temp, | ||
3977 | max_len, is_unicode, nls_codepage); | ||
3978 | if (rc) | ||
3979 | goto parse_DFS_REFERRALS_exit; | ||
3980 | |||
3981 | ref += ref->Size; | ||
3982 | } | ||
3983 | |||
3984 | parse_DFS_REFERRALS_exit: | ||
3985 | if (rc) { | ||
3986 | free_dfs_info_array(*target_nodes, *num_of_nodes); | ||
3987 | *target_nodes = NULL; | ||
3988 | *num_of_nodes = 0; | ||
3989 | } | ||
3990 | return rc; | ||
3991 | } | ||
3992 | |||
3870 | int | 3993 | int |
3871 | CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, | 3994 | CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, |
3872 | const unsigned char *searchName, | 3995 | const unsigned char *searchName, |
@@ -3877,12 +4000,9 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses, | |||
3877 | /* TRANS2_GET_DFS_REFERRAL */ | 4000 | /* TRANS2_GET_DFS_REFERRAL */ |
3878 | TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; | 4001 | TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL; |
3879 | TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; | 4002 | TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; |
3880 | struct dfs_referral_level_3 *referrals = NULL; | ||
3881 | int rc = 0; | 4003 | int rc = 0; |
3882 | int bytes_returned; | 4004 | int bytes_returned; |
3883 | int name_len; | 4005 | int name_len; |
3884 | unsigned int i; | ||
3885 | char *temp; | ||
3886 | __u16 params, byte_count; | 4006 | __u16 params, byte_count; |
3887 | *num_of_nodes = 0; | 4007 | *num_of_nodes = 0; |
3888 | *target_nodes = NULL; | 4008 | *target_nodes = NULL; |
@@ -3960,80 +4080,19 @@ getDFSRetry: | |||
3960 | rc = validate_t2((struct smb_t2_rsp *)pSMBr); | 4080 | rc = validate_t2((struct smb_t2_rsp *)pSMBr); |
3961 | 4081 | ||
3962 | /* BB Also check if enough total bytes returned? */ | 4082 | /* BB Also check if enough total bytes returned? */ |
3963 | if (rc || (pSMBr->ByteCount < 17)) | 4083 | if (rc || (pSMBr->ByteCount < 17)) { |
3964 | rc = -EIO; /* bad smb */ | 4084 | rc = -EIO; /* bad smb */ |
3965 | else { | 4085 | goto GetDFSRefExit; |
3966 | __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); | 4086 | } |
3967 | __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount); | ||
3968 | |||
3969 | cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d", | ||
3970 | pSMBr->ByteCount, data_offset)); | ||
3971 | referrals = | ||
3972 | (struct dfs_referral_level_3 *) | ||
3973 | (8 /* sizeof start of data block */ + | ||
3974 | data_offset + | ||
3975 | (char *) &pSMBr->hdr.Protocol); | ||
3976 | cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n" | ||
3977 | "for referral one refer size: 0x%x srv " | ||
3978 | "type: 0x%x refer flags: 0x%x ttl: 0x%x", | ||
3979 | le16_to_cpu(pSMBr->NumberOfReferrals), | ||
3980 | le16_to_cpu(pSMBr->DFSFlags), | ||
3981 | le16_to_cpu(referrals->ReferralSize), | ||
3982 | le16_to_cpu(referrals->ServerType), | ||
3983 | le16_to_cpu(referrals->ReferralFlags), | ||
3984 | le16_to_cpu(referrals->TimeToLive))); | ||
3985 | /* BB This field is actually two bytes in from start of | ||
3986 | data block so we could do safety check that DataBlock | ||
3987 | begins at address of pSMBr->NumberOfReferrals */ | ||
3988 | *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals); | ||
3989 | |||
3990 | /* BB Fix below so can return more than one referral */ | ||
3991 | if (*num_of_nodes > 1) | ||
3992 | *num_of_nodes = 1; | ||
3993 | |||
3994 | /* get the length of the strings describing refs */ | ||
3995 | name_len = 0; | ||
3996 | for (i = 0; i < *num_of_nodes; i++) { | ||
3997 | /* make sure that DfsPathOffset not past end */ | ||
3998 | __u16 offset = le16_to_cpu(referrals->DfsPathOffset); | ||
3999 | if (offset > data_count) { | ||
4000 | /* if invalid referral, stop here and do | ||
4001 | not try to copy any more */ | ||
4002 | *num_of_nodes = i; | ||
4003 | break; | ||
4004 | } | ||
4005 | temp = ((char *)referrals) + offset; | ||
4006 | 4087 | ||
4007 | if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { | 4088 | cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d", |
4008 | name_len += UniStrnlen((wchar_t *)temp, | 4089 | pSMBr->ByteCount, |
4009 | data_count); | 4090 | le16_to_cpu(pSMBr->t2.DataOffset))); |
4010 | } else { | ||
4011 | name_len += strnlen(temp, data_count); | ||
4012 | } | ||
4013 | referrals++; | ||
4014 | /* BB add check that referral pointer does | ||
4015 | not fall off end PDU */ | ||
4016 | } | ||
4017 | /* BB add check for name_len bigger than bcc */ | ||
4018 | *target_nodes = | ||
4019 | kmalloc(name_len+1+(*num_of_nodes), | ||
4020 | GFP_KERNEL); | ||
4021 | if (*target_nodes == NULL) { | ||
4022 | rc = -ENOMEM; | ||
4023 | goto GetDFSRefExit; | ||
4024 | } | ||
4025 | 4091 | ||
4026 | referrals = (struct dfs_referral_level_3 *) | 4092 | /* parse returned result into more usable form */ |
4027 | (8 /* sizeof data hdr */ + data_offset + | 4093 | rc = parse_DFS_REFERRALS(pSMBr, num_of_nodes, |
4028 | (char *) &pSMBr->hdr.Protocol); | 4094 | target_nodes, nls_codepage); |
4029 | 4095 | ||
4030 | for (i = 0; i < *num_of_nodes; i++) { | ||
4031 | temp = ((char *)referrals) + | ||
4032 | le16_to_cpu(referrals->DfsPathOffset); | ||
4033 | /* BB update target_uncs pointers */ | ||
4034 | referrals++; | ||
4035 | } | ||
4036 | } | ||
4037 | GetDFSRefExit: | 4096 | GetDFSRefExit: |
4038 | if (pSMB) | 4097 | if (pSMB) |
4039 | cifs_buf_release(pSMB); | 4098 | cifs_buf_release(pSMB); |