Skip to content

Commit

Permalink
p9: fix Remote usage of "." in walks (#67)
Browse files Browse the repository at this point in the history
* p9: Don't walk to "." in Remote.

* multiple: Fix some godoc comments.

* p9: Remove another possible ".".

* cmd/p9: Don't include `mount` command on systems that don't have FUSE.

* cmd/p9: Add some error logging to `mount` and fix EOFs when reading.
  • Loading branch information
DeedleFake authored Oct 18, 2019
1 parent 5eb85dc commit 54caf18
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 27 deletions.
21 changes: 18 additions & 3 deletions cmd/p9/mount.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// +build linux darwin freebsd

package main

import (
"context"
"errors"
"flag"
"fmt"
"io"
"log"
"path"

"bazil.org/fuse"
Expand Down Expand Up @@ -99,12 +104,12 @@ func (node *fuseNode) flags(f fuse.OpenFlags) (flags uint8) {
func (node *fuseNode) Attr(ctx context.Context, attr *fuse.Attr) error {
s, err := node.n.Stat(node.p)
if err != nil {
fmt.Printf("Error statting file: %v", err)
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()
Expand All @@ -124,7 +129,11 @@ func (node *fuseNode) Lookup(ctx context.Context, name string) (fs.Node, error)

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
if err != nil {
log.Printf("Error opening file: %v", err)
return nil, err
}
return &fuseNode{n: n}, nil
}

func (node *fuseNode) direntType(m p9.FileMode) fuse.DirentType {
Expand All @@ -142,18 +151,24 @@ func (node *fuseNode) direntType(m p9.FileMode) fuse.DirentType {

func (node *fuseNode) Read(ctx context.Context, req *fuse.ReadRequest, rsp *fuse.ReadResponse) error {
if req.Dir {
log.Printf("Tried to read file as a directory")
return fmt.Errorf("%#v", req)
}

buf := make([]byte, req.Size)
n, err := node.n.ReadAt(buf, req.Offset)
rsp.Data = buf[:n]
return err
if (err != nil) && !errors.Is(err, io.EOF) {
log.Printf("Error reading file: %v", err)
return err
}
return nil
}

func (node *fuseNode) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
e, err := node.n.Readdir()
if err != nil {
log.Printf("Error reading directory: %v", err)
return nil, err
}

Expand Down
11 changes: 8 additions & 3 deletions dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ func (d Dir) Stat(p string) (DirEntry, error) {
return DirEntry{}, err
}

return infoToEntry(fi), nil
e := infoToEntry(fi)
if e.EntryName == "." {
e.EntryName = ""
}

return e, nil
}

// WriteStat implements Attachment.WriteStat.
Expand Down Expand Up @@ -198,8 +203,8 @@ func (ro readOnlyAttachment) Remove(path string) error {
return errors.New("read-only filesystem")
}

// AuthFS allows simple wrapping and overwriting of the Auth() and
// Attach() methods of an existing FileSystem implementation, allowing
// AuthFS allows simple wrapping and overwriting of the Auth and
// Attach methods of an existing FileSystem implementation, allowing
// the user to add authentication support to a FileSystem that does
// not have it, or to change the implementation of that support for
// FileSystems that do.
Expand Down
50 changes: 40 additions & 10 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
// Package p9 contains an implementation of 9P, the Plan 9 from Bell Labs file protocol.
// Package p9 contains an implementation of 9P, the Plan 9 from Bell
// Labs file protocol.
//
// The package provides high-level APIs for both creating 9P servers and connecting to those servers as a client. Although it abstracts away a lot of the complexity of 9P, some familiarity with the protocol is advised. Like "net/http", it exposes a fair amount of the inner workings of the package so that a user can opt to build their own high-level implementation on top of it.
// The package provides high-level APIs for both creating 9P servers
// and connecting to those servers as a client. Although it abstracts
// away a lot of the complexity of 9P, some familiarity with the
// protocol is advised. Like "net/http", it exposes a fair amount of
// the inner workings of the package so that a user can opt to build
// their own high-level implementation on top of it.
//
// The primary concept that the user should understand is that in 9P, everything is referenced relative to previously referenced objects. For example, a typical 9P exchange, skipping version negotiation and authentication, might look something like this:
// The primary concept that the user should understand is that in 9P,
// everything is referenced relative to previously referenced objects.
// For example, a typical 9P exchange, skipping version negotiation
// and authentication, might look something like this:
//
// attach to filesystem "/" and call it 1
// navigate to file at "some/path" relative to 1 and call it 2
// navigate to file at "../woops/other/path" relative to 2 and call it 3
// open file 3
// read from file 3
//
// This package attempts to completely abstract away the navigation aspects of 9P, but a lot of things are still relative to others. For example, opening a file on the server from the client is done by calling the Open() method on an already existing file reference and passing it a path.
// This package attempts to completely abstract away the navigation
// aspects of 9P, but a lot of things are still relative to others.
// For example, opening a file on the server from the client is done
// by calling the Open method on an already existing file reference
// and passing it a path.
//
// The Client type provides a series of functionality that allows the user to connect to 9P servers. Here's an example of its use:
// The Client type provides a series of functionality that allows the
// user to connect to 9P servers. Here's an example of its use:
//
// c, _ := p9.Dial("tcp", addr)
// defer c.Close()
Expand All @@ -25,9 +39,25 @@
// defer file.Close()
// buf, _ := ioutil.ReadAll(file)
//
// The client is split into two main types: Client and Remote. Client provides the basic functionality for establishing a connection, performing authentication, and attaching to file hierarchies. Remote provides functionality for opening and creating files, getting information about them, and reading from and writing to them. They behave similarly to files themselves, implementing many of the same interfaces that os.File implements.
//
// The server works similarly to the "net/http" package, but, due to the major differences in the protocol being handled, is quite a bit more complicated. At the top level, there is a ListenAndServe() function, much like what is provided by "net/http". For most cases, the user can provide a filesystem by implementing the FileSystem interface and passing an instance of their implementation to the FSConnHandler() function to get a handler to pass to ListenAndServe().
//
// If the user only wants to serve local files, the Dir type provides a pre-built implementation of FileSystem that does just that. Similarly, the AuthFS type allows the user to add the ability to authenticate to a FileSystem implementation that otherwise has none, such as the aforementioned Dir.
// The client is split into two main types: Client and Remote. Client
// provides the basic functionality for establishing a connection,
// performing authentication, and attaching to file hierarchies.
// Remote provides functionality for opening and creating files,
// getting information about them, and reading from and writing to
// them. They behave similarly to files themselves, implementing many
// of the same interfaces that os.File implements.
//
// The server works similarly to the "net/http" package, but, due to
// the major differences in the protocol being handled, is quite a bit
// more complicated. At the top level, there is a ListenAndServe
// function, much like what is provided by "net/http". For most cases,
// the user can provide a filesystem by implementing the FileSystem
// interface and passing an instance of their implementation to the
// FSConnHandler function to get a handler to pass to ListenAndServe.
//
// If the user only wants to serve local files, the Dir type provides
// a pre-built implementation of FileSystem that does just that.
// Similarly, the AuthFS type allows the user to add the ability to
// authenticate to a FileSystem implementation that otherwise has
// none, such as the aforementioned Dir.
package p9
12 changes: 6 additions & 6 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ type FileSystem interface {
//
// All paths passed to the methods of this system begin with the aname
// used to attach the attachment, use forward slashes as separators,
// and have been cleaned using path.Clean(). For example, if the
// client attaches using the aname "/example" and then tries to open
// the file located at "some/other/example", the Open() method will be
// called with the path "/example/some/other/example".
// and have been cleaned using path.Clean. For example, if the client
// attaches using the aname "/example" and then tries to open the file
// located at "some/other/example", the Open method will be called
// with the path "/example/some/other/example".
type Attachment interface {
// Stat returns a DirEntry giving info about the file or directory
// at the given path. If an error is returned, the text of the error
Expand Down Expand Up @@ -147,7 +147,7 @@ type fsHandler struct {
//
// BUG: Tflush requests are not currently handled at all by this
// implementation due to no clear method of stopping a pending call to
// ReadAt() or WriteAt().
// ReadAt or WriteAt.
func FSHandler(fs FileSystem, msize uint32) proto.MessageHandler {
return &fsHandler{
fs: fs,
Expand All @@ -157,7 +157,7 @@ func FSHandler(fs FileSystem, msize uint32) proto.MessageHandler {
}
}

// FSConnHandler returns a ConnHandler that calls FSHandler() to
// FSConnHandler returns a ConnHandler that calls FSHandler to
// generate MessageHandlers.
//
// The returned ConnHandler implementation will print debug messages
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ go 1.13
require (
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
golang.org/x/tools v0.0.0-20191018000036-341939e08647 // indirect
)
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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=
golang.org/x/tools v0.0.0-20191018000036-341939e08647 h1:kfteFQIbFl9R5RwgjPgqDtpdeSnP/IfGo1R0ngom5vs=
golang.org/x/tools v0.0.0-20191018000036-341939e08647/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
4 changes: 2 additions & 2 deletions proto/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func Serve(lis net.Listener, p Proto, connHandler ConnHandler) (err error) {
}

// ListenAndServe is a convenience function that establishes a
// listener, via net.Listen(), and then calls Serve().
// listener, via net.Listen, and then calls Serve.
func ListenAndServe(network, addr string, p Proto, connHandler ConnHandler) (rerr error) {
lis, err := net.Listen(network, addr)
if err != nil {
Expand Down Expand Up @@ -130,7 +130,7 @@ func (h ConnHandlerFunc) MessageHandler() MessageHandler {

// MessageHandler handles messages for a single client connection.
//
// If a MessageHandler also implements io.Closer, then Close() will be
// If a MessageHandler also implements io.Closer, then Close will be
// called when the connection ends. Its return value is ignored.
type MessageHandler interface {
// HandleMessage is passed received messages from the client. Its
Expand Down
12 changes: 9 additions & 3 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ func (file *Remote) walk(p string) (*Remote, error) {
if w[0] != "/" {
w = strings.Split(w[0], "/")
}
if (len(w) == 1) && (w[0] == ".") {
w = nil
}
rsp, err := file.client.Send(&Twalk{
FID: file.fid,
NewFID: fid,
Expand All @@ -48,7 +51,10 @@ func (file *Remote) walk(p string) (*Remote, error) {
}
walk := rsp.(*Rwalk)

qid := walk.WQID[len(walk.WQID)-1]
qid := file.qid
if len(walk.WQID) != 0 {
qid = walk.WQID[len(walk.WQID)-1]
}
if len(walk.WQID) != len(w) {
qid = QID{
Type: 0xFF,
Expand Down Expand Up @@ -179,7 +185,7 @@ func (file *Remote) Seek(offset int64, whence int) (int64, error) {
}

// Read reads from the file at the internally-tracked offset. For more
// information, see ReadAt().
// information, see ReadAt.
func (file *Remote) Read(buf []byte) (int, error) {
file.m.Lock()
defer file.m.Unlock()
Expand Down Expand Up @@ -243,7 +249,7 @@ func (file *Remote) ReadAt(buf []byte, off int64) (int, error) {
}

// Write writes to the file at the internally-tracked offset. For more
// information, see WriteAt().
// information, see WriteAt.
func (file *Remote) Write(data []byte) (int, error) {
file.m.Lock()
defer file.m.Unlock()
Expand Down

0 comments on commit 54caf18

Please sign in to comment.