commit 50f4afc20fb2da188312238ae02e0219bde7d4cc
parent 7b859127166c4c370fb721ac8d88d81b22486bc9
Author: Matsuda Kenji <info@mtkn.jp>
Date: Wed, 8 Nov 2023 09:55:04 +0900
add plan9 files
Diffstat:
2 files changed, 138 insertions(+), 0 deletions(-)
diff --git a/diskfs/qid_plan9.go b/diskfs/qid_plan9.go
@@ -0,0 +1,18 @@
+//go:build plan9
+package diskfs
+
+import (
+ "io/fs"
+)
+
+type fileID struct{}
+
+func idFromInfo(fi fs.FileInfo) fileID { return fileID{} }
+
+// QidPool is the list of Qids in the file system.
+type QidPool struct {}
+
+// newQidPool allocates a QidPool.
+func newQidPool() *QidPool {
+ return &QidPool{}
+}
diff --git a/diskfs/stat_plan9.go b/diskfs/stat_plan9.go
@@ -0,0 +1,120 @@
+//go:build plan9
+package diskfs
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "os/user"
+ "path"
+ "strconv"
+ "syscall"
+ "time"
+
+ "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 {
+ dir := info.Sys().(*syscall.Dir)
+ qid := lib9p.Qid{
+ Type: lib9p.QidType(dir.Qid.Type),
+ Vers: dir.Qid.Vers,
+ Path: dir.Qid.Path,
+ }
+ return &lib9p.Stat{
+ Type: dir.Type,
+ Dev: dir.Dev,
+ Qid: qid,
+ Mode: info.Mode(),
+ Atime: dir.Atime,
+ Mtime: dir.Mtime,
+ Length: dir.Length,
+ Name: dir.Name,
+ Uid: dir.Uid,
+ Gid: dir.Gid,
+ Muid: dir.Muid,
+ }
+}
+
+// Stat is real implementation of File.Stat.
+func (f *File) stat() (*lib9p.FileInfo, error) {
+ fi, err := f.file.Stat()
+ if err != nil {
+ return nil, fmt.Errorf("stat: %v", err)
+ }
+ return &lib9p.FileInfo{Stat: *fiStat(nil, fileID{}, fi)}, 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)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ return fmt.Errorf("stat: %v", err)
+ }
+
+ oldStat := fi.Sys().(*lib9p.Stat)
+
+ if s.Name != oldStat.Name {
+ // TODO: check neither Names contains "/"
+ oldpath := path.Join(f.fs.rootPath, path.Dir(f.path), oldStat.Name)
+ newpath := path.Join(f.fs.rootPath, path.Dir(f.path), s.Name)
+ if err := os.Rename(oldpath, newpath); err != nil {
+ return fmt.Errorf("rename: %v", err)
+ }
+ }
+ if s.Length != oldStat.Length {
+ if err := file.Truncate(s.Length); err != nil {
+ return fmt.Errorf("truncate: %v", err)
+ }
+ ret, err := file.Seek(0, 0)
+ if err != nil {
+ return fmt.Errorf("seek 0: %d, %w", ret, err)
+ }
+ }
+ if s.Mode != oldStat.Mode {
+ if err := file.Chmod(s.Mode); err != nil {
+ return fmt.Errorf("chmod: %v", err)
+ }
+ }
+ if s.Mtime != oldStat.Mtime {
+ err := os.Chtimes(f.path, time.Time{}, time.Unix(int64(s.Mtime), 0))
+ if err != nil {
+ return fmt.Errorf("chtimes: %v", err)
+ }
+ }
+
+ if s.Gid != oldStat.Gid {
+ return fmt.Errorf("not implemented in plan9")
+ group, err := user.LookupGroup(s.Gid)
+ if err != nil {
+ return fmt.Errorf("lookupgroup: %v", err)
+ }
+ usr, err := user.Lookup(oldStat.Uid)
+ if err != nil {
+ return fmt.Errorf("lookup user: %v", err)
+ }
+ u, err := strconv.Atoi(usr.Uid)
+ if err != nil {
+ return fmt.Errorf("convert uid: %v", err)
+ }
+ g, err := strconv.Atoi(group.Gid)
+ if err != nil {
+ return fmt.Errorf("convert gid: %v", err)
+ }
+
+ if err := file.Chown(u, g); err != nil {
+ return fmt.Errorf("chown: %v", err)
+ }
+ }
+
+ return nil
+}