mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	fileserver: Browse can show symlink target if enabled (#5973)
* Added optional subdirective to browse allowing to reveal symlink paths. * Update modules/caddyhttp/fileserver/browsetplcontext.go --------- Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									a7479302fc
								
							
						
					
					
						commit
						feb07a7b59
					
				@ -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(fileSystem fs.FS, root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
 | 
			
		||||
 | 
			
		||||
@ -962,7 +962,15 @@ footer {
 | 
			
		||||
						<td>
 | 
			
		||||
							<a href="{{html .URL}}">
 | 
			
		||||
								{{template "icon" .}}
 | 
			
		||||
								{{- if not .SymlinkPath}}
 | 
			
		||||
								<span class="name">{{html .Name}}</span>
 | 
			
		||||
								{{- else}}
 | 
			
		||||
								<span class="name">{{html .Name}} <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-narrow-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
 | 
			
		||||
									<path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l14 0" />
 | 
			
		||||
									<path d="M15 16l4 -4" />
 | 
			
		||||
									<path d="M15 8l4 4" />
 | 
			
		||||
								</svg> {{html .SymlinkPath}}</span>
 | 
			
		||||
								{{- end}}
 | 
			
		||||
							</a>
 | 
			
		||||
						</td>
 | 
			
		||||
						{{- if .IsDir}}
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ import (
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@ -74,12 +75,21 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
 | 
			
		||||
 | 
			
		||||
		size := info.Size()
 | 
			
		||||
		fileIsSymlink := isSymlink(info)
 | 
			
		||||
		symlinkPath := ""
 | 
			
		||||
		if fileIsSymlink {
 | 
			
		||||
			path := caddyhttp.SanitizedPathJoin(root, path.Join(urlPath, info.Name()))
 | 
			
		||||
			fileInfo, err := fs.Stat(fileSystem, path)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				size = fileInfo.Size()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if fsrv.Browse.RevealSymlinks {
 | 
			
		||||
				symLinkTarget, err := filepath.EvalSymlinks(path)
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					symlinkPath = 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
 | 
			
		||||
@ -101,6 +111,7 @@ func (fsrv *FileServer) directoryListing(ctx context.Context, fileSystem fs.FS,
 | 
			
		||||
			ModTime:     info.ModTime().UTC(),
 | 
			
		||||
			Mode:        info.Mode(),
 | 
			
		||||
			Tpl:         tplCtx, // a reference up to the template context is useful
 | 
			
		||||
			SymlinkPath: symlinkPath,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -237,6 +248,7 @@ type fileInfo struct {
 | 
			
		||||
	Mode        os.FileMode `json:"mode"`
 | 
			
		||||
	IsDir       bool        `json:"is_dir"`
 | 
			
		||||
	IsSymlink   bool        `json:"is_symlink"`
 | 
			
		||||
	SymlinkPath string      `json:"symlink_path,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// a pointer to the template context is useful inside nested templates
 | 
			
		||||
	Tpl *browseTemplateContext `json:"-"`
 | 
			
		||||
 | 
			
		||||
@ -112,6 +112,15 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
			}
 | 
			
		||||
			fsrv.Browse = new(Browse)
 | 
			
		||||
			d.Args(&fsrv.Browse.TemplateFile)
 | 
			
		||||
			for nesting := d.Nesting(); d.NextBlock(nesting); {
 | 
			
		||||
				if d.Val() != "reveal_symlinks" {
 | 
			
		||||
					return d.Errf("unknown subdirective '%s'", d.Val())
 | 
			
		||||
				}
 | 
			
		||||
				if fsrv.Browse.RevealSymlinks {
 | 
			
		||||
					return d.Err("Symlinks path reveal is already enabled")
 | 
			
		||||
				}
 | 
			
		||||
				fsrv.Browse.RevealSymlinks = true
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case "precompressed":
 | 
			
		||||
			var order []string
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
@ -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")
 | 
			
		||||
@ -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 {
 | 
			
		||||
@ -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))
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user