package rootstate import ( "os" "path/filepath" "strings" "golang.org/x/sys/unix" ) func ReadTrustedLink(path string) (string, error) { if !filepath.IsAbs(path) || filepath.Clean(path) != path { return "", os.ErrPermission } s, err := Open(filepath.Dir(path), false, true) if err != nil { return "", err } defer s.Close() return s.(*directory).readLink(filepath.Base(path)) } func (d *directory) readLink(name string) (string, error) { if !component(strings.ReplaceAll(name, "@", "_")) { return "", os.ErrPermission } if err := d.check(); err != nil { return "", err } fd, err := unix.Openat(int(d.file.Fd()), name, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) if err != nil { return "", err } defer unix.Close(fd) var st unix.Stat_t if err := unix.Fstat(fd, &st); err != nil { return "", err } if st.Uid != d.owner || st.Mode&unix.S_IFMT != unix.S_IFLNK || st.Nlink != 1 { return "", os.ErrPermission } var buf [4096]byte n, err := unix.Readlinkat(fd, "", buf[:]) if err != nil { return "", err } if n == len(buf) { return "", os.ErrPermission } return string(buf[:n]), nil }