github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/renter/proto/filesection_test.go (about)

     1  package proto
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"SiaPrime/build"
    12  	"gitlab.com/NebulousLabs/fastrand"
    13  )
    14  
    15  // SafeReadAt is a wrapper for ReadAt that recovers from a potential panic and
    16  // returns it as an error.
    17  func (f *fileSection) SafeReadAt(b []byte, off int64) (n int, err error) {
    18  	defer func() {
    19  		if r := recover(); r != nil {
    20  			err = fmt.Errorf("%v", r)
    21  		}
    22  	}()
    23  	return f.ReadAt(b, off)
    24  }
    25  
    26  // SafeTruncate is a wrapper for Truncate that recovers from a potential panic
    27  // and returns it as an error.
    28  func (f *fileSection) SafeTruncate(size int64) (err error) {
    29  	defer func() {
    30  		if r := recover(); r != nil {
    31  			err = fmt.Errorf("%v", r)
    32  		}
    33  	}()
    34  	return f.Truncate(size)
    35  }
    36  
    37  // SafeWriteAt is a wrapper for WriteAt that recovers from a potential panic
    38  // and returns it as an error.
    39  func (f *fileSection) SafeWriteAt(b []byte, off int64) (n int, err error) {
    40  	defer func() {
    41  		if r := recover(); r != nil {
    42  			err = fmt.Errorf("%v", r)
    43  		}
    44  	}()
    45  	return f.WriteAt(b, off)
    46  }
    47  
    48  // TestFileSectionBoundariesValidReadWrites uses valid read and write
    49  // operations on the fileSection to make sure that the data is written to and
    50  // read from the section correctly without corrupting other sections.
    51  func TestFileSectionBoundariesValidReadWrites(t *testing.T) {
    52  	if testing.Short() {
    53  		t.SkipNow()
    54  	}
    55  	testDir := build.TempDir(t.Name())
    56  	if err := os.MkdirAll(testDir, 0700); err != nil {
    57  		t.Fatal(err)
    58  	}
    59  	testFile, err := os.Create(filepath.Join(testDir, "testfile.dat"))
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  
    64  	// Create 3 sections.
    65  	s1Size := 100
    66  	s2Size := 100
    67  	s1 := newFileSection(testFile, 0, int64(s1Size))
    68  	s2 := newFileSection(testFile, int64(s1Size), int64(s1Size+s2Size))
    69  	s3 := newFileSection(testFile, int64(s1Size+s2Size), remainingFile)
    70  
    71  	// Write as much data to the sections as they can fit.
    72  	s1Data := fastrand.Bytes(s1Size)
    73  	s2Data := fastrand.Bytes(s2Size)
    74  	s3Data := fastrand.Bytes(s2Size) // s3 has an infinite size so we just write s2Size bytes.
    75  
    76  	n, err := s1.SafeWriteAt(s1Data, 0)
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	if n != len(s1Data) {
    81  		t.Fatalf("expected %v bytes to be written instead of %v", len(s1Data), n)
    82  	}
    83  	n, err = s2.SafeWriteAt(s2Data, 0)
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  	if n != len(s2Data) {
    88  		t.Fatalf("expected %v bytes to be written instead of %v", len(s2Data), n)
    89  	}
    90  	n, err = s3.SafeWriteAt(s3Data, 0)
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  	if n != len(s3Data) {
    95  		t.Fatalf("expected %v bytes to be written instead of %v", len(s3Data), n)
    96  	}
    97  
    98  	// Read the written data from the file and check if it matches.
    99  	readS1Data := make([]byte, len(s1Data))
   100  	readS2Data := make([]byte, len(s2Data))
   101  	readS3Data := make([]byte, len(s3Data))
   102  	_, err = s1.SafeReadAt(readS1Data, 0)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	_, err = s2.SafeReadAt(readS2Data, 0)
   107  	if err != nil {
   108  		t.Fatal(err)
   109  	}
   110  	_, err = s3.SafeReadAt(readS3Data, 0)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	fi, err := testFile.Stat()
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	size := fi.Size()
   119  	size1, err := s1.Size()
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	size2, err := s2.Size()
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	size3, err := s3.Size()
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	if size1 != int64(s1Size) {
   132  		t.Fatalf("expected size to be %v but was %v", s1Size, size1)
   133  	}
   134  	if size2 != int64(s2Size) {
   135  		t.Fatalf("expected size to be %v but was %v", s2Size, size2)
   136  	}
   137  	if size3 != int64(s2Size) {
   138  		t.Fatalf("expected size to be %v but was %v", s2Size, size3)
   139  	}
   140  	if size != size1+size2+size3 {
   141  		t.Fatalf("total size should be %v but was %v", size, size1+size2+size3)
   142  	}
   143  
   144  	if !bytes.Equal(s1Data, readS1Data) {
   145  		t.Fatal("the read data doesn't match the written data")
   146  	}
   147  	if !bytes.Equal(s2Data, readS2Data) {
   148  		t.Fatal("the read data doesn't match the written data")
   149  	}
   150  	if !bytes.Equal(s3Data, readS3Data) {
   151  		t.Fatal("the read data doesn't match the written data")
   152  	}
   153  
   154  	// Read the written data directly from the underlying file and check again.
   155  	_, err = testFile.ReadAt(readS1Data, 0)
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  	_, err = testFile.ReadAt(readS2Data, int64(s1Size))
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	_, err = testFile.ReadAt(readS3Data, int64(s1Size+s2Size))
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	if !bytes.Equal(s1Data, readS1Data) {
   168  		t.Fatal("the read data doesn't match the written data")
   169  	}
   170  	if !bytes.Equal(s2Data, readS2Data) {
   171  		t.Fatal("the read data doesn't match the written data")
   172  	}
   173  	if !bytes.Equal(s3Data, readS3Data) {
   174  		t.Fatal("the read data doesn't match the written data")
   175  	}
   176  }
   177  
   178  // TestFileSectionBoundariesInvalidReadWrites tries a variation of invalid read
   179  // and write operations on the section to make sure the caller can't write to
   180  // neighboring sections by accident.
   181  func TestFileSectionBoundariesInvalidReadWrites(t *testing.T) {
   182  	if testing.Short() {
   183  		t.SkipNow()
   184  	}
   185  	testDir := build.TempDir(t.Name())
   186  	if err := os.MkdirAll(testDir, 0700); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	testFile, err := os.Create(filepath.Join(testDir, "testfile.dat"))
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	// Create 3 sections.
   195  	s1Size := 100
   196  	s2Size := 100
   197  	s1 := newFileSection(testFile, 0, int64(s1Size))
   198  	s2 := newFileSection(testFile, int64(s1Size), int64(s1Size+s2Size))
   199  
   200  	// Fill the file with some random data
   201  	randomData := fastrand.Bytes(s1Size + s2Size + 100)
   202  	if _, err := testFile.WriteAt(randomData, 0); err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	// Create some random data for the following calls to write. That data
   206  	// should never be written since all the calls should fail.
   207  	data := fastrand.Bytes(1)
   208  
   209  	// Try a number of invalid read and write operations. They should all fail.
   210  	if _, err := s1.SafeWriteAt(data, int64(s1Size)); err == nil {
   211  		t.Fatal("sector shouldn't be able to write data beyond its end boundary")
   212  	}
   213  	if _, err := s2.SafeWriteAt(data, -1); err == nil {
   214  		t.Fatal("sector shouldn't be able to write data below its start boundary")
   215  	}
   216  	if _, err := s1.SafeReadAt(data, int64(s1Size)); err == nil {
   217  		t.Fatal("sector shouldn't be able to read data beyond its end boundary")
   218  	}
   219  	if _, err := s2.SafeReadAt(data, -1); err == nil {
   220  		t.Fatal("sector shouldn't be able to read data below its start boundary")
   221  	}
   222  
   223  	// The file should still have the same random data from the beginning.
   224  	fileData, err := ioutil.ReadAll(testFile)
   225  	if err != nil {
   226  		t.Fatal(err)
   227  	}
   228  	if !bytes.Equal(fileData, randomData) {
   229  		t.Fatal("file data doesn't match the initial data")
   230  	}
   231  }
   232  
   233  // TestFileSectionTruncate checks if file sections without an open end boundary
   234  // can be truncated and makes sure that the last section can't truncate the
   235  // file below its boundary.
   236  func TestFileSectionTruncate(t *testing.T) {
   237  	if testing.Short() {
   238  		t.SkipNow()
   239  	}
   240  	testDir := build.TempDir(t.Name())
   241  	if err := os.MkdirAll(testDir, 0700); err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	testFile, err := os.Create(filepath.Join(testDir, "testfile.dat"))
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	// Create 3 sections.
   250  	s1Size := 100
   251  	s2Size := 100
   252  	s1 := newFileSection(testFile, 0, int64(s1Size))
   253  	s2 := newFileSection(testFile, int64(s1Size), int64(s1Size+s2Size))
   254  	s3 := newFileSection(testFile, int64(s1Size+s2Size), remainingFile)
   255  
   256  	// Write as much data to the sections as they can fit.
   257  	s1Data := fastrand.Bytes(s1Size)
   258  	s2Data := fastrand.Bytes(s2Size)
   259  	s3Data := fastrand.Bytes(s2Size) // s3 has an infinite size so we just write s2Size bytes.
   260  
   261  	n, err := s1.SafeWriteAt(s1Data, 0)
   262  	if err != nil {
   263  		t.Fatal(err)
   264  	}
   265  	if n != len(s1Data) {
   266  		t.Fatalf("expected %v bytes to be written instead of %v", len(s1Data), n)
   267  	}
   268  	n, err = s2.SafeWriteAt(s2Data, 0)
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	if n != len(s2Data) {
   273  		t.Fatalf("expected %v bytes to be written instead of %v", len(s2Data), n)
   274  	}
   275  	n, err = s3.SafeWriteAt(s3Data, 0)
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  	if n != len(s3Data) {
   280  		t.Fatalf("expected %v bytes to be written instead of %v", len(s3Data), n)
   281  	}
   282  
   283  	// Try to truncate s1 and s2. That shouldn't be possible.
   284  	if err := s1.SafeTruncate(int64(fastrand.Intn(s1Size + 1))); err == nil {
   285  		t.Fatal("it shouldn't be possible to truncate a section with a fixed end boundary.")
   286  	}
   287  	if err := s2.SafeTruncate(int64(fastrand.Intn(s2Size + 1))); err == nil {
   288  		t.Fatal("it shouldn't be possible to truncate a section with a fixed end boundary.")
   289  	}
   290  
   291  	// Try to truncate s3 to size 0. This should be possible and also reduce
   292  	// the total file size.
   293  	if err := s3.SafeTruncate(0); err != nil {
   294  		t.Fatal("failed to truncate s3", err)
   295  	}
   296  	fi, err := testFile.Stat()
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  	if fi.Size() != int64(s1Size+s2Size) {
   301  		t.Fatalf("expected size after truncate is %v but was %v", s1Size+s2Size, fi.Size())
   302  	}
   303  	size, err := s3.Size()
   304  	if err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	if size != 0 {
   308  		t.Fatalf("size was %v but should be %v", size, 0)
   309  	}
   310  	// Try to truncate s3 below its start. That shouldn't be possible.
   311  	if err := s3.SafeTruncate(-1); err == nil {
   312  		t.Fatal("it shouldn't be possible to truncate a section to a negative size")
   313  	}
   314  }