commit 3529506575eab96178d36dd4cd98d9b4bdb17bcd
parent 7e647df108e88e1dce78cb57c8830024d13158fb
Author: Matsuda Kenji <info@mtkn.jp>
Date: Fri, 29 Dec 2023 15:16:33 +0900
add documentation and gofmt
Diffstat:
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
}