smb-auth-20040711
[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 }
2451
2452 // Compare 'pattern' (containing metacharacters '*' and '?') with the file
2453 // name 'name'.
2454 // Note : this procedure works recursively calling itself.
2455 // Parameters
2456 // PSZ pattern    : string containing metacharacters.
2457 // PSZ name       : file name to be compared with 'pattern'.
2458 // Return value
2459 // BOOL : TRUE/FALSE (match/mistmatch)
2460
2461 BOOL szWildCardMatchFileName(PSZ pattern, PSZ name) {
2462    PSZ pename;         // points to the last 'name' character
2463    PSZ p;
2464    pename = name + strlen(name) - 1;
2465    while (*name) {
2466       switch (*pattern) {
2467          case '?':
2468             if (*(++pattern) != '<' || *(++pattern) != '*') {
2469                if (*name == '.') return FALSE;
2470                ++name;
2471                break;
2472             } /* endif */
2473          case '<':
2474          case '*':
2475             while ((*pattern == '<') || (*pattern == '*') || (*pattern == '?')) ++pattern;
2476             if (!*pattern) return TRUE;
2477             for (p = pename; p >= name; --p) {
2478                if ((mapCaseTable[*p] == mapCaseTable[*pattern]) &&
2479                    szWildCardMatchFileName(pattern + 1, p + 1))
2480                   return TRUE;
2481             } /* endfor */
2482             return FALSE;
2483          default:
2484             if (mapCaseTable[*name] != mapCaseTable[*pattern]) return FALSE;
2485             ++pattern, ++name;
2486             break;
2487       } /* endswitch */
2488    } /* endwhile */ return !*pattern;
2489 }
2490
2491 /* do a case-folding search of the star name mask with the name in namep.
2492  * Return 1 if we match, otherwise 0.
2493  */
2494 int smb_V3MatchMask(char *namep, char *maskp, int flags) 
2495 {
2496         /* make sure we only match 8.3 names, if requested */
2497         if ((flags & CM_FLAG_8DOT3) && !cm_Is8Dot3(namep)) 
2498         return 0;
2499         
2500         return szWildCardMatchFileName(maskp, namep) ? 1:0;
2501 }
2502
2503 #else /* USE_OLD_MATCHING */
2504 /* do a case-folding search of the star name mask with the name in namep.
2505  * Return 1 if we match, otherwise 0.
2506  */
2507 int smb_V3MatchMask(char *namep, char *maskp, int flags)
2508 {
2509         unsigned char tcp1, tcp2;       /* Pattern characters */
2510     unsigned char tcn1;         /* Name characters */
2511         int sawDot = 0, sawStar = 0, req8dot3 = 0;
2512         char *starNamep, *starMaskp;
2513         static char nullCharp[] = {0};
2514     int casefold = flags & CM_FLAG_CASEFOLD;
2515
2516         /* make sure we only match 8.3 names, if requested */
2517     req8dot3 = (flags & CM_FLAG_8DOT3);
2518         if (req8dot3 && !cm_Is8Dot3(namep)) 
2519         return 0;
2520
2521         /* loop */
2522         while (1) {
2523                 /* Next pattern character */
2524                 tcp1 = *maskp++;
2525
2526                 /* Next name character */
2527                 tcn1 = *namep;
2528
2529                 if (tcp1 == 0) {
2530                         /* 0 - end of pattern */
2531                         if (tcn1 == 0)
2532                                 return 1;
2533                         else
2534                                 return 0;
2535                 }
2536                 else if (tcp1 == '.' || tcp1 == '"') {
2537                         if (sawDot) {
2538                                 if (tcn1 == '.') {
2539                                         namep++;
2540                                         continue;
2541                                 } else
2542                                         return 0;
2543                         }
2544                         else {
2545                                 /*
2546                                  * first dot in pattern;
2547                                  * must match dot or end of name
2548                                  */
2549                                 sawDot = 1;
2550                                 if (tcn1 == 0)
2551                                         continue;
2552                                 else if (tcn1 == '.') {
2553                                         sawStar = 0;
2554                                         namep++;
2555                                         continue;
2556                                 }
2557                                 else
2558                                         return 0;
2559                         }
2560                 }
2561                 else if (tcp1 == '?') {
2562                         if (tcn1 == 0 || tcn1 == '.')
2563                                 return 0;
2564                         namep++;
2565                         continue;
2566                 }
2567                 else if (tcp1 == '>') {
2568                         if (tcn1 != 0 && tcn1 != '.')
2569                                 namep++;
2570                         continue;
2571                 }
2572                 else if (tcp1 == '*' || tcp1 == '<') {
2573                         tcp2 = *maskp++;
2574                         if (tcp2 == 0)
2575                                 return 1;
2576                         else if ((req8dot3 && tcp2 == '.') || tcp2 == '"') {
2577                                 while (req8dot3 && tcn1 != '.' && tcn1 != 0)
2578                                         tcn1 = *++namep;
2579                                 if (tcn1 == 0) {
2580                                         if (sawDot)
2581                                                 return 0;
2582                                         else
2583                                                 continue;
2584                                 }
2585                                 else {
2586                                         namep++;
2587                                         continue;
2588                                 }
2589                         }
2590                         else {
2591                                 /*
2592                                  * pattern character after '*' is not null or
2593                                  * period.  If it is '?' or '>', we are not
2594                                  * going to understand it.  If it is '*' or
2595                                  * '<', we are going to skip over it.  None of
2596                                  * these are likely, I hope.
2597                                  */
2598                                 /* skip over '*' and '<' */
2599                                 while (tcp2 == '*' || tcp2 == '<')
2600                                         tcp2 = *maskp++;
2601
2602                                 /* skip over characters that don't match tcp2 */
2603                                 while (req8dot3 && tcn1 != '.' && tcn1 != 0 && 
2604                        ((casefold && cm_foldUpper[tcn1] != cm_foldUpper[tcp2]) || 
2605                          (!casefold && tcn1 != tcp2)))
2606                                         tcn1 = *++namep;
2607
2608                                 /* No match */
2609                                 if ((req8dot3 && tcn1 == '.') || tcn1 == 0)
2610                                         return 0;
2611
2612                                 /* Remember where we are */
2613                                 sawStar = 1;
2614                                 starMaskp = maskp;
2615                                 starNamep = namep;
2616
2617                                 namep++;
2618                                 continue;
2619                         }
2620                 }
2621                 else {
2622                         /* tcp1 is not a wildcard */
2623             if ((casefold && cm_foldUpper[tcn1] == cm_foldUpper[tcp1]) || 
2624                 (!casefold && tcn1 == tcp1)) {
2625                                 /* they match */
2626                                 namep++;
2627                                 continue;
2628                         }
2629                         /* if trying to match a star pattern, go back */
2630                         if (sawStar) {
2631                                 maskp = starMaskp - 2;
2632                                 namep = starNamep + 1;
2633                                 sawStar = 0;
2634                                 continue;
2635                         }
2636                         /* that's all */
2637                         return 0;
2638                 }
2639         }
2640 }
2641 #endif /* USE_OLD_MATCHING */
2642
2643 long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
2644 {
2645         int attribute;
2646     long nextCookie;
2647     char *tp;
2648     long code = 0;
2649     char *pathp;
2650     cm_dirEntry_t *dep;
2651     int maxCount;
2652     smb_dirListPatch_t *dirListPatchesp;
2653     smb_dirListPatch_t *curPatchp;
2654     cm_buf_t *bufferp;
2655     long temp;
2656     long orbytes;                       /* # of bytes in this output record */
2657     long ohbytes;                       /* # of bytes, except file name */
2658     long onbytes;                       /* # of bytes in name, incl. term. null */
2659     osi_hyper_t dirLength;
2660     osi_hyper_t bufferOffset;
2661     osi_hyper_t curOffset;
2662     osi_hyper_t thyper;
2663     smb_dirSearch_t *dsp;
2664     cm_scache_t *scp;
2665     long entryInDir;
2666     long entryInBuffer;
2667         cm_pageHeader_t *pageHeaderp;
2668     cm_user_t *userp = NULL;
2669     int slotInPage;
2670     int returnedNames;
2671     long nextEntryCookie;
2672     int numDirChunks;           /* # of 32 byte dir chunks in this entry */
2673     char *op;                   /* output data ptr */
2674         char *origOp;                   /* original value of op */
2675     cm_space_t *spacep;         /* for pathname buffer */
2676     long maxReturnData;         /* max # of return data */
2677     long maxReturnParms;                /* max # of return parms */
2678     long bytesInBuffer;         /* # data bytes in the output buffer */
2679     int starPattern;
2680     char *maskp;                        /* mask part of path */
2681     int infoLevel;
2682     int searchFlags;
2683     int eos;
2684     smb_tran2Packet_t *outp;    /* response packet */
2685         char *tidPathp;
2686         int align;
2687         char shortName[13];             /* 8.3 name if needed */
2688         int NeedShortName;
2689     int foundInexact;
2690         char *shortNameEnd;
2691     int fileType;
2692     cm_fid_t fid;
2693
2694     cm_req_t req;
2695
2696         cm_InitReq(&req);
2697
2698         eos = 0;
2699         if (p->opcode == 1) {
2700                 /* find first; obtain basic parameters from request */
2701         attribute = p->parmsp[0];
2702         maxCount = p->parmsp[1];
2703         infoLevel = p->parmsp[3];
2704         searchFlags = p->parmsp[2];
2705         dsp = smb_NewDirSearch(1);
2706         dsp->attribute = attribute;
2707         pathp = ((char *) p->parmsp) + 12;      /* points to path */
2708         nextCookie = 0;
2709         maskp = strrchr(pathp, '\\');
2710         if (maskp == NULL) maskp = pathp;
2711                 else maskp++;   /* skip over backslash */
2712         strcpy(dsp->mask, maskp);       /* and save mask */
2713                 /* track if this is likely to match a lot of entries */
2714         starPattern = smb_V3IsStarMask(maskp);
2715         }
2716     else {
2717                 osi_assert(p->opcode == 2);
2718         /* find next; obtain basic parameters from request or open dir file */
2719         dsp = smb_FindDirSearch(p->parmsp[0]);
2720         if (!dsp) return CM_ERROR_BADFD;
2721         attribute = dsp->attribute;
2722         maxCount = p->parmsp[1];
2723         infoLevel = p->parmsp[2];
2724         searchFlags = p->parmsp[5];
2725         pathp = NULL;
2726         nextCookie = p->parmsp[3] | (p->parmsp[4] << 16);
2727         maskp = dsp->mask;
2728                 starPattern = 1;        /* assume, since required a Find Next */
2729     }
2730
2731         osi_Log4(smb_logp,
2732               "T2 search dir attr 0x%x, info level %d, max count %d, flags 0x%x",
2733               attribute, infoLevel, maxCount, searchFlags);
2734
2735         osi_Log2(smb_logp, "...T2 search op %d, nextCookie 0x%x",
2736               p->opcode, nextCookie);
2737
2738         if (infoLevel >= 0x101)
2739                 searchFlags &= ~4;      /* no resume keys */
2740
2741     dirListPatchesp = NULL;
2742
2743         maxReturnData = p->maxReturnData;
2744     if (p->opcode == 1) /* find first */
2745         maxReturnParms = 10;    /* bytes */
2746         else    
2747         maxReturnParms = 8;     /* bytes */
2748
2749 #ifndef CM_CONFIG_MULTITRAN2RESPONSES
2750     if (maxReturnData > 6000) 
2751         maxReturnData = 6000;
2752 #endif /* CM_CONFIG_MULTITRAN2RESPONSES */
2753
2754         outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
2755                                       maxReturnData);
2756
2757     osi_Log1(smb_logp, "T2 receive search dir %s",
2758              osi_LogSaveString(smb_logp, pathp));
2759         
2760     /* bail out if request looks bad */
2761     if (p->opcode == 1 && !pathp) {
2762         smb_ReleaseDirSearch(dsp);
2763         smb_FreeTran2Packet(outp);
2764         return CM_ERROR_BADSMB;
2765     }
2766         
2767         osi_Log2(smb_logp, "T2 dir search cookie 0x%x, connection %d",
2768              nextCookie, dsp->cookie);
2769
2770         userp = smb_GetTran2User(vcp, p);
2771     if (!userp) {
2772         osi_Log1(smb_logp, "T2 dir search unable to resolve user [%d]", p->uid);
2773         smb_ReleaseDirSearch(dsp);
2774         smb_FreeTran2Packet(outp);
2775         return CM_ERROR_BADSMB;
2776     }
2777
2778         /* try to get the vnode for the path name next */
2779         lock_ObtainMutex(&dsp->mx);
2780         if (dsp->scp) {
2781                 scp = dsp->scp;
2782         cm_HoldSCache(scp);
2783         code = 0;
2784     }
2785     else {
2786                 spacep = cm_GetSpace();
2787         smb_StripLastComponent(spacep->data, NULL, pathp);
2788         lock_ReleaseMutex(&dsp->mx);
2789
2790                 tidPathp = smb_GetTIDPath(vcp, p->tid);
2791         code = cm_NameI(cm_rootSCachep, spacep->data,
2792                         CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
2793                         userp, tidPathp, &req, &scp);
2794         cm_FreeSpace(spacep);
2795
2796         lock_ObtainMutex(&dsp->mx);
2797                 if (code == 0) {
2798             if (dsp->scp != 0) cm_ReleaseSCache(dsp->scp);
2799                         dsp->scp = scp;
2800                         /* we need one hold for the entry we just stored into,
2801              * and one for our own processing.  When we're done
2802                          * with this function, we'll drop the one for our own
2803                          * processing.  We held it once from the namei call,
2804                          * and so we do another hold now.
2805              */
2806             cm_HoldSCache(scp);
2807                         lock_ObtainMutex(&scp->mx);
2808                         if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
2809                             && LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
2810                 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
2811                                 dsp->flags |= SMB_DIRSEARCH_BULKST;
2812                         }
2813                         lock_ReleaseMutex(&scp->mx);
2814         }
2815     }
2816         lock_ReleaseMutex(&dsp->mx);
2817     if (code) {
2818                 cm_ReleaseUser(userp);
2819         smb_FreeTran2Packet(outp);
2820                 smb_DeleteDirSearch(dsp);
2821                 smb_ReleaseDirSearch(dsp);
2822         return code;
2823         }
2824
2825     /* get the directory size */
2826         lock_ObtainMutex(&scp->mx);
2827     code = cm_SyncOp(scp, NULL, userp, &req, 0,
2828                      CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2829         if (code) {
2830                 lock_ReleaseMutex(&scp->mx);
2831         cm_ReleaseSCache(scp);
2832         cm_ReleaseUser(userp);
2833         smb_FreeTran2Packet(outp);
2834                 smb_DeleteDirSearch(dsp);
2835                 smb_ReleaseDirSearch(dsp);
2836         return code;
2837     }
2838
2839   startsearch:
2840     dirLength = scp->length;
2841     bufferp = NULL;
2842     bufferOffset.LowPart = bufferOffset.HighPart = 0;
2843     curOffset.HighPart = 0;
2844     curOffset.LowPart = nextCookie;
2845         origOp = outp->datap;
2846
2847     foundInexact = 0;
2848     code = 0;
2849     returnedNames = 0;
2850     bytesInBuffer = 0;
2851     while (1) {
2852                 op = origOp;
2853                 if (searchFlags & 4)
2854                         /* skip over resume key */
2855                         op += 4;
2856
2857                 /* make sure that curOffset.LowPart doesn't point to the first
2858          * 32 bytes in the 2nd through last dir page, and that it doesn't
2859          * point at the first 13 32-byte chunks in the first dir page,
2860          * since those are dir and page headers, and don't contain useful
2861          * information.
2862          */
2863                 temp = curOffset.LowPart & (2048-1);
2864         if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
2865                         /* we're in the first page */
2866             if (temp < 13*32) temp = 13*32;
2867                 }
2868                 else {
2869                         /* we're in a later dir page */
2870             if (temp < 32) temp = 32;
2871         }
2872                 
2873         /* make sure the low order 5 bits are zero */
2874         temp &= ~(32-1);
2875                 
2876         /* now put temp bits back ito curOffset.LowPart */
2877         curOffset.LowPart &= ~(2048-1);
2878         curOffset.LowPart |= temp;
2879
2880         /* check if we've passed the dir's EOF */
2881         if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength)) {
2882             eos = 1;
2883             break;
2884         }
2885
2886         /* check if we've returned all the names that will fit in the
2887          * response packet; we check return count as well as the number
2888          * of bytes requested.  We check the # of bytes after we find
2889          * the dir entry, since we'll need to check its size.
2890          */
2891         if (returnedNames >= maxCount) {
2892             break;
2893         }
2894
2895         /* see if we can use the bufferp we have now; compute in which
2896          * page the current offset would be, and check whether that's
2897          * the offset of the buffer we have.  If not, get the buffer.
2898          */
2899         thyper.HighPart = curOffset.HighPart;
2900         thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
2901         if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
2902                         /* wrong buffer */
2903             if (bufferp) {
2904                 buf_Release(bufferp);
2905                 bufferp = NULL;
2906                         }       
2907                         lock_ReleaseMutex(&scp->mx);
2908                         lock_ObtainRead(&scp->bufCreateLock);
2909             code = buf_Get(scp, &thyper, &bufferp);
2910                         lock_ReleaseRead(&scp->bufCreateLock);
2911
2912                         /* now, if we're doing a star match, do bulk fetching
2913                          * of all of the status info for files in the dir.
2914              */
2915             if (starPattern) {
2916                                 smb_ApplyV3DirListPatches(scp, &dirListPatchesp,
2917                                           infoLevel, userp,
2918                                           &req);
2919                                 if ((dsp->flags & SMB_DIRSEARCH_BULKST)
2920                     && LargeIntegerGreaterThanOrEqualTo(thyper, scp->bulkStatProgress)) {
2921                                         /* Don't bulk stat if risking timeout */
2922                                         int now = GetCurrentTime();
2923                                         if (now - req.startTime > 5000) {
2924                                                 scp->bulkStatProgress = thyper;
2925                                                 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
2926                                                 dsp->flags &= ~SMB_DIRSEARCH_BULKST;
2927                                         } else
2928                         cm_TryBulkStat(scp, &thyper, userp, &req);
2929                                 }
2930                         }
2931
2932             lock_ObtainMutex(&scp->mx);
2933             if (code) break;
2934             bufferOffset = thyper;
2935
2936             /* now get the data in the cache */
2937             while (1) {
2938                                 code = cm_SyncOp(scp, bufferp, userp, &req,
2939                                  PRSFS_LOOKUP,
2940                                  CM_SCACHESYNC_NEEDCALLBACK
2941                                  | CM_SCACHESYNC_READ);
2942                                 if (code) break;
2943                                 
2944                 if (cm_HaveBuffer(scp, bufferp, 0)) break;
2945
2946                 /* otherwise, load the buffer and try again */
2947                 code = cm_GetBuffer(scp, bufferp, NULL, userp,
2948                                     &req);
2949                 if (code) break;
2950             }
2951             if (code) {
2952                                 buf_Release(bufferp);
2953                 bufferp = NULL;
2954                 break;
2955                         }
2956         }       /* if (wrong buffer) ... */
2957                 
2958         /* now we have the buffer containing the entry we're interested
2959          * in; copy it out if it represents a non-deleted entry.
2960          */
2961                 entryInDir = curOffset.LowPart & (2048-1);
2962         entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
2963
2964                 /* page header will help tell us which entries are free.  Page
2965                  * header can change more often than once per buffer, since
2966                  * AFS 3 dir page size may be less than (but not more than)
2967                  * a buffer package buffer.
2968          */
2969                 /* only look intra-buffer */
2970                 temp = curOffset.LowPart & (buf_bufferSize - 1);
2971         temp &= ~(2048 - 1);    /* turn off intra-page bits */
2972                 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
2973
2974                 /* now determine which entry we're looking at in the page.
2975                  * If it is free (there's a free bitmap at the start of the
2976                  * dir), we should skip these 32 bytes.
2977          */
2978         slotInPage = (entryInDir & 0x7e0) >> 5;
2979         if (!(pageHeaderp->freeBitmap[slotInPage>>3]
2980                & (1 << (slotInPage & 0x7)))) {
2981                         /* this entry is free */
2982             numDirChunks = 1;   /* only skip this guy */
2983             goto nextEntry;
2984         }
2985
2986                 tp = bufferp->datap + entryInBuffer;
2987         dep = (cm_dirEntry_t *) tp;     /* now points to AFS3 dir entry */
2988
2989         /* while we're here, compute the next entry's location, too,
2990                  * since we'll need it when writing out the cookie into the dir
2991                  * listing stream.
2992          *
2993          * XXXX Probably should do more sanity checking.
2994          */
2995                 numDirChunks = cm_NameEntries(dep->name, &onbytes);
2996                 
2997         /* compute offset of cookie representing next entry */
2998         nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
2999
3000                 /* Need 8.3 name? */
3001                 NeedShortName = 0;
3002                 if (infoLevel == 0x104
3003                     && dep->fid.vnode != 0
3004                     && !cm_Is8Dot3(dep->name)) {
3005                         cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
3006                         NeedShortName = 1;
3007                 }
3008
3009         /* When matching, we are using doing a case fold if we have a wildcard mask.
3010          * If we get a non-wildcard match, it's a lookup for a specific file. 
3011          */
3012         if (dep->fid.vnode != 0 && 
3013             (smb_V3MatchMask(dep->name, maskp, (starPattern? CM_FLAG_CASEFOLD : 0))
3014               || (NeedShortName
3015                    && smb_V3MatchMask(shortName, maskp, CM_FLAG_CASEFOLD)))) {
3016
3017             /* Eliminate entries that don't match requested attributes */
3018             if (smb_hideDotFiles && !(dsp->attribute & SMB_ATTR_HIDDEN) && 
3019                  smb_IsDotFile(dep->name))
3020                 goto nextEntry; /* no hidden files */
3021                     
3022             if (!(dsp->attribute & SMB_ATTR_DIRECTORY))  /* no directories */
3023             {
3024                 /* We have already done the cm_TryBulkStat above */
3025                 fid.cell = scp->fid.cell;
3026                 fid.volume = scp->fid.volume;
3027                 fid.vnode = ntohl(dep->fid.vnode);
3028                 fid.unique = ntohl(dep->fid.unique);
3029                 fileType = cm_FindFileType(&fid);
3030                 /*osi_Log2(smb_logp, "smb_ReceiveTran2SearchDir: file %s "
3031                  "has filetype %d", dep->name,
3032                  fileType);*/
3033                 if (fileType == CM_SCACHETYPE_DIRECTORY)
3034                     goto nextEntry;
3035             }
3036
3037                         /* finally check if this name will fit */
3038
3039                         /* standard dir entry stuff */
3040                         if (infoLevel < 0x101)
3041                                 ohbytes = 23;   /* pre-NT */
3042                         else if (infoLevel == 0x103)
3043                                 ohbytes = 12;   /* NT names only */
3044                         else
3045                                 ohbytes = 64;   /* NT */
3046
3047                         if (infoLevel == 0x104)
3048                                 ohbytes += 26;  /* Short name & length */
3049
3050             if (searchFlags & 4) {
3051                 ohbytes += 4;   /* if resume key required */
3052                         }   
3053
3054             if (infoLevel != 1
3055                  && infoLevel != 0x101
3056                  && infoLevel != 0x103)
3057                                 ohbytes += 4;   /* EASIZE */
3058
3059                         /* add header to name & term. null */
3060                         orbytes = onbytes + ohbytes + 1;
3061
3062                         /* now, we round up the record to a 4 byte alignment,
3063                          * and we make sure that we have enough room here for
3064                          * even the aligned version (so we don't have to worry
3065                          * about an * overflow when we pad things out below).
3066                          * That's the reason for the alignment arithmetic below.
3067              */
3068                         if (infoLevel >= 0x101)
3069                                 align = (4 - (orbytes & 3)) & 3;
3070                         else
3071                                 align = 0;
3072                         if (orbytes + bytesInBuffer + align > maxReturnData)
3073                 break;
3074
3075                         /* this is one of the entries to use: it is not deleted
3076                          * and it matches the star pattern we're looking for.
3077                          * Put out the name, preceded by its length.
3078              */
3079                         /* First zero everything else */
3080                         memset(origOp, 0, ohbytes);
3081
3082                         if (infoLevel <= 0x101)
3083                 *(origOp + ohbytes - 1) = (unsigned char) onbytes;
3084                         else if (infoLevel == 0x103)
3085                                 *((u_long *)(op + 8)) = onbytes;
3086                         else
3087                                 *((u_long *)(op + 60)) = onbytes;
3088             strcpy(origOp+ohbytes, dep->name);
3089
3090                         /* Short name if requested and needed */
3091             if (infoLevel == 0x104) {
3092                                 if (NeedShortName) {
3093                                         strcpy(op + 70, shortName);
3094                                         *(op + 68) = shortNameEnd - shortName;
3095                                 }
3096                         }
3097
3098             /* now, adjust the # of entries copied */
3099             returnedNames++;
3100
3101                         /* NextEntryOffset and FileIndex */
3102                         if (infoLevel >= 101) {
3103                                 int entryOffset = orbytes + align;
3104                                 *((u_long *)op) = entryOffset;
3105                                 *((u_long *)(op+4)) = nextEntryCookie;
3106                         }
3107
3108             /* now we emit the attribute.  This is tricky, since
3109              * we need to really stat the file to find out what
3110                          * type of entry we've got.  Right now, we're copying
3111                          * out data from * a buffer, while holding the scp
3112                          * locked, so it isn't really convenient to stat
3113                          * something now.  We'll put in a place holder
3114              * now, and make a second pass before returning this
3115                          * to get the real attributes.  So, we just skip the
3116                          * data for now, and adjust it later.  We allocate a
3117                          * patch record to make it easy to find this point
3118                          * later.  The replay will happen at a time when it is
3119                          * safe to unlock the directory.
3120              */
3121                         if (infoLevel != 0x103) {
3122                                 curPatchp = malloc(sizeof(*curPatchp));
3123                 osi_QAdd((osi_queue_t **) &dirListPatchesp,
3124                           &curPatchp->q);
3125                                 curPatchp->dptr = op;
3126                                 if (infoLevel >= 0x101)
3127                                         curPatchp->dptr += 8;
3128
3129                 if (smb_hideDotFiles && smb_IsDotFile(dep->name)) {
3130                     curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
3131                 }
3132                 else
3133                     curPatchp->flags = 0;
3134
3135                                 curPatchp->fid.cell = scp->fid.cell;
3136                                 curPatchp->fid.volume = scp->fid.volume;
3137                                 curPatchp->fid.vnode = ntohl(dep->fid.vnode);
3138                                 curPatchp->fid.unique = ntohl(dep->fid.unique);
3139
3140                 /* temp */
3141                 curPatchp->dep = dep;
3142                         }   
3143
3144                         if (searchFlags & 4)
3145                                 /* put out resume key */
3146                                 *((u_long *)origOp) = nextEntryCookie;
3147
3148                         /* Adjust byte ptr and count */
3149                         origOp += orbytes;      /* skip entire record */
3150             bytesInBuffer += orbytes;
3151
3152                         /* and pad the record out */
3153             while (--align >= 0) {
3154                                 *origOp++ = 0;
3155                 bytesInBuffer++;
3156             }
3157
3158                 }       /* if we're including this name */
3159         else if (!NeedShortName &&
3160                  !starPattern &&
3161                  !foundInexact &&
3162                                                         dep->fid.vnode != 0 &&
3163                  smb_V3MatchMask(dep->name, maskp, CM_FLAG_CASEFOLD)) {
3164             /* We were looking for exact matches, but here's an inexact one*/
3165             foundInexact = 1;
3166         }
3167                 
3168       nextEntry:
3169         /* and adjust curOffset to be where the new cookie is */
3170                 thyper.HighPart = 0;
3171         thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
3172         curOffset = LargeIntegerAdd(thyper, curOffset);
3173     }           /* while copying data for dir listing */
3174
3175     /* If we didn't get a star pattern, we did an exact match during the first pass. 
3176      * If there were no exact matches found, we fail over to inexact matches by
3177      * marking the query as a star pattern (matches all case permutations), and
3178      * re-running the query. 
3179      */
3180     if (returnedNames == 0 && !starPattern && foundInexact) {
3181         osi_Log0(afsd_logp,"T2 Search: No exact matches. Re-running for inexact matches");
3182         starPattern = 1;
3183         goto startsearch;
3184     }
3185
3186         /* release the mutex */
3187         lock_ReleaseMutex(&scp->mx);
3188     if (bufferp) buf_Release(bufferp);
3189
3190         /* apply and free last set of patches; if not doing a star match, this
3191          * will be empty, but better safe (and freeing everything) than sorry.
3192      */
3193     smb_ApplyV3DirListPatches(scp, &dirListPatchesp, infoLevel, userp,
3194                               &req);
3195         
3196     /* now put out the final parameters */
3197         if (returnedNames == 0) eos = 1;
3198     if (p->opcode == 1) {
3199                 /* find first */
3200         outp->parmsp[0] = (unsigned short) dsp->cookie;
3201         outp->parmsp[1] = returnedNames;
3202         outp->parmsp[2] = eos;
3203         outp->parmsp[3] = 0;            /* nothing wrong with EAS */
3204         outp->parmsp[4] = 0;    
3205         /* don't need last name to continue
3206          * search, cookie is enough.  Normally,
3207          * this is the offset of the file name
3208          * of the last entry returned.
3209          */
3210         outp->totalParms = 10;  /* in bytes */
3211     }
3212     else {
3213         /* find next */
3214         outp->parmsp[0] = returnedNames;
3215         outp->parmsp[1] = eos;
3216         outp->parmsp[2] = 0;    /* EAS error */
3217         outp->parmsp[3] = 0;    /* last name, as above */
3218         outp->totalParms = 8;   /* in bytes */
3219     }   
3220
3221         /* return # of bytes in the buffer */
3222     outp->totalData = bytesInBuffer;
3223
3224         osi_Log2(smb_logp, "T2 search dir done, %d names, code %d",
3225               returnedNames, code);
3226
3227         /* Return error code if unsuccessful on first request */
3228         if (code == 0 && p->opcode == 1 && returnedNames == 0)
3229                 code = CM_ERROR_NOSUCHFILE;
3230
3231         /* if we're supposed to close the search after this request, or if
3232      * we're supposed to close the search if we're done, and we're done,
3233      * or if something went wrong, close the search.
3234      */
3235     /* ((searchFlags & 1) || ((searchFlags & 2) && eos) */
3236         if ((searchFlags & 1) || (returnedNames == 0) || 
3237          ((searchFlags & 2) && eos) || code != 0)
3238             smb_DeleteDirSearch(dsp);
3239         if (code)
3240         smb_SendTran2Error(vcp, p, opx, code);
3241         else {
3242         smb_SendTran2Packet(vcp, outp, opx);
3243         }
3244         smb_FreeTran2Packet(outp);
3245     smb_ReleaseDirSearch(dsp);
3246     cm_ReleaseSCache(scp);
3247     cm_ReleaseUser(userp);
3248     return 0;
3249 }
3250
3251 long smb_ReceiveV3FindClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3252 {
3253     int dirHandle;
3254     smb_dirSearch_t *dsp;
3255
3256     dirHandle = smb_GetSMBParm(inp, 0);
3257         
3258     osi_Log1(smb_logp, "SMB3 find close handle %d", dirHandle);
3259
3260     dsp = smb_FindDirSearch(dirHandle);
3261         
3262     if (!dsp)
3263                 return CM_ERROR_BADFD;
3264         
3265     /* otherwise, we have an FD to destroy */
3266     smb_DeleteDirSearch(dsp);
3267     smb_ReleaseDirSearch(dsp);
3268         
3269         /* and return results */
3270         smb_SetSMBDataLength(outp, 0);
3271
3272     return 0;
3273 }
3274
3275 long smb_ReceiveV3FindNotifyClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3276 {
3277         smb_SetSMBDataLength(outp, 0);
3278     return 0;
3279 }
3280
3281 long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3282 {
3283         char *pathp;
3284     long code = 0;
3285         cm_space_t *spacep;
3286     int excl;
3287     cm_user_t *userp;
3288     cm_scache_t *dscp;          /* dir we're dealing with */
3289     cm_scache_t *scp;           /* file we're creating */
3290     cm_attr_t setAttr;
3291     int initialModeBits;
3292     smb_fid_t *fidp;
3293     int attributes;
3294     char *lastNamep;
3295     long dosTime;
3296     int openFun;
3297     int trunc;
3298     int openMode;
3299     int extraInfo;
3300     int openAction;
3301     int parmSlot;                       /* which parm we're dealing with */
3302         char *tidPathp;
3303         cm_req_t req;
3304
3305         cm_InitReq(&req);
3306
3307     scp = NULL;
3308         
3309         extraInfo = (smb_GetSMBParm(inp, 2) & 1);       /* return extra info */
3310         openFun = smb_GetSMBParm(inp, 8);       /* open function */
3311     excl = ((openFun & 3) == 0);
3312     trunc = ((openFun & 3) == 2);               /* truncate it */
3313         openMode = (smb_GetSMBParm(inp, 3) & 0x7);
3314     openAction = 0;                     /* tracks what we did */
3315
3316     attributes = smb_GetSMBParm(inp, 5);
3317     dosTime = smb_GetSMBParm(inp, 6) | (smb_GetSMBParm(inp, 7) << 16);
3318
3319         /* compute initial mode bits based on read-only flag in attributes */
3320     initialModeBits = 0666;
3321     if (attributes & 1) initialModeBits &= ~0222;
3322         
3323     pathp = smb_GetSMBData(inp, NULL);
3324
3325         spacep = inp->spacep;
3326     smb_StripLastComponent(spacep->data, &lastNamep, pathp);
3327
3328         if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
3329                 /* special case magic file name for receiving IOCTL requests
3330          * (since IOCTL calls themselves aren't getting through).
3331          */
3332 #ifdef NOTSERVICE
3333         osi_Log0(smb_logp, "IOCTL Open");
3334 #endif
3335
3336         fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
3337         smb_SetupIoctlFid(fidp, spacep);
3338
3339                 /* set inp->fid so that later read calls in same msg can find fid */
3340         inp->fid = fidp->fid;
3341         
3342         /* copy out remainder of the parms */
3343                 parmSlot = 2;
3344                 smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
3345                 if (extraInfo) {
3346             smb_SetSMBParm(outp, parmSlot, /* attrs */ 0); parmSlot++;
3347             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;      /* mod time */
3348             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
3349             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;      /* len */
3350             smb_SetSMBParm(outp, parmSlot, 0x7fff); parmSlot++;
3351             smb_SetSMBParm(outp, parmSlot, openMode); parmSlot++;
3352             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* file type 0 ==> normal file or dir */
3353             smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* IPC junk */
3354                 }   
3355                 /* and the final "always present" stuff */
3356         smb_SetSMBParm(outp, parmSlot, /* openAction found existing file */ 1); parmSlot++;
3357                 /* next write out the "unique" ID */
3358                 smb_SetSMBParm(outp, parmSlot, 0x1234); parmSlot++;
3359                 smb_SetSMBParm(outp, parmSlot, 0x5678); parmSlot++;
3360         smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
3361         smb_SetSMBDataLength(outp, 0);