use 9fans client directly
This commit is contained in:
parent
16a2b72051
commit
b321eed99f
198
client/client.go
198
client/client.go
|
|
@ -1,198 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"9fans.net/go/plan9"
|
||||
p9client "9fans.net/go/plan9/client"
|
||||
)
|
||||
|
||||
// Fsys is an attached 9P filesystem handle.
|
||||
type Fsys struct {
|
||||
fsys *p9client.Fsys
|
||||
}
|
||||
|
||||
// Dial connects to the 9P server at the given address and attaches with the
|
||||
// specified user identity and attach name. Address format: "unix!/path" or
|
||||
// "/path" for unix sockets, "tcp!host:port" or "host:port" for TCP.
|
||||
func Dial(address, user, aname string) (*Fsys, error) {
|
||||
network := "unix"
|
||||
dialAddr := address
|
||||
if strings.HasPrefix(address, "tcp!") {
|
||||
network = "tcp"
|
||||
dialAddr = strings.TrimPrefix(address, "tcp!")
|
||||
} else if strings.HasPrefix(address, "unix!") {
|
||||
dialAddr = strings.TrimPrefix(address, "unix!")
|
||||
} else if strings.Contains(address, ":") && !strings.HasPrefix(address, "/") {
|
||||
network = "tcp"
|
||||
}
|
||||
conn, err := p9client.Dial(network, dialAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fsys, err := conn.Attach(nil, user, aname)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &Fsys{fsys: fsys}, nil
|
||||
}
|
||||
|
||||
// DialDefault connects using NAMESPACE env var (falls back to plan9 client
|
||||
// namespace) and USER/OLLIE_UNAME for identity.
|
||||
func DialDefault() (*Fsys, error) {
|
||||
ns := os.Getenv("NAMESPACE")
|
||||
if ns == "" {
|
||||
ns = p9client.Namespace()
|
||||
}
|
||||
if ns == "" {
|
||||
return nil, fmt.Errorf("cannot determine namespace; use -a flag or set $NAMESPACE")
|
||||
}
|
||||
address := ns + "/ollie"
|
||||
|
||||
user := os.Getenv("USER")
|
||||
if user == "" {
|
||||
user = "none"
|
||||
}
|
||||
aname := os.Getenv("OLLIE_UNAME")
|
||||
if aname == "" {
|
||||
aname = user
|
||||
}
|
||||
return Dial(address, user, aname)
|
||||
}
|
||||
|
||||
// Close releases the filesystem attachment.
|
||||
func (f *Fsys) Close() error {
|
||||
return f.fsys.Close()
|
||||
}
|
||||
|
||||
// Read reads a file's contents.
|
||||
func (f *Fsys) Read(path string) ([]byte, error) {
|
||||
fid, err := f.fsys.Open(path, plan9.OREAD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fid.Close()
|
||||
return io.ReadAll(fid)
|
||||
}
|
||||
|
||||
// OpenRead opens a file for reading and returns an io.ReadCloser.
|
||||
// The caller is responsible for closing it. Used for streaming reads
|
||||
// where data should be consumed incrementally.
|
||||
func (f *Fsys) OpenRead(path string) (io.ReadCloser, error) {
|
||||
fid, err := f.fsys.Open(path, plan9.OREAD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fid, nil
|
||||
}
|
||||
|
||||
// Write writes data to a file. Opens OWRITE, writes, then closes (flush).
|
||||
func (f *Fsys) Write(path string, data []byte) error {
|
||||
fid, err := f.fsys.Open(path, plan9.OWRITE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fid.Write(data); err != nil {
|
||||
fid.Close()
|
||||
return err
|
||||
}
|
||||
return fid.Close()
|
||||
}
|
||||
|
||||
// Rdwr performs write-then-read on a request/response file.
|
||||
func (f *Fsys) Rdwr(path string, data []byte) ([]byte, error) {
|
||||
fid, err := f.fsys.Open(path, plan9.ORDWR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := fid.Write(data); err != nil {
|
||||
fid.Close()
|
||||
return nil, err
|
||||
}
|
||||
if _, err := fid.Seek(0, 0); err != nil {
|
||||
fid.Close()
|
||||
return nil, err
|
||||
}
|
||||
resp, err := io.ReadAll(fid)
|
||||
fid.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Stat returns file metadata.
|
||||
func (f *Fsys) Stat(path string) (*plan9.Dir, error) {
|
||||
return f.fsys.Stat(path)
|
||||
}
|
||||
|
||||
// Ls lists directory contents, returning entry names.
|
||||
func (f *Fsys) Ls(path string) ([]string, error) {
|
||||
fid, err := f.fsys.Open(path, plan9.OREAD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fid.Close()
|
||||
dirs, err := fid.Dirreadall()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, len(dirs))
|
||||
for i, d := range dirs {
|
||||
names[i] = d.Name
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Create creates a file with the given permissions.
|
||||
func (f *Fsys) Create(path string, perm plan9.Perm) error {
|
||||
fid, err := f.fsys.Create(path, plan9.OWRITE, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fid.Close()
|
||||
}
|
||||
|
||||
// Remove removes a file.
|
||||
func (f *Fsys) Remove(path string) error {
|
||||
return f.fsys.Remove(path)
|
||||
}
|
||||
|
||||
// Mkdir creates a directory.
|
||||
func (f *Fsys) Mkdir(path string) error {
|
||||
fid, err := f.fsys.Create(path, plan9.OREAD, plan9.DMDIR|0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fid.Close()
|
||||
}
|
||||
|
||||
// Rename renames a file (Twstat with new name).
|
||||
func (f *Fsys) Rename(oldPath, newName string) error {
|
||||
return f.fsys.Wstat(oldPath, &plan9.Dir{Name: newName})
|
||||
}
|
||||
|
||||
// ModeString returns a human-readable permission string like "-rw-r--r--".
|
||||
func ModeString(mode plan9.Perm) string {
|
||||
var buf [10]byte
|
||||
if mode&plan9.DMDIR != 0 {
|
||||
buf[0] = 'd'
|
||||
} else {
|
||||
buf[0] = '-'
|
||||
}
|
||||
const rwx = "rwx"
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
if mode&(1<<uint(8-i*3-j)) != 0 {
|
||||
buf[1+i*3+j] = rwx[j]
|
||||
} else {
|
||||
buf[1+i*3+j] = '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(buf[:])
|
||||
}
|
||||
|
|
@ -3,12 +3,15 @@ package agent
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"9fans.net/go/plan9"
|
||||
p9client "9fans.net/go/plan9/client"
|
||||
client "ollie/client"
|
||||
"ollie/cmd/olliesrv/internal/backend"
|
||||
olog "ollie/log"
|
||||
|
|
@ -652,17 +655,40 @@ func (ag *Agent) FeedWrite(data []byte) {
|
|||
// Exits when ctx is cancelled or on read error.
|
||||
func ConsumeFeed(ctx context.Context, ag *Agent) {
|
||||
path := fmt.Sprintf("session/%s/agent/%s/feed", ag.sessionID, ag.id)
|
||||
client, err := client.DialDefault()
|
||||
ns := os.Getenv("NAMESPACE")
|
||||
if ns == "" {
|
||||
ns = p9client.Namespace()
|
||||
}
|
||||
user := os.Getenv("USER")
|
||||
if user == "" {
|
||||
user = "none"
|
||||
}
|
||||
aname := os.Getenv("OLLIE_UNAME")
|
||||
if aname == "" {
|
||||
aname = user
|
||||
}
|
||||
conn, err := p9client.Dial("unix", ns+"/ollie")
|
||||
if err != nil {
|
||||
ag.log.Error("feed consumer: dial: %v", err)
|
||||
return
|
||||
}
|
||||
fsys, err := conn.Attach(nil, user, aname)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
ag.log.Error("feed consumer: attach: %v", err)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
client.Close()
|
||||
fsys.Close()
|
||||
}()
|
||||
for ctx.Err() == nil {
|
||||
data, err := client.Read(path)
|
||||
fid, err := fsys.Open(path, plan9.OREAD)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data, err := io.ReadAll(fid)
|
||||
fid.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package agent
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
client "ollie/client"
|
||||
"9fans.net/go/plan9"
|
||||
p9client "9fans.net/go/plan9/client"
|
||||
)
|
||||
|
||||
// HandleCommand intercepts /commands before they reach the LLM.
|
||||
|
|
@ -18,14 +21,45 @@ func (ag *Agent) HandleCommand(_ context.Context, input string) bool {
|
|||
if rest == "" {
|
||||
return false
|
||||
}
|
||||
fsys, err := client.DialDefault()
|
||||
ns := os.Getenv("NAMESPACE")
|
||||
if ns == "" {
|
||||
ns = p9client.Namespace()
|
||||
}
|
||||
user := os.Getenv("USER")
|
||||
if user == "" {
|
||||
user = "none"
|
||||
}
|
||||
aname := os.Getenv("OLLIE_UNAME")
|
||||
if aname == "" {
|
||||
aname = user
|
||||
}
|
||||
conn, err := p9client.Dial("unix", ns+"/ollie")
|
||||
if err != nil {
|
||||
ag.emit(infoEvent("error: " + err.Error()))
|
||||
return true
|
||||
}
|
||||
fsys, err := conn.Attach(nil, user, aname)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
ag.emit(infoEvent("error: " + err.Error()))
|
||||
return true
|
||||
}
|
||||
defer fsys.Close()
|
||||
ctlPath := "session/" + ag.sessionID + "/agent/" + ag.id + "/ctl"
|
||||
out, err := fsys.Rdwr(ctlPath, []byte(rest))
|
||||
fid, err := fsys.Open(ctlPath, plan9.ORDWR)
|
||||
if err == nil {
|
||||
_, err = fid.Write([]byte(rest))
|
||||
}
|
||||
var out []byte
|
||||
if err == nil {
|
||||
_, err = fid.Seek(0, 0)
|
||||
}
|
||||
if err == nil {
|
||||
out, err = io.ReadAll(fid)
|
||||
}
|
||||
if fid != nil {
|
||||
fid.Close()
|
||||
}
|
||||
if err != nil {
|
||||
ag.emit(infoEvent("error: " + err.Error()))
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
client "ollie/client"
|
||||
"9fans.net/go/plan9"
|
||||
p9client "9fans.net/go/plan9/client"
|
||||
|
||||
"ollie/cmd/toolsrv/internal/registry"
|
||||
"ollie/cmd/toolsrv/internal/server"
|
||||
|
||||
|
|
@ -242,16 +244,32 @@ func pushProcInterrupt(proc *server.Proc) {
|
|||
}
|
||||
sockPath := fmt.Sprintf("/tmp/ns.%s.%s/ollie", u.Username, display)
|
||||
|
||||
fsys, err := client.Dial(sockPath, u.Username, "")
|
||||
conn, err := p9client.Dial("unix", sockPath)
|
||||
if err != nil {
|
||||
logger.Warn("pushProcInterrupt: failed to connect to olliesrv: %v", err)
|
||||
return
|
||||
}
|
||||
fsys, err := conn.Attach(nil, u.Username, "")
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
logger.Warn("pushProcInterrupt: failed to attach to olliesrv: %v", err)
|
||||
return
|
||||
}
|
||||
defer fsys.Close()
|
||||
|
||||
// Write to session/{sid}/agent/{aid}/prompt
|
||||
promptPath := fmt.Sprintf("session/%s/agent/%s/prompt", sid, aid)
|
||||
if err := fsys.Write(promptPath, []byte(msg)); err != nil {
|
||||
fid, err := fsys.Open(promptPath, plan9.OWRITE)
|
||||
if err != nil {
|
||||
logger.Warn("pushProcInterrupt: failed to open prompt: %v", err)
|
||||
return
|
||||
}
|
||||
_, err = fid.Write([]byte(msg))
|
||||
closeErr := fid.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
if err != nil {
|
||||
logger.Warn("pushProcInterrupt: failed to write prompt: %v", err)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,18 +11,20 @@ package main
|
|||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"9fans.net/go/plan9"
|
||||
client "ollie/client"
|
||||
p9client "9fans.net/go/plan9/client"
|
||||
)
|
||||
|
||||
// Connection handle - opaque to C callers
|
||||
type connHandle struct {
|
||||
fsys *client.Fsys
|
||||
fsys *p9client.Fsys
|
||||
}
|
||||
|
||||
// File handle - for streaming reads
|
||||
|
|
@ -124,11 +126,17 @@ func ollie9p_connect(addr *C.char, user *C.char, aname *C.char) C.int {
|
|||
goUser := C.GoString(user)
|
||||
goAname := C.GoString(aname)
|
||||
|
||||
fsys, err := client.Dial(goAddr, goUser, goAname)
|
||||
conn, err := dial(goAddr)
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
}
|
||||
fsys, err := conn.Attach(nil, goUser, goAname)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
setError(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
h := &connHandle{fsys: fsys}
|
||||
setError(nil)
|
||||
|
|
@ -140,7 +148,7 @@ func ollie9p_connect(addr *C.char, user *C.char, aname *C.char) C.int {
|
|||
//
|
||||
//export ollie9p_connect_default
|
||||
func ollie9p_connect_default() C.int {
|
||||
fsys, err := client.DialDefault()
|
||||
fsys, err := dialDefault()
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
|
|
@ -175,7 +183,7 @@ func ollie9p_read(handle C.int, path *C.char, outLen *C.int) *C.char {
|
|||
return nil
|
||||
}
|
||||
|
||||
data, err := h.fsys.Read(C.GoString(path))
|
||||
data, err := readFile(h.fsys, C.GoString(path))
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return nil
|
||||
|
|
@ -201,7 +209,7 @@ func ollie9p_read_bytes(handle C.int, path *C.char, buf *C.char, bufLen C.int) C
|
|||
return -1
|
||||
}
|
||||
|
||||
data, err := h.fsys.Read(C.GoString(path))
|
||||
data, err := readFile(h.fsys, C.GoString(path))
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
|
|
@ -230,7 +238,7 @@ func ollie9p_write(handle C.int, path *C.char, data *C.char, dataLen C.int) C.in
|
|||
}
|
||||
|
||||
goData := C.GoBytes(unsafe.Pointer(data), dataLen)
|
||||
if err := h.fsys.Write(C.GoString(path), goData); err != nil {
|
||||
if err := writeFile(h.fsys, C.GoString(path), goData); err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
}
|
||||
|
|
@ -251,7 +259,7 @@ func ollie9p_rdwr(handle C.int, path *C.char, data *C.char, dataLen C.int, outLe
|
|||
}
|
||||
|
||||
goData := C.GoBytes(unsafe.Pointer(data), dataLen)
|
||||
resp, err := h.fsys.Rdwr(C.GoString(path), goData)
|
||||
resp, err := rdwr(h.fsys, C.GoString(path), goData)
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return nil
|
||||
|
|
@ -281,7 +289,7 @@ func ollie9p_stat(handle C.int, path *C.char) *C.char {
|
|||
|
||||
setError(nil)
|
||||
result := strings.Join([]string{
|
||||
client.ModeString(dir.Mode),
|
||||
modeString(dir.Mode),
|
||||
itoa(int64(dir.Length)),
|
||||
itoa(int64(dir.Mtime)),
|
||||
dir.Name,
|
||||
|
|
@ -300,7 +308,7 @@ func ollie9p_ls(handle C.int, path *C.char) *C.char {
|
|||
return nil
|
||||
}
|
||||
|
||||
names, err := h.fsys.Ls(C.GoString(path))
|
||||
names, err := listDir(h.fsys, C.GoString(path))
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return nil
|
||||
|
|
@ -341,7 +349,7 @@ func ollie9p_create(handle C.int, path *C.char, perm C.uint) C.int {
|
|||
return -1
|
||||
}
|
||||
|
||||
if err := h.fsys.Create(C.GoString(path), plan9.Perm(perm)); err != nil {
|
||||
if err := create(h.fsys, C.GoString(path), plan9.Perm(perm)); err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
}
|
||||
|
|
@ -361,7 +369,7 @@ func ollie9p_mkdir(handle C.int, path *C.char) C.int {
|
|||
return -1
|
||||
}
|
||||
|
||||
if err := h.fsys.Mkdir(C.GoString(path)); err != nil {
|
||||
if err := mkdir(h.fsys, C.GoString(path)); err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
}
|
||||
|
|
@ -381,7 +389,7 @@ func ollie9p_rename(handle C.int, oldPath *C.char, newName *C.char) C.int {
|
|||
return -1
|
||||
}
|
||||
|
||||
if err := h.fsys.Rename(C.GoString(oldPath), C.GoString(newName)); err != nil {
|
||||
if err := h.fsys.Wstat(C.GoString(oldPath), &plan9.Dir{Name: C.GoString(newName)}); err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
}
|
||||
|
|
@ -404,7 +412,7 @@ func ollie9p_open(handle C.int, path *C.char) C.int {
|
|||
return -1
|
||||
}
|
||||
|
||||
reader, err := h.fsys.OpenRead(C.GoString(path))
|
||||
reader, err := h.fsys.Open(C.GoString(path), plan9.OREAD)
|
||||
if err != nil {
|
||||
setError(err)
|
||||
return -1
|
||||
|
|
@ -459,6 +467,143 @@ func ollie9p_close_fid(fid C.int) {
|
|||
freeFid(fid)
|
||||
}
|
||||
|
||||
func dial(address string) (*p9client.Conn, error) {
|
||||
network := "unix"
|
||||
dialAddr := address
|
||||
if strings.HasPrefix(address, "tcp!") {
|
||||
network = "tcp"
|
||||
dialAddr = strings.TrimPrefix(address, "tcp!")
|
||||
} else if strings.HasPrefix(address, "unix!") {
|
||||
dialAddr = strings.TrimPrefix(address, "unix!")
|
||||
} else if strings.Contains(address, ":") && !strings.HasPrefix(address, "/") {
|
||||
network = "tcp"
|
||||
}
|
||||
return p9client.Dial(network, dialAddr)
|
||||
}
|
||||
|
||||
func dialDefault() (*p9client.Fsys, error) {
|
||||
ns := os.Getenv("NAMESPACE")
|
||||
if ns == "" {
|
||||
ns = p9client.Namespace()
|
||||
}
|
||||
if ns == "" {
|
||||
return nil, fmt.Errorf("cannot determine namespace")
|
||||
}
|
||||
user := os.Getenv("USER")
|
||||
if user == "" {
|
||||
user = "none"
|
||||
}
|
||||
aname := os.Getenv("OLLIE_UNAME")
|
||||
if aname == "" {
|
||||
aname = user
|
||||
}
|
||||
conn, err := dial(ns + "/ollie")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fsys, err := conn.Attach(nil, user, aname)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return fsys, nil
|
||||
}
|
||||
|
||||
func readFile(fsys *p9client.Fsys, path string) ([]byte, error) {
|
||||
fid, err := fsys.Open(path, plan9.OREAD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fid.Close()
|
||||
return io.ReadAll(fid)
|
||||
}
|
||||
|
||||
func writeFile(fsys *p9client.Fsys, path string, data []byte) error {
|
||||
fid, err := fsys.Open(path, plan9.OWRITE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fid.Write(data); err != nil {
|
||||
fid.Close()
|
||||
return err
|
||||
}
|
||||
return fid.Close()
|
||||
}
|
||||
|
||||
func rdwr(fsys *p9client.Fsys, path string, data []byte) ([]byte, error) {
|
||||
fid, err := fsys.Open(path, plan9.ORDWR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := fid.Write(data); err != nil {
|
||||
fid.Close()
|
||||
return nil, err
|
||||
}
|
||||
if _, err := fid.Seek(0, 0); err != nil {
|
||||
fid.Close()
|
||||
return nil, err
|
||||
}
|
||||
result, err := io.ReadAll(fid)
|
||||
closeErr := fid.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, closeErr
|
||||
}
|
||||
|
||||
func create(fsys *p9client.Fsys, path string, perm plan9.Perm) error {
|
||||
fid, err := fsys.Create(path, plan9.OWRITE, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fid.Close()
|
||||
}
|
||||
|
||||
func mkdir(fsys *p9client.Fsys, path string) error {
|
||||
fid, err := fsys.Create(path, plan9.OREAD, plan9.DMDIR|0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fid.Close()
|
||||
}
|
||||
|
||||
func listDir(fsys *p9client.Fsys, path string) ([]string, error) {
|
||||
fid, err := fsys.Open(path, plan9.OREAD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dirs, err := fid.Dirreadall()
|
||||
fid.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, len(dirs))
|
||||
for i, dir := range dirs {
|
||||
names[i] = dir.Name
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func modeString(mode plan9.Perm) string {
|
||||
var buf [10]byte
|
||||
if mode&plan9.DMDIR != 0 {
|
||||
buf[0] = 'd'
|
||||
} else {
|
||||
buf[0] = '-'
|
||||
}
|
||||
const rwx = "rwx"
|
||||
for i := 0; i < 3; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
if mode&(1<<uint(8-i*3-j)) != 0 {
|
||||
buf[1+i*3+j] = rwx[j]
|
||||
} else {
|
||||
buf[1+i*3+j] = '-'
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
// Helper types
|
||||
type errString struct{ s string }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue