diff --git a/fs-v1.go b/fs-v1.go index 9d4b87709..e66ad74ce 100644 --- a/fs-v1.go +++ b/fs-v1.go @@ -248,7 +248,7 @@ func (fs fsObjects) GetObject(bucket, object string, offset int64, length int64, } // Allocate a staging buffer. buf := make([]byte, int(bufSize)) - for totalLeft > 0 { + for { // Figure out the right size for the buffer. curLeft := bufSize if totalLeft < bufSize { @@ -282,6 +282,9 @@ func (fs fsObjects) GetObject(bucket, object string, offset int64, length int64, err = er break } + if totalLeft == 0 { + break + } } // Returns any error. return toObjectErr(err, bucket, object) diff --git a/xl-v1-object.go b/xl-v1-object.go index 06163bfa0..80fb98a5b 100644 --- a/xl-v1-object.go +++ b/xl-v1-object.go @@ -67,6 +67,11 @@ func (xl xlObjects) GetObject(bucket, object string, startOffset int64, length i return toObjectErr(errXLReadQuorum, bucket, object) } + // If all the disks returned error, we return error. + if err := reduceErrs(errs); err != nil { + return toObjectErr(err, bucket, object) + } + // List all online disks. onlineDisks, highestVersion, err := xl.listOnlineDisks(metaArr, errs) if err != nil { diff --git a/xl-v1-utils.go b/xl-v1-utils.go index 3221668e2..039d91193 100644 --- a/xl-v1-utils.go +++ b/xl-v1-utils.go @@ -23,6 +23,59 @@ import ( "path" ) +// Returns nil even if one of the slice elements is nil. +// Else returns the error which occours the most. +func reduceErrs(errs []error) error { + // In case the error type is not in the known error list. + var unknownErr = errors.New("unknown error") + var errTypes = []struct { + err error // error type + count int // occurance count + }{ + // List of known error types. Any new type that can be returned from StorageAPI should + // be added to this list. Most common errors are listed here. + {errDiskNotFound, 0}, {errFaultyDisk, 0}, {errFileAccessDenied, 0}, + {errFileNotFound, 0}, {errFileNameTooLong, 0}, {errVolumeNotFound, 0}, + {errDiskFull, 0}, + // unknownErr count - count of the number of unknown errors. + {unknownErr, 0}, + } + // In case unknownErr count occours maximum number of times, unknownErrType is used to + // to store it so that it can be used for the return error type. + var unknownErrType error + + // For each err in []errs increment the corresponding count value. + for _, err := range errs { + if err == nil { + // Return nil even if one of the elements is nil. + return nil + } + for i := range errTypes { + if errTypes[i].err == err { + errTypes[i].count++ + break + } + if errTypes[i].err == unknownErr { + errTypes[i].count++ + unknownErrType = err + break + } + } + } + max := 0 + // Get the error type which has the maximum count. + for i, errType := range errTypes { + if errType.count > errTypes[max].count { + max = i + } + } + if errTypes[max].err == unknownErr { + // Return the unknown error. + return unknownErrType + } + return errTypes[max].err +} + // Validates if we have quorum based on the errors with errDiskNotFound. func isQuorum(errs []error, minQuorumCount int) bool { var diskFoundCount int diff --git a/xl-v1-utils_test.go b/xl-v1-utils_test.go new file mode 100644 index 000000000..03a4378d2 --- /dev/null +++ b/xl-v1-utils_test.go @@ -0,0 +1,43 @@ +/* + * Minio Cloud Storage, (C) 2015, 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import "testing" +import "syscall" + +// Test for reduceErrs. +func TestReduceErrs(t *testing.T) { + testCases := []struct { + errs []error + err error + }{ + {[]error{errDiskNotFound, errDiskNotFound, errDiskFull}, errDiskNotFound}, + {[]error{errDiskNotFound, errDiskFull, errDiskFull}, errDiskFull}, + {[]error{errDiskFull, errDiskNotFound, errDiskFull}, errDiskFull}, + // A case for error not in the known errors list (refer to func reduceErrs) + {[]error{errDiskFull, syscall.EEXIST, syscall.EEXIST}, syscall.EEXIST}, + {[]error{errDiskNotFound, errDiskNotFound, nil}, nil}, + {[]error{nil, nil, nil}, nil}, + } + for i, testCase := range testCases { + expected := testCase.err + got := reduceErrs(testCase.errs) + if expected != got { + t.Errorf("Test %d : expected %s, got %s", i+1, expected, got) + } + } +}