func (c *ClientConn) readfile(fc *plan9.Fcall, ref *fileRef) *plan9.Fcall { size, err := c.explorer.Sizeof(ref.Path) if err != nil { return c.unexpectedErr(fc, err) } if size > 0 && fc.Offset >= size { // trying to reading past the end of file. // return count == 0 to signal EOF to client fc.Count = 0 return fc } fc.Data = allocBuffer(min(int(c.iounit), int(fc.Count), int(size))) defer discardBuffer(fc.Data) n, err := c.explorer.Read(fc.Data, fc.Offset, ref.Path) if err == io.EOF { if n == 0 { // returned EOF without reading anything, should return fc.Count = 0 discardBuffer(fc.Data) fc.Data = nil err = nil return fc } else { // was able to read som data from the file, should return the count // but not the error. The next call to read will trigger the EOF err = nil } } if err != nil { return c.unexpectedErr(fc, err) } fc.Count = uint32(n) fc.Data = fc.Data[:fc.Count] return fc }
func (c *ClientConn) attach(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rattach fc.Iounit = c.iounit fref, _ := c.createFileRef(c.explorer.Root(), FTDIR, 0) fc.Qid = fref.Qid c.bindFid(fc.Fid, fc.Qid.Path) return fc }
func (c *ClientConn) walk(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rwalk fref, has := c.fidRef(fc.Fid) if !has { return c.invalidFidErr(fc) } if _, has := c.fidRef(fc.Newfid); has { return c.fidUsedErr(fc) } current := fref.Path for idx, name := range fc.Wname { var ft FileType if current == 0 { return c.fileNotFoundErr(fc) } if ff, ok := c.explorer.(FileFinder); ok { f, err := ff.FindInDir(current, name) if err != nil { return c.unexpectedErr(fc, err) } current = f.Path } else { childs, err := c.explorer.ListDir(current) if err != nil { return c.unexpectedErr(fc, err) } idx, have := childs.FindExact(name) if !have { return c.fileNotFoundErr(fc) } current = childs[idx].Path } ref, err := c.createFileRef(current, ft, 0) if err != nil { return c.unexpectedErr(fc, err) } fc.Wqid = append(fc.Wqid, ref.Qid) // if the last match isn't a directory, there is no need to find // another part of the path // // so, just break here if ft == FTFILE && idx != len(fc.Wname)-1 { return c.fileNotFoundErr(fc) } } if len(fc.Wqid) == 0 { // newfid and fid will map to the same file if fc.Newfid != fc.Fid { c.bindFid(fc.Newfid, fref.Path) } } else { // make a bind between the last qid and the new fid c.bindFid(fc.Newfid, fc.Wqid[len(fc.Wqid)-1].Path) } return fc }
func (c *ClientConn) write(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rwrite if fref, have := c.fidRef(fc.Fid); have { n, err := c.explorer.Write(fref.Path, fc.Data, fc.Offset) if err != nil { return c.unexpectedErr(fc, err) } fc.Count = uint32(n) return fc } return c.invalidFidErr(fc) }
func (c *ClientConn) open(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Ropen if fref, has := c.fidRef(fc.Fid); has { err := c.explorer.Open(fref.Path, FileMode(fc.Mode)) if err != nil { return c.unexpectedErr(fc, err) } fc.Iounit = c.iounit fc.Qid = fref.Qid return fc } return c.invalidFidErr(fc) }
func (c *ClientConn) read(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rread if fref, has := c.fidRef(fc.Fid); has { if fref.IsDir() { return c.readdir(fc, fref) } return c.readfile(fc, fref) } return c.invalidFidErr(fc) }
func (c *ClientConn) readdir(fc *plan9.Fcall, ref *fileRef) *plan9.Fcall { // if the call have an offset, return 0 // since all readdir call's will return the full directory if fc.Offset > 0 { fc.Count = 0 return fc } childs, err := c.explorer.ListDir(ref.Path) if err != nil { return c.unexpectedErr(fc, err) } tmpBuf := allocBuffer(int(c.iounit)) out := bytes.NewBuffer(tmpBuf[:0]) defer discardBuffer(tmpBuf) for _, id := range childs { stat, err := c.explorer.Stat(id.Path) if err != nil { return c.unexpectedErr(fc, err) } dir := plan9.Dir{ Qid: plan9.Qid{Type: uint8(stat.Type), Vers: stat.Version, Path: id.Path}, Mode: plan9.Perm(stat.Mode), Atime: stat.Atime, Mtime: stat.Mtime, Length: stat.Size, Uid: stat.Uname, Gid: stat.Gname, Name: stat.Name, } buf, err := dir.Bytes() if err != nil { return c.unexpectedErr(fc, err) } _, err = out.Write(buf) if err != nil { return c.unexpectedErr(fc, err) } } fc.Count = uint32(len(fc.Data)) fc.Data = out.Bytes() return fc }
func (c *ClientConn) clunk(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rclunk oldpath, had := c.unbindFid(fc.Fid) if had { err := c.explorer.Close(oldpath) if err != nil { return c.unexpectedErr(fc, err) } } return fc }
func (c *ClientConn) create(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rcreate if fref, have := c.fidRef(fc.Fid); have { file := File{Name: fc.Name} file.Type = FileType(fc.Mode) path, err := c.explorer.Create(fref.Path, file, uint32(fc.Perm)) if err != nil { return c.unexpectedErr(fc, err) } cref, err := c.createFileRef(path, file.Type, 0) if err != nil { return c.unexpectedErr(fc, err) } fc.Iounit = c.iounit fc.Qid = cref.Qid c.unbindFid(fc.Fid) c.bindFid(fc.Fid, cref.Path) return fc } return c.invalidFidErr(fc) }
func (c *ClientConn) process(fc *plan9.Fcall, out chan *plan9.Fcall) { println(">>>\t", fc.String()) switch fc.Type { case plan9.Tversion: fc = c.version(fc) case plan9.Tattach: fc = c.attach(fc) case plan9.Twalk: fc = c.walk(fc) case plan9.Topen: fc = c.open(fc) case plan9.Tread: fc = c.read(fc) case plan9.Tclunk: fc = c.clunk(fc) case plan9.Tcreate: fc = c.create(fc) case plan9.Twrite: fc = c.write(fc) case plan9.Tauth: fc = noauth(fc) default: println("!!!\t", fc.String()) fc = nil } if fc != nil { println("<<<\t", fc.String()) } out <- fc }
func (c *ClientConn) unexpectedErr(fc *plan9.Fcall, err error) *plan9.Fcall { fc.Type = plan9.Rerror fc.Ename = err.Error() return fc }
func (c *ClientConn) fidUsedErr(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rerror fc.Ename = "fid in use" return fc }
func (c *ClientConn) fileNotFoundErr(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rerror fc.Ename = "file not found" return fc }
func (c *ClientConn) invalidFidErr(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rerror fc.Ename = "fid not found" return fc }
func (c *ClientConn) version(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rversion fc.Version = "9P2000" return fc }
func noauth(fc *plan9.Fcall) *plan9.Fcall { fc.Type = plan9.Rerror fc.Ename = "no auth requried" return fc }