Skip to content

Commit

Permalink
cmd/p9: add mount command (#66)
Browse files Browse the repository at this point in the history
* cmd/p9: Begin working on FUSE-based 9P mounting.

* cmd/p9: Get basic, read-only mount working.

* p9: Clunk intermediary FIDs created by Remote.Stat().

* cmd/p9: Cleaner Node and Handle stuff and handle closing.
  • Loading branch information
DeedleFake authored Oct 17, 2019
1 parent 655eb25 commit 5eb85dc
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 18 deletions.
178 changes: 178 additions & 0 deletions cmd/p9/mount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package main

import (
"context"
"flag"
"fmt"
"path"

"bazil.org/fuse"
"bazil.org/fuse/fs"
"github.com/DeedleFake/p9"
"github.com/DeedleFake/p9/internal/util"
)

type mountCmd struct {
}

func (cmd *mountCmd) Name() string {
return "mount"
}

func (cmd *mountCmd) Desc() string {
return "Mount a 9P filesystem."
}

func (cmd *mountCmd) Run(options GlobalOptions, args []string) error {
fset := flag.NewFlagSet(cmd.Name(), flag.ExitOnError)
fset.Usage = func() {
fmt.Fprintf(fset.Output(), "%v mounts a 9P filesystem.\n", cmd.Name())
fmt.Fprintf(fset.Output(), "\n")
fmt.Fprintf(fset.Output(), "Usage: %v <mount point>\n", cmd.Name())
fset.PrintDefaults()
}
err := fset.Parse(args[1:])
if err != nil {
return util.Errorf("parse flags: %w", err)
}

args = fset.Args()
if len(args) != 1 {
return flag.ErrHelp
}

return attach(options, func(a *p9.Remote) error {
c, err := fuse.Mount(
args[0],
fuse.FSName("9p!"+options.Address),
fuse.Subtype("9p"),
fuse.VolumeName("9p!"+options.Address),
)
if err != nil {
return util.Errorf("mount: %w", err)
}
defer c.Close()

err = fs.Serve(c, &fuseFS{root: a})
if err != nil {
return util.Errorf("serve: %w", err)
}

<-c.Ready
if err := c.MountError; err != nil {
return util.Errorf("ready: %w", err)
}

return nil
})
}

type fuseFS struct {
root *p9.Remote
}

func (fs *fuseFS) Root() (fs.Node, error) {
return &fuseNode{n: fs.root}, nil
}

type fuseNode struct {
n *p9.Remote
p string
}

func (node *fuseNode) flags(f fuse.OpenFlags) (flags uint8) {
switch {
case f.IsReadOnly():
flags = p9.OREAD
case f.IsWriteOnly():
flags = p9.OWRITE
case f.IsReadWrite():
flags = p9.ORDWR
}
if f&fuse.OpenTruncate != 0 {
flags |= p9.OTRUNC
}

return flags
}

func (node *fuseNode) Attr(ctx context.Context, attr *fuse.Attr) error {
s, err := node.n.Stat(node.p)
if err != nil {
return err
}

attr.Inode = s.Path
attr.Size = s.Length
attr.Blocks = s.Length / 512
attr.Atime = s.ATime
attr.Mtime = s.MTime
attr.Mode = s.FileMode.OS()

return nil
}

func (node *fuseNode) Lookup(ctx context.Context, name string) (fs.Node, error) {
p := path.Join(node.p, name)
_, err := node.n.Stat(p)
if err != nil {
return nil, fuse.ENOENT
}

return &fuseNode{node.n, p}, nil
}

func (node *fuseNode) Open(ctx context.Context, req *fuse.OpenRequest, rsp *fuse.OpenResponse) (fs.Handle, error) {
n, err := node.n.Open(node.p, node.flags(req.Flags))
return &fuseNode{n: n}, err
}

func (node *fuseNode) direntType(m p9.FileMode) fuse.DirentType {
switch {
case m&p9.ModeDir != 0:
return fuse.DT_Dir
case m&p9.ModeSymlink != 0:
return fuse.DT_Link
case m&p9.ModeSocket != 0:
return fuse.DT_Socket
default:
return fuse.DT_Unknown
}
}

func (node *fuseNode) Read(ctx context.Context, req *fuse.ReadRequest, rsp *fuse.ReadResponse) error {
if req.Dir {
return fmt.Errorf("%#v", req)
}

buf := make([]byte, req.Size)
n, err := node.n.ReadAt(buf, req.Offset)
rsp.Data = buf[:n]
return err
}

func (node *fuseNode) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
e, err := node.n.Readdir()
if err != nil {
return nil, err
}

r := make([]fuse.Dirent, len(e))
for i := range e {
r[i] = fuse.Dirent{
Inode: e[i].Path,
Type: node.direntType(e[i].FileMode),
Name: e[i].EntryName,
}
}

return r, nil
}

func (node *fuseNode) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
return node.n.Close()
}

func init() {
RegisterCommand(&mountCmd{})
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module github.com/DeedleFake/p9

go 1.13

require (
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669 h1:FNCRpXiquG1aoyqcIWVFmpTSKVcx2bQD38uZZeGtdlw=
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
40 changes: 22 additions & 18 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,20 @@ func (file *Remote) Create(p string, perm FileMode, mode uint8) (*Remote, error)
// Remove deletes the file at p, relative to the current file. If p is
// "", it closes the current file, if open, and deletes it.
func (file *Remote) Remove(p string) error {
if p == "" {
_, err := file.client.Send(&Tremove{
FID: file.fid,
})
return err
}
if p != "" {
file, err := file.walk(p)
if err != nil {
return err
}
// Close is not necessary. Remove is also a clunk.

file, err := file.walk(p)
if err != nil {
return err
return file.Remove("")
}
return file.Remove("")

_, err := file.client.Send(&Tremove{
FID: file.fid,
})
return err
}

// Seek seeks a file. As 9P requires clients to track their own
Expand Down Expand Up @@ -312,23 +314,25 @@ func (file *Remote) Close() error {
// relative to the current file. If p is "", it is considered to be
// the current file.
func (file *Remote) Stat(p string) (DirEntry, error) {
if p == "" {
rsp, err := file.client.Send(&Tstat{
FID: file.fid,
})
if p != "" {
file, err := file.walk(p)
if err != nil {
return DirEntry{}, err
}
stat := rsp.(*Rstat)
defer file.Close()

return stat.Stat.DirEntry(), nil
return file.Stat("")
}

file, err := file.walk(p)
rsp, err := file.client.Send(&Tstat{
FID: file.fid,
})
if err != nil {
return DirEntry{}, err
}
return file.Stat("")
stat := rsp.(*Rstat)

return stat.Stat.DirEntry(), nil
}

// Readdir reads the file as a directory, returning the list of
Expand Down

0 comments on commit 5eb85dc

Please sign in to comment.