diff --git a/cmd/ollie-9p/main.go b/cmd/ollie-9p/main.go index 03cf6fa..5542909 100644 --- a/cmd/ollie-9p/main.go +++ b/cmd/ollie-9p/main.go @@ -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< 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<