afslogon-20040714
[openafs.git] / src / WINNT / afsd / smb3.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #define SECURITY_WIN32
16 #include <security.h>
17 #endif /* !DJGPP */
18 #include <stdlib.h>
19 #include <malloc.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <time.h>
23
24 #include <osi.h>
25
26 #include "afsd.h"
27
28 #include "smb.h"
29
30 extern osi_hyper_t hzero;
31
32 smb_packet_t *smb_Directory_Watches = NULL;
33 osi_mutex_t smb_Dir_Watch_Lock;
34
35 smb_tran2Dispatch_t smb_tran2DispatchTable[SMB_TRAN2_NOPCODES];
36
37 /* protected by the smb_globalLock */
38 smb_tran2Packet_t *smb_tran2AssemblyQueuep;
39
40 /* retrieve a held reference to a user structure corresponding to an incoming
41  * request */
42 cm_user_t *smb_GetTran2User(smb_vc_t *vcp, smb_tran2Packet_t *inp)
43 {
44         smb_user_t *uidp;
45     cm_user_t *up = NULL;
46         
47     uidp = smb_FindUID(vcp, inp->uid, 0);
48     if (!uidp) return NULL;
49         
50         lock_ObtainMutex(&uidp->mx);
51     if (uidp->unp) {
52         up = uidp->unp->userp;
53         cm_HoldUser(up);
54     }
55         lock_ReleaseMutex(&uidp->mx);
56
57     smb_ReleaseUID(uidp);
58
59     return up;
60 }
61
62 /*
63  * Return extended attributes.
64  * Right now, we aren't using any of the "new" bits, so this looks exactly
65  * like smb_Attributes() (see smb.c).
66  */
67 unsigned long smb_ExtAttributes(cm_scache_t *scp)
68 {
69         unsigned long attrs;
70
71         if (scp->fileType == CM_SCACHETYPE_DIRECTORY
72             || scp->fileType == CM_SCACHETYPE_MOUNTPOINT)
73                 attrs = SMB_ATTR_DIRECTORY;
74         else
75                 attrs = 0;
76         /*
77          * We used to mark a file RO if it was in an RO volume, but that
78          * turns out to be impolitic in NT.  See defect 10007.
79          */
80 #ifdef notdef
81         if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
82 #endif
83         if ((scp->unixModeBits & 0222) == 0)
84                 attrs |= SMB_ATTR_READONLY;             /* Read-only */
85
86         if (attrs == 0)
87                 attrs = SMB_ATTR_NORMAL;                /* FILE_ATTRIBUTE_NORMAL */
88
89         return attrs;
90 }
91
92 int smb_V3IsStarMask(char *maskp)
93 {
94     char tc;
95
96         while (tc = *maskp++)
97         if (tc == '?' || tc == '*') 
98             return 1;
99         return 0;
100 }
101
102 unsigned char *smb_ParseString(unsigned char *inp, char **chainpp)
103 {
104     if (chainpp) {
105                 /* skip over null-terminated string */
106                 *chainpp = inp + strlen(inp) + 1;
107     }
108     return inp;
109 }   
110
111 /*DEBUG do not checkin*/
112 void OutputDebugF(char * format, ...) {
113 #ifdef COMMENT
114     va_list args;
115     int len;
116     char * buffer;
117
118     va_start( args, format );
119     len = _vscprintf( format, args ) // _vscprintf doesn't count
120                                + 3; // terminating '\0' + '\n'
121     buffer = malloc( len * sizeof(char) );
122     vsprintf( buffer, format, args );
123     strcat(buffer, "\n");
124     OutputDebugString(buffer);
125     free( buffer );
126 #endif
127 }
128
129 void OutputDebugHexDump(unsigned char * buffer, int len) {
130 #ifdef COMMENT
131     int i,j,k;
132     char buf[256];
133     static char tr[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
134
135     OutputDebugF("Hexdump length [%d]",len);
136
137     for(i=0;i<len;i++) {
138         if(!(i%16)) {
139             if(i)
140                 OutputDebugString(buf);
141             sprintf(buf,"%5x",i);
142             memset(buf+5,' ',80);
143             buf[85] = 0;
144             strcat(buf,"\n");
145         }
146
147         j = (i%16);
148         j = j*3 + 7 + ((j>7)?1:0);
149         k = buffer[i];
150
151         buf[j] = tr[k / 16]; buf[j+1] = tr[k % 16];
152
153         j = (i%16);
154         j = j + 56 + ((j>7)?1:0);
155
156         buf[j] = (k>32 && k<127)?k:'.';
157     }    
158     if(i)
159         OutputDebugString(buf);
160 #endif
161 }   
162 /**/
163
164 #define SMB_EXT_SEC_PACKAGE_NAME "Negotiate"
165 void smb_NegotiateExtendedSecurity(void ** secBlob, int * secBlobLength){
166     SECURITY_STATUS status, istatus;
167         CredHandle creds;
168         TimeStamp expiry;
169         SecBufferDesc secOut;
170         SecBuffer secTok;
171         CtxtHandle ctx;
172         ULONG flags;
173
174         *secBlob = NULL;
175         *secBlobLength = 0;
176
177     OutputDebugF("Negotiating Extended Security");
178
179         status = AcquireCredentialsHandle(
180                 NULL,
181                 SMB_EXT_SEC_PACKAGE_NAME,
182                 SECPKG_CRED_INBOUND,
183                 NULL,
184                 NULL,
185                 NULL,
186                 NULL,
187                 &creds,
188                 &expiry);
189
190         if (status != SEC_E_OK) {
191                 /* Really bad. We return an empty security blob */
192                 OutputDebugF("AcquireCredentialsHandle failed with %lX", status);
193                 goto nes_0;
194         }
195
196         secOut.cBuffers = 1;
197         secOut.pBuffers = &secTok;
198         secOut.ulVersion = SECBUFFER_VERSION;
199
200         secTok.BufferType = SECBUFFER_TOKEN;
201         secTok.cbBuffer = 0;
202         secTok.pvBuffer = NULL;
203
204         ctx.dwLower = ctx.dwUpper = 0;
205
206         status = AcceptSecurityContext(
207                 &creds,
208                 NULL,
209                 NULL,
210         ASC_REQ_CONNECTION | ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY,
211                 SECURITY_NETWORK_DREP,
212                 &ctx,
213                 &secOut,
214                 &flags,
215                 &expiry
216                 );
217
218         if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
219                 OutputDebugF("Completing token...");
220                 istatus = CompleteAuthToken(&ctx, &secOut);
221         if ( istatus != SEC_E_OK )
222             OutputDebugF("Token completion failed: %x", istatus);
223         }
224
225         if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
226                 if (secTok.pvBuffer) {
227                         *secBlobLength = secTok.cbBuffer;
228                         *secBlob = malloc( secTok.cbBuffer );
229                         memcpy(*secBlob, secTok.pvBuffer, secTok.cbBuffer );
230                 }
231         } else {
232         if ( status != SEC_E_OK )
233             OutputDebugF("AcceptSecurityContext status != CONTINUE  %lX", status);
234     }
235
236         if (secTok.pvBuffer) FreeContextBuffer( secTok.pvBuffer );
237
238         /* Discard credentials handle.  We'll reacquire one when we get the session setup X */
239         FreeCredentialsHandle(&creds);
240
241   nes_0:
242
243     if (secBlob) {
244         OutputDebugF("Returning initial token:");
245         OutputDebugHexDump(*secBlob,*secBlobLength);
246     } else {
247         OutputDebugF("No initial token");
248     }
249         return;
250 }
251
252 struct smb_ext_context {
253         CredHandle creds;
254         CtxtHandle ctx;
255         int partialTokenLen;
256         void * partialToken;
257 };
258
259 long smb_AuthenticateUserExt(smb_vc_t * vcp, char * usern, char * secBlobIn, int secBlobInLength, char ** secBlobOut, int * secBlobOutLength) {
260     SECURITY_STATUS status, istatus;
261         CredHandle creds;
262         TimeStamp expiry;
263         long code = 0;
264         SecBufferDesc secBufIn;
265         SecBuffer secTokIn;
266         SecBufferDesc secBufOut;
267         SecBuffer secTokOut;
268         CtxtHandle ctx;
269         struct smb_ext_context * secCtx = NULL;
270         struct smb_ext_context * newSecCtx = NULL;
271         void * assembledBlob = NULL;
272         int assembledBlobLength = 0;
273         ULONG flags;
274
275         OutputDebugF("In smb_AuthenticateUserExt");
276
277         *secBlobOut = NULL;
278         *secBlobOutLength = 0;
279
280         if (vcp->flags & SMB_VCFLAG_AUTH_IN_PROGRESS) {
281                 secCtx = vcp->secCtx;
282                 lock_ObtainMutex(&vcp->mx);
283                 vcp->flags &= ~SMB_VCFLAG_AUTH_IN_PROGRESS;
284                 vcp->secCtx = NULL;
285                 lock_ReleaseMutex(&vcp->mx);
286         }
287
288     if (secBlobIn) {
289         OutputDebugF("Received incoming token:");
290         OutputDebugHexDump(secBlobIn,secBlobInLength);
291     }
292     
293     if (secCtx) {
294         OutputDebugF("Continuing with existing context.");              
295         creds = secCtx->creds;
296         ctx = secCtx->ctx;
297
298                 if (secCtx->partialToken) {
299                         assembledBlobLength = secCtx->partialTokenLen + secBlobInLength;
300                         assembledBlob = malloc(assembledBlobLength);
301             memcpy(assembledBlob,secCtx->partialToken, secCtx->partialTokenLen);
302                         memcpy(((BYTE *)assembledBlob) + secCtx->partialTokenLen, secBlobIn, secBlobInLength);
303                 }
304         } else {
305                 status = AcquireCredentialsHandle(
306                         NULL,
307                         SMB_EXT_SEC_PACKAGE_NAME,
308                         SECPKG_CRED_INBOUND,
309                         NULL,
310                         NULL,
311                         NULL,
312                         NULL,
313                         &creds,
314                         &expiry);
315
316                 if (status != SEC_E_OK) {
317                         OutputDebugF("Can't acquire Credentials handle [%lX]", status);
318                         code = CM_ERROR_BADPASSWORD; /* means "try again when I'm sober" */
319                         goto aue_0;
320                 }
321
322                 ctx.dwLower = 0;
323                 ctx.dwUpper = 0;
324         }
325
326     secBufIn.cBuffers = 1;
327         secBufIn.pBuffers = &secTokIn;
328         secBufIn.ulVersion = SECBUFFER_VERSION;
329
330         secTokIn.BufferType = SECBUFFER_TOKEN;
331         if (assembledBlob) {
332                 secTokIn.cbBuffer = assembledBlobLength;
333                 secTokIn.pvBuffer = assembledBlob;
334         } else {
335                 secTokIn.cbBuffer = secBlobInLength;
336                 secTokIn.pvBuffer = secBlobIn;
337         }
338
339         secBufOut.cBuffers = 1;
340         secBufOut.pBuffers = &secTokOut;
341         secBufOut.ulVersion = SECBUFFER_VERSION;
342
343         secTokOut.BufferType = SECBUFFER_TOKEN;
344         secTokOut.cbBuffer = 0;
345         secTokOut.pvBuffer = NULL;
346
347         status = AcceptSecurityContext(
348                 &creds,
349                 ((secCtx)?&ctx:NULL),
350                 &secBufIn,
351                 ASC_REQ_CONNECTION | ASC_REQ_EXTENDED_ERROR     | ASC_REQ_ALLOCATE_MEMORY,
352                 SECURITY_NETWORK_DREP,
353                 &ctx,
354                 &secBufOut,
355                 &flags,
356                 &expiry
357                 );
358
359         if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
360                 OutputDebugF("Completing token...");
361                 istatus = CompleteAuthToken(&ctx, &secBufOut);
362         if ( istatus != SEC_E_OK )
363             OutputDebugF("Token completion failed: %lX", istatus);
364         }
365
366         if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
367                 OutputDebugF("Continue needed");
368
369                 newSecCtx = malloc(sizeof(*newSecCtx));
370
371                 newSecCtx->creds = creds;
372                 newSecCtx->ctx = ctx;
373                 newSecCtx->partialToken = NULL;
374                 newSecCtx->partialTokenLen = 0;
375
376                 lock_ObtainMutex( &vcp->mx );
377                 vcp->flags |= SMB_VCFLAG_AUTH_IN_PROGRESS;
378                 vcp->secCtx = newSecCtx;
379                 lock_ReleaseMutex( &vcp->mx );
380
381                 code = CM_ERROR_GSSCONTINUE;
382         }
383
384         if ((status == SEC_I_COMPLETE_NEEDED || status == SEC_E_OK || 
385          status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) && 
386         secTokOut.pvBuffer) {
387                 OutputDebugF("Need to send token back to client");
388
389                 *secBlobOutLength = secTokOut.cbBuffer;
390                 *secBlobOut = malloc(secTokOut.cbBuffer);
391                 memcpy(*secBlobOut, secTokOut.pvBuffer, secTokOut.cbBuffer);
392
393         OutputDebugF("Outgoing token:");
394         OutputDebugHexDump(*secBlobOut,*secBlobOutLength);
395         } else if (status == SEC_E_INCOMPLETE_MESSAGE) {
396                 OutputDebugF("Incomplete message");
397
398                 newSecCtx = malloc(sizeof(*newSecCtx));
399
400                 newSecCtx->creds = creds;
401                 newSecCtx->ctx = ctx;
402                 newSecCtx->partialToken = malloc(secTokOut.cbBuffer);
403                 memcpy(newSecCtx->partialToken, secTokOut.pvBuffer, secTokOut.cbBuffer);
404                 newSecCtx->partialTokenLen = secTokOut.cbBuffer;
405
406                 lock_ObtainMutex( &vcp->mx );
407                 vcp->flags |= SMB_VCFLAG_AUTH_IN_PROGRESS;
408                 vcp->secCtx = newSecCtx;
409                 lock_ReleaseMutex( &vcp->mx );
410
411                 code = CM_ERROR_GSSCONTINUE;
412         }
413
414         if (status == SEC_E_OK || status == SEC_I_COMPLETE_NEEDED) {
415                 /* woo hoo! */
416                 SecPkgContext_Names names;
417
418                 OutputDebugF("Authentication completed");
419         OutputDebugF("Returned flags : [%lX]", flags);
420
421                 if (!QueryContextAttributes(&ctx, SECPKG_ATTR_NAMES, &names)) {
422             OutputDebugF("Received name [%s]", names.sUserName);
423             strcpy(usern, names.sUserName);
424             strlwr(usern); /* in tandem with smb_GetNormalizedUsername */
425             FreeContextBuffer(names.sUserName);
426         } else {
427             /* Force the user to retry if the context is invalid */
428             OutputDebugF("QueryContextAttributes Names failed [%x]", GetLastError());
429             code = CM_ERROR_BADPASSWORD; 
430         }
431         } else if (!code) {
432         switch ( status ) {
433         case SEC_E_INVALID_TOKEN:
434             OutputDebugF("Returning bad password :: INVALID_TOKEN");
435             break;
436         case SEC_E_INVALID_HANDLE:
437             OutputDebugF("Returning bad password :: INVALID_HANDLE");
438             break;
439         case SEC_E_LOGON_DENIED:
440             OutputDebugF("Returning bad password :: LOGON_DENIED");
441             break;
442         case SEC_E_UNKNOWN_CREDENTIALS:
443             OutputDebugF("Returning bad password :: UNKNOWN_CREDENTIALS");
444             break;
445         case SEC_E_NO_CREDENTIALS:
446             OutputDebugF("Returning bad password :: NO_CREDENTIALS");
447             break;
448         case SEC_E_CONTEXT_EXPIRED:
449             OutputDebugF("Returning bad password :: CONTEXT_EXPIRED");
450             break;
451         case SEC_E_INCOMPLETE_CREDENTIALS:
452             OutputDebugF("Returning bad password :: INCOMPLETE_CREDENTIALS");
453             break;
454         case SEC_E_WRONG_PRINCIPAL:
455             OutputDebugF("Returning bad password :: WRONG_PRINCIPAL");
456             break;
457         case SEC_E_TIME_SKEW:
458             OutputDebugF("Returning bad password :: TIME_SKEW");
459             break;
460         default:
461             OutputDebugF("Returning bad password :: Status == %lX", status);
462         }
463                 code = CM_ERROR_BADPASSWORD;
464         }
465
466         if (secCtx) {
467                 if (secCtx->partialToken) free(secCtx->partialToken);
468                 free(secCtx);
469         }
470
471         if (assembledBlob) {
472                 free(assembledBlob);
473         }
474
475         if (secTokOut.pvBuffer)
476                 FreeContextBuffer(secTokOut.pvBuffer);
477
478         if (code != CM_ERROR_GSSCONTINUE) {
479                 DeleteSecurityContext(&ctx);
480                 FreeCredentialsHandle(&creds);
481         }
482
483   aue_0:
484         return code;
485 }
486
487 #define P_LEN 256
488 #define P_RESP_LEN 128
489
490 /* LsaLogonUser expects input parameters to be in a contiguous block of memory. 
491    So put stuff in a struct. */
492 struct Lm20AuthBlob {
493         MSV1_0_LM20_LOGON lmlogon;
494         BYTE ciResponse[P_RESP_LEN];    /* Unicode representation */
495         BYTE csResponse[P_RESP_LEN];    /* ANSI representation */
496         WCHAR accountNameW[P_LEN];
497         WCHAR primaryDomainW[P_LEN];
498         WCHAR workstationW[MAX_COMPUTERNAME_LENGTH + 1];
499         TOKEN_GROUPS tgroups;
500         TOKEN_SOURCE tsource;
501 };
502
503 long smb_AuthenticateUserLM(smb_vc_t *vcp, char * accountName, char * primaryDomain, char * ciPwd, unsigned ciPwdLength, char * csPwd, unsigned csPwdLength) {
504
505         NTSTATUS nts, ntsEx;
506         struct Lm20AuthBlob lmAuth;
507         PMSV1_0_LM20_LOGON_PROFILE lmprofilep;
508         QUOTA_LIMITS quotaLimits;
509         DWORD size;
510         ULONG lmprofilepSize;
511         LUID lmSession;
512         HANDLE lmToken;
513
514         OutputDebugF("In smb_AuthenticateUser for user [%s] domain [%s]", accountName, primaryDomain);
515         OutputDebugF("ciPwdLength is %d and csPwdLength is %d", ciPwdLength, csPwdLength);
516
517     OutputDebugF("csPassword:");
518     OutputDebugHexDump(csPwd,csPwdLength);
519     OutputDebugF("ciPassword:");
520     OutputDebugHexDump(ciPwd,ciPwdLength);
521
522         if (ciPwdLength > P_RESP_LEN || csPwdLength > P_RESP_LEN) {
523                 OutputDebugF("ciPwdLength or csPwdLength is too long");
524                 return CM_ERROR_BADPASSWORD;
525         }
526
527         memset(&lmAuth,0,sizeof(lmAuth));
528
529         lmAuth.lmlogon.MessageType = MsV1_0NetworkLogon;
530         
531         lmAuth.lmlogon.LogonDomainName.Buffer = lmAuth.primaryDomainW;
532         mbstowcs(lmAuth.primaryDomainW, primaryDomain, P_LEN);
533         lmAuth.lmlogon.LogonDomainName.Length = wcslen(lmAuth.primaryDomainW) * sizeof(WCHAR);
534         lmAuth.lmlogon.LogonDomainName.MaximumLength = P_LEN * sizeof(WCHAR);
535
536         lmAuth.lmlogon.UserName.Buffer = lmAuth.accountNameW;
537         mbstowcs(lmAuth.accountNameW, accountName, P_LEN);
538         lmAuth.lmlogon.UserName.Length = wcslen(lmAuth.accountNameW) * sizeof(WCHAR);
539         lmAuth.lmlogon.UserName.MaximumLength = P_LEN * sizeof(WCHAR);
540
541         lmAuth.lmlogon.Workstation.Buffer = lmAuth.workstationW;
542         lmAuth.lmlogon.Workstation.MaximumLength = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR);
543         size = MAX_COMPUTERNAME_LENGTH + 1;
544         GetComputerNameW(lmAuth.workstationW, &size);
545     lmAuth.lmlogon.Workstation.Length = wcslen(lmAuth.workstationW) * sizeof(WCHAR);
546
547         memcpy(lmAuth.lmlogon.ChallengeToClient, vcp->encKey, MSV1_0_CHALLENGE_LENGTH);
548
549         lmAuth.lmlogon.CaseInsensitiveChallengeResponse.Buffer = lmAuth.ciResponse;
550         lmAuth.lmlogon.CaseInsensitiveChallengeResponse.Length = ciPwdLength;
551         lmAuth.lmlogon.CaseInsensitiveChallengeResponse.MaximumLength = P_RESP_LEN;
552         memcpy(lmAuth.ciResponse, ciPwd, ciPwdLength);
553
554         lmAuth.lmlogon.CaseSensitiveChallengeResponse.Buffer = lmAuth.csResponse;
555         lmAuth.lmlogon.CaseSensitiveChallengeResponse.Length = csPwdLength;
556         lmAuth.lmlogon.CaseSensitiveChallengeResponse.MaximumLength = P_RESP_LEN;
557         memcpy(lmAuth.csResponse, csPwd, csPwdLength);
558
559         lmAuth.lmlogon.ParameterControl = 0;
560
561         lmAuth.tgroups.GroupCount = 0;
562         lmAuth.tgroups.Groups[0].Sid = NULL;
563         lmAuth.tgroups.Groups[0].Attributes = 0;
564
565         lmAuth.tsource.SourceIdentifier.HighPart = 0;
566         lmAuth.tsource.SourceIdentifier.LowPart = (DWORD) vcp;
567         strcpy(lmAuth.tsource.SourceName,"OpenAFS"); /* 8 char limit */
568
569         nts = LsaLogonUser(
570                 smb_lsaHandle,
571                 &smb_lsaLogonOrigin,
572                 Network, /*3*/
573         smb_lsaSecPackage,
574                 &lmAuth,
575                 sizeof(lmAuth),
576         &lmAuth.tgroups,
577                 &lmAuth.tsource,
578                 &lmprofilep,
579                 &lmprofilepSize,
580                 &lmSession,
581                 &lmToken,
582                 &quotaLimits,
583                 &ntsEx);
584
585         OutputDebugF("Return from LsaLogonUser is 0x%lX", nts);
586         OutputDebugF("Extended status is 0x%lX", ntsEx);
587
588         if (nts == ERROR_SUCCESS) {
589                 /* free the token */
590                 LsaFreeReturnBuffer(lmprofilep);
591         CloseHandle(lmToken);
592                 return 0;
593         } else {
594                 /* No AFS for you */
595                 if (nts == 0xC000015BL)
596                         return CM_ERROR_BADLOGONTYPE;
597                 else /* our catchall is a bad password though we could be more specific */
598                         return CM_ERROR_BADPASSWORD;
599         }
600 }
601
602 /* The buffer pointed to by usern is assumed to be at least SMB_MAX_USERNAME_LENGTH bytes */
603 long smb_GetNormalizedUsername(char * usern, const char * accountName, const char * domainName) {
604
605         char * atsign;
606         const char * domain;
607
608         /* check if we have sane input */
609         if ((strlen(accountName) + strlen(domainName) + 1) > SMB_MAX_USERNAME_LENGTH)
610                 return 1;
611
612         /* we could get : [accountName][domainName]
613            [user][domain]
614            [user@domain][]
615            [user][]/[user][?]
616            [][]/[][?] */
617
618         atsign = strchr(accountName, '@');
619
620         if (atsign) /* [user@domain][] -> [user@domain][domain] */
621                 domain = atsign + 1;
622         else
623                 domain = domainName;
624
625         /* if for some reason the client doesn't know what domain to use,
626                    it will either return an empty string or a '?' */
627         if (!domain[0] || domain[0] == '?')
628                 /* Empty domains and empty usernames are usually sent from tokenless contexts.
629                    This way such logins will get an empty username (easy to check).  I don't know 
630                    when a non-empty username would be supplied with an anonymous domain, but *shrug* */
631                 strcpy(usern,accountName);
632         else {
633                 /* TODO: what about WIN.MIT.EDU\user vs. WIN\user? */
634                 strcpy(usern,domain);
635                 strcat(usern,"\\");
636                 if (atsign)
637                         strncat(usern,accountName,atsign - accountName);
638                 else
639                         strcat(usern,accountName);
640         }
641
642         strlwr(usern);
643
644         return 0;
645 }
646
647 /* When using SMB auth, all SMB sessions have to pass through here first to
648  * authenticate the user. 
649  * Caveat: If not use the SMB auth the protocol does not require sending a
650  * session setup packet, which means that we can't rely on a UID in subsequent
651  * packets.  Though in practice we get one anyway.
652  */
653 long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
654 {
655     char *tp;
656     smb_user_t *uidp;
657     unsigned short newUid;
658     unsigned long caps = 0;
659     cm_user_t *userp;
660     smb_username_t *unp;
661     char *s1 = " ";
662     long code = 0; 
663     char usern[SMB_MAX_USERNAME_LENGTH];
664     char *secBlobOut = NULL;
665     int  secBlobOutLength = 0;
666
667     /* Check for bad conns */
668     if (vcp->flags & SMB_VCFLAG_REMOTECONN)
669         return CM_ERROR_REMOTECONN;
670
671     if (vcp->flags & SMB_VCFLAG_USENT) {
672         if (smb_authType == SMB_AUTH_EXTENDED) {
673             /* extended authentication */
674             char *secBlobIn;
675             int secBlobInLength;
676         
677             if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
678                 caps = smb_GetSMBParm(inp,10) | (((unsigned long) smb_GetSMBParm(inp,11)) << 16);
679             }
680
681             secBlobInLength = smb_GetSMBParm(inp, 7);
682             secBlobIn = smb_GetSMBData(inp, NULL);
683
684             code = smb_AuthenticateUserExt(vcp, usern, secBlobIn, secBlobInLength, &secBlobOut, &secBlobOutLength);
685
686             if (code == CM_ERROR_GSSCONTINUE) {
687                 smb_SetSMBParm(outp, 2, 0);
688                 smb_SetSMBParm(outp, 3, secBlobOutLength);
689                 smb_SetSMBDataLength(outp, secBlobOutLength + smb_ServerOSLength + smb_ServerLanManagerLength + smb_ServerDomainNameLength);
690                 tp = smb_GetSMBData(outp, NULL);
691                 if (secBlobOutLength) {
692                     memcpy(tp, secBlobOut, secBlobOutLength);
693                     free(secBlobOut);
694                     tp += secBlobOutLength;
695                 }       
696                 memcpy(tp,smb_ServerOS,smb_ServerOSLength);
697                 tp += smb_ServerOSLength;
698                 memcpy(tp,smb_ServerLanManager,smb_ServerLanManagerLength);
699                 tp += smb_ServerLanManagerLength;
700                 memcpy(tp,smb_ServerDomainName,smb_ServerDomainNameLength);
701                 tp += smb_ServerDomainNameLength;
702             }
703
704             /* TODO: handle return code and continue auth. Also free secBlobOut if applicable. */
705         } else {
706             unsigned ciPwdLength, csPwdLength;
707             char *ciPwd, *csPwd;
708             char *accountName;
709             char *primaryDomain;
710             int  datalen;
711
712             /* TODO: parse for extended auth as well */
713             ciPwdLength = smb_GetSMBParm(inp, 7); /* case insensitive password length */
714             csPwdLength = smb_GetSMBParm(inp, 8); /* case sensitive password length */
715
716             tp = smb_GetSMBData(inp, &datalen);
717
718             OutputDebugF("Session packet data size [%d]",datalen);
719
720             ciPwd = tp;
721             tp += ciPwdLength;
722             csPwd = tp;
723             tp += csPwdLength;
724
725             accountName = smb_ParseString(tp, &tp);
726             primaryDomain = smb_ParseString(tp, NULL);
727
728             if (smb_GetNormalizedUsername(usern, accountName, primaryDomain)) {
729                 /* shouldn't happen */
730                 code = CM_ERROR_BADSMB;
731                 goto after_read_packet;
732             }
733
734             /* capabilities are only valid for first session packet */
735             if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
736                 caps = smb_GetSMBParm(inp, 11) | (((unsigned long)smb_GetSMBParm(inp, 12)) << 16);
737             }
738
739             if (smb_authType == SMB_AUTH_NTLM) {
740                 code = smb_AuthenticateUserLM(vcp, accountName, primaryDomain, ciPwd, ciPwdLength, csPwd, csPwdLength);
741             }
742         }
743     }  else { /* V3 */
744         unsigned ciPwdLength;
745         char *ciPwd;
746                 char *accountName;
747                 char *primaryDomain;
748
749                 ciPwdLength = smb_GetSMBParm(inp, 7);
750         tp = smb_GetSMBData(inp, NULL);
751                 ciPwd = tp;
752                 tp += ciPwdLength;
753
754                 accountName = smb_ParseString(tp, &tp);
755                 primaryDomain = smb_ParseString(tp, NULL);
756
757                 if ( smb_GetNormalizedUsername(usern, accountName, primaryDomain)) {
758                         /* shouldn't happen */
759                         code = CM_ERROR_BADSMB;
760             goto after_read_packet;
761                 }
762
763         /* even if we wanted extended auth, if we only negotiated V3, we have to fallback
764          * to NTLM.
765          */
766                 if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
767                         code = smb_AuthenticateUserLM(vcp,accountName,primaryDomain,ciPwd,ciPwdLength,"",0);
768                 }
769         }
770
771   after_read_packet:
772         /* note down that we received a session setup X and set the capabilities flag */
773         if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
774                 lock_ObtainMutex(&vcp->mx);
775                 vcp->flags |= SMB_VCFLAG_SESSX_RCVD;
776         /* for the moment we can only deal with NTSTATUS */
777         if (caps & NTNEGOTIATE_CAPABILITY_NTSTATUS) {
778             vcp->flags |= SMB_VCFLAG_STATUS32;
779         }
780                 lock_ReleaseMutex(&vcp->mx);
781         }
782
783         /* code would be non-zero if there was an authentication failure.
784            Ideally we would like to invalidate the uid for this session or break
785            early to avoid accidently stealing someone else's tokens. */
786
787         if (code) {
788                 return code;
789         }
790
791         OutputDebugF("Received username=[%s]", usern);
792
793     /* On Windows 2000, this function appears to be called more often than
794        it is expected to be called. This resulted in multiple smb_user_t
795        records existing all for the same user session which results in all
796        of the users tokens disappearing.
797
798        To avoid this problem, we look for an existing smb_user_t record
799        based on the users name, and use that one if we find it.
800     */
801
802     uidp = smb_FindUserByNameThisSession(vcp, usern);
803     if (uidp) {   /* already there, so don't create a new one */
804         unp = uidp->unp;
805         userp = unp->userp;
806         newUid = (unsigned short)uidp->userID;  /* For some reason these are different types!*/
807                 osi_LogEvent("AFS smb_ReceiveV3SessionSetupX",NULL,"FindUserByName:Lana[%d],lsn[%d],userid[%d],name[%s]",vcp->lana,vcp->lsn,newUid,usern);
808                 osi_Log3(smb_logp,"smb_ReceiveV3SessionSetupX FindUserByName:Lana[%d],lsn[%d],userid[%d]",vcp->lana,vcp->lsn,newUid);
809         smb_ReleaseUID(uidp);
810     }
811     else {
812       /* do a global search for the username/machine name pair */
813         unp = smb_FindUserByName(usern, vcp->rname, SMB_FLAG_CREATE);
814
815         /* Create a new UID and cm_user_t structure */
816         userp = unp->userp;
817         if (!userp)
818             userp = cm_NewUser();
819         lock_ObtainMutex(&vcp->mx);
820         if (!vcp->uidCounter)
821             vcp->uidCounter++; /* handle unlikely wraparounds */
822         newUid = (strlen(usern)==0)?0:vcp->uidCounter++;
823         lock_ReleaseMutex(&vcp->mx);
824
825         /* Create a new smb_user_t structure and connect them up */
826         lock_ObtainMutex(&unp->mx);
827         unp->userp = userp;
828         lock_ReleaseMutex(&unp->mx);
829
830         uidp = smb_FindUID(vcp, newUid, SMB_FLAG_CREATE);
831         lock_ObtainMutex(&uidp->mx);
832         uidp->unp = unp;
833                 osi_LogEvent("AFS smb_ReceiveV3SessionSetupX",NULL,"MakeNewUser:VCP[%x],Lana[%d],lsn[%d],userid[%d],TicketKTCName[%s]",(int)vcp,vcp->lana,vcp->lsn,newUid,usern);
834                 osi_Log4(smb_logp,"smb_ReceiveV3SessionSetupX MakeNewUser:VCP[%x],Lana[%d],lsn[%d],userid[%d]",vcp,vcp->lana,vcp->lsn,newUid);
835         lock_ReleaseMutex(&uidp->mx);
836         smb_ReleaseUID(uidp);
837     }
838
839     /* Return UID to the client */
840     ((smb_t *)outp)->uid = newUid;
841     /* Also to the next chained message */
842     ((smb_t *)inp)->uid = newUid;
843
844     osi_Log3(smb_logp, "SMB3 session setup name %s creating ID %d%s",
845              osi_LogSaveString(smb_logp, usern), newUid, osi_LogSaveString(smb_logp, s1));
846
847     smb_SetSMBParm(outp, 2, 0);
848
849     if (vcp->flags & SMB_VCFLAG_USENT) {
850         if (smb_authType == SMB_AUTH_EXTENDED) {
851             smb_SetSMBParm(outp, 3, secBlobOutLength);
852             smb_SetSMBDataLength(outp, secBlobOutLength + smb_ServerOSLength + smb_ServerLanManagerLength + smb_ServerDomainNameLength);
853             tp = smb_GetSMBData(outp, NULL);
854             if (secBlobOutLength) {
855                 memcpy(tp, secBlobOut, secBlobOutLength);
856                 free(secBlobOut);
857                 tp += secBlobOutLength;
858             }   
859             memcpy(tp,smb_ServerOS,smb_ServerOSLength);
860             tp += smb_ServerOSLength;
861             memcpy(tp,smb_ServerLanManager,smb_ServerLanManagerLength);
862             tp += smb_ServerLanManagerLength;
863             memcpy(tp,smb_ServerDomainName,smb_ServerDomainNameLength);
864             tp += smb_ServerDomainNameLength;
865         } else {
866             smb_SetSMBDataLength(outp, 0);
867         }
868     } else {
869         if (smb_authType == SMB_AUTH_EXTENDED) {
870             smb_SetSMBDataLength(outp, smb_ServerOSLength + smb_ServerLanManagerLength + smb_ServerDomainNameLength);
871             tp = smb_GetSMBData(outp, NULL);
872             memcpy(tp,smb_ServerOS,smb_ServerOSLength);
873             tp += smb_ServerOSLength;
874             memcpy(tp,smb_ServerLanManager,smb_ServerLanManagerLength);
875             tp += smb_ServerLanManagerLength;
876             memcpy(tp,smb_ServerDomainName,smb_ServerDomainNameLength);
877             tp += smb_ServerDomainNameLength;
878         } else {
879             smb_SetSMBDataLength(outp, 0);
880         }
881     }
882
883     return 0;
884 }
885
886 long smb_ReceiveV3UserLogoffX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
887 {
888         smb_user_t *uidp;
889
890         /* don't get tokens from this VC */
891         vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
892
893         inp->flags |= SMB_PACKETFLAG_PROFILE_UPDATE_OK;
894
895         /* find the tree and free it */
896     uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
897     /* TODO: smb_ReleaseUID() ? */
898     if (uidp) {
899                 char *s1 = NULL, *s2 = NULL;
900
901                 if (s2 == NULL) s2 = " ";
902                 if (s1 == NULL) {s1 = s2; s2 = " ";}
903
904                 osi_Log4(smb_logp, "SMB3 user logoffX uid %d name %s%s%s", uidp->userID,
905                  osi_LogSaveString(smb_logp, (uidp->unp) ? uidp->unp->name: " "), s1, s2);
906
907                 lock_ObtainMutex(&uidp->mx);
908                 uidp->flags |= SMB_USERFLAG_DELETE;
909                 /*
910                  * it doesn't get deleted right away
911                  * because the vcp points to it
912                  */
913         lock_ReleaseMutex(&uidp->mx);
914     }
915         else    
916                 osi_Log0(smb_logp, "SMB3 user logoffX");
917
918     smb_SetSMBDataLength(outp, 0);
919     return 0;
920 }
921
922 #define SMB_SUPPORT_SEARCH_BITS        0x0001
923
924 long smb_ReceiveV3TreeConnectX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
925 {
926     smb_tid_t *tidp;
927     smb_user_t *uidp;
928     unsigned short newTid;
929     char shareName[256];
930         char *sharePath;
931         int shareFound;
932     char *tp;
933     char *pathp;
934     char *passwordp;
935         char *servicep;
936     cm_user_t *userp;
937         
938         osi_Log0(smb_logp, "SMB3 receive tree connect");
939
940         /* parse input parameters */
941         tp = smb_GetSMBData(inp, NULL);
942     passwordp = smb_ParseString(tp, &tp);
943         pathp = smb_ParseString(tp, &tp);
944         servicep = smb_ParseString(tp, &tp);
945
946         tp = strrchr(pathp, '\\');
947     if (!tp) {
948         return CM_ERROR_BADSMB;
949     }
950     strcpy(shareName, tp+1);
951
952         if (strcmp(servicep, "IPC") == 0 || strcmp(shareName, "IPC$") == 0)
953                 return CM_ERROR_NOIPC;
954
955     userp = smb_GetUser(vcp, inp);
956
957         lock_ObtainMutex(&vcp->mx);
958     newTid = vcp->tidCounter++;
959         lock_ReleaseMutex(&vcp->mx);
960         
961         tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
962     uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
963         shareFound = smb_FindShare(vcp, uidp, shareName, &sharePath);
964     if (uidp)
965         smb_ReleaseUID(uidp);
966         if (!shareFound) {
967                 smb_ReleaseTID(tidp);
968                 return CM_ERROR_BADSHARENAME;
969         }
970     lock_ObtainMutex(&tidp->mx);
971     tidp->userp = userp;
972         tidp->pathname = sharePath;
973     lock_ReleaseMutex(&tidp->mx);
974     smb_ReleaseTID(tidp);
975
976         if (vcp->flags & SMB_VCFLAG_USENT)
977     {
978         int policy = smb_FindShareCSCPolicy(shareName);
979         smb_SetSMBParm(outp, 2, SMB_SUPPORT_SEARCH_BITS | (policy << 2));
980     }
981
982         ((smb_t *)outp)->tid = newTid;
983         ((smb_t *)inp)->tid = newTid;
984         tp = smb_GetSMBData(outp, NULL);
985     *tp++ = 'A';
986     *tp++ = ':';
987     *tp++ = 0;
988     smb_SetSMBDataLength(outp, 3);
989
990     osi_Log1(smb_logp, "SMB3 tree connect created ID %d", newTid);
991     return 0;
992 }
993
994 /* must be called with global tran lock held */
995 smb_tran2Packet_t *smb_FindTran2Packet(smb_vc_t *vcp, smb_packet_t *inp)
996 {
997         smb_tran2Packet_t *tp;
998     smb_t *smbp;
999         
1000     smbp = (smb_t *) inp->data;
1001         for(tp = smb_tran2AssemblyQueuep; tp; tp = (smb_tran2Packet_t *) osi_QNext(&tp->q)) {
1002                 if (tp->vcp == vcp && tp->mid == smbp->mid && tp->tid == smbp->tid)
1003             return tp;
1004     }
1005     return NULL;
1006 }
1007
1008 smb_tran2Packet_t *smb_NewTran2Packet(smb_vc_t *vcp, smb_packet_t *inp,
1009         int totalParms, int totalData)
1010 {
1011         smb_tran2Packet_t *tp;
1012     smb_t *smbp;
1013         
1014     smbp = (smb_t *) inp->data;
1015         tp = malloc(sizeof(*tp));
1016     memset(tp, 0, sizeof(*tp));
1017     tp->vcp = vcp;
1018     smb_HoldVC(vcp);
1019     tp->curData = tp->curParms = 0;
1020     tp->totalData = totalData;
1021     tp->totalParms = totalParms;
1022     tp->tid = smbp->tid;
1023     tp->mid = smbp->mid;
1024     tp->uid = smbp->uid;
1025     tp->pid = smbp->pid;
1026         tp->res[0] = smbp->res[0];
1027         osi_QAdd((osi_queue_t **)&smb_tran2AssemblyQueuep, &tp->q);
1028     tp->opcode = smb_GetSMBParm(inp, 14);
1029         if (totalParms != 0)
1030         tp->parmsp = malloc(totalParms);
1031         if (totalData != 0)
1032         tp->datap = malloc(totalData);
1033         tp->flags |= SMB_TRAN2PFLAG_ALLOC;
1034     return tp;
1035 }
1036
1037 smb_tran2Packet_t *smb_GetTran2ResponsePacket(smb_vc_t *vcp,
1038         smb_tran2Packet_t *inp, smb_packet_t *outp,
1039         int totalParms, int totalData)
1040 {
1041         smb_tran2Packet_t *tp;
1042         unsigned short parmOffset;
1043         unsigned short dataOffset;
1044         unsigned short dataAlign;
1045         
1046         tp = malloc(sizeof(*tp));
1047     memset(tp, 0, sizeof(*tp));
1048     tp->vcp = NULL;
1049     tp->curData = tp->curParms = 0;
1050     tp->totalData = totalData;
1051     tp->totalParms = totalParms;
1052         tp->oldTotalParms = totalParms;
1053     tp->tid = inp->tid;
1054     tp->mid = inp->mid;
1055     tp->uid = inp->uid;
1056     tp->pid = inp->pid;
1057         tp->res[0] = inp->res[0];
1058     tp->opcode = inp->opcode;
1059
1060         /*
1061          * We calculate where the parameters and data will start.
1062          * This calculation must parallel the calculation in
1063          * smb_SendTran2Packet.
1064          */
1065
1066         parmOffset = 10*2 + 35;
1067         parmOffset++;                   /* round to even */
1068         tp->parmsp = (unsigned short *) (outp->data + parmOffset);
1069
1070         dataOffset = parmOffset + totalParms;
1071         dataAlign = dataOffset & 2;     /* quad-align */
1072         dataOffset += dataAlign;
1073         tp->datap = outp->data + dataOffset;
1074
1075     return tp;
1076 }
1077
1078 /* free a tran2 packet; must be called with smb_globalLock held */
1079 void smb_FreeTran2Packet(smb_tran2Packet_t *t2p)
1080 {
1081     if (t2p->vcp) smb_ReleaseVC(t2p->vcp);
1082         if (t2p->flags & SMB_TRAN2PFLAG_ALLOC) {
1083                 if (t2p->parmsp)
1084                         free(t2p->parmsp);
1085                 if (t2p->datap)
1086                         free(t2p->datap);
1087         }
1088     free(t2p);
1089 }
1090
1091 /* called with a VC, an input packet to respond to, and an error code.
1092  * sends an error response.
1093  */
1094 void smb_SendTran2Error(smb_vc_t *vcp, smb_tran2Packet_t *t2p,
1095         smb_packet_t *tp, long code)
1096 {
1097     smb_t *smbp;
1098     unsigned short errCode;
1099     unsigned char errClass;
1100         unsigned long NTStatus;
1101
1102     if (vcp->flags & SMB_VCFLAG_STATUS32)
1103                 smb_MapNTError(code, &NTStatus);
1104         else
1105                 smb_MapCoreError(code, vcp, &errCode, &errClass);
1106
1107     smb_FormatResponsePacket(vcp, NULL, tp);
1108     smbp = (smb_t *) tp;
1109
1110         /* We can handle long names */
1111         if (vcp->flags & SMB_VCFLAG_USENT)
1112                 smbp->flg2 |= 0x40;     /* IS_LONG_NAME */
1113         
1114     /* now copy important fields from the tran 2 packet */
1115     smbp->com = 0x32;           /* tran 2 response */
1116     smbp->tid = t2p->tid;
1117     smbp->mid = t2p->mid;
1118     smbp->pid = t2p->pid;
1119     smbp->uid = t2p->uid;
1120         smbp->res[0] = t2p->res[0];
1121         if (vcp->flags & SMB_VCFLAG_STATUS32) {
1122                 smbp->rcls = (unsigned char) (NTStatus & 0xff);
1123                 smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
1124                 smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
1125                 smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
1126                 smbp->flg2 |= 0x4000;
1127         }
1128         else {
1129         smbp->rcls = errClass;
1130                 smbp->errLow = (unsigned char) (errCode & 0xff);
1131                 smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
1132         }
1133         
1134     /* send packet */
1135     smb_SendPacket(vcp, tp);
1136 }        
1137
1138 void smb_SendTran2Packet(smb_vc_t *vcp, smb_tran2Packet_t *t2p, smb_packet_t *tp)
1139 {
1140     smb_t *smbp;
1141     unsigned short parmOffset;
1142         unsigned short dataOffset;
1143         unsigned short totalLength;
1144         unsigned short dataAlign;
1145     char *datap;
1146         
1147     smb_FormatResponsePacket(vcp, NULL, tp);
1148     smbp = (smb_t *) tp;
1149
1150         /* We can handle long names */
1151         if (vcp->flags & SMB_VCFLAG_USENT)
1152                 smbp->flg2 |= 0x40;     /* IS_LONG_NAME */
1153
1154     /* now copy important fields from the tran 2 packet */
1155     smbp->com = 0x32;           /* tran 2 response */
1156     smbp->tid = t2p->tid;
1157     smbp->mid = t2p->mid;
1158     smbp->pid = t2p->pid;
1159     smbp->uid = t2p->uid;
1160         smbp->res[0] = t2p->res[0];
1161
1162     totalLength = 1 + t2p->totalData + t2p->totalParms;
1163
1164     /* now add the core parameters (tran2 info) to the packet */
1165     smb_SetSMBParm(tp, 0, t2p->totalParms);     /* parm bytes */
1166     smb_SetSMBParm(tp, 1, t2p->totalData);      /* data bytes */
1167     smb_SetSMBParm(tp, 2, 0);           /* reserved */
1168     smb_SetSMBParm(tp, 3, t2p->totalParms);     /* parm bytes in this packet */
1169         parmOffset = 10*2 + 35;                 /* parm offset in packet */
1170         parmOffset++;                           /* round to even */
1171     smb_SetSMBParm(tp, 4, parmOffset);  /* 11 parm words plus *
1172     * hdr, bcc and wct */
1173     smb_SetSMBParm(tp, 5, 0);           /* parm displacement */
1174     smb_SetSMBParm(tp, 6, t2p->totalData);      /* data in this packet */
1175         dataOffset = parmOffset + t2p->oldTotalParms;
1176         dataAlign = dataOffset & 2;             /* quad-align */
1177         dataOffset += dataAlign;
1178     smb_SetSMBParm(tp, 7, dataOffset);  /* offset of data */
1179     smb_SetSMBParm(tp, 8, 0);           /* data displacement */
1180     smb_SetSMBParm(tp, 9, 0);           /* low: setup word count *
1181     * high: resvd */
1182
1183     datap = smb_GetSMBData(tp, NULL);
1184         *datap++ = 0;                           /* we rounded to even */
1185
1186         totalLength += dataAlign;
1187     smb_SetSMBDataLength(tp, totalLength);
1188         
1189     /* next, send the datagram */
1190     smb_SendPacket(vcp, tp);
1191 }   
1192
1193 long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
1194 {
1195     smb_tran2Packet_t *asp;
1196     int totalParms;
1197     int totalData;
1198     int parmDisp;
1199     int dataDisp;
1200     int parmOffset;
1201     int dataOffset;
1202     int parmCount;
1203     int dataCount;
1204     int firstPacket;
1205     long code = 0;
1206
1207         /* We sometimes see 0 word count.  What to do? */
1208         if (*inp->wctp == 0) {
1209 #ifndef DJGPP
1210                 HANDLE h;
1211                 char *ptbuf[1];
1212
1213                 osi_Log0(smb_logp, "TRANSACTION2 word count = 0"); 
1214
1215                 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1216                 ptbuf[0] = "Transaction2 word count = 0";
1217                 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1003, NULL,
1218                             1, inp->ncb_length, ptbuf, inp);
1219                 DeregisterEventSource(h);
1220 #else /* DJGPP */
1221                 osi_Log0(smb_logp, "TRANSACTION2 word count = 0"); 
1222 #endif /* !DJGPP */
1223
1224         smb_SetSMBDataLength(outp, 0);
1225         smb_SendPacket(vcp, outp);
1226                 return 0;
1227         }
1228
1229     totalParms = smb_GetSMBParm(inp, 0);
1230     totalData = smb_GetSMBParm(inp, 1);
1231         
1232     firstPacket = (inp->inCom == 0x32);
1233         
1234         /* find the packet we're reassembling */
1235         lock_ObtainWrite(&smb_globalLock);
1236     asp = smb_FindTran2Packet(vcp, inp);
1237     if (!asp) {
1238         asp = smb_NewTran2Packet(vcp, inp, totalParms, totalData);
1239         }
1240     lock_ReleaseWrite(&smb_globalLock);
1241         
1242     /* now merge in this latest packet; start by looking up offsets */
1243         if (firstPacket) {
1244                 parmDisp = dataDisp = 0;
1245         parmOffset = smb_GetSMBParm(inp, 10);
1246         dataOffset = smb_GetSMBParm(inp, 12);
1247         parmCount = smb_GetSMBParm(inp, 9);
1248         dataCount = smb_GetSMBParm(inp, 11);
1249                 asp->maxReturnParms = smb_GetSMBParm(inp, 2);
1250         asp->maxReturnData = smb_GetSMBParm(inp, 3);
1251
1252                 osi_Log3(smb_logp, "SMB3 received T2 init packet total data %d, cur data %d, max return data %d",
1253                  totalData, dataCount, asp->maxReturnData);
1254     }
1255     else {
1256         parmDisp = smb_GetSMBParm(inp, 4);
1257         parmOffset = smb_GetSMBParm(inp, 3);
1258         dataDisp = smb_GetSMBParm(inp, 7);
1259         dataOffset = smb_GetSMBParm(inp, 6);
1260         parmCount = smb_GetSMBParm(inp, 2);
1261         dataCount = smb_GetSMBParm(inp, 5);
1262
1263         osi_Log2(smb_logp, "SMB3 received T2 aux packet parms %d, data %d",
1264                  parmCount, dataCount);
1265     }   
1266
1267     /* now copy the parms and data */
1268     if ( parmCount != 0 )
1269     {
1270         memcpy(((char *)asp->parmsp) + parmDisp, inp->data + parmOffset, parmCount);
1271     }
1272     if ( dataCount != 0 ) {
1273         memcpy(asp->datap + dataDisp, inp->data + dataOffset, dataCount);
1274     }
1275
1276     /* account for new bytes */
1277     asp->curData += dataCount;
1278     asp->curParms += parmCount;
1279
1280     /* finally, if we're done, remove the packet from the queue and dispatch it */
1281     if (asp->totalData <= asp->curData && asp->totalParms <= asp->curParms) {
1282                 /* we've received it all */
1283         lock_ObtainWrite(&smb_globalLock);
1284                 osi_QRemove((osi_queue_t **) &smb_tran2AssemblyQueuep, &asp->q);
1285         lock_ReleaseWrite(&smb_globalLock);
1286
1287         /* now dispatch it */
1288         if ( asp->opcode >= 0 && asp->opcode < 20 && smb_tran2DispatchTable[asp->opcode].procp) {
1289             osi_LogEvent("AFS-Dispatch-2[%s]",myCrt_2Dispatch(asp->opcode),"vcp[%x] lana[%d] lsn[%d]",(int)vcp,vcp->lana,vcp->lsn);
1290             osi_Log4(smb_logp,"AFS Server - Dispatch-2 %s vcp[%x] lana[%d] lsn[%d]",myCrt_2Dispatch(asp->opcode),vcp,vcp->lana,vcp->lsn);
1291             code = (*smb_tran2DispatchTable[asp->opcode].procp)(vcp, asp, outp);
1292         }
1293         else {
1294             osi_LogEvent("AFS-Dispatch-2 [invalid]", NULL, "op[%x] vcp[%x] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
1295             osi_Log4(smb_logp,"AFS Server - Dispatch-2 [INVALID] op[%x] vcp[%x] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
1296             code = CM_ERROR_BADOP;
1297         }
1298
1299                 /* if an error is returned, we're supposed to send an error packet,
1300          * otherwise the dispatched function already did the data sending.
1301          * We give dispatched proc the responsibility since it knows how much
1302          * space to allocate.
1303          */
1304         if (code != 0) {
1305             smb_SendTran2Error(vcp, asp, outp, code);
1306         }
1307
1308                 /* free the input tran 2 packet */
1309                 lock_ObtainWrite(&smb_globalLock);
1310         smb_FreeTran2Packet(asp);
1311                 lock_ReleaseWrite(&smb_globalLock);
1312     }
1313     else if (firstPacket) {
1314                 /* the first packet in a multi-packet request, we need to send an
1315          * ack to get more data.
1316          */
1317         smb_SetSMBDataLength(outp, 0);
1318         smb_SendPacket(vcp, outp);
1319     }
1320
1321         return 0;
1322 }
1323
1324 long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
1325 {
1326         char *pathp;
1327     smb_tran2Packet_t *outp;
1328     long code = 0;
1329         cm_space_t *spacep;
1330     int excl;
1331     cm_user_t *userp;
1332     cm_scache_t *dscp;          /* dir we're dealing with */
1333     cm_scache_t *scp;           /* file we're creating */
1334     cm_attr_t setAttr;
1335     int initialModeBits;
1336     smb_fid_t *fidp;
1337     int attributes;
1338     char *lastNamep;
1339     long dosTime;
1340     int openFun;
1341     int trunc;
1342     int openMode;
1343     int extraInfo;
1344     int openAction;
1345     int parmSlot;                       /* which parm we're dealing with */
1346     long returnEALength;
1347         char *tidPathp;
1348         cm_req_t req;
1349
1350         cm_InitReq(&req);
1351
1352     scp = NULL;
1353         
1354         extraInfo = (p->parmsp[0] & 1); /* return extra info */
1355     returnEALength = (p->parmsp[0] & 8);        /* return extended attr length */
1356
1357         openFun = p->parmsp[6];         /* open function */
1358     excl = ((openFun & 3) == 0);
1359     trunc = ((openFun & 3) == 2);       /* truncate it */
1360         openMode = (p->parmsp[1] & 0x7);
1361     openAction = 0;                     /* tracks what we did */
1362
1363     attributes = p->parmsp[3];
1364     dosTime = p->parmsp[4] | (p->parmsp[5] << 16);
1365         
1366         /* compute initial mode bits based on read-only flag in attributes */
1367     initialModeBits = 0666;
1368     if (attributes & 1) initialModeBits &= ~0222;
1369         
1370     pathp = (char *) (&p->parmsp[14]);
1371         
1372     outp = smb_GetTran2ResponsePacket(vcp, p, op, 40, 0);
1373
1374         spacep = cm_GetSpace();
1375     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
1376
1377         if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
1378                 /* special case magic file name for receiving IOCTL requests
1379          * (since IOCTL calls themselves aren't getting through).
1380          */
1381         fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
1382         smb_SetupIoctlFid(fidp, spacep);
1383
1384         /* copy out remainder of the parms */
1385                 parmSlot = 0;
1386                 outp->parmsp[parmSlot] = fidp->fid; parmSlot++;
1387                 if (extraInfo) {
1388             outp->parmsp[parmSlot] = /* attrs */ 0; parmSlot++;
1389             outp->parmsp[parmSlot] = 0; parmSlot++;     /* mod time */
1390             outp->parmsp[parmSlot] = 0; parmSlot++;
1391             outp->parmsp[parmSlot] = 0; parmSlot++;     /* len */
1392             outp->parmsp[parmSlot] = 0x7fff; parmSlot++;
1393             outp->parmsp[parmSlot] = openMode; parmSlot++;
1394             outp->parmsp[parmSlot] = 0; parmSlot++; /* file type 0 ==> normal file or dir */
1395             outp->parmsp[parmSlot] = 0; parmSlot++; /* IPC junk */
1396                 }   
1397                 /* and the final "always present" stuff */
1398         outp->parmsp[parmSlot] = /* openAction found existing file */ 1; parmSlot++;
1399                 /* next write out the "unique" ID */
1400                 outp->parmsp[parmSlot] = 0x1234; parmSlot++;
1401                 outp->parmsp[parmSlot] = 0x5678; parmSlot++;
1402         outp->parmsp[parmSlot] = 0; parmSlot++;
1403                 if (returnEALength) {
1404                         outp->parmsp[parmSlot] = 0; parmSlot++;
1405                         outp->parmsp[parmSlot] = 0; parmSlot++;
1406         }
1407                 
1408         outp->totalData = 0;
1409         outp->totalParms = parmSlot * 2;
1410                 
1411         smb_SendTran2Packet(vcp, outp, op);
1412                 
1413         smb_FreeTran2Packet(outp);
1414
1415                 /* and clean up fid reference */
1416         smb_ReleaseFID(fidp);
1417         return 0;
1418     }
1419
1420 #ifdef DEBUG_VERBOSE
1421         {
1422                 char *hexp, *asciip;
1423                 asciip = (lastNamep ? lastNamep : pathp);
1424                 hexp = osi_HexifyString( asciip );
1425                 DEBUG_EVENT2("AFS","T2Open H[%s] A[%s]", hexp, asciip);
1426                 free(hexp);
1427         }
1428 #endif
1429
1430         userp = smb_GetTran2User(vcp, p);
1431     /* In the off chance that userp is NULL, we log and abandon */
1432     if (!userp) {
1433         osi_Log1(smb_logp, "ReceiveTran2Open user [%d] not resolvable", p->uid);
1434         smb_FreeTran2Packet(outp);
1435         return CM_ERROR_BADSMB;
1436     }
1437
1438         tidPathp = smb_GetTIDPath(vcp, p->tid);
1439
1440         dscp = NULL;
1441         code = cm_NameI(cm_rootSCachep, pathp,
1442                     CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
1443                     userp, tidPathp, &req, &scp);
1444         if (code != 0) {
1445                 code = cm_NameI(cm_rootSCachep, spacep->data,
1446                         CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
1447                         userp, tidPathp, &req, &dscp);
1448                 cm_FreeSpace(spacep);
1449
1450         if (code) {
1451             cm_ReleaseUser(userp);
1452                         smb_FreeTran2Packet(outp);
1453             return code;
1454         }
1455         
1456         /* otherwise, scp points to the parent directory.  Do a lookup,
1457                  * and truncate the file if we find it, otherwise we create the
1458                  * file.
1459          */
1460         if (!lastNamep) lastNamep = pathp;
1461         else lastNamep++;
1462         code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD, userp,
1463                          &req, &scp);
1464         if (code && code != CM_ERROR_NOSUCHFILE) {
1465                         cm_ReleaseSCache(dscp);
1466             cm_ReleaseUser(userp);
1467                         smb_FreeTran2Packet(outp);
1468             return code;
1469         }
1470         }
1471     else {
1472         cm_FreeSpace(spacep);
1473         }
1474         
1475     /* if we get here, if code is 0, the file exists and is represented by
1476      * scp.  Otherwise, we have to create it.
1477      */
1478         if (code == 0) {
1479         code = cm_CheckOpen(scp, openMode, trunc, userp, &req);
1480         if (code) {
1481             if (dscp) cm_ReleaseSCache(dscp);
1482             cm_ReleaseSCache(scp);
1483             cm_ReleaseUser(userp);
1484                         smb_FreeTran2Packet(outp);
1485             return code;
1486         }
1487
1488                 if (excl) {
1489                         /* oops, file shouldn't be there */
1490             if (dscp) cm_ReleaseSCache(dscp);
1491             cm_ReleaseSCache(scp);
1492             cm_ReleaseUser(userp);
1493                         smb_FreeTran2Packet(outp);
1494             return CM_ERROR_EXISTS;
1495         }
1496
1497                 if (trunc) {
1498                         setAttr.mask = CM_ATTRMASK_LENGTH;
1499             setAttr.length.LowPart = 0;
1500             setAttr.length.HighPart = 0;
1501                         code = cm_SetAttr(scp, &setAttr, userp, &req);
1502             openAction = 3;     /* truncated existing file */
1503                 }   
1504         else openAction = 1;    /* found existing file */
1505     }
1506         else if (!(openFun & SMB_ATTR_DIRECTORY)) {
1507                 /* don't create if not found */
1508         if (dscp) cm_ReleaseSCache(dscp);
1509         osi_assert(scp == NULL);
1510         cm_ReleaseUser(userp);
1511                 smb_FreeTran2Packet(outp);
1512         return CM_ERROR_NOSUCHFILE;
1513     }
1514     else {
1515                 osi_assert(dscp != NULL && scp == NULL);
1516                 openAction = 2; /* created file */
1517                 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
1518                 smb_UnixTimeFromSearchTime(&setAttr.clientModTime, dosTime);
1519         code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
1520                          &req);
1521                 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
1522                         smb_NotifyChange(FILE_ACTION_ADDED,
1523                              FILE_NOTIFY_CHANGE_FILE_NAME,  
1524                              dscp, lastNamep, NULL, TRUE);
1525         if (!excl && code == CM_ERROR_EXISTS) {
1526                         /* not an exclusive create, and someone else tried
1527                          * creating it already, then we open it anyway.  We
1528                          * don't bother retrying after this, since if this next
1529                          * fails, that means that the file was deleted after we
1530                          * started this call.
1531              */
1532             code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
1533                              userp, &req, &scp);
1534             if (code == 0) {
1535                 if (trunc) {
1536                                         setAttr.mask = CM_ATTRMASK_LENGTH;
1537                     setAttr.length.LowPart = 0;
1538                     setAttr.length.HighPart = 0;
1539                     code = cm_SetAttr(scp, &setAttr, userp,
1540                                       &req);
1541                 }   
1542                         }       /* lookup succeeded */
1543         }
1544     }
1545         
1546         /* we don't need this any longer */
1547         if (dscp) cm_ReleaseSCache(dscp);
1548
1549     if (code) {
1550                 /* something went wrong creating or truncating the file */
1551         if (scp) cm_ReleaseSCache(scp);
1552         cm_ReleaseUser(userp);
1553                 smb_FreeTran2Packet(outp);
1554         return code;
1555     }
1556         
1557         /* make sure we're about to open a file */
1558         if (scp->fileType != CM_SCACHETYPE_FILE) {
1559                 cm_ReleaseSCache(scp);
1560                 cm_ReleaseUser(userp);
1561                 smb_FreeTran2Packet(outp);
1562                 return CM_ERROR_ISDIR;
1563         }
1564
1565     /* now all we have to do is open the file itself */
1566     fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
1567     osi_assert(fidp);
1568         
1569         /* save a pointer to the vnode */
1570     fidp->scp = scp;
1571         
1572         /* compute open mode */
1573     if (openMode != 1) fidp->flags |= SMB_FID_OPENREAD;
1574     if (openMode == 1 || openMode == 2)
1575         fidp->flags |= SMB_FID_OPENWRITE;
1576
1577         smb_ReleaseFID(fidp);
1578         
1579         cm_Open(scp, 0, userp);
1580
1581     /* copy out remainder of the parms */
1582         parmSlot = 0;
1583         outp->parmsp[parmSlot] = fidp->fid; parmSlot++;
1584         lock_ObtainMutex(&scp->mx);
1585         if (extraInfo) {
1586         outp->parmsp[parmSlot] = smb_Attributes(scp); parmSlot++;
1587                 smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
1588         outp->parmsp[parmSlot] = (unsigned short)(dosTime & 0xffff); parmSlot++;
1589         outp->parmsp[parmSlot] = (unsigned short)((dosTime>>16) & 0xffff); parmSlot++;
1590         outp->parmsp[parmSlot] = (unsigned short) (scp->length.LowPart & 0xffff);
1591         parmSlot++;
1592         outp->parmsp[parmSlot] = (unsigned short) ((scp->length.LowPart >> 16) & 0xffff);
1593         parmSlot++;
1594         outp->parmsp[parmSlot] = openMode; parmSlot++;
1595         outp->parmsp[parmSlot] = 0; parmSlot++; /* file type 0 ==> normal file or dir */
1596         outp->parmsp[parmSlot] = 0; parmSlot++; /* IPC junk */
1597         }   
1598         /* and the final "always present" stuff */
1599     outp->parmsp[parmSlot] = openAction; parmSlot++;
1600         /* next write out the "unique" ID */
1601         outp->parmsp[parmSlot] = (unsigned short) (scp->fid.vnode & 0xffff); parmSlot++;
1602         outp->parmsp[parmSlot] = (unsigned short) (scp->fid.volume & 0xffff); parmSlot++;
1603     outp->parmsp[parmSlot] = 0; parmSlot++;
1604     if (returnEALength) {
1605                 outp->parmsp[parmSlot] = 0; parmSlot++;
1606                 outp->parmsp[parmSlot] = 0; parmSlot++;
1607     }
1608         lock_ReleaseMutex(&scp->mx);
1609         outp->totalData = 0;            /* total # of data bytes */
1610     outp->totalParms = parmSlot * 2;    /* shorts are two bytes */
1611
1612         smb_SendTran2Packet(vcp, outp, op);
1613         
1614     smb_FreeTran2Packet(outp);
1615
1616     cm_ReleaseUser(userp);
1617     /* leave scp held since we put it in fidp->scp */
1618     return 0;
1619 }   
1620
1621 long smb_ReceiveTran2FindFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
1622 {
1623     return CM_ERROR_BADOP;
1624 }
1625
1626 long smb_ReceiveTran2FindNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
1627 {
1628     return CM_ERROR_BADOP;
1629 }
1630
1631 long smb_ReceiveTran2QFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
1632 {
1633         smb_tran2Packet_t *outp;
1634     smb_tran2QFSInfo_t qi;
1635         int responseSize;
1636         osi_hyper_t temp;
1637         static char FSname[6] = {'A', 0, 'F', 0, 'S', 0};
1638         
1639         osi_Log1(smb_logp, "T2 QFSInfo type 0x%x", p->parmsp[0]);
1640
1641         switch (p->parmsp[0]) {
1642         case 1: responseSize = sizeof(qi.u.allocInfo); break;
1643         case 2: responseSize = sizeof(qi.u.volumeInfo); break;
1644         case 0x102: responseSize = sizeof(qi.u.FSvolumeInfo); break;
1645         case 0x103: responseSize = sizeof(qi.u.FSsizeInfo); break;
1646         case 0x104: responseSize = sizeof(qi.u.FSdeviceInfo); break;
1647         case 0x105: responseSize = sizeof(qi.u.FSattributeInfo); break;
1648         default: return CM_ERROR_INVAL;
1649         }
1650
1651     outp = smb_GetTran2ResponsePacket(vcp, p, op, 0, responseSize);
1652         switch (p->parmsp[0]) {
1653         case 1:
1654                 /* alloc info */
1655         qi.u.allocInfo.FSID = 0;
1656         qi.u.allocInfo.sectorsPerAllocUnit = 1;
1657         qi.u.allocInfo.totalAllocUnits = 0x7fffffff;
1658         qi.u.allocInfo.availAllocUnits = 0x3fffffff;
1659         qi.u.allocInfo.bytesPerSector = 1024;
1660                 break;
1661
1662     case 2:
1663                 /* volume info */
1664         qi.u.volumeInfo.vsn = 1234;
1665         qi.u.volumeInfo.vnCount = 4;
1666                 /* we're supposed to pad it out with zeroes to the end */
1667                 memset(&qi.u.volumeInfo.label, 0, sizeof(qi.u.volumeInfo.label));
1668         memcpy(qi.u.volumeInfo.label, "AFS", 4);
1669                 break;
1670
1671         case 0x102:
1672                 /* FS volume info */
1673                 memset((char *)&qi.u.FSvolumeInfo.vct, 0, sizeof(FILETIME));
1674                 qi.u.FSvolumeInfo.vsn = 1234;
1675                 qi.u.FSvolumeInfo.vnCount = 8;
1676                 memcpy(qi.u.FSvolumeInfo.label, "A\0F\0S\0\0", 8);
1677                 break;
1678
1679         case 0x103:
1680                 /* FS size info */
1681                 temp.HighPart = 0;
1682                 temp.LowPart = 0x7fffffff;
1683                 qi.u.FSsizeInfo.totalAllocUnits = temp;
1684                 temp.LowPart = 0x3fffffff;
1685                 qi.u.FSsizeInfo.availAllocUnits = temp;
1686                 qi.u.FSsizeInfo.sectorsPerAllocUnit = 1;
1687                 qi.u.FSsizeInfo.bytesPerSector = 1024;
1688                 break;
1689
1690         case 0x104:
1691                 /* FS device info */
1692                 qi.u.FSdeviceInfo.devType = 0;  /* don't have a number */
1693                 qi.u.FSdeviceInfo.characteristics = 0x50; /* remote, virtual */
1694                 break;
1695
1696         case 0x105:
1697                 /* FS attribute info */
1698                 /* attributes, defined in WINNT.H:
1699                  *      FILE_CASE_SENSITIVE_SEARCH      0x1
1700                  *      FILE_CASE_PRESERVED_NAMES       0x2
1701                  *      <no name defined>               0x4000
1702                  *         If bit 0x4000 is not set, Windows 95 thinks
1703                  *         we can't handle long (non-8.3) names,
1704                  *         despite our protestations to the contrary.
1705                  */
1706                 qi.u.FSattributeInfo.attributes = 0x4003;
1707                 qi.u.FSattributeInfo.maxCompLength = 255;
1708                 qi.u.FSattributeInfo.FSnameLength = 6;
1709                 memcpy(qi.u.FSattributeInfo.FSname, FSname, 6);
1710                 break;
1711     }
1712         
1713         /* copy out return data, and set corresponding sizes */
1714         outp->totalParms = 0;
1715     outp->totalData = responseSize;
1716     memcpy(outp->datap, &qi, responseSize);
1717
1718         /* send and free the packets */
1719         smb_SendTran2Packet(vcp, outp, op);
1720     smb_FreeTran2Packet(outp);
1721
1722     return 0;
1723 }
1724
1725 long smb_ReceiveTran2SetFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
1726 {
1727     return CM_ERROR_BADOP;
1728 }
1729
1730 struct smb_ShortNameRock {
1731         char *maskp;
1732         unsigned int vnode;
1733         char *shortName;
1734         size_t shortNameLen;
1735 };
1736
1737 int cm_GetShortNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *vrockp,
1738         osi_hyper_t *offp)
1739 {
1740         struct smb_ShortNameRock *rockp;
1741         char *shortNameEnd;
1742
1743         rockp = vrockp;
1744         /* compare both names and vnodes, though probably just comparing vnodes
1745          * would be safe enough.
1746          */
1747         if (cm_stricmp(dep->name, rockp->maskp) != 0)
1748                 return 0;
1749         if (ntohl(dep->fid.vnode) != rockp->vnode)
1750                 return 0;
1751         /* This is the entry */
1752         cm_Gen8Dot3Name(dep, rockp->shortName, &shortNameEnd);
1753         rockp->shortNameLen = shortNameEnd - rockp->shortName;
1754         return CM_ERROR_STOPNOW;
1755 }
1756
1757 long cm_GetShortName(char *pathp, cm_user_t *userp, cm_req_t *reqp,
1758         char *tidPathp, int vnode, char *shortName, size_t *shortNameLenp)
1759 {
1760         struct smb_ShortNameRock rock;
1761         char *lastNamep;
1762         cm_space_t *spacep;
1763         cm_scache_t *dscp;
1764         int caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
1765         long code = 0;
1766         osi_hyper_t thyper;
1767
1768         spacep = cm_GetSpace();
1769         smb_StripLastComponent(spacep->data, &lastNamep, pathp);
1770
1771         code = cm_NameI(cm_rootSCachep, spacep->data, caseFold, userp, tidPathp,
1772                      reqp, &dscp);
1773         cm_FreeSpace(spacep);
1774         if (code) return code;
1775
1776         if (!lastNamep) lastNamep = pathp;
1777         else lastNamep++;
1778         thyper.LowPart = 0;
1779         thyper.HighPart = 0;
1780         rock.shortName = shortName;
1781         rock.vnode = vnode;
1782         rock.maskp = lastNamep;
1783         code = cm_ApplyDir(dscp, cm_GetShortNameProc, &rock, &thyper, userp,
1784                         reqp, NULL);
1785
1786         cm_ReleaseSCache(dscp);
1787
1788         if (code == 0)
1789                 return CM_ERROR_NOSUCHFILE;
1790         if (code == CM_ERROR_STOPNOW) {
1791                 *shortNameLenp = rock.shortNameLen;
1792                 return 0;
1793         }
1794         return code;
1795 }
1796
1797 long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
1798 {
1799         smb_tran2Packet_t *outp;
1800     unsigned long dosTime;
1801         FILETIME ft;
1802     unsigned short infoLevel;
1803     int nbytesRequired;
1804     unsigned short attributes;
1805         unsigned long extAttributes;
1806         char shortName[13];
1807         unsigned int len;
1808     cm_user_t *userp;
1809         cm_space_t *spacep;
1810     cm_scache_t *scp, *dscp;
1811     long code = 0;
1812     char *op;
1813         char *tidPathp;
1814         char *lastComp;
1815         cm_req_t req;
1816
1817         cm_InitReq(&req);
1818
1819         infoLevel = p->parmsp[0];
1820     if (infoLevel == 6) nbytesRequired = 0;
1821     else if (infoLevel == 1) nbytesRequired = 22;
1822     else if (infoLevel == 2) nbytesRequired = 26;
1823         else if (infoLevel == 0x101) nbytesRequired = 40;
1824         else if (infoLevel == 0x102) nbytesRequired = 24;
1825         else if (infoLevel == 0x103) nbytesRequired = 4;
1826         else if (infoLevel == 0x108) nbytesRequired = 30;
1827     else {
1828                 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
1829                   p->opcode, infoLevel);
1830                 smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
1831         return 0;
1832     }
1833         osi_Log2(smb_logp, "T2 QPathInfo type 0x%x path %s", infoLevel,
1834              osi_LogSaveString(smb_logp, (char *)(&p->parmsp[3])));
1835
1836     outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, nbytesRequired);
1837
1838         if (infoLevel > 0x100)
1839                 outp->totalParms = 2;
1840         else
1841                 outp->totalParms = 0;
1842         outp->totalData = nbytesRequired;
1843         
1844     /* now, if we're at infoLevel 6, we're only being asked to check
1845      * the syntax, so we just OK things now.  In particular, we're *not*
1846      * being asked to verify anything about the state of any parent dirs.
1847      */
1848         if (infoLevel == 6) {
1849                 smb_SendTran2Packet(vcp, outp, opx);
1850         smb_FreeTran2Packet(outp);
1851                 return 0;
1852     }
1853         
1854     userp = smb_GetTran2User(vcp, p);
1855     if (!userp) {
1856         osi_Log1(smb_logp, "ReceiveTran2QPathInfo unable to resolve user [%d]", p->uid);
1857         smb_FreeTran2Packet(outp);
1858         return CM_ERROR_BADSMB;
1859     }
1860
1861         tidPathp = smb_GetTIDPath(vcp, p->tid);
1862
1863         /*
1864          * XXX Strange hack XXX
1865          *
1866          * As of Patch 7 (13 January 98), we are having the following problem:
1867          * In NT Explorer 4.0, whenever we click on a directory, AFS gets
1868          * requests to look up "desktop.ini" in all the subdirectories.
1869          * This can cause zillions of timeouts looking up non-existent cells
1870          * and volumes, especially in the top-level directory.
1871          *
1872          * We have not found any way to avoid this or work around it except
1873          * to explicitly ignore the requests for mount points that haven't
1874          * yet been evaluated and for directories that haven't yet been
1875          * fetched.
1876          */
1877         if (infoLevel == 0x101) {
1878                 spacep = cm_GetSpace();
1879                 smb_StripLastComponent(spacep->data, &lastComp,
1880                                         (char *)(&p->parmsp[3]));
1881                 /* Make sure that lastComp is not NULL */
1882                 if (lastComp) {
1883                     if (strcmp(lastComp, "\\desktop.ini") == 0) {
1884                 code = cm_NameI(cm_rootSCachep, spacep->data,
1885                                 CM_FLAG_CASEFOLD
1886                                 | CM_FLAG_DIRSEARCH
1887                                 | CM_FLAG_FOLLOW,
1888                                 userp, tidPathp, &req, &dscp);
1889                 if (code == 0) {
1890                     if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT
1891                          && !dscp->mountRootFidp)
1892                         code = CM_ERROR_NOSUCHFILE;
1893                     else if (dscp->fileType == CM_SCACHETYPE_DIRECTORY) {
1894                         cm_buf_t *bp = buf_Find(dscp, &hzero);
1895                         if (bp)
1896                             buf_Release(bp);
1897                         else
1898                             code = CM_ERROR_NOSUCHFILE;
1899                     }
1900                     cm_ReleaseSCache(dscp);
1901                     if (code) {
1902                         cm_FreeSpace(spacep);
1903                         cm_ReleaseUser(userp);
1904                         smb_SendTran2Error(vcp, p, opx, code);
1905                         smb_FreeTran2Packet(outp);
1906                         return 0;
1907                     }
1908                 }
1909             }
1910         }
1911                 cm_FreeSpace(spacep);
1912         }
1913
1914         /* now do namei and stat, and copy out the info */
1915     code = cm_NameI(cm_rootSCachep, (char *)(&p->parmsp[3]),
1916                     CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, tidPathp, &req, &scp);
1917
1918         if (code) {
1919                 cm_ReleaseUser(userp);
1920         smb_SendTran2Error(vcp, p, opx, code);
1921         smb_FreeTran2Packet(outp);
1922         return 0;
1923     }
1924
1925     lock_ObtainMutex(&scp->mx);
1926         code = cm_SyncOp(scp, NULL, userp, &req, 0,
1927                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
1928         if (code) goto done;
1929         
1930     /* now we have the status in the cache entry, and everything is locked.
1931          * Marshall the output data.
1932      */
1933         op = outp->datap;
1934         /* for info level 108, figure out short name */
1935         if (infoLevel == 0x108) {
1936                 code = cm_GetShortName((char *)(&p->parmsp[3]), userp, &req,
1937                                 tidPathp, scp->fid.vnode, shortName,
1938                                 (size_t *) &len);
1939                 if (code) {
1940                         goto done;
1941                 }
1942
1943                 op = outp->datap;
1944                 *((u_long *)op) = len * 2; op += 4;
1945                 mbstowcs((unsigned short *)op, shortName, len);
1946                 op += (len * 2);
1947
1948                 goto done;
1949         }
1950         if (infoLevel == 1 || infoLevel == 2) {
1951                 smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
1952         *((u_long *)op) = dosTime; op += 4;     /* creation time */
1953         *((u_long *)op) = dosTime; op += 4;     /* access time */
1954         *((u_long *)op) = dosTime; op += 4;     /* write time */
1955         *((u_long *)op) = scp->length.LowPart; op += 4; /* length */
1956         *((u_long *)op) = scp->length.LowPart; op += 4; /* alloc size */
1957                 attributes = smb_Attributes(scp);
1958                 *((u_short *)op) = attributes; op += 2; /* attributes */
1959         }
1960         else if (infoLevel == 0x101) {
1961                 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
1962                 *((FILETIME *)op) = ft; op += 8;        /* creation time */
1963                 *((FILETIME *)op) = ft; op += 8;        /* last access time */
1964                 *((FILETIME *)op) = ft; op += 8;        /* last write time */
1965                 *((FILETIME *)op) = ft; op += 8;        /* last change time */
1966                 extAttributes = smb_ExtAttributes(scp);
1967                 *((u_long *)op) = extAttributes; op += 4; /* extended attribs */
1968                 *((u_long *)op) = 0; op += 4;   /* don't know what this is */
1969         }
1970         else if (infoLevel == 0x102) {
1971                 *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* alloc size */
1972                 *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* EOF */
1973                 *((u_long *)op) = scp->linkCount; op += 4;
1974                 *op++ = 0;
1975                 *op++ = 0;
1976                 *op++ = (scp->fileType == CM_SCACHETYPE_DIRECTORY ? 1 : 0);
1977                 *op++ = 0;
1978         }
1979         else if (infoLevel == 0x103) {
1980                 memset(op, 0, 4); op += 4;      /* EA size */
1981         }
1982
1983         /* now, if we are being asked about extended attrs, return a 0 size */
1984         if (infoLevel == 2) {
1985                 *((u_long *)op) = 0; op += 4;
1986         }
1987         
1988
1989         /* send and free the packets */
1990   done:
1991         lock_ReleaseMutex(&scp->mx);
1992     cm_ReleaseSCache(scp);
1993     cm_ReleaseUser(userp);
1994         if (code == 0) 
1995         smb_SendTran2Packet(vcp, outp, opx);
1996     else 
1997         smb_SendTran2Error(vcp, p, opx, code);
1998     smb_FreeTran2Packet(outp);
1999
2000     return 0;
2001 }
2002
2003 long smb_ReceiveTran2SetPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2004 {
2005     return CM_ERROR_BADOP;
2006 }
2007
2008 long smb_ReceiveTran2QFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
2009 {
2010         smb_tran2Packet_t *outp;
2011         FILETIME ft;
2012         unsigned long attributes;
2013         unsigned short infoLevel;
2014         int nbytesRequired;
2015         unsigned short fid;
2016         cm_user_t *userp;
2017     smb_fid_t *fidp;
2018         cm_scache_t *scp;
2019         char *op;
2020         long code = 0;
2021         cm_req_t req;
2022
2023         cm_InitReq(&req);
2024
2025     fid = p->parmsp[0];
2026     fidp = smb_FindFID(vcp, fid, 0);
2027
2028         if (fidp == NULL) {
2029                 smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADFD);
2030                 return 0;
2031         }
2032
2033         infoLevel = p->parmsp[1];
2034         if (infoLevel == 0x101) nbytesRequired = 40;
2035         else if (infoLevel == 0x102) nbytesRequired = 24;
2036         else if (infoLevel == 0x103) nbytesRequired = 4;
2037         else if (infoLevel == 0x104) nbytesRequired = 6;
2038         else {
2039                 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
2040                  p->opcode, infoLevel);
2041                 smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
2042         smb_ReleaseFID(fidp);
2043                 return 0;
2044         }
2045         osi_Log2(smb_logp, "T2 QFileInfo type 0x%x fid %d", infoLevel, fid);
2046
2047         outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, nbytesRequired);
2048
2049         if (infoLevel > 0x100)
2050                 outp->totalParms = 2;
2051         else
2052                 outp->totalParms = 0;
2053         outp->totalData = nbytesRequired;
2054
2055         userp = smb_GetTran2User(vcp, p);
2056     if (!userp) {
2057         osi_Log1(smb_logp, "ReceiveTran2QFileInfo unable to resolve user [%d]", p->uid);
2058         code = CM_ERROR_BADSMB;
2059         goto done;
2060     }
2061
2062         scp = fidp->scp;
2063         lock_ObtainMutex(&scp->mx);
2064         code = cm_SyncOp(scp, NULL, userp, &req, 0,
2065                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2066         if (code) goto done;
2067
2068         /* now we have the status in the cache entry, and everything is locked.
2069          * Marshall the output data.
2070          */
2071         op = outp->datap;
2072         if (infoLevel == 0x101) {
2073                 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
2074                 *((FILETIME *)op) = ft; op += 8;        /* creation time */
2075                 *((FILETIME *)op) = ft; op += 8;        /* last access time */
2076                 *((FILETIME *)op) = ft; op += 8;        /* last write time */
2077                 *((FILETIME *)op) = ft; op += 8;        /* last change time */
2078                 attributes = smb_ExtAttributes(scp);
2079                 *((u_long *)op) = attributes; op += 4;
2080                 *((u_long *)op) = 0; op += 4;
2081         }
2082         else if (infoLevel == 0x102) {
2083                 *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* alloc size */
2084                 *((LARGE_INTEGER *)op) = scp->length; op += 8;  /* EOF */
2085                 *((u_long *)op) = scp->linkCount; op += 4;
2086                 *op++ = ((fidp->flags & SMB_FID_DELONCLOSE) ? 1 : 0);
2087                 *op++ = (scp->fileType == CM_SCACHETYPE_DIRECTORY ? 1 : 0);
2088                 *op++ = 0;
2089                 *op++ = 0;
2090         }
2091         else if (infoLevel == 0x103) {
2092                 *((u_long *)op) = 0; op += 4;
2093         }
2094         else if (infoLevel == 0x104) {
2095                 unsigned long len;
2096                 char *name;
2097
2098                 if (fidp->NTopen_wholepathp)
2099                         name = fidp->NTopen_wholepathp;
2100                 else
2101                         name = "\\";    /* probably can't happen */
2102                 len = strlen(name);
2103                 outp->totalData = (len*2) + 4;  /* this is actually what we want to return */
2104                 *((u_long *)op) = len * 2; op += 4;
2105                 mbstowcs((unsigned short *)op, name, len); op += (len * 2);
2106         }
2107
2108         /* send and free the packets */
2109   done:
2110         lock_ReleaseMutex(&scp->mx);
2111         cm_ReleaseUser(userp);
2112         smb_ReleaseFID(fidp);
2113         if (code == 0) smb_SendTran2Packet(vcp, outp, opx);
2114         else smb_SendTran2Error(vcp, p, opx, code);
2115         smb_FreeTran2Packet(outp);
2116
2117         return 0;
2118 }
2119
2120 long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
2121 {
2122         long code = 0;
2123         unsigned short fid;
2124         smb_fid_t *fidp;
2125         unsigned short infoLevel;
2126         smb_tran2Packet_t *outp;
2127         cm_user_t *userp;
2128         cm_scache_t *scp;
2129         cm_req_t req;
2130
2131         cm_InitReq(&req);
2132
2133     fid = p->parmsp[0];
2134         fidp = smb_FindFID(vcp, fid, 0);
2135
2136         if (fidp == NULL) {
2137                 smb_SendTran2Error(vcp, p, op, CM_ERROR_BADFD);
2138                 return 0;
2139         }
2140
2141         infoLevel = p->parmsp[1];
2142         if (infoLevel > 0x104 || infoLevel < 0x101) {
2143                 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
2144                          p->opcode, infoLevel);
2145                 smb_SendTran2Error(vcp, p, op, CM_ERROR_INVAL);
2146         smb_ReleaseFID(fidp);
2147                 return 0;
2148         }
2149
2150         if (infoLevel == 0x102 && !(fidp->flags & SMB_FID_OPENDELETE)) {
2151                 smb_SendTran2Error(vcp, p, op, CM_ERROR_NOACCESS);
2152         smb_ReleaseFID(fidp);
2153                 return 0;
2154         }
2155         if ((infoLevel == 0x103 || infoLevel == 0x104)
2156             && !(fidp->flags & SMB_FID_OPENWRITE)) {
2157                 smb_SendTran2Error(vcp, p, op, CM_ERROR_NOACCESS);
2158         smb_ReleaseFID(fidp);
2159                 return 0;
2160         }
2161
2162         osi_Log1(smb_logp, "T2 SFileInfo type 0x%x", infoLevel);
2163
2164         outp = smb_GetTran2ResponsePacket(vcp, p, op, 2, 0);
2165
2166         outp->totalParms = 2;
2167         outp->totalData = 0;
2168
2169         userp = smb_GetTran2User(vcp, p);
2170     if (!userp) {
2171         osi_Log1(smb_logp,"ReceiveTran2SetFileInfo unable to resolve user [%d]", p->uid);
2172         code = CM_ERROR_BADSMB;
2173         goto done;
2174     }
2175
2176         scp = fidp->scp;
2177
2178         if (infoLevel == 0x101) {
2179                 FILETIME lastMod;
2180                 unsigned int attribute;
2181                 cm_attr_t attr;
2182
2183                 /* lock the vnode with a callback; we need the current status
2184                  * to determine what the new status is, in some cases.
2185                  */
2186                 lock_ObtainMutex(&scp->mx);
2187                 code = cm_SyncOp(scp, NULL, userp, &req, 0,
2188                          CM_SCACHESYNC_GETSTATUS
2189                          | CM_SCACHESYNC_NEEDCALLBACK);
2190                 if (code) {
2191                         lock_ReleaseMutex(&scp->mx);
2192                         goto done;
2193                 }
2194
2195                 /* prepare for setattr call */
2196                 attr.mask = 0;
2197                 
2198                 lastMod = *((FILETIME *)(p->datap + 16));
2199                 /* when called as result of move a b, lastMod is (-1, -1). 
2200          * If the check for -1 is not present, timestamp
2201                  * of the resulting file will be 1969 (-1)
2202                  */
2203                 if (LargeIntegerNotEqualToZero(*((LARGE_INTEGER *)&lastMod)) && 
2204             lastMod.dwLowDateTime != -1 && lastMod.dwHighDateTime != -1) {
2205                         attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
2206                         smb_UnixTimeFromLargeSearchTime(&attr.clientModTime,
2207                                                         &lastMod);
2208                         fidp->flags |= SMB_FID_MTIMESETDONE;
2209                 }
2210                 
2211                 attribute = *((u_long *)(p->datap + 32));
2212                 if (attribute != 0) {
2213                         if ((scp->unixModeBits & 0222)
2214                             && (attribute & 1) != 0) {
2215                                 /* make a writable file read-only */
2216                                 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
2217                                 attr.unixModeBits = scp->unixModeBits & ~0222;
2218                         }
2219                         else if ((scp->unixModeBits & 0222) == 0
2220                                  && (attribute & 1) == 0) {
2221                                 /* make a read-only file writable */
2222                                 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
2223                                 attr.unixModeBits = scp->unixModeBits | 0222;
2224                         }
2225                 }
2226                 lock_ReleaseMutex(&scp->mx);
2227
2228                 /* call setattr */
2229                 if (attr.mask)
2230                         code = cm_SetAttr(scp, &attr, userp, &req);
2231                 else
2232                         code = 0;
2233         }
2234         else if (infoLevel == 0x103 || infoLevel == 0x104) {
2235                 LARGE_INTEGER size = *((LARGE_INTEGER *)(p->datap));
2236                 cm_attr_t attr;
2237
2238                 attr.mask = CM_ATTRMASK_LENGTH;
2239                 attr.length.LowPart = size.LowPart;
2240                 attr.length.HighPart = size.HighPart;
2241                 code = cm_SetAttr(scp, &attr, userp, &req);
2242         }
2243         else if (infoLevel == 0x102) {
2244                 if (*((char *)(p->datap))) {
2245                         code = cm_CheckNTDelete(fidp->NTopen_dscp, scp, userp,
2246                                                 &req);
2247                         if (code == 0)
2248                                 fidp->flags |= SMB_FID_DELONCLOSE;
2249                 }
2250                 else {
2251                         code = 0;
2252                         fidp->flags &= ~SMB_FID_DELONCLOSE;
2253                 }
2254         }
2255   done:
2256         cm_ReleaseUser(userp);
2257         smb_ReleaseFID(fidp);
2258         if (code == 0) smb_SendTran2Packet(vcp, outp, op);
2259         else smb_SendTran2Error(vcp, p, op, code);
2260         smb_FreeTran2Packet(outp);
2261
2262         return 0;
2263 }
2264
2265 long smb_ReceiveTran2FSCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2266 {
2267     return CM_ERROR_BADOP;
2268 }
2269
2270 long smb_ReceiveTran2IOCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2271 {
2272     return CM_ERROR_BADOP;
2273 }
2274
2275 long smb_ReceiveTran2FindNotifyFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2276 {
2277     return CM_ERROR_BADOP;
2278 }
2279
2280 long smb_ReceiveTran2FindNotifyNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2281 {
2282     return CM_ERROR_BADOP;
2283 }
2284
2285 long smb_ReceiveTran2MKDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2286 {
2287     return CM_ERROR_BADOP;
2288 }
2289
2290 long smb_ApplyV3DirListPatches(cm_scache_t *dscp,
2291         smb_dirListPatch_t **dirPatchespp, int infoLevel, cm_user_t *userp,
2292         cm_req_t *reqp)
2293 {
2294         long code = 0;
2295     cm_scache_t *scp;
2296     cm_scache_t *targetScp;                     /* target if scp is a symlink */
2297     char *dptr;
2298     long dosTime;
2299         FILETIME ft;
2300     int shortTemp;
2301     unsigned short attr;
2302         unsigned long lattr;
2303     smb_dirListPatch_t *patchp;
2304     smb_dirListPatch_t *npatchp;
2305         
2306     for(patchp = *dirPatchespp; patchp; patchp =
2307          (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
2308                 code = cm_GetSCache(&patchp->fid, &scp, userp, reqp);
2309         if (code) continue;
2310         lock_ObtainMutex(&scp->mx);
2311         code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2312                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2313                 if (code) { 
2314                         lock_ReleaseMutex(&scp->mx);
2315                         cm_ReleaseSCache(scp);
2316                         continue;
2317         }
2318                 
2319         /* now watch for a symlink */
2320         if (scp->fileType == CM_SCACHETYPE_SYMLINK) {
2321                         lock_ReleaseMutex(&scp->mx);
2322             code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, reqp);
2323             if (code == 0) {
2324                                 /* we have a more accurate file to use (the
2325                                  * target of the symbolic link).  Otherwise,
2326                                  * we'll just use the symlink anyway.
2327                  */
2328                                 osi_Log2(smb_logp, "symlink vp %x to vp %x",
2329                          scp, targetScp);
2330                                 cm_ReleaseSCache(scp);
2331                 scp = targetScp;
2332             }
2333             lock_ObtainMutex(&scp->mx);
2334         }
2335
2336                 dptr = patchp->dptr;
2337
2338                 if (infoLevel >= 0x101) {
2339                         /* get filetime */
2340                         smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
2341
2342                         /* copy to Creation Time */
2343                         *((FILETIME *)dptr) = ft;
2344                         dptr += 8;
2345
2346                         /* copy to Last Access Time */
2347                         *((FILETIME *)dptr) = ft;
2348                         dptr += 8;
2349
2350                         /* copy to Last Write Time */
2351                         *((FILETIME *)dptr) = ft;
2352                         dptr += 8;
2353
2354                         /* copy to Change Time */
2355                         *((FILETIME *)dptr) = ft;
2356                         dptr += 8;
2357
2358                         /* Use length for both file length and alloc length */
2359                         *((LARGE_INTEGER *)dptr) = scp->length;
2360                         dptr += 8;
2361                         *((LARGE_INTEGER *)dptr) = scp->length;
2362                         dptr += 8;
2363
2364                         /* Copy attributes */
2365                         lattr = smb_ExtAttributes(scp);
2366             /* merge in hidden (dot file) attribute */
2367                         if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
2368                                 lattr |= SMB_ATTR_HIDDEN;
2369                         *((u_long *)dptr) = lattr;
2370                         dptr += 4;
2371                 }
2372                 else {
2373                         /* get dos time */
2374                         smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
2375
2376                         /* and copy out date */
2377                         shortTemp = (dosTime>>16) & 0xffff;
2378                         *((u_short *)dptr) = shortTemp;
2379                         dptr += 2;
2380
2381                         /* copy out creation time */
2382                         shortTemp = dosTime & 0xffff;
2383                         *((u_short *)dptr) = shortTemp;
2384                         dptr += 2;
2385
2386                         /* and copy out date */
2387                         shortTemp = (dosTime>>16) & 0xffff;
2388                         *((u_short *)dptr) = shortTemp;
2389                         dptr += 2;
2390                         
2391                         /* copy out access time */
2392                         shortTemp = dosTime & 0xffff;
2393                         *((u_short *)dptr) = shortTemp;
2394                         dptr += 2;
2395
2396                         /* and copy out date */
2397                         shortTemp = (dosTime>>16) & 0xffff;
2398                         *((u_short *)dptr) = shortTemp;
2399                         dptr += 2;
2400                         
2401                         /* copy out mod time */
2402                         shortTemp = dosTime & 0xffff;
2403                         *((u_short *)dptr) = shortTemp;
2404                         dptr += 2;
2405
2406                         /* copy out file length and alloc length,
2407                          * using the same for both
2408                          */
2409                         *((u_long *)dptr) = scp->length.LowPart;
2410                         dptr += 4;
2411                         *((u_long *)dptr) = scp->length.LowPart;
2412                         dptr += 4;
2413
2414                         /* finally copy out attributes as short */
2415                         attr = smb_Attributes(scp);
2416             /* merge in hidden (dot file) attribute */
2417             if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE )
2418                 attr |= SMB_ATTR_HIDDEN;
2419                         *dptr++ = attr & 0xff;
2420                         *dptr++ = (attr >> 8) & 0xff;
2421                 }
2422
2423         lock_ReleaseMutex(&scp->mx);
2424         cm_ReleaseSCache(scp);
2425         }
2426         
2427     /* now free the patches */
2428     for(patchp = *dirPatchespp; patchp; patchp = npatchp) {
2429                 npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
2430         free(patchp);
2431         }
2432         
2433     /* and mark the list as empty */
2434     *dirPatchespp = NULL;
2435
2436     return code;
2437 }
2438
2439 #ifndef USE_OLD_MATCHING
2440 // char table for case insensitive comparison
2441 char mapCaseTable[256];
2442
2443 VOID initUpperCaseTable(VOID) 
2444 {
2445     int i;
2446     for (i = 0; i < 256; ++i) 
2447        mapCaseTable[i] = toupper(i);
2448     // make '"' match '.' 
2449     mapCaseTable[(int)'"'] = toupper('.');
2450     // make '<' match '*' 
2451     mapCaseTable[(int)'<'] = toupper('*');
2452     // make '>' match '?' 
2453     mapCaseTable[(int)'>'] = toupper('?');    
2454 }
2455
2456 // Compare 'pattern' (containing metacharacters '*' and '?') with the file
2457 // name 'name'.
2458 // Note : this procedure works recursively calling itself.
2459 // Parameters
2460 // PSZ pattern    : string containing metacharacters.
2461 // PSZ name       : file name to be compared with 'pattern'.
2462 // Return value
2463 // BOOL : TRUE/FALSE (match/mistmatch)
2464
2465 BOOL szWildCardMatchFileName(PSZ pattern, PSZ name) {
2466    PSZ pename;         // points to the last 'name' character
2467    PSZ p;
2468    pename = name + strlen(name) - 1;
2469    while (*name) {
2470       switch (*pattern) {
2471          case '?':
2472          case '>':
2473             if (*(++pattern) != '<' || *(++pattern) != '*') {
2474                if (*name == '.') 
2475                    return FALSE;
2476                ++name;
2477                break;
2478             } /* endif */
2479          case '<':
2480          case '*':
2481             while ((*pattern == '<') || (*pattern == '*') || (*pattern == '?') || (*pattern == '>')) 
2482                 ++pattern;
2483             if (!*pattern) 
2484                 return TRUE;
2485             for (p = pename; p >= name; --p) {
2486                if ((mapCaseTable[*p] == mapCaseTable[*pattern]) &&
2487                    szWildCardMatchFileName(pattern + 1, p + 1))
2488                   return TRUE;
2489             } /* endfor */
2490             return FALSE;
2491          default:
2492             if (mapCaseTable[*name] != mapCaseTable[*pattern]) 
2493                 return FALSE;
2494             ++pattern, ++name;
2495             break;
2496       } /* endswitch */
2497    } /* endwhile */ 
2498    return !*pattern;
2499 }
2500
2501 /* do a case-folding search of the star name mask with the name in namep.
2502  * Return 1 if we match, otherwise 0.
2503  */
2504 int smb_V3MatchMask(char *namep, char *maskp, int flags) 
2505 {
2506         /* make sure we only match 8.3 names, if requested */
2507         if ((flags & CM_FLAG_8DOT3) && !cm_Is8Dot3(namep)) 
2508         return 0;
2509         
2510         return szWildCardMatchFileName(maskp, namep) ? 1:0;
2511 }
2512
2513 #else /* USE_OLD_MATCHING */
2514 /* do a case-folding search of the star name mask with the name in namep.
2515  * Return 1 if we match, otherwise 0.
2516  */
2517 int smb_V3MatchMask(char *namep, char *maskp, int flags)
2518 {
2519         unsigned char tcp1, tcp2;       /* Pattern characters */
2520     unsigned char tcn1;         /* Name characters */
2521         int sawDot = 0, sawStar = 0, req8dot3 = 0;
2522         char *starNamep, *starMaskp;
2523         static char nullCharp[] = {0};
2524     int casefold = flags & CM_FLAG_CASEFOLD;
2525
2526         /* make sure we only match 8.3 names, if requested */
2527     req8dot3 = (flags & CM_FLAG_8DOT3);
2528         if (req8dot3 && !cm_Is8Dot3(namep)) 
2529         return 0;
2530
2531         /* loop */
2532         while (1) {
2533                 /* Next pattern character */
2534                 tcp1 = *maskp++;
2535
2536                 /* Next name character */
2537                 tcn1 = *namep;
2538
2539                 if (tcp1 == 0) {
2540                         /* 0 - end of pattern */
2541                         if (tcn1 == 0)
2542                                 return 1;
2543                         else
2544                                 return 0;
2545                 }
2546                 else if (tcp1 == '.' || tcp1 == '"') {
2547                         if (sawDot) {
2548                                 if (tcn1 == '.') {
2549                                         namep++;
2550                                         continue;
2551                                 } else
2552                                         return 0;
2553                         }
2554                         else {
2555                                 /*
2556                                  * first dot in pattern;
2557                                  * must match dot or end of name
2558                                  */
2559                                 sawDot = 1;
2560                                 if (tcn1 == 0)
2561                                         continue;
2562                                 else if (tcn1 == '.') {
2563                                         sawStar = 0;
2564                                         namep++;
2565                                         continue;
2566                                 }
2567                                 else
2568                                         return 0;
2569                         }
2570                 }
2571                 else if (tcp1 == '?') {
2572                         if (tcn1 == 0 || tcn1 == '.')
2573                                 return 0;
2574                         namep++;
2575                         continue;
2576                 }
2577                 else if (tcp1 == '>') {
2578                         if (tcn1 != 0 && tcn1 != '.')
2579                                 namep++;
2580                         continue;
2581                 }
2582                 else if (tcp1 == '*' || tcp1 == '<') {
2583                         tcp2 = *maskp++;
2584                         if (tcp2 == 0)
2585                                 return 1;
2586                         else if ((req8dot3 && tcp2 == '.') || tcp2 == '"') {
2587                                 while (req8dot3 && tcn1 != '.' && tcn1 != 0)
2588                                         tcn1 = *++namep;
2589                                 if (tcn1 == 0) {
2590                                         if (sawDot)
2591                                                 return 0;
2592                                         else
2593                                                 continue;
2594                                 }
2595                                 else {
2596                                         namep++;
2597                                         continue;
2598                                 }
2599                         }
2600                         else {
2601                                 /*
2602                                  * pattern character after '*' is not null or
2603                                  * period.  If it is '?' or '>', we are not
2604                                  * going to understand it.  If it is '*' or
2605                                  * '<', we are going to skip over it.  None of
2606                                  * these are likely, I hope.
2607                                  */
2608                                 /* skip over '*' and '<' */
2609                                 while (tcp2 == '*' || tcp2 == '<')
2610                                         tcp2 = *maskp++;
2611
2612                                 /* skip over characters that don't match tcp2 */
2613                                 while (req8dot3 && tcn1 != '.' && tcn1 != 0 && 
2614                        ((casefold && cm_foldUpper[tcn1] != cm_foldUpper[tcp2]) || 
2615                          (!casefold && tcn1 != tcp2)))
2616                                         tcn1 = *++namep;
2617
2618                                 /* No match */
2619                                 if ((req8dot3 && tcn1 == '.') || tcn1 == 0)
2620                                         return 0;
2621
2622                                 /* Remember where we are */
2623                                 sawStar = 1;
2624                                 starMaskp = maskp;
2625                                 starNamep = namep;
2626
2627                                 namep++;
2628                                 continue;
2629                         }
2630                 }
2631                 else {
2632                         /* tcp1 is not a wildcard */
2633             if ((casefold && cm_foldUpper[tcn1] == cm_foldUpper[tcp1]) || 
2634                 (!casefold && tcn1 == tcp1)) {
2635                                 /* they match */
2636                                 namep++;
2637                                 continue;
2638                         }
2639                         /* if trying to match a star pattern, go back */
2640                         if (sawStar) {
2641                                 maskp = starMaskp - 2;
2642                                 namep = starNamep + 1;
2643                                 sawStar = 0;
2644                                 continue;
2645                         }
2646                         /* that's all */
2647                         return 0;
2648                 }
2649         }
2650 }
2651 #endif /* USE_OLD_MATCHING */
2652
2653 long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
2654 {
2655         int attribute;
2656     long nextCookie;
2657     char *tp;
2658     long code = 0;
2659     char *pathp;
2660     cm_dirEntry_t *dep;
2661     int maxCount;
2662     smb_dirListPatch_t *dirListPatchesp;
2663     smb_dirListPatch_t *curPatchp;
2664     cm_buf_t *bufferp;
2665     long temp;
2666     long orbytes;                       /* # of bytes in this output record */
2667     long ohbytes;                       /* # of bytes, except file name */
2668     long onbytes;                       /* # of bytes in name, incl. term. null */
2669     osi_hyper_t dirLength;
2670     osi_hyper_t bufferOffset;
2671     osi_hyper_t curOffset;
2672     osi_hyper_t thyper;
2673     smb_dirSearch_t *dsp;
2674     cm_scache_t *scp;
2675     long entryInDir;
2676     long entryInBuffer;
2677         cm_pageHeader_t *pageHeaderp;
2678     cm_user_t *userp = NULL;
2679     int slotInPage;
2680     int returnedNames;
2681     long nextEntryCookie;
2682     int numDirChunks;           /* # of 32 byte dir chunks in this entry */
2683     char *op;                   /* output data ptr */
2684         char *origOp;                   /* original value of op */
2685     cm_space_t *spacep;         /* for pathname buffer */
2686     long maxReturnData;         /* max # of return data */
2687     long maxReturnParms;                /* max # of return parms */
2688     long bytesInBuffer;         /* # data bytes in the output buffer */
2689     int starPattern;
2690     char *maskp;                        /* mask part of path */
2691     int infoLevel;
2692     int searchFlags;
2693     int eos;
2694     smb_tran2Packet_t *outp;    /* response packet */
2695         char *tidPathp;
2696         int align;
2697         char shortName[13];             /* 8.3 name if needed */
2698         int NeedShortName;
2699     int foundInexact;
2700         char *shortNameEnd;
2701     int fileType;
2702     cm_fid_t fid;
2703
2704     cm_req_t req;
2705
2706         cm_InitReq(&req);
2707
2708         eos = 0;
2709         if (p->opcode == 1) {
2710                 /* find first; obtain basic parameters from request */
2711         attribute = p->parmsp[0];
2712         maxCount = p->parmsp[1];
2713         infoLevel = p->parmsp[3];
2714         searchFlags = p->parmsp[2];
2715         dsp = smb_NewDirSearch(1);
2716         dsp->attribute = attribute;
2717         pathp = ((char *) p->parmsp) + 12;      /* points to path */
2718         nextCookie = 0;
2719         maskp = strrchr(pathp, '\\');
2720         if (maskp == NULL) maskp = pathp;
2721                 else maskp++;   /* skip over backslash */
2722         strcpy(dsp->mask, maskp);       /* and save mask */
2723                 /* track if this is likely to match a lot of entries */
2724         starPattern = smb_V3IsStarMask(maskp);
2725         }
2726     else {
2727                 osi_assert(p->opcode == 2);
2728         /* find next; obtain basic parameters from request or open dir file */
2729         dsp = smb_FindDirSearch(p->parmsp[0]);
2730         if (!dsp) return CM_ERROR_BADFD;
2731         attribute = dsp->attribute;
2732         maxCount = p->parmsp[1];
2733         infoLevel = p->parmsp[2];
2734         searchFlags = p->parmsp[5];
2735         pathp = NULL;
2736         nextCookie = p->parmsp[3] | (p->parmsp[4] << 16);
2737         maskp = dsp->mask;
2738                 starPattern = 1;        /* assume, since required a Find Next */
2739     }
2740
2741         osi_Log4(smb_logp,
2742               "T2 search dir attr 0x%x, info level %d, max count %d, flags 0x%x",
2743               attribute, infoLevel, maxCount, searchFlags);
2744
2745         osi_Log2(smb_logp, "...T2 search op %d, nextCookie 0x%x",
2746               p->opcode, nextCookie);
2747
2748         if (infoLevel >= 0x101)
2749                 searchFlags &= ~4;      /* no resume keys */
2750
2751     dirListPatchesp = NULL;
2752
2753         maxReturnData = p->maxReturnData;
2754     if (p->opcode == 1) /* find first */
2755         maxReturnParms = 10;    /* bytes */
2756         else    
2757         maxReturnParms = 8;     /* bytes */
2758
2759 #ifndef CM_CONFIG_MULTITRAN2RESPONSES
2760     if (maxReturnData > 6000) 
2761         maxReturnData = 6000;
2762 #endif /* CM_CONFIG_MULTITRAN2RESPONSES */
2763
2764         outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
2765                                       maxReturnData);
2766
2767     osi_Log1(smb_logp, "T2 receive search dir %s",
2768              osi_LogSaveString(smb_logp, pathp));
2769         
2770     /* bail out if request looks bad */
2771     if (p->opcode == 1 && !pathp) {
2772         smb_ReleaseDirSearch(dsp);
2773         smb_FreeTran2Packet(outp);
2774         return CM_ERROR_BADSMB;
2775     }
2776         
2777         osi_Log2(smb_logp, "T2 dir search cookie 0x%x, connection %d",
2778              nextCookie, dsp->cookie);
2779
2780         userp = smb_GetTran2User(vcp, p);
2781     if (!userp) {
2782         osi_Log1(smb_logp, "T2 dir search unable to resolve user [%d]", p->uid);
2783         smb_ReleaseDirSearch(dsp);
2784         smb_FreeTran2Packet(outp);
2785         return CM_ERROR_BADSMB;
2786     }
2787
2788         /* try to get the vnode for the path name next */
2789         lock_ObtainMutex(&dsp->mx);
2790         if (dsp->scp) {
2791                 scp = dsp->scp;
2792         cm_HoldSCache(scp);
2793         code = 0;
2794     }
2795     else {
2796                 spacep = cm_GetSpace();
2797         smb_StripLastComponent(spacep->data, NULL, pathp);
2798         lock_ReleaseMutex(&dsp->mx);
2799
2800                 tidPathp = smb_GetTIDPath(vcp, p->tid);
2801         code = cm_NameI(cm_rootSCachep, spacep->data,
2802                         CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
2803                         userp, tidPathp, &req, &scp);
2804         cm_FreeSpace(spacep);
2805
2806         lock_ObtainMutex(&dsp->mx);
2807                 if (code == 0) {
2808             if (dsp->scp != 0) cm_ReleaseSCache(dsp->scp);
2809                         dsp->scp = scp;
2810                         /* we need one hold for the entry we just stored into,
2811              * and one for our own processing.  When we're done
2812                          * with this function, we'll drop the one for our own
2813                          * processing.  We held it once from the namei call,
2814                          * and so we do another hold now.
2815              */
2816             cm_HoldSCache(scp);
2817                         lock_ObtainMutex(&scp->mx);
2818                         if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
2819                             && LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
2820                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
2821                                 dsp->flags |= SMB_DIRSEARCH_BULKST;
2822                         }
2823                         lock_ReleaseMutex(&scp->mx);
2824         }
2825     }
2826         lock_ReleaseMutex(&dsp->mx);
2827     if (code) {
2828                 cm_ReleaseUser(userp);
2829         smb_FreeTran2Packet(outp);
2830                 smb_DeleteDirSearch(dsp);
2831                 smb_ReleaseDirSearch(dsp);
2832         return code;
2833         }
2834
2835     /* get the directory size */
2836         lock_ObtainMutex(&scp->mx);
2837     code = cm_SyncOp(scp, NULL, userp, &req, 0,
2838                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2839         if (code) {
2840                 lock_ReleaseMutex(&scp->mx);
2841         cm_ReleaseSCache(scp);
2842         cm_ReleaseUser(userp);
2843         smb_FreeTran2Packet(outp);
2844                 smb_DeleteDirSearch(dsp);
2845                 smb_ReleaseDirSearch(dsp);
2846         return code;
2847     }
2848
2849   startsearch:
2850     dirLength = scp->length;
2851     bufferp = NULL;
2852     bufferOffset.LowPart = bufferOffset.HighPart = 0;
2853     curOffset.HighPart = 0;
2854     curOffset.LowPart = nextCookie;
2855         origOp = outp->datap;
2856
2857     foundInexact = 0;
2858     code = 0;
2859     returnedNames = 0;
2860     bytesInBuffer = 0;
2861     while (1) {
2862                 op = origOp;
2863                 if (searchFlags & 4)
2864                         /* skip over resume key */
2865                         op += 4;
2866
2867                 /* make sure that curOffset.LowPart doesn't point to the first
2868          * 32 bytes in the 2nd through last dir page, and that it doesn't
2869          * point at the first 13 32-byte chunks in the first dir page,
2870          * since those are dir and page headers, and don't contain useful
2871          * information.
2872          */
2873                 temp = curOffset.LowPart & (2048-1);
2874         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
2875                         /* we're in the first page */
2876             if (temp < 13*32) temp = 13*32;
2877                 }
2878                 else {
2879                         /* we're in a later dir page */
2880             if (temp < 32) temp = 32;
2881         }
2882                 
2883         /* make sure the low order 5 bits are zero */
2884         temp &= ~(32-1);
2885                 
2886         /* now put temp bits back ito curOffset.LowPart */
2887         curOffset.LowPart &= ~(2048-1);
2888         curOffset.LowPart |= temp;
2889
2890         /* check if we've passed the dir's EOF */
2891         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength)) {
2892             eos = 1;
2893             break;
2894         }
2895
2896         /* check if we've returned all the names that will fit in the
2897          * response packet; we check return count as well as the number
2898          * of bytes requested.  We check the # of bytes after we find
2899          * the dir entry, since we'll need to check its size.
2900          */
2901         if (returnedNames >= maxCount) {
2902             break;
2903         }
2904
2905         /* see if we can use the bufferp we have now; compute in which
2906          * page the current offset would be, and check whether that's
2907          * the offset of the buffer we have.  If not, get the buffer.
2908          */
2909         thyper.HighPart = curOffset.HighPart;
2910         thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
2911         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
2912                         /* wrong buffer */
2913             if (bufferp) {
2914                 buf_Release(bufferp);
2915                 bufferp = NULL;
2916                         }       
2917                         lock_ReleaseMutex(&scp->mx);
2918                         lock_ObtainRead(&scp->bufCreateLock);
2919             code = buf_Get(scp, &thyper, &bufferp);
2920                         lock_ReleaseRead(&scp->bufCreateLock);
2921
2922                         /* now, if we're doing a star match, do bulk fetching
2923                          * of all of the status info for files in the dir.
2924              */
2925             if (starPattern) {
2926                                 smb_ApplyV3DirListPatches(scp, &dirListPatchesp,
2927                                           infoLevel, userp,
2928                                           &req);
2929                                 if ((dsp->flags & SMB_DIRSEARCH_BULKST)
2930                     && LargeIntegerGreaterThanOrEqualTo(thyper, scp->bulkStatProgress)) {
2931                                         /* Don't bulk stat if risking timeout */
2932                                         int now = GetCurrentTime();
2933                                         if (now - req.startTime > 5000) {
2934                                                 scp->bulkStatProgress = thyper;
2935                                                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
2936                                                 dsp->flags &= ~SMB_DIRSEARCH_BULKST;
2937                                         } else
2938                         cm_TryBulkStat(scp, &thyper, userp, &req);
2939                                 }
2940                         }
2941
2942             lock_ObtainMutex(&scp->mx);
2943             if (code) break;
2944             bufferOffset = thyper;
2945
2946             /* now get the data in the cache */
2947             while (1) {
2948                                 code = cm_SyncOp(scp, bufferp, userp, &req,
2949                                  PRSFS_LOOKUP,
2950                                  CM_SCACHESYNC_NEEDCALLBACK
2951                                  | CM_SCACHESYNC_READ);
2952                                 if (code) break;
2953                                 
2954                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
2955
2956                 /* otherwise, load the buffer and try again */
2957                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
2958                                     &req);
2959                 if (code) break;
2960             }
2961             if (code) {
2962                                 buf_Release(bufferp);
2963                 bufferp = NULL;
2964                 break;
2965                         }
2966         }       /* if (wrong buffer) ... */
2967                 
2968         /* now we have the buffer containing the entry we're interested
2969          * in; copy it out if it represents a non-deleted entry.
2970          */
2971                 entryInDir = curOffset.LowPart & (2048-1);
2972         entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
2973
2974                 /* page header will help tell us which entries are free.  Page
2975                  * header can change more often than once per buffer, since
2976                  * AFS 3 dir page size may be less than (but not more than)
2977                  * a buffer package buffer.
2978          */
2979                 /* only look intra-buffer */
2980                 temp = curOffset.LowPart & (buf_bufferSize - 1);
2981         temp &= ~(2048 - 1);    /* turn off intra-page bits */
2982                 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
2983
2984                 /* now determine which entry we're looking at in the page.
2985                  * If it is free (there's a free bitmap at the start of the
2986                  * dir), we should skip these 32 bytes.
2987          */
2988         slotInPage = (entryInDir & 0x7e0) >> 5;
2989         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
2990                & (1 << (slotInPage & 0x7)))) {
2991                         /* this entry is free */
2992             numDirChunks = 1;   /* only skip this guy */
2993             goto nextEntry;
2994         }
2995
2996                 tp = bufferp->datap + entryInBuffer;
2997         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
2998
2999         /* while we're here, compute the next entry's location, too,
3000                  * since we'll need it when writing out the cookie into the dir
3001                  * listing stream.
3002          *
3003          * XXXX Probably should do more sanity checking.
3004          */
3005                 numDirChunks = cm_NameEntries(dep->name, &onbytes);
3006                 
3007         /* compute offset of cookie representing next entry */
3008         nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
3009
3010                 /* Need 8.3 name? */
3011                 NeedShortName = 0;
3012                 if (infoLevel == 0x104
3013                     && dep->fid.vnode != 0
3014                     && !cm_Is8Dot3(dep->name)) {
3015                         cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
3016                         NeedShortName = 1;
3017                 }
3018
3019         /* When matching, we are using doing a case fold if we have a wildcard mask.
3020          * If we get a non-wildcard match, it's a lookup for a specific file. 
3021          */
3022         if (dep->fid.vnode != 0 && 
3023             (smb_V3MatchMask(dep->name, maskp, (starPattern? CM_FLAG_CASEFOLD : 0))
3024               || (NeedShortName
3025                    && smb_V3MatchMask(shortName, maskp, CM_FLAG_CASEFOLD)))) {
3026
3027             /* Eliminate entries that don't match requested attributes */
3028             if (smb_hideDotFiles && !(dsp->attribute & SMB_ATTR_HIDDEN) && 
3029                  smb_IsDotFile(dep->name))
3030                 goto nextEntry; /* no hidden files */
3031                     
3032             if (!(dsp->attribute & SMB_ATTR_DIRECTORY))  /* no directories */
3033             {
3034                 /* We have already done the cm_TryBulkStat above */
3035                 fid.cell = scp->fid.cell;
3036                 fid.volume = scp->fid.volume;
3037                 fid.vnode = ntohl(dep->fid.vnode);
3038                 fid.unique = ntohl(dep->fid.unique);
3039                 fileType = cm_FindFileType(&fid);
3040                 /*osi_Log2(smb_logp, "smb_ReceiveTran2SearchDir: file %s "
3041                  "has filetype %d", dep->name,
3042                  fileType);*/
3043                 if (fileType == CM_SCACHETYPE_DIRECTORY)
3044                     goto nextEntry;
3045             }
3046
3047                         /* finally check if this name will fit */
3048
3049                         /* standard dir entry stuff */
3050                         if (infoLevel < 0x101)
3051                                 ohbytes = 23;   /* pre-NT */
3052                         else if (infoLevel == 0x103)
3053                                 ohbytes = 12;   /* NT names only */
3054                         else
3055                                 ohbytes = 64;   /* NT */
3056
3057                         if (infoLevel == 0x104)
3058                                 ohbytes += 26;  /* Short name & length */
3059
3060             if (searchFlags & 4) {
3061                 ohbytes += 4;   /* if resume key required */
3062                         }   
3063
3064             if (infoLevel != 1
3065                  && infoLevel != 0x101
3066                  && infoLevel != 0x103)
3067                                 ohbytes += 4;   /* EASIZE */
3068
3069                         /* add header to name & term. null */
3070                         orbytes = onbytes + ohbytes + 1;
3071
3072                         /* now, we round up the record to a 4 byte alignment,
3073                          * and we make sure that we have enough room here for
3074                          * even the aligned version (so we don't have to worry
3075                          * about an * overflow when we pad things out below).
3076                          * That's the reason for the alignment arithmetic below.
3077              */
3078                         if (infoLevel >= 0x101)
3079                                 align = (4 - (orbytes & 3)) & 3;
3080                         else
3081                                 align = 0;
3082                         if (orbytes + bytesInBuffer + align > maxReturnData)
3083                 break;
3084
3085                         /* this is one of the entries to use: it is not deleted
3086                          * and it matches the star pattern we're looking for.
3087                          * Put out the name, preceded by its length.
3088              */
3089                         /* First zero everything else */
3090                         memset(origOp, 0, ohbytes);
3091
3092                         if (infoLevel <= 0x101)
3093                 *(origOp + ohbytes - 1) = (unsigned char) onbytes;
3094                         else if (infoLevel == 0x103)
3095                                 *((u_long *)(op + 8)) = onbytes;
3096                         else
3097                                 *((u_long *)(op + 60)) = onbytes;
3098             strcpy(origOp+ohbytes, dep->name);
3099
3100                         /* Short name if requested and needed */
3101             if (infoLevel == 0x104) {
3102                                 if (NeedShortName) {
3103                                         strcpy(op + 70, shortName);
3104                                         *(op + 68) = shortNameEnd - shortName;
3105                                 }
3106                         }
3107
3108             /* now, adjust the # of entries copied */
3109             returnedNames++;
3110
3111                         /* NextEntryOffset and FileIndex */
3112                         if (infoLevel >= 101) {
3113                                 int entryOffset = orbytes + align;
3114                                 *((u_long *)op) = entryOffset;
3115                                 *((u_long *)(op+4)) = nextEntryCookie;
3116                         }
3117
3118             /* now we emit the attribute.  This is tricky, since
3119              * we need to really stat the file to find out what
3120                          * type of entry we've got.  Right now, we're copying
3121                          * out data from * a buffer, while holding the scp
3122                          * locked, so it isn't really convenient to stat
3123                          * something now.  We'll put in a place holder
3124              * now, and make a second pass before returning this
3125                          * to get the real attributes.  So, we just skip the
3126                          * data for now, and adjust it later.  We allocate a
3127                          * patch record to make it easy to find this point
3128                          * later.  The replay will happen at a time when it is
3129                          * safe to unlock the directory.
3130              */
3131                         if (infoLevel != 0x103) {
3132                                 curPatchp = malloc(sizeof(*curPatchp));
3133                 osi_QAdd((osi_queue_t **) &dirListPatchesp,
3134                           &curPatchp->q);
3135                                 curPatchp->dptr = op;
3136                                 if (infoLevel >= 0x101)
3137                                         curPatchp->dptr += 8;
3138
3139                 if (smb_hideDotFiles && smb_IsDotFile(dep->name)) {
3140                     curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
3141                 }
3142                 else
3143                     curPatchp->flags = 0;
3144
3145                                 curPatchp->fid.cell = scp->fid.cell;
3146                                 curPatchp->fid.volume = scp->fid.volume;
3147                                 curPatchp->fid.vnode = ntohl(dep->fid.vnode);
3148                                 curPatchp->fid.unique = ntohl(dep->fid.unique);
3149
3150                 /* temp */
3151                 curPatchp->dep = dep;
3152                         }   
3153
3154                         if (searchFlags & 4)
3155                                 /* put out resume key */
3156                                 *((u_long *)origOp) = nextEntryCookie;
3157
3158                         /* Adjust byte ptr and count */
3159                         origOp += orbytes;      /* skip entire record */
3160             bytesInBuffer += orbytes;
3161
3162                         /* and pad the record out */
3163             while (--align >= 0) {
3164                                 *origOp++ = 0;
3165                 bytesInBuffer++;
3166             }
3167
3168                 }       /* if we're including this name */
3169         else if (!NeedShortName &&
3170                  !starPattern &&
3171                  !foundInexact &&
3172                                                         dep->fid.vnode != 0 &&
3173                  smb_V3MatchMask(dep->name, maskp, CM_FLAG_CASEFOLD)) {
3174             /* We were looking for exact matches, but here's an inexact one*/
3175             foundInexact = 1;
3176         }
3177                 
3178       nextEntry:
3179         /* and adjust curOffset to be where the new cookie is */
3180                 thyper.HighPart = 0;
3181         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
3182         curOffset = LargeIntegerAdd(thyper, curOffset);
3183     }           /* while copying data for dir listing */
3184
3185     /* If we didn't get a star pattern, we did an exact match during the first pass. 
3186      * If there were no exact matches found, we fail over to inexact matches by
3187      * marking the query as a star pattern (matches all case permutations), and
3188      * re-running the query. 
3189      */
3190     if (returnedNames == 0 && !starPattern && foundInexact) {
3191         osi_Log0(afsd_logp,"T2 Search: No exact matches. Re-running for inexact matches");
3192         starPattern = 1;
3193         goto startsearch;
3194     }
3195
3196         /* release the mutex */
3197         lock_ReleaseMutex(&scp->mx);
3198     if (bufferp) buf_Release(bufferp);
3199
3200         /* apply and free last set of patches; if not doing a star match, this
3201          * will be empty, but better safe (and freeing everything) than sorry.
3202      */
3203     smb_ApplyV3DirListPatches(scp, &dirListPatchesp, infoLevel, userp,
3204                               &req);
3205         
3206     /* now put out the final parameters */
3207         if (returnedNames == 0) eos = 1;
3208     if (p->opcode == 1) {
3209                 /* find first */
3210         outp->parmsp[0] = (unsigned short) dsp->cookie;
3211         outp->parmsp[1] = returnedNames;
3212         outp->parmsp[2] = eos;
3213         outp->parmsp[3] = 0;            /* nothing wrong with EAS */
3214         outp->parmsp[4] = 0;    
3215         /* don't need last name to continue
3216          * search, cookie is enough.  Normally,
3217          * this is the offset of the file name
3218          * of the last entry returned.
3219          */
3220         outp->totalParms = 10;  /* in bytes */
3221     }
3222     else {
3223         /* find next */
3224         outp->parmsp[0] = returnedNames;
3225         outp->parmsp[1] = eos;
3226         outp->parmsp[2] = 0;    /* EAS error */
3227         outp->parmsp[3] = 0;    /* last name, as above */
3228         outp->totalParms = 8;   /* in bytes */
3229     }   
3230
3231         /* return # of bytes in the buffer */
3232     outp->totalData = bytesInBuffer;
3233
3234         osi_Log2(smb_logp, "T2 search dir done, %d names, code %d",
3235               returnedNames, code);
3236
3237         /* Return error code if unsuccessful on first request */
3238         if (code == 0 && p->opcode == 1 && returnedNames == 0)
3239                 code = CM_ERROR_NOSUCHFILE;
3240
3241         /* if we're supposed to close the search after this request, or if
3242      * we're supposed to close the search if we're done, and we're done,
3243      * or if something went wrong, close the search.
3244      */
3245     /* ((searchFlags & 1) || ((searchFlags & 2) && eos) */
3246         if ((searchFlags & 1) || (returnedNames == 0) || 
3247          ((searchFlags & 2) && eos) || code != 0)
3248             smb_DeleteDirSearch(dsp);
3249         if (code)
3250         smb_SendTran2Error(vcp, p, opx, code);
3251         else {
3252         smb_SendTran2Packet(vcp, outp, opx);
3253         }
3254         smb_FreeTran2Packet(outp);
3255     smb_ReleaseDirSearch(dsp);
3256     cm_ReleaseSCache(scp);
3257     cm_ReleaseUser(userp);
3258     return 0;
3259 }
3260
3261 long smb_ReceiveV3FindClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3262 {
3263     int dirHandle;
3264     smb_dirSearch_t *dsp;
3265
3266     dirHandle = smb_GetSMBParm(inp, 0);
3267         
3268     osi_Log1(smb_logp, "SMB3 find close handle %d", dirHandle);
3269
3270     dsp = smb_FindDirSearch(dirHandle);
3271         
3272     if (!dsp)
3273                 return CM_ERROR_BADFD;
3274         
3275     /* otherwise, we have an FD to destroy */
3276     smb_DeleteDirSearch(dsp);
3277     smb_ReleaseDirSearch(dsp);
3278         
3279         /* and return results */
3280         smb_SetSMBDataLength(outp, 0);
3281
3282     return 0;
3283 }
3284
3285 long smb_ReceiveV3FindNotifyClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3286 {
3287         smb_SetSMBDataLength(outp, 0);
3288     return 0;
3289 }
3290
3291 long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3292 {
3293         char *pathp;
3294     long code = 0;
3295         cm_space_t *spacep;
3296     int excl;
3297     cm_user_t *userp;
3298     cm_scache_t *dscp;          /* dir we're dealing with */
3299     cm_scache_t *scp;           /* file we're creating */
3300     cm_attr_t setAttr;
3301     int initialModeBits;
3302     smb_fid_t *fidp;
3303     int attributes;
3304     char *lastNamep;
3305     long dosTime;
3306     int openFun;
3307     int trunc;
3308     int openMode;
3309     int extraInfo;
3310     int openAction;
3311     int parmSlot;                       /* which parm we're dealing with */
3312         char *tidPathp;
3313         cm_req_t req;
3314
3315         cm_InitReq(&req);
3316
3317     scp = NULL;
3318         
3319         extraInfo = (smb_GetSMBParm(inp, 2) & 1);       /* return extra info */
3320         openFun = smb_GetSMBParm(inp, 8);       /* open function */
3321     excl = ((openFun & 3) == 0);
3322     trunc = ((openFun & 3) == 2);               /* truncate it */
3323         openMode = (smb_GetSMBParm(inp, 3) & 0x7);
3324     openAction = 0;                     /* tracks what we did */
3325
3326     attributes = smb_GetSMBParm(inp, 5);
3327     dosTime = smb_GetSMBParm(inp, 6) | (smb_GetSMBParm(inp, 7) << 16);
3328
3329         /* compute initial mode bits based on read-only flag in attributes */
3330     initialModeBits = 0666;
3331     if (attributes & 1) initialModeBits &= ~0222;
3332         
3333     pathp = smb_GetSMBData(inp, NULL);
3334
3335         spacep = inp->spacep;
3336     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
3337
3338         if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
3339                 /* special case magic file name for receiving IOCTL requests
3340          * (since IOCTL calls themselves aren't getting through).
3341          */
3342 #ifdef NOTSERVICE
3343         osi_Log0(smb_logp, "IOCTL Open");
3344 #endif
3345
3346         fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
3347         smb_SetupIoctlFid(fidp, spacep);
3348
3349                 /* set inp->fid so that later read calls in same msg can find fid */
3350         inp->fid = fidp->fid;
3351         
3352         /* copy out remainder of the parms */
3353                 parmSlot = 2;
3354                 smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
3355                 if (extraInfo) {
3356             smb_SetSMBParm(outp, parmSlot, /* attrs */ 0); parmSlot++;
3357             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;      /* mod time */
3358             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
3359             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;      /* len */
3360             smb_SetSMBParm(outp, parmSlot, 0x7fff); parmSlot++;
3361             smb_SetSMBParm(outp, parmSlot, openMode); parmSlot++;
3362             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* file type 0 ==> normal file or dir */
3363             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* IPC junk */