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 }