func (s *Session) handleTcreate(cx context.Context, msg styxproto.Tcreate, file file) bool { qid := s.conn.qid(file.name, 0) if qid.Type()&styxproto.QTDIR == 0 { s.conn.clearTag(msg.Tag()) s.conn.Rerror(msg.Tag(), "not a directory: %q", file.name) s.conn.Flush() return true } s.requests <- Tcreate{ Name: string(msg.Name()), Mode: styxfile.ModeOS(msg.Perm()), Flag: openFlag(msg.Mode()), reqInfo: newReqInfo(cx, s, msg, file.name), } return true }
// The Ropen method signals to the client that a file has succesfully // been opened and is ready for I/O. After Ropen returns, future reads // and writes to the opened file handle will pass through rwc. // // The value rwc must implement some of the interfaces in the io package // for reading and writing. If the type implements io.Seeker or io.ReaderAt // and io.WriterAt, clients may read or write at arbitrary offsets within // the file. Types that only implement Read or Write operations will return // errors on writes and reads, respectively. // // If rwc implements the Stat method of os.File, that will be used to // answer Tstat requests. Otherwise, the styx package will assemble Rstat // responses out of default values merged with any methods rwc provides // from the os.FileInfo interface. // // If a file does not implement any of the Read or Write interfaces in // the io package, A generic error is returned to the client, and a message // will be written to the server's ErrorLog. func (t Topen) Ropen(rwc interface{}, err error) { var ( file file f styxfile.Interface ) if err != nil { t.Rerror("%s", err) return } // The type of the file (regular or directory) will have been // established in a previous Twalk request. qid := t.session.conn.qid(t.Path(), 0) mode := styxfile.ModeOS(uint32(qid.Type()) << 24) if dir, ok := rwc.(Directory); ok && mode.IsDir() { f = styxfile.NewDir(dir, t.Path(), t.session.conn.qidpool) } else { f, err = styxfile.New(rwc) } if err != nil { t.session.conn.srv.logf("%s open %s failed: %s", t.path, err) // Don't want to expose too many implementation details // to clients. t.Rerror("open failed") return } t.session.files.Update(t.fid, &file, func() { file.rwc = f }) t.session.unhandled = false if t.session.conn.clearTag(t.tag) { t.session.conn.Ropen(t.tag, qid, 0) } }
func (s *Session) handleTwstat(cx context.Context, msg styxproto.Twstat, file file) bool { // mode, atime+mtime, length, name, uid+gid, sync // we will ignore muid const numMutable = 6 // By convention, sending a Twstat message with a stat structure consisting // entirely of "don't touch" values indicates that the client wants the server // to sync the file to disk. var haveChanges bool var messages int stat := msg.Stat() // We buffer the channel so that the response // methods for each attribute do not block. status := make(chan error, numMutable) info := newReqInfo(cx, s, msg, file.name) filled := make([]int32, numMutable) atime, mtime := stat.Atime(), stat.Mtime() if atime != math.MaxUint32 || mtime != math.MaxUint32 { haveChanges = true s.requests <- Tutimes{ Atime: time.Unix(int64(atime), 0), Mtime: time.Unix(int64(mtime), 0), twstat: twstat{status, filled, messages, info}, } messages++ } if uid, gid := string(stat.Uid()), string(stat.Gid()); uid != "" || gid != "" { haveChanges = true s.requests <- Tchown{ User: uid, Group: gid, twstat: twstat{status, filled, messages, info}, } messages++ } if name := string(stat.Name()); name != "" && name != file.name { haveChanges = true s.requests <- Trename{ OldPath: file.name, NewPath: name, twstat: twstat{status, filled, messages, info}, } messages++ } if length := stat.Length(); length != -1 { haveChanges = true s.requests <- Ttruncate{ Size: length, twstat: twstat{status, filled, messages, info}, } messages++ } if stat.Mode() != math.MaxUint32 { haveChanges = true s.requests <- Tchmod{ Mode: styxfile.ModeOS(stat.Mode()), twstat: twstat{status, filled, messages, info}, } messages++ } if len(stat.Muid()) != 0 { // even though we won't respond to this field, we don't // want to needlessly stimulate a sync request haveChanges = true } if !haveChanges { s.requests <- Tsync{ twstat: twstat{status, filled, messages, info}, } messages++ } go func() { var ( success bool err error ) for i := 0; i < messages; i++ { if e, ok := <-status; !ok { panic("closed Twstat channel prematurely") } else if e != nil { err = e } else { success = true } } if !s.conn.clearTag(msg.Tag()) { return } if success { s.conn.Rwstat(msg.Tag()) } else { s.conn.Rerror(msg.Tag(), "%s", err) } s.conn.Flush() }() return true }