afs: free the Buffers array correctly during shutdown
[openafs.git] / src / afs / afs_buffer.c
index 68cd146..c7a394d 100644 (file)
@@ -14,7 +14,9 @@
 #include "afs/sysincludes.h"
 #include "afsincludes.h"
 #if !defined(UKERNEL)
-#include "h/param.h"
+#if !defined(AFS_LINUX26_ENV)
+# include "h/param.h"
+#endif
 #include "h/types.h"
 #include "h/time.h"
 #if    defined(AFS_AIX31_ENV)
@@ -145,8 +147,22 @@ DInit(int abuffers)
     return;
 }
 
+/*!
+ * Read and return the requested directory page.
+ *
+ * \param[in]   adc    pointer to directory dcache
+ * \param[in]  page    number of the desired directory page
+ * \param[out] entry   buffer to return requested page
+ * \param[out] physerr (optional) pointer to return errno, if any
+ *
+ * \retval 0       success
+ * \retval non-zero invalid directory or internal IO error;
+ *                 if physerr is supplied by caller, it will be set:
+ *                     0       logical error
+ *                     errno   physical error
+ */
 int
-DRead(struct dcache *adc, int page, struct DirBuffer *entry)
+DReadWithErrno(struct dcache *adc, int page, struct DirBuffer *entry, int *physerr)
 {
     /* Read a page from the disk. */
     struct buffer *tb, *tb2;
@@ -155,8 +171,20 @@ DRead(struct dcache *adc, int page, struct DirBuffer *entry)
 
     AFS_STATCNT(DRead);
 
+    if (physerr != NULL)
+       *physerr = 0;
+
     memset(entry, 0, sizeof(struct DirBuffer));
 
+    if (adc->f.chunk == 0 && adc->f.chunkBytes == 0) {
+        /* The directory blob is empty, apparently. This is not a valid dir
+         * blob, so throw an error. */
+        return EIO;
+    }
+    if (page * AFS_BUFFER_PAGESIZE >= adc->f.chunkBytes) {
+        return ENOENT; /* past the end */
+    }
+
     ObtainWriteLock(&afs_bufferLock, 256);
 
 #define bufmatch(tb) (tb->page == page && tb->fid == adc->index)
@@ -206,6 +234,7 @@ DRead(struct dcache *adc, int page, struct DirBuffer *entry)
                        ReleaseWriteLock(&tb->lock);
                        entry->buffer = tb;
                        entry->data = tb->data;
+                       return 0;
                    }
                } else
                    break;
@@ -228,24 +257,20 @@ DRead(struct dcache *adc, int page, struct DirBuffer *entry)
     ObtainWriteLock(&tb->lock, 260);
     tb->lockers++;
     ReleaseWriteLock(&afs_bufferLock);
-    if (page * AFS_BUFFER_PAGESIZE >= adc->f.chunkBytes) {
-       tb->fid = NULLIDX;
-       afs_reset_inode(&tb->inode);
-       tb->lockers--;
-       ReleaseWriteLock(&tb->lock);
-       return EIO;
-    }
     tfile = afs_CFileOpen(&adc->f.inode);
+    if (!tfile) {
+       code = EIO;
+       goto error;
+    }
     code =
        afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE, tb->data,
                      AFS_BUFFER_PAGESIZE);
     afs_CFileClose(tfile);
     if (code < AFS_BUFFER_PAGESIZE) {
-       tb->fid = NULLIDX;
-       afs_reset_inode(&tb->inode);
-       tb->lockers--;
-       ReleaseWriteLock(&tb->lock);
-       return EIO;
+       if (code < 0 && physerr != NULL)
+          *physerr = -code;
+       code = EIO;
+       goto error;
     }
     /* Note that findslot sets the page field in the buffer equal to
      * what it is searching for. */
@@ -253,6 +278,29 @@ DRead(struct dcache *adc, int page, struct DirBuffer *entry)
     entry->buffer = tb;
     entry->data = tb->data;
     return 0;
+
+ error:
+    tb->fid = NULLIDX;
+    afs_reset_inode(&tb->inode);
+    tb->lockers--;
+    ReleaseWriteLock(&tb->lock);
+    return code;
+}
+
+/*!
+ * Read and return the requested directory page.
+ *
+ * \param[in]   adc    pointer to directory dcache
+ * \param[in]  page    number of the desired directory page
+ * \param[out] entry   buffer to return requested page
+ *
+ * \retval 0       success
+ * \retval non-zero invalid directory or internal IO error;
+ */
+int
+DRead(struct dcache *adc, int page, struct DirBuffer *entry)
+{
+    return DReadWithErrno(adc, page, entry, NULL);
 }
 
 static void
@@ -369,6 +417,9 @@ afs_newslot(struct dcache *adc, afs_int32 apage, struct buffer *lp)
     if (lp->dirty) {
        /* see DFlush for rationale for not getting and locking the dcache */
         tfile = afs_CFileOpen(&lp->inode);
+       if (!tfile)
+           return NULL;    /* Callers will flag as EIO */
+
        afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, lp->data,
                       AFS_BUFFER_PAGESIZE);
        lp->dirty = 0;
@@ -376,6 +427,8 @@ afs_newslot(struct dcache *adc, afs_int32 apage, struct buffer *lp)
        AFS_STATS(afs_stats_cmperf.bufFlushDirty++);
     }
 
+    /* Zero out the data so we don't leak something we shouldn't. */
+    memset(lp->data, 0, AFS_BUFFER_PAGESIZE);
     /* Now fill in the header. */
     lp->fid = adc->index;
     afs_copy_inode(&lp->inode, &adc->f.inode);
@@ -397,7 +450,6 @@ DRelease(struct DirBuffer *entry, int flag)
     if (tp == NULL)
        return;
 
-    tp = entry->buffer;
     ObtainWriteLock(&tp->lock, 261);
     tp->lockers--;
     if (flag)
@@ -456,6 +508,7 @@ DFlushBuffer(struct buffer *ab)
     struct osi_file *tfile;
 
     tfile = afs_CFileOpen(&ab->inode);
+    osi_Assert(tfile);
     afs_CFileWrite(tfile, ab->page * AFS_BUFFER_PAGESIZE,
                   ab->data, AFS_BUFFER_PAGESIZE);
     ab->dirty = 0;     /* Clear the dirty flag */
@@ -487,7 +540,7 @@ DFlushDCache(struct dcache *adc)
     ReleaseReadLock(&afs_bufferLock);
 }
 
-void
+int
 DFlush(void)
 {
     /* Flush all the modified buffers. */
@@ -522,14 +575,28 @@ DFlush(void)
        }
     }
     ReleaseReadLock(&afs_bufferLock);
+
+    return 0;
 }
 
+/*!
+ * Prepare a new directory page buffer
+ *
+ * \param adc      pointer to the directory object dcache
+ * \param nblobs    page we want
+ * \param entry            buffer to return requested page
+ *
+ * \retval 0       success; entry is updated
+ * \retval non-zero internal error or IO error writing to disk
+ */
 int
 DNew(struct dcache *adc, int page, struct DirBuffer *entry)
 {
     /* Same as read, only do *not* even try to read the page, since it
      * probably doesn't exist. */
     struct buffer *tb;
+    int code;
+
     AFS_STATCNT(DNew);
 
     ObtainWriteLock(&afs_bufferLock, 264);
@@ -544,7 +611,11 @@ DNew(struct dcache *adc, int page, struct DirBuffer *entry)
      * DFlush due to lock hierarchy issues */
     if ((page + 1) * AFS_BUFFER_PAGESIZE > adc->f.chunkBytes) {
        afs_AdjustSize(adc, (page + 1) * AFS_BUFFER_PAGESIZE);
-       afs_WriteDCache(adc, 1);
+       code = afs_WriteDCache(adc, 1);
+       if (code) {
+           ReleaseWriteLock(&afs_bufferLock);
+           return code;
+       }
     }
     ObtainWriteLock(&tb->lock, 265);
     tb->lockers++;
@@ -565,17 +636,20 @@ shutdown_bufferpackage(void)
     AFS_STATCNT(shutdown_bufferpackage);
     /* Free all allocated Buffers and associated buffer pages */
     DFlush();
+
+    dinit_flag = 0;
+    tp = Buffers;
+    for (i = 0; i < nbuffers; i += NPB, tp += NPB) {
+       afs_osi_Free(tp->data, NPB * AFS_BUFFER_PAGESIZE);
+    }
+    afs_osi_Free(Buffers, afs_max_buffers * sizeof(struct buffer));
+    Buffers = NULL;
+    nbuffers = 0;
+    timecounter = 1;
+    for (i = 0; i < PHSIZE; i++)
+       phTable[i] = NULL;
+
     if (afs_cold_shutdown) {
-       dinit_flag = 0;
-       tp = Buffers;
-       for (i = 0; i < nbuffers; i += NPB, tp += NPB) {
-           afs_osi_Free(tp->data, NPB * AFS_BUFFER_PAGESIZE);
-       }
-       afs_osi_Free(Buffers, nbuffers * sizeof(struct buffer));
-       nbuffers = 0;
-       timecounter = 1;
-       for (i = 0; i < PHSIZE; i++)
-           phTable[i] = 0;
        memset(&afs_bufferLock, 0, sizeof(afs_lock_t));
     }
 }