2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afs/param.h>
15 #pragma warning(disable: 4005)
17 #define SECURITY_WIN32
29 #include <WINNT\afsreg.h>
34 extern osi_hyper_t hzero;
36 smb_packet_t *smb_Directory_Watches = NULL;
37 osi_mutex_t smb_Dir_Watch_Lock;
39 smb_tran2Dispatch_t smb_tran2DispatchTable[SMB_TRAN2_NOPCODES];
41 smb_tran2Dispatch_t smb_rapDispatchTable[SMB_RAP_NOPCODES];
43 /* protected by the smb_globalLock */
44 smb_tran2Packet_t *smb_tran2AssemblyQueuep;
46 const clientchar_t **smb_ExecutableExtensions = NULL;
48 /* retrieve a held reference to a user structure corresponding to an incoming
50 cm_user_t *smb_GetTran2User(smb_vc_t *vcp, smb_tran2Packet_t *inp)
55 uidp = smb_FindUID(vcp, inp->uid, 0);
59 up = smb_GetUserFromUID(uidp);
67 * Return boolean specifying if the path name is thought to be an
68 * executable file. For now .exe or .dll.
70 afs_uint32 smb_IsExecutableFileName(const clientchar_t *name)
74 if ( smb_ExecutableExtensions == NULL || name == NULL)
77 len = (int)cm_ClientStrLen(name);
79 for ( i=0; smb_ExecutableExtensions[i]; i++) {
80 j = len - (int)cm_ClientStrLen(smb_ExecutableExtensions[i]);
81 if (cm_ClientStrCmpI(smb_ExecutableExtensions[i], &name[j]) == 0)
89 * Return extended attributes.
90 * Right now, we aren't using any of the "new" bits, so this looks exactly
91 * like smb_Attributes() (see smb.c).
93 unsigned long smb_ExtAttributes(cm_scache_t *scp)
97 if (scp->fileType == CM_SCACHETYPE_DIRECTORY ||
98 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
99 scp->fileType == CM_SCACHETYPE_INVALID)
101 attrs = SMB_ATTR_DIRECTORY;
102 #ifdef SPECIAL_FOLDERS
103 attrs |= SMB_ATTR_SYSTEM; /* FILE_ATTRIBUTE_SYSTEM */
104 #endif /* SPECIAL_FOLDERS */
105 } else if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
106 attrs = SMB_ATTR_DIRECTORY | SMB_ATTR_SPARSE_FILE;
107 } else if (scp->fid.vnode & 0x1)
108 attrs = SMB_ATTR_DIRECTORY;
113 * We used to mark a file RO if it was in an RO volume, but that
114 * turns out to be impolitic in NT. See defect 10007.
117 if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
118 attrs |= SMB_ATTR_READONLY; /* Read-only */
120 if ((scp->unixModeBits & 0222) == 0)
121 attrs |= SMB_ATTR_READONLY; /* Read-only */
125 attrs = SMB_ATTR_NORMAL; /* FILE_ATTRIBUTE_NORMAL */
130 int smb_V3IsStarMask(clientchar_t *maskp)
134 while (tc = *maskp++)
135 if (tc == '?' || tc == '*' || tc == '<' || tc == '>')
140 void OutputDebugF(clientchar_t * format, ...) {
142 clientchar_t vbuffer[1024];
144 va_start( args, format );
145 cm_ClientStrPrintfV(vbuffer, lengthof(vbuffer), format, args);
146 osi_Log1(smb_logp, "%S", osi_LogSaveClientString(smb_logp, vbuffer));
147 cm_ClientStrCat(vbuffer, lengthof(vbuffer), _C("\n"));
148 OutputDebugStringW(vbuffer);
151 void OutputDebugHexDump(unsigned char * buffer, int len) {
154 static char tr[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
156 OutputDebugF(_C("Hexdump length [%d]"),len);
158 for (i=0;i<len;i++) {
161 osi_Log1(smb_logp, "%s", osi_LogSaveString(smb_logp, buf));
162 StringCchCatA(buf, lengthof(buf), "\r\n");
163 OutputDebugString(buf);
165 StringCchPrintfA(buf, lengthof(buf), "%5x", i);
166 memset(buf+5,' ',80);
171 j = j*3 + 7 + ((j>7)?1:0);
174 buf[j] = tr[k / 16]; buf[j+1] = tr[k % 16];
177 j = j + 56 + ((j>7)?1:0);
179 buf[j] = (k>32 && k<127)?k:'.';
182 osi_Log1(smb_logp, "%s", osi_LogSaveString(smb_logp, buf));
183 StringCchCatA(buf, lengthof(buf), "\r\n");
184 OutputDebugString(buf);
188 #define SMB_EXT_SEC_PACKAGE_NAME "Negotiate"
190 void smb_NegotiateExtendedSecurity(void ** secBlob, int * secBlobLength) {
191 SECURITY_STATUS status, istatus;
192 CredHandle creds = {0,0};
194 SecBufferDesc secOut;
202 OutputDebugF(_C("Negotiating Extended Security"));
204 status = AcquireCredentialsHandle( NULL,
205 SMB_EXT_SEC_PACKAGE_NAME,
214 if (status != SEC_E_OK) {
215 /* Really bad. We return an empty security blob */
216 OutputDebugF(_C("AcquireCredentialsHandle failed with %lX"), status);
221 secOut.pBuffers = &secTok;
222 secOut.ulVersion = SECBUFFER_VERSION;
224 secTok.BufferType = SECBUFFER_TOKEN;
226 secTok.pvBuffer = NULL;
228 ctx.dwLower = ctx.dwUpper = 0;
230 status = AcceptSecurityContext( &creds,
233 ASC_REQ_CONNECTION | ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY,
234 SECURITY_NETWORK_DREP,
241 if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
242 OutputDebugF(_C("Completing token..."));
243 istatus = CompleteAuthToken(&ctx, &secOut);
244 if ( istatus != SEC_E_OK )
245 OutputDebugF(_C("Token completion failed: %x"), istatus);
248 if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
249 if (secTok.pvBuffer) {
250 *secBlobLength = secTok.cbBuffer;
251 *secBlob = malloc( secTok.cbBuffer );
252 memcpy(*secBlob, secTok.pvBuffer, secTok.cbBuffer );
255 if ( status != SEC_E_OK )
256 OutputDebugF(_C("AcceptSecurityContext status != CONTINUE %lX"), status);
259 /* Discard partial security context */
260 DeleteSecurityContext(&ctx);
262 if (secTok.pvBuffer) FreeContextBuffer( secTok.pvBuffer );
264 /* Discard credentials handle. We'll reacquire one when we get the session setup X */
265 FreeCredentialsHandle(&creds);
271 struct smb_ext_context {
278 long smb_AuthenticateUserExt(smb_vc_t * vcp, clientchar_t * usern,
279 char * secBlobIn, int secBlobInLength,
280 char ** secBlobOut, int * secBlobOutLength) {
281 SECURITY_STATUS status, istatus;
285 SecBufferDesc secBufIn;
287 SecBufferDesc secBufOut;
290 struct smb_ext_context * secCtx = NULL;
291 struct smb_ext_context * newSecCtx = NULL;
292 void * assembledBlob = NULL;
293 int assembledBlobLength = 0;
296 OutputDebugF(_C("In smb_AuthenticateUserExt"));
299 *secBlobOutLength = 0;
301 if (vcp->flags & SMB_VCFLAG_AUTH_IN_PROGRESS) {
302 secCtx = vcp->secCtx;
303 lock_ObtainMutex(&vcp->mx);
304 vcp->flags &= ~SMB_VCFLAG_AUTH_IN_PROGRESS;
306 lock_ReleaseMutex(&vcp->mx);
310 OutputDebugF(_C("Received incoming token:"));
311 OutputDebugHexDump(secBlobIn,secBlobInLength);
315 OutputDebugF(_C("Continuing with existing context."));
316 creds = secCtx->creds;
319 if (secCtx->partialToken) {
320 assembledBlobLength = secCtx->partialTokenLen + secBlobInLength;
321 assembledBlob = malloc(assembledBlobLength);
322 memcpy(assembledBlob,secCtx->partialToken, secCtx->partialTokenLen);
323 memcpy(((BYTE *)assembledBlob) + secCtx->partialTokenLen, secBlobIn, secBlobInLength);
326 status = AcquireCredentialsHandle( NULL,
327 SMB_EXT_SEC_PACKAGE_NAME,
336 if (status != SEC_E_OK) {
337 OutputDebugF(_C("Can't acquire Credentials handle [%lX]"), status);
338 code = CM_ERROR_BADPASSWORD; /* means "try again when I'm sober" */
346 secBufIn.cBuffers = 1;
347 secBufIn.pBuffers = &secTokIn;
348 secBufIn.ulVersion = SECBUFFER_VERSION;
350 secTokIn.BufferType = SECBUFFER_TOKEN;
352 secTokIn.cbBuffer = assembledBlobLength;
353 secTokIn.pvBuffer = assembledBlob;
355 secTokIn.cbBuffer = secBlobInLength;
356 secTokIn.pvBuffer = secBlobIn;
359 secBufOut.cBuffers = 1;
360 secBufOut.pBuffers = &secTokOut;
361 secBufOut.ulVersion = SECBUFFER_VERSION;
363 secTokOut.BufferType = SECBUFFER_TOKEN;
364 secTokOut.cbBuffer = 0;
365 secTokOut.pvBuffer = NULL;
367 status = AcceptSecurityContext( &creds,
368 ((secCtx)?&ctx:NULL),
370 ASC_REQ_CONNECTION | ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOCATE_MEMORY,
371 SECURITY_NETWORK_DREP,
378 if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) {
379 OutputDebugF(_C("Completing token..."));
380 istatus = CompleteAuthToken(&ctx, &secBufOut);
381 if ( istatus != SEC_E_OK )
382 OutputDebugF(_C("Token completion failed: %lX"), istatus);
385 if (status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
386 OutputDebugF(_C("Continue needed"));
388 newSecCtx = malloc(sizeof(*newSecCtx));
390 newSecCtx->creds = creds;
391 newSecCtx->ctx = ctx;
392 newSecCtx->partialToken = NULL;
393 newSecCtx->partialTokenLen = 0;
395 lock_ObtainMutex( &vcp->mx );
396 vcp->flags |= SMB_VCFLAG_AUTH_IN_PROGRESS;
397 vcp->secCtx = newSecCtx;
398 lock_ReleaseMutex( &vcp->mx );
400 code = CM_ERROR_GSSCONTINUE;
403 if ((status == SEC_I_COMPLETE_NEEDED || status == SEC_E_OK ||
404 status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) &&
405 secTokOut.pvBuffer) {
406 OutputDebugF(_C("Need to send token back to client"));
408 *secBlobOutLength = secTokOut.cbBuffer;
409 *secBlobOut = malloc(secTokOut.cbBuffer);
410 memcpy(*secBlobOut, secTokOut.pvBuffer, secTokOut.cbBuffer);
412 OutputDebugF(_C("Outgoing token:"));
413 OutputDebugHexDump(*secBlobOut,*secBlobOutLength);
414 } else if (status == SEC_E_INCOMPLETE_MESSAGE) {
415 OutputDebugF(_C("Incomplete message"));
417 newSecCtx = malloc(sizeof(*newSecCtx));
419 newSecCtx->creds = creds;
420 newSecCtx->ctx = ctx;
421 newSecCtx->partialToken = malloc(secTokOut.cbBuffer);
422 memcpy(newSecCtx->partialToken, secTokOut.pvBuffer, secTokOut.cbBuffer);
423 newSecCtx->partialTokenLen = secTokOut.cbBuffer;
425 lock_ObtainMutex( &vcp->mx );
426 vcp->flags |= SMB_VCFLAG_AUTH_IN_PROGRESS;
427 vcp->secCtx = newSecCtx;
428 lock_ReleaseMutex( &vcp->mx );
430 code = CM_ERROR_GSSCONTINUE;
433 if (status == SEC_E_OK || status == SEC_I_COMPLETE_NEEDED) {
435 SecPkgContext_NamesW names;
437 OutputDebugF(_C("Authentication completed"));
438 OutputDebugF(_C("Returned flags : [%lX]"), flags);
440 if (!QueryContextAttributesW(&ctx, SECPKG_ATTR_NAMES, &names)) {
441 OutputDebugF(_C("Received name [%s]"), names.sUserName);
442 cm_ClientStrCpy(usern, SMB_MAX_USERNAME_LENGTH, names.sUserName);
443 cm_ClientStrLwr(usern); /* in tandem with smb_GetNormalizedUsername */
444 FreeContextBuffer(names.sUserName);
446 /* Force the user to retry if the context is invalid */
447 OutputDebugF(_C("QueryContextAttributes Names failed [%x]"), GetLastError());
448 code = CM_ERROR_BADPASSWORD;
452 case SEC_E_INVALID_TOKEN:
453 OutputDebugF(_C("Returning bad password :: INVALID_TOKEN"));
455 case SEC_E_INVALID_HANDLE:
456 OutputDebugF(_C("Returning bad password :: INVALID_HANDLE"));
458 case SEC_E_LOGON_DENIED:
459 OutputDebugF(_C("Returning bad password :: LOGON_DENIED"));
461 case SEC_E_UNKNOWN_CREDENTIALS:
462 OutputDebugF(_C("Returning bad password :: UNKNOWN_CREDENTIALS"));
464 case SEC_E_NO_CREDENTIALS:
465 OutputDebugF(_C("Returning bad password :: NO_CREDENTIALS"));
467 case SEC_E_CONTEXT_EXPIRED:
468 OutputDebugF(_C("Returning bad password :: CONTEXT_EXPIRED"));
470 case SEC_E_INCOMPLETE_CREDENTIALS:
471 OutputDebugF(_C("Returning bad password :: INCOMPLETE_CREDENTIALS"));
473 case SEC_E_WRONG_PRINCIPAL:
474 OutputDebugF(_C("Returning bad password :: WRONG_PRINCIPAL"));
476 case SEC_E_TIME_SKEW:
477 OutputDebugF(_C("Returning bad password :: TIME_SKEW"));
480 OutputDebugF(_C("Returning bad password :: Status == %lX"), status);
482 code = CM_ERROR_BADPASSWORD;
486 if (secCtx->partialToken) free(secCtx->partialToken);
494 if (secTokOut.pvBuffer)
495 FreeContextBuffer(secTokOut.pvBuffer);
497 if (code != CM_ERROR_GSSCONTINUE) {
498 DeleteSecurityContext(&ctx);
499 FreeCredentialsHandle(&creds);
507 #define P_RESP_LEN 128
509 /* LsaLogonUser expects input parameters to be in a contiguous block of memory.
510 So put stuff in a struct. */
511 struct Lm20AuthBlob {
512 MSV1_0_LM20_LOGON lmlogon;
513 BYTE ciResponse[P_RESP_LEN]; /* Unicode representation */
514 BYTE csResponse[P_RESP_LEN]; /* ANSI representation */
515 WCHAR accountNameW[P_LEN];
516 WCHAR primaryDomainW[P_LEN];
517 WCHAR workstationW[MAX_COMPUTERNAME_LENGTH + 1];
518 TOKEN_GROUPS tgroups;
519 TOKEN_SOURCE tsource;
522 long smb_AuthenticateUserLM(smb_vc_t *vcp, clientchar_t * accountName, clientchar_t * primaryDomain, char * ciPwd, unsigned ciPwdLength, char * csPwd, unsigned csPwdLength)
525 struct Lm20AuthBlob lmAuth;
526 PMSV1_0_LM20_LOGON_PROFILE lmprofilep;
527 QUOTA_LIMITS quotaLimits;
529 ULONG lmprofilepSize;
533 OutputDebugF(_C("In smb_AuthenticateUser for user [%s] domain [%s]"), accountName, primaryDomain);
534 OutputDebugF(_C("ciPwdLength is %d and csPwdLength is %d"), ciPwdLength, csPwdLength);
536 if (ciPwdLength > P_RESP_LEN || csPwdLength > P_RESP_LEN) {
537 OutputDebugF(_C("ciPwdLength or csPwdLength is too long"));
538 return CM_ERROR_BADPASSWORD;
541 memset(&lmAuth,0,sizeof(lmAuth));
543 lmAuth.lmlogon.MessageType = MsV1_0NetworkLogon;
545 lmAuth.lmlogon.LogonDomainName.Buffer = lmAuth.primaryDomainW;
546 cm_ClientStringToUtf16(primaryDomain, -1, lmAuth.primaryDomainW, P_LEN);
547 lmAuth.lmlogon.LogonDomainName.Length = (USHORT)(wcslen(lmAuth.primaryDomainW) * sizeof(WCHAR));
548 lmAuth.lmlogon.LogonDomainName.MaximumLength = P_LEN * sizeof(WCHAR);
550 lmAuth.lmlogon.UserName.Buffer = lmAuth.accountNameW;
551 cm_ClientStringToUtf16(accountName, -1, lmAuth.accountNameW, P_LEN);
552 lmAuth.lmlogon.UserName.Length = (USHORT)(wcslen(lmAuth.accountNameW) * sizeof(WCHAR));
553 lmAuth.lmlogon.UserName.MaximumLength = P_LEN * sizeof(WCHAR);
555 lmAuth.lmlogon.Workstation.Buffer = lmAuth.workstationW;
556 lmAuth.lmlogon.Workstation.MaximumLength = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR);
557 size = MAX_COMPUTERNAME_LENGTH + 1;
558 GetComputerNameW(lmAuth.workstationW, &size);
559 lmAuth.lmlogon.Workstation.Length = (USHORT)(wcslen(lmAuth.workstationW) * sizeof(WCHAR));
561 memcpy(lmAuth.lmlogon.ChallengeToClient, vcp->encKey, MSV1_0_CHALLENGE_LENGTH);
563 lmAuth.lmlogon.CaseInsensitiveChallengeResponse.Buffer = lmAuth.ciResponse;
564 lmAuth.lmlogon.CaseInsensitiveChallengeResponse.Length = ciPwdLength;
565 lmAuth.lmlogon.CaseInsensitiveChallengeResponse.MaximumLength = P_RESP_LEN;
566 memcpy(lmAuth.ciResponse, ciPwd, ciPwdLength);
568 lmAuth.lmlogon.CaseSensitiveChallengeResponse.Buffer = lmAuth.csResponse;
569 lmAuth.lmlogon.CaseSensitiveChallengeResponse.Length = csPwdLength;
570 lmAuth.lmlogon.CaseSensitiveChallengeResponse.MaximumLength = P_RESP_LEN;
571 memcpy(lmAuth.csResponse, csPwd, csPwdLength);
573 lmAuth.lmlogon.ParameterControl = 0;
575 lmAuth.tgroups.GroupCount = 0;
576 lmAuth.tgroups.Groups[0].Sid = NULL;
577 lmAuth.tgroups.Groups[0].Attributes = 0;
580 lmAuth.tsource.SourceIdentifier.HighPart = (DWORD)((LONG_PTR)vcp << 32);
582 lmAuth.tsource.SourceIdentifier.HighPart = 0;
584 lmAuth.tsource.SourceIdentifier.LowPart = (DWORD)((LONG_PTR)vcp & _UI32_MAX);
585 StringCchCopyA(lmAuth.tsource.SourceName, lengthof(lmAuth.tsource.SourceName),
586 "OpenAFS"); /* 8 char limit */
588 nts = LsaLogonUser( smb_lsaHandle,
603 if (nts != STATUS_SUCCESS || ntsEx != STATUS_SUCCESS)
604 osi_Log2(smb_logp,"LsaLogonUser failure: nts %u ntsEx %u",
607 OutputDebugF(_C("Return from LsaLogonUser is 0x%lX"), nts);
608 OutputDebugF(_C("Extended status is 0x%lX"), ntsEx);
610 if (nts == ERROR_SUCCESS) {
612 LsaFreeReturnBuffer(lmprofilep);
613 CloseHandle(lmToken);
617 if (nts == 0xC000015BL)
618 return CM_ERROR_BADLOGONTYPE;
619 else /* our catchall is a bad password though we could be more specific */
620 return CM_ERROR_BADPASSWORD;
624 /* The buffer pointed to by usern is assumed to be at least SMB_MAX_USERNAME_LENGTH bytes */
625 long smb_GetNormalizedUsername(clientchar_t * usern, const clientchar_t * accountName, const clientchar_t * domainName)
627 clientchar_t * atsign;
628 const clientchar_t * domain;
630 /* check if we have sane input */
631 if ((cm_ClientStrLen(accountName) + cm_ClientStrLen(domainName) + 1) > SMB_MAX_USERNAME_LENGTH)
634 /* we could get : [accountName][domainName]
640 atsign = cm_ClientStrChr(accountName, '@');
642 if (atsign) /* [user@domain][] -> [user@domain][domain] */
647 /* if for some reason the client doesn't know what domain to use,
648 it will either return an empty string or a '?' */
649 if (!domain[0] || domain[0] == '?')
650 /* Empty domains and empty usernames are usually sent from tokenless contexts.
651 This way such logins will get an empty username (easy to check). I don't know
652 when a non-empty username would be supplied with an anonymous domain, but *shrug* */
653 cm_ClientStrCpy(usern, SMB_MAX_USERNAME_LENGTH, accountName);
655 /* TODO: what about WIN.MIT.EDU\user vs. WIN\user? */
656 cm_ClientStrCpy(usern, SMB_MAX_USERNAME_LENGTH, domain);
657 cm_ClientStrCat(usern, SMB_MAX_USERNAME_LENGTH, _C("\\"));
659 cm_ClientStrCat(usern, SMB_MAX_USERNAME_LENGTH, accountName);
661 cm_ClientStrCat(usern, SMB_MAX_USERNAME_LENGTH, accountName);
664 cm_ClientStrLwr(usern);
669 /* When using SMB auth, all SMB sessions have to pass through here
670 * first to authenticate the user.
672 * Caveat: If not using SMB auth, the protocol does not require
673 * sending a session setup packet, which means that we can't rely on a
674 * UID in subsequent packets. Though in practice we get one anyway.
676 /* SMB_COM_SESSION_SETUP_ANDX */
677 long smb_ReceiveV3SessionSetupX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
681 unsigned short newUid;
682 unsigned long caps = 0;
684 clientchar_t *s1 = _C(" ");
686 clientchar_t usern[SMB_MAX_USERNAME_LENGTH];
687 char *secBlobOut = NULL;
688 int secBlobOutLength = 0;
690 /* Check for bad conns */
691 if (vcp->flags & SMB_VCFLAG_REMOTECONN)
692 return CM_ERROR_REMOTECONN;
694 if (vcp->flags & SMB_VCFLAG_USENT) {
695 if (smb_authType == SMB_AUTH_EXTENDED) {
696 /* extended authentication */
700 OutputDebugF(_C("NT Session Setup: Extended"));
702 if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
703 caps = smb_GetSMBParm(inp,10) | (((unsigned long) smb_GetSMBParm(inp,11)) << 16);
706 secBlobInLength = smb_GetSMBParm(inp, 7);
707 secBlobIn = smb_GetSMBData(inp, NULL);
709 code = smb_AuthenticateUserExt(vcp, usern, secBlobIn, secBlobInLength, &secBlobOut, &secBlobOutLength);
711 if (code == CM_ERROR_GSSCONTINUE) {
714 smb_SetSMBParm(outp, 2, 0);
715 smb_SetSMBParm(outp, 3, secBlobOutLength);
717 tp = smb_GetSMBData(outp, NULL);
718 if (secBlobOutLength) {
719 memcpy(tp, secBlobOut, secBlobOutLength);
721 tp += secBlobOutLength;
722 cb_data += secBlobOutLength;
724 tp = smb_UnparseString(outp, tp, smb_ServerOS, &cb_data, 0);
725 tp = smb_UnparseString(outp, tp, smb_ServerLanManager, &cb_data, 0);
726 tp = smb_UnparseString(outp, tp, smb_ServerDomainName, &cb_data, 0);
728 smb_SetSMBDataLength(outp, cb_data);
731 /* TODO: handle return code and continue auth. Also free secBlobOut if applicable. */
733 unsigned ciPwdLength, csPwdLength;
735 clientchar_t *accountName;
736 clientchar_t *primaryDomain;
739 if (smb_authType == SMB_AUTH_NTLM)
740 OutputDebugF(_C("NT Session Setup: NTLM"));
742 OutputDebugF(_C("NT Session Setup: None"));
744 /* TODO: parse for extended auth as well */
745 ciPwdLength = smb_GetSMBParm(inp, 7); /* case insensitive password length */
746 csPwdLength = smb_GetSMBParm(inp, 8); /* case sensitive password length */
748 tp = smb_GetSMBData(inp, &datalen);
750 OutputDebugF(_C("Session packet data size [%d]"),datalen);
757 accountName = smb_ParseString(inp, tp, &tp, 0);
758 primaryDomain = smb_ParseString(inp, tp, NULL, 0);
760 OutputDebugF(_C("Account Name: %s"),accountName);
761 OutputDebugF(_C("Primary Domain: %s"), primaryDomain);
762 OutputDebugF(_C("Case Sensitive Password: %s"),
763 csPwd && csPwd[0] ? _C("yes") : _C("no"));
764 OutputDebugF(_C("Case Insensitive Password: %s"),
765 ciPwd && ciPwd[0] ? _C("yes") : _C("no"));
767 if (smb_GetNormalizedUsername(usern, accountName, primaryDomain)) {
768 /* shouldn't happen */
769 code = CM_ERROR_BADSMB;
770 goto after_read_packet;
773 /* capabilities are only valid for first session packet */
774 if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
775 caps = smb_GetSMBParm(inp, 11) | (((unsigned long)smb_GetSMBParm(inp, 12)) << 16);
778 if (smb_authType == SMB_AUTH_NTLM) {
779 code = smb_AuthenticateUserLM(vcp, accountName, primaryDomain, ciPwd, ciPwdLength, csPwd, csPwdLength);
781 OutputDebugF(_C("LM authentication failed [%d]"), code);
783 OutputDebugF(_C("LM authentication succeeded"));
787 unsigned ciPwdLength;
789 clientchar_t *accountName;
790 clientchar_t *primaryDomain;
792 switch ( smb_authType ) {
793 case SMB_AUTH_EXTENDED:
794 OutputDebugF(_C("V3 Session Setup: Extended"));
797 OutputDebugF(_C("V3 Session Setup: NTLM"));
800 OutputDebugF(_C("V3 Session Setup: None"));
802 ciPwdLength = smb_GetSMBParm(inp, 7);
803 tp = smb_GetSMBData(inp, NULL);
807 accountName = smb_ParseString(inp, tp, &tp, 0);
808 primaryDomain = smb_ParseString(inp, tp, NULL, 0);
810 OutputDebugF(_C("Account Name: %s"),accountName);
811 OutputDebugF(_C("Primary Domain: %s"), primaryDomain);
812 OutputDebugF(_C("Case Insensitive Password: %s"), ciPwd && ciPwd[0] ? _C("yes") : _C("no"));
814 if ( smb_GetNormalizedUsername(usern, accountName, primaryDomain)) {
815 /* shouldn't happen */
816 code = CM_ERROR_BADSMB;
817 goto after_read_packet;
820 /* even if we wanted extended auth, if we only negotiated V3, we have to fallback
823 if (smb_authType == SMB_AUTH_NTLM || smb_authType == SMB_AUTH_EXTENDED) {
824 code = smb_AuthenticateUserLM(vcp,accountName,primaryDomain,ciPwd,ciPwdLength,"",0);
826 OutputDebugF(_C("LM authentication failed [%d]"), code);
828 OutputDebugF(_C("LM authentication succeeded"));
833 /* note down that we received a session setup X and set the capabilities flag */
834 if (!(vcp->flags & SMB_VCFLAG_SESSX_RCVD)) {
835 lock_ObtainMutex(&vcp->mx);
836 vcp->flags |= SMB_VCFLAG_SESSX_RCVD;
837 /* for the moment we can only deal with NTSTATUS */
838 if (caps & NTNEGOTIATE_CAPABILITY_NTSTATUS) {
839 vcp->flags |= SMB_VCFLAG_STATUS32;
843 if ((caps & NTNEGOTIATE_CAPABILITY_UNICODE) && smb_UseUnicode) {
844 vcp->flags |= SMB_VCFLAG_USEUNICODE;
847 lock_ReleaseMutex(&vcp->mx);
850 /* code would be non-zero if there was an authentication failure.
851 Ideally we would like to invalidate the uid for this session or break
852 early to avoid accidently stealing someone else's tokens. */
858 OutputDebugF(_C("Received username=[%s]"), usern);
860 /* On Windows 2000, this function appears to be called more often than
861 it is expected to be called. This resulted in multiple smb_user_t
862 records existing all for the same user session which results in all
863 of the users tokens disappearing.
865 To avoid this problem, we look for an existing smb_user_t record
866 based on the users name, and use that one if we find it.
869 uidp = smb_FindUserByNameThisSession(vcp, usern);
870 if (uidp) { /* already there, so don't create a new one */
872 newUid = uidp->userID;
873 osi_Log3(smb_logp,"smb_ReceiveV3SessionSetupX FindUserByName:Lana[%d],lsn[%d],userid[%d]",
874 vcp->lana,vcp->lsn,newUid);
875 smb_ReleaseUID(uidp);
880 /* do a global search for the username/machine name pair */
881 unp = smb_FindUserByName(usern, vcp->rname, SMB_FLAG_CREATE);
882 lock_ObtainMutex(&unp->mx);
883 if (unp->flags & SMB_USERNAMEFLAG_AFSLOGON) {
884 /* clear the afslogon flag so that the tickets can now
885 * be freed when the refCount returns to zero.
887 unp->flags &= ~SMB_USERNAMEFLAG_AFSLOGON;
889 lock_ReleaseMutex(&unp->mx);
891 /* Create a new UID and cm_user_t structure */
894 userp = cm_NewUser();
895 cm_HoldUserVCRef(userp);
896 lock_ObtainMutex(&vcp->mx);
897 if (!vcp->uidCounter)
898 vcp->uidCounter++; /* handle unlikely wraparounds */
899 newUid = (cm_ClientStrLen(usern)==0)?0:vcp->uidCounter++;
900 lock_ReleaseMutex(&vcp->mx);
902 /* Create a new smb_user_t structure and connect them up */
903 lock_ObtainMutex(&unp->mx);
905 lock_ReleaseMutex(&unp->mx);
907 uidp = smb_FindUID(vcp, newUid, SMB_FLAG_CREATE);
909 lock_ObtainMutex(&uidp->mx);
911 osi_Log4(smb_logp,"smb_ReceiveV3SessionSetupX MakeNewUser:VCP[%p],Lana[%d],lsn[%d],userid[%d]",vcp,vcp->lana,vcp->lsn,newUid);
912 lock_ReleaseMutex(&uidp->mx);
913 smb_ReleaseUID(uidp);
917 /* Return UID to the client */
918 ((smb_t *)outp)->uid = newUid;
919 /* Also to the next chained message */
920 ((smb_t *)inp)->uid = newUid;
922 osi_Log3(smb_logp, "SMB3 session setup name %S creating ID %d%S",
923 osi_LogSaveClientString(smb_logp, usern), newUid,
924 osi_LogSaveClientString(smb_logp, s1));
926 smb_SetSMBParm(outp, 2, 0);
928 if (vcp->flags & SMB_VCFLAG_USENT) {
929 if (smb_authType == SMB_AUTH_EXTENDED) {
932 smb_SetSMBParm(outp, 3, secBlobOutLength);
934 tp = smb_GetSMBData(outp, NULL);
935 if (secBlobOutLength) {
936 memcpy(tp, secBlobOut, secBlobOutLength);
938 tp += secBlobOutLength;
939 cb_data += secBlobOutLength;
942 tp = smb_UnparseString(outp, tp, smb_ServerOS, &cb_data, 0);
943 tp = smb_UnparseString(outp, tp, smb_ServerLanManager, &cb_data, 0);
944 tp = smb_UnparseString(outp, tp, smb_ServerDomainName, &cb_data, 0);
946 smb_SetSMBDataLength(outp, cb_data);
948 smb_SetSMBDataLength(outp, 0);
951 if (smb_authType == SMB_AUTH_EXTENDED) {
954 tp = smb_GetSMBData(outp, NULL);
956 tp = smb_UnparseString(outp, tp, smb_ServerOS, &cb_data, 0);
957 tp = smb_UnparseString(outp, tp, smb_ServerLanManager, &cb_data, 0);
958 tp = smb_UnparseString(outp, tp, smb_ServerDomainName, &cb_data, 0);
960 smb_SetSMBDataLength(outp, cb_data);
962 smb_SetSMBDataLength(outp, 0);
969 /* SMB_COM_LOGOFF_ANDX */
970 long smb_ReceiveV3UserLogoffX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
974 /* find the tree and free it */
975 uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
977 smb_username_t * unp;
979 osi_Log2(smb_logp, "SMB3 user logoffX uid %d name %S", uidp->userID,
980 osi_LogSaveClientString(smb_logp, (uidp->unp) ? uidp->unp->name: _C(" ")));
982 lock_ObtainMutex(&uidp->mx);
983 uidp->flags |= SMB_USERFLAG_DELETE;
985 * it doesn't get deleted right away
986 * because the vcp points to it
989 lock_ReleaseMutex(&uidp->mx);
992 /* we can't do this. we get logoff messages prior to a session
993 * disconnect even though it doesn't mean the user is logging out.
994 * we need to create a new pioctl and EventLogoff handler to set
995 * SMB_USERNAMEFLAG_LOGOFF.
997 if (unp && smb_LogoffTokenTransfer) {
998 lock_ObtainMutex(&unp->mx);
999 unp->flags |= SMB_USERNAMEFLAG_LOGOFF;
1000 unp->last_logoff_t = osi_Time() + smb_LogoffTransferTimeout;
1001 lock_ReleaseMutex(&unp->mx);
1005 smb_ReleaseUID(uidp);
1008 osi_Log0(smb_logp, "SMB3 user logoffX");
1010 smb_SetSMBDataLength(outp, 0);
1014 #define SMB_SUPPORT_SEARCH_BITS 0x0001
1015 #define SMB_SHARE_IS_IN_DFS 0x0002
1017 /* SMB_COM_TREE_CONNECT_ANDX */
1018 long smb_ReceiveV3TreeConnectX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
1021 smb_user_t *uidp = NULL;
1022 unsigned short newTid;
1023 clientchar_t shareName[AFSPATHMAX];
1024 clientchar_t *sharePath;
1027 clientchar_t *slashp;
1028 clientchar_t *pathp;
1029 clientchar_t *passwordp;
1030 clientchar_t *servicep;
1031 cm_user_t *userp = NULL;
1034 osi_Log0(smb_logp, "SMB3 receive tree connect");
1036 /* parse input parameters */
1037 tp = smb_GetSMBData(inp, NULL);
1038 passwordp = smb_ParseString(inp, tp, &tp, SMB_STRF_FORCEASCII);
1039 pathp = smb_ParseString(inp, tp, &tp, SMB_STRF_ANSIPATH);
1040 servicep = smb_ParseString(inp, tp, &tp, SMB_STRF_FORCEASCII);
1042 slashp = cm_ClientStrRChr(pathp, '\\');
1044 return CM_ERROR_BADSMB;
1046 cm_ClientStrCpy(shareName, lengthof(shareName), slashp+1);
1048 osi_Log3(smb_logp, "Tree connect pathp[%S] shareName[%S] service[%S]",
1049 osi_LogSaveClientString(smb_logp, pathp),
1050 osi_LogSaveClientString(smb_logp, shareName),
1051 osi_LogSaveClientString(smb_logp, servicep));
1053 if (cm_ClientStrCmp(servicep, _C("IPC")) == 0 ||
1054 cm_ClientStrCmp(shareName, _C("IPC$")) == 0) {
1056 osi_Log0(smb_logp, "TreeConnectX connecting to IPC$");
1059 return CM_ERROR_NOIPC;
1063 uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
1065 userp = smb_GetUserFromUID(uidp);
1067 lock_ObtainMutex(&vcp->mx);
1068 newTid = vcp->tidCounter++;
1069 lock_ReleaseMutex(&vcp->mx);
1071 tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
1074 if (!cm_ClientStrCmp(shareName, _C("*.")))
1075 cm_ClientStrCpy(shareName, lengthof(shareName), _C("all"));
1076 shareFound = smb_FindShare(vcp, uidp, shareName, &sharePath);
1079 smb_ReleaseUID(uidp);
1080 smb_ReleaseTID(tidp, FALSE);
1081 return CM_ERROR_BADSHARENAME;
1084 if (vcp->flags & SMB_VCFLAG_USENT)
1086 int policy = smb_FindShareCSCPolicy(shareName);
1089 DWORD dwAdvertiseDFS = 0, dwSize = sizeof(DWORD);
1091 code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
1092 0, KEY_QUERY_VALUE, &parmKey);
1093 if (code == ERROR_SUCCESS) {
1094 code = RegQueryValueEx(parmKey, "AdvertiseDFS", NULL, NULL,
1095 (BYTE *)&dwAdvertiseDFS, &dwSize);
1096 if (code != ERROR_SUCCESS)
1098 RegCloseKey (parmKey);
1100 smb_SetSMBParm(outp, 2, SMB_SUPPORT_SEARCH_BITS |
1101 (dwAdvertiseDFS ? SMB_SHARE_IS_IN_DFS : 0) |
1105 smb_SetSMBParm(outp, 2, 0);
1109 smb_ReleaseUID(uidp);
1111 lock_ObtainMutex(&tidp->mx);
1112 tidp->userp = userp;
1113 tidp->pathname = sharePath;
1115 tidp->flags |= SMB_TIDFLAG_IPC;
1116 lock_ReleaseMutex(&tidp->mx);
1117 smb_ReleaseTID(tidp, FALSE);
1119 ((smb_t *)outp)->tid = newTid;
1120 ((smb_t *)inp)->tid = newTid;
1121 tp = smb_GetSMBData(outp, NULL);
1125 tp = smb_UnparseString(outp, tp, _C("A:"), &cb_data, SMB_STRF_FORCEASCII);
1126 tp = smb_UnparseString(outp, tp, _C("AFS"), &cb_data, 0);
1127 smb_SetSMBDataLength(outp, cb_data);
1131 tp = smb_UnparseString(outp, tp, _C("IPC"), &cb_data, SMB_STRF_FORCEASCII);
1132 smb_SetSMBDataLength(outp, cb_data);
1135 osi_Log1(smb_logp, "SMB3 tree connect created ID %d", newTid);
1139 /* must be called with global tran lock held */
1140 smb_tran2Packet_t *smb_FindTran2Packet(smb_vc_t *vcp, smb_packet_t *inp)
1142 smb_tran2Packet_t *tp;
1145 smbp = (smb_t *) inp->data;
1146 for (tp = smb_tran2AssemblyQueuep; tp; tp = (smb_tran2Packet_t *) osi_QNext(&tp->q)) {
1147 if (tp->vcp == vcp && tp->mid == smbp->mid && tp->tid == smbp->tid)
1153 smb_tran2Packet_t *smb_NewTran2Packet(smb_vc_t *vcp, smb_packet_t *inp,
1154 int totalParms, int totalData)
1156 smb_tran2Packet_t *tp;
1159 smbp = (smb_t *) inp->data;
1160 tp = malloc(sizeof(*tp));
1161 memset(tp, 0, sizeof(*tp));
1164 tp->curData = tp->curParms = 0;
1165 tp->totalData = totalData;
1166 tp->totalParms = totalParms;
1167 tp->tid = smbp->tid;
1168 tp->mid = smbp->mid;
1169 tp->uid = smbp->uid;
1170 tp->pid = smbp->pid;
1171 tp->res[0] = smbp->res[0];
1172 osi_QAdd((osi_queue_t **)&smb_tran2AssemblyQueuep, &tp->q);
1173 if (totalParms != 0)
1174 tp->parmsp = malloc(totalParms);
1176 tp->datap = malloc(totalData);
1177 if (smbp->com == 0x25 || smbp->com == 0x26)
1180 tp->opcode = smb_GetSMBParm(inp, 14);
1183 tp->flags |= SMB_TRAN2PFLAG_ALLOC;
1185 if (WANTS_UNICODE(inp) && (vcp->flags & SMB_VCFLAG_USEUNICODE))
1186 tp->flags |= SMB_TRAN2PFLAG_USEUNICODE;
1191 smb_tran2Packet_t *smb_GetTran2ResponsePacket(smb_vc_t *vcp,
1192 smb_tran2Packet_t *inp, smb_packet_t *outp,
1193 int totalParms, int totalData)
1195 smb_tran2Packet_t *tp;
1196 unsigned short parmOffset;
1197 unsigned short dataOffset;
1198 unsigned short dataAlign;
1200 tp = malloc(sizeof(*tp));
1201 memset(tp, 0, sizeof(*tp));
1204 tp->curData = tp->curParms = 0;
1205 tp->totalData = totalData;
1206 tp->totalParms = totalParms;
1207 tp->oldTotalParms = totalParms;
1212 tp->res[0] = inp->res[0];
1213 tp->opcode = inp->opcode;
1217 * We calculate where the parameters and data will start.
1218 * This calculation must parallel the calculation in
1219 * smb_SendTran2Packet.
1222 parmOffset = 10*2 + 35;
1223 parmOffset++; /* round to even */
1224 tp->parmsp = (unsigned short *) (outp->data + parmOffset);
1226 dataOffset = parmOffset + totalParms;
1227 dataAlign = dataOffset & 2; /* quad-align */
1228 dataOffset += dataAlign;
1229 tp->datap = outp->data + dataOffset;
1234 /* free a tran2 packet */
1235 void smb_FreeTran2Packet(smb_tran2Packet_t *t2p)
1238 smb_ReleaseVC(t2p->vcp);
1241 if (t2p->flags & SMB_TRAN2PFLAG_ALLOC) {
1247 while (t2p->stringsp) {
1251 t2p->stringsp = ns->nextp;
1257 clientchar_t *smb_ParseStringT2Parm(smb_tran2Packet_t * p, unsigned char * inp,
1258 char ** chainpp, int flags)
1263 if (!(p->flags & SMB_TRAN2PFLAG_USEUNICODE))
1264 flags |= SMB_STRF_FORCEASCII;
1267 cb = p->totalParms - (inp - (char *)p->parmsp);
1268 if (inp < (char *) p->parmsp ||
1269 inp >= ((char *) p->parmsp) + p->totalParms) {
1270 #ifdef DEBUG_UNICODE
1276 return smb_ParseStringBuf((unsigned char *) p->parmsp, &p->stringsp,
1277 inp, &cb, chainpp, flags);
1280 /* called with a VC, an input packet to respond to, and an error code.
1281 * sends an error response.
1283 void smb_SendTran2Error(smb_vc_t *vcp, smb_tran2Packet_t *t2p,
1284 smb_packet_t *tp, long code)
1287 unsigned short errCode;
1288 unsigned char errClass;
1289 unsigned long NTStatus;
1291 if (vcp->flags & SMB_VCFLAG_STATUS32)
1292 smb_MapNTError(code, &NTStatus);
1294 smb_MapCoreError(code, vcp, &errCode, &errClass);
1296 smb_FormatResponsePacket(vcp, NULL, tp);
1297 smbp = (smb_t *) tp;
1299 /* We can handle long names */
1300 if (vcp->flags & SMB_VCFLAG_USENT)
1301 smbp->flg2 |= SMB_FLAGS2_IS_LONG_NAME;
1303 /* now copy important fields from the tran 2 packet */
1304 smbp->com = t2p->com;
1305 smbp->tid = t2p->tid;
1306 smbp->mid = t2p->mid;
1307 smbp->pid = t2p->pid;
1308 smbp->uid = t2p->uid;
1309 smbp->res[0] = t2p->res[0];
1310 if (vcp->flags & SMB_VCFLAG_STATUS32) {
1311 smbp->rcls = (unsigned char) (NTStatus & 0xff);
1312 smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
1313 smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
1314 smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
1315 smbp->flg2 |= SMB_FLAGS2_32BIT_STATUS;
1318 smbp->rcls = errClass;
1319 smbp->errLow = (unsigned char) (errCode & 0xff);
1320 smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
1324 smb_SendPacket(vcp, tp);
1327 void smb_SendTran2Packet(smb_vc_t *vcp, smb_tran2Packet_t *t2p, smb_packet_t *tp)
1330 unsigned short parmOffset;
1331 unsigned short dataOffset;
1332 unsigned short totalLength;
1333 unsigned short dataAlign;
1336 smb_FormatResponsePacket(vcp, NULL, tp);
1337 smbp = (smb_t *) tp;
1339 /* We can handle long names */
1340 if (vcp->flags & SMB_VCFLAG_USENT)
1341 smbp->flg2 |= SMB_FLAGS2_IS_LONG_NAME;
1343 /* now copy important fields from the tran 2 packet */
1344 smbp->com = t2p->com;
1345 smbp->tid = t2p->tid;
1346 smbp->mid = t2p->mid;
1347 smbp->pid = t2p->pid;
1348 smbp->uid = t2p->uid;
1349 smbp->res[0] = t2p->res[0];
1351 totalLength = 1 + t2p->totalData + t2p->totalParms;
1353 /* now add the core parameters (tran2 info) to the packet */
1354 smb_SetSMBParm(tp, 0, t2p->totalParms); /* parm bytes */
1355 smb_SetSMBParm(tp, 1, t2p->totalData); /* data bytes */
1356 smb_SetSMBParm(tp, 2, 0); /* reserved */
1357 smb_SetSMBParm(tp, 3, t2p->totalParms); /* parm bytes in this packet */
1358 parmOffset = 10*2 + 35; /* parm offset in packet */
1359 parmOffset++; /* round to even */
1360 smb_SetSMBParm(tp, 4, parmOffset); /* 11 parm words plus *
1361 * hdr, bcc and wct */
1362 smb_SetSMBParm(tp, 5, 0); /* parm displacement */
1363 smb_SetSMBParm(tp, 6, t2p->totalData); /* data in this packet */
1364 dataOffset = parmOffset + t2p->oldTotalParms;
1365 dataAlign = dataOffset & 2; /* quad-align */
1366 dataOffset += dataAlign;
1367 smb_SetSMBParm(tp, 7, dataOffset); /* offset of data */
1368 smb_SetSMBParm(tp, 8, 0); /* data displacement */
1369 smb_SetSMBParm(tp, 9, 0); /* low: setup word count *
1372 datap = smb_GetSMBData(tp, NULL);
1373 *datap++ = 0; /* we rounded to even */
1375 totalLength += dataAlign;
1376 smb_SetSMBDataLength(tp, totalLength);
1378 /* next, send the datagram */
1379 smb_SendPacket(vcp, tp);
1383 /* SMB_COM_TRANSACTION and SMB_COM_TRANSACTION_SECONDARY */
1384 long smb_ReceiveV3Trans(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
1386 smb_tran2Packet_t *asp;
1399 /* We sometimes see 0 word count. What to do? */
1400 if (*inp->wctp == 0) {
1401 osi_Log0(smb_logp, "Transaction2 word count = 0");
1402 LogEvent(EVENTLOG_WARNING_TYPE, MSG_SMB_ZERO_TRANSACTION_COUNT);
1404 smb_SetSMBDataLength(outp, 0);
1405 smb_SendPacket(vcp, outp);
1409 totalParms = smb_GetSMBParm(inp, 0);
1410 totalData = smb_GetSMBParm(inp, 1);
1412 firstPacket = (inp->inCom == 0x25);
1414 /* find the packet we're reassembling */
1415 lock_ObtainWrite(&smb_globalLock);
1416 asp = smb_FindTran2Packet(vcp, inp);
1418 asp = smb_NewTran2Packet(vcp, inp, totalParms, totalData);
1420 lock_ReleaseWrite(&smb_globalLock);
1422 /* now merge in this latest packet; start by looking up offsets */
1424 parmDisp = dataDisp = 0;
1425 parmOffset = smb_GetSMBParm(inp, 10);
1426 dataOffset = smb_GetSMBParm(inp, 12);
1427 parmCount = smb_GetSMBParm(inp, 9);
1428 dataCount = smb_GetSMBParm(inp, 11);
1429 asp->maxReturnParms = smb_GetSMBParm(inp, 2);
1430 asp->maxReturnData = smb_GetSMBParm(inp, 3);
1432 osi_Log3(smb_logp, "SMB3 received Trans init packet total data %d, cur data %d, max return data %d",
1433 totalData, dataCount, asp->maxReturnData);
1436 parmDisp = smb_GetSMBParm(inp, 4);
1437 parmOffset = smb_GetSMBParm(inp, 3);
1438 dataDisp = smb_GetSMBParm(inp, 7);
1439 dataOffset = smb_GetSMBParm(inp, 6);
1440 parmCount = smb_GetSMBParm(inp, 2);
1441 dataCount = smb_GetSMBParm(inp, 5);
1443 osi_Log2(smb_logp, "SMB3 received Trans aux packet parms %d, data %d",
1444 parmCount, dataCount);
1447 /* now copy the parms and data */
1448 if ( asp->totalParms > 0 && parmCount != 0 )
1450 memcpy(((char *)asp->parmsp) + parmDisp, inp->data + parmOffset, parmCount);
1452 if ( asp->totalData > 0 && dataCount != 0 ) {
1453 memcpy(asp->datap + dataDisp, inp->data + dataOffset, dataCount);
1456 /* account for new bytes */
1457 asp->curData += dataCount;
1458 asp->curParms += parmCount;
1460 /* finally, if we're done, remove the packet from the queue and dispatch it */
1461 if (asp->totalParms > 0 &&
1462 asp->curParms > 0 &&
1463 asp->totalData <= asp->curData &&
1464 asp->totalParms <= asp->curParms) {
1465 /* we've received it all */
1466 lock_ObtainWrite(&smb_globalLock);
1467 osi_QRemove((osi_queue_t **) &smb_tran2AssemblyQueuep, &asp->q);
1468 lock_ReleaseWrite(&smb_globalLock);
1470 /* now dispatch it */
1471 rapOp = asp->parmsp[0];
1473 if ( rapOp >= 0 && rapOp < SMB_RAP_NOPCODES && smb_rapDispatchTable[rapOp].procp) {
1474 osi_Log4(smb_logp,"AFS Server - Dispatch-RAP %s vcp[%p] lana[%d] lsn[%d]",myCrt_RapDispatch(rapOp),vcp,vcp->lana,vcp->lsn);
1475 code = (*smb_rapDispatchTable[rapOp].procp)(vcp, asp, outp);
1476 osi_Log4(smb_logp,"AFS Server - Dispatch-RAP return code 0x%x vcp[%x] lana[%d] lsn[%d]",code,vcp,vcp->lana,vcp->lsn);
1479 osi_Log4(smb_logp,"AFS Server - Dispatch-RAP [INVALID] op[%x] vcp[%p] lana[%d] lsn[%d]", rapOp, vcp, vcp->lana, vcp->lsn);
1480 code = CM_ERROR_BADOP;
1483 /* if an error is returned, we're supposed to send an error packet,
1484 * otherwise the dispatched function already did the data sending.
1485 * We give dispatched proc the responsibility since it knows how much
1486 * space to allocate.
1489 smb_SendTran2Error(vcp, asp, outp, code);
1492 /* free the input tran 2 packet */
1493 smb_FreeTran2Packet(asp);
1495 else if (firstPacket) {
1496 /* the first packet in a multi-packet request, we need to send an
1497 * ack to get more data.
1499 smb_SetSMBDataLength(outp, 0);
1500 smb_SendPacket(vcp, outp);
1506 /* ANSI versions. */
1508 #pragma pack(push, 1)
1510 typedef struct smb_rap_share_info_0 {
1511 BYTE shi0_netname[13];
1512 } smb_rap_share_info_0_t;
1514 typedef struct smb_rap_share_info_1 {
1515 BYTE shi1_netname[13];
1518 DWORD shi1_remark; /* char *shi1_remark; data offset */
1519 } smb_rap_share_info_1_t;
1521 typedef struct smb_rap_share_info_2 {
1522 BYTE shi2_netname[13];
1525 DWORD shi2_remark; /* char *shi2_remark; data offset */
1526 WORD shi2_permissions;
1528 WORD shi2_current_uses;
1529 DWORD shi2_path; /* char *shi2_path; data offset */
1530 WORD shi2_passwd[9];
1532 } smb_rap_share_info_2_t;
1534 #define SMB_RAP_MAX_SHARES 512
1536 typedef struct smb_rap_share_list {
1539 smb_rap_share_info_0_t * shares;
1540 } smb_rap_share_list_t;
1544 int smb_rapCollectSharesProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp) {
1545 smb_rap_share_list_t * sp;
1547 if (dep->name[0] == '.' && (!dep->name[1] || (dep->name[1] == '.' && !dep->name[2])))
1548 return 0; /* skip over '.' and '..' */
1550 sp = (smb_rap_share_list_t *) vrockp;
1552 strncpy(sp->shares[sp->cShare].shi0_netname, dep->name, 12);
1553 sp->shares[sp->cShare].shi0_netname[12] = 0;
1557 if (sp->cShare >= sp->maxShares)
1558 return CM_ERROR_STOPNOW;
1563 /* RAP NetShareEnumRequest */
1564 long smb_ReceiveRAPNetShareEnum(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
1566 smb_tran2Packet_t *outp;
1567 unsigned short * tp;
1571 int outParmsTotal; /* total parameter bytes */
1572 int outDataTotal; /* total data bytes */
1575 DWORD allSubmount = 0;
1577 DWORD nRegShares = 0;
1578 DWORD nSharesRet = 0;
1580 HKEY hkSubmount = NULL;
1581 smb_rap_share_info_1_t * shares;
1584 clientchar_t thisShare[AFSPATHMAX];
1588 smb_rap_share_list_t rootShares;
1593 tp = p->parmsp + 1; /* skip over function number (always 0) */
1596 clientchar_t * cdescp;
1598 cdescp = smb_ParseStringT2Parm(p, (char *) tp, (char **) &tp, SMB_STRF_FORCEASCII);
1599 if (cm_ClientStrCmp(cdescp, _C("WrLeh")))
1600 return CM_ERROR_INVAL;
1601 cdescp = smb_ParseStringT2Parm(p, (char *) tp, (char **) &tp, SMB_STRF_FORCEASCII);
1602 if (cm_ClientStrCmp(cdescp, _C("B13BWz")))
1603 return CM_ERROR_INVAL;
1609 if (infoLevel != 1) {
1610 return CM_ERROR_INVAL;
1613 /* We are supposed to use the same ASCII data structure even if
1614 Unicode is negotiated, which ultimately means that the share
1615 names that we return must be at most 13 characters in length,
1616 including the NULL terminator.
1618 The RAP specification states that shares with names longer than
1619 12 characters should not be included in the enumeration.
1620 However, since we support prefix cell references and since many
1621 cell names are going to exceed 12 characters, we lie and send
1622 the first 12 characters.
1625 /* first figure out how many shares there are */
1626 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0,
1627 KEY_QUERY_VALUE, &hkParam);
1628 if (rv == ERROR_SUCCESS) {
1629 len = sizeof(allSubmount);
1630 rv = RegQueryValueEx(hkParam, "AllSubmount", NULL, NULL,
1631 (BYTE *) &allSubmount, &len);
1632 if (rv != ERROR_SUCCESS || allSubmount != 0) {
1635 RegCloseKey (hkParam);
1638 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_OPENAFS_SUBKEY "\\Submounts",
1639 0, KEY_QUERY_VALUE, &hkSubmount);
1640 if (rv == ERROR_SUCCESS) {
1641 rv = RegQueryInfoKey(hkSubmount, NULL, NULL, NULL, NULL,
1642 NULL, NULL, &nRegShares, NULL, NULL, NULL, NULL);
1643 if (rv != ERROR_SUCCESS)
1649 /* fetch the root shares */
1650 rootShares.maxShares = SMB_RAP_MAX_SHARES;
1651 rootShares.cShare = 0;
1652 rootShares.shares = malloc( sizeof(smb_rap_share_info_0_t) * SMB_RAP_MAX_SHARES );
1656 userp = smb_GetTran2User(vcp,p);
1658 thyper.HighPart = 0;
1661 cm_HoldSCache(cm_data.rootSCachep);
1662 cm_ApplyDir(cm_data.rootSCachep, smb_rapCollectSharesProc, &rootShares, &thyper, userp, &req, NULL);
1663 cm_ReleaseSCache(cm_data.rootSCachep);
1665 cm_ReleaseUser(userp);
1667 nShares = (USHORT)(rootShares.cShare + nRegShares + allSubmount);
1669 #define REMARK_LEN 1
1670 outParmsTotal = 8; /* 4 dwords */
1671 outDataTotal = (sizeof(smb_rap_share_info_1_t) + REMARK_LEN) * nShares ;
1672 if(outDataTotal > bufsize) {
1673 nSharesRet = bufsize / (sizeof(smb_rap_share_info_1_t) + REMARK_LEN);
1674 outDataTotal = (sizeof(smb_rap_share_info_1_t) + REMARK_LEN) * nSharesRet;
1677 nSharesRet = nShares;
1680 outp = smb_GetTran2ResponsePacket(vcp, p, op, outParmsTotal, outDataTotal);
1682 /* now for the submounts */
1683 shares = (smb_rap_share_info_1_t *) outp->datap;
1684 cstrp = outp->datap + sizeof(smb_rap_share_info_1_t) * nSharesRet;
1686 memset(outp->datap, 0, (sizeof(smb_rap_share_info_1_t) + REMARK_LEN) * nSharesRet);
1689 StringCchCopyA(shares[cshare].shi1_netname,
1690 lengthof(shares[cshare].shi1_netname), "all" );
1691 shares[cshare].shi1_remark = (DWORD)(cstrp - outp->datap);
1692 /* type and pad are zero already */
1698 for (dw=0; dw < nRegShares && cshare < nSharesRet; dw++) {
1699 len = sizeof(thisShare);
1700 rv = RegEnumValueW(hkSubmount, dw, thisShare, &len, NULL, NULL, NULL, NULL);
1701 if (rv == ERROR_SUCCESS &&
1702 cm_ClientStrLen(thisShare) &&
1703 (!allSubmount || cm_ClientStrCmpI(thisShare,_C("all")))) {
1704 cm_ClientStringToUtf8(thisShare, -1, shares[cshare].shi1_netname,
1705 lengthof( shares[cshare].shi1_netname ));
1706 shares[cshare].shi1_netname[sizeof(shares->shi1_netname)-1] = 0; /* unfortunate truncation */
1707 shares[cshare].shi1_remark = (DWORD)(cstrp - outp->datap);
1712 nShares--; /* uncount key */
1715 RegCloseKey(hkSubmount);
1718 nonrootShares = cshare;
1720 for (i=0; i < rootShares.cShare && cshare < nSharesRet; i++) {
1721 /* in case there are collisions with submounts, submounts have
1723 for (j=0; j < nonrootShares; j++)
1724 if (!cm_stricmp_utf8(shares[j].shi1_netname, rootShares.shares[i].shi0_netname))
1727 if (j < nonrootShares) {
1728 nShares--; /* uncount */
1732 StringCchCopyA(shares[cshare].shi1_netname, lengthof(shares[cshare].shi1_netname),
1733 rootShares.shares[i].shi0_netname);
1734 shares[cshare].shi1_remark = (DWORD)(cstrp - outp->datap);
1739 outp->parmsp[0] = ((cshare == nShares)? ERROR_SUCCESS : ERROR_MORE_DATA);
1740 outp->parmsp[1] = 0;
1741 outp->parmsp[2] = cshare;
1742 outp->parmsp[3] = nShares;
1744 outp->totalData = (int)(cstrp - outp->datap);
1745 outp->totalParms = outParmsTotal;
1747 smb_SendTran2Packet(vcp, outp, op);
1748 smb_FreeTran2Packet(outp);
1750 free(rootShares.shares);
1755 /* RAP NetShareGetInfo */
1756 long smb_ReceiveRAPNetShareGetInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
1758 smb_tran2Packet_t *outp;
1759 unsigned short * tp;
1760 clientchar_t * shareName;
1761 BOOL shareFound = FALSE;
1762 unsigned short infoLevel;
1763 unsigned short bufsize;
1772 cm_scache_t *scp = NULL;
1778 tp = p->parmsp + 1; /* skip over function number (always 1) */
1781 clientchar_t * cdescp;
1783 cdescp = smb_ParseStringT2Parm(p, (char *) tp, (char **) &tp, SMB_STRF_FORCEASCII);
1784 if (cm_ClientStrCmp(cdescp, _C("zWrLh")))
1786 return CM_ERROR_INVAL;
1788 cdescp = smb_ParseStringT2Parm(p, (char *) tp, (char **) &tp, SMB_STRF_FORCEASCII);
1789 if (cm_ClientStrCmp(cdescp, _C("B13")) &&
1790 cm_ClientStrCmp(cdescp, _C("B13BWz")) &&
1791 cm_ClientStrCmp(cdescp, _C("B13BWzWWWzB9B")))
1793 return CM_ERROR_INVAL;
1795 shareName = smb_ParseStringT2Parm(p, (char *) tp, (char **) &tp, SMB_STRF_FORCEASCII);
1803 totalData = sizeof(smb_rap_share_info_0_t);
1804 else if(infoLevel == SMB_INFO_STANDARD)
1805 totalData = sizeof(smb_rap_share_info_1_t) + 1; /* + empty string */
1806 else if(infoLevel == SMB_INFO_QUERY_EA_SIZE)
1807 totalData = sizeof(smb_rap_share_info_2_t) + 2; /* + two empty strings */
1809 return CM_ERROR_INVAL;
1811 if(!cm_ClientStrCmpI(shareName, _C("all")) || !cm_ClientStrCmp(shareName,_C("*."))) {
1812 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0,
1813 KEY_QUERY_VALUE, &hkParam);
1814 if (rv == ERROR_SUCCESS) {
1815 len = sizeof(allSubmount);
1816 rv = RegQueryValueEx(hkParam, "AllSubmount", NULL, NULL,
1817 (BYTE *) &allSubmount, &len);
1818 if (rv != ERROR_SUCCESS || allSubmount != 0) {
1821 RegCloseKey (hkParam);
1828 userp = smb_GetTran2User(vcp, p);
1830 osi_Log1(smb_logp,"ReceiveTran2GetDfsReferral unable to resolve user [%d]", p->uid);
1831 return CM_ERROR_BADSMB;
1833 code = cm_NameI(cm_data.rootSCachep, shareName,
1834 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD | CM_FLAG_DFS_REFERRAL,
1835 userp, NULL, &req, &scp);
1837 cm_ReleaseSCache(scp);
1840 rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY "\\Submounts", 0,
1841 KEY_QUERY_VALUE, &hkSubmount);
1842 if (rv == ERROR_SUCCESS) {
1843 rv = RegQueryValueExW(hkSubmount, shareName, NULL, NULL, NULL, NULL);
1844 if (rv == ERROR_SUCCESS) {
1847 RegCloseKey(hkSubmount);
1853 return CM_ERROR_BADSHARENAME;
1855 outp = smb_GetTran2ResponsePacket(vcp, p, op, totalParam, totalData);
1856 memset(outp->datap, 0, totalData);
1858 outp->parmsp[0] = 0;
1859 outp->parmsp[1] = 0;
1860 outp->parmsp[2] = totalData;
1862 if (infoLevel == 0) {
1863 smb_rap_share_info_0_t * info = (smb_rap_share_info_0_t *) outp->datap;
1864 cm_ClientStringToUtf8(shareName, -1, info->shi0_netname,
1865 lengthof(info->shi0_netname));
1866 } else if(infoLevel == SMB_INFO_STANDARD) {
1867 smb_rap_share_info_1_t * info = (smb_rap_share_info_1_t *) outp->datap;
1868 cm_ClientStringToUtf8(shareName, -1, info->shi1_netname, lengthof(info->shi1_netname));
1869 info->shi1_netname[sizeof(info->shi1_netname)-1] = 0;
1870 info->shi1_remark = (DWORD)(((unsigned char *) (info + 1)) - outp->datap);
1871 /* type and pad are already zero */
1872 } else { /* infoLevel==2 */
1873 smb_rap_share_info_2_t * info = (smb_rap_share_info_2_t *) outp->datap;
1874 cm_ClientStringToUtf8(shareName, -1, info->shi2_netname, lengthof(info->shi2_netname));
1875 info->shi2_remark = (DWORD)(((unsigned char *) (info + 1)) - outp->datap);
1876 info->shi2_permissions = ACCESS_ALL;
1877 info->shi2_max_uses = (unsigned short) -1;
1878 info->shi2_path = (DWORD)(1 + (((unsigned char *) (info + 1)) - outp->datap));
1881 outp->totalData = totalData;
1882 outp->totalParms = totalParam;
1884 smb_SendTran2Packet(vcp, outp, op);
1885 smb_FreeTran2Packet(outp);
1890 #pragma pack(push, 1)
1892 typedef struct smb_rap_wksta_info_10 {
1893 DWORD wki10_computername; /*char *wki10_computername;*/
1894 DWORD wki10_username; /* char *wki10_username; */
1895 DWORD wki10_langroup; /* char *wki10_langroup;*/
1896 BYTE wki10_ver_major;
1897 BYTE wki10_ver_minor;
1898 DWORD wki10_logon_domain; /*char *wki10_logon_domain;*/
1899 DWORD wki10_oth_domains; /* char *wki10_oth_domains;*/
1900 } smb_rap_wksta_info_10_t;
1904 long smb_ReceiveRAPNetWkstaGetInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
1906 smb_tran2Packet_t *outp;
1910 unsigned short * tp;
1913 smb_rap_wksta_info_10_t * info;
1917 tp = p->parmsp + 1; /* Skip over function number */
1920 clientchar_t * cdescp;
1922 cdescp = smb_ParseStringT2Parm(p, (unsigned char*) tp, (char **) &tp,
1923 SMB_STRF_FORCEASCII);
1924 if (cm_ClientStrCmp(cdescp, _C("WrLh")))
1925 return CM_ERROR_INVAL;
1927 cdescp = smb_ParseStringT2Parm(p, (unsigned char*) tp, (char **) &tp,
1928 SMB_STRF_FORCEASCII);
1929 if (cm_ClientStrCmp(cdescp, _C("zzzBBzz")))
1930 return CM_ERROR_INVAL;
1936 if (infoLevel != 10) {
1937 return CM_ERROR_INVAL;
1943 totalData = sizeof(*info) + /* info */
1944 MAX_COMPUTERNAME_LENGTH + /* wki10_computername */
1945 SMB_MAX_USERNAME_LENGTH + /* wki10_username */
1946 MAX_COMPUTERNAME_LENGTH + /* wki10_langroup */
1947 MAX_COMPUTERNAME_LENGTH + /* wki10_logon_domain */
1948 1; /* wki10_oth_domains (null)*/
1950 outp = smb_GetTran2ResponsePacket(vcp, p, op, totalParams, totalData);
1952 memset(outp->parmsp,0,totalParams);
1953 memset(outp->datap,0,totalData);
1955 info = (smb_rap_wksta_info_10_t *) outp->datap;
1956 cstrp = (char *) (info + 1);
1958 info->wki10_computername = (DWORD) (cstrp - outp->datap);
1959 StringCbCopyA(cstrp, totalData, smb_localNamep);
1960 cstrp += strlen(cstrp) + 1;
1962 info->wki10_username = (DWORD) (cstrp - outp->datap);
1963 uidp = smb_FindUID(vcp, p->uid, 0);
1965 lock_ObtainMutex(&uidp->mx);
1966 if(uidp->unp && uidp->unp->name)
1967 cm_ClientStringToUtf8(uidp->unp->name, -1,
1968 cstrp, totalData/sizeof(char) - (cstrp - outp->datap));
1969 lock_ReleaseMutex(&uidp->mx);
1970 smb_ReleaseUID(uidp);
1972 cstrp += strlen(cstrp) + 1;
1974 info->wki10_langroup = (DWORD) (cstrp - outp->datap);
1975 StringCbCopyA(cstrp, totalData - (cstrp - outp->datap)*sizeof(char), "WORKGROUP");
1976 cstrp += strlen(cstrp) + 1;
1978 /* TODO: Not sure what values these should take, but these work */
1979 info->wki10_ver_major = 5;
1980 info->wki10_ver_minor = 1;
1982 info->wki10_logon_domain = (DWORD) (cstrp - outp->datap);
1983 cm_ClientStringToUtf8(smb_ServerDomainName, -1,
1984 cstrp, totalData/sizeof(char) - (cstrp - outp->datap));
1985 cstrp += strlen(cstrp) + 1;
1987 info->wki10_oth_domains = (DWORD) (cstrp - outp->datap);
1988 cstrp ++; /* no other domains */
1990 outp->totalData = (unsigned short) (cstrp - outp->datap); /* actual data size */
1991 outp->parmsp[2] = outp->totalData;
1992 outp->totalParms = totalParams;
1994 smb_SendTran2Packet(vcp,outp,op);
1995 smb_FreeTran2Packet(outp);
2000 #pragma pack(push, 1)
2002 typedef struct smb_rap_server_info_0 {
2004 } smb_rap_server_info_0_t;
2006 typedef struct smb_rap_server_info_1 {
2008 BYTE sv1_version_major;
2009 BYTE sv1_version_minor;
2011 DWORD sv1_comment_or_master_browser; /* char *sv1_comment_or_master_browser;*/
2012 } smb_rap_server_info_1_t;
2016 char smb_ServerComment[] = "OpenAFS Client";
2017 int smb_ServerCommentLen = sizeof(smb_ServerComment);
2019 #define SMB_SV_TYPE_SERVER 0x00000002L
2020 #define SMB_SV_TYPE_NT 0x00001000L
2021 #define SMB_SV_TYPE_SERVER_NT 0x00008000L
2023 long smb_ReceiveRAPNetServerGetInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
2025 smb_tran2Packet_t *outp;
2029 unsigned short * tp;
2032 smb_rap_server_info_0_t * info0;
2033 smb_rap_server_info_1_t * info1;
2036 tp = p->parmsp + 1; /* Skip over function number */
2039 clientchar_t * cdescp;
2041 cdescp = smb_ParseStringT2Parm(p, (unsigned char *) tp, (char **) &tp,
2042 SMB_STRF_FORCEASCII);
2043 if (cm_ClientStrCmp(cdescp, _C("WrLh")))
2044 return CM_ERROR_INVAL;
2045 cdescp = smb_ParseStringT2Parm(p, (unsigned char*) tp, (char **) &tp,
2046 SMB_STRF_FORCEASCII);
2047 if (cm_ClientStrCmp(cdescp, _C("B16")) ||
2048 cm_ClientStrCmp(cdescp, _C("B16BBDz")))
2049 return CM_ERROR_INVAL;
2055 if (infoLevel != 0 && infoLevel != 1) {
2056 return CM_ERROR_INVAL;
2062 (infoLevel == 0) ? sizeof(smb_rap_server_info_0_t)
2063 : (sizeof(smb_rap_server_info_1_t) + smb_ServerCommentLen);
2065 outp = smb_GetTran2ResponsePacket(vcp, p, op, totalParams, totalData);
2067 memset(outp->parmsp,0,totalParams);
2068 memset(outp->datap,0,totalData);
2070 if (infoLevel == 0) {
2071 info0 = (smb_rap_server_info_0_t *) outp->datap;
2072 cstrp = (char *) (info0 + 1);
2073 StringCchCopyA(info0->sv0_name, lengthof(info0->sv0_name), "AFS");
2074 } else { /* infoLevel == SMB_INFO_STANDARD */
2075 info1 = (smb_rap_server_info_1_t *) outp->datap;
2076 cstrp = (char *) (info1 + 1);
2077 StringCchCopyA(info1->sv1_name, lengthof(info1->sv1_name), "AFS");
2080 SMB_SV_TYPE_SERVER |
2082 SMB_SV_TYPE_SERVER_NT;
2084 info1->sv1_version_major = 5;
2085 info1->sv1_version_minor = 1;
2086 info1->sv1_comment_or_master_browser = (DWORD) (cstrp - outp->datap);
2088 StringCbCopyA(cstrp, smb_ServerCommentLen, smb_ServerComment);
2090 cstrp += smb_ServerCommentLen / sizeof(char);
2093 totalData = (DWORD)(cstrp - outp->datap);
2094 outp->totalData = min(bufsize,totalData); /* actual data size */
2095 outp->parmsp[0] = (outp->totalData == totalData)? 0 : ERROR_MORE_DATA;
2096 outp->parmsp[2] = totalData;
2097 outp->totalParms = totalParams;
2099 smb_SendTran2Packet(vcp,outp,op);
2100 smb_FreeTran2Packet(outp);
2105 /* SMB_COM_TRANSACTION2 and SMB_COM_TRANSACTION2_SECONDARY */
2106 long smb_ReceiveV3Tran2A(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2108 smb_tran2Packet_t *asp;
2120 /* We sometimes see 0 word count. What to do? */
2121 if (*inp->wctp == 0) {
2122 osi_Log0(smb_logp, "Transaction2 word count = 0");
2123 LogEvent(EVENTLOG_WARNING_TYPE, MSG_SMB_ZERO_TRANSACTION_COUNT);
2125 smb_SetSMBDataLength(outp, 0);
2126 smb_SendPacket(vcp, outp);
2130 totalParms = smb_GetSMBParm(inp, 0);
2131 totalData = smb_GetSMBParm(inp, 1);
2133 firstPacket = (inp->inCom == 0x32);
2135 /* find the packet we're reassembling */
2136 lock_ObtainWrite(&smb_globalLock);
2137 asp = smb_FindTran2Packet(vcp, inp);
2139 asp = smb_NewTran2Packet(vcp, inp, totalParms, totalData);
2141 lock_ReleaseWrite(&smb_globalLock);
2143 /* now merge in this latest packet; start by looking up offsets */
2145 parmDisp = dataDisp = 0;
2146 parmOffset = smb_GetSMBParm(inp, 10);
2147 dataOffset = smb_GetSMBParm(inp, 12);
2148 parmCount = smb_GetSMBParm(inp, 9);
2149 dataCount = smb_GetSMBParm(inp, 11);
2150 asp->maxReturnParms = smb_GetSMBParm(inp, 2);
2151 asp->maxReturnData = smb_GetSMBParm(inp, 3);
2153 osi_Log3(smb_logp, "SMB3 received T2 init packet total data %d, cur data %d, max return data %d",
2154 totalData, dataCount, asp->maxReturnData);
2157 parmDisp = smb_GetSMBParm(inp, 4);
2158 parmOffset = smb_GetSMBParm(inp, 3);
2159 dataDisp = smb_GetSMBParm(inp, 7);
2160 dataOffset = smb_GetSMBParm(inp, 6);
2161 parmCount = smb_GetSMBParm(inp, 2);
2162 dataCount = smb_GetSMBParm(inp, 5);
2164 osi_Log2(smb_logp, "SMB3 received T2 aux packet parms %d, data %d",
2165 parmCount, dataCount);
2168 /* now copy the parms and data */
2169 if ( asp->totalParms > 0 && parmCount != 0 )
2171 memcpy(((char *)asp->parmsp) + parmDisp, inp->data + parmOffset, parmCount);
2173 if ( asp->totalData > 0 && dataCount != 0 ) {
2174 memcpy(asp->datap + dataDisp, inp->data + dataOffset, dataCount);
2177 /* account for new bytes */
2178 asp->curData += dataCount;
2179 asp->curParms += parmCount;
2181 /* finally, if we're done, remove the packet from the queue and dispatch it */
2182 if (asp->totalParms > 0 &&
2183 asp->curParms > 0 &&
2184 asp->totalData <= asp->curData &&
2185 asp->totalParms <= asp->curParms) {
2186 /* we've received it all */
2187 lock_ObtainWrite(&smb_globalLock);
2188 osi_QRemove((osi_queue_t **) &smb_tran2AssemblyQueuep, &asp->q);
2189 lock_ReleaseWrite(&smb_globalLock);
2191 /* now dispatch it */
2192 if ( asp->opcode >= 0 && asp->opcode < 20 && smb_tran2DispatchTable[asp->opcode].procp) {
2193 osi_Log4(smb_logp,"AFS Server - Dispatch-2 %s vcp[%p] lana[%d] lsn[%d]",myCrt_2Dispatch(asp->opcode),vcp,vcp->lana,vcp->lsn);
2194 code = (*smb_tran2DispatchTable[asp->opcode].procp)(vcp, asp, outp);
2197 osi_Log4(smb_logp,"AFS Server - Dispatch-2 [INVALID] op[%x] vcp[%p] lana[%d] lsn[%d]", asp->opcode, vcp, vcp->lana, vcp->lsn);
2198 code = CM_ERROR_BADOP;
2201 /* if an error is returned, we're supposed to send an error packet,
2202 * otherwise the dispatched function already did the data sending.
2203 * We give dispatched proc the responsibility since it knows how much
2204 * space to allocate.
2207 smb_SendTran2Error(vcp, asp, outp, code);
2210 /* free the input tran 2 packet */
2211 smb_FreeTran2Packet(asp);
2213 else if (firstPacket) {
2214 /* the first packet in a multi-packet request, we need to send an
2215 * ack to get more data.
2217 smb_SetSMBDataLength(outp, 0);
2218 smb_SendPacket(vcp, outp);
2225 long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
2227 clientchar_t *pathp;
2228 smb_tran2Packet_t *outp;
2233 cm_scache_t *dscp; /* dir we're dealing with */
2234 cm_scache_t *scp; /* file we're creating */
2236 int initialModeBits;
2239 clientchar_t *lastNamep;
2246 int parmSlot; /* which parm we're dealing with */
2247 long returnEALength;
2248 clientchar_t *tidPathp;
2256 extraInfo = (p->parmsp[0] & 1); /* return extra info */
2257 returnEALength = (p->parmsp[0] & 8); /* return extended attr length */
2259 openFun = p->parmsp[6]; /* open function */
2260 excl = ((openFun & 3) == 0);
2261 trunc = ((openFun & 3) == 2); /* truncate it */
2262 openMode = (p->parmsp[1] & 0x7);
2263 openAction = 0; /* tracks what we did */
2265 attributes = p->parmsp[3];
2266 dosTime = p->parmsp[4] | (p->parmsp[5] << 16);
2268 /* compute initial mode bits based on read-only flag in attributes */
2269 initialModeBits = 0666;
2270 if (attributes & SMB_ATTR_READONLY)
2271 initialModeBits &= ~0222;
2273 pathp = smb_ParseStringT2Parm(p, (char *) (&p->parmsp[14]), NULL,
2276 outp = smb_GetTran2ResponsePacket(vcp, p, op, 40, 0);
2278 spacep = cm_GetSpace();
2279 smb_StripLastComponent(spacep->wdata, &lastNamep, pathp);
2282 (cm_ClientStrCmpI(lastNamep, _C(SMB_IOCTL_FILENAME)) == 0 ||
2283 cm_ClientStrCmpI(lastNamep, _C("\\srvsvc")) == 0 ||
2284 cm_ClientStrCmpI(lastNamep, _C("\\wkssvc")) == 0 ||
2285 cm_ClientStrCmpI(lastNamep, _C("\\ipc$")) == 0)) {
2286 /* special case magic file name for receiving IOCTL requests
2287 * (since IOCTL calls themselves aren't getting through).
2289 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
2290 smb_SetupIoctlFid(fidp, spacep);
2292 /* copy out remainder of the parms */
2294 outp->parmsp[parmSlot++] = fidp->fid;
2296 outp->parmsp[parmSlot++] = 0; /* attrs */
2297 outp->parmsp[parmSlot++] = 0; /* mod time */
2298 outp->parmsp[parmSlot++] = 0;
2299 outp->parmsp[parmSlot++] = 0; /* len */
2300 outp->parmsp[parmSlot++] = 0x7fff;
2301 outp->parmsp[parmSlot++] = openMode;
2302 outp->parmsp[parmSlot++] = 0; /* file type 0 ==> normal file or dir */
2303 outp->parmsp[parmSlot++] = 0; /* IPC junk */
2305 /* and the final "always present" stuff */
2306 outp->parmsp[parmSlot++] = 1; /* openAction found existing file */
2307 /* next write out the "unique" ID */
2308 outp->parmsp[parmSlot++] = 0x1234;
2309 outp->parmsp[parmSlot++] = 0x5678;
2310 outp->parmsp[parmSlot++] = 0;
2311 if (returnEALength) {
2312 outp->parmsp[parmSlot++] = 0;
2313 outp->parmsp[parmSlot++] = 0;
2316 outp->totalData = 0;
2317 outp->totalParms = parmSlot * 2;
2319 smb_SendTran2Packet(vcp, outp, op);
2321 smb_FreeTran2Packet(outp);
2323 /* and clean up fid reference */
2324 smb_ReleaseFID(fidp);
2328 #ifdef DEBUG_VERBOSE
2330 char *hexp, *asciip;
2331 asciip = (lastNamep ? lastNamep : pathp);
2332 hexp = osi_HexifyString( asciip );
2333 DEBUG_EVENT2("AFS","T2Open H[%s] A[%s]", hexp, asciip);
2338 userp = smb_GetTran2User(vcp, p);
2339 /* In the off chance that userp is NULL, we log and abandon */
2341 osi_Log1(smb_logp, "ReceiveTran2Open user [%d] not resolvable", p->uid);
2342 smb_FreeTran2Packet(outp);
2343 return CM_ERROR_BADSMB;
2346 code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
2347 if (code == CM_ERROR_TIDIPC) {
2348 /* Attempt to use a TID allocated for IPC. The client
2349 * is probably looking for DCE RPC end points which we
2350 * don't support OR it could be looking to make a DFS
2353 osi_Log0(smb_logp, "Tran2Open received IPC TID");
2355 cm_ReleaseUser(userp);
2356 smb_FreeTran2Packet(outp);
2357 return CM_ERROR_NOSUCHPATH;
2362 code = cm_NameI(cm_data.rootSCachep, pathp,
2363 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
2364 userp, tidPathp, &req, &scp);
2366 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
2367 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
2368 userp, tidPathp, &req, &dscp);
2369 cm_FreeSpace(spacep);
2372 cm_ReleaseUser(userp);
2373 smb_FreeTran2Packet(outp);
2378 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
2379 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp,
2380 (clientchar_t*) spacep->data);
2381 cm_ReleaseSCache(dscp);
2382 cm_ReleaseUser(userp);
2383 smb_FreeTran2Packet(outp);
2384 if ( WANTS_DFS_PATHNAMES(p) || pnc )
2385 return CM_ERROR_PATH_NOT_COVERED;
2387 return CM_ERROR_BADSHARENAME;
2389 #endif /* DFS_SUPPORT */
2391 /* otherwise, scp points to the parent directory. Do a lookup,
2392 * and truncate the file if we find it, otherwise we create the
2399 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD, userp,
2401 if (code && code != CM_ERROR_NOSUCHFILE && code != CM_ERROR_BPLUS_NOMATCH) {
2402 cm_ReleaseSCache(dscp);
2403 cm_ReleaseUser(userp);
2404 smb_FreeTran2Packet(outp);
2409 if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
2410 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, lastNamep);
2411 cm_ReleaseSCache(scp);
2412 cm_ReleaseUser(userp);
2413 smb_FreeTran2Packet(outp);
2414 if ( WANTS_DFS_PATHNAMES(p) || pnc )
2415 return CM_ERROR_PATH_NOT_COVERED;
2417 return CM_ERROR_BADSHARENAME;
2419 #endif /* DFS_SUPPORT */
2421 /* macintosh is expensive to program for it */
2422 cm_FreeSpace(spacep);
2425 /* if we get here, if code is 0, the file exists and is represented by
2426 * scp. Otherwise, we have to create it.
2429 code = cm_CheckOpen(scp, openMode, trunc, userp, &req);
2432 cm_ReleaseSCache(dscp);
2433 cm_ReleaseSCache(scp);
2434 cm_ReleaseUser(userp);
2435 smb_FreeTran2Packet(outp);
2440 /* oops, file shouldn't be there */
2442 cm_ReleaseSCache(dscp);
2443 cm_ReleaseSCache(scp);
2444 cm_ReleaseUser(userp);
2445 smb_FreeTran2Packet(outp);
2446 return CM_ERROR_EXISTS;
2450 setAttr.mask = CM_ATTRMASK_LENGTH;
2451 setAttr.length.LowPart = 0;
2452 setAttr.length.HighPart = 0;
2453 code = cm_SetAttr(scp, &setAttr, userp, &req);
2454 openAction = 3; /* truncated existing file */
2457 openAction = 1; /* found existing file */
2459 else if (!(openFun & 0x10)) {
2460 /* don't create if not found */
2462 cm_ReleaseSCache(dscp);
2463 osi_assertx(scp == NULL, "null cm_scache_t");
2464 cm_ReleaseUser(userp);
2465 smb_FreeTran2Packet(outp);
2466 return CM_ERROR_NOSUCHFILE;
2469 osi_assertx(dscp != NULL && scp == NULL, "null dsc || non-null sc");
2470 openAction = 2; /* created file */
2471 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
2472 smb_UnixTimeFromSearchTime(&setAttr.clientModTime, dosTime);
2473 code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
2477 if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
2478 smb_NotifyChange(FILE_ACTION_ADDED,
2479 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION,
2480 dscp, lastNamep, NULL, TRUE);
2481 } else if (!excl && code == CM_ERROR_EXISTS) {
2482 /* not an exclusive create, and someone else tried
2483 * creating it already, then we open it anyway. We
2484 * don't bother retrying after this, since if this next
2485 * fails, that means that the file was deleted after we
2486 * started this call.
2488 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
2492 setAttr.mask = CM_ATTRMASK_LENGTH;
2493 setAttr.length.LowPart = 0;
2494 setAttr.length.HighPart = 0;
2495 code = cm_SetAttr(scp, &setAttr, userp,
2498 } /* lookup succeeded */
2502 /* we don't need this any longer */
2504 cm_ReleaseSCache(dscp);
2507 /* something went wrong creating or truncating the file */
2509 cm_ReleaseSCache(scp);
2510 cm_ReleaseUser(userp);
2511 smb_FreeTran2Packet(outp);
2515 /* make sure we're about to open a file */
2516 if (scp->fileType != CM_SCACHETYPE_FILE) {
2518 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
2519 cm_scache_t * targetScp = 0;
2520 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
2522 /* we have a more accurate file to use (the
2523 * target of the symbolic link). Otherwise,
2524 * we'll just use the symlink anyway.
2526 osi_Log2(smb_logp, "symlink vp %x to vp %x",
2528 cm_ReleaseSCache(scp);
2532 if (scp->fileType != CM_SCACHETYPE_FILE) {
2533 cm_ReleaseSCache(scp);
2534 cm_ReleaseUser(userp);
2535 smb_FreeTran2Packet(outp);
2536 return CM_ERROR_ISDIR;
2540 /* now all we have to do is open the file itself */
2541 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
2542 osi_assertx(fidp, "null smb_fid_t");
2545 lock_ObtainMutex(&fidp->mx);
2546 /* save a pointer to the vnode */
2547 osi_Log2(smb_logp,"smb_ReceiveTran2Open fidp 0x%p scp 0x%p", fidp, scp);
2549 lock_ObtainWrite(&scp->rw);
2550 scp->flags |= CM_SCACHEFLAG_SMB_FID;
2551 lock_ReleaseWrite(&scp->rw);
2554 fidp->userp = userp;
2556 /* compute open mode */
2558 fidp->flags |= SMB_FID_OPENREAD_LISTDIR;
2559 if (openMode == 1 || openMode == 2)
2560 fidp->flags |= SMB_FID_OPENWRITE;
2562 /* remember that the file was newly created */
2564 fidp->flags |= SMB_FID_CREATED;
2566 lock_ReleaseMutex(&fidp->mx);
2568 smb_ReleaseFID(fidp);
2570 cm_Open(scp, 0, userp);
2572 /* copy out remainder of the parms */
2574 outp->parmsp[parmSlot++] = fidp->fid;
2575 lock_ObtainRead(&scp->rw);
2577 outp->parmsp[parmSlot++] = smb_Attributes(scp);
2578 smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
2579 outp->parmsp[parmSlot++] = (unsigned short)(dosTime & 0xffff);
2580 outp->parmsp[parmSlot++] = (unsigned short)((dosTime>>16) & 0xffff);
2581 outp->parmsp[parmSlot++] = (unsigned short) (scp->length.LowPart & 0xffff);
2582 outp->parmsp[parmSlot++] = (unsigned short) ((scp->length.LowPart >> 16) & 0xffff);
2583 outp->parmsp[parmSlot++] = openMode;
2584 outp->parmsp[parmSlot++] = 0; /* file type 0 ==> normal file or dir */
2585 outp->parmsp[parmSlot++] = 0; /* IPC junk */
2587 /* and the final "always present" stuff */
2588 outp->parmsp[parmSlot++] = openAction;
2589 /* next write out the "unique" ID */
2590 outp->parmsp[parmSlot++] = (unsigned short) (scp->fid.vnode & 0xffff);
2591 outp->parmsp[parmSlot++] = (unsigned short) (scp->fid.volume & 0xffff);
2592 outp->parmsp[parmSlot++] = 0;
2593 if (returnEALength) {
2594 outp->parmsp[parmSlot++] = 0;
2595 outp->parmsp[parmSlot++] = 0;
2597 lock_ReleaseRead(&scp->rw);
2598 outp->totalData = 0; /* total # of data bytes */
2599 outp->totalParms = parmSlot * 2; /* shorts are two bytes */
2601 smb_SendTran2Packet(vcp, outp, op);
2603 smb_FreeTran2Packet(outp);
2605 cm_ReleaseUser(userp);
2606 /* leave scp held since we put it in fidp->scp */
2610 long smb_ReceiveTran2QFSInfoFid(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
2613 unsigned short infolevel;
2615 infolevel = p->parmsp[0];
2617 osi_Log2(smb_logp, "T2 QFSInfoFid InfoLevel 0x%x fid 0x%x - NOT_SUPPORTED", infolevel, fid);
2619 return CM_ERROR_BAD_LEVEL;
2622 /* TRANS2_QUERY_FS_INFORMATION */
2623 long smb_ReceiveTran2QFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
2625 smb_tran2Packet_t *outp;
2626 smb_tran2QFSInfo_t qi;
2630 osi_Log1(smb_logp, "T2 QFSInfo type 0x%x", p->parmsp[0]);
2632 switch (p->parmsp[0]) {
2633 case SMB_INFO_ALLOCATION:
2635 responseSize = sizeof(qi.u.allocInfo);
2637 qi.u.allocInfo.FSID = 0;
2638 qi.u.allocInfo.sectorsPerAllocUnit = 1;
2639 qi.u.allocInfo.totalAllocUnits = 0x7fffffff;
2640 qi.u.allocInfo.availAllocUnits = 0x3fffffff;
2641 qi.u.allocInfo.bytesPerSector = 1024;
2644 case SMB_INFO_VOLUME:
2646 qi.u.volumeInfo.vsn = 1234; /* Volume serial number */
2647 qi.u.volumeInfo.vnCount = 3; /* Number of characters in label (AFS\0)*/
2649 /* we're supposed to pad it out with zeroes to the end */
2650 memset(&qi.u.volumeInfo.label, 0, sizeof(qi.u.volumeInfo.label));
2651 smb_UnparseString(op, qi.u.volumeInfo.label, _C("AFS"), &sz, 0);
2653 responseSize = sizeof(unsigned long) + sizeof(char) + max(12, sz);
2656 case SMB_QUERY_FS_VOLUME_INFO:
2657 /* FS volume info */
2658 responseSize = sizeof(qi.u.FSvolumeInfo);
2661 FILETIME ft = {0x832cf000, 0x01abfcc4}; /* October 1, 1982 00:00:00 +0600 */
2662 memcpy(&qi.u.FSvolumeInfo.vct, &ft, sizeof(ft));
2665 qi.u.FSvolumeInfo.vsn = 1234;
2666 qi.u.FSvolumeInfo.vnCount = 6; /* This is always in Unicode */
2667 memset(&qi.u.FSvolumeInfo.label, 0, sizeof(qi.u.FSvolumeInfo.label));
2668 memcpy(qi.u.FSvolumeInfo.label, L"AFS", sizeof(L"AFS"));
2671 case SMB_QUERY_FS_SIZE_INFO:
2673 responseSize = sizeof(qi.u.FSsizeInfo);
2675 qi.u.FSsizeInfo.totalAllocUnits.HighPart = 0;
2676 qi.u.FSsizeInfo.totalAllocUnits.LowPart= 0x7fffffff;
2677 qi.u.FSsizeInfo.availAllocUnits.HighPart = 0;
2678 qi.u.FSsizeInfo.availAllocUnits.LowPart= 0x3fffffff;
2679 qi.u.FSsizeInfo.sectorsPerAllocUnit = 1;
2680 qi.u.FSsizeInfo.bytesPerSector = 1024;
2683 case SMB_QUERY_FS_DEVICE_INFO:
2684 /* FS device info */
2685 responseSize = sizeof(qi.u.FSdeviceInfo);
2687 qi.u.FSdeviceInfo.devType = 0x14; /* network file system */
2688 qi.u.FSdeviceInfo.characteristics = 0x50; /* remote, virtual */
2691 case SMB_QUERY_FS_ATTRIBUTE_INFO:
2692 /* FS attribute info */
2694 /* attributes, defined in WINNT.H:
2695 * FILE_CASE_SENSITIVE_SEARCH 0x1
2696 * FILE_CASE_PRESERVED_NAMES 0x2
2697 * FILE_VOLUME_QUOTAS 0x10
2698 * <no name defined> 0x4000
2699 * If bit 0x4000 is not set, Windows 95 thinks
2700 * we can't handle long (non-8.3) names,
2701 * despite our protestations to the contrary.
2703 qi.u.FSattributeInfo.attributes = 0x4003;
2704 /* The maxCompLength is supposed to be in bytes */
2706 if ((vcp->flags & SMB_VCFLAG_USEUNICODE) == SMB_VCFLAG_USEUNICODE)
2707 qi.u.FSattributeInfo.maxCompLength = MAX_PATH * sizeof(wchar_t);
2710 qi.u.FSattributeInfo.maxCompLength = MAX_PATH;
2714 smb_UnparseString(op, qi.u.FSattributeInfo.FSname, _C("AFS"), &sz, SMB_STRF_IGNORENUL);
2715 qi.u.FSattributeInfo.FSnameLength = sz;
2718 sizeof(qi.u.FSattributeInfo.attributes) +
2719 sizeof(qi.u.FSattributeInfo.maxCompLength) +
2720 sizeof(qi.u.FSattributeInfo.FSnameLength) +
2725 case SMB_INFO_UNIX: /* CIFS Unix Info */
2726 case SMB_INFO_MACOS: /* Mac FS Info */
2728 return CM_ERROR_BADOP;
2731 outp = smb_GetTran2ResponsePacket(vcp, p, op, 0, responseSize);
2733 /* copy out return data, and set corresponding sizes */
2734 outp->totalParms = 0;
2735 outp->totalData = responseSize;
2736 memcpy(outp->datap, &qi, responseSize);
2738 /* send and free the packets */
2739 smb_SendTran2Packet(vcp, outp, op);
2740 smb_FreeTran2Packet(outp);
2745 long smb_ReceiveTran2SetFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
2747 osi_Log0(smb_logp,"ReceiveTran2SetFSInfo - NOT_SUPPORTED");
2748 return CM_ERROR_BADOP;
2751 struct smb_ShortNameRock {
2752 clientchar_t *maskp;
2754 clientchar_t *shortName;
2755 size_t shortNameLen;
2758 int cm_GetShortNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *vrockp,
2761 struct smb_ShortNameRock *rockp;
2762 normchar_t normName[MAX_PATH];
2763 clientchar_t *shortNameEnd;
2767 cm_FsStringToNormString(dep->name, -1, normName, sizeof(normName)/sizeof(clientchar_t));
2769 /* compare both names and vnodes, though probably just comparing vnodes
2770 * would be safe enough.
2772 if (cm_NormStrCmpI(normName, rockp->maskp) != 0)
2774 if (ntohl(dep->fid.vnode) != rockp->vnode)
2777 /* This is the entry */
2778 cm_Gen8Dot3Name(dep, rockp->shortName, &shortNameEnd);
2779 rockp->shortNameLen = shortNameEnd - rockp->shortName;
2781 return CM_ERROR_STOPNOW;
2784 long cm_GetShortName(clientchar_t *pathp, cm_user_t *userp, cm_req_t *reqp,
2785 clientchar_t *tidPathp, int vnode, clientchar_t *shortName, size_t *shortNameLenp)
2787 struct smb_ShortNameRock rock;
2788 clientchar_t *lastNamep;
2791 int caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
2795 spacep = cm_GetSpace();
2796 smb_StripLastComponent(spacep->wdata, &lastNamep, pathp);
2798 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
2799 caseFold, userp, tidPathp,
2801 cm_FreeSpace(spacep);
2806 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
2807 cm_ReleaseSCache(dscp);
2808 cm_ReleaseUser(userp);
2812 return CM_ERROR_PATH_NOT_COVERED;
2814 #endif /* DFS_SUPPORT */
2816 if (!lastNamep) lastNamep = pathp;
2819 thyper.HighPart = 0;
2820 rock.shortName = shortName;
2822 rock.maskp = lastNamep;
2823 code = cm_ApplyDir(dscp, cm_GetShortNameProc, &rock, &thyper, userp, reqp, NULL);
2825 cm_ReleaseSCache(dscp);
2828 return CM_ERROR_NOSUCHFILE;
2829 if (code == CM_ERROR_STOPNOW) {
2830 *shortNameLenp = rock.shortNameLen;
2836 /* TRANS2_QUERY_PATH_INFORMATION */
2837 long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
2839 smb_tran2Packet_t *outp;
2842 unsigned short infoLevel;
2843 smb_tran2QPathInfo_t qpi;
2845 unsigned short attributes;
2846 unsigned long extAttributes;
2847 clientchar_t shortName[13];
2851 cm_scache_t *scp, *dscp;
2852 int scp_rw_held = 0;
2855 clientchar_t *pathp;
2856 clientchar_t *tidPathp;
2857 clientchar_t *lastComp;
2862 infoLevel = p->parmsp[0];
2863 if (infoLevel == SMB_INFO_IS_NAME_VALID)
2865 else if (infoLevel == SMB_INFO_STANDARD)
2866 responseSize = sizeof(qpi.u.QPstandardInfo);
2867 else if (infoLevel == SMB_INFO_QUERY_EA_SIZE)
2868 responseSize = sizeof(qpi.u.QPeaSizeInfo);
2869 else if (infoLevel == SMB_QUERY_FILE_BASIC_INFO)
2870 responseSize = sizeof(qpi.u.QPfileBasicInfo);
2871 else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO)
2872 responseSize = sizeof(qpi.u.QPfileStandardInfo);
2873 else if (infoLevel == SMB_QUERY_FILE_EA_INFO)
2874 responseSize = sizeof(qpi.u.QPfileEaInfo);
2875 else if (infoLevel == SMB_QUERY_FILE_NAME_INFO)
2876 responseSize = sizeof(qpi.u.QPfileNameInfo);
2877 else if (infoLevel == SMB_QUERY_FILE_ALL_INFO)
2878 responseSize = sizeof(qpi.u.QPfileAllInfo);
2879 else if (infoLevel == SMB_QUERY_FILE_ALT_NAME_INFO)
2880 responseSize = sizeof(qpi.u.QPfileAltNameInfo);
2882 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
2883 p->opcode, infoLevel);
2884 smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
2888 pathp = smb_ParseStringT2Parm(p, (char *) (&p->parmsp[3]), NULL, SMB_STRF_ANSIPATH);
2889 osi_Log2(smb_logp, "T2 QPathInfo type 0x%x path %S", infoLevel,
2890 osi_LogSaveClientString(smb_logp, pathp));
2892 outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, responseSize);
2894 if (infoLevel > 0x100)
2895 outp->totalParms = 2;
2897 outp->totalParms = 0;
2898 outp->totalData = responseSize;
2900 /* now, if we're at infoLevel 6, we're only being asked to check
2901 * the syntax, so we just OK things now. In particular, we're *not*
2902 * being asked to verify anything about the state of any parent dirs.
2904 if (infoLevel == SMB_INFO_IS_NAME_VALID) {
2905 smb_SendTran2Packet(vcp, outp, opx);
2906 smb_FreeTran2Packet(outp);
2910 userp = smb_GetTran2User(vcp, p);
2912 osi_Log1(smb_logp, "ReceiveTran2QPathInfo unable to resolve user [%d]", p->uid);
2913 smb_FreeTran2Packet(outp);
2914 return CM_ERROR_BADSMB;
2917 code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
2919 cm_ReleaseUser(userp);
2920 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOSUCHPATH);
2921 smb_FreeTran2Packet(outp);
2926 * XXX Strange hack XXX
2928 * As of Patch 7 (13 January 98), we are having the following problem:
2929 * In NT Explorer 4.0, whenever we click on a directory, AFS gets
2930 * requests to look up "desktop.ini" in all the subdirectories.
2931 * This can cause zillions of timeouts looking up non-existent cells
2932 * and volumes, especially in the top-level directory.
2934 * We have not found any way to avoid this or work around it except
2935 * to explicitly ignore the requests for mount points that haven't
2936 * yet been evaluated and for directories that haven't yet been
2939 if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
2940 spacep = cm_GetSpace();
2941 smb_StripLastComponent(spacep->wdata, &lastComp, pathp);
2942 #ifndef SPECIAL_FOLDERS
2943 /* Make sure that lastComp is not NULL */
2945 if (cm_ClientStrCmpIA(lastComp, _C("\\desktop.ini")) == 0) {
2946 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
2950 userp, tidPathp, &req, &dscp);
2953 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
2954 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp,
2956 if ( WANTS_DFS_PATHNAMES(p) || pnc )
2957 code = CM_ERROR_PATH_NOT_COVERED;
2959 code = CM_ERROR_BADSHARENAME;
2961 #endif /* DFS_SUPPORT */
2962 if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT && !dscp->mountRootFid.volume)
2963 code = CM_ERROR_NOSUCHFILE;
2964 else if (dscp->fileType == CM_SCACHETYPE_DIRECTORY) {
2965 cm_buf_t *bp = buf_Find(dscp, &hzero);
2971 code = CM_ERROR_NOSUCHFILE;
2973 cm_ReleaseSCache(dscp);
2975 cm_FreeSpace(spacep);
2976 cm_ReleaseUser(userp);
2977 smb_SendTran2Error(vcp, p, opx, code);
2978 smb_FreeTran2Packet(outp);
2984 #endif /* SPECIAL_FOLDERS */
2986 cm_FreeSpace(spacep);
2989 /* now do namei and stat, and copy out the info */
2990 code = cm_NameI(cm_data.rootSCachep, pathp,
2991 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, tidPathp, &req, &scp);
2994 cm_ReleaseUser(userp);
2995 smb_SendTran2Error(vcp, p, opx, code);
2996 smb_FreeTran2Packet(outp);
3001 if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
3002 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, pathp);
3003 cm_ReleaseSCache(scp);
3004 cm_ReleaseUser(userp);
3005 if ( WANTS_DFS_PATHNAMES(p) || pnc )
3006 code = CM_ERROR_PATH_NOT_COVERED;
3008 code = CM_ERROR_BADSHARENAME;
3009 smb_SendTran2Error(vcp, p, opx, code);
3010 smb_FreeTran2Packet(outp);
3013 #endif /* DFS_SUPPORT */
3015 lock_ObtainWrite(&scp->rw);
3017 code = cm_SyncOp(scp, NULL, userp, &req, 0,
3018 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3022 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3024 lock_ConvertWToR(&scp->rw);
3029 /* now we have the status in the cache entry, and everything is locked.
3030 * Marshall the output data.
3032 /* for info level 108, figure out short name */
3033 if (infoLevel == SMB_QUERY_FILE_ALT_NAME_INFO) {
3034 code = cm_GetShortName(pathp, userp, &req,
3035 tidPathp, scp->fid.vnode, shortName,
3041 smb_UnparseString(opx, qpi.u.QPfileAltNameInfo.fileName, shortName, &len, SMB_STRF_IGNORENUL);
3042 qpi.u.QPfileAltNameInfo.fileNameLength = len;
3046 else if (infoLevel == SMB_QUERY_FILE_NAME_INFO) {
3047 smb_UnparseString(opx, qpi.u.QPfileNameInfo.fileName, lastComp, &len, SMB_STRF_IGNORENUL);
3048 qpi.u.QPfileNameInfo.fileNameLength = len;
3052 else if (infoLevel == SMB_INFO_STANDARD || infoLevel == SMB_INFO_QUERY_EA_SIZE) {
3053 smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
3054 qpi.u.QPstandardInfo.creationDateTime = dosTime;
3055 qpi.u.QPstandardInfo.lastAccessDateTime = dosTime;
3056 qpi.u.QPstandardInfo.lastWriteDateTime = dosTime;
3057 qpi.u.QPstandardInfo.dataSize = scp->length.LowPart;
3058 qpi.u.QPstandardInfo.allocationSize = scp->length.LowPart;
3059 attributes = smb_Attributes(scp);
3060 qpi.u.QPstandardInfo.attributes = attributes;
3061 qpi.u.QPstandardInfo.eaSize = 0;
3063 else if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
3064 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
3065 qpi.u.QPfileBasicInfo.creationTime = ft;
3066 qpi.u.QPfileBasicInfo.lastAccessTime = ft;
3067 qpi.u.QPfileBasicInfo.lastWriteTime = ft;
3068 qpi.u.QPfileBasicInfo.changeTime = ft;
3069 extAttributes = smb_ExtAttributes(scp);
3070 qpi.u.QPfileBasicInfo.attributes = extAttributes;
3071 qpi.u.QPfileBasicInfo.reserved = 0;
3073 else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) {
3076 lock_ReleaseRead(&scp->rw);
3078 fidp = smb_FindFIDByScache(vcp, scp);
3080 qpi.u.QPfileStandardInfo.allocationSize = scp->length;
3081 qpi.u.QPfileStandardInfo.endOfFile = scp->length;
3082 qpi.u.QPfileStandardInfo.numberOfLinks = scp->linkCount;
3083 qpi.u.QPfileStandardInfo.directory =
3084 ((scp->fileType == CM_SCACHETYPE_DIRECTORY ||
3085 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
3086 scp->fileType == CM_SCACHETYPE_INVALID) ? 1 : 0);
3087 qpi.u.QPfileStandardInfo.reserved = 0;
3090 lock_ObtainMutex(&fidp->mx);
3091 delonclose = fidp->flags & SMB_FID_DELONCLOSE;
3092 lock_ReleaseMutex(&fidp->mx);
3093 smb_ReleaseFID(fidp);
3095 qpi.u.QPfileStandardInfo.deletePending = (delonclose ? 1 : 0);
3097 else if (infoLevel == SMB_QUERY_FILE_EA_INFO) {
3098 qpi.u.QPfileEaInfo.eaSize = 0;
3100 else if (infoLevel == SMB_QUERY_FILE_ALL_INFO) {
3101 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
3102 qpi.u.QPfileAllInfo.creationTime = ft;
3103 qpi.u.QPfileAllInfo.lastAccessTime = ft;
3104 qpi.u.QPfileAllInfo.lastWriteTime = ft;
3105 qpi.u.QPfileAllInfo.changeTime = ft;
3106 extAttributes = smb_ExtAttributes(scp);
3107 qpi.u.QPfileAllInfo.attributes = extAttributes;
3108 qpi.u.QPfileAllInfo.allocationSize = scp->length;
3109 qpi.u.QPfileAllInfo.endOfFile = scp->length;
3110 qpi.u.QPfileAllInfo.numberOfLinks = scp->linkCount;
3111 qpi.u.QPfileAllInfo.deletePending = 0;
3112 qpi.u.QPfileAllInfo.directory =
3113 ((scp->fileType == CM_SCACHETYPE_DIRECTORY ||
3114 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
3115 scp->fileType == CM_SCACHETYPE_INVALID) ? 1 : 0);
3116 qpi.u.QPfileAllInfo.indexNumber.HighPart = scp->fid.cell;
3117 qpi.u.QPfileAllInfo.indexNumber.LowPart = scp->fid.volume;
3118 qpi.u.QPfileAllInfo.eaSize = 0;
3119 qpi.u.QPfileAllInfo.accessFlags = 0;
3120 qpi.u.QPfileAllInfo.indexNumber2.HighPart = scp->fid.vnode;
3121 qpi.u.QPfileAllInfo.indexNumber2.LowPart = scp->fid.unique;
3122 qpi.u.QPfileAllInfo.currentByteOffset.HighPart = 0;
3123 qpi.u.QPfileAllInfo.currentByteOffset.LowPart = 0;
3124 qpi.u.QPfileAllInfo.mode = 0;
3125 qpi.u.QPfileAllInfo.alignmentRequirement = 0;
3127 smb_UnparseString(opx, qpi.u.QPfileAllInfo.fileName, lastComp, &len, SMB_STRF_IGNORENUL);
3128 qpi.u.QPfileAllInfo.fileNameLength = len;
3131 /* send and free the packets */
3133 switch (scp_rw_held) {
3135 lock_ReleaseRead(&scp->rw);
3138 lock_ReleaseWrite(&scp->rw);
3142 cm_ReleaseSCache(scp);
3143 cm_ReleaseUser(userp);
3145 memcpy(outp->datap, &qpi, responseSize);
3146 smb_SendTran2Packet(vcp, outp, opx);
3148 smb_SendTran2Error(vcp, p, opx, code);
3150 smb_FreeTran2Packet(outp);
3155 /* TRANS2_SET_PATH_INFORMATION */
3156 long smb_ReceiveTran2SetPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
3159 osi_Log0(smb_logp,"ReceiveTran2SetPathInfo - NOT_SUPPORTED");
3160 return CM_ERROR_BADOP;
3164 unsigned short infoLevel;
3165 clientchar_t * pathp;
3166 smb_tran2Packet_t *outp;
3167 smb_tran2QPathInfo_t *spi;
3169 cm_scache_t *scp, *dscp;
3172 clientchar_t *tidPathp;
3173 clientchar_t *lastComp;
3177 infoLevel = p->parmsp[0];
3178 osi_Log1(smb_logp,"ReceiveTran2SetPathInfo type 0x%x", infoLevel);
3179 if (infoLevel != SMB_INFO_STANDARD &&
3180 infoLevel != SMB_INFO_QUERY_EA_SIZE &&
3181 infoLevel != SMB_INFO_QUERY_ALL_EAS) {
3182 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
3183 p->opcode, infoLevel);
3184 smb_SendTran2Error(vcp, p, opx,
3185 infoLevel == SMB_INFO_QUERY_ALL_EAS ? CM_ERROR_EAS_NOT_SUPPORTED : CM_ERROR_BAD_LEVEL);
3189 pathp = smb_ParseStringT2Parm(p, (char *) (&p->parmsp[3]), NULL, SMB_STRF_ANSIPATH);
3191 osi_Log2(smb_logp, "T2 SetPathInfo infolevel 0x%x path %S", infoLevel,
3192 osi_LogSaveClientString(smb_logp, pathp));
3194 userp = smb_GetTran2User(vcp, p);
3196 osi_Log1(smb_logp,"ReceiveTran2SetPathInfo unable to resolve user [%d]", p->uid);
3197 code = CM_ERROR_BADSMB;
3201 code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
3202 if (code == CM_ERROR_TIDIPC) {
3203 /* Attempt to use a TID allocated for IPC. The client
3204 * is probably looking for DCE RPC end points which we
3205 * don't support OR it could be looking to make a DFS
3208 osi_Log0(smb_logp, "Tran2Open received IPC TID");
3209 cm_ReleaseUser(userp);
3210 return CM_ERROR_NOSUCHPATH;
3214 * XXX Strange hack XXX
3216 * As of Patch 7 (13 January 98), we are having the following problem:
3217 * In NT Explorer 4.0, whenever we click on a directory, AFS gets
3218 * requests to look up "desktop.ini" in all the subdirectories.
3219 * This can cause zillions of timeouts looking up non-existent cells
3220 * and volumes, especially in the top-level directory.
3222 * We have not found any way to avoid this or work around it except
3223 * to explicitly ignore the requests for mount points that haven't
3224 * yet been evaluated and for directories that haven't yet been
3227 if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
3228 spacep = cm_GetSpace();
3229 smb_StripLastComponent(spacep->wdata, &lastComp, pathp);
3230 #ifndef SPECIAL_FOLDERS
3231 /* Make sure that lastComp is not NULL */
3233 if (cm_ClientStrCmpI(lastComp, _C("\\desktop.ini")) == 0) {
3234 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
3238 userp, tidPathp, &req, &dscp);
3241 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
3242 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp,
3244 if ( WANTS_DFS_PATHNAMES(p) || pnc )
3245 code = CM_ERROR_PATH_NOT_COVERED;
3247 code = CM_ERROR_BADSHARENAME;
3249 #endif /* DFS_SUPPORT */
3250 if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT && !dscp->mountRootFid.volume)
3251 code = CM_ERROR_NOSUCHFILE;
3252 else if (dscp->fileType == CM_SCACHETYPE_DIRECTORY) {
3253 cm_buf_t *bp = buf_Find(dscp, &hzero);
3259 code = CM_ERROR_NOSUCHFILE;
3261 cm_ReleaseSCache(dscp);
3263 cm_FreeSpace(spacep);
3264 cm_ReleaseUser(userp);
3265 smb_SendTran2Error(vcp, p, opx, code);
3271 #endif /* SPECIAL_FOLDERS */
3273 cm_FreeSpace(spacep);
3276 /* now do namei and stat, and copy out the info */
3277 code = cm_NameI(cm_data.rootSCachep, pathp,
3278 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, tidPathp, &req, &scp);
3280 cm_ReleaseUser(userp);
3281 smb_SendTran2Error(vcp, p, opx, code);
3285 fidp = smb_FindFIDByScache(vcp, scp);
3287 cm_ReleaseSCache(scp);
3288 cm_ReleaseUser(userp);
3289 smb_SendTran2Error(vcp, p, opx, code);
3293 lock_ObtainMutex(&fidp->mx);
3294 if (!(fidp->flags & SMB_FID_OPENWRITE)) {
3295 lock_ReleaseMutex(&fidp->mx);
3296 cm_ReleaseSCache(scp);
3297 smb_ReleaseFID(fidp);
3298 cm_ReleaseUser(userp);
3299 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOACCESS);
3302 lock_ReleaseMutex(&fidp->mx);
3304 outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, 0);
3306 outp->totalParms = 2;
3307 outp->totalData = 0;
3309 spi = (smb_tran2QPathInfo_t *)p->datap;
3310 if (infoLevel == SMB_INFO_STANDARD || infoLevel == SMB_INFO_QUERY_EA_SIZE) {
3313 /* lock the vnode with a callback; we need the current status
3314 * to determine what the new status is, in some cases.
3316 lock_ObtainWrite(&scp->rw);
3317 code = cm_SyncOp(scp, NULL, userp, &req, 0,
3318 CM_SCACHESYNC_GETSTATUS
3319 | CM_SCACHESYNC_NEEDCALLBACK);
3321 lock_ReleaseWrite(&scp->rw);
3324 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3326 lock_ReleaseWrite(&scp->rw);
3327 lock_ObtainMutex(&fidp->mx);
3328 lock_ObtainRead(&scp->rw);
3330 /* prepare for setattr call */
3331 attr.mask = CM_ATTRMASK_LENGTH;
3332 attr.length.LowPart = spi->u.QPstandardInfo.dataSize;
3333 attr.length.HighPart = 0;
3335 if (spi->u.QPstandardInfo.lastWriteDateTime != 0) {
3336 smb_UnixTimeFromSearchTime(&attr.clientModTime, spi->u.QPstandardInfo.lastWriteDateTime);
3337 attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
3338 fidp->flags |= SMB_FID_MTIMESETDONE;
3341 if (spi->u.QPstandardInfo.attributes != 0) {
3342 if ((scp->unixModeBits & 0222)
3343 && (spi->u.QPstandardInfo.attributes & SMB_ATTR_READONLY) != 0) {
3344 /* make a writable file read-only */
3345 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
3346 attr.unixModeBits = scp->unixModeBits & ~0222;
3348 else if ((scp->unixModeBits & 0222) == 0
3349 && (spi->u.QPstandardInfo.attributes & SMB_ATTR_READONLY) == 0) {
3350 /* make a read-only file writable */
3351 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
3352 attr.unixModeBits = scp->unixModeBits | 0222;
3355 lock_ReleaseRead(&scp->rw);
3356 lock_ReleaseMutex(&fidp->mx);
3360 code = cm_SetAttr(scp, &attr, userp, &req);
3364 else if (infoLevel == SMB_INFO_QUERY_ALL_EAS) {
3365 /* we don't support EAs */
3366 code = CM_ERROR_EAS_NOT_SUPPORTED;
3370 cm_ReleaseSCache(scp);
3371 cm_ReleaseUser(userp);
3372 smb_ReleaseFID(fidp);
3374 smb_SendTran2Packet(vcp, outp, opx);
3376 smb_SendTran2Error(vcp, p, opx, code);
3377 smb_FreeTran2Packet(outp);
3383 /* TRANS2_QUERY_FILE_INFORMATION */
3384 long smb_ReceiveTran2QFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
3386 smb_tran2Packet_t *outp;
3388 unsigned long attributes;
3389 unsigned short infoLevel;
3396 smb_tran2QFileInfo_t qfi;
3404 fidp = smb_FindFID(vcp, fid, 0);
3407 smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADFD);
3411 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
3412 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOSUCHFILE);
3413 smb_CloseFID(vcp, fidp, NULL, 0);
3414 smb_ReleaseFID(fidp);
3418 infoLevel = p->parmsp[1];
3419 if (infoLevel == SMB_QUERY_FILE_BASIC_INFO)
3420 responseSize = sizeof(qfi.u.QFbasicInfo);
3421 else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO)
3422 responseSize = sizeof(qfi.u.QFstandardInfo);
3423 else if (infoLevel == SMB_QUERY_FILE_EA_INFO)
3424 responseSize = sizeof(qfi.u.QFeaInfo);
3425 else if (infoLevel == SMB_QUERY_FILE_NAME_INFO)
3426 responseSize = sizeof(qfi.u.QFfileNameInfo);
3428 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
3429 p->opcode, infoLevel);
3430 smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
3431 smb_ReleaseFID(fidp);
3434 osi_Log2(smb_logp, "T2 QFileInfo type 0x%x fid %d", infoLevel, fid);
3436 outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, responseSize);
3438 if (infoLevel > 0x100)
3439 outp->totalParms = 2;
3441 outp->totalParms = 0;
3442 outp->totalData = responseSize;
3444 userp = smb_GetTran2User(vcp, p);
3446 osi_Log1(smb_logp, "ReceiveTran2QFileInfo unable to resolve user [%d]", p->uid);
3447 code = CM_ERROR_BADSMB;
3451 lock_ObtainMutex(&fidp->mx);
3452 delonclose = fidp->flags & SMB_FID_DELONCLOSE;
3454 osi_Log2(smb_logp,"smb_ReleaseTran2QFileInfo fidp 0x%p scp 0x%p", fidp, scp);
3456 lock_ReleaseMutex(&fidp->mx);
3457 lock_ObtainWrite(&scp->rw);
3458 code = cm_SyncOp(scp, NULL, userp, &req, 0,
3459 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3463 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3465 lock_ConvertWToR(&scp->rw);
3468 /* now we have the status in the cache entry, and everything is locked.
3469 * Marshall the output data.
3471 if (infoLevel == SMB_QUERY_FILE_BASIC_INFO) {
3472 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
3473 qfi.u.QFbasicInfo.creationTime = ft;
3474 qfi.u.QFbasicInfo.lastAccessTime = ft;
3475 qfi.u.QFbasicInfo.lastWriteTime = ft;
3476 qfi.u.QFbasicInfo.lastChangeTime = ft;
3477 attributes = smb_ExtAttributes(scp);
3478 qfi.u.QFbasicInfo.attributes = attributes;
3480 else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) {
3481 qfi.u.QFstandardInfo.allocationSize = scp->length;
3482 qfi.u.QFstandardInfo.endOfFile = scp->length;
3483 qfi.u.QFstandardInfo.numberOfLinks = scp->linkCount;
3484 qfi.u.QFstandardInfo.deletePending = (delonclose ? 1 : 0);
3485 qfi.u.QFstandardInfo.directory =
3486 ((scp->fileType == CM_SCACHETYPE_DIRECTORY ||
3487 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
3488 scp->fileType == CM_SCACHETYPE_INVALID)? 1 : 0);
3490 else if (infoLevel == SMB_QUERY_FILE_EA_INFO) {
3491 qfi.u.QFeaInfo.eaSize = 0;
3493 else if (infoLevel == SMB_QUERY_FILE_NAME_INFO) {
3497 lock_ReleaseRead(&scp->rw);
3498 lock_ObtainMutex(&fidp->mx);
3499 lock_ObtainRead(&scp->rw);
3500 if (fidp->NTopen_wholepathp)
3501 name = fidp->NTopen_wholepathp;
3503 name = _C("\\"); /* probably can't happen */
3504 lock_ReleaseMutex(&fidp->mx);
3506 smb_UnparseString(opx, qfi.u.QFfileNameInfo.fileName, name, &len, SMB_STRF_IGNORENUL);
3507 outp->totalData = len + 4; /* this is actually what we want to return */
3508 qfi.u.QFfileNameInfo.fileNameLength = len;
3511 /* send and free the packets */
3514 lock_ReleaseRead(&scp->rw);
3516 lock_ReleaseWrite(&scp->rw);
3517 cm_ReleaseSCache(scp);
3518 cm_ReleaseUser(userp);
3519 smb_ReleaseFID(fidp);
3521 memcpy(outp->datap, &qfi, responseSize);
3522 smb_SendTran2Packet(vcp, outp, opx);
3524 smb_SendTran2Error(vcp, p, opx, code);
3526 smb_FreeTran2Packet(outp);
3532 /* TRANS2_SET_FILE_INFORMATION */
3533 long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
3538 unsigned short infoLevel;
3539 smb_tran2Packet_t *outp;
3540 cm_user_t *userp = NULL;
3541 cm_scache_t *scp = NULL;
3547 fidp = smb_FindFID(vcp, fid, 0);
3550 smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADFD);
3554 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
3555 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOSUCHFILE);
3556 smb_CloseFID(vcp, fidp, NULL, 0);
3557 smb_ReleaseFID(fidp);
3561 infoLevel = p->parmsp[1];
3562 osi_Log2(smb_logp,"ReceiveTran2SetFileInfo type 0x%x fid %d", infoLevel, fid);
3563 if (infoLevel > SMB_SET_FILE_END_OF_FILE_INFO || infoLevel < SMB_SET_FILE_BASIC_INFO) {
3564 osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
3565 p->opcode, infoLevel);
3566 smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
3567 smb_ReleaseFID(fidp);
3571 lock_ObtainMutex(&fidp->mx);
3572 if (infoLevel == SMB_SET_FILE_DISPOSITION_INFO &&
3573 !(fidp->flags & SMB_FID_OPENDELETE)) {
3574 osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo !SMB_FID_OPENDELETE fidp 0x%p scp 0x%p fidp->flags 0x%x",
3575 fidp, fidp->scp, fidp->flags);
3576 lock_ReleaseMutex(&fidp->mx);
3577 smb_ReleaseFID(fidp);
3578 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOACCESS);
3581 if ((infoLevel == SMB_SET_FILE_ALLOCATION_INFO ||
3582 infoLevel == SMB_SET_FILE_END_OF_FILE_INFO)
3583 && !(fidp->flags & SMB_FID_OPENWRITE)) {
3584 osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo !SMB_FID_OPENWRITE fidp 0x%p scp 0x%p fidp->flags 0x%x",
3585 fidp, fidp->scp, fidp->flags);
3586 lock_ReleaseMutex(&fidp->mx);
3587 smb_ReleaseFID(fidp);
3588 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOACCESS);
3593 osi_Log2(smb_logp,"smb_ReceiveTran2SetFileInfo fidp 0x%p scp 0x%p", fidp, scp);
3595 lock_ReleaseMutex(&fidp->mx);
3597 outp = smb_GetTran2ResponsePacket(vcp, p, opx, 2, 0);
3599 outp->totalParms = 2;
3600 outp->totalData = 0;
3602 userp = smb_GetTran2User(vcp, p);
3604 osi_Log1(smb_logp,"ReceiveTran2SetFileInfo unable to resolve user [%d]", p->uid);
3605 code = CM_ERROR_BADSMB;
3609 if (infoLevel == SMB_SET_FILE_BASIC_INFO) {
3611 unsigned int attribute;
3613 smb_tran2QFileInfo_t *sfi;
3615 sfi = (smb_tran2QFileInfo_t *)p->datap;
3617 /* lock the vnode with a callback; we need the current status
3618 * to determine what the new status is, in some cases.
3620 lock_ObtainWrite(&scp->rw);
3621 code = cm_SyncOp(scp, NULL, userp, &req, 0,
3622 CM_SCACHESYNC_GETSTATUS
3623 | CM_SCACHESYNC_NEEDCALLBACK);
3625 lock_ReleaseWrite(&scp->rw);
3629 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
3631 lock_ReleaseWrite(&scp->rw);
3632 lock_ObtainMutex(&fidp->mx);
3633 lock_ObtainRead(&scp->rw);
3635 /* prepare for setattr call */
3638 lastMod = sfi->u.QFbasicInfo.lastWriteTime;
3639 /* when called as result of move a b, lastMod is (-1, -1).
3640 * If the check for -1 is not present, timestamp
3641 * of the resulting file will be 1969 (-1)
3643 if (LargeIntegerNotEqualToZero(*((LARGE_INTEGER *)&lastMod)) &&
3644 lastMod.dwLowDateTime != -1 && lastMod.dwHighDateTime != -1) {
3645 attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
3646 smb_UnixTimeFromLargeSearchTime(&attr.clientModTime, &lastMod);
3647 fidp->flags |= SMB_FID_MTIMESETDONE;
3650 attribute = sfi->u.QFbasicInfo.attributes;
3651 if (attribute != 0) {
3652 if ((scp->unixModeBits & 0222)
3653 && (attribute & SMB_ATTR_READONLY) != 0) {
3654 /* make a writable file read-only */
3655 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
3656 attr.unixModeBits = scp->unixModeBits & ~0222;
3658 else if ((scp->unixModeBits & 0222) == 0
3659 && (attribute & SMB_ATTR_READONLY) == 0) {
3660 /* make a read-only file writable */
3661 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
3662 attr.unixModeBits = scp->unixModeBits | 0222;
3665 lock_ReleaseRead(&scp->rw);
3666 lock_ReleaseMutex(&fidp->mx);
3670 code = cm_SetAttr(scp, &attr, userp, &req);
3674 else if (infoLevel == SMB_SET_FILE_DISPOSITION_INFO) {
3675 int delflag = *((char *)(p->datap));
3676 osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo Delete? %d fidp 0x%p scp 0x%p",
3677 delflag, fidp, scp);
3678 if (*((char *)(p->datap))) { /* File is Deleted */
3679 code = cm_CheckNTDelete(fidp->NTopen_dscp, scp, userp,
3682 lock_ObtainMutex(&fidp->mx);
3683 fidp->flags |= SMB_FID_DELONCLOSE;
3684 lock_ReleaseMutex(&fidp->mx);
3686 osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo CheckNTDelete fidp 0x%p scp 0x%p code 0x%x",
3692 lock_ObtainMutex(&fidp->mx);
3693 fidp->flags &= ~SMB_FID_DELONCLOSE;
3694 lock_ReleaseMutex(&fidp->mx);
3697 else if (infoLevel == SMB_SET_FILE_ALLOCATION_INFO ||
3698 infoLevel == SMB_SET_FILE_END_OF_FILE_INFO) {
3699 LARGE_INTEGER size = *((LARGE_INTEGER *)(p->datap));
3702 attr.mask = CM_ATTRMASK_LENGTH;
3703 attr.length.LowPart = size.LowPart;
3704 attr.length.HighPart = size.HighPart;
3705 code = cm_SetAttr(scp, &attr, userp, &req);
3709 cm_ReleaseSCache(scp);
3710 cm_ReleaseUser(userp);
3711 smb_ReleaseFID(fidp);
3713 smb_SendTran2Packet(vcp, outp, opx);
3715 smb_SendTran2Error(vcp, p, opx, code);
3716 smb_FreeTran2Packet(outp);
3723 smb_ReceiveTran2FSCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3725 osi_Log0(smb_logp,"ReceiveTran2FSCTL - NOT_SUPPORTED");
3726 return CM_ERROR_BADOP;
3731 smb_ReceiveTran2IOCTL(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3733 osi_Log0(smb_logp,"ReceiveTran2IOCTL - NOT_SUPPORTED");
3734 return CM_ERROR_BADOP;
3737 /* TRANS2_FIND_NOTIFY_FIRST */
3739 smb_ReceiveTran2FindNotifyFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3741 osi_Log0(smb_logp,"ReceiveTran2FindNotifyFirst - NOT_SUPPORTED");
3742 return CM_ERROR_BADOP;
3745 /* TRANS2_FIND_NOTIFY_NEXT */
3747 smb_ReceiveTran2FindNotifyNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3749 osi_Log0(smb_logp,"ReceiveTran2FindNotifyNext - NOT_SUPPORTED");
3750 return CM_ERROR_BADOP;
3753 /* TRANS2_CREATE_DIRECTORY */
3755 smb_ReceiveTran2CreateDirectory(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3757 osi_Log0(smb_logp,"ReceiveTran2CreateDirectory - NOT_SUPPORTED");
3758 return CM_ERROR_BADOP;
3761 /* TRANS2_SESSION_SETUP */
3763 smb_ReceiveTran2SessionSetup(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3765 osi_Log0(smb_logp,"ReceiveTran2SessionSetup - NOT_SUPPORTED");
3766 return CM_ERROR_BADOP;
3769 struct smb_v2_referral {
3771 USHORT ReferralFlags;
3774 USHORT DfsPathOffset;
3775 USHORT DfsAlternativePathOffset;
3776 USHORT NetworkAddressOffset;
3779 /* TRANS2_GET_DFS_REFERRAL */
3781 smb_ReceiveTran2GetDFSReferral(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
3783 /* This is a UNICODE only request (bit15 of Flags2) */
3784 /* The TID must be IPC$ */
3786 /* The documentation for the Flags response field is contradictory */
3788 /* Use Version 1 Referral Element Format */
3789 /* ServerType = 0; indicates the next server should be queried for the file */
3790 /* ReferralFlags = 0x01; PathConsumed characters should be stripped */
3791 /* Node = UnicodeString of UNC path of the next share name */
3794 int maxReferralLevel = 0;
3795 clientchar_t requestFileName[1024] = _C("");
3796 clientchar_t referralPath[1024] = _C("");
3797 smb_tran2Packet_t *outp = 0;
3798 cm_user_t *userp = 0;
3799 cm_scache_t *scp = 0;
3800 cm_scache_t *dscp = 0;
3802 CPINFO CodePageInfo;
3803 int i, nbnLen, reqLen, refLen;
3808 maxReferralLevel = p->parmsp[0];
3810 GetCPInfo(CP_ACP, &CodePageInfo);
3811 cm_Utf16ToClientString(&p->parmsp[1], -1, requestFileName, lengthof(requestFileName));
3813 osi_Log2(smb_logp,"ReceiveTran2GetDfsReferral [%d][%S]",
3814 maxReferralLevel, osi_LogSaveClientString(smb_logp, requestFileName));
3816 nbnLen = (int)cm_ClientStrLen(cm_NetbiosNameC);
3817 reqLen = (int)cm_ClientStrLen(requestFileName);
3819 if (reqLen > nbnLen + 2 && requestFileName[0] == '\\' &&
3820 !cm_ClientStrCmpNI(cm_NetbiosNameC, &requestFileName[1], nbnLen) &&
3821 requestFileName[nbnLen+1] == '\\')
3825 if (!cm_ClientStrCmpNI(_C("all"), &requestFileName[nbnLen+2], 3) ||
3826 !cm_ClientStrCmpNI(_C("*."), &requestFileName[nbnLen+2], 2)) {
3828 cm_ClientStrCpy(referralPath, lengthof(referralPath), requestFileName);
3831 userp = smb_GetTran2User(vcp, p);
3833 osi_Log1(smb_logp,"ReceiveTran2GetDfsReferral unable to resolve user [%d]", p->uid);
3834 code = CM_ERROR_BADSMB;
3839 * We have a requested path. Check to see if it is something
3842 * But be careful because the name that we might be searching
3843 * for might be a known name with the final character stripped
3846 code = cm_NameI(cm_data.rootSCachep, &requestFileName[nbnLen+2],
3847 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD | CM_FLAG_DFS_REFERRAL,
3848 userp, NULL, &req, &scp);
3852 cm_ClientStrCpy(referralPath, lengthof(referralPath), requestFileName);
3854 } else if (code == CM_ERROR_PATH_NOT_COVERED ) {
3855 clientchar_t temp[1024];
3856 clientchar_t pathName[1024];
3857 clientchar_t *lastComponent;
3859 * we have a msdfs link somewhere in the path
3860 * we should figure out where in the path the link is.
3863 osi_Log1(smb_logp,"ReceiveTran2GetDfsReferral PATH_NOT_COVERED [%S]", requestFileName);
3865 cm_ClientStrCpy(temp, lengthof(temp), &requestFileName[nbnLen+2]);
3869 cm_ReleaseSCache(dscp);
3873 cm_ReleaseSCache(scp);
3876 smb_StripLastComponent(pathName, &lastComponent, temp);
3878 code = cm_NameI(cm_data.rootSCachep, pathName,
3879 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
3880 userp, NULL, &req, &dscp);
3882 code = cm_NameI(dscp, ++lastComponent,
3884 userp, NULL, &req, &scp);
3885 if (code == 0 && scp->fileType == CM_SCACHETYPE_DFSLINK)
3888 } while (code == CM_ERROR_PATH_NOT_COVERED);
3890 /* scp should now be the DfsLink we are looking for */
3892 /* figure out how much of the input path was used */
3893 reqLen = (int)(nbnLen+2 + cm_ClientStrLen(pathName) + 1 + cm_ClientStrLen(lastComponent));
3895 cm_FsStringToClientString(&scp->mountPointStringp[strlen("msdfs:")], -1,
3896 referralPath, lengthof(referralPath));
3897 refLen = (int)cm_ClientStrLen(referralPath);
3901 clientchar_t shareName[MAX_PATH + 1];
3902 clientchar_t *p, *q;
3903 /* we may have a sharename that is a volume reference */
3905 for (p = &requestFileName[nbnLen+2], q = shareName; *p && *p != '\\'; p++, q++)
3911 if (smb_FindShare(vcp, vcp->usersp, shareName, &p)) {
3912 code = cm_NameI(cm_data.rootSCachep, _C(""),
3913 CM_FLAG_CASEFOLD | CM_FLAG_FOLLOW,
3914 userp, p, &req, &scp);
3919 cm_ClientStrCpy(referralPath, lengthof(referralPath),
3930 struct smb_v2_referral * v2ref;
3931 outp = smb_GetTran2ResponsePacket(vcp, p, op, 0, 2 * (refLen + 8));
3933 sp = (USHORT *)outp->datap;
3935 sp[idx++] = reqLen; /* path consumed */
3936 sp[idx++] = 1; /* number of referrals */
3937 sp[idx++] = 0x03; /* flags */
3938 #ifdef DFS_VERSION_1
3939 sp[idx++] = 1; /* Version Number */
3940 sp[idx++] = refLen + 4; /* Referral Size */
3941 sp[idx++] = 1; /* Type = SMB Server */
3942 sp[idx++] = 0; /* Do not strip path consumed */
3943 for ( i=0;i<=refLen; i++ )
3944 sp[i+idx] = referralPath[i];
3945 #else /* DFS_VERSION_2 */
3946 sp[idx++] = 2; /* Version Number */
3947 sp[idx++] = sizeof(struct smb_v2_referral); /* Referral Size */
3948 idx += (sizeof(struct smb_v2_referral) / 2);
3949 v2ref = (struct smb_v2_referral *) &sp[5];
3950 v2ref->ServerType = 1; /* SMB Server */
3951 v2ref->ReferralFlags = 0x03;
3952 v2ref->Proximity = 0; /* closest */
3953 v2ref->TimeToLive = 3600; /* seconds */
3954 v2ref->DfsPathOffset = idx * 2;
3955 v2ref->DfsAlternativePathOffset = idx * 2;
3956 v2ref->NetworkAddressOffset = 0;
3957 for ( i=0;i<=refLen; i++ )
3958 sp[i+idx] = referralPath[i];
3962 code = CM_ERROR_NOSUCHPATH;
3967 cm_ReleaseSCache(dscp);
3969 cm_ReleaseSCache(scp);
3971 cm_ReleaseUser(userp);
3973 smb_SendTran2Packet(vcp, outp, op);
3975 smb_SendTran2Error(vcp, p, op, code);
3977 smb_FreeTran2Packet(outp);
3980 #else /* DFS_SUPPORT */
3981 osi_Log0(smb_logp,"ReceiveTran2GetDfsReferral - NOT_SUPPORTED");
3982 return CM_ERROR_NOSUCHDEVICE;
3983 #endif /* DFS_SUPPORT */
3986 /* TRANS2_REPORT_DFS_INCONSISTENCY */
3988 smb_ReceiveTran2ReportDFSInconsistency(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
3990 /* This is a UNICODE only request (bit15 of Flags2) */
3992 /* There is nothing we can do about this operation. The client is going to
3993 * tell us that there is a Version 1 Referral Element for which there is a DFS Error.
3994 * Unfortunately, there is really nothing we can do about it other then log it
3995 * somewhere. Even then I don't think there is anything for us to do.
3996 * So let's return an error value.
3999 osi_Log0(smb_logp,"ReceiveTran2ReportDFSInconsistency - NOT_SUPPORTED");
4000 return CM_ERROR_BADOP;
4004 smb_ApplyV3DirListPatches(cm_scache_t *dscp, smb_dirListPatch_t **dirPatchespp,
4005 clientchar_t * tidPathp, clientchar_t * relPathp,
4006 int infoLevel, cm_user_t *userp, cm_req_t *reqp)
4010 cm_scache_t *targetScp; /* target if scp is a symlink */
4013 unsigned short attr;
4014 unsigned long lattr;
4015 smb_dirListPatch_t *patchp;
4016 smb_dirListPatch_t *npatchp;
4018 afs_int32 mustFake = 0;
4019 clientchar_t path[AFSPATHMAX];
4021 code = cm_FindACLCache(dscp, userp, &rights);
4023 lock_ObtainWrite(&dscp->rw);
4024 code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_READ,
4025 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4027 cm_SyncOpDone(dscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4028 lock_ReleaseWrite(&dscp->rw);
4029 if (code == CM_ERROR_NOACCESS) {
4037 if (!mustFake) { /* Bulk Stat */
4039 cm_bulkStat_t *bsp = malloc(sizeof(cm_bulkStat_t));
4041 memset(bsp, 0, sizeof(cm_bulkStat_t));
4043 for (patchp = *dirPatchespp, count=0;
4045 patchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
4046 cm_scache_t *tscp = NULL;
4049 code = cm_GetSCache(&patchp->fid, &tscp, userp, reqp);
4051 if (lock_TryWrite(&tscp->rw)) {
4052 /* we have an entry that we can look at */
4053 #ifdef AFS_FREELANCE_CLIENT
4054 if (dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID && dscp->fid.volume == AFS_FAKE_ROOT_VOL_ID) {
4055 code = cm_SyncOp(tscp, NULL, userp, reqp, 0,
4056 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4058 cm_SyncOpDone(tscp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4060 lock_ReleaseWrite(&tscp->rw);
4061 cm_ReleaseSCache(tscp);
4064 #endif /* AFS_FREELANCE_CLIENT */
4065 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
4066 /* we have a callback on it. Don't bother
4067 * fetching this stat entry, since we're happy
4068 * with the info we have.
4070 lock_ReleaseWrite(&tscp->rw);
4071 cm_ReleaseSCache(tscp);
4074 lock_ReleaseWrite(&tscp->rw);
4076 cm_ReleaseSCache(tscp);
4080 bsp->fids[i].Volume = patchp->fid.volume;
4081 bsp->fids[i].Vnode = patchp->fid.vnode;
4082 bsp->fids[i].Unique = patchp->fid.unique;
4084 if (bsp->counter == AFSCBMAX) {
4085 code = cm_TryBulkStatRPC(dscp, bsp, userp, reqp);
4086 memset(bsp, 0, sizeof(cm_bulkStat_t));
4090 if (bsp->counter > 0)
4091 code = cm_TryBulkStatRPC(dscp, bsp, userp, reqp);
4096 for( patchp = *dirPatchespp;
4098 patchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
4099 cm_ClientStrPrintfN(path, lengthof(path),_C("%s\\%S"),
4100 relPathp ? relPathp : _C(""), patchp->dep->name);
4101 reqp->relPathp = path;
4102 reqp->tidPathp = tidPathp;
4104 code = cm_GetSCache(&patchp->fid, &scp, userp, reqp);
4105 reqp->relPathp = reqp->tidPathp = NULL;
4109 lock_ObtainWrite(&scp->rw);
4110 if (mustFake || (scp->flags & CM_SCACHEFLAG_EACCESS) || !cm_HaveCallback(scp)) {
4111 lock_ReleaseWrite(&scp->rw);
4113 /* Plug in fake timestamps. A time stamp of 0 causes 'invalid parameter'
4114 errors in the client. */
4115 if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO) {
4116 smb_V3FileAttrsLong * fa = (smb_V3FileAttrsLong *) patchp->dptr;
4118 /* 1969-12-31 23:59:59 +00 */
4119 ft.dwHighDateTime = 0x19DB200;
4120 ft.dwLowDateTime = 0x5BB78980;
4122 /* copy to Creation Time */
4123 fa->creationTime = ft;
4124 fa->lastAccessTime = ft;
4125 fa->lastWriteTime = ft;
4126 fa->lastChangeTime = ft;
4128 switch (scp->fileType) {
4129 case CM_SCACHETYPE_DIRECTORY:
4130 case CM_SCACHETYPE_MOUNTPOINT:
4131 case CM_SCACHETYPE_SYMLINK:
4132 case CM_SCACHETYPE_INVALID:
4133 fa->extFileAttributes = SMB_ATTR_DIRECTORY;
4136 /* if we get here we either have a normal file
4137 * or we have a file for which we have never
4138 * received status info. In this case, we can
4139 * check the even/odd value of the entry's vnode.
4140 * even means it is to be treated as a directory
4141 * and odd means it is to be treated as a file.
4143 if (mustFake && (scp->fid.vnode & 0x1))
4144 fa->extFileAttributes = SMB_ATTR_DIRECTORY;
4146 fa->extFileAttributes = SMB_ATTR_NORMAL;
4148 /* merge in hidden attribute */
4149 if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
4150 fa->extFileAttributes |= SMB_ATTR_HIDDEN;
4153 smb_V3FileAttrsShort * fa = (smb_V3FileAttrsShort *) patchp->dptr;
4155 /* 1969-12-31 23:59:58 +00*/
4156 dosTime = 0xEBBFBF7D;
4158 fa->creationDateTime = MAKELONG(HIWORD(dosTime),LOWORD(dosTime));
4159 fa->lastAccessDateTime = fa->creationDateTime;
4160 fa->lastWriteDateTime = fa->creationDateTime;
4162 /* set the attribute */
4163 switch (scp->fileType) {
4164 case CM_SCACHETYPE_DIRECTORY:
4165 case CM_SCACHETYPE_MOUNTPOINT:
4166 case CM_SCACHETYPE_SYMLINK:
4167 case CM_SCACHETYPE_INVALID:
4168 fa->attributes = SMB_ATTR_DIRECTORY;
4171 /* if we get here we either have a normal file
4172 * or we have a file for which we have never
4173 * received status info. In this case, we can
4174 * check the even/odd value of the entry's vnode.
4175 * even means it is to be treated as a directory
4176 * and odd means it is to be treated as a file.
4178 if (mustFake && (scp->fid.vnode & 0x1))
4179 fa->attributes = SMB_ATTR_DIRECTORY;
4181 fa->attributes = SMB_ATTR_NORMAL;
4184 /* merge in hidden (dot file) attribute */
4185 if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
4186 fa->attributes |= SMB_ATTR_HIDDEN;
4190 cm_ReleaseSCache(scp);
4194 /* now watch for a symlink */
4196 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
4197 lock_ReleaseWrite(&scp->rw);
4198 cm_ClientStrPrintfN(path, lengthof(path), _C("%s\\%S"),
4199 relPathp ? relPathp : _C(""), patchp->dep->name);
4200 reqp->relPathp = path;
4201 reqp->tidPathp = tidPathp;
4202 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, reqp);
4203 reqp->relPathp = reqp->tidPathp = NULL;
4205 /* we have a more accurate file to use (the
4206 * target of the symbolic link). Otherwise,
4207 * we'll just use the symlink anyway.
4209 osi_Log2(smb_logp, "symlink vp %x to vp %x",
4211 cm_ReleaseSCache(scp);
4214 lock_ObtainWrite(&scp->rw);
4217 lock_ConvertWToR(&scp->rw);
4219 if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO) {
4220 smb_V3FileAttrsLong * fa = (smb_V3FileAttrsLong *) patchp->dptr;
4223 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
4225 fa->creationTime = ft;
4226 fa->lastAccessTime = ft;
4227 fa->lastWriteTime = ft;
4228 fa->lastChangeTime = ft;
4230 /* Use length for both file length and alloc length */
4231 fa->endOfFile = scp->length;
4232 fa->allocationSize = scp->length;
4234 /* Copy attributes */
4235 lattr = smb_ExtAttributes(scp);
4236 if (code == CM_ERROR_NOSUCHPATH && scp->fileType == CM_SCACHETYPE_SYMLINK ||
4237 code == CM_ERROR_PATH_NOT_COVERED && scp->fileType == CM_SCACHETYPE_DFSLINK) {
4238 if (lattr == SMB_ATTR_NORMAL)
4239 lattr = SMB_ATTR_DIRECTORY;
4241 lattr |= SMB_ATTR_DIRECTORY;
4243 /* merge in hidden (dot file) attribute */
4244 if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
4245 if (lattr == SMB_ATTR_NORMAL)
4246 lattr = SMB_ATTR_HIDDEN;
4248 lattr |= SMB_ATTR_HIDDEN;
4251 fa->extFileAttributes = lattr;
4253 smb_V3FileAttrsShort * fa = (smb_V3FileAttrsShort *) patchp->dptr;
4256 smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
4258 fa->creationDateTime = MAKELONG(HIWORD(dosTime), LOWORD(dosTime));
4259 fa->lastAccessDateTime = fa->creationDateTime;
4260 fa->lastWriteDateTime = fa->creationDateTime;
4262 /* copy out file length and alloc length,
4263 * using the same for both
4265 fa->dataSize = scp->length.LowPart;
4266 fa->allocationSize = scp->length.LowPart;
4268 /* finally copy out attributes as short */
4269 attr = smb_Attributes(scp);
4270 /* merge in hidden (dot file) attribute */
4271 if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
4272 if (lattr == SMB_ATTR_NORMAL)
4273 lattr = SMB_ATTR_HIDDEN;
4275 lattr |= SMB_ATTR_HIDDEN;
4277 fa->attributes = attr;
4280 lock_ReleaseRead(&scp->rw);
4281 cm_ReleaseSCache(scp);
4284 /* now free the patches */
4285 for (patchp = *dirPatchespp; patchp; patchp = npatchp) {
4286 npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
4290 /* and mark the list as empty */
4291 *dirPatchespp = NULL;
4297 /* smb_ReceiveTran2SearchDir implements both
4298 * Tran2_Find_First and Tran2_Find_Next
4300 #define TRAN2_FIND_FLAG_CLOSE_SEARCH 0x01
4301 #define TRAN2_FIND_FLAG_CLOSE_SEARCH_IF_END 0x02
4302 #define TRAN2_FIND_FLAG_RETURN_RESUME_KEYS 0x04
4303 #define TRAN2_FIND_FLAG_CONTINUE_SEARCH 0x08
4304 #define TRAN2_FIND_FLAG_BACKUP_INTENT 0x10
4306 /* this is an optimized handler for T2SearchDir that handles the case
4307 where there are no wildcards in the search path. I.e. an
4308 application is using FindFirst(Ex) to get information about a
4309 single file or directory. It will attempt to do a single lookup.
4310 If that fails, then smb_ReceiveTran2SearchDir() will fall back to
4311 the usual mechanism.
4313 This function will return either CM_ERROR_NOSUCHFILE or SUCCESS.
4315 TRANS2_FIND_FIRST2 and TRANS2_FIND_NEXT2
4317 long smb_T2SearchDirSingle(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
4321 long code = 0, code2 = 0;
4322 clientchar_t *pathp = 0;
4324 smb_dirListPatch_t *dirListPatchesp;
4325 smb_dirListPatch_t *curPatchp;
4326 size_t orbytes; /* # of bytes in this output record */
4327 size_t ohbytes; /* # of bytes, except file name */
4328 size_t onbytes; /* # of bytes in name, incl. term. null */
4329 cm_scache_t *scp = NULL;
4330 cm_scache_t *targetscp = NULL;
4331 cm_user_t *userp = NULL;
4332 char *op; /* output data ptr */
4333 char *origOp; /* original value of op */
4334 cm_space_t *spacep; /* for pathname buffer */
4335 unsigned long maxReturnData; /* max # of return data */
4336 long maxReturnParms; /* max # of return parms */
4337 long bytesInBuffer; /* # data bytes in the output buffer */
4338 clientchar_t *maskp; /* mask part of path */
4342 smb_tran2Packet_t *outp; /* response packet */
4343 clientchar_t *tidPathp = 0;
4345 clientchar_t shortName[13]; /* 8.3 name if needed */
4347 clientchar_t *shortNameEnd;
4348 cm_dirEntry_t * dep = NULL;
4351 void * attrp = NULL;
4352 smb_tran2Find_t * fp;
4357 osi_assertx(p->opcode == 1, "invalid opcode");
4359 /* find first; obtain basic parameters from request */
4361 /* note that since we are going to failover to regular
4362 * processing at smb_ReceiveTran2SearchDir(), we shouldn't
4363 * modify any of the input parameters here. */
4364 attribute = p->parmsp[0];
4365 maxCount = p->parmsp[1];
4366 infoLevel = p->parmsp[3];
4367 searchFlags = p->parmsp[2];
4368 pathp = smb_ParseStringT2Parm(p, (char *) &(p->parmsp[6]), NULL, SMB_STRF_ANSIPATH);
4370 maskp = cm_ClientStrRChr(pathp, '\\');
4374 maskp++; /* skip over backslash */
4375 /* track if this is likely to match a lot of entries */
4377 osi_Log2(smb_logp, "smb_T2SearchDirSingle : path[%S], mask[%S]",
4378 osi_LogSaveClientString(smb_logp, pathp),
4379 osi_LogSaveClientString(smb_logp, maskp));
4381 switch ( infoLevel ) {
4382 case SMB_INFO_STANDARD:
4384 ohbytes = sizeof(fp->u.FstandardInfo);
4387 case SMB_INFO_QUERY_EA_SIZE:
4388 ohbytes = sizeof(fp->u.FeaSizeInfo);
4389 s = "InfoQueryEaSize";
4392 case SMB_INFO_QUERY_EAS_FROM_LIST:
4393 ohbytes = sizeof(fp->u.FeasFromListInfo);
4394 s = "InfoQueryEasFromList";
4397 case SMB_FIND_FILE_DIRECTORY_INFO:
4398 s = "FindFileDirectoryInfo";
4399 ohbytes = sizeof(fp->u.FfileDirectoryInfo);
4402 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
4403 s = "FindFileFullDirectoryInfo";
4404 ohbytes = sizeof(fp->u.FfileFullDirectoryInfo);
4407 case SMB_FIND_FILE_NAMES_INFO:
4408 s = "FindFileNamesInfo";
4409 ohbytes = sizeof(fp->u.FfileNamesInfo);
4412 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
4413 s = "FindFileBothDirectoryInfo";
4414 ohbytes = sizeof(fp->u.FfileBothDirectoryInfo);
4418 s = "unknownInfoLevel";
4422 osi_Log1(smb_logp, "smb_T2SearchDirSingle info level: %s", s);
4425 "smb_T2SearchDirSingle attr 0x%x, info level 0x%x, max count %d, flags 0x%x",
4426 attribute, infoLevel, maxCount, searchFlags);
4429 osi_Log1(smb_logp, "Unsupported InfoLevel 0x%x", infoLevel);
4430 return CM_ERROR_INVAL;
4433 if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
4434 searchFlags &= ~TRAN2_FIND_FLAG_RETURN_RESUME_KEYS; /* no resume keys */
4436 if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS)
4439 dirListPatchesp = NULL;
4441 maxReturnData = p->maxReturnData;
4442 maxReturnParms = 10; /* return params for findfirst, which
4443 is the only one we handle.*/
4445 outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
4448 osi_Log2(smb_logp, "T2SDSingle search dir count %d [%S]",
4449 maxCount, osi_LogSaveClientString(smb_logp, pathp));
4451 /* bail out if request looks bad */
4453 smb_FreeTran2Packet(outp);
4454 return CM_ERROR_BADSMB;
4457 userp = smb_GetTran2User(vcp, p);
4459 osi_Log1(smb_logp, "T2SDSingle search dir unable to resolve user [%d]", p->uid);
4460 smb_FreeTran2Packet(outp);
4461 return CM_ERROR_BADSMB;
4464 /* try to get the vnode for the path name next */
4465 spacep = cm_GetSpace();
4466 smb_StripLastComponent(spacep->wdata, NULL, pathp);
4467 code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
4469 cm_ReleaseUser(userp);
4470 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOFILES);
4471 smb_FreeTran2Packet(outp);
4475 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
4476 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
4477 userp, tidPathp, &req, &scp);
4478 cm_FreeSpace(spacep);
4481 cm_ReleaseUser(userp);
4482 smb_SendTran2Error(vcp, p, opx, code);
4483 smb_FreeTran2Packet(outp);
4487 #ifdef DFS_SUPPORT_BUT_NOT_FIND_FIRST
4488 if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
4489 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, spacep->data);
4490 cm_ReleaseSCache(scp);
4491 cm_ReleaseUser(userp);
4492 if ( WANTS_DFS_PATHNAMES(p) || pnc )
4493 code = CM_ERROR_PATH_NOT_COVERED;
4495 code = CM_ERROR_BADSHARENAME;
4496 smb_SendTran2Error(vcp, p, opx, code);
4497 smb_FreeTran2Packet(outp);
4500 #endif /* DFS_SUPPORT */
4501 osi_Log1(smb_logp,"T2SDSingle scp 0x%p", scp);
4503 /* now do a single case sensitive lookup for the file in question */
4504 code = cm_Lookup(scp, maskp, CM_FLAG_NOMOUNTCHASE, userp, &req, &targetscp);
4506 /* if a case sensitive match failed, we try a case insensitive one
4508 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) {
4509 code = cm_Lookup(scp, maskp, CM_FLAG_NOMOUNTCHASE | CM_FLAG_CASEFOLD, userp, &req, &targetscp);
4512 if (code == 0 && targetscp->fid.vnode == 0) {
4513 cm_ReleaseSCache(targetscp);
4514 code = CM_ERROR_NOSUCHFILE;
4518 /* if we can't find the directory entry, this block will
4519 return CM_ERROR_NOSUCHFILE, which we will pass on to
4520 smb_ReceiveTran2SearchDir(). */
4521 cm_ReleaseSCache(scp);
4522 cm_ReleaseUser(userp);
4523 if (code != CM_ERROR_NOSUCHFILE && code != CM_ERROR_BPLUS_NOMATCH) {
4524 smb_SendTran2Error(vcp, p, opx, code);
4527 smb_FreeTran2Packet(outp);
4531 /* now that we have the target in sight, we proceed with filling
4532 up the return data. */
4534 op = origOp = outp->datap;
4537 if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS) {
4538 /* skip over resume key */
4542 fp = (smb_tran2Find_t *) op;
4544 if (infoLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO
4545 && targetscp->fid.vnode != 0
4546 && !cm_Is8Dot3(maskp)) {
4549 dfid.vnode = htonl(targetscp->fid.vnode);
4550 dfid.unique = htonl(targetscp->fid.unique);
4552 cm_Gen8Dot3NameIntW(maskp, &dfid, shortName, &shortNameEnd);
4558 osi_Log4(smb_logp, "T2SDSingle dir vn %u uniq %u name %S (%S)",
4559 htonl(targetscp->fid.vnode),
4560 htonl(targetscp->fid.unique),
4561 osi_LogSaveClientString(smb_logp, pathp),
4562 (NeedShortName)? osi_LogSaveClientString(smb_logp, shortName) : _C(""));
4564 /* Eliminate entries that don't match requested attributes */
4565 if (smb_hideDotFiles && !(attribute & SMB_ATTR_HIDDEN) &&
4566 smb_IsDotFile(maskp)) {
4568 code = CM_ERROR_NOSUCHFILE;
4569 osi_Log0(smb_logp, "T2SDSingle skipping hidden file");
4574 if (!(attribute & SMB_ATTR_DIRECTORY) &&
4575 (targetscp->fileType == CM_SCACHETYPE_DIRECTORY ||
4576 targetscp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
4577 targetscp->fileType == CM_SCACHETYPE_DFSLINK ||
4578 targetscp->fileType == CM_SCACHETYPE_INVALID)) {
4580 code = CM_ERROR_NOSUCHFILE;
4581 osi_Log0(smb_logp, "T2SDSingle skipping directory or bad link");
4586 /* add header to name & term. null */
4588 smb_UnparseString(opx, NULL, maskp, &onbytes, SMB_STRF_ANSIPATH|SMB_STRF_IGNORENUL);
4589 orbytes = ohbytes + onbytes;
4591 /* now, we round up the record to a 4 byte alignment, and we make
4592 * sure that we have enough room here for even the aligned version
4593 * (so we don't have to worry about an * overflow when we pad
4594 * things out below). That's the reason for the alignment
4597 if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
4598 align = (4 - (orbytes & 3)) & 3;
4602 if (orbytes + align > maxReturnData) {
4604 /* even though this request is unlikely to succeed with a
4605 failover, we do it anyway. */
4606 code = CM_ERROR_NOSUCHFILE;
4607 osi_Log1(smb_logp, "T2 dir search exceed max return data %d",
4612 /* this is one of the entries to use: it is not deleted and it
4613 * matches the star pattern we're looking for. Put out the name,
4614 * preceded by its length.
4616 /* First zero everything else */
4617 memset(origOp, 0, orbytes);
4620 smb_UnparseString(opx, origOp + ohbytes, maskp, &onbytes, SMB_STRF_ANSIPATH|SMB_STRF_IGNORENUL);
4622 switch (infoLevel) {
4623 case SMB_INFO_STANDARD:
4624 fp->u.FstandardInfo.fileNameLength = onbytes;
4625 attrp = &fp->u.FstandardInfo.fileAttrs;
4628 case SMB_INFO_QUERY_EA_SIZE:
4629 fp->u.FeaSizeInfo.fileNameLength = onbytes;
4630 attrp = &fp->u.FeaSizeInfo.fileAttrs;
4631 fp->u.FeaSizeInfo.eaSize = 0;
4634 case SMB_INFO_QUERY_EAS_FROM_LIST:
4635 fp->u.FeasFromListInfo.fileNameLength = onbytes;
4636 attrp = &fp->u.FeasFromListInfo.fileAttrs;
4637 fp->u.FeasFromListInfo.eaSize = 0;
4640 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
4641 if (NeedShortName) {
4645 nchars = cm_ClientStringToUtf16(shortName, cm_ClientStrLen(shortName),
4646 fp->u.FfileBothDirectoryInfo.shortName,
4647 sizeof(fp->u.FfileBothDirectoryInfo.shortName)/sizeof(wchar_t));
4649 fp->u.FfileBothDirectoryInfo.shortNameLength = nchars*sizeof(wchar_t);
4651 fp->u.FfileBothDirectoryInfo.shortNameLength = 0;
4652 fp->u.FfileBothDirectoryInfo.reserved = 0;
4654 strcpy(fp->u.FfileBothDirectoryInfo.shortName,
4656 fp->u.FfileBothDirectoryInfo.shortNameLength = cm_ClientStrLen(shortName);
4661 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
4662 fp->u.FfileFullDirectoryInfo.eaSize = 0;
4665 case SMB_FIND_FILE_DIRECTORY_INFO:
4666 fp->u.FfileDirectoryInfo.nextEntryOffset = 0;
4667 fp->u.FfileDirectoryInfo.fileIndex = 0;
4668 attrp = &fp->u.FfileDirectoryInfo.fileAttrs;
4669 fp->u.FfileDirectoryInfo.fileNameLength = onbytes;
4672 case SMB_FIND_FILE_NAMES_INFO:
4673 fp->u.FfileNamesInfo.nextEntryOffset = 0;
4674 fp->u.FfileNamesInfo.fileIndex = 0;
4675 fp->u.FfileNamesInfo.fileNameLength = onbytes;
4679 /* we shouldn't hit this case */
4680 osi_assertx(FALSE, "Unknown query type");
4683 if (infoLevel != SMB_FIND_FILE_NAMES_INFO) {
4684 osi_assert(attrp != NULL);
4686 curPatchp = malloc(sizeof(*curPatchp));
4687 osi_QAdd((osi_queue_t **) &dirListPatchesp,
4689 curPatchp->dptr = attrp;
4691 if (smb_hideDotFiles && smb_IsDotFile(maskp)) {
4692 curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
4694 curPatchp->flags = 0;
4697 cm_SetFid(&curPatchp->fid, targetscp->fid.cell, targetscp->fid.volume, targetscp->fid.vnode, targetscp->fid.unique);
4701 int namelen = cm_ClientStringToFsString(maskp, -1, NULL, 0);
4702 dep = (cm_dirEntry_t *)malloc(sizeof(cm_dirEntry_t)+namelen);
4703 cm_ClientStringToFsString(maskp, -1, dep->name, namelen);
4705 dep->fid.vnode = targetscp->fid.vnode;
4706 dep->fid.unique = targetscp->fid.unique;
4707 curPatchp->dep = dep;
4710 if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS) {
4711 /* put out resume key */
4712 *((u_long *)origOp) = 0;
4715 /* Adjust byte ptr and count */
4716 origOp += orbytes; /* skip entire record */
4717 bytesInBuffer += orbytes;
4719 /* and pad the record out */
4720 while (--align >= 0) {
4725 /* apply the patches */
4726 code2 = smb_ApplyV3DirListPatches(scp, &dirListPatchesp, tidPathp, spacep->wdata, infoLevel, userp, &req);
4728 outp->parmsp[0] = 0;
4729 outp->parmsp[1] = 1; /* number of names returned */
4730 outp->parmsp[2] = 1; /* end of search */
4731 outp->parmsp[3] = 0; /* nothing wrong with EAS */
4732 outp->parmsp[4] = 0;
4734 outp->totalParms = 10; /* in bytes */
4736 outp->totalData = bytesInBuffer;
4738 osi_Log0(smb_logp, "T2SDSingle done.");
4740 if (code != CM_ERROR_NOSUCHFILE && code != CM_ERROR_BPLUS_NOMATCH) {
4742 smb_SendTran2Error(vcp, p, opx, code);
4744 smb_SendTran2Packet(vcp, outp, opx);
4749 smb_FreeTran2Packet(outp);
4753 cm_ReleaseSCache(scp);
4754 cm_ReleaseSCache(targetscp);
4755 cm_ReleaseUser(userp);
4761 /* TRANS2_FIND_FIRST2 and TRANS2_FIND_NEXT2 */
4762 long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
4767 long code = 0, code2 = 0;
4768 clientchar_t *pathp;
4769 cm_dirEntry_t *dep = 0;
4771 smb_dirListPatch_t *dirListPatchesp = 0;
4772 smb_dirListPatch_t *curPatchp = 0;
4775 size_t orbytes; /* # of bytes in this output record */
4776 size_t ohbytes; /* # of bytes, except file name */
4777 size_t onbytes; /* # of bytes in name, incl. term. null */
4778 osi_hyper_t dirLength;
4779 osi_hyper_t bufferOffset;
4780 osi_hyper_t curOffset;
4782 smb_dirSearch_t *dsp;
4786 cm_pageHeader_t *pageHeaderp;
4787 cm_user_t *userp = NULL;
4790 long nextEntryCookie;
4791 int numDirChunks; /* # of 32 byte dir chunks in this entry */
4792 char *op; /* output data ptr */
4793 char *origOp; /* original value of op */
4794 cm_space_t *spacep; /* for pathname buffer */
4795 unsigned long maxReturnData; /* max # of return data */
4796 unsigned long maxReturnParms; /* max # of return parms */
4797 long bytesInBuffer; /* # data bytes in the output buffer */
4799 clientchar_t *maskp; /* mask part of path */
4803 smb_tran2Packet_t *outp; /* response packet */
4804 clientchar_t *tidPathp;
4806 clientchar_t shortName[13]; /* 8.3 name if needed */
4809 clientchar_t *shortNameEnd;
4815 smb_tran2Find_t * fp;
4820 if (p->opcode == 1) {
4821 /* find first; obtain basic parameters from request */
4822 attribute = p->parmsp[0];
4823 maxCount = p->parmsp[1];
4824 infoLevel = p->parmsp[3];
4825 searchFlags = p->parmsp[2];
4826 pathp = smb_ParseStringT2Parm(p, (char *) (&p->parmsp[6]), NULL, SMB_STRF_ANSIPATH);
4828 maskp = cm_ClientStrRChr(pathp, '\\');
4832 maskp++; /* skip over backslash */
4834 /* track if this is likely to match a lot of entries */
4835 starPattern = smb_V3IsStarMask(maskp);
4837 #ifndef NOFINDFIRSTOPTIMIZE
4839 /* if this is for a single directory or file, we let the
4840 optimized routine handle it. The only error it
4841 returns is CM_ERROR_NOSUCHFILE. The */
4842 code = smb_T2SearchDirSingle(vcp, p, opx);
4844 /* we only failover if we see a CM_ERROR_NOSUCHFILE */
4845 if (code != CM_ERROR_NOSUCHFILE) {
4847 /* unless we are using the BPlusTree */
4848 if (code == CM_ERROR_BPLUS_NOMATCH)
4849 code = CM_ERROR_NOSUCHFILE;
4850 #endif /* USE_BPLUS */
4854 #endif /* NOFINDFIRSTOPTIMIZE */
4857 dsp = smb_NewDirSearch(1);
4858 dsp->attribute = attribute;
4859 cm_ClientStrCpy(dsp->mask, lengthof(dsp->mask), maskp); /* and save mask */
4862 osi_assertx(p->opcode == 2, "invalid opcode");
4863 /* find next; obtain basic parameters from request or open dir file */
4864 dsp = smb_FindDirSearch(p->parmsp[0]);
4865 maxCount = p->parmsp[1];
4866 infoLevel = p->parmsp[2];
4867 nextCookie = p->parmsp[3] | (p->parmsp[4] << 16);
4868 searchFlags = p->parmsp[5];
4870 osi_Log2(smb_logp, "T2 search dir bad search ID: id %d nextCookie 0x%x",
4871 p->parmsp[0], nextCookie);
4872 return CM_ERROR_BADFD;
4874 attribute = dsp->attribute;
4877 starPattern = 1; /* assume, since required a Find Next */
4880 switch ( infoLevel ) {
4881 case SMB_INFO_STANDARD:
4883 ohbytes = sizeof(fp->u.FstandardInfo);
4886 case SMB_INFO_QUERY_EA_SIZE:
4887 ohbytes = sizeof(fp->u.FeaSizeInfo);
4888 s = "InfoQueryEaSize";
4891 case SMB_INFO_QUERY_EAS_FROM_LIST:
4892 ohbytes = sizeof(fp->u.FeasFromListInfo);
4893 s = "InfoQueryEasFromList";
4896 case SMB_FIND_FILE_DIRECTORY_INFO:
4897 s = "FindFileDirectoryInfo";
4898 ohbytes = sizeof(fp->u.FfileDirectoryInfo);
4901 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
4902 s = "FindFileFullDirectoryInfo";
4903 ohbytes = sizeof(fp->u.FfileFullDirectoryInfo);
4906 case SMB_FIND_FILE_NAMES_INFO:
4907 s = "FindFileNamesInfo";
4908 ohbytes = sizeof(fp->u.FfileNamesInfo);
4911 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
4912 s = "FindFileBothDirectoryInfo";
4913 ohbytes = sizeof(fp->u.FfileBothDirectoryInfo);
4917 s = "unknownInfoLevel";
4921 osi_Log1(smb_logp, "T2 search dir info level: %s", s);
4924 "T2 search dir attr 0x%x, info level 0x%x, max count %d, flags 0x%x",
4925 attribute, infoLevel, maxCount, searchFlags);
4927 osi_Log3(smb_logp, "...T2 search op %d, id %d, nextCookie 0x%x",
4928 p->opcode, dsp->cookie, nextCookie);
4931 osi_Log1(smb_logp, "Unsupported InfoLevel 0x%x", infoLevel);
4932 smb_ReleaseDirSearch(dsp);
4933 return CM_ERROR_INVAL;
4936 if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
4937 searchFlags &= ~TRAN2_FIND_FLAG_RETURN_RESUME_KEYS; /* no resume keys */
4939 if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS)
4942 dirListPatchesp = NULL;
4944 maxReturnData = p->maxReturnData;
4945 if (p->opcode == 1) /* find first */
4946 maxReturnParms = 10; /* bytes */
4948 maxReturnParms = 8; /* bytes */
4950 outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
4956 osi_Log2(smb_logp, "T2 receive search dir count %d [%S]",
4957 maxCount, osi_LogSaveClientString(smb_logp, pathp));
4959 /* bail out if request looks bad */
4960 if (p->opcode == 1 && !pathp) {
4961 smb_ReleaseDirSearch(dsp);
4962 smb_FreeTran2Packet(outp);
4963 return CM_ERROR_BADSMB;
4966 osi_Log3(smb_logp, "T2 search dir id %d, nextCookie 0x%x, attr 0x%x",
4967 dsp->cookie, nextCookie, attribute);
4969 userp = smb_GetTran2User(vcp, p);
4971 osi_Log1(smb_logp, "T2 search dir unable to resolve user [%d]", p->uid);
4972 smb_ReleaseDirSearch(dsp);
4973 smb_FreeTran2Packet(outp);
4974 return CM_ERROR_BADSMB;
4977 /* try to get the vnode for the path name next */
4978 lock_ObtainMutex(&dsp->mx);
4981 osi_Log2(smb_logp,"smb_ReceiveTran2SearchDir dsp 0x%p scp 0x%p", dsp, scp);
4985 spacep = cm_GetSpace();
4986 smb_StripLastComponent(spacep->wdata, NULL, pathp);
4987 code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
4989 cm_ReleaseUser(userp);
4990 smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOFILES);
4991 smb_FreeTran2Packet(outp);
4992 lock_ReleaseMutex(&dsp->mx);
4993 smb_DeleteDirSearch(dsp);
4994 smb_ReleaseDirSearch(dsp);
4998 cm_ClientStrCpy(dsp->tidPath, lengthof(dsp->tidPath), tidPathp ? tidPathp : _C("/"));
4999 cm_ClientStrCpy(dsp->relPath, lengthof(dsp->relPath), spacep->wdata);
5001 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
5002 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
5003 userp, tidPathp, &req, &scp);
5004 cm_FreeSpace(spacep);
5007 #ifdef DFS_SUPPORT_BUT_NOT_FIND_FIRST
5008 if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
5009 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, spacep->data);
5010 cm_ReleaseSCache(scp);
5011 cm_ReleaseUser(userp);
5012 if ( WANTS_DFS_PATHNAMES(p) || pnc )
5013 code = CM_ERROR_PATH_NOT_COVERED;
5015 code = CM_ERROR_BADSHARENAME;
5016 smb_SendTran2Error(vcp, p, opx, code);
5017 smb_FreeTran2Packet(outp);
5018 lock_ReleaseMutex(&dsp->mx);
5019 smb_DeleteDirSearch(dsp);
5020 smb_ReleaseDirSearch(dsp);
5023 #endif /* DFS_SUPPORT */
5025 osi_Log2(smb_logp,"smb_ReceiveTran2SearchDir dsp 0x%p scp 0x%p", dsp, scp);
5026 /* we need one hold for the entry we just stored into,
5027 * and one for our own processing. When we're done
5028 * with this function, we'll drop the one for our own
5029 * processing. We held it once from the namei call,
5030 * and so we do another hold now.
5033 dsp->flags |= SMB_DIRSEARCH_BULKST;
5036 lock_ReleaseMutex(&dsp->mx);
5038 cm_ReleaseUser(userp);
5039 smb_FreeTran2Packet(outp);
5040 smb_DeleteDirSearch(dsp);
5041 smb_ReleaseDirSearch(dsp);
5045 /* get the directory size */
5046 lock_ObtainWrite(&scp->rw);
5047 code = cm_SyncOp(scp, NULL, userp, &req, 0,
5048 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
5050 lock_ReleaseWrite(&scp->rw);
5051 cm_ReleaseSCache(scp);
5052 cm_ReleaseUser(userp);
5053 smb_FreeTran2Packet(outp);
5054 smb_DeleteDirSearch(dsp);
5055 smb_ReleaseDirSearch(dsp);
5059 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
5062 dirLength = scp->length;
5064 bufferOffset.LowPart = bufferOffset.HighPart = 0;
5065 curOffset.HighPart = 0;
5066 curOffset.LowPart = nextCookie;
5067 origOp = outp->datap;
5074 normchar_t normName[MAX_PATH]; /* Normalized name */
5075 clientchar_t cfileName[MAX_PATH]; /* Non-normalized name */
5078 if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS)
5079 /* skip over resume key */
5082 fp = (smb_tran2Find_t *) op;
5084 /* make sure that curOffset.LowPart doesn't point to the first
5085 * 32 bytes in the 2nd through last dir page, and that it doesn't
5086 * point at the first 13 32-byte chunks in the first dir page,
5087 * since those are dir and page headers, and don't contain useful
5090 temp = curOffset.LowPart & (2048-1);
5091 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
5092 /* we're in the first page */
5093 if (temp < 13*32) temp = 13*32;
5096 /* we're in a later dir page */
5097 if (temp < 32) temp = 32;
5100 /* make sure the low order 5 bits are zero */
5103 /* now put temp bits back ito curOffset.LowPart */
5104 curOffset.LowPart &= ~(2048-1);
5105 curOffset.LowPart |= temp;
5107 /* check if we've passed the dir's EOF */
5108 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength)) {
5109 osi_Log0(smb_logp, "T2 search dir passed eof");
5114 /* check if we've returned all the names that will fit in the
5115 * response packet; we check return count as well as the number
5116 * of bytes requested. We check the # of bytes after we find
5117 * the dir entry, since we'll need to check its size.
5119 if (returnedNames >= maxCount) {
5120 osi_Log2(smb_logp, "T2 search dir returnedNames %d >= maxCount %d",
5121 returnedNames, maxCount);
5125 /* when we have obtained as many entries as can be processed in
5126 * a single Bulk Status call to the file server, apply the dir listing
5129 if (returnedNames > 0 && returnedNames % AFSCBMAX == 0) {
5130 lock_ReleaseWrite(&scp->rw);
5131 code2 = smb_ApplyV3DirListPatches(scp, &dirListPatchesp, dsp->tidPath,
5132 dsp->relPath, infoLevel, userp, &req);
5133 lock_ObtainWrite(&scp->rw);
5135 /* Then check to see if we have time left to process more entries */
5136 if (GetTickCount() - req.startTime > (RDRtimeout - 15) * 1000) {
5137 osi_Log0(smb_logp, "T2 search dir RDRtimeout exceeded");
5141 /* see if we can use the bufferp we have now; compute in which
5142 * page the current offset would be, and check whether that's
5143 * the offset of the buffer we have. If not, get the buffer.
5145 thyper.HighPart = curOffset.HighPart;
5146 thyper.LowPart = curOffset.LowPart & ~(cm_data.buf_blockSize-1);
5147 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
5150 buf_Release(bufferp);
5153 lock_ReleaseWrite(&scp->rw);
5154 code = buf_Get(scp, &thyper, &bufferp);
5155 lock_ObtainWrite(&scp->rw);
5157 osi_Log2(smb_logp, "T2 search dir buf_Get scp %x failed %d", scp, code);
5161 bufferOffset = thyper;
5163 /* now get the data in the cache */
5165 code = cm_SyncOp(scp, bufferp, userp, &req,
5167 CM_SCACHESYNC_NEEDCALLBACK
5168 | CM_SCACHESYNC_READ);
5170 osi_Log2(smb_logp, "T2 search dir cm_SyncOp scp %x failed %d", scp, code);
5174 if (cm_HaveBuffer(scp, bufferp, 0)) {
5175 osi_Log2(smb_logp, "T2 search dir !HaveBuffer scp %x bufferp %x", scp, bufferp);
5176 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
5180 /* otherwise, load the buffer and try again */
5181 code = cm_GetBuffer(scp, bufferp, NULL, userp,
5183 cm_SyncOpDone(scp, bufferp, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_READ);
5185 osi_Log3(smb_logp, "T2 search dir cm_GetBuffer failed scp %x bufferp %x code %d",
5186 scp, bufferp, code);
5191 buf_Release(bufferp);
5195 } /* if (wrong buffer) ... */
5197 /* now we have the buffer containing the entry we're interested
5198 * in; copy it out if it represents a non-deleted entry.
5200 entryInDir = curOffset.LowPart & (2048-1);
5201 entryInBuffer = curOffset.LowPart & (cm_data.buf_blockSize - 1);
5203 /* page header will help tell us which entries are free. Page
5204 * header can change more often than once per buffer, since
5205 * AFS 3 dir page size may be less than (but not more than)
5206 * a buffer package buffer.
5208 /* only look intra-buffer */
5209 temp = curOffset.LowPart & (cm_data.buf_blockSize - 1);
5210 temp &= ~(2048 - 1); /* turn off intra-page bits */
5211 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
5213 /* now determine which entry we're looking at in the page.
5214 * If it is free (there's a free bitmap at the start of the
5215 * dir), we should skip these 32 bytes.
5217 slotInPage = (entryInDir & 0x7e0) >> 5;
5218 if (!(pageHeaderp->freeBitmap[slotInPage>>3] &
5219 (1 << (slotInPage & 0x7)))) {
5220 /* this entry is free */
5221 numDirChunks = 1; /* only skip this guy */
5225 tp = bufferp->datap + entryInBuffer;
5226 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
5228 /* while we're here, compute the next entry's location, too,
5229 * since we'll need it when writing out the cookie into the dir
5232 * XXXX Probably should do more sanity checking.
5234 numDirChunks = cm_NameEntries(dep->name, &onbytes);
5236 /* compute offset of cookie representing next entry */
5237 nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
5239 if (dep->fid.vnode == 0)
5240 goto nextEntry; /* This entry is not in use */
5242 cm_FsStringToClientString(dep->name, -1, cfileName, lengthof(cfileName));
5243 cm_ClientStringToNormString(cfileName, -1, normName, lengthof(normName));
5245 /* Need 8.3 name? */
5247 if (infoLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO &&
5248 !cm_Is8Dot3(cfileName)) {
5249 cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
5253 osi_Log4(smb_logp, "T2 search dir vn %u uniq %u name %S (%S)",
5254 dep->fid.vnode, dep->fid.unique,
5255 osi_LogSaveClientString(smb_logp, cfileName),
5256 NeedShortName ? osi_LogSaveClientString(smb_logp, shortName) : _C(""));
5258 /* When matching, we are using doing a case fold if we have a wildcard mask.
5259 * If we get a non-wildcard match, it's a lookup for a specific file.
5261 if (cm_MatchMask(normName, maskp, (starPattern? CM_FLAG_CASEFOLD : 0)) ||
5262 (NeedShortName && cm_MatchMask(shortName, maskp, CM_FLAG_CASEFOLD)))
5264 /* Eliminate entries that don't match requested attributes */
5265 if (smb_hideDotFiles && !(dsp->attribute & SMB_ATTR_HIDDEN) &&
5266 smb_IsDotFile(cfileName)) {
5267 osi_Log0(smb_logp, "T2 search dir skipping hidden");
5268 goto nextEntry; /* no hidden files */
5271 if (!(dsp->attribute & SMB_ATTR_DIRECTORY)) /* no directories */
5273 /* We have already done the cm_TryBulkStat above */
5274 cm_SetFid(&fid, scp->fid.cell, scp->fid.volume,
5275 ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
5276 fileType = cm_FindFileType(&fid);
5277 /* osi_Log2(smb_logp, "smb_ReceiveTran2SearchDir: file %s "
5278 * "has filetype %d", dep->name, fileType);
5280 if ( fileType == CM_SCACHETYPE_DIRECTORY ||
5281 fileType == CM_SCACHETYPE_MOUNTPOINT ||
5282 fileType == CM_SCACHETYPE_DFSLINK ||
5283 fileType == CM_SCACHETYPE_INVALID)
5284 osi_Log0(smb_logp, "T2 search dir skipping directory or bad link");
5288 /* finally check if this name will fit */
5290 smb_UnparseString(opx, NULL, cfileName, &onbytes, SMB_STRF_ANSIPATH|SMB_STRF_IGNORENUL);
5291 orbytes = ohbytes + onbytes;
5293 /* now, we round up the record to a 4 byte alignment,
5294 * and we make sure that we have enough room here for
5295 * even the aligned version (so we don't have to worry
5296 * about an overflow when we pad things out below).
5297 * That's the reason for the alignment arithmetic below.
5299 if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
5300 align = (4 - (orbytes & 3)) & 3;
5304 if (orbytes + bytesInBuffer + align > maxReturnData) {
5305 osi_Log1(smb_logp, "T2 dir search exceed max return data %d",
5310 /* this is one of the entries to use: it is not deleted
5311 * and it matches the star pattern we're looking for.
5312 * Put out the name, preceded by its length.
5314 /* First zero everything else */
5315 memset(origOp, 0, orbytes);
5318 smb_UnparseString(opx, origOp + ohbytes, cfileName, &onbytes, SMB_STRF_ANSIPATH|SMB_STRF_IGNORENUL);
5320 switch (infoLevel) {
5321 case SMB_INFO_STANDARD:
5322 fp->u.FstandardInfo.fileNameLength = onbytes;
5323 attrp = &fp->u.FstandardInfo.fileAttrs;
5326 case SMB_INFO_QUERY_EA_SIZE:
5327 fp->u.FeaSizeInfo.fileNameLength = onbytes;
5328 attrp = &fp->u.FeaSizeInfo.fileAttrs;
5329 fp->u.FeaSizeInfo.eaSize = 0;
5332 case SMB_INFO_QUERY_EAS_FROM_LIST:
5333 fp->u.FeasFromListInfo.fileNameLength = onbytes;
5334 attrp = &fp->u.FeasFromListInfo.fileAttrs;
5335 fp->u.FeasFromListInfo.eaSize = 0;
5338 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
5339 if (NeedShortName) {
5343 nchars = cm_ClientStringToUtf16(shortName, cm_ClientStrLen(shortName),
5344 fp->u.FfileBothDirectoryInfo.shortName,
5345 sizeof(fp->u.FfileBothDirectoryInfo.shortName)/sizeof(wchar_t));
5347 fp->u.FfileBothDirectoryInfo.shortNameLength = nchars*sizeof(wchar_t);
5349 fp->u.FfileBothDirectoryInfo.shortNameLength = 0;
5350 fp->u.FfileBothDirectoryInfo.reserved = 0;
5352 cm_ClientStrCpy(fp->u.FfileBothDirectoryInfo.shortName,
5353 lengthof(fp->u.FfileBothDirectoryInfo.shortName),
5355 fp->u.FfileBothDirectoryInfo.shortNameLength = cm_ClientStrLen(shortName);
5360 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
5361 fp->u.FfileFullDirectoryInfo.eaSize = 0;
5364 case SMB_FIND_FILE_DIRECTORY_INFO:
5365 fp->u.FfileDirectoryInfo.nextEntryOffset = orbytes + align;
5366 fp->u.FfileDirectoryInfo.fileIndex = nextEntryCookie;
5367 attrp = &fp->u.FfileDirectoryInfo.fileAttrs;
5368 fp->u.FfileDirectoryInfo.fileNameLength = onbytes;
5371 case SMB_FIND_FILE_NAMES_INFO:
5372 fp->u.FfileNamesInfo.nextEntryOffset = orbytes + align;
5373 fp->u.FfileNamesInfo.fileIndex = nextEntryCookie;
5374 fp->u.FfileNamesInfo.fileNameLength = onbytes;
5379 /* we shouldn't hit this case */
5380 osi_assertx(FALSE, "Unknown query type");
5383 /* now, adjust the # of entries copied */
5386 /* now we emit the attribute. This is tricky, since
5387 * we need to really stat the file to find out what
5388 * type of entry we've got. Right now, we're copying
5389 * out data from a buffer, while holding the scp
5390 * locked, so it isn't really convenient to stat
5391 * something now. We'll put in a place holder
5392 * now, and make a second pass before returning this
5393 * to get the real attributes. So, we just skip the
5394 * data for now, and adjust it later. We allocate a
5395 * patch record to make it easy to find this point
5396 * later. The replay will happen at a time when it is
5397 * safe to unlock the directory.
5399 if (infoLevel != SMB_FIND_FILE_NAMES_INFO) {
5400 osi_assert(attrp != NULL);
5401 curPatchp = malloc(sizeof(*curPatchp));
5402 osi_QAdd((osi_queue_t **) &dirListPatchesp, &curPatchp->q);
5403 curPatchp->dptr = attrp;
5405 if (smb_hideDotFiles && smb_IsDotFile(cfileName)) {
5406 curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
5408 curPatchp->flags = 0;
5411 cm_SetFid(&curPatchp->fid, scp->fid.cell, scp->fid.volume, ntohl(dep->fid.vnode), ntohl(dep->fid.unique));
5414 curPatchp->dep = dep;
5417 if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS)
5418 /* put out resume key */
5419 *((u_long *)origOp) = nextEntryCookie;
5421 /* Adjust byte ptr and count */
5422 origOp += orbytes; /* skip entire record */
5423 bytesInBuffer += orbytes;
5425 /* and pad the record out */
5426 while (align-- > 0) {
5430 } /* if we're including this name */
5431 else if (!starPattern &&
5433 cm_MatchMask(normName, maskp, CM_FLAG_CASEFOLD)) {
5434 /* We were looking for exact matches, but here's an inexact one*/
5439 /* and adjust curOffset to be where the new cookie is */
5440 thyper.HighPart = 0;
5441 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
5442 curOffset = LargeIntegerAdd(thyper, curOffset);
5443 } /* while copying data for dir listing */
5445 /* If we didn't get a star pattern, we did an exact match during the first pass.
5446 * If there were no exact matches found, we fail over to inexact matches by
5447 * marking the query as a star pattern (matches all case permutations), and
5448 * re-running the query.
5450 if (returnedNames == 0 && !starPattern && foundInexact) {
5451 osi_Log0(smb_logp,"T2 Search: No exact matches. Re-running for inexact matches");
5456 /* release the mutex */
5457 lock_ReleaseWrite(&scp->rw);
5459 buf_Release(bufferp);
5464 * Finally, process whatever entries we have left.
5466 code2 = smb_ApplyV3DirListPatches(scp, &dirListPatchesp, dsp->tidPath,
5467 dsp->relPath, infoLevel, userp, &req);
5469 /* now put out the final parameters */
5470 if (returnedNames == 0)
5472 if (p->opcode == 1) {
5474 outp->parmsp[0] = (unsigned short) dsp->cookie;
5475 outp->parmsp[1] = returnedNames;
5476 outp->parmsp[2] = eos;
5477 outp->parmsp[3] = 0; /* nothing wrong with EAS */
5478 outp->parmsp[4] = 0;
5479 /* don't need last name to continue
5480 * search, cookie is enough. Normally,
5481 * this is the offset of the file name
5482 * of the last entry returned.
5484 outp->totalParms = 10; /* in bytes */
5488 outp->parmsp[0] = returnedNames;
5489 outp->parmsp[1] = eos;
5490 outp->parmsp[2] = 0; /* EAS error */
5491 outp->parmsp[3] = 0; /* last name, as above */
5492 outp->totalParms = 8; /* in bytes */
5495 /* return # of bytes in the buffer */
5496 outp->totalData = bytesInBuffer;
5498 /* Return error code if unsuccessful on first request */
5499 if (code == 0 && p->opcode == 1 && returnedNames == 0)
5500 code = CM_ERROR_NOSUCHFILE;
5502 osi_Log4(smb_logp, "T2 search dir done, opcode %d, id %d, %d names, code %d",
5503 p->opcode, dsp->cookie, returnedNames, code);
5505 /* if we're supposed to close the search after this request, or if
5506 * we're supposed to close the search if we're done, and we're done,
5507 * or if something went wrong, close the search.
5509 if ((searchFlags & TRAN2_FIND_FLAG_CLOSE_SEARCH) ||
5510 (returnedNames == 0) ||
5511 ((searchFlags & TRAN2_FIND_FLAG_CLOSE_SEARCH_IF_END) && eos) ||
5513 smb_DeleteDirSearch(dsp);
5516 smb_SendTran2Error(vcp, p, opx, code);
5518 smb_SendTran2Packet(vcp, outp, opx);
5520 smb_FreeTran2Packet(outp);
5521 smb_ReleaseDirSearch(dsp);
5522 cm_ReleaseSCache(scp);
5523 cm_ReleaseUser(userp);
5527 /* SMB_COM_FIND_CLOSE2 */
5528 long smb_ReceiveV3FindClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
5531 smb_dirSearch_t *dsp;
5533 dirHandle = smb_GetSMBParm(inp, 0);
5535 osi_Log1(smb_logp, "SMB3 find close handle %d", dirHandle);
5537 dsp = smb_FindDirSearch(dirHandle);
5540 return CM_ERROR_BADFD;
5542 /* otherwise, we have an FD to destroy */
5543 smb_DeleteDirSearch(dsp);
5544 smb_ReleaseDirSearch(dsp);
5546 /* and return results */
5547 smb_SetSMBDataLength(outp, 0);
5553 /* SMB_COM_FIND_NOTIFY_CLOSE */
5554 long smb_ReceiveV3FindNotifyClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
5556 smb_SetSMBDataLength(outp, 0);
5560 /* SMB_COM_OPEN_ANDX */
5561 long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
5563 clientchar_t *pathp;
5568 cm_scache_t *dscp; /* dir we're dealing with */
5569 cm_scache_t *scp; /* file we're creating */
5571 int initialModeBits;
5574 clientchar_t *lastNamep;
5575 unsigned long dosTime;
5581 int parmSlot; /* which parm we're dealing with */
5582 clientchar_t *tidPathp;
5590 extraInfo = (smb_GetSMBParm(inp, 2) & 1); /* return extra info */
5591 openFun = smb_GetSMBParm(inp, 8); /* open function */
5592 excl = ((openFun & 3) == 0);
5593 trunc = ((openFun & 3) == 2); /* truncate it */
5594 openMode = (smb_GetSMBParm(inp, 3) & 0x7);
5595 openAction = 0; /* tracks what we did */
5597 attributes = smb_GetSMBParm(inp, 5);
5598 dosTime = smb_GetSMBParm(inp, 6) | (smb_GetSMBParm(inp, 7) << 16);
5600 /* compute initial mode bits based on read-only flag in attributes */
5601 initialModeBits = 0666;
5602 if (attributes & SMB_ATTR_READONLY)
5603 initialModeBits &= ~0222;
5605 pathp = smb_ParseASCIIBlock(inp, smb_GetSMBData(inp, NULL), NULL,
5608 spacep = inp->spacep;
5609 smb_StripLastComponent(spacep->wdata, &lastNamep, pathp);
5612 (cm_ClientStrCmpIA(lastNamep, _C(SMB_IOCTL_FILENAME)) == 0 ||
5613 cm_ClientStrCmpIA(lastNamep, _C("\\srvsvc")) == 0 ||
5614 cm_ClientStrCmpIA(lastNamep, _C("\\wkssvc")) == 0 ||
5615 cm_ClientStrCmpIA(lastNamep, _C("ipc$")) == 0)) {
5616 /* special case magic file name for receiving IOCTL requests
5617 * (since IOCTL calls themselves aren't getting through).
5620 osi_Log0(smb_logp, "IOCTL Open");
5623 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
5624 smb_SetupIoctlFid(fidp, spacep);
5626 /* set inp->fid so that later read calls in same msg can find fid */
5627 inp->fid = fidp->fid;
5629 /* copy out remainder of the parms */
5631 smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
5633 smb_SetSMBParm(outp, parmSlot, /* attrs */ 0); parmSlot++;
5634 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* mod time */
5635 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
5636 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* len */
5637 smb_SetSMBParm(outp, parmSlot, 0x7fff); parmSlot++;
5638 smb_SetSMBParm(outp, parmSlot, openMode); parmSlot++;
5639 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* file type 0 ==> normal file or dir */
5640 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* IPC junk */
5642 /* and the final "always present" stuff */
5643 smb_SetSMBParm(outp, parmSlot, /* openAction found existing file */ 1); parmSlot++;
5644 /* next write out the "unique" ID */
5645 smb_SetSMBParm(outp, parmSlot, 0x1234); parmSlot++;
5646 smb_SetSMBParm(outp, parmSlot, 0x5678); parmSlot++;
5647 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
5648 smb_SetSMBDataLength(outp, 0);
5650 /* and clean up fid reference */
5651 smb_ReleaseFID(fidp);
5655 #ifdef DEBUG_VERBOSE
5657 char *hexp, *asciip;
5658 asciip = (lastNamep ? lastNamep : pathp );
5659 hexp = osi_HexifyString(asciip);
5660 DEBUG_EVENT2("AFS", "V3Open H[%s] A[%s]", hexp, asciip );
5664 userp = smb_GetUserFromVCP(vcp, inp);
5667 code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
5669 cm_ReleaseUser(userp);
5670 return CM_ERROR_NOSUCHPATH;
5672 code = cm_NameI(cm_data.rootSCachep, pathp,
5673 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
5674 userp, tidPathp, &req, &scp);
5677 if (code == 0 && scp->fileType == CM_SCACHETYPE_DFSLINK) {
5678 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, pathp);
5679 cm_ReleaseSCache(scp);
5680 cm_ReleaseUser(userp);
5681 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
5682 return CM_ERROR_PATH_NOT_COVERED;
5684 return CM_ERROR_BADSHARENAME;
5686 #endif /* DFS_SUPPORT */
5689 code = cm_NameI(cm_data.rootSCachep, spacep->wdata,
5690 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
5691 userp, tidPathp, &req, &dscp);
5693 cm_ReleaseUser(userp);
5698 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
5699 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp,
5701 cm_ReleaseSCache(dscp);
5702 cm_ReleaseUser(userp);
5703 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
5704 return CM_ERROR_PATH_NOT_COVERED;
5706 return CM_ERROR_BADSHARENAME;
5708 #endif /* DFS_SUPPORT */
5709 /* otherwise, scp points to the parent directory. Do a lookup,
5710 * and truncate the file if we find it, otherwise we create the
5717 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD, userp,
5719 if (code && code != CM_ERROR_NOSUCHFILE && code != CM_ERROR_BPLUS_NOMATCH) {
5720 cm_ReleaseSCache(dscp);
5721 cm_ReleaseUser(userp);
5726 /* if we get here, if code is 0, the file exists and is represented by
5727 * scp. Otherwise, we have to create it. The dir may be represented
5728 * by dscp, or we may have found the file directly. If code is non-zero,
5732 code = cm_CheckOpen(scp, openMode, trunc, userp, &req);
5734 if (dscp) cm_ReleaseSCache(dscp);
5735 cm_ReleaseSCache(scp);
5736 cm_ReleaseUser(userp);
5741 /* oops, file shouldn't be there */
5743 cm_ReleaseSCache(dscp);
5744 cm_ReleaseSCache(scp);
5745 cm_ReleaseUser(userp);
5746 return CM_ERROR_EXISTS;
5750 setAttr.mask = CM_ATTRMASK_LENGTH;
5751 setAttr.length.LowPart = 0;
5752 setAttr.length.HighPart = 0;
5753 code = cm_SetAttr(scp, &setAttr, userp, &req);
5754 openAction = 3; /* truncated existing file */
5756 else openAction = 1; /* found existing file */
5758 else if (!(openFun & SMB_ATTR_DIRECTORY)) {
5759 /* don't create if not found */
5760 if (dscp) cm_ReleaseSCache(dscp);
5761 cm_ReleaseUser(userp);
5762 return CM_ERROR_NOSUCHFILE;
5765 osi_assertx(dscp != NULL, "null cm_scache_t");
5766 osi_Log1(smb_logp, "smb_ReceiveV3OpenX creating file %S",
5767 osi_LogSaveClientString(smb_logp, lastNamep));
5768 openAction = 2; /* created file */
5769 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
5770 smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
5771 code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
5775 if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
5776 smb_NotifyChange(FILE_ACTION_ADDED,
5777 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION,
5778 dscp, lastNamep, NULL, TRUE);
5779 } else if (!excl && code == CM_ERROR_EXISTS) {
5780 /* not an exclusive create, and someone else tried
5781 * creating it already, then we open it anyway. We
5782 * don't bother retrying after this, since if this next
5783 * fails, that means that the file was deleted after we
5784 * started this call.
5786 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
5790 setAttr.mask = CM_ATTRMASK_LENGTH;
5791 setAttr.length.LowPart = 0;
5792 setAttr.length.HighPart = 0;
5793 code = cm_SetAttr(scp, &setAttr, userp, &req);
5795 } /* lookup succeeded */
5799 /* we don't need this any longer */
5801 cm_ReleaseSCache(dscp);
5804 /* something went wrong creating or truncating the file */
5806 cm_ReleaseSCache(scp);
5807 cm_ReleaseUser(userp);
5811 /* make sure we're about to open a file */
5812 if (scp->fileType != CM_SCACHETYPE_FILE) {
5813 cm_ReleaseSCache(scp);
5814 cm_ReleaseUser(userp);
5815 return CM_ERROR_ISDIR;
5818 /* now all we have to do is open the file itself */
5819 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
5820 osi_assertx(fidp, "null smb_fid_t");
5823 lock_ObtainMutex(&fidp->mx);
5824 /* save a pointer to the vnode */
5826 lock_ObtainWrite(&scp->rw);
5827 scp->flags |= CM_SCACHEFLAG_SMB_FID;
5828 lock_ReleaseWrite(&scp->rw);
5829 osi_Log2(smb_logp,"smb_ReceiveV3OpenX fidp 0x%p scp 0x%p", fidp, scp);
5831 fidp->userp = userp;
5833 /* compute open mode */
5835 fidp->flags |= SMB_FID_OPENREAD_LISTDIR;
5836 if (openMode == 1 || openMode == 2)
5837 fidp->flags |= SMB_FID_OPENWRITE;
5839 /* remember if the file was newly created */
5841 fidp->flags |= SMB_FID_CREATED;
5843 lock_ReleaseMutex(&fidp->mx);
5844 smb_ReleaseFID(fidp);
5846 cm_Open(scp, 0, userp);
5848 /* set inp->fid so that later read calls in same msg can find fid */
5849 inp->fid = fidp->fid;
5851 /* copy out remainder of the parms */
5853 smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
5854 lock_ObtainRead(&scp->rw);
5856 smb_SetSMBParm(outp, parmSlot, smb_Attributes(scp)); parmSlot++;
5857 smb_DosUTimeFromUnixTime(&dosTime, scp->clientModTime);
5858 smb_SetSMBParm(outp, parmSlot, dosTime & 0xffff); parmSlot++;
5859 smb_SetSMBParm(outp, parmSlot, (dosTime>>16) & 0xffff); parmSlot++;
5860 smb_SetSMBParm(outp, parmSlot, scp->length.LowPart & 0xffff); parmSlot++;
5861 smb_SetSMBParm(outp, parmSlot, (scp->length.LowPart >> 16) & 0xffff); parmSlot++;
5862 smb_SetSMBParm(outp, parmSlot, openMode); parmSlot++;
5863 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* file type 0 ==> normal file or dir */
5864 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* IPC junk */
5866 /* and the final "always present" stuff */
5867 smb_SetSMBParm(outp, parmSlot, openAction); parmSlot++;
5868 /* next write out the "unique" ID */
5869 smb_SetSMBParm(outp, parmSlot, scp->fid.vnode & 0xffff); parmSlot++;
5870 smb_SetSMBParm(outp, parmSlot, scp->fid.volume & 0xffff); parmSlot++;
5871 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++;
5872 lock_ReleaseRead(&scp->rw);
5873 smb_SetSMBDataLength(outp, 0);
5875 osi_Log1(smb_logp, "SMB OpenX opening fid %d", fidp->fid);
5877 cm_ReleaseUser(userp);
5878 /* leave scp held since we put it in fidp->scp */
5882 static void smb_GetLockParams(unsigned char LockType,
5884 unsigned int * ppid,
5885 LARGE_INTEGER * pOffset,
5886 LARGE_INTEGER * pLength)
5888 if (LockType & LOCKING_ANDX_LARGE_FILES) {
5890 *ppid = *((USHORT *) *buf);
5891 pOffset->HighPart = *((LONG *)(*buf + 4));
5892 pOffset->LowPart = *((DWORD *)(*buf + 8));
5893 pLength->HighPart = *((LONG *)(*buf + 12));
5894 pLength->LowPart = *((DWORD *)(*buf + 16));
5898 /* Not Large Files */
5899 *ppid = *((USHORT *) *buf);
5900 pOffset->HighPart = 0;
5901 pOffset->LowPart = *((DWORD *)(*buf + 2));
5902 pLength->HighPart = 0;
5903 pLength->LowPart = *((DWORD *)(*buf + 6));
5908 /* SMB_COM_LOCKING_ANDX */
5909 long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
5916 unsigned char LockType;
5917 unsigned short NumberOfUnlocks, NumberOfLocks;
5921 LARGE_INTEGER LOffset, LLength;
5922 smb_waitingLockRequest_t *wlRequest = NULL;
5923 cm_file_lock_t *lockp;
5931 fid = smb_GetSMBParm(inp, 2);
5932 fid = smb_ChainFID(fid, inp);
5934 fidp = smb_FindFID(vcp, fid, 0);
5936 return CM_ERROR_BADFD;
5938 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
5939 smb_CloseFID(vcp, fidp, NULL, 0);
5940 smb_ReleaseFID(fidp);
5941 return CM_ERROR_NOSUCHFILE;
5944 lock_ObtainMutex(&fidp->mx);
5945 if (fidp->flags & SMB_FID_IOCTL) {
5946 osi_Log0(smb_logp, "smb_ReceiveV3Locking BadFD");
5947 lock_ReleaseMutex(&fidp->mx);
5948 smb_ReleaseFID(fidp);
5949 return CM_ERROR_BADFD;
5952 osi_Log2(smb_logp,"smb_ReceiveV3LockingX fidp 0x%p scp 0x%p", fidp, scp);
5954 lock_ReleaseMutex(&fidp->mx);
5956 /* set inp->fid so that later read calls in same msg can find fid */
5959 userp = smb_GetUserFromVCP(vcp, inp);
5962 lock_ObtainWrite(&scp->rw);
5963 code = cm_SyncOp(scp, NULL, userp, &req, 0,
5964 CM_SCACHESYNC_NEEDCALLBACK
5965 | CM_SCACHESYNC_GETSTATUS
5966 | CM_SCACHESYNC_LOCK);
5968 osi_Log1(smb_logp, "smb_ReceiveV3Locking SyncOp failure code 0x%x", code);
5972 LockType = smb_GetSMBParm(inp, 3) & 0xff;
5973 Timeout = (smb_GetSMBParm(inp, 5) << 16) + smb_GetSMBParm(inp, 4);
5974 NumberOfUnlocks = smb_GetSMBParm(inp, 6);
5975 NumberOfLocks = smb_GetSMBParm(inp, 7);
5977 if (!(fidp->flags & SMB_FID_OPENWRITE) &&
5978 !(LockType & LOCKING_ANDX_SHARED_LOCK)) {
5979 /* somebody wants exclusive locks on a file that they only
5980 opened for reading. We downgrade this to a shared lock. */
5981 osi_Log0(smb_logp, "smb_ReceiveV3Locking reinterpreting exclusive lock as shared for read-only fid");
5982 LockType |= LOCKING_ANDX_SHARED_LOCK;
5985 if (LockType & LOCKING_ANDX_CHANGE_LOCKTYPE) {
5986 /* AFS does not support atomic changes of lock types from read or write and vice-versa */
5987 osi_Log0(smb_logp, "smb_ReceiveV3Locking received unsupported request [LOCKING_ANDX_CHANGE_LOCKTYPE]");
5988 code = CM_ERROR_BADOP;
5993 op = smb_GetSMBData(inp, NULL);
5995 if (LockType & LOCKING_ANDX_CANCEL_LOCK) {
5996 /* Cancel outstanding lock requests */
5997 smb_waitingLock_t * wl;
5999 for (i=0; i<NumberOfLocks; i++) {
6000 smb_GetLockParams(LockType, &op, &pid, &LOffset, &LLength);
6002 key = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
6004 lock_ObtainWrite(&smb_globalLock);
6005 for (wlRequest = smb_allWaitingLocks; wlRequest; wlRequest = (smb_waitingLockRequest_t *) osi_QNext(&wlRequest->q))
6007 for (wl = wlRequest->locks; wl; wl = (smb_waitingLock_t *) osi_QNext(&wl->q)) {
6008 if (cm_KeyEquals(&wl->key, &key, 0) && LargeIntegerEqualTo(wl->LOffset, LOffset) &&
6009 LargeIntegerEqualTo(wl->LLength, LLength)) {
6010 wl->state = SMB_WAITINGLOCKSTATE_CANCELLED;
6011 goto found_lock_request;
6016 lock_ReleaseWrite(&smb_globalLock);
6019 smb_SetSMBDataLength(outp, 0);
6024 for (i=0; i<NumberOfUnlocks; i++) {
6025 smb_GetLockParams(LockType, &op, &pid, &LOffset, &LLength);
6027 key = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
6029 code = cm_Unlock(scp, LockType, LOffset, LLength, key, userp, &req);
6037 for (i=0; i<NumberOfLocks; i++) {
6038 smb_GetLockParams(LockType, &op, &pid, &LOffset, &LLength);
6040 key = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
6042 code = cm_Lock(scp, LockType, LOffset, LLength, key, (Timeout != 0),
6043 userp, &req, &lockp);
6045 if (code == CM_ERROR_NOACCESS && LockType == LockWrite &&
6046 (fidp->flags & (SMB_FID_OPENREAD_LISTDIR | SMB_FID_OPENWRITE)) == SMB_FID_OPENREAD_LISTDIR)
6048 code = cm_Lock(scp, LockRead, LOffset, LLength, key, (Timeout != 0),
6049 userp, &req, &lockp);
6052 if (code == CM_ERROR_WOULDBLOCK && Timeout != 0) {
6053 smb_waitingLock_t * wLock;
6055 /* Put on waiting list */
6056 if(wlRequest == NULL) {
6060 LARGE_INTEGER tOffset, tLength;
6062 wlRequest = malloc(sizeof(smb_waitingLockRequest_t));
6064 osi_assertx(wlRequest != NULL, "null wlRequest");
6066 wlRequest->vcp = vcp;
6068 wlRequest->scp = scp;
6069 osi_Log2(smb_logp,"smb_ReceiveV3LockingX wlRequest 0x%p scp 0x%p", wlRequest, scp);
6071 wlRequest->inp = smb_CopyPacket(inp);
6072 wlRequest->outp = smb_CopyPacket(outp);
6073 wlRequest->lockType = LockType;
6074 wlRequest->msTimeout = Timeout;
6075 wlRequest->start_t = osi_Time();
6076 wlRequest->locks = NULL;
6078 /* The waiting lock request needs to have enough
6079 information to undo all the locks in the request.
6080 We do the following to store info about locks that
6081 have already been granted. Sure, we can get most
6082 of the info from the packet, but the packet doesn't
6083 hold the result of cm_Lock call. In practice we
6084 only receive packets with one or two locks, so we
6085 are only wasting a few bytes here and there and
6086 only for a limited period of time until the waiting
6087 lock times out or is freed. */
6089 for(opt = op_locks, j=i; j > 0; j--) {
6090 smb_GetLockParams(LockType, &opt, &pid, &tOffset, &tLength);
6092 tkey = cm_GenerateKey(vcp->vcID, pid, fidp->fid);
6094 wLock = malloc(sizeof(smb_waitingLock_t));
6096 osi_assertx(wLock != NULL, "null smb_waitingLock_t");
6099 wLock->LOffset = tOffset;
6100 wLock->LLength = tLength;
6101 wLock->lockp = NULL;
6102 wLock->state = SMB_WAITINGLOCKSTATE_DONE;
6103 osi_QAdd((osi_queue_t **) &wlRequest->locks,
6108 wLock = malloc(sizeof(smb_waitingLock_t));
6110 osi_assertx(wLock != NULL, "null smb_waitingLock_t");
6113 wLock->LOffset = LOffset;
6114 wLock->LLength = LLength;
6115 wLock->lockp = lockp;
6116 wLock->state = SMB_WAITINGLOCKSTATE_WAITING;
6117 osi_QAdd((osi_queue_t **) &wlRequest->locks,
6120 osi_Log1(smb_logp, "smb_ReceiveV3Locking WaitingLock created 0x%p",
6128 osi_Log1(smb_logp, "smb_ReceiveV3Locking cm_Lock failure code 0x%x", code);
6135 /* Since something went wrong with the lock number i, we now
6136 have to go ahead and release any locks acquired before the
6137 failure. All locks before lock number i (of which there
6138 are i of them) have either been successful or are waiting.
6139 Either case requires calling cm_Unlock(). */
6141 /* And purge the waiting lock */
6142 if(wlRequest != NULL) {
6143 smb_waitingLock_t * wl;
6144 smb_waitingLock_t * wlNext;
6147 for(wl = wlRequest->locks; wl; wl = wlNext) {
6149 wlNext = (smb_waitingLock_t *) osi_QNext(&wl->q);
6151 ul_code = cm_Unlock(scp, LockType, wl->LOffset, wl->LLength, wl->key, userp, &req);
6154 osi_Log1(smb_logp, "smb_ReceiveV3Locking cm_Unlock returns code %d", ul_code);
6156 osi_Log0(smb_logp, "smb_ReceiveV3Locking cm_Unlock successful");
6159 osi_QRemove((osi_queue_t **) &wlRequest->locks, &wl->q);
6164 smb_ReleaseVC(wlRequest->vcp);
6165 cm_ReleaseSCache(wlRequest->scp);
6166 smb_FreePacket(wlRequest->inp);
6167 smb_FreePacket(wlRequest->outp);
6176 if (wlRequest != NULL) {
6178 lock_ObtainWrite(&smb_globalLock);
6179 osi_QAdd((osi_queue_t **)&smb_allWaitingLocks,
6181 osi_Wakeup((LONG_PTR)&smb_allWaitingLocks);
6182 lock_ReleaseWrite(&smb_globalLock);
6184 /* don't send reply immediately */
6185 outp->flags |= SMB_PACKETFLAG_NOSEND;
6188 smb_SetSMBDataLength(outp, 0);
6192 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_LOCK);
6195 lock_ReleaseWrite(&scp->rw);
6196 cm_ReleaseSCache(scp);
6197 cm_ReleaseUser(userp);
6198 smb_ReleaseFID(fidp);
6203 /* SMB_COM_QUERY_INFORMATION2 */
6204 long smb_ReceiveV3GetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
6210 afs_uint32 searchTime;
6217 fid = smb_GetSMBParm(inp, 0);
6218 fid = smb_ChainFID(fid, inp);
6220 fidp = smb_FindFID(vcp, fid, 0);
6222 return CM_ERROR_BADFD;
6224 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
6225 smb_CloseFID(vcp, fidp, NULL, 0);
6226 smb_ReleaseFID(fidp);
6227 return CM_ERROR_NOSUCHFILE;
6230 lock_ObtainMutex(&fidp->mx);
6231 if (fidp->flags & SMB_FID_IOCTL) {
6232 lock_ReleaseMutex(&fidp->mx);
6233 smb_ReleaseFID(fidp);
6234 return CM_ERROR_BADFD;
6237 osi_Log2(smb_logp,"smb_ReceiveV3GetAttributes fidp 0x%p scp 0x%p", fidp, scp);
6239 lock_ReleaseMutex(&fidp->mx);
6241 userp = smb_GetUserFromVCP(vcp, inp);
6244 /* otherwise, stat the file */
6245 lock_ObtainWrite(&scp->rw);
6246 code = cm_SyncOp(scp, NULL, userp, &req, 0,
6247 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
6251 cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
6253 lock_ConvertWToR(&scp->rw);
6256 /* decode times. We need a search time, but the response to this
6257 * call provides the date first, not the time, as returned in the
6258 * searchTime variable. So we take the high-order bits first.
6260 smb_SearchTimeFromUnixTime(&searchTime, scp->clientModTime);
6261 smb_SetSMBParm(outp, 0, (searchTime >> 16) & 0xffff); /* ctime */
6262 smb_SetSMBParm(outp, 1, searchTime & 0xffff);
6263 smb_SetSMBParm(outp, 2, (searchTime >> 16) & 0xffff); /* atime */
6264 smb_SetSMBParm(outp, 3, searchTime & 0xffff);
6265 smb_SetSMBParm(outp, 4, (searchTime >> 16) & 0xffff); /* mtime */
6266 smb_SetSMBParm(outp, 5, searchTime & 0xffff);
6268 /* now handle file size and allocation size */
6269 smb_SetSMBParm(outp, 6, scp->length.LowPart & 0xffff); /* file size */
6270 smb_SetSMBParm(outp, 7, (scp->length.LowPart >> 16) & 0xffff);
6271 smb_SetSMBParm(outp, 8, scp->length.LowPart & 0xffff); /* alloc size */
6272 smb_SetSMBParm(outp, 9, (scp->length.LowPart >> 16) & 0xffff);
6274 /* file attribute */
6275 smb_SetSMBParm(outp, 10, smb_Attributes(scp));
6277 /* and finalize stuff */
6278 smb_SetSMBDataLength(outp, 0);
6283 lock_ReleaseRead(&scp->rw);
6285 lock_ReleaseWrite(&scp->rw);
6286 cm_ReleaseSCache(scp);
6287 cm_ReleaseUser(userp);
6288 smb_ReleaseFID(fidp);
6292 /* SMB_COM_SET_INFORMATION2 */
6293 long smb_ReceiveV3SetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
6299 afs_uint32 searchTime;
6307 fid = smb_GetSMBParm(inp, 0);
6308 fid = smb_ChainFID(fid, inp);
6310 fidp = smb_FindFID(vcp, fid, 0);
6312 return CM_ERROR_BADFD;
6314 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
6315 smb_CloseFID(vcp, fidp, NULL, 0);
6316 smb_ReleaseFID(fidp);
6317 return CM_ERROR_NOSUCHFILE;
6320 lock_ObtainMutex(&fidp->mx);
6321 if (fidp->flags & SMB_FID_IOCTL) {
6322 lock_ReleaseMutex(&fidp->mx);
6323 smb_ReleaseFID(fidp);
6324 return CM_ERROR_BADFD;
6327 osi_Log2(smb_logp,"smb_ReceiveV3SetAttributes fidp 0x%p scp 0x%p", fidp, scp);
6329 lock_ReleaseMutex(&fidp->mx);
6331 userp = smb_GetUserFromVCP(vcp, inp);
6334 /* now prepare to call cm_setattr. This message only sets various times,
6335 * and AFS only implements mtime, and we'll set the mtime if that's
6336 * requested. The others we'll ignore.
6338 searchTime = smb_GetSMBParm(inp, 5) | (smb_GetSMBParm(inp, 6) << 16);
6340 if (searchTime != 0) {
6341 smb_UnixTimeFromSearchTime(&unixTime, searchTime);
6343 if ( unixTime != -1 ) {
6344 attrs.mask = CM_ATTRMASK_CLIENTMODTIME;
6345 attrs.clientModTime = unixTime;
6346 code = cm_SetAttr(scp, &attrs, userp, &req);
6348 osi_Log1(smb_logp, "SMB receive V3SetAttributes [fid=%ld]", fid);
6350 osi_Log1(smb_logp, "**smb_UnixTimeFromSearchTime failed searchTime=%ld", searchTime);
6356 cm_ReleaseSCache(scp);
6357 cm_ReleaseUser(userp);
6358 smb_ReleaseFID(fidp);
6362 /* SMB_COM_WRITE_ANDX */
6363 long smb_ReceiveV3WriteX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
6366 long count, written = 0, total_written = 0;
6370 smb_t *smbp = (smb_t*) inp;
6374 int inDataBlockCount;
6376 fd = smb_GetSMBParm(inp, 2);
6377 count = smb_GetSMBParm(inp, 10);
6379 offset.HighPart = 0;
6380 offset.LowPart = smb_GetSMBParm(inp, 3) | (smb_GetSMBParm(inp, 4) << 16);
6382 if (*inp->wctp == 14) {
6383 /* we have a request with 64-bit file offsets */
6384 #ifdef AFS_LARGEFILES
6385 offset.HighPart = smb_GetSMBParm(inp, 12) | (smb_GetSMBParm(inp, 13) << 16);
6387 if ((smb_GetSMBParm(inp, 12) | (smb_GetSMBParm(inp, 13) << 16)) != 0) {
6389 osi_Log0(smb_logp, "smb_ReceiveV3WriteX offset requires largefile support");
6390 /* we shouldn't have received this op if we didn't specify
6391 largefile support */
6392 return CM_ERROR_BADOP;
6397 op = inp->data + smb_GetSMBParm(inp, 11);
6398 inDataBlockCount = count;
6400 osi_Log4(smb_logp, "smb_ReceiveV3WriteX fid %d, off 0x%x:%08x, size 0x%x",
6401 fd, offset.HighPart, offset.LowPart, count);
6403 fd = smb_ChainFID(fd, inp);
6404 fidp = smb_FindFID(vcp, fd, 0);
6406 return CM_ERROR_BADFD;
6408 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
6409 smb_CloseFID(vcp, fidp, NULL, 0);
6410 smb_ReleaseFID(fidp);
6411 return CM_ERROR_NOSUCHFILE;
6414 lock_ObtainMutex(&fidp->mx);
6415 if (fidp->flags & SMB_FID_IOCTL) {
6416 lock_ReleaseMutex(&fidp->mx);
6417 code = smb_IoctlV3Write(fidp, vcp, inp, outp);
6418 smb_ReleaseFID(fidp);
6421 lock_ReleaseMutex(&fidp->mx);
6422 userp = smb_GetUserFromVCP(vcp, inp);
6424 /* special case: 0 bytes transferred means there is no data
6425 transferred. A slight departure from SMB_COM_WRITE where this
6426 means that we are supposed to truncate the file at this
6431 LARGE_INTEGER LOffset;
6432 LARGE_INTEGER LLength;
6436 key = cm_GenerateKey(vcp->vcID, pid, fd);
6438 LOffset.HighPart = offset.HighPart;
6439 LOffset.LowPart = offset.LowPart;
6440 LLength.HighPart = 0;
6441 LLength.LowPart = count;
6444 lock_ObtainWrite(&scp->rw);
6445 code = cm_LockCheckWrite(scp, LOffset, LLength, key);
6446 lock_ReleaseWrite(&scp->rw);
6453 * Work around bug in NT client
6455 * When copying a file, the NT client should first copy the data,
6456 * then copy the last write time. But sometimes the NT client does
6457 * these in the wrong order, so the data copies would inadvertently
6458 * cause the last write time to be overwritten. We try to detect this,
6459 * and don't set client mod time if we think that would go against the
6462 lock_ObtainMutex(&fidp->mx);
6463 if ((fidp->flags & SMB_FID_MTIMESETDONE) != SMB_FID_MTIMESETDONE) {
6464 fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
6465 fidp->scp->clientModTime = time(NULL);
6467 lock_ReleaseMutex(&fidp->mx);
6470 while ( code == 0 && count > 0 ) {
6471 code = smb_WriteData(fidp, &offset, count, op, userp, &written);
6472 if (code == 0 && written == 0)
6473 code = CM_ERROR_PARTIALWRITE;
6475 offset = LargeIntegerAdd(offset,
6476 ConvertLongToLargeInteger(written));
6478 total_written += written;
6482 /* slots 0 and 1 are reserved for request chaining and will be
6483 filled in when we return. */
6484 smb_SetSMBParm(outp, 2, total_written);
6485 smb_SetSMBParm(outp, 3, 0); /* reserved */
6486 smb_SetSMBParm(outp, 4, 0); /* reserved */
6487 smb_SetSMBParm(outp, 5, 0); /* reserved */
6488 smb_SetSMBDataLength(outp, 0);
6491 cm_ReleaseUser(userp);
6492 smb_ReleaseFID(fidp);
6497 /* SMB_COM_READ_ANDX */
6498 long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
6502 long finalCount = 0;
6506 smb_t *smbp = (smb_t*) inp;
6512 fd = smb_GetSMBParm(inp, 2);
6513 count = smb_GetSMBParm(inp, 5);
6514 offset.LowPart = smb_GetSMBParm(inp, 3) | (smb_GetSMBParm(inp, 4) << 16);
6516 if (*inp->wctp == 12) {
6517 /* a request with 64-bit offsets */
6518 #ifdef AFS_LARGEFILES
6519 offset.HighPart = smb_GetSMBParm(inp, 10) | (smb_GetSMBParm(inp, 11) << 16);
6521 if (LargeIntegerLessThanZero(offset)) {
6522 osi_Log2(smb_logp, "smb_ReceiveV3Read offset too large (0x%x:%08x)",
6523 offset.HighPart, offset.LowPart);
6524 return CM_ERROR_BADSMB;
6527 if ((smb_GetSMBParm(inp, 10) | (smb_GetSMBParm(inp, 11) << 16)) != 0) {
6528 osi_Log0(smb_logp, "smb_ReceiveV3Read offset is 64-bit. Dropping");
6529 return CM_ERROR_BADSMB;
6531 offset.HighPart = 0;
6535 offset.HighPart = 0;
6538 osi_Log4(smb_logp, "smb_ReceiveV3Read fd %d, off 0x%x:%08x, size 0x%x",
6539 fd, offset.HighPart, offset.LowPart, count);
6541 fd = smb_ChainFID(fd, inp);
6542 fidp = smb_FindFID(vcp, fd, 0);
6544 return CM_ERROR_BADFD;
6547 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
6548 smb_CloseFID(vcp, fidp, NULL, 0);
6549 smb_ReleaseFID(fidp);
6550 return CM_ERROR_NOSUCHFILE;
6554 key = cm_GenerateKey(vcp->vcID, pid, fd);
6556 LARGE_INTEGER LOffset, LLength;
6559 LOffset.HighPart = offset.HighPart;
6560 LOffset.LowPart = offset.LowPart;
6561 LLength.HighPart = 0;
6562 LLength.LowPart = count;
6565 lock_ObtainWrite(&scp->rw);
6566 code = cm_LockCheckRead(scp, LOffset, LLength, key);
6567 lock_ReleaseWrite(&scp->rw);
6571 smb_ReleaseFID(fidp);
6575 /* set inp->fid so that later read calls in same msg can find fid */
6578 lock_ObtainMutex(&fidp->mx);
6579 if (fidp->flags & SMB_FID_IOCTL) {
6580 lock_ReleaseMutex(&fidp->mx);
6581 code = smb_IoctlV3Read(fidp, vcp, inp, outp);
6582 smb_ReleaseFID(fidp);
6585 lock_ReleaseMutex(&fidp->mx);
6587 userp = smb_GetUserFromVCP(vcp, inp);
6589 /* 0 and 1 are reserved for request chaining, were setup by our caller,
6590 * and will be further filled in after we return.
6592 smb_SetSMBParm(outp, 2, 0); /* remaining bytes, for pipes */
6593 smb_SetSMBParm(outp, 3, 0); /* resvd */
6594 smb_SetSMBParm(outp, 4, 0); /* resvd */
6595 smb_SetSMBParm(outp, 5, count); /* # of bytes we're going to read */
6596 /* fill in #6 when we have all the parameters' space reserved */
6597 smb_SetSMBParm(outp, 7, 0); /* resv'd */
6598 smb_SetSMBParm(outp, 8, 0); /* resv'd */
6599 smb_SetSMBParm(outp, 9, 0); /* resv'd */
6600 smb_SetSMBParm(outp, 10, 0); /* resv'd */
6601 smb_SetSMBParm(outp, 11, 0); /* reserved */
6603 /* get op ptr after putting in the parms, since otherwise we don't
6604 * know where the data really is.
6606 op = smb_GetSMBData(outp, NULL);
6608 /* now fill in offset from start of SMB header to first data byte (to op) */
6609 smb_SetSMBParm(outp, 6, ((int) (op - outp->data)));
6611 /* set the packet data length the count of the # of bytes */
6612 smb_SetSMBDataLength(outp, count);
6614 code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount);
6616 /* fix some things up */
6617 smb_SetSMBParm(outp, 5, finalCount);
6618 smb_SetSMBDataLength(outp, finalCount);
6620 cm_ReleaseUser(userp);
6621 smb_ReleaseFID(fidp);
6626 * Values for createDisp, copied from NTDDK.H
6628 #define FILE_SUPERSEDE 0 // (???)
6629 #define FILE_OPEN 1 // (open)
6630 #define FILE_CREATE 2 // (exclusive)
6631 #define FILE_OPEN_IF 3 // (non-exclusive)
6632 #define FILE_OVERWRITE 4 // (open & truncate, but do not create)
6633 #define FILE_OVERWRITE_IF 5 // (open & truncate, or create)
6636 #define REQUEST_OPLOCK 2
6637 #define REQUEST_BATCH_OPLOCK 4
6638 #define OPEN_DIRECTORY 8
6639 #define EXTENDED_RESPONSE_REQUIRED 0x10
6641 /* CreateOptions field. */
6642 #define FILE_DIRECTORY_FILE 0x0001
6643 #define FILE_WRITE_THROUGH 0x0002
6644 #define FILE_SEQUENTIAL_ONLY 0x0004
6645 #define FILE_NON_DIRECTORY_FILE 0x0040
6646 #define FILE_NO_EA_KNOWLEDGE 0x0200
6647 #define FILE_EIGHT_DOT_THREE_ONLY 0x0400
6648 #define FILE_RANDOM_ACCESS 0x0800
6649 #define FILE_DELETE_ON_CLOSE 0x1000
6650 #define FILE_OPEN_BY_FILE_ID 0x2000
6651 #define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
6652 #define FILE_NO_COMPRESSION 0x00008000
6653 #define FILE_RESERVE_OPFILTER 0x00100000
6654 #define FILE_OPEN_REPARSE_POINT 0x00200000
6655 #define FILE_OPEN_NO_RECALL 0x00400000
6656 #define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
6658 /* SMB_COM_NT_CREATE_ANDX */
6659 long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
6661 clientchar_t *pathp, *realPathp;
6665 cm_scache_t *dscp; /* parent dir */
6666 cm_scache_t *scp; /* file to create or open */
6667 cm_scache_t *targetScp; /* if scp is a symlink */
6669 clientchar_t *lastNamep;
6670 clientchar_t *treeStartp;
6671 unsigned short nameLength;
6673 unsigned int requestOpLock;
6674 unsigned int requestBatchOpLock;
6675 unsigned int mustBeDir;
6676 unsigned int extendedRespRequired;
6677 unsigned int treeCreate;
6679 unsigned int desiredAccess;
6680 unsigned int extAttributes;
6681 unsigned int createDisp;
6682 unsigned int createOptions;
6683 unsigned int shareAccess;
6684 int initialModeBits;
6685 unsigned short baseFid;
6686 smb_fid_t *baseFidp;
6688 cm_scache_t *baseDirp;
6689 unsigned short openAction;
6694 clientchar_t *tidPathp;
6699 int checkDoneRequired = 0;
6700 cm_lock_data_t *ldp = NULL;
6704 /* This code is very long and has a lot of if-then-else clauses
6705 * scp and dscp get reused frequently and we need to ensure that
6706 * we don't lose a reference. Start by ensuring that they are NULL.
6713 nameLength = smb_GetSMBOffsetParm(inp, 2, 1);
6714 flags = smb_GetSMBOffsetParm(inp, 3, 1)
6715 | (smb_GetSMBOffsetParm(inp, 4, 1) << 16);
6716 requestOpLock = flags & REQUEST_OPLOCK;
6717 requestBatchOpLock = flags & REQUEST_BATCH_OPLOCK;
6718 mustBeDir = flags & OPEN_DIRECTORY;
6719 extendedRespRequired = flags & EXTENDED_RESPONSE_REQUIRED;
6722 * Why all of a sudden 32-bit FID?
6723 * We will reject all bits higher than 16.
6725 if (smb_GetSMBOffsetParm(inp, 6, 1) != 0)
6726 return CM_ERROR_INVAL;
6727 baseFid = smb_GetSMBOffsetParm(inp, 5, 1);
6728 desiredAccess = smb_GetSMBOffsetParm(inp, 7, 1)
6729 | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
6730 extAttributes = smb_GetSMBOffsetParm(inp, 13, 1)
6731 | (smb_GetSMBOffsetParm(inp, 14, 1) << 16);
6732 shareAccess = smb_GetSMBOffsetParm(inp, 15, 1)
6733 | (smb_GetSMBOffsetParm(inp, 16, 1) << 16);
6734 createDisp = smb_GetSMBOffsetParm(inp, 17, 1)
6735 | (smb_GetSMBOffsetParm(inp, 18, 1) << 16);
6736 createOptions = smb_GetSMBOffsetParm(inp, 19, 1)
6737 | (smb_GetSMBOffsetParm(inp, 20, 1) << 16);
6739 /* mustBeDir is never set; createOptions directory bit seems to be
6742 if (createOptions & FILE_DIRECTORY_FILE)
6744 else if (createOptions & FILE_NON_DIRECTORY_FILE)
6750 * compute initial mode bits based on read-only flag in
6751 * extended attributes
6753 initialModeBits = 0666;
6754 if (extAttributes & SMB_ATTR_READONLY)
6755 initialModeBits &= ~0222;
6757 pathp = smb_ParseStringCb(inp, smb_GetSMBData(inp, NULL), nameLength,
6758 NULL, SMB_STRF_ANSIPATH);
6760 /* Sometimes path is not null-terminated, so we make a copy. */
6761 realPathp = malloc(nameLength+sizeof(clientchar_t));
6762 memcpy(realPathp, pathp, nameLength+sizeof(clientchar_t));
6763 realPathp[nameLength/sizeof(clientchar_t)] = 0;
6765 spacep = inp->spacep;
6766 smb_StripLastComponent(spacep->wdata, &lastNamep, realPathp);
6768 osi_Log1(smb_logp,"NTCreateX for [%S]",osi_LogSaveClientString(smb_logp,realPathp));
6769 osi_Log4(smb_logp,"... da=[%x] ea=[%x] cd=[%x] co=[%x]", desiredAccess, extAttributes, createDisp, createOptions);
6770 osi_Log3(smb_logp,"... share=[%x] flags=[%x] lastNamep=[%S]", shareAccess, flags, osi_LogSaveClientString(smb_logp,(lastNamep?lastNamep:_C("null"))));
6773 (cm_ClientStrCmpIA(lastNamep, _C(SMB_IOCTL_FILENAME)) == 0 ||
6774 cm_ClientStrCmpIA(lastNamep, _C("\\srvsvc")) == 0 ||
6775 cm_ClientStrCmpIA(lastNamep, _C("\\wkssvc")) == 0 ||
6776 cm_ClientStrCmpIA(lastNamep, _C("ipc$")) == 0)) {
6777 /* special case magic file name for receiving IOCTL requests
6778 * (since IOCTL calls themselves aren't getting through).
6780 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
6781 smb_SetupIoctlFid(fidp, spacep);
6782 osi_Log1(smb_logp,"NTCreateX Setting up IOCTL on fid[%d]",fidp->fid);
6784 /* set inp->fid so that later read calls in same msg can find fid */
6785 inp->fid = fidp->fid;
6789 smb_SetSMBParmByte(outp, parmSlot, 0); /* oplock */
6790 smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
6791 smb_SetSMBParmLong(outp, parmSlot, 1); parmSlot += 2; /* Action */
6793 memset(&ft, 0, sizeof(ft));
6794 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
6795 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
6796 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
6797 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
6798 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2; /* attr */
6799 sz.HighPart = 0x7fff; sz.LowPart = 0;
6800 smb_SetSMBParmDouble(outp, parmSlot, (char *)&sz); parmSlot += 4; /* alen */
6801 smb_SetSMBParmDouble(outp, parmSlot, (char *)&sz); parmSlot += 4; /* len */
6802 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* filetype */
6803 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* dev state */
6804 smb_SetSMBParmByte(outp, parmSlot, 0); /* is a dir? */
6805 smb_SetSMBDataLength(outp, 0);
6807 /* clean up fid reference */
6808 smb_ReleaseFID(fidp);
6813 #ifdef DEBUG_VERBOSE
6815 char *hexp, *asciip;
6816 asciip = (lastNamep? lastNamep : realPathp);
6817 hexp = osi_HexifyString( asciip );
6818 DEBUG_EVENT2("AFS", "NTCreateX H[%s] A[%s]", hexp, asciip);
6823 userp = smb_GetUserFromVCP(vcp, inp);
6825 osi_Log1(smb_logp, "NTCreateX Invalid user [%d]", ((smb_t *) inp)->uid);
6827 return CM_ERROR_INVAL;
6832 baseDirp = cm_data.rootSCachep;
6833 code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
6834 if (code == CM_ERROR_TIDIPC) {
6835 /* Attempt to use a TID allocated for IPC. The client
6836 * is probably looking for DCE RPC end points which we
6837 * don't support OR it could be looking to make a DFS
6840 osi_Log0(smb_logp, "NTCreateX received IPC TID");
6843 cm_ReleaseUser(userp);
6844 return CM_ERROR_NOSUCHFILE;
6845 #endif /* DFS_SUPPORT */
6848 baseFidp = smb_FindFID(vcp, baseFid, 0);
6850 osi_Log1(smb_logp, "NTCreateX Invalid base fid [%d]", baseFid);
6852 cm_ReleaseUser(userp);
6853 return CM_ERROR_INVAL;
6856 if (baseFidp->scp && (baseFidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
6858 cm_ReleaseUser(userp);
6859 smb_CloseFID(vcp, baseFidp, NULL, 0);
6860 smb_ReleaseFID(baseFidp);
6861 return CM_ERROR_NOSUCHPATH;
6864 baseDirp = baseFidp->scp;
6868 osi_Log1(smb_logp, "NTCreateX tidPathp=[%S]", (tidPathp==NULL)?_C("null"): osi_LogSaveClientString(smb_logp,tidPathp));
6870 /* compute open mode */
6872 if (desiredAccess & DELETE)
6873 fidflags |= SMB_FID_OPENDELETE;
6874 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
6875 fidflags |= SMB_FID_OPENREAD_LISTDIR;
6876 if (desiredAccess & AFS_ACCESS_WRITE)
6877 fidflags |= SMB_FID_OPENWRITE;
6878 if (createOptions & FILE_DELETE_ON_CLOSE)
6879 fidflags |= SMB_FID_DELONCLOSE;
6880 if (createOptions & FILE_SEQUENTIAL_ONLY && !(createOptions & FILE_RANDOM_ACCESS))
6881 fidflags |= SMB_FID_SEQUENTIAL;
6882 if (createOptions & FILE_RANDOM_ACCESS && !(createOptions & FILE_SEQUENTIAL_ONLY))
6883 fidflags |= SMB_FID_RANDOM;
6884 if (createOptions & FILE_OPEN_REPARSE_POINT)
6885 osi_Log0(smb_logp, "NTCreateX Open Reparse Point");
6886 if (smb_IsExecutableFileName(lastNamep))
6887 fidflags |= SMB_FID_EXECUTABLE;
6889 /* and the share mode */
6890 if (shareAccess & FILE_SHARE_READ)
6891 fidflags |= SMB_FID_SHARE_READ;
6892 if (shareAccess & FILE_SHARE_WRITE)
6893 fidflags |= SMB_FID_SHARE_WRITE;
6895 osi_Log1(smb_logp, "NTCreateX fidflags 0x%x", fidflags);
6898 /* For an exclusive create, we want to do a case sensitive match for the last component. */
6899 if ( createDisp == FILE_CREATE ||
6900 createDisp == FILE_OVERWRITE ||
6901 createDisp == FILE_OVERWRITE_IF) {
6902 code = cm_NameI(baseDirp, spacep->wdata, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
6903 userp, tidPathp, &req, &dscp);
6906 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
6907 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp,
6909 cm_ReleaseSCache(dscp);
6910 cm_ReleaseUser(userp);
6913 smb_ReleaseFID(baseFidp);
6914 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
6915 return CM_ERROR_PATH_NOT_COVERED;
6917 return CM_ERROR_BADSHARENAME;
6919 #endif /* DFS_SUPPORT */
6920 code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp, CM_FLAG_FOLLOW,
6922 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) {
6923 code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp,
6924 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, &req, &scp);
6925 if (code == 0 && realDirFlag == 1) {
6926 cm_ReleaseSCache(scp);
6927 cm_ReleaseSCache(dscp);
6928 cm_ReleaseUser(userp);
6931 smb_ReleaseFID(baseFidp);
6932 return CM_ERROR_EXISTS;
6936 /* we have both scp and dscp */
6938 code = cm_NameI(baseDirp, realPathp, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
6939 userp, tidPathp, &req, &scp);
6941 if (code == 0 && scp->fileType == CM_SCACHETYPE_DFSLINK) {
6942 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, realPathp);
6943 cm_ReleaseSCache(scp);
6944 cm_ReleaseUser(userp);
6947 smb_ReleaseFID(baseFidp);
6948 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
6949 return CM_ERROR_PATH_NOT_COVERED;
6951 return CM_ERROR_BADSHARENAME;
6953 #endif /* DFS_SUPPORT */
6954 /* we might have scp but not dscp */
6960 if (!foundscp || (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE))) {
6961 /* look up parent directory */
6962 /* If we are trying to create a path (i.e. multiple nested directories), then we don't *need*
6963 * the immediate parent. We have to work our way up realPathp until we hit something that we
6967 /* we might or might not have scp */
6973 code = cm_NameI(baseDirp, spacep->wdata,
6974 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
6975 userp, tidPathp, &req, &dscp);
6978 if (code == 0 && dscp->fileType == CM_SCACHETYPE_DFSLINK) {
6979 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp,
6982 cm_ReleaseSCache(scp);
6983 cm_ReleaseSCache(dscp);
6984 cm_ReleaseUser(userp);
6987 smb_ReleaseFID(baseFidp);
6988 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
6989 return CM_ERROR_PATH_NOT_COVERED;
6991 return CM_ERROR_BADSHARENAME;
6993 #endif /* DFS_SUPPORT */
6996 (tp = cm_ClientStrRChr(spacep->wdata, '\\')) &&
6997 (createDisp == FILE_CREATE) &&
6998 (realDirFlag == 1)) {
7001 treeStartp = realPathp + (tp - spacep->wdata);
7003 if (*tp && !smb_IsLegalFilename(tp)) {
7004 cm_ReleaseUser(userp);
7006 smb_ReleaseFID(baseFidp);
7009 cm_ReleaseSCache(scp);
7010 return CM_ERROR_BADNTFILENAME;
7014 } while (dscp == NULL && code == 0);
7018 /* we might have scp and we might have dscp */
7021 smb_ReleaseFID(baseFidp);
7024 osi_Log0(smb_logp,"NTCreateX parent not found");
7026 cm_ReleaseSCache(scp);
7028 cm_ReleaseSCache(dscp);
7029 cm_ReleaseUser(userp);
7034 if (treeCreate && dscp->fileType == CM_SCACHETYPE_FILE) {
7035 /* A file exists where we want a directory. */
7037 cm_ReleaseSCache(scp);
7038 cm_ReleaseSCache(dscp);
7039 cm_ReleaseUser(userp);
7041 return CM_ERROR_EXISTS;
7045 lastNamep = realPathp;
7049 if (!smb_IsLegalFilename(lastNamep)) {
7051 cm_ReleaseSCache(scp);
7053 cm_ReleaseSCache(dscp);
7054 cm_ReleaseUser(userp);
7056 return CM_ERROR_BADNTFILENAME;
7059 if (!foundscp && !treeCreate) {
7060 if ( createDisp == FILE_CREATE ||
7061 createDisp == FILE_OVERWRITE ||
7062 createDisp == FILE_OVERWRITE_IF)
7064 code = cm_Lookup(dscp, lastNamep,
7065 CM_FLAG_FOLLOW, userp, &req, &scp);
7067 code = cm_Lookup(dscp, lastNamep,
7068 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
7071 if (code && (code != CM_ERROR_NOSUCHFILE && code != CM_ERROR_BPLUS_NOMATCH)) {
7073 cm_ReleaseSCache(dscp);
7074 cm_ReleaseUser(userp);
7079 /* we have scp and dscp */
7081 /* we have scp but not dscp */
7083 smb_ReleaseFID(baseFidp);
7086 /* if we get here, if code is 0, the file exists and is represented by
7087 * scp. Otherwise, we have to create it. The dir may be represented
7088 * by dscp, or we may have found the file directly. If code is non-zero,
7091 if (code == 0 && !treeCreate) {
7092 code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
7094 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7096 cm_ReleaseSCache(dscp);
7098 cm_ReleaseSCache(scp);
7099 cm_ReleaseUser(userp);
7103 checkDoneRequired = 1;
7105 if (createDisp == FILE_CREATE) {
7106 /* oops, file shouldn't be there */
7107 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7109 cm_ReleaseSCache(dscp);
7111 cm_ReleaseSCache(scp);
7112 cm_ReleaseUser(userp);
7114 return CM_ERROR_EXISTS;
7117 if ( createDisp == FILE_OVERWRITE ||
7118 createDisp == FILE_OVERWRITE_IF) {
7120 setAttr.mask = CM_ATTRMASK_LENGTH;
7121 setAttr.length.LowPart = 0;
7122 setAttr.length.HighPart = 0;
7123 /* now watch for a symlink */
7125 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
7127 osi_assertx(dscp != NULL, "null cm_scache_t");
7128 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
7130 /* we have a more accurate file to use (the
7131 * target of the symbolic link). Otherwise,
7132 * we'll just use the symlink anyway.
7134 osi_Log2(smb_logp, "symlink vp %x to vp %x",
7136 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7137 cm_ReleaseSCache(scp);
7139 code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
7141 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7143 cm_ReleaseSCache(dscp);
7145 cm_ReleaseSCache(scp);
7146 cm_ReleaseUser(userp);
7152 code = cm_SetAttr(scp, &setAttr, userp, &req);
7153 openAction = 3; /* truncated existing file */
7156 openAction = 1; /* found existing file */
7158 } else if (createDisp == FILE_OPEN || createDisp == FILE_OVERWRITE) {
7159 /* don't create if not found */
7161 cm_ReleaseSCache(dscp);
7163 cm_ReleaseSCache(scp);
7164 cm_ReleaseUser(userp);
7166 return CM_ERROR_NOSUCHFILE;
7167 } else if (realDirFlag == 0 || realDirFlag == -1) {
7168 osi_assertx(dscp != NULL, "null cm_scache_t");
7169 osi_Log1(smb_logp, "smb_ReceiveNTCreateX creating file %S",
7170 osi_LogSaveClientString(smb_logp, lastNamep));
7171 openAction = 2; /* created file */
7172 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
7173 setAttr.clientModTime = time(NULL);
7174 code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp, &req);
7177 if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
7178 smb_NotifyChange(FILE_ACTION_ADDED,
7179 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION,
7180 dscp, lastNamep, NULL, TRUE);
7181 } else if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
7182 /* Not an exclusive create, and someone else tried
7183 * creating it already, then we open it anyway. We
7184 * don't bother retrying after this, since if this next
7185 * fails, that means that the file was deleted after we
7186 * started this call.
7188 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
7191 if (createDisp == FILE_OVERWRITE_IF) {
7192 setAttr.mask = CM_ATTRMASK_LENGTH;
7193 setAttr.length.LowPart = 0;
7194 setAttr.length.HighPart = 0;
7196 /* now watch for a symlink */
7198 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
7200 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
7202 /* we have a more accurate file to use (the
7203 * target of the symbolic link). Otherwise,
7204 * we'll just use the symlink anyway.
7206 osi_Log2(smb_logp, "symlink vp %x to vp %x",
7208 cm_ReleaseSCache(scp);
7212 code = cm_SetAttr(scp, &setAttr, userp, &req);
7214 } /* lookup succeeded */
7217 clientchar_t *tp, *pp;
7218 clientchar_t *cp; /* This component */
7219 int clen = 0; /* length of component */
7220 cm_scache_t *tscp1, *tscp2;
7223 /* create directory */
7225 treeStartp = lastNamep;
7226 osi_assertx(dscp != NULL, "null cm_scache_t");
7227 osi_Log1(smb_logp, "smb_ReceiveNTCreateX creating directory [%S]",
7228 osi_LogSaveClientString(smb_logp, treeStartp));
7229 openAction = 2; /* created directory */
7231 /* if the request is to create the root directory
7232 * it will appear as a directory name of the nul-string
7233 * and a code of CM_ERROR_NOSUCHFILE
7235 if ( !*treeStartp && (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH))
7236 code = CM_ERROR_EXISTS;
7238 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
7239 setAttr.clientModTime = time(NULL);
7244 cm_HoldSCache(tscp1);
7248 tp = cm_ClientStrChr(pp, '\\');
7250 cm_ClientStrCpy(cp, lengthof(spacep->wdata) - (cp - spacep->wdata), pp);
7251 clen = (int)cm_ClientStrLen(cp);
7252 isLast = 1; /* indicate last component. the supplied path never ends in a slash */
7254 clen = (int)(tp - pp);
7255 cm_ClientStrCpyN(cp, lengthof(spacep->wdata) - (cp - spacep->wdata),
7263 continue; /* the supplied path can't have consecutive slashes either , but */
7265 /* cp is the next component to be created. */
7266 code = cm_MakeDir(tscp1, cp, 0, &setAttr, userp, &req, NULL);
7267 if (code == 0 && (tscp1->flags & CM_SCACHEFLAG_ANYWATCH))
7268 smb_NotifyChange(FILE_ACTION_ADDED,
7269 FILE_NOTIFY_CHANGE_DIR_NAME,
7270 tscp1, cp, NULL, TRUE);
7272 (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE)) {
7273 /* Not an exclusive create, and someone else tried
7274 * creating it already, then we open it anyway. We
7275 * don't bother retrying after this, since if this next
7276 * fails, that means that the file was deleted after we
7277 * started this call.
7279 code = cm_Lookup(tscp1, cp, CM_FLAG_CASEFOLD,
7280 userp, &req, &tscp2);
7285 if (!isLast) { /* for anything other than dscp, release it unless it's the last one */
7286 cm_ReleaseSCache(tscp1);
7287 tscp1 = tscp2; /* Newly created directory will be next parent */
7288 /* the hold is transfered to tscp1 from tscp2 */
7293 cm_ReleaseSCache(dscp);
7296 cm_ReleaseSCache(scp);
7299 * if we get here and code == 0, then scp is the last directory created, and dscp is the
7305 /* something went wrong creating or truncating the file */
7306 if (checkDoneRequired)
7307 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7309 cm_ReleaseSCache(scp);
7311 cm_ReleaseSCache(dscp);
7312 cm_ReleaseUser(userp);
7317 /* make sure we have file vs. dir right (only applies for single component case) */
7318 if (realDirFlag == 0 && scp->fileType != CM_SCACHETYPE_FILE) {
7319 /* now watch for a symlink */
7321 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
7322 cm_scache_t * targetScp = 0;
7323 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
7325 /* we have a more accurate file to use (the
7326 * target of the symbolic link). Otherwise,
7327 * we'll just use the symlink anyway.
7329 osi_Log2(smb_logp, "symlink vp %x to vp %x", scp, targetScp);
7330 if (checkDoneRequired) {
7331 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7332 checkDoneRequired = 0;
7334 cm_ReleaseSCache(scp);
7339 if (scp->fileType != CM_SCACHETYPE_FILE) {
7340 if (checkDoneRequired)
7341 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7343 cm_ReleaseSCache(dscp);
7344 cm_ReleaseSCache(scp);
7345 cm_ReleaseUser(userp);
7347 return CM_ERROR_ISDIR;
7351 /* (only applies to single component case) */
7352 if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
7353 if (checkDoneRequired)
7354 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7355 cm_ReleaseSCache(scp);
7357 cm_ReleaseSCache(dscp);
7358 cm_ReleaseUser(userp);
7360 return CM_ERROR_NOTDIR;
7363 /* open the file itself */
7364 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
7365 osi_assertx(fidp, "null smb_fid_t");
7367 /* save a reference to the user */
7369 fidp->userp = userp;
7371 /* If we are restricting sharing, we should do so with a suitable
7373 if (scp->fileType == CM_SCACHETYPE_FILE &&
7374 !(fidflags & SMB_FID_SHARE_WRITE)) {
7376 LARGE_INTEGER LOffset, LLength;
7379 LOffset.HighPart = SMB_FID_QLOCK_HIGH;
7380 LOffset.LowPart = SMB_FID_QLOCK_LOW;
7381 LLength.HighPart = 0;
7382 LLength.LowPart = SMB_FID_QLOCK_LENGTH;
7384 /* If we are not opening the file for writing, then we don't
7385 try to get an exclusive lock. No one else should be able to
7386 get an exclusive lock on the file anyway, although someone
7387 else can get a shared lock. */
7388 if ((fidflags & SMB_FID_SHARE_READ) ||
7389 !(fidflags & SMB_FID_OPENWRITE)) {
7390 sLockType = LOCKING_ANDX_SHARED_LOCK;
7395 key = cm_GenerateKey(vcp->vcID, SMB_FID_QLOCK_PID, fidp->fid);
7397 lock_ObtainWrite(&scp->rw);
7398 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, &req, NULL);
7399 lock_ReleaseWrite(&scp->rw);
7402 if (checkDoneRequired)
7403 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7404 cm_ReleaseSCache(scp);
7406 cm_ReleaseSCache(dscp);
7407 cm_ReleaseUser(userp);
7408 /* Shouldn't this be smb_CloseFID()? fidp->flags = SMB_FID_DELETE; */
7409 smb_CloseFID(vcp, fidp, NULL, 0);
7410 smb_ReleaseFID(fidp);
7416 /* Now its safe to release the file server lock obtained by cm_CheckNTOpen() */
7417 if (checkDoneRequired) {
7418 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7419 checkDoneRequired = 0;
7422 lock_ObtainMutex(&fidp->mx);
7423 /* save a pointer to the vnode */
7424 fidp->scp = scp; /* Hold transfered to fidp->scp and no longer needed */
7425 lock_ObtainWrite(&scp->rw);
7426 scp->flags |= CM_SCACHEFLAG_SMB_FID;
7427 lock_ReleaseWrite(&scp->rw);
7428 osi_Log2(smb_logp,"smb_ReceiveNTCreateX fidp 0x%p scp 0x%p", fidp, scp);
7430 fidp->flags = fidflags;
7432 /* remember if the file was newly created */
7434 fidp->flags |= SMB_FID_CREATED;
7436 /* save parent dir and pathname for delete or change notification */
7437 if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
7438 osi_Log2(smb_logp,"smb_ReceiveNTCreateX fidp 0x%p dscp 0x%p", fidp, dscp);
7439 fidp->flags |= SMB_FID_NTOPEN;
7440 fidp->NTopen_dscp = dscp;
7442 fidp->NTopen_pathp = cm_ClientStrDup(lastNamep);
7444 fidp->NTopen_wholepathp = realPathp;
7445 lock_ReleaseMutex(&fidp->mx);
7447 /* we don't need this any longer */
7449 cm_ReleaseSCache(dscp);
7453 cm_Open(scp, 0, userp);
7455 /* set inp->fid so that later read calls in same msg can find fid */
7456 inp->fid = fidp->fid;
7460 lock_ObtainRead(&scp->rw);
7461 smb_SetSMBParmByte(outp, parmSlot, 0); /* oplock */
7462 smb_SetSMBParm(outp, parmSlot, fidp->fid); parmSlot++;
7463 smb_SetSMBParmLong(outp, parmSlot, openAction); parmSlot += 2;
7464 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
7465 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
7466 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
7467 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
7468 smb_SetSMBParmDouble(outp, parmSlot, (char *)&ft); parmSlot += 4;
7469 smb_SetSMBParmLong(outp, parmSlot, smb_ExtAttributes(scp));
7471 smb_SetSMBParmDouble(outp, parmSlot, (char *)&scp->length); parmSlot += 4;
7472 smb_SetSMBParmDouble(outp, parmSlot, (char *)&scp->length); parmSlot += 4;
7473 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* filetype */
7474 smb_SetSMBParm(outp, parmSlot, 0); parmSlot++; /* dev state */
7475 smb_SetSMBParmByte(outp, parmSlot,
7476 (scp->fileType == CM_SCACHETYPE_DIRECTORY ||
7477 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
7478 scp->fileType == CM_SCACHETYPE_INVALID) ? 1 : 0); /* is a dir? */
7479 smb_SetSMBDataLength(outp, 0);
7481 if ((fidp->flags & SMB_FID_EXECUTABLE) &&
7482 LargeIntegerGreaterThanZero(fidp->scp->length) &&
7483 !(scp->flags & CM_SCACHEFLAG_PREFETCHING)) {
7486 lock_ReleaseRead(&scp->rw);
7489 cm_QueueBKGRequest(fidp->scp, cm_BkgPrefetch, 0, 0,
7490 fidp->scp->length.LowPart, fidp->scp->length.HighPart,
7494 osi_Log2(smb_logp, "SMB NT CreateX opening fid %d path %S", fidp->fid,
7495 osi_LogSaveClientString(smb_logp, realPathp));
7497 cm_ReleaseUser(userp);
7498 smb_ReleaseFID(fidp);
7500 /* Can't free realPathp if we get here since
7501 fidp->NTopen_wholepathp is pointing there */
7503 /* leave scp held since we put it in fidp->scp */
7508 * A lot of stuff copied verbatim from NT Create&X to NT Tran Create.
7509 * Instead, ultimately, would like to use a subroutine for common code.
7512 /* NT_TRANSACT_CREATE (SMB_COM_NT_TRANSACT) */
7513 long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
7515 clientchar_t *pathp, *realPathp;
7519 cm_scache_t *dscp; /* parent dir */
7520 cm_scache_t *scp; /* file to create or open */
7521 cm_scache_t *targetScp; /* if scp is a symlink */
7523 clientchar_t *lastNamep;
7524 unsigned long nameLength;
7526 unsigned int requestOpLock;
7527 unsigned int requestBatchOpLock;
7528 unsigned int mustBeDir;
7529 unsigned int extendedRespRequired;
7531 unsigned int desiredAccess;
7532 unsigned int allocSize;
7533 unsigned int shareAccess;
7534 unsigned int extAttributes;
7535 unsigned int createDisp;
7538 unsigned int impLevel;
7539 unsigned int secFlags;
7540 unsigned int createOptions;
7541 int initialModeBits;
7542 unsigned short baseFid;
7543 smb_fid_t *baseFidp;
7545 cm_scache_t *baseDirp;
7546 unsigned short openAction;
7550 clientchar_t *tidPathp;
7552 int parmOffset, dataOffset;
7559 cm_lock_data_t *ldp = NULL;
7560 int checkDoneRequired = 0;
7567 parmOffset = smb_GetSMBOffsetParm(inp, 11, 1)
7568 | (smb_GetSMBOffsetParm(inp, 12, 1) << 16);
7569 parmp = inp->data + parmOffset;
7570 lparmp = (ULONG *) parmp;
7573 requestOpLock = flags & REQUEST_OPLOCK;
7574 requestBatchOpLock = flags & REQUEST_BATCH_OPLOCK;
7575 mustBeDir = flags & OPEN_DIRECTORY;
7576 extendedRespRequired = flags & EXTENDED_RESPONSE_REQUIRED;
7579 * Why all of a sudden 32-bit FID?
7580 * We will reject all bits higher than 16.
7582 if (lparmp[1] & 0xFFFF0000)
7583 return CM_ERROR_INVAL;
7584 baseFid = (unsigned short)lparmp[1];
7585 desiredAccess = lparmp[2];
7586 allocSize = lparmp[3];
7587 extAttributes = lparmp[5];
7588 shareAccess = lparmp[6];
7589 createDisp = lparmp[7];
7590 createOptions = lparmp[8];
7593 nameLength = lparmp[11]; /* spec says chars but appears to be bytes */
7594 impLevel = lparmp[12];
7595 secFlags = lparmp[13];
7597 /* mustBeDir is never set; createOptions directory bit seems to be
7600 if (createOptions & FILE_DIRECTORY_FILE)
7602 else if (createOptions & FILE_NON_DIRECTORY_FILE)
7608 * compute initial mode bits based on read-only flag in
7609 * extended attributes
7611 initialModeBits = 0666;
7612 if (extAttributes & SMB_ATTR_READONLY)
7613 initialModeBits &= ~0222;
7615 pathp = smb_ParseStringCb(inp, (parmp + (13 * sizeof(ULONG)) + sizeof(UCHAR)),
7616 nameLength, NULL, SMB_STRF_ANSIPATH);
7617 /* Sometimes path is not nul-terminated, so we make a copy. */
7618 realPathp = malloc(nameLength+sizeof(clientchar_t));
7619 memcpy(realPathp, pathp, nameLength);
7620 realPathp[nameLength/sizeof(clientchar_t)] = 0;
7621 spacep = cm_GetSpace();
7622 smb_StripLastComponent(spacep->wdata, &lastNamep, realPathp);
7624 osi_Log1(smb_logp,"NTTranCreate %S",osi_LogSaveStringW(smb_logp,realPathp));
7625 osi_Log4(smb_logp,"... da[%x],ea[%x],sa[%x],cd[%x]",desiredAccess,extAttributes,shareAccess,createDisp);
7626 osi_Log4(smb_logp,"... co[%x],sdl[%x],eal[%x],as[%x],flags[%x]",createOptions,sdLen,eaLen,allocSize);
7627 osi_Log3(smb_logp,"... imp[%x],sec[%x],flags[%x]", impLevel, secFlags, flags);
7630 * Nothing here to handle SMB_IOCTL_FILENAME.
7631 * Will add it if necessary.
7634 #ifdef DEBUG_VERBOSE
7636 char *hexp, *asciip;
7637 asciip = (lastNamep? lastNamep : realPathp);
7638 hexp = osi_HexifyString( asciip );
7639 DEBUG_EVENT2("AFS", "NTTranCreate H[%s] A[%s]", hexp, asciip);
7644 userp = smb_GetUserFromVCP(vcp, inp);
7646 osi_Log1(smb_logp, "NTTranCreate invalid user [%d]", ((smb_t *) inp)->uid);
7648 return CM_ERROR_INVAL;
7653 baseDirp = cm_data.rootSCachep;
7654 code = smb_LookupTIDPath(vcp, ((smb_t *)inp)->tid, &tidPathp);
7655 if (code == CM_ERROR_TIDIPC) {
7656 /* Attempt to use a TID allocated for IPC. The client
7657 * is probably looking for DCE RPC end points which we
7658 * don't support OR it could be looking to make a DFS
7661 osi_Log0(smb_logp, "NTTranCreate received IPC TID");
7664 cm_ReleaseUser(userp);
7665 return CM_ERROR_NOSUCHPATH;
7669 baseFidp = smb_FindFID(vcp, baseFid, 0);
7671 osi_Log1(smb_logp, "NTTranCreate Invalid fid [%d]", baseFid);
7673 cm_ReleaseUser(userp);
7674 return CM_ERROR_BADFD;
7677 if (baseFidp->scp && (baseFidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
7679 cm_ReleaseUser(userp);
7680 smb_CloseFID(vcp, baseFidp, NULL, 0);
7681 smb_ReleaseFID(baseFidp);
7682 return CM_ERROR_NOSUCHPATH;
7685 baseDirp = baseFidp->scp;
7689 /* compute open mode */
7691 if (desiredAccess & DELETE)
7692 fidflags |= SMB_FID_OPENDELETE;
7693 if (desiredAccess & (AFS_ACCESS_READ|AFS_ACCESS_EXECUTE))
7694 fidflags |= SMB_FID_OPENREAD_LISTDIR;
7695 if (desiredAccess & AFS_ACCESS_WRITE)
7696 fidflags |= SMB_FID_OPENWRITE;
7697 if (createOptions & FILE_DELETE_ON_CLOSE)
7698 fidflags |= SMB_FID_DELONCLOSE;
7699 if (createOptions & FILE_SEQUENTIAL_ONLY && !(createOptions & FILE_RANDOM_ACCESS))
7700 fidflags |= SMB_FID_SEQUENTIAL;
7701 if (createOptions & FILE_RANDOM_ACCESS && !(createOptions & FILE_SEQUENTIAL_ONLY))
7702 fidflags |= SMB_FID_RANDOM;
7703 if (createOptions & FILE_OPEN_REPARSE_POINT)
7704 osi_Log0(smb_logp, "NTTranCreate Open Reparse Point");
7705 if (smb_IsExecutableFileName(lastNamep))
7706 fidflags |= SMB_FID_EXECUTABLE;
7708 /* And the share mode */
7709 if (shareAccess & FILE_SHARE_READ)
7710 fidflags |= SMB_FID_SHARE_READ;
7711 if (shareAccess & FILE_SHARE_WRITE)
7712 fidflags |= SMB_FID_SHARE_WRITE;
7716 if ( createDisp == FILE_OPEN ||
7717 createDisp == FILE_OVERWRITE ||
7718 createDisp == FILE_OVERWRITE_IF) {
7719 code = cm_NameI(baseDirp, spacep->wdata, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
7720 userp, tidPathp, &req, &dscp);
7723 if (dscp->fileType == CM_SCACHETYPE_DFSLINK) {
7724 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp, spacep->wdata);
7725 cm_ReleaseSCache(dscp);
7726 cm_ReleaseUser(userp);
7729 smb_ReleaseFID(baseFidp);
7730 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
7731 return CM_ERROR_PATH_NOT_COVERED;
7733 return CM_ERROR_BADSHARENAME;
7735 #endif /* DFS_SUPPORT */
7736 code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp, CM_FLAG_FOLLOW,
7738 if (code == CM_ERROR_NOSUCHFILE || code == CM_ERROR_BPLUS_NOMATCH) {
7739 code = cm_Lookup(dscp, (lastNamep)?(lastNamep+1):realPathp,
7740 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD, userp, &req, &scp);
7741 if (code == 0 && realDirFlag == 1) {
7742 cm_ReleaseSCache(scp);
7743 cm_ReleaseSCache(dscp);
7744 cm_ReleaseUser(userp);
7747 smb_ReleaseFID(baseFidp);
7748 return CM_ERROR_EXISTS;
7754 code = cm_NameI(baseDirp, realPathp, CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
7755 userp, tidPathp, &req, &scp);
7757 if (code == 0 && scp->fileType == CM_SCACHETYPE_DFSLINK) {
7758 int pnc = cm_VolStatus_Notify_DFS_Mapping(scp, tidPathp, realPathp);
7759 cm_ReleaseSCache(scp);
7760 cm_ReleaseUser(userp);
7763 smb_ReleaseFID(baseFidp);
7764 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
7765 return CM_ERROR_PATH_NOT_COVERED;
7767 return CM_ERROR_BADSHARENAME;
7769 #endif /* DFS_SUPPORT */
7775 if (code != 0 || (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE))) {
7776 /* look up parent directory */
7778 code = cm_NameI(baseDirp, spacep->wdata,
7779 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
7780 userp, tidPathp, &req, &dscp);
7782 if (code == 0 && dscp->fileType == CM_SCACHETYPE_DFSLINK) {
7783 int pnc = cm_VolStatus_Notify_DFS_Mapping(dscp, tidPathp, spacep->wdata);
7784 cm_ReleaseSCache(dscp);
7785 cm_ReleaseUser(userp);
7788 smb_ReleaseFID(baseFidp);
7789 if ( WANTS_DFS_PATHNAMES(inp) || pnc )
7790 return CM_ERROR_PATH_NOT_COVERED;
7792 return CM_ERROR_BADSHARENAME;
7794 #endif /* DFS_SUPPORT */
7798 cm_FreeSpace(spacep);
7801 smb_ReleaseFID(baseFidp);
7804 cm_ReleaseUser(userp);
7810 lastNamep = realPathp;
7814 if (!smb_IsLegalFilename(lastNamep))
7815 return CM_ERROR_BADNTFILENAME;
7818 if (createDisp == FILE_CREATE || createDisp == FILE_OVERWRITE_IF) {
7819 code = cm_Lookup(dscp, lastNamep,
7820 CM_FLAG_FOLLOW, userp, &req, &scp);
7822 code = cm_Lookup(dscp, lastNamep,
7823 CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
7826 if (code && code != CM_ERROR_NOSUCHFILE && code != CM_ERROR_BPLUS_NOMATCH) {
7827 cm_ReleaseSCache(dscp);
7828 cm_ReleaseUser(userp);
7835 smb_ReleaseFID(baseFidp);
7836 cm_FreeSpace(spacep);
7839 /* if we get here, if code is 0, the file exists and is represented by
7840 * scp. Otherwise, we have to create it. The dir may be represented
7841 * by dscp, or we may have found the file directly. If code is non-zero,
7845 code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
7847 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7849 cm_ReleaseSCache(dscp);
7850 cm_ReleaseSCache(scp);
7851 cm_ReleaseUser(userp);
7855 checkDoneRequired = 1;
7857 if (createDisp == FILE_CREATE) {
7858 /* oops, file shouldn't be there */
7859 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7861 cm_ReleaseSCache(dscp);
7862 cm_ReleaseSCache(scp);
7863 cm_ReleaseUser(userp);
7865 return CM_ERROR_EXISTS;
7868 if (createDisp == FILE_OVERWRITE ||
7869 createDisp == FILE_OVERWRITE_IF) {
7870 setAttr.mask = CM_ATTRMASK_LENGTH;
7871 setAttr.length.LowPart = 0;
7872 setAttr.length.HighPart = 0;
7874 /* now watch for a symlink */
7876 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
7878 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
7880 /* we have a more accurate file to use (the
7881 * target of the symbolic link). Otherwise,
7882 * we'll just use the symlink anyway.
7884 osi_Log2(smb_logp, "symlink vp %x to vp %x",
7886 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7887 cm_ReleaseSCache(scp);
7889 code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
7891 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7893 cm_ReleaseSCache(dscp);
7895 cm_ReleaseSCache(scp);
7896 cm_ReleaseUser(userp);
7902 code = cm_SetAttr(scp, &setAttr, userp, &req);
7903 openAction = 3; /* truncated existing file */
7905 else openAction = 1; /* found existing file */
7907 else if (createDisp == FILE_OPEN || createDisp == FILE_OVERWRITE) {
7908 /* don't create if not found */
7910 cm_ReleaseSCache(dscp);
7911 cm_ReleaseUser(userp);
7913 return CM_ERROR_NOSUCHFILE;
7915 else if (realDirFlag == 0 || realDirFlag == -1) {
7916 osi_assertx(dscp != NULL, "null cm_scache_t");
7917 osi_Log1(smb_logp, "smb_ReceiveNTTranCreate creating file %S",
7918 osi_LogSaveClientString(smb_logp, lastNamep));
7919 openAction = 2; /* created file */
7920 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
7921 setAttr.clientModTime = time(NULL);
7922 code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
7926 if (dscp->flags & CM_SCACHEFLAG_ANYWATCH)
7927 smb_NotifyChange(FILE_ACTION_ADDED,
7928 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION,
7929 dscp, lastNamep, NULL, TRUE);
7930 } else if (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE) {
7931 /* Not an exclusive create, and someone else tried
7932 * creating it already, then we open it anyway. We
7933 * don't bother retrying after this, since if this next
7934 * fails, that means that the file was deleted after we
7935 * started this call.
7937 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
7940 if (createDisp == FILE_OVERWRITE_IF) {
7941 setAttr.mask = CM_ATTRMASK_LENGTH;
7942 setAttr.length.LowPart = 0;
7943 setAttr.length.HighPart = 0;
7945 /* now watch for a symlink */
7947 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
7949 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
7951 /* we have a more accurate file to use (the
7952 * target of the symbolic link). Otherwise,
7953 * we'll just use the symlink anyway.
7955 osi_Log2(smb_logp, "symlink vp %x to vp %x",
7957 cm_ReleaseSCache(scp);
7961 code = cm_SetAttr(scp, &setAttr, userp, &req);
7963 } /* lookup succeeded */
7966 /* create directory */
7967 osi_assertx(dscp != NULL, "null cm_scache_t");
7969 "smb_ReceiveNTTranCreate creating directory %S",
7970 osi_LogSaveClientString(smb_logp, lastNamep));
7971 openAction = 2; /* created directory */
7972 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
7973 setAttr.clientModTime = time(NULL);
7974 code = cm_MakeDir(dscp, lastNamep, 0, &setAttr, userp, &req, NULL);
7975 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
7976 smb_NotifyChange(FILE_ACTION_ADDED,
7977 FILE_NOTIFY_CHANGE_DIR_NAME,
7978 dscp, lastNamep, NULL, TRUE);
7980 (code == CM_ERROR_EXISTS && createDisp != FILE_CREATE)) {
7981 /* Not an exclusive create, and someone else tried
7982 * creating it already, then we open it anyway. We
7983 * don't bother retrying after this, since if this next
7984 * fails, that means that the file was deleted after we
7985 * started this call.
7987 code = cm_Lookup(dscp, lastNamep, CM_FLAG_CASEFOLD,
7993 /* something went wrong creating or truncating the file */
7994 if (checkDoneRequired)
7995 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
7997 cm_ReleaseSCache(scp);
7998 cm_ReleaseUser(userp);
8003 /* make sure we have file vs. dir right */
8004 if (realDirFlag == 0 && scp->fileType != CM_SCACHETYPE_FILE) {
8005 /* now watch for a symlink */
8007 while (code == 0 && scp->fileType == CM_SCACHETYPE_SYMLINK) {
8009 code = cm_EvaluateSymLink(dscp, scp, &targetScp, userp, &req);
8011 /* we have a more accurate file to use (the
8012 * target of the symbolic link). Otherwise,
8013 * we'll just use the symlink anyway.
8015 osi_Log2(smb_logp, "symlink vp %x to vp %x",
8017 if (checkDoneRequired) {
8018 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
8019 checkDoneRequired = 0;
8021 cm_ReleaseSCache(scp);
8026 if (scp->fileType != CM_SCACHETYPE_FILE) {
8027 if (checkDoneRequired)
8028 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
8029 cm_ReleaseSCache(scp);
8030 cm_ReleaseUser(userp);
8032 return CM_ERROR_ISDIR;
8036 if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
8037 if (checkDoneRequired)
8038 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
8039 cm_ReleaseSCache(scp);
8040 cm_ReleaseUser(userp);
8042 return CM_ERROR_NOTDIR;
8045 /* open the file itself */
8046 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
8047 osi_assertx(fidp, "null smb_fid_t");
8049 /* save a reference to the user */
8051 fidp->userp = userp;
8053 /* If we are restricting sharing, we should do so with a suitable
8055 if (scp->fileType == CM_SCACHETYPE_FILE &&
8056 !(fidflags & SMB_FID_SHARE_WRITE)) {
8058 LARGE_INTEGER LOffset, LLength;
8061 LOffset.HighPart = SMB_FID_QLOCK_HIGH;
8062 LOffset.LowPart = SMB_FID_QLOCK_LOW;
8063 LLength.HighPart = 0;
8064 LLength.LowPart = SMB_FID_QLOCK_LENGTH;
8066 /* Similar to what we do in handling NTCreateX. We get a
8067 shared lock if we are only opening the file for reading. */
8068 if ((fidflags & SMB_FID_SHARE_READ) ||
8069 !(fidflags & SMB_FID_OPENWRITE)) {
8070 sLockType = LOCKING_ANDX_SHARED_LOCK;
8075 key = cm_GenerateKey(vcp->vcID, SMB_FID_QLOCK_PID, fidp->fid);
8077 lock_ObtainWrite(&scp->rw);
8078 code = cm_Lock(scp, sLockType, LOffset, LLength, key, 0, userp, &req, NULL);
8079 lock_ReleaseWrite(&scp->rw);
8082 if (checkDoneRequired)
8083 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
8084 cm_ReleaseSCache(scp);
8085 cm_ReleaseUser(userp);
8086 /* Shouldn't this be smb_CloseFID()? fidp->flags = SMB_FID_DELETE; */
8087 smb_CloseFID(vcp, fidp, NULL, 0);
8088 smb_ReleaseFID(fidp);
8090 return CM_ERROR_SHARING_VIOLATION;
8094 /* Now its safe to drop the file server lock obtained by cm_CheckNTOpen() */
8095 if (checkDoneRequired) {
8096 cm_CheckNTOpenDone(scp, userp, &req, &ldp);
8097 checkDoneRequired = 0;
8100 lock_ObtainMutex(&fidp->mx);
8101 /* save a pointer to the vnode */
8103 lock_ObtainWrite(&scp->rw);
8104 scp->flags |= CM_SCACHEFLAG_SMB_FID;
8105 lock_ReleaseWrite(&scp->rw);
8106 osi_Log2(smb_logp,"smb_ReceiveNTTranCreate fidp 0x%p scp 0x%p", fidp, scp);
8108 fidp->flags = fidflags;
8110 /* remember if the file was newly created */
8112 fidp->flags |= SMB_FID_CREATED;
8114 /* save parent dir and pathname for deletion or change notification */
8115 if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
8116 fidp->flags |= SMB_FID_NTOPEN;
8117 fidp->NTopen_dscp = dscp;
8118 osi_Log2(smb_logp,"smb_ReceiveNTTranCreate fidp 0x%p dscp 0x%p", fidp, dscp);
8120 fidp->NTopen_pathp = cm_ClientStrDup(lastNamep);
8122 fidp->NTopen_wholepathp = realPathp;
8123 lock_ReleaseMutex(&fidp->mx);
8125 /* we don't need this any longer */
8127 cm_ReleaseSCache(dscp);
8129 cm_Open(scp, 0, userp);
8131 /* set inp->fid so that later read calls in same msg can find fid */
8132 inp->fid = fidp->fid;
8134 /* check whether we are required to send an extended response */
8135 if (!extendedRespRequired) {
8137 parmOffset = 8*4 + 39;
8138 parmOffset += 1; /* pad to 4 */
8139 dataOffset = parmOffset + 70;
8143 /* Total Parameter Count */
8144 smb_SetSMBParmLong(outp, parmSlot, 70); parmSlot += 2;
8145 /* Total Data Count */
8146 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8147 /* Parameter Count */
8148 smb_SetSMBParmLong(outp, parmSlot, 70); parmSlot += 2;
8149 /* Parameter Offset */
8150 smb_SetSMBParmLong(outp, parmSlot, parmOffset); parmSlot += 2;
8151 /* Parameter Displacement */
8152 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8154 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8156 smb_SetSMBParmLong(outp, parmSlot, dataOffset); parmSlot += 2;
8157 /* Data Displacement */
8158 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8159 smb_SetSMBParmByte(outp, parmSlot, 0); /* Setup Count */
8160 smb_SetSMBDataLength(outp, 70);
8162 lock_ObtainRead(&scp->rw);
8163 outData = smb_GetSMBData(outp, NULL);
8164 outData++; /* round to get to parmOffset */
8165 *outData = 0; outData++; /* oplock */
8166 *outData = 0; outData++; /* reserved */
8167 *((USHORT *)outData) = fidp->fid; outData += 2; /* fid */
8168 *((ULONG *)outData) = openAction; outData += 4;
8169 *((ULONG *)outData) = 0; outData += 4; /* EA error offset */
8170 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
8171 *((FILETIME *)outData) = ft; outData += 8; /* creation time */
8172 *((FILETIME *)outData) = ft; outData += 8; /* last access time */
8173 *((FILETIME *)outData) = ft; outData += 8; /* last write time */
8174 *((FILETIME *)outData) = ft; outData += 8; /* change time */
8175 *((ULONG *)outData) = smb_ExtAttributes(scp); outData += 4;
8176 *((LARGE_INTEGER *)outData) = scp->length; outData += 8; /* alloc sz */
8177 *((LARGE_INTEGER *)outData) = scp->length; outData += 8; /* EOF */
8178 *((USHORT *)outData) = 0; outData += 2; /* filetype */
8179 *((USHORT *)outData) = 0; outData += 2; /* dev state */
8180 *((USHORT *)outData) = ((scp->fileType == CM_SCACHETYPE_DIRECTORY ||
8181 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
8182 scp->fileType == CM_SCACHETYPE_INVALID) ? 1 : 0);
8183 outData += 2; /* is a dir? */
8186 parmOffset = 8*4 + 39;
8187 parmOffset += 1; /* pad to 4 */
8188 dataOffset = parmOffset + 104;
8192 /* Total Parameter Count */
8193 smb_SetSMBParmLong(outp, parmSlot, 101); parmSlot += 2;
8194 /* Total Data Count */
8195 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8196 /* Parameter Count */
8197 smb_SetSMBParmLong(outp, parmSlot, 101); parmSlot += 2;
8198 /* Parameter Offset */
8199 smb_SetSMBParmLong(outp, parmSlot, parmOffset); parmSlot += 2;
8200 /* Parameter Displacement */
8201 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8203 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8205 smb_SetSMBParmLong(outp, parmSlot, dataOffset); parmSlot += 2;
8206 /* Data Displacement */
8207 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8208 smb_SetSMBParmByte(outp, parmSlot, 0); /* Setup Count */
8209 smb_SetSMBDataLength(outp, 105);
8211 lock_ObtainRead(&scp->rw);
8212 outData = smb_GetSMBData(outp, NULL);
8213 outData++; /* round to get to parmOffset */
8214 *outData = 0; outData++; /* oplock */
8215 *outData = 1; outData++; /* response type */
8216 *((USHORT *)outData) = fidp->fid; outData += 2; /* fid */
8217 *((ULONG *)outData) = openAction; outData += 4;
8218 *((ULONG *)outData) = 0; outData += 4; /* EA error offset */
8219 smb_LargeSearchTimeFromUnixTime(&ft, scp->clientModTime);
8220 *((FILETIME *)outData) = ft; outData += 8; /* creation time */
8221 *((FILETIME *)outData) = ft; outData += 8; /* last access time */
8222 *((FILETIME *)outData) = ft; outData += 8; /* last write time */
8223 *((FILETIME *)outData) = ft; outData += 8; /* change time */
8224 *((ULONG *)outData) = smb_ExtAttributes(scp); outData += 4;
8225 *((LARGE_INTEGER *)outData) = scp->length; outData += 8; /* alloc sz */
8226 *((LARGE_INTEGER *)outData) = scp->length; outData += 8; /* EOF */
8227 *((USHORT *)outData) = 0; outData += 2; /* filetype */
8228 *((USHORT *)outData) = 0; outData += 2; /* dev state */
8229 *((USHORT *)outData) = ((scp->fileType == CM_SCACHETYPE_DIRECTORY ||
8230 scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
8231 scp->fileType == CM_SCACHETYPE_INVALID) ? 1 : 0);
8232 outData += 1; /* is a dir? */
8233 memset(outData,0,24); outData += 24; /* Volume ID and file ID */
8234 *((ULONG *)outData) = 0x001f01ffL; outData += 4; /* Maxmimal access rights */
8235 *((ULONG *)outData) = 0; outData += 4; /* Guest Access rights */
8238 if ((fidp->flags & SMB_FID_EXECUTABLE) &&
8239 LargeIntegerGreaterThanZero(fidp->scp->length) &&
8240 !(scp->flags & CM_SCACHEFLAG_PREFETCHING)) {
8243 lock_ReleaseRead(&scp->rw);
8246 cm_QueueBKGRequest(fidp->scp, cm_BkgPrefetch, 0, 0,
8247 fidp->scp->length.LowPart, fidp->scp->length.HighPart,
8250 osi_Log1(smb_logp, "SMB NTTranCreate opening fid %d", fidp->fid);
8252 cm_ReleaseUser(userp);
8253 smb_ReleaseFID(fidp);
8255 /* free(realPathp); Can't free realPathp here because fidp->NTopen_wholepathp points there */
8256 /* leave scp held since we put it in fidp->scp */
8260 /* NT_TRANSACT_NOTIFY_CHANGE (SMB_COM_NT_TRANSACT) */
8261 long smb_ReceiveNTTranNotifyChange(smb_vc_t *vcp, smb_packet_t *inp,
8264 smb_packet_t *savedPacketp;
8266 USHORT fid, watchtree;
8270 filter = smb_GetSMBParm(inp, 19) |
8271 (smb_GetSMBParm(inp, 20) << 16);
8272 fid = smb_GetSMBParm(inp, 21);
8273 watchtree = (smb_GetSMBParm(inp, 22) & 0xff) ? 1 : 0;
8275 fidp = smb_FindFID(vcp, fid, 0);
8277 osi_Log1(smb_logp, "ERROR: NotifyChange given invalid fid [%d]", fid);
8278 return CM_ERROR_BADFD;
8281 if (fidp->scp && (fidp->scp->flags & CM_SCACHEFLAG_DELETED)) {
8282 smb_CloseFID(vcp, fidp, NULL, 0);
8283 smb_ReleaseFID(fidp);
8284 return CM_ERROR_NOSUCHFILE;
8287 /* Create a copy of the Directory Watch Packet to use when sending the
8288 * notification if in the future a matching change is detected.
8290 savedPacketp = smb_CopyPacket(inp);
8291 if (vcp != savedPacketp->vcp) {
8293 if (savedPacketp->vcp)
8294 smb_ReleaseVC(savedPacketp->vcp);
8295 savedPacketp->vcp = vcp;
8298 /* Add the watch to the list of events to send notifications for */
8299 lock_ObtainMutex(&smb_Dir_Watch_Lock);
8300 savedPacketp->nextp = smb_Directory_Watches;
8301 smb_Directory_Watches = savedPacketp;
8302 lock_ReleaseMutex(&smb_Dir_Watch_Lock);
8305 osi_Log3(smb_logp,"smb_ReceiveNTTranNotifyChange fidp 0x%p scp 0x%p file \"%S\"",
8306 fidp, scp, osi_LogSaveClientString(smb_logp, fidp->NTopen_wholepathp));
8307 osi_Log3(smb_logp, "Request for NotifyChange filter 0x%x fid %d wtree %d",
8308 filter, fid, watchtree);
8309 if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
8310 osi_Log0(smb_logp, " Notify Change File Name");
8311 if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
8312 osi_Log0(smb_logp, " Notify Change Directory Name");
8313 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
8314 osi_Log0(smb_logp, " Notify Change Attributes");
8315 if (filter & FILE_NOTIFY_CHANGE_SIZE)
8316 osi_Log0(smb_logp, " Notify Change Size");
8317 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
8318 osi_Log0(smb_logp, " Notify Change Last Write");
8319 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
8320 osi_Log0(smb_logp, " Notify Change Last Access");
8321 if (filter & FILE_NOTIFY_CHANGE_CREATION)
8322 osi_Log0(smb_logp, " Notify Change Creation");
8323 if (filter & FILE_NOTIFY_CHANGE_EA)
8324 osi_Log0(smb_logp, " Notify Change Extended Attributes");
8325 if (filter & FILE_NOTIFY_CHANGE_SECURITY)
8326 osi_Log0(smb_logp, " Notify Change Security");
8327 if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
8328 osi_Log0(smb_logp, " Notify Change Stream Name");
8329 if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
8330 osi_Log0(smb_logp, " Notify Change Stream Size");
8331 if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
8332 osi_Log0(smb_logp, " Notify Change Stream Write");
8334 lock_ObtainWrite(&scp->rw);
8336 scp->flags |= CM_SCACHEFLAG_WATCHEDSUBTREE;
8338 scp->flags |= CM_SCACHEFLAG_WATCHED;
8339 lock_ReleaseWrite(&scp->rw);
8340 smb_ReleaseFID(fidp);
8342 outp->flags |= SMB_PACKETFLAG_NOSEND;
8346 unsigned char nullSecurityDesc[36] = {
8347 0x01, /* security descriptor revision */
8348 0x00, /* reserved, should be zero */
8349 0x00, 0x80, /* security descriptor control;
8350 * 0x8000 : self-relative format */
8351 0x14, 0x00, 0x00, 0x00, /* offset of owner SID */
8352 0x1c, 0x00, 0x00, 0x00, /* offset of group SID */
8353 0x00, 0x00, 0x00, 0x00, /* offset of DACL would go here */
8354 0x00, 0x00, 0x00, 0x00, /* offset of SACL would go here */
8355 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8356 /* "null SID" owner SID */
8357 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8358 /* "null SID" group SID */
8361 /* NT_TRANSACT_QUERY_SECURITY_DESC (SMB_COM_NT_TRANSACT) */
8362 long smb_ReceiveNTTranQuerySecurityDesc(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
8364 int parmOffset, parmCount, dataOffset, dataCount;
8372 ULONG securityInformation;
8374 parmOffset = smb_GetSMBOffsetParm(inp, 11, 1)
8375 | (smb_GetSMBOffsetParm(inp, 12, 1) << 16);
8376 parmp = inp->data + parmOffset;
8377 sparmp = (USHORT *) parmp;
8378 lparmp = (ULONG *) parmp;
8381 securityInformation = lparmp[1];
8383 maxData = smb_GetSMBOffsetParm(inp, 7, 1)
8384 | (smb_GetSMBOffsetParm(inp, 8, 1) << 16);
8392 parmOffset = 8*4 + 39;
8393 parmOffset += 1; /* pad to 4 */
8395 dataOffset = parmOffset + parmCount;
8399 /* Total Parameter Count */
8400 smb_SetSMBParmLong(outp, parmSlot, parmCount); parmSlot += 2;
8401 /* Total Data Count */
8402 smb_SetSMBParmLong(outp, parmSlot, dataCount); parmSlot += 2;
8403 /* Parameter Count */
8404 smb_SetSMBParmLong(outp, parmSlot, parmCount); parmSlot += 2;
8405 /* Parameter Offset */
8406 smb_SetSMBParmLong(outp, parmSlot, parmOffset); parmSlot += 2;
8407 /* Parameter Displacement */
8408 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8410 smb_SetSMBParmLong(outp, parmSlot, dataCount); parmSlot += 2;
8412 smb_SetSMBParmLong(outp, parmSlot, dataOffset); parmSlot += 2;
8413 /* Data Displacement */
8414 smb_SetSMBParmLong(outp, parmSlot, 0); parmSlot += 2;
8415 smb_SetSMBParmByte(outp, parmSlot, 0); /* Setup Count */
8416 smb_SetSMBDataLength(outp, 1 + parmCount + dataCount);
8418 outData = smb_GetSMBData(outp, NULL);
8419 outData++; /* round to get to parmOffset */
8420 *((ULONG *)outData) = 36; outData += 4; /* length */
8422 if (maxData >= 36) {
8423 memcpy(outData, nullSecurityDesc, 36);
8427 return CM_ERROR_BUFFERTOOSMALL;
8430 /* SMB_COM_NT_TRANSACT
8432 SMB_COM_NT_TRANSACT_SECONDARY should also be handled here.
8434 long smb_ReceiveNTTransact(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
8436 unsigned short function;
8438 function = smb_GetSMBParm(inp, 18);
8440 osi_Log1(smb_logp, "SMB NT Transact function %d", function);
8442 /* We can handle long names */
8443 if (vcp->flags & SMB_VCFLAG_USENT)
8444 ((smb_t *)outp)->flg2 |= SMB_FLAGS2_IS_LONG_NAME;
8447 case 1: /* NT_TRANSACT_CREATE */
8448 return smb_ReceiveNTTranCreate(vcp, inp, outp);
8449 case 2: /* NT_TRANSACT_IOCTL */
8450 osi_Log0(smb_logp, "SMB NT Transact Ioctl - not implemented");
8452 case 3: /* NT_TRANSACT_SET_SECURITY_DESC */
8453 osi_Log0(smb_logp, "SMB NT Transact SetSecurityDesc - not implemented");
8455 case 4: /* NT_TRANSACT_NOTIFY_CHANGE */
8456 return smb_ReceiveNTTranNotifyChange(vcp, inp, outp);
8457 case 5: /* NT_TRANSACT_RENAME */
8458 osi_Log0(smb_logp, "SMB NT Transact Rename - not implemented");
8460 case 6: /* NT_TRANSACT_QUERY_SECURITY_DESC */
8461 return smb_ReceiveNTTranQuerySecurityDesc(vcp, inp, outp);
8463 osi_Log0(smb_logp, "SMB NT Transact Query Quota - not implemented");
8466 osi_Log0(smb_logp, "SMB NT Transact Set Quota - not implemented");
8469 return CM_ERROR_INVAL;
8473 * smb_NotifyChange -- find relevant change notification messages and
8476 * If we don't know the file name (i.e. a callback break), filename is
8477 * NULL, and we return a zero-length list.
8479 * At present there is not a single call to smb_NotifyChange that
8480 * has the isDirectParent parameter set to FALSE.
8482 void smb_NotifyChange(DWORD action, DWORD notifyFilter,
8483 cm_scache_t *dscp, clientchar_t *filename, clientchar_t *otherFilename,
8484 BOOL isDirectParent)
8486 smb_packet_t *watch, *lastWatch, *nextWatch;
8487 ULONG parmSlot, parmCount, parmOffset, dataOffset, nameLen = 0;
8488 char *outData, *oldOutData;
8492 BOOL twoEntries = FALSE;
8493 ULONG otherNameLen, oldParmCount = 0;
8497 /* Get ready for rename within directory */
8498 if (action == FILE_ACTION_RENAMED_OLD_NAME && otherFilename != NULL) {
8500 otherAction = FILE_ACTION_RENAMED_NEW_NAME;
8503 osi_Log4(smb_logp,"in smb_NotifyChange for file [%S] dscp [%p] notification 0x%x parent %d",
8504 osi_LogSaveClientString(smb_logp,filename),dscp, notifyFilter, isDirectParent);
8506 osi_Log0(smb_logp," FILE_ACTION_NONE");
8507 if (action == FILE_ACTION_ADDED)
8508 osi_Log0(smb_logp," FILE_ACTION_ADDED");
8509 if (action == FILE_ACTION_REMOVED)
8510 osi_Log0(smb_logp," FILE_ACTION_REMOVED");
8511 if (action == FILE_ACTION_MODIFIED)
8512 osi_Log0(smb_logp," FILE_ACTION_MODIFIED");
8513 if (action == FILE_ACTION_RENAMED_OLD_NAME)
8514 osi_Log0(smb_logp," FILE_ACTION_RENAMED_OLD_NAME");
8515 if (action == FILE_ACTION_RENAMED_NEW_NAME)
8516 osi_Log0(smb_logp," FILE_ACTION_RENAMED_NEW_NAME");
8518 lock_ObtainMutex(&smb_Dir_Watch_Lock);
8519 watch = smb_Directory_Watches;
8521 filter = smb_GetSMBParm(watch, 19)
8522 | (smb_GetSMBParm(watch, 20) << 16);
8523 fid = smb_GetSMBParm(watch, 21);
8524 wtree = (smb_GetSMBParm(watch, 22) & 0xff) ? 1 : 0;
8526 maxLen = smb_GetSMBOffsetParm(watch, 5, 1)
8527 | (smb_GetSMBOffsetParm(watch, 6, 1) << 16);
8530 * Strange hack - bug in NT Client and NT Server that we must emulate?
8532 if ((filter == (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME)) && wtree)
8533 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
8535 fidp = smb_FindFID(watch->vcp, fid, 0);
8537 osi_Log2(smb_logp," no fidp for fid[%d] in vcp 0x%p",fid, watch->vcp);
8539 watch = watch->nextp;
8543 if (fidp->scp != dscp ||
8544 fidp->scp->flags & CM_SCACHEFLAG_DELETED ||
8545 (filter & notifyFilter) == 0 ||
8546 (!isDirectParent && !wtree))
8548 osi_Log1(smb_logp," skipping fidp->scp[%x]", fidp->scp);
8550 watch = watch->nextp;
8551 lock_ReleaseMutex(&smb_Dir_Watch_Lock);
8552 smb_ReleaseFID(fidp);
8553 lock_ObtainMutex(&smb_Dir_Watch_Lock);
8558 "Sending Change Notification for fid %d filter 0x%x wtree %d file %S",
8559 fid, filter, wtree, osi_LogSaveClientString(smb_logp, filename));
8560 if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
8561 osi_Log0(smb_logp, " Notify Change File Name");
8562 if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
8563 osi_Log0(smb_logp, " Notify Change Directory Name");
8564 if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
8565 osi_Log0(smb_logp, " Notify Change Attributes");
8566 if (filter & FILE_NOTIFY_CHANGE_SIZE)
8567 osi_Log0(smb_logp, " Notify Change Size");
8568 if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
8569 osi_Log0(smb_logp, " Notify Change Last Write");
8570 if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
8571 osi_Log0(smb_logp, " Notify Change Last Access");
8572 if (filter & FILE_NOTIFY_CHANGE_CREATION)
8573 osi_Log0(smb_logp, " Notify Change Creation");
8574 if (filter & FILE_NOTIFY_CHANGE_EA)
8575 osi_Log0(smb_logp, " Notify Change Extended Attributes");
8576 if (filter & FILE_NOTIFY_CHANGE_SECURITY)
8577 osi_Log0(smb_logp, " Notify Change Security");
8578 if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
8579 osi_Log0(smb_logp, " Notify Change Stream Name");
8580 if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
8581 osi_Log0(smb_logp, " Notify Change Stream Size");
8582 if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
8583 osi_Log0(smb_logp, " Notify Change Stream Write");
8585 /* A watch can only be notified once. Remove it from the list */
8586 nextWatch = watch->nextp;
8587 if (watch == smb_Directory_Watches)
8588 smb_Directory_Watches = nextWatch;
8590 lastWatch->nextp = nextWatch;
8592 /* The watch is off the list, its ours now, safe to drop the lock */
8593 lock_ReleaseMutex(&smb_Dir_Watch_Lock);
8595 /* Turn off WATCHED flag in dscp */
8596 lock_ObtainWrite(&dscp->rw);
8598 dscp->flags &= ~CM_SCACHEFLAG_WATCHEDSUBTREE;
8600 dscp->flags &= ~CM_SCACHEFLAG_WATCHED;
8601 lock_ReleaseWrite(&dscp->rw);
8603 /* Convert to response packet */
8604 ((smb_t *) watch)->reb = SMB_FLAGS_SERVER_TO_CLIENT;
8605 #ifdef SEND_CANONICAL_PATHNAMES
8606 ((smb_t *) watch)->reb |= SMB_FLAGS_CANONICAL_PATHNAMES;
8608 ((smb_t *) watch)->wct = 0;
8611 if (filename == NULL) {
8614 nameLen = (ULONG)cm_ClientStrLen(filename);
8615 parmCount = 3*4 + nameLen*2;
8616 parmCount = (parmCount + 3) & ~3; /* pad to 4 */
8618 otherNameLen = (ULONG)cm_ClientStrLen(otherFilename);
8619 oldParmCount = parmCount;
8620 parmCount += 3*4 + otherNameLen*2;
8621 parmCount = (parmCount + 3) & ~3; /* pad to 4 */
8623 if (maxLen < parmCount)
8624 parmCount = 0; /* not enough room */
8626 parmOffset = 8*4 + 39;
8627 parmOffset += 1; /* pad to 4 */
8628 dataOffset = parmOffset + parmCount;
8632 /* Total Parameter Count */
8633 smb_SetSMBParmLong(watch, parmSlot, parmCount); parmSlot += 2;
8634 /* Total Data Count */
8635 smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
8636 /* Parameter Count */
8637 smb_SetSMBParmLong(watch, parmSlot, parmCount); parmSlot += 2;
8638 /* Parameter Offset */
8639 smb_SetSMBParmLong(watch, parmSlot, parmOffset); parmSlot += 2;
8640 /* Parameter Displacement */
8641 smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
8643 smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
8645 smb_SetSMBParmLong(watch, parmSlot, dataOffset); parmSlot += 2;
8646 /* Data Displacement */
8647 smb_SetSMBParmLong(watch, parmSlot, 0); parmSlot += 2;
8648 smb_SetSMBParmByte(watch, parmSlot, 0); /* Setup Count */
8649 smb_SetSMBDataLength(watch, parmCount + 1);
8651 if (parmCount != 0) {
8652 outData = smb_GetSMBData(watch, NULL);
8653 outData++; /* round to get to parmOffset */
8654 oldOutData = outData;
8655 *((DWORD *)outData) = oldParmCount; outData += 4;
8656 /* Next Entry Offset */
8657 *((DWORD *)outData) = action; outData += 4;
8659 *((DWORD *)outData) = nameLen*2; outData += 4;
8660 /* File Name Length */
8662 smb_UnparseString(watch, outData, filename, NULL, 0);
8666 outData = oldOutData + oldParmCount;
8667 *((DWORD *)outData) = 0; outData += 4;
8668 /* Next Entry Offset */
8669 *((DWORD *)outData) = otherAction; outData += 4;
8671 *((DWORD *)outData) = otherNameLen*2;
8672 outData += 4; /* File Name Length */
8673 smb_UnparseString(watch, outData, otherFilename, NULL, 0);
8678 * If filename is null, we don't know the cause of the
8679 * change notification. We return zero data (see above),
8680 * and set error code to NT_STATUS_NOTIFY_ENUM_DIR
8681 * (= 0x010C). We set the error code here by hand, without
8682 * modifying wct and bcc.
8684 if (filename == NULL) {
8685 ((smb_t *) watch)->rcls = 0x0C;
8686 ((smb_t *) watch)->reh = 0x01;
8687 ((smb_t *) watch)->errLow = 0;
8688 ((smb_t *) watch)->errHigh = 0;
8689 /* Set NT Status codes flag */
8690 ((smb_t *) watch)->flg2 |= SMB_FLAGS2_32BIT_STATUS;
8693 smb_SendPacket(watch->vcp, watch);
8694 smb_FreePacket(watch);
8696 smb_ReleaseFID(fidp);
8697 lock_ObtainMutex(&smb_Dir_Watch_Lock);
8700 lock_ReleaseMutex(&smb_Dir_Watch_Lock);
8703 /* SMB_COM_NT_CANCEL */
8704 long smb_ReceiveNTCancel(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
8706 unsigned char *replyWctp;
8707 smb_packet_t *watch, *lastWatch;
8708 USHORT fid, watchtree;
8712 osi_Log0(smb_logp, "SMB3 receive NT cancel");
8714 lock_ObtainMutex(&smb_Dir_Watch_Lock);
8715 watch = smb_Directory_Watches;
8717 if (((smb_t *)watch)->uid == ((smb_t *)inp)->uid
8718 && ((smb_t *)watch)->pid == ((smb_t *)inp)->pid
8719 && ((smb_t *)watch)->mid == ((smb_t *)inp)->mid
8720 && ((smb_t *)watch)->tid == ((smb_t *)inp)->tid) {
8721 if (watch == smb_Directory_Watches)
8722 smb_Directory_Watches = watch->nextp;
8724 lastWatch->nextp = watch->nextp;
8725 lock_ReleaseMutex(&smb_Dir_Watch_Lock);
8727 /* Turn off WATCHED flag in scp */
8728 fid = smb_GetSMBParm(watch, 21);
8729 watchtree = smb_GetSMBParm(watch, 22) & 0xffff;
8731 if (vcp != watch->vcp)
8732 osi_Log2(smb_logp, "smb_ReceiveNTCancel: vcp %x not equal to watch vcp %x",
8735 fidp = smb_FindFID(vcp, fid, 0);
8737 osi_Log3(smb_logp, "Cancelling change notification for fid %d wtree %d file %S",
8739 (fidp ? osi_LogSaveClientString(smb_logp, fidp->NTopen_wholepathp) :_C("")));
8742 osi_Log2(smb_logp,"smb_ReceiveNTCancel fidp 0x%p scp 0x%p", fidp, scp);
8743 lock_ObtainWrite(&scp->rw);
8745 scp->flags &= ~CM_SCACHEFLAG_WATCHEDSUBTREE;
8747 scp->flags &= ~CM_SCACHEFLAG_WATCHED;
8748 lock_ReleaseWrite(&scp->rw);
8749 smb_ReleaseFID(fidp);
8751 osi_Log2(smb_logp,"NTCancel unable to resolve fid [%d] in vcp[%x]", fid,vcp);
8754 /* assume STATUS32; return 0xC0000120 (CANCELED) */
8755 replyWctp = watch->wctp;
8759 ((smb_t *)watch)->rcls = 0x20;
8760 ((smb_t *)watch)->reh = 0x1;
8761 ((smb_t *)watch)->errLow = 0;
8762 ((smb_t *)watch)->errHigh = 0xC0;
8763 ((smb_t *)watch)->flg2 |= SMB_FLAGS2_32BIT_STATUS;
8764 smb_SendPacket(vcp, watch);
8765 smb_FreePacket(watch);
8769 watch = watch->nextp;
8771 lock_ReleaseMutex(&smb_Dir_Watch_Lock);
8777 * NT rename also does hard links.
8780 #define RENAME_FLAG_MOVE_CLUSTER_INFORMATION 0x102
8781 #define RENAME_FLAG_HARD_LINK 0x103
8782 #define RENAME_FLAG_RENAME 0x104
8783 #define RENAME_FLAG_COPY 0x105
8785 long smb_ReceiveNTRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
8787 clientchar_t *oldPathp, *newPathp;
8793 attrs = smb_GetSMBParm(inp, 0);
8794 rename_type = smb_GetSMBParm(inp, 1);
8796 if (rename_type != RENAME_FLAG_RENAME && rename_type != RENAME_FLAG_HARD_LINK) {
8797 osi_Log1(smb_logp, "NTRename invalid rename_type [%x]", rename_type);
8798 return CM_ERROR_NOACCESS;
8801 tp = smb_GetSMBData(inp, NULL);
8802 oldPathp = smb_ParseASCIIBlock(inp, tp, &tp, 0);
8803 newPathp = smb_ParseASCIIBlock(inp, tp, &tp, 0);
8805 osi_Log3(smb_logp, "NTRename for [%S]->[%S] type [%s]",
8806 osi_LogSaveClientString(smb_logp, oldPathp),
8807 osi_LogSaveClientString(smb_logp, newPathp),
8808 ((rename_type==RENAME_FLAG_RENAME)?"rename":(rename_type==RENAME_FLAG_HARD_LINK)?"hardlink":"other"));
8810 if (rename_type == RENAME_FLAG_RENAME) {
8811 code = smb_Rename(vcp,inp,oldPathp,newPathp,attrs);
8812 } else if (rename_type == RENAME_FLAG_HARD_LINK) { /* RENAME_FLAG_HARD_LINK */
8813 code = smb_Link(vcp,inp,oldPathp,newPathp);
8815 code = CM_ERROR_BADOP;
8821 lock_InitializeMutex(&smb_Dir_Watch_Lock, "Directory Watch List Lock", LOCK_HIERARCHY_SMB_DIRWATCH);
8824 cm_user_t *smb_FindCMUserByName(clientchar_t *usern, clientchar_t *machine, afs_uint32 flags)
8826 smb_username_t *unp;
8829 unp = smb_FindUserByName(usern, machine, flags);
8831 lock_ObtainMutex(&unp->mx);
8832 unp->userp = cm_NewUser();
8833 lock_ReleaseMutex(&unp->mx);
8834 osi_Log2(smb_logp,"smb_FindCMUserByName New user name[%S] machine[%S]",osi_LogSaveClientString(smb_logp,usern),osi_LogSaveClientString(smb_logp,machine));
8836 osi_Log2(smb_logp,"smb_FindCMUserByName Found name[%S] machine[%S]",osi_LogSaveClientString(smb_logp,usern),osi_LogSaveClientString(smb_logp,machine));
8840 smb_ReleaseUsername(unp);