diff --git a/opencloud/pkg/command/trash.go b/opencloud/pkg/command/trash.go index 54a2950043..9b572c3cff 100644 --- a/opencloud/pkg/command/trash.go +++ b/opencloud/pkg/command/trash.go @@ -36,7 +36,7 @@ func TrashPurgeEmptyDirsCommand(cfg *config.Config) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { basePath, _ := cmd.Flags().GetString("basepath") dryRun, _ := cmd.Flags().GetBool("dry-run") - posix := cfg.StorageUsers.Driver == "posix" + posix := cfg.StorageUsers.Driver == posixDriver if err := trash.PurgeTrashEmptyPaths(basePath, dryRun, posix); err != nil { fmt.Println(err) return err diff --git a/opencloud/pkg/trash/trash.go b/opencloud/pkg/trash/trash.go index 823334f4dc..f3b184a22f 100644 --- a/opencloud/pkg/trash/trash.go +++ b/opencloud/pkg/trash/trash.go @@ -10,8 +10,12 @@ import ( const ( // _trashGlobPattern is the glob pattern to find all trash items _trashGlobPattern = "spaces/*/*/trash/*/*/*/*" + // _trashRootPattern is the glob pattern of the trash container root + _trashRootPattern = "spaces/*/*/trash" // _posixTrashGlobPattern is the glob pattern to find all trash items on posix _posixTrashGlobPattern = "*/*/.Trash/files/*" + // _posixTrashRootPattern is the glob pattern of the trash container root on posix + _posixTrashRootPattern = "*/*/.Trash/files" ) // PurgeTrashEmptyPaths purges empty paths in the trash @@ -32,18 +36,14 @@ func PurgeTrashEmptyPaths(p string, dryRun bool, posix bool) error { } for _, d := range dirs { - if err := removeEmptyFolder(d, dryRun, posix); err != nil { + if err := removeEmptyFolder(d, dryRun, posix, p); err != nil { return err } } return nil } -func removeEmptyFolder(path string, dryRun bool, posix bool) error { - stop := "trash" - if posix { - stop = "files" - } +func removeEmptyFolder(path string, dryRun bool, posix bool, basePath string) error { if dryRun { if posix { @@ -80,8 +80,17 @@ func removeEmptyFolder(path string, dryRun bool, posix bool) error { return nil } nd := filepath.Dir(path) - if filepath.Base(nd) == stop { + if isTrashRoot(nd, basePath, posix) { return nil } - return removeEmptyFolder(nd, dryRun, posix) + return removeEmptyFolder(nd, dryRun, posix, basePath) +} + +func isTrashRoot(path, basePath string, posix bool) bool { + rootPattern := _trashRootPattern + if posix { + rootPattern = _posixTrashRootPattern + } + matched, _ := filepath.Match(filepath.Join(basePath, rootPattern), path) + return matched } diff --git a/opencloud/pkg/trash/trash_test.go b/opencloud/pkg/trash/trash_test.go new file mode 100644 index 0000000000..4b5cefc6b0 --- /dev/null +++ b/opencloud/pkg/trash/trash_test.go @@ -0,0 +1,109 @@ +package trash + +import ( + "os" + "testing" + + "github.com/test-go/testify/require" +) + +func TestIsTrashRootDecomposed(t *testing.T) { + storageRoot := "test_temp_" + t.Name() + defer os.RemoveAll(storageRoot) + + require.True(t, isTrashRoot(storageRoot+"/spaces/id/id/trash", storageRoot, false)) + require.False(t, isTrashRoot(storageRoot+"/spaces/id/id/trash/s1/s2/s3/node", storageRoot, false)) + require.False(t, isTrashRoot(storageRoot+"/spaces/id/id/trash/s1/s2/trash/node", storageRoot, false)) +} + +func TestIsTrashRootPosix(t *testing.T) { + storageRoot := "test_temp_" + t.Name() + defer os.RemoveAll(storageRoot) + + require.True(t, isTrashRoot(storageRoot+"/users/alice/.Trash/files", storageRoot, true)) + require.False(t, isTrashRoot(storageRoot+"/users/alice/.Trash/files/item.trashitem", storageRoot, true)) + require.False(t, isTrashRoot(storageRoot+"/users/alice/.Trash/files/item.trashitem/files", storageRoot, true)) +} + +func TestRemoveEmptyFolderPosix(t *testing.T) { + storageRoot := "test_temp_" + t.Name() + base := storageRoot + "/users/alice/.Trash/files" + defer os.RemoveAll(storageRoot) + + emptyChain := base + "/empty.trashitem/sub/subsub" + require.NoError(t, os.MkdirAll(emptyChain, os.ModePerm)) + + nonEmpty := base + "/keep.trashitem" + require.NoError(t, os.MkdirAll(nonEmpty, os.ModePerm)) + require.NoError(t, os.WriteFile(nonEmpty+"/file.txt", []byte("some text"), os.ModePerm)) + + require.NoError(t, removeEmptyFolder(emptyChain, false, true, storageRoot)) + + assertNoDirExists(t, emptyChain) + assertNoDirExists(t, base+"/empty.trashitem") + assertDirExists(t, base) + assertDirExists(t, nonEmpty) +} + +func TestRemoveEmptyFolderPosixUserDirNamedFiles(t *testing.T) { + storageRoot := "test_temp_" + t.Name() + base := storageRoot + "/users/alice/.Trash/files" + defer os.RemoveAll(storageRoot) + + nestedFilesDir := base + "/folder.trashitem/files/files" + require.NoError(t, os.MkdirAll(nestedFilesDir, os.ModePerm)) + + require.NoError(t, removeEmptyFolder(nestedFilesDir, false, true, storageRoot)) + + assertNoDirExists(t, nestedFilesDir) + assertNoDirExists(t, base+"/folder.trashitem/files") + assertNoDirExists(t, base+"/folder.trashitem") + assertDirExists(t, base) +} + +func TestRemoveEmptyFolderDecomposed(t *testing.T) { + storageRoot := "test_temp_" + t.Name() + base := storageRoot + "/spaces/id/id/trash" + defer os.RemoveAll(storageRoot) + + emptyChain := base + "/s1/s2/s3/node" + require.NoError(t, os.MkdirAll(emptyChain, os.ModePerm)) + + require.NoError(t, removeEmptyFolder(emptyChain, false, false, storageRoot)) + + assertNoDirExists(t, emptyChain) + assertNoDirExists(t, base+"/s1/s2/s3") + assertNoDirExists(t, base+"/s1/s2") + assertNoDirExists(t, base+"/s1") + assertDirExists(t, base) +} + +func TestRemoveEmptyFolderDecomposedUserDirNamedTrash(t *testing.T) { + storageRoot := "test_temp_" + t.Name() + base := storageRoot + "/spaces/id/id/trash" + defer os.RemoveAll(storageRoot) + + nestedTrashDir := base + "/s1/trash/s2/node" + require.NoError(t, os.MkdirAll(nestedTrashDir, os.ModePerm)) + + require.NoError(t, removeEmptyFolder(nestedTrashDir, false, false, storageRoot)) + + assertNoDirExists(t, nestedTrashDir) + assertNoDirExists(t, base+"/s1/trash/s2") + assertNoDirExists(t, base+"/s1/trash") + assertNoDirExists(t, base+"/s1") + assertDirExists(t, base) +} + +func assertNoDirExists(t *testing.T, path string) { + t.Helper() + _, err := os.Stat(path) + require.True(t, os.IsNotExist(err)) +} + +func assertDirExists(t *testing.T, path string) { + t.Helper() + fi, err := os.Stat(path) + require.NoError(t, err) + require.True(t, fi.IsDir()) +}