Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Feb 10-13, 2025
Register
Loading...
Note: File does not exist in v6.8.
  1/*
  2 *  SMB1 (CIFS) version specific operations
  3 *
  4 *  Copyright (c) 2012, Jeff Layton <jlayton@redhat.com>
  5 *
  6 *  This library is free software; you can redistribute it and/or modify
  7 *  it under the terms of the GNU General Public License v2 as published
  8 *  by the Free Software Foundation.
  9 *
 10 *  This library is distributed in the hope that it will be useful,
 11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 13 *  the GNU Lesser General Public License for more details.
 14 *
 15 *  You should have received a copy of the GNU Lesser General Public License
 16 *  along with this library; if not, write to the Free Software
 17 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 18 */
 19
 20#include "cifsglob.h"
 21#include "cifsproto.h"
 22#include "cifs_debug.h"
 23#include "cifspdu.h"
 24
 25/*
 26 * An NT cancel request header looks just like the original request except:
 27 *
 28 * The Command is SMB_COM_NT_CANCEL
 29 * The WordCount is zeroed out
 30 * The ByteCount is zeroed out
 31 *
 32 * This function mangles an existing request buffer into a
 33 * SMB_COM_NT_CANCEL request and then sends it.
 34 */
 35static int
 36send_nt_cancel(struct TCP_Server_Info *server, void *buf,
 37	       struct mid_q_entry *mid)
 38{
 39	int rc = 0;
 40	struct smb_hdr *in_buf = (struct smb_hdr *)buf;
 41
 42	/* -4 for RFC1001 length and +2 for BCC field */
 43	in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4  + 2);
 44	in_buf->Command = SMB_COM_NT_CANCEL;
 45	in_buf->WordCount = 0;
 46	put_bcc(0, in_buf);
 47
 48	mutex_lock(&server->srv_mutex);
 49	rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
 50	if (rc) {
 51		mutex_unlock(&server->srv_mutex);
 52		return rc;
 53	}
 54	rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
 55	mutex_unlock(&server->srv_mutex);
 56
 57	cFYI(1, "issued NT_CANCEL for mid %u, rc = %d",
 58		in_buf->Mid, rc);
 59
 60	return rc;
 61}
 62
 63static bool
 64cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
 65{
 66	return ob1->netfid == ob2->netfid;
 67}
 68
 69static unsigned int
 70cifs_read_data_offset(char *buf)
 71{
 72	READ_RSP *rsp = (READ_RSP *)buf;
 73	return le16_to_cpu(rsp->DataOffset);
 74}
 75
 76static unsigned int
 77cifs_read_data_length(char *buf)
 78{
 79	READ_RSP *rsp = (READ_RSP *)buf;
 80	return (le16_to_cpu(rsp->DataLengthHigh) << 16) +
 81	       le16_to_cpu(rsp->DataLength);
 82}
 83
 84static struct mid_q_entry *
 85cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
 86{
 87	struct smb_hdr *buf = (struct smb_hdr *)buffer;
 88	struct mid_q_entry *mid;
 89
 90	spin_lock(&GlobalMid_Lock);
 91	list_for_each_entry(mid, &server->pending_mid_q, qhead) {
 92		if (mid->mid == buf->Mid &&
 93		    mid->mid_state == MID_REQUEST_SUBMITTED &&
 94		    le16_to_cpu(mid->command) == buf->Command) {
 95			spin_unlock(&GlobalMid_Lock);
 96			return mid;
 97		}
 98	}
 99	spin_unlock(&GlobalMid_Lock);
100	return NULL;
101}
102
103static void
104cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add)
105{
106	spin_lock(&server->req_lock);
107	server->credits += add;
108	server->in_flight--;
109	spin_unlock(&server->req_lock);
110	wake_up(&server->request_q);
111}
112
113static void
114cifs_set_credits(struct TCP_Server_Info *server, const int val)
115{
116	spin_lock(&server->req_lock);
117	server->credits = val;
118	server->oplocks = val > 1 ? enable_oplocks : false;
119	spin_unlock(&server->req_lock);
120}
121
122static int *
123cifs_get_credits_field(struct TCP_Server_Info *server)
124{
125	return &server->credits;
126}
127
128/*
129 * Find a free multiplex id (SMB mid). Otherwise there could be
130 * mid collisions which might cause problems, demultiplexing the
131 * wrong response to this request. Multiplex ids could collide if
132 * one of a series requests takes much longer than the others, or
133 * if a very large number of long lived requests (byte range
134 * locks or FindNotify requests) are pending. No more than
135 * 64K-1 requests can be outstanding at one time. If no
136 * mids are available, return zero. A future optimization
137 * could make the combination of mids and uid the key we use
138 * to demultiplex on (rather than mid alone).
139 * In addition to the above check, the cifs demultiplex
140 * code already used the command code as a secondary
141 * check of the frame and if signing is negotiated the
142 * response would be discarded if the mid were the same
143 * but the signature was wrong. Since the mid is not put in the
144 * pending queue until later (when it is about to be dispatched)
145 * we do have to limit the number of outstanding requests
146 * to somewhat less than 64K-1 although it is hard to imagine
147 * so many threads being in the vfs at one time.
148 */
149static __u64
150cifs_get_next_mid(struct TCP_Server_Info *server)
151{
152	__u64 mid = 0;
153	__u16 last_mid, cur_mid;
154	bool collision;
155
156	spin_lock(&GlobalMid_Lock);
157
158	/* mid is 16 bit only for CIFS/SMB */
159	cur_mid = (__u16)((server->CurrentMid) & 0xffff);
160	/* we do not want to loop forever */
161	last_mid = cur_mid;
162	cur_mid++;
163
164	/*
165	 * This nested loop looks more expensive than it is.
166	 * In practice the list of pending requests is short,
167	 * fewer than 50, and the mids are likely to be unique
168	 * on the first pass through the loop unless some request
169	 * takes longer than the 64 thousand requests before it
170	 * (and it would also have to have been a request that
171	 * did not time out).
172	 */
173	while (cur_mid != last_mid) {
174		struct mid_q_entry *mid_entry;
175		unsigned int num_mids;
176
177		collision = false;
178		if (cur_mid == 0)
179			cur_mid++;
180
181		num_mids = 0;
182		list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
183			++num_mids;
184			if (mid_entry->mid == cur_mid &&
185			    mid_entry->mid_state == MID_REQUEST_SUBMITTED) {
186				/* This mid is in use, try a different one */
187				collision = true;
188				break;
189			}
190		}
191
192		/*
193		 * if we have more than 32k mids in the list, then something
194		 * is very wrong. Possibly a local user is trying to DoS the
195		 * box by issuing long-running calls and SIGKILL'ing them. If
196		 * we get to 2^16 mids then we're in big trouble as this
197		 * function could loop forever.
198		 *
199		 * Go ahead and assign out the mid in this situation, but force
200		 * an eventual reconnect to clean out the pending_mid_q.
201		 */
202		if (num_mids > 32768)
203			server->tcpStatus = CifsNeedReconnect;
204
205		if (!collision) {
206			mid = (__u64)cur_mid;
207			server->CurrentMid = mid;
208			break;
209		}
210		cur_mid++;
211	}
212	spin_unlock(&GlobalMid_Lock);
213	return mid;
214}
215
216struct smb_version_operations smb1_operations = {
217	.send_cancel = send_nt_cancel,
218	.compare_fids = cifs_compare_fids,
219	.setup_request = cifs_setup_request,
220	.check_receive = cifs_check_receive,
221	.add_credits = cifs_add_credits,
222	.set_credits = cifs_set_credits,
223	.get_credits_field = cifs_get_credits_field,
224	.get_next_mid = cifs_get_next_mid,
225	.read_data_offset = cifs_read_data_offset,
226	.read_data_length = cifs_read_data_length,
227	.map_error = map_smb_to_linux_error,
228	.find_mid = cifs_find_mid,
229	.check_message = checkSMB,
230	.dump_detail = cifs_dump_detail,
231	.is_oplock_break = is_valid_oplock_break,
232};
233
234struct smb_version_values smb1_values = {
235	.version_string = SMB1_VERSION_STRING,
236	.large_lock_type = LOCKING_ANDX_LARGE_FILES,
237	.exclusive_lock_type = 0,
238	.shared_lock_type = LOCKING_ANDX_SHARED_LOCK,
239	.unlock_lock_type = 0,
240	.header_size = sizeof(struct smb_hdr),
241	.max_header_size = MAX_CIFS_HDR_SIZE,
242	.read_rsp_size = sizeof(READ_RSP),
243};