fs.go (5381B)
1 package testfs 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/fs" 7 "strings" 8 9 "git.mtkn.jp/lib9p" 10 ) 11 12 type File struct { 13 // Fsys is the FS this File belongs to. 14 Fsys *FS 15 // Parent is the parent directory this File located at. 16 Parent *File 17 // Children is the child files this File has. 18 // It is nil if this File is not a directory 19 Children []*File 20 // Content is the content of this File. 21 // It is nil if this File is a directory 22 Content []byte 23 // Reader is used if this File is not a directory and 24 // when this File is read. 25 // It contains Content. 26 Reader *bytes.Reader 27 // St is the Stat of this File. 28 St lib9p.Stat 29 } 30 31 func (f *File) Stat() (*lib9p.FileInfo, error) { 32 return &lib9p.FileInfo{Stat: f.St}, nil 33 } 34 35 func (f *File) Close() error { 36 f.Reader = nil 37 return nil 38 } 39 40 func (f *File) Read(b []byte) (int, error) { 41 if f.Fsys.Slow { 42 f.Fsys.Waitc <- struct{}{} 43 <-f.Fsys.Waitc 44 } 45 return f.Reader.Read(b) 46 } 47 48 func (f *File) ReadAt(b []byte, off int64) (n int, err error) { 49 if f.Fsys.Slow { 50 f.Fsys.Waitc <- struct{}{} 51 <-f.Fsys.Waitc 52 } 53 return f.Reader.ReadAt(b, off) 54 } 55 56 func (f *File) ReadDir(n int) ([]fs.DirEntry, error) { 57 de := make([]fs.DirEntry, len(f.Children)) 58 for i, c := range f.Children { 59 info, _ := c.Stat() 60 de[i] = info 61 } 62 return de, nil 63 } 64 65 func (f *File) WriteAt(p []byte, off int64) (int, error) { 66 if f.Fsys.Slow { 67 f.Fsys.Waitc <- struct{}{} 68 } 69 if f.Reader == nil { 70 return 0, fmt.Errorf("not open") 71 } 72 if off < 0 || off > int64(len(f.Content)) { 73 return 0, fmt.Errorf("bad offset") 74 } 75 if off+int64(len(p)) > int64(len(f.Content)) { 76 newcon := make([]byte, off+int64(len(p))) 77 copy(newcon, f.Content) 78 f.Content = newcon 79 } 80 copy(f.Content[off:], p) 81 f.Reader.Reset(f.Content) 82 if f.Fsys.Slow { 83 <-f.Fsys.Waitc 84 } 85 return len(p), nil 86 } 87 88 type FS struct { 89 // Root is the root File of this file system. 90 Root *File 91 // If Slow is true, each function on files of this FS waits for 92 // something arrives on Waitc. 93 Slow bool 94 // Waitc is used to wait some timing to emulate slow response. 95 Waitc chan struct{} 96 // groups holds the information of groups and it leader and members. 97 groups map[string]group 98 } 99 100 type group struct { 101 leader string 102 members map[string]bool 103 } 104 105 func (fs *FS) OpenFile(path string, omode lib9p.OpenMode) (lib9p.File, error) { 106 wnames := strings.Split(path, "/") 107 f, err := fs.Walk(wnames) 108 if err != nil { 109 return nil, fmt.Errorf("walk: %v", err) 110 } 111 f.Reader = bytes.NewReader(f.Content) 112 return f, nil 113 } 114 115 func (fs *FS) IsGroupLeader(gid, uid string) bool { 116 g, ok := fs.groups[gid] 117 if !ok { 118 return false 119 } 120 return g.leader == uid 121 } 122 123 func (fs *FS) IsGroupMember(gid, uid string) bool { 124 g, ok := fs.groups[gid] 125 if !ok { 126 return false 127 } 128 return g.members[uid] 129 } 130 131 func (fs *FS) Walk(wnames []string) (*File, error) { 132 if len(wnames) == 1 && (wnames[0] == "." || wnames[0] == "") { 133 return fs.Root, nil 134 } 135 cwd := fs.Root 136 L: 137 for i, name := range wnames { 138 for _, child := range cwd.Children { 139 if child.St.Name == name { 140 cwd = child 141 continue L 142 } 143 } 144 return nil, fmt.Errorf("%s not found", strings.Join(wnames[:i+1], "/")) 145 } 146 return cwd, nil 147 } 148 149 func (fs *FS) WalkPath(pathname string) (*File, error) { 150 wnames := strings.Split(pathname, "/") 151 return fs.Walk(wnames) 152 } 153 154 var Fsys *FS 155 156 func init() { 157 Fsys = &FS{ 158 Root: &File{ 159 St: lib9p.Stat{ 160 Qid: lib9p.Qid{Path: 0, Type: lib9p.QTDIR}, 161 Mode: lib9p.FileMode(fs.ModeDir | 0755), 162 Name: "root", 163 Uid: "glenda", 164 Gid: "glenda", 165 Muid: "glenda", 166 }, 167 Children: []*File{ 168 &File{ 169 Content: []byte("a\n"), 170 St: lib9p.Stat{ 171 Qid: lib9p.Qid{Path: 1, Type: lib9p.QTFILE}, 172 Mode: lib9p.FileMode(0644), 173 Name: "a", 174 Uid: "glenda", 175 Gid: "glenda", 176 Muid: "glenda", 177 }, 178 }, 179 &File{ 180 Content: []byte("b\n"), 181 St: lib9p.Stat{ 182 Qid: lib9p.Qid{Path: 2, Type: lib9p.QTFILE}, 183 Mode: lib9p.FileMode(0600), 184 Name: "b", 185 Uid: "ken", 186 Gid: "ken", 187 Muid: "ken", 188 }, 189 }, 190 &File{ 191 St: lib9p.Stat{ 192 Qid: lib9p.Qid{Path: 3, Type: lib9p.QTDIR}, 193 Mode: lib9p.FileMode(fs.ModeDir | 0755), 194 Name: "dir", 195 Uid: "rob", 196 Gid: "rob", 197 Muid: "rob", 198 }, 199 Children: []*File{ 200 &File{ 201 Content: []byte("unko\n"), 202 St: lib9p.Stat{ 203 Qid: lib9p.Qid{Path: 4, Type: lib9p.QTFILE}, 204 Mode: lib9p.FileMode(0666), 205 Name: "file", 206 Uid: "brian", 207 Gid: "brian", 208 Muid: "dennis", 209 }, 210 }, 211 }, 212 }, 213 }, 214 }, 215 Waitc: make(chan struct{}), 216 groups: map[string]group{ 217 "bell": group{ 218 leader: "glenda", 219 members: map[string]bool{ 220 "glenda": true, 221 "ken": true, 222 "dennis": true, 223 "brian": true, 224 "rob": true, 225 }, 226 }, 227 "kessoku": group{ 228 leader: "ijichi", 229 members: map[string]bool{ 230 "ijichi": true, 231 "yamada": true, 232 "gotoh": true, 233 "kita": true, 234 }, 235 }, 236 }, 237 } 238 SetFsysAndParent(Fsys, nil) 239 } 240 241 // SetFsysAndParent sets file.fsys and file.parent for every file in the fsys. 242 // Pass nil as file to setup entire file system. 243 func SetFsysAndParent(fsys *FS, file *File) { 244 if file == nil { 245 file = fsys.Root 246 file.Parent = fsys.Root 247 file.Fsys = fsys 248 } 249 for _, child := range file.Children { 250 child.Parent = file 251 child.Fsys = fsys 252 SetFsysAndParent(fsys, child) 253 } 254 }