diff --git a/changelog/unreleased/empty-trash-dirs.md b/changelog/unreleased/empty-trash-dirs.md new file mode 100644 index 0000000000..89c5f54b51 --- /dev/null +++ b/changelog/unreleased/empty-trash-dirs.md @@ -0,0 +1,7 @@ +Enhancement: Empty trash directories + +We have added a cli-command that allows cleaning up empty directories in the trashbins folder structure in decomposedFS. + +https://github.com/owncloud/ocis/pull/9483 +https://github.com/owncloud/ocis/issues/9393 +https://github.com/owncloud/ocis/issues/9271 diff --git a/ocis/pkg/command/trash.go b/ocis/pkg/command/trash.go new file mode 100644 index 0000000000..a68b66e6d0 --- /dev/null +++ b/ocis/pkg/command/trash.go @@ -0,0 +1,67 @@ +package command + +import ( + "fmt" + "github.com/owncloud/ocis/v2/ocis/pkg/trash" + + "github.com/owncloud/ocis/v2/ocis-pkg/config" + "github.com/owncloud/ocis/v2/ocis-pkg/config/configlog" + "github.com/owncloud/ocis/v2/ocis-pkg/config/parser" + "github.com/owncloud/ocis/v2/ocis/pkg/register" + "github.com/urfave/cli/v2" +) + +func TrashCommand(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "trash", + Usage: "ocis trash functionality", + Subcommands: []*cli.Command{ + TrashPurgeEmptyDirsCommand(cfg), + }, + Before: func(c *cli.Context) error { + return configlog.ReturnError(parser.ParseConfig(cfg, true)) + }, + Action: func(_ *cli.Context) error { + fmt.Println("Read the docs") + return nil + }, + } +} + +func TrashPurgeEmptyDirsCommand(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "purge-empty-dirs", + Usage: "purge empty directories", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "basepath", + Aliases: []string{"p"}, + Usage: "the basepath of the decomposedfs (e.g. /var/tmp/ocis/storage/users)", + Required: true, + }, + &cli.BoolFlag{ + Name: "dry-run", + Usage: "do not delete anything, just print what would be deleted", + Value: true, + }, + }, + Action: func(c *cli.Context) error { + basePath := c.String("basepath") + if basePath == "" { + fmt.Println("basepath is required") + return cli.ShowCommandHelp(c, "trash") + } + + if err := trash.PurgeTrashEmptyPaths(basePath, c.Bool("dry-run")); err != nil { + fmt.Println(err) + return err + } + + return nil + }, + } +} + +func init() { + register.AddCommand(TrashCommand) +} diff --git a/ocis/pkg/trash/trash.go b/ocis/pkg/trash/trash.go new file mode 100644 index 0000000000..985960c06e --- /dev/null +++ b/ocis/pkg/trash/trash.go @@ -0,0 +1,57 @@ +package trash + +import ( + "errors" + "fmt" + "os" + "path/filepath" +) + +const ( + // _trashGlobPattern is the glob pattern to find all trash items + _trashGlobPattern = "storage/users/spaces/*/*/trash/*/*/*/*" +) + +// PurgeTrashEmptyPaths purges empty paths in the trash +func PurgeTrashEmptyPaths(p string, dryRun bool) error { + // we have all trash nodes in all spaces now + dirs, err := filepath.Glob(filepath.Join(p, _trashGlobPattern)) + if err != nil { + return err + } + + if len(dirs) == 0 { + return errors.New("no trash found. Double check storage path") + } + + for _, d := range dirs { + if err := removeEmptyFolder(d, dryRun); err != nil { + return err + } + } + return nil +} + +func removeEmptyFolder(path string, dryRun bool) error { + if dryRun { + f, err := os.ReadDir(path) + if err != nil { + return err + } + if len(f) < 1 { + fmt.Println("would remove", path) + } + return nil + } + if err := os.Remove(path); err != nil { + // we do not really care about the error here + // if the folder is not empty we will get an error, + // this is our signal to break out of the recursion + return nil + } + nd := filepath.Dir(path) + if filepath.Base(nd) == "trash" { + return nil + } + return removeEmptyFolder(nd, dryRun) +}