Skip to content

Commit

Permalink
Added optional subdirective to browse allowing to reveal symlink paths.
Browse files Browse the repository at this point in the history
  • Loading branch information
armadi1809 committed Jan 9, 2024
1 parent 76611fa commit d04bdd9
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 3 deletions.
2 changes: 2 additions & 0 deletions modules/caddyhttp/fileserver/browse.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ var BrowseTemplate string
type Browse struct {
// Filename of the template to use instead of the embedded browse template.
TemplateFile string `json:"template_file,omitempty"`
// Determines whether or not targets of symlinks should be revealed.
RevealSymlinks bool `json:"reveal_symlinks,omitempty"`
}

func (fsrv *FileServer) serveBrowse(root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
Expand Down
15 changes: 15 additions & 0 deletions modules/caddyhttp/fileserver/browsetplcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net/url"
"os"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -74,12 +75,21 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, entries []fs.DirEn

size := info.Size()
fileIsSymlink := isSymlink(info)
symLinkRepresentation := ""
if fileIsSymlink {
path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name()))
fileInfo, err := fs.Stat(fsrv.fileSystem, path)
if err == nil {
size = fileInfo.Size()
}

if fsrv.Browse.RevealSymlinks {
symLinkTarget, err := filepath.EvalSymlinks(path)
if err == nil {
symLinkRepresentation = name + " -> " + symLinkTarget
}
}

// An error most likely means the symlink target doesn't exist,
// which isn't entirely unusual and shouldn't fail the listing.
// In this case, just use the size of the symlink itself, which
Expand All @@ -92,6 +102,11 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, entries []fs.DirEn

u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name

// Switch the name to the symlink representation if the entry is a symlink
if fsrv.Browse.RevealSymlinks && symLinkRepresentation != "" {
name = symLinkRepresentation
}

tplCtx.Items = append(tplCtx.Items, fileInfo{
IsDir: isDir,
IsSymlink: fileIsSymlink,
Expand Down
9 changes: 9 additions & 0 deletions modules/caddyhttp/fileserver/caddyfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
}
fsrv.Browse = new(Browse)
h.Args(&fsrv.Browse.TemplateFile)
for nesting := h.Nesting(); h.NextBlock(nesting); {
if h.Val() != "reveal_symlinks" {
return nil, h.Errf("unknown subdirective '%s'", h.Val())
}
if fsrv.Browse.RevealSymlinks {
return nil, h.Err("Symlinks path reveal is already enabled")
}
fsrv.Browse.RevealSymlinks = true
}

case "precompressed":
var order []string
Expand Down
7 changes: 4 additions & 3 deletions modules/caddyhttp/fileserver/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (
func init() {
caddycmd.RegisterCommand(caddycmd.Command{
Name: "file-server",
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--browse] [--access-log] [--precompressed]",
Usage: "[--domain <example.com>] [--root <path>] [--listen <addr>] [--browse] [--reveal-symlinks] [--access-log] [--precompressed]",
Short: "Spins up a production-ready file server",
Long: `
A simple but production-ready file server. Useful for quick deployments,
Expand All @@ -62,6 +62,7 @@ respond with a file listing.`,
cmd.Flags().StringP("root", "r", "", "The path to the root of the site")
cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener")
cmd.Flags().BoolP("browse", "b", false, "Enable directory browsing")
cmd.Flags().BoolP("reveal-symlinks", "", false, "Show symlink paths when browse is enabled.")
cmd.Flags().BoolP("templates", "t", false, "Enable template rendering")
cmd.Flags().BoolP("access-log", "a", false, "Enable the access log")
cmd.Flags().BoolP("debug", "v", false, "Enable verbose debug logs")
Expand Down Expand Up @@ -91,12 +92,12 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
templates := fs.Bool("templates")
accessLog := fs.Bool("access-log")
debug := fs.Bool("debug")
revealSymlinks := fs.Bool("reveal-symlinks")
compress := !fs.Bool("no-compress")
precompressed, err := fs.GetStringSlice("precompressed")
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid precompressed flag: %v", err)
}

var handlers []json.RawMessage

if compress {
Expand Down Expand Up @@ -150,7 +151,7 @@ func cmdFileServer(fs caddycmd.Flags) (int, error) {
}

if browse {
handler.Browse = new(Browse)
handler.Browse = &Browse{RevealSymlinks: revealSymlinks}
}

handlers = append(handlers, caddyconfig.JSONModuleObject(handler, "handler", "file_server", nil))
Expand Down

0 comments on commit d04bdd9

Please sign in to comment.