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>
16 #include <sys/timeb.h>
32 /* These characters are illegal in Windows filenames */
33 static char *illegalChars = "\\/:*?\"<>|";
34 BOOL isWindows2000 = FALSE;
36 smb_vc_t *dead_vcp = NULL;
37 smb_vc_t *active_vcp = NULL;
39 char *loggedOutName = NULL;
40 smb_user_t *loggedOutUserp = NULL;
41 unsigned long loggedOutTime;
44 int smbShutdownFlag = 0;
47 int smb_LogoffTokenTransfer;
48 unsigned long smb_LogoffTransferTimeout;
50 DWORD last_msg_time = 0;
54 unsigned int sessionGen = 0;
58 osi_hyper_t hzero = {0, 0};
59 osi_hyper_t hones = {0xFFFFFFFF, -1};
62 osi_rwlock_t smb_globalLock;
63 osi_rwlock_t smb_rctLock;
65 unsigned char smb_LANadapter;
66 unsigned char smb_sharename[NCBNAMSZ+1] = {0};
69 long smb_maxObsConcurrentCalls=0;
70 long smb_concurrentCalls=0;
72 smb_dispatch_t smb_dispatchTable[SMB_NOPCODES];
74 smb_packet_t *smb_packetFreeListp;
75 smb_ncb_t *smb_ncbFreeListp;
77 int smb_NumServerThreads;
79 int numNCBs, numSessions;
82 EVENT_HANDLE NCBavails[NCBmax], NCBevents[NCBmax];
83 EVENT_HANDLE **NCBreturns;
84 DWORD NCBsessions[NCBmax];
86 struct smb_packet *bufs[NCBmax];
88 #define Sessionmax 100
89 EVENT_HANDLE SessionEvents[Sessionmax];
90 unsigned short LSNs[Sessionmax];
91 BOOL dead_sessions[Sessionmax];
94 osi_mutex_t smb_RawBufLock;
96 #define SMB_RAW_BUFS 4
98 int smb_RawBufSel[SMB_RAW_BUFS];
103 #define RAWTIMEOUT INFINITE
106 typedef struct raw_write_cont {
119 /* dir search stuff */
120 long smb_dirSearchCounter = 1;
121 smb_dirSearch_t *smb_firstDirSearchp;
122 smb_dirSearch_t *smb_lastDirSearchp;
124 /* global state about V3 protocols */
125 int smb_useV3; /* try to negotiate V3 */
128 /* MessageBox or something like it */
129 int (WINAPI *smb_MBfunc)(HWND, LPCTSTR, LPCTSTR, UINT) = NULL;
133 * Time in Unix format of midnight, 1/1/1970 local time.
134 * When added to dosUTime, gives Unix (AFS) time.
138 /* Time difference for converting to kludge-GMT */
141 char *smb_localNamep;
143 smb_vc_t *smb_allVCsp;
145 smb_waitingLock_t *smb_allWaitingLocks;
148 void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
149 NCB *ncbp, raw_write_cont_t *rwcp);
150 void smb_NetbiosInit();
151 extern char cm_HostName[];
153 extern char cm_confDir[];
157 #define LPTSTR char *
158 #define GetComputerName(str, sizep) \
159 strcpy((str), cm_HostName); \
160 *(sizep) = strlen(cm_HostName)
166 * To build an expiring version, comment out the definition of NOEXPIRE,
167 * and set the definition of EXPIREDATE to the desired value.
170 #define EXPIREDATE 834000000 /* Wed Jun 5 1996 */
175 /* scache must be locked */
176 unsigned int smb_Attributes(cm_scache_t *scp)
180 if (scp->fileType == CM_SCACHETYPE_DIRECTORY
181 || scp->fileType == CM_SCACHETYPE_MOUNTPOINT)
187 * We used to mark a file RO if it was in an RO volume, but that
188 * turns out to be impolitic in NT. See defect 10007.
191 if ((scp->unixModeBits & 0222) == 0 || (scp->flags & CM_SCACHEFLAG_RO))
193 if ((scp->unixModeBits & 0222) == 0)
194 attrs |= 1; /* turn on read-only flag */
199 static int ExtractBits(WORD bits, short start, short len)
206 num = bits << (16 - end);
207 num = num >> ((16 - end) + start);
213 void ShowUnixTime(char *FuncName, long unixTime)
218 smb_LargeSearchTimeFromUnixTime(&ft, unixTime);
220 if (!FileTimeToDosDateTime(&ft, &wDate, &wTime))
221 osi_Log1(afsd_logp, "Failed to convert filetime to dos datetime: %d", GetLastError());
223 int day, month, year, sec, min, hour;
226 day = ExtractBits(wDate, 0, 5);
227 month = ExtractBits(wDate, 5, 4);
228 year = ExtractBits(wDate, 9, 7) + 1980;
230 sec = ExtractBits(wTime, 0, 5);
231 min = ExtractBits(wTime, 5, 6);
232 hour = ExtractBits(wTime, 11, 5);
234 sprintf(msg, "%s = %02d-%02d-%04d %02d:%02d:%02d", FuncName, month, day, year, hour, min, sec);
235 osi_Log1(afsd_logp, "%s", osi_LogSaveString(afsd_logp, msg));
241 /* Determine if we are observing daylight savings time */
242 void GetTimeZoneInfo(BOOL *pDST, LONG *pDstBias, LONG *pBias)
244 TIME_ZONE_INFORMATION timeZoneInformation;
245 SYSTEMTIME utc, local, localDST;
247 /* Get the time zone info. NT uses this to calc if we are in DST. */
248 GetTimeZoneInformation(&timeZoneInformation);
250 /* Return the daylight bias */
251 *pDstBias = timeZoneInformation.DaylightBias;
253 /* Return the bias */
254 *pBias = timeZoneInformation.Bias;
256 /* Now determine if DST is being observed */
258 /* Get the UTC (GMT) time */
261 /* Convert UTC time to local time using the time zone info. If we are
262 observing DST, the calculated local time will include this.
264 SystemTimeToTzSpecificLocalTime(&timeZoneInformation, &utc, &localDST);
266 /* Set the daylight bias to 0. The daylight bias is the amount of change
267 in time that we use for daylight savings time. By setting this to 0
268 we cause there to be no change in time during daylight savings time.
270 timeZoneInformation.DaylightBias = 0;
272 /* Convert the utc time to local time again, but this time without any
273 adjustment for daylight savings time.
275 SystemTimeToTzSpecificLocalTime(&timeZoneInformation, &utc, &local);
277 /* If the two times are different, then it means that the localDST that
278 we calculated includes the daylight bias, and therefore we are
279 observing daylight savings time.
281 *pDST = localDST.wHour != local.wHour;
284 /* Determine if we are observing daylight savings time */
285 void GetTimeZoneInfo(BOOL *pDST, LONG *pDstBias, LONG *pBias)
291 *pDstBias = -60; /* where can this be different? */
297 void CompensateForSmbClientLastWriteTimeBugs(long *pLastWriteTime)
299 BOOL dst; /* Will be TRUE if observing DST */
300 LONG dstBias; /* Offset from local time if observing DST */
301 LONG bias; /* Offset from GMT for local time */
304 * This function will adjust the last write time to compensate
305 * for two bugs in the smb client:
307 * 1) During Daylight Savings Time, the LastWriteTime is ahead
308 * in time by the DaylightBias (ignoring the sign - the
309 * DaylightBias is always stored as a negative number). If
310 * the DaylightBias is -60, then the LastWriteTime will be
311 * ahead by 60 minutes.
313 * 2) If the local time zone is a positive offset from GMT, then
314 * the LastWriteTime will be the correct local time plus the
315 * Bias (ignoring the sign - a positive offset from GMT is
316 * always stored as a negative Bias). If the Bias is -120,
317 * then the LastWriteTime will be ahead by 120 minutes.
319 * These bugs can occur at the same time.
322 GetTimeZoneInfo(&dst, &dstBias, &bias);
324 /* First adjust for DST */
326 *pLastWriteTime -= (-dstBias * 60); /* Convert dstBias to seconds */
328 /* Now adjust for a positive offset from GMT (a negative bias). */
330 *pLastWriteTime -= (-bias * 60); /* Convert bias to seconds */
334 * Calculate the difference (in seconds) between local time and GMT.
335 * This enables us to convert file times to kludge-GMT.
341 struct tm gmt_tm, local_tm;
342 int days, hours, minutes, seconds;
345 gmt_tm = *(gmtime(&t));
346 local_tm = *(localtime(&t));
348 days = local_tm.tm_yday - gmt_tm.tm_yday;
349 hours = 24 * days + local_tm.tm_hour - gmt_tm.tm_hour;
350 minutes = 60 * hours + local_tm.tm_min - gmt_tm.tm_min;
351 seconds = 60 * minutes + local_tm.tm_sec - gmt_tm.tm_sec;
357 void smb_LargeSearchTimeFromUnixTime(FILETIME *largeTimep, long unixTime)
362 long ersatz_unixTime;
365 * Must use kludge-GMT instead of real GMT.
366 * kludge-GMT is computed by adding time zone difference to localtime.
369 * ltp = gmtime(&unixTime);
371 ersatz_unixTime = unixTime - smb_NowTZ;
372 ltp = localtime(&ersatz_unixTime);
374 /* if we fail, make up something */
377 localJunk.tm_year = 89 - 20;
378 localJunk.tm_mon = 4;
379 localJunk.tm_mday = 12;
380 localJunk.tm_hour = 0;
381 localJunk.tm_min = 0;
382 localJunk.tm_sec = 0;
385 stm.wYear = ltp->tm_year + 1900;
386 stm.wMonth = ltp->tm_mon + 1;
387 stm.wDayOfWeek = ltp->tm_wday;
388 stm.wDay = ltp->tm_mday;
389 stm.wHour = ltp->tm_hour;
390 stm.wMinute = ltp->tm_min;
391 stm.wSecond = ltp->tm_sec;
392 stm.wMilliseconds = 0;
394 SystemTimeToFileTime(&stm, largeTimep);
397 void smb_LargeSearchTimeFromUnixTime(FILETIME *largeTimep, long unixTime)
399 /* unixTime: seconds since 1/1/1970 00:00:00 GMT */
400 /* FILETIME: 100ns intervals since 1/1/1601 00:00:00 ??? */
401 LARGE_INTEGER *ft = (LARGE_INTEGER *) largeTimep;
403 int leap_years = 89; /* leap years betw 1/1/1601 and 1/1/1970 */
405 /* set ft to number of 100ns intervals betw 1/1/1601 and 1/1/1970 GMT */
406 *ft = ConvertLongToLargeInteger(((EPOCH_YEAR-1601) * 365 + leap_years)
408 *ft = LargeIntegerMultiplyByLong(*ft, 60);
409 *ft = LargeIntegerMultiplyByLong(*ft, 10000000);
412 ut = ConvertLongToLargeInteger(unixTime);
413 ut = LargeIntegerMultiplyByLong(ut, 10000000);
414 *ft = LargeIntegerAdd(*ft, ut);
419 void smb_UnixTimeFromLargeSearchTime(long *unixTimep, FILETIME *largeTimep)
425 FileTimeToSystemTime(largeTimep, &stm);
427 lt.tm_year = stm.wYear - 1900;
428 lt.tm_mon = stm.wMonth - 1;
429 lt.tm_wday = stm.wDayOfWeek;
430 lt.tm_mday = stm.wDay;
431 lt.tm_hour = stm.wHour;
432 lt.tm_min = stm.wMinute;
433 lt.tm_sec = stm.wSecond;
436 save_timezone = _timezone;
437 _timezone += smb_NowTZ;
438 *unixTimep = mktime(<);
439 _timezone = save_timezone;
442 void smb_UnixTimeFromLargeSearchTime(long *unixTimep, FILETIME *largeTimep)
444 /* unixTime: seconds since 1/1/1970 00:00:00 GMT */
445 /* FILETIME: 100ns intervals since 1/1/1601 00:00:00 GMT? */
446 LARGE_INTEGER *ft = (LARGE_INTEGER *) largeTimep;
450 /* set to number of 100ns intervals betw 1/1/1601 and 1/1/1970 */
451 a = ConvertLongToLargeInteger(((EPOCH_YEAR-1601) * 365 + leap_years) * 24 * 60
453 a = LargeIntegerMultiplyByLong(a, 60);
454 a = LargeIntegerMultiplyByLong(a, 10000000);
456 /* subtract it from ft */
457 a = LargeIntegerSubtract(*ft, a);
459 /* divide down to seconds */
460 *unixTimep = LargeIntegerDivideByLong(a, 10000000);
464 void smb_SearchTimeFromUnixTime(long *dosTimep, long unixTime)
471 ltp = localtime((time_t*) &unixTime);
473 /* if we fail, make up something */
476 localJunk.tm_year = 89 - 20;
477 localJunk.tm_mon = 4;
478 localJunk.tm_mday = 12;
479 localJunk.tm_hour = 0;
480 localJunk.tm_min = 0;
481 localJunk.tm_sec = 0;
484 dosDate = ((ltp->tm_year-80)<<9) | ((ltp->tm_mon+1) << 5) | (ltp->tm_mday);
485 dosTime = (ltp->tm_hour<<11) | (ltp->tm_min << 5) | (ltp->tm_sec / 2);
486 *dosTimep = (dosDate<<16) | dosTime;
489 void smb_UnixTimeFromSearchTime(long *unixTimep, long searchTime)
491 unsigned short dosDate;
492 unsigned short dosTime;
495 dosDate = searchTime & 0xffff;
496 dosTime = (searchTime >> 16) & 0xffff;
498 localTm.tm_year = 80 + ((dosDate>>9) & 0x3f);
499 localTm.tm_mon = ((dosDate >> 5) & 0xf) - 1; /* January is 0 in localTm */
500 localTm.tm_mday = (dosDate) & 0x1f;
501 localTm.tm_hour = (dosTime>>11) & 0x1f;
502 localTm.tm_min = (dosTime >> 5) & 0x3f;
503 localTm.tm_sec = (dosTime & 0x1f) * 2;
504 localTm.tm_isdst = -1; /* compute whether DST in effect */
506 *unixTimep = mktime(&localTm);
509 void smb_DosUTimeFromUnixTime(long *dosUTimep, long unixTime)
511 *dosUTimep = unixTime - smb_localZero;
514 void smb_UnixTimeFromDosUTime(long *unixTimep, long dosTime)
517 *unixTimep = dosTime + smb_localZero;
519 /* dosTime seems to be already adjusted for GMT */
520 *unixTimep = dosTime;
524 smb_vc_t *smb_FindVC(unsigned short lsn, int flags)
528 lock_ObtainWrite(&smb_rctLock);
529 for(vcp = smb_allVCsp; vcp; vcp=vcp->nextp) {
530 if (lsn == vcp->lsn) {
535 if (!vcp && (flags & SMB_FLAG_CREATE)) {
536 vcp = malloc(sizeof(*vcp));
537 memset(vcp, 0, sizeof(*vcp));
541 vcp->nextp = smb_allVCsp;
543 lock_InitializeMutex(&vcp->mx, "vc_t mutex");
546 lock_ReleaseWrite(&smb_rctLock);
550 int smb_IsStarMask(char *maskp)
555 for(i=0; i<11; i++) {
557 if (tc == '?' || tc == '*' || tc == '>') return 1;
562 void smb_ReleaseVC(smb_vc_t *vcp)
564 lock_ObtainWrite(&smb_rctLock);
565 osi_assert(vcp->refCount-- > 0);
566 lock_ReleaseWrite(&smb_rctLock);
569 void smb_HoldVC(smb_vc_t *vcp)
571 lock_ObtainWrite(&smb_rctLock);
573 lock_ReleaseWrite(&smb_rctLock);
576 smb_tid_t *smb_FindTID(smb_vc_t *vcp, unsigned short tid, int flags)
580 lock_ObtainWrite(&smb_rctLock);
581 for(tidp = vcp->tidsp; tidp; tidp = tidp->nextp) {
582 if (tid == tidp->tid) {
587 if (!tidp && (flags & SMB_FLAG_CREATE)) {
588 tidp = malloc(sizeof(*tidp));
589 memset(tidp, 0, sizeof(*tidp));
590 tidp->nextp = vcp->tidsp;
594 lock_InitializeMutex(&tidp->mx, "tid_t mutex");
597 lock_ReleaseWrite(&smb_rctLock);
601 void smb_ReleaseTID(smb_tid_t *tidp)
608 lock_ObtainWrite(&smb_rctLock);
609 osi_assert(tidp->refCount-- > 0);
610 if (tidp->refCount == 0 && (tidp->flags & SMB_TIDFLAG_DELETE)) {
611 ltpp = &tidp->vcp->tidsp;
612 for(tp = *ltpp; tp; ltpp = &tp->nextp, tp = *ltpp) {
613 if (tp == tidp) break;
615 osi_assert(tp != NULL);
617 lock_FinalizeMutex(&tidp->mx);
618 userp = tidp->userp; /* remember to drop ref later */
620 lock_ReleaseWrite(&smb_rctLock);
622 cm_ReleaseUser(userp);
626 smb_user_t *smb_FindUID(smb_vc_t *vcp, unsigned short uid, int flags)
630 lock_ObtainWrite(&smb_rctLock);
631 for(uidp = vcp->usersp; uidp; uidp = uidp->nextp) {
632 if (uid == uidp->userID) {
637 if (!uidp && (flags & SMB_FLAG_CREATE)) {
638 uidp = malloc(sizeof(*uidp));
639 memset(uidp, 0, sizeof(*uidp));
640 uidp->nextp = vcp->usersp;
644 lock_InitializeMutex(&uidp->mx, "uid_t mutex");
647 lock_ReleaseWrite(&smb_rctLock);
651 void smb_ReleaseUID(smb_user_t *uidp)
658 lock_ObtainWrite(&smb_rctLock);
659 osi_assert(uidp->refCount-- > 0);
660 if (uidp->refCount == 0 && (uidp->flags & SMB_USERFLAG_DELETE)) {
661 lupp = &uidp->vcp->usersp;
662 for(up = *lupp; up; lupp = &up->nextp, up = *lupp) {
663 if (up == uidp) break;
665 osi_assert(up != NULL);
667 lock_FinalizeMutex(&uidp->mx);
668 userp = uidp->userp; /* remember to drop ref later */
670 lock_ReleaseWrite(&smb_rctLock);
672 cm_ReleaseUserVCRef(userp);
673 cm_ReleaseUser(userp);
677 /* retrieve a held reference to a user structure corresponding to an incoming
679 * corresponding release function is cm_ReleaseUser.
681 cm_user_t *smb_GetUser(smb_vc_t *vcp, smb_packet_t *inp)
687 smbp = (smb_t *) inp;
688 uidp = smb_FindUID(vcp, smbp->uid, 0);
689 if (!uidp) return NULL;
691 lock_ObtainMutex(&uidp->mx);
694 lock_ReleaseMutex(&uidp->mx);
696 smb_ReleaseUID(uidp);
702 * Return a pointer to a pathname extracted from a TID structure. The
703 * TID structure is not held; assume it won't go away.
705 char *smb_GetTIDPath(smb_vc_t *vcp, unsigned short tid)
710 tidp = smb_FindTID(vcp, tid, 0);
711 tpath = tidp->pathname;
712 smb_ReleaseTID(tidp);
716 /* check to see if we have a chained fid, that is, a fid that comes from an
717 * OpenAndX message that ran earlier in this packet. In this case, the fid
718 * field in a read, for example, request, isn't set, since the value is
719 * supposed to be inherited from the openAndX call.
721 int smb_ChainFID(int fid, smb_packet_t *inp)
723 if (inp->fid == 0 || inp->inCount == 0) return fid;
724 else return inp->fid;
727 /* are we a priv'd user? What does this mean on NT? */
728 int smb_SUser(cm_user_t *userp)
733 /* find a file ID. If pass in 0, we'll allocate on on a create operation. */
734 smb_fid_t *smb_FindFID(smb_vc_t *vcp, unsigned short fid, int flags)
739 /* figure out if we need to allocate a new file ID */
742 fid = vcp->fidCounter;
746 lock_ObtainWrite(&smb_rctLock);
748 for(fidp = vcp->fidsp; fidp; fidp = (smb_fid_t *) osi_QNext(&fidp->q)) {
749 if (fid == fidp->fid) {
752 if (fid == 0) fid = 1;
759 if (!fidp && (flags & SMB_FLAG_CREATE)) {
760 fidp = malloc(sizeof(*fidp));
761 memset(fidp, 0, sizeof(*fidp));
762 osi_QAdd((osi_queue_t **)&vcp->fidsp, &fidp->q);
765 lock_InitializeMutex(&fidp->mx, "fid_t mutex");
767 fidp->curr_chunk = fidp->prev_chunk = -2;
768 fidp->raw_write_event = thrd_CreateEvent(NULL, FALSE, TRUE, NULL);
770 vcp->fidCounter = fid+1;
771 if (vcp->fidCounter == 0) vcp->fidCounter = 1;
774 lock_ReleaseWrite(&smb_rctLock);
778 void smb_ReleaseFID(smb_fid_t *fidp)
785 lock_ObtainWrite(&smb_rctLock);
786 osi_assert(fidp->refCount-- > 0);
787 if (fidp->refCount == 0 && (fidp->flags & SMB_FID_DELETE)) {
789 if (!(fidp->flags & SMB_FID_IOCTL))
791 osi_QRemove((osi_queue_t **) &vcp->fidsp, &fidp->q);
792 thrd_CloseHandle(fidp->raw_write_event);
794 /* and see if there is ioctl stuff to free */
795 ioctlp = fidp->ioctlp;
797 if (ioctlp->prefix) cm_FreeSpace(ioctlp->prefix);
798 if (ioctlp->inAllocp) free(ioctlp->inAllocp);
799 if (ioctlp->outAllocp) free(ioctlp->outAllocp);
805 lock_ReleaseWrite(&smb_rctLock);
807 /* now release the scache structure */
808 if (scp) cm_ReleaseSCache(scp);
812 * Case-insensitive search for one string in another;
813 * used to find variable names in submount pathnames.
815 static char *smb_stristr(char *str1, char *str2)
819 for (cursor = str1; *cursor; cursor++)
820 if (stricmp(cursor, str2) == 0)
827 * Substitute a variable value for its name in a submount pathname. Variable
828 * name has been identified by smb_stristr() and is in substr. Variable name
829 * length (plus one) is in substr_size. Variable value is in newstr.
831 static void smb_subst(char *str1, char *substr, unsigned int substr_size,
836 strcpy(temp, substr + substr_size - 1);
837 strcpy(substr, newstr);
841 char VNUserName[] = "%USERNAME%";
842 char VNLCUserName[] = "%LCUSERNAME%";
843 char VNComputerName[] = "%COMPUTERNAME%";
844 char VNLCComputerName[] = "%LCCOMPUTERNAME%";
846 /* List available shares */
859 /*strcpy(shareNameList[num_shares], "all");
860 strcpy(pathNameList[num_shares++], "/afs");*/
861 fprintf(stderr, "The following shares are available:\n");
862 fprintf(stderr, "Share Name (AFS Path)\n");
863 fprintf(stderr, "---------------------\n");
864 fprintf(stderr, "\\\\%s\\%-16s (/afs)\n", smb_localNamep, "ALL");
867 code = GetWindowsDirectory(sbmtpath, sizeof(sbmtpath));
868 if (code == 0 || code > sizeof(sbmtpath)) return -1;
870 strcpy(sbmtpath, cm_confDir);
872 strcat(sbmtpath, "/afsdsbmt.ini");
873 len = GetPrivateProfileString("AFS Submounts", NULL, NULL,
874 shareBuf, sizeof(shareBuf),
880 this_share = shareBuf;
884 /*strcpy(shareNameList[num_shares], this_share);*/
885 len = GetPrivateProfileString("AFS Submounts", this_share,
889 if (!len) return num_shares;
891 if (strncmp(p, "/afs", 4) != 0)
894 if (*p == '\\') *p = '/'; /* change to / */
898 fprintf(stderr, "\\\\%s\\%-16s (%s%s)\n",
899 smb_localNamep, this_share, (print_afs ? "/afs" : "\0"),
902 while (*this_share != NULL) this_share++; /* find next NULL */
903 this_share++; /* skip past the NULL */
904 } while (*this_share != NULL); /* stop at final NULL */
909 /* find a shareName in the table of submounts */
910 int smb_FindShare(smb_vc_t *vcp, smb_packet_t *inp, char *shareName,
922 if (strcmp(shareName, "IPC$") == 0) {
927 if (_stricmp(shareName, "all") == 0) {
933 strcpy(sbmtpath, "afsdsbmt.ini");
935 strcpy(sbmtpath, cm_confDir);
936 strcat(sbmtpath, "/afsdsbmt.ini");
938 len = GetPrivateProfileString("AFS Submounts", shareName, "",
939 pathName, sizeof(pathName), sbmtpath);
940 if (len == 0 || len == sizeof(pathName) - 1) {
945 /* We can accept either unix or PC style AFS pathnames. Convert
946 Unix-style to PC style here for internal use. */
948 if (strncmp(p, "/afs", 4) == 0)
949 p += 4; /* skip /afs */
952 if (*q == '/') *q = '\\'; /* change to \ */
958 if (var = smb_stristr(p, VNUserName)) {
959 uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
960 smb_subst(p, var, sizeof(VNUserName),
962 smb_ReleaseUID(uidp);
964 else if (var = smb_stristr(p, VNLCUserName)) {
965 uidp = smb_FindUID(vcp, ((smb_t *)inp)->uid, 0);
966 strcpy(temp, uidp->name);
968 smb_subst(p, var, sizeof(VNLCUserName), temp);
969 smb_ReleaseUID(uidp);
971 else if (var = smb_stristr(p, VNComputerName)) {
972 sizeTemp = sizeof(temp);
973 GetComputerName((LPTSTR)temp, &sizeTemp);
974 smb_subst(p, var, sizeof(VNComputerName),
977 else if (var = smb_stristr(p, VNLCComputerName)) {
978 sizeTemp = sizeof(temp);
979 GetComputerName((LPTSTR)temp, &sizeTemp);
981 smb_subst(p, var, sizeof(VNLCComputerName),
987 *pathNamep = strdup(p);
991 /* find a dir search structure by cookie value, and return it held.
992 * Must be called with smb_globalLock held.
994 smb_dirSearch_t *smb_FindDirSearchNL(long cookie)
996 smb_dirSearch_t *dsp;
998 for(dsp = smb_firstDirSearchp; dsp; dsp = (smb_dirSearch_t *) osi_QNext(&dsp->q)) {
999 if (dsp->cookie == cookie) {
1000 if (dsp != smb_firstDirSearchp) {
1001 /* move to head of LRU queue, too, if we're not already there */
1002 if (smb_lastDirSearchp == (smb_dirSearch_t *) &dsp->q)
1003 smb_lastDirSearchp = (smb_dirSearch_t *)
1005 osi_QRemove((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
1006 osi_QAdd((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
1007 if (!smb_lastDirSearchp)
1008 smb_lastDirSearchp = (smb_dirSearch_t *) &dsp->q;
1017 void smb_DeleteDirSearch(smb_dirSearch_t *dsp)
1019 lock_ObtainWrite(&smb_globalLock);
1020 dsp->flags |= SMB_DIRSEARCH_DELETE;
1021 lock_ReleaseWrite(&smb_globalLock);
1022 lock_ObtainMutex(&dsp->mx);
1023 if(dsp->scp != NULL) {
1024 lock_ObtainMutex(&dsp->scp->mx);
1025 if (dsp->flags & SMB_DIRSEARCH_BULKST) {
1026 dsp->flags &= ~SMB_DIRSEARCH_BULKST;
1027 dsp->scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
1028 dsp->scp->bulkStatProgress = hones;
1030 lock_ReleaseMutex(&dsp->scp->mx);
1032 lock_ReleaseMutex(&dsp->mx);
1035 void smb_ReleaseDirSearch(smb_dirSearch_t *dsp)
1041 lock_ObtainWrite(&smb_globalLock);
1042 osi_assert(dsp->refCount-- > 0);
1043 if (dsp->refCount == 0 && (dsp->flags & SMB_DIRSEARCH_DELETE)) {
1044 if (&dsp->q == (osi_queue_t *) smb_lastDirSearchp)
1045 smb_lastDirSearchp = (smb_dirSearch_t *) osi_QPrev(&smb_lastDirSearchp->q);
1046 osi_QRemove((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
1047 lock_FinalizeMutex(&dsp->mx);
1051 lock_ReleaseWrite(&smb_globalLock);
1053 /* do this now to avoid spurious locking hierarchy creation */
1054 if (scp) cm_ReleaseSCache(scp);
1057 /* find a dir search structure by cookie value, and return it held */
1058 smb_dirSearch_t *smb_FindDirSearch(long cookie)
1060 smb_dirSearch_t *dsp;
1062 lock_ObtainWrite(&smb_globalLock);
1063 dsp = smb_FindDirSearchNL(cookie);
1064 lock_ReleaseWrite(&smb_globalLock);
1068 /* GC some dir search entries, in the address space expected by the specific protocol.
1069 * Must be called with smb_globalLock held; release the lock temporarily.
1071 #define SMB_DIRSEARCH_GCMAX 10 /* how many at once */
1072 void smb_GCDirSearches(int isV3)
1074 smb_dirSearch_t *prevp;
1075 smb_dirSearch_t *tp;
1076 smb_dirSearch_t *victimsp[SMB_DIRSEARCH_GCMAX];
1080 victimCount = 0; /* how many have we got so far */
1081 for(tp = smb_lastDirSearchp; tp; tp=prevp) {
1082 prevp = (smb_dirSearch_t *) osi_QPrev(&tp->q); /* we'll move tp from queue, so
1085 /* if no one is using this guy, and we're either in the new protocol,
1086 * or we're in the old one and this is a small enough ID to be useful
1087 * to the old protocol, GC this guy.
1089 if (tp->refCount == 0 && (isV3 || tp->cookie <= 255)) {
1090 /* hold and delete */
1091 tp->flags |= SMB_DIRSEARCH_DELETE;
1092 victimsp[victimCount++] = tp;
1096 /* don't do more than this */
1097 if (victimCount >= SMB_DIRSEARCH_GCMAX) break;
1100 /* now release them */
1101 lock_ReleaseWrite(&smb_globalLock);
1102 for(i = 0; i < victimCount; i++) {
1103 smb_ReleaseDirSearch(victimsp[i]);
1105 lock_ObtainWrite(&smb_globalLock);
1108 /* function for allocating a dir search entry. We need these to remember enough context
1109 * since we don't get passed the path from call to call during a directory search.
1111 * Returns a held dir search structure, and bumps the reference count on the vnode,
1112 * since it saves a pointer to the vnode.
1114 smb_dirSearch_t *smb_NewDirSearch(int isV3)
1116 smb_dirSearch_t *dsp;
1120 lock_ObtainWrite(&smb_globalLock);
1123 /* what's the biggest ID allowed in this version of the protocol */
1124 if (isV3) maxAllowed = 65535;
1125 else maxAllowed = 255;
1128 /* twice so we have enough tries to find guys we GC after one pass;
1129 * 10 extra is just in case I mis-counted.
1131 if (++counter > 2*maxAllowed+10) osi_panic("afsd: dir search cookie leak",
1132 __FILE__, __LINE__);
1133 if (smb_dirSearchCounter > maxAllowed) {
1134 smb_dirSearchCounter = 1;
1135 smb_GCDirSearches(isV3); /* GC some (drops global lock) */
1137 dsp = smb_FindDirSearchNL(smb_dirSearchCounter);
1139 /* don't need to watch for refcount zero and deleted, since
1140 * we haven't dropped the global lock.
1143 ++smb_dirSearchCounter;
1147 dsp = malloc(sizeof(*dsp));
1148 memset(dsp, 0, sizeof(*dsp));
1149 osi_QAdd((osi_queue_t **) &smb_firstDirSearchp, &dsp->q);
1150 if (!smb_lastDirSearchp) smb_lastDirSearchp = (smb_dirSearch_t *) &dsp->q;
1151 dsp->cookie = smb_dirSearchCounter;
1152 ++smb_dirSearchCounter;
1154 lock_InitializeMutex(&dsp->mx, "cm_dirSearch_t");
1155 dsp->lastTime = osi_Time();
1158 lock_ReleaseWrite(&smb_globalLock);
1162 static smb_packet_t *GetPacket(void)
1166 unsigned int npar, seg, tb_sel;
1169 lock_ObtainWrite(&smb_globalLock);
1170 tbp = smb_packetFreeListp;
1171 if (tbp) smb_packetFreeListp = tbp->nextp;
1172 lock_ReleaseWrite(&smb_globalLock);
1175 tbp = GlobalAlloc(GMEM_FIXED, 65540);
1177 tbp = malloc(sizeof(smb_packet_t));
1179 tbp->magic = SMB_PACKETMAGIC;
1182 tbp->resumeCode = 0;
1188 tbp->ncb_length = 0;
1192 npar = SMB_PACKETSIZE >> 4; /* number of paragraphs */
1195 __dpmi_allocate_dos_memory(npar, &tb_sel); /* DOS segment */
1197 afsi_log("Cannot allocate %d paragraphs of DOS memory",
1199 osi_panic("",__FILE__,__LINE__);
1202 afsi_log("Allocated %d paragraphs of DOS mem at 0x%X",
1207 tbp->dos_pkt = (seg * 16) + 0; /* DOS physical address */
1208 tbp->dos_pkt_sel = tb_sel;
1211 osi_assert(tbp->magic == SMB_PACKETMAGIC);
1216 smb_packet_t *smb_CopyPacket(smb_packet_t *pkt)
1220 memcpy(tbp, pkt, sizeof(smb_packet_t));
1221 tbp->wctp = tbp->data + ((unsigned int)pkt->wctp -
1222 (unsigned int)pkt->data);
1226 static NCB *GetNCB(void)
1231 unsigned int npar, seg, tb_sel;
1234 lock_ObtainWrite(&smb_globalLock);
1235 tbp = smb_ncbFreeListp;
1236 if (tbp) smb_ncbFreeListp = tbp->nextp;
1237 lock_ReleaseWrite(&smb_globalLock);
1240 tbp = GlobalAlloc(GMEM_FIXED, sizeof(*tbp));
1242 tbp = malloc(sizeof(*tbp));
1243 npar = (sizeof(NCB)+15) >> 4; /* number of paragraphs */
1246 __dpmi_allocate_dos_memory(npar, &tb_sel); /* DOS segment */
1248 afsi_log("Cannot allocate %d paragraphs of DOS mem in GetNCB",
1250 osi_panic("",__FILE__,__LINE__);
1252 afsi_log("Allocated %d paragraphs of DOS mem at 0x%X in GetNCB",
1257 tbp->dos_ncb = (seg * 16) + 0; /* DOS physical address */
1258 tbp->dos_ncb_sel = tb_sel;
1260 tbp->magic = SMB_NCBMAGIC;
1263 osi_assert(tbp->magic == SMB_NCBMAGIC);
1265 memset(&tbp->ncb, 0, sizeof(NCB));
1268 dos_memset(tbp->dos_ncb, 0, sizeof(NCB));
1273 void smb_FreePacket(smb_packet_t *tbp)
1275 osi_assert(tbp->magic == SMB_PACKETMAGIC);
1277 lock_ObtainWrite(&smb_globalLock);
1278 tbp->nextp = smb_packetFreeListp;
1279 smb_packetFreeListp = tbp;
1280 tbp->magic = SMB_PACKETMAGIC;
1283 tbp->resumeCode = 0;
1289 tbp->ncb_length = 0;
1291 lock_ReleaseWrite(&smb_globalLock);
1294 static void FreeNCB(NCB *bufferp)
1298 tbp = (smb_ncb_t *) bufferp;
1299 osi_assert(tbp->magic == SMB_NCBMAGIC);
1301 lock_ObtainWrite(&smb_globalLock);
1302 tbp->nextp = smb_ncbFreeListp;
1303 smb_ncbFreeListp = tbp;
1304 lock_ReleaseWrite(&smb_globalLock);
1307 /* get a ptr to the data part of a packet, and its count */
1308 unsigned char *smb_GetSMBData(smb_packet_t *smbp, int *nbytesp)
1312 unsigned char *afterParmsp;
1314 parmBytes = *smbp->wctp << 1;
1315 afterParmsp = smbp->wctp + parmBytes + 1;
1317 dataBytes = afterParmsp[0] + (afterParmsp[1]<<8);
1318 if (nbytesp) *nbytesp = dataBytes;
1320 /* don't forget to skip the data byte count, since it follows
1321 * the parameters; that's where the "2" comes from below.
1323 return (unsigned char *) (afterParmsp + 2);
1326 /* must set all the returned parameters before playing around with the
1327 * data region, since the data region is located past the end of the
1328 * variable number of parameters.
1330 void smb_SetSMBDataLength(smb_packet_t *smbp, unsigned int dsize)
1332 unsigned char *afterParmsp;
1334 afterParmsp = smbp->wctp + ((*smbp->wctp)<<1) + 1;
1336 *afterParmsp++ = dsize & 0xff;
1337 *afterParmsp = (dsize>>8) & 0xff;
1340 /* return the parm'th parameter in the smbp packet */
1341 unsigned int smb_GetSMBParm(smb_packet_t *smbp, int parm)
1344 unsigned char *parmDatap;
1346 parmCount = *smbp->wctp;
1348 if (parm >= parmCount) {
1353 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1354 sprintf(s, "Bad SMB param %d out of %d, ncb len %d",
1355 parm, parmCount, smbp->ncb_length);
1357 ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1006, NULL,
1358 1, smbp->ncb_length, ptbuf, smbp);
1359 DeregisterEventSource(h);
1363 sprintf(s, "Bad SMB param %d out of %d, ncb len %d",
1364 parm, parmCount, smbp->ncb_length);
1365 osi_Log0(afsd_logp, s);
1367 osi_panic(s, __FILE__, __LINE__);
1369 parmDatap = smbp->wctp + (2*parm) + 1;
1371 return parmDatap[0] + (parmDatap[1] << 8);
1374 /* return the parm'th parameter in the smbp packet */
1375 unsigned int smb_GetSMBOffsetParm(smb_packet_t *smbp, int parm, int offset)
1378 unsigned char *parmDatap;
1380 parmCount = *smbp->wctp;
1382 if (parm * 2 + offset >= parmCount * 2) {
1387 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1388 sprintf(s, "Bad SMB param %d offset %d out of %d, ncb len %d",
1389 parm, offset, parmCount, smbp->ncb_length);
1391 ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1006, NULL,
1392 1, smbp->ncb_length, ptbuf, smbp);
1393 DeregisterEventSource(h);
1397 sprintf(s, "Bad SMB param %d offset %d out of %d, "
1399 parm, offset, parmCount, smbp->ncb_length);
1400 osi_Log0(afsd_logp, s);
1403 osi_panic(s, __FILE__, __LINE__);
1405 parmDatap = smbp->wctp + (2*parm) + 1 + offset;
1407 return parmDatap[0] + (parmDatap[1] << 8);
1410 void smb_SetSMBParm(smb_packet_t *smbp, int slot, unsigned int parmValue)
1414 /* make sure we have enough slots */
1415 if (*smbp->wctp <= slot) *smbp->wctp = slot+1;
1417 parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
1418 *parmDatap++ = parmValue & 0xff;
1419 *parmDatap = (parmValue>>8) & 0xff;
1422 void smb_SetSMBParmLong(smb_packet_t *smbp, int slot, unsigned int parmValue)
1426 /* make sure we have enough slots */
1427 if (*smbp->wctp <= slot) *smbp->wctp = slot+2;
1429 parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
1430 *parmDatap++ = parmValue & 0xff;
1431 *parmDatap++ = (parmValue>>8) & 0xff;
1432 *parmDatap++ = (parmValue>>16) & 0xff;
1433 *parmDatap++ = (parmValue>>24) & 0xff;
1436 void smb_SetSMBParmDouble(smb_packet_t *smbp, int slot, char *parmValuep)
1441 /* make sure we have enough slots */
1442 if (*smbp->wctp <= slot) *smbp->wctp = slot+4;
1444 parmDatap = smbp->wctp + 2*slot + 1 + smbp->oddByte;
1446 *parmDatap++ = *parmValuep++;
1449 void smb_SetSMBParmByte(smb_packet_t *smbp, int slot, unsigned int parmValue)
1453 /* make sure we have enough slots */
1454 if (*smbp->wctp <= slot) {
1455 if (smbp->oddByte) {
1457 *smbp->wctp = slot+1;
1462 parmDatap = smbp->wctp + 2*slot + 1 + (1 - smbp->oddByte);
1463 *parmDatap++ = parmValue & 0xff;
1466 void smb_StripLastComponent(char *outPathp, char **lastComponentp, char *inPathp)
1470 lastSlashp = strrchr(inPathp, '\\');
1472 *lastComponentp = lastSlashp;
1475 if (inPathp == lastSlashp) break;
1476 *outPathp++ = *inPathp++;
1485 unsigned char *smb_ParseASCIIBlock(unsigned char *inp, char **chainpp)
1487 if (*inp++ != 0x4) return NULL;
1489 *chainpp = inp + strlen(inp) + 1; /* skip over null-terminated string */
1494 unsigned char *smb_ParseVblBlock(unsigned char *inp, char **chainpp, int *lengthp)
1498 if (*inp++ != 0x5) return NULL;
1499 tlen = inp[0] + (inp[1]<<8);
1500 inp += 2; /* skip length field */
1503 *chainpp = inp + tlen;
1506 if (lengthp) *lengthp = tlen;
1511 /* format a packet as a response */
1512 void smb_FormatResponsePacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *op)
1517 outp = (smb_t *) op;
1519 /* zero the basic structure through the smb_wct field, and zero the data
1520 * size field, assuming that wct stays zero; otherwise, you have to
1521 * explicitly set the data size field, too.
1523 inSmbp = (smb_t *) inp;
1524 memset(outp, 0, sizeof(smb_t)+2);
1530 outp->com = inSmbp->com;
1531 outp->tid = inSmbp->tid;
1532 outp->pid = inSmbp->pid;
1533 outp->uid = inSmbp->uid;
1534 outp->mid = inSmbp->mid;
1535 outp->res[0] = inSmbp->res[0];
1536 outp->res[1] = inSmbp->res[1];
1537 op->inCom = inSmbp->com;
1539 outp->reb = 0x80; /* SERVER_RESP */
1540 outp->flg2 = 0x1; /* KNOWS_LONG_NAMES */
1542 /* copy fields in generic packet area */
1543 op->wctp = &outp->wct;
1546 /* send a (probably response) packet; vcp tells us to whom to send it.
1547 * we compute the length by looking at wct and bcc fields.
1549 void smb_SendPacket(smb_vc_t *vcp, smb_packet_t *inp)
1566 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
1569 memset((char *)ncbp, 0, sizeof(NCB));
1571 extra = 2 * (*inp->wctp); /* space used by parms, in bytes */
1572 tp = inp->wctp + 1+ extra; /* points to count of data bytes */
1573 extra += tp[0] + (tp[1]<<8);
1574 extra += ((unsigned int)inp->wctp - (unsigned int)inp->data); /* distance to last wct field */
1575 extra += 3; /* wct and length fields */
1577 ncbp->ncb_length = extra; /* bytes to send */
1578 ncbp->ncb_lsn = (unsigned char) vcp->lsn; /* vc to use */
1579 ncbp->ncb_lana_num = smb_LANadapter;
1580 ncbp->ncb_command = NCBSEND; /* op means send data */
1582 ncbp->ncb_buffer = (char *) inp;/* packet */
1583 code = Netbios(ncbp);
1585 ncbp->ncb_buffer = inp->dos_pkt;/* packet */
1586 ((smb_ncb_t*)ncbp)->orig_pkt = inp;
1588 /* copy header information from virtual to DOS address space */
1589 dosmemput((char*)inp, SMB_PACKETSIZE, inp->dos_pkt);
1590 code = Netbios(ncbp, dos_ncb);
1594 osi_Log1(afsd_logp, "SendPacket failure code %d", code);
1600 void smb_MapNTError(long code, unsigned long *NTStatusp)
1602 unsigned long NTStatus;
1604 /* map CM_ERROR_* errors to NT 32-bit status codes */
1605 if (code == CM_ERROR_NOSUCHCELL) {
1606 NTStatus = 0xC000000FL; /* No such file */
1608 else if (code == CM_ERROR_NOSUCHVOLUME) {
1609 NTStatus = 0xC000000FL; /* No such file */
1611 else if (code == CM_ERROR_TIMEDOUT) {
1612 NTStatus = 0xC00000CFL; /* Paused */
1614 else if (code == CM_ERROR_RETRY) {
1615 NTStatus = 0xC000022DL; /* Retry */
1617 else if (code == CM_ERROR_NOACCESS) {
1618 NTStatus = 0xC0000022L; /* Access denied */
1620 else if (code == CM_ERROR_READONLY) {
1621 NTStatus = 0xC00000A2L; /* Write protected */
1623 else if (code == CM_ERROR_NOSUCHFILE) {
1624 NTStatus = 0xC000000FL; /* No such file */
1626 else if (code == CM_ERROR_NOSUCHPATH) {
1627 NTStatus = 0xC000003AL; /* Object path not found */
1629 else if (code == CM_ERROR_TOOBIG) {
1630 NTStatus = 0xC000007BL; /* Invalid image format */
1632 else if (code == CM_ERROR_INVAL) {
1633 NTStatus = 0xC000000DL; /* Invalid parameter */
1635 else if (code == CM_ERROR_BADFD) {
1636 NTStatus = 0xC0000008L; /* Invalid handle */
1638 else if (code == CM_ERROR_BADFDOP) {
1639 NTStatus = 0xC0000022L; /* Access denied */
1641 else if (code == CM_ERROR_EXISTS) {
1642 NTStatus = 0xC0000035L; /* Object name collision */
1644 else if (code == CM_ERROR_NOTEMPTY) {
1645 NTStatus = 0xC0000101L; /* Directory not empty */
1647 else if (code == CM_ERROR_CROSSDEVLINK) {
1648 NTStatus = 0xC00000D4L; /* Not same device */
1650 else if (code == CM_ERROR_NOTDIR) {
1651 NTStatus = 0xC0000103L; /* Not a directory */
1653 else if (code == CM_ERROR_ISDIR) {
1654 NTStatus = 0xC00000BAL; /* File is a directory */
1656 else if (code == CM_ERROR_BADOP) {
1657 NTStatus = 0xC09820FFL; /* SMB no support */
1659 else if (code == CM_ERROR_BADSHARENAME) {
1660 NTStatus = 0xC00000CCL; /* Bad network name */
1662 else if (code == CM_ERROR_NOIPC) {
1663 NTStatus = 0xC00000CCL; /* Bad network name */
1665 else if (code == CM_ERROR_CLOCKSKEW) {
1666 NTStatus = 0xC0000133L; /* Time difference at DC */
1668 else if (code == CM_ERROR_BADTID) {
1669 NTStatus = 0xC0982005L; /* SMB bad TID */
1671 else if (code == CM_ERROR_USESTD) {
1672 NTStatus = 0xC09820FBL; /* SMB use standard */
1674 else if (code == CM_ERROR_QUOTA) {
1675 NTStatus = 0xC0000044L; /* Quota exceeded */
1677 else if (code == CM_ERROR_SPACE) {
1678 NTStatus = 0xC000007FL; /* Disk full */
1680 else if (code == CM_ERROR_ATSYS) {
1681 NTStatus = 0xC0000033L; /* Object name invalid */
1683 else if (code == CM_ERROR_BADNTFILENAME) {
1684 NTStatus = 0xC0000033L; /* Object name invalid */
1686 else if (code == CM_ERROR_WOULDBLOCK) {
1687 NTStatus = 0xC0000055L; /* Lock not granted */
1689 else if (code == CM_ERROR_PARTIALWRITE) {
1690 NTStatus = 0xC000007FL; /* Disk full */
1692 else if (code == CM_ERROR_BUFFERTOOSMALL) {
1693 NTStatus = 0xC0000023L; /* Buffer too small */
1696 NTStatus = 0xC0982001L; /* SMB non-specific error */
1699 *NTStatusp = NTStatus;
1700 osi_Log2(afsd_logp, "SMB SEND code %x as NT %x", code, NTStatus);
1703 void smb_MapCoreError(long code, smb_vc_t *vcp, unsigned short *scodep,
1704 unsigned char *classp)
1706 unsigned char class;
1707 unsigned short error;
1709 /* map CM_ERROR_* errors to SMB errors */
1710 if (code == CM_ERROR_NOSUCHCELL) {
1712 error = 3; /* bad path */
1714 else if (code == CM_ERROR_NOSUCHVOLUME) {
1716 error = 3; /* bad path */
1718 else if (code == CM_ERROR_TIMEDOUT) {
1720 error = 81; /* server is paused */
1722 else if (code == CM_ERROR_RETRY) {
1723 class = 2; /* shouldn't happen */
1726 else if (code == CM_ERROR_NOACCESS) {
1728 error = 4; /* bad access */
1730 else if (code == CM_ERROR_READONLY) {
1732 error = 19; /* read only */
1734 else if (code == CM_ERROR_NOSUCHFILE) {
1736 error = 2; /* ENOENT! */
1738 else if (code == CM_ERROR_NOSUCHPATH) {
1740 error = 3; /* Bad path */
1742 else if (code == CM_ERROR_TOOBIG) {
1744 error = 11; /* bad format */
1746 else if (code == CM_ERROR_INVAL) {
1747 class = 2; /* server non-specific error code */
1750 else if (code == CM_ERROR_BADFD) {
1752 error = 6; /* invalid file handle */
1754 else if (code == CM_ERROR_BADFDOP) {
1755 class = 1; /* invalid op on FD */
1758 else if (code == CM_ERROR_EXISTS) {
1760 error = 80; /* file already exists */
1762 else if (code == CM_ERROR_NOTEMPTY) {
1764 error = 5; /* delete directory not empty */
1766 else if (code == CM_ERROR_CROSSDEVLINK) {
1768 error = 17; /* EXDEV */
1770 else if (code == CM_ERROR_NOTDIR) {
1771 class = 1; /* bad path */
1774 else if (code == CM_ERROR_ISDIR) {
1775 class = 1; /* access denied; DOS doesn't have a good match */
1778 else if (code == CM_ERROR_BADOP) {
1782 else if (code == CM_ERROR_BADSHARENAME) {
1786 else if (code == CM_ERROR_NOIPC) {
1790 else if (code == CM_ERROR_CLOCKSKEW) {
1791 class = 1; /* invalid function */
1794 else if (code == CM_ERROR_BADTID) {
1798 else if (code == CM_ERROR_USESTD) {
1802 else if (code == CM_ERROR_REMOTECONN) {
1806 else if (code == CM_ERROR_QUOTA) {
1807 if (vcp->flags & SMB_VCFLAG_USEV3) {
1809 error = 39; /* disk full */
1813 error = 5; /* access denied */
1816 else if (code == CM_ERROR_SPACE) {
1817 if (vcp->flags & SMB_VCFLAG_USEV3) {
1819 error = 39; /* disk full */
1823 error = 5; /* access denied */
1826 else if (code == CM_ERROR_PARTIALWRITE) {
1828 error = 39; /* disk full */
1830 else if (code == CM_ERROR_ATSYS) {
1832 error = 2; /* ENOENT */
1834 else if (code == CM_ERROR_WOULDBLOCK) {
1836 error = 33; /* lock conflict */
1838 else if (code == CM_ERROR_NOFILES) {
1840 error = 18; /* no files in search */
1842 else if (code == CM_ERROR_RENAME_IDENTICAL) {
1844 error = 183; /* Samba uses this */
1853 osi_Log3(afsd_logp, "SMB SEND code %x as SMB %d: %d", code, class, error);
1856 long smb_SendCoreBadOp(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
1858 return CM_ERROR_BADOP;
1861 long smb_ReceiveCoreEcho(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
1863 unsigned short EchoCount, i;
1864 char *data, *outdata;
1867 EchoCount = (unsigned short) smb_GetSMBParm(inp, 0);
1869 for (i=1; i<=EchoCount; i++) {
1870 data = smb_GetSMBData(inp, &dataSize);
1871 smb_SetSMBParm(outp, 0, i);
1872 smb_SetSMBDataLength(outp, dataSize);
1873 outdata = smb_GetSMBData(outp, NULL);
1874 memcpy(outdata, data, dataSize);
1875 smb_SendPacket(vcp, outp);
1881 long smb_ReceiveCoreReadRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
1884 long count, minCount, finalCount;
1888 cm_user_t *userp = NULL;
1892 char *rawBuf = NULL;
1894 dos_ptr rawBuf = NULL;
1901 fd = smb_GetSMBParm(inp, 0);
1902 count = smb_GetSMBParm(inp, 3);
1903 minCount = smb_GetSMBParm(inp, 4);
1904 offset.HighPart = 0; /* too bad */
1905 offset.LowPart = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
1907 osi_Log3(afsd_logp, "smb_ReceieveCoreReadRaw fd %d, off 0x%x, size 0x%x",
1908 fd, offset.LowPart, count);
1910 fidp = smb_FindFID(vcp, fd, 0);
1914 lock_ObtainMutex(&smb_RawBufLock);
1916 /* Get a raw buf, from head of list */
1917 rawBuf = smb_RawBufs;
1919 smb_RawBufs = *(char **)smb_RawBufs;
1921 smb_RawBufs = _farpeekl(_dos_ds, smb_RawBufs);
1924 lock_ReleaseMutex(&smb_RawBufLock);
1928 if (fidp->flags & SMB_FID_IOCTL)
1931 rc = smb_IoctlReadRaw(fidp, vcp, inp, outp);
1933 rc = smb_IoctlReadRaw(fidp, vcp, inp, outp, rawBuf);
1936 /* Give back raw buffer */
1937 lock_ObtainMutex(&smb_RawBufLock);
1939 *((char **) rawBuf) = smb_RawBufs;
1941 _farpokel(_dos_ds, rawBuf, smb_RawBufs);
1944 smb_RawBufs = rawBuf;
1945 lock_ReleaseMutex(&smb_RawBufLock);
1950 userp = smb_GetUser(vcp, inp);
1953 code = smb_ReadData(fidp, &offset, count, rawBuf, userp, &finalCount);
1955 /* have to give ReadData flag so it will treat buffer as DOS mem. */
1956 code = smb_ReadData(fidp, &offset, count, (unsigned char *)rawBuf,
1957 userp, &finalCount, TRUE /* rawFlag */);
1964 cm_ReleaseUser(userp);
1966 smb_ReleaseFID(fidp);
1971 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
1973 memset((char *)ncbp, 0, sizeof(NCB));
1975 ncbp->ncb_length = (unsigned short) finalCount;
1976 ncbp->ncb_lsn = (unsigned char) vcp->lsn;
1977 ncbp->ncb_lana_num = smb_LANadapter;
1978 ncbp->ncb_command = NCBSEND;
1979 ncbp->ncb_buffer = rawBuf;
1982 code = Netbios(ncbp);
1984 code = Netbios(ncbp, dos_ncb);
1987 osi_Log1(afsd_logp, "ReadRaw send failure code %d", code);
1990 /* Give back raw buffer */
1991 lock_ObtainMutex(&smb_RawBufLock);
1993 *((char **) rawBuf) = smb_RawBufs;
1995 _farpokel(_dos_ds, rawBuf, smb_RawBufs);
1998 smb_RawBufs = rawBuf;
1999 lock_ReleaseMutex(&smb_RawBufLock);
2005 long smb_ReceiveCoreLockRecord(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2010 long smb_ReceiveCoreUnlockRecord(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2015 long smb_ReceiveNegotiate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2021 int protoIndex; /* index we're using */
2026 char protocol_array[10][1024]; /* protocol signature of the client */
2029 osi_Log1(afsd_logp, "SMB receive negotiate; %d + 1 ongoing ops",
2033 DWORD now = GetCurrentTime();
2034 if (now - last_msg_time >= 30000
2035 && now - last_msg_time <= 90000) {
2037 "Setting dead_vcp %x", active_vcp);
2038 dead_vcp = active_vcp;
2039 dead_vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
2044 inp->flags |= SMB_PACKETFLAG_PROFILE_UPDATE_OK;
2046 namep = smb_GetSMBData(inp, &dbytes);
2049 coreProtoIndex = -1; /* not found */
2052 while(namex < dbytes) {
2053 osi_Log1(afsd_logp, "Protocol %s",
2054 osi_LogSaveString(afsd_logp, namep+1));
2055 strcpy(protocol_array[tcounter], namep+1);
2057 /* namep points at the first protocol, or really, a 0x02
2058 * byte preceding the null-terminated ASCII name.
2060 if (strcmp("PC NETWORK PROGRAM 1.0", namep+1) == 0) {
2061 coreProtoIndex = tcounter;
2063 else if (smb_useV3 && strcmp("LM1.2X002", namep+1) == 0) {
2064 v3ProtoIndex = tcounter;
2066 else if (smb_useV3 && strcmp("NT LM 0.12", namep+1) == 0) {
2067 NTProtoIndex = tcounter;
2070 /* compute size of protocol entry */
2071 entryLength = strlen(namep+1);
2072 entryLength += 2; /* 0x02 bytes and null termination */
2074 /* advance over this protocol entry */
2075 namex += entryLength;
2076 namep += entryLength;
2077 tcounter++; /* which proto entry we're looking at */
2080 * NOTE: We can determine what OS (NT4.0, W2K, W9X, etc)
2081 * the client is running by reading the protocol signature.
2082 * ie. the order in which it sends us the protocol list.
2084 * Special handling for Windows 2000 clients (defect 11765 )
2086 if (tcounter == 6) {
2088 smb_t *ip = (smb_t *) inp;
2089 smb_t *op = (smb_t *) outp;
2091 if ((strcmp("PC NETWORK PROGRAM 1.0", protocol_array[0]) == 0) &&
2092 (strcmp("LANMAN1.0", protocol_array[1]) == 0) &&
2093 (strcmp("Windows for Workgroups 3.1a", protocol_array[2]) == 0) &&
2094 (strcmp("LM1.2X002", protocol_array[3]) == 0) &&
2095 (strcmp("LANMAN2.1", protocol_array[4]) == 0) &&
2096 (strcmp("NT LM 0.12", protocol_array[5]) == 0)) {
2097 isWindows2000 = TRUE;
2098 osi_Log0(afsd_logp, "Looks like a Windows 2000 client");
2100 * HACK: for now - just negotiate a lower protocol till we
2101 * figure out which flag/flag2 or some other field
2102 * (capabilities maybe?) to set in order for this to work
2103 * correctly with Windows 2000 clients (defect 11765)
2106 /* Things to try (after looking at tcpdump output could be
2107 * setting flags and flags2 to 0x98 and 0xc853 like this
2108 * op->reb = 0x98; op->flg2 = 0xc853;
2109 * osi_Log2(afsd_logp, "Flags:0x%x Flags2:0x%x", ip->reb, ip->flg2);
2114 if (NTProtoIndex != -1) {
2115 protoIndex = NTProtoIndex;
2116 vcp->flags |= (SMB_VCFLAG_USENT | SMB_VCFLAG_USEV3);
2118 else if (v3ProtoIndex != -1) {
2119 protoIndex = v3ProtoIndex;
2120 vcp->flags |= SMB_VCFLAG_USEV3;
2122 else if (coreProtoIndex != -1) {
2123 protoIndex = coreProtoIndex;
2124 vcp->flags |= SMB_VCFLAG_USECORE;
2126 else protoIndex = -1;
2128 if (protoIndex == -1)
2129 return CM_ERROR_INVAL;
2130 else if (NTProtoIndex != -1) {
2131 smb_SetSMBParm(outp, 0, protoIndex);
2132 smb_SetSMBParmByte(outp, 1, 0); /* share level security, no passwd encrypt */
2133 smb_SetSMBParm(outp, 1, 8); /* max multiplexed requests */
2134 smb_SetSMBParm(outp, 2, 100); /* max VCs per consumer/server connection */
2135 smb_SetSMBParmLong(outp, 3, SMB_PACKETSIZE); /* xmit buffer size */
2136 smb_SetSMBParmLong(outp, 5, 65536); /* raw buffer size */
2137 smb_SetSMBParm(outp, 7, 1); /* next 2: session key */
2138 smb_SetSMBParm(outp, 8, 1);
2140 * Tried changing the capabilities to support for W2K - defect 117695
2141 * Maybe something else needs to be changed here?
2145 smb_SetSMBParmLong(outp, 9, 0x43fd);
2147 smb_SetSMBParmLong(outp, 9, 0x251);
2149 smb_SetSMBParmLong(outp, 9, 0x251); /* Capabilities: *
2150 * 32-bit error codes *
2154 smb_SetSMBParmLong(outp, 11, 0);/* XXX server time: do we need? */
2155 smb_SetSMBParmLong(outp, 13, 0);/* XXX server date: do we need? */
2156 smb_SetSMBParm(outp, 15, 0); /* XXX server tzone: do we need? */
2157 smb_SetSMBParmByte(outp, 16, 0);/* Encryption key length */
2158 smb_SetSMBDataLength(outp, 0); /* perhaps should specify 8 bytes anyway */
2160 else if (v3ProtoIndex != -1) {
2161 smb_SetSMBParm(outp, 0, protoIndex);
2162 smb_SetSMBParm(outp, 1, 0); /* share level security, no passwd encrypt */
2163 smb_SetSMBParm(outp, 2, SMB_PACKETSIZE);
2164 smb_SetSMBParm(outp, 3, 8); /* max multiplexed requests */
2165 smb_SetSMBParm(outp, 4, 100); /* max VCs per consumer/server connection */
2166 smb_SetSMBParm(outp, 5, 0); /* no support of block mode for read or write */
2167 smb_SetSMBParm(outp, 6, 1); /* next 2: session key */
2168 smb_SetSMBParm(outp, 7, 1);
2169 smb_SetSMBParm(outp, 8, 0); /* XXX server time: do we need? */
2170 smb_SetSMBParm(outp, 9, 0); /* XXX server date: do we need? */
2171 smb_SetSMBParm(outp, 10, 0); /* XXX server tzone: do we need? */
2172 smb_SetSMBParm(outp, 11, 0); /* resvd */
2173 smb_SetSMBParm(outp, 12, 0); /* resvd */
2174 smb_SetSMBDataLength(outp, 0); /* perhaps should specify 8 bytes anyway */
2176 else if (coreProtoIndex != -1) {
2177 smb_SetSMBParm(outp, 0, protoIndex);
2178 smb_SetSMBDataLength(outp, 0);
2183 void smb_Daemon(void *parmp)
2190 if ((count % 360) == 0) /* every hour */
2191 smb_CalculateNowTZ();
2192 /* XXX GC dir search entries */
2196 void smb_WaitingLocksDaemon()
2198 smb_waitingLock_t *wL, *nwL;
2201 smb_packet_t *inp, *outp;
2206 lock_ObtainWrite(&smb_globalLock);
2207 nwL = smb_allWaitingLocks;
2209 osi_SleepW((long)&smb_allWaitingLocks, &smb_globalLock);
2218 lock_ObtainWrite(&smb_globalLock);
2220 nwL = (smb_waitingLock_t *) osi_QNext(&wL->q);
2221 lock_ReleaseWrite(&smb_globalLock);
2222 code = cm_RetryLock((cm_file_lock_t *) wL->lockp,
2223 wL->vcp->flags & SMB_VCFLAG_ALREADYDEAD);
2224 if (code == CM_ERROR_WOULDBLOCK) {
2226 if (wL->timeRemaining != 0xffffffff
2227 && (wL->timeRemaining -= 1000) < 0)
2236 ncbp->ncb_length = inp->ncb_length;
2237 inp->spacep = cm_GetSpace();
2239 /* Remove waitingLock from list */
2240 lock_ObtainWrite(&smb_globalLock);
2241 osi_QRemove((osi_queue_t **)&smb_allWaitingLocks,
2243 lock_ReleaseWrite(&smb_globalLock);
2245 /* Resume packet processing */
2247 smb_SetSMBDataLength(outp, 0);
2248 outp->flags |= SMB_PACKETFLAG_SUSPENDED;
2249 outp->resumeCode = code;
2251 smb_DispatchPacket(vcp, inp, outp, ncbp, NULL);
2254 cm_FreeSpace(inp->spacep);
2255 smb_FreePacket(inp);
2256 smb_FreePacket(outp);
2264 long smb_ReceiveCoreGetDiskAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2266 osi_Log0(afsd_logp, "SMB receive get disk attributes");
2268 smb_SetSMBParm(outp, 0, 32000);
2269 smb_SetSMBParm(outp, 1, 64);
2270 smb_SetSMBParm(outp, 2, 1024);
2271 smb_SetSMBParm(outp, 3, 30000);
2272 smb_SetSMBParm(outp, 4, 0);
2273 smb_SetSMBDataLength(outp, 0);
2277 long smb_ReceiveCoreTreeConnect(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *rsp)
2280 unsigned short newTid;
2281 char shareName[256];
2289 osi_Log0(afsd_logp, "SMB receive tree connect");
2291 /* parse input parameters */
2292 tp = smb_GetSMBData(inp, NULL);
2293 pathp = smb_ParseASCIIBlock(tp, &tp);
2294 passwordp = smb_ParseASCIIBlock(tp, &tp);
2295 tp = strrchr(pathp, '\\');
2297 return CM_ERROR_BADSMB;
2298 strcpy(shareName, tp+1);
2300 userp = smb_GetUser(vcp, inp);
2302 lock_ObtainMutex(&vcp->mx);
2303 newTid = vcp->tidCounter++;
2304 lock_ReleaseMutex(&vcp->mx);
2306 tidp = smb_FindTID(vcp, newTid, SMB_FLAG_CREATE);
2307 shareFound = smb_FindShare(vcp, inp, shareName, &sharePath);
2309 smb_ReleaseTID(tidp);
2310 return CM_ERROR_BADSHARENAME;
2312 lock_ObtainMutex(&tidp->mx);
2313 tidp->userp = userp;
2314 tidp->pathname = sharePath;
2315 lock_ReleaseMutex(&tidp->mx);
2316 smb_ReleaseTID(tidp);
2318 smb_SetSMBParm(rsp, 0, SMB_PACKETSIZE);
2319 smb_SetSMBParm(rsp, 1, newTid);
2320 smb_SetSMBDataLength(rsp, 0);
2322 osi_Log1(afsd_logp, "SMB tree connect created ID %d", newTid);
2326 unsigned char *smb_ParseDataBlock(unsigned char *inp, char **chainpp, int *lengthp)
2330 if (*inp++ != 0x1) return NULL;
2331 tlen = inp[0] + (inp[1]<<8);
2332 inp += 2; /* skip length field */
2335 *chainpp = inp + tlen;
2338 if (lengthp) *lengthp = tlen;
2343 /* set maskp to the mask part of the incoming path.
2344 * Mask is 11 bytes long (8.3 with the dot elided).
2345 * Returns true if succeeds with a valid name, otherwise it does
2346 * its best, but returns false.
2348 int smb_Get8Dot3MaskFromPath(unsigned char *maskp, unsigned char *pathp)
2356 /* starts off valid */
2359 /* mask starts out all blanks */
2360 memset(maskp, ' ', 11);
2362 /* find last backslash, or use whole thing if there is none */
2363 tp = strrchr(pathp, '\\');
2364 if (!tp) tp = pathp;
2365 else tp++; /* skip slash */
2369 /* names starting with a dot are illegal */
2370 if (*tp == '.') valid8Dot3 = 0;
2374 if (tc == 0) return valid8Dot3;
2375 if (tc == '.' || tc == '"') break;
2376 if (i < 8) *up++ = tc;
2377 else valid8Dot3 = 0;
2380 /* if we get here, tp point after the dot */
2381 up = maskp+8; /* ext goes here */
2384 if (tc == 0) return valid8Dot3;
2387 if (tc == '.' || tc == '"') valid8Dot3 = 0;
2389 /* copy extension if not too long */
2390 if (i < 3) *up++ = tc;
2391 else valid8Dot3 = 0;
2395 int smb_Match8Dot3Mask(char *unixNamep, char *maskp)
2405 /* XXX redo this, calling smb_V3MatchMask with a converted mask */
2407 valid = smb_Get8Dot3MaskFromPath(umask, unixNamep);
2408 if (!valid) return 0;
2410 /* otherwise, we have a valid 8.3 name; see if we have a match,
2411 * treating '?' as a wildcard in maskp (but not in the file name).
2413 tp1 = umask; /* real name, in mask format */
2414 tp2 = maskp; /* mask, in mask format */
2415 for(i=0; i<11; i++) {
2416 tc1 = *tp1++; /* char from real name */
2417 tc2 = *tp2++; /* char from mask */
2418 tc1 = (char) cm_foldUpper[(unsigned char)tc1];
2419 tc2 = (char) cm_foldUpper[(unsigned char)tc2];
2420 if (tc1 == tc2) continue;
2421 if (tc2 == '?' && tc1 != ' ') continue;
2422 if (tc2 == '>') continue;
2426 /* we got a match */
2430 char *smb_FindMask(char *pathp)
2434 tp = strrchr(pathp, '\\'); /* find last slash */
2436 if (tp) return tp+1; /* skip the slash */
2437 else return pathp; /* no slash, return the entire path */
2440 long smb_ReceiveCoreSearchVolume(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2442 unsigned char *pathp;
2444 unsigned char mask[11];
2445 unsigned char *statBlockp;
2446 unsigned char initStatBlock[21];
2449 osi_Log0(afsd_logp, "SMB receive search volume");
2451 /* pull pathname and stat block out of request */
2452 tp = smb_GetSMBData(inp, NULL);
2453 pathp = smb_ParseASCIIBlock(tp, (char **) &tp);
2454 osi_assert(pathp != NULL);
2455 statBlockp = smb_ParseVblBlock(tp, (char **) &tp, &statLen);
2456 osi_assert(statBlockp != NULL);
2458 statBlockp = initStatBlock;
2462 /* for returning to caller */
2463 smb_Get8Dot3MaskFromPath(mask, pathp);
2465 smb_SetSMBParm(outp, 0, 1); /* we're returning one entry */
2466 tp = smb_GetSMBData(outp, NULL);
2468 *tp++ = 43; /* bytes in a dir entry */
2469 *tp++ = 0; /* high byte in counter */
2471 /* now marshall the dir entry, starting with the search status */
2472 *tp++ = statBlockp[0]; /* Reserved */
2473 memcpy(tp, mask, 11); tp += 11; /* FileName */
2475 /* now pass back server use info, with 1st byte non-zero */
2477 memset(tp, 0, 4); tp += 4; /* reserved for server use */
2479 memcpy(tp, statBlockp+17, 4); tp += 4; /* reserved for consumer */
2481 *tp++ = 0x8; /* attribute: volume */
2491 /* 4 byte file size */
2497 /* finally, null-terminated 8.3 pathname, which we set to AFS */
2498 memset(tp, ' ', 13);
2501 /* set the length of the data part of the packet to 43 + 3, for the dir
2502 * entry plus the 5 and the length fields.
2504 smb_SetSMBDataLength(outp, 46);
2508 long smb_ApplyDirListPatches(smb_dirListPatch_t **dirPatchespp,
2509 cm_user_t *userp, cm_req_t *reqp)
2517 smb_dirListPatch_t *patchp;
2518 smb_dirListPatch_t *npatchp;
2520 for(patchp = *dirPatchespp; patchp; patchp =
2521 (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
2522 code = cm_GetSCache(&patchp->fid, &scp, userp, reqp);
2524 lock_ObtainMutex(&scp->mx);
2525 code = cm_SyncOp(scp, NULL, userp, reqp, 0,
2526 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2528 lock_ReleaseMutex(&scp->mx);
2529 cm_ReleaseSCache(scp);
2532 dptr = patchp->dptr;
2534 attr = smb_Attributes(scp);
2538 smb_SearchTimeFromUnixTime(&dosTime, scp->clientModTime);
2541 shortTemp = dosTime & 0xffff;
2542 *((u_short *)dptr) = shortTemp;
2545 /* and copy out date */
2546 shortTemp = (dosTime>>16) & 0xffff;
2547 *((u_short *)dptr) = shortTemp;
2550 /* copy out file length */
2551 *((u_long *)dptr) = scp->length.LowPart;
2553 lock_ReleaseMutex(&scp->mx);
2554 cm_ReleaseSCache(scp);
2557 /* now free the patches */
2558 for(patchp = *dirPatchespp; patchp; patchp = npatchp) {
2559 npatchp = (smb_dirListPatch_t *) osi_QNext(&patchp->q);
2563 /* and mark the list as empty */
2564 *dirPatchespp = NULL;
2569 long smb_ReceiveCoreSearchDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
2578 smb_dirListPatch_t *dirListPatchesp;
2579 smb_dirListPatch_t *curPatchp;
2583 osi_hyper_t dirLength;
2584 osi_hyper_t bufferOffset;
2585 osi_hyper_t curOffset;
2587 unsigned char *inCookiep;
2588 smb_dirSearch_t *dsp;
2592 unsigned long clientCookie;
2593 cm_pageHeader_t *pageHeaderp;
2594 cm_user_t *userp = NULL;
2601 long nextEntryCookie;
2602 int numDirChunks; /* # of 32 byte dir chunks in this entry */
2603 char resByte; /* reserved byte from the cookie */
2604 char *op; /* output data ptr */
2605 char *origOp; /* original value of op */
2606 cm_space_t *spacep; /* for pathname buffer */
2617 maxCount = smb_GetSMBParm(inp, 0);
2619 dirListPatchesp = NULL;
2621 caseFold = CM_FLAG_CASEFOLD;
2623 tp = smb_GetSMBData(inp, NULL);
2624 pathp = smb_ParseASCIIBlock(tp, &tp);
2625 inCookiep = smb_ParseVblBlock(tp, &tp, &dataLength);
2627 /* bail out if request looks bad */
2628 if (!tp || !pathp) {
2629 return CM_ERROR_BADSMB;
2632 /* We can handle long names */
2633 if (vcp->flags & SMB_VCFLAG_USENT)
2634 ((smb_t *)outp)->flg2 |= 0x40; /* IS_LONG_NAME */
2636 /* make sure we got a whole search status */
2637 if (dataLength < 21) {
2638 nextCookie = 0; /* start at the beginning of the dir */
2641 attribute = smb_GetSMBParm(inp, 1);
2643 /* handle volume info in another function */
2644 if (attribute & 0x8)
2645 return smb_ReceiveCoreSearchVolume(vcp, inp, outp);
2647 osi_Log2(afsd_logp, "SMB receive search dir count %d |%s|",
2648 maxCount, osi_LogSaveString(afsd_logp, pathp));
2650 if (*pathp == 0) { /* null pathp, treat as root dir */
2651 if (!(attribute & 0x10)) /* exclude dirs */
2652 return CM_ERROR_NOFILES;
2656 dsp = smb_NewDirSearch(0);
2657 dsp->attribute = attribute;
2658 smb_Get8Dot3MaskFromPath(mask, pathp);
2659 memcpy(dsp->mask, mask, 11);
2661 /* track if this is likely to match a lot of entries */
2662 if (smb_IsStarMask(mask)) starPattern = 1;
2663 else starPattern = 0;
2666 /* pull the next cookie value out of the search status block */
2667 nextCookie = inCookiep[13] + (inCookiep[14]<<8) + (inCookiep[15]<<16)
2668 + (inCookiep[16]<<24);
2669 dsp = smb_FindDirSearch(inCookiep[12]);
2671 /* can't find dir search status; fatal error */
2672 return CM_ERROR_BADFD;
2674 attribute = dsp->attribute;
2675 resByte = inCookiep[0];
2677 /* copy out client cookie, in host byte order. Don't bother
2678 * interpreting it, since we're just passing it through, anyway.
2680 memcpy(&clientCookie, &inCookiep[17], 4);
2682 memcpy(mask, dsp->mask, 11);
2684 /* assume we're doing a star match if it has continued for more
2690 osi_Log3(afsd_logp, "SMB dir search cookie 0x%x, connection %d, attr 0x%x",
2691 nextCookie, dsp->cookie, attribute);
2693 userp = smb_GetUser(vcp, inp);
2695 /* try to get the vnode for the path name next */
2696 lock_ObtainMutex(&dsp->mx);
2703 spacep = inp->spacep;
2704 smb_StripLastComponent(spacep->data, NULL, pathp);
2705 lock_ReleaseMutex(&dsp->mx);
2706 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
2707 code = cm_NameI(cm_rootSCachep, spacep->data,
2708 caseFold | CM_FLAG_FOLLOW, userp, tidPathp, &req, &scp);
2709 lock_ObtainMutex(&dsp->mx);
2711 if (dsp->scp != 0) cm_ReleaseSCache(dsp->scp);
2713 /* we need one hold for the entry we just stored into,
2714 * and one for our own processing. When we're done with this
2715 * function, we'll drop the one for our own processing.
2716 * We held it once from the namei call, and so we do another hold
2720 lock_ObtainMutex(&scp->mx);
2721 if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0
2722 && LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
2723 scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
2724 dsp->flags |= SMB_DIRSEARCH_BULKST;
2726 lock_ReleaseMutex(&scp->mx);
2729 lock_ReleaseMutex(&dsp->mx);
2731 cm_ReleaseUser(userp);
2732 smb_DeleteDirSearch(dsp);
2733 smb_ReleaseDirSearch(dsp);
2737 /* reserves space for parameter; we'll adjust it again later to the
2738 * real count of the # of entries we returned once we've actually
2739 * assembled the directory listing.
2741 smb_SetSMBParm(outp, 0, 0);
2743 /* get the directory size */
2744 lock_ObtainMutex(&scp->mx);
2745 code = cm_SyncOp(scp, NULL, userp, &req, 0,
2746 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
2748 lock_ReleaseMutex(&scp->mx);
2749 cm_ReleaseSCache(scp);
2750 cm_ReleaseUser(userp);
2751 smb_DeleteDirSearch(dsp);
2752 smb_ReleaseDirSearch(dsp);
2756 dirLength = scp->length;
2758 bufferOffset.LowPart = bufferOffset.HighPart = 0;
2759 curOffset.HighPart = 0;
2760 curOffset.LowPart = nextCookie;
2761 origOp = op = smb_GetSMBData(outp, NULL);
2762 /* and write out the basic header */
2763 *op++ = 5; /* variable block */
2764 op += 2; /* skip vbl block length; we'll fill it in later */
2768 /* make sure that curOffset.LowPart doesn't point to the first
2769 * 32 bytes in the 2nd through last dir page, and that it doesn't
2770 * point at the first 13 32-byte chunks in the first dir page,
2771 * since those are dir and page headers, and don't contain useful
2774 temp = curOffset.LowPart & (2048-1);
2775 if (curOffset.HighPart == 0 && curOffset.LowPart < 2048) {
2776 /* we're in the first page */
2777 if (temp < 13*32) temp = 13*32;
2780 /* we're in a later dir page */
2781 if (temp < 32) temp = 32;
2784 /* make sure the low order 5 bits are zero */
2787 /* now put temp bits back ito curOffset.LowPart */
2788 curOffset.LowPart &= ~(2048-1);
2789 curOffset.LowPart |= temp;
2791 /* check if we've returned all the names that will fit in the
2794 if (returnedNames >= maxCount) break;
2796 /* check if we've passed the dir's EOF */
2797 if (LargeIntegerGreaterThanOrEqualTo(curOffset, dirLength)) break;
2799 /* see if we can use the bufferp we have now; compute in which page
2800 * the current offset would be, and check whether that's the offset
2801 * of the buffer we have. If not, get the buffer.
2803 thyper.HighPart = curOffset.HighPart;
2804 thyper.LowPart = curOffset.LowPart & ~(buf_bufferSize-1);
2805 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
2808 buf_Release(bufferp);
2811 lock_ReleaseMutex(&scp->mx);
2812 lock_ObtainRead(&scp->bufCreateLock);
2813 code = buf_Get(scp, &thyper, &bufferp);
2814 lock_ReleaseRead(&scp->bufCreateLock);
2816 /* now, if we're doing a star match, do bulk fetching of all of
2817 * the status info for files in the dir.
2820 smb_ApplyDirListPatches(&dirListPatchesp, userp,
2822 if ((dsp->flags & SMB_DIRSEARCH_BULKST)
2823 && LargeIntegerGreaterThanOrEqualTo(thyper,
2824 scp->bulkStatProgress)) {
2825 /* Don't bulk stat if risking timeout */
2826 int now = GetCurrentTime();
2827 if (now - req.startTime > 5000) {
2828 scp->bulkStatProgress = thyper;
2829 scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
2830 dsp->flags &= ~SMB_DIRSEARCH_BULKST;
2832 cm_TryBulkStat(scp, &thyper,
2837 lock_ObtainMutex(&scp->mx);
2839 bufferOffset = thyper;
2841 /* now get the data in the cache */
2843 code = cm_SyncOp(scp, bufferp, userp, &req,
2845 CM_SCACHESYNC_NEEDCALLBACK
2846 | CM_SCACHESYNC_READ);
2849 if (cm_HaveBuffer(scp, bufferp, 0)) break;
2851 /* otherwise, load the buffer and try again */
2852 code = cm_GetBuffer(scp, bufferp, NULL, userp,
2857 buf_Release(bufferp);
2861 } /* if (wrong buffer) ... */
2863 /* now we have the buffer containing the entry we're interested in; copy
2864 * it out if it represents a non-deleted entry.
2866 entryInDir = curOffset.LowPart & (2048-1);
2867 entryInBuffer = curOffset.LowPart & (buf_bufferSize - 1);
2869 /* page header will help tell us which entries are free. Page header
2870 * can change more often than once per buffer, since AFS 3 dir page size
2871 * may be less than (but not more than a buffer package buffer.
2873 temp = curOffset.LowPart & (buf_bufferSize - 1); /* only look intra-buffer */
2874 temp &= ~(2048 - 1); /* turn off intra-page bits */
2875 pageHeaderp = (cm_pageHeader_t *) (bufferp->datap + temp);
2877 /* now determine which entry we're looking at in the page. If it is
2878 * free (there's a free bitmap at the start of the dir), we should
2879 * skip these 32 bytes.
2881 slotInPage = (entryInDir & 0x7e0) >> 5;
2882 if (!(pageHeaderp->freeBitmap[slotInPage>>3] & (1 << (slotInPage & 0x7)))) {
2883 /* this entry is free */
2884 numDirChunks = 1; /* only skip this guy */
2888 tp = bufferp->datap + entryInBuffer;
2889 dep = (cm_dirEntry_t *) tp; /* now points to AFS3 dir entry */
2891 /* while we're here, compute the next entry's location, too,
2892 * since we'll need it when writing out the cookie into the dir
2895 * XXXX Probably should do more sanity checking.
2897 numDirChunks = cm_NameEntries(dep->name, NULL);
2899 /* compute the offset of the cookie representing the next entry */
2900 nextEntryCookie = curOffset.LowPart + (CM_DIR_CHUNKSIZE * numDirChunks);
2902 /* Compute 8.3 name if necessary */
2903 actualName = dep->name;
2904 if (dep->fid.vnode != 0 && !cm_Is8Dot3(actualName)) {
2905 cm_Gen8Dot3Name(dep, shortName, &shortNameEnd);
2906 actualName = shortName;
2909 if (dep->fid.vnode != 0 && smb_Match8Dot3Mask(actualName, mask)) {
2910 /* this is one of the entries to use: it is not deleted
2911 * and it matches the star pattern we're looking for.
2914 /* Eliminate entries that don't match requested
2916 if (!(dsp->attribute & 0x10)) /* no directories */
2918 /* We have already done the cm_TryBulkStat above */
2919 fid.cell = scp->fid.cell;
2920 fid.volume = scp->fid.volume;
2921 fid.vnode = ntohl(dep->fid.vnode);
2922 fid.unique = ntohl(dep->fid.unique);
2923 fileType = cm_FindFileType(&fid);
2924 osi_Log2(afsd_logp, "smb_ReceiveCoreSearchDir: file %s "
2925 "has filetype %d", dep->name,
2927 if (fileType == CM_SCACHETYPE_DIRECTORY)
2932 memcpy(op, mask, 11); op += 11;
2933 *op++ = (char) dsp->cookie; /* they say it must be non-zero */
2934 *op++ = nextEntryCookie & 0xff;
2935 *op++ = (nextEntryCookie>>8) & 0xff;
2936 *op++ = (nextEntryCookie>>16) & 0xff;
2937 *op++ = (nextEntryCookie>>24) & 0xff;
2938 memcpy(op, &clientCookie, 4); op += 4;
2940 /* now we emit the attribute. This is sort of tricky,
2941 * since we need to really stat the file to find out
2942 * what type of entry we've got. Right now, we're
2943 * copying out data from a buffer, while holding the
2944 * scp locked, so it isn't really convenient to stat
2945 * something now. We'll put in a place holder now,
2946 * and make a second pass before returning this to get
2947 * the real attributes. So, we just skip the data for
2948 * now, and adjust it later. We allocate a patch
2949 * record to make it easy to find this point later.
2950 * The replay will happen at a time when it is safe to
2951 * unlock the directory.
2953 curPatchp = malloc(sizeof(*curPatchp));
2954 osi_QAdd((osi_queue_t **) &dirListPatchesp, &curPatchp->q);
2955 curPatchp->dptr = op;
2956 curPatchp->fid.cell = scp->fid.cell;
2957 curPatchp->fid.volume = scp->fid.volume;
2958 curPatchp->fid.vnode = ntohl(dep->fid.vnode);
2959 curPatchp->fid.unique = ntohl(dep->fid.unique);
2960 op += 9; /* skip attr, time, date and size */
2962 /* zero out name area. The spec says to pad with
2963 * spaces, but Samba doesn't, and neither do we.
2967 /* finally, we get to copy out the name; we know that
2968 * it fits in 8.3 or the pattern wouldn't match, but it
2969 * never hurts to be sure.
2971 strncpy(op, actualName, 13);
2973 /* Uppercase if requested by client */
2974 if ((((smb_t *)inp)->flg2 & 1) == 0)
2979 /* now, adjust the # of entries copied */
2981 } /* if we're including this name */
2984 /* and adjust curOffset to be where the new cookie is */
2985 thyper.HighPart = 0;
2986 thyper.LowPart = CM_DIR_CHUNKSIZE * numDirChunks;
2987 curOffset = LargeIntegerAdd(thyper, curOffset);
2988 } /* while copying data for dir listing */
2990 /* release the mutex */
2991 lock_ReleaseMutex(&scp->mx);
2992 if (bufferp) buf_Release(bufferp);
2994 /* apply and free last set of patches; if not doing a star match, this
2995 * will be empty, but better safe (and freeing everything) than sorry.
2997 smb_ApplyDirListPatches(&dirListPatchesp, userp, &req);
2999 /* special return code for unsuccessful search */
3000 if (code == 0 && dataLength < 21 && returnedNames == 0)
3001 code = CM_ERROR_NOFILES;
3003 osi_Log2(afsd_logp, "SMB search dir done, %d names, code %d",
3004 returnedNames, code);
3007 smb_DeleteDirSearch(dsp);
3008 smb_ReleaseDirSearch(dsp);
3009 cm_ReleaseSCache(scp);
3010 cm_ReleaseUser(userp);
3014 /* finalize the output buffer */
3015 smb_SetSMBParm(outp, 0, returnedNames);
3016 temp = (long) (op - origOp);
3017 smb_SetSMBDataLength(outp, temp);
3019 /* the data area is a variable block, which has a 5 (already there)
3020 * followed by the length of the # of data bytes. We now know this to
3021 * be "temp," although that includes the 3 bytes of vbl block header.
3022 * Deduct for them and fill in the length field.
3024 temp -= 3; /* deduct vbl block info */
3025 osi_assert(temp == (43 * returnedNames));
3026 origOp[1] = temp & 0xff;
3027 origOp[2] = (temp>>8) & 0xff;
3028 if (returnedNames == 0) smb_DeleteDirSearch(dsp);
3029 smb_ReleaseDirSearch(dsp);
3030 cm_ReleaseSCache(scp);
3031 cm_ReleaseUser(userp);
3035 /* verify that this is a valid path to a directory. I don't know why they
3036 * don't use the get file attributes call.
3038 long smb_ReceiveCoreCheckPath(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3042 cm_scache_t *rootScp;
3043 cm_scache_t *newScp;
3052 pathp = smb_GetSMBData(inp, NULL);
3053 pathp = smb_ParseASCIIBlock(pathp, NULL);
3054 osi_Log1(afsd_logp, "SMB receive check path %s",
3055 osi_LogSaveString(afsd_logp, pathp));
3058 return CM_ERROR_BADFD;
3061 rootScp = cm_rootSCachep;
3063 userp = smb_GetUser(vcp, inp);
3065 caseFold = CM_FLAG_CASEFOLD;
3067 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3068 code = cm_NameI(rootScp, pathp,
3069 caseFold | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH,
3070 userp, tidPathp, &req, &newScp);
3073 cm_ReleaseUser(userp);
3077 /* now lock the vnode with a callback; returns with newScp locked */
3078 lock_ObtainMutex(&newScp->mx);
3079 code = cm_SyncOp(newScp, NULL, userp, &req, PRSFS_LOOKUP,
3080 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
3081 if (code && code != CM_ERROR_NOACCESS) {
3082 lock_ReleaseMutex(&newScp->mx);
3083 cm_ReleaseSCache(newScp);
3084 cm_ReleaseUser(userp);
3088 attrs = smb_Attributes(newScp);
3090 if (!(attrs & 0x10))
3091 code = CM_ERROR_NOTDIR;
3093 lock_ReleaseMutex(&newScp->mx);
3095 cm_ReleaseSCache(newScp);
3096 cm_ReleaseUser(userp);
3100 long smb_ReceiveCoreSetFileAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3104 cm_scache_t *rootScp;
3105 unsigned short attribute;
3107 cm_scache_t *newScp;
3116 /* decode basic attributes we're passed */
3117 attribute = smb_GetSMBParm(inp, 0);
3118 dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
3120 pathp = smb_GetSMBData(inp, NULL);
3121 pathp = smb_ParseASCIIBlock(pathp, NULL);
3124 return CM_ERROR_BADSMB;
3127 osi_Log2(afsd_logp, "SMB receive setfile attributes time %d, attr 0x%x",
3128 dosTime, attribute);
3130 rootScp = cm_rootSCachep;
3132 userp = smb_GetUser(vcp, inp);
3134 caseFold = CM_FLAG_CASEFOLD;
3136 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3137 code = cm_NameI(rootScp, pathp, caseFold | CM_FLAG_FOLLOW, userp,
3138 tidPathp, &req, &newScp);
3141 cm_ReleaseUser(userp);
3145 /* now lock the vnode with a callback; returns with newScp locked; we
3146 * need the current status to determine what the new status is, in some
3149 lock_ObtainMutex(&newScp->mx);
3150 code = cm_SyncOp(newScp, NULL, userp, &req, 0,
3151 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
3153 lock_ReleaseMutex(&newScp->mx);
3154 cm_ReleaseSCache(newScp);
3155 cm_ReleaseUser(userp);
3159 /* Check for RO volume */
3160 if (newScp->flags & CM_SCACHEFLAG_RO) {
3161 lock_ReleaseMutex(&newScp->mx);
3162 cm_ReleaseSCache(newScp);
3163 cm_ReleaseUser(userp);
3164 return CM_ERROR_READONLY;
3167 /* prepare for setattr call */
3170 attr.mask |= CM_ATTRMASK_CLIENTMODTIME;
3171 smb_UnixTimeFromDosUTime(&attr.clientModTime, dosTime);
3173 if ((newScp->unixModeBits & 0222) && (attribute & 1) != 0) {
3174 /* we're told to make a writable file read-only */
3175 attr.unixModeBits = newScp->unixModeBits & ~0222;
3176 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
3178 else if ((newScp->unixModeBits & 0222) == 0 && (attribute & 1) == 0) {
3179 /* we're told to make a read-only file writable */
3180 attr.unixModeBits = newScp->unixModeBits | 0222;
3181 attr.mask |= CM_ATTRMASK_UNIXMODEBITS;
3183 lock_ReleaseMutex(&newScp->mx);
3185 /* now call setattr */
3187 code = cm_SetAttr(newScp, &attr, userp, &req);
3191 cm_ReleaseSCache(newScp);
3192 cm_ReleaseUser(userp);
3197 long smb_ReceiveCoreGetFileAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3201 cm_scache_t *rootScp;
3202 cm_scache_t *newScp, *dscp;
3214 pathp = smb_GetSMBData(inp, NULL);
3215 pathp = smb_ParseASCIIBlock(pathp, NULL);
3218 return CM_ERROR_BADSMB;
3221 if (*pathp == 0) /* null path */
3224 osi_Log1(afsd_logp, "SMB receive getfile attributes path %s",
3225 osi_LogSaveString(afsd_logp, pathp));
3227 rootScp = cm_rootSCachep;
3229 userp = smb_GetUser(vcp, inp);
3231 /* we shouldn't need this for V3 requests, but we seem to */
3232 caseFold = CM_FLAG_CASEFOLD;
3234 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3237 * XXX Strange hack XXX
3239 * As of Patch 5 (16 July 97), we are having the following problem:
3240 * In NT Explorer 4.0, whenever we click on a directory, AFS gets
3241 * requests to look up "desktop.ini" in all the subdirectories.
3242 * This can cause zillions of timeouts looking up non-existent cells
3243 * and volumes, especially in the top-level directory.
3245 * We have not found any way to avoid this or work around it except
3246 * to explicitly ignore the requests for mount points that haven't
3247 * yet been evaluated and for directories that haven't yet been
3250 spacep = inp->spacep;
3251 smb_StripLastComponent(spacep->data, &lastComp, pathp);
3252 if (strcmp(lastComp, "\\desktop.ini") == 0) {
3253 code = cm_NameI(rootScp, spacep->data,
3254 caseFold | CM_FLAG_DIRSEARCH | CM_FLAG_FOLLOW,
3255 userp, tidPathp, &req, &dscp);
3257 if (dscp->fileType == CM_SCACHETYPE_MOUNTPOINT
3258 && !dscp->mountRootFidp)
3259 code = CM_ERROR_NOSUCHFILE;
3260 else if (dscp->fileType == CM_SCACHETYPE_DIRECTORY) {
3261 cm_buf_t *bp = buf_Find(dscp, &hzero);
3265 code = CM_ERROR_NOSUCHFILE;
3267 cm_ReleaseSCache(dscp);
3269 cm_ReleaseUser(userp);
3275 code = cm_NameI(rootScp, pathp, caseFold | CM_FLAG_FOLLOW, userp,
3276 tidPathp, &req, &newScp);
3279 cm_ReleaseUser(userp);
3283 /* now lock the vnode with a callback; returns with newScp locked */
3284 lock_ObtainMutex(&newScp->mx);
3285 code = cm_SyncOp(newScp, NULL, userp, &req, 0,
3286 CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_NEEDCALLBACK);
3288 lock_ReleaseMutex(&newScp->mx);
3289 cm_ReleaseSCache(newScp);
3290 cm_ReleaseUser(userp);
3294 if (newScp->fileType == CM_SCACHETYPE_DIRECTORY
3295 || newScp->fileType == CM_SCACHETYPE_MOUNTPOINT)
3299 if ((newScp->unixModeBits & 0222) == 0 || (newScp->flags & CM_SCACHEFLAG_RO))
3300 attrs |= 1; /* turn on read-only flag */
3301 smb_SetSMBParm(outp, 0, attrs);
3303 smb_DosUTimeFromUnixTime(&dosTime, newScp->clientModTime);
3304 smb_SetSMBParm(outp, 1, dosTime & 0xffff);
3305 smb_SetSMBParm(outp, 2, (dosTime>>16) & 0xffff);
3306 smb_SetSMBParm(outp, 3, newScp->length.LowPart & 0xffff);
3307 smb_SetSMBParm(outp, 4, (newScp->length.LowPart >> 16) & 0xffff);
3308 smb_SetSMBParm(outp, 5, 0);
3309 smb_SetSMBParm(outp, 6, 0);
3310 smb_SetSMBParm(outp, 7, 0);
3311 smb_SetSMBParm(outp, 8, 0);
3312 smb_SetSMBParm(outp, 9, 0);
3313 smb_SetSMBDataLength(outp, 0);
3314 lock_ReleaseMutex(&newScp->mx);
3316 cm_ReleaseSCache(newScp);
3317 cm_ReleaseUser(userp);
3322 long smb_ReceiveCoreTreeDisconnect(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3326 osi_Log0(afsd_logp, "SMB receive tree disconnect");
3328 /* find the tree and free it */
3329 tidp = smb_FindTID(vcp, ((smb_t *)inp)->tid, 0);
3331 lock_ObtainMutex(&tidp->mx);
3332 tidp->flags |= SMB_TIDFLAG_DELETE;
3333 lock_ReleaseMutex(&tidp->mx);
3334 smb_ReleaseTID(tidp);
3340 long smb_ReceiveCoreOpen(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3358 osi_Log0(afsd_logp, "SMB receive open");
3360 pathp = smb_GetSMBData(inp, NULL);
3361 pathp = smb_ParseASCIIBlock(pathp, NULL);
3363 share = smb_GetSMBParm(inp, 0);
3364 attribute = smb_GetSMBParm(inp, 1);
3366 spacep = inp->spacep;
3367 smb_StripLastComponent(spacep->data, &lastNamep, pathp);
3368 if (lastNamep && strcmp(lastNamep, SMB_IOCTL_FILENAME) == 0) {
3369 /* special case magic file name for receiving IOCTL requests
3370 * (since IOCTL calls themselves aren't getting through).
3372 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
3373 smb_SetupIoctlFid(fidp, spacep);
3374 smb_SetSMBParm(outp, 0, fidp->fid);
3375 smb_SetSMBParm(outp, 1, 0); /* attrs */
3376 smb_SetSMBParm(outp, 2, 0); /* next 2 are DOS time */
3377 smb_SetSMBParm(outp, 3, 0);
3378 smb_SetSMBParm(outp, 4, 0); /* next 2 are length */
3379 smb_SetSMBParm(outp, 5, 0x7fff);
3380 /* pass the open mode back */
3381 smb_SetSMBParm(outp, 6, (share & 0xf));
3382 smb_SetSMBDataLength(outp, 0);
3383 smb_ReleaseFID(fidp);
3387 userp = smb_GetUser(vcp, inp);
3389 caseFold = CM_FLAG_CASEFOLD;
3391 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3392 code = cm_NameI(cm_rootSCachep, pathp, caseFold | CM_FLAG_FOLLOW, userp,
3393 tidPathp, &req, &scp);
3396 cm_ReleaseUser(userp);
3400 code = cm_CheckOpen(scp, share & 0x7, 0, userp, &req);
3402 cm_ReleaseSCache(scp);
3403 cm_ReleaseUser(userp);
3407 /* don't need callback to check file type, since file types never
3408 * change, and namei and cm_Lookup all stat the object at least once on
3409 * a successful return.
3411 if (scp->fileType != CM_SCACHETYPE_FILE) {
3412 cm_ReleaseSCache(scp);
3413 cm_ReleaseUser(userp);
3414 return CM_ERROR_ISDIR;
3417 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
3420 /* save a pointer to the vnode */
3423 if ((share & 0xf) == 0)
3424 fidp->flags |= SMB_FID_OPENREAD;
3425 else if ((share & 0xf) == 1)
3426 fidp->flags |= SMB_FID_OPENWRITE;
3427 else fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
3429 lock_ObtainMutex(&scp->mx);
3430 smb_SetSMBParm(outp, 0, fidp->fid);
3431 smb_SetSMBParm(outp, 1, smb_Attributes(scp));
3432 smb_DosUTimeFromUnixTime(&dosTime, scp->clientModTime);
3433 smb_SetSMBParm(outp, 2, dosTime & 0xffff);
3434 smb_SetSMBParm(outp, 3, (dosTime >> 16) & 0xffff);
3435 smb_SetSMBParm(outp, 4, scp->length.LowPart & 0xffff);
3436 smb_SetSMBParm(outp, 5, (scp->length.LowPart >> 16) & 0xffff);
3437 /* pass the open mode back; XXXX add access checks */
3438 smb_SetSMBParm(outp, 6, (share & 0xf));
3439 smb_SetSMBDataLength(outp, 0);
3440 lock_ReleaseMutex(&scp->mx);
3443 cm_Open(scp, 0, userp);
3445 /* send and free packet */
3446 smb_ReleaseFID(fidp);
3447 cm_ReleaseUser(userp);
3448 /* don't release scp, since we've squirreled away the pointer in the fid struct */
3453 typedef struct smb_unlinkRock {
3458 char *maskp; /* pointer to the star pattern */
3463 int smb_UnlinkProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
3466 smb_unlinkRock_t *rockp;
3474 if (rockp->vcp->flags & SMB_VCFLAG_USEV3)
3475 caseFold = CM_FLAG_CASEFOLD;
3477 caseFold = CM_FLAG_CASEFOLD | CM_FLAG_8DOT3;
3479 matchName = dep->name;
3480 match = smb_V3MatchMask(matchName, rockp->maskp, caseFold);
3483 && !cm_Is8Dot3(dep->name)) {
3484 cm_Gen8Dot3Name(dep, shortName, NULL);
3485 matchName = shortName;
3486 match = smb_V3MatchMask(matchName, rockp->maskp, caseFold);
3489 osi_Log1(smb_logp, "Unlinking %s",
3490 osi_LogSaveString(smb_logp, matchName));
3491 code = cm_Unlink(dscp, dep->name, rockp->userp, rockp->reqp);
3492 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
3493 smb_NotifyChange(FILE_ACTION_REMOVED,
3494 FILE_NOTIFY_CHANGE_FILE_NAME,
3495 dscp, dep->name, NULL, TRUE);
3504 long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3513 smb_unlinkRock_t rock;
3522 attribute = smb_GetSMBParm(inp, 0);
3524 tp = smb_GetSMBData(inp, NULL);
3525 pathp = smb_ParseASCIIBlock(tp, &tp);
3527 osi_Log1(smb_logp, "SMB receive unlink %s",
3528 osi_LogSaveString(smb_logp, pathp));
3530 spacep = inp->spacep;
3531 smb_StripLastComponent(spacep->data, &lastNamep, pathp);
3533 userp = smb_GetUser(vcp, inp);
3535 caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
3537 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3538 code = cm_NameI(cm_rootSCachep, spacep->data, caseFold, userp, tidPathp,
3542 cm_ReleaseUser(userp);
3546 /* otherwise, scp points to the parent directory.
3548 if (!lastNamep) lastNamep = pathp;
3552 rock.maskp = smb_FindMask(pathp);
3553 rock.hasTilde = ((strchr(rock.maskp, '~') != NULL) ? 1 : 0);
3556 thyper.HighPart = 0;
3561 code = cm_ApplyDir(dscp, smb_UnlinkProc, &rock, &thyper, userp, &req, NULL);
3563 cm_ReleaseUser(userp);
3565 cm_ReleaseSCache(dscp);
3567 if (code == 0 && !rock.any)
3568 code = CM_ERROR_NOSUCHFILE;
3572 typedef struct smb_renameRock {
3573 cm_scache_t *odscp; /* old dir */
3574 cm_scache_t *ndscp; /* new dir */
3575 cm_user_t *userp; /* user */
3576 cm_req_t *reqp; /* request struct */
3577 smb_vc_t *vcp; /* virtual circuit */
3578 char *maskp; /* pointer to star pattern of old file name */
3579 int hasTilde; /* star pattern might be shortname? */
3580 char *newNamep; /* ptr to the new file's name */
3583 int smb_RenameProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
3586 smb_renameRock_t *rockp;
3593 if (rockp->vcp->flags & SMB_VCFLAG_USEV3)
3594 caseFold = CM_FLAG_CASEFOLD;
3596 caseFold = CM_FLAG_CASEFOLD | CM_FLAG_8DOT3;
3598 match = smb_V3MatchMask(dep->name, rockp->maskp, caseFold);
3601 && !cm_Is8Dot3(dep->name)) {
3602 cm_Gen8Dot3Name(dep, shortName, NULL);
3603 match = smb_V3MatchMask(shortName, rockp->maskp, caseFold);
3606 code = cm_Rename(rockp->odscp, dep->name,
3607 rockp->ndscp, rockp->newNamep, rockp->userp,
3609 /* if the call worked, stop doing the search now, since we
3610 * really only want to rename one file.
3612 if (code == 0) code = CM_ERROR_STOPNOW;
3619 long smb_ReceiveCoreRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3626 smb_renameRock_t rock;
3627 cm_scache_t *oldDscp;
3628 cm_scache_t *newDscp;
3640 tp = smb_GetSMBData(inp, NULL);
3641 oldPathp = smb_ParseASCIIBlock(tp, &tp);
3642 newPathp = smb_ParseASCIIBlock(tp, &tp);
3644 osi_Log2(afsd_logp, "smb rename %s to %s",
3645 osi_LogSaveString(afsd_logp, oldPathp),
3646 osi_LogSaveString(afsd_logp, newPathp));
3648 spacep = inp->spacep;
3649 smb_StripLastComponent(spacep->data, &oldLastNamep, oldPathp);
3651 userp = smb_GetUser(vcp, inp);
3654 * Changed to use CASEFOLD always. This enables us to rename Foo/baz when
3655 * what actually exists is foo/baz. I don't know why the code used to be
3656 * the way it was. 1/29/96
3658 * caseFold = ((vcp->flags & SMB_VCFLAG_USEV3) ? 0: CM_FLAG_CASEFOLD);
3660 * Changed to use CM_FLAG_FOLLOW. 7/24/96
3662 * caseFold = CM_FLAG_CASEFOLD;
3664 caseFold = CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD;
3666 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3667 code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
3668 userp, tidPathp, &req, &oldDscp);
3671 cm_ReleaseUser(userp);
3675 smb_StripLastComponent(spacep->data, &newLastNamep, newPathp);
3676 code = cm_NameI(cm_rootSCachep, spacep->data, caseFold,
3677 userp, tidPathp, &req, &newDscp);
3680 cm_ReleaseSCache(oldDscp);
3681 cm_ReleaseUser(userp);
3685 /* otherwise, oldDscp and newDscp point to the corresponding directories.
3686 * next, get the component names, and lower case them.
3689 /* handle the old name first */
3690 if (!oldLastNamep) oldLastNamep = oldPathp;
3691 else oldLastNamep++;
3693 /* and handle the new name, too */
3694 if (!newLastNamep) newLastNamep = newPathp;
3695 else newLastNamep++;
3697 /* do the vnode call */
3698 rock.odscp = oldDscp;
3699 rock.ndscp = newDscp;
3703 rock.maskp = oldLastNamep;
3704 rock.hasTilde = ((strchr(oldLastNamep, '~') != NULL) ? 1 : 0);
3705 rock.newNamep = newLastNamep;
3707 /* now search the dir for the pattern, and do the appropriate rename when
3710 thyper.LowPart = 0; /* search dir from here */
3711 thyper.HighPart = 0;
3712 code = cm_ApplyDir(oldDscp, smb_RenameProc, &rock, &thyper, userp, &req, NULL);
3714 if (code == CM_ERROR_STOPNOW)
3717 code = CM_ERROR_NOSUCHFILE;
3719 /* Handle Change Notification */
3721 * Being lazy, not distinguishing between files and dirs in this
3722 * filter, since we'd have to do a lookup.
3724 filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
3725 if (oldDscp == newDscp) {
3726 if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
3727 smb_NotifyChange(FILE_ACTION_RENAMED_OLD_NAME,
3728 filter, oldDscp, oldLastNamep,
3729 newLastNamep, TRUE);
3731 if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
3732 smb_NotifyChange(FILE_ACTION_RENAMED_OLD_NAME,
3733 filter, oldDscp, oldLastNamep,
3735 if (newDscp->flags & CM_SCACHEFLAG_ANYWATCH)
3736 smb_NotifyChange(FILE_ACTION_RENAMED_NEW_NAME,
3737 filter, newDscp, newLastNamep,
3741 cm_ReleaseUser(userp);
3743 cm_ReleaseSCache(oldDscp);
3744 cm_ReleaseSCache(newDscp);
3749 typedef struct smb_rmdirRock {
3753 char *maskp; /* pointer to the star pattern */
3758 int smb_RmdirProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
3761 smb_rmdirRock_t *rockp;
3768 matchName = dep->name;
3769 match = (cm_stricmp(matchName, rockp->maskp) == 0);
3772 && !cm_Is8Dot3(dep->name)) {
3773 cm_Gen8Dot3Name(dep, shortName, NULL);
3774 matchName = shortName;
3775 match = (cm_stricmp(matchName, rockp->maskp) == 0);
3778 osi_Log1(smb_logp, "Removing directory %s",
3779 osi_LogSaveString(smb_logp, matchName));
3780 code = cm_RemoveDir(dscp, dep->name, rockp->userp, rockp->reqp);
3781 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
3782 smb_NotifyChange(FILE_ACTION_REMOVED,
3783 FILE_NOTIFY_CHANGE_DIR_NAME,
3784 dscp, dep->name, NULL, TRUE);
3793 long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3801 smb_rmdirRock_t rock;
3810 tp = smb_GetSMBData(inp, NULL);
3811 pathp = smb_ParseASCIIBlock(tp, &tp);
3813 spacep = inp->spacep;
3814 smb_StripLastComponent(spacep->data, &lastNamep, pathp);
3816 userp = smb_GetUser(vcp, inp);
3818 caseFold = CM_FLAG_CASEFOLD;
3820 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
3821 code = cm_NameI(cm_rootSCachep, spacep->data, caseFold | CM_FLAG_FOLLOW,
3822 userp, tidPathp, &req, &dscp);
3825 cm_ReleaseUser(userp);
3829 /* otherwise, scp points to the parent directory. */
3830 if (!lastNamep) lastNamep = pathp;
3834 rock.maskp = lastNamep;
3835 rock.hasTilde = ((strchr(rock.maskp, '~') != NULL) ? 1 : 0);
3838 thyper.HighPart = 0;
3842 code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
3844 cm_ReleaseUser(userp);
3846 cm_ReleaseSCache(dscp);
3848 if (code == 0 && !rock.any)
3849 code = CM_ERROR_NOSUCHFILE;
3853 long smb_ReceiveCoreFlush(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3863 fid = smb_GetSMBParm(inp, 0);
3865 osi_Log1(afsd_logp, "SMB flush fid %d", fid);
3867 fid = smb_ChainFID(fid, inp);
3868 fidp = smb_FindFID(vcp, fid, 0);
3869 if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
3870 return CM_ERROR_BADFD;
3873 userp = smb_GetUser(vcp, inp);
3875 lock_ObtainMutex(&fidp->mx);
3876 if (fidp->flags & SMB_FID_OPENWRITE)
3877 code = cm_FSync(fidp->scp, userp, &req);
3879 lock_ReleaseMutex(&fidp->mx);
3881 smb_ReleaseFID(fidp);
3883 cm_ReleaseUser(userp);
3888 struct smb_FullNameRock {
3894 int smb_FullNameProc(cm_scache_t *scp, cm_dirEntry_t *dep, void *rockp,
3898 struct smb_FullNameRock *vrockp;
3902 if (!cm_Is8Dot3(dep->name)) {
3903 cm_Gen8Dot3Name(dep, shortName, NULL);
3905 if (strcmp(shortName, vrockp->name) == 0) {
3906 vrockp->fullName = strdup(dep->name);
3907 return CM_ERROR_STOPNOW;
3910 if (stricmp(dep->name, vrockp->name) == 0
3911 && ntohl(dep->fid.vnode) == vrockp->vnode->fid.vnode
3912 && ntohl(dep->fid.unique) == vrockp->vnode->fid.unique) {
3913 vrockp->fullName = strdup(dep->name);
3914 return CM_ERROR_STOPNOW;
3919 void smb_FullName(cm_scache_t *dscp, cm_scache_t *scp, char *pathp,
3920 char **newPathp, cm_user_t *userp, cm_req_t *reqp)
3922 struct smb_FullNameRock rock;
3928 code = cm_ApplyDir(dscp, smb_FullNameProc, &rock, NULL,
3930 if (code == CM_ERROR_STOPNOW)
3931 *newPathp = rock.fullName;
3933 *newPathp = strdup(pathp);
3936 long smb_ReceiveCoreClose(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
3947 fid = smb_GetSMBParm(inp, 0);
3948 dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
3950 osi_Log1(afsd_logp, "SMB close fid %d", fid);
3952 fid = smb_ChainFID(fid, inp);
3953 fidp = smb_FindFID(vcp, fid, 0);
3955 return CM_ERROR_BADFD;
3958 userp = smb_GetUser(vcp, inp);
3960 lock_ObtainMutex(&fidp->mx);
3962 /* Don't jump the gun on an async raw write */
3963 while (fidp->raw_writers) {
3964 lock_ReleaseMutex(&fidp->mx);
3965 thrd_WaitForSingleObject_Event(fidp->raw_write_event, RAWTIMEOUT);
3966 lock_ObtainMutex(&fidp->mx);
3969 fidp->flags |= SMB_FID_DELETE;
3971 /* watch for ioctl closes, and read-only opens */
3972 if (fidp->scp != NULL
3973 && (fidp->flags & (SMB_FID_OPENWRITE | SMB_FID_DELONCLOSE))
3974 == SMB_FID_OPENWRITE) {
3975 if (dosTime != 0 && dosTime != -1) {
3976 fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
3977 /* This fixes defect 10958 */
3978 CompensateForSmbClientLastWriteTimeBugs(&dosTime);
3979 smb_UnixTimeFromDosUTime(&fidp->scp->clientModTime,
3982 code = cm_FSync(fidp->scp, userp, &req);
3986 if (fidp->flags & SMB_FID_DELONCLOSE) {
3987 cm_scache_t *dscp = fidp->NTopen_dscp;
3988 char *pathp = fidp->NTopen_pathp;
3991 smb_FullName(dscp, fidp->scp, pathp, &fullPathp, userp, &req);
3992 if (fidp->scp->fileType == CM_SCACHETYPE_DIRECTORY) {
3993 code = cm_RemoveDir(dscp, fullPathp, userp, &req);
3994 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
3995 smb_NotifyChange(FILE_ACTION_REMOVED,
3996 FILE_NOTIFY_CHANGE_DIR_NAME,
3997 dscp, fullPathp, NULL, TRUE);
4000 code = cm_Unlink(dscp, fullPathp, userp, &req);
4001 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
4002 smb_NotifyChange(FILE_ACTION_REMOVED,
4003 FILE_NOTIFY_CHANGE_FILE_NAME,
4004 dscp, fullPathp, NULL, TRUE);
4008 lock_ReleaseMutex(&fidp->mx);
4010 if (fidp->flags & SMB_FID_NTOPEN) {
4011 cm_ReleaseSCache(fidp->NTopen_dscp);
4012 free(fidp->NTopen_pathp);
4014 if (fidp->NTopen_wholepathp)
4015 free(fidp->NTopen_wholepathp);
4016 smb_ReleaseFID(fidp);
4018 cm_ReleaseUser(userp);
4024 * smb_ReadData -- common code for Read, Read And X, and Raw Read
4027 long smb_ReadData(smb_fid_t *fidp, osi_hyper_t *offsetp, long count, char *op,
4028 cm_user_t *userp, long *readp)
4030 long smb_ReadData(smb_fid_t *fidp, osi_hyper_t *offsetp, long count, char *op,
4031 cm_user_t *userp, long *readp, int dosflag)
4038 osi_hyper_t fileLength;
4040 osi_hyper_t lastByte;
4041 osi_hyper_t bufferOffset;
4042 long bufIndex, nbytes;
4052 lock_ObtainMutex(&fidp->mx);
4054 lock_ObtainMutex(&scp->mx);
4056 if (offset.HighPart == 0) {
4057 chunk = offset.LowPart >> cm_logChunkSize;
4058 if (chunk != fidp->curr_chunk) {
4059 fidp->prev_chunk = fidp->curr_chunk;
4060 fidp->curr_chunk = chunk;
4062 if (fidp->curr_chunk == fidp->prev_chunk + 1)
4066 /* start by looking up the file's end */
4067 code = cm_SyncOp(scp, NULL, userp, &req, 0,
4068 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
4069 if (code) goto done;
4071 /* now we have the entry locked, look up the length */
4072 fileLength = scp->length;
4074 /* adjust count down so that it won't go past EOF */
4075 thyper.LowPart = count;
4076 thyper.HighPart = 0;
4077 thyper = LargeIntegerAdd(offset, thyper); /* where read should end */
4079 if (LargeIntegerGreaterThan(thyper, fileLength)) {
4080 /* we'd read past EOF, so just stop at fileLength bytes.
4081 * Start by computing how many bytes remain in the file.
4083 thyper = LargeIntegerSubtract(fileLength, offset);
4085 /* if we are past EOF, read 0 bytes */
4086 if (LargeIntegerLessThanZero(thyper))
4089 count = thyper.LowPart;
4094 /* now, copy the data one buffer at a time,
4095 * until we've filled the request packet
4098 /* if we've copied all the data requested, we're done */
4099 if (count <= 0) break;
4101 /* otherwise, load up a buffer of data */
4102 thyper.HighPart = offset.HighPart;
4103 thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
4104 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
4107 buf_Release(bufferp);
4110 lock_ReleaseMutex(&scp->mx);
4112 lock_ObtainRead(&scp->bufCreateLock);
4113 code = buf_Get(scp, &thyper, &bufferp);
4114 lock_ReleaseRead(&scp->bufCreateLock);
4116 lock_ObtainMutex(&scp->mx);
4117 if (code) goto done;
4118 bufferOffset = thyper;
4120 /* now get the data in the cache */
4122 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
4123 CM_SCACHESYNC_NEEDCALLBACK
4124 | CM_SCACHESYNC_READ);
4125 if (code) goto done;
4127 if (cm_HaveBuffer(scp, bufferp, 0)) break;
4129 /* otherwise, load the buffer and try again */
4130 code = cm_GetBuffer(scp, bufferp, NULL, userp, &req);
4134 buf_Release(bufferp);
4138 } /* if (wrong buffer) ... */
4140 /* now we have the right buffer loaded. Copy out the
4141 * data from here to the user's buffer.
4143 bufIndex = offset.LowPart & (buf_bufferSize - 1);
4145 /* and figure out how many bytes we want from this buffer */
4146 nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */
4147 if (nbytes > count) nbytes = count; /* don't go past EOF */
4149 /* now copy the data */
4152 dosmemput(bufferp->datap + bufIndex, nbytes, (dos_ptr)op);
4155 memcpy(op, bufferp->datap + bufIndex, nbytes);
4157 /* adjust counters, pointers, etc. */
4160 thyper.LowPart = nbytes;
4161 thyper.HighPart = 0;
4162 offset = LargeIntegerAdd(thyper, offset);
4166 lock_ReleaseMutex(&scp->mx);
4167 lock_ReleaseMutex(&fidp->mx);
4168 if (bufferp) buf_Release(bufferp);
4170 if (code == 0 && sequential)
4171 cm_ConsiderPrefetch(scp, &lastByte, userp, &req);
4177 * smb_WriteData -- common code for Write and Raw Write
4180 long smb_WriteData(smb_fid_t *fidp, osi_hyper_t *offsetp, long count, char *op,
4181 cm_user_t *userp, long *writtenp)
4183 long smb_WriteData(smb_fid_t *fidp, osi_hyper_t *offsetp, long count, char *op,
4184 cm_user_t *userp, long *writtenp, int dosflag)
4191 osi_hyper_t fileLength; /* file's length at start of write */
4192 osi_hyper_t minLength; /* don't read past this */
4193 long nbytes; /* # of bytes to transfer this iteration */
4195 osi_hyper_t thyper; /* hyper tmp variable */
4196 osi_hyper_t bufferOffset;
4197 long bufIndex; /* index in buffer where our data is */
4199 osi_hyper_t writeBackOffset; /* offset of region to write back when
4210 lock_ObtainMutex(&fidp->mx);
4212 lock_ObtainMutex(&scp->mx);
4214 /* start by looking up the file's end */
4215 code = cm_SyncOp(scp, NULL, userp, &req, 0,
4216 CM_SCACHESYNC_NEEDCALLBACK
4217 | CM_SCACHESYNC_SETSTATUS
4218 | CM_SCACHESYNC_GETSTATUS);
4219 if (code) goto done;
4221 /* make sure we have a writable FD */
4222 if (!(fidp->flags & SMB_FID_OPENWRITE)) {
4223 code = CM_ERROR_BADFDOP;
4227 /* now we have the entry locked, look up the length */
4228 fileLength = scp->length;
4229 minLength = fileLength;
4230 if (LargeIntegerGreaterThan(minLength, scp->serverLength))
4231 minLength = scp->serverLength;
4233 /* adjust file length if we extend past EOF */
4234 thyper.LowPart = count;
4235 thyper.HighPart = 0;
4236 thyper = LargeIntegerAdd(offset, thyper); /* where write should end */
4237 if (LargeIntegerGreaterThan(thyper, fileLength)) {
4238 /* we'd write past EOF, so extend the file */
4239 scp->mask |= CM_SCACHEMASK_LENGTH;
4240 scp->length = thyper;
4242 (FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE);
4244 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
4246 /* now, if the new position (thyper) and the old (offset) are in
4247 * different storeback windows, remember to store back the previous
4248 * storeback window when we're done with the write.
4250 if ((thyper.LowPart & (-cm_chunkSize)) !=
4251 (offset.LowPart & (-cm_chunkSize))) {
4252 /* they're different */
4254 writeBackOffset.HighPart = offset.HighPart;
4255 writeBackOffset.LowPart = offset.LowPart & (-cm_chunkSize);
4260 /* now, copy the data one buffer at a time, until we've filled the
4263 /* if we've copied all the data requested, we're done */
4264 if (count <= 0) break;
4266 /* handle over quota or out of space */
4267 if (scp->flags & (CM_SCACHEFLAG_OVERQUOTA
4268 | CM_SCACHEFLAG_OUTOFSPACE)) {
4269 *writtenp = written;
4273 /* otherwise, load up a buffer of data */
4274 thyper.HighPart = offset.HighPart;
4275 thyper.LowPart = offset.LowPart & ~(buf_bufferSize-1);
4276 if (!bufferp || !LargeIntegerEqualTo(thyper, bufferOffset)) {
4279 lock_ReleaseMutex(&bufferp->mx);
4280 buf_Release(bufferp);
4283 lock_ReleaseMutex(&scp->mx);
4285 lock_ObtainRead(&scp->bufCreateLock);
4286 code = buf_Get(scp, &thyper, &bufferp);
4287 lock_ReleaseRead(&scp->bufCreateLock);
4289 lock_ObtainMutex(&bufferp->mx);
4290 lock_ObtainMutex(&scp->mx);
4291 if (code) goto done;
4293 bufferOffset = thyper;
4295 /* now get the data in the cache */
4297 code = cm_SyncOp(scp, bufferp, userp, &req, 0,
4298 CM_SCACHESYNC_NEEDCALLBACK
4299 | CM_SCACHESYNC_WRITE
4300 | CM_SCACHESYNC_BUFLOCKED);
4301 if (code) goto done;
4303 /* If we're overwriting the entire buffer, or
4304 * if we're writing at or past EOF, mark the
4305 * buffer as current so we don't call
4306 * cm_GetBuffer. This skips the fetch from the
4307 * server in those cases where we're going to
4308 * obliterate all the data in the buffer anyway,
4309 * or in those cases where there is no useful
4310 * data at the server to start with.
4312 * Use minLength instead of scp->length, since
4313 * the latter has already been updated by this
4316 if (LargeIntegerGreaterThanOrEqualTo(
4317 bufferp->offset, minLength)
4318 || LargeIntegerEqualTo(offset, bufferp->offset)
4319 && (count >= buf_bufferSize
4320 || LargeIntegerGreaterThanOrEqualTo(
4321 LargeIntegerAdd(offset,
4322 ConvertLongToLargeInteger(count)),
4324 if (count < buf_bufferSize
4325 && bufferp->dataVersion == -1)
4326 memset(bufferp->datap, 0,
4328 bufferp->dataVersion = scp->dataVersion;
4331 if (cm_HaveBuffer(scp, bufferp, 1)) break;
4333 /* otherwise, load the buffer and try again */
4334 lock_ReleaseMutex(&bufferp->mx);
4335 code = cm_GetBuffer(scp, bufferp, NULL, userp,
4337 lock_ReleaseMutex(&scp->mx);
4338 lock_ObtainMutex(&bufferp->mx);
4339 lock_ObtainMutex(&scp->mx);
4343 lock_ReleaseMutex(&bufferp->mx);
4344 buf_Release(bufferp);
4348 } /* if (wrong buffer) ... */
4350 /* now we have the right buffer loaded. Copy out the
4351 * data from here to the user's buffer.
4353 bufIndex = offset.LowPart & (buf_bufferSize - 1);
4355 /* and figure out how many bytes we want from this buffer */
4356 nbytes = buf_bufferSize - bufIndex; /* what remains in buffer */
4357 if (nbytes > count) nbytes = count; /* don't go past end of request */
4359 /* now copy the data */
4362 dosmemget((dos_ptr)op, nbytes, bufferp->datap + bufIndex);
4365 memcpy(bufferp->datap + bufIndex, op, nbytes);
4366 buf_SetDirty(bufferp);
4368 /* and record the last writer */
4369 if (bufferp->userp != userp) {
4370 if (bufferp->userp) cm_ReleaseUser(bufferp->userp);
4371 bufferp->userp = userp;
4375 /* adjust counters, pointers, etc. */
4379 thyper.LowPart = nbytes;
4380 thyper.HighPart = 0;
4381 offset = LargeIntegerAdd(thyper, offset);
4385 lock_ReleaseMutex(&scp->mx);
4386 lock_ReleaseMutex(&fidp->mx);
4388 lock_ReleaseMutex(&bufferp->mx);
4389 buf_Release(bufferp);
4392 if (code == 0 && filter != 0 && (fidp->flags & SMB_FID_NTOPEN)
4393 && (fidp->NTopen_dscp->flags & CM_SCACHEFLAG_ANYWATCH)) {
4394 smb_NotifyChange(FILE_ACTION_MODIFIED, filter,
4395 fidp->NTopen_dscp, fidp->NTopen_pathp,
4399 if (code == 0 && doWriteBack) {
4400 lock_ObtainMutex(&scp->mx);
4401 cm_SyncOp(scp, NULL, userp, &req, 0, CM_SCACHESYNC_ASYNCSTORE);
4402 lock_ReleaseMutex(&scp->mx);
4403 cm_QueueBKGRequest(scp, cm_BkgStore, writeBackOffset.LowPart,
4404 writeBackOffset.HighPart, cm_chunkSize, 0, userp);
4410 long smb_ReceiveCoreWrite(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
4413 long count, written = 0;
4418 cm_attr_t truncAttr; /* attribute struct used for truncating file */
4420 int inDataBlockCount;
4422 fd = smb_GetSMBParm(inp, 0);
4423 count = smb_GetSMBParm(inp, 1);
4424 offset.HighPart = 0; /* too bad */
4425 offset.LowPart = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
4427 op = smb_GetSMBData(inp, NULL);
4428 op = smb_ParseDataBlock(op, NULL, &inDataBlockCount);
4430 osi_Log3(afsd_logp, "smb_ReceiveCoreWrite fd %d, off 0x%x, size 0x%x",
4431 fd, offset.LowPart, count);
4433 fd = smb_ChainFID(fd, inp);
4434 fidp = smb_FindFID(vcp, fd, 0);
4436 return CM_ERROR_BADFD;
4439 if (fidp->flags & SMB_FID_IOCTL)
4440 return smb_IoctlWrite(fidp, vcp, inp, outp);
4442 userp = smb_GetUser(vcp, inp);
4444 /* special case: 0 bytes transferred means truncate to this position */
4450 truncAttr.mask = CM_ATTRMASK_LENGTH;
4451 truncAttr.length.LowPart = offset.LowPart;
4452 truncAttr.length.HighPart = 0;
4453 lock_ObtainMutex(&fidp->mx);
4454 code = cm_SetAttr(fidp->scp, &truncAttr, userp, &req);
4455 lock_ReleaseMutex(&fidp->mx);
4456 smb_SetSMBParm(outp, 0, /* count */ 0);
4457 smb_SetSMBDataLength(outp, 0);
4458 fidp->flags |= SMB_FID_LENGTHSETDONE;
4463 * Work around bug in NT client
4465 * When copying a file, the NT client should first copy the data,
4466 * then copy the last write time. But sometimes the NT client does
4467 * these in the wrong order, so the data copies would inadvertently
4468 * cause the last write time to be overwritten. We try to detect this,
4469 * and don't set client mod time if we think that would go against the
4472 if ((fidp->flags & SMB_FID_MTIMESETDONE) != SMB_FID_MTIMESETDONE) {
4473 fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
4474 fidp->scp->clientModTime = time(NULL);
4478 code = smb_WriteData(fidp, &offset, count, op, userp, &written);
4480 code = smb_WriteData(fidp, &offset, count, op, userp, &written, FALSE);
4482 if (code == 0 && written < count)
4483 code = CM_ERROR_PARTIALWRITE;
4485 /* set the packet data length to 3 bytes for the data block header,
4486 * plus the size of the data.
4488 smb_SetSMBParm(outp, 0, written);
4489 smb_SetSMBDataLength(outp, 0);
4492 smb_ReleaseFID(fidp);
4493 cm_ReleaseUser(userp);
4498 void smb_CompleteWriteRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
4499 NCB *ncbp, raw_write_cont_t *rwcp)
4512 fd = smb_GetSMBParm(inp, 0);
4513 fidp = smb_FindFID(vcp, fd, 0);
4515 osi_Log2(afsd_logp, "Completing Raw Write offset %x count %x",
4516 rwcp->offset.LowPart, rwcp->count);
4518 userp = smb_GetUser(vcp, inp);
4522 code = smb_WriteData(fidp, &rwcp->offset, rwcp->count, rawBuf, userp,
4525 rawBuf = (dos_ptr) rwcp->buf;
4526 code = smb_WriteData(fidp, &rwcp->offset, rwcp->count,
4527 (unsigned char *) rawBuf, userp,
4531 if (rwcp->writeMode & 0x1) { /* synchronous */
4534 smb_FormatResponsePacket(vcp, inp, outp);
4535 op = (smb_t *) outp;
4536 op->com = 0x20; /* SMB_COM_WRITE_COMPLETE */
4537 smb_SetSMBParm(outp, 0, written + rwcp->alreadyWritten);
4538 smb_SetSMBDataLength(outp, 0);
4539 smb_SendPacket(vcp, outp);
4540 smb_FreePacket(outp);
4542 else { /* asynchronous */
4543 lock_ObtainMutex(&fidp->mx);
4544 fidp->raw_writers--;
4545 if (fidp->raw_writers == 0)
4546 thrd_SetEvent(fidp->raw_write_event);
4547 lock_ReleaseMutex(&fidp->mx);
4550 /* Give back raw buffer */
4551 lock_ObtainMutex(&smb_RawBufLock);
4553 *((char **)rawBuf) = smb_RawBufs;
4555 _farpokel(_dos_ds, rawBuf, smb_RawBufs);
4557 smb_RawBufs = rawBuf;
4558 lock_ReleaseMutex(&smb_RawBufLock);
4560 smb_ReleaseFID(fidp);
4561 cm_ReleaseUser(userp);
4564 long smb_ReceiveCoreWriteRawDummy(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
4569 long smb_ReceiveCoreWriteRaw(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp, raw_write_cont_t *rwcp)
4572 long count, written = 0;
4579 unsigned short writeMode;
4586 fd = smb_GetSMBParm(inp, 0);
4587 totalCount = smb_GetSMBParm(inp, 1);
4588 count = smb_GetSMBParm(inp, 10);
4589 offset.HighPart = 0; /* too bad */
4590 offset.LowPart = smb_GetSMBParm(inp, 3) | (smb_GetSMBParm(inp, 4) << 16);
4591 writeMode = smb_GetSMBParm(inp, 7);
4593 op = (char *) inp->data;
4594 op += smb_GetSMBParm(inp, 11);
4597 "smb_ReceiveCoreWriteRaw fd %d, off 0x%x, size 0x%x, WriteMode 0x%x",
4598 fd, offset.LowPart, count, writeMode);
4600 fd = smb_ChainFID(fd, inp);
4601 fidp = smb_FindFID(vcp, fd, 0);
4603 return CM_ERROR_BADFD;
4606 userp = smb_GetUser(vcp, inp);
4609 * Work around bug in NT client
4611 * When copying a file, the NT client should first copy the data,
4612 * then copy the last write time. But sometimes the NT client does
4613 * these in the wrong order, so the data copies would inadvertently
4614 * cause the last write time to be overwritten. We try to detect this,
4615 * and don't set client mod time if we think that would go against the
4618 if ((fidp->flags & SMB_FID_LOOKSLIKECOPY) != SMB_FID_LOOKSLIKECOPY) {
4619 fidp->scp->mask |= CM_SCACHEMASK_CLIENTMODTIME;
4620 fidp->scp->clientModTime = time(NULL);
4624 code = smb_WriteData(fidp, &offset, count, op, userp, &written);
4626 code = smb_WriteData(fidp, &offset, count, op, userp, &written, FALSE);
4628 if (code == 0 && written < count)
4629 code = CM_ERROR_PARTIALWRITE;
4631 /* Get a raw buffer */
4634 lock_ObtainMutex(&smb_RawBufLock);
4636 /* Get a raw buf, from head of list */
4637 rawBuf = smb_RawBufs;
4639 smb_RawBufs = *(char **)smb_RawBufs;
4641 smb_RawBufs = _farpeekl(_dos_ds, smb_RawBufs);
4645 code = CM_ERROR_USESTD;
4646 lock_ReleaseMutex(&smb_RawBufLock);
4649 /* Don't allow a premature Close */
4650 if (code == 0 && (writeMode & 1) == 0) {
4651 lock_ObtainMutex(&fidp->mx);
4652 fidp->raw_writers++;
4653 thrd_ResetEvent(fidp->raw_write_event);
4654 lock_ReleaseMutex(&fidp->mx);
4657 smb_ReleaseFID(fidp);
4658 cm_ReleaseUser(userp);
4661 smb_SetSMBParm(outp, 0, written);
4662 smb_SetSMBDataLength(outp, 0);
4663 ((smb_t *)outp)->com = 0x20; /* SMB_COM_WRITE_COMPLETE */
4670 rwcp->offset.HighPart = 0;
4671 rwcp->offset.LowPart = offset.LowPart + count;
4672 rwcp->count = totalCount - count;
4673 rwcp->writeMode = writeMode;
4674 rwcp->alreadyWritten = written;
4676 /* set the packet data length to 3 bytes for the data block header,
4677 * plus the size of the data.
4679 smb_SetSMBParm(outp, 0, 0xffff);
4680 smb_SetSMBDataLength(outp, 0);
4685 long smb_ReceiveCoreRead(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
4688 long count, finalCount;
4695 fd = smb_GetSMBParm(inp, 0);
4696 count = smb_GetSMBParm(inp, 1);
4697 offset.HighPart = 0; /* too bad */
4698 offset.LowPart = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
4700 osi_Log3(afsd_logp, "smb_ReceiveCoreRead fd %d, off 0x%x, size 0x%x",
4701 fd, offset.LowPart, count);
4703 fd = smb_ChainFID(fd, inp);
4704 fidp = smb_FindFID(vcp, fd, 0);
4706 return CM_ERROR_BADFD;
4709 if (fidp->flags & SMB_FID_IOCTL) {
4710 return smb_IoctlRead(fidp, vcp, inp, outp);
4713 userp = smb_GetUser(vcp, inp);
4715 /* remember this for final results */
4716 smb_SetSMBParm(outp, 0, count);
4717 smb_SetSMBParm(outp, 1, 0);
4718 smb_SetSMBParm(outp, 2, 0);
4719 smb_SetSMBParm(outp, 3, 0);
4720 smb_SetSMBParm(outp, 4, 0);
4722 /* set the packet data length to 3 bytes for the data block header,
4723 * plus the size of the data.
4725 smb_SetSMBDataLength(outp, count+3);
4727 /* get op ptr after putting in the parms, since otherwise we don't
4728 * know where the data really is.
4730 op = smb_GetSMBData(outp, NULL);
4732 /* now emit the data block header: 1 byte of type and 2 bytes of length */
4733 *op++ = 1; /* data block marker */
4734 *op++ = (unsigned char) (count & 0xff);
4735 *op++ = (unsigned char) ((count >> 8) & 0xff);
4738 code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount);
4740 code = smb_ReadData(fidp, &offset, count, op, userp, &finalCount, FALSE);
4743 /* fix some things up */
4744 smb_SetSMBParm(outp, 0, finalCount);
4745 smb_SetSMBDataLength(outp, finalCount+3);
4747 smb_ReleaseFID(fidp);
4749 cm_ReleaseUser(userp);
4753 long smb_ReceiveCoreMakeDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
4760 cm_scache_t *dscp; /* dir we're dealing with */
4761 cm_scache_t *scp; /* file we're creating */
4763 int initialModeBits;
4773 /* compute initial mode bits based on read-only flag in attributes */
4774 initialModeBits = 0777;
4776 tp = smb_GetSMBData(inp, NULL);
4777 pathp = smb_ParseASCIIBlock(tp, &tp);
4779 if (strcmp(pathp, "\\") == 0)
4780 return CM_ERROR_EXISTS;
4782 spacep = inp->spacep;
4783 smb_StripLastComponent(spacep->data, &lastNamep, pathp);
4785 userp = smb_GetUser(vcp, inp);
4787 caseFold = CM_FLAG_CASEFOLD;
4789 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
4791 code = cm_NameI(cm_rootSCachep, spacep->data,
4792 caseFold | CM_FLAG_FOLLOW | CM_FLAG_CHECKPATH,
4793 userp, tidPathp, &req, &dscp);
4796 cm_ReleaseUser(userp);
4800 /* otherwise, scp points to the parent directory. Do a lookup, and
4801 * fail if we find it. Otherwise, we do the create.
4803 if (!lastNamep) lastNamep = pathp;
4805 code = cm_Lookup(dscp, lastNamep, caseFold, userp, &req, &scp);
4806 if (scp) cm_ReleaseSCache(scp);
4807 if (code != CM_ERROR_NOSUCHFILE) {
4808 if (code == 0) code = CM_ERROR_EXISTS;
4809 cm_ReleaseSCache(dscp);
4810 cm_ReleaseUser(userp);
4814 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
4815 setAttr.clientModTime = time(NULL);
4816 code = cm_MakeDir(dscp, lastNamep, 0, &setAttr, userp, &req);
4817 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
4818 smb_NotifyChange(FILE_ACTION_ADDED,
4819 FILE_NOTIFY_CHANGE_DIR_NAME,
4820 dscp, lastNamep, NULL, TRUE);
4822 /* we don't need this any longer */
4823 cm_ReleaseSCache(dscp);
4826 /* something went wrong creating or truncating the file */
4827 cm_ReleaseUser(userp);
4831 /* otherwise we succeeded */
4832 smb_SetSMBDataLength(outp, 0);
4833 cm_ReleaseUser(userp);
4838 BOOL smb_IsLegalFilename(char *filename)
4841 * Find the longest substring of filename that does not contain
4842 * any of the chars in illegalChars. If that substring is less
4843 * than the length of the whole string, then one or more of the
4844 * illegal chars is in filename.
4846 if (strcspn(filename, illegalChars) < strlen(filename))
4852 long smb_ReceiveCoreCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
4860 cm_scache_t *dscp; /* dir we're dealing with */
4861 cm_scache_t *scp; /* file we're creating */
4863 int initialModeBits;
4875 excl = (inp->inCom == 0x03)? 0 : 1;
4877 attributes = smb_GetSMBParm(inp, 0);
4878 dosTime = smb_GetSMBParm(inp, 1) | (smb_GetSMBParm(inp, 2) << 16);
4880 /* compute initial mode bits based on read-only flag in attributes */
4881 initialModeBits = 0666;
4882 if (attributes & 1) initialModeBits &= ~0222;
4884 tp = smb_GetSMBData(inp, NULL);
4885 pathp = smb_ParseASCIIBlock(tp, &tp);
4887 spacep = inp->spacep;
4888 smb_StripLastComponent(spacep->data, &lastNamep, pathp);
4890 userp = smb_GetUser(vcp, inp);
4892 caseFold = CM_FLAG_CASEFOLD;
4894 tidPathp = smb_GetTIDPath(vcp, ((smb_t *)inp)->tid);
4895 code = cm_NameI(cm_rootSCachep, spacep->data, caseFold | CM_FLAG_FOLLOW,
4896 userp, tidPathp, &req, &dscp);
4899 cm_ReleaseUser(userp);
4903 /* otherwise, scp points to the parent directory. Do a lookup, and
4904 * truncate the file if we find it, otherwise we create the file.
4906 if (!lastNamep) lastNamep = pathp;
4909 if (!smb_IsLegalFilename(lastNamep))
4910 return CM_ERROR_BADNTFILENAME;
4912 code = cm_Lookup(dscp, lastNamep, caseFold, userp, &req, &scp);
4913 if (code && code != CM_ERROR_NOSUCHFILE) {
4914 cm_ReleaseSCache(dscp);
4915 cm_ReleaseUser(userp);
4919 /* if we get here, if code is 0, the file exists and is represented by
4920 * scp. Otherwise, we have to create it.
4924 /* oops, file shouldn't be there */
4925 cm_ReleaseSCache(dscp);
4926 cm_ReleaseSCache(scp);
4927 cm_ReleaseUser(userp);
4928 return CM_ERROR_EXISTS;
4931 setAttr.mask = CM_ATTRMASK_LENGTH;
4932 setAttr.length.LowPart = 0;
4933 setAttr.length.HighPart = 0;
4934 code = cm_SetAttr(scp, &setAttr, userp, &req);
4937 setAttr.mask = CM_ATTRMASK_CLIENTMODTIME;
4938 smb_UnixTimeFromDosUTime(&setAttr.clientModTime, dosTime);
4939 code = cm_Create(dscp, lastNamep, 0, &setAttr, &scp, userp,
4941 if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
4942 smb_NotifyChange(FILE_ACTION_ADDED,
4943 FILE_NOTIFY_CHANGE_FILE_NAME,
4944 dscp, lastNamep, NULL, TRUE);
4945 if (!excl && code == CM_ERROR_EXISTS) {
4946 /* not an exclusive create, and someone else tried
4947 * creating it already, then we open it anyway. We
4948 * don't bother retrying after this, since if this next
4949 * fails, that means that the file was deleted after
4950 * we started this call.
4952 code = cm_Lookup(dscp, lastNamep, caseFold, userp,
4955 setAttr.mask = CM_ATTRMASK_LENGTH;
4956 setAttr.length.LowPart = 0;
4957 setAttr.length.HighPart = 0;
4958 code = cm_SetAttr(scp, &setAttr, userp, &req);
4963 /* we don't need this any longer */
4964 cm_ReleaseSCache(dscp);
4967 /* something went wrong creating or truncating the file */
4968 if (scp) cm_ReleaseSCache(scp);
4969 cm_ReleaseUser(userp);
4973 /* make sure we only open files */
4974 if (scp->fileType != CM_SCACHETYPE_FILE) {
4975 cm_ReleaseSCache(scp);
4976 cm_ReleaseUser(userp);
4977 return CM_ERROR_ISDIR;
4980 /* now all we have to do is open the file itself */
4981 fidp = smb_FindFID(vcp, 0, SMB_FLAG_CREATE);
4984 /* save a pointer to the vnode */
4987 /* always create it open for read/write */
4988 fidp->flags |= (SMB_FID_OPENREAD | SMB_FID_OPENWRITE);
4990 smb_ReleaseFID(fidp);
4992 smb_SetSMBParm(outp, 0, fidp->fid);
4993 smb_SetSMBDataLength(outp, 0);
4995 cm_Open(scp, 0, userp);
4997 cm_ReleaseUser(userp);
4998 /* leave scp held since we put it in fidp->scp */
5002 long smb_ReceiveCoreSeek(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
5015 fd = smb_GetSMBParm(inp, 0);
5016 whence = smb_GetSMBParm(inp, 1);
5017 offset = smb_GetSMBParm(inp, 2) | (smb_GetSMBParm(inp, 3) << 16);
5019 /* try to find the file descriptor */
5020 fd = smb_ChainFID(fd, inp);
5021 fidp = smb_FindFID(vcp, fd, 0);
5022 if (!fidp || (fidp->flags & SMB_FID_IOCTL)) {
5023 return CM_ERROR_BADFD;
5026 userp = smb_GetUser(vcp, inp);
5028 lock_ObtainMutex(&fidp->mx);
5030 lock_ObtainMutex(&scp->mx);
5031 code = cm_SyncOp(scp, NULL, userp, &req, 0,
5032 CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
5035 /* offset from current offset */
5036 offset += fidp->offset;
5038 else if (whence == 2) {
5039 /* offset from current EOF */
5040 offset += scp->length.LowPart;
5042 fidp->offset = offset;
5043 smb_SetSMBParm(outp, 0, offset & 0xffff);
5044 smb_SetSMBParm(outp, 1, (offset>>16) & 0xffff);
5045 smb_SetSMBDataLength(outp, 0);
5047 lock_ReleaseMutex(&scp->mx);
5048 lock_ReleaseMutex(&fidp->mx);
5049 smb_ReleaseFID(fidp);
5050 cm_ReleaseUser(userp);
5054 /* dispatch all of the requests received in a packet. Due to chaining, this may
5055 * be more than one request.
5057 void smb_DispatchPacket(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp,
5058 NCB *ncbp, raw_write_cont_t *rwcp)
5060 static showErrors = 1;
5064 unsigned char *outWctp;
5065 int nparms; /* # of bytes of parameters */
5067 int nbytes; /* bytes of data, excluding count */
5070 unsigned short errCode;
5071 unsigned long NTStatus;
5073 unsigned char errClass;
5074 unsigned int oldGen;
5075 DWORD oldTime, newTime;
5077 /* get easy pointer to the data */
5078 smbp = (smb_t *) inp->data;
5080 if (!(outp->flags & SMB_PACKETFLAG_SUSPENDED)) {
5081 /* setup the basic parms for the initial request in the packet */
5082 inp->inCom = smbp->com;
5083 inp->wctp = &smbp->wct;
5085 inp->ncb_length = ncbp->ncb_length;
5090 if (ncbp->ncb_length < offsetof(struct smb, vdata)) {
5091 /* log it and discard it */
5096 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
5097 sprintf(s, "SMB message too short, len %d", ncbp->ncb_length);
5099 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1007, NULL,
5100 1, ncbp->ncb_length, ptbuf, inp);
5101 DeregisterEventSource(h);
5103 osi_Log1(smb_logp, "SMB message too short, len %d",
5110 /* We are an ongoing op */
5111 thrd_Increment(&ongoingOps);
5113 /* set up response packet for receiving output */
5114 if (!(outp->flags & SMB_PACKETFLAG_SUSPENDED))
5115 smb_FormatResponsePacket(vcp, inp, outp);
5116 outWctp = outp->wctp;
5118 /* Remember session generation number and time */
5119 oldGen = sessionGen;
5120 oldTime = GetCurrentTime();
5122 while(inp->inCom != 0xff) {
5123 dp = &smb_dispatchTable[inp->inCom];
5125 if (outp->flags & SMB_PACKETFLAG_SUSPENDED) {
5126 outp->flags &= ~SMB_PACKETFLAG_SUSPENDED;
5127 code = outp->resumeCode;
5131 /* process each request in the packet; inCom, wctp and inCount
5132 * are already set up.
5134 osi_Log2(afsd_logp, "SMB received op 0x%x lsn %d", inp->inCom,
5137 /* now do the dispatch */
5138 /* start by formatting the response record a little, as a default */
5139 if (dp->flags & SMB_DISPATCHFLAG_CHAINED) {
5141 outWctp[1] = 0xff; /* no operation */
5142 outWctp[2] = 0; /* padding */
5147 /* not a chained request, this is a more reasonable default */
5148 outWctp[0] = 0; /* wct of zero */
5149 outWctp[1] = 0; /* and bcc (word) of zero */
5153 /* once set, stays set. Doesn't matter, since we never chain
5154 * "no response" calls.
5156 if (dp->flags & SMB_DISPATCHFLAG_NORESPONSE)
5160 /* we have a recognized operation */
5162 if (inp->inCom == 0x1d)
5164 code = smb_ReceiveCoreWriteRaw (vcp, inp, outp,
5167 code = (*(dp->procp)) (vcp, inp, outp);
5169 if (oldGen != sessionGen) {
5174 newTime = GetCurrentTime();
5175 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
5176 sprintf(s, "Pkt straddled session startup, took %d ms, ncb length %d",
5177 newTime - oldTime, ncbp->ncb_length);
5179 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0,
5180 1005, NULL, 1, ncbp->ncb_length, ptbuf, smbp);
5181 DeregisterEventSource(h);
5183 osi_Log1(afsd_logp, "Pkt straddled session startup, "
5184 "ncb length %d", ncbp->ncb_length);
5189 /* bad opcode, fail the request, after displaying it */
5192 sprintf(tbuffer, "Received bad SMB req 0x%x", inp->inCom);
5193 code = (*smb_MBfunc)(NULL, tbuffer, "Cancel: don't show again",
5195 if (code == IDCANCEL) showErrors = 0;
5198 code = CM_ERROR_BADOP;
5201 /* catastrophic failure: log as much as possible */
5202 if (code == CM_ERROR_BADSMB) {
5209 "Invalid SMB, ncb_length %d",
5212 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
5213 sprintf(s, "Invalid SMB message, length %d",
5216 ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, 1002, NULL,
5217 1, ncbp->ncb_length, ptbuf, smbp);
5218 DeregisterEventSource(h);
5220 osi_Log1(afsd_logp, "Invalid SMB message, length %d",
5224 code = CM_ERROR_INVAL;
5227 if (outp->flags & SMB_PACKETFLAG_NOSEND) {
5228 thrd_Decrement(&ongoingOps);
5233 /* now, if we failed, turn the current response into an empty
5234 * one, and fill in the response packet's error code.
5237 if (vcp->flags & SMB_VCFLAG_STATUS32) {
5238 smb_MapNTError(code, &NTStatus);
5239 outWctp = outp->wctp;
5240 smbp = (smb_t *) &outp->data;
5241 if (code != CM_ERROR_PARTIALWRITE
5242 && code != CM_ERROR_BUFFERTOOSMALL) {
5243 /* nuke wct and bcc. For a partial
5244 * write, assume they're OK.
5250 smbp->rcls = (unsigned char) (NTStatus & 0xff);
5251 smbp->reh = (unsigned char) ((NTStatus >> 8) & 0xff);
5252 smbp->errLow = (unsigned char) ((NTStatus >> 16) & 0xff);
5253 smbp->errHigh = (unsigned char) ((NTStatus >> 24) & 0xff);
5254 smbp->flg2 |= 0x4000;
5258 smb_MapCoreError(code, vcp, &errCode, &errClass);
5259 outWctp = outp->wctp;
5260 smbp = (smb_t *) &outp->data;
5261 if (code != CM_ERROR_PARTIALWRITE) {
5262 /* nuke wct and bcc. For a partial
5263 * write, assume they're OK.
5269 smbp->errLow = (unsigned char) (errCode & 0xff);
5270 smbp->errHigh = (unsigned char) ((errCode >> 8) & 0xff);
5271 smbp->rcls = errClass;
5274 } /* error occurred */
5276 /* if we're here, we've finished one request. Look to see if
5277 * this is a chained opcode. If it is, setup things to process
5278 * the chained request, and setup the output buffer to hold the
5279 * chained response. Start by finding the next input record.
5281 if (!(dp->flags & SMB_DISPATCHFLAG_CHAINED))
5282 break; /* not a chained req */
5283 tp = inp->wctp; /* points to start of last request */
5284 if (tp[0] < 2) break; /* in a chained request, the first two
5285 * parm fields are required, and are
5286 * AndXCommand/AndXReserved and
5288 if (tp[1] == 0xff) break; /* no more chained opcodes */
5290 inp->wctp = inp->data + tp[3] + (tp[4] << 8);
5293 /* and now append the next output request to the end of this
5294 * last request. Begin by finding out where the last response
5295 * ends, since that's where we'll put our new response.
5297 outWctp = outp->wctp; /* ptr to out parameters */
5298 osi_assert (outWctp[0] >= 2); /* need this for all chained requests */
5299 nparms = outWctp[0] << 1;
5300 tp = outWctp + nparms + 1; /* now points to bcc field */
5301 nbytes = tp[0] + (tp[1] << 8); /* # of data bytes */
5302 tp += 2 /* for the count itself */ + nbytes;
5303 /* tp now points to the new output record; go back and patch the
5304 * second parameter (off2) to point to the new record.
5306 temp = (unsigned int)tp - ((unsigned int) outp->data);
5307 outWctp[3] = (unsigned char) (temp & 0xff);
5308 outWctp[4] = (unsigned char) ((temp >> 8) & 0xff);
5309 outWctp[2] = 0; /* padding */
5310 outWctp[1] = inp->inCom; /* next opcode */
5312 /* finally, setup for the next iteration */
5315 } /* while loop over all requests in the packet */
5317 /* done logging out, turn off logging-out flag */
5318 if (!(inp->flags & SMB_PACKETFLAG_PROFILE_UPDATE_OK)) {
5319 vcp->justLoggedOut = NULL;
5322 free(loggedOutName);
5323 loggedOutName = NULL;
5324 smb_ReleaseUID(loggedOutUserp);
5325 loggedOutUserp = NULL;
5329 /* now send the output packet, and return */
5331 smb_SendPacket(vcp, outp);
5332 thrd_Decrement(&ongoingOps);
5334 if (!(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
5336 last_msg_time = GetCurrentTime();
5338 else if (active_vcp == vcp)
5345 /* Wait for Netbios() calls to return, and make the results available to server
5346 * threads. Note that server threads can't wait on the NCBevents array
5347 * themselves, because NCB events are manual-reset, and the servers would race
5348 * each other to reset them.
5350 void smb_ClientWaiter(void *parmp)
5355 code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBevents,
5357 if (code == WAIT_OBJECT_0)
5359 idx = code - WAIT_OBJECT_0;
5361 thrd_ResetEvent(NCBevents[idx]);
5362 thrd_SetEvent(NCBreturns[0][idx]);
5368 * Try to have one NCBRECV request waiting for every live session. Not more
5369 * than one, because if there is more than one, it's hard to handle Write Raw.
5371 void smb_ServerWaiter(void *parmp)
5373 DWORD code, idx_session, idx_NCB;
5381 code = thrd_WaitForMultipleObjects_Event(numSessions, SessionEvents,
5383 if (code == WAIT_OBJECT_0)
5385 idx_session = code - WAIT_OBJECT_0;
5389 code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBavails,
5391 if (code == WAIT_OBJECT_0)
5393 idx_NCB = code - WAIT_OBJECT_0;
5395 /* Link them together */
5396 NCBsessions[idx_NCB] = idx_session;
5399 ncbp = NCBs[idx_NCB];
5401 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
5403 ncbp->ncb_lsn = (unsigned char) LSNs[idx_session];
5404 ncbp->ncb_command = NCBRECV | ASYNCH;
5405 ncbp->ncb_lana_num = smb_LANadapter;
5407 ncbp->ncb_buffer = (unsigned char *) bufs[idx_NCB];
5408 ncbp->ncb_event = NCBevents[idx_NCB];
5409 ncbp->ncb_length = SMB_PACKETSIZE;
5412 ncbp->ncb_buffer = bufs[idx_NCB]->dos_pkt;
5413 ((smb_ncb_t*)ncbp)->orig_pkt = bufs[idx_NCB];
5414 ncbp->ncb_event = NCBreturns[0][idx_NCB];
5415 ncbp->ncb_length = SMB_PACKETSIZE;
5416 Netbios(ncbp, dos_ncb);
5422 * The top level loop for handling SMB request messages. Each server thread
5423 * has its own NCB and buffer for sending replies (outncbp, outbufp), but the
5424 * NCB and buffer for the incoming request are loaned to us.
5426 * Write Raw trickery starts here. When we get a Write Raw, we are supposed
5427 * to immediately send a request for the rest of the data. This must come
5428 * before any other traffic for that session, so we delay setting the session
5429 * event until that data has come in.
5431 void smb_Server(VOID *parmp)
5433 int myIdx = (int) parmp;
5437 smb_packet_t *outbufp;
5438 DWORD code, rcode, idx_NCB, idx_session;
5447 outbufp = GetPacket();
5448 outbufp->ncbp = outncbp;
5452 /* check for demo expiration */
5454 unsigned long tod = time((void *) 0);
5455 if (tod > EXPIREDATE) {
5456 (*smb_MBfunc)(NULL, "AFS demo expiration",
5458 MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
5462 #endif /* !NOEXPIRE */
5464 code = thrd_WaitForMultipleObjects_Event(numNCBs, NCBreturns[myIdx],
5466 if (code == WAIT_OBJECT_0)
5468 idx_NCB = code - WAIT_OBJECT_0;
5470 ncbp = NCBs[idx_NCB];
5472 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
5474 idx_session = NCBsessions[idx_NCB];
5475 rc = ncbp->ncb_retcode;
5477 if (rc != NRC_PENDING && rc != NRC_GOODRET)
5478 osi_Log1(afsd_logp, "NCBRECV failure code %d", rc);
5481 case NRC_GOODRET: break;
5484 /* Can this happen? Or is it just my
5490 /* Client closed session */
5491 dead_sessions[idx_session] = TRUE;
5492 vcp = smb_FindVC(ncbp->ncb_lsn, 0);
5493 /* Should also release vcp. Also, would do
5494 * sanity check that all TID's are gone. */
5497 "dead_vcp already set, %x",
5500 && !(vcp->flags & SMB_VCFLAG_ALREADYDEAD)) {
5502 "setting dead_vcp %x, user struct %x",
5505 vcp->flags |= SMB_VCFLAG_ALREADYDEAD;
5507 if (vcp->justLoggedOut) {
5509 loggedOutTime = vcp->logoffTime;
5511 strdup(vcp->justLoggedOut->name);
5512 loggedOutUserp = vcp->justLoggedOut;
5513 lock_ObtainWrite(&smb_rctLock);
5514 loggedOutUserp->refCount++;
5515 lock_ReleaseWrite(&smb_rctLock);
5520 /* Treat as transient error */
5528 "dispatch smb recv failed, message incomplete, ncb_length %d",
5530 h = RegisterEventSource(NULL,
5531 AFS_DAEMON_EVENT_NAME);
5532 sprintf(s, "SMB message incomplete, length %d",
5535 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0,
5537 ncbp->ncb_length, ptbuf,
5539 DeregisterEventSource(h);
5542 "dispatch smb recv failed, message incomplete, ncb_length %d",
5545 "SMB message incomplete, "
5546 "length %d", ncbp->ncb_length);
5550 * We used to discard the packet.
5551 * Instead, try handling it normally.
5559 /* A weird error code. Log it, sleep, and
5561 if (vcp->errorCount++ > 3)
5562 dead_sessions[idx_session] = TRUE;
5565 thrd_SetEvent(SessionEvents[idx_session]);
5570 /* Success, so now dispatch on all the data in the packet */
5572 smb_concurrentCalls++;
5573 if (smb_concurrentCalls > smb_maxObsConcurrentCalls)
5574 smb_maxObsConcurrentCalls = smb_concurrentCalls;
5576 vcp = smb_FindVC(ncbp->ncb_lsn, 0);
5577 vcp->errorCount = 0;
5578 bufp = (struct smb_packet *) ncbp->ncb_buffer;
5580 bufp = ((smb_ncb_t *) ncbp)->orig_pkt;
5581 /* copy whole packet to virtual memory */
5582 /*fprintf(stderr, "smb_Server: copying dos packet at 0x%x, "
5584 bufp->dos_pkt / 16, bufp);*/
5586 dosmemget(bufp->dos_pkt, ncbp->ncb_length, bufp->data);
5588 smbp = (smb_t *)bufp->data;
5591 if (smbp->com == 0x1d) {
5592 /* Special handling for Write Raw */
5593 raw_write_cont_t rwc;
5594 EVENT_HANDLE rwevent;
5595 smb_DispatchPacket(vcp, bufp, outbufp, ncbp, &rwc);
5596 if (rwc.code == 0) {
5597 rwevent = thrd_CreateEvent(NULL, FALSE, FALSE, NULL);
5598 ncbp->ncb_command = NCBRECV | ASYNCH;
5599 ncbp->ncb_lsn = (unsigned char) vcp->lsn;
5600 ncbp->ncb_lana_num = smb_LANadapter;
5601 ncbp->ncb_buffer = rwc.buf;
5602 ncbp->ncb_length = 65535;
5603 ncbp->ncb_event = rwevent;
5607 Netbios(ncbp, dos_ncb);
5609 rcode = thrd_WaitForSingleObject_Event(rwevent,
5611 thrd_CloseHandle(rwevent);
5613 thrd_SetEvent(SessionEvents[idx_session]);
5615 smb_CompleteWriteRaw(vcp, bufp, outbufp, ncbp,
5617 } else if (smbp->com == 0xa0) {
5619 * Serialize the handling for NT Transact
5622 smb_DispatchPacket(vcp, bufp, outbufp, ncbp, NULL);
5623 thrd_SetEvent(SessionEvents[idx_session]);
5625 thrd_SetEvent(SessionEvents[idx_session]);
5626 smb_DispatchPacket(vcp, bufp, outbufp, ncbp, NULL);
5629 smb_concurrentCalls--;
5632 thrd_SetEvent(NCBavails[idx_NCB]);
5637 * Create a new NCB and associated events, packet buffer, and "space" buffer.
5638 * If the number of server threads is M, and the number of live sessions is
5639 * N, then the number of NCB's in use at any time either waiting for, or
5640 * holding, received messages is M + N, so that is how many NCB's get created.
5642 void InitNCBslot(int idx)
5644 struct smb_packet *bufp;
5645 EVENT_HANDLE retHandle;
5648 NCBs[idx] = GetNCB();
5649 NCBavails[idx] = thrd_CreateEvent(NULL, FALSE, TRUE, NULL);
5651 NCBevents[idx] = thrd_CreateEvent(NULL, TRUE, FALSE, NULL);
5653 retHandle = thrd_CreateEvent(NULL, FALSE, FALSE, NULL);
5654 for (i=0; i<smb_NumServerThreads; i++)
5655 NCBreturns[i][idx] = retHandle;
5657 bufp->spacep = cm_GetSpace();
5661 /* listen for new connections */
5662 void smb_Listener(void *parmp)
5670 char rname[NCBNAMSZ+1];
5671 char cname[MAX_COMPUTERNAME_LENGTH+1];
5672 int cnamelen = MAX_COMPUTERNAME_LENGTH+1;
5680 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
5685 /* terminate if shutdown flag is set */
5686 if (smbShutdownFlag == 1)
5691 /* check for demo expiration */
5693 unsigned long tod = time((void *) 0);
5694 if (tod > EXPIREDATE) {
5695 (*smb_MBfunc)(NULL, "AFS demo expiration",
5697 MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
5701 #endif /* !NOEXPIRE */
5703 ncbp->ncb_command = NCBLISTEN;
5704 ncbp->ncb_rto = 0; /* No receive timeout */
5705 ncbp->ncb_sto = 0; /* No send timeout */
5707 /* pad out with spaces instead of null termination */
5708 len = strlen(smb_localNamep);
5709 strncpy(ncbp->ncb_name, smb_localNamep, NCBNAMSZ);
5710 for(i=len; i<NCBNAMSZ; i++) ncbp->ncb_name[i] = ' ';
5712 strcpy(ncbp->ncb_callname, "*");
5713 for(i=1; i<NCBNAMSZ; i++) ncbp->ncb_callname[i] = ' ';
5715 ncbp->ncb_lana_num = smb_LANadapter;
5718 code = Netbios(ncbp);
5720 code = Netbios(ncbp, dos_ncb);
5724 fprintf(stderr, "NCBLISTEN lana=%d (smb_LANadapter=%d) "
5725 "failed with code %d\n",
5726 ncbp->ncb_lana_num, smb_LANadapter, code);
5727 osi_Log3(0, "NCBLISTEN lana=%d (smb_LANadapter=%d) "
5728 "failed with code %d",
5729 ncbp->ncb_lana_num, smb_LANadapter, code);
5730 fprintf(stderr, "\nClient exiting due to network failure "
5731 "(possibly due to power-saving mode)\n");
5732 fprintf(stderr, "Please restart client.\n");
5733 afs_exit(AFS_EXITCODE_NETWORK_FAILURE);
5736 osi_assert(code == 0);
5738 /* check for remote conns */
5739 /* first get remote name and insert null terminator */
5740 memcpy(rname, ncbp->ncb_callname, NCBNAMSZ);
5741 for (i=NCBNAMSZ; i>0; i--) {
5742 if (rname[i-1] != ' ' && rname[i-1] != 0) {
5747 /* get local name and compare */
5748 GetComputerName(cname, &cnamelen);
5751 if (strncmp(rname, cname, NCBNAMSZ) != 0)
5752 flags |= SMB_VCFLAG_REMOTECONN;
5754 osi_Log1(afsd_logp, "New session lsn %d", ncbp->ncb_lsn);
5756 /* New generation */
5759 /* Log session startup */
5760 if (reportSessionStartups) {
5766 h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
5767 sprintf(s, "SMB session startup, %d ongoing ops",
5770 ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1004, NULL,
5772 DeregisterEventSource(h);
5774 afsi_log("NCBLISTEN completed, call from %s",rname);
5775 osi_Log1(afsd_logp, "SMB session startup, %d ongoing o
5779 fprintf(stderr, "%s: New session starting from host %s
5781 asctime(localtime(&now)), rname);
5786 /* now ncbp->ncb_lsn is the connection ID */
5787 vcp = smb_FindVC(ncbp->ncb_lsn, SMB_FLAG_CREATE);
5788 vcp->flags |= flags;
5790 /* Allocate slot in session arrays */
5791 /* Re-use dead session if possible, otherwise add one more */
5792 for (i = 0; i < numSessions; i++) {
5793 if (dead_sessions[i]) {
5794 dead_sessions[i] = FALSE;
5798 LSNs[i] = ncbp->ncb_lsn;
5799 if (i == numSessions) {
5800 /* Add new NCB for new session */
5801 InitNCBslot(numNCBs);
5803 thrd_SetEvent(NCBavails[0]);
5804 thrd_SetEvent(NCBevents[0]);
5805 for (j = 0; j < smb_NumServerThreads; j++)
5806 thrd_SetEvent(NCBreturns[j][0]);
5807 /* Also add new session event */
5808 SessionEvents[i] = thrd_CreateEvent(NULL, FALSE, TRUE, NULL);
5810 thrd_SetEvent(SessionEvents[0]);
5812 thrd_SetEvent(SessionEvents[i]);
5815 } /* dispatch while loop */
5818 /* initialize Netbios */
5819 void smb_NetbiosInit()
5827 int delname_tried=0;
5830 /* setup the NCB system */
5833 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
5837 /* reset the adaptor: in Win32, this is required for every process, and
5838 * acts as an init call, not as a real hardware reset.
5840 ncbp->ncb_command = NCBRESET;
5841 ncbp->ncb_callname[0] = 100;
5842 ncbp->ncb_callname[2] = 100;
5843 ncbp->ncb_lana_num = smb_LANadapter;
5844 code = Netbios(ncbp);
5845 if (code == 0) code = ncbp->ncb_retcode;
5847 sprintf(s, "Netbios NCBRESET error code %d", code);
5849 osi_panic(s, __FILE__, __LINE__);
5854 /* and declare our name so we can receive connections */
5855 memset(ncbp, 0, sizeof(*ncbp));
5856 ncbp->ncb_lana_num = smb_LANadapter;
5857 ncbp->ncb_command = NCBADDNAME;
5858 strcpy(ncbp->ncb_name, smb_localNamep);
5859 len = strlen(smb_localNamep);
5860 for(i=len; i<NCBNAMSZ; i++) ncbp->ncb_name[i] = ' ';
5861 /* Keep the name so we can unregister it later */
5862 memcpy(smb_sharename,ncbp->ncb_name,NCBNAMSZ);
5863 lana = smb_LANadapter;
5865 do { /* try multiple LANA numbers until we find one that works */
5866 ncbp->ncb_lana_num = lana;
5868 code = Netbios(ncbp);
5870 code = Netbios(ncbp, dos_ncb);
5873 afsi_log("Netbios NCBADDNAME code=%d retcode=%d complete=%d",code,
5874 ncbp->ncb_retcode,ncbp->ncb_cmd_cplt);
5878 for (i=0;i<NCBNAMSZ;++i)
5879 name[i] = ncbp->ncb_name[i];
5881 afsi_log("Netbios NCBADDNAME added new name >%s<",name);
5886 code = ncbp->ncb_retcode;
5887 smb_LANadapter = lana; /* correct LANA number */
5892 sprintf(s, "Netbios NCBADDNAME lana %d error code %d", lana, code);
5894 if (code != NRC_BRIDGE) /* invalid LANA num */
5897 lana = (lana + 1) % 8;
5899 } while (lana != smb_LANadapter); /* quit when we loop back to orig. */
5901 if (code == NRC_DUPNAME)
5903 /* Name already exists; try to delete it */
5904 memset(ncbp, 0, sizeof(*ncbp));
5905 ncbp->ncb_command = NCBDELNAME;
5906 memcpy(ncbp->ncb_name,smb_sharename,NCBNAMSZ);
5907 ncbp->ncb_lana_num = smb_LANadapter;
5909 code = Netbios(ncbp);
5911 code = Netbios(ncbp, dos_ncb);
5913 if (code == 0) code = ncbp->ncb_retcode;
5915 fprintf(stderr, "Netbios NCBDELNAME error code %d", code);
5918 if (code == 0 && !delname_tried)
5926 osi_panic(s, __FILE__, __LINE__);
5928 fprintf(stderr, "Using LAN Adapter %d\n", smb_LANadapter, code);
5929 afsi_log("Netbios NCBADDNAME lana=%d name number=%d", smb_LANadapter,
5932 /* we're done with the NCB now */
5936 void smb_Init(osi_log_t *logp, char *snamep, int useV3, int LANadapt,
5958 smb_MBfunc = aMBfunc;
5962 /* check for demo expiration */
5964 unsigned long tod = time((void *) 0);
5965 if (tod > EXPIREDATE) {
5967 (*smb_MBfunc)(NULL, "AFS demo expiration",
5969 MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
5972 fprintf(stderr, "AFS demo expiration\n");
5977 #endif /* !NOEXPIRE */
5980 smb_LANadapter = LANadapt;
5982 /* Initialize smb_localZero */
5983 myTime.tm_isdst = -1; /* compute whether on DST or not */
5984 myTime.tm_year = 70;
5990 smb_localZero = mktime(&myTime);
5992 /* Initialize kludge-GMT */
5993 smb_CalculateNowTZ();
5995 /* initialize the remote debugging log */
5998 /* remember the name */
5999 len = strlen(snamep);
6000 smb_localNamep = malloc(len+1);
6001 strcpy(smb_localNamep, snamep);
6003 /* and the global lock */
6004 lock_InitializeRWLock(&smb_globalLock, "smb global lock");
6005 lock_InitializeRWLock(&smb_rctLock, "smb refct and tree struct lock");
6007 /* Raw I/O data structures */
6008 lock_InitializeMutex(&smb_RawBufLock, "smb raw buffer lock");
6010 /* 4 Raw I/O buffers */
6012 smb_RawBufs = GlobalAlloc(GMEM_FIXED, 65536);
6013 *((char **)smb_RawBufs) = NULL;
6014 for (i=0; i<3; i++) {
6015 char *rawBuf = GlobalAlloc(GMEM_FIXED, 65536);
6016 *((char **)rawBuf) = smb_RawBufs;
6017 smb_RawBufs = rawBuf;
6020 npar = 65536 >> 4; /* number of paragraphs */
6021 seg = __dpmi_allocate_dos_memory(npar, &smb_RawBufSel[0]);
6023 afsi_log("Cannot allocate %d paragraphs of DOS memory",
6025 osi_panic("",__FILE__,__LINE__);
6028 afsi_log("Allocated %d paragraphs of DOS mem at 0x%X",
6031 smb_RawBufs = (seg * 16) + 0; /* DOS physical address */
6033 _farpokel(_dos_ds, smb_RawBufs, NULL);
6034 for (i=0; i<SMB_RAW_BUFS-1; i++) {
6035 npar = 65536 >> 4; /* number of paragraphs */
6036 seg = __dpmi_allocate_dos_memory(npar, &smb_RawBufSel[i+1]);
6038 afsi_log("Cannot allocate %d paragraphs of DOS memory",
6040 osi_panic("",__FILE__,__LINE__);
6043 afsi_log("Allocated %d paragraphs of DOS mem at 0x%X",
6046 rawBuf = (seg * 16) + 0; /* DOS physical address */
6047 /*_farpokel(_dos_ds, smb_RawBufs, smb_RawBufs);*/
6048 _farpokel(_dos_ds, rawBuf, smb_RawBufs);
6049 smb_RawBufs = rawBuf;
6053 /* global free lists */
6054 smb_ncbFreeListp = NULL;
6055 smb_packetFreeListp = NULL;
6059 /* Initialize listener and server structures */
6060 memset(dead_sessions, 0, sizeof(dead_sessions));
6061 SessionEvents[0] = thrd_CreateEvent(NULL, FALSE, FALSE, NULL);
6063 smb_NumServerThreads = nThreads;
6064 NCBavails[0] = thrd_CreateEvent(NULL, FALSE, FALSE, NULL);
6065 NCBevents[0] = thrd_CreateEvent(NULL, FALSE, FALSE, NULL);
6066 NCBreturns = malloc(nThreads * sizeof(EVENT_HANDLE *));
6067 for (i = 0; i < nThreads; i++) {
6068 NCBreturns[i] = malloc(NCBmax * sizeof(EVENT_HANDLE));
6069 NCBreturns[i][0] = thrd_CreateEvent(NULL, FALSE, FALSE, NULL);
6071 for (i = 1; i <= nThreads; i++)
6073 numNCBs = nThreads + 1;
6075 /* Initialize dispatch table */
6076 memset(&smb_dispatchTable, 0, sizeof(smb_dispatchTable));
6077 smb_dispatchTable[0x00].procp = smb_ReceiveCoreMakeDir;
6078 smb_dispatchTable[0x01].procp = smb_ReceiveCoreRemoveDir;
6079 smb_dispatchTable[0x02].procp = smb_ReceiveCoreOpen;
6080 smb_dispatchTable[0x03].procp = smb_ReceiveCoreCreate;
6081 smb_dispatchTable[0x04].procp = smb_ReceiveCoreClose;
6082 smb_dispatchTable[0x05].procp = smb_ReceiveCoreFlush;
6083 smb_dispatchTable[0x06].procp = smb_ReceiveCoreUnlink;
6084 smb_dispatchTable[0x07].procp = smb_ReceiveCoreRename;
6085 smb_dispatchTable[0x08].procp = smb_ReceiveCoreGetFileAttributes;
6086 smb_dispatchTable[0x09].procp = smb_ReceiveCoreSetFileAttributes;
6087 smb_dispatchTable[0x0a].procp = smb_ReceiveCoreRead;
6088 smb_dispatchTable[0x0b].procp = smb_ReceiveCoreWrite;
6089 smb_dispatchTable[0x0c].procp = smb_ReceiveCoreLockRecord;
6090 smb_dispatchTable[0x0d].procp = smb_ReceiveCoreUnlockRecord;
6091 smb_dispatchTable[0x0e].procp = smb_SendCoreBadOp;
6092 smb_dispatchTable[0x0f].procp = smb_ReceiveCoreCreate;
6093 smb_dispatchTable[0x10].procp = smb_ReceiveCoreCheckPath;
6094 smb_dispatchTable[0x11].procp = smb_SendCoreBadOp; /* process exit */
6095 smb_dispatchTable[0x12].procp = smb_ReceiveCoreSeek;
6096 smb_dispatchTable[0x1a].procp = smb_ReceiveCoreReadRaw;
6097 /* Set NORESPONSE because smb_ReceiveCoreReadRaw() does the responses itself */
6098 smb_dispatchTable[0x1a].flags |= SMB_DISPATCHFLAG_NORESPONSE;
6099 smb_dispatchTable[0x1d].procp = smb_ReceiveCoreWriteRawDummy;
6100 smb_dispatchTable[0x22].procp = smb_ReceiveV3SetAttributes;
6101 smb_dispatchTable[0x23].procp = smb_ReceiveV3GetAttributes;
6102 smb_dispatchTable[0x24].procp = smb_ReceiveV3LockingX;
6103 smb_dispatchTable[0x24].flags |= SMB_DISPATCHFLAG_CHAINED;
6104 smb_dispatchTable[0x29].procp = smb_SendCoreBadOp; /* copy file */
6105 smb_dispatchTable[0x2b].procp = smb_ReceiveCoreEcho;
6106 /* Set NORESPONSE because smb_ReceiveCoreEcho() does the responses itself */
6107 smb_dispatchTable[0x2b].flags |= SMB_DISPATCHFLAG_NORESPONSE;
6108 smb_dispatchTable[0x2d].procp = smb_ReceiveV3OpenX;
6109 smb_dispatchTable[0x2d].flags |= SMB_DISPATCHFLAG_CHAINED;
6110 smb_dispatchTable[0x2e].procp = smb_ReceiveV3ReadX;
6111 smb_dispatchTable[0x2e].flags |= SMB_DISPATCHFLAG_CHAINED;
6112 smb_dispatchTable[0x32].procp = smb_ReceiveV3Tran2A; /* both are same */
6113 smb_dispatchTable[0x32].flags |= SMB_DISPATCHFLAG_NORESPONSE;
6114 smb_dispatchTable[0x33].procp = smb_ReceiveV3Tran2A;
6115 smb_dispatchTable[0x33].flags |= SMB_DISPATCHFLAG_NORESPONSE;
6116 smb_dispatchTable[0x34].procp = smb_ReceiveV3FindClose;
6117 smb_dispatchTable[0x35].procp = smb_ReceiveV3FindNotifyClose;
6118 smb_dispatchTable[0x70].procp = smb_ReceiveCoreTreeConnect;
6119 smb_dispatchTable[0x71].procp = smb_ReceiveCoreTreeDisconnect;
6120 smb_dispatchTable[0x72].procp = smb_ReceiveNegotiate;
6121 smb_dispatchTable[0x73].procp = smb_ReceiveV3SessionSetupX;
6122 smb_dispatchTable[0x73].flags |= SMB_DISPATCHFLAG_CHAINED;
6123 smb_dispatchTable[0x74].procp = smb_ReceiveV3UserLogoffX;
6124 smb_dispatchTable[0x74].flags |= SMB_DISPATCHFLAG_CHAINED;
6125 smb_dispatchTable[0x75].procp = smb_ReceiveV3TreeConnectX;
6126 smb_dispatchTable[0x75].flags |= SMB_DISPATCHFLAG_CHAINED;
6127 smb_dispatchTable[0x80].procp = smb_ReceiveCoreGetDiskAttributes;
6128 smb_dispatchTable[0x81].procp = smb_ReceiveCoreSearchDir;
6129 smb_dispatchTable[0xA0].procp = smb_ReceiveNTTransact;
6130 smb_dispatchTable[0xA2].procp = smb_ReceiveNTCreateX;
6131 smb_dispatchTable[0xA2].flags |= SMB_DISPATCHFLAG_CHAINED;
6132 smb_dispatchTable[0xA4].procp = smb_ReceiveNTCancel;
6133 smb_dispatchTable[0xA4].flags |= SMB_DISPATCHFLAG_NORESPONSE;
6134 smb_dispatchTable[0xc0].procp = smb_SendCoreBadOp;
6135 smb_dispatchTable[0xc1].procp = smb_SendCoreBadOp;
6136 smb_dispatchTable[0xc2].procp = smb_SendCoreBadOp;
6137 smb_dispatchTable[0xc3].procp = smb_SendCoreBadOp;
6138 for(i=0xd0; i<= 0xd7; i++) {
6139 smb_dispatchTable[i].procp = smb_SendCoreBadOp;
6142 /* setup tran 2 dispatch table */
6143 smb_tran2DispatchTable[0].procp = smb_ReceiveTran2Open;
6144 smb_tran2DispatchTable[1].procp = smb_ReceiveTran2SearchDir; /* FindFirst */
6145 smb_tran2DispatchTable[2].procp = smb_ReceiveTran2SearchDir; /* FindNext */
6146 smb_tran2DispatchTable[3].procp = smb_ReceiveTran2QFSInfo;
6147 smb_tran2DispatchTable[4].procp = smb_ReceiveTran2SetFSInfo;
6148 smb_tran2DispatchTable[5].procp = smb_ReceiveTran2QPathInfo;
6149 smb_tran2DispatchTable[6].procp = smb_ReceiveTran2SetPathInfo;
6150 smb_tran2DispatchTable[7].procp = smb_ReceiveTran2QFileInfo;
6151 smb_tran2DispatchTable[8].procp = smb_ReceiveTran2SetFileInfo;
6152 smb_tran2DispatchTable[9].procp = smb_ReceiveTran2FSCTL;
6153 smb_tran2DispatchTable[10].procp = smb_ReceiveTran2IOCTL;
6154 smb_tran2DispatchTable[11].procp = smb_ReceiveTran2FindNotifyFirst;
6155 smb_tran2DispatchTable[12].procp = smb_ReceiveTran2FindNotifyNext;
6156 smb_tran2DispatchTable[13].procp = smb_ReceiveTran2MKDir;
6160 /* Start listeners, waiters, servers, and daemons */
6162 phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_Listener,
6163 NULL, 0, &lpid, "smb_Listener");
6164 osi_assert(phandle != NULL);
6165 thrd_CloseHandle(phandle);
6168 phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_ClientWaiter,
6169 NULL, 0, &lpid, "smb_ClientWaiter");
6170 osi_assert(phandle != NULL);
6171 thrd_CloseHandle(phandle);
6174 phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_ServerWaiter,
6175 NULL, 0, &lpid, "smb_ServerWaiter");
6176 osi_assert(phandle != NULL);
6177 thrd_CloseHandle(phandle);
6179 for (i=0; i<nThreads; i++) {
6180 phandle = thrd_Create(NULL, 65536,
6181 (ThreadFunc) smb_Server,
6182 (void *) i, 0, &lpid, "smb_Server");
6183 osi_assert(phandle != NULL);
6184 thrd_CloseHandle(phandle);
6187 phandle = thrd_Create(NULL, 65536, (ThreadFunc) smb_Daemon,
6188 NULL, 0, &lpid, "smb_Daemon");
6189 osi_assert(phandle != NULL);
6190 thrd_CloseHandle(phandle);
6192 phandle = thrd_Create(NULL, 65536,
6193 (ThreadFunc) smb_WaitingLocksDaemon,
6194 NULL, 0, &lpid, "smb_WaitingLocksDaemon");
6195 osi_assert(phandle != NULL);
6196 thrd_CloseHandle(phandle);
6206 void smb_Shutdown(void)
6213 /*fprintf(stderr, "Entering smb_Shutdown\n");*/
6215 /* setup the NCB system */
6217 dos_ncb = ((smb_ncb_t *)ncbp)->dos_ncb;
6219 /* Block new sessions by setting shutdown flag */
6220 /*smbShutdownFlag = 1;*/
6222 /* Hang up all sessions */
6223 for (i = 1; i < numSessions; i++)
6225 if (dead_sessions[i])
6228 /*fprintf(stderr, "NCBHANGUP session %d LSN %d\n", i, LSNs[i]);*/
6229 ncbp->ncb_command = NCBHANGUP;
6230 ncbp->ncb_lana_num = smb_LANadapter;
6231 ncbp->ncb_lsn = LSNs[i];
6232 code = Netbios(ncbp, dos_ncb);
6233 /*fprintf(stderr, "returned from NCBHANGUP session %d LSN %d\n", i, LS
6235 if (code == 0) code = ncbp->ncb_retcode;
6237 fprintf(stderr, "Session %d Netbios NCBHANGUP error code %d", i, code);
6241 /* Delete Netbios name */
6242 ncbp->ncb_command = NCBDELNAME;
6243 memcpy(ncbp->ncb_name,smb_sharename,NCBNAMSZ);
6244 ncbp->ncb_lana_num = smb_LANadapter;
6245 code = Netbios(ncbp, dos_ncb);
6246 if (code == 0) code = ncbp->ncb_retcode;
6248 fprintf(stderr, "Netbios NCBDELNAME error code %d", code);