lib9p

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

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 }