afs: Log weird 'size' fetchdata errors
[openafs.git] / src / afs / afs_fetchstore.c
index 35c357a..f65f40c 100644 (file)
@@ -240,18 +240,15 @@ rxfs_storeClose(void *r, struct AFSFetchStatus *OutStatus, int *doProcessFS)
 }
 
 afs_int32
-rxfs_storeDestroy(void **r, afs_int32 error)
+rxfs_storeDestroy(void **r, afs_int32 code)
 {
-    afs_int32 code = error;
     struct rxfs_storeVariables *v = (struct rxfs_storeVariables *)*r;
 
     *r = NULL;
     if (v->call) {
        RX_AFS_GUNLOCK();
-       code = rx_EndCall(v->call, error);
+       code = rx_EndCall(v->call, code);
        RX_AFS_GLOCK();
-       if (!code && error)
-           code = error;
     }
     if (v->tbuffer)
        osi_FreeLargeSpace(v->tbuffer);
@@ -811,7 +808,7 @@ afs_int32
 rxfs_fetchClose(void *r, struct vcache *avc, struct dcache * adc,
                struct afs_FetchOutput *o)
 {
-    afs_int32 code, code1 = 0;
+    afs_int32 code;
     struct rxfs_fetchVariables *v = (struct rxfs_fetchVariables *)r;
 
     if (!v->call)
@@ -826,10 +823,8 @@ rxfs_fetchClose(void *r, struct vcache *avc, struct dcache * adc,
 #endif
         code = EndRXAFS_FetchData(v->call, &o->OutStatus, &o->CallBack,
                                &o->tsync);
-    code1 = rx_EndCall(v->call, code);
+    code = rx_EndCall(v->call, code);
     RX_AFS_GLOCK();
-    if (!code && code1)
-       code = code1;
 
     v->call = NULL;
 
@@ -837,18 +832,15 @@ rxfs_fetchClose(void *r, struct vcache *avc, struct dcache * adc,
 }
 
 afs_int32
-rxfs_fetchDestroy(void **r, afs_int32 error)
+rxfs_fetchDestroy(void **r, afs_int32 code)
 {
-    afs_int32 code = error;
     struct rxfs_fetchVariables *v = (struct rxfs_fetchVariables *)*r;
 
     *r = NULL;
     if (v->call) {
         RX_AFS_GUNLOCK();
-       code = rx_EndCall(v->call, error);
+       code = rx_EndCall(v->call, code);
         RX_AFS_GLOCK();
-       if (error)
-           code = error;
     }
     if (v->tbuffer)
        osi_FreeLargeSpace(v->tbuffer);
@@ -914,11 +906,11 @@ rxfs_fetchInit(struct afs_conn *tc, struct rx_connection *rxconn,
               struct osi_file *fP, struct fetchOps **ops, void **rock)
 {
     struct rxfs_fetchVariables *v;
-    int code = 0, code1 = 0;
+    int code = 0;
 #ifdef AFS_64BIT_CLIENT
     afs_uint32 length_hi = 0;
 #endif
-    afs_uint32 length, bytes;
+    afs_uint32 length = 0, bytes;
 
     v = (struct rxfs_fetchVariables *)
            osi_AllocSmallSpace(sizeof(struct rxfs_fetchVariables));
@@ -948,9 +940,8 @@ rxfs_fetchInit(struct afs_conn *tc, struct rx_connection *rxconn,
                if (bytes == sizeof(afs_int32)) {
                    length_hi = ntohl(length_hi);
                } else {
-                   code = rx_Error(v->call);
                    RX_AFS_GUNLOCK();
-                   code1 = rx_EndCall(v->call, code);
+                   code = rx_EndCall(v->call, RX_PROTOCOL_ERROR);
                    RX_AFS_GLOCK();
                    v->call = NULL;
                }
@@ -982,18 +973,55 @@ rxfs_fetchInit(struct afs_conn *tc, struct rx_connection *rxconn,
                length = ntohl(length);
            else {
                RX_AFS_GUNLOCK();
-               code = rx_Error(v->call);
-                code1 = rx_EndCall(v->call, code);
+                code = rx_EndCall(v->call, RX_PROTOCOL_ERROR);
                v->call = NULL;
+               length = 0;
                RX_AFS_GLOCK();
            }
        }
        FillInt64(length64, length_hi, length);
+
+        if (!code) {
+            /* Check if the fileserver said our length is bigger than can fit
+             * in a signed 32-bit integer. If it is, we can't handle that, so
+             * error out. */
+           if (length64 > MAX_AFS_INT32) {
+                static int warned;
+                if (!warned) {
+                    warned = 1;
+                    afs_warn("afs: Warning: FetchData64 returned too much data "
+                             "(length64 %u.%u); this should not happen! "
+                             "Aborting fetch request.\n",
+                             length_hi, length);
+                }
+               RX_AFS_GUNLOCK();
+                code = rx_EndCall(v->call, RX_PROTOCOL_ERROR);
+               v->call = NULL;
+               length = 0;
+               RX_AFS_GLOCK();
+                code = code != 0 ? code : EIO;
+            }
+        }
+
+        if (!code) {
+            /* Check if the fileserver said our length was negative. If it
+             * is, just treat it as a 0 length, since some older fileservers
+             * returned negative numbers when they meant to return 0. Note
+             * that we must do this in this 64-bit-specific block, since
+             * length64 being negative will screw up our conversion to the
+             * 32-bit 'alength' below. */
+            if (length64 < 0) {
+                length_hi = length = 0;
+                FillInt64(length64, 0, 0);
+            }
+        }
+
        afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64LENG,
                   ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
                   ICL_TYPE_OFFSET,
                   ICL_HANDLE_OFFSET(length64));
-       *alength = length;
+       if (!code)
+           *alength = length;
 #else /* AFS_64BIT_CLIENT */
        RX_AFS_GUNLOCK();
        code = StartRXAFS_FetchData(v->call, (struct AFSFid *)&avc->f.fid.Fid,
@@ -1006,9 +1034,14 @@ rxfs_fetchInit(struct afs_conn *tc, struct rx_connection *rxconn,
            RX_AFS_GLOCK();
            if (bytes == sizeof(afs_int32)) {
                 *alength = ntohl(length);
+                if (*alength < 0) {
+                    /* Older fileservers can return a negative length when they
+                     * meant to return 0; just assume negative lengths were
+                     * meant to be 0 lengths. */
+                    *alength = 0;
+                }
            } else {
-               code = rx_Error(v->call);
-                code1 = rx_EndCall(v->call, code);
+                code = rx_EndCall(v->call, RX_PROTOCOL_ERROR);
                v->call = NULL;
            }
        }
@@ -1024,17 +1057,21 @@ rxfs_fetchInit(struct afs_conn *tc, struct rx_connection *rxconn,
         * requested. It shouldn't do that, and accepting that much data
         * can make us take up more cache space than we're supposed to,
         * so error. */
-       code = rx_Error(v->call);
+        static int warned;
+        if (!warned) {
+            warned = 1;
+            afs_warn("afs: Warning: FetchData64 returned more data than "
+                     "requested (requested %ld, got %ld); this should not "
+                     "happen! Aborting fetch request.\n",
+                     (long)size, (long)*alength);
+        }
        RX_AFS_GUNLOCK();
-       code1 = rx_EndCall(v->call, code);
+       code = rx_EndCall(v->call, RX_PROTOCOL_ERROR);
        RX_AFS_GLOCK();
        v->call = NULL;
        code = EIO;
     }
 
-    if (!code && code1)
-       code = code1;
-
     if (code) {
        osi_FreeSmallSpace(v);
         return code;
@@ -1166,7 +1203,7 @@ afs_CacheFetchProc(struct afs_conn *tc, struct rx_connection *rxconn,
     if (!code)
        code = (*ops->close)(rock, avc, adc, tsmall);
     if (ops)
-       (*ops->destroy)(&rock, code);
+       code = (*ops->destroy)(&rock, code);
 
 #ifndef AFS_NOSTATS
     FillStoreStats(code, AFS_STATS_FS_XFERIDX_FETCHDATA, xferStartTime,