aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/misc.c')
-rw-r--r--fs/cifs/misc.c516
1 files changed, 516 insertions, 0 deletions
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
new file mode 100644
index 000000000000..7b38d3059a83
--- /dev/null
+++ b/fs/cifs/misc.c
@@ -0,0 +1,516 @@
1/*
2 * fs/cifs/misc.c
3 *
4 * Copyright (C) International Business Machines Corp., 2002,2004
5 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/slab.h>
23#include <linux/ctype.h>
24#include <linux/mempool.h>
25#include "cifspdu.h"
26#include "cifsglob.h"
27#include "cifsproto.h"
28#include "cifs_debug.h"
29#include "smberr.h"
30#include "nterr.h"
31
32extern mempool_t *cifs_sm_req_poolp;
33extern mempool_t *cifs_req_poolp;
34extern struct task_struct * oplockThread;
35
36static __u16 GlobalMid; /* multiplex id - rotating counter */
37
38/* The xid serves as a useful identifier for each incoming vfs request,
39 in a similar way to the mid which is useful to track each sent smb,
40 and CurrentXid can also provide a running counter (although it
41 will eventually wrap past zero) of the total vfs operations handled
42 since the cifs fs was mounted */
43
44unsigned int
45_GetXid(void)
46{
47 unsigned int xid;
48
49 spin_lock(&GlobalMid_Lock);
50 GlobalTotalActiveXid++;
51 if (GlobalTotalActiveXid > GlobalMaxActiveXid)
52 GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
53 xid = GlobalCurrentXid++;
54 spin_unlock(&GlobalMid_Lock);
55 return xid;
56}
57
58void
59_FreeXid(unsigned int xid)
60{
61 spin_lock(&GlobalMid_Lock);
62 /* if(GlobalTotalActiveXid == 0)
63 BUG(); */
64 GlobalTotalActiveXid--;
65 spin_unlock(&GlobalMid_Lock);
66}
67
68struct cifsSesInfo *
69sesInfoAlloc(void)
70{
71 struct cifsSesInfo *ret_buf;
72
73 ret_buf =
74 (struct cifsSesInfo *) kmalloc(sizeof (struct cifsSesInfo),
75 GFP_KERNEL);
76 if (ret_buf) {
77 memset(ret_buf, 0, sizeof (struct cifsSesInfo));
78 write_lock(&GlobalSMBSeslock);
79 atomic_inc(&sesInfoAllocCount);
80 ret_buf->status = CifsNew;
81 list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
82 init_MUTEX(&ret_buf->sesSem);
83 write_unlock(&GlobalSMBSeslock);
84 }
85 return ret_buf;
86}
87
88void
89sesInfoFree(struct cifsSesInfo *buf_to_free)
90{
91 if (buf_to_free == NULL) {
92 cFYI(1, ("Null buffer passed to sesInfoFree"));
93 return;
94 }
95
96 write_lock(&GlobalSMBSeslock);
97 atomic_dec(&sesInfoAllocCount);
98 list_del(&buf_to_free->cifsSessionList);
99 write_unlock(&GlobalSMBSeslock);
100 if (buf_to_free->serverOS)
101 kfree(buf_to_free->serverOS);
102 if (buf_to_free->serverDomain)
103 kfree(buf_to_free->serverDomain);
104 if (buf_to_free->serverNOS)
105 kfree(buf_to_free->serverNOS);
106 if (buf_to_free->password)
107 kfree(buf_to_free->password);
108 kfree(buf_to_free);
109}
110
111struct cifsTconInfo *
112tconInfoAlloc(void)
113{
114 struct cifsTconInfo *ret_buf;
115 ret_buf =
116 (struct cifsTconInfo *) kmalloc(sizeof (struct cifsTconInfo),
117 GFP_KERNEL);
118 if (ret_buf) {
119 memset(ret_buf, 0, sizeof (struct cifsTconInfo));
120 write_lock(&GlobalSMBSeslock);
121 atomic_inc(&tconInfoAllocCount);
122 list_add(&ret_buf->cifsConnectionList,
123 &GlobalTreeConnectionList);
124 ret_buf->tidStatus = CifsNew;
125 INIT_LIST_HEAD(&ret_buf->openFileList);
126 init_MUTEX(&ret_buf->tconSem);
127#ifdef CONFIG_CIFS_STATS
128 spin_lock_init(&ret_buf->stat_lock);
129#endif
130 write_unlock(&GlobalSMBSeslock);
131 }
132 return ret_buf;
133}
134
135void
136tconInfoFree(struct cifsTconInfo *buf_to_free)
137{
138 if (buf_to_free == NULL) {
139 cFYI(1, ("Null buffer passed to tconInfoFree"));
140 return;
141 }
142 write_lock(&GlobalSMBSeslock);
143 atomic_dec(&tconInfoAllocCount);
144 list_del(&buf_to_free->cifsConnectionList);
145 write_unlock(&GlobalSMBSeslock);
146 if (buf_to_free->nativeFileSystem)
147 kfree(buf_to_free->nativeFileSystem);
148 kfree(buf_to_free);
149}
150
151struct smb_hdr *
152cifs_buf_get(void)
153{
154 struct smb_hdr *ret_buf = NULL;
155
156/* We could use negotiated size instead of max_msgsize -
157 but it may be more efficient to always alloc same size
158 albeit slightly larger than necessary and maxbuffersize
159 defaults to this and can not be bigger */
160 ret_buf =
161 (struct smb_hdr *) mempool_alloc(cifs_req_poolp, SLAB_KERNEL | SLAB_NOFS);
162
163 /* clear the first few header bytes */
164 /* for most paths, more is cleared in header_assemble */
165 if (ret_buf) {
166 memset(ret_buf, 0, sizeof(struct smb_hdr) + 3);
167 atomic_inc(&bufAllocCount);
168 }
169
170 return ret_buf;
171}
172
173void
174cifs_buf_release(void *buf_to_free)
175{
176
177 if (buf_to_free == NULL) {
178 /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/
179 return;
180 }
181 mempool_free(buf_to_free,cifs_req_poolp);
182
183 atomic_dec(&bufAllocCount);
184 return;
185}
186
187struct smb_hdr *
188cifs_small_buf_get(void)
189{
190 struct smb_hdr *ret_buf = NULL;
191
192/* We could use negotiated size instead of max_msgsize -
193 but it may be more efficient to always alloc same size
194 albeit slightly larger than necessary and maxbuffersize
195 defaults to this and can not be bigger */
196 ret_buf =
197 (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp, SLAB_KERNEL | SLAB_NOFS);
198 if (ret_buf) {
199 /* No need to clear memory here, cleared in header assemble */
200 /* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
201 atomic_inc(&smBufAllocCount);
202 }
203 return ret_buf;
204}
205
206void
207cifs_small_buf_release(void *buf_to_free)
208{
209
210 if (buf_to_free == NULL) {
211 cFYI(1, ("Null buffer passed to cifs_small_buf_release"));
212 return;
213 }
214 mempool_free(buf_to_free,cifs_sm_req_poolp);
215
216 atomic_dec(&smBufAllocCount);
217 return;
218}
219
220void
221header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
222 const struct cifsTconInfo *treeCon, int word_count
223 /* length of fixed section (word count) in two byte units */)
224{
225 struct list_head* temp_item;
226 struct cifsSesInfo * ses;
227 char *temp = (char *) buffer;
228
229 memset(temp,0,MAX_CIFS_HDR_SIZE);
230
231 buffer->smb_buf_length =
232 (2 * word_count) + sizeof (struct smb_hdr) -
233 4 /* RFC 1001 length field does not count */ +
234 2 /* for bcc field itself */ ;
235 /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */
236
237 buffer->Protocol[0] = 0xFF;
238 buffer->Protocol[1] = 'S';
239 buffer->Protocol[2] = 'M';
240 buffer->Protocol[3] = 'B';
241 buffer->Command = smb_command;
242 buffer->Flags = 0x00; /* case sensitive */
243 buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
244 buffer->Pid = cpu_to_le16((__u16)current->tgid);
245 buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
246 spin_lock(&GlobalMid_Lock);
247 GlobalMid++;
248 buffer->Mid = GlobalMid;
249 spin_unlock(&GlobalMid_Lock);
250 if (treeCon) {
251 buffer->Tid = treeCon->tid;
252 if (treeCon->ses) {
253 if (treeCon->ses->capabilities & CAP_UNICODE)
254 buffer->Flags2 |= SMBFLG2_UNICODE;
255 if (treeCon->ses->capabilities & CAP_STATUS32) {
256 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
257 }
258
259 buffer->Uid = treeCon->ses->Suid; /* always in LE format */
260 if(multiuser_mount != 0) {
261 /* For the multiuser case, there are few obvious technically */
262 /* possible mechanisms to match the local linux user (uid) */
263 /* to a valid remote smb user (smb_uid): */
264 /* 1) Query Winbind (or other local pam/nss daemon */
265 /* for userid/password/logon_domain or credential */
266 /* 2) Query Winbind for uid to sid to username mapping */
267 /* and see if we have a matching password for existing*/
268 /* session for that user perhas getting password by */
269 /* adding a new pam_cifs module that stores passwords */
270 /* so that the cifs vfs can get at that for all logged*/
271 /* on users */
272 /* 3) (Which is the mechanism we have chosen) */
273 /* Search through sessions to the same server for a */
274 /* a match on the uid that was passed in on mount */
275 /* with the current processes uid (or euid?) and use */
276 /* that smb uid. If no existing smb session for */
277 /* that uid found, use the default smb session ie */
278 /* the smb session for the volume mounted which is */
279 /* the same as would be used if the multiuser mount */
280 /* flag were disabled. */
281
282 /* BB Add support for establishing new tCon and SMB Session */
283 /* with userid/password pairs found on the smb session */
284 /* for other target tcp/ip addresses BB */
285 if(current->uid != treeCon->ses->linux_uid) {
286 cFYI(1,("Multiuser mode and UID did not match tcon uid "));
287 read_lock(&GlobalSMBSeslock);
288 list_for_each(temp_item, &GlobalSMBSessionList) {
289 ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
290 if(ses->linux_uid == current->uid) {
291 if(ses->server == treeCon->ses->server) {
292 cFYI(1,("found matching uid substitute right smb_uid"));
293 buffer->Uid = ses->Suid;
294 break;
295 } else {
296 /* BB eventually call cifs_setup_session here */
297 cFYI(1,("local UID found but smb sess with this server does not exist"));
298 }
299 }
300 }
301 read_unlock(&GlobalSMBSeslock);
302 }
303 }
304 }
305 if (treeCon->Flags & SMB_SHARE_IS_IN_DFS)
306 buffer->Flags2 |= SMBFLG2_DFS;
307 if((treeCon->ses) && (treeCon->ses->server))
308 if(treeCon->ses->server->secMode &
309 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
310 buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
311 }
312
313/* endian conversion of flags is now done just before sending */
314 buffer->WordCount = (char) word_count;
315 return;
316}
317
318int
319checkSMBhdr(struct smb_hdr *smb, __u16 mid)
320{
321 /* Make sure that this really is an SMB, that it is a response,
322 and that the message ids match */
323 if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) &&
324 (mid == smb->Mid)) {
325 if(smb->Flags & SMBFLG_RESPONSE)
326 return 0;
327 else {
328 /* only one valid case where server sends us request */
329 if(smb->Command == SMB_COM_LOCKING_ANDX)
330 return 0;
331 else
332 cERROR(1, ("Rcvd Request not response "));
333 }
334 } else { /* bad signature or mid */
335 if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff))
336 cERROR(1,
337 ("Bad protocol string signature header %x ",
338 *(unsigned int *) smb->Protocol));
339 if (mid != smb->Mid)
340 cERROR(1, ("Mids do not match"));
341 }
342 cERROR(1, ("bad smb detected. The Mid=%d", smb->Mid));
343 return 1;
344}
345
346int
347checkSMB(struct smb_hdr *smb, __u16 mid, int length)
348{
349 __u32 len = be32_to_cpu(smb->smb_buf_length);
350 cFYI(0,
351 ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
352 length, len));
353 if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) ||
354 (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) {
355 if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) {
356 if (((unsigned int)length >=
357 sizeof (struct smb_hdr) - 1)
358 && (smb->Status.CifsError != 0)) {
359 smb->WordCount = 0;
360 return 0; /* some error cases do not return wct and bcc */
361 } else {
362 cERROR(1, ("Length less than smb header size"));
363 }
364
365 }
366 if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)
367 cERROR(1,
368 ("smb_buf_length greater than MaxBufSize"));
369 cERROR(1,
370 ("bad smb detected. Illegal length. The mid=%d",
371 smb->Mid));
372 return 1;
373 }
374
375 if (checkSMBhdr(smb, mid))
376 return 1;
377
378 if ((4 + len != smbCalcSize(smb))
379 || (4 + len != (unsigned int)length)) {
380 return 0;
381 } else {
382 cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb)));
383 cERROR(1,
384 ("bad smb size detected. The Mid=%d", smb->Mid));
385 return 1;
386 }
387}
388int
389is_valid_oplock_break(struct smb_hdr *buf)
390{
391 struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf;
392 struct list_head *tmp;
393 struct list_head *tmp1;
394 struct cifsTconInfo *tcon;
395 struct cifsFileInfo *netfile;
396
397 cFYI(1,("Checking for oplock break or dnotify response"));
398 if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
399 (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
400 struct smb_com_transaction_change_notify_rsp * pSMBr =
401 (struct smb_com_transaction_change_notify_rsp *)buf;
402 struct file_notify_information * pnotify;
403 __u32 data_offset = 0;
404 if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
405 data_offset = le32_to_cpu(pSMBr->DataOffset);
406
407 pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
408 + data_offset);
409 cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
410 pnotify->Action)); /* BB removeme BB */
411 /* cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
412 return TRUE;
413 }
414 if(pSMBr->hdr.Status.CifsError) {
415 cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError));
416 return TRUE;
417 }
418 return FALSE;
419 }
420 if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
421 return FALSE;
422 if(pSMB->hdr.Flags & SMBFLG_RESPONSE) {
423 /* no sense logging error on invalid handle on oplock
424 break - harmless race between close request and oplock
425 break response is expected from time to time writing out
426 large dirty files cached on the client */
427 if ((NT_STATUS_INVALID_HANDLE) ==
428 le32_to_cpu(pSMB->hdr.Status.CifsError)) {
429 cFYI(1,("invalid handle on oplock break"));
430 return TRUE;
431 } else if (ERRbadfid ==
432 le16_to_cpu(pSMB->hdr.Status.DosError.Error)) {
433 return TRUE;
434 } else {
435 return FALSE; /* on valid oplock brk we get "request" */
436 }
437 }
438 if(pSMB->hdr.WordCount != 8)
439 return FALSE;
440
441 cFYI(1,(" oplock type 0x%d level 0x%d",pSMB->LockType,pSMB->OplockLevel));
442 if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
443 return FALSE;
444
445 /* look up tcon based on tid & uid */
446 read_lock(&GlobalSMBSeslock);
447 list_for_each(tmp, &GlobalTreeConnectionList) {
448 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
449 if (tcon->tid == buf->Tid) {
450#ifdef CONFIG_CIFS_STATS
451 atomic_inc(&tcon->num_oplock_brks);
452#endif
453 list_for_each(tmp1,&tcon->openFileList){
454 netfile = list_entry(tmp1,struct cifsFileInfo,tlist);
455 if(pSMB->Fid == netfile->netfid) {
456 struct cifsInodeInfo *pCifsInode;
457 read_unlock(&GlobalSMBSeslock);
458 cFYI(1,("Matching file id, processing oplock break"));
459 pCifsInode =
460 CIFS_I(netfile->pInode);
461 pCifsInode->clientCanCacheAll = FALSE;
462 if(pSMB->OplockLevel == 0)
463 pCifsInode->clientCanCacheRead = FALSE;
464 pCifsInode->oplockPending = TRUE;
465 AllocOplockQEntry(netfile->pInode, netfile->netfid, tcon);
466 cFYI(1,("about to wake up oplock thd"));
467 wake_up_process(oplockThread);
468 return TRUE;
469 }
470 }
471 read_unlock(&GlobalSMBSeslock);
472 cFYI(1,("No matching file for oplock break on connection"));
473 return TRUE;
474 }
475 }
476 read_unlock(&GlobalSMBSeslock);
477 cFYI(1,("Can not process oplock break for non-existent connection"));
478 return TRUE;
479}
480
481void
482dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
483{
484 int i, j;
485 char debug_line[17];
486 unsigned char *buffer;
487
488 if (traceSMB == 0)
489 return;
490
491 buffer = (unsigned char *) smb_buf;
492 for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
493 if (i % 8 == 0) { /* we have reached the beginning of line */
494 printk(KERN_DEBUG "| ");
495 j = 0;
496 }
497 printk("%0#4x ", buffer[i]);
498 debug_line[2 * j] = ' ';
499 if (isprint(buffer[i]))
500 debug_line[1 + (2 * j)] = buffer[i];
501 else
502 debug_line[1 + (2 * j)] = '_';
503
504 if (i % 8 == 7) { /* we have reached end of line, time to print ascii */
505 debug_line[16] = 0;
506 printk(" | %s\n", debug_line);
507 }
508 }
509 for (; j < 8; j++) {
510 printk(" ");
511 debug_line[2 * j] = ' ';
512 debug_line[1 + (2 * j)] = ' ';
513 }
514 printk( " | %s\n", debug_line);
515 return;
516}