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