lib9p

Go 9P library.
Log | Files | Refs | LICENSE

commit 3529506575eab96178d36dd4cd98d9b4bdb17bcd
parent 7e647df108e88e1dce78cb57c8830024d13158fb
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Fri, 29 Dec 2023 15:16:33 +0900

add documentation and gofmt

Diffstat:
Mauth.go | 4+++-
Mclient/client_test.go | 5++---
Mclient/file.go | 6+++---
Mclient/file_test.go | 5++---
Mclient/fs.go | 3+--
Mexport_fs.go | 8++++----
Mexport_test.go | 31+++++++++++++++++--------------
Mfcall.go | 14+++++++-------
Mfid.go | 22++++++++++++++++------
Mfile.go | 20+++++++++++++-------
Mgo.mod | 6++++++
Mreq.go | 2+-
Mserver2_test.go | 4++--
Mserver_test.go | 16++++++++--------
Mstat.go | 17+++++++++--------
Mtestfs/fs.go | 16++++++++--------
16 files changed, 102 insertions(+), 77 deletions(-)

diff --git a/auth.go b/auth.go @@ -6,7 +6,9 @@ import ( "io/fs" ) -// AuthFile is a file allocated by Tauth messages. +// An AuthFile is a file allocated by Auth function of Server struct. +// If authentication is desired, Auth member of Server struct must be set +// and it should set an AuthFile ready to Read/Write via R/W struct. type AuthFile struct { Qid Qid Uname string diff --git a/client/client_test.go b/client/client_test.go @@ -71,7 +71,7 @@ func TestDupTag(t *testing.T) { testfs.Fsys.Slow = true defer func() { testfs.Fsys.Slow = false }() bg := context.Background() - _, _, err := conn.C.Version(bg, 0, 8 * 1024, "9P2000") + _, _, err := conn.C.Version(bg, 0, 8*1024, "9P2000") if err != nil { t.Fatal(err) } @@ -100,4 +100,4 @@ func TestDupTag(t *testing.T) { if err == nil { t.Error("dup tag not reported") } -} -\ No newline at end of file +} diff --git a/client/file.go b/client/file.go @@ -97,10 +97,10 @@ func (cf *File) ReadDir(n int) ([]fs.DirEntry, error) { return nil, errors.New("not open") } var ( - de []fs.DirEntry - err error + de []fs.DirEntry + err error data []byte - tag uint16 + tag uint16 ) for n <= 0 || len(cf.dirBuf) < n { tag, err = cf.fs.tPool.add() diff --git a/client/file_test.go b/client/file_test.go @@ -21,7 +21,7 @@ type FS struct { func mountTestFS(t *testing.T) *FS { cr, sw := io.Pipe() sr, cw := io.Pipe() - s := lib9p.NewServer(testfs.Fsys, 8 * 1024, sr, sw) + s := lib9p.NewServer(testfs.Fsys, 8*1024, sr, sw) ctx, cancel := context.WithCancel(context.Background()) go s.Serve(ctx) fsys, err := client.Mount(cr, cw, "ken", "") @@ -92,4 +92,4 @@ L: } t.Errorf("file name not in the testfs: %s", f.Name()) } -} -\ No newline at end of file +} diff --git a/client/fs.go b/client/fs.go @@ -139,4 +139,4 @@ func Mount(r io.Reader, w io.Writer, uname, aname string) (fs *FS, err error) { return cfs, nil } -func (fsys *FS) Unmount() { fsys.c.Stop() } -\ No newline at end of file +func (fsys *FS) Unmount() { fsys.c.Stop() } diff --git a/export_fs.go b/export_fs.go @@ -1,11 +1,11 @@ package lib9p import ( - "io/fs" "errors" + "io/fs" ) -// exportFS is a wrapper of FS to export it as fs.FS. +// An ExportFS is a wrapper of FS to export it as an fs.FS. type ExportFS struct { FS } @@ -15,6 +15,7 @@ func (fsys ExportFS) Open(name string) (fs.File, error) { return ExportFile{f}, err } +// An ExportFile is a wrapper of File to export it as an fs.File. type ExportFile struct { f File } @@ -38,4 +39,4 @@ func (f ExportFile) ReadDir(n int) ([]fs.DirEntry, error) { return nil, errors.New("not a directory") } return d.ReadDir(n) -} -\ No newline at end of file +} diff --git a/export_test.go b/export_test.go @@ -37,7 +37,7 @@ var ( SWalk = sWalk SOpen = sOpen - HasPerm = hasPerm + HasPerm = hasPerm NewFidPool = newFidPool ) @@ -51,33 +51,36 @@ func (s *Server) RunListener(ctx context.Context, rp *ReqPool) { func (s *Server) RunResponder(ctx context.Context, rp *ReqPool) { s.runResponder(ctx, rp) } -func (s *Server) SetFS(fs FS) { s.fs = fs } +func (s *Server) SetFS(fs FS) { s.fs = fs } func (s *Server) SetRespChan(rc chan *Req) { s.respChan = rc } -func (s *Server) SetFPool(fp *FidPool) { s.fPool = fp } -func (s *Server) NewMSizeLock() { s.mSizeLock = new(sync.Mutex) } -func (s *Server) SetMSize(size uint32) { s.setMSize(size) } +func (s *Server) SetFPool(fp *FidPool) { s.fPool = fp } +func (s *Server) NewMSizeLock() { s.mSizeLock = new(sync.Mutex) } +func (s *Server) SetMSize(size uint32) { s.setMSize(size) } type Req = request -func (r *Req) Ifcall() Msg { return r.ifcall } + +func (r *Req) Ifcall() Msg { return r.ifcall } func (r *Req) SetIfcall(m Msg) { r.ifcall = m } -func (r *Req) Ofcall() Msg { return r.ofcall } +func (r *Req) Ofcall() Msg { return r.ofcall } func (r *Req) SetOfcall(m Msg) { r.ofcall = m } -func (r *Req) Afid() *Fid { return r.afid } +func (r *Req) Afid() *Fid { return r.afid } type ReqPool = reqPool + func (rp *ReqPool) Add(tag uint16) (*Req, error) { return rp.add(tag) } type Fid = fid + func (fid *Fid) SetPath(path string) { fid.path = path } -func (fid *Fid) SetUid(uid string) { fid.uid = uid } -func (fid *Fid) File() File { return fid.file } -func (fid *Fid) SetFile(f File) { fid.file = f } +func (fid *Fid) SetUid(uid string) { fid.uid = uid } +func (fid *Fid) File() File { return fid.file } +func (fid *Fid) SetFile(f File) { fid.file = f } func (fid *Fid) SetOmode(m OpenMode) { fid.omode = m } type FidPool = fidPool + func (fp *FidPool) Lookup(fid uint32) (*Fid, bool) { return fp.lookup(fid) } -func (fp *FidPool) Add(fid uint32) (*Fid, error) { return fp.add(fid) } -func (fp *FidPool) Delete(fid uint32) { fp.delete(fid) } +func (fp *FidPool) Add(fid uint32) (*Fid, error) { return fp.add(fid) } +func (fp *FidPool) Delete(fid uint32) { fp.delete(fid) } func MarshalMsg(m Msg) []byte { return m.marshal() } - diff --git a/fcall.go b/fcall.go @@ -4,7 +4,7 @@ import ( "fmt" ) -// Message types defined by 9P. +// A MsgType is Message type defined by 9P. type MsgType uint8 const ( @@ -39,9 +39,10 @@ const ( Tmax = 128 ) -// Ample room for Twrite/Rread header (iounit). -// Twrite: size[4] type[1] Tag[2] fid[4] offset[8] count[4] = 23 -// Rread: size[4] type[1] Tag[2] count[4] = 11 +// IOHDRSZ is the ample room for Twrite/Rread header. +// - Twrite: size[4] type[1] Tag[2] fid[4] offset[8] count[4] = 23 +// - Rread: size[4] type[1] Tag[2] count[4] = 11 +// // In Plan9, this const is 24. const IOHDRSZ = 23 @@ -50,7 +51,7 @@ const IOHDRSZ = 23 // override Tag matching in version messages. const NOTAG = ^uint16(0) -// Msg represents any kind of message of 9P. +// A Msg represents any kind of message of 9P. // It defines methods for common fields. // For each message type <T>, new<T>([]byte) function parses the byte array // of 9P message into the corresponding message struct. @@ -70,13 +71,12 @@ type Msg interface { GetTag() uint16 // SetTag sets the Tag field of the message. SetTag(uint16) - // Marshal convert Msg to byte array to be transmitted. marshal() []byte String() string } -// bufMsg is Msg with just an array of bytes. +// A bufMsg is Msg with just an array of bytes. // TODO: rename. type bufMsg []byte diff --git a/fid.go b/fid.go @@ -5,9 +5,11 @@ import ( "sync" ) -type OpenMode int8 // defined by 9P +// An OpenMode is the mode of an open file. +// This is used by fid structure. +// If the file is not open, the mode must be -1. +type OpenMode int8 -// -1 = not open const ( OREAD OpenMode = 0 OWRITE = 1 @@ -18,7 +20,13 @@ const ( ORCLOSE = 64 ) -// fid represents the fid defined by 9P +// An fid represents an fid defined by 9P. +// It is used as the identifier of a file in 9P session. +// It is specified by the client and the server connect it to a file in the +// file system. +// Multiple fids can be connected to a single file. +// On the other hand, a Qid is the Identifier in the file system itself, +// and not change between 9P sessions. type fid struct { fid uint32 omode OpenMode /* -1 = not open */ @@ -29,6 +37,8 @@ type fid struct { dirIndex int // Used when reading directory. } +// NOFID is used in afid field of Tattach message when no authentication +// is required. const NOFID = ^uint32(0) func newFid(id uint32) *fid { @@ -46,8 +56,9 @@ func (f *fid) String() string { return fmt.Sprintf("%d", fid) } -// fidPool is a pool of fid. -// It is used to track the fid in the file system the server is exporting. +// An fidPool is a pool of fid. +// It is used to track the fids of each 9P connection (not of each session). +// It is safe for concurrent use by multiple goroutines except the String method. type fidPool struct { m map[uint32]*fid lock *sync.Mutex @@ -82,7 +93,6 @@ func (pool *fidPool) add(id uint32) (*fid, error) { func (pool *fidPool) delete(id uint32) { pool.lock.Lock() defer pool.lock.Unlock() - delete(pool.m, id) } diff --git a/file.go b/file.go @@ -1,24 +1,29 @@ package lib9p import ( - "io" "io/fs" ) // A File is an open file. -// The interface is identical to that of [fs.File], but the [fs.FileInfo] -// returned by Stat function must be *FileInfo of this package. +// The interface is similar to that of [fs.File], but Stat returns +// [*FileInfo] of this package instead of [fs.FileInfo]. type File interface { Stat() (*FileInfo, error) + Read([]byte) (int, error) Close() error - io.Reader } +// A WriterFile is a file with Write method. +// If the server is to accept Twrite messages, files in this server +// must implement this interface. type WriterFile interface { File - io.Writer + Write([]byte) (int, error) } +// A WriterStatFile is a file which can modify its attributes such as +// the permission and the group (Note that changing ownership of a file +// is prohibited by the protocol). type WriterStatFile interface { File // WStat sets file Stat to stat. @@ -28,8 +33,9 @@ type WriterStatFile interface { WStat(stat *Stat) error } -// ReadDirFile is a directory. -// Note that non-directory file can implement this. In this case, ReadDir should +// A ReadDirFile is a directory. +// Every directory file shoul implement this interface. +// Non-directory files can implement this. In this case, ReadDir should // return an error. type ReadDirFile interface { File diff --git a/go.mod b/go.mod @@ -1,3 +1,9 @@ module git.mtkn.jp/lib9p go 1.20 + +require ( + github.com/yuin/goldmark v1.4.13 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/tools v0.16.1 // indirect +) diff --git a/req.go b/req.go @@ -16,7 +16,7 @@ type request struct { // Oldreq is set with Tflush message. oldreq *request // Pool is the pool this request belongs to. - pool *reqPool + pool *reqPool // Done is used by time consuming goroutines to check whether the request // is flushed. done chan struct{} diff --git a/server2_test.go b/server2_test.go @@ -27,7 +27,7 @@ func TestRunListener(t *testing.T) { } defer tFile2.Close() s := &Server{ - r: tFile, + r: tFile, } oldReqPoolAdd := reqPoolAdd defer func() { reqPoolAdd = oldReqPoolAdd }() @@ -80,7 +80,7 @@ func TestRunResponder(t *testing.T) { t.Fatalf("unmarshal %v", err) } s.respChan <- &request{ - tag: msg.GetTag(), + tag: msg.GetTag(), ofcall: msg, } got := make([]byte, len(want)) diff --git a/server_test.go b/server_test.go @@ -15,7 +15,7 @@ import ( func TestSAttach(t *testing.T) { dammyAuth := func(context.Context, *lib9p.Req) {} af := &lib9p.AuthFile{ - Qid: lib9p.Qid{Type: lib9p.QTAUTH}, + Qid: lib9p.Qid{Type: lib9p.QTAUTH}, Uname: "kenji", Aname: "", } @@ -26,10 +26,10 @@ func TestSAttach(t *testing.T) { } fid.SetFile(af) tests := []struct { - input lib9p.Msg - auth bool - authOK bool - want lib9p.Msg + input lib9p.Msg + auth bool + authOK bool + want lib9p.Msg }{ {&lib9p.TAttach{Fid: 0, Afid: lib9p.NOFID, Uname: "kenji", Aname: ""}, false, false, @@ -93,9 +93,9 @@ func TestSAttach(t *testing.T) { func TestSWalk(t *testing.T) { tests := []struct { - input lib9p.Msg - wantLen int // len(Ofcall.(*lib9p.RWalk).Qids) - wantErr error + input lib9p.Msg + wantLen int // len(Ofcall.(*lib9p.RWalk).Qids) + wantErr error }{ {&lib9p.TWalk{Fid: 0, Newfid: 1, Wnames: []string{"a"}}, 1, nil}, diff --git a/stat.go b/stat.go @@ -8,7 +8,7 @@ import ( type FileMode = fs.FileMode -// permString returns the string representation of perm's +// PermString returns the string representation of perm's // lower 9 bits for debugging. func permString(perm FileMode) string { s := "" @@ -33,6 +33,8 @@ func permString(perm FileMode) string { return s } +// These consts are used to record the access mode to a file. +// This is derived from plan9's lib9p, but I think this is not needed. const ( AEXEC fs.FileMode = 1 << iota AWRITE @@ -96,7 +98,7 @@ func NewStat(b []byte) *Stat { return stat } -// size returns the size of marshaled stat in bytes excluding the +// Size returns the size of marshaled stat in bytes excluding the // size field itself. func (s *Stat) Size() uint16 { // type + dev + qid + mode + atime + mtime + length @@ -107,7 +109,7 @@ func (s *Stat) Size() uint16 { 2 + len(s.Muid)) } -// marshal converts a Stat struct into a byte array of stat to be transmitted +// Marshal converts a Stat struct into a byte array of stat to be transmitted // in 9P conversatino. func (s *Stat) marshal() []byte { cur := 0 @@ -171,8 +173,9 @@ func (s *Stat) String() string { s.Atime, s.Mtime, s.Length, s.Type, s.Dev) } -// FileInfo is a struct returned by File.Stat(). -// This struct is needed because of the name collision: Stat.Name and FileInfo.Name. +// A FileInfo is a struct returned by File.Stat(). +// This struct is needed because of the name collision: +// Stat.Name and FileInfo.Name. type FileInfo struct { Stat Stat } @@ -186,9 +189,7 @@ func (fi *FileInfo) IsDir() bool { return fi.Stat.Mode&fs.ModeDir != 0 } // Sys returns *Stat func (fi *FileInfo) Sys() any { return &fi.Stat } -//func (fi *FileInfo) Qid() Qid { return fi.Stat.Qid } - -// DirEntry is returned by File.ReadDir. +// A DirEntry is returned by File.ReadDir. type DirEntry = FileInfo func (de *DirEntry) Type() fs.FileMode { return de.Mode().Type() } diff --git a/testfs/fs.go b/testfs/fs.go @@ -11,19 +11,19 @@ import ( type File struct { // Fsys is the FS this File belongs to. - Fsys *FS + Fsys *FS // Parent is the parent directory this File located at. - Parent *File + Parent *File // Children is the child files this File has. // It is nil if this File is not a directory Children []*File // Content is the content of this File. // It is nil if this File is a directory - Content []byte + Content []byte // Reader is used if this File is not a directory and // when this File is read. // It contains Content. - Reader *bytes.Reader + Reader *bytes.Reader // St is the Stat of this File. St lib9p.Stat } @@ -39,7 +39,7 @@ func (f *File) Close() error { func (f *File) Read(b []byte) (int, error) { if f.Fsys.Slow { - f.Fsys.Waitc <-struct{}{} + f.Fsys.Waitc <- struct{}{} <-f.Fsys.Waitc } return f.Reader.Read(b) @@ -87,12 +87,12 @@ func (f *File) WriteAt(p []byte, off int64) (int, error) { type FS struct { // Root is the root File of this file system. - Root *File + Root *File // If Slow is true, each function on files of this FS waits for // something arrives on Waitc. - Slow bool + Slow bool // Waitc is used to wait some timing to emulate slow response. - Waitc chan struct{} + Waitc chan struct{} // groups holds the information of groups and it leader and members. groups map[string]group }