p9

package module
v0.6.12 Latest Latest
Warning

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

Go to latest
Published: Mar 25, 2021 License: MIT Imports: 24 Imported by: 1

README

p9

Go Report Card GoDoc

An experimental Go package for dealing with 9P, the Plan 9 file protocol. The primary idea behind this package is to make building 9P servers and clients as simple as building HTTP servers and clients is in Go. Due to the complexity of the protocol compared to HTTP, this package is unlikely to reach that level of simplicity, but it's certainly simpler than many other existing packages.

Example

Server
err := p9.ListenAndServe("tcp", "localhost:5640", p9.FSConnHandler(fsImplementation))
if err != nil {
	log.Fatalf("Failed to start server: %v", err)
}
Client
c, err := p9.Dial("tcp", "localhost:5640")
if err != nil {
	log.Fatalf("Failed to dial address: %v", err)
}
defer c.Close()

_, err := c.Handshake(2048)
if err != nil {
	log.Fatalf("Failed to perform handshake: %v", err)
}

root, err := c.Attach(nil, "anyone", "/")
if err != nil {
	log.Fatalf("Failed to attach: %v", err)
}
defer root.Close()

file, err := root.Open("path/to/a/file.txt", p9.OREAD)
if err != nil {
	log.Fatalf("Failed to open file: %v", err)
}
defer file.Close()

_, err = io.Copy(os.Stdout, file)
if err != nil {
	log.Fatalf("Failed to read file: %v", err)
}

Documentation

Overview

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

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()
c.Handshake(4096)

root, _ := c.Attach(nil, "anyone", "/")
defer root.Close()

file, _ := root.Open("path/to/a/file", p9.OREAD)
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.

Index

Constants

View Source
const (
	TversionType uint8 = 100 + iota
	RversionType
	TauthType
	RauthType
	TattachType
	RattachType

	RerrorType
	TflushType
	RflushType
	TwalkType
	RwalkType
	TopenType
	RopenType
	TcreateType
	RcreateType
	TreadType
	RreadType
	TwriteType
	RwriteType
	TclunkType
	RclunkType
	TremoveType
	RremoveType
	TstatType
	RstatType
	TwstatType
	RwstatType
)

Message type identifiers.

View Source
const (
	// NoTag is a special tag that is used when the tag is unimportant.
	NoTag uint16 = math.MaxUint16

	// NoFID is a special FID that is used to signal a lack of an FID.
	NoFID uint32 = math.MaxUint32
)
View Source
const (
	OREAD uint8 = iota
	OWRITE
	ORDWR
	OEXEC

	OTRUNC = 1 << (iota + 1)
	OCEXEC
	ORCLOSE
	ODIRECT
	ONONBLOCK
	OEXCL
	OLOCK
	OAPPEND
)

File open modes and flags. Note that not all flags are supported where you might expect them to be. If you get an error saying that a flag won't fit into uint8, the flag you're trying to use probably isn't supported there.

View Source
const (
	IOHeaderSize = 24
)

Other constants.

View Source
const (
	// Version is the 9P version implemented by this package, both for
	// server and client.
	Version = "9P2000"
)

Variables

View Source
var (
	// ErrLargeStat is returned during decoding when a stat is larger
	// than its own declared size.
	ErrLargeStat = errors.New("stat larger than declared size")
)
View Source
var (
	// ErrUnsupportedVersion is returned from a handshake attempt that
	// fails due to a version mismatch.
	ErrUnsupportedVersion = errors.New("unsupported version")
)

Functions

func FSConnHandler

func FSConnHandler(fs FileSystem, msize uint32) proto.ConnHandler

FSConnHandler returns a ConnHandler that calls FSHandler to generate MessageHandlers.

The returned ConnHandler implementation will print debug messages to stderr if the p9debug build tag is set.

func FSHandler

func FSHandler(fs FileSystem, msize uint32) proto.MessageHandler

FSHandler returns a MessageHandler that provides a virtual filesystem using the provided FileSystem implementation. msize is the maximum size that messages from either the server or the client are allowed to be.

The returned MessageHandler implementation will print debug messages to stderr if the p9debug build tag is set.

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.

func GetNamespace added in v0.6.7

func GetNamespace(name string) (network, addr string)

GetNamespace returns the network and address that should be used to connect to the named program in the current namespace.

func IsNamespaceAddr added in v0.6.7

func IsNamespaceAddr(addr string) bool

IsNamespaceAddr returns true if the given address would result in a namespace network/address pair if passed to ParseAddr.

func NamespaceDir added in v0.6.7

func NamespaceDir() string

NamespaceDir returns the path of the directory that is used for the current namespace. On Unix-like systems, this is /tmp/ns.$USER.$DISPLAY.

If looking up the current user's name fails, this function will panic.

func ParseAddr added in v0.6.7

func ParseAddr(addr string) (network, address string)

ParseAddr returns a network and address pair for a basic address string. The parsing is done like this:

If the address starts with "$", it is assumed to be a namespace and is located using GetNamespace.

If the address starts with "./" or "/", it is assumed to be a path to a Unix socket.

If the address contains a ":", it is a assumed to be a TCP address and port combo. As a special case, the pseudo-ports 9p and 9fs map to the standard 9P port number.

If the address contains a single "!", it is assumed to be a network and address combo. If the network type is TCP, the standard 9P port is assumed.

If the address contains two "!", it is assumed to be a network, address, and port, in that order.

In all other cases, it is assumed to be only the address specificiation of a TCP address and the standard port is assumed.

func Proto added in v0.3.0

func Proto() proto.Proto

Proto returns the protocol implementation for 9P.

func WriteDir

func WriteDir(w io.Writer, entries []DirEntry) error

WriteDir writes a series of directory entries to w.

Types

type Attachment

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
	// will be transmitted to the client.
	Stat(path string) (DirEntry, error)

	// WriteStat applies changes to the metadata of the file at path.
	// If a method of the changes argument returns false, it should not
	// be changed.
	//
	// If an error is returned, it is assumed that the entire operation
	// failed. In particular, name changes will not be tracked by the
	// system if this returns an error.
	WriteStat(path string, changes StatChanges) error

	// Open opens the file at path in the given mode. If an error is
	// returned, it will be transmitted to the client.
	Open(path string, mode uint8) (File, error)

	// Create creates and opens a file at path with the given perms and
	// mode. If an error is returned, it will be transmitted to the
	// client.
	//
	// When creating a directory, this method will be called with the
	// DMDIR bit set in perm. Even in this situation it should still
	// open and return the newly created file.
	Create(path string, perm FileMode, mode uint8) (File, error)

	// Remove deletes the file at path, returning any errors encountered.
	Remove(path string) error
}

Attachment is a file hierarchy provided by an FS.

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

type AuthFS

type AuthFS struct {
	FileSystem

	// AuthFunc is the function called when the Auth() method is called.
	AuthFunc func(user, aname string) (File, error)

	// AttachFunc is the function called when the Attach() method is
	// called. Note that this function, unlike the method, does not
	// return an Attachment. Instead, if this function returns a nil
	// error, the underlying implementation's Attach() method is called
	// with the returned file as its afile argument.
	AttachFunc func(afile File, user, aname string) (File, error)
}

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.

func (AuthFS) Attach

func (a AuthFS) Attach(afile File, user, aname string) (Attachment, error)

Attach implements FileSystem.Attach.

func (AuthFS) Auth

func (a AuthFS) Auth(user, aname string) (File, error)

Auth implements FileSystem.Auth.

type Client

type Client struct {
	*proto.Client
	// contains filtered or unexported fields
}

Client provides functionality for sending requests to and receiving responses from a 9P server.

A Client must be closed when it is no longer going to be used in order to free up the related resources.

func Dial

func Dial(network, addr string) (*Client, error)

Dial is a convenience function that dials and creates a client in the same step.

func NewClient

func NewClient(c net.Conn) *Client

NewClient returns a client that communicates using c. The Client will close c when the Client is closed.

func (*Client) Attach

func (c *Client) Attach(afile *Remote, user, aname string) (*Remote, error)

Attach attaches to a filesystem provided by the connected server with the given attributes. If no authentication has been done, afile may be nil.

func (*Client) Auth

func (c *Client) Auth(user, aname string) (*Remote, error)

Auth requests an auth file from the server, returning a Remote representing it or an error if one occurred.

func (*Client) Handshake

func (c *Client) Handshake(msize uint32) (uint32, error)

Handshake performs an initial handshake to establish the maximum allowed message size. A handshake must be performed before any other request types may be sent.

type Dir

type Dir string

Dir is an implementation of FileSystem that serves from the local filesystem. It accepts attachments of either "" or "/", but rejects all others.

Note that Dir does not support authentication, simply returning an error for any attempt to do so. If authentication is necessary, wrap a Dir in an AuthFS instance.

func (Dir) Attach

func (d Dir) Attach(afile File, user, aname string) (Attachment, error)

Attach implements FileSystem.Attach.

func (Dir) Auth

func (d Dir) Auth(user, aname string) (File, error)

Auth implements FileSystem.Auth.

func (Dir) Create

func (d Dir) Create(p string, perm FileMode, mode uint8) (File, error)

Create implements Attachment.Create.

func (Dir) GetQID added in v0.6.8

func (d Dir) GetQID(p string) (QID, error)

func (Dir) Open

func (d Dir) Open(p string, mode uint8) (File, error)

Open implements Attachment.Open.

func (Dir) Remove

func (d Dir) Remove(p string) error

Remove implements Attachment.Remove.

func (Dir) Stat

func (d Dir) Stat(p string) (DirEntry, error)

Stat implements Attachment.Stat.

func (Dir) WriteStat

func (d Dir) WriteStat(p string, changes StatChanges) error

WriteStat implements Attachment.WriteStat.

type DirEntry

type DirEntry struct {
	FileMode  FileMode
	ATime     time.Time
	MTime     time.Time
	Length    uint64
	EntryName string
	UID       string
	GID       string
	MUID      string

	Path    uint64
	Version uint32
}

DirEntry is a smaller version of Stat that eliminates unnecessary or duplicate fields.

func ReadDir

func ReadDir(r io.Reader) ([]DirEntry, error)

ReadDir decodes a series of directory entries from a reader. It reads until EOF, so it doesn't return io.EOF as a possible error.

It is recommended that the reader passed to ReadDir have some form of buffering, as some servers will silently mishandle attempts to read pieces of a directory. Wrapping the reader with a bufio.Reader is often sufficient.

func (DirEntry) IsDir added in v0.5.0

func (d DirEntry) IsDir() bool

func (DirEntry) ModTime added in v0.5.0

func (d DirEntry) ModTime() time.Time

func (DirEntry) Mode

func (d DirEntry) Mode() os.FileMode

func (DirEntry) Name

func (d DirEntry) Name() string

func (DirEntry) Size added in v0.5.0

func (d DirEntry) Size() int64

func (DirEntry) Stat added in v0.5.0

func (d DirEntry) Stat() Stat

Stat returns a Stat that corresponds to the DirEntry.

func (DirEntry) Sys added in v0.5.0

func (d DirEntry) Sys() interface{}

type File

type File interface {
	// Used to handle 9P read requests.
	io.ReaderAt

	// Used to handle 9P write requests.
	io.WriterAt

	// Used to handle 9P clunk requests.
	io.Closer

	// Readdir is called when an attempt is made to read a directory. It
	// should return a list of entries in the directory or an error. If
	// an error is returned, the error will be transmitted to the
	// client.
	//
	// When a client attempts to read a file, if file reports itself as
	// a QTDir, then this method will be used to read it instead of
	// ReadAt().
	Readdir() ([]DirEntry, error)
}

File is the interface implemented by files being dealt with by a FileSystem.

Note that although this interface implements io.ReaderAt and io.WriterAt, a number of the restrictions placed on those interfaces may be ignored. The implementation need only follow restrictions placed by the 9P protocol specification.

type FileMode added in v0.2.0

type FileMode uint32

FileMode stores permission and type information about a file or directory.

const (
	ModeDir FileMode = 1 << (31 - iota)
	ModeAppend
	ModeExclusive
	ModeMount
	ModeAuth
	ModeTemporary
	ModeSymlink

	ModeDevice
	ModeNamedPipe
	ModeSocket
	ModeSetuid
	ModeSetgid
)

FileMode type bitmasks.

func ModeFromOS added in v0.2.0

func ModeFromOS(m os.FileMode) FileMode

ModeFromOS converts an os.FileMode to a FileMode.

func (FileMode) OS added in v0.2.0

func (m FileMode) OS() os.FileMode

OS converts a FileMode to an os.FileMode.

func (FileMode) Perm added in v0.2.0

func (m FileMode) Perm() FileMode

Perm returns a FileMode containing only the permission bits of m.

func (FileMode) QIDType added in v0.2.0

func (m FileMode) QIDType() QIDType

QIDType converts a FileMode to a QIDType. Note that this will result in a loss of information, as the information stored by QIDType is a direct subset of that handled by FileMode.

func (FileMode) String added in v0.2.0

func (m FileMode) String() string

func (FileMode) Type added in v0.2.0

func (m FileMode) Type() FileMode

Type returns a FileMode containing only the type bits of m.

type FileSystem

type FileSystem interface {
	// Auth returns an authentication file. This file can be used to
	// send authentication information back and forth between the server
	// and the client.
	//
	// The file returned from this method must report its own type as
	// QTAuth.
	Auth(user, aname string) (File, error)

	// Attach attachs to a file hierarchy provided by the filesystem.
	// If the client is attempting to authenticate, a File previously
	// returned from Auth() will be passed as afile.
	Attach(afile File, user, aname string) (Attachment, error)
}

FileSystem is an interface that allows high-level implementations of 9P servers by allowing the implementation to ignore the majority of the details of the protocol.

func ReadOnlyFS added in v0.2.0

func ReadOnlyFS(fs FileSystem) FileSystem

ReadOnlyFS wraps a filesystem implementation with an implementation that rejects any attempts to cause changes to the filesystem with the exception of writing to an authfile.

type IOUnitFS

type IOUnitFS interface {
	IOUnit() uint32
}

IOUnitFS is implemented by Attachments that want to report an IOUnit value to clients when open and create requests are made. An IOUnit value lets the client know the maximum amount of data during reads and writes that is guaranteed to be an atomic operation.

type QID

type QID struct {
	Type    QIDType
	Version uint32
	Path    uint64
}

QID represents a QID value.

type QIDFS added in v0.2.0

type QIDFS interface {
	GetQID(p string) (QID, error)
}

QIDFS is implemented by Attachments that want to deal with QIDs manually. A QID represents a unique identifier for a given file. In particular, the Path field must be unique for every path, even if the file at that path has been deleted and replaced with a completely new file.

type QIDType

type QIDType uint8

QIDType represents an 8-bit QID type identifier.

const (
	QTFile QIDType = 0
	QTDir  QIDType = 1 << (8 - iota)
	QTAppend
	QTExcl
	QTMount
	QTAuth
	QTTmp
	QTSymlink
)

Valid types of files.

func (QIDType) FileMode added in v0.2.0

func (t QIDType) FileMode() FileMode

FileMode converts the QIDType to a FileMode.

type Rattach

type Rattach struct {
	QID QID
}

type Rauth

type Rauth struct {
	AQID QID
}

type Rclunk

type Rclunk struct {
}

type Rcreate

type Rcreate struct {
	QID    QID
	IOUnit uint32
}

type Remote

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

Remote provides a file-like interface for performing operations on files presented by a 9P server.

Remote implements File, allowing it to be itself served using FileSystem.

func (*Remote) Close

func (file *Remote) Close() error

Close closes the file on the server. Further usage of the file will produce errors.

func (*Remote) Create

func (file *Remote) Create(p string, perm FileMode, mode uint8) (*Remote, error)

Create creates, with the given permissions, and opens, with the given mode, a new file at p relative to the current file.

func (*Remote) Open

func (file *Remote) Open(p string, mode uint8) (*Remote, error)

Open opens and returns a file relative to the current one. In many cases, this will likely be relative to the filesystem root. For example:

root, _ := client.Attach(nil, "anyone", "/")
file, _ := root.Open("some/file/or/another", p9.OREAD)

func (*Remote) Read

func (file *Remote) Read(buf []byte) (int, error)

Read reads from the file at the internally-tracked offset. For more information, see ReadAt.

func (*Remote) ReadAt

func (file *Remote) ReadAt(buf []byte, off int64) (int, error)

ReadAt reads from the file at the given offset. If the buffer given will result in a response that is larger than the currently allowed message size, as established by the handshake, it will perform multiple read requests in sequence, reading each into the appropriate parts of the buffer. It returns the number of bytes read and an error, if any occurred.

If an error occurs while performing the sequential requests, it will return immediately.

func (*Remote) Readdir

func (file *Remote) Readdir() ([]DirEntry, error)

Readdir reads the file as a directory, returning the list of directory entries returned by the server.

As it returns the full list of entries, it never returns io.EOF.

Note that to read this list again, the file must first be seeked to the beginning.

func (*Remote) Remove

func (file *Remote) Remove(p string) 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 (*Remote) Seek

func (file *Remote) Seek(offset int64, whence int) (int64, error)

Seek seeks a file. As 9P requires clients to track their own positions in files, this is purely a local operation with the exception of the case of whence being io.SeekEnd, in which case a request will be made to the server in order to get the file's size.

func (*Remote) Stat

func (file *Remote) Stat(p string) (DirEntry, error)

Stat fetches and returns the DirEntry for the file located at p, relative to the current file. If p is "", it is considered to be the current file.

func (*Remote) Type

func (file *Remote) Type() QIDType

Type returns the type of the file represented by the Remote.

func (*Remote) Write

func (file *Remote) Write(data []byte) (int, error)

Write writes to the file at the internally-tracked offset. For more information, see WriteAt.

func (*Remote) WriteAt

func (file *Remote) WriteAt(data []byte, off int64) (int, error)

WriteAt writes from the file at the given offset. If the buffer given will result in a request that is larger than the currently allowed message size, as established by the handshake, it will perform multiple write requests in sequence, writing each with appropriate offsets such that the entire buffer is written. It returns the number of bytes written and an error, if any occurred.

If an error occurs while performing the sequential requests, it will return immediately.

type Rerror

type Rerror struct {
	Ename string
}

Rerror is a special response that represents an error. As a special case, this type also implements error for convenience.

func (*Rerror) Error

func (msg *Rerror) Error() string

type Rflush

type Rflush struct {
}

type Ropen

type Ropen struct {
	QID    QID
	IOUnit uint32
}

type Rread

type Rread struct {
	Data []byte
}

type Rremove

type Rremove struct {
}

type Rstat

type Rstat struct {
	Stat Stat
}

func (*Rstat) P9Decode added in v0.3.0

func (stat *Rstat) P9Decode(r io.Reader) error

func (*Rstat) P9Encode added in v0.3.0

func (stat *Rstat) P9Encode() ([]byte, error)

type Rversion

type Rversion struct {
	Msize   uint32
	Version string
}

func (*Rversion) P9Msize added in v0.4.0

func (r *Rversion) P9Msize() uint32

type Rwalk

type Rwalk struct {
	WQID []QID
}

type Rwrite

type Rwrite struct {
	Count uint32
}

type Rwstat

type Rwstat struct {
}

type Stat

type Stat struct {
	Type   uint16
	Dev    uint32
	QID    QID
	Mode   FileMode
	ATime  time.Time
	MTime  time.Time
	Length uint64
	Name   string
	UID    string
	GID    string
	MUID   string
}

Stat is a stat value.

func (Stat) DirEntry added in v0.5.0

func (s Stat) DirEntry() DirEntry

DirEntry returns a DirEntry that corresponds to the Stat.

func (*Stat) P9Decode added in v0.3.0

func (s *Stat) P9Decode(r io.Reader) (err error)

func (Stat) P9Encode added in v0.3.0

func (s Stat) P9Encode() (r []byte, err error)

type StatChanges

type StatChanges struct {
	DirEntry
}

StatChanges is a wrapper around DirEntry that is used in wstat requests. If one of its methods returns false, that field should be considered unset in the DirEntry.

func (StatChanges) ATime

func (c StatChanges) ATime() (time.Time, bool)

func (StatChanges) GID

func (c StatChanges) GID() (string, bool)

func (StatChanges) Length

func (c StatChanges) Length() (uint64, bool)

func (StatChanges) MTime

func (c StatChanges) MTime() (time.Time, bool)

func (StatChanges) MUID

func (c StatChanges) MUID() (string, bool)

func (StatChanges) Mode

func (c StatChanges) Mode() (FileMode, bool)

func (StatChanges) Name

func (c StatChanges) Name() (string, bool)

func (StatChanges) UID

func (c StatChanges) UID() (string, bool)

type Tattach

type Tattach struct {
	FID   uint32
	AFID  uint32
	Uname string
	Aname string
}

type Tauth

type Tauth struct {
	AFID  uint32
	Uname string
	Aname string
}

type Tclunk

type Tclunk struct {
	FID uint32
}

type Tcreate

type Tcreate struct {
	FID  uint32
	Name string
	Perm FileMode
	Mode uint8 // TODO: Make a Mode type?
}

type Tflush

type Tflush struct {
	OldTag uint16
}

type Topen

type Topen struct {
	FID  uint32
	Mode uint8 // TODO: Make a Mode type?
}

type Tread

type Tread struct {
	FID    uint32
	Offset uint64
	Count  uint32
}

type Tremove

type Tremove struct {
	FID uint32
}

type Tstat

type Tstat struct {
	FID uint32
}

type Tversion

type Tversion struct {
	Msize   uint32
	Version string
}

func (*Tversion) P9NoTag added in v0.3.0

func (*Tversion) P9NoTag()

type Twalk

type Twalk struct {
	FID    uint32
	NewFID uint32
	Wname  []string
}

type Twrite

type Twrite struct {
	FID    uint32
	Offset uint64
	Data   []byte
}

type Twstat

type Twstat struct {
	FID  uint32
	Stat Stat
}

func (*Twstat) P9Decode added in v0.6.4

func (stat *Twstat) P9Decode(r io.Reader) error

func (*Twstat) P9Encode added in v0.6.4

func (stat *Twstat) P9Encode() ([]byte, error)

Directories

Path Synopsis
cmd
p9
p9w
internal
Package proto provides a usage-inspecific wrapper around 9P's data serialization and communication scheme.
Package proto provides a usage-inspecific wrapper around 9P's data serialization and communication scheme.

Jump to

Keyboard shortcuts

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