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:
parent
84b8b03dad
commit
851ced1286
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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[:])
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module ollie/lib9p
|
||||
|
||||
go 1.22
|
||||
|
||||
require 9fans.net/go v0.0.5
|
||||
34
lib9p/go.sum
34
lib9p/go.sum
|
|
@ -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=
|
||||
174
lib9p/lib9p.go
174
lib9p/lib9p.go
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue