Files
exec/proot/pathmap.go
T

126 lines
3.4 KiB
Go

package proot
import (
"errors"
"os"
"path/filepath"
"strings"
prootext "sirherobrine23.com.br/go-bds/exec/v2/proot/extensions/extensions"
)
type pathMapper struct {
root string
res []prootext.PathResolver
}
func newPathMapper(root string, resolvers []prootext.PathResolver) (*pathMapper, error) {
rootAbs, err := filepath.Abs(root)
if err != nil {
return nil, err
}
rootAbs = filepath.Clean(rootAbs)
if st, err := os.Stat(rootAbs); err != nil {
return nil, err
} else if !st.IsDir() {
return nil, errors.New("proot: Rootfs is not a directory")
}
return &pathMapper{root: rootAbs, res: append([]prootext.PathResolver(nil), resolvers...)}, nil
}
func cleanGuestPath(p string) string {
if p == "" {
return "/"
}
if !strings.HasPrefix(p, "/") {
p = "/" + p
}
p = filepath.Clean(p)
if p == "." {
return "/"
}
return p
}
func joinGuest(cwd, p string) string {
if p == "" {
return cleanGuestPath(cwd)
}
if strings.HasPrefix(p, "/") {
return cleanGuestPath(p)
}
return cleanGuestPath(filepath.Join(cleanGuestPath(cwd), p))
}
func hasHostPrefix(path, prefix string) bool {
path = filepath.Clean(path)
prefix = filepath.Clean(prefix)
return path == prefix || strings.HasPrefix(path, strings.TrimRight(prefix, string(os.PathSeparator))+string(os.PathSeparator))
}
func suffixAfterHostPrefix(path, prefix string) string {
if filepath.Clean(path) == filepath.Clean(prefix) {
return ""
}
return strings.TrimPrefix(filepath.Clean(path), strings.TrimRight(filepath.Clean(prefix), string(os.PathSeparator)))
}
func (pm *pathMapper) GuestToHost(guest string) string {
return pm.GuestToHostMode(guest, prootext.PathRead)
}
func (pm *pathMapper) GuestToHostMode(guest string, mode prootext.PathMode) string {
guest = cleanGuestPath(guest)
for i := len(pm.res) - 1; i >= 0; i-- {
resolver := pm.res[i]
resolution, err := resolver.ResolvePath(prootext.PathRequest{GuestPath: guest, Mode: mode})
if err == nil && resolution.Handled && resolution.HostPath != "" {
return filepath.Clean(resolution.HostPath)
}
}
return filepath.Clean(filepath.Join(pm.root, strings.TrimPrefix(guest, "/")))
}
func (pm *pathMapper) HostToGuest(host string) string {
if guest, ok := pm.hostToGuestAny(host); ok {
return guest
}
return filepath.Clean(host)
}
func (pm *pathMapper) hostToGuestAny(host string) (string, bool) {
for i := len(pm.res) - 1; i >= 0; i-- {
resolver := pm.res[i]
if guest, ok := resolver.HostToGuest(host); ok {
return guest, true
}
}
host = filepath.Clean(host)
if hasHostPrefix(host, pm.root) {
return cleanGuestPath(filepath.Join("/", suffixAfterHostPrefix(host, pm.root))), true
}
return "", false
}
func (pm *pathMapper) Translate(cwd, path string) (guest, host string) {
return pm.TranslateMode(cwd, path, prootext.PathRead)
}
func (pm *pathMapper) TranslateMode(cwd, path string, mode prootext.PathMode) (guest, host string) {
// A few paths are necessarily injected as host paths before the tracee is
// fully under ptrace control. The dynamic linker is the most visible case:
// when the kernel starts /rootfs/bin/ls directly it may later reopen that
// same absolute host path. Treat already-mapped host paths as canonical
// host paths instead of prefixing the rootfs a second time.
if filepath.IsAbs(path) {
if mappedGuest, ok := pm.hostToGuestAny(path); ok {
return mappedGuest, filepath.Clean(path)
}
}
guest = joinGuest(cwd, path)
host = pm.GuestToHostMode(guest, mode)
return guest, host
}