lib9p

Go 9P library.
Log | Files | Refs | LICENSE

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 }