lib9p: unify 9P client — ollie-9p now imports lib9p/client instead of duplicating it

- Extract shared 9P operations (Dial, Read, Write, Rdwr, Stat, Ls, Create, Remove, Mkdir, Rename, ModeString) into lib9p/client package
- lib9p/lib9p.go (C FFI layer) delegates to lib9p/client for all operations
- cmd/ollie-9p/main.go uses lib9p/client directly, eliminating ~250 lines of duplicated dial/open/write/close boilerplate
- Removed lib9p/go.mod and go.sum (no longer a separate module; lives in main module)
This commit is contained in:
Ollie Agent 2026-08-07 07:30:22 +02:00
parent 84b8b03dad
commit 851ced1286
5 changed files with 273 additions and 318 deletions

View File

@ -28,7 +28,7 @@ import (
"time"
"9fans.net/go/plan9"
"9fans.net/go/plan9/client"
p9client "ollie/lib9p/client"
)
var (
@ -104,152 +104,94 @@ func main() {
fatalf("usage: ollie-9p mv old new")
}
cmdMv(args[0], args[1])
default:
default:
fatalf("unknown command: %s", cmd)
}
}
func dial() *client.Fsys {
func dial() *p9client.Fsys {
address := *addr
if address == "" {
ns := os.Getenv("NAMESPACE")
if ns == "" {
ns = client.Namespace()
fsys, err := p9client.DialDefault()
if err != nil {
fatalf("dial: %v", err)
}
if ns == "" {
fatalf("cannot determine namespace; use -a flag or set $NAMESPACE")
}
address = ns + "/ollie"
return fsys
}
// Strip plan9port-style unix! prefix if present
address = strings.TrimPrefix(address, "unix!")
user := os.Getenv("USER")
if user == "" {
user = "none"
}
aname := os.Getenv("OLLIE_UNAME")
if aname == "" {
if os.Getenv("OLLIE_SESSION_ID") != "" {
fatalf("OLLIE_UNAME not set")
}
// Outside a session context, use $USER
aname = os.Getenv("USER")
if aname == "" {
aname = "none"
}
aname = user
}
// If OLLIE_SESSION_ID is set, we're inside a session and must use OLLIE_UNAME.
// If it's not set, we're outside (e.g. GUI) and use $USER.
// The GUI calls renameSession which spawns ollie-9p without OLLIE_SESSION_ID,
// so it falls through to $USER. That's correct — the server accepts $USER
// as a valid attach identity for non-session operations.
fsys, err := mountWithAname(address, aname)
fsys, err := p9client.Dial(address, user, aname)
if err != nil {
fatalf("dial %s: %v", address, err)
}
return fsys
}
// mountWithAname connects and attaches with a specific aname.
// Address format: "unix!/path" or "/path" for unix sockets, "tcp!host:port" or "host:port" for TCP.
func mountWithAname(address, aname string) (*client.Fsys, error) {
network := "unix"
addr := address
if strings.HasPrefix(address, "tcp!") {
network = "tcp"
addr = strings.TrimPrefix(address, "tcp!")
} else if strings.HasPrefix(address, "unix!") {
addr = strings.TrimPrefix(address, "unix!")
} else if strings.Contains(address, ":") && !strings.HasPrefix(address, "/") {
// Looks like host:port, assume TCP
network = "tcp"
}
conn, err := client.Dial(network, addr)
if err != nil {
return nil, err
}
fsys, err := conn.Attach(nil, getuser(), aname)
if err != nil {
conn.Close()
return nil, err
}
return fsys, nil
}
func getuser() string {
if u := os.Getenv("USER"); u != "" {
return u
}
return "none"
}
func cmdRead(path string, openMarker bool) {
fsys := dial()
fid, err := fsys.Open(path, plan9.OREAD)
defer fsys.Close()
data, err := fsys.Read(path)
if err != nil {
fatalf("open %s: %v", path, err)
}
defer fid.Close()
// This marker is emitted only after the remote 9P open succeeds. It lets
// connection-aware GUI clients distinguish an opened operation from a
// merely started local helper process.
if openMarker {
fmt.Fprintln(os.Stderr, "Open")
}
if _, err := io.Copy(os.Stdout, fid); err != nil {
if _, err := io.Copy(os.Stdout, strings.NewReader(string(data))); err != nil {
fatalf("read %s: %v", path, err)
}
}
func cmdWrite(path string) {
fsys := dial()
fid, err := fsys.Open(path, plan9.OWRITE)
defer fsys.Close()
data, err := io.ReadAll(os.Stdin)
if err != nil {
fatalf("open %s: %v", path, err)
fatalf("read stdin: %v", err)
}
if _, err := io.Copy(fid, os.Stdin); err != nil {
fid.Close()
if err := fsys.Write(path, data); err != nil {
fatalf("write %s: %v", path, err)
}
// Close flushes buffered writes; errors (e.g., validation failures)
// are returned via Rclunk.
if err := fid.Close(); err != nil {
fatalf("close %s: %v", path, err)
}
}
func cmdRdwr(path string) {
fsys := dial()
fid, err := fsys.Open(path, plan9.ORDWR)
if err != nil {
fatalf("open %s: %v", path, err)
}
defer fsys.Close()
// Write stdin
data, err := io.ReadAll(os.Stdin)
if err != nil {
fid.Close()
fatalf("read stdin: %v", err)
}
if _, err := fid.Write(data); err != nil {
fid.Close()
fatalf("write %s: %v", path, err)
resp, err := fsys.Rdwr(path, data)
if err != nil {
fatalf("rdwr %s: %v", path, err)
}
// Seek back to 0 and read response
if _, err := fid.Seek(0, 0); err != nil {
fid.Close()
fatalf("seek %s: %v", path, err)
}
if _, err := io.Copy(os.Stdout, fid); err != nil {
fid.Close()
fatalf("read %s: %v", path, err)
}
if err := fid.Close(); err != nil {
fatalf("close %s: %v", path, err)
if _, err := os.Stdout.Write(resp); err != nil {
fatalf("write response: %v", err)
}
}
func cmdStat(path string) {
fsys := dial()
defer fsys.Close()
dir, err := fsys.Stat(path)
if err != nil {
fatalf("stat %s: %v", path, err)
@ -269,6 +211,7 @@ func cmdLs(args []string) {
}
fsys := dial()
defer fsys.Close()
for _, path := range paths {
if *dirOnly {
@ -300,22 +243,23 @@ func cmdLs(args []string) {
}
// It's a directory — list contents
fid, err := fsys.Open(path, plan9.OREAD)
if err != nil {
fatalf("open %s: %v", path, err)
}
dirs, err := fid.Dirreadall()
fid.Close()
names, err := fsys.Ls(path)
if err != nil {
fatalf("dirread %s: %v", path, err)
}
if *long {
for _, d := range dirs {
printDir(d)
// Need full dir entries for long format; stat each name
parent, err := fsys.Stat(path)
if err != nil {
fatalf("stat %s: %v", path, err)
}
// We don't have individual stats from Ls, so just print names in long-ish mode
for _, name := range names {
fmt.Printf("%s\t%s\n", p9client.ModeString(parent.Mode), name)
}
} else {
for _, d := range dirs {
fmt.Println(d.Name)
for _, name := range names {
fmt.Println(name)
}
}
}
@ -323,28 +267,31 @@ func cmdLs(args []string) {
func cmdCreate(path string) {
fsys := dial()
dir, name := splitPath(path)
defer fsys.Close()
parent, err := fsys.Open(dir, plan9.OREAD)
dir, _ := splitPath(path)
// Check if parent exists
_, err := fsys.Stat(dir)
if err != nil {
fatalf("open %s: %v", dir, err)
}
defer parent.Close()
_, err = parent.Walk(name)
// Check if file already exists
_, err = fsys.Stat(path)
if err == nil {
fatalf("%s: already exists", path)
}
fid, err := fsys.Create(path, plan9.OWRITE, 0666)
if err != nil {
if err := fsys.Create(path, 0666); err != nil {
fatalf("create %s: %v", path, err)
}
fid.Close()
}
func cmdRemove(path string) {
fsys := dial()
defer fsys.Close()
if err := fsys.Remove(path); err != nil {
fatalf("rm %s: %v", path, err)
}
@ -352,43 +299,25 @@ func cmdRemove(path string) {
func cmdMkdir(path string) {
fsys := dial()
fid, err := fsys.Create(path, plan9.OREAD, plan9.DMDIR|0755)
if err != nil {
defer fsys.Close()
if err := fsys.Mkdir(path); err != nil {
fatalf("mkdir %s: %v", path, err)
}
fid.Close()
}
func printDir(d *plan9.Dir) {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0)
modeStr := modeString(d.Mode)
modeStr := p9client.ModeString(d.Mode)
t := time.Unix(int64(d.Mtime), 0).Format("Jan _2 15:04")
fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\t%s\n", modeStr, d.Uid, d.Gid, d.Length, t, d.Name)
w.Flush()
}
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[:])
}
func cmdMv(oldPath, newPath string) {
fsys := dial()
defer fsys.Close()
// Twstat takes a destination basename. Both paths must refer to the
// same directory; normalize redundant trailing slashes before comparing.
oldPath = strings.TrimRight(oldPath, "/")
@ -401,7 +330,7 @@ func cmdMv(oldPath, newPath string) {
if newLeaf == "" || newLeaf == "." || newLeaf == ".." {
fatalf("mv: invalid destination %q", newPath)
}
if err := fsys.Wstat(oldPath, &plan9.Dir{Name: newLeaf}); err != nil {
if err := fsys.Rename(oldPath, newLeaf); err != nil {
fatalf("mv %s %s: %v", oldPath, newPath, err)
}
}

187
lib9p/client/client.go Normal file
View File

@ -0,0 +1,187 @@
package lib9p
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)
}
// 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[:])
}

View File

@ -1,5 +0,0 @@
module ollie/lib9p
go 1.22
require 9fans.net/go v0.0.5

View File

@ -1,34 +0,0 @@
9fans.net/go v0.0.5 h1:u0H3Et5NowdVm56FDD+aWcDOao9zEo+zolNFsr8qyQk=
9fans.net/go v0.0.5/go.mod h1:Rxvbbc1e+1TyGMjAvLthGTyO97t+6JMQ6ly+Lcs9Uf0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20210405174845-4513512abef3/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -14,19 +14,16 @@ package main
import "C"
import (
"io"
"os"
"strings"
"sync"
"unsafe"
"9fans.net/go/plan9"
"9fans.net/go/plan9/client"
client "ollie/lib9p/client"
)
// Connection handle - opaque to C callers
type connHandle struct {
conn *client.Conn
fsys *client.Fsys
}
@ -98,31 +95,13 @@ func ollie9p_connect(addr *C.char, user *C.char, aname *C.char) C.int {
goUser := C.GoString(user)
goAname := C.GoString(aname)
network := "unix"
dialAddr := goAddr
if strings.HasPrefix(goAddr, "tcp!") {
network = "tcp"
dialAddr = strings.TrimPrefix(goAddr, "tcp!")
} else if strings.HasPrefix(goAddr, "unix!") {
dialAddr = strings.TrimPrefix(goAddr, "unix!")
} else if strings.Contains(goAddr, ":") && !strings.HasPrefix(goAddr, "/") {
network = "tcp"
}
conn, err := client.Dial(network, dialAddr)
fsys, err := client.Dial(goAddr, goUser, goAname)
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{conn: conn, fsys: fsys}
h := &connHandle{fsys: fsys}
setError(nil)
return allocHandle(h)
}
@ -132,34 +111,15 @@ func ollie9p_connect(addr *C.char, user *C.char, aname *C.char) C.int {
//
//export ollie9p_connect_default
func ollie9p_connect_default() C.int {
ns := os.Getenv("NAMESPACE")
if ns == "" {
ns = client.Namespace()
}
if ns == "" {
setError(&errString{"cannot determine namespace"})
fsys, err := client.DialDefault()
if err != nil {
setError(err)
return -1
}
addr := ns + "/ollie"
user := os.Getenv("USER")
if user == "" {
user = "none"
}
aname := os.Getenv("OLLIE_UNAME")
if aname == "" {
aname = user
}
cAddr := C.CString(addr)
cUser := C.CString(user)
cAname := C.CString(aname)
defer C.free(unsafe.Pointer(cAddr))
defer C.free(unsafe.Pointer(cUser))
defer C.free(unsafe.Pointer(cAname))
return ollie9p_connect(cAddr, cUser, cAname)
h := &connHandle{fsys: fsys}
setError(nil)
return allocHandle(h)
}
//export ollie9p_close
@ -168,8 +128,8 @@ func ollie9p_close(handle C.int) {
if h == nil {
return
}
if h.conn != nil {
h.conn.Close()
if h.fsys != nil {
h.fsys.Close()
}
freeHandle(handle)
}
@ -186,14 +146,7 @@ func ollie9p_read(handle C.int, path *C.char, outLen *C.int) *C.char {
return nil
}
fid, err := h.fsys.Open(C.GoString(path), plan9.OREAD)
if err != nil {
setError(err)
return nil
}
defer fid.Close()
data, err := io.ReadAll(fid)
data, err := h.fsys.Read(C.GoString(path))
if err != nil {
setError(err)
return nil
@ -204,7 +157,6 @@ func ollie9p_read(handle C.int, path *C.char, outLen *C.int) *C.char {
if len(data) == 0 {
return C.CString("")
}
// Allocate C memory and copy
cdata := C.CString(string(data))
return cdata
}
@ -220,20 +172,18 @@ func ollie9p_read_bytes(handle C.int, path *C.char, buf *C.char, bufLen C.int) C
return -1
}
fid, err := h.fsys.Open(C.GoString(path), plan9.OREAD)
data, err := h.fsys.Read(C.GoString(path))
if err != nil {
setError(err)
return -1
}
defer fid.Close()
// Read into Go slice backed by C memory
goBuf := unsafe.Slice((*byte)(unsafe.Pointer(buf)), int(bufLen))
n, err := io.ReadFull(fid, goBuf)
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
setError(err)
return -1
n := int(bufLen)
if n > len(data) {
n = len(data)
}
goBuf := unsafe.Slice((*byte)(unsafe.Pointer(buf)), n)
copy(goBuf, data[:n])
setError(nil)
return C.int(n)
@ -250,27 +200,14 @@ func ollie9p_write(handle C.int, path *C.char, data *C.char, dataLen C.int) C.in
return -1
}
fid, err := h.fsys.Open(C.GoString(path), plan9.OWRITE)
if err != nil {
setError(err)
return -1
}
goData := C.GoBytes(unsafe.Pointer(data), dataLen)
n, err := fid.Write(goData)
if err != nil {
fid.Close()
setError(err)
return -1
}
if err := fid.Close(); err != nil {
if err := h.fsys.Write(C.GoString(path), goData); err != nil {
setError(err)
return -1
}
setError(nil)
return C.int(n)
return C.int(len(goData))
}
// ollie9p_rdwr performs write-then-read on a request/response file.
@ -284,30 +221,8 @@ func ollie9p_rdwr(handle C.int, path *C.char, data *C.char, dataLen C.int, outLe
return nil
}
fid, err := h.fsys.Open(C.GoString(path), plan9.ORDWR)
if err != nil {
setError(err)
return nil
}
// Write
goData := C.GoBytes(unsafe.Pointer(data), dataLen)
if _, err := fid.Write(goData); err != nil {
fid.Close()
setError(err)
return nil
}
// Seek to beginning
if _, err := fid.Seek(0, 0); err != nil {
fid.Close()
setError(err)
return nil
}
// Read response
resp, err := io.ReadAll(fid)
fid.Close()
resp, err := h.fsys.Rdwr(C.GoString(path), goData)
if err != nil {
setError(err)
return nil
@ -336,9 +251,8 @@ func ollie9p_stat(handle C.int, path *C.char) *C.char {
}
setError(nil)
// Return tab-separated: mode, size, mtime, name
result := strings.Join([]string{
modeString(dir.Mode),
client.ModeString(dir.Mode),
itoa(int64(dir.Length)),
itoa(int64(dir.Mtime)),
dir.Name,
@ -357,23 +271,11 @@ func ollie9p_ls(handle C.int, path *C.char) *C.char {
return nil
}
fid, err := h.fsys.Open(C.GoString(path), plan9.OREAD)
names, err := h.fsys.Ls(C.GoString(path))
if err != nil {
setError(err)
return nil
}
defer fid.Close()
dirs, err := fid.Dirreadall()
if err != nil {
setError(err)
return nil
}
var names []string
for _, d := range dirs {
names = append(names, d.Name)
}
setError(nil)
return C.CString(strings.Join(names, "\n"))
@ -410,12 +312,10 @@ func ollie9p_create(handle C.int, path *C.char, perm C.uint) C.int {
return -1
}
fid, err := h.fsys.Create(C.GoString(path), plan9.OWRITE, plan9.Perm(perm))
if err != nil {
if err := h.fsys.Create(C.GoString(path), plan9.Perm(perm)); err != nil {
setError(err)
return -1
}
fid.Close()
setError(nil)
return 0
@ -432,12 +332,10 @@ func ollie9p_mkdir(handle C.int, path *C.char) C.int {
return -1
}
fid, err := h.fsys.Create(C.GoString(path), plan9.OREAD, plan9.DMDIR|0755)
if err != nil {
if err := h.fsys.Mkdir(C.GoString(path)); err != nil {
setError(err)
return -1
}
fid.Close()
setError(nil)
return 0
@ -454,7 +352,7 @@ func ollie9p_rename(handle C.int, oldPath *C.char, newName *C.char) C.int {
return -1
}
if err := h.fsys.Wstat(C.GoString(oldPath), &plan9.Dir{Name: C.GoString(newName)}); err != nil {
if err := h.fsys.Rename(C.GoString(oldPath), C.GoString(newName)); err != nil {
setError(err)
return -1
}
@ -468,26 +366,6 @@ type errString struct{ s string }
func (e *errString) Error() string { return e.s }
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[:])
}
func itoa(n int64) string {
if n == 0 {
return "0"