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
- Variables
- func FSConnHandler(fs FileSystem, msize uint32) proto.ConnHandler
- func FSHandler(fs FileSystem, msize uint32) proto.MessageHandler
- func GetNamespace(name string) (network, addr string)
- func IsNamespaceAddr(addr string) bool
- func NamespaceDir() string
- func ParseAddr(addr string) (network, address string)
- func Proto() proto.Proto
- func WriteDir(w io.Writer, entries []DirEntry) error
- type Attachment
- type AuthFS
- type Client
- type Dir
- func (d Dir) Attach(afile File, user, aname string) (Attachment, error)
- func (d Dir) Auth(user, aname string) (File, error)
- func (d Dir) Create(p string, perm FileMode, mode uint8) (File, error)
- func (d Dir) GetQID(p string) (QID, error)
- func (d Dir) Open(p string, mode uint8) (File, error)
- func (d Dir) Remove(p string) error
- func (d Dir) Stat(p string) (DirEntry, error)
- func (d Dir) WriteStat(p string, changes StatChanges) error
- type DirEntry
- type File
- type FileMode
- type FileSystem
- type IOUnitFS
- type QID
- type QIDFS
- type QIDType
- type Rattach
- type Rauth
- type Rclunk
- type Rcreate
- type Remote
- func (file *Remote) Close() error
- func (file *Remote) Create(p string, perm FileMode, mode uint8) (*Remote, error)
- func (file *Remote) Open(p string, mode uint8) (*Remote, error)
- func (file *Remote) Read(buf []byte) (int, error)
- func (file *Remote) ReadAt(buf []byte, off int64) (int, error)
- func (file *Remote) Readdir() ([]DirEntry, error)
- func (file *Remote) Remove(p string) error
- func (file *Remote) Seek(offset int64, whence int) (int64, error)
- func (file *Remote) Stat(p string) (DirEntry, error)
- func (file *Remote) Type() QIDType
- func (file *Remote) Write(data []byte) (int, error)
- func (file *Remote) WriteAt(data []byte, off int64) (int, error)
- type Rerror
- type Rflush
- type Ropen
- type Rread
- type Rremove
- type Rstat
- type Rversion
- type Rwalk
- type Rwrite
- type Rwstat
- type Stat
- type StatChanges
- func (c StatChanges) ATime() (time.Time, bool)
- func (c StatChanges) GID() (string, bool)
- func (c StatChanges) Length() (uint64, bool)
- func (c StatChanges) MTime() (time.Time, bool)
- func (c StatChanges) MUID() (string, bool)
- func (c StatChanges) Mode() (FileMode, bool)
- func (c StatChanges) Name() (string, bool)
- func (c StatChanges) UID() (string, bool)
- type Tattach
- type Tauth
- type Tclunk
- type Tcreate
- type Tflush
- type Topen
- type Tread
- type Tremove
- type Tstat
- type Tversion
- type Twalk
- type Twrite
- type Twstat
Constants ¶
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.
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 )
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.
const (
IOHeaderSize = 24
)
Other constants.
const ( // Version is the 9P version implemented by this package, both for // server and client. Version = "9P2000" )
Variables ¶
var ( // ErrLargeStat is returned during decoding when a stat is larger // than its own declared size. ErrLargeStat = errors.New("stat larger than declared size") )
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
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
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
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.
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.
type Client ¶
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 NewClient ¶
NewClient returns a client that communicates using c. The Client will close c when the Client is closed.
func (*Client) Attach ¶
Attach attaches to a filesystem provided by the connected server with the given attributes. If no authentication has been done, afile may be nil.
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.
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 ¶
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.
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
ModeFromOS converts an os.FileMode to a FileMode.
func (FileMode) Perm ¶ added in v0.2.0
Perm returns a FileMode containing only the permission 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 QIDFS ¶ added in v0.2.0
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.
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 ¶
Close closes the file on the server. Further usage of the file will produce errors.
func (*Remote) Create ¶
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 ¶
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 ¶
Read reads from the file at the internally-tracked offset. For more information, see ReadAt.
func (*Remote) ReadAt ¶
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 ¶
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 ¶
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 ¶
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 ¶
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) Write ¶
Write writes to the file at the internally-tracked offset. For more information, see WriteAt.
func (*Remote) WriteAt ¶
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.
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.
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) GID ¶
func (c StatChanges) GID() (string, bool)
func (StatChanges) Length ¶
func (c StatChanges) Length() (uint64, 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)