diff --git a/posix-utils_windows_test.go b/posix-utils_windows_test.go index 2fc30590d..50547edb9 100644 --- a/posix-utils_windows_test.go +++ b/posix-utils_windows_test.go @@ -97,9 +97,8 @@ func TestUNCPathENOTDIR(t *testing.T) { // Try to create a file that includes a file in its path components. // In *nix, this returns syscall.ENOTDIR while in windows we receive the following error. err = fs.AppendFile("voldir", "/file/obj1", []byte("hello")) - winErr := "The system cannot find the path specified." - if !strings.Contains(err.Error(), winErr) { - t.Errorf("expected to recieve %s, but received %s", winErr, err.Error()) + if err != errFileAccessDenied { + t.Errorf("expected: %s, got: %s", errFileAccessDenied, err) } } diff --git a/posix.go b/posix.go index e868d73af..1a8b1ed32 100644 --- a/posix.go +++ b/posix.go @@ -48,6 +48,11 @@ var errFaultyDisk = errors.New("Faulty disk") // checkPathLength - returns error if given path name length more than 255 func checkPathLength(pathName string) error { + // Apple OS X path length is limited to 1016 + if runtime.GOOS == "darwin" && len(pathName) > 1016 { + return errFileNameTooLong + } + // Check each path segment length is > 255 for len(pathName) > 0 && pathName != "." && pathName != "/" { dir, file := slashpath.Dir(pathName), slashpath.Base(pathName) @@ -551,6 +556,12 @@ func (s *posix) AppendFile(volume, path string, buf []byte) (err error) { // Create top level directories if they don't exist. // with mode 0777 mkdir honors system umask. if err = mkdirAll(filepath.Dir(filePath), 0777); err != nil { + // File path cannot be verified since one of the parents is a file. + if strings.Contains(err.Error(), "not a directory") { + return errFileAccessDenied + } else if runtime.GOOS == "windows" && strings.Contains(err.Error(), "system cannot find the path specified") { + return errFileAccessDenied + } return err } diff --git a/posix_test.go b/posix_test.go index 5d69c5643..bccaccdaa 100644 --- a/posix_test.go +++ b/posix_test.go @@ -19,6 +19,8 @@ package main import ( "io/ioutil" "os" + slashpath "path" + "runtime" "syscall" "testing" ) @@ -158,3 +160,420 @@ func TestNewPosix(t *testing.T) { } } } + +// Test posix.MakeVol() +func TestMakeVol(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + // Create a file. + if err := ioutil.WriteFile(slashpath.Join(path, "vol-as-file"), []byte{}, os.ModePerm); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + // Create a directory. + if err := os.Mkdir(slashpath.Join(path, "existing-vol"), 0777); err != nil { + t.Fatalf("Unable to create directory, %s", err) + } + + testCases := []struct { + volName string + expectedErr error + }{ + {"success-vol", nil}, + {"vol-as-file", errVolumeExists}, + {"existing-vol", errVolumeExists}, + } + + for _, testCase := range testCases { + if err := posix.MakeVol(testCase.volName); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } + + // Test for permission denied. + if runtime.GOOS != "windows" { + // Initialize posix storage layer for permission denied error. + posix, err := newPosix("/usr") + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + if err := posix.MakeVol("test-vol"); err != errDiskAccessDenied { + t.Fatalf("expected: %s, got: %s", errDiskAccessDenied, err) + } + } +} + +// Test posix.DeleteVol() +func TestDeleteVol(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + if err := posix.MakeVol("success-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + // Test failure cases. + vol := slashpath.Join(path, "nonempty-vol") + if err := os.Mkdir(vol, 0777); err != nil { + t.Fatalf("Unable to create directory, %s", err) + } + if err := ioutil.WriteFile(slashpath.Join(vol, "test-file"), []byte{}, os.ModePerm); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + testCases := []struct { + volName string + expectedErr error + }{ + {"success-vol", nil}, + {"nonexistent-vol", errVolumeNotFound}, + {"nonempty-vol", errVolumeNotEmpty}, + } + + for _, testCase := range testCases { + if err := posix.DeleteVol(testCase.volName); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } + + // Test for permission denied. + if runtime.GOOS != "windows" { + // Initialize posix storage layer for permission denied error. + posix, err := newPosix("/usr") + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + if err := posix.DeleteVol("bin"); !os.IsPermission(err) { + t.Fatalf("expected: Permission error, got: %s", err) + } + } +} + +// Test posix.StatVol() +func TestStatVol(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + if err := posix.MakeVol("success-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + testCases := []struct { + volName string + expectedErr error + }{ + {"success-vol", nil}, + {"nonexistent-vol", errVolumeNotFound}, + } + + for _, testCase := range testCases { + if _, err := posix.StatVol(testCase.volName); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } +} + +// Test posix.ListVols() +func TestListVols(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Test empty list vols. + if volInfo, err := posix.ListVols(); err != nil { + t.Fatalf("expected: , got: %s", err) + } else if len(volInfo) != 0 { + t.Fatalf("expected: [], got: %s", volInfo) + } + + // Test non-empty list vols. + if err := posix.MakeVol("success-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + if volInfo, err := posix.ListVols(); err != nil { + t.Fatalf("expected: , got: %s", err) + } else if len(volInfo) != 1 { + t.Fatalf("expected: 1, got: %d", len(volInfo)) + } else if volInfo[0].Name != "success-vol" { + t.Fatalf("expected: success-vol, got: %s", volInfo[0].Name) + } +} + +// Test posix.DeleteFile() +func TestDeleteFile(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + if err := posix.MakeVol("success-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + if err := posix.AppendFile("success-vol", "success-file", []byte("Hello, world")); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + testCases := []struct { + fileName string + expectedErr error + }{ + {"success-file", nil}, + {"nonexistent-file", errFileNotFound}, + } + + for _, testCase := range testCases { + if err := posix.DeleteFile("success-vol", testCase.fileName); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } + + // Test for permission denied. + if runtime.GOOS != "windows" { + // Initialize posix storage layer for permission denied error. + posix, err := newPosix("/usr") + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + if err := posix.DeleteFile("bin", "yes"); !os.IsPermission(err) { + t.Fatalf("expected: Permission error, got: %s", err) + } + } +} + +// Test posix.AppendFile() +func TestAppendFile(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + if err = posix.MakeVol("success-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + // Create directory to make errIsNotRegular + if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0777); err != nil { + t.Fatalf("Unable to create directory, %s", err) + } + + testCases := []struct { + fileName string + expectedErr error + }{ + {"myobject", nil}, + {"path/to/my/object", nil}, + // Test to append to previously created file. + {"myobject", nil}, + // Test to use same path of previously created file. + {"path/to/my/testobject", nil}, + // One path segment length is 255 chars long. + {"path/to/my/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", nil}, + {"object-as-dir", errIsNotRegular}, + // path segment uses previously uploaded object. + {"myobject/testobject", errFileAccessDenied}, + // One path segment length is > 255 chars long. + {"path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong}, + // path length is > 1024 chars long. + {"level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong}, + } + + // Add path length > 1024 test specially as OS X system does not support 1024 long path. + err = errFileNameTooLong + if runtime.GOOS != "darwin" { + err = nil + } + // path length is 1024 chars long. + testCases = append(testCases, struct { + fileName string + expectedErr error + }{"level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", err}) + + for _, testCase := range testCases { + if err := posix.AppendFile("success-vol", testCase.fileName, []byte("hello, world")); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } + + // Test for permission denied. + if runtime.GOOS != "windows" { + // Initialize posix storage layer for permission denied error. + posix, err := newPosix("/usr") + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + if err := posix.AppendFile("bin", "yes", []byte("hello, world")); !os.IsPermission(err) { + t.Fatalf("expected: Permission error, got: %s", err) + } + } +} + +// Test posix.RenameFile() +func TestRenameFile(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + if err := posix.MakeVol("src-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + if err := posix.MakeVol("dest-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + if err := posix.AppendFile("src-vol", "file1", []byte("Hello, world")); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + if err := posix.AppendFile("src-vol", "file2", []byte("Hello, world")); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + if err := posix.AppendFile("src-vol", "path/to/file1", []byte("Hello, world")); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + testCases := []struct { + srcPath string + destPath string + expectedErr error + }{ + {"file1", "file-one", nil}, + {"path/", "new-path/", nil}, + // Test to overwrite destination file. + {"file2", "file-one", nil}, + // Test to check failure of source and destination are not same type. + {"path/", "file-one", errFileAccessDenied}, + // Test to check failure of destination directory exists. + {"path/", "new-path/", errFileAccessDenied}, + } + + for _, testCase := range testCases { + if err := posix.RenameFile("src-vol", testCase.srcPath, "dest-vol", testCase.destPath); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } +} + +// Test posix.StatFile() +func TestStatFile(t *testing.T) { + // Create temporary directory. + path, err := ioutil.TempDir("", "minio-") + if err != nil { + t.Fatalf("Unable to create a temporary directory, %s", err) + } + defer removeAll(path) + + // Initialize posix storage layer. + posix, err := newPosix(path) + if err != nil { + t.Fatalf("Unable to initialize posix, %s", err) + } + + // Setup test environment. + if err := posix.MakeVol("success-vol"); err != nil { + t.Fatalf("Unable to create volume, %s", err) + } + + if err := posix.AppendFile("success-vol", "success-file", []byte("Hello, world")); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + if err := posix.AppendFile("success-vol", "path/to/success-file", []byte("Hello, world")); err != nil { + t.Fatalf("Unable to create file, %s", err) + } + + testCases := []struct { + path string + expectedErr error + }{ + {"success-file", nil}, + {"path/to/success-file", nil}, + // Test nonexistent file. + {"nonexistent-file", errFileNotFound}, + // Test nonexistent path. + {"path/2/success-file", errFileNotFound}, + // Test a directory. + {"path", errFileNotFound}, + } + + for _, testCase := range testCases { + if _, err := posix.StatFile("success-vol", testCase.path); err != testCase.expectedErr { + t.Fatalf("case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err) + } + } +}