From 0a7721dcfe30b76c66c6364ccb6874a2eadcba21 Mon Sep 17 00:00:00 2001
From: Matthew Holt <mholt@users.noreply.github.com>
Date: Tue, 24 Nov 2020 12:24:44 -0700
Subject: [PATCH] fileserver: Preserve transformed root (fix #3838)

---
 modules/caddyhttp/fileserver/browse.go        | 10 +++++-----
 modules/caddyhttp/fileserver/browselisting.go |  4 ++--
 modules/caddyhttp/fileserver/staticfiles.go   |  2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/modules/caddyhttp/fileserver/browse.go b/modules/caddyhttp/fileserver/browse.go
index cd41ea540..af3620a43 100644
--- a/modules/caddyhttp/fileserver/browse.go
+++ b/modules/caddyhttp/fileserver/browse.go
@@ -35,7 +35,7 @@ type Browse struct {
 	template *template.Template
 }
 
-func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
+func (fsrv *FileServer) serveBrowse(root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
 	// navigation on the client-side gets messed up if the
 	// URL doesn't end in a trailing slash because hrefs like
 	// "/b/c" on a path like "/a" end up going to "/b/c" instead
@@ -55,7 +55,7 @@ func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *ht
 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
 
 	// calling path.Clean here prevents weird breadcrumbs when URL paths are sketchy like /%2e%2e%2f
-	listing, err := fsrv.loadDirectoryContents(dir, path.Clean(r.URL.Path), repl)
+	listing, err := fsrv.loadDirectoryContents(dir, root, path.Clean(r.URL.Path), repl)
 	switch {
 	case os.IsPermission(err):
 		return caddyhttp.Error(http.StatusForbidden, err)
@@ -87,7 +87,7 @@ func (fsrv *FileServer) serveBrowse(dirPath string, w http.ResponseWriter, r *ht
 	return nil
 }
 
-func (fsrv *FileServer) loadDirectoryContents(dir *os.File, urlPath string, repl *caddy.Replacer) (browseListing, error) {
+func (fsrv *FileServer) loadDirectoryContents(dir *os.File, root, urlPath string, repl *caddy.Replacer) (browseListing, error) {
 	files, err := dir.Readdir(-1)
 	if err != nil {
 		return browseListing{}, err
@@ -95,9 +95,9 @@ func (fsrv *FileServer) loadDirectoryContents(dir *os.File, urlPath string, repl
 
 	// determine if user can browse up another folder
 	curPathDir := path.Dir(strings.TrimSuffix(urlPath, "/"))
-	canGoUp := strings.HasPrefix(curPathDir, fsrv.Root)
+	canGoUp := strings.HasPrefix(curPathDir, root)
 
-	return fsrv.directoryListing(files, canGoUp, urlPath, repl), nil
+	return fsrv.directoryListing(files, canGoUp, root, urlPath, repl), nil
 }
 
 // browseApplyQueryParams applies query parameters to the listing.
diff --git a/modules/caddyhttp/fileserver/browselisting.go b/modules/caddyhttp/fileserver/browselisting.go
index f3f85a37e..2b8c66ceb 100644
--- a/modules/caddyhttp/fileserver/browselisting.go
+++ b/modules/caddyhttp/fileserver/browselisting.go
@@ -27,7 +27,7 @@ import (
 	"github.com/dustin/go-humanize"
 )
 
-func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, repl *caddy.Replacer) browseListing {
+func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, root, urlPath string, repl *caddy.Replacer) browseListing {
 	filesToHide := fsrv.transformHidePaths(repl)
 
 	var dirCount, fileCount int
@@ -40,7 +40,7 @@ func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, urlP
 			continue
 		}
 
-		isDir := f.IsDir() || isSymlinkTargetDir(f, fsrv.Root, urlPath)
+		isDir := f.IsDir() || isSymlinkTargetDir(f, root, urlPath)
 
 		if isDir {
 			name += "/"
diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go
index caad0b4de..f64056431 100644
--- a/modules/caddyhttp/fileserver/staticfiles.go
+++ b/modules/caddyhttp/fileserver/staticfiles.go
@@ -184,7 +184,7 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
 	// to browse or return an error
 	if info.IsDir() {
 		if fsrv.Browse != nil && !fileHidden(filename, filesToHide) {
-			return fsrv.serveBrowse(filename, w, r, next)
+			return fsrv.serveBrowse(root, filename, w, r, next)
 		}
 		return fsrv.notFound(w, r, next)
 	}