lib9p

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

file.go (3393B)


      1 package client
      2 
      3 import (
      4 	"errors"
      5 	"io"
      6 	"io/fs"
      7 
      8 	"git.mtkn.jp/lib9p"
      9 )
     10 
     11 // File is a File for Client.
     12 type File struct {
     13 	name   string
     14 	path   string // must not contain trailing slash.
     15 	fid    uint32
     16 	omode  lib9p.OpenMode
     17 	offset uint64
     18 	qid    lib9p.Qid
     19 	iounit uint32
     20 	fs     *FS
     21 	dirBuf []fs.DirEntry
     22 }
     23 
     24 func (cf *File) Stat() (*lib9p.FileInfo, error) {
     25 	tag, err := cf.fs.tPool.add()
     26 	if err != nil {
     27 		return nil, err
     28 	}
     29 	st, err := cf.fs.c.Stat(tag, cf.fid)
     30 	cf.fs.tPool.delete(tag)
     31 	if err != nil {
     32 		return nil, err
     33 	}
     34 	return &lib9p.FileInfo{Stat: *st}, nil
     35 }
     36 
     37 // TODO: check permission?
     38 func (cf *File) WStat(stat *lib9p.Stat) error {
     39 	tag, err := cf.fs.tPool.add()
     40 	if err != nil {
     41 		return err
     42 	}
     43 	err = cf.fs.c.Wstat(tag, cf.fid, stat)
     44 	cf.fs.tPool.delete(tag)
     45 	return err
     46 }
     47 
     48 // Don't use closed file.
     49 func (cf *File) Close() error {
     50 	tag, err := cf.fs.tPool.add()
     51 	if err != nil {
     52 		return err
     53 	}
     54 	err = cf.fs.c.Clunk(tag, cf.fid)
     55 	cf.fs.tPool.delete(tag)
     56 	cf.fs.fPool.delete(cf.fid)
     57 	cf.fid = lib9p.NOFID
     58 	return err
     59 }
     60 
     61 func (cf *File) Read(b []byte) (int, error) {
     62 	if cf.omode == -1 {
     63 		return 0, errors.New("not open")
     64 	}
     65 	if len(b) == 0 {
     66 		return 0, nil
     67 	}
     68 	if cf.omode&3 != lib9p.OREAD && cf.omode&3 != lib9p.ORDWR {
     69 		return 0, lib9p.ErrPerm
     70 	}
     71 	count := len(b)
     72 	cur := 0
     73 	for count > 0 {
     74 		var c uint32
     75 		if uint32(count) > cf.iounit {
     76 			c = cf.iounit
     77 		} else {
     78 			c = uint32(count)
     79 		}
     80 		tag, err := cf.fs.tPool.add()
     81 		if err != nil {
     82 			return 0, err
     83 		}
     84 		buf, err := cf.fs.c.Read(tag, cf.fid, cf.offset, c)
     85 		cf.fs.tPool.delete(tag)
     86 		var i int
     87 		for i = 0; i < len(buf); i++ {
     88 			b[cur+i] = buf[i]
     89 		}
     90 		cf.offset += uint64(i)
     91 		cur += i
     92 		if err != nil {
     93 			return cur, err
     94 		}
     95 		count -= int(c)
     96 	}
     97 	if cur == 0 {
     98 		return 0, io.EOF
     99 	} else {
    100 		return cur, nil
    101 	}
    102 }
    103 
    104 func (cf *File) Write(b []byte) (int, error) {
    105 	if cf.omode == -1 {
    106 		return 0, errors.New("not open")
    107 	}
    108 	if len(b) == 0 {
    109 		return 0, nil
    110 	}
    111 	if cf.omode&3 != lib9p.OWRITE && cf.omode&3 != lib9p.ORDWR {
    112 		return 0, lib9p.ErrPerm
    113 	}
    114 	count := len(b)
    115 	cur := 0
    116 	for count > 0 {
    117 		var c uint32
    118 		if uint32(count) > cf.iounit {
    119 			c = cf.iounit
    120 		} else {
    121 			c = uint32(count)
    122 		}
    123 		tag, err := cf.fs.tPool.add()
    124 		if err != nil {
    125 			return 0, err
    126 		}
    127 		n, err := cf.fs.c.Write(tag, cf.fid, cf.offset, c, b[cur:cur+int(c)])
    128 		cf.fs.tPool.delete(tag)
    129 		cf.offset += uint64(n)
    130 		cur += int(n)
    131 		if err != nil {
    132 			return cur, err
    133 		}
    134 		count -= int(c)
    135 	}
    136 	return cur, nil
    137 }
    138 
    139 func (cf *File) ReadDir(n int) ([]fs.DirEntry, error) {
    140 	if cf.qid.Type&lib9p.QTDIR == 0 {
    141 		return nil, errors.New("not a directory")
    142 	}
    143 	if cf.omode == -1 {
    144 		return nil, errors.New("not open")
    145 	}
    146 	var (
    147 		de   []fs.DirEntry
    148 		err  error
    149 		data []byte
    150 		tag  uint16
    151 	)
    152 	for n <= 0 || len(cf.dirBuf) < n {
    153 		tag, err = cf.fs.tPool.add()
    154 		if err != nil {
    155 			break
    156 		}
    157 		data, err = cf.fs.c.Read(tag, cf.fid, cf.offset, cf.iounit)
    158 		cf.fs.tPool.delete(tag)
    159 		if err != nil {
    160 			break
    161 		} else if len(data) == 0 {
    162 			break
    163 		}
    164 		cf.offset += uint64(len(data))
    165 		for len(data) > 0 {
    166 			st := lib9p.NewStat(data)
    167 			data = data[2+st.Size():]
    168 			cf.dirBuf = append(cf.dirBuf, &lib9p.FileInfo{Stat: *st})
    169 		}
    170 	}
    171 	if 0 < n && n <= len(cf.dirBuf) {
    172 		de = cf.dirBuf[:n]
    173 		cf.dirBuf = cf.dirBuf[n:]
    174 	} else {
    175 		de = cf.dirBuf
    176 		cf.dirBuf = nil
    177 	}
    178 	if err == nil && len(de) == 0 && n > 0 {
    179 		err = io.EOF
    180 	}
    181 	return de, err
    182 }