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  }