windows-local-directory-updates-smb-20070802
authorAsanka Herath <asanka@secure-endpoints.com>
Thu, 2 Aug 2007 22:05:39 +0000 (22:05 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Thu, 2 Aug 2007 22:05:39 +0000 (22:05 +0000)
The windows cache manager has suffered from poor performance as a result
of Create, Rename, and Delete operations because they invalidate the
contents of the directory pages in the cache thereby forcing them to be
reloaded from the file server.   As the directory size increases, the clock
time necessary to perform the reload increases.

This delta adds support for parsing and updating the AFS3 directory buffers
to cm_dir.c.  It then uses that functionality to perform local updates to
the directory buffers whenever the following conditions are met:

 1. the data version on the directory as a result of the change
    was incremented by one.

 2. all of the directory buffers required for the update are in
    the cache.

If these conditions are not met, the directory is reloaded from the file
server.

src/WINNT/afsd/smb.c

index 8f2e722..ccefda0 100644 (file)
@@ -4902,6 +4902,7 @@ typedef struct smb_unlinkRock {
     char *maskp;               /* pointer to the star pattern */
     int flags;
     int any;
+    cm_dirEntryList_t * matches;
 } smb_unlinkRock_t;
 
 int smb_UnlinkProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
@@ -4930,20 +4931,18 @@ int smb_UnlinkProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hype
         match = smb_V3MatchMask(matchName, rockp->maskp, caseFold | CM_FLAG_CASEFOLD);
     }
     if (match) {
-        osi_Log1(smb_logp, "Unlinking %s",
+        osi_Log1(smb_logp, "Found match %s",
                  osi_LogSaveString(smb_logp, matchName));
-        code = cm_Unlink(dscp, dep->name, rockp->userp, rockp->reqp);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_REMOVED,
-                            FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION,
-                             dscp, dep->name, NULL, TRUE);
-        if (code == 0) {
+
+        cm_DirEntryListAdd(dep->name, &rockp->matches);
+
             rockp->any = 1;
 
             /* If we made a case sensitive exact match, we might as well quit now. */
             if (!(rockp->flags & SMB_MASKFLAG_CASEFOLD) && !strcmp(matchName, rockp->maskp))
                 code = CM_ERROR_STOPNOW;
-        }
+        else
+            code = 0;
     }
     else code = 0;
 
@@ -5024,6 +5023,7 @@ long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     rock.reqp = &req;
     rock.dscp = dscp;
     rock.vcp = vcp;
+    rock.matches = NULL;
 
     /* Now, if we aren't dealing with a wildcard match, we first try an exact 
      * match.  If that fails, we do a case insensitve match. 
@@ -5044,6 +5044,24 @@ long smb_ReceiveCoreUnlink(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
     if (code == CM_ERROR_STOPNOW) 
         code = 0;
 
+    if (code == 0 && rock.matches) {
+        cm_dirEntryList_t * entry;
+
+        for (entry = rock.matches; code == 0 && entry; entry = entry->nextp) {
+
+            osi_Log1(smb_logp, "Unlinking %s",
+                     osi_LogSaveString(smb_logp, entry->name));
+            code = cm_Unlink(dscp, entry->name, userp, &req);
+
+            if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+                smb_NotifyChange(FILE_ACTION_REMOVED,
+                                 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION,
+                                 dscp, entry->name, NULL, TRUE);
+        }
+    }
+
+    cm_DirEntryListFree(&rock.matches);
+
     cm_ReleaseUser(userp);
         
     cm_ReleaseSCache(dscp);
@@ -5062,6 +5080,7 @@ typedef struct smb_renameRock {
     char *maskp;               /* pointer to star pattern of old file name */
     int flags;             /* tilde, casefold, etc */
     char *newNamep;            /* ptr to the new file's name */
+    char oldName[MAX_PATH];
     int any;
 } smb_renameRock_t;
 
@@ -5086,21 +5105,15 @@ int smb_RenameProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hype
         cm_Gen8Dot3Name(dep, shortName, NULL);
         match = smb_V3MatchMask(shortName, rockp->maskp, caseFold);
     }
+
     if (match) {
        rockp->any = 1;
-
-       code = cm_Rename(rockp->odscp, dep->name,
-                         rockp->ndscp, rockp->newNamep, rockp->userp,
-                         rockp->reqp); 
-        /* if the call worked, stop doing the search now, since we
-         * really only want to rename one file.
-         */
-       osi_Log1(smb_logp, "cm_Rename returns %ld", code);
-        if (code == 0) 
+        strncpy(rockp->oldName, dep->name, sizeof(rockp->oldName)/sizeof(char) - 1);
+        rockp->oldName[sizeof(rockp->oldName)/sizeof(char) - 1] = '\0';
             code = CM_ERROR_STOPNOW;
-    }       
-    else 
+    } else {
        code = 0;
+    }
 
     return code;
 }
@@ -5205,6 +5218,7 @@ smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, char * oldPathp, char * newPathp, i
     rock.maskp = oldLastNamep;
     rock.flags = ((strchr(oldLastNamep, '~') != NULL) ? SMB_MASKFLAG_TILDE : 0);
     rock.newNamep = newLastNamep;
+    rock.oldName[0] = '\0';
     rock.any = 0;
 
     /* Check if the file already exists; if so return error */
@@ -5257,16 +5271,24 @@ smb_Rename(smb_vc_t *vcp, smb_packet_t *inp, char * oldPathp, char * newPathp, i
     }
     osi_Log1(smb_logp, "smb_RenameProc returns %ld", code);
 
-    if (code == CM_ERROR_STOPNOW)
-        code = 0;
-    else if (code == 0)
+    if (code == CM_ERROR_STOPNOW && rock.oldName[0] != '\0') {
+       code = cm_Rename(rock.odscp, rock.oldName,
+                         rock.ndscp, rock.newNamep, rock.userp,
+                         rock.reqp);   
+        /* if the call worked, stop doing the search now, since we
+         * really only want to rename one file.
+         */
+       osi_Log1(smb_logp, "cm_Rename returns %ld", code);
+    } else if (code == 0) {
         code = CM_ERROR_NOSUCHFILE;
+    }
 
     /* Handle Change Notification */
     /*
     * Being lazy, not distinguishing between files and dirs in this
     * filter, since we'd have to do a lookup.
     */
+    if (code == 0) {
     filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
     if (oldDscp == newDscp) {
         if (oldDscp->flags & CM_SCACHEFLAG_ANYWATCH)
@@ -5434,6 +5456,7 @@ smb_Link(smb_vc_t *vcp, smb_packet_t *inp, char * oldPathp, char * newPathp)
                              filter, newDscp, newLastNamep,
                              NULL, TRUE);
     }
+    }
 
     if (tmpscp != NULL) 
         cm_ReleaseSCache(tmpscp);
@@ -5479,6 +5502,7 @@ typedef struct smb_rmdirRock {
     char *maskp;               /* pointer to the star pattern */
     int flags;
     int any;
+    cm_dirEntryList_t * matches;
 } smb_rmdirRock_t;
 
 int smb_RmdirProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper_t *offp)
@@ -5503,20 +5527,13 @@ int smb_RmdirProc(cm_scache_t *dscp, cm_dirEntry_t *dep, void *vrockp, osi_hyper
         matchName = shortName;
         match = (cm_stricmp(matchName, rockp->maskp) == 0);
     }       
+
     if (match) {
-        osi_Log1(smb_logp, "Removing directory %s",
-                 osi_LogSaveString(smb_logp, matchName));
-        code = cm_RemoveDir(dscp, dep->name, rockp->userp, rockp->reqp);
-        if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
-            smb_NotifyChange(FILE_ACTION_REMOVED,
-                             FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_CREATION,
-                             dscp, dep->name, NULL, TRUE);
-        if (code == 0)
             rockp->any = 1;
+        cm_DirEntryListAdd(dep->name, &rockp->matches);
     }
-    else code = 0;
 
-    return code;
+    return 0;
 }
 
 long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *outp)
@@ -5587,6 +5604,7 @@ long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
     rock.userp = userp;
     rock.reqp = &req;
     rock.dscp = dscp;
+    rock.matches = NULL;
 
     /* First do a case sensitive match, and if that fails, do a case insensitive match */
     code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
@@ -5597,6 +5615,24 @@ long smb_ReceiveCoreRemoveDir(smb_vc_t *vcp, smb_packet_t *inp, smb_packet_t *ou
         code = cm_ApplyDir(dscp, smb_RmdirProc, &rock, &thyper, userp, &req, NULL);
     }
 
+    if (code == 0 && rock.matches) {
+        cm_dirEntryList_t * entry;
+
+        for (entry = rock.matches; code == 0 && entry; entry = entry->nextp) {
+            osi_Log1(smb_logp, "Removing directory %s",
+                     osi_LogSaveString(smb_logp, entry->name));
+
+            code = cm_RemoveDir(dscp, entry->name, userp, &req);
+
+            if (code == 0 && (dscp->flags & CM_SCACHEFLAG_ANYWATCH))
+                smb_NotifyChange(FILE_ACTION_REMOVED,
+                                 FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_CREATION,
+                                 dscp, entry->name, NULL, TRUE);
+        }
+    }
+
+    cm_DirEntryListFree(&rock.matches);
+
     cm_ReleaseUser(userp);
         
     cm_ReleaseSCache(dscp);