p9p

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2023 License: Apache-2.0 Imports: 15 Imported by: 1

README

p9p

GoDoc Apache licensed Go Report Card CI Test

A modern, performant 9P library for Go.

For API documentation, see the GoDoc.

Refer to 9P's documentation for more details on the protocol.

Example

Download the package in the usual way:

go get github.com/frobnitzem/go-p9p

Now run the server and client. Since there's no authentication, it's safest to put it into a unix socket.

cd $HOME/go/frobnitzem/go-p9p
go run cmd/9ps/main.go -root $HOME/src -addr unix:/tmp/sock9 &
chmod 700 /tmp/sock9
go run cmd/9pr/main.go -addr unix:/tmp/sock9

You should get a prompt,

/ 🐳 >

There's actually a tab-autocomplete to show you commands to run.

Bring the server down with:

kill %
rm /tmp/sock9

Build your own filesystem

To build a filesystem using this library, implement the FileSys interface, and then pass it to SFileSys. For details on the FileSys interface, see filesys.go. For examples, see ufs/ and sleepfs/ subdirectories.

For a main program running the ufs server, see cmd/9fs/.

In case you don't already have a 9p client, try cmd/9fr/, but note it is not full-featured.

Alternatively, if you want to implement a server that works at a lower level of the stack, you can implement a Session interface directly. See session.go for the interface definition and servefs.go for an implementation at this level.

Protocol Stack

This package handles the 9P protocol by implementing a stack of layers. For the server, each layer does some incremental translation of the messages "on the wire" into program state. The client is opposite, translating function calls into wire representations, and invoking client callbacks with responses from the server.

Applications can pick one of the layers to interact with. Lower layers generally require more work on the applications side (tracking state, encoding messages, etc.). It's generally recommended to interface only with the uppermost layer's API.

Extending the layers upward to make even simpler interface API-s, as well as implementing proper authentication and session encryption is on the TODO list.

Server Stack

sfilesys.go: SFileSys(fs FileSys) Session

  • Creates a Session type from an FileSys
  • The FileSys represents a fileserver broken into 3 levels, AuthFile-s, Dirent-s, and Files
  • Rather than track Fid-s, calls to Dirent-s create more Dirent and Files.

ssesssion.go: Dispatch(session Session) Handler

  • Delegates each type of messages.go:Message to a function call (from Session).
  • Tags and TFlush-s are handled at this level, so higher levels do not see them. Instead, they see context.Context objects to indicate potential cancellation.

serveconn.go: ServeConn(ctx context.Context, cn net.Conn, handler Handler) error

  • Negotiates protocol (with a timeout of 1 second).
  • Calls server loop, reading messages and sending them to the handler.
  • Version messages are handled at this level. They have the effect of deleting the current session and starting a new one. (Note: Check this to be sure.)

serverconn.go: (c *conn) serve() error

  • Server loop, strips Tags and TFlush messages
  • details:
    • runs reader and writer goroutines
    • maps from Tags to activeRequest structures
    • spawns a goroutine to call c.handler.Handle on every non-TFlush
      • these handlers get new contexts
    • for TFlush, cancels the corresponding call
Client Stack

cfilesys.go: CFileSys(session Session) FileSys

  • Creates an FileSys from a Session type
  • The FileSys wraps up the session API so the user does not need to see Fid-s or Qid-s, unless you want to.

// Note: we could make into CSession(Handler) (Session, error) client.go: CSession(ctx context.Context, conn net.Conn) (Session, error)

  • negotiates protocol, returns client object
  • client object has Walk/Stat/Open/Read methods that appear like synchronous send/receive pairs. Many requests can be sent in parallel, however (e.g. one goroutine each), and the transport will handle them in parallel, doing tag matching to return to the correct call.

transport.go: func newTransport(ctx context.Context, ch Channel) roundTripper

  • starts a handle goroutine to take messages off the wire and invoke waiting response actions in the client.
  • roundTripper uses an internal channel to communicate with the handle so that each roundTripper can be synchronous, while the handler actually handles may requests.
Common lower-layers

channel.go: NewChannel(conn net.Conn, msize int) Channel

  • typical argument for codec is codec9p
  • called by ServeConn to serialize read/write from the net.Conn
  • Channel interface provides ReadFcall, WriteFcall, MSize, SetMSize

channel.go: (ch *channel) WriteFcall(ctx context.Context, fcall *Fcall) error channel.go: channel.go: (ch *channel) ReadFcall(ctx context.Context, fcall *Fcall) error

  • check context for I/O cancellations
  • last check on Msize (may result in error)
  • for writing, call codec.Marshal, then sendmsg()
  • for reading, call readmsg, then codec.Unmarshal

messages.go: newMessage(typ FcallType) (Message, error)

  • called by encoding.go to write out structs (e.g. MessageTopen) for each type of message

encoding.go: interface Codec

  • provides Marshal, Unmarshal, and Size for converting between []byte and 9P message structs.
Differences between client and server:
  • The server auto-generates calls to File.Close(). This function does nothing on the client side (use Clunk instead).

  • The client qids = Walk(names...) returns an Error/Warning when Walk returns a partial, incomplete walk to the destination. This happens if len(names) > 1 and walk returns len(qids) != len(names).

Server Locking Sequences

Lookup a Fid:

  1. assert Fid != NOFID
  2. lock session, lookup fid, unlock session
  3. lock ref
    • This ensures that the ref was unlocked at some past point.
  4. if ref.ent == nil, fid is being deleted, unlock and return "no fid"
  5. return ref in locked state
    • Client unlocks when they are done with operation on Fid.
    • This forces no parallel operations on Fid at all.
    • Convention: clone fids if you want parallel access.

Clunk/Remove a Fid:

  1. lock session, lookup fid, unlock session
  2. lock ref, lock session, delete fid:ref, unlock session, unlock ref
  3. close if ref.file != nil
  4. clunk/remove ref.ent
    • This doesn't work because we may get multiple Walk/Open/Stat requests in parallel with Clunk. We need a way to stop those actions, then clunk.

Auth:

  1. lock session, lookup afid (ensure not present), create locked aref, store afid:aref, unlock session
  2. call Auth function to set aref.afile
  3. unlock afid

Attach:

  1. if fs.RequireAuth()): a. lock session, lookup aref, unlock session b. lock aref, call ok := aref.afile.Success(), unlock aref c. assert ok
  2. ref := holdRef [lock session, lookup fid (ensure not present), create locked ref, store afid:aref, unlock session]
  3. ent, err := Root()
  4. if err { lock session, delete fid:ref, unlock session, return }
  5. set ref.ent = ent, unlock ref

Note: Rather than using a locked fid lookup table, we could have the dispatcher do fid lookups, and send requests on a channel dedicated to (a goroutine) serving only that fid. This may be an optimization, or it may not - only lots of work and profiling will tell. So, this has not been tried.

Copyright © 2015 Docker, Inc. Copyright © 2023 UT-Battelle LLC. go-p9p is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

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, deferring 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 SSession function or can generate sessions for dispatch in response to client messages. (See logging.go and cmd/9ps for an example)

On the client side, CSession 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

There is a lot of state in the 9P2000 protocol. Most of this is necessary to track conversations between clients and servers. However, it also makes validating the server very hard. In particular the protocol definition itself does not have a formal finite-state machine for client or server activities. These should be separately formulated and the implementations should be checked against them to ensure things like shutdown sequences are well defined. Recent updates to the package have made a best effort to close out all state when clients disconnect.

Also, not all of the permission and validity checks mentioned in the 9P2000 docs are currently implemented. For example, both C and S-FileSys have the ability to cache all file Qids, so they should check QType and error out early if the resource type doesn't make sense for a call.

In addition, testing could use improvement. More servers using this functionality and generating bug reports are welcome!

Index

Constants

View Source
const (
	// DefaultMSize messages size used to establish a session.
	DefaultMSize = 64 << 10

	// DefaultVersion for this package. Currently, the only supported version.
	DefaultVersion = "9P2000"
)
View Source
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

	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
)

Mode constants for use Dir.Mode.

Variables

View Source
var (
	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")
	ErrNoread       = new9pError("read prohibited")
	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")

	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")
)

9p wire errors returned by Session interface methods

Functions

func CreateName

func CreateName(dir string, name string) (string, error)

Find the result of Create(name) from the directory dir. Checks that name is valid (i.e. does not contain slashes, and is not "", "." or "..").

On success, the result is always a valid internal path.

func DecodeDir

func DecodeDir(codec Codec, rd io.Reader, d *Dir) error

DecodeDir decodes a directory entry from rd using the provided codec.

func EncodeDir

func EncodeDir(codec Codec, wr io.Writer, d *Dir) error

EncodeDir writes the directory to wr.

func EnsureNonNil

func EnsureNonNil(x interface{}, err error) error

TODO(frobnitzem): validate required server returns to ensure non-nil.

func GetVersion

func GetVersion(ctx context.Context) string

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.

func IsDir

func IsDir(d Dirent) bool

Helper function to check Dirent.Qid().Type for QTDIR bit.

func NormalizePath

func NormalizePath(args []string) ([]string, int)

Normalize a path by removing all "" and "." elements, and treating all ".." as backspaces. The result may only contain ".." elements at the beginning of the path. Functional, so it effectively copies the path.

Returns (cleaned path, backspaces) where backspaces = -1 in case of an error or else indicates the number of leading ".." elements in case of success.

Note: path.Clean does this probably more efficiently, but doesn't leave .. at the root, which we need.

func Overflow

func Overflow(err error) int

Overflow will return a positive number, indicating there was an overflow for the error.

func ServeConn

func ServeConn(ctx context.Context, cn net.Conn, handler Handler) error

ServeConn the 9p handler over the provided network connection. When the connection encounters an error or disconnects, this returns the value of handler.Stop(err). TODO(frobnitzem): Ensure unexpected version messages are handled correctly.

func ToWalk

func ToWalk(ent Dirent, p string) (isAbs bool, steps []string, err error)

Determine the starting Dirent and path elements to send to Walk() in order to reach path p.

func ValidPath

func ValidPath(args []string) int

Returns -1 if any path elements are '.' or contain characters '/' or '\' or if .. follows a non-.. Otherwise, returns the number of leading .. elements.

func WalkName

func WalkName(dir string, names ...string) (string, error)

Find the absolute path of names relative to dir.

dir must be a valid internal path. names are validated. They are not re-ordered or changed (e.g. to process "a/../" etc.), so names that contain ".", "", or non-".." before ".." will return an error.

On success, the result is always a valid internal path.

Types

type AuthFile

type AuthFile interface {
	File // For read/write in auth protocols.
	Close(ctx context.Context) error
	Success() bool // Was the authentication successful?
}

type CancelledCtxt

type CancelledCtxt struct{}

Simple context representing a past-due deadline.

func (CancelledCtxt) Deadline

func (_ CancelledCtxt) Deadline() (time.Time, bool)

func (CancelledCtxt) Done

func (_ CancelledCtxt) Done() <-chan struct{}

func (CancelledCtxt) Err

func (_ CancelledCtxt) Err() error

func (CancelledCtxt) Value

func (_ CancelledCtxt) Value(key interface{}) (val interface{})

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.

func NewChannel

func NewChannel(conn net.Conn, msize int) Channel

NewChannel returns a new channel to read and write Fcalls with the provided connection and message size.

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.

func NewCodec

func NewCodec() Codec

NewCodec returns a new, standard 9P2000 codec, ready for use. TODO(frobnitzem): enforce no extensions (e.g. 9P2000.u) at protocol level

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
}

Dir defines the structure used for expressing resources in stat/wstat and when reading directories.

func ReaddirAll

func ReaddirAll(session Session, fid Fid) ([]Dir, error)

ReaddirAll reads all the directory entries for the resource fid.

func (Dir) String

func (d Dir) String() string

type Dirent

type Dirent interface {
	Qid() Qid

	// OpenDir, Walk, Create are only called if IsDir()
	// Note: IsDir iff. Qid.Type & p9p.QTDIR != 0
	OpenDir(ctx context.Context) (ReadNext, error)

	// Walk is guaranteed not to see '.' or have paths containing '/'
	// NOTE(frobnitzem): we could expand Walk to take a bool
	// indicating whether to re-use the same Fid.
	// For now, the client assumes it will make a new Fid,
	// and the server honors the client's choice either way.
	Walk(ctx context.Context, name ...string) ([]Qid, Dirent, error)
	// Note: Open is not called after create.
	// The server must create and open together.
	//
	// If the file represents a Dir, then the file returned
	// is discarded and replaced with a nice directory entry reader.
	Create(ctx context.Context, name string,
		perm uint32, mode Flag) (Dirent, File, error)

	// Methods on file
	// Note: Open() is not called if IsDir()
	// An internal implementation calls OpenDir() instead.
	Open(ctx context.Context, mode Flag) (File, error)

	// Note: If remove is called, the Dirent will no longer
	// be referenced by the server, but Clunk will not be called.
	// If remove is called on an open file, it is responsible
	// for releasing server resources associated with the file.
	Remove(ctx context.Context) error
	// If Clunk is called on an open file, it is responsible
	// for releasing server resources associated with the file.
	// It is also responsible for implementing actions like ORCLOSE.
	Clunk(ctx context.Context) error

	Stat(ctx context.Context) (Dir, error)
	WStat(ctx context.Context, stat Dir) error
}

Simplified interface for servers to implement files.

type Fcall

type Fcall struct {
	Type    FcallType
	Tag     Tag
	Message Message
}

Fcall defines the fields for sending a 9p formatted message. The type will be introspected from the Message implementation.

func (*Fcall) String

func (fc *Fcall) String() string

type FcallType

type FcallType uint8

FcallType encodes the message type for the target Fcall.

const (
	Tversion FcallType = iota + 100
	Rversion
	Tauth
	Rauth
	Tattach
	Rattach
	Terror
	Rerror
	Tflush
	Rflush
	Twalk
	Rwalk
	Topen
	Ropen
	Tcreate
	Rcreate
	Tread
	Rread
	Twrite
	Rwrite
	Tclunk
	Rclunk
	Tremove
	Rremove
	Tstat
	Rstat
	Twstat
	Rwstat
	Tmax
)

Definitions for Fcall's used in 9P2000.

func (FcallType) String

func (fct FcallType) String() string

type Fid

type Fid uint32

Fid defines a type to hold Fid values.

const NOFID Fid = ^Fid(0)

NOFID indicates the lack of an Fid.

type File

type File interface {
	Read(ctx context.Context, p []byte, offset int64) (int, error)
	Write(ctx context.Context, p []byte, offset int64) (int, error)

	// IOUnit must be >0 on the client side (unless the File is invalid)
	// May be 0 on the server side.
	// Must not be <0.
	IOUnit() int
}

Simplified interface to a file that has been Open-ed. Note: Since a Dirent can only be opened once, it is up to Clunk to close any underlying File state, Including remove on close (ORCLOSE flag) FileSys won't call it automatically.

type FileSys

type FileSys interface {
	RequireAuth(ctx context.Context) bool
	Auth(ctx context.Context, uname, aname string) (AuthFile, error)
	Attach(ctx context.Context, uname, aname string, af AuthFile) (Dirent, error)
}

Note: Like information from all ent/file inputs, the server should copy details from the AuthFile if you want to retain them internally past the lifetime of this function call.

The client, on the other hand, uniquely owns the function outputs.

RequireAuth is not able to be determined reliably by the client, which will always return false without asking the server.

func CFileSys

func CFileSys(session Session) FileSys

type Flag

type Flag uint8

Flag defines the flag type for use with open and create

const (
	OREAD  Flag = 0x00 // open for read
	OWRITE Flag = 0x01 // write
	ORDWR  Flag = 0x02 // read and write
	OEXEC  Flag = 0x03 // execute, == read but check execute permission

	OSYMLINK Flag = 0x04

	OTRUNC  Flag = 0x10 // or'ed in (except for exec), truncate file first
	OCEXEC  Flag = 0x20 // or'ed in, close on exec
	ORCLOSE Flag = 0x40 // or'ed in, remove on close
)

Constants to use when opening files.

type Handler

type Handler interface {
	Handle(ctx context.Context, msg Message) (Message, error)
	Stop(error) error
}

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. This is different than roundTripper because it handles multiple messages, and needs to provide a shutdown callback.

func SSession

func SSession(session Session) Handler

SSession returns a handler that transforms messages into function calls (dispatching to Session's methods). Since the ServeConn function calls the handler from gorotines, no concurrency is managed by the handler this defined by SSession.

Instead, the Handler simply turns messages into function calls on the session.

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

type MessageRcreate struct {
	Qid    Qid
	IOUnit uint32
}

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 ensures that 9p errors can be transparently used as a 9p message in an Fcall.

type MessageRflush

type MessageRflush struct{}

func (MessageRflush) Type

func (MessageRflush) Type() FcallType

type MessageRopen

type MessageRopen struct {
	Qid    Qid
	IOUnit uint32
}

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

type MessageRversion struct {
	MSize   uint32
	Version string
}

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

type MessageTattach struct {
	Fid   Fid
	Afid  Fid
	Uname string
	Aname string
}

func (MessageTattach) Type

func (MessageTattach) Type() FcallType

type MessageTauth

type MessageTauth struct {
	Afid  Fid
	Uname string
	Aname string
}

func (MessageTauth) Type

func (MessageTauth) Type() FcallType

type MessageTclunk

type MessageTclunk struct {
	Fid Fid
}

func (MessageTclunk) Type

func (MessageTclunk) Type() FcallType

type MessageTcreate

type MessageTcreate struct {
	Fid  Fid
	Name string
	Perm uint32
	Mode Flag
}

func (MessageTcreate) Type

func (MessageTcreate) Type() FcallType

type MessageTflush

type MessageTflush struct {
	Oldtag Tag
}

func (MessageTflush) Type

func (MessageTflush) Type() FcallType

type MessageTopen

type MessageTopen struct {
	Fid  Fid
	Mode Flag
}

func (MessageTopen) Type

func (MessageTopen) Type() FcallType

type MessageTread

type MessageTread struct {
	Fid    Fid
	Offset uint64
	Count  uint32
}

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

type MessageTversion struct {
	MSize   uint32
	Version string
}

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

type MessageTwalk struct {
	Fid    Fid
	Newfid Fid
	Wnames []string
}

func (MessageTwalk) Type

func (MessageTwalk) Type() FcallType

type MessageTwrite

type MessageTwrite struct {
	Fid    Fid
	Offset uint64
	Data   []byte
}

func (MessageTwrite) Type

func (MessageTwrite) Type() FcallType

type MessageTwstat

type MessageTwstat struct {
	Fid  Fid
	Stat Dir
}

func (MessageTwstat) Type

func (MessageTwstat) Type() FcallType

type QType

type QType uint8

QType indicates the type of a resource within the Qid.

const (
	QTDIR    QType = 0x80 // type bit for directories
	QTAPPEND QType = 0x40 // type bit for append only files
	QTEXCL   QType = 0x20 // type bit for exclusive use files
	QTMOUNT  QType = 0x10 // type bit for mounted channel
	QTAUTH   QType = 0x08 // type bit for authentication file
	QTTMP    QType = 0x04 // type bit for not-backed-up file
	QTFILE   QType = 0x00 // plain file
)

Constants for use in Qid to indicate resource type.

func (QType) String

func (qt QType) String() string

type Qid

type Qid struct {
	Type    QType `9p:"type,1"`
	Version uint32
	Path    uint64
}

Qid indicates the type, path and version of the resource returned by a server. It is only valid for a session.

Typically, a client maintains a mapping of Fid-Qid as Qids are returned by the server.

func (Qid) String

func (qid Qid) String() string

type ReadNext

type ReadNext func(context.Context) ([]Dir, error)

type ReadNext1

type ReadNext1 func(context.Context) (Dir, error)

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

func NewFixedReaddir(codec Codec, dir []Dir) *Readdir

NewFixedReaddir returns a Readdir that will return a fixed set of directory entries.

func NewReaddir

func NewReaddir(codec Codec, next ReadNext) *Readdir

NewReaddir returns a new Readdir to assist implementing server-side Readdir. The codec will be used to decode messages with Dir entries. The provided function next will be called until io.EOF is returned.

func NewReaddir1

func NewReaddir1(codec Codec, next ReadNext1) *Readdir

func (*Readdir) Close

func (rd *Readdir) Close(context.Context) error

func (*Readdir) IOUnit

func (_ *Readdir) IOUnit() int

func (*Readdir) Read

func (rd *Readdir) Read(ctx context.Context, p []byte, offset int64) (n int, err error)

func (*Readdir) Write

func (rd *Readdir) Write(context.Context, []byte, int64) (int, error)

Build out Readdir to implement the full File interface

type SFid

type SFid struct {
	sync.Mutex
	Ent  Dirent // non-nil if unlocked (unless Ent deleted before being defined)
	File File   // non-nil if Open-ed

	// This is an *internal path*.
	// at the time the Ent was created by a Walk/Create.
	// If modified by the server, changes will only
	// apply to future Walk/Create-s from this Ent.
	Mode Flag // Defined if Open-ed.
	// contains filtered or unexported fields
}

FileSystem implementions gather more data than the required minimal information to send messages. The following expanded interfaces are generated and used internally. They contain useful information that can be looked up from the server, but are only guaranteed to be active while an active call is running on the server.

Internal representation of a Dirent File is nil unless Open has been called.

Path is an internal path. Internal paths have the invariants:

  • Path always begins with "/".
  • Path does not contain any "\\" characters.
  • Path never contains "." or ".." or "" (empty) elements.

The lock is used during all operations on the SFid -- especially during creation and Opening to protect cases where an inconsistent state of the SFid may be seen.

getRef acquires the lock before returning it, to ensure that the receiver sees such updates.

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 follows the semantics of io.ReaderAt.ReadAtt method except it takes
	// a contxt and Fid.
	Read(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error)

	// Write follows the semantics of io.WriterAt.WriteAt except takes a context and an Fid.
	//
	// If n == len(p), no error is returned.
	// If n < len(p), io.ErrShortWrite will be returned.
	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)

	// Called at server shutdown.
	Stop(error) error
}

Session provides the first level of abstraction for a 9p connection. Clients implement sessions and servers serve sessions.

Sessions can be proxied by serving up a client session.

This dual interface pattern is followed at higher levels of abstraction.

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 CSession

func CSession(ctx context.Context, conn net.Conn) (Session, error)

CSession returns a session using the connection. The Context ctx provides a context for out of band messages, such as flushes, that may be sent by the session. The session can effectively shutdown with this context.

func SFileSys

func SFileSys(fs FileSys) Session

Create a session object able to respond to 9P calls. Fid-s are managed at this level, so the FileSys only works with Dirent-s. All operations on a Fid are transactional, since a lock is held while server code is doing something to the corresponding Dirent.

For example, p9p/ufs translates all fid-s using sess.getRef(fid)
and https://9fans.github.io/usr/local/plan9/src/cmd/ramfs.c
uses user-defined structs for Fid-s.

type Tag

type Tag uint16

Tag uniquely identifies an outstanding fcall in a 9p session.

const NOTAG Tag = ^Tag(0)

NOTAG is a reserved values for messages sent before establishing a session, such as Tversion.

type Warning

type Warning struct {
	// contains filtered or unexported fields
}

func (Warning) Error

func (w Warning) Error() string

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.

  • The exact handling of an unknown tag is unclear at this point. These may not necessarily fatal to the session, since they could be messages that the client no longer cares for. When we figure this out, replace this panic with something more sensible.

  • 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.

Directories

Path Synopsis
cmd
9pr
9ps

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL