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 }