lib9p

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

commit 1cf5e4d64da123409ed0a48d08e62d285692c9a4
parent 5d8df008aeeaf963b0f5ab9c77c7ec8dc96b8d04
Author: Matsuda Kenji <info@mtkn.jp>
Date:   Wed, 25 Oct 2023 06:59:14 +0900

add comments

Diffstat:
Mdiskfs/file.go | 14+++++++-------
Adiskfs/fs.go | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdiskfs/qid_unix.go | 25+++++++++++++++++++++----
Mdiskfs/stat_unix.go | 10++++++----
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)