Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
186 lines (167 sloc) 4.22 KB
// Package styxfile provides helper routines and interfaces
// for serving 9P files from Go types.
package styxfile
import (
// 9P read/write requests contain an offset. This makes them
// well-suited to the io.ReaderAt and io.WriterAt interfaces.
// However to keep our APIs flexible, we do the heavy lifting,
// accepting broader interfaces and forcing them into what we
// need.
// ErrNotSupported is returned when a given type does not
// implement the necessary functionality to complete a given
// read/write operation.
var ErrNotSupported = errors.New("not supported")
// Interface describes the methods a type must implement to
// be used as a file by a 9P file server. The New function converts
// types that implement some, but not all of these methods into
// types that do.
type Interface interface {
type interfaceWithoutClose interface {
type nopCloser struct {
func (nopCloser) Close() error { return nil }
// New creates a new Interface that reads and writes to and from
// rwc. The type of rwc determines the implementation selected
// by New; if rwc already implements Interface, it is used as-is. If
// some methods are missing, wrapper types are used to implement
// missing functionality. If the provided type cannot be adapted into
// an Interface, New returns a non-nil error.
func New(rwc interface{}) (Interface, error) {
switch rwc := rwc.(type) {
case Interface:
return rwc, nil
case interfaceWithoutClose:
return nopCloser{rwc}, nil
case io.Seeker:
return &seekerAt{rwc: rwc}, nil
case io.ReadWriter:
return &dumbPipe{rwc: rwc}, nil
case io.Reader:
return &dumbPipe{rwc: rwc}, nil
case io.Writer:
return &dumbPipe{rwc: rwc}, nil
return nil, fmt.Errorf("Cannot convert type %T into a styxfile.Interface", rwc)
// SetDeadline sets read/write deadlines for a file, if the type supports it.
func SetDeadline(file Interface, t time.Time) error {
var real interface{}
switch v := file.(type) {
case *seekerAt:
real = v.rwc
case *dumbPipe:
real = v.rwc
case *dirReader:
real = v.Directory
real = v
type deadline interface {
SetDeadline(time.Time) error
if v, ok := real.(deadline); ok {
return v.SetDeadline(t)
return ErrNotSupported
// Stat produces a styxproto.Stat from an open file. If the value
// provides a Stat method matching that of os.File, that is used.
// Otherwise, the styxfile package determines the file's attributes
// based on other characteristics.
func Stat(buf []byte, file Interface, name string, qid styxproto.Qid) (styxproto.Stat, error) {
var (
fi os.FileInfo
err error
type hasStat interface {
Stat() (os.FileInfo, error)
if v, ok := file.(hasStat); ok {
fi, err = v.Stat()
if err != nil {
return nil, err
} else {
fi = statGuess{file, name, qid.Type()}
uid, gid, muid := sys.FileOwner(fi)
stat, _, err := styxproto.NewStat(buf, fi.Name(), uid, gid, muid)
if err != nil {
return nil, err
return stat, nil
type statGuess struct {
file Interface
name string
qtype uint8
func (sg statGuess) Name() string {
type hasName interface {
Name() string
if v, ok := sg.file.(hasName); ok {
return v.Name()
func (sg statGuess) Size() int64 {
type hasSize interface {
Size() int64
if v, ok := sg.file.(hasSize); ok {
return v.Size()
return -1
func (sg statGuess) Mode() os.FileMode {
type hasMode interface {
Mode() os.FileMode
if v, ok := sg.file.(hasMode); ok {
return v.Mode()
return ModeOS(uint32(sg.qtype)<<24) | 0777
func (sg statGuess) IsDir() bool {
type hasDir interface {
IsDir() bool
if v, ok := sg.file.(hasDir); ok {
return v.IsDir()
return sg.Mode().IsDir()
func (sg statGuess) ModTime() time.Time {
type hasTime interface {
ModTime() time.Time
if v, ok := sg.file.(hasTime); ok {
return v.ModTime()
return time.Time{}
func (sg statGuess) Sys() interface{} {
return sg.file