styx: aqwari.net/net/styx Index | Examples | Files | Directories

package styx

import "aqwari.net/net/styx"

Package styx serves network filesystems using the 9P2000 protocol.

The styx package provides types and routines for implementing 9P servers. The files served may reflect real files on the host operating system, or an in-memory filesystem, or bi-directional RPC endpoints. Regardless, the protocol operations used to access these files are the same.

The ListenAndServe and ListenAndServeTLS functions run 9P servers bound to a TCP port. To create a 9P server, define a type that implements the Serve9P method. The HandlerFunc type allows regular functions to be used:

fs := styx.HandlerFunc(func(s *styx.Session) {
	for s.Next() {
		switch msg := s.Request().(type) {
		case styx.Twalk:
			msg.Rwalk(os.Stat(msg.Path()))
		case styx.Topen:
			msg.Ropen(os.OpenFile(msg.Path(), msg.Flag, 0777))
		case styx.Tstat:
			msg.Rstat(os.Stat(msg.Path())
		}
	}
})
styx.ListenAndServe(":564", fs)

Multiple handlers can be overlaid using the Stack function.

echo := styx.HandlerFunc(func(s *styx.Session) {
	for s.Next() {
		log.Printf("%s %q %s", s.User, s.Access, s.Request())
	}
	log.Printf("session %s %q ended", s.User, s.Access)
})
styx.ListenAndServe(":564", styx.Stack(echo, handler))

Handlers may pass data downstream using a message's WithContext method:

sessionid := styx.HandlerFunc(func(s *styx.Session) {
	uuid := rand.Int63()
	for s.Next() {
		msg := s.Request()
		ctx := context.WithValue(msg.Context(), "session", uuid)
		s.UpdateRequest(msg.WithContext(ctx))
	}
})
styx.ListenAndServe(":564", styx.Stack(sessionid, echo, fs))

Code:

package main

import (
    "io"
    "log"
    "os"
    "time"

    "aqwari.net/net/styx"
)

type emptyDir struct{}

func (emptyDir) Readdir(n int) ([]os.FileInfo, error) { return nil, io.EOF }
func (emptyDir) Mode() os.FileMode                    { return os.ModeDir | 0777 }
func (emptyDir) IsDir() bool                          { return true }
func (emptyDir) ModTime() time.Time                   { return time.Now() }
func (emptyDir) Name() string                         { return "" }
func (emptyDir) Size() int64                          { return 0 }
func (emptyDir) Sys() interface{}                     { return nil }

func main() {
    // Run a file server that creates directories (and only directories)
    // on-demand, as a client walks to them.

    h := styx.HandlerFunc(func(s *styx.Session) {
        for s.Next() {
            switch t := s.Request().(type) {
            case styx.Tstat:
                t.Rstat(emptyDir{}, nil)
            case styx.Twalk:
                t.Rwalk(emptyDir{}, nil)
            case styx.Topen:
                t.Ropen(emptyDir{}, nil)
            }
        }
    })
    log.Fatal(styx.ListenAndServe(":564", h))
}

Index

Examples

Package Files

auth.go conn.go doc.go file.go request.go server.go session.go stack.go walk.go wstat.go

func ListenAndServe Uses

func ListenAndServe(addr string, handler Handler) error

ListenAndServe listens on the specified TCP address, and then calls Serve with handler to handle requests of incoming connections.

func ListenAndServeTLS Uses

func ListenAndServeTLS(addr string, certFile, keyFile string, handler Handler) error

ListenAndServeTLS listens on the specified TCP address for incoming TLS connections. certFile must be a valid x509 certificate in PEM format, concatenated with any intermediate and CA certificates.

type AuthFunc Uses

type AuthFunc func(rwc *Channel, user, access string) error

An AuthFunc is used to authenticate a user to a 9P server. The authentication protocol itself is tunnelled over 9P via read and write operations to a special file, and is outside the scope of the 9P protocol.

An AuthFunc must determine that a client is authorized to start a 9P session to the file tree specified by the access parameter. The Auth method may receive and send data over rwc. Alternatively, additional information can be passed through the Channel's context for external authentication. Notably, the Conn method of the Channel can be used to access the underlying network connection, in order to authenticate based on TLS certificates, unix uid values (on a unix socket), etc.

An AuthFunc must return a non-nil error if authentication fails. The error may be sent to the client and should not contain any sensitive information. If authentication succeeds, an AuthFunc must return nil.

Existing AuthFunc implementations can be found in the styxauth package.

type Channel Uses

type Channel struct {
    context.Context
    io.ReadWriteCloser
}

A Channel provides authentication methods with a bidirectional channel between the client and server, along with any contextual information recorded by the server. Of note is the "conn" value, which returns the underlying net.Conn value for the network connection.

func (*Channel) Conn Uses

func (ch *Channel) Conn() interface{}

Conn retrieves the underlying io.ReadWriteCloser for a Channel.

type Directory Uses

type Directory interface {
    Readdir(n int) ([]os.FileInfo, error)
}

In the 9P protocol, a directory is simply a file that returns zero or more styxproto.Stat structures when read. Types that implement the Directory interface can avoid marshalling styxproto.Stat methods in the Read methods. The Readdir method should return up to n os.FileInfo values, based on the contents of the given directory. Further calls to Readdir should pick up where the previous call left off.

If n <= 0, Readdir should return os.FileInfo values for all files in the directory.

type Handler Uses

type Handler interface {
    Serve9P(*Session)
}

Types implementing the Handler interface can receive and respond to 9P requests from clients.

When a client connects to the server and starts a session, a new goroutine is created running the handler's Serve9P method. Each 9P message can be retrieved using the Session's Next and Request methods. Serve9P is expected to last for the duration of the 9P session; if the client ends the session, the Session's Next method will return false. If the Serve9P method exits prematurely, all open files and other resources associated with that session are released, and any further requests for that session will result in an error.

The Serve9P method is not required to answer every type of 9P message. If an existing request is unanswered when Serve9P fetches the next request, the styx package will reply to the client with a default response. The documentation for each request type notes its default response.

In practice, a Handler is usually composed of a for loop and a type switch, like so:

func (srv *Srv) Serve9P(s *styx.Session) {
	for s.Next() {
		switch msg := s.Request().(type) {
		case styx.Twalk:
			if (srv.exists(msg.Path()) {
				msg.Rwalk(srv.filemode(msg.Path())
			} else {
				msg.Rerror("%s does not exist", msg.Path())
			}
		case styx.Topen:
			msg.Ropen(srv.getfile(msg.Path()))
		case styx.Tcreate:
			msg.Rcreate(srv.newfile(msg.Path())
		}
	}
}

Possible message types are listed in the documentation for the Request type.

func Stack Uses

func Stack(handlers ...Handler) Handler

Stack combines multiple handlers into one. When a new message is received from the client, it is passed to each handler, from left to right, until a response is sent. If no response is sent. by any handlers in the stack, the documented default response for that message type will be sent to the client. Handlers may use the UpdateRequest to pass information to downstream handlers.

Code:

// Associate a session ID with each session
var sessionID int64
sessionid := styx.HandlerFunc(func(s *styx.Session) {
    id := atomic.AddInt64(&sessionID, 1)
    for s.Next() {
        req := s.Request()
        ctx := context.WithValue(req.Context(), "session", id)
        s.UpdateRequest(req.WithContext(ctx))
    }
})

// echo requests to stdout
echo := styx.HandlerFunc(func(s *styx.Session) {
    for s.Next() {
        req := s.Request()
        id := req.Context().Value("session")
        fmt.Printf("session %v user %q %q %T %s",
            id, s.User, s.Access, req, req.Path())
    }
})

// Disallow removal of any files
blockops := styx.HandlerFunc(func(s *styx.Session) {
    for s.Next() {
        if t, ok := s.Request().(styx.Tremove); ok {
            t.Rerror("permission denied")
        }
    }
})

handler := styx.Stack(sessionid, echo, blockops)
styx.ListenAndServe(":564", handler)

type HandlerFunc Uses

type HandlerFunc func(s *Session)

The HandlerFunc provides a convenient adapter type that allows for normal functions to handle 9P sessions.

func (HandlerFunc) Serve9P Uses

func (fn HandlerFunc) Serve9P(s *Session)

Serve9P calls fn(s).

type Logger Uses

type Logger interface {
    Printf(string, ...interface{})
}

Types implementing the Logger interface can receive diagnostic information during a Server's operation. The Logger interface is implemented by *log.Logger.

type OwnerInfo Uses

type OwnerInfo interface {
    Uid() string
    Gid() string
    Muid() string
}

The styx package will attempt to determine the ownership of a file by asking the host operating system, if it is a real file. If a given type implements the OwnerInfo interface, the styx package will use the methods therein to determine file ownership. Uid should return the user name of a file's owner. Gid should return the primary group of the file. Muid, if implemented, should return the name of the user who last modified the file. If Muid is not implemented, the styx package will always return the owner of the file for its Muid.

Usage of this interface is opportunistic; a type can implement all or some of the methods.

type Request Uses

type Request interface {
    // Context is used to implement cancellation and request timeouts. If
    // an operation is going to take a long time to complete, you can
    // allow for the client to cancel the request by receiving on the
    // channel returned by the Context's Done method.
    Context() context.Context

    // WithContext returns a copy of the request with a new Context. It
    // can be used with nested handlers to attach information, deadlines,
    // and cancellations to a request.
    WithContext(context.Context) Request

    // If a request is invalid, not allowed, or cannot be completed properly
    // for some other reason, its Rerror method should be used to respond
    // to it.
    Rerror(format string, args ...interface{})

    // Path returns the Path of the file being operated on.
    Path() string
    // contains filtered or unexported methods
}

A Request is a request by a client to perform an operation on a file or set of files. Types of requests may range from checking if a file exists (Twalk) to opening a file (Topen) to changing a file's name (Twstat).

type Server Uses

type Server struct {
    // Address to listen on, ":9pfs" if empty.
    Addr string

    // maximum wait before timing out write of the response.
    WriteTimeout time.Duration

    // maximum wait before closing an idle connection.
    IdleTimeout time.Duration

    // maximum size of a 9P message, DefaultMsize if unset.
    MaxSize int64

    // optional TLS config, used by ListenAndServeTLS
    TLSConfig *tls.Config

    // Handler to invoke for each session
    Handler Handler

    // Auth is used to authenticate user sessions. If nil,
    // authentication is disabled.
    Auth AuthFunc

    // If not nil, ErrorLog will be used to log unexpected
    // errors accepting or handling connections. TraceLog,
    // if not nil, will receive detailed protocol tracing
    // information.
    ErrorLog, TraceLog Logger
}

A Server defines parameters for running a 9P server. The zero value of a Server is usable as a 9P server, and will use the defaults set by the styx package.

func (*Server) ListenAndServe Uses

func (srv *Server) ListenAndServe() error

ListenAndServe listens on the TCP network address srv.Addr and calls Serve to handle requests on incoming connections. If srv.Addr is blank, :564 is used.

func (*Server) ListenAndServeTLS Uses

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error

ListenAndServeTLS listens on the TCP network address srv.Addr for incoming TLS connections.

func (*Server) Serve Uses

func (srv *Server) Serve(l net.Listener) error

Serve accepts connections on the listener l, creating a new service goroutine for each. The service goroutines read requests and relays them to the appropriate Handler goroutines.

type Session Uses

type Session struct {
    // User is the name of the user associated with the session.
    // When establishing a session, the client provides a username, This
    // may or may not be authenticated, depending on the Server in use.
    User string

    // Access is the name of the file tree requested by a client when
    // it establishes a session, in the "aname" field of its "Tattach"
    // request. When the EnableVHost option is used, if a client does
    // not specify one, this is set to the hostname the client used
    // to connect to the server, for non-TLS connections, and the SNI
    // provided by the client, for TLS connections.
    Access string

    // This tracks the number of fids pointing to this session in
    // conn.sessionFid. We need to know when all references are gone
    // so we can properly close any session channels.
    util.RefCount
    // contains filtered or unexported fields
}

A Session is a sequence of related 9P messages from a single client. It begins when a client opens the root of a file tree, and ends when all of its files are closed. Sessions occur over a single connection and are associated with a single user and file tree. Over a single session, a user may perform multiple operations on multiple files. Multiple sessions may be multiplexed over a single connection.

func (*Session) Next Uses

func (s *Session) Next() bool

Next waits for the next Request for a 9P session. The next request for the session can be accessed via the Request method if and only if Next returns true. Any previous messages retrieved for the session should not be modified or responded to after Next is called; if they have not been answered, the styx package will send default responses for them. The default response for a message can be found in the comments for each message type. Next returns false if the session has ended or there was an error receiving the next Request.

func (*Session) Request Uses

func (s *Session) Request() Request

Request returns the last 9P message received by the Session. It is only valid until the next call to Next.

func (*Session) UpdateRequest Uses

func (s *Session) UpdateRequest(r Request)

When multiple Handlers are combined together using Stack, a handler may modify the incoming request using the UpdateRequest method. The current request will be overwritten with r, and reflected in calls to the Request method in he current and all downstream handlers.

type Tchmod Uses

type Tchmod struct {
    Mode os.FileMode
    // contains filtered or unexported fields
}

A Tchmod message is sent by the client to change the permissions of an existing file. The client must have write access to the file's containing directory. Use the Rchmod method to indicate success.

The default response for a Tchmod request is an Rerror saying "permission denied"

func (Tchmod) Rchmod Uses

func (t Tchmod) Rchmod(err error)

Rchmod, when called with a nil error, indicates that the permissions of the file were updated. Future stat requests should reflect the new file mode.

func (Tchmod) Rerror Uses

func (t Tchmod) Rerror(format string, args ...interface{})

Call Rerror to provide a descriptive error message explaining why a file attribute could not be updated.

func (Tchmod) WithContext Uses

func (t Tchmod) WithContext(ctx context.Context) Request

type Tchown Uses

type Tchown struct {
    User, Group string

    // These will only be set if using the 9P2000.u or 9P2000.L
    // extensions, and will be -1 otherwise.
    Uid, Gid int
    // contains filtered or unexported fields
}

A Tchown message is sent by the client to change the user and group associated with a file. Use the Rchown method to indicate success.

The default response to a Tchown message is an Rerror message saying "permission denied".

func (Tchown) Rchown Uses

func (t Tchown) Rchown(err error)

Rchown, when called with a nil error, indicates that file and group ownership attributes were updated for the given file. Future stat requests for the same file should reflect the changes.

func (Tchown) Rerror Uses

func (t Tchown) Rerror(format string, args ...interface{})

Call Rerror to provide a descriptive error message explaining why a file attribute could not be updated.

func (Tchown) WithContext Uses

func (t Tchown) WithContext(ctx context.Context) Request

type Tcreate Uses

type Tcreate struct {
    Name string      // name of the file to create
    Mode os.FileMode // permissions and file type to create
    Flag int         // flags to open the new file with
    // contains filtered or unexported fields
}

A Tcreate message is sent when a client wants to create a new file and open it with the provided Mode. The Path method of a Tcreate message returns the absolute path of the containing directory. A user must have write permissions in the directory to create a file.

The default response to a Tcreate message is an Rerror message saying "permission denied".

func (Tcreate) Context Uses

func (t Tcreate) Context() context.Context

Context returns the context associated with the request.

func (Tcreate) NewPath Uses

func (t Tcreate) NewPath() string

NewPath joins the path for the Tcreate's containing directory with its Name field, returning the absolute path to the new file.

func (Tcreate) Path Uses

func (t Tcreate) Path() string

Path returns the absolute path to the containing directory of the new file.

func (Tcreate) Rcreate Uses

func (t Tcreate) Rcreate(rwc interface{}, err error)

Rcreate is used to respond to a succesful create request. With 9P, creating a file also opens the file for I/O. Once Rcreate returns, future read and write requests to the file handle will pass through rwc. The value rwc must meet the same criteria listed for the Ropen method of a Topen request.

func (Tcreate) Rerror Uses

func (t Tcreate) Rerror(format string, args ...interface{})

Rerror sends an error to the client.

func (Tcreate) WithContext Uses

func (t Tcreate) WithContext(ctx context.Context) Request

type Topen Uses

type Topen struct {
    // The mode to open the file with. One of the flag constants
    // in the os package, such as O_RDWR, O_APPEND etc.
    Flag int
    // contains filtered or unexported fields
}

A Topen message is sent when a client wants to open a file for I/O Use the Ropen method to provide the opened file.

The default response to a Topen message to send an Rerror message saying "permssion denied".

func (Topen) Context Uses

func (t Topen) Context() context.Context

Context returns the context associated with the request.

func (Topen) Path Uses

func (t Topen) Path() string

Path returns the absolute path of the file being operated on.

func (Topen) Rerror Uses

func (t Topen) Rerror(format string, args ...interface{})

Rerror sends an error to the client.

func (Topen) Ropen Uses

func (t Topen) Ropen(rwc interface{}, err error)

The Ropen method signals to the client that a file has succesfully been opened and is ready for I/O. After Ropen returns, future reads and writes to the opened file handle will pass through rwc.

The value rwc must implement some of the interfaces in the io package for reading and writing. If the type implements io.Seeker or io.ReaderAt and io.WriterAt, clients may read or write at arbitrary offsets within the file. Types that only implement Read or Write operations will return errors on writes and reads, respectively.

If rwc implements the Stat method of os.File, that will be used to answer Tstat requests. Otherwise, the styx package will assemble Rstat responses out of default values merged with any methods rwc provides from the os.FileInfo interface.

If a file does not implement any of the Read or Write interfaces in the io package, A generic error is returned to the client, and a message will be written to the server's ErrorLog.

func (Topen) WithContext Uses

func (t Topen) WithContext(ctx context.Context) Request

type Tremove Uses

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

A Tremove message is sent when a client wants to delete a file from the server. The Rremove method should be called once the file has been succesfully deleted.

The default response to a Tremove message is an Rerror message saying "permission denied".

func (Tremove) Context Uses

func (t Tremove) Context() context.Context

Context returns the context associated with the request.

func (Tremove) Path Uses

func (t Tremove) Path() string

Path returns the absolute path of the file being operated on.

func (Tremove) Rerror Uses

func (t Tremove) Rerror(format string, args ...interface{})

Rerror sends an error to the client.

func (Tremove) Rremove Uses

func (t Tremove) Rremove(err error)

Rremove signals to the client that a file has been succesfully removed. The file handle for the file is no longer valid, and may be re-used for other files. Whether or not any other file handles associated with the file continue to be usable for I/O is implementation-defined; many Unix file systems allow a process to continue writing to a file that has been "unlinked", so long as the process has an open file descriptor.

If err is non-nil, an Rerror message is sent to the client. Regardless, the file handle is no longer valid.

func (Tremove) WithContext Uses

func (t Tremove) WithContext(ctx context.Context) Request

type Trename Uses

type Trename struct {
    OldPath, NewPath string
    // contains filtered or unexported fields
}

A Trename message is sent by the client to change the name of an existing file. Use the Rrename method to indicate success.

The default response for a Trename request is an Rerror message saying "permission denied"

func (Trename) Path Uses

func (t Trename) Path() string

The Path method of a Trename request returns the current path to the file, before a rename has taken place.

func (Trename) Rerror Uses

func (t Trename) Rerror(format string, args ...interface{})

Call Rerror to provide a descriptive error message explaining why a file attribute could not be updated.

func (Trename) Rrename Uses

func (t Trename) Rrename(err error)

Rrename indicates to the server that the rename was succesful. Once Rrename is called with a nil error, future stat requests should reflect the updated name.

func (Trename) WithContext Uses

func (t Trename) WithContext(ctx context.Context) Request

type Tstat Uses

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

A Tstat message is sent when a client wants metadata about a file. A client should have read access to the file's containing directory. Call the Rstat method for a succesful request.

The default response for a Tstat message is an Rerror message saying "permission denied".

func (Tstat) Context Uses

func (t Tstat) Context() context.Context

Context returns the context associated with the request.

func (Tstat) Path Uses

func (t Tstat) Path() string

Path returns the absolute path of the file being operated on.

func (Tstat) Rerror Uses

func (t Tstat) Rerror(format string, args ...interface{})

Rerror sends an error to the client.

func (Tstat) Rstat Uses

func (t Tstat) Rstat(info os.FileInfo, err error)

Rstat responds to a succesful Tstat request. The styx package will translate the os.FileInfo value into the appropriate 9P structure. Rstat will attempt to resolve the names of the file's owner and group. If that cannot be done, an empty string is sent. If err is non-nil, and error is sent to the client instead.

func (Tstat) WithContext Uses

func (t Tstat) WithContext(ctx context.Context) Request

type Tsync Uses

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

A Tsync request is made by the client to indicate that the client would like any changes made to the file to be flushed to durable storage. Use the Rsync method to indicate success.

The default response to a Tsync message is an Rerror message saying "not supported".

func (Tsync) Rerror Uses

func (t Tsync) Rerror(format string, args ...interface{})

Call Rerror to provide a descriptive error message explaining why a file attribute could not be updated.

func (Tsync) Rsync Uses

func (t Tsync) Rsync(err error)

Rsync, when called with a nil error, indicates that the file has been flushed to durable storage. Note that different servers will have different definitions of what "durable" means, and provide different consistency guarantees.

func (Tsync) WithContext Uses

func (t Tsync) WithContext(ctx context.Context) Request

type Ttruncate Uses

type Ttruncate struct {
    Size int64
    // contains filtered or unexported fields
}

A Ttruncate requests for the size of a file to be changed. Use the Rtruncate method to indicate success.

The default response to a Ttruncate message is an Rerror message saying "permission denied".

func (Ttruncate) Rerror Uses

func (t Ttruncate) Rerror(format string, args ...interface{})

Call Rerror to provide a descriptive error message explaining why a file attribute could not be updated.

func (Ttruncate) Rtruncate Uses

func (t Ttruncate) Rtruncate(err error)

Rtruncate, when called with a nil error, indicates that the file has been updated to reflect Size. Future reads, writes and stats should reflect the new file length.

func (Ttruncate) WithContext Uses

func (t Ttruncate) WithContext(ctx context.Context) Request

type Tutimes Uses

type Tutimes struct {
    Atime, Mtime time.Time
    // contains filtered or unexported fields
}

A Tutimes message is sent by the client to change the modification time of a file. Use the Rutime method to indicate success.

The default response to a Tutimes message is an Rerror message saying "permission denied"

func (Tutimes) Rerror Uses

func (t Tutimes) Rerror(format string, args ...interface{})

Call Rerror to provide a descriptive error message explaining why a file attribute could not be updated.

func (Tutimes) Rutimes Uses

func (t Tutimes) Rutimes(err error)

Rutimes, when called with a nil error, indicates that the file times were succesfully updated. Future stat requests should reflect the new access and modification times.

func (Tutimes) WithContext Uses

func (t Tutimes) WithContext(ctx context.Context) Request

type Twalk Uses

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

A client sends a Twalk message both to probe if a file exists, and to move a "cursor" within the filesystem hierarchy. In a traditional file system, a Twalk request is similar to using chdir to change the current directory. File servers are free to attach additional meaning to Twalk requests. For instance, a server may create directories on-demand as clients walk to them.

The 9P protocol allows for clients to walk multiple directories with a single 9P message. The styx package translates such requests into multiple Twalk values, providing the following guarantees:

- Path() will return a cleaned, absolute path
- Consecutive, related Twalk requests will differ by at
  most 1 path element.

The default response to a Twalk request is an Rerror message saying "No such file or directory".

func (Twalk) Context Uses

func (t Twalk) Context() context.Context

Context returns the context associated with the request.

func (Twalk) Path Uses

func (t Twalk) Path() string

Path returns the absolute path of the file being operated on.

func (Twalk) Rerror Uses

func (t Twalk) Rerror(format string, args ...interface{})

Rerror signals to the client that the file named by the Twalk's Path method does not exist.

func (Twalk) Rwalk Uses

func (t Twalk) Rwalk(info os.FileInfo, err error)

Rwalk signals to the client that the file named by the Twalk's Path method exists and is of the given mode. The permission bits of mode are ignored, and only the file type bits, such as os.ModeDir, are sent to the client. If err is non-nil, an error response is sent to the client instead.

func (Twalk) WithContext Uses

func (t Twalk) WithContext(ctx context.Context) Request

Bugs

cancellation of write requests is not yet implemented.

renaming a file with one fid will break Twalk requests that attempt to clone another fid pointing to the same file.

Directories

PathSynopsis
internal/netutilPackage netutil contains useful types for testing network services.
internal/poolPackage pool manages pools of integer identifiers.
internal/qidpoolPackage qidpool manages pools of 9P Qids, 13-bit unique identifiers for files.
internal/styxfilePackage styxfile provides helper routines and interfaces for serving 9P files from Go types.
internal/sysPackage sys contains non-portable routines.
internal/threadsafePackage threadsafe implements data structures that are safe for use from multiple goroutines.
internal/tracingPackage tracing provides tracing of sent and received 9P messages.
internal/utilPackage util contains internal routines used by the styx packages.
styxauthPackage styxauth provides authentication methods for 9P servers.
styxprotoPackage styxproto provides low-level routines for parsing and producing 9P2000 messages.

Package styx imports 22 packages (graph) and is imported by 4 packages. Updated 2018-04-13. Refresh now. Tools for package owners.