diff --git a/tools/integration_tests/readonly/copy_object_test.go b/tools/integration_tests/readonly/copy_object_test.go new file mode 100644 index 0000000000..902b3118be --- /dev/null +++ b/tools/integration_tests/readonly/copy_object_test.go @@ -0,0 +1,96 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Provides integration tests for file operations with --o=ro flag set. +package readonly_test + +import ( + "io" + "os" + "os/exec" + "path" + "syscall" + "testing" + + "github.com/googlecloudplatform/gcsfuse/tools/integration_tests/setup" +) + +// Copy srcFile in testBucket/Test/b/b.txt destination. +func checkIfFileCopyFailed(srcFilePath string, t *testing.T) { + source, err := os.OpenFile(srcFilePath, syscall.O_DIRECT, setup.FilePermission_0600) + if err != nil { + t.Errorf("Error in the opening file: %v", err) + } + + // cp without destination file creates a destination file and create workflow is already covered separately. + // Checking if destination object exist. + copyFile := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNameInSubDirectoryTestBucket) + if _, err := os.Stat(copyFile); err != nil { + t.Errorf("Copied file %s is not present", copyFile) + } + + destination, err := os.OpenFile(copyFile, syscall.O_DIRECT, setup.FilePermission_0600) + if err != nil { + t.Errorf("File %s opening error: %v", destination.Name(), err) + } + defer destination.Close() + + // File copying with io.Copy() utility. + // In read only filesystem, io.Copy throws "copy_file_range: bad file descriptor" error. + _, err = io.Copy(destination, source) + if err == nil { + t.Errorf("File copied in read-only file system.") + } +} + +// Copy testBucket/Test1.txt to testBucket/Test/b/b.txt +func TestCopyFile(t *testing.T) { + file := path.Join(setup.MntDir(), FileNameInTestBucket) + + checkIfFileCopyFailed(file, t) +} + +// Copy testBucket/Test/a.txt to testBucket/Test/b/b.txt +func TestCopyFileFromBucketDirectory(t *testing.T) { + file := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNameInDirectoryTestBucket) + + checkIfFileCopyFailed(file, t) +} + +func checkIfDirCopyFailed(srcDirPath string, destDirPath string, t *testing.T) { + // io packages do not have a method to copy the directory. + cmd := exec.Command("cp", "--recursive", srcDirPath, destDirPath) + + // In the read-only filesystem, It is Throwing an exit status 1. + err := cmd.Run() + if err == nil { + t.Errorf("Directory copied in read-only file system.") + } +} + +// Copy testBucket/Test to testBucket/Test/b +func TestCopyDirectory(t *testing.T) { + srcDir := path.Join(setup.MntDir(), DirectoryNameInTestBucket) + destDir := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket) + + checkIfDirCopyFailed(srcDir, destDir, t) +} + +// Copy testBucket/Test/b to testBucket +func TestCopySubDirectory(t *testing.T) { + srcDir := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket) + destDir := path.Join(setup.MntDir()) + + checkIfDirCopyFailed(srcDir, destDir, t) +} diff --git a/tools/integration_tests/readonly/read_test.go b/tools/integration_tests/readonly/read_test.go new file mode 100644 index 0000000000..f1330f24d8 --- /dev/null +++ b/tools/integration_tests/readonly/read_test.go @@ -0,0 +1,91 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Provides integration tests for file operations with --o=ro flag set. +package readonly_test + +import ( + "os" + "path" + "syscall" + "testing" + + "github.com/googlecloudplatform/gcsfuse/tools/integration_tests/setup" +) + +func checkIfFileReadSucceeded(filePath string, expectedContent string, t *testing.T) { + file, err := os.OpenFile(filePath, os.O_RDONLY|syscall.O_DIRECT, setup.FilePermission_0600) + if err != nil { + t.Errorf("Error in the opening the file %v", err) + } + defer file.Close() + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Errorf("ReadAll: %v", err) + } + if got, want := string(content), expectedContent; got != want { + t.Errorf("File content %q not match %q", got, want) + } +} + +// testBucket/Test1.txt +func TestReadFile(t *testing.T) { + filePath := path.Join(setup.MntDir(), FileNameInTestBucket) + + checkIfFileReadSucceeded(filePath, ContentInFileInTestBucket, t) +} + +// testBucket/Test/a.txt +func TestReadFileFromBucketDirectory(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNameInDirectoryTestBucket) + + checkIfFileReadSucceeded(filePath, ContentInFileInDirectoryTestBucket, t) +} + +// testBucket/Test/b/b.txt +func TestReadFileFromBucketSubDirectory(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNameInSubDirectoryTestBucket) + + checkIfFileReadSucceeded(filePath, ContentInFileInSubDirectoryTestBucket, t) +} + +func checkIfNonExistentFileFailedToOpen(filePath string, t *testing.T) { + file, err := os.OpenFile(filePath, os.O_RDONLY|syscall.O_DIRECT, setup.FilePermission_0600) + + checkErrorForObjectNotExist(err, t) + + if err == nil { + t.Errorf("Nonexistent file opened to read.") + } + defer file.Close() +} + +func TestReadNonExistentFile(t *testing.T) { + filePath := path.Join(setup.MntDir(), FileNotExist) + + checkIfNonExistentFileFailedToOpen(filePath, t) +} + +func TestReadNonExistentFileFromBucketDirectory(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNotExist) + + checkIfNonExistentFileFailedToOpen(filePath, t) +} + +func TestReadNonExistentFileFromBucketSubDirectory(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNotExist) + + checkIfNonExistentFileFailedToOpen(filePath, t) +} diff --git a/tools/integration_tests/readonly/readonly_test.go b/tools/integration_tests/readonly/readonly_test.go index 88e1a5202b..1935791307 100644 --- a/tools/integration_tests/readonly/readonly_test.go +++ b/tools/integration_tests/readonly/readonly_test.go @@ -31,8 +31,14 @@ const FileNameInDirectoryTestBucket = "a.txt" // testBucket/Test/a.txt const FileNameInSubDirectoryTestBucket = "b.txt" // testBucket/Test/b/b.txt const NumberOfObjectsInTestBucket = 2 const NumberOfObjectsInDirectoryTestBucket = 2 +const NumberOfObjectsInSubDirectoryTestBucket = 1 const FileNotExist = "notExist.txt" const DirNotExist = "notExist" +const ContentInFileInTestBucket = "This is from file Test1\n" +const ContentInFileInDirectoryTestBucket = "This is from directory Test file a\n" +const ContentInFileInSubDirectoryTestBucket = "This is from directory Test/b file b\n" +const RenameFile = "rename.txt" +const RenameDir = "rename" // Run shell script func runScriptForTestData(script string, testBucket string) { @@ -45,13 +51,13 @@ func runScriptForTestData(script string, testBucket string) { func checkErrorForReadOnlyFileSystem(err error, t *testing.T) { if !strings.Contains(err.Error(), "read-only file system") && !strings.Contains(err.Error(), "permission denied") { - t.Errorf("Incorrect error for readonly filesystem.") + t.Errorf("Incorrect error for readonly filesystem: %v", err.Error()) } } func checkErrorForObjectNotExist(err error, t *testing.T) { if !strings.Contains(err.Error(), "no such file or directory") { - t.Errorf("Incorrect error for object not exist.") + t.Errorf("Incorrect error for object not exist: %v", err.Error()) } } diff --git a/tools/integration_tests/readonly/rename_object_test.go b/tools/integration_tests/readonly/rename_object_test.go new file mode 100644 index 0000000000..ce02e199aa --- /dev/null +++ b/tools/integration_tests/readonly/rename_object_test.go @@ -0,0 +1,140 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Provides integration tests for file operations with --o=ro flag set. +package readonly_test + +import ( + "os" + "path" + "testing" + + "github.com/googlecloudplatform/gcsfuse/tools/integration_tests/setup" +) + +// Rename oldObj to newObj +func checkIfRenameFailed(oldObjPath string, newObjPath string, t *testing.T) { + _, err := os.Stat(oldObjPath) + if err != nil { + t.Errorf("Error in the stating object: %v", err) + } + + if _, err := os.Stat(newObjPath); err == nil { + t.Errorf("Renamed object %s already present", newObjPath) + } + + err = os.Rename(oldObjPath, newObjPath) + + if err == nil { + t.Errorf("Object renamed in read-only file system.") + } + + checkErrorForReadOnlyFileSystem(err, t) + + if _, err := os.Stat(oldObjPath); err != nil { + t.Errorf("OldObj is deleted in read-only file system.") + } + if _, err := os.Stat(newObjPath); err == nil { + t.Errorf("Renamed object found in read-only file system.") + } +} + +// Rename testBucket/Test1.txt to testBucket/Rename.txt +func TestRenameFile(t *testing.T) { + oldFilePath := path.Join(setup.MntDir(), FileNameInTestBucket) + newFilePath := path.Join(setup.MntDir(), RenameFile) + + checkIfRenameFailed(oldFilePath, newFilePath, t) +} + +// Rename testBucket/Test/a.txt to testBucket/Test/Rename.txt +func TestRenameFileFromBucketDirectory(t *testing.T) { + oldFilePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNameInDirectoryTestBucket) + newFilePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, RenameFile) + + checkIfRenameFailed(oldFilePath, newFilePath, t) +} + +// Rename testBucket/Test/b/b.txt to testBucket/Test/b/Rename.txt +func TestRenameFileFromBucketSubDirectory(t *testing.T) { + oldFilePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNameInSubDirectoryTestBucket) + newFilePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, RenameFile) + + checkIfRenameFailed(oldFilePath, newFilePath, t) +} + +// Rename testBucket/Test to testBucket/Rename +func TestRenameDir(t *testing.T) { + oldDirPath := path.Join(setup.MntDir(), DirectoryNameInTestBucket) + newDirPath := path.Join(setup.MntDir(), RenameDir) + + checkIfRenameFailed(oldDirPath, newDirPath, t) + + // Ensure none of the child is deleted during the directory rename test. + // ** OldDirectory structure ** + // Test + // Test/b -- Dir + // Test/a.txt -- File + + obj, err := os.ReadDir(oldDirPath) + if err != nil { + t.Errorf("Error in reading directory %v ,", err.Error()) + } + + // Comparing number of objects in the oldDirectory - 2 + if len(obj) != NumberOfObjectsInDirectoryTestBucket { + t.Errorf("The number of objects in the current directory doesn't match.") + } + + // Comparing first object name and type + // Name - Test/a.txt, Type - File + if obj[0].Name() != FileNameInDirectoryTestBucket || obj[0].IsDir() != false { + t.Errorf("Object Listed for file in bucket is incorrect.") + } + + // Comparing second object name and type + // Name - Test/b , Type - Dir + if obj[1].Name() != SubDirectoryNameInTestBucket || obj[1].IsDir() != true { + t.Errorf("Object Listed for bucket directory is incorrect.") + } +} + +// Rename testBucket/Test/b to testBucket/Test/Rename +func TestRenameSubDirectory(t *testing.T) { + oldDirPath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket) + newDirPath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, RenameDir) + + checkIfRenameFailed(oldDirPath, newDirPath, t) + + // Ensure none of the child is deleted during the directory rename test. + // ** OldDirectory structure ** + // b + // b/b.txt -- File + + obj, err := os.ReadDir(oldDirPath) + if err != nil { + t.Errorf("Error in reading directory %v ,", err.Error()) + } + + // Comparing number of objects in the oldDirectory - 1 + if len(obj) != NumberOfObjectsInSubDirectoryTestBucket { + t.Errorf("The number of objects in the current directory doesn't match.") + } + + // Comparing object name and type + // Name - b/b.txt, Type - File + if obj[0].Name() != FileNameInSubDirectoryTestBucket || obj[0].IsDir() != false { + t.Errorf("Object Listed for file in bucket SubDirectory is incorrect.") + } +} diff --git a/tools/integration_tests/readonly/write_test.go b/tools/integration_tests/readonly/write_test.go new file mode 100644 index 0000000000..3954416cf3 --- /dev/null +++ b/tools/integration_tests/readonly/write_test.go @@ -0,0 +1,145 @@ +// Copyright 2023 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Provides integration tests for file operations with --o=ro flag set. +package readonly_test + +import ( + "os" + "path" + "syscall" + "testing" + + "github.com/googlecloudplatform/gcsfuse/tools/integration_tests/setup" +) + +func checkIfFileFailedToOpenForWrite(filePath string, t *testing.T) { + file, err := os.OpenFile(filePath, os.O_RDWR|syscall.O_DIRECT, setup.FilePermission_0600) + + checkErrorForReadOnlyFileSystem(err, t) + + if err == nil { + t.Errorf("File opened for writing in read-only mount.") + } + defer file.Close() +} + +// testBucket/Test1.txt +func TestOpenFileWithReadWriteAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), FileNameInTestBucket) + + checkIfFileFailedToOpenForWrite(filePath, t) +} + +// testBucket/Test/a.txt +func TestOpenFileFromBucketDirectoryWithReadWriteAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNameInDirectoryTestBucket) + + checkIfFileFailedToOpenForWrite(filePath, t) +} + +// testBucket/Test/b/b.txt +func TestOpenFileFromBucketSubDirectoryWithReadWriteAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNameInSubDirectoryTestBucket) + + checkIfFileFailedToOpenForWrite(filePath, t) +} + +func checkIfNonExistentFileFailedToOpenForWrite(filePath string, t *testing.T) { + file, err := os.OpenFile(filePath, os.O_RDWR|syscall.O_DIRECT, setup.FilePermission_0600) + + checkErrorForObjectNotExist(err, t) + + if err == nil { + t.Errorf("NonExist file opened for writing in read-only mount.") + } + defer file.Close() +} + +func TestOpenNonExistentFileWithReadWriteAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), FileNotExist) + + checkIfNonExistentFileFailedToOpenForWrite(filePath, t) +} + +func TestOpenNonExistentFileFromBucketDirectoryWithReadWriteAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNotExist) + + checkIfNonExistentFileFailedToOpenForWrite(filePath, t) +} + +func TestOpenNonExistentFileFromBucketSubDirectoryWithReadWriteAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNotExist) + + checkIfNonExistentFileFailedToOpenForWrite(filePath, t) +} + +func checkIfFileFailedToOpenForAppend(filePath string, t *testing.T) { + file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|syscall.O_DIRECT, setup.FilePermission_0600) + + checkErrorForReadOnlyFileSystem(err, t) + + if err == nil { + t.Errorf("File opened for appending content in read-only mount.") + } + defer file.Close() +} + +func TestOpenFileWithAppendAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), FileNameInTestBucket) + + checkIfFileFailedToOpenForAppend(filePath, t) +} + +func TestOpenFileFromBucketDirectoryWithAppendAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNameInDirectoryTestBucket) + + checkIfFileFailedToOpenForAppend(filePath, t) +} + +func TestOpenFileFromBucketSubDirectoryWithAppendAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNameInSubDirectoryTestBucket) + + checkIfFileFailedToOpenForAppend(filePath, t) +} + +func checkIfNonExistentFileFailedToOpenForAppend(filePath string, t *testing.T) { + file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|syscall.O_DIRECT, setup.FilePermission_0600) + + if err == nil { + t.Errorf("File opened for appending content in read-only mount.") + } + + checkErrorForObjectNotExist(err, t) + + defer file.Close() +} + +func TestOpenNonExistentFileWithAppendAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), FileNotExist) + + checkIfNonExistentFileFailedToOpenForAppend(filePath, t) +} + +func TestOpenNonExistentFileFromBucketDirectoryWithAppendAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, FileNotExist) + + checkIfNonExistentFileFailedToOpenForAppend(filePath, t) +} + +func TestOpenNonExistentFileFromBucketSubDirectoryWithAppendAccess(t *testing.T) { + filePath := path.Join(setup.MntDir(), DirectoryNameInTestBucket, SubDirectoryNameInTestBucket, FileNotExist) + + checkIfNonExistentFileFailedToOpenForAppend(filePath, t) +}