Windows: add DV and error status to dir enumerations
authorJeffrey Altman <jaltman@your-file-system.com>
Fri, 30 Dec 2011 03:20:38 +0000 (22:20 -0500)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 31 Dec 2011 21:19:45 +0000 (13:19 -0800)
The cm_BPlusDirEnum family of functions are atomic when generating
the directory enumeration but are not atomic with respect to the
rest of the system as the enumeration is accessed.  Therefore, the
data version of the directory at the time the enumeration is created
may not be the same as the directory version when the enumeration
is fully processed.  We therefore store the initial data version in the
cm_direnum_t object.

When the enumeration is fetching status information for each of the
directory entries, it is possible that the fetch status will fail.
We therefore store the fetch status error code in the cm_direnum_entry_t
object.   By doing so, the consumer of the enumeration can make a
reasonable decision about the lack of status info.  For example,
if the resulting error is CM_ERROR_BADFD it is known that the entry
has been removed from the directory since the initial enumeration.

Change-Id: I289881e2c59525a9f998559b00769d3ac3f335c0
Reviewed-on: http://gerrit.openafs.org/6452
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@secure-endpoints.com>
Tested-by: Jeffrey Altman <jaltman@secure-endpoints.com>

src/WINNT/afsd/cm_btree.c
src/WINNT/afsd/cm_btree.h

index 97a3323..c9bb068 100644 (file)
@@ -2302,6 +2302,7 @@ cm_BPlusDirEnumerate(cm_scache_t *dscp, cm_user_t *userp, cm_req_t *reqp,
     enump->userp = userp;
     enump->reqFlags = reqp->flags;
     enump->fetchStatus = fetchStatus;
+    enump->dataVersion = dscp->dirDataVersion;
 
   done:
     if (!locked)
@@ -2340,10 +2341,14 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
 {
     cm_scache_t *dscp = enump->dscp;
     cm_user_t   *userp = enump->userp;
-    cm_bulkStat_t *bsp;
+    cm_bulkStat_t *bsp = NULL;
+    afs_uint32 ** bs_errorCodep = NULL;
+    afs_uint32    dscp_errorCode = 0;
     afs_uint32 count;
     afs_uint32 code = 0;
     cm_req_t req;
+    int i;
+    cm_scache_t   *tscp;
 
     cm_InitReq(&req);
     req.flags = enump->reqFlags;
@@ -2352,14 +2357,36 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
         return 0;
 
     bsp = malloc(sizeof(cm_bulkStat_t));
-    if (!bsp)
-        return ENOMEM;
+    if (!bsp) {
+        code = ENOMEM;
+        goto done;
+    }
     memset(bsp, 0, sizeof(cm_bulkStat_t));
 
-    for ( count = 0; count < enump->count; count++ ) {
-        cm_scache_t   *tscp = cm_FindSCache(&enump->entry[count].fid);
-        int i;
+    bs_errorCodep = malloc(sizeof(DWORD *) * AFSCBMAX);
+    if (!bs_errorCodep) {
+        code = ENOMEM;
+        goto done;
+    }
 
+    /*
+     * In order to prevent the directory callback from expiring
+     * on really large directories with many symlinks to mount
+     * points such as /afs/andrew.cmu.edu/usr/, always include
+     * the directory fid in the search.
+     */
+    bsp->fids[0].Volume = dscp->fid.volume;
+    bsp->fids[0].Vnode = dscp->fid.vnode;
+    bsp->fids[0].Unique = dscp->fid.unique;
+    bs_errorCodep[0] = &dscp_errorCode;
+    bsp->counter++;
+
+    for ( count = 0; count < enump->count; count++ ) {
+        if ( !wcscmp(L".", enump->entry[count].name) || !wcscmp(L"..", enump->entry[count].name) ) {
+            continue;
+        }
+        
+        tscp = cm_FindSCache(&enump->entry[count].fid);
         if (tscp) {
             if (lock_TryWrite(&tscp->rw)) {
                 /* we have an entry that we can look at */
@@ -2371,6 +2398,7 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
                     lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
                     enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[count].errorCode = 0;
                     continue;
                 }
                 lock_ReleaseWrite(&tscp->rw);
@@ -2383,24 +2411,64 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
         bsp->fids[i].Vnode = enump->entry[count].fid.vnode;
         bsp->fids[i].Unique = enump->entry[count].fid.unique;
         enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+        bs_errorCodep[i] = &enump->entry[count].errorCode;
 
         if (bsp->counter == AFSCBMAX) {
             code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+            if (code)
+                goto done;
+
+            for ( i=0; i<bsp->counter; i++) {
+                *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+            }
+
+            if (dscp_errorCode) {
+                code = dscp_errorCode;
+                goto done;
+            }
             memset(bsp, 0, sizeof(cm_bulkStat_t));
+            /*
+             * In order to prevent the directory callback from expiring
+             * on really large directories with many symlinks to mount
+             * points such as /afs/andrew.cmu.edu/usr/, always include
+             * the directory fid in the search.
+             */
+            bsp->fids[0].Volume = dscp->fid.volume;
+            bsp->fids[0].Vnode = dscp->fid.vnode;
+            bsp->fids[0].Unique = dscp->fid.unique;
+            bs_errorCodep[0] = &dscp_errorCode;
+            bsp->counter++;
         }
     }
 
-    if (bsp->counter > 0)
+    if (bsp->counter > 0) {
         code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+        if (code)
+            goto done;
+
+        for ( i=0; i<bsp->counter; i++) {
+            *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+        }
+
+        if (dscp_errorCode) {
+            code = dscp_errorCode;
+            goto done;
+        }
+    }
+
+  done:
+    if (bsp)
+        free(bsp);
+    if (bs_errorCodep)
+        free(bs_errorCodep);
 
-    free(bsp);
     return code;
 }
 
 /*
  * Similar to cm_BPlusDirEnumBulkStat() except that only
  * one RPC is issued containing the provided scp FID and up to
- * AFSCBMAX - 1 other FIDs for which the status info has yet
+ * AFSCBMAX - 2 other FIDs for which the status info has yet
  * to be obtained.
  */
 long
@@ -2408,10 +2476,14 @@ cm_BPlusDirEnumBulkStatOne(cm_direnum_t *enump, cm_scache_t *scp)
 {
     cm_scache_t *dscp = enump->dscp;
     cm_user_t   *userp = enump->userp;
-    cm_bulkStat_t *bsp;
+    cm_bulkStat_t *bsp = NULL;
+    afs_uint32 ** bs_errorCodep = NULL;
+    afs_uint32    dscp_errorCode = 0;
+    afs_uint32    scp_errorCode = 0;
     afs_uint32 code = 0;
     afs_uint32 i;
     cm_req_t req;
+    cm_scache_t   *tscp;
 
     cm_InitReq(&req);
     req.flags = enump->reqFlags;
@@ -2420,59 +2492,65 @@ cm_BPlusDirEnumBulkStatOne(cm_direnum_t *enump, cm_scache_t *scp)
         return 0;
 
     bsp = malloc(sizeof(cm_bulkStat_t));
-    if (!bsp)
-        return ENOMEM;
+    if (!bsp) {
+        code = ENOMEM;
+        goto done;
+    }
     memset(bsp, 0, sizeof(cm_bulkStat_t));
 
-     /*
-      * In order to prevent the directory callback from expiring
-      * on really large directories with many symlinks to mount
-      * points such as /afs/andrew.cmu.edu/usr/, always include
-      * the directory fid in the search.
-      */
-    if (lock_TryWrite(&dscp->rw)) {
-        bsp->fids[bsp->counter].Volume = dscp->fid.volume;
-        bsp->fids[bsp->counter].Vnode = dscp->fid.vnode;
-        bsp->fids[bsp->counter].Unique = dscp->fid.unique;
-        bsp->counter++;
-        lock_ReleaseWrite(&dscp->rw);
+    bs_errorCodep = malloc(sizeof(DWORD *) * AFSCBMAX);
+    if (!bs_errorCodep) {
+        code = ENOMEM;
+        goto done;
     }
 
-    /* First process the requested scp if we can */
-    if (lock_TryWrite(&scp->rw)) {
-        bsp->fids[bsp->counter].Volume = scp->fid.volume;
-        bsp->fids[bsp->counter].Vnode = scp->fid.vnode;
-        bsp->fids[bsp->counter].Unique = scp->fid.unique;
-        bsp->counter++;
-        lock_ReleaseWrite(&scp->rw);
-    }
+    /*
+     * In order to prevent the directory callback from expiring
+     * on really large directories with many symlinks to mount
+     * points such as /afs/andrew.cmu.edu/usr/, always include
+     * the directory fid in the search.
+     */
+    bsp->fids[0].Volume = dscp->fid.volume;
+    bsp->fids[0].Vnode = dscp->fid.vnode;
+    bsp->fids[0].Unique = dscp->fid.unique;
+    bs_errorCodep[0] = &dscp_errorCode;
+    bsp->counter++;
+
+    /*
+     * There is an assumption that this FID is located
+     * within the directory enumeration but it could be
+     * the case that the enumeration is out of date and
+     * the FID is not listed.  So we explicitly add it
+     * after the directory FID and then skip it later
+     * if we find it.
+     */
+    bsp->fids[1].Volume = scp->fid.volume;
+    bsp->fids[1].Vnode = scp->fid.vnode;
+    bsp->fids[1].Unique = scp->fid.unique;
+    bs_errorCodep[1] = &scp_errorCode;
+    bsp->counter++;
 
     if (enump->count <= AFSCBMAX - 1) {
         i = 0;
     } else {
         /*
-         * Find the requested FID in the enumeration
-         * and start from there.
+         * Find the requested FID in the enumeration and start from there.
          */
         for (i=0; i < enump->count && cm_FidCmp(&scp->fid, &enump->entry[i].fid); i++);
     }
 
     for ( ; bsp->counter < AFSCBMAX && i < enump->count; i++) {
-        cm_scache_t   *tscp;
-        int count = bsp->counter;
-
         if ( !wcscmp(L".", enump->entry[i].name) || !wcscmp(L"..", enump->entry[i].name) ) {
             continue;
-        } else {
-            tscp = cm_FindSCache(&enump->entry[i].fid);
-            if (scp == tscp) {
-                enump->entry[i].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+        }
+
+        tscp = cm_FindSCache(&enump->entry[i].fid);
+        if (tscp) {
+            if (tscp == scp) {
                 cm_ReleaseSCache(tscp);
                 continue;
             }
-        }
 
-        if (tscp) {
             if (lock_TryWrite(&tscp->rw)) {
                 /* we have an entry that we can look at */
                 if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) {
@@ -2483,25 +2561,45 @@ cm_BPlusDirEnumBulkStatOne(cm_direnum_t *enump, cm_scache_t *scp)
                     lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
                     enump->entry[i].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[i].errorCode = 0;
                     continue;
                 }
                 lock_ReleaseWrite(&tscp->rw);
             }  /* got lock */
-            if (scp != tscp)
-                cm_ReleaseSCache(tscp);
+            cm_ReleaseSCache(tscp);
         } /* found entry */
 
-        bsp->fids[count].Volume = enump->entry[i].fid.volume;
-        bsp->fids[count].Vnode = enump->entry[i].fid.vnode;
-        bsp->fids[count].Unique = enump->entry[i].fid.unique;
-        bsp->counter++;
+        bsp->fids[bsp->counter].Volume = enump->entry[i].fid.volume;
+        bsp->fids[bsp->counter].Vnode = enump->entry[i].fid.vnode;
+        bsp->fids[bsp->counter].Unique = enump->entry[i].fid.unique;
         enump->entry[i].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+        bs_errorCodep[bsp->counter] = &enump->entry[i].errorCode;
+        bsp->counter++;
     }
 
-    if (bsp->counter > 0)
+    if (bsp->counter > 0) {
         code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+        /* Now process any errors that might have occurred */
+        if (code)
+            goto done;
+
+        for ( i=0; i<bsp->counter; i++) {
+            *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+        }
+
+        /* Check if there was an error on the requested FID, if so return it */
+        if ( scp_errorCode ) {
+            code = scp_errorCode;
+            goto done;
+        }
+    }
+
+  done:
+    if (bsp)
+        free(bsp);
+    if (bs_errorCodep)
+        free(bs_errorCodep);
 
-    free(bsp);
     return code;
 }
 
@@ -2510,10 +2608,14 @@ cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
 {
     cm_scache_t *dscp = enump->dscp;
     cm_user_t   *userp = enump->userp;
-    cm_bulkStat_t *bsp;
+    cm_bulkStat_t *bsp = NULL;
+    afs_uint32 ** bs_errorCodep = NULL;
+    afs_uint32    dscp_errorCode = 0;
     afs_uint32 count;
     afs_uint32 code = 0;
     cm_req_t req;
+    cm_scache_t   *tscp;
+    int i;
 
     cm_InitReq(&req);
     req.flags = enump->reqFlags;
@@ -2522,14 +2624,36 @@ cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
         return 0;
 
     bsp = malloc(sizeof(cm_bulkStat_t));
-    if (!bsp)
-        return ENOMEM;
+    if (!bsp) {
+        code = ENOMEM;
+        goto done;
+    }
     memset(bsp, 0, sizeof(cm_bulkStat_t));
 
-    for ( count = enump->next; count < enump->count; count++ ) {
-        cm_scache_t   *tscp = cm_FindSCache(&enump->entry[count].fid);
-        int i;
+    bs_errorCodep = malloc(sizeof(DWORD *) * AFSCBMAX);
+    if (!bs_errorCodep) {
+        code = ENOMEM;
+        goto done;
+    }
+
+    /*
+     * In order to prevent the directory callback from expiring
+     * on really large directories with many symlinks to mount
+     * points such as /afs/andrew.cmu.edu/usr/, always include
+     * the directory fid in the search.
+     */
+    bsp->fids[0].Volume = dscp->fid.volume;
+    bsp->fids[0].Vnode = dscp->fid.vnode;
+    bsp->fids[0].Unique = dscp->fid.unique;
+    bs_errorCodep[0] = &dscp_errorCode;
+    bsp->counter++;
+
+    for ( count = enump->next; count < enump->count && bsp->counter < AFSCBMAX; count++ ) {
+        if ( !wcscmp(L".", enump->entry[count].name) || !wcscmp(L"..", enump->entry[count].name) ) {
+            continue;
+        }
 
+        tscp = cm_FindSCache(&enump->entry[count].fid);
         if (tscp) {
             if (lock_TryWrite(&tscp->rw)) {
                 /* we have an entry that we can look at */
@@ -2541,6 +2665,7 @@ cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
                     lock_ReleaseWrite(&tscp->rw);
                     cm_ReleaseSCache(tscp);
                     enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    enump->entry[count].errorCode = 0;
                     continue;
                 }
                 lock_ReleaseWrite(&tscp->rw);
@@ -2548,22 +2673,36 @@ cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
             cm_ReleaseSCache(tscp);
         }      /* found entry */
 
-        i = bsp->counter++;
-        bsp->fids[i].Volume = enump->entry[count].fid.volume;
-        bsp->fids[i].Vnode = enump->entry[count].fid.vnode;
-        bsp->fids[i].Unique = enump->entry[count].fid.unique;
+        bsp->fids[bsp->counter].Volume = enump->entry[count].fid.volume;
+        bsp->fids[bsp->counter].Vnode = enump->entry[count].fid.vnode;
+        bsp->fids[bsp->counter].Unique = enump->entry[count].fid.unique;
         enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+        bs_errorCodep[bsp->counter] = &enump->entry[count].errorCode;
+        bsp->counter++;
+    }
 
-        if (bsp->counter == AFSCBMAX) {
-            code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
-            break;
+    if (bsp->counter > 0) {
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+        /* Now process any errors that might have occurred */
+        if (code)
+            goto done;
+
+        for ( i=0; i<bsp->counter; i++) {
+            *(bs_errorCodep[i]) = cm_MapRPCError(bsp->stats[i].errorCode, &req);
+        }
+
+        if (dscp_errorCode) {
+            code = dscp_errorCode;
+            goto done;
         }
     }
 
-    if (bsp->counter > 0 && bsp->counter < AFSCBMAX)
-        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+  done:
+    if (bsp)
+        free(bsp);
+    if (bs_errorCodep)
+        free(bs_errorCodep);
 
-    free(bsp);
     return code;
 }
 
index 32eade8..7a7166e 100644 (file)
@@ -158,6 +158,7 @@ typedef struct cm_direnum_entry {
     cm_fid_t    fid;
     normchar_t   shortName[13];
     afs_uint32   flags;
+    afs_uint32   errorCode;
 } cm_direnum_entry_t;
 
 #define CM_DIRENUM_FLAG_GOT_STATUS     1
@@ -165,6 +166,7 @@ typedef struct cm_direnum_entry {
 typedef struct cm_direnum {
     cm_scache_t        *dscp;
     cm_user_t          *userp;
+    afs_uint64          dataVersion;    /* enumeration snapshot dir version */
     afs_uint32          reqFlags;
     afs_uint32         count;
     afs_uint32         next;