Windows: return ENOMEM from cm_BPlus functions on malloc failure
[openafs.git] / src / WINNT / afsd / cm_btree.c
index 200525c..5002832 100644 (file)
@@ -1582,12 +1582,16 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry,
     normchar_t * entry = NULL;
 
     if (op->scp->dirBplus == NULL || 
-        op->dataVersion != op->scp->dirDataVersion) {
+        op->dataVersion > op->scp->dirDataVersion) {
         rc = EINVAL;
         goto done;
     }
 
     entry = cm_ClientStringToNormStringAlloc(centry, -1, NULL);
+    if (!entry) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = entry;
 
     lock_AssertAny(&op->scp->dirlock);
@@ -1609,7 +1613,7 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry,
          */
         slot = getSlot(op->scp->dirBplus, leafNode);
         if (slot <= BTERROR) {
-            op->scp->dirDataVersion = 0;
+            op->scp->dirDataVersion = CM_SCACHE_VERSION_BAD;
             rc = (slot == BTERROR ? EINVAL : ENOENT);
             goto done;
         }
@@ -1674,12 +1678,16 @@ cm_BPlusDirLookup(cm_dirOp_t * op, clientchar_t * centry, cm_fid_t * cfid)
     LARGE_INTEGER start, end;
 
     if (op->scp->dirBplus == NULL || 
-        op->dataVersion != op->scp->dirDataVersion) {
+        op->dataVersion > op->scp->dirDataVersion) {
         rc = EINVAL;
         goto done;
     }
 
     entry = cm_ClientStringToNormStringAlloc(centry, -1, NULL);
+    if (!entry) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = entry;
 
     lock_AssertAny(&op->scp->dirlock);
@@ -1767,6 +1775,10 @@ long cm_BPlusDirCreateEntry(cm_dirOp_t * op, clientchar_t * entry, cm_fid_t * cf
     }
 
     normalizedName = cm_ClientStringToNormStringAlloc(entry, -1, NULL);
+    if (!normalizedName) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = normalizedName;
 
     lock_AssertWrite(&op->scp->dirlock);
@@ -1833,6 +1845,10 @@ int  cm_BPlusDirDeleteEntry(cm_dirOp_t * op, clientchar_t *centry)
     }
 
     normalizedEntry = cm_ClientStringToNormStringAlloc(centry, -1, NULL);
+    if (!normalizedEntry) {
+        rc = EINVAL;
+        goto done;
+    }
     key.name = normalizedEntry;
 
     lock_AssertWrite(&op->scp->dirlock);
@@ -2000,6 +2016,12 @@ int cm_BPlusDirFoo(struct cm_scache *scp, struct cm_dirEntry *dep,
     }
 
     data.cname = cm_FsStringToClientStringAlloc(dep->name, -1, NULL);
+    if (data.cname == NULL) {
+#ifdef DEBUG
+        DebugBreak();
+#endif
+        return 0;
+    }
     data.fsname = cm_FsStrDup(dep->name);
     data.shortform = FALSE;
 
@@ -2017,10 +2039,12 @@ int cm_BPlusDirFoo(struct cm_scache *scp, struct cm_dirEntry *dep,
 
         key.name = wshortName;
         data.cname = cm_FsStringToClientStringAlloc(dep->name, -1, NULL);
-        data.fsname = cm_FsStrDup(dep->name);
-        data.shortform = TRUE;
+        if (data.cname) {
+            data.fsname = cm_FsStrDup(dep->name);
+            data.shortform = TRUE;
 
-        insert(scp->dirBplus, key, data);
+            insert(scp->dirBplus, key, data);
+        }
     }
 
     if (normalized_name)
@@ -2136,23 +2160,26 @@ cm_BPlusEnumAlloc(afs_uint32 entries)
     size_t        size;
 
     if (entries == 0)
-       return NULL;
-
-    size = sizeof(cm_direnum_t)+(entries-1)*sizeof(cm_direnum_entry_t);
+        size = sizeof(cm_direnum_t);
+    else
+        size = sizeof(cm_direnum_t)+(entries-1)*sizeof(cm_direnum_entry_t);
     enump = (cm_direnum_t *)malloc(size);
-    memset(enump, 0, size);
-    enump->count = entries;
+    if (enump) {
+        memset(enump, 0, size);
+        enump->count = entries;
+    }
     return enump;
 }
 
 long 
-cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, 
-                     clientchar_t * maskp, cm_direnum_t **enumpp)
+cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, 
+                     afs_uint32 locked, clientchar_t * maskp, 
+                     afs_uint32 fetchStatus, cm_direnum_t **enumpp)
 {
     afs_uint32 count = 0, slot, numentries;
     Nptr leafNode = NONODE, nextLeafNode;
     Nptr firstDataNode, dataNode, nextDataNode;
-    cm_direnum_t * enump;
+    cm_direnum_t * enump = NULL;
     long rc = 0;
     char buffer[512];
 
@@ -2162,8 +2189,16 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
     if (!locked)
        lock_ObtainRead(&scp->dirlock);
 
+    /* 
+     * Hold a reference to the directory so that it wont' be
+     * recycled while the enumeration is active. 
+     */
+    cm_HoldSCache(scp);
+    cm_HoldUser(userp);
+
     if (scp->dirBplus == NULL) {
        osi_Log0(afsd_logp, "cm_BPlusDirEnumerate No BPlus Tree");
+        rc = CM_ERROR_WOULDBLOCK;
        goto done;
     }
 
@@ -2260,6 +2295,11 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
        nextLeafNode = getnextnode(leafNode);
     }   
 
+    enump->dscp = scp;
+    enump->userp = userp;
+    enump->reqFlags = reqp->flags;
+    enump->fetchStatus = fetchStatus;
+
   done:
     if (!locked)
        lock_ReleaseRead(&scp->dirlock);
@@ -2267,7 +2307,11 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
     /* if we failed, cleanup any mess */
     if (rc != 0) {
        osi_Log0(afsd_logp, "cm_BPlusDirEnumerate rc != 0");
-       if (enump) {
+       
+        /* release the directory because we failed to generate an enumeration object */
+        cm_ReleaseSCache(scp);
+        cm_ReleaseUser(userp);
+        if (enump) {
            for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) {
                free(enump->entry[count].name);
            }
@@ -2282,15 +2326,143 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked,
 }
 
 long 
+cm_BPlusDirEnumBulkStat(cm_direnum_t *enump)
+{
+    cm_scache_t *dscp = enump->dscp;
+    cm_user_t   *userp = enump->userp;
+    cm_bulkStat_t *bsp;
+    afs_uint32 count;
+    afs_uint32 code = 0;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+    req.flags = enump->reqFlags;
+
+    if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID )
+        return 0;
+
+    bsp = malloc(sizeof(cm_bulkStat_t));
+    if (!bsp)
+        return ENOMEM;
+    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;
+
+        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)) {
+                    /* we have a callback on it.  Don't bother
+                     * fetching this stat entry, since we're happy
+                     * with the info we have.
+                     */
+                    lock_ReleaseWrite(&tscp->rw);
+                    cm_ReleaseSCache(tscp);
+                    enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    continue;
+                }
+                lock_ReleaseWrite(&tscp->rw);
+            }  /* got lock */
+            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;
+        enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+
+        if (bsp->counter == AFSCBMAX) {
+            code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+            memset(bsp, 0, sizeof(cm_bulkStat_t));
+        }
+    }
+
+    if (bsp->counter > 0)
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+
+    free(bsp);
+    return code;
+}
+
+static long 
+cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump)
+{
+    cm_scache_t *dscp = enump->dscp;
+    cm_user_t   *userp = enump->userp;
+    cm_bulkStat_t *bsp;
+    afs_uint32 count;
+    afs_uint32 code = 0;
+    cm_req_t req;
+
+    cm_InitReq(&req);
+    req.flags = enump->reqFlags;
+
+    if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID )
+        return 0;
+
+    bsp = malloc(sizeof(cm_bulkStat_t));
+    if (!bsp)
+        return ENOMEM;
+    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;
+
+        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)) {
+                    /* we have a callback on it.  Don't bother
+                     * fetching this stat entry, since we're happy
+                     * with the info we have.
+                     */
+                    lock_ReleaseWrite(&tscp->rw);
+                    cm_ReleaseSCache(tscp);
+                    enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+                    continue;
+                }
+                lock_ReleaseWrite(&tscp->rw);
+            }  /* got lock */
+            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;
+        enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS;
+
+        if (bsp->counter == AFSCBMAX) {
+            code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+            break;
+        }
+    }
+
+    if (bsp->counter > 0 && bsp->counter < AFSCBMAX)
+        code = cm_TryBulkStatRPC(dscp, bsp, userp, &req);
+
+    free(bsp);
+    return code;
+}
+
+long
 cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
 {      
-    if (enump == NULL || entrypp == NULL || enump->next > enump->count) {
+    if (enump == NULL || entrypp == NULL || enump->next >= enump->count) {
        if (entrypp)
            *entrypp = NULL;
        osi_Log0(afsd_logp, "cm_BPlusDirNextEnumEntry invalid input");
-       return CM_ERROR_INVAL;                        \
+       return CM_ERROR_INVAL;
     }
 
+    if (enump->fetchStatus && 
+        !(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS))
+        cm_BPlusDirEnumBulkStatNext(enump);
+
     *entrypp = &enump->entry[enump->next++];
     if ( enump->next == enump->count ) {
        osi_Log0(afsd_logp, "cm_BPlusDirNextEnumEntry STOPNOW");
@@ -2302,6 +2474,31 @@ cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
     }
 }
 
+long
+cm_BPlusDirPeekNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp)
+{      
+    if (enump == NULL || entrypp == NULL || enump->next >= enump->count) {
+       if (entrypp)
+           *entrypp = NULL;
+       osi_Log0(afsd_logp, "cm_BPlusDirPeekNextEnumEntry invalid input");
+       return CM_ERROR_INVAL;
+    }
+
+    if (enump->fetchStatus && 
+        !(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS))
+        cm_BPlusDirEnumBulkStatNext(enump);
+
+    *entrypp = &enump->entry[enump->next];
+    if ( enump->next == enump->count ) {
+       osi_Log0(afsd_logp, "cm_BPlusDirPeekNextEnumEntry STOPNOW");
+       return CM_ERROR_STOPNOW;
+    }
+    else {
+       osi_Log0(afsd_logp, "cm_BPlusDirPeekNextEnumEntry SUCCESS");
+       return 0;
+    }
+}
+
 long 
 cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
 {
@@ -2310,6 +2507,10 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
     osi_Log0(afsd_logp, "cm_BPlusDirFreeEnumeration");
 
     if (enump) {
+        /* Release the directory object */
+        cm_ReleaseSCache(enump->dscp);
+        cm_ReleaseUser(enump->userp);
+
        for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) {
            free(enump->entry[count].name);
        }
@@ -2319,15 +2520,16 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump)
 }
 
 long
-cm_BPlusDirEnumTest(cm_scache_t * dscp, afs_uint32 locked)
+cm_BPlusDirEnumTest(cm_scache_t * dscp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked)
 {
     cm_direnum_t *      enump = NULL;
     cm_direnum_entry_t * entryp;
     long                code;
 
+
     osi_Log0(afsd_logp, "cm_BPlusDirEnumTest start");
 
-    for (code = cm_BPlusDirEnumerate(dscp, locked, NULL, &enump); code == 0; ) {
+    for (code = cm_BPlusDirEnumerate(dscp, userp, reqp, locked, NULL, 1, &enump); code == 0; ) {
        code = cm_BPlusDirNextEnumEntry(enump, &entryp);
        if (code == 0 || code == CM_ERROR_STOPNOW) {
            char buffer[1024];