windows-mountpoint-is-directory-20070622
[openafs.git] / src / WINNT / afsd / smb3.c
index 0433f76..480c92d 100644 (file)
@@ -1498,6 +1498,7 @@ long smb_ReceiveRAPNetShareEnum(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_
     char * cstrp;
     char thisShare[256];
     int i,j;
+    DWORD dw;
     int nonrootShares;
     smb_rap_share_list_t rootShares;
     cm_req_t req;
@@ -1586,9 +1587,9 @@ long smb_ReceiveRAPNetShareEnum(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_
     }
 
     if (hkSubmount) {
-        for (i=0; i < nRegShares && cshare < nSharesRet; i++) {
+        for (dw=0; dw < nRegShares && cshare < nSharesRet; dw++) {
             len = sizeof(thisShare);
-            rv = RegEnumValue(hkSubmount, i, thisShare, &len, NULL, NULL, NULL, NULL);
+            rv = RegEnumValue(hkSubmount, dw, thisShare, &len, NULL, NULL, NULL, NULL);
             if (rv == ERROR_SUCCESS && strlen(thisShare) && (!allSubmount || stricmp(thisShare,"all"))) {
                 strncpy(shares[cshare].shi1_netname, thisShare, sizeof(shares->shi1_netname)-1);
                 shares[cshare].shi1_netname[sizeof(shares->shi1_netname)-1] = 0; /* unfortunate truncation */
@@ -2357,7 +2358,7 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
     cm_HoldUser(userp);
     lock_ObtainMutex(&fidp->mx);
     /* save a pointer to the vnode */
-    osi_Log2(afsd_logp,"smb_ReceiveTran2Open fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveTran2Open fidp 0x%p scp 0x%p", fidp, scp);
     fidp->scp = scp;
     lock_ObtainMutex(&scp->mx);
     scp->flags |= CM_SCACHEFLAG_SMB_FID;
@@ -2368,7 +2369,7 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
         
     /* compute open mode */
     if (openMode != 1) 
-       fidp->flags |= SMB_FID_OPENREAD;
+       fidp->flags |= SMB_FID_OPENREAD_LISTDIR;
     if (openMode == 1 || openMode == 2)
         fidp->flags |= SMB_FID_OPENWRITE;
 
@@ -2420,18 +2421,6 @@ long smb_ReceiveTran2Open(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
     return 0;
 }   
 
-long smb_ReceiveTran2FindFirst(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
-{
-    osi_Log0(smb_logp,"ReceiveTran2FindFirst - NOT_SUPPORTED");
-    return CM_ERROR_BADOP;
-}
-
-long smb_ReceiveTran2FindNext(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *outp)
-{
-    osi_Log0(smb_logp,"ReceiveTran2FindNext - NOT_SUPPORTED");
-    return CM_ERROR_BADOP;
-}
-
 long smb_ReceiveTran2QFSInfoFid(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
 {
     unsigned short fid;
@@ -2441,7 +2430,7 @@ long smb_ReceiveTran2QFSInfoFid(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_
     fid = p->parmsp[1];
     osi_Log2(smb_logp, "T2 QFSInfoFid InfoLevel 0x%x fid 0x%x - NOT_SUPPORTED", infolevel, fid);
     
-    return CM_ERROR_BADOP;
+    return CM_ERROR_BAD_LEVEL;
 }
 
 long smb_ReceiveTran2QFSInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *op)
@@ -2648,6 +2637,7 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     cm_user_t *userp;
     cm_space_t *spacep;
     cm_scache_t *scp, *dscp;
+    int scp_mx_held = 0;
     int delonclose = 0;
     long code = 0;
     char *pathp;
@@ -2679,7 +2669,7 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     else {
         osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
                   p->opcode, infoLevel);
-        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADOP);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
         return 0;
     }
 
@@ -2808,6 +2798,7 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
 #endif /* DFS_SUPPORT */
 
     lock_ObtainMutex(&scp->mx);
+    scp_mx_held = 1;
     code = cm_SyncOp(scp, NULL, userp, &req, 0,
                       CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
     if (code) goto done;
@@ -2861,22 +2852,25 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     }
     else if (infoLevel == SMB_QUERY_FILE_STANDARD_INFO) {
        smb_fid_t *fidp = smb_FindFIDByScache(vcp, scp);
-       if (fidp) {
-           lock_ObtainMutex(&fidp->mx);
-           delonclose = fidp->flags & SMB_FID_DELONCLOSE;
-           lock_ReleaseMutex(&fidp->mx);
-           smb_ReleaseFID(fidp);
-       }
 
         qpi.u.QPfileStandardInfo.allocationSize = scp->length;
         qpi.u.QPfileStandardInfo.endOfFile = scp->length;
         qpi.u.QPfileStandardInfo.numberOfLinks = scp->linkCount;
-        qpi.u.QPfileStandardInfo.deletePending = (delonclose ? 1 : 0);
         qpi.u.QPfileStandardInfo.directory = 
            ((scp->fileType == CM_SCACHETYPE_DIRECTORY ||
              scp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
              scp->fileType == CM_SCACHETYPE_INVALID) ? 1 : 0);
         qpi.u.QPfileStandardInfo.reserved = 0;
+
+       if (fidp) {
+           lock_ReleaseMutex(&scp->mx);
+           scp_mx_held = 0;
+           lock_ObtainMutex(&fidp->mx);
+           delonclose = fidp->flags & SMB_FID_DELONCLOSE;
+           lock_ReleaseMutex(&fidp->mx);
+           smb_ReleaseFID(fidp);
+       }
+        qpi.u.QPfileStandardInfo.deletePending = (delonclose ? 1 : 0);
     }
     else if (infoLevel == SMB_QUERY_FILE_EA_INFO) {
         qpi.u.QPfileEaInfo.eaSize = 0;
@@ -2914,7 +2908,8 @@ long smb_ReceiveTran2QPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
 
     /* send and free the packets */
   done:
-    lock_ReleaseMutex(&scp->mx);
+    if (scp_mx_held)
+       lock_ReleaseMutex(&scp->mx);
     cm_ReleaseSCache(scp);
     cm_ReleaseUser(userp);
     if (code == 0) {
@@ -2956,7 +2951,7 @@ long smb_ReceiveTran2SetPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
        infoLevel != SMB_INFO_QUERY_ALL_EAS) {
         osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
                   p->opcode, infoLevel);
-        smb_SendTran2Error(vcp, p, opx, CM_ERROR_INVAL);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
         return 0;
     }
 
@@ -3055,8 +3050,8 @@ long smb_ReceiveTran2SetPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
 
     fidp = smb_FindFIDByScache(vcp, scp);
     if (!fidp) {
-        cm_ReleaseUser(userp);
         cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
        smb_SendTran2Error(vcp, p, opx, code);
         return 0;
     }
@@ -3064,9 +3059,9 @@ long smb_ReceiveTran2SetPathInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
     lock_ObtainMutex(&fidp->mx);
     if (!(fidp->flags & SMB_FID_OPENWRITE)) {
        lock_ReleaseMutex(&fidp->mx);
+        cm_ReleaseSCache(scp);
         smb_ReleaseFID(fidp);
         cm_ReleaseUser(userp);
-        cm_ReleaseSCache(scp);
         smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOACCESS);
         return 0;
     }
@@ -3189,7 +3184,7 @@ long smb_ReceiveTran2QFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     else {
         osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
                   p->opcode, infoLevel);
-        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADOP);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
         smb_ReleaseFID(fidp);
         return 0;
     }
@@ -3213,7 +3208,7 @@ long smb_ReceiveTran2QFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     lock_ObtainMutex(&fidp->mx);
     delonclose = fidp->flags & SMB_FID_DELONCLOSE;
     scp = fidp->scp;
-    osi_Log2(afsd_logp,"smb_ReleaseTran2QFileInfo fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReleaseTran2QFileInfo fidp 0x%p scp 0x%p", fidp, scp);
     cm_HoldSCache(scp);
     lock_ReleaseMutex(&fidp->mx);
     lock_ObtainMutex(&scp->mx);
@@ -3291,8 +3286,8 @@ long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
     smb_fid_t *fidp;
     unsigned short infoLevel;
     smb_tran2Packet_t *outp;
-    cm_user_t *userp;
-    cm_scache_t *scp;
+    cm_user_t *userp = NULL;
+    cm_scache_t *scp = NULL;
     cm_req_t req;
 
     cm_InitReq(&req);
@@ -3310,13 +3305,16 @@ long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
     if (infoLevel > SMB_SET_FILE_END_OF_FILE_INFO || infoLevel < SMB_SET_FILE_BASIC_INFO) {
         osi_Log2(smb_logp, "Bad Tran2 op 0x%x infolevel 0x%x",
                   p->opcode, infoLevel);
-        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BADOP);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_BAD_LEVEL);
         smb_ReleaseFID(fidp);
         return 0;
     }
 
     lock_ObtainMutex(&fidp->mx);
-    if (infoLevel == SMB_SET_FILE_DISPOSITION_INFO && !(fidp->flags & SMB_FID_OPENDELETE)) {
+    if (infoLevel == SMB_SET_FILE_DISPOSITION_INFO && 
+       !(fidp->flags & SMB_FID_OPENDELETE)) {
+       osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo !SMB_FID_OPENDELETE fidp 0x%p scp 0x%p fidp->flags 0x%x", 
+                 fidp, fidp->scp, fidp->flags);
        lock_ReleaseMutex(&fidp->mx);
         smb_ReleaseFID(fidp);
         smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOACCESS);
@@ -3325,6 +3323,8 @@ long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
     if ((infoLevel == SMB_SET_FILE_ALLOCATION_INFO || 
         infoLevel == SMB_SET_FILE_END_OF_FILE_INFO)
          && !(fidp->flags & SMB_FID_OPENWRITE)) {
+       osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo !SMB_FID_OPENWRITE fidp 0x%p scp 0x%p fidp->flags 0x%x", 
+                 fidp, fidp->scp, fidp->flags);
        lock_ReleaseMutex(&fidp->mx);
         smb_ReleaseFID(fidp);
         smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOACCESS);
@@ -3332,7 +3332,7 @@ long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
     }
 
     scp = fidp->scp;
-    osi_Log2(afsd_logp,"smb_ReceiveTran2SetFileInfo fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveTran2SetFileInfo fidp 0x%p scp 0x%p", fidp, scp);
     cm_HoldSCache(scp);
     lock_ReleaseMutex(&fidp->mx);
 
@@ -3414,6 +3414,9 @@ long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
             code = 0;
     }
     else if (infoLevel == SMB_SET_FILE_DISPOSITION_INFO) {
+       int delflag = *((char *)(p->datap));
+       osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo Delete? %d fidp 0x%p scp 0x%p", 
+                delflag, fidp, scp);
         if (*((char *)(p->datap))) {   /* File is Deleted */
             code = cm_CheckNTDelete(fidp->NTopen_dscp, scp, userp,
                                      &req);
@@ -3421,6 +3424,9 @@ long smb_ReceiveTran2SetFileInfo(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet
                lock_ObtainMutex(&fidp->mx);
                 fidp->flags |= SMB_FID_DELONCLOSE;
                lock_ReleaseMutex(&fidp->mx);
+           } else {
+               osi_Log3(smb_logp,"smb_ReceiveTran2SetFileInfo CheckNTDelete fidp 0x%p scp 0x%p code 0x%x", 
+                        fidp, scp, code);
            }
        }               
         else {  
@@ -3642,17 +3648,37 @@ smb_ApplyV3DirListPatches(cm_scache_t *dscp,
     unsigned long lattr;
     smb_dirListPatch_t *patchp;
     smb_dirListPatch_t *npatchp;
-        
+    afs_uint32 rights;
+    afs_int32 mustFake = 0;
+
+    code = cm_FindACLCache(dscp, userp, &rights);
+    if (code == 0 && !(rights & PRSFS_READ))
+        mustFake = 1;
+    else if (code == -1) {
+        lock_ObtainMutex(&dscp->mx);
+        code = cm_SyncOp(dscp, NULL, userp, reqp, PRSFS_READ,
+                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        lock_ReleaseMutex(&dscp->mx);
+        if (code == CM_ERROR_NOACCESS) {
+            mustFake = 1;
+            code = 0;
+        }
+    }
+    if (code)
+        return code;
+
     for(patchp = *dirPatchespp; patchp; patchp =
          (smb_dirListPatch_t *) osi_QNext(&patchp->q)) {
-               code = cm_GetSCache(&patchp->fid, &scp, userp, reqp);
-        if (code) continue;
+        code = cm_GetSCache(&patchp->fid, &scp, userp, reqp);
+        if (code) 
+            continue;
+
         lock_ObtainMutex(&scp->mx);
-        code = cm_SyncOp(scp, NULL, userp, reqp, 0,
-                          CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
-        if (code) { 
+        if (mustFake == 0)
+            code = cm_SyncOp(scp, NULL, userp, reqp, 0,
+                             CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
+        if (mustFake || code) { 
             lock_ReleaseMutex(&scp->mx);
-            cm_ReleaseSCache(scp);
 
             dptr = patchp->dptr;
 
@@ -3679,9 +3705,20 @@ smb_ApplyV3DirListPatches(cm_scache_t *dscp,
                 *((FILETIME *)dptr) = ft;
                 dptr += 24;
 
+                switch (scp->fileType) {
+                case CM_SCACHETYPE_DIRECTORY:
+                case CM_SCACHETYPE_MOUNTPOINT:
+                case CM_SCACHETYPE_SYMLINK:
+                case CM_SCACHETYPE_INVALID:
+                    *((u_long *)dptr) = SMB_ATTR_DIRECTORY;
+                    break;
+                default:
+                    *((u_long *)dptr) = SMB_ATTR_NORMAL;
+                        
+                }
                 /* merge in hidden attribute */
                 if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
-                    *((u_long *)dptr) = SMB_ATTR_HIDDEN;
+                    *((u_long *)dptr) |= SMB_ATTR_HIDDEN;
                 }
                 dptr += 4;
             } else {
@@ -3718,13 +3755,25 @@ smb_ApplyV3DirListPatches(cm_scache_t *dscp,
                 *((u_short *)dptr) = shortTemp;
                 dptr += 10;
 
+                /* set the attribute */
+                switch (scp->fileType) {
+                case CM_SCACHETYPE_DIRECTORY:
+                case CM_SCACHETYPE_MOUNTPOINT:
+                case CM_SCACHETYPE_SYMLINK:
+                case CM_SCACHETYPE_INVALID:
+                    attr = SMB_ATTR_DIRECTORY;
+                default:
+                    attr = SMB_ATTR_NORMAL;
+                }
                 /* merge in hidden (dot file) attribute */
                 if ( patchp->flags & SMB_DIRLISTPATCH_DOTFILE ) {
-                    attr = SMB_ATTR_HIDDEN;
-                    *dptr++ = attr & 0xff;
-                    *dptr++ = (attr >> 8) & 0xff;
+                    attr |= SMB_ATTR_HIDDEN;
                 }       
+                *dptr++ = attr & 0xff;
+                *dptr++ = (attr >> 8) & 0xff;
             }
+            
+            cm_ReleaseSCache(scp);
             continue;
         }
         
@@ -4128,6 +4177,430 @@ int smb_V3MatchMask(char *namep, char *maskp, int flags)
 }
 #endif /* USE_OLD_MATCHING */
 
+/* smb_ReceiveTran2SearchDir implements both 
+ * Tran2_Find_First and Tran2_Find_Next
+ */
+#define TRAN2_FIND_FLAG_CLOSE_SEARCH           0x01
+#define TRAN2_FIND_FLAG_CLOSE_SEARCH_IF_END    0x02
+#define TRAN2_FIND_FLAG_RETURN_RESUME_KEYS     0x04
+#define TRAN2_FIND_FLAG_CONTINUE_SEARCH                0x08
+#define TRAN2_FIND_FLAG_BACKUP_INTENT          0x10
+
+/* this is an optimized handler for T2SearchDir that handles the case
+   where there are no wildcards in the search path.  I.e. an
+   application is using FindFirst(Ex) to get information about a
+   single file or directory.  It will attempt to do a single lookup.
+   If that fails, then smb_ReceiveTran2SearchDir() will fall back to
+   the usual mechanism. 
+   
+   This function will return either CM_ERROR_NOSUCHFILE or SUCCESS.
+   */
+long smb_T2SearchDirSingle(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
+{
+    int attribute;
+    long nextCookie;
+    long code = 0, code2 = 0;
+    char *pathp;
+    int maxCount;
+    smb_dirListPatch_t *dirListPatchesp;
+    smb_dirListPatch_t *curPatchp;
+    long orbytes;                      /* # of bytes in this output record */
+    long ohbytes;                      /* # of bytes, except file name */
+    long onbytes;                      /* # of bytes in name, incl. term. null */
+    cm_scache_t *scp = NULL;
+    cm_scache_t *targetscp = NULL;
+    cm_user_t *userp = NULL;
+    char *op;                          /* output data ptr */
+    char *origOp;                      /* original value of op */
+    cm_space_t *spacep;                        /* for pathname buffer */
+    long maxReturnData;                        /* max # of return data */
+    long maxReturnParms;               /* max # of return parms */
+    long bytesInBuffer;                        /* # data bytes in the output buffer */
+    char *maskp;                       /* mask part of path */
+    int infoLevel;
+    int searchFlags;
+    int eos;
+    smb_tran2Packet_t *outp;           /* response packet */
+    char *tidPathp;
+    int align;
+    char shortName[13];                        /* 8.3 name if needed */
+    int NeedShortName;
+    char *shortNameEnd;
+    cm_req_t req;
+    char * s;
+
+    cm_InitReq(&req);
+
+    eos = 0;
+    osi_assert(p->opcode == 1);
+
+    /* find first; obtain basic parameters from request */
+
+    /* note that since we are going to failover to regular
+     * processing at smb_ReceiveTran2SearchDir(), we shouldn't
+     * modify any of the input parameters here. */
+    attribute = p->parmsp[0];
+    maxCount = p->parmsp[1];
+    infoLevel = p->parmsp[3];
+    searchFlags = p->parmsp[2];
+    pathp = ((char *) p->parmsp) + 12; /* points to path */
+    nextCookie = 0;
+    maskp = strrchr(pathp, '\\');
+    if (maskp == NULL) 
+       maskp = pathp;
+    else 
+       maskp++;        /* skip over backslash */
+    /* track if this is likely to match a lot of entries */
+
+    osi_Log2(smb_logp, "smb_T2SearchDirSingle : path[%s], mask[%s]",
+            osi_LogSaveString(smb_logp, pathp),
+            osi_LogSaveString(smb_logp, maskp));
+
+    switch ( infoLevel ) {
+    case SMB_INFO_STANDARD:
+       s = "InfoStandard";
+       break;
+    case SMB_INFO_QUERY_EA_SIZE:
+       s = "InfoQueryEaSize";
+       break;
+    case SMB_INFO_QUERY_EAS_FROM_LIST:
+       s = "InfoQueryEasFromList";
+       break;
+    case SMB_FIND_FILE_DIRECTORY_INFO:
+       s = "FindFileDirectoryInfo";
+       break;
+    case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+       s = "FindFileFullDirectoryInfo";
+       break;
+    case SMB_FIND_FILE_NAMES_INFO:
+       s = "FindFileNamesInfo";
+       break;
+    case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+       s = "FindFileBothDirectoryInfo";
+       break;
+    default:
+       s = "unknownInfoLevel";
+    }
+
+    osi_Log1(smb_logp, "smb_T2SearchDirSingle info level: %s", s);
+
+    osi_Log4(smb_logp,
+             "smb_T2SearchDirSingle attr 0x%x, info level 0x%x, max count %d, flags 0x%x",
+             attribute, infoLevel, maxCount, searchFlags);
+    
+    if (infoLevel > SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+        osi_Log1(smb_logp, "Unsupported InfoLevel 0x%x", infoLevel);
+        return CM_ERROR_INVAL;
+    }
+
+    if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
+        searchFlags &= ~TRAN2_FIND_FLAG_RETURN_RESUME_KEYS;    /* no resume keys */
+
+    dirListPatchesp = NULL;
+
+    maxReturnData = p->maxReturnData;
+    maxReturnParms = 10;       /* return params for findfirst, which
+                                   is the only one we handle.*/
+
+#ifndef CM_CONFIG_MULTITRAN2RESPONSES
+    if (maxReturnData > 6000) 
+        maxReturnData = 6000;
+#endif /* CM_CONFIG_MULTITRAN2RESPONSES */
+
+    outp = smb_GetTran2ResponsePacket(vcp, p, opx, maxReturnParms,
+                                      maxReturnData);
+
+    osi_Log2(smb_logp, "T2SDSingle search dir count %d [%s]",
+             maxCount, osi_LogSaveString(smb_logp, pathp));
+        
+    /* bail out if request looks bad */
+    if (!pathp) {
+        smb_FreeTran2Packet(outp);
+        return CM_ERROR_BADSMB;
+    }
+        
+    userp = smb_GetTran2User(vcp, p);
+    if (!userp) {
+       osi_Log1(smb_logp, "T2 search dir unable to resolve user [%d]", p->uid);
+       smb_FreeTran2Packet(outp);
+       return CM_ERROR_BADSMB;
+    }
+
+    /* try to get the vnode for the path name next */
+    spacep = cm_GetSpace();
+    smb_StripLastComponent(spacep->data, NULL, pathp);
+    code = smb_LookupTIDPath(vcp, p->tid, &tidPathp);
+    if (code) {
+        cm_ReleaseUser(userp);
+        smb_SendTran2Error(vcp, p, opx, CM_ERROR_NOFILES);
+        smb_FreeTran2Packet(outp);
+        return 0;
+    }
+
+    code = cm_NameI(cm_data.rootSCachep, spacep->data,
+                    CM_FLAG_FOLLOW | CM_FLAG_CASEFOLD,
+                    userp, tidPathp, &req, &scp);
+    cm_FreeSpace(spacep);
+
+    if (code) {
+        cm_ReleaseUser(userp);
+       smb_SendTran2Error(vcp, p, opx, code);
+        smb_FreeTran2Packet(outp);
+        return 0;
+    }
+
+#ifdef DFS_SUPPORT_BUT_NOT_FIND_FIRST
+    if (scp->fileType == CM_SCACHETYPE_DFSLINK) {
+       cm_ReleaseSCache(scp);
+       cm_ReleaseUser(userp);
+       if ( WANTS_DFS_PATHNAMES(p) )
+           code = CM_ERROR_PATH_NOT_COVERED;
+       else
+           code = CM_ERROR_BADSHARENAME;
+       smb_SendTran2Error(vcp, p, opx, code);
+       smb_FreeTran2Packet(outp);
+       return 0;
+    }
+#endif /* DFS_SUPPORT */
+    osi_Log1(smb_logp,"smb_ReceiveTran2SearchDir scp 0x%p", scp);
+    lock_ObtainMutex(&scp->mx);
+    if ((scp->flags & CM_SCACHEFLAG_BULKSTATTING) == 0 &&
+        LargeIntegerGreaterOrEqualToZero(scp->bulkStatProgress)) {
+       scp->flags |= CM_SCACHEFLAG_BULKSTATTING;
+    }
+    lock_ReleaseMutex(&scp->mx);
+
+    /* now do a single case sensitive lookup for the file in question */
+    code = cm_Lookup(scp, maskp, CM_FLAG_NOMOUNTCHASE, userp, &req, &targetscp);
+
+    /* if a case sensitive match failed, we try a case insensitive one
+       next. */
+    if (code == CM_ERROR_NOSUCHFILE) {
+        code = cm_Lookup(scp, maskp, CM_FLAG_NOMOUNTCHASE | CM_FLAG_CASEFOLD, userp, &req, &targetscp);
+    }
+
+    if (code == 0 && targetscp->fid.vnode == 0) {
+        cm_ReleaseSCache(targetscp);
+        code = CM_ERROR_NOSUCHFILE;
+    }
+
+    if (code) {
+        /* if we can't find the directory entry, this block will
+           return CM_ERROR_NOSUCHFILE, which we will pass on to
+           smb_ReceiveTran2SearchDir(). */
+        cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
+       if (code != CM_ERROR_NOSUCHFILE) {
+           smb_SendTran2Error(vcp, p, opx, code);
+           code = 0;
+       }
+       smb_FreeTran2Packet(outp);
+        return code;
+    }
+
+    /* now that we have the target in sight, we proceed with filling
+       up the return data. */
+
+    op = origOp = outp->datap;
+    bytesInBuffer = 0;
+
+    if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS) {
+        /* skip over resume key */
+        op += 4;
+    }
+
+    if (infoLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO
+        && targetscp->fid.vnode != 0
+        && !cm_Is8Dot3(maskp)) {
+
+        cm_dirFid_t dfid;
+        dfid.vnode = htonl(targetscp->fid.vnode);
+        dfid.unique = htonl(targetscp->fid.unique);
+
+        cm_Gen8Dot3NameInt(maskp, &dfid, shortName, &shortNameEnd);
+        NeedShortName = 1;
+    } else {
+        NeedShortName = 0;
+    }
+
+    osi_Log4(smb_logp, "T2SDSingle dir vn %u uniq %u name %s (%s)",
+             htonl(targetscp->fid.vnode),
+             htonl(targetscp->fid.unique),
+             osi_LogSaveString(smb_logp, pathp),
+             NeedShortName ? osi_LogSaveString(smb_logp, shortName) : "");
+
+    /* Eliminate entries that don't match requested attributes */
+    if (smb_hideDotFiles && !(attribute & SMB_ATTR_HIDDEN) &&
+        smb_IsDotFile(maskp)) {
+
+        code = CM_ERROR_NOSUCHFILE;
+        osi_Log0(smb_logp, "T2SDSingle skipping hidden file");
+        goto skip_file;
+
+    }
+
+    if (!(attribute & SMB_ATTR_DIRECTORY) &&
+        (targetscp->fileType == CM_SCACHETYPE_DIRECTORY ||
+         targetscp->fileType == CM_SCACHETYPE_MOUNTPOINT ||
+         targetscp->fileType == CM_SCACHETYPE_DFSLINK ||
+         targetscp->fileType == CM_SCACHETYPE_INVALID)) {
+
+        code = CM_ERROR_NOSUCHFILE;
+        osi_Log0(smb_logp, "T2SDSingle skipping directory or bad link");
+        goto skip_file;
+
+    }
+
+    /* Check if the name will fit */
+    if (infoLevel < 0x101)
+        ohbytes = 23;           /* pre-NT */
+    else if (infoLevel == SMB_FIND_FILE_NAMES_INFO)
+        ohbytes = 12;           /* NT names only */
+    else
+        ohbytes = 64;           /* NT */
+
+    if (infoLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
+        ohbytes += 26;          /* Short name & length */
+
+    if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS) {
+        ohbytes += 4;           /* if resume key required */
+    }
+
+    if (infoLevel != SMB_INFO_STANDARD
+        && infoLevel != SMB_FIND_FILE_DIRECTORY_INFO
+        && infoLevel != SMB_FIND_FILE_NAMES_INFO)
+        ohbytes += 4;           /* EASIZE */
+
+    /* add header to name & term. null */
+    onbytes = strlen(maskp);
+    orbytes = ohbytes + onbytes + 1;
+
+    /* now, we round up the record to a 4 byte alignment, and we make
+     * sure that we have enough room here for even the aligned version
+     * (so we don't have to worry about an * overflow when we pad
+     * things out below).  That's the reason for the alignment
+     * arithmetic below.
+     */
+    if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
+        align = (4 - (orbytes & 3)) & 3;
+    else
+        align = 0;
+
+    if (orbytes + align > maxReturnData) {
+
+        /* even though this request is unlikely to succeed with a
+           failover, we do it anyway. */
+        code = CM_ERROR_NOSUCHFILE;
+        osi_Log1(smb_logp, "T2 dir search exceed max return data %d",
+                 maxReturnData);
+        goto skip_file;
+    }
+
+    /* this is one of the entries to use: it is not deleted and it
+     * matches the star pattern we're looking for.  Put out the name,
+     * preceded by its length.
+     */
+    /* First zero everything else */
+    memset(origOp, 0, ohbytes);
+
+    if (infoLevel <= SMB_FIND_FILE_DIRECTORY_INFO)
+        *(origOp + ohbytes - 1) = (unsigned char) onbytes;
+    else if (infoLevel == SMB_FIND_FILE_NAMES_INFO)
+        *((u_long *)(op + 8)) = onbytes;
+    else
+        *((u_long *)(op + 60)) = onbytes;
+    strcpy(origOp+ohbytes, maskp);
+    if (smb_StoreAnsiFilenames)
+        CharToOem(origOp+ohbytes, origOp+ohbytes);
+
+    /* Short name if requested and needed */
+    if (infoLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+        if (NeedShortName) {
+            strcpy(op + 70, shortName);
+            if (smb_StoreAnsiFilenames)
+                CharToOem(op + 70, op + 70);
+            *(op + 68) = (char)(shortNameEnd - shortName);
+        }
+    }
+
+    /* NextEntryOffset and FileIndex */
+    if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO) {
+        int entryOffset = orbytes + align;
+        *((u_long *)op) = 0;
+        *((u_long *)(op+4)) = 0;
+    }
+
+    if (infoLevel != SMB_FIND_FILE_NAMES_INFO) {
+        curPatchp = malloc(sizeof(*curPatchp));
+        osi_QAdd((osi_queue_t **) &dirListPatchesp,
+                 &curPatchp->q);
+        curPatchp->dptr = op;
+        if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
+            curPatchp->dptr += 8;
+
+        if (smb_hideDotFiles && smb_IsDotFile(maskp)) {
+            curPatchp->flags = SMB_DIRLISTPATCH_DOTFILE;
+        } else {
+            curPatchp->flags = 0;
+        }
+
+        curPatchp->fid.cell = targetscp->fid.cell;
+        curPatchp->fid.volume = targetscp->fid.volume;
+        curPatchp->fid.vnode = targetscp->fid.vnode;
+        curPatchp->fid.unique = targetscp->fid.unique;
+
+        /* temp */
+        curPatchp->dep = NULL;
+    }   
+
+    if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS) {
+        /* put out resume key */
+        *((u_long *)origOp) = 0;
+    }
+
+    /* Adjust byte ptr and count */
+    origOp += orbytes; /* skip entire record */
+    bytesInBuffer += orbytes;
+
+    /* and pad the record out */
+    while (--align >= 0) {
+        *origOp++ = 0;
+        bytesInBuffer++;
+    }
+
+    /* apply the patches */
+    code2 = smb_ApplyV3DirListPatches(scp, &dirListPatchesp, infoLevel, userp, &req);
+
+    outp->parmsp[0] = 0;
+    outp->parmsp[1] = 1;        /* number of names returned */
+    outp->parmsp[2] = 1;        /* end of search */
+    outp->parmsp[3] = 0;        /* nothing wrong with EAS */
+    outp->parmsp[4] = 0;
+
+    outp->totalParms = 10;      /* in bytes */
+
+    outp->totalData = bytesInBuffer;
+
+    osi_Log0(smb_logp, "T2SDSingle done.");
+
+    if (code != CM_ERROR_NOSUCHFILE) {
+       if (code)
+           smb_SendTran2Error(vcp, p, opx, code);
+       else
+           smb_SendTran2Packet(vcp, outp, opx);
+       code = 0;
+    }
+
+ skip_file:
+    smb_FreeTran2Packet(outp);
+    cm_ReleaseSCache(scp);
+    cm_ReleaseSCache(targetscp);
+    cm_ReleaseUser(userp);
+
+    return code;
+}
+
+
 long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t *opx)
 {
     int attribute;
@@ -4190,8 +4663,6 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
         maxCount = p->parmsp[1];
         infoLevel = p->parmsp[3];
         searchFlags = p->parmsp[2];
-        dsp = smb_NewDirSearch(1);
-        dsp->attribute = attribute;
         pathp = ((char *) p->parmsp) + 12;     /* points to path */
         if (smb_StoreAnsiFilenames)
             OemToChar(pathp,pathp);
@@ -4201,9 +4672,27 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
             maskp = pathp;
         else 
             maskp++;   /* skip over backslash */
-        strcpy(dsp->mask, maskp);      /* and save mask */
+
         /* track if this is likely to match a lot of entries */
         starPattern = smb_V3IsStarMask(maskp);
+
+#ifndef NOFINDFIRSTOPTIMIZE
+        if (!starPattern) {
+            /* if this is for a single directory or file, we let the
+               optimized routine handle it.  The only error it 
+              returns is CM_ERROR_NOSUCHFILE.  The  */
+            code = smb_T2SearchDirSingle(vcp, p, opx);
+
+            /* we only failover if we see a CM_ERROR_NOSUCHFILE */
+            if (code != CM_ERROR_NOSUCHFILE) {
+                return code;
+            }
+        }
+#endif
+
+        dsp = smb_NewDirSearch(1);
+        dsp->attribute = attribute;
+        strcpy(dsp->mask, maskp);      /* and save mask */
     }
     else {
         osi_assert(p->opcode == 2);
@@ -4266,7 +4755,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     }
 
     if (infoLevel >= SMB_FIND_FILE_DIRECTORY_INFO)
-        searchFlags &= ~4;     /* no resume keys */
+        searchFlags &= ~TRAN2_FIND_FLAG_RETURN_RESUME_KEYS;    /* no resume keys */
 
     dirListPatchesp = NULL;
 
@@ -4309,7 +4798,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     lock_ObtainMutex(&dsp->mx);
     if (dsp->scp) {
         scp = dsp->scp;
-       osi_Log2(afsd_logp,"smb_ReceiveTran2SearchDir dsp 0x%p scp 0x%p", dsp, scp);
+       osi_Log2(smb_logp,"smb_ReceiveTran2SearchDir dsp 0x%p scp 0x%p", dsp, scp);
         cm_HoldSCache(scp);
         code = 0;
     } else {
@@ -4348,7 +4837,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
             }
 #endif /* DFS_SUPPORT */
             dsp->scp = scp;
-           osi_Log2(afsd_logp,"smb_ReceiveTran2SearchDir dsp 0x%p scp 0x%p", dsp, scp);
+           osi_Log2(smb_logp,"smb_ReceiveTran2SearchDir dsp 0x%p scp 0x%p", dsp, scp);
             /* we need one hold for the entry we just stored into,
              * and one for our own processing.  When we're done
              * with this function, we'll drop the one for our own
@@ -4404,7 +4893,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
     bytesInBuffer = 0;
     while (1) {
         op = origOp;
-        if (searchFlags & 4)
+        if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS)
             /* skip over resume key */
             op += 4;
 
@@ -4478,7 +4967,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
                 if ((dsp->flags & SMB_DIRSEARCH_BULKST) &&
                     LargeIntegerGreaterThanOrEqualTo(thyper, scp->bulkStatProgress)) {
                     /* Don't bulk stat if risking timeout */
-                    int now = GetTickCount();
+                    DWORD now = GetTickCount();
                     if (now - req.startTime > RDRtimeout) {
                         scp->bulkStatProgress = thyper;
                         scp->flags &= ~CM_SCACHEFLAG_BULKSTATTING;
@@ -4613,6 +5102,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
                  "has filetype %d", dep->name,
                  fileType);*/
                 if (fileType == CM_SCACHETYPE_DIRECTORY ||
+                    fileType == CM_SCACHETYPE_MOUNTPOINT ||
                     fileType == CM_SCACHETYPE_DFSLINK ||
                     fileType == CM_SCACHETYPE_INVALID)
                     osi_Log0(smb_logp, "T2 search dir skipping directory or bad link");
@@ -4632,7 +5122,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
             if (infoLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
                 ohbytes += 26; /* Short name & length */
 
-            if (searchFlags & 4) {
+            if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS) {
                 ohbytes += 4;  /* if resume key required */
             }   
 
@@ -4733,7 +5223,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
                 curPatchp->dep = dep;
             }   
 
-            if (searchFlags & 4)
+            if (searchFlags & TRAN2_FIND_FLAG_RETURN_RESUME_KEYS)
                 /* put out resume key */
                 *((u_long *)origOp) = nextEntryCookie;
 
@@ -4768,7 +5258,7 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
      * re-running the query. 
      */
     if (returnedNames == 0 && !starPattern && foundInexact) {
-        osi_Log0(afsd_logp,"T2 Search: No exact matches. Re-running for inexact matches");
+        osi_Log0(smb_logp,"T2 Search: No exact matches. Re-running for inexact matches");
         starPattern = 1;
         goto startsearch;
     }
@@ -4826,10 +5316,12 @@ long smb_ReceiveTran2SearchDir(smb_vc_t *vcp, smb_tran2Packet_t *p, smb_packet_t
      * we're supposed to close the search if we're done, and we're done,
      * or if something went wrong, close the search.
      */
-    /* ((searchFlags & 1) || ((searchFlags & 2) && eos) */
-    if ((searchFlags & 1) || (returnedNames == 0) || 
-         ((searchFlags & 2) && eos) || code != 0)
+    if ((searchFlags & TRAN2_FIND_FLAG_CLOSE_SEARCH) || 
+       (returnedNames == 0) ||
+        ((searchFlags & TRAN2_FIND_FLAG_CLOSE_SEARCH_IF_END) && eos) || 
+       code != 0)
         smb_DeleteDirSearch(dsp);
+
     if (code)
         smb_SendTran2Error(vcp, p, opx, code);
     else
@@ -5134,13 +5626,13 @@ long smb_ReceiveV3OpenX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     lock_ObtainMutex(&scp->mx);
     scp->flags |= CM_SCACHEFLAG_SMB_FID;
     lock_ReleaseMutex(&scp->mx);
-    osi_Log2(afsd_logp,"smb_ReceiveV3OpenX fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveV3OpenX fidp 0x%p scp 0x%p", fidp, scp);
     /* also the user */
     fidp->userp = userp;
         
     /* compute open mode */
     if (openMode != 1) 
-        fidp->flags |= SMB_FID_OPENREAD;
+        fidp->flags |= SMB_FID_OPENREAD_LISTDIR;
     if (openMode == 1 || openMode == 2)
         fidp->flags |= SMB_FID_OPENWRITE;
 
@@ -5250,7 +5742,7 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         return CM_ERROR_BADFD;
     }
     scp = fidp->scp;
-    osi_Log2(afsd_logp,"smb_ReceiveV3LockingX fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveV3LockingX fidp 0x%p scp 0x%p", fidp, scp);
     cm_HoldSCache(scp);
     lock_ReleaseMutex(&fidp->mx);
 
@@ -5275,6 +5767,14 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     NumberOfUnlocks = smb_GetSMBParm(inp, 6);
     NumberOfLocks = smb_GetSMBParm(inp, 7);
 
+    if (!(fidp->flags & SMB_FID_OPENWRITE) &&
+        !(LockType & LOCKING_ANDX_SHARED_LOCK)) {
+        /* somebody wants exclusive locks on a file that they only
+           opened for reading.  We downgrade this to a shared lock. */
+        osi_Log0(smb_logp, "smb_ReceiveV3Locking reinterpreting exclusive lock as shared for read-only fid");
+        LockType |= LOCKING_ANDX_SHARED_LOCK;
+    }
+
     if ((LockType & LOCKING_ANDX_CANCEL_LOCK) ||
         (LockType & LOCKING_ANDX_CHANGE_LOCKTYPE)) {
 
@@ -5315,6 +5815,13 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         code = cm_Lock(scp, LockType, LOffset, LLength, key, (Timeout != 0),
                         userp, &req, &lockp);
 
+       if (code == CM_ERROR_NOACCESS && LockType == LockWrite && 
+           (fidp->flags & (SMB_FID_OPENREAD_LISTDIR | SMB_FID_OPENWRITE)) == SMB_FID_OPENREAD_LISTDIR)
+       {
+           code = cm_Lock(scp, LockRead, LOffset, LLength, key, (Timeout != 0),
+                           userp, &req, &lockp);
+       }
+
         if (code == CM_ERROR_WOULDBLOCK && Timeout != 0) {
             smb_waitingLock_t * wLock;
 
@@ -5332,7 +5839,7 @@ long smb_ReceiveV3LockingX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                 wlRequest->vcp = vcp;
                 smb_HoldVC(vcp);
                 wlRequest->scp = scp;
-               osi_Log2(afsd_logp,"smb_ReceiveV3LockingX wlRequest 0x%p scp 0x%p", wlRequest, scp);
+               osi_Log2(smb_logp,"smb_ReceiveV3LockingX wlRequest 0x%p scp 0x%p", wlRequest, scp);
                 cm_HoldSCache(scp);
                 wlRequest->inp = smb_CopyPacket(inp);
                 wlRequest->outp = smb_CopyPacket(outp);
@@ -5491,7 +5998,7 @@ long smb_ReceiveV3GetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
         return CM_ERROR_BADFD;
     }
     scp = fidp->scp;
-    osi_Log2(afsd_logp,"smb_ReceiveV3GetAttributes fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveV3GetAttributes fidp 0x%p scp 0x%p", fidp, scp);
     cm_HoldSCache(scp);
     lock_ReleaseMutex(&fidp->mx);
         
@@ -5568,7 +6075,7 @@ long smb_ReceiveV3SetAttributes(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *
         return CM_ERROR_BADFD;
     }
     scp = fidp->scp;
-    osi_Log2(afsd_logp,"smb_ReceiveV3SetAttributes fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveV3SetAttributes fidp 0x%p scp 0x%p", fidp, scp);
     cm_HoldSCache(scp);
     lock_ReleaseMutex(&fidp->mx);
         
@@ -5724,8 +6231,8 @@ long smb_ReceiveV3WriteX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     smb_SetSMBDataLength(outp, 0);
 
  done:
-    smb_ReleaseFID(fidp);
     cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
 
     return code;
 }
@@ -5843,9 +6350,8 @@ long smb_ReceiveV3ReadX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     smb_SetSMBParm(outp, 5, finalCount);
     smb_SetSMBDataLength(outp, finalCount);
 
-    smb_ReleaseFID(fidp);
-
     cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
     return code;
 }   
         
@@ -5915,6 +6421,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     BOOL foundscp;
     cm_req_t req;
     int created = 0;
+    cm_lock_data_t *ldp = NULL;
 
     cm_InitReq(&req);
 
@@ -6076,7 +6583,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (desiredAccess & DELETE)
         fidflags |= SMB_FID_OPENDELETE;
     if (desiredAccess & AFS_ACCESS_READ)
-        fidflags |= SMB_FID_OPENREAD;
+        fidflags |= SMB_FID_OPENREAD_LISTDIR;
     if (desiredAccess & AFS_ACCESS_WRITE)
         fidflags |= SMB_FID_OPENWRITE;
     if (createOptions & FILE_DELETE_ON_CLOSE)
@@ -6196,9 +6703,9 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                     treeStartp = realPathp + (tp - spacep->data);
 
                     if (*tp && !smb_IsLegalFilename(tp)) {
+                        cm_ReleaseUser(userp);
                         if (baseFidp) 
                             smb_ReleaseFID(baseFidp);
-                        cm_ReleaseUser(userp);
                         free(realPathp);
                         if (scp)
                             cm_ReleaseSCache(scp);
@@ -6284,7 +6791,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
      * scp is NULL.
      */
     if (code == 0 && !treeCreate) {
-        code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req);
+        code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
         if (code) {
             if (dscp)
                 cm_ReleaseSCache(dscp);
@@ -6297,6 +6804,7 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
        if (createDisp == FILE_CREATE) {
             /* oops, file shouldn't be there */
+           cm_CheckNTOpenDone(scp, userp, &req, &ldp);
             if (dscp)
                 cm_ReleaseSCache(dscp);
             if (scp)
@@ -6325,9 +6833,20 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                      */
                     osi_Log2(smb_logp, "symlink vp %x to vp %x",
                               scp, targetScp);
+                   cm_CheckNTOpenDone(scp, userp, &req, &ldp);
                     cm_ReleaseSCache(scp);
                     scp = targetScp;
-                }
+                   code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
+                   if (code) {
+                       if (dscp)
+                           cm_ReleaseSCache(dscp);
+                       if (scp)
+                           cm_ReleaseSCache(scp);
+                       cm_ReleaseUser(userp);
+                       free(realPathp);
+                       return code;
+                   }
+               }
             }
             code = cm_SetAttr(scp, &setAttr, userp, &req);
             openAction = 3;    /* truncated existing file */
@@ -6482,6 +7001,8 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     if (code) {
         /* something went wrong creating or truncating the file */
+       if (ldp)
+           cm_CheckNTOpenDone(scp, userp, &req, &ldp);
         if (scp) 
             cm_ReleaseSCache(scp);
         if (dscp) 
@@ -6504,12 +7025,16 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                 * we'll just use the symlink anyway.
                 */
                 osi_Log2(smb_logp, "symlink vp %x to vp %x", scp, targetScp);
+               if (ldp)
+                   cm_CheckNTOpenDone(scp, userp, &req, &ldp);
                 cm_ReleaseSCache(scp);
                 scp = targetScp;
             }
         }
 
         if (scp->fileType != CM_SCACHETYPE_FILE) {
+           if (ldp)
+               cm_CheckNTOpenDone(scp, userp, &req, &ldp);
             if (dscp)
                 cm_ReleaseSCache(dscp);
             cm_ReleaseSCache(scp);
@@ -6521,6 +7046,8 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     /* (only applies to single component case) */
     if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
+       if (ldp)
+           cm_CheckNTOpenDone(scp, userp, &req, &ldp);
         cm_ReleaseSCache(scp);
         if (dscp)
             cm_ReleaseSCache(dscp);
@@ -6550,7 +7077,12 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         LLength.HighPart = 0;
         LLength.LowPart = SMB_FID_QLOCK_LENGTH;
 
-        if (fidflags & SMB_FID_SHARE_READ) {
+        /* If we are not opening the file for writing, then we don't
+           try to get an exclusive lock.  Noone else should be able to
+           get an exclusive lock on the file anyway, although someone
+           else can get a shared lock. */
+        if ((fidflags & SMB_FID_SHARE_READ) ||
+            !(fidflags & SMB_FID_OPENWRITE)) {
             sLockType = LOCKING_ANDX_SHARED_LOCK;
         } else {
             sLockType = 0;
@@ -6563,27 +7095,31 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
         lock_ReleaseMutex(&scp->mx);
 
         if (code) {
-            /* shouldn't this be smb_CloseFID() fidp->flags = SMB_FID_DELETE; */
-           smb_CloseFID(vcp, fidp, NULL, 0);
-            smb_ReleaseFID(fidp);
-
+           if (ldp)
+               cm_CheckNTOpenDone(scp, userp, &req, &ldp);
             cm_ReleaseSCache(scp);
             if (dscp)
                 cm_ReleaseSCache(dscp);
-            cm_ReleaseUser(userp);
+           cm_ReleaseUser(userp);
+           /* Shouldn't this be smb_CloseFID()?  fidp->flags = SMB_FID_DELETE; */
+           smb_CloseFID(vcp, fidp, NULL, 0);
+           smb_ReleaseFID(fidp);
             free(realPathp);
-            
             return code;
         }
     }
 
+    /* Now its safe to release the file server lock obtained by cm_CheckNTOpen() */
+    if (ldp)
+       cm_CheckNTOpenDone(scp, userp, &req, &ldp);
+
     lock_ObtainMutex(&fidp->mx);
     /* save a pointer to the vnode */
     fidp->scp = scp;    /* Hold transfered to fidp->scp and no longer needed */
     lock_ObtainMutex(&scp->mx);
     scp->flags |= CM_SCACHEFLAG_SMB_FID;
     lock_ReleaseMutex(&scp->mx);
-    osi_Log2(afsd_logp,"smb_ReceiveNTCreateX fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveNTCreateX fidp 0x%p scp 0x%p", fidp, scp);
 
     fidp->flags = fidflags;
 
@@ -6593,10 +7129,10 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
 
     /* save parent dir and pathname for delete or change notification */
     if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
+       osi_Log2(smb_logp,"smb_ReceiveNTCreateX fidp 0x%p dscp 0x%p", fidp, dscp);
         fidp->flags |= SMB_FID_NTOPEN;
         fidp->NTopen_dscp = dscp;
-       osi_Log2(afsd_logp,"smb_ReceiveNTCreateX fidp 0x%p dscp 0x%p", fidp, dscp);
-        cm_HoldSCache(dscp);
+       dscp = NULL;
         fidp->NTopen_pathp = strdup(lastNamep);
     }
     fidp->NTopen_wholepathp = realPathp;
@@ -6640,9 +7176,8 @@ long smb_ReceiveNTCreateX(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     osi_Log2(smb_logp, "SMB NT CreateX opening fid %d path %s", fidp->fid,
               osi_LogSaveString(smb_logp, realPathp));
 
-    smb_ReleaseFID(fidp);
-
     cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
 
     /* Can't free realPathp if we get here since
        fidp->NTopen_wholepathp is pointing there */
@@ -6701,6 +7236,7 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     char *outData;
     cm_req_t req;
     int created = 0;
+    cm_lock_data_t *ldp = NULL;
 
     cm_InitReq(&req);
 
@@ -6829,7 +7365,7 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     if (desiredAccess & DELETE)
         fidflags |= SMB_FID_OPENDELETE;
     if (desiredAccess & AFS_ACCESS_READ)
-        fidflags |= SMB_FID_OPENREAD;
+        fidflags |= SMB_FID_OPENREAD_LISTDIR;
     if (desiredAccess & AFS_ACCESS_WRITE)
         fidflags |= SMB_FID_OPENWRITE;
     if (createOptions & FILE_DELETE_ON_CLOSE)
@@ -6973,8 +7509,7 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
      * scp is NULL.
      */
     if (code == 0) {
-        code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp,
-                               &req);
+        code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
         if (code) {     
             if (dscp) 
                 cm_ReleaseSCache(dscp);
@@ -6986,6 +7521,7 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
 
         if (createDisp == FILE_CREATE) {
             /* oops, file shouldn't be there */
+           cm_CheckNTOpenDone(scp, userp, &req, &ldp);
             if (dscp) 
                 cm_ReleaseSCache(dscp);
             cm_ReleaseSCache(scp);
@@ -7012,8 +7548,19 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
                     */
                     osi_Log2(smb_logp, "symlink vp %x to vp %x",
                               scp, targetScp);
+                   cm_CheckNTOpenDone(scp, userp, &req, &ldp);
                     cm_ReleaseSCache(scp);
                     scp = targetScp;
+                   code = cm_CheckNTOpen(scp, desiredAccess, createDisp, userp, &req, &ldp);
+                   if (code) {
+                       if (dscp)
+                           cm_ReleaseSCache(dscp);
+                       if (scp)
+                           cm_ReleaseSCache(scp);
+                       cm_ReleaseUser(userp);
+                       free(realPathp);
+                       return code;
+                   }
                 }
             }
             code = cm_SetAttr(scp, &setAttr, userp, &req);
@@ -7108,7 +7655,9 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
 
     if (code) {
         /* something went wrong creating or truncating the file */
-        if (scp) 
+       if (ldp)
+           cm_CheckNTOpenDone(scp, userp, &req, &ldp);
+       if (scp) 
             cm_ReleaseSCache(scp);
         cm_ReleaseUser(userp);
         free(realPathp);
@@ -7129,12 +7678,16 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
                 */
                 osi_Log2(smb_logp, "symlink vp %x to vp %x",
                           scp, targetScp);
+               if (ldp)
+                   cm_CheckNTOpenDone(scp, userp, &req, &ldp);
                 cm_ReleaseSCache(scp);
                 scp = targetScp;
             }
         }
 
         if (scp->fileType != CM_SCACHETYPE_FILE) {
+           if (ldp)
+               cm_CheckNTOpenDone(scp, userp, &req, &ldp);
             cm_ReleaseSCache(scp);
             cm_ReleaseUser(userp);
             free(realPathp);
@@ -7143,6 +7696,8 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     }
 
     if (realDirFlag == 1 && scp->fileType == CM_SCACHETYPE_FILE) {
+       if (ldp)
+           cm_CheckNTOpenDone(scp, userp, &req, &ldp);
         cm_ReleaseSCache(scp);
         cm_ReleaseUser(userp);
         free(realPathp);
@@ -7170,7 +7725,10 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
         LLength.HighPart = 0;
         LLength.LowPart = SMB_FID_QLOCK_LENGTH;
 
-        if (fidflags & SMB_FID_SHARE_READ) {
+        /* Similar to what we do in handling NTCreateX.  We get a
+           shared lock if we are only opening the file for reading. */
+        if ((fidflags & SMB_FID_SHARE_READ) ||
+            !(fidflags & SMB_FID_OPENWRITE)) {
             sLockType = LOCKING_ANDX_SHARED_LOCK;
         } else {
             sLockType = 0;
@@ -7183,25 +7741,29 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
         lock_ReleaseMutex(&scp->mx);
 
         if (code) {
-            /* Shouldn't this be smb_CloseFID()?  fidp->flags = SMB_FID_DELETE; */
-           smb_CloseFID(vcp, fidp, NULL, 0);
-            smb_ReleaseFID(fidp);
-
+           if (ldp)
+               cm_CheckNTOpenDone(scp, userp, &req, &ldp);
             cm_ReleaseSCache(scp);
             cm_ReleaseUser(userp);
-            free(realPathp);
-            
+           /* Shouldn't this be smb_CloseFID()?  fidp->flags = SMB_FID_DELETE; */
+           smb_CloseFID(vcp, fidp, NULL, 0);
+           smb_ReleaseFID(fidp);
+           free(realPathp);
             return CM_ERROR_SHARING_VIOLATION;
         }
     }
 
+    /* Now its safe to drop the file server lock obtained by cm_CheckNTOpen() */
+    if (ldp)
+       cm_CheckNTOpenDone(scp, userp, &req, &ldp);
+
     lock_ObtainMutex(&fidp->mx);
     /* save a pointer to the vnode */
     fidp->scp = scp;
     lock_ObtainMutex(&scp->mx);
     scp->flags |= CM_SCACHEFLAG_SMB_FID;
     lock_ReleaseMutex(&scp->mx);
-    osi_Log2(afsd_logp,"smb_ReceiveNTTranCreate fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log2(smb_logp,"smb_ReceiveNTTranCreate fidp 0x%p scp 0x%p", fidp, scp);
 
     fidp->flags = fidflags;
 
@@ -7213,8 +7775,8 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
     if (fidflags & (SMB_FID_OPENDELETE | SMB_FID_OPENWRITE)) {
         fidp->flags |= SMB_FID_NTOPEN;
         fidp->NTopen_dscp = dscp;
-       osi_Log2(afsd_logp,"smb_ReceiveNTTranCreate fidp 0x%p dscp 0x%p", fidp, dscp);
-        cm_HoldSCache(dscp);
+       osi_Log2(smb_logp,"smb_ReceiveNTTranCreate fidp 0x%p dscp 0x%p", fidp, dscp);
+       dscp = NULL;
         fidp->NTopen_pathp = strdup(lastNamep);
     }
     fidp->NTopen_wholepathp = realPathp;
@@ -7337,9 +7899,8 @@ long smb_ReceiveNTTranCreate(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *out
 
     osi_Log1(smb_logp, "SMB NTTranCreate opening fid %d", fidp->fid);
 
-    smb_ReleaseFID(fidp);
-
     cm_ReleaseUser(userp);
+    smb_ReleaseFID(fidp);
 
     /* free(realPathp); Can't free realPathp here because fidp->NTopen_wholepathp points there */
     /* leave scp held since we put it in fidp->scp */
@@ -7350,14 +7911,15 @@ long smb_ReceiveNTTranNotifyChange(smb_vc_t *vcp, smb_packet_t *inp,
        smb_packet_t *outp)
 {
     smb_packet_t *savedPacketp;
-    ULONG filter; USHORT fid, watchtree;
+    ULONG filter; 
+    USHORT fid, watchtree;
     smb_fid_t *fidp;
     cm_scache_t *scp;
         
     filter = smb_GetSMBParm(inp, 19) |
              (smb_GetSMBParm(inp, 20) << 16);
     fid = smb_GetSMBParm(inp, 21);
-    watchtree = smb_GetSMBParm(inp, 22) && 0xffff;  /* TODO: should this be 0xff ? */
+    watchtree = (smb_GetSMBParm(inp, 22) & 0xff) ? 1 : 0;
 
     fidp = smb_FindFID(vcp, fid, 0);
     if (!fidp) {
@@ -7365,21 +7927,51 @@ long smb_ReceiveNTTranNotifyChange(smb_vc_t *vcp, smb_packet_t *inp,
         return CM_ERROR_BADFD;
     }
 
+    /* Create a copy of the Directory Watch Packet to use when sending the
+     * notification if in the future a matching change is detected.
+     */
     savedPacketp = smb_CopyPacket(inp);
     smb_HoldVC(vcp);
     if (savedPacketp->vcp)
        smb_ReleaseVC(savedPacketp->vcp);
     savedPacketp->vcp = vcp;
+
+    /* Add the watch to the list of events to send notifications for */
     lock_ObtainMutex(&smb_Dir_Watch_Lock);
     savedPacketp->nextp = smb_Directory_Watches;
     smb_Directory_Watches = savedPacketp;
     lock_ReleaseMutex(&smb_Dir_Watch_Lock);
 
-    osi_Log4(smb_logp, "Request for NotifyChange filter 0x%x fid %d wtree %d file %s",
-             filter, fid, watchtree, osi_LogSaveString(smb_logp, fidp->NTopen_wholepathp));
-
     scp = fidp->scp;
-    osi_Log2(afsd_logp,"smb_ReceiveNTTranNotifyChange fidp 0x%p scp 0x%p", fidp, scp);
+    osi_Log3(smb_logp,"smb_ReceiveNTTranNotifyChange fidp 0x%p scp 0x%p file \"%s\"", 
+             fidp, scp, osi_LogSaveString(smb_logp, fidp->NTopen_wholepathp));
+    osi_Log3(smb_logp, "Request for NotifyChange filter 0x%x fid %d wtree %d",
+             filter, fid, watchtree);
+    if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
+       osi_Log0(smb_logp, "      Notify Change File Name");
+    if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
+       osi_Log0(smb_logp, "      Notify Change Directory Name");
+    if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
+       osi_Log0(smb_logp, "      Notify Change Attributes");
+    if (filter & FILE_NOTIFY_CHANGE_SIZE)
+       osi_Log0(smb_logp, "      Notify Change Size");
+    if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
+       osi_Log0(smb_logp, "      Notify Change Last Write");
+    if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
+       osi_Log0(smb_logp, "      Notify Change Last Access");
+    if (filter & FILE_NOTIFY_CHANGE_CREATION)
+       osi_Log0(smb_logp, "      Notify Change Creation");
+    if (filter & FILE_NOTIFY_CHANGE_EA)
+       osi_Log0(smb_logp, "      Notify Change Extended Attributes");
+    if (filter & FILE_NOTIFY_CHANGE_SECURITY)
+       osi_Log0(smb_logp, "      Notify Change Security");
+    if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
+       osi_Log0(smb_logp, "      Notify Change Stream Name");
+    if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
+       osi_Log0(smb_logp, "      Notify Change Stream Size");
+    if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
+       osi_Log0(smb_logp, "      Notify Change Stream Write");
+
     lock_ObtainMutex(&scp->mx);
     if (watchtree)
         scp->flags |= CM_SCACHEFLAG_WATCHEDSUBTREE;
@@ -7513,6 +8105,9 @@ long smb_ReceiveNTTransact(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
  *
  * If we don't know the file name (i.e. a callback break), filename is
  * NULL, and we return a zero-length list.
+ *
+ * At present there is not a single call to smb_NotifyChange that 
+ * has the isDirectParent parameter set to FALSE.  
  */
 void smb_NotifyChange(DWORD action, DWORD notifyFilter,
        cm_scache_t *dscp, char *filename, char *otherFilename,
@@ -7535,8 +8130,20 @@ void smb_NotifyChange(DWORD action, DWORD notifyFilter,
         otherAction = FILE_ACTION_RENAMED_NEW_NAME;
     }
 
-    osi_Log2(smb_logp,"in smb_NotifyChange for file [%s] dscp [%x]",
-             osi_LogSaveString(smb_logp,filename),dscp);
+    osi_Log4(smb_logp,"in smb_NotifyChange for file [%s] dscp [%p] notification 0x%x parent %d",
+             osi_LogSaveString(smb_logp,filename),dscp, notifyFilter, isDirectParent);
+    if (action == 0)
+       osi_Log0(smb_logp,"      FILE_ACTION_NONE");
+    if (action == FILE_ACTION_ADDED)
+       osi_Log0(smb_logp,"      FILE_ACTION_ADDED");
+    if (action == FILE_ACTION_REMOVED)
+       osi_Log0(smb_logp,"      FILE_ACTION_REMOVED");
+    if (action == FILE_ACTION_MODIFIED)
+       osi_Log0(smb_logp,"      FILE_ACTION_MODIFIED");
+    if (action == FILE_ACTION_RENAMED_OLD_NAME)
+       osi_Log0(smb_logp,"      FILE_ACTION_RENAMED_OLD_NAME");
+    if (action == FILE_ACTION_RENAMED_NEW_NAME)
+       osi_Log0(smb_logp,"      FILE_ACTION_RENAMED_NEW_NAME");
 
     lock_ObtainMutex(&smb_Dir_Watch_Lock);
     watch = smb_Directory_Watches;
@@ -7544,20 +8151,20 @@ void smb_NotifyChange(DWORD action, DWORD notifyFilter,
         filter = smb_GetSMBParm(watch, 19)
             | (smb_GetSMBParm(watch, 20) << 16);
         fid = smb_GetSMBParm(watch, 21);
-        wtree = smb_GetSMBParm(watch, 22) & 0xffff;  /* TODO: should this be 0xff ? */
+        wtree = (smb_GetSMBParm(watch, 22) & 0xff) ? 1 : 0;
+
         maxLen = smb_GetSMBOffsetParm(watch, 5, 1)
             | (smb_GetSMBOffsetParm(watch, 6, 1) << 16);
 
         /*
-         * Strange hack - bug in NT Client and NT Server that we
-         * must emulate?
+         * Strange hack - bug in NT Client and NT Server that we must emulate?
          */
-        if (filter == 3 && wtree)
-            filter = 0x17;
+        if ((filter == (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME)) && wtree)
+            filter |= FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
 
         fidp = smb_FindFID(watch->vcp, fid, 0);
         if (!fidp) {
-            osi_Log1(smb_logp," no fidp for fid[%d]",fid);
+            osi_Log2(smb_logp," no fidp for fid[%d] in vcp 0x%p",fid, watch->vcp);
             lastWatch = watch;
             watch = watch->nextp;
             continue;
@@ -7565,7 +8172,7 @@ void smb_NotifyChange(DWORD action, DWORD notifyFilter,
         if (fidp->scp != dscp
              || (filter & notifyFilter) == 0
              || (!isDirectParent && !wtree)) {
-            osi_Log1(smb_logp," passing fidp->scp[%x]", fidp->scp);
+            osi_Log1(smb_logp," skipping fidp->scp[%x]", fidp->scp);
             smb_ReleaseFID(fidp);
             lastWatch = watch;
             watch = watch->nextp;
@@ -7576,7 +8183,32 @@ void smb_NotifyChange(DWORD action, DWORD notifyFilter,
         osi_Log4(smb_logp,
                   "Sending Change Notification for fid %d filter 0x%x wtree %d file %s",
                   fid, filter, wtree, osi_LogSaveString(smb_logp, filename));
-
+       if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
+           osi_Log0(smb_logp, "      Notify Change File Name");
+       if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
+           osi_Log0(smb_logp, "      Notify Change Directory Name");
+       if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
+           osi_Log0(smb_logp, "      Notify Change Attributes");
+       if (filter & FILE_NOTIFY_CHANGE_SIZE)
+           osi_Log0(smb_logp, "      Notify Change Size");
+       if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
+           osi_Log0(smb_logp, "      Notify Change Last Write");
+       if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
+           osi_Log0(smb_logp, "      Notify Change Last Access");
+       if (filter & FILE_NOTIFY_CHANGE_CREATION)
+           osi_Log0(smb_logp, "      Notify Change Creation");
+       if (filter & FILE_NOTIFY_CHANGE_EA)
+           osi_Log0(smb_logp, "      Notify Change Extended Attributes");
+       if (filter & FILE_NOTIFY_CHANGE_SECURITY)
+           osi_Log0(smb_logp, "      Notify Change Security");
+       if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
+           osi_Log0(smb_logp, "      Notify Change Stream Name");
+       if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
+           osi_Log0(smb_logp, "      Notify Change Stream Size");
+       if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
+           osi_Log0(smb_logp, "      Notify Change Stream Write");
+                    
+       /* A watch can only be notified once.  Remove it from the list */
         nextWatch = watch->nextp;
         if (watch == smb_Directory_Watches)
             smb_Directory_Watches = nextWatch;
@@ -7730,7 +8362,7 @@ long smb_ReceiveNTCancel(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
                           osi_LogSaveString(smb_logp, (fidp)?fidp->NTopen_wholepathp:""));
 
                 scp = fidp->scp;
-               osi_Log2(afsd_logp,"smb_ReceiveNTCancel fidp 0x%p scp 0x%p", fidp, scp);
+               osi_Log2(smb_logp,"smb_ReceiveNTCancel fidp 0x%p scp 0x%p", fidp, scp);
                 lock_ObtainMutex(&scp->mx);
                 if (watchtree)
                     scp->flags &= ~CM_SCACHEFLAG_WATCHEDSUBTREE;
@@ -7785,7 +8417,7 @@ long smb_ReceiveNTRename(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     rename_type = smb_GetSMBParm(inp, 1);
 
     if (rename_type != RENAME_FLAG_RENAME && rename_type != RENAME_FLAG_HARD_LINK) {
-        osi_Log1(smb_logp, "NTRename invalid infolevel [%x]", rename_type);
+        osi_Log1(smb_logp, "NTRename invalid rename_type [%x]", rename_type);
         return CM_ERROR_NOACCESS;
     }