filebrowser / filebrowser

📂 Web File Browser
https://filebrowser.org
Apache License 2.0
25.66k stars 2.94k forks source link

Follow the symlinks for a search #3171

Closed awsms closed 1 month ago

awsms commented 4 months ago

Is your feature request related to a problem? Please describe. The folders I use with filebrowser are arranged with symlinks. Since filebrowser doesn't follow them for the search (only for browsing), they can't be found with a search, unless I'm in the actual symlinked folder. I assume this was made this way to avoid infinite loops, so I'm wondering if something could be done about it.

tilusnet commented 3 months ago

I desperately need this feature to work!

github-actions[bot] commented 2 months ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

tilusnet commented 2 months ago

I attempted to track the logic down the Walk function in search/search.go. That logic is likely ignoring symbolic links. @o1egl can you please confirm? Thanks.

awsms commented 2 months ago

i've implemented this on my side a while ago, but note that it will always follow symlinks

diff --git a/search/search.go b/search/search.go
index 380dcde3..68728122 100644
--- a/search/search.go
+++ b/search/search.go
@@ -17,6 +17,57 @@ type searchOptions struct {
    Terms         []string
 }

+// Walk in symlinls
+func walkLink(fs afero.Fs, root string, walkFn filepath.WalkFunc) error {
+   info, err := fs.Stat(root)
+   if err != nil {
+       return walkFn(root, nil, err)
+   }
+   if info.Mode()&os.ModeSymlink != 0 {
+       link, err := os.Readlink(root)
+       if err != nil {
+           return err
+       }
+       info, err = fs.Stat(link)
+       if err != nil {
+           return walkFn(root, info, err)
+       }
+   }
+   return walk(fs, root, walkFn)
+}
+
+// Aero.walk reimplementation
+func walk(fs afero.Fs, root string, walkFn filepath.WalkFunc) error {
+   info, err := fs.Stat(root)
+   if err != nil {
+       return err
+   }
+
+   err = walkFn(root, info, nil)
+   if err != nil {
+       return err
+   }
+
+   if info.IsDir() {
+       files, err := afero.ReadDir(fs, root)
+       if err != nil {
+           return err
+       }
+
+       for _, file := range files {
+           err = walkLink(fs, filepath.Join(root, file.Name()), walkFn)
+           if err != nil {
+               if err == filepath.SkipDir {
+                   continue
+               }
+               return err
+           }
+       }
+   }
+
+   return nil
+}
+
 // Search searches for a query in a fs.
 func Search(fs afero.Fs, scope, query string, checker rules.Checker, found func(path string, f os.FileInfo) error) error {
    search := parseSearch(query)
@@ -24,7 +75,7 @@ func Search(fs afero.Fs, scope, query string, checker rules.Checker, found func(
    scope = filepath.ToSlash(filepath.Clean(scope))
    scope = path.Join("/", scope)

-   return afero.Walk(fs, scope, func(fPath string, f os.FileInfo, _ error) error {
+   return walk(fs, scope, func(fPath string, f os.FileInfo, _ error) error {
        fPath = filepath.ToSlash(filepath.Clean(fPath))
        fPath = path.Join("/", fPath)
        relativePath := strings.TrimPrefix(fPath, scope)
tilusnet commented 2 months ago

Awesome @awsms, thanks for sharing!

I can't think of a case when I wouldn't want to follow symlinks, apart from circular symlinking - which I take care of.

github-actions[bot] commented 1 month ago

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] commented 1 month ago

This issue was closed because it has been stalled for 5 days with no activity.