vserver 1.9.5.x5
[linux-2.6.git] / fs / cifs / misc.c
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
32 extern mempool_t *cifs_sm_req_poolp;
33 extern mempool_t *cifs_req_poolp;
34 extern struct task_struct * oplockThread;
35
36 __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
44 unsigned 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
58 void
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
68 struct cifsSesInfo *
69 sesInfoAlloc(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
88 void
89 sesInfoFree(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
111 struct cifsTconInfo *
112 tconInfoAlloc(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
135 void
136 tconInfoFree(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
151 struct smb_hdr *
152 cifs_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
173 void
174 cifs_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
187 struct smb_hdr *
188 cifs_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
206 void
207 cifs_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
220 void
221 header_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
318 int
319 checkSMBhdr(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
346 int
347 checkSMB(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                         cERROR(1, ("Length less than 2 + sizeof smb_hdr "));
357                         if (((unsigned int)length >= sizeof (struct smb_hdr) - 1)
358                             && (smb->Status.CifsError != 0))
359                                 return 0;       /* some error cases do not return wct and bcc */
360
361                 }
362                 if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)
363                         cERROR(1,
364                                ("smb_buf_length greater than MaxBufSize"));
365                 cERROR(1,
366                        ("bad smb detected. Illegal length. The mid=%d",
367                         smb->Mid));
368                 return 1;
369         }
370
371         if (checkSMBhdr(smb, mid))
372                 return 1;
373
374         if ((4 + len != smbCalcSize(smb))
375             || (4 + len != (unsigned int)length)) {
376                 return 0;
377         } else {
378                 cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb)));
379                 cERROR(1,
380                        ("bad smb size detected. The Mid=%d", smb->Mid));
381                 return 1;
382         }
383 }
384 int
385 is_valid_oplock_break(struct smb_hdr *buf)
386 {    
387         struct smb_com_lock_req * pSMB = (struct smb_com_lock_req *)buf;
388         struct list_head *tmp;
389         struct list_head *tmp1;
390         struct cifsTconInfo *tcon;
391         struct cifsFileInfo *netfile;
392
393         cFYI(1,("Checking for oplock break or dnotify response"));
394         if((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) &&
395            (pSMB->hdr.Flags & SMBFLG_RESPONSE)) {
396                 struct smb_com_transaction_change_notify_rsp * pSMBr =
397                         (struct smb_com_transaction_change_notify_rsp *)buf;
398                 struct file_notify_information * pnotify;
399                 __u32 data_offset = 0;
400                 if(pSMBr->ByteCount > sizeof(struct file_notify_information)) {
401                         data_offset = le32_to_cpu(pSMBr->DataOffset);
402
403                         pnotify = (struct file_notify_information *)((char *)&pSMBr->hdr.Protocol
404                                 + data_offset);
405                         cFYI(1,("dnotify on %s with action: 0x%x",pnotify->FileName,
406                                 pnotify->Action));  /* BB removeme BB */
407                      /*   cifs_dump_mem("Received notify Data is: ",buf,sizeof(struct smb_hdr)+60); */
408                         return TRUE;
409                 }
410                 if(pSMBr->hdr.Status.CifsError) {
411                         cFYI(1,("notify err 0x%d",pSMBr->hdr.Status.CifsError));
412                         return TRUE;
413                 }
414                 return FALSE;
415         }  
416         if(pSMB->hdr.Command != SMB_COM_LOCKING_ANDX)
417                 return FALSE;
418         if(pSMB->hdr.Flags & SMBFLG_RESPONSE) {
419                 /* no sense logging error on invalid handle on oplock
420                    break - harmless race between close request and oplock
421                    break response is expected from time to time writing out
422                    large dirty files cached on the client */
423                 if ((NT_STATUS_INVALID_HANDLE) == 
424                    le32_to_cpu(pSMB->hdr.Status.CifsError)) { 
425                         cFYI(1,("invalid handle on oplock break"));
426                         return TRUE;
427                 } else if (ERRbadfid == 
428                    le16_to_cpu(pSMB->hdr.Status.DosError.Error)) {
429                         return TRUE;      
430                 } else {
431                         return FALSE; /* on valid oplock brk we get "request" */
432                 }
433         }
434         if(pSMB->hdr.WordCount != 8)
435                 return FALSE;
436
437         cFYI(1,(" oplock type 0x%d level 0x%d",pSMB->LockType,pSMB->OplockLevel));
438         if(!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
439                 return FALSE;    
440
441         /* look up tcon based on tid & uid */
442         read_lock(&GlobalSMBSeslock);
443         list_for_each(tmp, &GlobalTreeConnectionList) {
444                 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
445                 if (tcon->tid == buf->Tid) {
446 #ifdef CONFIG_CIFS_STATS
447                         atomic_inc(&tcon->num_oplock_brks);
448 #endif
449                         list_for_each(tmp1,&tcon->openFileList){
450                                 netfile = list_entry(tmp1,struct cifsFileInfo,tlist);
451                                 if(pSMB->Fid == netfile->netfid) {
452                                         struct cifsInodeInfo *pCifsInode;
453                                         read_unlock(&GlobalSMBSeslock);
454                                         cFYI(1,("Matching file id, processing oplock break"));
455                                         pCifsInode = 
456                                                 CIFS_I(netfile->pInode);
457                                         pCifsInode->clientCanCacheAll = FALSE;
458                                         if(pSMB->OplockLevel == 0)
459                                                 pCifsInode->clientCanCacheRead = FALSE;
460                                         pCifsInode->oplockPending = TRUE;
461                                         AllocOplockQEntry(netfile->pInode, netfile->netfid, tcon);
462                                         cFYI(1,("about to wake up oplock thd"));
463                                         wake_up_process(oplockThread);               
464                                         return TRUE;
465                                 }
466                         }
467                         read_unlock(&GlobalSMBSeslock);
468                         cFYI(1,("No matching file for oplock break on connection"));
469                         return TRUE;
470                 }
471         }
472         read_unlock(&GlobalSMBSeslock);
473         cFYI(1,("Can not process oplock break for non-existent connection"));
474         return TRUE;
475 }
476
477 void
478 dump_smb(struct smb_hdr *smb_buf, int smb_buf_length)
479 {
480         int i, j;
481         char debug_line[17];
482         unsigned char *buffer;
483
484         if (traceSMB == 0)
485                 return;
486
487         buffer = (unsigned char *) smb_buf;
488         for (i = 0, j = 0; i < smb_buf_length; i++, j++) {
489                 if (i % 8 == 0) {       /* we have reached the beginning of line  */
490                         printk(KERN_DEBUG "| ");
491                         j = 0;
492                 }
493                 printk("%0#4x ", buffer[i]);
494                 debug_line[2 * j] = ' ';
495                 if (isprint(buffer[i]))
496                         debug_line[1 + (2 * j)] = buffer[i];
497                 else
498                         debug_line[1 + (2 * j)] = '_';
499
500                 if (i % 8 == 7) {       /* we have reached end of line, time to print ascii */
501                         debug_line[16] = 0;
502                         printk(" | %s\n", debug_line);
503                 }
504         }
505         for (; j < 8; j++) {
506                 printk("     ");
507                 debug_line[2 * j] = ' ';
508                 debug_line[1 + (2 * j)] = ' ';
509         }
510         printk( " | %s\n", debug_line);
511         return;
512 }