commit 590904aef9dc59d0c0e13487691917464ff513b8
parent f993443f32b94acf4f6ac5428acb66cf89c434c6
Author: Matsuda Kenji <info@mtkn.jp>
Date: Thu, 26 Oct 2023 11:28:26 +0900
add comments
Diffstat:
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 {