package main import ( "bytes" "code.google.com/p/go9p/p" "code.google.com/p/go9p/p/srv" "errors" "factotum" "fmt" "log" "net" "os" "reflect" "strconv" "time" ) type ReflectFS struct { srv.Srv root reflect.Value } type Fid struct { path []string name string qid *p.Qid v reflect.Value } type Users struct { } type User struct { name string } var users Users const indexRadix = 10 type Conf struct { a [5]int8 b bool d SubConf i int m map[int]string n []string r *Conf s []int } type SubConf struct { a int b int } var conf Conf func confinit() { conf.a = [5]int8{1,2,3,4,5} conf.b = true conf.d = SubConf{1, 2} conf.i = 8 conf.m = map[int]string{ 1: "A", 2: "B", } conf.r = &conf conf.s = []int{1,2,3} } var starttime uint32 // seconds since 1970-01-01 GMT func init() { t := time.Now() starttime = uint32(t.Unix()) } func NewReflectFS(a interface{}) *ReflectFS { rfs := &ReflectFS{ root: reflect.ValueOf(a).Elem(), } return rfs } func typeisdir(t reflect.Type) bool { retry: switch t.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: return true case reflect.Ptr: t = t.Elem() goto retry default: return false } panic("bad programming") } func keystring(v reflect.Value) string { var s string switch k := v.Kind(); k { case reflect.Bool: s = fmt.Sprint(v.Bool()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: s = fmt.Sprint(v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: s = fmt.Sprint(v.Uint()) case reflect.String: s = v.String() default: s = "" } return s } func (fid *Fid) dirs() []*p.Dir { v := fid.v t := v.Type() if !typeisdir(t) { panic("(*Fid).dirs called on a " + t.Kind().String()) } again: switch t.Kind() { case reflect.Array, reflect.Slice: dirs := make([]*p.Dir, v.Len()) for i:=0; i < v.Len(); i++ { dirs[i] = value2dir(v.Index(i), strconv.FormatInt(int64(i), indexRadix)) } return dirs case reflect.Map: dirs := make([]*p.Dir, v.Len()) for i, k := range v.MapKeys() { dirs[i] = value2dir(v.MapIndex(k), keystring(k)) } return dirs case reflect.Struct: dirs := make([]*p.Dir, t.NumField()) for i:=0; i < t.NumField(); i++ { dirs[i] = value2dir(v.Field(i), t.Field(i).Name) } return dirs case reflect.Ptr: t = t.Elem() v = v.Elem() if !v.IsValid() { return make([]*p.Dir, 0) } goto again } panic("bad programming") } func (fid *Fid) dir() *p.Dir { return value2dir(fid.v, fid.name) } func value2dir(v reflect.Value, name string) *p.Dir { var qtype uint8 var mode uint32 t := v.Type() if typeisdir(t) { qtype = p.QTDIR mode = p.DMDIR|0111 } dir := &p.Dir { Qid: p.Qid{ Type: qtype, Version: 0, Path: 0, }, Mode: mode|0444, Atime: 0, Mtime: starttime, Length: uint64(t.Size()), Name: name, Uid: "none", Gid: t.String(), Muid: "none", } return dir } func (*ReflectFS) Attach(req *srv.Req) { log.Print("attach") log.Print(" ", req.Fid.User.Name()) fid := &Fid{ path: make([]string, 0), name: "/", v: reflect.ValueOf(&conf).Elem(), } req.Fid.Aux = fid var qtype uint8 if typeisdir(fid.v.Type()) { qtype = p.QTDIR } else { qtype = p.QTFILE } qid := &p.Qid{ Type: qtype, Version: 0, Path: 0, } fid.qid = qid req.RespondRattach(qid) } func (rfs *ReflectFS) Walk(req *srv.Req) { fid := req.Fid.Aux.(*Fid) if req.Newfid.Aux == nil { req.Newfid.Aux = new(Fid) } nfid := req.Newfid.Aux.(*Fid) wqids := make([]p.Qid, len(req.Tc.Wname)) path := fid.path name := fid.name qid := fid.qid for _, wname := range req.Tc.Wname { if wname == ".." { path = path[:len(path)-1] } else { path = append(path, wname) } } v := rfs.root nskip := len(path) - len(req.Tc.Wname) i := 0 for ; i < len(path); i++ { if !typeisdir(v.Type()) { req.RespondError(srv.Enotdir) return } retry: switch v.Type().Kind() { case reflect.Array, reflect.Slice: var j int if k, err := strconv.ParseInt(path[i], indexRadix, 0); err != nil { req.RespondError(err) return } else { j = int(k) } if j < 0 || j >= v.Len() { req.RespondError(srv.Enoent) return } v = v.Index(j) case reflect.Map: var found bool for _, k := range v.MapKeys() { if keystring(k) == path[i] { v = v.MapIndex(k) found = true } } if !found { req.RespondError(srv.Enoent) return } case reflect.Struct: if fv := v.FieldByName(path[i]); fv.IsValid() { v = fv } else { req.RespondError(srv.Enoent) return } case reflect.Ptr: v = v.Elem() goto retry default: if typeisdir(v.Type()) { req.RespondError(errors.New("can't list a directory of type " + v.Kind().String())) return } } if i >= nskip { var qtype uint8 if typeisdir(v.Type()) { qtype = p.QTDIR } else { qtype = p.QTFILE } name = path[i] wqids[i-nskip] = p.Qid{ Type: qtype, Version: 0, Path: 0, } qid = &wqids[i-nskip] } } nfid.path = path nfid.name = name nfid.qid = qid nfid.v = v req.RespondRwalk(wqids[:i-nskip]) } func (*ReflectFS) Open(req *srv.Req) { fid := req.Fid.Aux.(*Fid) req.RespondRopen(fid.qid, 1024) } func (*ReflectFS) Create(req *srv.Req) { req.RespondError(errors.New("create stub")) } func (*ReflectFS) dirread(req *srv.Req) { fid := req.Fid.Aux.(*Fid) dirs := fid.dirs() p.InitRread(req.Rc, req.Tc.Count) if req.Tc.Offset == 0 { var n int for i:=0; i < len(dirs); i++ { n += p.PackDir(dirs[i], req.Rc.Data[n:], req.Conn.Srv.Dotu) } p.SetRreadCount(req.Rc, uint32(n)) } else { p.SetRreadCount(req.Rc, 0) } req.Respond() } func (rfs *ReflectFS) Read(req *srv.Req) { if req.Fid.Type & p.QTDIR > 0 { rfs.dirread(req) return } p.InitRread(req.Rc, 0) if req.Tc.Offset > 0 { req.Respond() return } fid := req.Fid.Aux.(*Fid) v := fid.v w := bytes.NewBuffer(req.Rc.Data) switch kind := v.Kind(); kind { case reflect.Bool: fmt.Fprint(w, v.Bool()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fmt.Fprint(w, v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: fmt.Fprint(w, v.Uint()) case reflect.String: fmt.Fprint(w, v.String()) default: req.RespondError(errors.New("can't read type " + kind.String())) return } w.WriteByte('\n') p.SetRreadCount(req.Rc, uint32(w.Len())) req.Respond() } func (*ReflectFS) Write(req *srv.Req) { req.RespondError(errors.New("write stub")) } func (*ReflectFS) Clunk(req *srv.Req) { req.RespondRclunk() } func (*ReflectFS) Remove(req *srv.Req) { req.RespondError(errors.New("remove stub")) } func (*ReflectFS) Stat(req *srv.Req) { fid := req.Fid.Aux.(*Fid) req.RespondRstat(fid.dir()) } func (*ReflectFS) Wstat(req *srv.Req) { req.RespondError(errors.New("wstat stub")) } func (*ReflectFS) ConnOpened(*srv.Conn) { log.Print("conn opened") } func (*ReflectFS) ConnClosed(*srv.Conn) { log.Print("conn closed") } func (u *User) Name() string { return u.name } func (*User) Id() int { return 0 } func (*User) Groups() []p.Group { return nil } func (*User) IsMember(p.Group) bool { return false } func (*Users) Uid2User(uid int) p.User { log.Print("uid lookup: ", uid) return nil } func (*Users) Uname2User(name string) p.User { log.Print("uname lookup: " + name) u := &User{ name: "none", } return u } func (*Users) Gid2Group(int) p.Group { return nil } func (*Users) Gname2Group(string) p.Group { return nil } func (*ReflectFS) AuthInit(fid *srv.Fid, s string) (*p.Qid, error) { f, err := os.OpenFile("/mnt/factotum/rpc", os.O_RDWR, 0000) if err != nil { return nil, errors.New("open /mnt/factotum/rpc: " + err.Error()) } // closed by AuthDestroy rpc := &factotum.AuthRpc{ F: f, } if r, msg := rpc.Rpc("start", "proto=p9any role=server"); r != factotum.ARok { return nil, errors.New(msg) } fid.Aux = rpc aqid := &p.Qid{ Type: p.QTAUTH, Path: 0, Version: 0, } return aqid, nil } func (*ReflectFS) AuthDestroy(fid *srv.Fid) { rpc := fid.Aux.(*factotum.AuthRpc) rpc.F.Close() } func (rfs *ReflectFS) AuthCheck(fid *srv.Fid, afid *srv.Fid, aname string) error { if afid == nil { return errors.New("authentication required") } dummy := make([]byte, 0) if _, err := rfs.AuthRead(afid, 0, dummy); err != nil { return errors.New("AuthRead: " + err.Error()) } rpc := afid.Aux.(*factotum.AuthRpc) fid.User = &User{ name: rpc.Ai.Cuid, } return nil } func (*ReflectFS) AuthRead(fid *srv.Fid, offset uint64, buf []byte) (int, error) { rpc := fid.Aux.(*factotum.AuthRpc) switch r, msg := rpc.Rpc("read", ""); r { case factotum.ARdone: if err := rpc.GetInfo(); err != nil { return 0, errors.New("GetInfo: " + err.Error()) } return 0, nil case factotum.ARok: if len(buf) < len(rpc.Arg) { return 0, fmt.Errorf("buffer too small: %d; need %d", len(buf), len(rpc.Arg)) } copy(buf, rpc.Arg) return len(rpc.Arg), nil case factotum.ARphase: return 0, errors.New(msg) default: return 0, errors.New(msg) } panic("can't happen") } func (*ReflectFS) AuthWrite(fid *srv.Fid, offset uint64, buf []byte) (int, error) { rpc := fid.Aux.(*factotum.AuthRpc) if r, msg := rpc.Rpc("write", string(buf)); r != factotum.ARok { return 0, errors.New(msg) } return len(buf), nil } func (*ReflectFS) exportfs(c net.Conn) error { defer c.Close() _, err := factotum.Proxy(c, "proto=p9any role=server") if err != nil { return errors.New("factotum.Proxy: " + err.Error()) } log.Print("exportfs reading") buf := make([]byte, 1<<16) var n int if n, err = c.Read(buf); err != nil { return errors.New("read: " + err.Error()) } tree := string(buf[:n]) log.Print("exportfs tree ", tree) if _, err := c.Write([]byte("OK")); err != nil { return errors.New("ack: " + err.Error()) } if n, err = c.Read(buf); err != nil { return errors.New("impo: " + err.Error()) } log.Print(string(buf[:n])) return nil } func (rfs *ReflectFS) exportfslisten() { l, err := net.Listen("tcp", ":5641") if err != nil { log.Fatal("exportfs listen: " + err.Error()) } for { c, err := l.Accept() if err != nil { continue } log.Print("exportfs accept") go rfs.exportfs(c) } } func main() { log.SetFlags(0) confinit() rfs := NewReflectFS(&conf) var _ srv.ReqOps = rfs var _ srv.AuthOps = rfs rfs.Id = "rfs" rfs.Upool = &users rfs.Debuglevel = 0 rfs.Start(rfs) go rfs.exportfslisten() if err := rfs.StartNetListener("tcp", ":5640"); err != nil { log.Fatal("9p listen: " + err.Error()) } }