4 * Copyright (C) International Business Machines Corp., 2002,2004
5 * Author(s): Steve French (sfrench@us.ibm.com)
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.
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.
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
22 #include <linux/slab.h>
23 #include <linux/ctype.h>
24 #include <linux/mempool.h>
27 #include "cifsproto.h"
28 #include "cifs_debug.h"
32 extern mempool_t
*cifs_sm_req_poolp
;
33 extern mempool_t
*cifs_req_poolp
;
34 extern struct task_struct
* oplockThread
;
36 static __u16 GlobalMid
; /* multiplex id - rotating counter */
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 */
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
);
59 _FreeXid(unsigned int xid
)
61 spin_lock(&GlobalMid_Lock
);
62 /* if(GlobalTotalActiveXid == 0)
64 GlobalTotalActiveXid
--;
65 spin_unlock(&GlobalMid_Lock
);
71 struct cifsSesInfo
*ret_buf
;
74 (struct cifsSesInfo
*) kmalloc(sizeof (struct cifsSesInfo
),
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
);
89 sesInfoFree(struct cifsSesInfo
*buf_to_free
)
91 if (buf_to_free
== NULL
) {
92 cFYI(1, ("Null buffer passed to sesInfoFree"));
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
);
111 struct cifsTconInfo
*
114 struct cifsTconInfo
*ret_buf
;
116 (struct cifsTconInfo
*) kmalloc(sizeof (struct cifsTconInfo
),
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
);
130 write_unlock(&GlobalSMBSeslock
);
136 tconInfoFree(struct cifsTconInfo
*buf_to_free
)
138 if (buf_to_free
== NULL
) {
139 cFYI(1, ("Null buffer passed to tconInfoFree"));
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
);
154 struct smb_hdr
*ret_buf
= NULL
;
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 */
161 (struct smb_hdr
*) mempool_alloc(cifs_req_poolp
, SLAB_KERNEL
| SLAB_NOFS
);
163 /* clear the first few header bytes */
164 /* for most paths, more is cleared in header_assemble */
166 memset(ret_buf
, 0, sizeof(struct smb_hdr
) + 3);
167 atomic_inc(&bufAllocCount
);
174 cifs_buf_release(void *buf_to_free
)
177 if (buf_to_free
== NULL
) {
178 /* cFYI(1, ("Null buffer passed to cifs_buf_release"));*/
181 mempool_free(buf_to_free
,cifs_req_poolp
);
183 atomic_dec(&bufAllocCount
);
188 cifs_small_buf_get(void)
190 struct smb_hdr
*ret_buf
= NULL
;
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 */
197 (struct smb_hdr
*) mempool_alloc(cifs_sm_req_poolp
, SLAB_KERNEL
| SLAB_NOFS
);
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
);
207 cifs_small_buf_release(void *buf_to_free
)
210 if (buf_to_free
== NULL
) {
211 cFYI(1, ("Null buffer passed to cifs_small_buf_release"));
214 mempool_free(buf_to_free
,cifs_sm_req_poolp
);
216 atomic_dec(&smBufAllocCount
);
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 */)
225 struct list_head
* temp_item
;
226 struct cifsSesInfo
* ses
;
227 char *temp
= (char *) buffer
;
229 memset(temp
,0,MAX_CIFS_HDR_SIZE
);
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 */
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
);
248 buffer
->Mid
= GlobalMid
;
249 spin_unlock(&GlobalMid_Lock
);
251 buffer
->Tid
= treeCon
->tid
;
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
;
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*/
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. */
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
;
296 /* BB eventually call cifs_setup_session here */
297 cFYI(1,("local UID found but smb sess with this server does not exist"));
301 read_unlock(&GlobalSMBSeslock
);
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
;
313 /* endian conversion of flags is now done just before sending */
314 buffer
->WordCount
= (char) word_count
;
319 checkSMBhdr(struct smb_hdr
*smb
, __u16 mid
)
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)) &&
325 if(smb
->Flags
& SMBFLG_RESPONSE
)
328 /* only one valid case where server sends us request */
329 if(smb
->Command
== SMB_COM_LOCKING_ANDX
)
332 cERROR(1, ("Rcvd Request not response "));
334 } else { /* bad signature or mid */
335 if (*(__le32
*) smb
->Protocol
!= cpu_to_le32(0x424d53ff))
337 ("Bad protocol string signature header %x ",
338 *(unsigned int *) smb
->Protocol
));
340 cERROR(1, ("Mids do not match"));
342 cERROR(1, ("bad smb detected. The Mid=%d", smb
->Mid
));
347 checkSMB(struct smb_hdr
*smb
, __u16 mid
, int length
)
349 __u32 len
= be32_to_cpu(smb
->smb_buf_length
);
351 ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
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)) {
360 return 0; /* some error cases do not return wct and bcc */
362 cERROR(1, ("Length less than smb header size"));
366 if (len
> CIFSMaxBufSize
+ MAX_CIFS_HDR_SIZE
- 4)
368 ("smb_buf_length greater than MaxBufSize"));
370 ("bad smb detected. Illegal length. The mid=%d",
375 if (checkSMBhdr(smb
, mid
))
378 if ((4 + len
!= smbCalcSize(smb
))
379 || (4 + len
!= (unsigned int)length
)) {
382 cERROR(1, ("smbCalcSize %x ", smbCalcSize(smb
)));
384 ("bad smb size detected. The Mid=%d", smb
->Mid
));
389 is_valid_oplock_break(struct smb_hdr
*buf
)
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
;
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
);
407 pnotify
= (struct file_notify_information
*)((char *)&pSMBr
->hdr
.Protocol
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); */
414 if(pSMBr
->hdr
.Status
.CifsError
) {
415 cFYI(1,("notify err 0x%d",pSMBr
->hdr
.Status
.CifsError
));
420 if(pSMB
->hdr
.Command
!= SMB_COM_LOCKING_ANDX
)
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"));
431 } else if (ERRbadfid
==
432 le16_to_cpu(pSMB
->hdr
.Status
.DosError
.Error
)) {
435 return FALSE
; /* on valid oplock brk we get "request" */
438 if(pSMB
->hdr
.WordCount
!= 8)
441 cFYI(1,(" oplock type 0x%d level 0x%d",pSMB
->LockType
,pSMB
->OplockLevel
));
442 if(!(pSMB
->LockType
& LOCKING_ANDX_OPLOCK_RELEASE
))
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
);
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"));
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
);
471 read_unlock(&GlobalSMBSeslock
);
472 cFYI(1,("No matching file for oplock break on connection"));
476 read_unlock(&GlobalSMBSeslock
);
477 cFYI(1,("Can not process oplock break for non-existent connection"));
482 dump_smb(struct smb_hdr
*smb_buf
, int smb_buf_length
)
486 unsigned char *buffer
;
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
"| ");
497 printk("%0#4x ", buffer
[i
]);
498 debug_line
[2 * j
] = ' ';
499 if (isprint(buffer
[i
]))
500 debug_line
[1 + (2 * j
)] = buffer
[i
];
502 debug_line
[1 + (2 * j
)] = '_';
504 if (i
% 8 == 7) { /* we have reached end of line, time to print ascii */
506 printk(" | %s\n", debug_line
);
511 debug_line
[2 * j
] = ' ';
512 debug_line
[1 + (2 * j
)] = ' ';
514 printk( " | %s\n", debug_line
);