package file import ( "bufio" "bytes" "fmt" "os" "strconv" "strings" ) type File interface { String() string Name() string Flags() string } type fdFile struct { fd int32 name string flags int32 } func NewFd(fd int32, name []byte, flags int32) fdFile { return fdFile{fd, stringValue(name), flags} } func NewFdWithPid(fd int32, pid uint32) fdFile { linkName, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", pid, fd)) if err != nil { return fdFile{fd, "?", -1} } flags, _ := readFlagsFromFdInfo(fd, pid) return fdFile{fd, linkName, flags} } func readFlagsFromFdInfo(fd int32, pid uint32) (int32, error) { data, err := os.ReadFile(fmt.Sprintf("/proc/%d/fdinfo/%d", pid, fd)) if err != nil { return -1, err } scanner := bufio.NewScanner(bytes.NewReader(data)) for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "flags:") { flagsStr := strings.Fields(line)[1] flags, err := strconv.ParseUint(flagsStr, 8, 32) return int32(flags), err } } return -1, scanner.Err() } func (f fdFile) Name() string { return f.name } func (f fdFile) Flags() string { return flagsToStr(f.flags) } func (f fdFile) String() string { var sb strings.Builder if len(f.name) == 0 { sb.WriteString("?") } else { sb.WriteString(f.name) sb.WriteString(" (") sb.WriteString(strconv.FormatInt(int64(f.fd), 10)) sb.WriteString(",") sb.WriteString(f.Flags()) sb.WriteString(")") } return sb.String() } type oldnameNewnameFile struct { Oldname, Newname string } func NewOldnameNewname(oldname, newname []byte) oldnameNewnameFile { return oldnameNewnameFile{stringValue(oldname), stringValue(newname)} } func (f oldnameNewnameFile) Name() string { return f.Newname } func (f oldnameNewnameFile) Flags() string { return "" } func (f oldnameNewnameFile) String() string { var sb strings.Builder sb.WriteString("old:") sb.WriteString(f.Oldname) sb.WriteString(" ->new:") sb.WriteString(f.Newname) sb.WriteString(" (") sb.WriteString(f.Flags()) sb.WriteString(")") return sb.String() } type pathnameFile struct { Pathname string } func NewPathname(pathname []byte) pathnameFile { return pathnameFile{stringValue(pathname)} } func (f pathnameFile) Name() string { return f.Pathname } func (f pathnameFile) Flags() string { return "" } func (f pathnameFile) String() string { var sb strings.Builder sb.WriteString("pathname:") sb.WriteString(f.Pathname) sb.WriteString(" (") sb.WriteString(f.Flags()) sb.WriteString(")") return sb.String() } // As data comes in from arrays, converted to slices, there will be null-bytes at the end.. func stringValue(byteStr []byte) string { // TODO: Hopefully, this won't cause a panic when the filename is as long as the array itself. Unit test this! return string(byteStr[:bytes.IndexByte(byteStr, 0)]) }