fs.go (3742B)
1 package main 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "io/fs" 8 "strconv" 9 "strings" 10 "time" 11 12 "git.mtkn.jp/lib9p" 13 ) 14 15 type semFS struct { 16 semfiles map[int]*semFile 17 lastpath int 18 } 19 20 func (fsys *semFS) OpenFile(name string, _ int) (lib9p.File, error) { 21 wnames := strings.Split(name, "/") 22 if len(wnames) != 1 { 23 return nil, errors.New("not found") 24 } 25 switch wnames[0] { 26 case "", ".", "/": 27 return fsys, nil 28 default: 29 for _, f := range fsys.semfiles { 30 if f == nil { 31 continue 32 } 33 if f.name == wnames[0] { 34 return f, nil 35 } 36 } 37 return nil, errors.New("not found") 38 } 39 } 40 41 func (fsys *semFS) Create(name string, uid string, mode lib9p.OpenMode, perm lib9p.FileMode) (lib9p.File, error) { 42 for _, f := range fsys.semfiles { 43 if f.name == name { 44 return nil, errors.New("file already exists") 45 } 46 } 47 ctx, cancel := context.WithCancel(context.Background()) 48 newfile := &semFile{ 49 fs: fsys, 50 name: name, 51 sem: 0, 52 path: uint64(fsys.lastpath + 1), 53 cancel: cancel, 54 } 55 newfile.start(ctx) 56 fsys.semfiles[fsys.lastpath+1] = newfile 57 fsys.lastpath++ 58 return newfile, nil 59 } 60 61 func (fsys *semFS) Remove(name string) error { 62 var ( 63 f *semFile 64 path int 65 ) 66 for i, ff := range fsys.semfiles { 67 if ff.name == name { 68 f = ff 69 path = i 70 break 71 } 72 } 73 if f == nil { 74 return errors.New("not found") 75 } 76 f.cancel() 77 close(f.rchan) 78 close(f.wchan) 79 delete(fsys.semfiles, path) 80 return nil 81 } 82 83 func (root *semFS) Stat() (*lib9p.FileInfo, error) { 84 t := uint32(time.Now().Unix()) 85 return &lib9p.FileInfo{Stat: lib9p.Stat{ 86 Qid: lib9p.Qid{Type: lib9p.QTDIR, Vers: 0, Path: 0}, 87 Mode: fs.ModeDir | 0777, 88 Atime: t, 89 Mtime: t, 90 Length: 0, 91 Name: ".", 92 }}, nil 93 } 94 95 func (root *semFS) Close() error { return nil } 96 97 func (root *semFS) Read(p []byte) (n int, err error) { 98 return 0, errors.New("is a directory") 99 } 100 101 // TODO: set diroffset. 102 func (root *semFS) ReadDir(n int) ([]fs.DirEntry, error) { 103 de := make([]fs.DirEntry, 0, len(root.semfiles)) 104 for _, f := range root.semfiles { 105 if f == nil { 106 continue 107 } 108 fi, err := f.Stat() 109 if err != nil { 110 return nil, err 111 } 112 de = append(de, fi) 113 } 114 return de, nil 115 } 116 117 type semFile struct { 118 fs *semFS 119 name string 120 sem int 121 path uint64 122 offset int 123 rchan chan<- chan struct{} 124 wchan chan<- int 125 queue []chan struct{} 126 cancel context.CancelFunc 127 } 128 129 func (f *semFile) start(ctx context.Context) { 130 rchan := make(chan chan struct{}) 131 wchan := make(chan int) 132 go func(rchan <-chan chan struct{}, wchan <-chan int) { 133 for { 134 select { 135 case <-ctx.Done(): 136 for _, c := range f.queue { 137 close(c) 138 } 139 return 140 case c := <-rchan: 141 if c == nil { 142 return 143 } 144 if f.sem > 0 { 145 f.sem-- 146 close(c) 147 } else { 148 f.queue = append(f.queue, c) 149 } 150 case n := <-wchan: 151 f.sem += n 152 for len(f.queue) > 0 && f.sem > 0 { 153 close(f.queue[0]) 154 f.queue = f.queue[1:] 155 f.sem-- 156 } 157 } 158 } 159 }(rchan, wchan) 160 f.rchan = rchan 161 f.wchan = wchan 162 } 163 164 func (f *semFile) Stat() (*lib9p.FileInfo, error) { 165 t := uint32(time.Now().Unix()) 166 return &lib9p.FileInfo{Stat: lib9p.Stat{ 167 Qid: lib9p.Qid{Type: lib9p.QTFILE, Vers: 0, Path: f.path}, 168 Mode: 0666, 169 Atime: t, 170 Mtime: t, 171 Length: int64(f.sem), 172 Name: f.name, 173 }}, nil 174 } 175 176 func (f *semFile) WStat(stat *lib9p.Stat) error { return nil } 177 178 func (f *semFile) Close() error { 179 return nil 180 } 181 182 func (f *semFile) Read(p []byte) (n int, err error) { 183 if len(p) > 0 { 184 c := make(chan struct{}) 185 f.rchan <- c 186 <-c 187 return 0, io.EOF 188 } 189 return 0, nil 190 } 191 192 func (f *semFile) Write(p []byte) (int, error) { 193 n, err := strconv.Atoi(strings.Split(string(p), "\n")[0]) 194 if err != nil { 195 return 0, errors.New("invalid sem") 196 } 197 f.wchan <- n 198 return len(p), nil 199 }