mirror of
https://github.com/minio/minio.git
synced 2025-11-09 21:49:46 -05:00
Add new ReadFileWithVerify storage-layer API (#4349)
This is an enhancement to the XL/distributed-XL mode. FS mode is unaffected. The ReadFileWithVerify storage-layer call is similar to ReadFile with the additional functionality of performing bit-rot checking. It accepts additional parameters for a hashing algorithm to use and the expected hex-encoded hash string. This patch provides significant performance improvement because: 1. combines the step of reading the file (during erasure-decoding/reconstruction) with bit-rot verification; 2. limits the number of file-reads; and 3. avoids transferring the file over the network for bit-rot verification. ReadFile API is implemented as ReadFileWithVerify with empty hashing arguments. Credits to AB and Harsha for the algorithmic improvement. Fixes #4236.
This commit is contained in:
committed by
Harshavardhana
parent
cae4683971
commit
8975da4e84
@@ -18,6 +18,8 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -26,6 +28,8 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
// creates a temp dir and sets up posix layer.
|
||||
@@ -1017,6 +1021,115 @@ func TestPosixReadFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestPosixReadFileWithVerify - tests the posix level
|
||||
// ReadFileWithVerify API. Only tests hashing related
|
||||
// functionality. Other functionality is tested with
|
||||
// TestPosixReadFile.
|
||||
func TestPosixReadFileWithVerify(t *testing.T) {
|
||||
// create posix test setup
|
||||
posixStorage, path, err := newPosixTestSetup()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create posix test setup, %s", err)
|
||||
}
|
||||
defer removeAll(path)
|
||||
|
||||
volume := "success-vol"
|
||||
// Setup test environment.
|
||||
if err = posixStorage.MakeVol(volume); err != nil {
|
||||
t.Fatalf("Unable to create volume, %s", err)
|
||||
}
|
||||
|
||||
blakeHash := func(s string) string {
|
||||
k := blake2b.Sum512([]byte(s))
|
||||
return hex.EncodeToString(k[:])
|
||||
}
|
||||
|
||||
sha256Hash := func(s string) string {
|
||||
k := sha256.Sum256([]byte(s))
|
||||
return hex.EncodeToString(k[:])
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
fileName string
|
||||
offset int64
|
||||
bufSize int
|
||||
algo HashAlgo
|
||||
expectedHash string
|
||||
|
||||
expectedBuf []byte
|
||||
expectedErr error
|
||||
}{
|
||||
// Hash verification is skipped with empty expected
|
||||
// hash - 1
|
||||
{
|
||||
"myobject", 0, 5, HashBlake2b, "",
|
||||
[]byte("Hello"), nil,
|
||||
},
|
||||
// Hash verification failure case - 2
|
||||
{
|
||||
"myobject", 0, 5, HashBlake2b, "a",
|
||||
[]byte(""),
|
||||
hashMismatchError{"a", blakeHash("Hello, world!")},
|
||||
},
|
||||
// Hash verification success with full content requested - 3
|
||||
{
|
||||
"myobject", 0, 13, HashBlake2b, blakeHash("Hello, world!"),
|
||||
[]byte("Hello, world!"), nil,
|
||||
},
|
||||
// Hash verification success with full content and Sha256 - 4
|
||||
{
|
||||
"myobject", 0, 13, HashSha256, sha256Hash("Hello, world!"),
|
||||
[]byte("Hello, world!"), nil,
|
||||
},
|
||||
// Hash verification success with partial content requested - 5
|
||||
{
|
||||
"myobject", 7, 4, HashBlake2b, blakeHash("Hello, world!"),
|
||||
[]byte("worl"), nil,
|
||||
},
|
||||
// Hash verification success with partial content and Sha256 - 6
|
||||
{
|
||||
"myobject", 7, 4, HashSha256, sha256Hash("Hello, world!"),
|
||||
[]byte("worl"), nil,
|
||||
},
|
||||
// Empty hash-algo returns error - 7
|
||||
{
|
||||
"myobject", 7, 4, "", blakeHash("Hello, world!"),
|
||||
[]byte("worl"), errBitrotHashAlgoInvalid,
|
||||
},
|
||||
// Empty content hash verification with empty
|
||||
// hash-algo algo returns error - 8
|
||||
{
|
||||
"myobject", 7, 0, "", blakeHash("Hello, world!"),
|
||||
[]byte(""), errBitrotHashAlgoInvalid,
|
||||
},
|
||||
}
|
||||
|
||||
// Create file used in testcases
|
||||
err = posixStorage.AppendFile(volume, "myobject", []byte("Hello, world!"))
|
||||
if err != nil {
|
||||
t.Fatalf("Failure in test setup: %v\n", err)
|
||||
}
|
||||
|
||||
// Validate each test case.
|
||||
for i, testCase := range testCases {
|
||||
var n int64
|
||||
// Common read buffer.
|
||||
var buf = make([]byte, testCase.bufSize)
|
||||
n, err = posixStorage.ReadFileWithVerify(volume, testCase.fileName, testCase.offset, buf, testCase.algo, testCase.expectedHash)
|
||||
|
||||
switch {
|
||||
case err == nil && testCase.expectedErr != nil:
|
||||
t.Errorf("Test %d: Expected error %v but got none.", i+1, testCase.expectedErr)
|
||||
case err == nil && n != int64(testCase.bufSize):
|
||||
t.Errorf("Test %d: %d bytes were expected, but %d were written", i+1, testCase.bufSize, n)
|
||||
case err == nil && !bytes.Equal(testCase.expectedBuf, buf):
|
||||
t.Errorf("Test %d: Expected bytes: %v, but got: %v", i+1, testCase.expectedBuf, buf)
|
||||
case err != nil && err != testCase.expectedErr:
|
||||
t.Errorf("Test %d: Expected error: %v, but got: %v", i+1, testCase.expectedErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPosix posix.AppendFile()
|
||||
func TestPosixAppendFile(t *testing.T) {
|
||||
// create posix test setup
|
||||
|
||||
Reference in New Issue
Block a user