Documentation ¶
Overview ¶
Package p9p implements a compliant 9P2000 client and server library for use in modern, production Go services. This package differentiates itself in that is has departed from the plan 9 implementation primitives and better follows idiomatic Go style.
The package revolves around the session type, which is an enumeration of raw 9p message calls. A few calls, such as flush and version, have been elided, defering their usage to the server implementation. Sessions can be trivially proxied through clients and servers.
Getting Started ¶
The best place to get started is with Serve. Serve can be provided a connection and a handler. A typical implementation will call Serve as part of a listen/accept loop. As each network connection is created, Serve can be called with a handler for the specific connection. The handler can be implemented with a Session via the Dispatch function or can generate sessions for dispatch in response to client messages. (See cmd/9ps for an example)
On the client side, NewSession provides a 9p session from a connection. After a version negotiation, methods can be called on the session, in parallel, and calls will be sent over the connection. Call timeouts can be controlled via the context provided to each method call.
Framework ¶
This package has the beginning of a nice client-server framework for working with 9p. Some of the abstractions aren't entirely fleshed out, but most of this can center around the Handler.
Missing from this are a number of tools for implementing 9p servers. The most glaring are directory read and walk helpers. Other, more complex additions might be a system to manage in memory filesystem trees that expose multi-user sessions.
Differences ¶
The largest difference between this package and other 9p packages is simplification of the types needed to implement a server. To avoid confusing bugs and odd behavior, the components are separated by each level of the protocol. One example is that requests and responses are separated and they no longer hold mutable state. This means that framing, transport management, encoding, and dispatching are componentized. Little work will be required to swap out encodings, transports or connection implementations.
Context Integration ¶
This package has been wired from top to bottom to support context-based resource management. Everything from startup to shutdown can have timeouts using contexts. Not all close methods are fully in place, but we are very close to having controlled, predictable cleanup for both servers and clients. Timeouts can be very granular or very course, depending on the context of the timeout. For example, it is very easy to set a short timeout for a stat call but a long timeout for reading data.
Multiversion Support ¶
Currently, there is not multiversion support. The hooks and functionality are in place to add multi-version support. Generally, the correct space to do this is in the codec. Types, such as Dir, simply need to be extended to support the possibility of extra fields.
The real question to ask here is what is the role of the version number in the 9p protocol. It really comes down to the level of support required. Do we just need it at the protocol level, or do handlers and sessions need to be have differently based on negotiated versions?
Caveats ¶
This package has a number of TODOs to make it easier to use. Most of the existing code provides a solid base to work from. Don't be discouraged by the sawdust.
In addition, the testing is embarassingly lacking. With time, we can get full testing going and ensure we have confidence in the implementation.
Index ¶
- Constants
- Variables
- func DecodeDir(codec Codec, rd io.Reader, d *Dir) error
- func EncodeDir(codec Codec, wr io.Writer, d *Dir) error
- func GetVersion(ctx context.Context) string
- func ServeConn(ctx context.Context, cn net.Conn, handler Handler) error
- type Channel
- type Codec
- type Dir
- type Fcall
- type FcallType
- type Fid
- type Flag
- type Handler
- type HandlerFunc
- type Message
- type MessageRattach
- type MessageRauth
- type MessageRclunk
- type MessageRcreate
- type MessageRerror
- type MessageRflush
- type MessageRopen
- type MessageRread
- type MessageRremove
- type MessageRstat
- type MessageRversion
- type MessageRwalk
- type MessageRwrite
- type MessageRwstat
- type MessageTattach
- type MessageTauth
- type MessageTclunk
- type MessageTcreate
- type MessageTflush
- type MessageTopen
- type MessageTread
- type MessageTremove
- type MessageTstat
- type MessageTversion
- type MessageTwalk
- type MessageTwrite
- type MessageTwstat
- type QType
- type Qid
- type Readdir
- type Session
- type Tag
- Bugs
Constants ¶
const ( DefaultMSize = 64 << 10 DefaultVersion = "9P2000" )
const ( DMDIR = 0x80000000 // mode bit for directories DMAPPEND = 0x40000000 // mode bit for append only files DMEXCL = 0x20000000 // mode bit for exclusive use files DMMOUNT = 0x10000000 // mode bit for mounted channel DMAUTH = 0x08000000 // mode bit for authentication file DMTMP = 0x04000000 // mode bit for non-backed-up files // 9p2000.u extensions DMSYMLINK = 0x02000000 DMDEVICE = 0x00800000 DMNAMEDPIPE = 0x00200000 DMSOCKET = 0x00100000 DMSETUID = 0x00080000 DMSETGID = 0x00040000 DMREAD = 0x4 // mode bit for read permission DMWRITE = 0x2 // mode bit for write permission DMEXEC = 0x1 // mode bit for execute permission )
const ( OREAD Flag = 0x00 // open for read OWRITE = 0x01 // write ORDWR = 0x02 // read and write OEXEC = 0x03 // execute, == read but check execute permission // PROPOSAL(stevvooe): Possible protocal extension to allow the create of // symlinks. Initially, the link is created with no value. Read and write // to read and set the link value. OSYMLINK = 0x04 // or'd in OTRUNC = 0x10 // or'ed in (except for exec), truncate file first OCEXEC = 0x20 // or'ed in, close on exec ORCLOSE = 0x40 // or'ed in, remove on close )
const ( QTDIR QType = 0x80 // type bit for directories QTAPPEND = 0x40 // type bit for append only files QTEXCL = 0x20 // type bit for exclusive use files QTMOUNT = 0x10 // type bit for mounted channel QTAUTH = 0x08 // type bit for authentication file QTTMP = 0x04 // type bit for not-backed-up file QTFILE = 0x00 // plain file )
Variables ¶
var ( // 9p wire errors returned by Session interface methods ErrBadattach = new9pError("unknown specifier in attach") ErrBadoffset = new9pError("bad offset") ErrBadcount = new9pError("bad count") ErrBotch = new9pError("9P protocol botch") ErrCreatenondir = new9pError("create in non-directory") ErrDupfid = new9pError("duplicate fid") ErrDuptag = new9pError("duplicate tag") ErrIsdir = new9pError("is a directory") ErrNocreate = new9pError("create prohibited") ErrNomem = new9pError("out of memory") ErrNoremove = new9pError("remove prohibited") ErrNostat = new9pError("stat prohibited") ErrNotfound = new9pError("file not found") ErrNowrite = new9pError("write prohibited") ErrNowstat = new9pError("wstat prohibited") ErrPerm = new9pError("permission denied") ErrUnknownfid = new9pError("unknown fid") ErrBaddir = new9pError("bad directory in wstat") ErrWalknodir = new9pError("walk in non-directory") // extra errors not part of the normal protocol ErrTimeout = new9pError("fcall timeout") // returned when timing out on the fcall ErrUnknownTag = new9pError("unknown tag") ErrUnknownMsg = new9pError("unknown message") // returned when encountering unknown message type ErrUnexpectedMsg = new9pError("unexpected message") // returned when an unexpected message is encountered ErrWalkLimit = new9pError("too many wnames in walk") ErrClosed = errors.New("closed") )
Functions ¶
func GetVersion ¶
GetVersion returns the protocol version from the context. If the version is not known, an empty string is returned. This is typically set on the context passed into function calls in a server implementation.
Types ¶
type Channel ¶
type Channel interface { // ReadFcall reads one fcall frame into the provided fcall structure. The // Fcall may be cleared whether there is an error or not. If the operation // is successful, the contents of the fcall will be populated in the // argument. ReadFcall cannot be called concurrently with other calls to // ReadFcall. This both to preserve message ordering and to allow lockless // buffer reusage. ReadFcall(ctx context.Context, fcall *Fcall) error // WriteFcall writes the provided fcall to the channel. WriteFcall cannot // be called concurrently with other calls to WriteFcall. WriteFcall(ctx context.Context, fcall *Fcall) error // MSize returns the current msize for the channel. MSize() int // SetMSize sets the maximum message size for the channel. This must never // be called currently with ReadFcall or WriteFcall. SetMSize(msize int) }
Channel defines the operations necessary to implement a 9p message channel interface. Typically, message channels do no protocol processing except to send and receive message frames.
type Codec ¶
type Codec interface { // Unmarshal from data into the value pointed to by v. Unmarshal(data []byte, v interface{}) error // Marshal the value v into a byte slice. Marshal(v interface{}) ([]byte, error) // Size returns the encoded size for the target of v. Size(v interface{}) int }
Codec defines the interface for encoding and decoding of 9p types. Unsupported types will throw an error.
type Dir ¶
type Dir struct { Type uint16 Dev uint32 Qid Qid Mode uint32 AccessTime time.Time ModTime time.Time Length uint64 Name string UID string GID string MUID string }
func ReaddirAll ¶
ReaddirAll reads all the directory entries for the resource fid.
type Handler ¶
Handler defines an interface for 9p message handlers. A handler implementation could be used to intercept calls of all types before sending them to the next handler.
type HandlerFunc ¶
HandlerFunc is a convenience type for defining inline handlers.
type Message ¶
type Message interface { // Type returns the type of call for the target message. Type() FcallType }
Message represents the target of an fcall.
type MessageRattach ¶
type MessageRattach struct {
Qid Qid
}
func (MessageRattach) Type ¶
func (MessageRattach) Type() FcallType
type MessageRauth ¶
type MessageRauth struct {
Qid Qid
}
func (MessageRauth) Type ¶
func (MessageRauth) Type() FcallType
type MessageRclunk ¶
type MessageRclunk struct{}
func (MessageRclunk) Type ¶
func (MessageRclunk) Type() FcallType
type MessageRcreate ¶
func (MessageRcreate) Type ¶
func (MessageRcreate) Type() FcallType
type MessageRerror ¶
type MessageRerror struct {
Ename string
}
MessageRerror provides both a Go error type and message type.
func (MessageRerror) Error ¶
func (e MessageRerror) Error() string
func (MessageRerror) Type ¶
func (MessageRerror) Type() FcallType
type MessageRflush ¶
type MessageRflush struct{}
func (MessageRflush) Type ¶
func (MessageRflush) Type() FcallType
type MessageRopen ¶
func (MessageRopen) Type ¶
func (MessageRopen) Type() FcallType
type MessageRread ¶
type MessageRread struct {
Data []byte
}
func (MessageRread) Type ¶
func (MessageRread) Type() FcallType
type MessageRremove ¶
type MessageRremove struct{}
func (MessageRremove) Type ¶
func (MessageRremove) Type() FcallType
type MessageRstat ¶
type MessageRstat struct {
Stat Dir
}
func (MessageRstat) Type ¶
func (MessageRstat) Type() FcallType
type MessageRversion ¶
func (MessageRversion) Type ¶
func (MessageRversion) Type() FcallType
type MessageRwalk ¶
type MessageRwalk struct {
Qids []Qid
}
func (MessageRwalk) Type ¶
func (MessageRwalk) Type() FcallType
type MessageRwrite ¶
type MessageRwrite struct {
Count uint32
}
func (MessageRwrite) Type ¶
func (MessageRwrite) Type() FcallType
type MessageRwstat ¶
type MessageRwstat struct{}
func (MessageRwstat) Type ¶
func (MessageRwstat) Type() FcallType
type MessageTattach ¶
func (MessageTattach) Type ¶
func (MessageTattach) Type() FcallType
type MessageTauth ¶
func (MessageTauth) Type ¶
func (MessageTauth) Type() FcallType
type MessageTclunk ¶
type MessageTclunk struct {
Fid Fid
}
func (MessageTclunk) Type ¶
func (MessageTclunk) Type() FcallType
type MessageTcreate ¶
func (MessageTcreate) Type ¶
func (MessageTcreate) Type() FcallType
type MessageTflush ¶
type MessageTflush struct {
Oldtag Tag
}
func (MessageTflush) Type ¶
func (MessageTflush) Type() FcallType
type MessageTopen ¶
func (MessageTopen) Type ¶
func (MessageTopen) Type() FcallType
type MessageTread ¶
func (MessageTread) Type ¶
func (MessageTread) Type() FcallType
type MessageTremove ¶
type MessageTremove struct {
Fid Fid
}
func (MessageTremove) Type ¶
func (MessageTremove) Type() FcallType
type MessageTstat ¶
type MessageTstat struct {
Fid Fid
}
func (MessageTstat) Type ¶
func (MessageTstat) Type() FcallType
type MessageTversion ¶
MessageVersion encodes the message body for Tversion and Rversion RPC calls. The body is identical in both directions.
func (MessageTversion) Type ¶
func (MessageTversion) Type() FcallType
type MessageTwalk ¶
func (MessageTwalk) Type ¶
func (MessageTwalk) Type() FcallType
type MessageTwrite ¶
func (MessageTwrite) Type ¶
func (MessageTwrite) Type() FcallType
type MessageTwstat ¶
func (MessageTwstat) Type ¶
func (MessageTwstat) Type() FcallType
type Readdir ¶
type Readdir struct {
// contains filtered or unexported fields
}
Readdir helps one to implement the server-side of Session.Read on directories.
func NewFixedReaddir ¶
type Session ¶
type Session interface { Auth(ctx context.Context, afid Fid, uname, aname string) (Qid, error) Attach(ctx context.Context, fid, afid Fid, uname, aname string) (Qid, error) Clunk(ctx context.Context, fid Fid) error Remove(ctx context.Context, fid Fid) error Walk(ctx context.Context, fid Fid, newfid Fid, names ...string) ([]Qid, error) Read(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) Write(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) Open(ctx context.Context, fid Fid, mode Flag) (Qid, uint32, error) Create(ctx context.Context, parent Fid, name string, perm uint32, mode Flag) (Qid, uint32, error) Stat(ctx context.Context, fid Fid) (Dir, error) WStat(ctx context.Context, fid Fid, dir Dir) error // Version returns the supported version and msize of the session. This // can be affected by negotiating or the level of support provided by the // session implementation. Version() (msize int, version string) }
Session provides the central abstraction for a 9p connection. Clients implement sessions and servers serve sessions. Sessions can be proxied by serving up a client session.
The interface is also wired up with full context support to manage timeouts and resource clean up.
Session represents the operations covered in section 5 of the plan 9 manual (http://man.cat-v.org/plan_9/5/). Requests are managed internally, so the Flush method is handled by the internal implementation. Consider preceeding these all with context to control request timeout.
func NewSession ¶
NewSession returns a session using the connection. The Context ctx provides a context for out of bad messages, such as flushes, that may be sent by the session. The session can effectively shutdown with this context.
Notes ¶
Bugs ¶
Y2038 is coming.
The options here are to return 0, panic or make this return an error. Ideally, we make it safe to return 0 and have the rest of the package do the right thing. For now, we do this, but may want to panic until things are stable.
The options here are to return 0, panic or make this return an error. Ideally, we make it safe to return 0 and have the rest of the package do the right thing. For now, we do this, but may want to panic until things are stable.
There may be partial reads under timeout errors where this is actually fatal.
This is an awful tag allocation procedure. Replace this with something that let's us allocate tags and associate data with them, returning to them to a pool when complete. Such a system would provide a lot of information about outstanding requests.
Must detect duplicate tag and ensure that we are waking up the right caller. If a duplicate is received, the entry should not be deleted.
The Year 2038 is coming soon. 9p wire protocol has these as 4 byte epoch times. Some possibilities include time dilation fields or atemporal files. We can also just not use them and set them to zero.