github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/xl-storage_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bytes" 22 "context" 23 "crypto/rand" 24 "fmt" 25 "io" 26 "os" 27 slashpath "path" 28 "runtime" 29 "strings" 30 "syscall" 31 "testing" 32 33 "github.com/google/uuid" 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(tb testing.TB) (*xlStorageDiskIDCheck, string, error) { 120 diskPath := tb.TempDir() 121 122 // Initialize a new xlStorage layer. 123 storage, err := newLocalXLStorage(diskPath) 124 if err != nil { 125 return nil, "", err 126 } 127 128 // Create a sample format.json file 129 if 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"}}`)); err != nil { 130 return nil, "", err 131 } 132 133 disk := newXLStorageDiskIDCheck(storage, false) 134 disk.SetDiskID("da017d62-70e3-45f1-8a1a-587707e69ad1") 135 return disk, diskPath, nil 136 } 137 138 // createPermDeniedFile - creates temporary directory and file with path '/mybucket/myobject' 139 func createPermDeniedFile(t *testing.T) (permDeniedDir string) { 140 var err error 141 permDeniedDir = t.TempDir() 142 143 if err = os.Mkdir(slashpath.Join(permDeniedDir, "mybucket"), 0o775); err != nil { 144 t.Fatalf(fmt.Sprintf("Unable to create temporary directory %v. %v", slashpath.Join(permDeniedDir, "mybucket"), err)) 145 } 146 147 if err = os.WriteFile(slashpath.Join(permDeniedDir, "mybucket", "myobject"), []byte(""), 0o400); err != nil { 148 t.Fatalf(fmt.Sprintf("Unable to create file %v. %v", slashpath.Join(permDeniedDir, "mybucket", "myobject"), err)) 149 } 150 151 if err = os.Chmod(slashpath.Join(permDeniedDir, "mybucket"), 0o400); err != nil { 152 t.Fatalf(fmt.Sprintf("Unable to change permission to temporary directory %v. %v", slashpath.Join(permDeniedDir, "mybucket"), err)) 153 } 154 t.Cleanup(func() { 155 os.Chmod(slashpath.Join(permDeniedDir, "mybucket"), 0o775) 156 }) 157 158 if err = os.Chmod(permDeniedDir, 0o400); err != nil { 159 t.Fatalf(fmt.Sprintf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err)) 160 } 161 t.Cleanup(func() { 162 os.Chmod(permDeniedDir, 0o775) 163 }) 164 165 return permDeniedDir 166 } 167 168 // TestXLStorages xlStorage.getDiskInfo() 169 func TestXLStorageGetDiskInfo(t *testing.T) { 170 path := t.TempDir() 171 172 testCases := []struct { 173 diskPath string 174 expectedErr error 175 }{ 176 {path, nil}, 177 {"/nonexistent-dir", errDiskNotFound}, 178 } 179 180 // Check test cases. 181 for _, testCase := range testCases { 182 if _, err := getDiskInfo(testCase.diskPath); err != testCase.expectedErr { 183 t.Fatalf("expected: %s, got: %s", testCase.expectedErr, err) 184 } 185 } 186 } 187 188 func TestXLStorageIsDirEmpty(t *testing.T) { 189 tmp := t.TempDir() 190 191 // Should give false on non-existent directory. 192 dir1 := slashpath.Join(tmp, "non-existent-directory") 193 if isDirEmpty(dir1) { 194 t.Error("expected false for non-existent directory, got true") 195 } 196 197 // Should give false for not-a-directory. 198 dir2 := slashpath.Join(tmp, "file") 199 err := os.WriteFile(dir2, []byte("hello"), 0o777) 200 if err != nil { 201 t.Fatal(err) 202 } 203 204 if isDirEmpty(dir2) { 205 t.Error("expected false for a file, got true") 206 } 207 208 // Should give true for a real empty directory. 209 dir3 := slashpath.Join(tmp, "empty") 210 err = os.Mkdir(dir3, 0o777) 211 if err != nil { 212 t.Fatal(err) 213 } 214 215 if !isDirEmpty(dir3) { 216 t.Error("expected true for empty dir, got false") 217 } 218 } 219 220 func TestXLStorageReadVersionLegacy(t *testing.T) { 221 const legacyJSON = `{"version":"1.0.1","format":"xl","stat":{"size":2016,"modTime":"2021-10-11T23:40:34.914361617Z"},"erasure":{"algorithm":"klauspost/reedsolomon/vandermonde","data":2,"parity":2,"blockSize":10485760,"index":2,"distribution":[2,3,4,1],"checksum":[{"name":"part.1","algorithm":"highwayhash256S"}]},"minio":{"release":"RELEASE.2019-12-30T05-45-39Z"},"meta":{"X-Minio-Internal-Server-Side-Encryption-Iv":"kInsJB/0yxyz/40ZI+lmQYJfZacDYqZsGh2wEiv+N50=","X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id":"my-minio-key","X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key":"eyJhZWFkIjoiQUVTLTI1Ni1HQ00tSE1BQy1TSEEtMjU2IiwiaWQiOiJjMzEwNDVjODFmMTA2MWU5NTI4ODcxZmNhMmRkYzA3YyIsIml2IjoiOWQ5cUxGMFhSaFBXbEVqT2JDMmo0QT09Iiwibm9uY2UiOiJYaERsemlCU1cwSENuK2RDIiwiYnl0ZXMiOiJUM0lmY1haQ1dtMWpLeWxBWmFUUnczbDVoYldLWW95dm5iNTZVaWJEbE5LOFZVU2tuQmx3NytIMG8yZnRzZ1UrIn0=","X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key":"IAAfANqt801MT+wwzQRkfFhTrndmhfNiN0alKwDS4AQ1dznNADRQgoq6I4pPVfRsbDp5rQawlripQZvPWUSNJA==","X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm":"DAREv2-HMAC-SHA256","content-type":"application/octet-stream","etag":"20000f00cf5e68d3d6b60e44fcd8b9e8-1"},"parts":[{"number":1,"name":"part.1","etag":"","size":2016,"actualSize":1984}]}` 222 223 // create xlStorage test setup 224 xlStorage, _, err := newXLStorageTestSetup(t) 225 if err != nil { 226 t.Fatalf("Unable to cfgreate xlStorage test setup, %s", err) 227 } 228 229 // Create files for the test cases. 230 if err = xlStorage.MakeVol(context.Background(), "exists-legacy"); err != nil { 231 t.Fatalf("Unable to create a volume \"exists-legacy\", %s", err) 232 } 233 234 if err = xlStorage.AppendFile(context.Background(), "exists-legacy", "as-file/xl.json", []byte(legacyJSON)); err != nil { 235 t.Fatalf("Unable to create a file \"as-file\", %s", err) 236 } 237 238 fi, err := xlStorage.ReadVersion(context.Background(), "", "exists-legacy", "as-file", "", ReadOptions{}) 239 if err != nil { 240 t.Fatalf("Unable to read older 'xl.json' content: %s", err) 241 } 242 243 if !fi.XLV1 { 244 t.Fatal("Unexpected 'xl.json' content should be correctly interpreted as legacy content") 245 } 246 } 247 248 // TestXLStorageReadVersion - TestXLStorages the functionality implemented by xlStorage ReadVersion storage API. 249 func TestXLStorageReadVersion(t *testing.T) { 250 // create xlStorage test setup 251 xlStorage, _, err := newXLStorageTestSetup(t) 252 if err != nil { 253 t.Fatalf("Unable to cfgreate xlStorage test setup, %s", err) 254 } 255 256 xlMeta, _ := os.ReadFile("testdata/xl.meta") 257 fi, _ := getFileInfo(xlMeta, "exists", "as-file", "", false, true) 258 259 // Create files for the test cases. 260 if err = xlStorage.MakeVol(context.Background(), "exists"); err != nil { 261 t.Fatalf("Unable to create a volume \"exists\", %s", err) 262 } 263 if err = xlStorage.AppendFile(context.Background(), "exists", "as-directory/as-file/xl.meta", xlMeta); err != nil { 264 t.Fatalf("Unable to create a file \"as-directory/as-file\", %s", err) 265 } 266 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file/xl.meta", xlMeta); err != nil { 267 t.Fatalf("Unable to create a file \"as-file\", %s", err) 268 } 269 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file-parent/xl.meta", xlMeta); err != nil { 270 t.Fatalf("Unable to create a file \"as-file-parent\", %s", err) 271 } 272 if err = xlStorage.MakeVol(context.Background(), "exists/as-file/"+fi.DataDir); err != nil { 273 t.Fatalf("Unable to create a dataDir %s, %s", fi.DataDir, err) 274 } 275 276 // TestXLStoragecases to validate different conditions for ReadVersion API. 277 testCases := []struct { 278 volume string 279 path string 280 err error 281 }{ 282 // TestXLStorage case - 1. 283 // Validate volume does not exist. 284 { 285 volume: "i-dont-exist", 286 path: "", 287 err: errVolumeNotFound, 288 }, 289 // TestXLStorage case - 2. 290 // Validate bad condition file does not exist. 291 { 292 volume: "exists", 293 path: "as-file-not-found", 294 err: errFileNotFound, 295 }, 296 // TestXLStorage case - 3. 297 // Validate bad condition file exists as prefix/directory and 298 // we are attempting to read it. 299 { 300 volume: "exists", 301 path: "as-directory", 302 err: errFileNotFound, 303 }, 304 // TestXLStorage case - 4. 305 { 306 volume: "exists", 307 path: "as-file-parent/as-file", 308 err: errFileNotFound, 309 }, 310 // TestXLStorage case - 5. 311 // Validate the good condition file exists and we are able to read it. 312 { 313 volume: "exists", 314 path: "as-file", 315 err: nil, 316 }, 317 // TestXLStorage case - 6. 318 // TestXLStorage case with invalid volume name. 319 { 320 volume: "ab", 321 path: "as-file", 322 err: errVolumeNotFound, 323 }, 324 } 325 326 // Run through all the test cases and validate for ReadVersion. 327 for i, testCase := range testCases { 328 _, err = xlStorage.ReadVersion(context.Background(), "", testCase.volume, testCase.path, "", ReadOptions{}) 329 if err != testCase.err { 330 t.Fatalf("TestXLStorage %d: Expected err \"%s\", got err \"%s\"", i+1, testCase.err, err) 331 } 332 } 333 } 334 335 // TestXLStorageReadAll - TestXLStorages the functionality implemented by xlStorage ReadAll storage API. 336 func TestXLStorageReadAll(t *testing.T) { 337 // create xlStorage test setup 338 xlStorage, _, err := newXLStorageTestSetup(t) 339 if err != nil { 340 t.Fatalf("Unable to create xlStorage test setup, %s", err) 341 } 342 343 // Create files for the test cases. 344 if err = xlStorage.MakeVol(context.Background(), "exists"); err != nil { 345 t.Fatalf("Unable to create a volume \"exists\", %s", err) 346 } 347 if err = xlStorage.AppendFile(context.Background(), "exists", "as-directory/as-file", []byte("Hello, World")); err != nil { 348 t.Fatalf("Unable to create a file \"as-directory/as-file\", %s", err) 349 } 350 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file", []byte("Hello, World")); err != nil { 351 t.Fatalf("Unable to create a file \"as-file\", %s", err) 352 } 353 if err = xlStorage.AppendFile(context.Background(), "exists", "as-file-parent", []byte("Hello, World")); err != nil { 354 t.Fatalf("Unable to create a file \"as-file-parent\", %s", err) 355 } 356 357 // TestXLStoragecases to validate different conditions for ReadAll API. 358 testCases := []struct { 359 volume string 360 path string 361 err error 362 }{ 363 // TestXLStorage case - 1. 364 // Validate volume does not exist. 365 { 366 volume: "i-dont-exist", 367 path: "", 368 err: errVolumeNotFound, 369 }, 370 // TestXLStorage case - 2. 371 // Validate bad condition file does not exist. 372 { 373 volume: "exists", 374 path: "as-file-not-found", 375 err: errFileNotFound, 376 }, 377 // TestXLStorage case - 3. 378 // Validate bad condition file exists as prefix/directory and 379 // we are attempting to read it. 380 { 381 volume: "exists", 382 path: "as-directory", 383 err: errFileNotFound, 384 }, 385 // TestXLStorage case - 4. 386 { 387 volume: "exists", 388 path: "as-file-parent/as-file", 389 err: errFileNotFound, 390 }, 391 // TestXLStorage case - 5. 392 // Validate the good condition file exists and we are able to read it. 393 { 394 volume: "exists", 395 path: "as-file", 396 err: nil, 397 }, 398 // TestXLStorage case - 6. 399 // TestXLStorage case with invalid volume name. 400 { 401 volume: "ab", 402 path: "as-file", 403 err: errVolumeNotFound, 404 }, 405 } 406 407 var dataRead []byte 408 // Run through all the test cases and validate for ReadAll. 409 for i, testCase := range testCases { 410 dataRead, err = xlStorage.ReadAll(context.Background(), testCase.volume, testCase.path) 411 if err != testCase.err { 412 t.Errorf("TestXLStorage %d: Expected err \"%v\", got err \"%v\"", i+1, testCase.err, err) 413 continue 414 } 415 if err == nil { 416 if !bytes.Equal(dataRead, []byte("Hello, World")) { 417 t.Errorf("TestXLStorage %d: Expected the data read to be \"%s\", but instead got \"%s\"", i+1, "Hello, World", string(dataRead)) 418 } 419 } 420 } 421 } 422 423 // TestNewXLStorage all the cases handled in xlStorage storage layer initialization. 424 func TestNewXLStorage(t *testing.T) { 425 // Temporary dir name. 426 tmpDirName := globalTestTmpDir + SlashSeparator + "minio-" + nextSuffix() 427 // Temporary file name. 428 tmpFileName := globalTestTmpDir + SlashSeparator + "minio-" + nextSuffix() 429 f, _ := os.Create(tmpFileName) 430 f.Close() 431 defer os.Remove(tmpFileName) 432 433 // List of all tests for xlStorage initialization. 434 testCases := []struct { 435 name string 436 err error 437 }{ 438 // Validates input argument cannot be empty. 439 { 440 "", 441 errInvalidArgument, 442 }, 443 // Validates if the directory does not exist and 444 // gets automatically created. 445 { 446 tmpDirName, 447 nil, 448 }, 449 // Validates if the disk exists as file and returns error 450 // not a directory. 451 { 452 tmpFileName, 453 errDiskNotDir, 454 }, 455 } 456 457 // Validate all test cases. 458 for i, testCase := range testCases { 459 // Initialize a new xlStorage layer. 460 _, err := newLocalXLStorage(testCase.name) 461 if err != testCase.err { 462 t.Fatalf("TestXLStorage %d failed wanted: %s, got: %s", i+1, err, testCase.err) 463 } 464 } 465 } 466 467 // TestXLStorageMakeVol - TestXLStorage validate the logic for creation of new xlStorage volume. 468 // Asserts the failures too against the expected failures. 469 func TestXLStorageMakeVol(t *testing.T) { 470 // create xlStorage test setup 471 xlStorage, path, err := newXLStorageTestSetup(t) 472 if err != nil { 473 t.Fatalf("Unable to create xlStorage test setup, %s", err) 474 } 475 476 // Setup test environment. 477 // Create a file. 478 if err := os.WriteFile(slashpath.Join(path, "vol-as-file"), []byte{}, os.ModePerm); err != nil { 479 t.Fatalf("Unable to create file, %s", err) 480 } 481 // Create a directory. 482 if err := os.Mkdir(slashpath.Join(path, "existing-vol"), 0o777); err != nil { 483 t.Fatalf("Unable to create directory, %s", err) 484 } 485 486 testCases := []struct { 487 volName string 488 expectedErr error 489 }{ 490 // TestXLStorage case - 1. 491 // A valid case, volume creation is expected to succeed. 492 { 493 volName: "success-vol", 494 expectedErr: nil, 495 }, 496 // TestXLStorage case - 2. 497 // Case where a file exists by the name of the volume to be created. 498 { 499 volName: "vol-as-file", 500 expectedErr: errVolumeExists, 501 }, 502 // TestXLStorage case - 3. 503 { 504 volName: "existing-vol", 505 expectedErr: errVolumeExists, 506 }, 507 // TestXLStorage case - 5. 508 // TestXLStorage case with invalid volume name. 509 { 510 volName: "ab", 511 expectedErr: errInvalidArgument, 512 }, 513 } 514 515 for i, testCase := range testCases { 516 if err := xlStorage.MakeVol(context.Background(), testCase.volName); err != testCase.expectedErr { 517 t.Fatalf("TestXLStorage %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 518 } 519 } 520 521 // TestXLStorage for permission denied. 522 if runtime.GOOS != globalWindowsOSName { 523 permDeniedDir := createPermDeniedFile(t) 524 if err = os.Chmod(permDeniedDir, 0o400); err != nil { 525 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 526 } 527 528 // Initialize xlStorage storage layer for permission denied error. 529 _, err = newLocalXLStorage(permDeniedDir) 530 if err != nil && err != errDiskAccessDenied { 531 t.Fatalf("Unable to initialize xlStorage, %s", err) 532 } 533 534 if err = os.Chmod(permDeniedDir, 0o755); err != nil { 535 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 536 } 537 538 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 539 if err != nil { 540 t.Fatalf("Unable to initialize xlStorage, %s", err) 541 } 542 543 // change backend permissions for MakeVol error. 544 if err = os.Chmod(permDeniedDir, 0o400); err != nil { 545 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 546 } 547 548 if err := xlStorageNew.MakeVol(context.Background(), "test-vol"); err != errDiskAccessDenied { 549 t.Fatalf("expected: %s, got: %s", errDiskAccessDenied, err) 550 } 551 } 552 } 553 554 // TestXLStorageDeleteVol - Validates the expected behavior of xlStorage.DeleteVol for various cases. 555 func TestXLStorageDeleteVol(t *testing.T) { 556 // create xlStorage test setup 557 xlStorage, path, err := newXLStorageTestSetup(t) 558 if err != nil { 559 t.Fatalf("Unable to create xlStorage test setup, %s", err) 560 } 561 562 // Setup test environment. 563 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 564 t.Fatalf("Unable to create volume, %s", err) 565 } 566 567 // TestXLStorage failure cases. 568 vol := slashpath.Join(path, "nonempty-vol") 569 if err = os.Mkdir(vol, 0o777); err != nil { 570 t.Fatalf("Unable to create directory, %s", err) 571 } 572 if err = os.WriteFile(slashpath.Join(vol, "test-file"), []byte{}, os.ModePerm); err != nil { 573 t.Fatalf("Unable to create file, %s", err) 574 } 575 576 testCases := []struct { 577 volName string 578 expectedErr error 579 }{ 580 // TestXLStorage case - 1. 581 // A valida case. Empty vol, should be possible to delete. 582 { 583 volName: "success-vol", 584 expectedErr: nil, 585 }, 586 // TestXLStorage case - 2. 587 // volume is non-existent. 588 { 589 volName: "nonexistent-vol", 590 expectedErr: errVolumeNotFound, 591 }, 592 // TestXLStorage case - 3. 593 // It shouldn't be possible to delete an non-empty volume, validating the same. 594 { 595 volName: "nonempty-vol", 596 expectedErr: errVolumeNotEmpty, 597 }, 598 // TestXLStorage case - 5. 599 // Invalid volume name. 600 { 601 volName: "ab", 602 expectedErr: errVolumeNotFound, 603 }, 604 } 605 606 for i, testCase := range testCases { 607 if err = xlStorage.DeleteVol(context.Background(), testCase.volName, false); err != testCase.expectedErr { 608 t.Fatalf("TestXLStorage: %d, expected: %s, got: %s", i+1, testCase.expectedErr, err) 609 } 610 } 611 612 // TestXLStorage for permission denied. 613 if runtime.GOOS != globalWindowsOSName { 614 permDeniedDir := t.TempDir() 615 if err = os.Mkdir(slashpath.Join(permDeniedDir, "mybucket"), 0o400); err != nil { 616 t.Fatalf("Unable to create temporary directory %v. %v", slashpath.Join(permDeniedDir, "mybucket"), err) 617 } 618 t.Cleanup(func() { 619 os.Chmod(slashpath.Join(permDeniedDir, "mybucket"), 0o775) 620 }) 621 622 if err = os.Chmod(permDeniedDir, 0o400); err != nil { 623 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 624 } 625 t.Cleanup(func() { 626 os.Chmod(permDeniedDir, 0o775) 627 }) 628 629 // Initialize xlStorage storage layer for permission denied error. 630 _, err = newLocalXLStorage(permDeniedDir) 631 if err != nil && err != errDiskAccessDenied { 632 t.Fatalf("Unable to initialize xlStorage, %s", err) 633 } 634 635 if err = os.Chmod(permDeniedDir, 0o755); err != nil { 636 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 637 } 638 639 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 640 if err != nil { 641 t.Fatalf("Unable to initialize xlStorage, %s", err) 642 } 643 644 // change backend permissions for MakeVol error. 645 if err = os.Chmod(permDeniedDir, 0o400); err != nil { 646 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 647 } 648 649 if err = xlStorageNew.DeleteVol(context.Background(), "mybucket", false); err != errDiskAccessDenied { 650 t.Fatalf("expected: Permission error, got: %s", err) 651 } 652 } 653 654 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup(t) 655 if err != nil { 656 t.Fatalf("Unable to create xlStorage test setup, %s", err) 657 } 658 // removing the disk, used to recreate disk not found error. 659 os.RemoveAll(diskPath) 660 661 // TestXLStorage for delete on an removed disk. 662 // should fail with disk not found. 663 err = xlStorageDeletedStorage.DeleteVol(context.Background(), "Del-Vol", false) 664 if err != errDiskNotFound { 665 t.Errorf("Expected: \"Drive not found\", got \"%s\"", err) 666 } 667 } 668 669 // TestXLStorageStatVol - TestXLStorages validate the volume info returned by xlStorage.StatVol() for various inputs. 670 func TestXLStorageStatVol(t *testing.T) { 671 // create xlStorage test setup 672 xlStorage, _, err := newXLStorageTestSetup(t) 673 if err != nil { 674 t.Fatalf("Unable to create xlStorage test setup, %s", err) 675 } 676 677 // Setup test environment. 678 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 679 t.Fatalf("Unable to create volume, %s", err) 680 } 681 682 testCases := []struct { 683 volName string 684 expectedErr error 685 }{ 686 // TestXLStorage case - 1. 687 { 688 volName: "success-vol", 689 expectedErr: nil, 690 }, 691 // TestXLStorage case - 2. 692 { 693 volName: "nonexistent-vol", 694 expectedErr: errVolumeNotFound, 695 }, 696 // TestXLStorage case - 3. 697 { 698 volName: "ab", 699 expectedErr: errVolumeNotFound, 700 }, 701 } 702 703 for i, testCase := range testCases { 704 var volInfo VolInfo 705 volInfo, err = xlStorage.StatVol(context.Background(), testCase.volName) 706 if err != testCase.expectedErr { 707 t.Fatalf("TestXLStorage case : %d, Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 708 } 709 710 if err == nil { 711 if volInfo.Name != testCase.volName { 712 t.Errorf("TestXLStorage case %d: Expected the volume name to be \"%s\", instead found \"%s\"", 713 i+1, volInfo.Name, testCase.volName) 714 } 715 } 716 } 717 718 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup(t) 719 if err != nil { 720 t.Fatalf("Unable to create xlStorage test setup, %s", err) 721 } 722 // removing the disk, used to recreate disk not found error. 723 os.RemoveAll(diskPath) 724 725 // TestXLStorage for delete on an removed disk. 726 // should fail with disk not found. 727 _, err = xlStorageDeletedStorage.StatVol(context.Background(), "Stat vol") 728 if err != errDiskNotFound { 729 t.Errorf("Expected: \"Drive not found\", got \"%s\"", err) 730 } 731 } 732 733 // TestXLStorageListVols - Validates the result and the error output for xlStorage volume listing functionality xlStorage.ListVols(). 734 func TestXLStorageListVols(t *testing.T) { 735 // create xlStorage test setup 736 xlStorage, path, err := newXLStorageTestSetup(t) 737 if err != nil { 738 t.Fatalf("Unable to create xlStorage test setup, %s", err) 739 } 740 741 var volInfos []VolInfo 742 // TestXLStorage empty list vols. 743 if volInfos, err = xlStorage.ListVols(context.Background()); err != nil { 744 t.Fatalf("expected: <nil>, got: %s", err) 745 } else if len(volInfos) != 1 { 746 t.Fatalf("expected: one entry, got: %s", volInfos) 747 } 748 749 // TestXLStorage non-empty list vols. 750 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 751 t.Fatalf("Unable to create volume, %s", err) 752 } 753 754 volInfos, err = xlStorage.ListVols(context.Background()) 755 if err != nil { 756 t.Fatalf("expected: <nil>, got: %s", err) 757 } 758 if len(volInfos) != 2 { 759 t.Fatalf("expected: 2, got: %d", len(volInfos)) 760 } 761 volFound := false 762 for _, info := range volInfos { 763 if info.Name == "success-vol" { 764 volFound = true 765 break 766 } 767 } 768 if !volFound { 769 t.Errorf("expected: success-vol to be created") 770 } 771 772 // removing the path and simulating disk failure 773 os.RemoveAll(path) 774 // should fail with errDiskNotFound. 775 if _, err = xlStorage.ListVols(context.Background()); err != errDiskNotFound { 776 t.Errorf("Expected to fail with \"%s\", but instead failed with \"%s\"", errDiskNotFound, err) 777 } 778 } 779 780 // TestXLStorageListDir - TestXLStorages validate the directory listing functionality provided by xlStorage.ListDir . 781 func TestXLStorageListDir(t *testing.T) { 782 // create xlStorage test setup 783 xlStorage, _, err := newXLStorageTestSetup(t) 784 if err != nil { 785 t.Fatalf("Unable to create xlStorage test setup, %s", err) 786 } 787 788 // create xlStorage test setup. 789 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup(t) 790 if err != nil { 791 t.Fatalf("Unable to create xlStorage test setup, %s", err) 792 } 793 // removing the disk, used to recreate disk not found error. 794 os.RemoveAll(diskPath) 795 // Setup test environment. 796 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 797 t.Fatalf("Unable to create volume, %s", err) 798 } 799 if err = xlStorage.AppendFile(context.Background(), "success-vol", "abc/def/ghi/success-file", []byte("Hello, world")); err != nil { 800 t.Fatalf("Unable to create file, %s", err) 801 } 802 if err = xlStorage.AppendFile(context.Background(), "success-vol", "abc/xyz/ghi/success-file", []byte("Hello, world")); err != nil { 803 t.Fatalf("Unable to create file, %s", err) 804 } 805 806 testCases := []struct { 807 srcVol string 808 srcPath string 809 // expected result. 810 expectedListDir []string 811 expectedErr error 812 }{ 813 // TestXLStorage case - 1. 814 // valid case with existing volume and file to delete. 815 { 816 srcVol: "success-vol", 817 srcPath: "abc", 818 expectedListDir: []string{"def/", "xyz/"}, 819 expectedErr: nil, 820 }, 821 // TestXLStorage case - 1. 822 // valid case with existing volume and file to delete. 823 { 824 srcVol: "success-vol", 825 srcPath: "abc/def", 826 expectedListDir: []string{"ghi/"}, 827 expectedErr: nil, 828 }, 829 // TestXLStorage case - 1. 830 // valid case with existing volume and file to delete. 831 { 832 srcVol: "success-vol", 833 srcPath: "abc/def/ghi", 834 expectedListDir: []string{"success-file"}, 835 expectedErr: nil, 836 }, 837 // TestXLStorage case - 2. 838 { 839 srcVol: "success-vol", 840 srcPath: "abcdef", 841 expectedErr: errFileNotFound, 842 }, 843 // TestXLStorage case - 3. 844 // TestXLStorage case with invalid volume name. 845 { 846 srcVol: "ab", 847 srcPath: "success-file", 848 expectedErr: errVolumeNotFound, 849 }, 850 // TestXLStorage case - 4. 851 // TestXLStorage case with non existent volume. 852 { 853 srcVol: "non-existent-vol", 854 srcPath: "success-file", 855 expectedErr: errVolumeNotFound, 856 }, 857 } 858 859 for i, testCase := range testCases { 860 var dirList []string 861 dirList, err = xlStorage.ListDir(context.Background(), "", testCase.srcVol, testCase.srcPath, -1) 862 if err != testCase.expectedErr { 863 t.Errorf("TestXLStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 864 } 865 if err == nil { 866 for _, expected := range testCase.expectedListDir { 867 if !strings.Contains(strings.Join(dirList, ","), expected) { 868 t.Errorf("TestXLStorage case %d: Expected the directory listing to be \"%v\", but got \"%v\"", i+1, testCase.expectedListDir, dirList) 869 } 870 } 871 } 872 } 873 874 // TestXLStorage for permission denied. 875 if runtime.GOOS != globalWindowsOSName { 876 permDeniedDir := createPermDeniedFile(t) 877 878 // Initialize xlStorage storage layer for permission denied error. 879 _, err = newLocalXLStorage(permDeniedDir) 880 if err != nil && err != errDiskAccessDenied { 881 t.Fatalf("Unable to initialize xlStorage, %s", err) 882 } 883 884 if err = os.Chmod(permDeniedDir, 0o755); err != nil { 885 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 886 } 887 888 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 889 if err != nil { 890 t.Fatalf("Unable to initialize xlStorage, %s", err) 891 } 892 893 if err = xlStorageNew.Delete(context.Background(), "mybucket", "myobject", DeleteOptions{ 894 Recursive: false, 895 Immediate: false, 896 }); err != errFileAccessDenied { 897 t.Errorf("expected: %s, got: %s", errFileAccessDenied, err) 898 } 899 } 900 901 // TestXLStorage for delete on an removed disk. 902 // should fail with disk not found. 903 err = xlStorageDeletedStorage.Delete(context.Background(), "del-vol", "my-file", DeleteOptions{ 904 Recursive: false, 905 Immediate: false, 906 }) 907 if err != errDiskNotFound { 908 t.Errorf("Expected: \"Drive not found\", got \"%s\"", err) 909 } 910 } 911 912 // TestXLStorageDeleteFile - Series of test cases construct valid and invalid input data and validates the result and the error response. 913 func TestXLStorageDeleteFile(t *testing.T) { 914 if runtime.GOOS == globalWindowsOSName { 915 t.Skip() 916 } 917 918 // create xlStorage test setup 919 xlStorage, path, err := newXLStorageTestSetup(t) 920 if err != nil { 921 t.Fatalf("Unable to create xlStorage test setup, %s", err) 922 } 923 924 // Setup test environment. 925 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 926 t.Fatalf("Unable to create volume, %s", err) 927 } 928 if err = xlStorage.AppendFile(context.Background(), "success-vol", "success-file", []byte("Hello, world")); err != nil { 929 t.Fatalf("Unable to create file, %s", err) 930 } 931 932 if err = xlStorage.MakeVol(context.Background(), "no-permissions"); err != nil { 933 t.Fatalf("Unable to create volume, %s", err.Error()) 934 } 935 if err = xlStorage.AppendFile(context.Background(), "no-permissions", "dir/file", []byte("Hello, world")); err != nil { 936 t.Fatalf("Unable to create file, %s", err.Error()) 937 } 938 // Parent directory must have write permissions, this is read + execute. 939 if err = os.Chmod(pathJoin(path, "no-permissions"), 0o555); err != nil { 940 t.Fatalf("Unable to chmod directory, %s", err.Error()) 941 } 942 t.Cleanup(func() { 943 os.Chmod(pathJoin(path, "no-permissions"), 0o775) 944 }) 945 946 testCases := []struct { 947 srcVol string 948 srcPath string 949 expectedErr error 950 }{ 951 // TestXLStorage case - 1. 952 // valid case with existing volume and file to delete. 953 { 954 srcVol: "success-vol", 955 srcPath: "success-file", 956 expectedErr: nil, 957 }, 958 // TestXLStorage case - 2. 959 // The file was deleted in the last case, so Delete should not fail. 960 { 961 srcVol: "success-vol", 962 srcPath: "success-file", 963 expectedErr: nil, 964 }, 965 // TestXLStorage case - 3. 966 // TestXLStorage case with segment of the volume name > 255. 967 { 968 srcVol: "my", 969 srcPath: "success-file", 970 expectedErr: errVolumeNotFound, 971 }, 972 // TestXLStorage case - 4. 973 // TestXLStorage case with non-existent volume. 974 { 975 srcVol: "non-existent-vol", 976 srcPath: "success-file", 977 expectedErr: errVolumeNotFound, 978 }, 979 // TestXLStorage case - 5. 980 // TestXLStorage case with src path segment > 255. 981 { 982 srcVol: "success-vol", 983 srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 984 expectedErr: errFileNameTooLong, 985 }, 986 } 987 988 for i, testCase := range testCases { 989 if err = xlStorage.Delete(context.Background(), testCase.srcVol, testCase.srcPath, DeleteOptions{ 990 Recursive: false, 991 Immediate: false, 992 }); err != testCase.expectedErr { 993 t.Errorf("TestXLStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 994 } 995 } 996 997 // TestXLStorage for permission denied. 998 if runtime.GOOS != globalWindowsOSName { 999 permDeniedDir := createPermDeniedFile(t) 1000 1001 // Initialize xlStorage storage layer for permission denied error. 1002 _, err = newLocalXLStorage(permDeniedDir) 1003 if err != nil && err != errDiskAccessDenied { 1004 t.Fatalf("Unable to initialize xlStorage, %s", err) 1005 } 1006 1007 if err = os.Chmod(permDeniedDir, 0o755); err != nil { 1008 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 1009 } 1010 1011 xlStorageNew, err := newLocalXLStorage(permDeniedDir) 1012 if err != nil { 1013 t.Fatalf("Unable to initialize xlStorage, %s", err) 1014 } 1015 1016 if err = xlStorageNew.Delete(context.Background(), "mybucket", "myobject", DeleteOptions{ 1017 Recursive: false, 1018 Immediate: false, 1019 }); err != errFileAccessDenied { 1020 t.Errorf("expected: %s, got: %s", errFileAccessDenied, err) 1021 } 1022 } 1023 1024 // create xlStorage test setup 1025 xlStorageDeletedStorage, diskPath, err := newXLStorageTestSetup(t) 1026 if err != nil { 1027 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1028 } 1029 // removing the disk, used to recreate disk not found error. 1030 err = os.RemoveAll(diskPath) 1031 if err != nil { 1032 t.Fatalf("Unable to remoe xlStorage diskpath, %s", err) 1033 } 1034 1035 // TestXLStorage for delete on an removed disk. 1036 // should fail with disk not found. 1037 err = xlStorageDeletedStorage.Delete(context.Background(), "del-vol", "my-file", DeleteOptions{ 1038 Recursive: false, 1039 Immediate: false, 1040 }) 1041 if err != errDiskNotFound { 1042 t.Errorf("Expected: \"Drive not found\", got \"%s\"", err) 1043 } 1044 } 1045 1046 // TestXLStorageReadFile - TestXLStorages xlStorage.ReadFile with wide range of cases and asserts the result and error response. 1047 func TestXLStorageReadFile(t *testing.T) { 1048 // create xlStorage test setup 1049 xlStorage, path, err := newXLStorageTestSetup(t) 1050 if err != nil { 1051 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1052 } 1053 1054 volume := "success-vol" 1055 // Setup test environment. 1056 if err = xlStorage.MakeVol(context.Background(), volume); err != nil { 1057 t.Fatalf("Unable to create volume, %s", err) 1058 } 1059 1060 // Create directory to make errIsNotRegular 1061 if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0o777); err != nil { 1062 t.Fatalf("Unable to create directory, %s", err) 1063 } 1064 1065 testCases := []struct { 1066 volume string 1067 fileName string 1068 offset int64 1069 bufSize int 1070 expectedBuf []byte 1071 expectedErr error 1072 }{ 1073 // Successful read at offset 0 and proper buffer size. - 1 1074 { 1075 volume, "myobject", 0, 5, 1076 []byte("hello"), nil, 1077 }, 1078 // Success read at hierarchy. - 2 1079 { 1080 volume, "path/to/my/object", 0, 5, 1081 []byte("hello"), nil, 1082 }, 1083 // Object is a directory. - 3 1084 { 1085 volume, "object-as-dir", 1086 0, 5, nil, errIsNotRegular, 1087 }, 1088 // One path segment length is > 255 chars long. - 4 1089 { 1090 volume, "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1091 0, 5, nil, errFileNameTooLong, 1092 }, 1093 // Path length is > 1024 chars long. - 5 1094 { 1095 volume, "level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1096 0, 5, nil, errFileNameTooLong, 1097 }, 1098 // Buffer size greater than object size. - 6 1099 { 1100 volume, "myobject", 0, 16, 1101 []byte("hello, world"), 1102 io.ErrUnexpectedEOF, 1103 }, 1104 // Reading from an offset success. - 7 1105 { 1106 volume, "myobject", 7, 5, 1107 []byte("world"), nil, 1108 }, 1109 // Reading from an object but buffer size greater. - 8 1110 { 1111 volume, "myobject", 1112 7, 8, 1113 []byte("world"), 1114 io.ErrUnexpectedEOF, 1115 }, 1116 // Seeking ahead returns io.EOF. - 9 1117 { 1118 volume, "myobject", 14, 1, nil, io.EOF, 1119 }, 1120 // Empty volume name. - 10 1121 { 1122 "", "myobject", 14, 1, nil, errVolumeNotFound, 1123 }, 1124 // Empty filename name. - 11 1125 { 1126 volume, "", 14, 1, nil, errIsNotRegular, 1127 }, 1128 // Non existent volume name - 12 1129 { 1130 "abcd", "", 14, 1, nil, errVolumeNotFound, 1131 }, 1132 // Non existent filename - 13 1133 { 1134 volume, "abcd", 14, 1, nil, errFileNotFound, 1135 }, 1136 } 1137 1138 // Create all files needed during testing. 1139 appendFiles := testCases[:4] 1140 v := NewBitrotVerifier(SHA256, getSHA256Sum([]byte("hello, world"))) 1141 // Create test files for further reading. 1142 for i, appendFile := range appendFiles { 1143 err = xlStorage.AppendFile(context.Background(), volume, appendFile.fileName, []byte("hello, world")) 1144 if err != appendFile.expectedErr { 1145 t.Fatalf("Creating file failed: %d %#v, expected: %s, got: %s", i+1, appendFile, appendFile.expectedErr, err) 1146 } 1147 } 1148 1149 { 1150 buf := make([]byte, 5) 1151 // Test for negative offset. 1152 if _, err = xlStorage.ReadFile(context.Background(), volume, "myobject", -1, buf, v); err == nil { 1153 t.Fatalf("expected: error, got: <nil>") 1154 } 1155 } 1156 1157 for l := 0; l < 2; l++ { 1158 // Following block validates all ReadFile test cases. 1159 for i, testCase := range testCases { 1160 var n int64 1161 // Common read buffer. 1162 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 returned, proceed further to validate the returned results. 1204 if err != nil && testCase.expectedErr == nil { 1205 t.Errorf("Case: %d %#v, expected: %s, got :%s", i+1, testCase, testCase.expectedErr, err) 1206 } 1207 if err == nil { 1208 if !bytes.Equal(testCase.expectedBuf, buf) { 1209 t.Errorf("Case: %d %#v, expected: \"%s\", got: \"%s\"", i+1, testCase, string(testCase.expectedBuf), string(buf[:testCase.bufSize])) 1210 } 1211 if n != int64(testCase.bufSize) { 1212 t.Errorf("Case: %d %#v, expected: %d, got: %d", i+1, testCase, testCase.bufSize, n) 1213 } 1214 } 1215 } 1216 } 1217 1218 // TestXLStorage for permission denied. 1219 if runtime.GOOS != globalWindowsOSName { 1220 permDeniedDir := createPermDeniedFile(t) 1221 1222 // Initialize xlStorage storage layer for permission denied error. 1223 _, err = newLocalXLStorage(permDeniedDir) 1224 if err != nil && err != errDiskAccessDenied { 1225 t.Fatalf("Unable to initialize xlStorage, %s", err) 1226 } 1227 1228 if err = os.Chmod(permDeniedDir, 0o755); err != nil { 1229 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 1230 } 1231 1232 xlStoragePermStorage, err := newLocalXLStorage(permDeniedDir) 1233 if err != nil { 1234 t.Fatalf("Unable to initialize xlStorage, %s", err) 1235 } 1236 1237 // Common read buffer. 1238 buf := make([]byte, 10) 1239 if _, err = xlStoragePermStorage.ReadFile(context.Background(), "mybucket", "myobject", 0, buf, v); err != errFileAccessDenied { 1240 t.Errorf("expected: %s, got: %s", errFileAccessDenied, err) 1241 } 1242 } 1243 } 1244 1245 var xlStorageReadFileWithVerifyTests = []struct { 1246 file string 1247 offset int 1248 length int 1249 algorithm BitrotAlgorithm 1250 expError error 1251 }{ 1252 {file: "myobject", offset: 0, length: 100, algorithm: SHA256, expError: nil}, // 0 1253 {file: "myobject", offset: 25, length: 74, algorithm: SHA256, expError: nil}, // 1 1254 {file: "myobject", offset: 29, length: 70, algorithm: SHA256, expError: nil}, // 2 1255 {file: "myobject", offset: 100, length: 0, algorithm: SHA256, expError: nil}, // 3 1256 {file: "myobject", offset: 1, length: 120, algorithm: SHA256, expError: errFileCorrupt}, // 4 1257 {file: "myobject", offset: 3, length: 1100, algorithm: SHA256, expError: nil}, // 5 1258 {file: "myobject", offset: 2, length: 100, algorithm: SHA256, expError: errFileCorrupt}, // 6 1259 {file: "myobject", offset: 1000, length: 1001, algorithm: SHA256, expError: nil}, // 7 1260 {file: "myobject", offset: 0, length: 100, algorithm: BLAKE2b512, expError: errFileCorrupt}, // 8 1261 {file: "myobject", offset: 25, length: 74, algorithm: BLAKE2b512, expError: nil}, // 9 1262 {file: "myobject", offset: 29, length: 70, algorithm: BLAKE2b512, expError: errFileCorrupt}, // 10 1263 {file: "myobject", offset: 100, length: 0, algorithm: BLAKE2b512, expError: nil}, // 11 1264 {file: "myobject", offset: 1, length: 120, algorithm: BLAKE2b512, expError: nil}, // 12 1265 {file: "myobject", offset: 3, length: 1100, algorithm: BLAKE2b512, expError: nil}, // 13 1266 {file: "myobject", offset: 2, length: 100, algorithm: BLAKE2b512, expError: nil}, // 14 1267 {file: "myobject", offset: 1000, length: 1001, algorithm: BLAKE2b512, expError: nil}, // 15 1268 } 1269 1270 // TestXLStorageReadFile with bitrot verification - tests the xlStorage level 1271 // ReadFile API with a BitrotVerifier. Only tests hashing related 1272 // functionality. Other functionality is tested with 1273 // TestXLStorageReadFile. 1274 func TestXLStorageReadFileWithVerify(t *testing.T) { 1275 volume, object := "test-vol", "myobject" 1276 xlStorage, _, err := newXLStorageTestSetup(t) 1277 if err != nil { 1278 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1279 } 1280 if err = xlStorage.MakeVol(context.Background(), volume); err != nil { 1281 t.Fatalf("Unable to create volume %s: %v", volume, err) 1282 } 1283 data := make([]byte, 8*1024) 1284 if _, err = io.ReadFull(rand.Reader, data); err != nil { 1285 t.Fatalf("Unable to create generate random data: %v", err) 1286 } 1287 if err = xlStorage.AppendFile(context.Background(), volume, object, data); err != nil { 1288 t.Fatalf("Unable to create object: %v", err) 1289 } 1290 1291 for i, test := range xlStorageReadFileWithVerifyTests { 1292 h := test.algorithm.New() 1293 h.Write(data) 1294 if test.expError != nil { 1295 h.Write([]byte{0}) 1296 } 1297 1298 buffer := make([]byte, test.length) 1299 n, err := xlStorage.ReadFile(context.Background(), volume, test.file, int64(test.offset), buffer, NewBitrotVerifier(test.algorithm, h.Sum(nil))) 1300 1301 switch { 1302 case err == nil && test.expError != nil: 1303 t.Errorf("Test %d: Expected error %v but got none.", i, test.expError) 1304 case err == nil && n != int64(test.length): 1305 t.Errorf("Test %d: %d bytes were expected, but %d were written", i, test.length, n) 1306 case err == nil && !bytes.Equal(data[test.offset:test.offset+test.length], buffer): 1307 t.Errorf("Test %d: Expected bytes: %v, but got: %v", i, data[test.offset:test.offset+test.length], buffer) 1308 case err != nil && err != test.expError: 1309 t.Errorf("Test %d: Expected error: %v, but got: %v", i, test.expError, err) 1310 } 1311 } 1312 } 1313 1314 // TestXLStorageFormatFileChange - to test if changing the diskID makes the calls fail. 1315 func TestXLStorageFormatFileChange(t *testing.T) { 1316 volume := "fail-vol" 1317 xlStorage, _, err := newXLStorageTestSetup(t) 1318 if err != nil { 1319 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1320 } 1321 1322 if err = xlStorage.MakeVol(context.Background(), volume); err != nil { 1323 t.Fatalf("MakeVol failed with %s", err) 1324 } 1325 1326 // Change the format.json such that "this" is changed to "randomid". 1327 if err = os.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"}}`), 0o644); err != nil { 1328 t.Fatalf("ioutil.WriteFile failed with %s", err) 1329 } 1330 1331 err = xlStorage.MakeVol(context.Background(), volume) 1332 if err != errVolumeExists { 1333 t.Fatalf("MakeVol expected to fail with errDiskNotFound but failed with %s", err) 1334 } 1335 } 1336 1337 // TestXLStorage xlStorage.AppendFile() 1338 func TestXLStorageAppendFile(t *testing.T) { 1339 // create xlStorage test setup 1340 xlStorage, path, err := newXLStorageTestSetup(t) 1341 if err != nil { 1342 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1343 } 1344 1345 // Setup test environment. 1346 if err = xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 1347 t.Fatalf("Unable to create volume, %s", err) 1348 } 1349 1350 // Create directory to make errIsNotRegular 1351 if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0o777); err != nil { 1352 t.Fatalf("Unable to create directory, %s", err) 1353 } 1354 1355 testCases := []struct { 1356 fileName string 1357 expectedErr error 1358 }{ 1359 {"myobject", nil}, 1360 {"path/to/my/object", nil}, 1361 // TestXLStorage to append to previously created file. 1362 {"myobject", nil}, 1363 // TestXLStorage to use same path of previously created file. 1364 {"path/to/my/testobject", nil}, 1365 // TestXLStorage to use object is a directory now. 1366 {"object-as-dir", errIsNotRegular}, 1367 // path segment uses previously uploaded object. 1368 {"myobject/testobject", errFileAccessDenied}, 1369 // One path segment length is > 255 chars long. 1370 {"path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong}, 1371 // path length is > 1024 chars long. 1372 {"level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong}, 1373 } 1374 1375 for i, testCase := range testCases { 1376 if err = xlStorage.AppendFile(context.Background(), "success-vol", testCase.fileName, []byte("hello, world")); err != testCase.expectedErr { 1377 t.Errorf("Case: %d, expected: %s, got: %s", i+1, testCase.expectedErr, err) 1378 } 1379 } 1380 1381 // TestXLStorage for permission denied. 1382 if runtime.GOOS != globalWindowsOSName { 1383 permDeniedDir := createPermDeniedFile(t) 1384 1385 var xlStoragePermStorage StorageAPI 1386 // Initialize xlStorage storage layer for permission denied error. 1387 _, err = newLocalXLStorage(permDeniedDir) 1388 if err != nil && err != errDiskAccessDenied { 1389 t.Fatalf("Unable to initialize xlStorage, %s", err) 1390 } 1391 1392 if err = os.Chmod(permDeniedDir, 0o755); err != nil { 1393 t.Fatalf("Unable to change permission to temporary directory %v. %v", permDeniedDir, err) 1394 } 1395 1396 xlStoragePermStorage, err = newLocalXLStorage(permDeniedDir) 1397 if err != nil { 1398 t.Fatalf("Unable to initialize xlStorage, %s", err) 1399 } 1400 1401 if err = xlStoragePermStorage.AppendFile(context.Background(), "mybucket", "myobject", []byte("hello, world")); err != errFileAccessDenied { 1402 t.Fatalf("expected: errFileAccessDenied error, got: %s", err) 1403 } 1404 } 1405 1406 // TestXLStorage case with invalid volume name. 1407 // A valid volume name should be at least of size 3. 1408 err = xlStorage.AppendFile(context.Background(), "bn", "yes", []byte("hello, world")) 1409 if err != errVolumeNotFound { 1410 t.Fatalf("expected: \"Invalid argument error\", got: \"%s\"", err) 1411 } 1412 } 1413 1414 // TestXLStorage xlStorage.RenameFile() 1415 func TestXLStorageRenameFile(t *testing.T) { 1416 // create xlStorage test setup 1417 xlStorage, _, err := newXLStorageTestSetup(t) 1418 if err != nil { 1419 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1420 } 1421 1422 // Setup test environment. 1423 if err := xlStorage.MakeVol(context.Background(), "src-vol"); err != nil { 1424 t.Fatalf("Unable to create volume, %s", err) 1425 } 1426 1427 if err := xlStorage.MakeVol(context.Background(), "dest-vol"); err != nil { 1428 t.Fatalf("Unable to create volume, %s", err) 1429 } 1430 1431 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file1", []byte("Hello, world")); err != nil { 1432 t.Fatalf("Unable to create file, %s", err) 1433 } 1434 1435 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file2", []byte("Hello, world")); err != nil { 1436 t.Fatalf("Unable to create file, %s", err) 1437 } 1438 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file3", []byte("Hello, world")); err != nil { 1439 t.Fatalf("Unable to create file, %s", err) 1440 } 1441 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file4", []byte("Hello, world")); err != nil { 1442 t.Fatalf("Unable to create file, %s", err) 1443 } 1444 1445 if err := xlStorage.AppendFile(context.Background(), "src-vol", "file5", []byte("Hello, world")); err != nil { 1446 t.Fatalf("Unable to create file, %s", err) 1447 } 1448 if err := xlStorage.AppendFile(context.Background(), "src-vol", "path/to/file1", []byte("Hello, world")); err != nil { 1449 t.Fatalf("Unable to create file, %s", err) 1450 } 1451 1452 testCases := []struct { 1453 srcVol string 1454 destVol string 1455 srcPath string 1456 destPath string 1457 expectedErr error 1458 }{ 1459 // TestXLStorage case - 1. 1460 { 1461 srcVol: "src-vol", 1462 destVol: "dest-vol", 1463 srcPath: "file1", 1464 destPath: "file-one", 1465 expectedErr: nil, 1466 }, 1467 // TestXLStorage case - 2. 1468 { 1469 srcVol: "src-vol", 1470 destVol: "dest-vol", 1471 srcPath: "path/", 1472 destPath: "new-path/", 1473 expectedErr: nil, 1474 }, 1475 // TestXLStorage case - 3. 1476 // TestXLStorage to overwrite destination file. 1477 { 1478 srcVol: "src-vol", 1479 destVol: "dest-vol", 1480 srcPath: "file2", 1481 destPath: "file-one", 1482 expectedErr: nil, 1483 }, 1484 // TestXLStorage case - 4. 1485 // TestXLStorage case with io error count set to 1. 1486 // expected not to fail. 1487 { 1488 srcVol: "src-vol", 1489 destVol: "dest-vol", 1490 srcPath: "file3", 1491 destPath: "file-two", 1492 expectedErr: nil, 1493 }, 1494 // TestXLStorage case - 5. 1495 // TestXLStorage case with io error count set to maximum allowed count. 1496 // expected not to fail. 1497 { 1498 srcVol: "src-vol", 1499 destVol: "dest-vol", 1500 srcPath: "file4", 1501 destPath: "file-three", 1502 expectedErr: nil, 1503 }, 1504 // TestXLStorage case - 6. 1505 // TestXLStorage case with non-existent source file. 1506 { 1507 srcVol: "src-vol", 1508 destVol: "dest-vol", 1509 srcPath: "non-existent-file", 1510 destPath: "file-three", 1511 expectedErr: errFileNotFound, 1512 }, 1513 // TestXLStorage case - 7. 1514 // TestXLStorage to check failure of source and destination are not same type. 1515 { 1516 srcVol: "src-vol", 1517 destVol: "dest-vol", 1518 srcPath: "path/", 1519 destPath: "file-one", 1520 expectedErr: errFileAccessDenied, 1521 }, 1522 // TestXLStorage case - 8. 1523 // TestXLStorage to check failure of destination directory exists. 1524 { 1525 srcVol: "src-vol", 1526 destVol: "dest-vol", 1527 srcPath: "path/", 1528 destPath: "new-path/", 1529 expectedErr: errFileAccessDenied, 1530 }, 1531 // TestXLStorage case - 9. 1532 // TestXLStorage case with source being a file and destination being a directory. 1533 // Either both have to be files or directories. 1534 // Expecting to fail with `errFileAccessDenied`. 1535 { 1536 srcVol: "src-vol", 1537 destVol: "dest-vol", 1538 srcPath: "file4", 1539 destPath: "new-path/", 1540 expectedErr: errFileAccessDenied, 1541 }, 1542 // TestXLStorage case - 10. 1543 // TestXLStorage case with non-existent source volume. 1544 // Expecting to fail with `errVolumeNotFound`. 1545 { 1546 srcVol: "src-vol-non-existent", 1547 destVol: "dest-vol", 1548 srcPath: "file4", 1549 destPath: "new-path/", 1550 expectedErr: errVolumeNotFound, 1551 }, 1552 // TestXLStorage case - 11. 1553 // TestXLStorage case with non-existent destination volume. 1554 // Expecting to fail with `errVolumeNotFound`. 1555 { 1556 srcVol: "src-vol", 1557 destVol: "dest-vol-non-existent", 1558 srcPath: "file4", 1559 destPath: "new-path/", 1560 expectedErr: errVolumeNotFound, 1561 }, 1562 // TestXLStorage case - 12. 1563 // TestXLStorage case with invalid src volume name. Length should be at least 3. 1564 // Expecting to fail with `errInvalidArgument`. 1565 { 1566 srcVol: "ab", 1567 destVol: "dest-vol-non-existent", 1568 srcPath: "file4", 1569 destPath: "new-path/", 1570 expectedErr: errVolumeNotFound, 1571 }, 1572 // TestXLStorage case - 13. 1573 // TestXLStorage case with invalid destination volume name. Length should be at least 3. 1574 // Expecting to fail with `errInvalidArgument`. 1575 { 1576 srcVol: "abcd", 1577 destVol: "ef", 1578 srcPath: "file4", 1579 destPath: "new-path/", 1580 expectedErr: errVolumeNotFound, 1581 }, 1582 // TestXLStorage case - 14. 1583 // TestXLStorage case with invalid destination volume name. Length should be at least 3. 1584 // Expecting to fail with `errInvalidArgument`. 1585 { 1586 srcVol: "abcd", 1587 destVol: "ef", 1588 srcPath: "file4", 1589 destPath: "new-path/", 1590 expectedErr: errVolumeNotFound, 1591 }, 1592 // TestXLStorage case - 15. 1593 // TestXLStorage case with the parent of the destination being a file. 1594 // expected to fail with `errFileAccessDenied`. 1595 { 1596 srcVol: "src-vol", 1597 destVol: "dest-vol", 1598 srcPath: "file5", 1599 destPath: "file-one/parent-is-file", 1600 expectedErr: errFileAccessDenied, 1601 }, 1602 // TestXLStorage case - 16. 1603 // TestXLStorage case with segment of source file name more than 255. 1604 // expected not to fail. 1605 { 1606 srcVol: "src-vol", 1607 destVol: "dest-vol", 1608 srcPath: "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1609 destPath: "file-six", 1610 expectedErr: errFileNameTooLong, 1611 }, 1612 // TestXLStorage case - 17. 1613 // TestXLStorage case with segment of destination file name more than 255. 1614 // expected not to fail. 1615 { 1616 srcVol: "src-vol", 1617 destVol: "dest-vol", 1618 srcPath: "file6", 1619 destPath: "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 1620 expectedErr: errFileNameTooLong, 1621 }, 1622 } 1623 1624 for i, testCase := range testCases { 1625 if err := xlStorage.RenameFile(context.Background(), testCase.srcVol, testCase.srcPath, testCase.destVol, testCase.destPath); err != testCase.expectedErr { 1626 t.Fatalf("TestXLStorage %d: Expected the error to be : \"%v\", got: \"%v\".", i+1, testCase.expectedErr, err) 1627 } 1628 } 1629 } 1630 1631 // TestXLStorageDeleteVersion will test if version deletes and bulk deletes work as expected. 1632 func TestXLStorageDeleteVersion(t *testing.T) { 1633 // create xlStorage test setup 1634 xl, _, err := newXLStorageTestSetup(t) 1635 if err != nil { 1636 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1637 } 1638 ctx := context.Background() 1639 1640 volume := "myvol-vol" 1641 object := "my-object" 1642 if err := xl.MakeVol(ctx, volume); err != nil { 1643 t.Fatalf("Unable to create volume, %s", err) 1644 } 1645 var versions [50]string 1646 for i := range versions { 1647 versions[i] = uuid.New().String() 1648 fi := FileInfo{ 1649 Name: object, Volume: volume, VersionID: versions[i], ModTime: UTCNow(), DataDir: "", Size: 10000, 1650 Erasure: ErasureInfo{ 1651 Algorithm: erasureAlgorithm, 1652 DataBlocks: 4, 1653 ParityBlocks: 4, 1654 BlockSize: blockSizeV2, 1655 Index: 1, 1656 Distribution: []int{0, 1, 2, 3, 4, 5, 6, 7}, 1657 Checksums: nil, 1658 }, 1659 } 1660 if err := xl.WriteMetadata(ctx, "", volume, object, fi); err != nil { 1661 t.Fatalf("Unable to create object, %s", err) 1662 } 1663 } 1664 var deleted [len(versions)]bool 1665 checkVerExist := func(t testing.TB) { 1666 t.Helper() 1667 for i := range versions { 1668 shouldExist := !deleted[i] 1669 fi, err := xl.ReadVersion(ctx, "", volume, object, versions[i], ReadOptions{}) 1670 if shouldExist { 1671 if err != nil { 1672 t.Fatalf("Version %s should exist, but got err %v", versions[i], err) 1673 } 1674 return 1675 } 1676 if err != errFileVersionNotFound { 1677 t.Fatalf("Version %s should not exist, but returned: %#v", versions[i], fi) 1678 } 1679 } 1680 } 1681 1682 // Delete version 0... 1683 checkVerExist(t) 1684 err = xl.DeleteVersion(ctx, volume, object, FileInfo{Name: object, Volume: volume, VersionID: versions[0]}, false, DeleteOptions{}) 1685 if err != nil { 1686 t.Fatal(err) 1687 } 1688 deleted[0] = true 1689 checkVerExist(t) 1690 1691 // Delete 10 in bulk, including a non-existing. 1692 fis := []FileInfoVersions{{Name: object, Volume: volume}} 1693 for i := range versions[:10] { 1694 fis[0].Versions = append(fis[0].Versions, FileInfo{Name: object, Volume: volume, VersionID: versions[i]}) 1695 deleted[i] = true 1696 } 1697 errs := xl.DeleteVersions(ctx, volume, fis, DeleteOptions{}) 1698 if errs[0] != nil { 1699 t.Fatalf("expected nil error, got %v", errs[0]) 1700 } 1701 checkVerExist(t) 1702 1703 // Delete them all... (some again) 1704 fis[0].Versions = nil 1705 for i := range versions[:] { 1706 fis[0].Versions = append(fis[0].Versions, FileInfo{Name: object, Volume: volume, VersionID: versions[i]}) 1707 deleted[i] = true 1708 } 1709 errs = xl.DeleteVersions(ctx, volume, fis, DeleteOptions{}) 1710 if errs[0] != nil { 1711 t.Fatalf("expected nil error, got %v", errs[0]) 1712 } 1713 checkVerExist(t) 1714 1715 // Meta should be deleted now... 1716 fi, err := xl.ReadVersion(ctx, "", volume, object, "", ReadOptions{}) 1717 if err != errFileNotFound { 1718 t.Fatalf("Object %s should not exist, but returned: %#v", object, fi) 1719 } 1720 } 1721 1722 // TestXLStorage xlStorage.StatInfoFile() 1723 func TestXLStorageStatInfoFile(t *testing.T) { 1724 // create xlStorage test setup 1725 xlStorage, _, err := newXLStorageTestSetup(t) 1726 if err != nil { 1727 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1728 } 1729 1730 // Setup test environment. 1731 if err := xlStorage.MakeVol(context.Background(), "success-vol"); err != nil { 1732 t.Fatalf("Unable to create volume, %s", err) 1733 } 1734 1735 if err := xlStorage.AppendFile(context.Background(), "success-vol", pathJoin("success-file", xlStorageFormatFile), []byte("Hello, world")); err != nil { 1736 t.Fatalf("Unable to create file, %s", err) 1737 } 1738 1739 if err := xlStorage.AppendFile(context.Background(), "success-vol", pathJoin("path/to/success-file", xlStorageFormatFile), []byte("Hello, world")); err != nil { 1740 t.Fatalf("Unable to create file, %s", err) 1741 } 1742 1743 if err := xlStorage.MakeVol(context.Background(), "success-vol/path/to/"+xlStorageFormatFile); err != nil { 1744 t.Fatalf("Unable to create path, %s", err) 1745 } 1746 1747 testCases := []struct { 1748 srcVol string 1749 srcPath string 1750 expectedErr error 1751 }{ 1752 // TestXLStorage case - 1. 1753 // TestXLStorage case with valid inputs, expected to pass. 1754 { 1755 srcVol: "success-vol", 1756 srcPath: "success-file", 1757 expectedErr: nil, 1758 }, 1759 // TestXLStorage case - 2. 1760 // TestXLStorage case with valid inputs, expected to pass. 1761 { 1762 srcVol: "success-vol", 1763 srcPath: "path/to/success-file", 1764 expectedErr: nil, 1765 }, 1766 // TestXLStorage case - 3. 1767 // TestXLStorage case with non-existent file. 1768 { 1769 srcVol: "success-vol", 1770 srcPath: "nonexistent-file", 1771 expectedErr: errPathNotFound, 1772 }, 1773 // TestXLStorage case - 4. 1774 // TestXLStorage case with non-existent file path. 1775 { 1776 srcVol: "success-vol", 1777 srcPath: "path/2/success-file", 1778 expectedErr: errPathNotFound, 1779 }, 1780 // TestXLStorage case - 5. 1781 // TestXLStorage case with path being a directory. 1782 { 1783 srcVol: "success-vol", 1784 srcPath: "path", 1785 expectedErr: errPathNotFound, 1786 }, 1787 // TestXLStorage case - 6. 1788 // TestXLStorage case with non existent volume. 1789 { 1790 srcVol: "non-existent-vol", 1791 srcPath: "success-file", 1792 expectedErr: errVolumeNotFound, 1793 }, 1794 // TestXLStorage case - 7. 1795 // TestXLStorage case with file with directory. 1796 { 1797 srcVol: "success-vol", 1798 srcPath: "path/to", 1799 expectedErr: nil, 1800 }, 1801 } 1802 1803 for i, testCase := range testCases { 1804 _, err := xlStorage.StatInfoFile(context.Background(), testCase.srcVol, testCase.srcPath+"/"+xlStorageFormatFile, false) 1805 if err != testCase.expectedErr { 1806 t.Errorf("TestXLStorage case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err) 1807 } 1808 } 1809 } 1810 1811 // Test xlStorage.VerifyFile() 1812 func TestXLStorageVerifyFile(t *testing.T) { 1813 // We test 4 cases: 1814 // 1) Whole-file bitrot check on proper file 1815 // 2) Whole-file bitrot check on corrupted file 1816 // 3) Streaming bitrot check on proper file 1817 // 4) Streaming bitrot check on corrupted file 1818 1819 // create xlStorage test setup 1820 storage, path, err := newXLStorageTestSetup(t) 1821 if err != nil { 1822 t.Fatalf("Unable to create xlStorage test setup, %s", err) 1823 } 1824 1825 volName := "testvol" 1826 fileName := "testfile" 1827 if err := storage.MakeVol(context.Background(), volName); err != nil { 1828 t.Fatal(err) 1829 } 1830 1831 // 1) Whole-file bitrot check on proper file 1832 size := int64(4*1024*1024 + 100*1024) // 4.1 MB 1833 data := make([]byte, size) 1834 if _, err := rand.Read(data); err != nil { 1835 t.Fatal(err) 1836 } 1837 algo := HighwayHash256 1838 h := algo.New() 1839 h.Write(data) 1840 hashBytes := h.Sum(nil) 1841 if err := storage.WriteAll(context.Background(), volName, fileName, data); err != nil { 1842 t.Fatal(err) 1843 } 1844 if err := storage.storage.bitrotVerify(context.Background(), pathJoin(path, volName, fileName), size, algo, hashBytes, 0); err != nil { 1845 t.Fatal(err) 1846 } 1847 1848 // 2) Whole-file bitrot check on corrupted file 1849 if err := storage.AppendFile(context.Background(), volName, fileName, []byte("a")); err != nil { 1850 t.Fatal(err) 1851 } 1852 1853 // Check if VerifyFile reports the incorrect file length (the correct length is `size+1`) 1854 if err := storage.storage.bitrotVerify(context.Background(), pathJoin(path, volName, fileName), size, algo, hashBytes, 0); err == nil { 1855 t.Fatal("expected to fail bitrot check") 1856 } 1857 1858 // Check if bitrot fails 1859 if err := storage.storage.bitrotVerify(context.Background(), pathJoin(path, volName, fileName), size+1, algo, hashBytes, 0); err == nil { 1860 t.Fatal("expected to fail bitrot check") 1861 } 1862 1863 if err := storage.Delete(context.Background(), volName, fileName, DeleteOptions{ 1864 Recursive: false, 1865 Immediate: false, 1866 }); err != nil { 1867 t.Fatal(err) 1868 } 1869 1870 // 3) Streaming bitrot check on proper file 1871 algo = HighwayHash256S 1872 shardSize := int64(1024 * 1024) 1873 shard := make([]byte, shardSize) 1874 w := newStreamingBitrotWriter(storage, "", volName, fileName, size, algo, shardSize) 1875 reader := bytes.NewReader(data) 1876 for { 1877 // Using io.Copy instead of this loop will not work for us as io.Copy 1878 // will use bytes.Reader.WriteTo() which will not do shardSize'ed writes 1879 // causing error. 1880 n, err := reader.Read(shard) 1881 w.Write(shard[:n]) 1882 if err == nil { 1883 continue 1884 } 1885 if err == io.EOF { 1886 break 1887 } 1888 t.Fatal(err) 1889 } 1890 w.(io.Closer).Close() 1891 if err := storage.storage.bitrotVerify(context.Background(), pathJoin(path, volName, fileName), size, algo, nil, shardSize); err != nil { 1892 t.Fatal(err) 1893 } 1894 1895 // 4) Streaming bitrot check on corrupted file 1896 filePath := pathJoin(storage.String(), volName, fileName) 1897 f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_SYNC, 0o644) 1898 if err != nil { 1899 t.Fatal(err) 1900 } 1901 // Replace first 256 with 'a'. 1902 if _, err := f.WriteString(strings.Repeat("a", 256)); err != nil { 1903 t.Fatal(err) 1904 } 1905 f.Close() 1906 if err := storage.storage.bitrotVerify(context.Background(), pathJoin(path, volName, fileName), size, algo, nil, shardSize); err == nil { 1907 t.Fatal("expected to fail bitrot check") 1908 } 1909 if err := storage.storage.bitrotVerify(context.Background(), pathJoin(path, volName, fileName), size+1, algo, nil, shardSize); err == nil { 1910 t.Fatal("expected to fail bitrot check") 1911 } 1912 } 1913 1914 // TestXLStorageReadMetadata tests readMetadata 1915 func TestXLStorageReadMetadata(t *testing.T) { 1916 volume, object := "test-vol", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1917 tmpDir := t.TempDir() 1918 1919 disk, err := newLocalXLStorage(tmpDir) 1920 if err != nil { 1921 t.Fatal(err) 1922 } 1923 1924 disk.MakeVol(context.Background(), volume) 1925 if _, err := disk.readMetadata(context.Background(), pathJoin(tmpDir, volume, object)); err != errFileNameTooLong { 1926 t.Fatalf("Unexpected error from readMetadata - expect %v: got %v", errFileNameTooLong, err) 1927 } 1928 }