// Rcreate is used to respond to a succesful create request. With 9P, creating // a file also opens the file for I/O. Once Rcreate returns, future read // and write requests to the file handle will pass through rwc. The value // rwc must meet the same criteria listed for the Ropen method of a Topen // request. func (t Tcreate) Rcreate(rwc interface{}, err error) { var ( f styxfile.Interface ) if err != nil { t.Rerror("%s", err) return } if dir, ok := rwc.(Directory); t.Mode.IsDir() && ok { f = styxfile.NewDir(dir, path.Join(t.Path(), t.Name), t.session.conn.qidpool) } else { f, err = styxfile.New(rwc) } if err != nil { t.session.conn.srv.logf("create %s failed: %s", t.Name, err) t.Rerror("create failed") return } file := file{name: path.Join(t.Path(), t.Name), rwc: f} // fid for parent directory is now the fid for the new file, // so there is no increase in references to this session. t.session.files.Put(t.fid, file) qtype := styxfile.QidType(styxfile.Mode9P(t.Mode)) qid := t.session.conn.qid(file.name, qtype) t.session.unhandled = false if t.session.conn.clearTag(t.tag) { t.session.conn.Rcreate(t.tag, qid, 0) } }
// NOTE(droyo) consider a scenario where a malicious actor connects // to the server that repeatedly spams Tauth requests. It can quickly // use up resources on the server. Consider the following measures: // // - rate-limiting Tauth requests // - Setting a per-connection session limit // - close connections that have not established a session in N seconds func (c *conn) handleTauth(cx context.Context, m styxproto.Tauth) bool { defer c.Flush() if c.srv.Auth == nil { c.clearTag(m.Tag()) c.Rerror(m.Tag(), "%s", errNotSupported) return true } if _, ok := c.sessionFid.Get(m.Afid()); ok { c.clearTag(m.Tag()) c.Rerror(m.Tag(), "fid %x in use", m.Afid()) return true } client, server := net.Pipe() ch := &Channel{ Context: c.cx, ReadWriteCloser: server, } rwc, err := styxfile.New(client) if err != nil { // This should never happen panic(err) } s := newSession(c, m) go func() { s.authC <- c.srv.Auth(ch, s.User, s.Access) close(s.authC) }() c.sessionFid.Put(m.Afid(), s) s.files.Put(m.Afid(), file{rwc: rwc, auth: true}) s.IncRef() 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) } }