commit 6e160f8c1271735c3023c09bdd433eae1e09e8a0
parent 3b765680088fc16e4fd3dbdcbb33e001cfe8a4db
Author: Matsuda Kenji <info@mtkn.jp>
Date: Sun, 30 Jul 2023 15:21:06 +0900
change FileInfo and stat
Diffstat:
| M | fcall.go | | | 125 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
| M | file.go | | | 227 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
| M | parse.go | | | 4 | ++++ |
| M | server.go | | | 73 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
4 files changed, 364 insertions(+), 65 deletions(-)
diff --git a/fcall.go b/fcall.go
@@ -598,8 +598,8 @@ func (msg *TOpen) String() string {
}
type ROpen struct {
- tag uint16
- qid *Qid
+ tag uint16
+ qid *Qid
iounit uint32
}
@@ -607,9 +607,9 @@ func newROpen(buf []byte) *ROpen { panic("not implemented") }
func (msg *ROpen) Size() uint32 {
return uint32(4 + 1 + 2 + 13 + 4)
}
-func (msg *ROpen) Type() MsgType { return Ropen }
-func (msg *ROpen) Tag() uint16 { return msg.tag }
-func (msg *ROpen) Qid() *Qid { return msg.qid }
+func (msg *ROpen) Type() MsgType { return Ropen }
+func (msg *ROpen) Tag() uint16 { return msg.tag }
+func (msg *ROpen) Qid() *Qid { return msg.qid }
func (msg *ROpen) IoUnit() uint32 { return msg.iounit }
func (msg *ROpen) conv2M() []byte {
cur := 0
@@ -637,6 +637,107 @@ func (msg *ROpen) String() string {
msg.Tag(), msg.Qid(), msg.IoUnit())
}
+type TRead struct {
+ size uint32
+ tag uint16
+ fid uint32
+ offset uint64
+ count uint32
+}
+
+func newTRead(buf []byte) *TRead {
+ cur := 0
+ msg := new(TRead)
+ msg.size = gbit32(buf[cur : cur+4])
+ cur += 4
+ cur += 1 // type
+ msg.tag = gbit16(buf[cur : cur+2])
+ cur += 2
+ msg.fid = gbit32(buf[cur : cur+4])
+ cur += 4
+ msg.offset = gbit64(buf[cur : cur+8])
+ cur += 8
+ msg.count = gbit32(buf[cur : cur+4])
+ cur += 4
+ if cur != len(buf) {
+ panic("length of buf and cursor position don't match")
+ }
+ return msg
+}
+func (msg *TRead) Size() uint32 { return msg.size }
+func (msg *TRead) Type() MsgType { return Tread }
+func (msg *TRead) Tag() uint16 { return msg.tag }
+func (msg *TRead) Fid() uint32 { return msg.fid }
+func (msg *TRead) Offset() uint64 { return msg.offset }
+func (msg *TRead) Count() uint32 { return msg.count }
+func (msg *TRead) conv2M() []byte {
+ cur := 0
+ buf := make([]byte, msg.Size())
+ pbit32(buf[cur:cur+4], msg.Size())
+ cur += 4
+ buf[cur] = uint8(Tread)
+ cur += 1
+ pbit16(buf[cur:cur+2], msg.Tag())
+ cur += 2
+ pbit32(buf[cur:cur+4], msg.Fid())
+ cur += 4
+ pbit64(buf[cur:cur+8], msg.Offset())
+ cur += 8
+ pbit32(buf[cur:cur+4], msg.Count())
+ cur += 4
+ if cur != len(buf) {
+ panic("length of buf and cursor position don't match")
+ }
+ return buf
+}
+func (msg *TRead) String() string {
+ return fmt.Sprintf("Tread tag %d fid %d offset %d count %d",
+ msg.Tag(), msg.Fid(), msg.Offset(), msg.Count())
+}
+
+type RRead struct {
+ tag uint16
+ count uint32
+ data []byte
+}
+
+func newRRead(buf []byte) *RRead { panic("not implemented") }
+func (msg *RRead) Size() uint32 {
+ return uint32(4 + 1 + 2 + 4 + msg.Count())
+}
+func (msg *RRead) Type() MsgType { return Rread }
+func (msg *RRead) Tag() uint16 { return msg.tag }
+func (msg *RRead) Count() uint32 { return msg.count }
+func (msg *RRead) Data() []byte { return msg.data }
+func (msg *RRead) conv2M() []byte {
+ if uint32(len(msg.Data())) != msg.Count() {
+ panic(fmt.Errorf("data size %d and count %d don't match",
+ len(msg.Data()), msg.Count()))
+ }
+ cur := 0
+ buf := make([]byte, msg.Size())
+ pbit32(buf[cur:cur+4], msg.Size())
+ cur += 4
+ buf[cur] = uint8(msg.Type())
+ cur += 1
+ pbit16(buf[cur:cur+2], msg.Tag())
+ cur += 2
+ pbit32(buf[cur:cur+4], msg.Count())
+ cur += 4
+ data := msg.Data()
+ for i := 0; i < len(data); i++ {
+ buf[cur+i] = data[i]
+ }
+ cur += len(data)
+ if cur != len(buf) {
+ panic("length of buf and cursor position don't match")
+ }
+ return buf
+}
+func (msg *RRead) String() string {
+ return fmt.Sprintf("Rread tag %d count %d data %v",
+ msg.Tag(), msg.Count(), msg.Data())
+}
type TStat struct {
size uint32
@@ -670,11 +771,11 @@ func (msg *TStat) String() string {
type RStat struct {
tag uint16
- stat *stat
+ info *FileInfo
}
func newRStat(buf []byte) *RStat { panic("not implemented") }
-func (msg *RStat) Size() uint32 { return uint32(4 + 1 + 2 + 2 + 2 + msg.stat.size()) } // TODO: collect ?
+func (msg *RStat) Size() uint32 { return uint32(4 + 1 + 2 + 2 + 2 + msg.info.statSize()) } // TODO: collect ?
func (msg *RStat) Type() MsgType { return Rstat }
func (msg *RStat) Tag() uint16 { return msg.tag }
func (msg *RStat) conv2M() []byte {
@@ -682,12 +783,12 @@ func (msg *RStat) conv2M() []byte {
pbit32(buf[0:4], msg.Size())
buf[4] = uint8(Rstat)
pbit16(buf[5:7], msg.Tag())
- statBuf := msg.stat.conv2M()
- pbit16(buf[7:9], uint16(len(statBuf)))
- for i := 0; i < len(statBuf); i++ {
- buf[9+i] = statBuf[i]
+ fiBuf := msg.info.conv2M()
+ pbit16(buf[7:9], uint16(len(fiBuf)))
+ for i := 0; i < len(fiBuf); i++ {
+ buf[9+i] = fiBuf[i]
}
return buf
}
-func (msg *RStat) String() string { return fmt.Sprintf("Rstat tag %d stat %s", msg.Tag(), msg.stat) }
+func (msg *RStat) String() string { return fmt.Sprintf("Rstat tag %d stat %s", msg.Tag(), msg.info) }
diff --git a/file.go b/file.go
@@ -2,6 +2,7 @@ package lib9p
import (
"fmt"
+ "io"
"io/fs"
"time"
)
@@ -24,19 +25,18 @@ func hasPerm(f *File, uid string, p fs.FileMode) (bool, error) {
if err != nil {
return false, fmt.Errorf("stat: %v", err)
}
- st := (*stat)(fi)
fp := fi.Mode().Perm()
m := fp & 7 // other
if (p & m) == p {
return true, nil
}
- if st.uid == uid {
+ if fi.statUid() == uid {
m |= (fp >> 6) & 7
if (p & m) == p {
return true, nil
}
}
- if st.gid == uid {
+ if fi.statGid() == uid {
m |= (fp >> 3) & 7
if (p & m) == p {
return true, nil
@@ -130,65 +130,203 @@ func (s *stat) String() string {
s.aTime.Unix(), s.mTime.Unix(), s.length, s.t, s.dev)
}
-type FileInfo stat
+type FileInfo struct {
+ info fs.FileInfo
+ qid *Qid
+ /*
+ t uint16
+ dev uint32
+ qid *Qid
+ mode fs.FileMode
+ aTime time.Time
+ mTime time.Time
+ length int64
+ name string
+ uid string
+ gid string
+ muid string
+ */
+}
-func (fi *FileInfo) Name() string { return (*stat)(fi).name }
-func (fi *FileInfo) Size() int64 { return int64((*stat)(fi).length) }
-func (fi *FileInfo) Mode() fs.FileMode {
- var mode fs.FileMode
- smode := (*stat)(fi).mode
- if smode&DMDIR != 0 {
- mode |= fs.ModeDir
- }
- if smode&DMAPPEND != 0 {
- mode |= fs.ModeAppend
- }
- if smode&DMEXCL != 0 {
- mode |= fs.ModeExclusive
- }
- if smode&DMTMP != 0 {
- mode |= fs.ModeTemporary
- }
- // Permission bits are fixed according to godoc of fs.FileMode
- mode |= fs.FileMode(smode & 0777)
- return mode
+func (fi *FileInfo) Name() string { return fi.info.Name() }
+func (fi *FileInfo) Size() int64 { return fi.info.Size() }
+func (fi *FileInfo) Mode() fs.FileMode { return fi.info.Mode() }
+func (fi *FileInfo) ModTime() time.Time { return fi.info.ModTime() }
+func (fi *FileInfo) IsDir() bool { return fi.info.IsDir() }
+func (fi *FileInfo) Sys() any { return fi }
+
+func (fi *FileInfo) statSize() uint16 {
+ return uint16(2 + 4 + 13 + 4 + 4 + 4 + 8 +
+ 2 + len(fi.statName()) + 2 + len(fi.statUid()) +
+ 2 + len(fi.statGid()) + 2 + len(fi.statMuid()))
+}
+func (fi *FileInfo) statType() uint16 { return 0 }
+func (fi *FileInfo) statDev() uint32 { return 0 }
+func (fi *FileInfo) statQid() *Qid { return fi.qid }
+func (fi *FileInfo) statMode() uint32 { return DMDIR|0777 }
+func (fi *FileInfo) statATime() uint32 { return uint32(fi.ModTime().Unix()) }
+func (fi *FileInfo) statMTime() uint32 { return uint32(fi.ModTime().Unix()) }
+func (fi *FileInfo) statLength() uint64 {
+ if fi.IsDir() {
+ return 0
+ } else {
+ return uint64(fi.Size())
+ }
+}
+func (fi *FileInfo) statName() string { return fi.Name() } // TODO: "/" for root
+func (fi *FileInfo) statUid() string { return "" }
+func (fi *FileInfo) statGid() string { return "" }
+func (fi *FileInfo) statMuid() string { return "" }
+func (fi *FileInfo) conv2M() []byte {
+ cur := 0
+ buf := make([]byte, 2 + fi.statSize())
+ pbit16(buf[cur:cur+2], fi.statSize())
+ cur += 2
+ pbit16(buf[cur:cur+2], fi.statType())
+ cur += 2
+ pbit32(buf[cur:cur+4], fi.statDev())
+ cur += 4
+ qidBuf := fi.statQid().conv2M()
+ for i := 0; i < len(qidBuf); i++ {
+ buf[cur+i] = qidBuf[i]
+ }
+ cur += len(qidBuf)
+ pbit32(buf[cur:cur+4], fi.statMode())
+ cur += 4
+ pbit32(buf[cur:cur+4], fi.statATime())
+ cur += 4
+ pbit32(buf[cur:cur+4], fi.statMTime())
+ cur += 4
+ pbit64(buf[cur:cur+8], fi.statLength())
+ cur += 8
+ name := fi.statName()
+ pbit16(buf[cur:cur+2], uint16(len(name)))
+ cur += 2
+ for i := 0; i < len(name); i++ {
+ buf[cur+i] = name[i]
+ }
+ cur += len(name)
+ uid := fi.statUid()
+ pbit16(buf[cur:cur+2], uint16(len(uid)))
+ cur += 2
+ for i := 0; i < len(uid); i++ {
+ buf[cur+i] = uid[i]
+ }
+ cur += len(uid)
+ gid := fi.statGid()
+ pbit16(buf[cur:cur+2], uint16(len(gid)))
+ cur += 2
+ for i := 0; i < len(gid); i++ {
+ buf[cur+i] = gid[i]
+ }
+ cur += len(gid)
+ muid := fi.statMuid()
+ pbit16(buf[cur:cur+2], uint16(len(muid)))
+ cur += 2
+ for i := 0; i < len(muid); i++ {
+ buf[cur+i] = muid[i]
+ }
+ cur += len(muid)
+ if len(buf) != cur {
+ panic(fmt.Errorf("cursor position %d and buf length %d don't match",
+ cur, len(buf)))
+ }
+ return buf
+}
+func (fi *FileInfo) String() string {
+ return fmt.Sprintf("'%s' '%s' '%s' '%s' q %v m %#o at %d mt %d l %d t %d d %d",
+ fi.statName(), fi.statUid(), fi.statGid(), fi.statMuid(),
+ fi.statQid(), fi.statMode(), fi.statATime(), fi.statMTime(),
+ fi.statLength(), fi.statType(), fi.statDev())
}
-func (fi *FileInfo) ModTime() time.Time { return (*stat)(fi).mTime }
-func (fi *FileInfo) IsDir() bool { return (*stat)(fi).mode&DMDIR != 0 }
-func (fi *FileInfo) Sys() any { return (*stat)(fi) }
type File struct {
+ fs *FS // file system to which this file belongs
file fs.File // underlying file
qid *Qid
}
func (f *File) Stat() (*FileInfo, error) {
- fi, err := f.file.Stat()
+ fsfi, err := f.file.Stat()
if err != nil {
return nil, fmt.Errorf("stat file: %v, %v", f, err)
}
- var size int64
- if fi.IsDir() {
- size = 0
- } else {
- size = fi.Size()
- }
- st := &stat{
- qid: f.qid,
- mode: fi.Mode(), // TODO: convert mode from fs's to 9p
- aTime: fi.ModTime(),
- mTime: fi.ModTime(),
- length: size,
- name: fi.Name(),
- // TODO: size, t, dev, uid, gid, muid
- }
- return (*FileInfo)(st), nil
+ fi := &FileInfo{
+ info: fsfi,
+ qid: f.qid,
+ // TODO: t, dev, uid, gid, muid
+ }
+ return fi, nil
}
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, ok := f.file.(io.ReaderAt); ok {
+ return f.ReadAt(p, off)
+ }
+ file := f.file
+
+ buf := make([]byte, 8*1024) // TODO: set appropreate size
+ var n int64
+ for n+int64(len(buf)) < off {
+ m, err := file.Read(buf)
+ if err != nil {
+ return 0, err
+ }
+ n += int64(m)
+ }
+ m, err := file.Read(buf[:off-n])
+ if err != nil {
+ return 0, err
+ }
+ n += int64(m)
+ if n != off {
+ panic("wrong offset")
+ }
+ return file.Read(p)
+}
+
+func (f *File) ReadDir(n int) ([]*DirEntry, error) {
+ fi, err := f.file.Stat()
+ if err != nil {
+ return nil, fmt.Errorf("stat: %v", err)
+ }
+ dir, ok := f.file.(fs.ReadDirFile)
+ if !ok || !fi.IsDir() {
+ return nil, fmt.Errorf("not a directory")
+ }
+ fsde, err := dir.ReadDir(n)
+ if err != nil {
+ return nil, err
+ }
+ de := make([]*DirEntry, len(fsde))
+ for i := 0; i < len(de); i++ {
+ fsinfo, err := fsde[i].Info()
+ if err != nil {
+ return nil, fmt.Errorf("stat: %v", err)
+ }
+ de[i] = &DirEntry{
+ dirEnt: fsde[i],
+ info: &FileInfo{fsinfo, &Qid{path:f.fs.nextQid}}, // TODO: need mutex?
+ }
+ f.fs.nextQid++
+ }
+ return de, nil
+}
+
+type DirEntry struct {
+ dirEnt fs.DirEntry // underlying fs.DirEntry
+ info *FileInfo
+}
+
+func (e *DirEntry) Name() string { return e.dirEnt.Name() }
+func (e *DirEntry) IsDir() bool { return e.dirEnt.IsDir() }
+func (e *DirEntry) Type() fs.FileMode { return e.dirEnt.Type() }
+func (e *DirEntry) Info() (*FileInfo, error) { return e.info, nil }
+
func (f *File) Close() error {
return f.file.Close()
}
@@ -231,6 +369,7 @@ func (fsys *FS) Open(name string) (*File, error) {
fsys.nextQid++
file := &File{
+ fs: fsys,
file: f,
qid: qid,
}
diff --git a/parse.go b/parse.go
@@ -54,6 +54,10 @@ func unmarshal(buf []byte) (Msg, error) {
return newRWalk(buf), nil
case Topen:
return newTOpen(buf), nil
+ case Tread:
+ return newTRead(buf), nil
+ case Rread:
+ return newRRead(buf), nil
case Tstat:
return newTStat(buf), nil
case Rstat:
diff --git a/server.go b/server.go
@@ -230,10 +230,10 @@ func sOpen(s *Server, r *Req) {
case OEXEC:
p = AEXEC
}
- if ifcall.Mode() & OTRUNC != 0 {
+ if ifcall.Mode()&OTRUNC != 0 {
p |= AWRITE
}
- if fidStruct.Qid.t & QTDIR != 0 && p != AREAD {
+ if fidStruct.Qid.t&QTDIR != 0 && p != AREAD {
respond(r, fmt.Errorf("permission denied"))
return
}
@@ -249,19 +249,70 @@ func sOpen(s *Server, r *Req) {
return
}
- if ifcall.Mode() & ORCLOSE != 0 {
+ if ifcall.Mode()&ORCLOSE != 0 {
panic("ORCLOSE not implemented")
}
fidStruct.OMode = ifcall.Mode()
r.ofcall = &ROpen{
- qid: fidStruct.Qid,
+ qid: fidStruct.Qid,
iounit: s.MSize - 23,
}
respond(r, nil)
}
func rOpen(r *Req, err error) {}
+func sRead(s *Server, r *Req) {
+ ifcall, ok := r.ifcall.(*TRead)
+ if !ok {
+ panic("not TRead")
+ }
+
+ fid, ok := s.fPool.lookupFid(ifcall.Fid())
+ if !ok {
+ respond(r, fmt.Errorf("unknown fid"))
+ return
+ }
+ if fid.OMode == -1 {
+ respond(r, fmt.Errorf("not open"))
+ return
+ }
+ /*
+ if fid.OMode|OREAD == 0 || fid.OMode|ORDWR == 0 {
+ log.Printf("permission: %o\n", fid.OMode)
+ respond(r, fmt.Errorf("unko permission denied"))
+ return
+ }
+ */
+ data := make([]byte, ifcall.Count())
+ var n int
+ var err error
+ fi, err := fid.File.Stat()
+ if err != nil {
+ log.Printf("stat: %v")
+ respond(r, fmt.Errorf("internal error"))
+ return
+ }
+ if fi.IsDir() {
+
+ } else {
+ n, err = fid.File.ReadAt(data, int64(ifcall.Offset()))
+ if err != nil {
+ respond(r, err)
+ return
+ }
+ }
+
+ ofcall := &RRead{
+ tag: ifcall.Tag(),
+ count: uint32(n),
+ data: data[:n],
+ }
+ r.ofcall = ofcall
+ respond(r, nil)
+}
+func rRead(r *Req, err error) {}
+
func rError(r *Req, err error) {
ofcall := &RError{
tag: r.ifcall.Tag(),
@@ -290,7 +341,7 @@ func sStat(s *Server, r *Req) {
ofcall := &RStat{
tag: ifcall.Tag(),
- stat: (*stat)(fileInfo),
+ info: fileInfo,
}
r.ofcall = ofcall
@@ -302,13 +353,13 @@ func rStat(r *Req, err error) {}
func (s *Server) Serve() {
for {
r, err := s.getReq()
- if r == nil {
- log.Printf("getReq returns nil request: %v", err)
- break
- }
if err == io.EOF {
log.Printf("getReq: %v\n", err)
break
+ }
+ if r == nil {
+ log.Printf("getReq returns nil request: %v", err)
+ break
} else if err != nil {
log.Printf("getReq: %v\n", err)
respond(r, err)
@@ -327,6 +378,8 @@ func (s *Server) Serve() {
sWalk(s, r)
case *TOpen:
sOpen(s, r)
+ case *TRead:
+ sRead(s, r)
case *TStat:
sStat(s, r)
}
@@ -360,6 +413,8 @@ func respond(r *Req, err error) {
rWalk(r, err)
case *ROpen:
rOpen(r, err)
+ case *RRead:
+ rRead(r, err)
case *RStat:
rStat(r, err)
}