lib9p

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

commit 590904aef9dc59d0c0e13487691917464ff513b8
parent f993443f32b94acf4f6ac5428acb66cf89c434c6
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Thu, 26 Oct 2023 11:28:26 +0900

add comments

Diffstat:
Mclient.go | 5+++++
Mclient2.go | 9+++++++++
Mfid.go | 15++++++++++-----
Mfile.go | 8+++++++-
Mfs.go | 4++++
Mparse.go | 4+++-
Mqid.go | 8++++++--
Mreq.go | 20++++++++++++--------
Mstat.go | 11++++++++++-
Muid.go | 1+
10 files changed, 67 insertions(+), 18 deletions(-)

diff --git a/client.go b/client.go @@ -7,6 +7,7 @@ import ( "sync" ) +// Client is a client side of the 9P conversation. type Client struct { msize uint32 mSizeLock *sync.Mutex @@ -19,6 +20,7 @@ type Client struct { wg *sync.WaitGroup } +// newClient creates a Client. func NewClient(mSize uint32, uname string, r io.Reader, w io.Writer) *Client { ctx, cancel := context.WithCancel(context.Background()) c := &Client{ @@ -36,18 +38,21 @@ func NewClient(mSize uint32, uname string, r io.Reader, w io.Writer) *Client { return c } +// Stop stops the Client. func (c *Client) Stop() { c.cancel() c.wg.Wait() close(c.errc) } +// mSize returns the maximum message size of the Client. func (c *Client) mSize() uint32 { c.mSizeLock.Lock() defer c.mSizeLock.Unlock() return c.msize } +// setMSize changes the maximum message size of the Client. func (c *Client) setMSize(mSize uint32) { c.mSizeLock.Lock() defer c.mSizeLock.Unlock() diff --git a/client2.go b/client2.go @@ -9,15 +9,19 @@ import ( "strings" ) +// ClientFS represents the file system the client imports. type ClientFS struct { c *Client tPool *tagPool } +// Open opens the file named name in fsys. func (fsys *ClientFS) Open(name string) (File, error) { return fsys.OpenFile(name, OREAD, 0) } +// OpenFile opens the file named name in fsys with omode. +// If the file does not exist, it create it with perm. func (fsys *ClientFS) OpenFile(name string, omode OpenMode, perm fs.FileMode) (File, error) { var ( qid Qid @@ -59,6 +63,9 @@ func (fsys *ClientFS) OpenFile(name string, omode OpenMode, perm fs.FileMode) (F return f, nil } +// walkFile walks the file system to the named file name and +// returns the corresponding file. +// returned file is not open. func (fsys *ClientFS) walkFile(name string) (*ClientFile, error) { fid, err := fsys.c.fPool.add() if err != nil { @@ -96,6 +103,8 @@ func (fsys *ClientFS) walkFile(name string) (*ClientFile, error) { return f, nil } +// Mount initiates a 9P session and returns the resulting file system. +// The 9P session is established by writing to w and reading from r. func Mount(r io.Reader, w io.Writer, uname, aname string) (fs *ClientFS, err error) { var ( mSize uint32 = 8192 diff --git a/fid.go b/fid.go @@ -18,14 +18,15 @@ const ( ORCLOSE = 64 ) +// Fid represents the fid defined by 9P type Fid struct { Fid uint32 OMode OpenMode /* -1 = not open */ - path string - File File - Uid string - dirOffset uint64 - dirIndex int + path string // The path from the root of the FS. + File File // The associated File. + Uid string // The user id derived from the attach message. + dirOffset uint64 // Used when reading directory. + dirIndex int // Used when reading directory. } const NOFID = ^uint32(0) @@ -45,11 +46,14 @@ 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. type FidPool struct { m map[uint32]*Fid lock *sync.Mutex } +// newFidPool allocates an FidPool. func newFidPool() *FidPool { return &FidPool{ m: make(map[uint32]*Fid), @@ -103,6 +107,7 @@ func (pool *FidPool) String() string { return s } +// clientFid represents the Fid in the client side. type clientFid struct { fid uint32 omode OpenMode // -1 for not open diff --git a/file.go b/file.go @@ -6,6 +6,7 @@ import ( "io" ) +// File is an open file. type File interface { Stat() (*FileInfo, error) Close() error @@ -17,6 +18,7 @@ type WriterFile interface { io.Writer } +// TODO: Delete this. Create should be implemented by using FS.OpenFile. type CreaterFile interface { File // Create creates a file named name in this directory. @@ -34,17 +36,21 @@ type WriterStatFile interface { WStat(stat *Stat) error } +// TODO: I think Remove should be implemented by the FS's method. type RemoverFile interface { File Remove() error } +// ReadDirFile is a directory. +// Note that non-directory file can implement this. In this case, ReadDir should +// return an error. type ReadDirFile interface { File ReadDir(n int) ([]*DirEntry, error) } -// ClientFile is File for Client. +// ClientFile is a File for Client. type ClientFile struct { name string path string // must not contain trailing slash. diff --git a/fs.go b/fs.go @@ -6,10 +6,14 @@ import ( "path" ) +// FS is an file system to be exported by 9P server. type FS interface { + // OpenFile opens file named name with omode. + // If the file does not exists, it creates it with FileMode perm. OpenFile(name string, omode OpenMode, perm fs.FileMode) (File, error) } +// getChildren returns the child files in the directory specified by dirpath. func getChildren(fsys FS, dirpath string) ([]File, error) { dir0, err := fsys.OpenFile(dirpath, OREAD, 0) if err != nil { diff --git a/parse.go b/parse.go @@ -6,7 +6,7 @@ import ( "io" ) -// Read9PMsg reads 9P message from r and saves into a byte slice. +// Read9PMsg reads a 9P message from r and saves it in a byte slice. // It returns the byte slice read and error if any. func read9PMsg(r io.Reader) ([]byte, error) { buf := make([]byte, 4) @@ -33,6 +33,8 @@ func read9PMsg(r io.Reader) ([]byte, error) { return buf, nil } +// unmarshal converts a byte array of 9P message into the corresponding +// Msg struct. func unmarshal(buf []byte) (Msg, error) { switch t := bufMsg(buf).Type(); t { case Tversion: diff --git a/qid.go b/qid.go @@ -18,12 +18,15 @@ const ( QTFILE = 0x00 /* type bits for plain file */ ) +// Qid is the identifier of each file in 9P server. type Qid struct { - Type QidType + Type QidType // type of the file. Vers uint32 // version of the file. - Path uint64 + Path uint64 // uniq number of each file. } +// unmarshalQid converts a byte array of Qid defined by 9P protocol +// into a Qid struct. func unmarshalQid(b []byte) Qid { return Qid{ Type: QidType(b[0]), @@ -32,6 +35,7 @@ func unmarshalQid(b []byte) Qid { } } +// marshal converts the Qid into a byte array of Qid defined by 9P protocol. func (q Qid) marshal() []byte { buf := make([]byte, 13) buf[0] = uint8(q.Type) diff --git a/req.go b/req.go @@ -6,9 +6,9 @@ import ( "sync" ) +// Req represents each requests. type Req struct { tag uint16 - tagDeleted chan struct{} srv *Server ifcall Msg ofcall Msg @@ -19,20 +19,21 @@ type Req struct { cancel context.CancelFunc } +// flush cancels the Req by calling r.cancel. func (r *Req) flush() { // TODO: need mutex? // BUG: cancel() can be nil. r.cancel() r.pool.delete(r.tag) - // wait for tag being deleted. - // <-r.tagDeleted } +// ReqPool is the pool of Reqs the server is dealing with. type ReqPool struct { m map[uint16]*Req lock *sync.Mutex } +// newReqPool allocats a ReqPool. func newReqPool() *ReqPool { return &ReqPool{ m: make(map[uint16]*Req), @@ -50,12 +51,14 @@ func (rp *ReqPool) add(tag uint16) (*Req, error) { } req := &Req{ pool: rp, - tagDeleted: make(chan struct{}), } rp.m[tag] = req return req, nil } +// lookup looks for the Req in the pool with tag. +// If found, it returns the found Req and true, otherwise +// it returns nil and false. func (rp *ReqPool) lookup(tag uint16) (*Req, bool) { rp.lock.Lock() defer rp.lock.Unlock() @@ -63,16 +66,14 @@ func (rp *ReqPool) lookup(tag uint16) (*Req, bool) { return r, ok } +// delete delets the Req with tag from the pool. func (rp *ReqPool) delete(tag uint16) { rp.lock.Lock() defer rp.lock.Unlock() - req, ok := rp.m[tag] - if ok { - defer close(req.tagDeleted) - } delete(rp.m, tag) } +// clientReq represents each requests of the client. type clientReq struct { tag uint16 tmsg Msg @@ -82,6 +83,8 @@ type clientReq struct { ctxDone <-chan struct{} } +// newClientReq allocates a clientReq with msg. +// It also sets the ctxDone channel to ctx.Done(). // TODO: passing ctx is confusing? // it only needs the done channel. func newClientReq(ctx context.Context, msg Msg) *clientReq { @@ -93,6 +96,7 @@ func newClientReq(ctx context.Context, msg Msg) *clientReq { } } +// tagPool is a pool of tags being used by a client. type tagPool struct { m map[uint16]bool lock *sync.Mutex diff --git a/stat.go b/stat.go @@ -8,6 +8,8 @@ import ( type FileMode = fs.FileMode +// permString returns the string representation of perm's +// lower 9 bits for debugging. func permString(perm FileMode) string { s := "" for i := 6; i >= 0; i -= 3 { @@ -37,6 +39,7 @@ const ( AREAD ) +// Stat represents the stat defined by 9P. type Stat struct { Type uint16 Dev uint32 @@ -52,6 +55,7 @@ type Stat struct { Muid string } +// newStat converts a byte array of stat from a 9P message into Stat struct. func newStat(b []byte) *Stat { stat := new(Stat) size := gbit16(b[:2]) + 2 @@ -93,6 +97,7 @@ func newStat(b []byte) *Stat { return stat } +// size returns the size of marshaled stat in bytes. func (s *Stat) size() uint16 { // type + dev + qid + mode + atime + mtime + length return uint16(2 + 4 + 13 + 4 + 4 + 4 + 8 + @@ -102,6 +107,8 @@ func (s *Stat) size() uint16 { 2 + len(s.Muid)) } +// marshal converts a Stat struct into a byte array of stat to be transmitted +// in 9P conversatino. func (s *Stat) marshal() []byte { cur := 0 size := s.size() @@ -164,6 +171,8 @@ 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. type FileInfo struct { Stat Stat } @@ -173,11 +182,11 @@ func (fi *FileInfo) Size() int64 { return fi.Stat.Length } func (fi *FileInfo) Mode() fs.FileMode { return fi.Stat.Mode } func (fi *FileInfo) ModTime() time.Time { return time.Unix(int64(fi.Stat.Mtime), 0) } 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. type DirEntry = FileInfo func (de *DirEntry) Info() fs.FileInfo { return de } diff --git a/uid.go b/uid.go @@ -5,6 +5,7 @@ import ( "log" ) +// hasPerm reports whether the user uid have the permission p on the File f. // p is logical OR of AREAD, AWRITE, AEXEC. // TODO: user is assumed to be the leader of the group func hasPerm(f File, uid string, p fs.FileMode) bool {