commit 1cf5e4d64da123409ed0a48d08e62d285692c9a4
parent 5d8df008aeeaf963b0f5ab9c77c7ec8dc96b8d04
Author: Matsuda Kenji <info@mtkn.jp>
Date: Wed, 25 Oct 2023 06:59:14 +0900
add comments
Diffstat:
4 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/diskfs/file.go b/diskfs/file.go
@@ -103,6 +103,13 @@ func (f *File) Read(b []byte) (int, error) {
return f.file.Read(b)
}
+func (f *File) ReadAt(p []byte, off int64) (int, error) {
+ if f.file == nil {
+ return 0, fmt.Errorf("not open")
+ }
+ return f.file.ReadAt(p, off)
+}
+
// ReadDir reads the contents of the directory and returns
// a slice of up to n *lib9p.DirEntry values in directory order.
// Subsequent calls on the same file will yield further DirEntry values.
@@ -135,13 +142,6 @@ func (f *File) ReadDir(n int) ([]*lib9p.DirEntry, error) {
return de, nil
}
-func (f *File) ReadAt(p []byte, off int64) (int, error) {
- if f.file == nil {
- return 0, fmt.Errorf("not open")
- }
- return f.file.ReadAt(p, off)
-}
-
func (f *File) WriteAt(p []byte, off int64) (int, error) {
if f.file == nil {
return 0, fmt.Errorf("not open")
diff --git a/diskfs/fs.go b/diskfs/fs.go
@@ -0,0 +1,65 @@
+// Package diskfs provides a glue layer between OS's file system and
+// 9P file system. It can be used to export OS's file tree via 9P protocol.
+package diskfs
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+
+ "git.mtkn.jp/lib9p"
+)
+
+// FS is a file system to export OS's file system via 9P protocol.
+type FS struct {
+ rootPath string
+ qidPool *QidPool
+}
+
+// Open opens OS's file tree as *FS rooted at name.
+func Open(name string) (*FS, error) {
+ f, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ if err := f.Close(); err != nil {
+ panic(err)
+ }
+ root := &File{
+ path: ".",
+ }
+ fsys := &FS{
+ rootPath: name,
+ qidPool: newQidPool(),
+ }
+ root.fs = fsys
+ return fsys, nil
+}
+
+// OpenFile opens the named file with specified omode by calling os.OpenFile.
+// This signature is derived from os.OpenFile(), but creating the specified file
+// is not implemented yet and therefore, perm is not used.
+func (fsys *FS) OpenFile(name string, omode lib9p.OpenMode, perm fs.FileMode) (lib9p.File, error) {
+ fp := filepath.Join(fsys.rootPath, name)
+ var m int
+ switch omode & 3 {
+ case lib9p.OREAD:
+ m = os.O_RDONLY
+ case lib9p.OWRITE:
+ m = os.O_WRONLY
+ case lib9p.ORDWR:
+ m = os.O_RDWR
+ }
+ if omode&lib9p.OTRUNC != 0 {
+ m |= os.O_TRUNC
+ }
+ if omode&lib9p.ORCLOSE != 0 {
+ return nil, fmt.Errorf("orclose not implemented")
+ }
+ osf, err := os.OpenFile(fp, m, perm)
+ if err != nil {
+ return nil, err
+ }
+ return &File{fs: fsys, path: name, file: osf}, nil
+}
diff --git a/diskfs/qid_unix.go b/diskfs/qid_unix.go
@@ -11,21 +11,27 @@ import (
"git.mtkn.jp/lib9p"
)
+// fileID is the identifier of a unix file.
+// It is used to assosiate the file with Qid.
type fileID struct {
device uint64
inode uint64
}
+// qidReq is used to update the Qid.Vers by checking the file's
+// modified time.
type qidRec struct {
qid *lib9p.Qid
mtime time.Time
}
+// QidPool is the list of Qids in the file system.
type QidPool struct {
m map[fileID]*qidRec
nextQid uint64
}
+// id derives the fileID from the file.
func (f *File) id() (fileID, error) {
ospath := filepath.Join(f.fs.rootPath, f.path)
fi, err := os.Stat(ospath)
@@ -40,17 +46,22 @@ func (f *File) id() (fileID, error) {
return id, nil
}
+// idFromInfo derives the fileID from the fs.FileInfo.
func idFromInfo(info fs.FileInfo) fileID {
stat := info.Sys().(*syscall.Stat_t)
return fileID{device: stat.Dev, inode: stat.Ino}
}
-func allocQidPool() *QidPool {
+// newQidPool allocates a QidPool.
+func newQidPool() *QidPool {
return &QidPool{
m: make(map[fileID]*qidRec),
}
}
+// lookup looks up the file in the QidPool.
+// If found, it returns the corresponding Qid and true.
+// If not found, it returns nil and false.
func (pool *QidPool) lookup(f *File) (lib9p.Qid, bool) {
id, err := f.id()
if err != nil {
@@ -64,6 +75,9 @@ func (pool *QidPool) lookup(f *File) (lib9p.Qid, bool) {
return pool.lookupID(id, fsfi.ModTime())
}
+// lookupID looksup the file in the QidPool by fileID.
+// It also checks if the file is modified after the last access by
+// comparing the qidReq.mtime and mtime, and update Qid.Vers if needed.
func (pool *QidPool) lookupID(id fileID, mtime time.Time) (lib9p.Qid, bool) {
qrec, ok := pool.m[id]
if !ok {
@@ -76,7 +90,8 @@ func (pool *QidPool) lookupID(id fileID, mtime time.Time) (lib9p.Qid, bool) {
return *qrec.qid, ok
}
-func (pool *QidPool) alloc(f *File) (lib9p.Qid, error) {
+// add adds the file's Qid to the QidPool and returns the newly added Qid.
+func (pool *QidPool) add(f *File) (lib9p.Qid, error) {
id, err := f.id()
if err != nil {
return lib9p.Qid{}, fmt.Errorf("get id: %v", err)
@@ -86,10 +101,11 @@ func (pool *QidPool) alloc(f *File) (lib9p.Qid, error) {
if err != nil {
return lib9p.Qid{}, fmt.Errorf("stat %v: %v", f, err)
}
- return pool.allocID(id, fi)
+ return pool.addID(id, fi)
}
-func (pool *QidPool) allocID(id fileID, info fs.FileInfo) (lib9p.Qid, error) {
+// addID does the same as add, but by fileID.
+func (pool *QidPool) addID(id fileID, info fs.FileInfo) (lib9p.Qid, error) {
qtype := lib9p.FSModeToQidType(info.Mode())
qid := &lib9p.Qid{
Path: pool.nextQid,
@@ -100,6 +116,7 @@ func (pool *QidPool) allocID(id fileID, info fs.FileInfo) (lib9p.Qid, error) {
return *qid, nil
}
+// delete deletes Qid associated with f from the QidPool.
func (pool *QidPool) delete(f *File) {
id, err := f.id()
if err != nil {
diff --git a/diskfs/stat_unix.go b/diskfs/stat_unix.go
@@ -14,11 +14,13 @@ import (
"git.mtkn.jp/lib9p"
)
+// fiStat generates lib9p.Stat from the FileInfo.
+// It requires QidPool and fileID to fill the Qid field of Stat.
func fiStat(pool *QidPool, id fileID, info fs.FileInfo) *lib9p.Stat {
qid, ok := pool.lookupID(id, info.ModTime())
if !ok {
var err error
- qid, err = pool.allocID(id, info)
+ qid, err = pool.addID(id, info)
if err != nil {
panic(fmt.Errorf("allocID: %v", err))
}
@@ -49,7 +51,7 @@ func fiStat(pool *QidPool, id fileID, info fs.FileInfo) *lib9p.Stat {
}
}
-// Stat is real implementation of Stat.
+// Stat is real implementation of File.Stat.
func (f *File) stat() (*lib9p.FileInfo, error) {
ospath := filepath.Join(f.fs.rootPath, f.path)
fsfi, err := os.Stat(ospath)
@@ -61,7 +63,7 @@ func (f *File) stat() (*lib9p.FileInfo, error) {
stat.Dev = 0
qid, ok := f.fs.qidPool.lookup(f)
if !ok {
- qid, err = f.fs.qidPool.alloc(f)
+ qid, err = f.fs.qidPool.add(f)
if err != nil {
panic(fmt.Errorf("alloc qid: %v", err))
}
@@ -89,7 +91,7 @@ func (f *File) stat() (*lib9p.FileInfo, error) {
return &lib9p.FileInfo{stat}, nil
}
-
+// wstat is the real implementation of File.WStat.
// TODO: when error occurs, file stat should be restored.
func (f *File) wstat(s *lib9p.Stat) error {
file, err := os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0)