storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/xl-storage_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/rand" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "os" 27 slashpath "path" 28 "runtime" 29 "strings" 30 "syscall" 31 "testing" 32 33 "storj.io/minio/cmd/config/storageclass" 34 ) 35 36 func TestCheckPathLength(t *testing.T) { 37 // Check path length restrictions are not same on windows/darwin 38 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 39 t.Skip() 40 } 41 42 testCases := []struct { 43 path string 44 expectedErr error 45 }{ 46 {".", errFileAccessDenied}, 47 {"/", errFileAccessDenied}, 48 {"..", errFileAccessDenied}, 49 {"data/G_792/srv-tse/c/users/denis/documents/gestion!20locative/heritier/propri!E9taire/20190101_a2.03!20-!20m.!20heritier!20re!B4mi!20-!20proce!60s-verbal!20de!20livraison!20et!20de!20remise!20des!20cle!B4s!20acque!B4reurs!20-!204-!20livraison!20-!20lp!20promotion!20toulouse!20-!20encre!20et!20plume!20-!205!20de!B4c.!202019!20a!60!2012-49.pdf.ecc", errFileNameTooLong}, 50 {"data/G_792/srv-tse/c/users/denis/documents/gestionlocative.txt", nil}, 51 } 52 53 for _, testCase := range testCases { 54 gotErr := checkPathLength(testCase.path) 55 t.Run("", func(t *testing.T) { 56 if gotErr != testCase.expectedErr { 57 t.Errorf("Expected %s, got %s", testCase.expectedErr, gotErr) 58 } 59 }) 60 } 61 } 62 63 // Tests validate volume name. 64 func TestIsValidVolname(t *testing.T) { 65 testCases := []struct { 66 volName string 67 shouldPass bool 68 }{ 69 // Cases which should pass the test. 70 // passing in valid bucket names. 71 {"lol", true}, 72 {"1-this-is-valid", true}, 73 {"1-this-too-is-valid-1", true}, 74 {"this.works.too.1", true}, 75 {"1234567", true}, 76 {"123", true}, 77 {"s3-eu-west-1.amazonaws.com", true}, 78 {"ideas-are-more-powerful-than-guns", true}, 79 {"testbucket", true}, 80 {"1bucket", true}, 81 {"bucket1", true}, 82 {"$this-is-not-valid-too", true}, 83 {"contains-$-dollar", true}, 84 {"contains-^-carrot", true}, 85 {"contains-$-dollar", true}, 86 {"contains-$-dollar", true}, 87 {".starts-with-a-dot", true}, 88 {"ends-with-a-dot.", true}, 89 {"ends-with-a-dash-", true}, 90 {"-starts-with-a-dash", true}, 91 {"THIS-BEINGS-WITH-UPPERCASe", true}, 92 {"tHIS-ENDS-WITH-UPPERCASE", true}, 93 {"ThisBeginsAndEndsWithUpperCase", true}, 94 {"una ñina", true}, 95 {"lalalallalallalalalallalallalala-theString-size-is-greater-than-64", true}, 96 // cases for which test should fail. 97 // passing invalid bucket names. 98 {"", false}, 99 {SlashSeparator, false}, 100 {"a", false}, 101 {"ab", false}, 102 {"ab/", true}, 103 {"......", true}, 104 } 105 106 for i, testCase := range testCases { 107 isValidVolname := isValidVolname(testCase.volName) 108 if testCase.shouldPass && !isValidVolname { 109 t.Errorf("Test case %d: Expected \"%s\" to be a valid bucket name", i+1, testCase.volName) 110 } 111 if !testCase.shouldPass && isValidVolname { 112 t.Errorf("Test case %d: Expected bucket name \"%s\" to be invalid", i+1, testCase.volName) 113 } 114 } 115 } 116 117 // creates a temp dir and sets up xlStorage layer. 118 // returns xlStorage layer, temp dir path to be used for the purpose of tests. 119 func newXLStorageTestSetup() (*xlStorageDiskIDCheck, string, error) { 120 diskPath, err := ioutil.TempDir(globalTestTmpDir, "minio-") 121 if err != nil { 122 return nil, "", err 123 } 124 125 // Initialize a new xlStorage layer. 126 storage, err := newLocalXLStorage(diskPath) 127 if err != nil { 128 return nil, "", err 129 } 130 // Create a sample format.json file 131 err = storage.WriteAll(context.Background(), minioMetaBucket, formatConfigFile, []byte(`{"version":"1","format":"xl","id":"592a41c2-b7cc-4130-b883-c4b5cb15965b","xl":{"version":"3","this":"da017d62-70e3-45f1-8a1a-587707e69ad1","sets":[["e07285a6-8c73-4962-89c6-047fb939f803","33b8d431-482d-4376-b63c-626d229f0a29","cff6513a-4439-4dc1-bcaa-56c9e880c352","da017d62-70e3-45f1-8a1a-587707e69ad1","9c9f21d5-1f15-4737-bce6-835faa0d9626","0a59b346-1424-4fc2-9fa2-a2e80541d0c1","7924a3dc-b69a-4971-9a2e-014966d6aebb","4d2b8dd9-4e48-444b-bdca-c89194b26042"]],"distributionAlgo":"CRCMOD"}}`)) 132 if err != nil { 133 return nil, "", err 134 } 135 disk := newXLStorageDiskIDCheck(storage) 136 disk.diskID = "da017d62-70e3-45f1-8a1a-587707e69ad1" 137 return disk, diskPath, nil 138 } 139 140 // createPermDeniedFile - creates temporary directory and file with path '/mybucket/myobject' 141 func createPermDeniedFile(t *testing.T) (permDeniedDir string) { 142 var errMsg string 143 144 defer func() { 145 if errMsg == "" { 146 return 147 } 148 149 if permDeniedDir != "" { 150 os.RemoveAll(permDeniedDir) 151 } 152 153 t.Fatalf(errMsg) 154 }() 155 156 var err error 157 if permDeniedDir, err = ioutil.TempDir(globalTestTmpDir, "minio-"); err != nil { 158 errMsg = fmt.Sprintf("Unable to create temporary directory. %v", err) 159 return permDeniedDir 160 } 161 162 if err = os.Mkdir(slashpath.Join(permDeniedDir, "mybucket"), 0775); err != nil { 163 errMsg = fmt.Sprintf("Unable to create temporary directory %v. %v", slashpath.Join(permDeniedDir, "mybucket"), err) 164 return permDeniedDir 165 } 166 167 if err = ioutil.WriteFile(slashpath.Join(permDeniedDir, "mybucket", "myobject"), []byte(""), 0400); err != nil { 168 errMsg = fmt.Sprintf("Unable to create file %v. %v", slashpath.Join(permDeniedDir, "mybucket", "myobject"), err) 169 return permDeniedDir 170 } 171 172 if err = os.Chmod(slashpath.Join(permDeniedDir, "mybucket"), 0400); err != nil { 173 errMsg = fmt.Sprintf("Unable to change permission to temporary directory %v. %v", slashpath.Join(permDeniedDir, "mybucket"), err) 174 return permDeniedDir 175 } 176 177 if err = os.Chmod(permDeniedDir, 0400); err != nil { 178 errMsg = fmt.Sprintf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 179 } 180 181 return permDeniedDir 182 } 183 184 // removePermDeniedFile - removes temporary directory and file with path '/mybucket/myobject' 185 func removePermDeniedFile(permDeniedDir string) { 186 if err := os.Chmod(permDeniedDir, 0775); err == nil { 187 if err = os.Chmod(slashpath.Join(permDeniedDir, "mybucket"), 0775); err == nil { 188 os.RemoveAll(permDeniedDir) 189 } 190 } 191 } 192 193 // TestXLStorages xlStorage.getDiskInfo() 194 func TestXLStorageGetDiskInfo(t *testing.T) { 195 path, err := ioutil.TempDir(globalTestTmpDir, "minio-") 196 if err != nil { 197 t.Fatalf("Unable to create a temporary directory, %s", err) 198 } 199 defer os.RemoveAll(path) 200 201 testCases := []struct { 202 diskPath string 203 expectedErr error 204 }{ 205 {path, nil}, 206 {"/nonexistent-dir", errDiskNotFound}, 207 } 208 209 // Check test cases. 210 for _, testCase := range testCases { 211 if _, err := getDiskInfo(testCase.diskPath); err != testCase.expectedErr { 212 t.Fatalf("expected: %s, got: %s", testCase.expectedErr, err) 213 } 214 } 215 } 216 217 func TestXLStorageIsDirEmpty(t *testing.T) { 218 tmp, err := ioutil.TempDir(globalTestTmpDir, "minio-") 219 if err != nil { 220 t.Fatal(err) 221 } 222 defer os.RemoveAll(tmp) 223 224 // Should give false on non-existent directory. 225 dir1 := slashpath.Join(tmp, "non-existent-directory") 226 if isDirEmpty(dir1) { 227 t.Error("expected false for non-existent directory, got true") 228 } 229 230 // Should give false for not-a-directory. 231 dir2 := slashpath.Join(tmp, "file") 232 err = ioutil.WriteFile(dir2, []byte("hello"), 0777) 233 if err != nil { 234 t.Fatal(err) 235 } 236 237 if isDirEmpty(dir2) { 238 t.Error("expected false for a file, got true") 239 } 240 241 // Should give true for a real empty directory. 242 dir3 := slashpath.Join(tmp, "empty") 243 err = os.Mkdir(dir3, 0777) 244 if err != nil { 245 t.Fatal(err) 246 } 247 248 if !isDirEmpty(dir3) { 249 t.Error("expected true for empty dir, got false") 250 } 251 } 252 253 // TestXLStorageReadVersion - TestXLStorages the functionality implemented by xlStorage ReadVersion storage API. 254 func TestXLStorageReadVersion(t *testing.T) { 255 // create xlStorage test setup 256 xlStorage, path, err := newXLStorageTestSetup() 257 if err != nil { 258 t.Fatalf("Unable to cfgreate xlStorage test setup, %s", err) 259 } 260 261 defer os.RemoveAll(path) 262 263 xlMeta, _ := ioutil.ReadFile("testdata/xl.meta") 264 265 // Create files for the test cases. 266 if err = xlStorage.MakeVol(context.Background(), "exists"); err != nil { 267 t.Fatalf("Unable to create a volume \"exists\", %s", err) 268 } 269 if err = xlStorage.AppendFile(context.Background(), "exists", "as-directory/as-file/xl.meta", xlMeta); err != nil { 270 t.Fatalf("Unable to create a file \"as-directory/as-file\", %s", err) 271 } 272 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file/xl.meta", xlMeta); err != nil { 273 t.Fatalf("Unable to create a file \"as-file\", %s", err) 274 } 275 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file-parent/xl.meta", xlMeta); err != nil { 276 t.Fatalf("Unable to create a file \"as-file-parent\", %s", err) 277 } 278 279 // TestXLStoragecases to validate different conditions for ReadVersion API. 280 testCases := []struct { 281 volume string 282 path string 283 err error 284 }{ 285 // TestXLStorage case - 1. 286 // Validate volume does not exist. 287 { 288 volume: "i-dont-exist", 289 path: "", 290 err: errVolumeNotFound, 291 }, 292 // TestXLStorage case - 2. 293 // Validate bad condition file does not exist. 294 { 295 volume: "exists", 296 path: "as-file-not-found", 297 err: errFileNotFound, 298 }, 299 // TestXLStorage case - 3. 300 // Validate bad condition file exists as prefix/directory and 301 // we are attempting to read it. 302 { 303 volume: "exists", 304 path: "as-directory", 305 err: errFileNotFound, 306 }, 307 // TestXLStorage case - 4. 308 { 309 volume: "exists", 310 path: "as-file-parent/as-file", 311 err: errFileNotFound, 312 }, 313 // TestXLStorage case - 5. 314 // Validate the good condition file exists and we are able to read it. 315 { 316 volume: "exists", 317 path: "as-file", 318 err: nil, 319 }, 320 // TestXLStorage case - 6. 321 // TestXLStorage case with invalid volume name. 322 { 323 volume: "ab", 324 path: "as-file", 325 err: errVolumeNotFound, 326 }, 327 } 328 329 // Run through all the test cases and validate for ReadVersion. 330 for i, testCase := range testCases { 331 _, err = xlStorage.ReadVersion(context.Background(), testCase.volume, testCase.path, "", false) 332 if err != testCase.err { 333 t.Fatalf("TestXLStorage %d: Expected err \"%s\", got err \"%s\"", i+1, testCase.err, err) 334 } 335 } 336 } 337 338 // TestXLStorageReadAll - TestXLStorages the functionality implemented by xlStorage ReadAll storage API. 339 func TestXLStorageReadAll(t *testing.T) { 340 // create xlStorage test setup 341 xlStorage, path, err := newXLStorageTestSetup() 342 if err != nil { 343 t.Fatalf("Unable to create xlStorage test setup, %s", err) 344 } 345 346 defer os.RemoveAll(path) 347 348 // Create files for the test cases. 349 if err = xlStorage.MakeVol(context.Background(), "exists"); err != nil { 350 t.Fatalf("Unable to create a volume \"exists\", %s", err) 351 } 352 if err = xlStorage.AppendFile(context.Background(), "exists", "as-directory/as-file", []byte("Hello, World")); err != nil { 353 t.Fatalf("Unable to create a file \"as-directory/as-file\", %s", err) 354 } 355 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file", []byte("Hello, World")); err != nil { 356 t.Fatalf("Unable to create a file \"as-file\", %s", err) 357 } 358 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file-parent", []byte("Hello, World")); err != nil { 359 t.Fatalf("Unable to create a file \"as-file-parent\", %s", err) 360 } 361 362 // TestXLStoragecases to validate different conditions for ReadAll API. 363 testCases := []struct { 364 volume string 365 path string 366 err error 367 }{ 368 // TestXLStorage case - 1. 369 // Validate volume does not exist. 370 { 371 volume: "i-dont-exist", 372 path: "", 373 err: errVolumeNotFound, 374 }, 375 // TestXLStorage case - 2. 376 // Validate bad condition file does not exist. 377 { 378 volume: "exists", 379 path: "as-file-not-found", 380 err: errFileNotFound, 381 }, 382 // TestXLStorage case - 3. 383 // Validate bad condition file exists as prefix/directory and 384 // we are attempting to read it. 385 { 386 volume: "exists", 387 path: "as-directory", 388 err: errFileNotFound, 389 }, 390 // TestXLStorage case - 4. 391 { 392 volume: "exists", 393 path: "as-file-parent/as-file", 394 err: errFileNotFound, 395 }, 396 // TestXLStorage case - 5. 397 // Validate the good condition file exists and we are able to read it. 398 { 399 volume: "exists", 400 path: "as-file", 401 err: nil, 402 }, 403 // TestXLStorage case - 6. 404 // TestXLStorage case with invalid volume name. 405 { 406 volume: "ab", 407 path: "as-file", 408 err: errVolumeNotFound, 409 }, 410 } 411 412 var dataRead []byte 413 // Run through all the test cases and validate for ReadAll. 414 for i, testCase := range testCases { 415 dataRead, err = xlStorage.ReadAll(context.Background(), testCase.volume, testCase.path) 416 if err != testCase.err { 417 t.Fatalf("TestXLStorage %d: Expected err \"%s\", got err \"%s\"", i+1, testCase.err, err) 418 } 419 if err == nil { 420 if string(dataRead) != string([]byte("Hello, World")) { 421 t.Errorf("TestXLStorage %d: Expected the data read to be \"%s\", but instead got \"%s\"", i+1, "Hello, World", string(dataRead)) 422 } 423 } 424 } 425 } 426 427 // TestNewXLStorage all the cases handled in xlStorage storage layer initialization. 428 func TestNewXLStorage(t *testing.T) { 429 // Temporary dir name. 430 tmpDirName := globalTestTmpDir + SlashSeparator + "minio-" + nextSuffix() 431 // Temporary file name. 432 tmpFileName := globalTestTmpDir + SlashSeparator + "minio-" + nextSuffix() 433 f, _ := os.Create(tmpFileName) 434 f.Close() 435 defer os.Remove(tmpFileName) 436 437 // List of all tests for xlStorage initialization. 438 testCases := []struct { 439 name string 440 err error 441 }{ 442 // Validates input argument cannot be empty. 443 { 444 "", 445 errInvalidArgument, 446 }, 447 // Validates if the directory does not exist and 448 // gets automatically created. 449 { 450 tmpDirName, 451 nil, 452 }, 453 // Validates if the disk exists as file and returns error 454 // not a directory. 455 { 456 tmpFileName, 457 errDiskNotDir, 458 }, 459 } 460 461 // Validate all test cases. 462 for i, testCase := range testCases { 463 // Initialize a new xlStorage layer. 464 _, err := newLocalXLStorage(testCase.name) 465 if err != testCase.err { 466 t.Fatalf("TestXLStorage %d failed wanted: %s, got: %s", i+1, err, testCase.err) 467 } 468 } 469 } 470 471 // TestXLStorageMakeVol - TestXLStorage validate the logic for creation of new xlStorage volume. 472 // Asserts the failures too against the expected failures. 473 func TestXLStorageMakeVol(t *testing.T) { 474 // create xlStorage test setup 475 xlStorage, path, err := newXLStorageTestSetup() 476 if err != nil { 477 t.Fatalf("Unable to create xlStorage test setup, %s", err) 478 } 479 defer os.RemoveAll(path) 480 481 // Setup test environment. 482 // Create a file. 483 if err := ioutil.WriteFile(slashpath.Join(path, "vol-as-file"), []byte{}, os.ModePerm); err != nil { 484 t.Fatalf("Unable to create file, %s", err) 485 } 486 // Create a directory. 487 if err := os.Mkdir(slashpath.Join(path, "existing-vol"), 0777); err != nil { 488 t.Fatalf("Unable to create directory, %s", err) 489 } 490 491 testCases := []struct { 492 volName string 493 expectedErr error 494 }{ 495 // TestXLStorage case - 1. 496 // A valid case, volume creation is expected to succeed. 497 { 498 volName: "success-vol", 499 expectedErr: nil, 500 }, 501 // TestXLStorage case - 2. 502 // Case where a file exists by the name of the volume to be created. 503 { 504 volName: "vol-as-file", 505 expectedErr: errVolumeExists, 506 }, 507 // TestXLStorage case - 3. 508 { 509 volName: "existing-vol", 510 expectedErr: errVolumeExists, 511 }, 512 // TestXLStorage case - 5. 513 // TestXLStorage case with invalid volume name. 514 { 515 volName: "ab", 516 expectedErr: errInvalidArgument, 517 }, 518 } 519 520 for i, testCase := range testCases { 521 if err := xlStorage.MakeVol(context.Background(), testCase.volName); err != testCase.expectedErr { 522 t.Fatalf("TestXLStorage %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 523 } 524 } 525 526 // TestXLStorage for permission denied. 527 if runtime.GOOS != globalWindowsOSName { 528 permDeniedDir, err := ioutil.TempDir(globalTestTmpDir, "minio-") 529 if err != nil { 530 t.Fatalf("Unable to create temporary directory. %v", err) 531 } 532 defer os.RemoveAll(permDeniedDir) 533 if err = os.Chmod(permDeniedDir, 0400); err != nil { 534 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 535 } 536 537 // Initialize xlStorage storage layer for permission denied error. 538 _, err = newLocalXLStorage(permDeniedDir) 539 if err != nil && err != errDiskAccessDenied { 540 t.Fatalf("Unable to initialize xlStorage, %s", err) 541 } 542 543 if err = os.Chmod(permDeniedDir, 0755); err != nil { 544 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 545 } 546 547 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 548 if err != nil { 549 t.Fatalf("Unable to initialize xlStorage, %s", err) 550 } 551 552 // change backend permissions for MakeVol error. 553 if err = os.Chmod(permDeniedDir, 0400); err != nil { 554 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 555 } 556 557 if err := xlStorageNew.MakeVol(context.Background(), "test-vol"); err != errDiskAccessDenied { 558 t.Fatalf("expected: %s, got: %s", errDiskAccessDenied, err) 559 } 560 } 561 } 562 563 // TestXLStorageDeleteVol - Validates the expected behavior of xlStorage.DeleteVol for various cases. 564 func TestXLStorageDeleteVol(t *testing.T) { 565 // create xlStorage test setup 566 xlStorage, path, err := newXLStorageTestSetup() 567 if err != nil { 568 t.Fatalf("Unable to create xlStorage test setup, %s", err) 569 } 570 defer os.RemoveAll(path) 571 572 // Setup test environment. 573 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 574 t.Fatalf("Unable to create volume, %s", err) 575 } 576 577 // TestXLStorage failure cases. 578 vol := slashpath.Join(path, "nonempty-vol") 579 if err = os.Mkdir(vol, 0777); err != nil { 580 t.Fatalf("Unable to create directory, %s", err) 581 } 582 if err = ioutil.WriteFile(slashpath.Join(vol, "test-file"), []byte{}, os.ModePerm); err != nil { 583 t.Fatalf("Unable to create file, %s", err) 584 } 585 586 testCases := []struct { 587 volName string 588 expectedErr error 589 }{ 590 // TestXLStorage case - 1. 591 // A valida case. Empty vol, should be possible to delete. 592 { 593 volName: "success-vol", 594 expectedErr: nil, 595 }, 596 // TestXLStorage case - 2. 597 // volume is non-existent. 598 { 599 volName: "nonexistent-vol", 600 expectedErr: errVolumeNotFound, 601 }, 602 // TestXLStorage case - 3. 603 // It shouldn't be possible to delete an non-empty volume, validating the same. 604 { 605 volName: "nonempty-vol", 606 expectedErr: errVolumeNotEmpty, 607 }, 608 // TestXLStorage case - 5. 609 // Invalid volume name. 610 { 611 volName: "ab", 612 expectedErr: errVolumeNotFound, 613 }, 614 } 615 616 for i, testCase := range testCases { 617 if err = xlStorage.DeleteVol(context.Background(), testCase.volName, false); err != testCase.expectedErr { 618 t.Fatalf("TestXLStorage: %d, expected: %s, got: %s", i+1, testCase.expectedErr, err) 619 } 620 } 621 622 // TestXLStorage for permission denied. 623 if runtime.GOOS != globalWindowsOSName { 624 var permDeniedDir string 625 if permDeniedDir, err = ioutil.TempDir(globalTestTmpDir, "minio-"); err != nil { 626 t.Fatalf("Unable to create temporary directory. %v", err) 627 } 628 defer removePermDeniedFile(permDeniedDir) 629 if err = os.Mkdir(slashpath.Join(permDeniedDir, "mybucket"), 0400); err != nil { 630 t.Fatalf("Unable to create temporary directory %v. %v", slashpath.Join(permDeniedDir, "mybucket"), err) 631 } 632 if err = os.Chmod(permDeniedDir, 0400); err != nil { 633 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 634 } 635 636 // Initialize xlStorage storage layer for permission denied error. 637 _, err = newLocalXLStorage(permDeniedDir) 638 if err != nil && err != errDiskAccessDenied { 639 t.Fatalf("Unable to initialize xlStorage, %s", err) 640 } 641 642 if err = os.Chmod(permDeniedDir, 0755); err != nil { 643 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 644 } 645 646 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 647 if err != nil { 648 t.Fatalf("Unable to initialize xlStorage, %s", err) 649 } 650 651 // change backend permissions for MakeVol error. 652 if err = os.Chmod(permDeniedDir, 0400); err != nil { 653 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 654 } 655 656 if err = xlStorageNew.DeleteVol(context.Background(), "mybucket", false); err != errDiskAccessDenied { 657 t.Fatalf("expected: Permission error, got: %s", err) 658 } 659 } 660 661 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup() 662 if err != nil { 663 t.Fatalf("Unable to create xlStorage test setup, %s", err) 664 } 665 // removing the disk, used to recreate disk not found error. 666 os.RemoveAll(diskPath) 667 668 // TestXLStorage for delete on an removed disk. 669 // should fail with disk not found. 670 err = xlStorageDeletedStorage.DeleteVol(context.Background(), "Del-Vol", false) 671 if err != errDiskNotFound { 672 t.Errorf("Expected: \"Disk not found\", got \"%s\"", err) 673 } 674 } 675 676 // TestXLStorageStatVol - TestXLStorages validate the volume info returned by xlStorage.StatVol() for various inputs. 677 func TestXLStorageStatVol(t *testing.T) { 678 // create xlStorage test setup 679 xlStorage, path, err := newXLStorageTestSetup() 680 if err != nil { 681 t.Fatalf("Unable to create xlStorage test setup, %s", err) 682 } 683 defer os.RemoveAll(path) 684 685 // Setup test environment. 686 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 687 t.Fatalf("Unable to create volume, %s", err) 688 } 689 690 testCases := []struct { 691 volName string 692 expectedErr error 693 }{ 694 // TestXLStorage case - 1. 695 { 696 volName: "success-vol", 697 expectedErr: nil, 698 }, 699 // TestXLStorage case - 2. 700 { 701 volName: "nonexistent-vol", 702 expectedErr: errVolumeNotFound, 703 }, 704 // TestXLStorage case - 3. 705 { 706 volName: "ab", 707 expectedErr: errVolumeNotFound, 708 }, 709 } 710 711 for i, testCase := range testCases { 712 var volInfo VolInfo 713 volInfo, err = xlStorage.StatVol(context.Background(), testCase.volName) 714 if err != testCase.expectedErr { 715 t.Fatalf("TestXLStorage case : %d, Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 716 } 717 718 if err == nil { 719 if volInfo.Name != testCase.volName { 720 t.Errorf("TestXLStorage case %d: Expected the volume name to be \"%s\", instead found \"%s\"", 721 i+1, volInfo.Name, testCase.volName) 722 } 723 } 724 } 725 726 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup() 727 if err != nil { 728 t.Fatalf("Unable to create xlStorage test setup, %s", err) 729 } 730 // removing the disk, used to recreate disk not found error. 731 os.RemoveAll(diskPath) 732 733 // TestXLStorage for delete on an removed disk. 734 // should fail with disk not found. 735 _, err = xlStorageDeletedStorage.StatVol(context.Background(), "Stat vol") 736 if err != errDiskNotFound { 737 t.Errorf("Expected: \"Disk not found\", got \"%s\"", err) 738 } 739 } 740 741 // TestXLStorageListVols - Validates the result and the error output for xlStorage volume listing functionality xlStorage.ListVols(). 742 func TestXLStorageListVols(t *testing.T) { 743 // create xlStorage test setup 744 xlStorage, path, err := newXLStorageTestSetup() 745 if err != nil { 746 t.Fatalf("Unable to create xlStorage test setup, %s", err) 747 } 748 749 var volInfos []VolInfo 750 // TestXLStorage empty list vols. 751 if volInfos, err = xlStorage.ListVols(context.Background()); err != nil { 752 t.Fatalf("expected: <nil>, got: %s", err) 753 } else if len(volInfos) != 1 { 754 t.Fatalf("expected: one entry, got: %s", volInfos) 755 } 756 757 // TestXLStorage non-empty list vols. 758 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 759 t.Fatalf("Unable to create volume, %s", err) 760 } 761 762 volInfos, err = xlStorage.ListVols(context.Background()) 763 if err != nil { 764 t.Fatalf("expected: <nil>, got: %s", err) 765 } 766 if len(volInfos) != 2 { 767 t.Fatalf("expected: 2, got: %d", len(volInfos)) 768 } 769 volFound := false 770 for _, info := range volInfos { 771 if info.Name == "success-vol" { 772 volFound = true 773 break 774 } 775 } 776 if !volFound { 777 t.Errorf("expected: success-vol to be created") 778 } 779 780 // removing the path and simulating disk failure 781 os.RemoveAll(path) 782 // should fail with errDiskNotFound. 783 if _, err = xlStorage.ListVols(context.Background()); err != errDiskNotFound { 784 t.Errorf("Expected to fail with \"%s\", but instead failed with \"%s\"", errDiskNotFound, err) 785 } 786 } 787 788 // TestXLStorageListDir - TestXLStorages validate the directory listing functionality provided by xlStorage.ListDir . 789 func TestXLStorageListDir(t *testing.T) { 790 // create xlStorage test setup 791 xlStorage, path, err := newXLStorageTestSetup() 792 if err != nil { 793 t.Fatalf("Unable to create xlStorage test setup, %s", err) 794 } 795 defer os.RemoveAll(path) 796 797 // create xlStorage test setup. 798 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup() 799 if err != nil { 800 t.Fatalf("Unable to create xlStorage test setup, %s", err) 801 } 802 // removing the disk, used to recreate disk not found error. 803 os.RemoveAll(diskPath) 804 // Setup test environment. 805 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 806 t.Fatalf("Unable to create volume, %s", err) 807 } 808 if err = xlStorage.AppendFile(context.Background(), "success-vol", "abc/def/ghi/success-file", []byte("Hello, world")); err != nil { 809 t.Fatalf("Unable to create file, %s", err) 810 } 811 if err = xlStorage.AppendFile(context.Background(), "success-vol", "abc/xyz/ghi/success-file", []byte("Hello, world")); err != nil { 812 t.Fatalf("Unable to create file, %s", err) 813 } 814 815 testCases := []struct { 816 srcVol string 817 srcPath string 818 // expected result. 819 expectedListDir []string 820 expectedErr error 821 }{ 822 // TestXLStorage case - 1. 823 // valid case with existing volume and file to delete. 824 { 825 srcVol: "success-vol", 826 srcPath: "abc", 827 expectedListDir: []string{"def/", "xyz/"}, 828 expectedErr: nil, 829 }, 830 // TestXLStorage case - 1. 831 // valid case with existing volume and file to delete. 832 { 833 srcVol: "success-vol", 834 srcPath: "abc/def", 835 expectedListDir: []string{"ghi/"}, 836 expectedErr: nil, 837 }, 838 // TestXLStorage case - 1. 839 // valid case with existing volume and file to delete. 840 { 841 srcVol: "success-vol", 842 srcPath: "abc/def/ghi", 843 expectedListDir: []string{"success-file"}, 844 expectedErr: nil, 845 }, 846 // TestXLStorage case - 2. 847 { 848 srcVol: "success-vol", 849 srcPath: "abcdef", 850 expectedErr: errFileNotFound, 851 }, 852 // TestXLStorage case - 3. 853 // TestXLStorage case with invalid volume name. 854 { 855 srcVol: "ab", 856 srcPath: "success-file", 857 expectedErr: errVolumeNotFound, 858 }, 859 // TestXLStorage case - 4. 860 // TestXLStorage case with non existent volume. 861 { 862 srcVol: "non-existent-vol", 863 srcPath: "success-file", 864 expectedErr: errVolumeNotFound, 865 }, 866 } 867 868 for i, testCase := range testCases { 869 var dirList []string 870 dirList, err = xlStorage.ListDir(context.Background(), testCase.srcVol, testCase.srcPath, -1) 871 if err != testCase.expectedErr { 872 t.Errorf("TestXLStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 873 } 874 if err == nil { 875 for _, expected := range testCase.expectedListDir { 876 if !strings.Contains(strings.Join(dirList, ","), expected) { 877 t.Errorf("TestXLStorage case %d: Expected the directory listing to be \"%v\", but got \"%v\"", i+1, testCase.expectedListDir, dirList) 878 } 879 } 880 } 881 } 882 883 // TestXLStorage for permission denied. 884 if runtime.GOOS != globalWindowsOSName { 885 permDeniedDir := createPermDeniedFile(t) 886 defer removePermDeniedFile(permDeniedDir) 887 888 // Initialize xlStorage storage layer for permission denied error. 889 _, err = newLocalXLStorage(permDeniedDir) 890 if err != nil && err != errDiskAccessDenied { 891 t.Fatalf("Unable to initialize xlStorage, %s", err) 892 } 893 894 if err = os.Chmod(permDeniedDir, 0755); err != nil { 895 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 896 } 897 898 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 899 if err != nil { 900 t.Fatalf("Unable to initialize xlStorage, %s", err) 901 } 902 903 if err = xlStorageNew.Delete(context.Background(), "mybucket", "myobject", false); err != errVolumeAccessDenied { 904 t.Errorf("expected: %s, got: %s", errVolumeAccessDenied, err) 905 } 906 } 907 908 // TestXLStorage for delete on an removed disk. 909 // should fail with disk not found. 910 err = xlStorageDeletedStorage.Delete(context.Background(), "del-vol", "my-file", false) 911 if err != errDiskNotFound { 912 t.Errorf("Expected: \"Disk not found\", got \"%s\"", err) 913 } 914 } 915 916 // TestXLStorageDeleteFile - Series of test cases construct valid and invalid input data and validates the result and the error response. 917 func TestXLStorageDeleteFile(t *testing.T) { 918 if runtime.GOOS == globalWindowsOSName { 919 t.Skip() 920 } 921 922 // create xlStorage test setup 923 xlStorage, path, err := newXLStorageTestSetup() 924 if err != nil { 925 t.Fatalf("Unable to create xlStorage test setup, %s", err) 926 } 927 defer os.RemoveAll(path) 928 929 // create xlStorage test setup 930 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup() 931 if err != nil { 932 t.Fatalf("Unable to create xlStorage test setup, %s", err) 933 } 934 // removing the disk, used to recreate disk not found error. 935 os.RemoveAll(diskPath) 936 // Setup test environment. 937 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 938 t.Fatalf("Unable to create volume, %s", err) 939 } 940 if err = xlStorage.AppendFile(context.Background(), "success-vol", "success-file", []byte("Hello, world")); err != nil { 941 t.Fatalf("Unable to create file, %s", err) 942 } 943 944 if err = xlStorage.MakeVol(context.Background(), "no-permissions"); err != nil { 945 t.Fatalf("Unable to create volume, %s", err.Error()) 946 } 947 if err = xlStorage.AppendFile(context.Background(), "no-permissions", "dir/file", []byte("Hello, world")); err != nil { 948 t.Fatalf("Unable to create file, %s", err.Error()) 949 } 950 // Parent directory must have write permissions, this is read + execute. 951 if err = os.Chmod(pathJoin(path, "no-permissions"), 0555); err != nil { 952 t.Fatalf("Unable to chmod directory, %s", err.Error()) 953 } 954 955 testCases := []struct { 956 srcVol string 957 srcPath string 958 expectedErr error 959 }{ 960 // TestXLStorage case - 1. 961 // valid case with existing volume and file to delete. 962 { 963 srcVol: "success-vol", 964 srcPath: "success-file", 965 expectedErr: nil, 966 }, 967 // TestXLStorage case - 2. 968 // The file was deleted in the last case, so Delete should fail. 969 { 970 srcVol: "success-vol", 971 srcPath: "success-file", 972 expectedErr: errFileNotFound, 973 }, 974 // TestXLStorage case - 3. 975 // TestXLStorage case with segment of the volume name > 255. 976 { 977 srcVol: "my", 978 srcPath: "success-file", 979 expectedErr: errVolumeNotFound, 980 }, 981 // TestXLStorage case - 4. 982 // TestXLStorage case with non-existent volume. 983 { 984 srcVol: "non-existent-vol", 985 srcPath: "success-file", 986 expectedErr: errVolumeNotFound, 987 }, 988 // TestXLStorage case - 5. 989 // TestXLStorage case with src path segment > 255. 990 { 991 srcVol: "success-vol", 992 srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 993 expectedErr: errFileNameTooLong, 994 }, 995 // TestXLStorage case - 6. 996 // TestXLStorage case with undeletable parent directory. 997 // File can delete, dir cannot delete because no-permissions doesn't have write perms. 998 { 999 srcVol: "no-permissions", 1000 srcPath: "dir/file", 1001 expectedErr: errVolumeAccessDenied, 1002 }, 1003 } 1004 1005 for i, testCase := range testCases { 1006 if err = xlStorage.Delete(context.Background(), testCase.srcVol, testCase.srcPath, false); err != testCase.expectedErr { 1007 t.Errorf("TestXLStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 1008 } 1009 } 1010 1011 // TestXLStorage for permission denied. 1012 if runtime.GOOS != globalWindowsOSName { 1013 permDeniedDir := createPermDeniedFile(t) 1014 defer removePermDeniedFile(permDeniedDir) 1015 1016 // Initialize xlStorage storage layer for permission denied error. 1017 _, err = newLocalXLStorage(permDeniedDir) 1018 if err != nil && err != errDiskAccessDenied { 1019 t.Fatalf("Unable to initialize xlStorage, %s", err) 1020 } 1021 1022 if err = os.Chmod(permDeniedDir, 0755); err != nil { 1023 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 1024 } 1025 1026 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 1027 if err != nil { 1028 t.Fatalf("Unable to initialize xlStorage, %s", err) 1029 } 1030 1031 if err = xlStorageNew.Delete(context.Background(), "mybucket", "myobject", false); err != errVolumeAccessDenied { 1032 t.Errorf("expected: %s, got: %s", errVolumeAccessDenied, err) 1033 } 1034 } 1035 1036 // TestXLStorage for delete on an removed disk. 1037 // should fail with disk not found. 1038 err = xlStorageDeletedStorage.Delete(context.Background(), "del-vol", "my-file", false) 1039 if err != errDiskNotFound { 1040 t.Errorf("Expected: \"Disk not found\", got \"%s\"", err) 1041 } 1042 } 1043 1044 // TestXLStorageReadFile - TestXLStorages xlStorage.ReadFile with wide range of cases and asserts the result and error response. 1045 func TestXLStorageReadFile(t *testing.T) { 1046 // create xlStorage test setup 1047 xlStorage, path, err := newXLStorageTestSetup() 1048 if err != nil { 1049 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1050 } 1051 defer os.RemoveAll(path) 1052 1053 volume := "success-vol" 1054 // Setup test environment. 1055 if err = xlStorage.MakeVol(context.Background(), volume); err != nil { 1056 t.Fatalf("Unable to create volume, %s", err) 1057 } 1058 1059 // Create directory to make errIsNotRegular 1060 if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0777); err != nil { 1061 t.Fatalf("Unable to create directory, %s", err) 1062 } 1063 1064 testCases := []struct { 1065 volume string 1066 fileName string 1067 offset int64 1068 bufSize int 1069 expectedBuf []byte 1070 expectedErr error 1071 }{ 1072 // Successful read at offset 0 and proper buffer size. - 1 1073 { 1074 volume, "myobject", 0, 5, 1075 []byte("hello"), nil, 1076 }, 1077 // Success read at hierarchy. - 2 1078 { 1079 volume, "path/to/my/object", 0, 5, 1080 []byte("hello"), nil, 1081 }, 1082 // Object is a directory. - 3 1083 { 1084 volume, "object-as-dir", 1085 0, 5, nil, errIsNotRegular}, 1086 // One path segment length is > 255 chars long. - 4 1087 { 1088 volume, "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1089 0, 5, nil, errFileNameTooLong}, 1090 // Path length is > 1024 chars long. - 5 1091 { 1092 volume, "level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1093 0, 5, nil, errFileNameTooLong}, 1094 // Buffer size greater than object size. - 6 1095 { 1096 volume, "myobject", 0, 16, 1097 []byte("hello, world"), 1098 io.ErrUnexpectedEOF, 1099 }, 1100 // Reading from an offset success. - 7 1101 { 1102 volume, "myobject", 7, 5, 1103 []byte("world"), nil, 1104 }, 1105 // Reading from an object but buffer size greater. - 8 1106 { 1107 volume, "myobject", 1108 7, 8, 1109 []byte("world"), 1110 io.ErrUnexpectedEOF, 1111 }, 1112 // Seeking ahead returns io.EOF. - 9 1113 { 1114 volume, "myobject", 14, 1, nil, io.EOF, 1115 }, 1116 // Empty volume name. - 10 1117 { 1118 "", "myobject", 14, 1, nil, errVolumeNotFound, 1119 }, 1120 // Empty filename name. - 11 1121 { 1122 volume, "", 14, 1, nil, errIsNotRegular, 1123 }, 1124 // Non existent volume name - 12 1125 { 1126 "abcd", "", 14, 1, nil, errVolumeNotFound, 1127 }, 1128 // Non existent filename - 13 1129 { 1130 volume, "abcd", 14, 1, nil, errFileNotFound, 1131 }, 1132 } 1133 1134 // Create all files needed during testing. 1135 appendFiles := testCases[:4] 1136 v := NewBitrotVerifier(SHA256, getSHA256Sum([]byte("hello, world"))) 1137 // Create test files for further reading. 1138 for i, appendFile := range appendFiles { 1139 err = xlStorage.AppendFile(context.Background(), volume, appendFile.fileName, []byte("hello, world")) 1140 if err != appendFile.expectedErr { 1141 t.Fatalf("Creating file failed: %d %#v, expected: %s, got: %s", i+1, appendFile, appendFile.expectedErr, err) 1142 } 1143 } 1144 1145 { 1146 buf := make([]byte, 5) 1147 // Test for negative offset. 1148 if _, err = xlStorage.ReadFile(context.Background(), volume, "myobject", -1, buf, v); err == nil { 1149 t.Fatalf("expected: error, got: <nil>") 1150 } 1151 } 1152 1153 for l := 0; l < 2; l++ { 1154 // 1st loop tests with dma=write, 2nd loop tests with dma=read-write. 1155 if l == 1 { 1156 globalStorageClass.DMA = storageclass.DMAReadWrite 1157 } 1158 // Following block validates all ReadFile test cases. 1159 for i, testCase := range testCases { 1160 var n int64 1161 // Common read buffer. 1162 var buf = make([]byte, testCase.bufSize) 1163 n, err = xlStorage.ReadFile(context.Background(), testCase.volume, testCase.fileName, testCase.offset, buf, v) 1164 if err != nil && testCase.expectedErr != nil { 1165 // Validate if the type string of the errors are an exact match. 1166 if err.Error() != testCase.expectedErr.Error() { 1167 if runtime.GOOS != globalWindowsOSName { 1168 t.Errorf("Case: %d %#v, expected: %s, got: %s", i+1, testCase, testCase.expectedErr, err) 1169 } else { 1170 var resultErrno, expectErrno uintptr 1171 if pathErr, ok := err.(*os.PathError); ok { 1172 if errno, pok := pathErr.Err.(syscall.Errno); pok { 1173 resultErrno = uintptr(errno) 1174 } 1175 } 1176 if pathErr, ok := testCase.expectedErr.(*os.PathError); ok { 1177 if errno, pok := pathErr.Err.(syscall.Errno); pok { 1178 expectErrno = uintptr(errno) 1179 } 1180 } 1181 if !(expectErrno != 0 && resultErrno != 0 && expectErrno == resultErrno) { 1182 t.Errorf("Case: %d %#v, expected: %s, got: %s", i+1, testCase, testCase.expectedErr, err) 1183 } 1184 } 1185 } 1186 // Err unexpected EOF special case, where we verify we have provided a larger 1187 // buffer than the data itself, but the results are in-fact valid. So we validate 1188 // this error condition specifically treating it as a good condition with valid 1189 // results. In this scenario return 'n' is always lesser than the input buffer. 1190 if err == io.ErrUnexpectedEOF { 1191 if !bytes.Equal(testCase.expectedBuf, buf[:n]) { 1192 t.Errorf("Case: %d %#v, expected: \"%s\", got: \"%s\"", i+1, testCase, string(testCase.expectedBuf), string(buf[:n])) 1193 } 1194 if n > int64(len(buf)) { 1195 t.Errorf("Case: %d %#v, expected: %d, got: %d", i+1, testCase, testCase.bufSize, n) 1196 } 1197 } 1198 } 1199 // ReadFile has returned success, but our expected error is non 'nil'. 1200 if err == nil && err != testCase.expectedErr { 1201 t.Errorf("Case: %d %#v, expected: %s, got :%s", i+1, testCase, testCase.expectedErr, err) 1202 } 1203 // Expected error retured, proceed further to validate the returned results. 1204 if err == nil && err == testCase.expectedErr { 1205 if !bytes.Equal(testCase.expectedBuf, buf) { 1206 t.Errorf("Case: %d %#v, expected: \"%s\", got: \"%s\"", i+1, testCase, string(testCase.expectedBuf), string(buf[:testCase.bufSize])) 1207 } 1208 if n != int64(testCase.bufSize) { 1209 t.Errorf("Case: %d %#v, expected: %d, got: %d", i+1, testCase, testCase.bufSize, n) 1210 } 1211 } 1212 } 1213 } 1214 1215 // Reset the flag. 1216 globalStorageClass.DMA = storageclass.DMAWrite 1217 1218 // TestXLStorage for permission denied. 1219 if runtime.GOOS != globalWindowsOSName { 1220 permDeniedDir := createPermDeniedFile(t) 1221 defer removePermDeniedFile(permDeniedDir) 1222 1223 // Initialize xlStorage storage layer for permission denied error. 1224 _, err = newLocalXLStorage(permDeniedDir) 1225 if err != nil && err != errDiskAccessDenied { 1226 t.Fatalf("Unable to initialize xlStorage, %s", err) 1227 } 1228 1229 if err = os.Chmod(permDeniedDir, 0755); err != nil { 1230 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 1231 } 1232 1233 xlStoragePermStorage, err := newLocalXLStorage(permDeniedDir) 1234 if err != nil { 1235 t.Fatalf("Unable to initialize xlStorage, %s", err) 1236 } 1237 1238 // Common read buffer. 1239 var buf = make([]byte, 10) 1240 if _, err = xlStoragePermStorage.ReadFile(context.Background(), "mybucket", "myobject", 0, buf, v); err != errFileAccessDenied { 1241 t.Errorf("expected: %s, got: %s", errFileAccessDenied, err) 1242 } 1243 } 1244 } 1245 1246 var xlStorageReadFileWithVerifyTests = []struct { 1247 file string 1248 offset int 1249 length int 1250 algorithm BitrotAlgorithm 1251 expError error 1252 }{ 1253 {file: "myobject", offset: 0, length: 100, algorithm: SHA256, expError: nil}, // 0 1254 {file: "myobject", offset: 25, length: 74, algorithm: SHA256, expError: nil}, // 1 1255 {file: "myobject", offset: 29, length: 70, algorithm: SHA256, expError: nil}, // 2 1256 {file: "myobject", offset: 100, length: 0, algorithm: SHA256, expError: nil}, // 3 1257 {file: "myobject", offset: 1, length: 120, algorithm: SHA256, expError: errFileCorrupt}, // 4 1258 {file: "myobject", offset: 3, length: 1100, algorithm: SHA256, expError: nil}, // 5 1259 {file: "myobject", offset: 2, length: 100, algorithm: SHA256, expError: errFileCorrupt}, // 6 1260 {file: "myobject", offset: 1000, length: 1001, algorithm: SHA256, expError: nil}, // 7 1261 {file: "myobject", offset: 0, length: 100, algorithm: BLAKE2b512, expError: errFileCorrupt}, // 8 1262 {file: "myobject", offset: 25, length: 74, algorithm: BLAKE2b512, expError: nil}, // 9 1263 {file: "myobject", offset: 29, length: 70, algorithm: BLAKE2b512, expError: errFileCorrupt}, // 10 1264 {file: "myobject", offset: 100, length: 0, algorithm: BLAKE2b512, expError: nil}, // 11 1265 {file: "myobject", offset: 1, length: 120, algorithm: BLAKE2b512, expError: nil}, // 12 1266 {file: "myobject", offset: 3, length: 1100, algorithm: BLAKE2b512, expError: nil}, // 13 1267 {file: "myobject", offset: 2, length: 100, algorithm: BLAKE2b512, expError: nil}, // 14 1268 {file: "myobject", offset: 1000, length: 1001, algorithm: BLAKE2b512, expError: nil}, // 15 1269 } 1270 1271 // TestXLStorageReadFile with bitrot verification - tests the xlStorage level 1272 // ReadFile API with a BitrotVerifier. Only tests hashing related 1273 // functionality. Other functionality is tested with 1274 // TestXLStorageReadFile. 1275 func TestXLStorageReadFileWithVerify(t *testing.T) { 1276 volume, object := "test-vol", "myobject" 1277 xlStorage, path, err := newXLStorageTestSetup() 1278 if err != nil { 1279 os.RemoveAll(path) 1280 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1281 } 1282 if err = xlStorage.MakeVol(context.Background(), volume); err != nil { 1283 os.RemoveAll(path) 1284 t.Fatalf("Unable to create volume %s: %v", volume, err) 1285 } 1286 data := make([]byte, 8*1024) 1287 if _, err = io.ReadFull(rand.Reader, data); err != nil { 1288 os.RemoveAll(path) 1289 t.Fatalf("Unable to create generate random data: %v", err) 1290 } 1291 if err = xlStorage.AppendFile(context.Background(), volume, object, data); err != nil { 1292 os.RemoveAll(path) 1293 t.Fatalf("Unable to create object: %v", err) 1294 } 1295 1296 for i, test := range xlStorageReadFileWithVerifyTests { 1297 h := test.algorithm.New() 1298 h.Write(data) 1299 if test.expError != nil { 1300 h.Write([]byte{0}) 1301 } 1302 1303 buffer := make([]byte, test.length) 1304 n, err := xlStorage.ReadFile(context.Background(), volume, test.file, int64(test.offset), buffer, NewBitrotVerifier(test.algorithm, h.Sum(nil))) 1305 1306 switch { 1307 case err == nil && test.expError != nil: 1308 t.Errorf("Test %d: Expected error %v but got none.", i, test.expError) 1309 case err == nil && n != int64(test.length): 1310 t.Errorf("Test %d: %d bytes were expected, but %d were written", i, test.length, n) 1311 case err == nil && !bytes.Equal(data[test.offset:test.offset+test.length], buffer): 1312 t.Errorf("Test %d: Expected bytes: %v, but got: %v", i, data[test.offset:test.offset+test.length], buffer) 1313 case err != nil && err != test.expError: 1314 t.Errorf("Test %d: Expected error: %v, but got: %v", i, test.expError, err) 1315 } 1316 } 1317 } 1318 1319 // TestXLStorageFormatFileChange - to test if changing the diskID makes the calls fail. 1320 func TestXLStorageFormatFileChange(t *testing.T) { 1321 xlStorage, path, err := newXLStorageTestSetup() 1322 if err != nil { 1323 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1324 } 1325 defer os.RemoveAll(path) 1326 1327 if err = xlStorage.MakeVol(context.Background(), volume); err != nil { 1328 t.Fatalf("MakeVol failed with %s", err) 1329 } 1330 1331 // Change the format.json such that "this" is changed to "randomid". 1332 if err = ioutil.WriteFile(pathJoin(xlStorage.String(), minioMetaBucket, formatConfigFile), []byte(`{"version":"1","format":"xl","id":"592a41c2-b7cc-4130-b883-c4b5cb15965b","xl":{"version":"3","this":"randomid","sets":[["e07285a6-8c73-4962-89c6-047fb939f803","33b8d431-482d-4376-b63c-626d229f0a29","cff6513a-4439-4dc1-bcaa-56c9e880c352","randomid","9c9f21d5-1f15-4737-bce6-835faa0d9626","0a59b346-1424-4fc2-9fa2-a2e80541d0c1","7924a3dc-b69a-4971-9a2e-014966d6aebb","4d2b8dd9-4e48-444b-bdca-c89194b26042"]],"distributionAlgo":"CRCMOD"}}`), 0644); err != nil { 1333 t.Fatalf("ioutil.WriteFile failed with %s", err) 1334 } 1335 1336 err = xlStorage.MakeVol(context.Background(), volume) 1337 if err != errVolumeExists { 1338 t.Fatalf("MakeVol expected to fail with errDiskNotFound but failed with %s", err) 1339 } 1340 } 1341 1342 // TestXLStorage xlStorage.AppendFile() 1343 func TestXLStorageAppendFile(t *testing.T) { 1344 // create xlStorage test setup 1345 xlStorage, path, err := newXLStorageTestSetup() 1346 if err != nil { 1347 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1348 } 1349 defer os.RemoveAll(path) 1350 1351 // Setup test environment. 1352 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 1353 t.Fatalf("Unable to create volume, %s", err) 1354 } 1355 1356 // Create directory to make errIsNotRegular 1357 if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0777); err != nil { 1358 t.Fatalf("Unable to create directory, %s", err) 1359 } 1360 1361 testCases := []struct { 1362 fileName string 1363 expectedErr error 1364 }{ 1365 {"myobject", nil}, 1366 {"path/to/my/object", nil}, 1367 // TestXLStorage to append to previously created file. 1368 {"myobject", nil}, 1369 // TestXLStorage to use same path of previously created file. 1370 {"path/to/my/testobject", nil}, 1371 // TestXLStorage to use object is a directory now. 1372 {"object-as-dir", errIsNotRegular}, 1373 // path segment uses previously uploaded object. 1374 {"myobject/testobject", errFileAccessDenied}, 1375 // One path segment length is > 255 chars long. 1376 {"path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong}, 1377 // path length is > 1024 chars long. 1378 {"level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong}, 1379 } 1380 1381 for i, testCase := range testCases { 1382 if err = xlStorage.AppendFile(context.Background(), "success-vol", testCase.fileName, []byte("hello, world")); err != testCase.expectedErr { 1383 t.Errorf("Case: %d, expected: %s, got: %s", i+1, testCase.expectedErr, err) 1384 } 1385 } 1386 1387 // TestXLStorage for permission denied. 1388 if runtime.GOOS != globalWindowsOSName { 1389 permDeniedDir := createPermDeniedFile(t) 1390 defer removePermDeniedFile(permDeniedDir) 1391 1392 var xlStoragePermStorage StorageAPI 1393 // Initialize xlStorage storage layer for permission denied error. 1394 _, err = newLocalXLStorage(permDeniedDir) 1395 if err != nil && err != errDiskAccessDenied { 1396 t.Fatalf("Unable to initialize xlStorage, %s", err) 1397 } 1398 1399 if err = os.Chmod(permDeniedDir, 0755); err != nil { 1400 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 1401 } 1402 1403 xlStoragePermStorage, err = newLocalXLStorage(permDeniedDir) 1404 if err != nil { 1405 t.Fatalf("Unable to initialize xlStorage, %s", err) 1406 } 1407 1408 if err = xlStoragePermStorage.AppendFile(context.Background(), "mybucket", "myobject", []byte("hello, world")); err != errVolumeAccessDenied { 1409 t.Fatalf("expected: errVolumeAccessDenied error, got: %s", err) 1410 } 1411 } 1412 1413 // TestXLStorage case with invalid volume name. 1414 // A valid volume name should be atleast of size 3. 1415 err = xlStorage.AppendFile(context.Background(), "bn", "yes", []byte("hello, world")) 1416 if err != errVolumeNotFound { 1417 t.Fatalf("expected: \"Invalid argument error\", got: \"%s\"", err) 1418 } 1419 } 1420 1421 // TestXLStorage xlStorage.RenameFile() 1422 func TestXLStorageRenameFile(t *testing.T) { 1423 // create xlStorage test setup 1424 xlStorage, path, err := newXLStorageTestSetup() 1425 if err != nil { 1426 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1427 } 1428 defer os.RemoveAll(path) 1429 1430 // Setup test environment. 1431 if err := xlStorage.MakeVol(context.Background(), "src-vol"); err != nil { 1432 t.Fatalf("Unable to create volume, %s", err) 1433 } 1434 1435 if err := xlStorage.MakeVol(context.Background(), "dest-vol"); err != nil { 1436 t.Fatalf("Unable to create volume, %s", err) 1437 } 1438 1439 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file1", []byte("Hello, world")); err != nil { 1440 t.Fatalf("Unable to create file, %s", err) 1441 } 1442 1443 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file2", []byte("Hello, world")); err != nil { 1444 t.Fatalf("Unable to create file, %s", err) 1445 } 1446 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file3", []byte("Hello, world")); err != nil { 1447 t.Fatalf("Unable to create file, %s", err) 1448 } 1449 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file4", []byte("Hello, world")); err != nil { 1450 t.Fatalf("Unable to create file, %s", err) 1451 } 1452 1453 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file5", []byte("Hello, world")); err != nil { 1454 t.Fatalf("Unable to create file, %s", err) 1455 } 1456 if err := xlStorage.AppendFile(context.Background(), "src-vol", "path/to/file1", []byte("Hello, world")); err != nil { 1457 t.Fatalf("Unable to create file, %s", err) 1458 } 1459 1460 testCases := []struct { 1461 srcVol string 1462 destVol string 1463 srcPath string 1464 destPath string 1465 expectedErr error 1466 }{ 1467 // TestXLStorage case - 1. 1468 { 1469 srcVol: "src-vol", 1470 destVol: "dest-vol", 1471 srcPath: "file1", 1472 destPath: "file-one", 1473 expectedErr: nil, 1474 }, 1475 // TestXLStorage case - 2. 1476 { 1477 srcVol: "src-vol", 1478 destVol: "dest-vol", 1479 srcPath: "path/", 1480 destPath: "new-path/", 1481 expectedErr: nil, 1482 }, 1483 // TestXLStorage case - 3. 1484 // TestXLStorage to overwrite destination file. 1485 { 1486 srcVol: "src-vol", 1487 destVol: "dest-vol", 1488 srcPath: "file2", 1489 destPath: "file-one", 1490 expectedErr: nil, 1491 }, 1492 // TestXLStorage case - 4. 1493 // TestXLStorage case with io error count set to 1. 1494 // expected not to fail. 1495 { 1496 srcVol: "src-vol", 1497 destVol: "dest-vol", 1498 srcPath: "file3", 1499 destPath: "file-two", 1500 expectedErr: nil, 1501 }, 1502 // TestXLStorage case - 5. 1503 // TestXLStorage case with io error count set to maximum allowed count. 1504 // expected not to fail. 1505 { 1506 srcVol: "src-vol", 1507 destVol: "dest-vol", 1508 srcPath: "file4", 1509 destPath: "file-three", 1510 expectedErr: nil, 1511 }, 1512 // TestXLStorage case - 6. 1513 // TestXLStorage case with non-existent source file. 1514 { 1515 srcVol: "src-vol", 1516 destVol: "dest-vol", 1517 srcPath: "non-existent-file", 1518 destPath: "file-three", 1519 expectedErr: errFileNotFound, 1520 }, 1521 // TestXLStorage case - 7. 1522 // TestXLStorage to check failure of source and destination are not same type. 1523 { 1524 srcVol: "src-vol", 1525 destVol: "dest-vol", 1526 srcPath: "path/", 1527 destPath: "file-one", 1528 expectedErr: errFileAccessDenied, 1529 }, 1530 // TestXLStorage case - 8. 1531 // TestXLStorage to check failure of destination directory exists. 1532 { 1533 srcVol: "src-vol", 1534 destVol: "dest-vol", 1535 srcPath: "path/", 1536 destPath: "new-path/", 1537 expectedErr: errFileAccessDenied, 1538 }, 1539 // TestXLStorage case - 9. 1540 // TestXLStorage case with source being a file and destination being a directory. 1541 // Either both have to be files or directories. 1542 // Expecting to fail with `errFileAccessDenied`. 1543 { 1544 srcVol: "src-vol", 1545 destVol: "dest-vol", 1546 srcPath: "file4", 1547 destPath: "new-path/", 1548 expectedErr: errFileAccessDenied, 1549 }, 1550 // TestXLStorage case - 10. 1551 // TestXLStorage case with non-existent source volume. 1552 // Expecting to fail with `errVolumeNotFound`. 1553 { 1554 srcVol: "src-vol-non-existent", 1555 destVol: "dest-vol", 1556 srcPath: "file4", 1557 destPath: "new-path/", 1558 expectedErr: errVolumeNotFound, 1559 }, 1560 // TestXLStorage case - 11. 1561 // TestXLStorage case with non-existent destination volume. 1562 // Expecting to fail with `errVolumeNotFound`. 1563 { 1564 srcVol: "src-vol", 1565 destVol: "dest-vol-non-existent", 1566 srcPath: "file4", 1567 destPath: "new-path/", 1568 expectedErr: errVolumeNotFound, 1569 }, 1570 // TestXLStorage case - 12. 1571 // TestXLStorage case with invalid src volume name. Length should be atleast 3. 1572 // Expecting to fail with `errInvalidArgument`. 1573 { 1574 srcVol: "ab", 1575 destVol: "dest-vol-non-existent", 1576 srcPath: "file4", 1577 destPath: "new-path/", 1578 expectedErr: errVolumeNotFound, 1579 }, 1580 // TestXLStorage case - 13. 1581 // TestXLStorage case with invalid destination volume name. Length should be atleast 3. 1582 // Expecting to fail with `errInvalidArgument`. 1583 { 1584 srcVol: "abcd", 1585 destVol: "ef", 1586 srcPath: "file4", 1587 destPath: "new-path/", 1588 expectedErr: errVolumeNotFound, 1589 }, 1590 // TestXLStorage case - 14. 1591 // TestXLStorage case with invalid destination volume name. Length should be atleast 3. 1592 // Expecting to fail with `errInvalidArgument`. 1593 { 1594 srcVol: "abcd", 1595 destVol: "ef", 1596 srcPath: "file4", 1597 destPath: "new-path/", 1598 expectedErr: errVolumeNotFound, 1599 }, 1600 // TestXLStorage case - 15. 1601 // TestXLStorage case with the parent of the destination being a file. 1602 // expected to fail with `errFileAccessDenied`. 1603 { 1604 srcVol: "src-vol", 1605 destVol: "dest-vol", 1606 srcPath: "file5", 1607 destPath: "file-one/parent-is-file", 1608 expectedErr: errFileAccessDenied, 1609 }, 1610 // TestXLStorage case - 16. 1611 // TestXLStorage case with segment of source file name more than 255. 1612 // expected not to fail. 1613 { 1614 srcVol: "src-vol", 1615 destVol: "dest-vol", 1616 srcPath: "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1617 destPath: "file-six", 1618 expectedErr: errFileNameTooLong, 1619 }, 1620 // TestXLStorage case - 17. 1621 // TestXLStorage case with segment of destination file name more than 255. 1622 // expected not to fail. 1623 { 1624 srcVol: "src-vol", 1625 destVol: "dest-vol", 1626 srcPath: "file6", 1627 destPath: "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1628 expectedErr: errFileNameTooLong, 1629 }, 1630 } 1631 1632 for i, testCase := range testCases { 1633 if err := xlStorage.RenameFile(context.Background(), testCase.srcVol, testCase.srcPath, testCase.destVol, testCase.destPath); err != testCase.expectedErr { 1634 t.Fatalf("TestXLStorage %d: Expected the error to be : \"%v\", got: \"%v\".", i+1, testCase.expectedErr, err) 1635 } 1636 } 1637 } 1638 1639 // TestXLStorage xlStorage.CheckFile() 1640 func TestXLStorageCheckFile(t *testing.T) { 1641 // create xlStorage test setup 1642 xlStorage, path, err := newXLStorageTestSetup() 1643 if err != nil { 1644 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1645 } 1646 defer os.RemoveAll(path) 1647 1648 // Setup test environment. 1649 if err := xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 1650 t.Fatalf("Unable to create volume, %s", err) 1651 } 1652 1653 if err := xlStorage.AppendFile(context.Background(), "success-vol", pathJoin("success-file", xlStorageFormatFile), []byte("Hello, world")); err != nil { 1654 t.Fatalf("Unable to create file, %s", err) 1655 } 1656 1657 if err := xlStorage.AppendFile(context.Background(), "success-vol", pathJoin("path/to/success-file", xlStorageFormatFile), []byte("Hello, world")); err != nil { 1658 t.Fatalf("Unable to create file, %s", err) 1659 } 1660 1661 if err := xlStorage.MakeVol(context.Background(), "success-vol/path/to/"+xlStorageFormatFile); err != nil { 1662 t.Fatalf("Unable to create path, %s", err) 1663 } 1664 1665 testCases := []struct { 1666 srcVol string 1667 srcPath string 1668 expectedErr error 1669 }{ 1670 // TestXLStorage case - 1. 1671 // TestXLStorage case with valid inputs, expected to pass. 1672 { 1673 srcVol: "success-vol", 1674 srcPath: "success-file", 1675 expectedErr: nil, 1676 }, 1677 // TestXLStorage case - 2. 1678 // TestXLStorage case with valid inputs, expected to pass. 1679 { 1680 srcVol: "success-vol", 1681 srcPath: "path/to/success-file", 1682 expectedErr: nil, 1683 }, 1684 // TestXLStorage case - 3. 1685 // TestXLStorage case with non-existent file. 1686 { 1687 srcVol: "success-vol", 1688 srcPath: "nonexistent-file", 1689 expectedErr: errPathNotFound, 1690 }, 1691 // TestXLStorage case - 4. 1692 // TestXLStorage case with non-existent file path. 1693 { 1694 srcVol: "success-vol", 1695 srcPath: "path/2/success-file", 1696 expectedErr: errPathNotFound, 1697 }, 1698 // TestXLStorage case - 5. 1699 // TestXLStorage case with path being a directory. 1700 { 1701 srcVol: "success-vol", 1702 srcPath: "path", 1703 expectedErr: errPathNotFound, 1704 }, 1705 // TestXLStorage case - 6. 1706 // TestXLStorage case with non existent volume. 1707 { 1708 srcVol: "non-existent-vol", 1709 srcPath: "success-file", 1710 expectedErr: errPathNotFound, 1711 }, 1712 // TestXLStorage case - 7. 1713 // TestXLStorage case with file with directory. 1714 { 1715 srcVol: "success-vol", 1716 srcPath: "path/to", 1717 expectedErr: errFileNotFound, 1718 }, 1719 } 1720 1721 for i, testCase := range testCases { 1722 if err := xlStorage.CheckFile(context.Background(), testCase.srcVol, testCase.srcPath); err != testCase.expectedErr { 1723 t.Errorf("TestXLStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 1724 } 1725 } 1726 } 1727 1728 // Test xlStorage.VerifyFile() 1729 func TestXLStorageVerifyFile(t *testing.T) { 1730 // We test 4 cases: 1731 // 1) Whole-file bitrot check on proper file 1732 // 2) Whole-file bitrot check on corrupted file 1733 // 3) Streaming bitrot check on proper file 1734 // 4) Streaming bitrot check on corrupted file 1735 1736 // create xlStorage test setup 1737 storage, path, err := newXLStorageTestSetup() 1738 if err != nil { 1739 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1740 } 1741 defer os.RemoveAll(path) 1742 1743 volName := "testvol" 1744 fileName := "testfile" 1745 if err := storage.MakeVol(context.Background(), volName); err != nil { 1746 t.Fatal(err) 1747 } 1748 1749 // 1) Whole-file bitrot check on proper file 1750 size := int64(4*1024*1024 + 100*1024) // 4.1 MB 1751 data := make([]byte, size) 1752 if _, err := rand.Read(data); err != nil { 1753 t.Fatal(err) 1754 } 1755 algo := HighwayHash256 1756 h := algo.New() 1757 h.Write(data) 1758 hashBytes := h.Sum(nil) 1759 if err := storage.WriteAll(context.Background(), volName, fileName, data); err != nil { 1760 t.Fatal(err) 1761 } 1762 if err := storage.storage.(*xlStorage).bitrotVerify(pathJoin(path, volName, fileName), size, algo, hashBytes, 0); err != nil { 1763 t.Fatal(err) 1764 } 1765 1766 // 2) Whole-file bitrot check on corrupted file 1767 if err := storage.AppendFile(context.Background(), volName, fileName, []byte("a")); err != nil { 1768 t.Fatal(err) 1769 } 1770 1771 // Check if VerifyFile reports the incorrect file length (the correct length is `size+1`) 1772 if err := storage.storage.(*xlStorage).bitrotVerify(pathJoin(path, volName, fileName), size, algo, hashBytes, 0); err == nil { 1773 t.Fatal("expected to fail bitrot check") 1774 } 1775 1776 // Check if bitrot fails 1777 if err := storage.storage.(*xlStorage).bitrotVerify(pathJoin(path, volName, fileName), size+1, algo, hashBytes, 0); err == nil { 1778 t.Fatal("expected to fail bitrot check") 1779 } 1780 1781 if err := storage.Delete(context.Background(), volName, fileName, false); err != nil { 1782 t.Fatal(err) 1783 } 1784 1785 // 3) Streaming bitrot check on proper file 1786 algo = HighwayHash256S 1787 shardSize := int64(1024 * 1024) 1788 shard := make([]byte, shardSize) 1789 w := newStreamingBitrotWriter(storage, volName, fileName, size, algo, shardSize, false) 1790 reader := bytes.NewReader(data) 1791 for { 1792 // Using io.Copy instead of this loop will not work for us as io.Copy 1793 // will use bytes.Reader.WriteTo() which will not do shardSize'ed writes 1794 // causing error. 1795 n, err := reader.Read(shard) 1796 w.Write(shard[:n]) 1797 if err == nil { 1798 continue 1799 } 1800 if err == io.EOF { 1801 break 1802 } 1803 t.Fatal(err) 1804 } 1805 w.(io.Closer).Close() 1806 if err := storage.storage.(*xlStorage).bitrotVerify(pathJoin(path, volName, fileName), size, algo, nil, shardSize); err != nil { 1807 t.Fatal(err) 1808 } 1809 1810 // 4) Streaming bitrot check on corrupted file 1811 filePath := pathJoin(storage.String(), volName, fileName) 1812 f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_SYNC, 0644) 1813 if err != nil { 1814 t.Fatal(err) 1815 } 1816 // Replace first 256 with 'a'. 1817 if _, err := f.WriteString(strings.Repeat("a", 256)); err != nil { 1818 t.Fatal(err) 1819 } 1820 f.Close() 1821 if err := storage.storage.(*xlStorage).bitrotVerify(pathJoin(path, volName, fileName), size, algo, nil, shardSize); err == nil { 1822 t.Fatal("expected to fail bitrot check") 1823 } 1824 if err := storage.storage.(*xlStorage).bitrotVerify(pathJoin(path, volName, fileName), size+1, algo, nil, shardSize); err == nil { 1825 t.Fatal("expected to fail bitrot check") 1826 } 1827 }