Ctrl K
ComunidadGuías…

utils/files.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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"
}