utils/files.go
Rama main · réplica de lectura del repositorio original.
package utils
import (
"errors"
"io/fs"
"math"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/sirupsen/logrus"
)
const (
// Pathsep is the path separator for the current platform
Pathsep = string(filepath.Separator)
// KiB is a kilobyte (kibibyte)
KiB = 1024
// MiB is a megabyte (mibibyte)
MiB = 1024 * 1024
)
// WithinDir reports whether p is the same as, or a descendant of, root.
// The comparison is lexical, so resolve symlinks first, if that matters.
func WithinDir(root, p string) bool {
rel, err := filepath.Rel(root, p)
if err != nil {
return false
}
return rel != ".." && !strings.HasPrefix(rel, ".."+Pathsep)
}
// ResolvesWithinDir reports whether p, after following any symlinks, is
// still within root. Paths that do not exist are reported as within root,
// since there is nothing there to be served.
func ResolvesWithinDir(root, p string) bool {
resolved, err := filepath.EvalSymlinks(p)
if errors.Is(err, fs.ErrNotExist) {
return true
} else if err != nil {
return false
}
if r, err := filepath.EvalSymlinks(root); err == nil {
root = r
}
rootAbs, err := filepath.Abs(root)
if err != nil {
return false
}
resolvedAbs, err := filepath.Abs(resolved)
if err != nil {
return false
}
return WithinDir(rootAbs, resolvedAbs)
}
// URL2filename translates a given URL path to a probable full filename.
// URL paths that lead outside of dirname result in dirname itself.
func URL2filename(dirname, urlpath string) string {
if strings.Contains(urlpath, "..") {
logrus.Warn("Someone was trying to access a directory with .. in the URL")
return dirname + Pathsep
}
// Canonicalize first, so that the result can only be within dirname
cleaned := CanonicalURLPath(urlpath)
filename := filepath.Join(dirname, filepath.FromSlash(cleaned))
if !WithinDir(dirname, filename) {
logrus.Warn("Refusing to serve a filename outside of " + dirname)
return dirname + Pathsep
}
// filepath.Join drops the trailing slash that tells a directory from a file
if strings.HasSuffix(cleaned, "/") && !strings.HasSuffix(filename, Pathsep) {
filename += Pathsep
}
return filename
}
// GetFilenames retrieves a list of filenames from a given directory name (that must exist)
func GetFilenames(dirname string) []string {
dir, err := os.Open(dirname)
if err != nil {
logrus.WithFields(logrus.Fields{
"dirname": dirname,
"error": err.Error(),
}).Error("Could not open directory")
return []string{}
}
defer dir.Close()
filenames, err := dir.Readdirnames(-1)
if err != nil {
logrus.WithFields(logrus.Fields{
"dirname": dirname,
"error": err.Error(),
}).Error("Could not read filenames from directory")
return []string{}
}
return filenames
}
// DescribeBytes converts bytes to KiB or MiB. Returns a string.
func DescribeBytes(size int64) string {
if size < MiB {
return strconv.Itoa(int(math.Round(float64(size)*100.0/KiB)/100)) + " KiB"
}
return strconv.Itoa(int(math.Round(float64(size)*100.0/MiB)/100)) + " MiB"
}