@@ -14,6 +14,7 @@ import (
14
14
"time"
15
15
16
16
"aqwari.net/net/styx/internal/netutil"
17
+ "aqwari.net/net/styx/internal/styxfile"
17
18
"aqwari.net/net/styx/styxproto"
18
19
)
19
20
@@ -86,25 +87,48 @@ func (emptyFS) Serve9P(s *Session) {
86
87
switch req := s .Request ().(type ) {
87
88
case Tstat :
88
89
if req .Path () == "/" {
89
- req .Rstat (emptyDir (req .Path ()), nil )
90
+ req .Rstat (emptyDir { emptyStatDir (req .Path ())} , nil )
90
91
}
91
92
case Topen :
92
- req .Ropen (emptyDir (req .Path ()), nil )
93
+ req .Ropen (emptyDir { emptyStatDir (req .Path ())} , nil )
93
94
}
94
95
}
95
96
}
96
97
97
- type emptyDir string
98
+ type emptyStatFile string
99
+
100
+ // fs.FileInfo
101
+ func (s emptyStatFile ) Mode () os.FileMode { return 0222 }
102
+ func (s emptyStatFile ) IsDir () bool { return s .Mode ().IsDir () }
103
+ func (s emptyStatFile ) Name () string { return string (s ) }
104
+ func (s emptyStatFile ) Sys () interface {} { return nil }
105
+ func (s emptyStatFile ) Size () int64 { return 0 }
106
+ func (s emptyStatFile ) ModTime () time.Time { return time.Time {} }
107
+
108
+ type emptyStatDir string
109
+
110
+ // fs.FileInfo
111
+ func (s emptyStatDir ) Mode () os.FileMode { return 0222 | os .ModeDir }
112
+ func (s emptyStatDir ) IsDir () bool { return s .Mode ().IsDir () }
113
+ func (s emptyStatDir ) Name () string { return string (s ) }
114
+ func (s emptyStatDir ) Sys () interface {} { return nil }
115
+ func (s emptyStatDir ) Size () int64 { return 0 }
116
+ func (s emptyStatDir ) ModTime () time.Time { return time.Time {} }
117
+
118
+ type emptyFile struct { emptyStatFile }
119
+
120
+ var _ styxfile.Interface = emptyFile {}
121
+
122
+ func (f emptyFile ) ReadAt (p []byte , offset int64 ) (written int , err error ) { return 0 , io .EOF }
123
+
124
+ func (f emptyFile ) WriteAt (p []byte , offset int64 ) (int , error ) { return 0 , styxfile .ErrNotSupported }
125
+
126
+ func (f emptyFile ) Close () error { return nil }
127
+
128
+ type emptyDir struct { emptyStatDir }
129
+
130
+ var _ styxfile.Directory = emptyDir {}
98
131
99
- // os.FileInfo
100
- func (d emptyDir ) Mode () os.FileMode { return os .ModeDir }
101
- func (d emptyDir ) IsDir () bool { return d .Mode ().IsDir () }
102
- func (d emptyDir ) Name () string { return string (d ) }
103
- func (d emptyDir ) Sys () interface {} { return nil }
104
- func (d emptyDir ) Size () int64 { return 0 }
105
- func (d emptyDir ) ModTime () time.Time { return time.Time {} }
106
-
107
- // styx.Directory
108
132
func (d emptyDir ) Readdir (int ) ([]os.FileInfo , error ) { return nil , nil }
109
133
110
134
func chanServer (t * testing.T , handler Handler ) (in , out chan styxproto.Msg ) {
@@ -516,6 +540,70 @@ func TestWalkNonexistent(t *testing.T) {
516
540
})
517
541
}
518
542
543
+ func TestTcreate (t * testing.T ) {
544
+ srv := testServer {test : t }
545
+
546
+ type expectedstat struct {
547
+ name string
548
+ mode os.FileMode
549
+ }
550
+ fidnames := map [uint32 ]expectedstat {
551
+ 1 : {name : "dir" , mode : 0222 | os .ModeDir },
552
+ 2 : {name : "file" , mode : 0222 },
553
+ }
554
+
555
+ srv .callback = func (req , rsp styxproto.Msg ) {
556
+ if _ , ok := rsp .(styxproto.Rerror ); ok {
557
+ t .Errorf ("got %T response to %T" , rsp , req )
558
+ }
559
+ if req , ok := req .(styxproto.Tstat ); ok {
560
+ if rsp , ok := rsp .(styxproto.Rstat ); ! ok {
561
+ t .Errorf ("got %T response to %T" , rsp , req )
562
+ } else {
563
+ expected := fidnames [req .Fid ()]
564
+ name := string (rsp .Stat ().Name ())
565
+ if name != expected .name {
566
+ t .Errorf ("expected name to be %s, instead got %s" , expected .name , name )
567
+ }
568
+ // FIXME: For directories, the mode does not match
569
+ mode := styxfile .ModeOS (rsp .Stat ().Mode ())
570
+ if mode != expected .mode {
571
+ t .Errorf ("expected mode to be %s, instead got %s" , expected .mode , mode )
572
+ }
573
+ }
574
+ }
575
+ }
576
+ srv .handler = HandlerFunc (func (s * Session ) {
577
+ for s .Next () {
578
+ switch req := s .Request ().(type ) {
579
+ case Tcreate :
580
+ t .Logf ("Tcreate %s %s" , req .Path (), req .NewPath ())
581
+ var f any
582
+ if req .Mode .IsDir () {
583
+ f = emptyDir {emptyStatDir (req .Name )}
584
+ } else {
585
+ f = emptyFile {emptyStatFile (req .Name )}
586
+ }
587
+ req .Rcreate (f , nil )
588
+ case Twalk :
589
+ // Empty walks get automatically handled, no need to handle
590
+ case Tstat :
591
+ // Because Rcreate returns an opened file, Tstat is called on styxfile.Interface or styxfile.Directory,
592
+ // so it will use styxfile.Stat to get stat, no need to handle
593
+ }
594
+ }
595
+ })
596
+
597
+ srv .runMsg (func (enc * styxproto.Encoder ) {
598
+ enc .Twalk (1 , 0 , 1 )
599
+ enc .Tcreate (1 , 1 , "dir" , 0222 | styxproto .DMDIR , styxproto .DMREAD )
600
+ enc .Tstat (1 , 1 )
601
+ enc .Twalk (1 , 0 , 2 )
602
+ enc .Tcreate (1 , 2 , "file" , 0222 , styxproto .DMREAD )
603
+ enc .Tstat (1 , 2 )
604
+ })
605
+ }
606
+
519
607
func blankQid () styxproto.Qid {
520
608
buf := make ([]byte , styxproto .QidLen )
521
609
qid , _ , err := styxproto .NewQid (buf , 0 , 0 , 0 )
0 commit comments