diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /fs/cifs/misc.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'fs/cifs/misc.c')
-rw-r--r-- | fs/cifs/misc.c | 269 |
1 files changed, 168 insertions, 101 deletions
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 3a00c0d0cea..7c169339259 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -29,9 +29,6 @@ | |||
29 | #include "smberr.h" | 29 | #include "smberr.h" |
30 | #include "nterr.h" | 30 | #include "nterr.h" |
31 | #include "cifs_unicode.h" | 31 | #include "cifs_unicode.h" |
32 | #ifdef CONFIG_CIFS_SMB2 | ||
33 | #include "smb2pdu.h" | ||
34 | #endif | ||
35 | 32 | ||
36 | extern mempool_t *cifs_sm_req_poolp; | 33 | extern mempool_t *cifs_sm_req_poolp; |
37 | extern mempool_t *cifs_req_poolp; | 34 | extern mempool_t *cifs_req_poolp; |
@@ -43,7 +40,7 @@ extern mempool_t *cifs_req_poolp; | |||
43 | since the cifs fs was mounted */ | 40 | since the cifs fs was mounted */ |
44 | 41 | ||
45 | unsigned int | 42 | unsigned int |
46 | _get_xid(void) | 43 | _GetXid(void) |
47 | { | 44 | { |
48 | unsigned int xid; | 45 | unsigned int xid; |
49 | 46 | ||
@@ -61,7 +58,7 @@ _get_xid(void) | |||
61 | } | 58 | } |
62 | 59 | ||
63 | void | 60 | void |
64 | _free_xid(unsigned int xid) | 61 | _FreeXid(unsigned int xid) |
65 | { | 62 | { |
66 | spin_lock(&GlobalMid_Lock); | 63 | spin_lock(&GlobalMid_Lock); |
67 | /* if (GlobalTotalActiveXid == 0) | 64 | /* if (GlobalTotalActiveXid == 0) |
@@ -146,27 +143,17 @@ struct smb_hdr * | |||
146 | cifs_buf_get(void) | 143 | cifs_buf_get(void) |
147 | { | 144 | { |
148 | struct smb_hdr *ret_buf = NULL; | 145 | struct smb_hdr *ret_buf = NULL; |
149 | size_t buf_size = sizeof(struct smb_hdr); | 146 | |
150 | 147 | /* We could use negotiated size instead of max_msgsize - | |
151 | #ifdef CONFIG_CIFS_SMB2 | 148 | but it may be more efficient to always alloc same size |
152 | /* | 149 | albeit slightly larger than necessary and maxbuffersize |
153 | * SMB2 header is bigger than CIFS one - no problems to clean some | 150 | defaults to this and can not be bigger */ |
154 | * more bytes for CIFS. | ||
155 | */ | ||
156 | buf_size = sizeof(struct smb2_hdr); | ||
157 | #endif | ||
158 | /* | ||
159 | * We could use negotiated size instead of max_msgsize - | ||
160 | * but it may be more efficient to always alloc same size | ||
161 | * albeit slightly larger than necessary and maxbuffersize | ||
162 | * defaults to this and can not be bigger. | ||
163 | */ | ||
164 | ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); | 151 | ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS); |
165 | 152 | ||
166 | /* clear the first few header bytes */ | 153 | /* clear the first few header bytes */ |
167 | /* for most paths, more is cleared in header_assemble */ | 154 | /* for most paths, more is cleared in header_assemble */ |
168 | if (ret_buf) { | 155 | if (ret_buf) { |
169 | memset(ret_buf, 0, buf_size + 3); | 156 | memset(ret_buf, 0, sizeof(struct smb_hdr) + 3); |
170 | atomic_inc(&bufAllocCount); | 157 | atomic_inc(&bufAllocCount); |
171 | #ifdef CONFIG_CIFS_STATS2 | 158 | #ifdef CONFIG_CIFS_STATS2 |
172 | atomic_inc(&totBufAllocCount); | 159 | atomic_inc(&totBufAllocCount); |
@@ -225,6 +212,85 @@ cifs_small_buf_release(void *buf_to_free) | |||
225 | return; | 212 | return; |
226 | } | 213 | } |
227 | 214 | ||
215 | /* | ||
216 | Find a free multiplex id (SMB mid). Otherwise there could be | ||
217 | mid collisions which might cause problems, demultiplexing the | ||
218 | wrong response to this request. Multiplex ids could collide if | ||
219 | one of a series requests takes much longer than the others, or | ||
220 | if a very large number of long lived requests (byte range | ||
221 | locks or FindNotify requests) are pending. No more than | ||
222 | 64K-1 requests can be outstanding at one time. If no | ||
223 | mids are available, return zero. A future optimization | ||
224 | could make the combination of mids and uid the key we use | ||
225 | to demultiplex on (rather than mid alone). | ||
226 | In addition to the above check, the cifs demultiplex | ||
227 | code already used the command code as a secondary | ||
228 | check of the frame and if signing is negotiated the | ||
229 | response would be discarded if the mid were the same | ||
230 | but the signature was wrong. Since the mid is not put in the | ||
231 | pending queue until later (when it is about to be dispatched) | ||
232 | we do have to limit the number of outstanding requests | ||
233 | to somewhat less than 64K-1 although it is hard to imagine | ||
234 | so many threads being in the vfs at one time. | ||
235 | */ | ||
236 | __u16 GetNextMid(struct TCP_Server_Info *server) | ||
237 | { | ||
238 | __u16 mid = 0; | ||
239 | __u16 last_mid; | ||
240 | bool collision; | ||
241 | |||
242 | spin_lock(&GlobalMid_Lock); | ||
243 | last_mid = server->CurrentMid; /* we do not want to loop forever */ | ||
244 | server->CurrentMid++; | ||
245 | /* This nested loop looks more expensive than it is. | ||
246 | In practice the list of pending requests is short, | ||
247 | fewer than 50, and the mids are likely to be unique | ||
248 | on the first pass through the loop unless some request | ||
249 | takes longer than the 64 thousand requests before it | ||
250 | (and it would also have to have been a request that | ||
251 | did not time out) */ | ||
252 | while (server->CurrentMid != last_mid) { | ||
253 | struct mid_q_entry *mid_entry; | ||
254 | unsigned int num_mids; | ||
255 | |||
256 | collision = false; | ||
257 | if (server->CurrentMid == 0) | ||
258 | server->CurrentMid++; | ||
259 | |||
260 | num_mids = 0; | ||
261 | list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { | ||
262 | ++num_mids; | ||
263 | if (mid_entry->mid == server->CurrentMid && | ||
264 | mid_entry->midState == MID_REQUEST_SUBMITTED) { | ||
265 | /* This mid is in use, try a different one */ | ||
266 | collision = true; | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * if we have more than 32k mids in the list, then something | ||
273 | * is very wrong. Possibly a local user is trying to DoS the | ||
274 | * box by issuing long-running calls and SIGKILL'ing them. If | ||
275 | * we get to 2^16 mids then we're in big trouble as this | ||
276 | * function could loop forever. | ||
277 | * | ||
278 | * Go ahead and assign out the mid in this situation, but force | ||
279 | * an eventual reconnect to clean out the pending_mid_q. | ||
280 | */ | ||
281 | if (num_mids > 32768) | ||
282 | server->tcpStatus = CifsNeedReconnect; | ||
283 | |||
284 | if (!collision) { | ||
285 | mid = server->CurrentMid; | ||
286 | break; | ||
287 | } | ||
288 | server->CurrentMid++; | ||
289 | } | ||
290 | spin_unlock(&GlobalMid_Lock); | ||
291 | return mid; | ||
292 | } | ||
293 | |||
228 | /* NB: MID can not be set if treeCon not passed in, in that | 294 | /* NB: MID can not be set if treeCon not passed in, in that |
229 | case it is responsbility of caller to set the mid */ | 295 | case it is responsbility of caller to set the mid */ |
230 | void | 296 | void |
@@ -232,6 +298,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
232 | const struct cifs_tcon *treeCon, int word_count | 298 | const struct cifs_tcon *treeCon, int word_count |
233 | /* length of fixed section (word count) in two byte units */) | 299 | /* length of fixed section (word count) in two byte units */) |
234 | { | 300 | { |
301 | struct list_head *temp_item; | ||
302 | struct cifs_ses *ses; | ||
235 | char *temp = (char *) buffer; | 303 | char *temp = (char *) buffer; |
236 | 304 | ||
237 | memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ | 305 | memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ |
@@ -260,7 +328,52 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
260 | 328 | ||
261 | /* Uid is not converted */ | 329 | /* Uid is not converted */ |
262 | buffer->Uid = treeCon->ses->Suid; | 330 | buffer->Uid = treeCon->ses->Suid; |
263 | buffer->Mid = get_next_mid(treeCon->ses->server); | 331 | buffer->Mid = GetNextMid(treeCon->ses->server); |
332 | if (multiuser_mount != 0) { | ||
333 | /* For the multiuser case, there are few obvious technically */ | ||
334 | /* possible mechanisms to match the local linux user (uid) */ | ||
335 | /* to a valid remote smb user (smb_uid): */ | ||
336 | /* 1) Query Winbind (or other local pam/nss daemon */ | ||
337 | /* for userid/password/logon_domain or credential */ | ||
338 | /* 2) Query Winbind for uid to sid to username mapping */ | ||
339 | /* and see if we have a matching password for existing*/ | ||
340 | /* session for that user perhas getting password by */ | ||
341 | /* adding a new pam_cifs module that stores passwords */ | ||
342 | /* so that the cifs vfs can get at that for all logged*/ | ||
343 | /* on users */ | ||
344 | /* 3) (Which is the mechanism we have chosen) */ | ||
345 | /* Search through sessions to the same server for a */ | ||
346 | /* a match on the uid that was passed in on mount */ | ||
347 | /* with the current processes uid (or euid?) and use */ | ||
348 | /* that smb uid. If no existing smb session for */ | ||
349 | /* that uid found, use the default smb session ie */ | ||
350 | /* the smb session for the volume mounted which is */ | ||
351 | /* the same as would be used if the multiuser mount */ | ||
352 | /* flag were disabled. */ | ||
353 | |||
354 | /* BB Add support for establishing new tCon and SMB Session */ | ||
355 | /* with userid/password pairs found on the smb session */ | ||
356 | /* for other target tcp/ip addresses BB */ | ||
357 | if (current_fsuid() != treeCon->ses->linux_uid) { | ||
358 | cFYI(1, "Multiuser mode and UID " | ||
359 | "did not match tcon uid"); | ||
360 | spin_lock(&cifs_tcp_ses_lock); | ||
361 | list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { | ||
362 | ses = list_entry(temp_item, struct cifs_ses, smb_ses_list); | ||
363 | if (ses->linux_uid == current_fsuid()) { | ||
364 | if (ses->server == treeCon->ses->server) { | ||
365 | cFYI(1, "found matching uid substitute right smb_uid"); | ||
366 | buffer->Uid = ses->Suid; | ||
367 | break; | ||
368 | } else { | ||
369 | /* BB eventually call cifs_setup_session here */ | ||
370 | cFYI(1, "local UID found but no smb sess with this server exists"); | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | spin_unlock(&cifs_tcp_ses_lock); | ||
375 | } | ||
376 | } | ||
264 | } | 377 | } |
265 | if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) | 378 | if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) |
266 | buffer->Flags2 |= SMBFLG2_DFS; | 379 | buffer->Flags2 |= SMBFLG2_DFS; |
@@ -307,24 +420,19 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid) | |||
307 | } | 420 | } |
308 | 421 | ||
309 | int | 422 | int |
310 | checkSMB(char *buf, unsigned int total_read) | 423 | checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) |
311 | { | 424 | { |
312 | struct smb_hdr *smb = (struct smb_hdr *)buf; | 425 | __u32 len = be32_to_cpu(smb->smb_buf_length); |
313 | __u16 mid = smb->Mid; | ||
314 | __u32 rfclen = be32_to_cpu(smb->smb_buf_length); | ||
315 | __u32 clc_len; /* calculated length */ | 426 | __u32 clc_len; /* calculated length */ |
316 | cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", | 427 | cFYI(0, "checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len); |
317 | total_read, rfclen); | ||
318 | 428 | ||
319 | /* is this frame too small to even get to a BCC? */ | 429 | if (length < 2 + sizeof(struct smb_hdr)) { |
320 | if (total_read < 2 + sizeof(struct smb_hdr)) { | 430 | if ((length >= sizeof(struct smb_hdr) - 1) |
321 | if ((total_read >= sizeof(struct smb_hdr) - 1) | ||
322 | && (smb->Status.CifsError != 0)) { | 431 | && (smb->Status.CifsError != 0)) { |
323 | /* it's an error return */ | ||
324 | smb->WordCount = 0; | 432 | smb->WordCount = 0; |
325 | /* some error cases do not return wct and bcc */ | 433 | /* some error cases do not return wct and bcc */ |
326 | return 0; | 434 | return 0; |
327 | } else if ((total_read == sizeof(struct smb_hdr) + 1) && | 435 | } else if ((length == sizeof(struct smb_hdr) + 1) && |
328 | (smb->WordCount == 0)) { | 436 | (smb->WordCount == 0)) { |
329 | char *tmp = (char *)smb; | 437 | char *tmp = (char *)smb; |
330 | /* Need to work around a bug in two servers here */ | 438 | /* Need to work around a bug in two servers here */ |
@@ -344,35 +452,39 @@ checkSMB(char *buf, unsigned int total_read) | |||
344 | } else { | 452 | } else { |
345 | cERROR(1, "Length less than smb header size"); | 453 | cERROR(1, "Length less than smb header size"); |
346 | } | 454 | } |
347 | return -EIO; | 455 | return 1; |
456 | } | ||
457 | if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { | ||
458 | cERROR(1, "smb length greater than MaxBufSize, mid=%d", | ||
459 | smb->Mid); | ||
460 | return 1; | ||
348 | } | 461 | } |
349 | 462 | ||
350 | /* otherwise, there is enough to get to the BCC */ | ||
351 | if (check_smb_hdr(smb, mid)) | 463 | if (check_smb_hdr(smb, mid)) |
352 | return -EIO; | 464 | return 1; |
353 | clc_len = smbCalcSize(smb); | 465 | clc_len = smbCalcSize(smb); |
354 | 466 | ||
355 | if (4 + rfclen != total_read) { | 467 | if (4 + len != length) { |
356 | cERROR(1, "Length read does not match RFC1001 length %d", | 468 | cERROR(1, "Length read does not match RFC1001 length %d", |
357 | rfclen); | 469 | len); |
358 | return -EIO; | 470 | return 1; |
359 | } | 471 | } |
360 | 472 | ||
361 | if (4 + rfclen != clc_len) { | 473 | if (4 + len != clc_len) { |
362 | /* check if bcc wrapped around for large read responses */ | 474 | /* check if bcc wrapped around for large read responses */ |
363 | if ((rfclen > 64 * 1024) && (rfclen > clc_len)) { | 475 | if ((len > 64 * 1024) && (len > clc_len)) { |
364 | /* check if lengths match mod 64K */ | 476 | /* check if lengths match mod 64K */ |
365 | if (((4 + rfclen) & 0xFFFF) == (clc_len & 0xFFFF)) | 477 | if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF)) |
366 | return 0; /* bcc wrapped */ | 478 | return 0; /* bcc wrapped */ |
367 | } | 479 | } |
368 | cFYI(1, "Calculated size %u vs length %u mismatch for mid=%u", | 480 | cFYI(1, "Calculated size %u vs length %u mismatch for mid=%u", |
369 | clc_len, 4 + rfclen, smb->Mid); | 481 | clc_len, 4 + len, smb->Mid); |
370 | 482 | ||
371 | if (4 + rfclen < clc_len) { | 483 | if (4 + len < clc_len) { |
372 | cERROR(1, "RFC1001 size %u smaller than SMB for mid=%u", | 484 | cERROR(1, "RFC1001 size %u smaller than SMB for mid=%u", |
373 | rfclen, smb->Mid); | 485 | len, smb->Mid); |
374 | return -EIO; | 486 | return 1; |
375 | } else if (rfclen > clc_len + 512) { | 487 | } else if (len > clc_len + 512) { |
376 | /* | 488 | /* |
377 | * Some servers (Windows XP in particular) send more | 489 | * Some servers (Windows XP in particular) send more |
378 | * data than the lengths in the SMB packet would | 490 | * data than the lengths in the SMB packet would |
@@ -383,17 +495,16 @@ checkSMB(char *buf, unsigned int total_read) | |||
383 | * data to 512 bytes. | 495 | * data to 512 bytes. |
384 | */ | 496 | */ |
385 | cERROR(1, "RFC1001 size %u more than 512 bytes larger " | 497 | cERROR(1, "RFC1001 size %u more than 512 bytes larger " |
386 | "than SMB for mid=%u", rfclen, smb->Mid); | 498 | "than SMB for mid=%u", len, smb->Mid); |
387 | return -EIO; | 499 | return 1; |
388 | } | 500 | } |
389 | } | 501 | } |
390 | return 0; | 502 | return 0; |
391 | } | 503 | } |
392 | 504 | ||
393 | bool | 505 | bool |
394 | is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | 506 | is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) |
395 | { | 507 | { |
396 | struct smb_hdr *buf = (struct smb_hdr *)buffer; | ||
397 | struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; | 508 | struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; |
398 | struct list_head *tmp, *tmp1, *tmp2; | 509 | struct list_head *tmp, *tmp1, *tmp2; |
399 | struct cifs_ses *ses; | 510 | struct cifs_ses *ses; |
@@ -461,12 +572,12 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
461 | if (tcon->tid != buf->Tid) | 572 | if (tcon->tid != buf->Tid) |
462 | continue; | 573 | continue; |
463 | 574 | ||
464 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 575 | cifs_stats_inc(&tcon->num_oplock_brks); |
465 | spin_lock(&cifs_file_list_lock); | 576 | spin_lock(&cifs_file_list_lock); |
466 | list_for_each(tmp2, &tcon->openFileList) { | 577 | list_for_each(tmp2, &tcon->openFileList) { |
467 | netfile = list_entry(tmp2, struct cifsFileInfo, | 578 | netfile = list_entry(tmp2, struct cifsFileInfo, |
468 | tlist); | 579 | tlist); |
469 | if (pSMB->Fid != netfile->fid.netfid) | 580 | if (pSMB->Fid != netfile->netfid) |
470 | continue; | 581 | continue; |
471 | 582 | ||
472 | cFYI(1, "file id match, oplock break"); | 583 | cFYI(1, "file id match, oplock break"); |
@@ -474,7 +585,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
474 | 585 | ||
475 | cifs_set_oplock_level(pCifsInode, | 586 | cifs_set_oplock_level(pCifsInode, |
476 | pSMB->OplockLevel ? OPLOCK_READ : 0); | 587 | pSMB->OplockLevel ? OPLOCK_READ : 0); |
477 | queue_work(cifsiod_wq, | 588 | queue_work(system_nrt_wq, |
478 | &netfile->oplock_break); | 589 | &netfile->oplock_break); |
479 | netfile->oplock_break_cancelled = false; | 590 | netfile->oplock_break_cancelled = false; |
480 | 591 | ||
@@ -494,15 +605,16 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
494 | } | 605 | } |
495 | 606 | ||
496 | void | 607 | void |
497 | dump_smb(void *buf, int smb_buf_length) | 608 | dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) |
498 | { | 609 | { |
499 | int i, j; | 610 | int i, j; |
500 | char debug_line[17]; | 611 | char debug_line[17]; |
501 | unsigned char *buffer = buf; | 612 | unsigned char *buffer; |
502 | 613 | ||
503 | if (traceSMB == 0) | 614 | if (traceSMB == 0) |
504 | return; | 615 | return; |
505 | 616 | ||
617 | buffer = (unsigned char *) smb_buf; | ||
506 | for (i = 0, j = 0; i < smb_buf_length; i++, j++) { | 618 | for (i = 0, j = 0; i < smb_buf_length; i++, j++) { |
507 | if (i % 8 == 0) { | 619 | if (i % 8 == 0) { |
508 | /* have reached the beginning of line */ | 620 | /* have reached the beginning of line */ |
@@ -564,48 +676,3 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) | |||
564 | cinode->clientCanCacheRead = false; | 676 | cinode->clientCanCacheRead = false; |
565 | } | 677 | } |
566 | } | 678 | } |
567 | |||
568 | bool | ||
569 | backup_cred(struct cifs_sb_info *cifs_sb) | ||
570 | { | ||
571 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { | ||
572 | if (cifs_sb->mnt_backupuid == current_fsuid()) | ||
573 | return true; | ||
574 | } | ||
575 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { | ||
576 | if (in_group_p(cifs_sb->mnt_backupgid)) | ||
577 | return true; | ||
578 | } | ||
579 | |||
580 | return false; | ||
581 | } | ||
582 | |||
583 | void | ||
584 | cifs_del_pending_open(struct cifs_pending_open *open) | ||
585 | { | ||
586 | spin_lock(&cifs_file_list_lock); | ||
587 | list_del(&open->olist); | ||
588 | spin_unlock(&cifs_file_list_lock); | ||
589 | } | ||
590 | |||
591 | void | ||
592 | cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink, | ||
593 | struct cifs_pending_open *open) | ||
594 | { | ||
595 | #ifdef CONFIG_CIFS_SMB2 | ||
596 | memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE); | ||
597 | #endif | ||
598 | open->oplock = CIFS_OPLOCK_NO_CHANGE; | ||
599 | open->tlink = tlink; | ||
600 | fid->pending_open = open; | ||
601 | list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens); | ||
602 | } | ||
603 | |||
604 | void | ||
605 | cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, | ||
606 | struct cifs_pending_open *open) | ||
607 | { | ||
608 | spin_lock(&cifs_file_list_lock); | ||
609 | cifs_add_pending_open_locked(fid, tlink, open); | ||
610 | spin_unlock(&cifs_file_list_lock); | ||
611 | } | ||