diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index d43be52749..e38a400afb 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -13,6 +13,7 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config/configlog" "github.com/opencloud-eu/opencloud/pkg/config/parser" oclog "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/event" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/revaconfig" @@ -95,6 +96,39 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { os.Exit(1) } + storageRoot := cfg.Drivers.Posix.Root + root := storageRoot + defaultRoot := true + if v, err := cmd.Flags().GetString("basepath"); err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse command-line parameter '--basepath': %v\n", err) + os.Exit(1) + } else if v != "" { + root = v + if !filepath.IsAbs(v) { + if v, err = filepath.Abs(v); err != nil { + fmt.Fprintf(os.Stderr, "Failed to make the basepath mentioned using '--basepath' absolute: %v\n", err) + os.Exit(1) + } else { + root = v + } + } else { + root = v + } + root = filepath.Clean(root) + defaultRoot = false + } + + // ensure that, if a basepath has been indicated, it is under the storage root + if !defaultRoot { + if contained, err := filepathx.IsSameOrContainedBy(storageRoot, root); err != nil { + fmt.Fprintf(os.Stderr, "Failed to determine whether the specified basepath %q is contained by the storage root %q: %v\n", root, storageRoot, err) + os.Exit(1) + } else if !contained { + fmt.Fprintf(os.Stderr, "The specified basepath %q is neither the storage root %q, nor a subdirectory thereof, nor a file underneath it\n", root, storageRoot) + os.Exit(1) + } + } + // We want to initialize the driver but disable scanfs on boot, so we can trigger it manually afterwards drivers := revaconfig.StorageProviderDrivers(cfg) drivers["posix"] = revaconfig.Posix(cfg, false, false) @@ -112,6 +146,10 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { oclog.Pretty(true), oclog.Color(false)).Logger + if !defaultRoot { + log = log.With().Str("basepath", root).Logger() + } + f, ok := registry.NewFuncs["posix"] if !ok { fmt.Fprintf(os.Stderr, "posix driver not found in registry\n") @@ -130,8 +168,12 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { os.Exit(1) } - fmt.Println("Starting posixfs scan...") - err = cacher.WarmupIDCache(cfg.Drivers.Posix.Root, true, false) + if defaultRoot { + fmt.Println("Starting posixfs scan...") + } else { + fmt.Printf("Starting posixfs scan at '%s'...\n", root) + } + err = cacher.WarmupIDCache(root, true, false) if err != nil { fmt.Fprintf(os.Stderr, "Scan failed: %v\n", err) return err @@ -141,6 +183,7 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { return nil }, } + cmd.Flags().StringP("basepath", "p", "", "the root under which to scan files, which may be a directory or a file (when omitted, detaults to using the storage root)") return cmd } diff --git a/pkg/x/path/filepathx/path.go b/pkg/x/path/filepathx/path.go index 8101e89331..d105290fc3 100644 --- a/pkg/x/path/filepathx/path.go +++ b/pkg/x/path/filepathx/path.go @@ -1,7 +1,9 @@ package filepathx import ( + "fmt" "path/filepath" + "strings" ) // JailJoin joins any number of path elements into a single path, @@ -10,3 +12,27 @@ import ( func JailJoin(jail string, elem ...string) string { return filepath.Join(jail, filepath.Join(append([]string{"/"}, elem...)...)) } + +// Determines whether the file or directory 'child' is same as or underneath the directory 'parent'. +// +// Note that 'parent' is expected to be a directory. +func IsSameOrContainedBy(parent string, child string) (bool, error) { + absParent, err := filepath.Abs(parent) + if err != nil { + return false, fmt.Errorf("failed to make parent directory absolute: %q: %w", parent, err) + } + + absChild, err := filepath.Abs(child) + if err != nil { + return false, fmt.Errorf("failed to make child file/directory absolute: %q: %w", child, err) + } + + rel, err := filepath.Rel(absParent, absChild) + if err != nil { + return false, fmt.Errorf("failed to determine the relative path between the parent directory %q and the child file/directory: %q: %w", absParent, absChild, err) + } + if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { + return false, nil + } + return true, nil +} diff --git a/pkg/x/path/filepathx/path_test.go b/pkg/x/path/filepathx/path_test.go index ca8782d942..c5b098f21d 100644 --- a/pkg/x/path/filepathx/path_test.go +++ b/pkg/x/path/filepathx/path_test.go @@ -1,9 +1,12 @@ package filepathx_test import ( + "fmt" + "strings" "testing" "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" + "github.com/stretchr/testify/require" ) func TestJailJoin(t *testing.T) { @@ -61,3 +64,23 @@ func TestJailJoin(t *testing.T) { }) } } + +func TestIsSameOrContainedBy(t *testing.T) { + for _, tt := range []struct { + parent string + child string + expected bool + }{ + {"foo", "foo", true}, + {"/foo", "/foo", true}, + {"foo", "foo/bar", true}, + {"foo", "bar", false}, + } { + t.Run(fmt.Sprintf("%s: %s vs %s", t.Name(), strings.ReplaceAll(tt.parent, "/", "."), strings.ReplaceAll(tt.child, "/", ".")), func(t *testing.T) { + require := require.New(t) + b, err := filepathx.IsSameOrContainedBy(tt.parent, tt.child) + require.NoError(err) + require.Equal(tt.expected, b) + }) + } +}