├── .gitignore ├── AUTHORS ├── CONTRIBUTORS ├── LICENSE └── p ├── 9p_test.go ├── clnt ├── 9p_test.go ├── clnt.go ├── close.go ├── examples │ ├── cl │ │ └── cl.go │ ├── ls │ │ └── ls.go │ ├── read │ │ └── read.go │ ├── tag │ │ └── tag.go │ ├── tls │ │ └── tls.go │ └── write │ │ └── write.go ├── mount.go ├── open.go ├── pool.go ├── read.go ├── remove.go ├── seek.go ├── stat.go ├── stats_http.go ├── tag.go ├── walk.go └── write.go ├── fmt.go ├── log.go ├── osusers.go ├── p9.go ├── packr.go ├── packt.go ├── srv ├── conn.go ├── examples │ ├── clonefs │ │ └── clonefs.go │ ├── ramfs │ │ └── ramfs.go │ ├── timefs │ │ └── timefs.go │ ├── tlsramfs │ │ └── tlsramfs.go │ └── ufs │ │ ├── .gitignore │ │ └── ufs.go ├── fcall.go ├── file.go ├── respond.go ├── srv.go ├── stats_http.go └── ufs │ ├── ufs.go │ ├── ufs_darwin.go │ └── ufs_linux.go └── unpack.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Go9p authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as 6 | # Name or Organization 7 | # The email address is not required for organizations. 8 | 9 | # Please keep the list sorted. 10 | 11 | Andrey Mirtchovski 12 | Latchesar Ionkov 13 | Roger Peppe 14 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This is the official list of people who can contribute 2 | # (and typically have contributed) code to the Go9p repository. 3 | # The AUTHORS file lists the copyright holders; this file 4 | # lists people. For example, Google employees are listed here 5 | # but not in AUTHORS, because Google holds the copyright. 6 | # 7 | # The submission process automatically checks to make sure 8 | # that people submitting code are listed in this file (by email address). 9 | # XXX more bumph here? 10 | # Names should be added to this file like so: 11 | # Name 12 | 13 | # Please keep the list sorted. 14 | 15 | Andrey Mirtchovski 16 | Latchesar Ionkov 17 | Akshat Kumar 18 | Roger Peppe 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go9p Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * The names of Go9p's contributors may not be used to endorse 14 | or promote products derived from this software without specific prior 15 | written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /p/9p_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package p 6 | 7 | import ( 8 | "flag" 9 | "testing" 10 | ) 11 | 12 | var debug = flag.Int("debug", 0, "print debug messages") 13 | 14 | // Two files, dotu was true. 15 | var testunpackbytes = []byte{ 16 | 79, 0, 0, 0, 0, 0, 0, 0, 0, 228, 193, 233, 248, 44, 145, 3, 0, 0, 0, 0, 0, 164, 1, 0, 0, 0, 0, 0, 0, 47, 117, 180, 83, 102, 3, 0, 0, 0, 0, 0, 0, 6, 0, 112, 97, 115, 115, 119, 100, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 0, 0, 232, 3, 0, 0, 232, 3, 0, 0, 255, 255, 255, 255, 78, 0, 0, 0, 0, 0, 0, 0, 0, 123, 171, 233, 248, 42, 145, 3, 0, 0, 0, 0, 0, 164, 1, 0, 0, 0, 0, 0, 0, 41, 117, 180, 83, 195, 0, 0, 0, 0, 0, 0, 0, 5, 0, 104, 111, 115, 116, 115, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 4, 0, 110, 111, 110, 101, 0, 0, 232, 3, 0, 0, 232, 3, 0, 0, 255, 255, 255, 255, 17 | } 18 | 19 | func TestUnpackDir(t *testing.T) { 20 | b := testunpackbytes 21 | for len(b) > 0 { 22 | var err error 23 | if _, b, _, err = UnpackDir(b, true); err != nil { 24 | t.Fatalf("Unpackdir: %v", err) 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /p/clnt/9p_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "net" 13 | "os" 14 | "path" 15 | "strconv" 16 | "testing" 17 | 18 | "github.com/lionkov/go9p/p" 19 | "github.com/lionkov/go9p/p/srv/ufs" 20 | ) 21 | 22 | var debug = flag.Int("debug", 0, "print debug messages") 23 | var numDir = flag.Int("numdir", 16384, "Number of directory entries for readdir testing") 24 | var numAttach = flag.Int("numattach", 65536, "Number of attaches in make in TestAttach") 25 | 26 | func TestAttach(t *testing.T) { 27 | var err error 28 | flag.Parse() 29 | ufs := new(ufs.Ufs) 30 | ufs.Dotu = false 31 | ufs.Id = "ufs" 32 | ufs.Debuglevel = *debug 33 | ufs.Start(ufs) 34 | 35 | t.Log("ufs starting\n") 36 | // determined by build tags 37 | //extraFuncs() 38 | l, err := net.Listen("unix", "") 39 | if err != nil { 40 | t.Fatalf("Can not start listener: %v", err) 41 | } 42 | srvAddr := l.Addr().String() 43 | t.Logf("Server is at %v", srvAddr) 44 | go func() { 45 | if err = ufs.StartListener(l); err != nil { 46 | t.Fatalf("Can not start listener: %v", err) 47 | } 48 | }() 49 | var conn net.Conn 50 | if conn, err = net.Dial("unix", srvAddr); err != nil { 51 | t.Fatalf("%v", err) 52 | } else { 53 | t.Logf("Got a conn, %v\n", conn) 54 | } 55 | 56 | user := p.OsUsers.Uid2User(os.Geteuid()) 57 | clnt := NewClnt(conn, 8192, false) 58 | // run enough attaches to maybe let the race detector trip. 59 | // The default, 1024, is lower than I'd like, but some environments don't 60 | // let you do a huge number, as they throttle the accept rate. 61 | for i := 0; i < *numAttach; i++ { 62 | _, err := clnt.Attach(nil, user, "/tmp") 63 | 64 | if err != nil { 65 | t.Fatalf("Connect failed: %v\n", err) 66 | } 67 | defer clnt.Unmount() 68 | 69 | } 70 | } 71 | 72 | func TestAttachOpenReaddir(t *testing.T) { 73 | var err error 74 | flag.Parse() 75 | ufs := new(ufs.Ufs) 76 | ufs.Dotu = false 77 | ufs.Id = "ufs" 78 | ufs.Debuglevel = *debug 79 | ufs.Start(ufs) 80 | tmpDir, err := ioutil.TempDir("", "go9") 81 | if err != nil { 82 | t.Fatal("Can't create temp directory") 83 | } 84 | //defer os.RemoveAll(tmpDir) 85 | ufs.Root = tmpDir 86 | 87 | t.Logf("ufs starting in %v\n", tmpDir) 88 | // determined by build tags 89 | //extraFuncs() 90 | l, err := net.Listen("unix", "") 91 | if err != nil { 92 | t.Fatalf("Can not start listener: %v", err) 93 | } 94 | srvAddr := l.Addr().String() 95 | t.Logf("Server is at %v", srvAddr) 96 | go func() { 97 | if err = ufs.StartListener(l); err != nil { 98 | t.Fatalf("Can not start listener: %v", err) 99 | } 100 | }() 101 | var conn net.Conn 102 | if conn, err = net.Dial("unix", srvAddr); err != nil { 103 | t.Fatalf("%v", err) 104 | } else { 105 | t.Logf("Got a conn, %v\n", conn) 106 | } 107 | 108 | clnt := NewClnt(conn, 8192, false) 109 | // packet debugging on clients is broken. 110 | clnt.Debuglevel = 0 // *debug 111 | user := p.OsUsers.Uid2User(os.Geteuid()) 112 | rootfid, err := clnt.Attach(nil, user, "/") 113 | if err != nil { 114 | t.Fatalf("%v", err) 115 | } 116 | t.Logf("attached, rootfid %v\n", rootfid) 117 | dirfid := clnt.FidAlloc() 118 | if _, err = clnt.Walk(rootfid, dirfid, []string{"."}); err != nil { 119 | t.Fatalf("%v", err) 120 | } 121 | 122 | // Now create a whole bunch of files to test readdir 123 | for i := 0; i < *numDir; i++ { 124 | f := fmt.Sprintf(path.Join(tmpDir, fmt.Sprintf("%d", i))) 125 | if err := ioutil.WriteFile(f, []byte(f), 0600); err != nil { 126 | t.Fatalf("Create %v: got %v, want nil", f, err) 127 | } 128 | } 129 | 130 | if err = clnt.Open(dirfid, 0); err != nil { 131 | t.Fatalf("%v", err) 132 | } 133 | var b []byte 134 | if b, err = clnt.Read(dirfid, 0, 64*1024); err != nil { 135 | t.Fatalf("%v", err) 136 | } 137 | var i, amt int 138 | var offset uint64 139 | err = nil 140 | found := make([]int, *numDir) 141 | fail := false 142 | for err == nil { 143 | if b, err = clnt.Read(dirfid, offset, 64*1024); err != nil { 144 | t.Fatalf("%v", err) 145 | } 146 | t.Logf("clnt.Read returns [%v,%v]", len(b), err) 147 | if len(b) == 0 { 148 | break 149 | } 150 | for b != nil && len(b) > 0 { 151 | var d *p.Dir 152 | if d, b, amt, err = p.UnpackDir(b, ufs.Dotu); err != nil { 153 | t.Errorf("UnpackDir returns %v", err) 154 | break 155 | } else { 156 | if *debug > 128 { 157 | t.Logf("Entry %d: %v", i, d) 158 | } 159 | i++ 160 | offset += uint64(amt) 161 | ix, err := strconv.Atoi(d.Name) 162 | if err != nil { 163 | t.Errorf("File name %v is wrong; %v (dirent %v)", d.Name, err, d) 164 | continue 165 | } 166 | if found[ix] > 0 { 167 | t.Errorf("Element %d already returned %d times", ix, found[ix]) 168 | } 169 | found[ix]++ 170 | } 171 | } 172 | } 173 | if i != *numDir { 174 | t.Fatalf("Reading %v: got %d entries, wanted %d, err %v", tmpDir, i, *numDir, err) 175 | } 176 | if fail { 177 | t.Fatalf("I give up") 178 | } 179 | 180 | t.Logf("-----------------------------> Alternate form, using readdir and File") 181 | // Alternate form, using readdir and File 182 | dirfile, err := clnt.FOpen(".", p.OREAD) 183 | if err != nil { 184 | t.Fatalf("%v", err) 185 | } 186 | i, amt, offset = 0, 0, 0 187 | err = nil 188 | passes := 0 189 | 190 | found = make([]int, *numDir) 191 | fail = false 192 | for err == nil { 193 | d, err := dirfile.Readdir(*numDir) 194 | if err != nil && err != io.EOF { 195 | t.Errorf("%v", err) 196 | } 197 | 198 | t.Logf("d is %v", d) 199 | if len(d) == 0 { 200 | break 201 | } 202 | for _, v := range d { 203 | ix, err := strconv.Atoi(v.Name) 204 | if err != nil { 205 | t.Errorf("File name %v is wrong; %v (dirent %v)", v.Name, err, v) 206 | continue 207 | } 208 | if found[ix] > 0 { 209 | t.Errorf("Element %d already returned %d times", ix, found[ix]) 210 | } 211 | found[ix]++ 212 | } 213 | i += len(d) 214 | if i >= *numDir { 215 | break 216 | } 217 | if passes > *numDir { 218 | t.Fatalf("%d iterations, %d read: no progress", passes, i) 219 | } 220 | passes++ 221 | } 222 | if i != *numDir { 223 | t.Fatalf("Readdir %v: got %d entries, wanted %d", tmpDir, i, *numDir) 224 | } 225 | } 226 | 227 | func TestRename(t *testing.T) { 228 | var err error 229 | flag.Parse() 230 | ufs := new(ufs.Ufs) 231 | ufs.Dotu = false 232 | ufs.Id = "ufs" 233 | ufs.Debuglevel = *debug 234 | ufs.Msize = 8192 235 | ufs.Start(ufs) 236 | 237 | tmpDir, err := ioutil.TempDir("", "go9") 238 | if err != nil { 239 | t.Fatal("Can't create temp directory") 240 | } 241 | defer os.RemoveAll(tmpDir) 242 | ufs.Root = tmpDir 243 | t.Logf("ufs starting in %v", tmpDir) 244 | // determined by build tags 245 | //extraFuncs() 246 | l, err := net.Listen("unix", "") 247 | if err != nil { 248 | t.Fatalf("Can not start listener: %v", err) 249 | } 250 | srvAddr := l.Addr().String() 251 | t.Logf("Server is at %v", srvAddr) 252 | go func() { 253 | if err = ufs.StartListener(l); err != nil { 254 | t.Fatalf("Can not start listener: %v", err) 255 | } 256 | }() 257 | var conn net.Conn 258 | if conn, err = net.Dial("unix", srvAddr); err != nil { 259 | t.Fatalf("%v", err) 260 | } else { 261 | t.Logf("Got a conn, %v\n", conn) 262 | } 263 | 264 | clnt := NewClnt(conn, 8192, false) 265 | user := p.OsUsers.Uid2User(os.Geteuid()) 266 | rootfid, err := clnt.Attach(nil, user, "/") 267 | if err != nil { 268 | t.Fatalf("%v", err) 269 | } 270 | t.Logf("attached to %v, rootfid %v\n", tmpDir, rootfid) 271 | // OK, create a file behind go9ps back and then rename it. 272 | b := make([]byte, 0) 273 | from := path.Join(tmpDir, "a") 274 | to := path.Join(tmpDir, "b") 275 | if err = ioutil.WriteFile(from, b, 0666); err != nil { 276 | t.Fatalf("%v", err) 277 | } 278 | 279 | f := clnt.FidAlloc() 280 | if _, err = clnt.Walk(rootfid, f, []string{"a"}); err != nil { 281 | t.Fatalf("%v", err) 282 | } 283 | t.Logf("Walked to a") 284 | if _, err := clnt.Stat(f); err != nil { 285 | t.Fatalf("%v", err) 286 | } 287 | if err := clnt.FSync(f); err != nil { 288 | t.Fatalf("%v", err) 289 | } 290 | if err = clnt.Rename(f, "b"); err != nil { 291 | t.Errorf("%v", err) 292 | } 293 | // the old one should be gone, and the new one should be there. 294 | if _, err = ioutil.ReadFile(from); err == nil { 295 | t.Errorf("ReadFile(%v): got nil, want err", from) 296 | } 297 | 298 | if _, err = ioutil.ReadFile(to); err != nil { 299 | t.Errorf("ReadFile(%v): got %v, want nil", from, err) 300 | } 301 | 302 | // now try with an absolute path, which is supported on ufs servers. 303 | // It's not guaranteed to work on all servers, but it is hugely useful 304 | // on those that can do it -- which is almost all of them, save Plan 9 305 | // of course. 306 | from = to 307 | if err = clnt.Rename(f, "c"); err != nil { 308 | t.Errorf("%v", err) 309 | } 310 | 311 | // the old one should be gone, and the new one should be there. 312 | if _, err = ioutil.ReadFile(from); err == nil { 313 | t.Errorf("ReadFile(%v): got nil, want err", from) 314 | } 315 | 316 | to = path.Join(tmpDir, "c") 317 | if _, err = ioutil.ReadFile(to); err != nil { 318 | t.Errorf("ReadFile(%v): got %v, want nil", to, err) 319 | } 320 | 321 | // Make sure they can't walk out of the root. 322 | 323 | from = to 324 | if err = clnt.Rename(f, "../../../../d"); err != nil { 325 | t.Errorf("%v", err) 326 | } 327 | 328 | // the old one should be gone, and the new one should be there. 329 | if _, err = ioutil.ReadFile(from); err == nil { 330 | t.Errorf("ReadFile(%v): got nil, want err", from) 331 | } 332 | 333 | to = path.Join(tmpDir, "d") 334 | if _, err = ioutil.ReadFile(to); err != nil { 335 | t.Errorf("ReadFile(%v): got %v, want nil", to, err) 336 | } 337 | 338 | } 339 | -------------------------------------------------------------------------------- /p/clnt/clnt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The clnt package provides definitions and functions used to implement 6 | // a 9P2000 file client. 7 | package clnt 8 | 9 | import ( 10 | "fmt" 11 | "github.com/lionkov/go9p/p" 12 | "log" 13 | "net" 14 | "sync" 15 | "sync/atomic" 16 | ) 17 | 18 | // Debug flags 19 | const ( 20 | DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr 21 | DbgPrintPackets // print the raw packets on stderr 22 | DbgLogFcalls // keep the last N 9P messages (can be accessed over http) 23 | DbgLogPackets // keep the last N 9P messages (can be accessed over http) 24 | ) 25 | 26 | type StatsOps interface { 27 | statsRegister() 28 | statsUnregister() 29 | } 30 | 31 | // The Clnt type represents a 9P2000 client. The client is connected to 32 | // a 9P2000 file server and its methods can be used to access and manipulate 33 | // the files exported by the server. 34 | type Clnt struct { 35 | sync.Mutex 36 | Debuglevel int // =0 don't print anything, >0 print Fcalls, >1 print raw packets 37 | Msize uint32 // Maximum size of the 9P messages 38 | Dotu bool // If true, 9P2000.u protocol is spoken 39 | Root *Fid // Fid that points to the rood directory 40 | Id string // Used when printing debug messages 41 | Log *p.Logger 42 | 43 | conn net.Conn 44 | tagpool *pool 45 | fidpool *pool 46 | reqout chan *Req 47 | done chan bool 48 | reqfirst *Req 49 | reqlast *Req 50 | err error 51 | 52 | reqchan chan *Req 53 | tchan chan *p.Fcall 54 | 55 | next, prev *Clnt 56 | } 57 | 58 | // A Fid type represents a file on the server. Fids are used for the 59 | // low level methods that correspond directly to the 9P2000 message requests 60 | type Fid struct { 61 | sync.Mutex 62 | Clnt *Clnt // Client the fid belongs to 63 | Iounit uint32 64 | p.Qid // The Qid description for the file 65 | Mode uint8 // Open mode (one of p.O* values) (if file is open) 66 | Fid uint32 // Fid number 67 | p.User // The user the fid belongs to 68 | walked bool // true if the fid points to a walked file on the server 69 | } 70 | 71 | // The file is similar to the Fid, but is used in the high-level client 72 | // interface. 73 | type File struct { 74 | fid *Fid 75 | offset uint64 76 | } 77 | 78 | type pool struct { 79 | sync.Mutex 80 | need int 81 | nchan chan uint32 82 | maxid uint32 83 | imap []byte 84 | } 85 | 86 | type Req struct { 87 | sync.Mutex 88 | Clnt *Clnt 89 | Tc *p.Fcall 90 | Rc *p.Fcall 91 | Err error 92 | Done chan *Req 93 | tag uint16 94 | prev, next *Req 95 | fid *Fid 96 | } 97 | 98 | type ClntList struct { 99 | sync.Mutex 100 | clntList, clntLast *Clnt 101 | } 102 | 103 | var clnts *ClntList 104 | var DefaultDebuglevel int 105 | var DefaultLogger *p.Logger 106 | 107 | func (clnt *Clnt) Rpcnb(r *Req) error { 108 | var tag uint16 109 | 110 | if r.Tc.Type == p.Tversion { 111 | tag = p.NOTAG 112 | } else { 113 | tag = r.tag 114 | } 115 | 116 | p.SetTag(r.Tc, tag) 117 | clnt.Lock() 118 | if clnt.err != nil { 119 | clnt.Unlock() 120 | return clnt.err 121 | } 122 | 123 | if clnt.reqlast != nil { 124 | clnt.reqlast.next = r 125 | } else { 126 | clnt.reqfirst = r 127 | } 128 | 129 | r.prev = clnt.reqlast 130 | clnt.reqlast = r 131 | clnt.Unlock() 132 | 133 | clnt.reqout <- r 134 | return nil 135 | } 136 | 137 | func (clnt *Clnt) Rpc(tc *p.Fcall) (rc *p.Fcall, err error) { 138 | r := clnt.ReqAlloc() 139 | r.Tc = tc 140 | r.Done = make(chan *Req) 141 | err = clnt.Rpcnb(r) 142 | if err != nil { 143 | return 144 | } 145 | 146 | <-r.Done 147 | rc = r.Rc 148 | err = r.Err 149 | clnt.ReqFree(r) 150 | return 151 | } 152 | 153 | func (clnt *Clnt) recv() { 154 | var err error 155 | var buf []byte 156 | 157 | err = nil 158 | pos := 0 159 | for { 160 | // Connect can change the client Msize. 161 | clntmsize := int(atomic.LoadUint32(&clnt.Msize)) 162 | if len(buf) < clntmsize { 163 | b := make([]byte, clntmsize*8) 164 | copy(b, buf[0:pos]) 165 | buf = b 166 | b = nil 167 | } 168 | 169 | n, oerr := clnt.conn.Read(buf[pos:]) 170 | if oerr != nil || n == 0 { 171 | err = &p.Error{oerr.Error(), p.EIO} 172 | clnt.Lock() 173 | clnt.err = err 174 | clnt.Unlock() 175 | goto closed 176 | } 177 | 178 | pos += n 179 | for pos > 4 { 180 | sz, _ := p.Gint32(buf) 181 | if pos < int(sz) { 182 | if len(buf) < int(sz) { 183 | b := make([]byte, atomic.LoadUint32(&clnt.Msize)*8) 184 | copy(b, buf[0:pos]) 185 | buf = b 186 | b = nil 187 | } 188 | 189 | break 190 | } 191 | 192 | fc, err, fcsize := p.Unpack(buf, clnt.Dotu) 193 | clnt.Lock() 194 | if err != nil { 195 | clnt.err = err 196 | clnt.conn.Close() 197 | clnt.Unlock() 198 | goto closed 199 | } 200 | 201 | if clnt.Debuglevel > 0 { 202 | clnt.logFcall(fc) 203 | if clnt.Debuglevel&DbgPrintPackets != 0 { 204 | log.Println("}-}", clnt.Id, fmt.Sprint(fc.Pkt)) 205 | } 206 | 207 | if clnt.Debuglevel&DbgPrintFcalls != 0 { 208 | log.Println("}}}", clnt.Id, fc.String()) 209 | } 210 | } 211 | 212 | var r *Req = nil 213 | for r = clnt.reqfirst; r != nil; r = r.next { 214 | if r.Tc.Tag == fc.Tag { 215 | break 216 | } 217 | } 218 | 219 | if r == nil { 220 | clnt.err = &p.Error{"unexpected response", p.EINVAL} 221 | clnt.conn.Close() 222 | clnt.Unlock() 223 | goto closed 224 | } 225 | 226 | r.Rc = fc 227 | switch { 228 | case r.next == nil && r.prev == nil: 229 | clnt.reqlast = nil 230 | clnt.reqfirst = nil 231 | case r.next == nil: 232 | clnt.reqlast = r.prev 233 | r.prev.next = nil 234 | r.prev = nil 235 | case r.prev == nil: 236 | clnt.reqfirst = r.next 237 | r.next.prev = nil 238 | r.next = nil 239 | default: 240 | r.next.prev = r.prev 241 | r.prev.next = r.next 242 | r.next = nil 243 | r.prev = nil 244 | } 245 | clnt.Unlock() 246 | 247 | if r.Tc.Type != r.Rc.Type-1 { 248 | if r.Rc.Type != p.Rerror { 249 | r.Err = &p.Error{"invalid response", p.EINVAL} 250 | log.Println(fmt.Sprintf("TTT %v", r.Tc)) 251 | log.Println(fmt.Sprintf("RRR %v", r.Rc)) 252 | } else { 253 | if r.Err == nil { 254 | r.Err = &p.Error{r.Rc.Error, r.Rc.Errornum} 255 | } 256 | } 257 | } 258 | 259 | if r.Done != nil { 260 | r.Done <- r 261 | } 262 | 263 | pos -= fcsize 264 | buf = buf[fcsize:] 265 | } 266 | } 267 | 268 | closed: 269 | clnt.done <- true 270 | 271 | /* send error to all pending requests */ 272 | clnt.Lock() 273 | r := clnt.reqfirst 274 | clnt.reqfirst = nil 275 | clnt.reqlast = nil 276 | if err == nil { 277 | err = clnt.err 278 | } 279 | clnt.Unlock() 280 | for r != nil { 281 | next := r.next 282 | r.Err = err 283 | r.next = nil 284 | r.prev = nil 285 | if r.Done != nil { 286 | r.Done <- r 287 | } 288 | r = next 289 | } 290 | 291 | clnts.Lock() 292 | if clnt.prev != nil { 293 | clnt.prev.next = clnt.next 294 | clnt.prev = nil 295 | } else { 296 | clnts.clntList = clnt.next 297 | } 298 | 299 | if clnt.next != nil { 300 | clnt.next.prev = clnt.prev 301 | clnt.next = nil 302 | } else { 303 | clnts.clntLast = clnt.prev 304 | } 305 | clnts.Unlock() 306 | 307 | if sop, ok := (interface{}(clnt)).(StatsOps); ok { 308 | sop.statsUnregister() 309 | } 310 | } 311 | 312 | func (clnt *Clnt) send() { 313 | for { 314 | select { 315 | case <-clnt.done: 316 | return 317 | 318 | case req := <-clnt.reqout: 319 | if clnt.Debuglevel > 0 { 320 | clnt.logFcall(req.Tc) 321 | if clnt.Debuglevel&DbgPrintPackets != 0 { 322 | log.Println("{-{", clnt.Id, fmt.Sprint(req.Tc.Pkt)) 323 | } 324 | 325 | if clnt.Debuglevel&DbgPrintFcalls != 0 { 326 | log.Println("{{{", clnt.Id, req.Tc.String()) 327 | } 328 | } 329 | 330 | for buf := req.Tc.Pkt; len(buf) > 0; { 331 | n, err := clnt.conn.Write(buf) 332 | if err != nil { 333 | /* just close the socket, will get signal on clnt.done */ 334 | clnt.conn.Close() 335 | break 336 | } 337 | 338 | buf = buf[n:] 339 | } 340 | } 341 | } 342 | } 343 | 344 | // Creates and initializes a new Clnt object. Doesn't send any data 345 | // on the wire. 346 | func NewClnt(c net.Conn, msize uint32, dotu bool) *Clnt { 347 | clnt := new(Clnt) 348 | clnt.conn = c 349 | clnt.Msize = msize 350 | clnt.Dotu = dotu 351 | clnt.Debuglevel = DefaultDebuglevel 352 | clnt.Log = DefaultLogger 353 | clnt.Id = c.RemoteAddr().String() + ":" 354 | clnt.tagpool = newPool(uint32(p.NOTAG)) 355 | clnt.fidpool = newPool(p.NOFID) 356 | clnt.reqout = make(chan *Req) 357 | clnt.done = make(chan bool) 358 | clnt.reqchan = make(chan *Req, 16) 359 | clnt.tchan = make(chan *p.Fcall, 16) 360 | 361 | go clnt.recv() 362 | go clnt.send() 363 | 364 | clnts.Lock() 365 | if clnts.clntLast != nil { 366 | clnts.clntLast.next = clnt 367 | } else { 368 | clnts.clntList = clnt 369 | } 370 | 371 | clnt.prev = clnts.clntLast 372 | clnts.clntLast = clnt 373 | clnts.Unlock() 374 | 375 | if sop, ok := (interface{}(clnt)).(StatsOps); ok { 376 | sop.statsRegister() 377 | } 378 | 379 | return clnt 380 | } 381 | 382 | // Establishes a new socket connection to the 9P server and creates 383 | // a client object for it. Negotiates the dialect and msize for the 384 | // connection. Returns a Clnt object, or Error. 385 | func Connect(c net.Conn, msize uint32, dotu bool) (*Clnt, error) { 386 | clnt := NewClnt(c, msize, dotu) 387 | ver := "9P2000" 388 | if clnt.Dotu { 389 | ver = "9P2000.u" 390 | } 391 | 392 | clntmsize := atomic.LoadUint32(&clnt.Msize) 393 | tc := p.NewFcall(clntmsize) 394 | err := p.PackTversion(tc, clntmsize, ver) 395 | if err != nil { 396 | return nil, err 397 | } 398 | 399 | rc, err := clnt.Rpc(tc) 400 | if err != nil { 401 | return nil, err 402 | } 403 | 404 | if rc.Msize < atomic.LoadUint32(&clnt.Msize) { 405 | atomic.StoreUint32(&clnt.Msize, rc.Msize) 406 | } 407 | 408 | clnt.Dotu = rc.Version == "9P2000.u" && clnt.Dotu 409 | return clnt, nil 410 | } 411 | 412 | // Creates a new Fid object for the client 413 | func (clnt *Clnt) FidAlloc() *Fid { 414 | fid := new(Fid) 415 | fid.Fid = clnt.fidpool.getId() 416 | fid.Clnt = clnt 417 | 418 | return fid 419 | } 420 | 421 | func (clnt *Clnt) NewFcall() *p.Fcall { 422 | select { 423 | case tc := <-clnt.tchan: 424 | return tc 425 | default: 426 | } 427 | return p.NewFcall(atomic.LoadUint32(&clnt.Msize)) 428 | } 429 | 430 | func (clnt *Clnt) FreeFcall(fc *p.Fcall) { 431 | if false && fc != nil && len(fc.Buf) >= int(atomic.LoadUint32(&clnt.Msize)) { 432 | select { 433 | case clnt.tchan <- fc: 434 | break 435 | default: 436 | } 437 | } 438 | } 439 | 440 | func (clnt *Clnt) ReqAlloc() *Req { 441 | var req *Req 442 | select { 443 | case req = <-clnt.reqchan: 444 | break 445 | default: 446 | req = new(Req) 447 | req.Clnt = clnt 448 | req.tag = uint16(clnt.tagpool.getId()) 449 | } 450 | return req 451 | } 452 | 453 | func (clnt *Clnt) ReqFree(req *Req) { 454 | clnt.FreeFcall(req.Tc) 455 | req.Tc = nil 456 | req.Rc = nil 457 | req.Err = nil 458 | req.Done = nil 459 | 460 | select { 461 | case clnt.reqchan <- req: 462 | break 463 | default: 464 | clnt.tagpool.putId(uint32(req.tag)) 465 | } 466 | } 467 | 468 | func NewFile(f *Fid, offset uint64) *File { 469 | return &File{f, offset} 470 | } 471 | 472 | func (f *File) Fid() *Fid { 473 | return f.fid 474 | } 475 | 476 | func (clnt *Clnt) logFcall(fc *p.Fcall) { 477 | if clnt.Debuglevel&DbgLogPackets != 0 { 478 | pkt := make([]byte, len(fc.Pkt)) 479 | copy(pkt, fc.Pkt) 480 | clnt.Log.Log(pkt, clnt, DbgLogPackets) 481 | } 482 | 483 | if clnt.Debuglevel&DbgLogFcalls != 0 { 484 | f := new(p.Fcall) 485 | *f = *fc 486 | f.Pkt = nil 487 | clnt.Log.Log(f, clnt, DbgLogFcalls) 488 | } 489 | } 490 | 491 | func init() { 492 | clnts = new(ClntList) 493 | if sop, ok := (interface{}(clnts)).(StatsOps); ok { 494 | sop.statsRegister() 495 | } 496 | } 497 | -------------------------------------------------------------------------------- /p/clnt/close.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import "github.com/lionkov/go9p/p" 8 | 9 | // Clunks a fid. Returns nil if successful. 10 | func (clnt *Clnt) Clunk(fid *Fid) (err error) { 11 | err = nil 12 | if fid.walked { 13 | tc := clnt.NewFcall() 14 | err := p.PackTclunk(tc, fid.Fid) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | _, err = clnt.Rpc(tc) 20 | } 21 | 22 | clnt.fidpool.putId(fid.Fid) 23 | fid.walked = false 24 | fid.Fid = p.NOFID 25 | return 26 | } 27 | 28 | // Closes a file. Returns nil if successful. 29 | func (file *File) Close() error { 30 | // Should we cancel all pending requests for the File 31 | return file.fid.Clnt.Clunk(file.fid) 32 | } 33 | -------------------------------------------------------------------------------- /p/clnt/examples/cl/cl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // An interactive client for 9P servers. 4 | 5 | import ( 6 | "bufio" 7 | "github.com/lionkov/go9p/p" 8 | "github.com/lionkov/go9p/p/clnt" 9 | "flag" 10 | "fmt" 11 | "io" 12 | "os" 13 | "path" 14 | "strings" 15 | ) 16 | 17 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 18 | var ouser = flag.String("user", "", "user to connect as") 19 | var cmdfile = flag.String("file", "", "read commands from file") 20 | var prompt = flag.String("prompt", "9p> ", "prompt for interactive client") 21 | var debug = flag.Bool("d", false, "enable debugging (fcalls)") 22 | var debugall = flag.Bool("D", false, "enable debugging (raw packets)") 23 | var msize = flag.Uint("m", 8192, "Msize for 9p") 24 | 25 | var cwd = "/" 26 | var cfid *clnt.Fid 27 | 28 | type Cmd struct { 29 | fun func(c *clnt.Clnt, s []string) 30 | help string 31 | } 32 | 33 | var cmds map[string]*Cmd 34 | 35 | func init() { 36 | cmds = make(map[string]*Cmd) 37 | cmds["write"] = &Cmd{cmdwrite, "write file string [...]\t«write the unmodified string to file, create file if necessary»"} 38 | cmds["echo"] = &Cmd{cmdecho, "echo file string [...]\t«echo string to file (newline appended)»"} 39 | cmds["stat"] = &Cmd{cmdstat, "stat file [...]\t«stat file»"} 40 | cmds["ls"] = &Cmd{cmdls, "ls [-l] file [...]\t«list contents of directory or file»"} 41 | cmds["cd"] = &Cmd{cmdcd, "cd dir\t«change working directory»"} 42 | cmds["cat"] = &Cmd{cmdcat, "cat file [...]\t«print the contents of file»"} 43 | cmds["mkdir"] = &Cmd{cmdmkdir, "mkdir dir [...]\t«create dir on remote server»"} 44 | cmds["get"] = &Cmd{cmdget, "get file [local]\t«get file from remote server»"} 45 | cmds["put"] = &Cmd{cmdput, "put file [remote]\t«put file on the remote server as 'file'»"} 46 | cmds["pwd"] = &Cmd{cmdpwd, "pwd\t«print working directory»"} 47 | cmds["rm"] = &Cmd{cmdrm, "rm file [...]\t«remove file from remote server»"} 48 | cmds["help"] = &Cmd{cmdhelp, "help [cmd]\t«print available commands or help on cmd»"} 49 | cmds["quit"] = &Cmd{cmdquit, "quit\t«exit»"} 50 | cmds["exit"] = &Cmd{cmdquit, "exit\t«quit»"} 51 | } 52 | 53 | // normalize user-supplied path. path starting with '/' is left untouched, otherwise is considered 54 | // local from cwd 55 | func normpath(s string) string { 56 | if len(s) > 0 { 57 | if s[0] == '/' { 58 | return path.Clean(s) 59 | } 60 | return path.Clean(cwd + "/" + s) 61 | } 62 | return "/" 63 | } 64 | 65 | func b(mode uint32, s uint8) string { 66 | var bits = []string{"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"} 67 | return bits[(mode>>s)&7] 68 | } 69 | 70 | // Convert file mode bits to string representation 71 | func modetostr(mode uint32) string { 72 | d := "-" 73 | if mode&p.DMDIR != 0 { 74 | d = "d" 75 | } else if mode&p.DMAPPEND != 0 { 76 | d = "a" 77 | } 78 | return fmt.Sprintf("%s%s%s%s", d, b(mode, 6), b(mode, 3), b(mode, 0)) 79 | } 80 | 81 | // Write the string s to remote file f. Create f if it doesn't exist 82 | func writeone(c *clnt.Clnt, f, s string) { 83 | fname := normpath(f) 84 | file, oserr := c.FCreate(fname, 0666, p.OWRITE) 85 | if oserr != nil { 86 | file, oserr = c.FOpen(fname, p.OWRITE|p.OTRUNC) 87 | if oserr != nil { 88 | fmt.Fprintf(os.Stderr, "error opening %s: %v\n", fname, oserr) 89 | return 90 | } 91 | } 92 | defer file.Close() 93 | 94 | m, oserr := file.Write([]byte(s)) 95 | if oserr != nil { 96 | fmt.Fprintf(os.Stderr, "error writing to %s: %v\n", fname, oserr) 97 | return 98 | } 99 | 100 | if m != len(s) { 101 | fmt.Fprintf(os.Stderr, "short write %s\n", fname) 102 | return 103 | } 104 | } 105 | 106 | // Write s[1:] (with appended spaces) to the file s[0] 107 | func cmdwrite(c *clnt.Clnt, s []string) { 108 | fname := normpath(s[0]) 109 | str := strings.Join(s[1:], " ") 110 | writeone(c, fname, str) 111 | } 112 | 113 | // Echo (append newline) s[1:] to s[0] 114 | func cmdecho(c *clnt.Clnt, s []string) { 115 | fname := normpath(s[0]) 116 | str := strings.Join(s[1:], " ") + "\n" 117 | writeone(c, fname, str) 118 | } 119 | 120 | // Stat the remote file f 121 | func statone(c *clnt.Clnt, f string) { 122 | fname := normpath(f) 123 | 124 | stat, oserr := c.FStat(fname) 125 | if oserr != nil { 126 | fmt.Fprintf(os.Stderr, "error in stat %s: %v\n", fname, oserr) 127 | return 128 | } 129 | fmt.Fprintf(os.Stdout, "%s\n", stat) 130 | } 131 | 132 | func cmdstat(c *clnt.Clnt, s []string) { 133 | for _, f := range s { 134 | statone(c, normpath(f)) 135 | } 136 | } 137 | 138 | func dirtostr(d *p.Dir) string { 139 | return fmt.Sprintf("%s %s %s %-8d\t\t%s", modetostr(d.Mode), d.Uid, d.Gid, d.Length, d.Name) 140 | } 141 | 142 | func lsone(c *clnt.Clnt, s string, long bool) { 143 | st, oserr := c.FStat(normpath(s)) 144 | if oserr != nil { 145 | fmt.Fprintf(os.Stderr, "error stat: %v\n", oserr) 146 | return 147 | } 148 | if st.Mode&p.DMDIR != 0 { 149 | file, oserr := c.FOpen(s, p.OREAD) 150 | if oserr != nil { 151 | fmt.Fprintf(os.Stderr, "error opening dir: %s\n", oserr) 152 | return 153 | } 154 | defer file.Close() 155 | for { 156 | d, oserr := file.Readdir(0) 157 | if oserr != nil { 158 | fmt.Fprintf(os.Stderr, "error reading dir: %v\n", oserr) 159 | } 160 | if d == nil || len(d) == 0 { 161 | break 162 | } 163 | for _, dir := range d { 164 | if long { 165 | fmt.Fprintf(os.Stdout, "%s\n", dirtostr(dir)) 166 | } else { 167 | os.Stdout.WriteString(dir.Name + "\n") 168 | } 169 | } 170 | } 171 | } else { 172 | fmt.Fprintf(os.Stdout, "%s\n", dirtostr(st)) 173 | } 174 | } 175 | 176 | func cmdls(c *clnt.Clnt, s []string) { 177 | long := false 178 | if len(s) > 0 && s[0] == "-l" { 179 | long = true 180 | s = s[1:] 181 | } 182 | if len(s) == 0 { 183 | lsone(c, cwd, long) 184 | } else { 185 | for _, d := range s { 186 | lsone(c, cwd+d, long) 187 | } 188 | } 189 | } 190 | 191 | func walkone(c *clnt.Clnt, s string, fileok bool) { 192 | ncwd := normpath(s) 193 | 194 | fid, err := c.FWalk(ncwd) 195 | defer c.Clunk(fid) 196 | 197 | if err != nil { 198 | fmt.Fprintf(os.Stderr, "walk error: %s\n", err) 199 | return 200 | } 201 | 202 | if fileok != true && (fid.Type&p.QTDIR == 0) { 203 | fmt.Fprintf(os.Stderr, "can't cd to file [%s]\n", ncwd) 204 | return 205 | } 206 | 207 | cwd = ncwd 208 | } 209 | 210 | func cmdcd(c *clnt.Clnt, s []string) { 211 | if s != nil { 212 | walkone(c, strings.Join(s, "/"), false) 213 | } 214 | } 215 | 216 | // Print the contents of f 217 | func cmdcat(c *clnt.Clnt, s []string) { 218 | buf := make([]byte, 8192) 219 | Outer: 220 | for _, f := range s { 221 | fname := normpath(f) 222 | file, oserr := c.FOpen(fname, p.OREAD) 223 | if oserr != nil { 224 | fmt.Fprintf(os.Stderr, "error opening %s: %v\n", f, oserr) 225 | continue Outer 226 | } 227 | defer file.Close() 228 | for { 229 | n, oserr := file.Read(buf) 230 | if oserr != nil && oserr != io.EOF { 231 | fmt.Fprintf(os.Stderr, "error reading %s: %v\n", f, oserr) 232 | } 233 | if n == 0 { 234 | break 235 | } 236 | os.Stdout.Write(buf[0:n]) 237 | } 238 | } 239 | } 240 | 241 | // Create a single directory on remote server 242 | func mkone(c *clnt.Clnt, s string) { 243 | fname := normpath(s) 244 | file, oserr := c.FCreate(fname, 0777|p.DMDIR, p.OWRITE) 245 | if oserr != nil { 246 | fmt.Fprintf(os.Stderr, "error creating directory %s: %v\n", fname, oserr) 247 | return 248 | } 249 | file.Close() 250 | } 251 | 252 | // Create directories on remote server 253 | func cmdmkdir(c *clnt.Clnt, s []string) { 254 | for _, f := range s { 255 | mkone(c, f) 256 | } 257 | } 258 | 259 | // Copy a remote file to local filesystem 260 | func cmdget(c *clnt.Clnt, s []string) { 261 | var from, to string 262 | switch len(s) { 263 | case 1: 264 | from = normpath(s[0]) 265 | _, to = path.Split(s[0]) 266 | case 2: 267 | from, to = normpath(s[0]), s[1] 268 | default: 269 | fmt.Fprintf(os.Stderr, "from arguments; usage: get from to\n") 270 | } 271 | 272 | tofile, err := os.Create(to) 273 | if err != nil { 274 | fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err) 275 | return 276 | } 277 | defer tofile.Close() 278 | 279 | file, ferr := c.FOpen(from, p.OREAD) 280 | if ferr != nil { 281 | fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err) 282 | return 283 | } 284 | defer file.Close() 285 | 286 | buf := make([]byte, 8192) 287 | for { 288 | n, oserr := file.Read(buf) 289 | if oserr != nil { 290 | fmt.Fprintf(os.Stderr, "error reading %s: %s\n", from, oserr) 291 | return 292 | } 293 | if n == 0 { 294 | break 295 | } 296 | 297 | m, err := tofile.Write(buf[0:n]) 298 | if err != nil { 299 | fmt.Fprintf(os.Stderr, "error writing %s: %s\n", to, err) 300 | return 301 | } 302 | 303 | if m != n { 304 | fmt.Fprintf(os.Stderr, "short write %s\n", to) 305 | return 306 | } 307 | } 308 | } 309 | 310 | // Copy a local file to remote server 311 | func cmdput(c *clnt.Clnt, s []string) { 312 | var from, to string 313 | switch len(s) { 314 | case 1: 315 | _, to = path.Split(s[0]) 316 | to = normpath(to) 317 | from = s[0] 318 | case 2: 319 | from, to = s[0], normpath(s[1]) 320 | default: 321 | fmt.Fprintf(os.Stderr, "incorrect arguments; usage: put local [remote]\n") 322 | } 323 | 324 | fromfile, err := os.Open(from) 325 | if err != nil { 326 | fmt.Fprintf(os.Stderr, "error opening %s for reading: %s\n", from, err) 327 | return 328 | } 329 | defer fromfile.Close() 330 | 331 | file, ferr := c.FOpen(to, p.OWRITE|p.OTRUNC) 332 | if ferr != nil { 333 | file, ferr = c.FCreate(to, 0666, p.OWRITE) 334 | if ferr != nil { 335 | fmt.Fprintf(os.Stderr, "error opening %s for writing: %s\n", to, err) 336 | return 337 | } 338 | } 339 | defer file.Close() 340 | 341 | buf := make([]byte, 8192) 342 | for { 343 | n, oserr := fromfile.Read(buf) 344 | if oserr != nil && oserr != io.EOF { 345 | fmt.Fprintf(os.Stderr, "error reading %s: %s\n", from, oserr) 346 | return 347 | } 348 | 349 | if n == 0 { 350 | break 351 | } 352 | 353 | m, oserr := file.Write(buf[0:n]) 354 | if oserr != nil { 355 | fmt.Fprintf(os.Stderr, "error writing %s: %v\n", to, oserr) 356 | return 357 | } 358 | 359 | if m != n { 360 | fmt.Fprintf(os.Stderr, "short write %s\n", to) 361 | return 362 | } 363 | } 364 | } 365 | 366 | func cmdpwd(c *clnt.Clnt, s []string) { fmt.Fprintf(os.Stdout, cwd+"\n") } 367 | 368 | // Remove f from remote server 369 | func rmone(c *clnt.Clnt, f string) { 370 | fname := normpath(f) 371 | 372 | err := c.FRemove(fname) 373 | if err != nil { 374 | fmt.Fprintf(os.Stderr, "error in stat %s", err) 375 | return 376 | } 377 | } 378 | 379 | // Remove one or more files from the server 380 | func cmdrm(c *clnt.Clnt, s []string) { 381 | for _, f := range s { 382 | rmone(c, normpath(f)) 383 | } 384 | } 385 | 386 | // Print available commands 387 | func cmdhelp(c *clnt.Clnt, s []string) { 388 | cmdstr := "" 389 | if len(s) > 0 { 390 | for _, h := range s { 391 | v, ok := cmds[h] 392 | if ok { 393 | cmdstr = cmdstr + v.help + "\n" 394 | } else { 395 | cmdstr = cmdstr + "unknown command: " + h + "\n" 396 | } 397 | } 398 | } else { 399 | cmdstr = "available commands: " 400 | for k := range cmds { 401 | cmdstr = cmdstr + " " + k 402 | } 403 | cmdstr = cmdstr + "\n" 404 | } 405 | fmt.Fprintf(os.Stdout, "%s", cmdstr) 406 | } 407 | 408 | func cmdquit(c *clnt.Clnt, s []string) { os.Exit(0) } 409 | 410 | func cmd(c *clnt.Clnt, cmd string) { 411 | ncmd := strings.Fields(cmd) 412 | if len(ncmd) <= 0 { 413 | return 414 | } 415 | v, ok := cmds[ncmd[0]] 416 | if ok == false { 417 | fmt.Fprintf(os.Stderr, "unknown command: %s\n", ncmd[0]) 418 | return 419 | } 420 | v.fun(c, ncmd[1:]) 421 | return 422 | } 423 | 424 | func interactive(c *clnt.Clnt) { 425 | reader := bufio.NewReaderSize(os.Stdin, 8192) 426 | for { 427 | fmt.Print(*prompt) 428 | line, err := reader.ReadSlice('\n') 429 | if err != nil { 430 | fmt.Fprintf(os.Stderr, "exiting...\n") 431 | break 432 | } 433 | str := strings.TrimSpace(string(line)) 434 | // TODO: handle larger input lines by doubling buffer 435 | in := strings.Split(str, "\n") 436 | for i := range in { 437 | if len(in[i]) > 0 { 438 | cmd(c, in[i]) 439 | } 440 | } 441 | } 442 | } 443 | 444 | func main() { 445 | var user p.User 446 | var err error 447 | var c *clnt.Clnt 448 | var file *clnt.File 449 | 450 | flag.Parse() 451 | 452 | if *ouser == "" { 453 | user = p.OsUsers.Uid2User(os.Geteuid()) 454 | } else { 455 | user = p.OsUsers.Uname2User(*ouser) 456 | } 457 | 458 | naddr := *addr 459 | if strings.LastIndex(naddr, ":") == -1 { 460 | naddr = naddr + ":5640" 461 | } 462 | c, err = clnt.Mount("tcp", naddr, "", uint32(*msize), user) 463 | if err != nil { 464 | fmt.Fprintf(os.Stderr, "error mounting %s: %s\n", naddr, err) 465 | os.Exit(1) 466 | } 467 | 468 | if *debug { 469 | c.Debuglevel = 1 470 | } 471 | if *debugall { 472 | c.Debuglevel = 2 473 | } 474 | 475 | walkone(c, "/", false) 476 | 477 | if file != nil { 478 | //process(c) 479 | fmt.Fprint(os.Stderr, "file reading unimplemented\n") 480 | } else if flag.NArg() > 0 { 481 | flags := flag.Args() 482 | for _, uc := range flags { 483 | cmd(c, uc) 484 | } 485 | } else { 486 | interactive(c) 487 | } 488 | 489 | return 490 | } 491 | -------------------------------------------------------------------------------- /p/clnt/examples/ls/ls.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lionkov/go9p/p" 5 | "github.com/lionkov/go9p/p/clnt" 6 | "flag" 7 | "io" 8 | "log" 9 | "os" 10 | ) 11 | 12 | var debuglevel = flag.Int("d", 0, "debuglevel") 13 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 14 | var msize = flag.Uint("m", 8192, "Msize for 9p") 15 | 16 | func main() { 17 | var user p.User 18 | var err error 19 | var c *clnt.Clnt 20 | var file *clnt.File 21 | var d []*p.Dir 22 | 23 | flag.Parse() 24 | user = p.OsUsers.Uid2User(os.Geteuid()) 25 | clnt.DefaultDebuglevel = *debuglevel 26 | c, err = clnt.Mount("tcp", *addr, "", uint32(*msize), user) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | lsarg := "/" 32 | if flag.NArg() == 1 { 33 | lsarg = flag.Arg(0) 34 | } else if flag.NArg() > 1 { 35 | log.Fatal("error: only one argument expected") 36 | } 37 | 38 | file, err = c.FOpen(lsarg, p.OREAD) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | for { 44 | d, err = file.Readdir(0) 45 | if d == nil || len(d) == 0 || err != nil { 46 | break 47 | } 48 | 49 | for i := 0; i < len(d); i++ { 50 | os.Stdout.WriteString(d[i].Name + "\n") 51 | } 52 | } 53 | 54 | file.Close() 55 | if err != nil && err != io.EOF { 56 | log.Fatal(err) 57 | } 58 | 59 | return 60 | } 61 | -------------------------------------------------------------------------------- /p/clnt/examples/read/read.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lionkov/go9p/p" 5 | "github.com/lionkov/go9p/p/clnt" 6 | "flag" 7 | "io" 8 | "log" 9 | "os" 10 | ) 11 | 12 | var debuglevel = flag.Int("d", 0, "debuglevel") 13 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 14 | var msize = flag.Uint("m", 8192, "Msize for 9p") 15 | 16 | func main() { 17 | var n int 18 | var user p.User 19 | var err error 20 | var c *clnt.Clnt 21 | var file *clnt.File 22 | var buf []byte 23 | 24 | flag.Parse() 25 | user = p.OsUsers.Uid2User(os.Geteuid()) 26 | clnt.DefaultDebuglevel = *debuglevel 27 | c, err = clnt.Mount("tcp", *addr, "", uint32(*msize), user) 28 | if err != nil { 29 | goto error 30 | } 31 | 32 | if flag.NArg() != 1 { 33 | log.Println("invalid arguments") 34 | return 35 | } 36 | 37 | file, err = c.FOpen(flag.Arg(0), p.OREAD) 38 | if err != nil { 39 | goto error 40 | } 41 | 42 | buf = make([]byte, 8192) 43 | for { 44 | n, err = file.Read(buf) 45 | if n == 0 { 46 | break 47 | } 48 | 49 | os.Stdout.Write(buf[0:n]) 50 | } 51 | 52 | file.Close() 53 | 54 | if err != nil && err != io.EOF { 55 | goto error 56 | } 57 | 58 | return 59 | 60 | error: 61 | log.Println("Error", err) 62 | } 63 | -------------------------------------------------------------------------------- /p/clnt/examples/tag/tag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lionkov/go9p/p" 5 | "github.com/lionkov/go9p/p/clnt" 6 | "flag" 7 | "log" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var debuglevel = flag.Int("d", 0, "debuglevel") 13 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 14 | var msize = flag.Uint("m", 8192, "Msize for 9p") 15 | 16 | func main() { 17 | var user p.User 18 | var ba [][]byte 19 | var nreqs int 20 | var rchan chan *clnt.Req 21 | var tag *clnt.Tag 22 | var fid *clnt.Fid 23 | var wnames []string 24 | 25 | flag.Parse() 26 | user = p.OsUsers.Uid2User(os.Geteuid()) 27 | clnt.DefaultDebuglevel = *debuglevel 28 | c, err := clnt.Mount("tcp", *addr, "", uint32(*msize), user) 29 | if err != nil { 30 | goto error 31 | } 32 | 33 | if flag.NArg() != 1 { 34 | log.Println("invalid arguments") 35 | return 36 | } 37 | 38 | ba = make([][]byte, 100) 39 | for i := 0; i < len(ba); i++ { 40 | ba[i] = make([]byte, 8192) 41 | } 42 | 43 | nreqs = 0 44 | rchan = make(chan *clnt.Req) 45 | tag = c.TagAlloc(rchan) 46 | 47 | // walk the file 48 | wnames = strings.Split(flag.Arg(0), "/") 49 | for wnames[0] == "" { 50 | wnames = wnames[1:] 51 | } 52 | 53 | fid = c.FidAlloc() 54 | for root := c.Root; len(wnames) > 0; root = fid { 55 | n := len(wnames) 56 | if n > 8 { 57 | n = 8 58 | } 59 | 60 | err = tag.Walk(root, fid, wnames[0:n]) 61 | if err != nil { 62 | goto error 63 | } 64 | 65 | nreqs++ 66 | wnames = wnames[n:] 67 | } 68 | err = tag.Open(fid, p.OREAD) 69 | if err != nil { 70 | goto error 71 | } 72 | 73 | for i := 0; i < len(ba); i++ { 74 | err = tag.Read(fid, uint64(i*8192), 8192) 75 | if err != nil { 76 | goto error 77 | } 78 | nreqs++ 79 | } 80 | 81 | err = tag.Clunk(fid) 82 | 83 | // now start reading... 84 | for nreqs > 0 { 85 | r := <-rchan 86 | if r.Tc.Type == p.Tread { 87 | i := r.Tc.Offset / 8192 88 | copy(ba[i], r.Rc.Data) 89 | ba[i] = ba[i][0:r.Rc.Count] 90 | } 91 | nreqs-- 92 | } 93 | 94 | for i := 0; i < len(ba); i++ { 95 | os.Stdout.Write(ba[i]) 96 | } 97 | 98 | return 99 | 100 | error: 101 | log.Println("error: ", err) 102 | } 103 | -------------------------------------------------------------------------------- /p/clnt/examples/tls/tls.go: -------------------------------------------------------------------------------- 1 | // Connects to a server over TLS and lists the specified directory 2 | package main 3 | 4 | import ( 5 | "github.com/lionkov/go9p/p" 6 | "github.com/lionkov/go9p/p/clnt" 7 | "crypto/rand" 8 | "crypto/tls" 9 | "flag" 10 | "fmt" 11 | "log" 12 | "os" 13 | ) 14 | 15 | var debuglevel = flag.Int("d", 0, "debuglevel") 16 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 17 | var msize = flag.Uint("m", 8192, "Msize for 9p") 18 | 19 | func main() { 20 | var user p.User 21 | var file *clnt.File 22 | 23 | flag.Parse() 24 | user = p.OsUsers.Uid2User(os.Geteuid()) 25 | clnt.DefaultDebuglevel = *debuglevel 26 | 27 | c, oerr := tls.Dial("tcp", *addr, &tls.Config{ 28 | Rand: rand.Reader, 29 | InsecureSkipVerify: true, 30 | }) 31 | if oerr != nil { 32 | log.Println("can't dial", oerr) 33 | return 34 | } 35 | 36 | clnt, err := clnt.MountConn(c, "", uint32(*msize), user) 37 | if err != nil { 38 | goto error 39 | } 40 | 41 | if flag.NArg() != 1 { 42 | log.Println("invalid arguments") 43 | return 44 | } 45 | 46 | file, oerr = clnt.FOpen(flag.Arg(0), p.OREAD) 47 | if oerr != nil { 48 | goto oerror 49 | } 50 | 51 | for { 52 | d, oerr := file.Readdir(0) 53 | if oerr != nil { 54 | goto oerror 55 | } 56 | 57 | if d == nil || len(d) == 0 { 58 | break 59 | } 60 | 61 | for i := 0; i < len(d); i++ { 62 | os.Stdout.WriteString(d[i].Name + "\n") 63 | } 64 | } 65 | 66 | file.Close() 67 | return 68 | 69 | error: 70 | log.Println(fmt.Sprintf("Error: %s", err)) 71 | return 72 | 73 | oerror: 74 | log.Println("Error", oerr) 75 | } 76 | -------------------------------------------------------------------------------- /p/clnt/examples/write/write.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/lionkov/go9p/p" 5 | "github.com/lionkov/go9p/p/clnt" 6 | "flag" 7 | "io" 8 | "log" 9 | "os" 10 | ) 11 | 12 | var debuglevel = flag.Int("d", 0, "debuglevel") 13 | var addr = flag.String("addr", "127.0.0.1:5640", "network address") 14 | var msize = flag.Uint("m", 8192, "Msize for 9p") 15 | 16 | func main() { 17 | var n, m int 18 | var user p.User 19 | var err error 20 | var c *clnt.Clnt 21 | var file *clnt.File 22 | var buf []byte 23 | 24 | flag.Parse() 25 | user = p.OsUsers.Uid2User(os.Geteuid()) 26 | clnt.DefaultDebuglevel = *debuglevel 27 | c, err = clnt.Mount("tcp", *addr, "", uint32(*msize), user) 28 | if err != nil { 29 | goto error 30 | } 31 | 32 | if flag.NArg() != 1 { 33 | log.Println("invalid arguments") 34 | return 35 | } 36 | 37 | file, err = c.FOpen(flag.Arg(0), p.OWRITE|p.OTRUNC) 38 | if err != nil { 39 | file, err = c.FCreate(flag.Arg(0), 0666, p.OWRITE) 40 | if err != nil { 41 | goto error 42 | } 43 | } 44 | 45 | buf = make([]byte, 8192) 46 | for { 47 | n, err = os.Stdin.Read(buf) 48 | if err != nil && err != io.EOF { 49 | goto error 50 | } 51 | 52 | if n == 0 { 53 | break 54 | } 55 | 56 | m, err = file.Write(buf[0:n]) 57 | if err != nil { 58 | goto error 59 | } 60 | 61 | if m != n { 62 | err = &p.Error{"short write", 0} 63 | goto error 64 | } 65 | } 66 | 67 | file.Close() 68 | return 69 | 70 | error: 71 | log.Println("Error", err) 72 | } 73 | -------------------------------------------------------------------------------- /p/clnt/mount.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "net" 10 | ) 11 | 12 | // Creates an authentication fid for the specified user. Returns the fid, if 13 | // successful, or an Error. 14 | func (clnt *Clnt) Auth(user p.User, aname string) (*Fid, error) { 15 | fid := clnt.FidAlloc() 16 | tc := clnt.NewFcall() 17 | err := p.PackTauth(tc, fid.Fid, user.Name(), aname, uint32(user.Id()), clnt.Dotu) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | _, err = clnt.Rpc(tc) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | fid.Iounit = clnt.Msize - p.IOHDRSZ 28 | fid.User = user 29 | fid.walked = true 30 | return fid, nil 31 | } 32 | 33 | // Creates a fid for the specified user that points to the root 34 | // of the file server's file tree. Returns a Fid pointing to the root, 35 | // if successful, or an Error. 36 | func (clnt *Clnt) Attach(afid *Fid, user p.User, aname string) (*Fid, error) { 37 | var afno uint32 38 | 39 | if afid != nil { 40 | afno = afid.Fid 41 | } else { 42 | afno = p.NOFID 43 | } 44 | 45 | fid := clnt.FidAlloc() 46 | tc := clnt.NewFcall() 47 | err := p.PackTattach(tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), clnt.Dotu) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | rc, err := clnt.Rpc(tc) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | fid.Qid = rc.Qid 58 | fid.User = user 59 | fid.walked = true 60 | clnt.Root = fid 61 | return fid, nil 62 | } 63 | 64 | // Connects to a file server and attaches to it as the specified user. 65 | func Mount(ntype, addr, aname string, msize uint32, user p.User) (*Clnt, error) { 66 | c, e := net.Dial(ntype, addr) 67 | if e != nil { 68 | return nil, &p.Error{e.Error(), p.EIO} 69 | } 70 | 71 | return MountConn(c, aname, msize, user) 72 | } 73 | 74 | func MountConn(c net.Conn, aname string, msize uint32, user p.User) (*Clnt, error) { 75 | clnt, err := Connect(c, msize+p.IOHDRSZ, true) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | fid, err := clnt.Attach(nil, user, aname) 81 | if err != nil { 82 | clnt.Unmount() 83 | return nil, err 84 | } 85 | 86 | clnt.Root = fid 87 | return clnt, nil 88 | } 89 | 90 | // Closes the connection to the file sever. 91 | func (clnt *Clnt) Unmount() { 92 | clnt.Lock() 93 | clnt.err = &p.Error{"connection closed", p.EIO} 94 | clnt.conn.Close() 95 | clnt.Unlock() 96 | } 97 | -------------------------------------------------------------------------------- /p/clnt/open.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "strings" 10 | ) 11 | 12 | // Opens the file associated with the fid. Returns nil if 13 | // the operation is successful. 14 | func (clnt *Clnt) Open(fid *Fid, mode uint8) error { 15 | tc := clnt.NewFcall() 16 | err := p.PackTopen(tc, fid.Fid, mode) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | rc, err := clnt.Rpc(tc) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | fid.Qid = rc.Qid 27 | fid.Iounit = rc.Iounit 28 | if fid.Iounit == 0 || fid.Iounit > clnt.Msize-p.IOHDRSZ { 29 | fid.Iounit = clnt.Msize - p.IOHDRSZ 30 | } 31 | fid.Mode = mode 32 | return nil 33 | } 34 | 35 | // Creates a file in the directory associated with the fid. Returns nil 36 | // if the operation is successful. 37 | func (clnt *Clnt) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error { 38 | tc := clnt.NewFcall() 39 | err := p.PackTcreate(tc, fid.Fid, name, perm, mode, ext, clnt.Dotu) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | rc, err := clnt.Rpc(tc) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | fid.Qid = rc.Qid 50 | fid.Iounit = rc.Iounit 51 | if fid.Iounit == 0 || fid.Iounit > clnt.Msize-p.IOHDRSZ { 52 | fid.Iounit = clnt.Msize - p.IOHDRSZ 53 | } 54 | fid.Mode = mode 55 | return nil 56 | } 57 | 58 | // Creates and opens a named file. 59 | // Returns the file if the operation is successful, or an Error. 60 | func (clnt *Clnt) FCreate(path string, perm uint32, mode uint8) (*File, error) { 61 | n := strings.LastIndex(path, "/") 62 | if n < 0 { 63 | n = 0 64 | } 65 | 66 | fid, err := clnt.FWalk(path[0:n]) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | if path[n] == '/' { 72 | n++ 73 | } 74 | 75 | err = clnt.Create(fid, path[n:], perm, mode, "") 76 | if err != nil { 77 | clnt.Clunk(fid) 78 | return nil, err 79 | } 80 | 81 | return &File{fid, 0}, nil 82 | } 83 | 84 | // Opens a named file. Returns the opened file, or an Error. 85 | func (clnt *Clnt) FOpen(path string, mode uint8) (*File, error) { 86 | fid, err := clnt.FWalk(path) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | err = clnt.Open(fid, mode) 92 | if err != nil { 93 | clnt.Clunk(fid) 94 | return nil, err 95 | } 96 | 97 | return &File{fid, 0}, nil 98 | } 99 | -------------------------------------------------------------------------------- /p/clnt/pool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | var m2id = [...]uint8{ 8 | 0, 1, 0, 2, 0, 1, 0, 3, 9 | 0, 1, 0, 2, 0, 1, 0, 4, 10 | 0, 1, 0, 2, 0, 1, 0, 3, 11 | 0, 1, 0, 2, 0, 1, 0, 5, 12 | 0, 1, 0, 2, 0, 1, 0, 3, 13 | 0, 1, 0, 2, 0, 1, 0, 4, 14 | 0, 1, 0, 2, 0, 1, 0, 3, 15 | 0, 1, 0, 2, 0, 1, 0, 6, 16 | 0, 1, 0, 2, 0, 1, 0, 3, 17 | 0, 1, 0, 2, 0, 1, 0, 4, 18 | 0, 1, 0, 2, 0, 1, 0, 3, 19 | 0, 1, 0, 2, 0, 1, 0, 5, 20 | 0, 1, 0, 2, 0, 1, 0, 3, 21 | 0, 1, 0, 2, 0, 1, 0, 4, 22 | 0, 1, 0, 2, 0, 1, 0, 3, 23 | 0, 1, 0, 2, 0, 1, 0, 7, 24 | 0, 1, 0, 2, 0, 1, 0, 3, 25 | 0, 1, 0, 2, 0, 1, 0, 4, 26 | 0, 1, 0, 2, 0, 1, 0, 3, 27 | 0, 1, 0, 2, 0, 1, 0, 5, 28 | 0, 1, 0, 2, 0, 1, 0, 3, 29 | 0, 1, 0, 2, 0, 1, 0, 4, 30 | 0, 1, 0, 2, 0, 1, 0, 3, 31 | 0, 1, 0, 2, 0, 1, 0, 6, 32 | 0, 1, 0, 2, 0, 1, 0, 3, 33 | 0, 1, 0, 2, 0, 1, 0, 4, 34 | 0, 1, 0, 2, 0, 1, 0, 3, 35 | 0, 1, 0, 2, 0, 1, 0, 5, 36 | 0, 1, 0, 2, 0, 1, 0, 3, 37 | 0, 1, 0, 2, 0, 1, 0, 4, 38 | 0, 1, 0, 2, 0, 1, 0, 3, 39 | 0, 1, 0, 2, 0, 1, 0, 0, 40 | } 41 | 42 | func newPool(maxid uint32) *pool { 43 | p := new(pool) 44 | p.maxid = maxid 45 | p.nchan = make(chan uint32) 46 | 47 | return p 48 | } 49 | 50 | func (p *pool) getId() uint32 { 51 | var n uint32 = 0 52 | var ret uint32 53 | 54 | p.Lock() 55 | for n = 0; n < uint32(len(p.imap)); n++ { 56 | if p.imap[n] != 0xFF { 57 | break 58 | } 59 | } 60 | 61 | if int(n) >= len(p.imap) { 62 | m := uint32(len(p.imap) + 32) 63 | if uint32(m*8) > p.maxid { 64 | m = p.maxid/8 + 1 65 | } 66 | 67 | b := make([]byte, m) 68 | copy(b, p.imap) 69 | p.imap = b 70 | } 71 | 72 | if n >= uint32(len(p.imap)) { 73 | p.need++ 74 | p.Unlock() 75 | ret = <-p.nchan 76 | } else { 77 | ret = uint32(m2id[p.imap[n]]) 78 | p.imap[n] |= 1 << ret 79 | ret += n * 8 80 | p.Unlock() 81 | } 82 | 83 | return ret 84 | } 85 | 86 | func (p *pool) putId(id uint32) { 87 | p.Lock() 88 | if p.need > 0 { 89 | p.nchan <- id 90 | p.need-- 91 | p.Unlock() 92 | return 93 | } 94 | 95 | p.imap[id/8] &= ^(1 << (id % 8)) 96 | p.Unlock() 97 | } 98 | -------------------------------------------------------------------------------- /p/clnt/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "io" 10 | ) 11 | 12 | // Reads count bytes starting from offset from the file associated with the fid. 13 | // Returns a slice with the data read, if the operation was successful, or an 14 | // Error. 15 | func (clnt *Clnt) Read(fid *Fid, offset uint64, count uint32) ([]byte, error) { 16 | if count > fid.Iounit { 17 | count = fid.Iounit 18 | } 19 | 20 | tc := clnt.NewFcall() 21 | err := p.PackTread(tc, fid.Fid, offset, count) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | rc, err := clnt.Rpc(tc) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return rc.Data, nil 32 | } 33 | 34 | // Reads up to len(buf) bytes from the File. Returns the number 35 | // of bytes read, or an Error. 36 | func (file *File) Read(buf []byte) (int, error) { 37 | n, err := file.ReadAt(buf, int64(file.offset)) 38 | if err == nil { 39 | file.offset += uint64(n) 40 | } 41 | 42 | return n, err 43 | } 44 | 45 | // Reads up to len(buf) bytes from the file starting from offset. 46 | // Returns the number of bytes read, or an Error. 47 | func (file *File) ReadAt(buf []byte, offset int64) (int, error) { 48 | b, err := file.fid.Clnt.Read(file.fid, uint64(offset), uint32(len(buf))) 49 | if err != nil { 50 | return 0, err 51 | } 52 | 53 | if len(b) == 0 { 54 | return 0, io.EOF 55 | } 56 | 57 | copy(buf, b) 58 | return len(b), nil 59 | } 60 | 61 | // Reads exactly len(buf) bytes from the File starting from offset. 62 | // Returns the number of bytes read (could be less than len(buf) if 63 | // end-of-file is reached), or an Error. 64 | func (file *File) Readn(buf []byte, offset uint64) (int, error) { 65 | ret := 0 66 | for len(buf) > 0 { 67 | n, err := file.ReadAt(buf, int64(offset)) 68 | if err != nil { 69 | return 0, err 70 | } 71 | 72 | if n == 0 { 73 | break 74 | } 75 | 76 | buf = buf[n:] 77 | offset += uint64(n) 78 | ret += n 79 | } 80 | 81 | return ret, nil 82 | } 83 | 84 | // Reads the content of the directory associated with the File. 85 | // Returns an array of maximum num entries (if num is 0, returns 86 | // all entries from the directory). If the operation fails, returns 87 | // an Error. 88 | func (file *File) Readdir(num int) ([]*p.Dir, error) { 89 | buf := make([]byte, file.fid.Clnt.Msize-p.IOHDRSZ) 90 | var dirs []*p.Dir 91 | pos := 0 92 | for { 93 | n, err := file.Read(buf) 94 | if err != nil && err != io.EOF { 95 | return dirs[0:pos], err 96 | } 97 | 98 | if n == 0 { 99 | return dirs[0:pos], io.EOF 100 | } 101 | 102 | var d *p.Dir 103 | b := buf[:n] 104 | for len(b) > 0 { 105 | var perr error 106 | d, b, _, perr = p.UnpackDir(b, file.Fid().Clnt.Dotu) 107 | if perr != nil { 108 | // If we have unpacked anything, it is almost certainly 109 | // a too-short buffer. So return what we got. 110 | if pos > 0 { 111 | return dirs[0:pos], nil 112 | } 113 | return nil, perr 114 | } 115 | 116 | dirs = append(dirs, d) 117 | pos++ 118 | if num != 0 && pos >= num { 119 | break 120 | } 121 | } 122 | } 123 | return dirs[0:pos], nil 124 | } 125 | -------------------------------------------------------------------------------- /p/clnt/remove.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import "github.com/lionkov/go9p/p" 8 | 9 | // Removes the file associated with the Fid. Returns nil if the 10 | // operation is successful. 11 | func (clnt *Clnt) Remove(fid *Fid) error { 12 | tc := clnt.NewFcall() 13 | err := p.PackTremove(tc, fid.Fid) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | _, err = clnt.Rpc(tc) 19 | clnt.fidpool.putId(fid.Fid) 20 | fid.Fid = p.NOFID 21 | 22 | return err 23 | } 24 | 25 | // Removes the named file. Returns nil if the operation is successful. 26 | func (clnt *Clnt) FRemove(path string) error { 27 | var err error 28 | fid, err := clnt.FWalk(path) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | err = clnt.Remove(fid) 34 | return err 35 | } 36 | -------------------------------------------------------------------------------- /p/clnt/seek.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | ) 10 | 11 | var Eisdir = &p.Error{"file is a directory", p.EIO} 12 | var Enegoff = &p.Error{"negative i/o offset", p.EIO} 13 | 14 | // Seek sets the offset for the next Read or Write to offset, 15 | // interpreted according to whence: 0 means relative to the origin of 16 | // the file, 1 means relative to the current offset, and 2 means 17 | // relative to the end. Seek returns the new offset and an error, if 18 | // any. 19 | // 20 | // Seeking to a negative offset is an error, and results in Enegoff. 21 | // Seeking to 0 in a directory is only valid if whence is 0. Seek returns 22 | // Eisdir otherwise. 23 | func (f *File) Seek(offset int64, whence int) (int64, error) { 24 | var off int64 25 | 26 | switch whence { 27 | case 0: 28 | // origin 29 | off = offset 30 | if f.fid.Qid.Type&p.QTDIR > 0 && off != 0 { 31 | return 0, Eisdir 32 | } 33 | 34 | case 1: 35 | // current 36 | if f.fid.Qid.Type&p.QTDIR > 0 { 37 | return 0, Eisdir 38 | } 39 | off = offset + int64(f.offset) 40 | 41 | case 2: 42 | // end 43 | if f.fid.Qid.Type&p.QTDIR > 0 { 44 | return 0, Eisdir 45 | } 46 | 47 | dir, err := f.fid.Clnt.Stat(f.fid) 48 | if err != nil { 49 | return 0, &p.Error{"stat error in seek: " + err.Error(), p.EIO} 50 | } 51 | off = int64(dir.Length) + offset 52 | 53 | default: 54 | return 0, &p.Error{"bad whence in seek", p.EIO} 55 | } 56 | 57 | if off < 0 { 58 | return 0, Enegoff 59 | } 60 | f.offset = uint64(off) 61 | 62 | return off, nil 63 | } 64 | -------------------------------------------------------------------------------- /p/clnt/stat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import "github.com/lionkov/go9p/p" 8 | 9 | // Returns the metadata for the file associated with the Fid, or an Error. 10 | func (clnt *Clnt) Stat(fid *Fid) (*p.Dir, error) { 11 | tc := clnt.NewFcall() 12 | err := p.PackTstat(tc, fid.Fid) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | rc, err := clnt.Rpc(tc) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return &rc.Dir, nil 23 | } 24 | 25 | // Returns the metadata for a named file, or an Error. 26 | func (clnt *Clnt) FStat(path string) (*p.Dir, error) { 27 | fid, err := clnt.FWalk(path) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | d, err := clnt.Stat(fid) 33 | clnt.Clunk(fid) 34 | return d, err 35 | } 36 | 37 | // Modifies the data of the file associated with the Fid, or an Error. 38 | func (clnt *Clnt) Wstat(fid *Fid, dir *p.Dir) error { 39 | tc := clnt.NewFcall() 40 | err := p.PackTwstat(tc, fid.Fid, dir, clnt.Dotu) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | _, err = clnt.Rpc(tc) 46 | return err 47 | } 48 | 49 | // FSync syncs the file for a fid. It does this by sending a NewWstatDir, i.e. a 50 | // Dir with all fields set to 'not set'. 51 | func (clnt *Clnt) FSync(fid *Fid) error { 52 | return clnt.Wstat(fid, p.NewWstatDir()) 53 | } 54 | 55 | // Rename renames the file for a fid. 56 | func (clnt *Clnt) Rename(fid *Fid, name string) error { 57 | d := p.NewWstatDir() 58 | d.Name = name 59 | return clnt.Wstat(fid, d) 60 | } 61 | -------------------------------------------------------------------------------- /p/clnt/stats_http.go: -------------------------------------------------------------------------------- 1 | // +build httpstats 2 | 3 | package clnt 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "net/http" 9 | 10 | "github.com/lionkov/go9p/p" 11 | ) 12 | 13 | func (clnt *Clnt) ServeHTTP(c http.ResponseWriter, r *http.Request) { 14 | io.WriteString(c, fmt.Sprintf("

Client %s

", clnt.Id)) 15 | defer io.WriteString(c, "") 16 | 17 | // fcalls 18 | if clnt.Debuglevel&DbgLogFcalls != 0 { 19 | fs := clnt.Log.Filter(clnt, DbgLogFcalls) 20 | io.WriteString(c, fmt.Sprintf("

Last %d 9P messages

", len(fs))) 21 | for _, l := range fs { 22 | fc := l.Data.(*p.Fcall) 23 | if fc.Type != 0 { 24 | io.WriteString(c, fmt.Sprintf("
%s", fc)) 25 | } 26 | } 27 | } 28 | } 29 | 30 | func clntServeHTTP(c http.ResponseWriter, r *http.Request) { 31 | io.WriteString(c, fmt.Sprintf("")) 32 | defer io.WriteString(c, "") 33 | 34 | clnts.Lock() 35 | if clnts.clntList == nil { 36 | io.WriteString(c, "no clients") 37 | } 38 | 39 | for clnt := clnts.clntList; clnt != nil; clnt = clnt.next { 40 | io.WriteString(c, fmt.Sprintf("%s
", clnt.Id, clnt.Id)) 41 | } 42 | clnts.Unlock() 43 | } 44 | 45 | func (clnt *Clnt) statsRegister() { 46 | http.Handle("/go9p/clnt/"+clnt.Id, clnt) 47 | } 48 | 49 | func (clnt *Clnt) statsUnregister() { 50 | http.Handle("/go9p/clnt/"+clnt.Id, nil) 51 | } 52 | 53 | func (c *ClntList) statsRegister() { 54 | http.HandleFunc("/go9p/clnt", clntServeHTTP) 55 | } 56 | 57 | func (c *ClntList) statsUnregister() { 58 | http.HandleFunc("/go9p/clnt", nil) 59 | } 60 | -------------------------------------------------------------------------------- /p/clnt/tag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import "github.com/lionkov/go9p/p" 8 | 9 | type Tag struct { 10 | clnt *Clnt 11 | tag uint16 12 | reqchan chan *Req 13 | respchan chan *Req 14 | donechan chan bool 15 | } 16 | 17 | func (clnt *Clnt) TagAlloc(reqchan chan *Req) *Tag { 18 | tag := new(Tag) 19 | tag.clnt = clnt 20 | tag.tag = uint16(clnt.tagpool.getId()) 21 | tag.reqchan = reqchan 22 | tag.respchan = make(chan *Req, 16) 23 | tag.donechan = make(chan bool) 24 | go tag.reqproc() 25 | 26 | return tag 27 | } 28 | 29 | func (clnt *Clnt) TagFree(tag *Tag) { 30 | tag.donechan <- true 31 | clnt.tagpool.putId(uint32(tag.tag)) 32 | } 33 | 34 | func (tag *Tag) reqAlloc() *Req { 35 | r := new(Req) 36 | r.tag = tag.tag 37 | r.Clnt = tag.clnt 38 | r.Done = tag.respchan 39 | r.Tc = tag.clnt.NewFcall() 40 | 41 | return r 42 | } 43 | 44 | func (tag *Tag) ReqFree(r *Req) { 45 | tag.clnt.FreeFcall(r.Tc) 46 | } 47 | 48 | func (tag *Tag) reqproc() { 49 | for { 50 | select { 51 | case <-tag.donechan: 52 | return 53 | 54 | case r := <-tag.respchan: 55 | rc := r.Rc 56 | fid := r.fid 57 | err := r.Rc.Type == p.Rerror 58 | 59 | switch r.Tc.Type { 60 | case p.Tauth: 61 | if err { 62 | fid.User = nil 63 | } 64 | 65 | case p.Tattach: 66 | if !err { 67 | fid.Qid = rc.Qid 68 | } else { 69 | fid.User = nil 70 | } 71 | 72 | case p.Twalk: 73 | if !err { 74 | fid.walked = true 75 | if len(rc.Wqid) > 0 { 76 | fid.Qid = rc.Wqid[len(rc.Wqid)-1] 77 | } 78 | } else { 79 | fid.User = nil 80 | } 81 | 82 | case p.Topen: 83 | case p.Tcreate: 84 | if !err { 85 | fid.Iounit = rc.Iounit 86 | fid.Qid = rc.Qid 87 | } else { 88 | fid.Mode = 0 89 | } 90 | 91 | case p.Tclunk: 92 | case p.Tremove: 93 | tag.clnt.fidpool.putId(fid.Fid) 94 | } 95 | 96 | tag.reqchan <- r 97 | } 98 | } 99 | } 100 | 101 | func (tag *Tag) Auth(afid *Fid, user p.User, aname string) error { 102 | req := tag.reqAlloc() 103 | req.fid = afid 104 | err := p.PackTauth(req.Tc, afid.Fid, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | afid.User = user 110 | return tag.clnt.Rpcnb(req) 111 | } 112 | 113 | func (tag *Tag) Attach(fid, afid *Fid, user p.User, aname string) error { 114 | var afno uint32 115 | 116 | if afid != nil { 117 | afno = afid.Fid 118 | } else { 119 | afno = p.NOFID 120 | } 121 | 122 | req := tag.reqAlloc() 123 | req.fid = fid 124 | err := p.PackTattach(req.Tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu) 125 | if err != nil { 126 | return err 127 | } 128 | 129 | fid.User = user 130 | return tag.clnt.Rpcnb(req) 131 | } 132 | 133 | func (tag *Tag) Walk(fid *Fid, newfid *Fid, wnames []string) error { 134 | req := tag.reqAlloc() 135 | req.fid = newfid 136 | if len(wnames) == 0 { 137 | newfid.Qid = fid.Qid 138 | } 139 | 140 | err := p.PackTwalk(req.Tc, fid.Fid, newfid.Fid, wnames) 141 | if err != nil { 142 | return err 143 | } 144 | 145 | newfid.User = fid.User 146 | return tag.clnt.Rpcnb(req) 147 | } 148 | 149 | func (tag *Tag) Open(fid *Fid, mode uint8) error { 150 | req := tag.reqAlloc() 151 | req.fid = fid 152 | err := p.PackTopen(req.Tc, fid.Fid, mode) 153 | if err != nil { 154 | return err 155 | } 156 | 157 | fid.Mode = mode 158 | return tag.clnt.Rpcnb(req) 159 | } 160 | 161 | func (tag *Tag) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error { 162 | req := tag.reqAlloc() 163 | req.fid = fid 164 | err := p.PackTcreate(req.Tc, fid.Fid, name, perm, mode, ext, tag.clnt.Dotu) 165 | if err != nil { 166 | return err 167 | } 168 | 169 | fid.Mode = mode 170 | return tag.clnt.Rpcnb(req) 171 | } 172 | 173 | func (tag *Tag) Read(fid *Fid, offset uint64, count uint32) error { 174 | req := tag.reqAlloc() 175 | req.fid = fid 176 | err := p.PackTread(req.Tc, fid.Fid, offset, count) 177 | if err != nil { 178 | return err 179 | } 180 | 181 | return tag.clnt.Rpcnb(req) 182 | } 183 | 184 | func (tag *Tag) Write(fid *Fid, data []byte, offset uint64) error { 185 | req := tag.reqAlloc() 186 | req.fid = fid 187 | err := p.PackTwrite(req.Tc, fid.Fid, offset, uint32(len(data)), data) 188 | if err != nil { 189 | return err 190 | } 191 | 192 | return tag.clnt.Rpcnb(req) 193 | } 194 | 195 | func (tag *Tag) Clunk(fid *Fid) error { 196 | req := tag.reqAlloc() 197 | req.fid = fid 198 | err := p.PackTclunk(req.Tc, fid.Fid) 199 | if err != nil { 200 | return err 201 | } 202 | 203 | return tag.clnt.Rpcnb(req) 204 | } 205 | 206 | func (tag *Tag) Remove(fid *Fid) error { 207 | req := tag.reqAlloc() 208 | req.fid = fid 209 | err := p.PackTremove(req.Tc, fid.Fid) 210 | if err != nil { 211 | return err 212 | } 213 | 214 | return tag.clnt.Rpcnb(req) 215 | } 216 | 217 | func (tag *Tag) Stat(fid *Fid) error { 218 | req := tag.reqAlloc() 219 | req.fid = fid 220 | err := p.PackTstat(req.Tc, fid.Fid) 221 | if err != nil { 222 | return err 223 | } 224 | 225 | return tag.clnt.Rpcnb(req) 226 | } 227 | 228 | func (tag *Tag) Wstat(fid *Fid, dir *p.Dir) error { 229 | req := tag.reqAlloc() 230 | req.fid = fid 231 | err := p.PackTwstat(req.Tc, fid.Fid, dir, tag.clnt.Dotu) 232 | if err != nil { 233 | return err 234 | } 235 | 236 | return tag.clnt.Rpcnb(req) 237 | } 238 | -------------------------------------------------------------------------------- /p/clnt/walk.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "strings" 10 | ) 11 | 12 | // Starting from the file associated with fid, walks all wnames in 13 | // sequence and associates the resulting file with newfid. If no wnames 14 | // were walked successfully, an Error is returned. Otherwise a slice with a 15 | // Qid for each walked name is returned. 16 | func (clnt *Clnt) Walk(fid *Fid, newfid *Fid, wnames []string) ([]p.Qid, error) { 17 | tc := clnt.NewFcall() 18 | err := p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | rc, err := clnt.Rpc(tc) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | newfid.walked = true 29 | return rc.Wqid, nil 30 | } 31 | 32 | // Walks to a named file. Returns a Fid associated with the file, 33 | // or an Error. 34 | func (clnt *Clnt) FWalk(path string) (*Fid, error) { 35 | var err error = nil 36 | 37 | var i, m int 38 | for i = 0; i < len(path); i++ { 39 | if path[i] != '/' { 40 | break 41 | } 42 | } 43 | 44 | if i > 0 { 45 | path = path[i:] 46 | } 47 | 48 | wnames := strings.Split(path, "/") 49 | newfid := clnt.FidAlloc() 50 | fid := clnt.Root 51 | if fid == nil { 52 | panic("clnt.Root is nil") 53 | } 54 | newfid.User = fid.User 55 | 56 | /* get rid of the empty names */ 57 | for i, m = 0, 0; i < len(wnames); i++ { 58 | if wnames[i] != "" { 59 | wnames[m] = wnames[i] 60 | m++ 61 | } 62 | } 63 | 64 | wnames = wnames[0:m] 65 | for { 66 | n := len(wnames) 67 | if n > 16 { 68 | n = 16 69 | } 70 | 71 | tc := clnt.NewFcall() 72 | err = p.PackTwalk(tc, fid.Fid, newfid.Fid, wnames[0:n]) 73 | if err != nil { 74 | goto error 75 | } 76 | 77 | var rc *p.Fcall 78 | rc, err = clnt.Rpc(tc) 79 | if err != nil { 80 | goto error 81 | } 82 | 83 | newfid.walked = true 84 | if len(rc.Wqid) != n { 85 | err = &p.Error{"file not found", p.ENOENT} 86 | goto error 87 | } 88 | 89 | if len(rc.Wqid) > 0 { 90 | newfid.Qid = rc.Wqid[len(rc.Wqid)-1] 91 | } else { 92 | newfid.Qid = fid.Qid 93 | } 94 | 95 | wnames = wnames[n:] 96 | fid = newfid 97 | if len(wnames) == 0 { 98 | break 99 | } 100 | } 101 | 102 | return newfid, nil 103 | 104 | error: 105 | clnt.Clunk(newfid) 106 | return nil, err 107 | } 108 | -------------------------------------------------------------------------------- /p/clnt/write.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package clnt 6 | 7 | import "github.com/lionkov/go9p/p" 8 | 9 | // Write up to len(data) bytes starting from offset. Returns the 10 | // number of bytes written, or an Error. 11 | func (clnt *Clnt) Write(fid *Fid, data []byte, offset uint64) (int, error) { 12 | if uint32(len(data)) > fid.Iounit { 13 | data = data[0:fid.Iounit] 14 | } 15 | 16 | tc := clnt.NewFcall() 17 | err := p.PackTwrite(tc, fid.Fid, offset, uint32(len(data)), data) 18 | if err != nil { 19 | return 0, err 20 | } 21 | 22 | rc, err := clnt.Rpc(tc) 23 | if err != nil { 24 | return 0, err 25 | } 26 | 27 | return int(rc.Count), nil 28 | } 29 | 30 | // Writes up to len(buf) bytes to a file. Returns the number of 31 | // bytes written, or an Error. 32 | func (file *File) Write(buf []byte) (int, error) { 33 | n, err := file.WriteAt(buf, int64(file.offset)) 34 | if err == nil { 35 | file.offset += uint64(n) 36 | } 37 | 38 | return n, err 39 | } 40 | 41 | // Writes up to len(buf) bytes starting from offset. Returns the number 42 | // of bytes written, or an Error. 43 | func (file *File) WriteAt(buf []byte, offset int64) (int, error) { 44 | return file.fid.Clnt.Write(file.fid, buf, uint64(offset)) 45 | } 46 | 47 | // Writes exactly len(buf) bytes starting from offset. Returns the number of 48 | // bytes written. If Error is returned the number of bytes can be less 49 | // than len(buf). 50 | func (file *File) Writen(buf []byte, offset uint64) (int, error) { 51 | ret := 0 52 | for len(buf) > 0 { 53 | n, err := file.WriteAt(buf, int64(offset)) 54 | if err != nil { 55 | return ret, err 56 | } 57 | 58 | if n == 0 { 59 | break 60 | } 61 | 62 | buf = buf[n:] 63 | offset += uint64(n) 64 | ret += n 65 | } 66 | 67 | return ret, nil 68 | } 69 | -------------------------------------------------------------------------------- /p/fmt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package p 6 | 7 | import "fmt" 8 | 9 | func permToString(perm uint32) string { 10 | ret := "" 11 | 12 | if perm&DMDIR != 0 { 13 | ret += "d" 14 | } 15 | 16 | if perm&DMAPPEND != 0 { 17 | ret += "a" 18 | } 19 | 20 | if perm&DMAUTH != 0 { 21 | ret += "A" 22 | } 23 | 24 | if perm&DMEXCL != 0 { 25 | ret += "l" 26 | } 27 | 28 | if perm&DMTMP != 0 { 29 | ret += "t" 30 | } 31 | 32 | if perm&DMDEVICE != 0 { 33 | ret += "D" 34 | } 35 | 36 | if perm&DMSOCKET != 0 { 37 | ret += "S" 38 | } 39 | 40 | if perm&DMNAMEDPIPE != 0 { 41 | ret += "P" 42 | } 43 | 44 | if perm&DMSYMLINK != 0 { 45 | ret += "L" 46 | } 47 | 48 | ret += fmt.Sprintf("%o", perm&0777) 49 | return ret 50 | } 51 | 52 | func (qid *Qid) String() string { 53 | b := "" 54 | if qid.Type&QTDIR != 0 { 55 | b += "d" 56 | } 57 | if qid.Type&QTAPPEND != 0 { 58 | b += "a" 59 | } 60 | if qid.Type&QTAUTH != 0 { 61 | b += "A" 62 | } 63 | if qid.Type&QTEXCL != 0 { 64 | b += "l" 65 | } 66 | if qid.Type&QTTMP != 0 { 67 | b += "t" 68 | } 69 | if qid.Type&QTSYMLINK != 0 { 70 | b += "L" 71 | } 72 | 73 | return fmt.Sprintf("(%x %x '%s')", qid.Path, qid.Version, b) 74 | } 75 | 76 | func (d *Dir) String() string { 77 | ret := fmt.Sprintf("'%s' '%s' '%s' '%s' q ", d.Name, d.Uid, d.Gid, d.Muid) 78 | ret += d.Qid.String() + " m " + permToString(d.Mode) 79 | ret += fmt.Sprintf(" at %d mt %d l %d t %d d %d", d.Atime, d.Mtime, 80 | d.Length, d.Type, d.Dev) 81 | 82 | /* dotu ? */ 83 | ret += " ext " + d.Ext 84 | 85 | return ret 86 | } 87 | 88 | func (fc *Fcall) String() string { 89 | ret := "" 90 | 91 | switch fc.Type { 92 | default: 93 | ret = fmt.Sprintf("invalid call: %d", fc.Type) 94 | case Tversion: 95 | ret = fmt.Sprintf("Tversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version) 96 | case Rversion: 97 | ret = fmt.Sprintf("Rversion tag %d msize %d version '%s'", fc.Tag, fc.Msize, fc.Version) 98 | case Tauth: 99 | ret = fmt.Sprintf("Tauth tag %d afid %d uname '%s' nuname %d aname '%s'", 100 | fc.Tag, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname) 101 | case Rauth: 102 | ret = fmt.Sprintf("Rauth tag %d aqid %v", fc.Tag, &fc.Qid) 103 | case Rattach: 104 | ret = fmt.Sprintf("Rattach tag %d aqid %v", fc.Tag, &fc.Qid) 105 | case Tattach: 106 | ret = fmt.Sprintf("Tattach tag %d fid %d afid %d uname '%s' nuname %d aname '%s'", 107 | fc.Tag, fc.Fid, fc.Afid, fc.Uname, fc.Unamenum, fc.Aname) 108 | case Tflush: 109 | ret = fmt.Sprintf("Tflush tag %d oldtag %d", fc.Tag, fc.Oldtag) 110 | case Rerror: 111 | ret = fmt.Sprintf("Rerror tag %d ename '%s' ecode %d", fc.Tag, fc.Error, fc.Errornum) 112 | case Twalk: 113 | ret = fmt.Sprintf("Twalk tag %d fid %d newfid %d ", fc.Tag, fc.Fid, fc.Newfid) 114 | for i := 0; i < len(fc.Wname); i++ { 115 | ret += fmt.Sprintf("%d:'%s' ", i, fc.Wname[i]) 116 | } 117 | case Rwalk: 118 | ret = fmt.Sprintf("Rwalk tag %d ", fc.Tag) 119 | for i := 0; i < len(fc.Wqid); i++ { 120 | ret += fmt.Sprintf("%v ", &fc.Wqid[i]) 121 | } 122 | case Topen: 123 | ret = fmt.Sprintf("Topen tag %d fid %d mode %x", fc.Tag, fc.Fid, fc.Mode) 124 | case Ropen: 125 | ret = fmt.Sprintf("Ropen tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit) 126 | case Rcreate: 127 | ret = fmt.Sprintf("Rcreate tag %d qid %v iounit %d", fc.Tag, &fc.Qid, fc.Iounit) 128 | case Tcreate: 129 | ret = fmt.Sprintf("Tcreate tag %d fid %d name '%s' perm ", fc.Tag, fc.Fid, fc.Name) 130 | ret += permToString(fc.Perm) 131 | ret += fmt.Sprintf(" mode %x ", fc.Mode) 132 | case Tread: 133 | ret = fmt.Sprintf("Tread tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count) 134 | case Rread: 135 | ret = fmt.Sprintf("Rread tag %d count %d", fc.Tag, fc.Count) 136 | case Twrite: 137 | ret = fmt.Sprintf("Twrite tag %d fid %d offset %d count %d", fc.Tag, fc.Fid, fc.Offset, fc.Count) 138 | case Rwrite: 139 | ret = fmt.Sprintf("Rwrite tag %d count %d", fc.Tag, fc.Count) 140 | case Tclunk: 141 | ret = fmt.Sprintf("Tclunk tag %d fid %d", fc.Tag, fc.Fid) 142 | case Rclunk: 143 | ret = fmt.Sprintf("Rclunk tag %d", fc.Tag) 144 | case Tremove: 145 | ret = fmt.Sprintf("Tremove tag %d fid %d", fc.Tag, fc.Fid) 146 | case Tstat: 147 | ret = fmt.Sprintf("Tstat tag %d fid %d", fc.Tag, fc.Fid) 148 | case Rstat: 149 | ret = fmt.Sprintf("Rstat tag %d st (%v)", fc.Tag, &fc.Dir) 150 | case Twstat: 151 | ret = fmt.Sprintf("Twstat tag %d fid %d st (%v)", fc.Tag, fc.Fid, &fc.Dir) 152 | case Rflush: 153 | ret = fmt.Sprintf("Rflush tag %d", fc.Tag) 154 | case Rremove: 155 | ret = fmt.Sprintf("Rremove tag %d", fc.Tag) 156 | case Rwstat: 157 | ret = fmt.Sprintf("Rwstat tag %d", fc.Tag) 158 | } 159 | 160 | return ret 161 | } 162 | -------------------------------------------------------------------------------- /p/log.go: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type Log struct { 4 | Data interface{} 5 | Owner interface{} 6 | Type int 7 | } 8 | 9 | type Logger struct { 10 | items []*Log 11 | idx int 12 | logchan chan *Log 13 | fltchan chan *flt 14 | rszchan chan int 15 | } 16 | 17 | type flt struct { 18 | owner interface{} 19 | itype int 20 | fltchan chan []*Log 21 | } 22 | 23 | func NewLogger(sz int) *Logger { 24 | if sz == 0 { 25 | return nil 26 | } 27 | 28 | l := new(Logger) 29 | l.items = make([]*Log, sz) 30 | l.logchan = make(chan *Log, 16) 31 | l.fltchan = make(chan *flt) 32 | l.rszchan = make(chan int) 33 | 34 | go l.doLog() 35 | return l 36 | } 37 | 38 | func (l *Logger) Resize(sz int) { 39 | if sz == 0 { 40 | return 41 | } 42 | 43 | l.rszchan <- sz 44 | } 45 | 46 | func (l *Logger) Log(data, owner interface{}, itype int) { 47 | l.logchan <- &Log{data, owner, itype} 48 | } 49 | 50 | func (l *Logger) Filter(owner interface{}, itype int) []*Log { 51 | c := make(chan []*Log) 52 | l.fltchan <- &flt{owner, itype, c} 53 | return <-c 54 | } 55 | 56 | func (l *Logger) doLog() { 57 | for { 58 | select { 59 | case it := <-l.logchan: 60 | if l.idx >= len(l.items) { 61 | l.idx = 0 62 | } 63 | 64 | l.items[l.idx] = it 65 | l.idx++ 66 | 67 | case sz := <-l.rszchan: 68 | it := make([]*Log, sz) 69 | for i, j := l.idx, 0; j < len(it); j++ { 70 | if i >= len(l.items) { 71 | i = 0 72 | } 73 | 74 | it[j] = l.items[i] 75 | i++ 76 | if i == l.idx { 77 | break 78 | } 79 | } 80 | 81 | l.items = it 82 | l.idx = 0 83 | 84 | case flt := <-l.fltchan: 85 | n := 0 86 | // we don't care about the order while counting 87 | for _, it := range l.items { 88 | if it == nil { 89 | continue 90 | } 91 | 92 | if (flt.owner == nil || it.Owner == flt.owner) && 93 | (flt.itype == 0 || it.Type == flt.itype) { 94 | n++ 95 | } 96 | } 97 | 98 | its := make([]*Log, n) 99 | for i, m := l.idx, 0; m < len(its); i++ { 100 | if i >= len(l.items) { 101 | i = 0 102 | } 103 | 104 | it := l.items[i] 105 | if it != nil && (flt.owner == nil || it.Owner == flt.owner) && 106 | (flt.itype == 0 || it.Type == flt.itype) { 107 | its[m] = it 108 | m++ 109 | } 110 | } 111 | 112 | flt.fltchan <- its 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /p/osusers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package p 6 | 7 | import ( 8 | "os/user" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | type osUser struct { 14 | *user.User 15 | uid int 16 | gid int 17 | } 18 | 19 | type osUsers struct { 20 | groups map[int]*osGroup 21 | sync.Mutex 22 | } 23 | 24 | // Simple Users implementation that defers to os/user and fakes 25 | // looking up groups by gid only. 26 | var OsUsers *osUsers 27 | 28 | func init() { 29 | OsUsers = new(osUsers) 30 | OsUsers.groups = make(map[int]*osGroup) 31 | } 32 | 33 | func (u *osUser) Name() string { return u.Username } 34 | 35 | func (u *osUser) Id() int { return u.uid } 36 | 37 | func (u *osUser) Groups() []Group { return []Group{OsUsers.Gid2Group(u.gid)} } 38 | 39 | func (u *osUser) IsMember(g Group) bool { return u.gid == g.Id() } 40 | 41 | type osGroup struct { 42 | gid int 43 | } 44 | 45 | func (g *osGroup) Name() string { return "" } 46 | 47 | func (g *osGroup) Id() int { return g.gid } 48 | 49 | func (g *osGroup) Members() []User { return nil } 50 | 51 | func newUser(u *user.User) *osUser { 52 | uid, uerr := strconv.Atoi(u.Uid) 53 | gid, gerr := strconv.Atoi(u.Gid) 54 | if uerr != nil || gerr != nil { 55 | /* non-numeric uid/gid => unsupported system */ 56 | return nil 57 | } 58 | return &osUser{u, uid, gid} 59 | } 60 | 61 | func (up *osUsers) Uid2User(uid int) User { 62 | u, err := user.LookupId(strconv.Itoa(uid)) 63 | if err != nil { 64 | return nil 65 | } 66 | return newUser(u) 67 | } 68 | 69 | func (up *osUsers) Uname2User(uname string) User { 70 | u, err := user.Lookup(uname) 71 | if err != nil { 72 | return nil 73 | } 74 | return newUser(u) 75 | } 76 | 77 | func (up *osUsers) Gid2Group(gid int) Group { 78 | up.Lock() 79 | group, present := up.groups[gid] 80 | if present { 81 | up.Unlock() 82 | return group 83 | } 84 | 85 | group = new(osGroup) 86 | group.gid = gid 87 | up.groups[gid] = group 88 | up.Unlock() 89 | return group 90 | } 91 | 92 | func (up *osUsers) Gname2Group(gname string) Group { 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /p/p9.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The p9 package provides the definitions and functions used to implement 6 | // the 9P2000 protocol. 7 | package p 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | // 9P2000 message types 14 | const ( 15 | Tversion = 100 + iota 16 | Rversion 17 | Tauth 18 | Rauth 19 | Tattach 20 | Rattach 21 | Terror 22 | Rerror 23 | Tflush 24 | Rflush 25 | Twalk 26 | Rwalk 27 | Topen 28 | Ropen 29 | Tcreate 30 | Rcreate 31 | Tread 32 | Rread 33 | Twrite 34 | Rwrite 35 | Tclunk 36 | Rclunk 37 | Tremove 38 | Rremove 39 | Tstat 40 | Rstat 41 | Twstat 42 | Rwstat 43 | Tlast 44 | ) 45 | 46 | const ( 47 | MSIZE = 1048576 + IOHDRSZ // default message size (1048576+IOHdrSz) 48 | IOHDRSZ = 24 // the non-data size of the Twrite messages 49 | PORT = 564 // default port for 9P file servers 50 | ) 51 | 52 | // Qid types 53 | const ( 54 | QTDIR = 0x80 // directories 55 | QTAPPEND = 0x40 // append only files 56 | QTEXCL = 0x20 // exclusive use files 57 | QTMOUNT = 0x10 // mounted channel 58 | QTAUTH = 0x08 // authentication file 59 | QTTMP = 0x04 // non-backed-up file 60 | QTSYMLINK = 0x02 // symbolic link (Unix, 9P2000.u) 61 | QTLINK = 0x01 // hard link (Unix, 9P2000.u) 62 | QTFILE = 0x00 63 | ) 64 | 65 | // Flags for the mode field in Topen and Tcreate messages 66 | const ( 67 | OREAD = 0x0 // open read-only 68 | OWRITE = 0x1 // open write-only 69 | ORDWR = 0x2 // open read-write 70 | OEXEC = 0x3 // execute (== read but check execute permission) 71 | OTRUNC = 0x10 // or'ed in (except for exec), truncate file first 72 | OCEXEC = 0x20 // or'ed in, close on exec 73 | ORCLOSE = 0x40 // or'ed in, remove on close 74 | OAPPEND = 0x80 // or'ed in, append only 75 | OEXCL = 0x1000 // or'ed in, exclusive client use 76 | ) 77 | 78 | // File modes 79 | const ( 80 | DMDIR = 0x80000000 // mode bit for directories 81 | DMAPPEND = 0x40000000 // mode bit for append only files 82 | DMEXCL = 0x20000000 // mode bit for exclusive use files 83 | DMMOUNT = 0x10000000 // mode bit for mounted channel 84 | DMAUTH = 0x08000000 // mode bit for authentication file 85 | DMTMP = 0x04000000 // mode bit for non-backed-up file 86 | DMSYMLINK = 0x02000000 // mode bit for symbolic link (Unix, 9P2000.u) 87 | DMLINK = 0x01000000 // mode bit for hard link (Unix, 9P2000.u) 88 | DMDEVICE = 0x00800000 // mode bit for device file (Unix, 9P2000.u) 89 | DMNAMEDPIPE = 0x00200000 // mode bit for named pipe (Unix, 9P2000.u) 90 | DMSOCKET = 0x00100000 // mode bit for socket (Unix, 9P2000.u) 91 | DMSETUID = 0x00080000 // mode bit for setuid (Unix, 9P2000.u) 92 | DMSETGID = 0x00040000 // mode bit for setgid (Unix, 9P2000.u) 93 | DMREAD = 0x4 // mode bit for read permission 94 | DMWRITE = 0x2 // mode bit for write permission 95 | DMEXEC = 0x1 // mode bit for execute permission 96 | ) 97 | 98 | const ( 99 | NOTAG uint16 = 0xFFFF // no tag specified 100 | NOFID uint32 = 0xFFFFFFFF // no fid specified 101 | NOUID uint32 = 0xFFFFFFFF // no uid specified 102 | ) 103 | 104 | // Error values 105 | const ( 106 | EPERM = 1 107 | ENOENT = 2 108 | EIO = 5 109 | EACCES = 13 110 | EEXIST = 17 111 | ENOTDIR = 20 112 | EINVAL = 22 113 | ) 114 | 115 | // Error represents a 9P2000 (and 9P2000.u) error 116 | type Error struct { 117 | Err string // textual representation of the error 118 | Errornum uint32 // numeric representation of the error (9P2000.u) 119 | } 120 | 121 | // File identifier 122 | type Qid struct { 123 | Type uint8 // type of the file (high 8 bits of the mode) 124 | Version uint32 // version number for the path 125 | Path uint64 // server's unique identification of the file 126 | } 127 | 128 | // Dir describes a file 129 | type Dir struct { 130 | Size uint16 // size-2 of the Dir on the wire 131 | Type uint16 132 | Dev uint32 133 | Qid // file's Qid 134 | Mode uint32 // permissions and flags 135 | Atime uint32 // last access time in seconds 136 | Mtime uint32 // last modified time in seconds 137 | Length uint64 // file length in bytes 138 | Name string // file name 139 | Uid string // owner name 140 | Gid string // group name 141 | Muid string // name of the last user that modified the file 142 | 143 | /* 9P2000.u extension */ 144 | Ext string // special file's descriptor 145 | Uidnum uint32 // owner ID 146 | Gidnum uint32 // group ID 147 | Muidnum uint32 // ID of the last user that modified the file 148 | } 149 | 150 | // Fcall represents a 9P2000 message 151 | type Fcall struct { 152 | Size uint32 // size of the message 153 | Type uint8 // message type 154 | Fid uint32 // file identifier 155 | Tag uint16 // message tag 156 | Msize uint32 // maximum message size (used by Tversion, Rversion) 157 | Version string // protocol version (used by Tversion, Rversion) 158 | Oldtag uint16 // tag of the message to flush (used by Tflush) 159 | Error string // error (used by Rerror) 160 | Qid // file Qid (used by Rauth, Rattach, Ropen, Rcreate) 161 | Iounit uint32 // maximum bytes read without breaking in multiple messages (used by Ropen, Rcreate) 162 | Afid uint32 // authentication fid (used by Tauth, Tattach) 163 | Uname string // user name (used by Tauth, Tattach) 164 | Aname string // attach name (used by Tauth, Tattach) 165 | Perm uint32 // file permission (mode) (used by Tcreate) 166 | Name string // file name (used by Tcreate) 167 | Mode uint8 // open mode (used by Topen, Tcreate) 168 | Newfid uint32 // the fid that represents the file walked to (used by Twalk) 169 | Wname []string // list of names to walk (used by Twalk) 170 | Wqid []Qid // list of Qids for the walked files (used by Rwalk) 171 | Offset uint64 // offset in the file to read/write from/to (used by Tread, Twrite) 172 | Count uint32 // number of bytes read/written (used by Tread, Rread, Twrite, Rwrite) 173 | Data []uint8 // data read/to-write (used by Rread, Twrite) 174 | Dir // file description (used by Rstat, Twstat) 175 | 176 | /* 9P2000.u extensions */ 177 | Errornum uint32 // error code, 9P2000.u only (used by Rerror) 178 | Ext string // special file description, 9P2000.u only (used by Tcreate) 179 | Unamenum uint32 // user ID, 9P2000.u only (used by Tauth, Tattach) 180 | 181 | Pkt []uint8 // raw packet data 182 | Buf []uint8 // buffer to put the raw data in 183 | } 184 | 185 | // Interface for accessing users and groups 186 | type Users interface { 187 | Uid2User(uid int) User 188 | Uname2User(uname string) User 189 | Gid2Group(gid int) Group 190 | Gname2Group(gname string) Group 191 | } 192 | 193 | // Represents a user 194 | type User interface { 195 | Name() string // user name 196 | Id() int // user id 197 | Groups() []Group // groups the user belongs to (can return nil) 198 | IsMember(g Group) bool // returns true if the user is member of the specified group 199 | } 200 | 201 | // Represents a group of users 202 | type Group interface { 203 | Name() string // group name 204 | Id() int // group id 205 | Members() []User // list of members that belong to the group (can return nil) 206 | } 207 | 208 | // minimum size of a 9P2000 message for a type 209 | var minFcsize = [...]uint32{ 210 | 6, /* Tversion msize[4] version[s] */ 211 | 6, /* Rversion msize[4] version[s] */ 212 | 8, /* Tauth fid[4] uname[s] aname[s] */ 213 | 13, /* Rauth aqid[13] */ 214 | 12, /* Tattach fid[4] afid[4] uname[s] aname[s] */ 215 | 13, /* Rattach qid[13] */ 216 | 0, /* Terror */ 217 | 2, /* Rerror ename[s] (ecode[4]) */ 218 | 2, /* Tflush oldtag[2] */ 219 | 0, /* Rflush */ 220 | 10, /* Twalk fid[4] newfid[4] nwname[2] */ 221 | 2, /* Rwalk nwqid[2] */ 222 | 5, /* Topen fid[4] mode[1] */ 223 | 17, /* Ropen qid[13] iounit[4] */ 224 | 11, /* Tcreate fid[4] name[s] perm[4] mode[1] */ 225 | 17, /* Rcreate qid[13] iounit[4] */ 226 | 16, /* Tread fid[4] offset[8] count[4] */ 227 | 4, /* Rread count[4] */ 228 | 16, /* Twrite fid[4] offset[8] count[4] */ 229 | 4, /* Rwrite count[4] */ 230 | 4, /* Tclunk fid[4] */ 231 | 0, /* Rclunk */ 232 | 4, /* Tremove fid[4] */ 233 | 0, /* Rremove */ 234 | 4, /* Tstat fid[4] */ 235 | 4, /* Rstat stat[n] */ 236 | 8, /* Twstat fid[4] stat[n] */ 237 | 0, /* Rwstat */ 238 | 20, /* Tbread fileid[8] offset[8] count[4] */ 239 | 4, /* Rbread count[4] */ 240 | 20, /* Tbwrite fileid[8] offset[8] count[4] */ 241 | 4, /* Rbwrite count[4] */ 242 | 16, /* Tbtrunc fileid[8] offset[8] */ 243 | 0, /* Rbtrunc */ 244 | } 245 | 246 | // minimum size of a 9P2000.u message for a type 247 | var minFcusize = [...]uint32{ 248 | 6, /* Tversion msize[4] version[s] */ 249 | 6, /* Rversion msize[4] version[s] */ 250 | 12, /* Tauth fid[4] uname[s] aname[s] */ 251 | 13, /* Rauth aqid[13] */ 252 | 16, /* Tattach fid[4] afid[4] uname[s] aname[s] */ 253 | 13, /* Rattach qid[13] */ 254 | 0, /* Terror */ 255 | 6, /* Rerror ename[s] (ecode[4]) */ 256 | 2, /* Tflush oldtag[2] */ 257 | 0, /* Rflush */ 258 | 10, /* Twalk fid[4] newfid[4] nwname[2] */ 259 | 2, /* Rwalk nwqid[2] */ 260 | 5, /* Topen fid[4] mode[1] */ 261 | 17, /* Ropen qid[13] iounit[4] */ 262 | 13, /* Tcreate fid[4] name[s] perm[4] mode[1] */ 263 | 17, /* Rcreate qid[13] iounit[4] */ 264 | 16, /* Tread fid[4] offset[8] count[4] */ 265 | 4, /* Rread count[4] */ 266 | 16, /* Twrite fid[4] offset[8] count[4] */ 267 | 4, /* Rwrite count[4] */ 268 | 4, /* Tclunk fid[4] */ 269 | 0, /* Rclunk */ 270 | 4, /* Tremove fid[4] */ 271 | 0, /* Rremove */ 272 | 4, /* Tstat fid[4] */ 273 | 4, /* Rstat stat[n] */ 274 | 8, /* Twstat fid[4] stat[n] */ 275 | 0, /* Rwstat */ 276 | 20, /* Tbread fileid[8] offset[8] count[4] */ 277 | 4, /* Rbread count[4] */ 278 | 20, /* Tbwrite fileid[8] offset[8] count[4] */ 279 | 4, /* Rbwrite count[4] */ 280 | 16, /* Tbtrunc fileid[8] offset[8] */ 281 | 0, /* Rbtrunc */ 282 | } 283 | 284 | func gint8(buf []byte) (uint8, []byte) { return buf[0], buf[1:] } 285 | 286 | func gint16(buf []byte) (uint16, []byte) { 287 | return uint16(buf[0]) | (uint16(buf[1]) << 8), buf[2:] 288 | } 289 | 290 | func gint32(buf []byte) (uint32, []byte) { 291 | return uint32(buf[0]) | (uint32(buf[1]) << 8) | (uint32(buf[2]) << 16) | 292 | (uint32(buf[3]) << 24), 293 | buf[4:] 294 | } 295 | 296 | func Gint32(buf []byte) (uint32, []byte) { return gint32(buf) } 297 | 298 | func gint64(buf []byte) (uint64, []byte) { 299 | return uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) | 300 | (uint64(buf[3]) << 24) | (uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) | 301 | (uint64(buf[6]) << 48) | (uint64(buf[7]) << 56), 302 | buf[8:] 303 | } 304 | 305 | func gstr(buf []byte) (string, []byte) { 306 | var n uint16 307 | 308 | if buf == nil { 309 | return "", nil 310 | } 311 | 312 | n, buf = gint16(buf) 313 | if int(n) > len(buf) { 314 | return "", nil 315 | } 316 | 317 | return string(buf[0:n]), buf[n:] 318 | } 319 | 320 | func gqid(buf []byte, qid *Qid) []byte { 321 | qid.Type, buf = gint8(buf) 322 | qid.Version, buf = gint32(buf) 323 | qid.Path, buf = gint64(buf) 324 | 325 | return buf 326 | } 327 | 328 | func gstat(buf []byte, d *Dir, dotu bool) ([]byte, error) { 329 | sz := len(buf) 330 | d.Size, buf = gint16(buf) 331 | d.Type, buf = gint16(buf) 332 | d.Dev, buf = gint32(buf) 333 | buf = gqid(buf, &d.Qid) 334 | d.Mode, buf = gint32(buf) 335 | d.Atime, buf = gint32(buf) 336 | d.Mtime, buf = gint32(buf) 337 | d.Length, buf = gint64(buf) 338 | d.Name, buf = gstr(buf) 339 | if buf == nil { 340 | s := fmt.Sprintf("Buffer too short for basic 9p: need %d, have %d", 341 | 49, sz) 342 | return nil, &Error{s, EINVAL} 343 | } 344 | 345 | d.Uid, buf = gstr(buf) 346 | if buf == nil { 347 | return nil, &Error{"d.Uid failed", EINVAL} 348 | } 349 | d.Gid, buf = gstr(buf) 350 | if buf == nil { 351 | return nil, &Error{"d.Gid failed", EINVAL} 352 | } 353 | 354 | d.Muid, buf = gstr(buf) 355 | if buf == nil { 356 | return nil, &Error{"d.Muid failed", EINVAL} 357 | } 358 | 359 | if dotu { 360 | d.Ext, buf = gstr(buf) 361 | if buf == nil { 362 | return nil, &Error{"d.Ext failed", EINVAL} 363 | } 364 | 365 | d.Uidnum, buf = gint32(buf) 366 | d.Gidnum, buf = gint32(buf) 367 | d.Muidnum, buf = gint32(buf) 368 | } else { 369 | d.Uidnum = NOUID 370 | d.Gidnum = NOUID 371 | d.Muidnum = NOUID 372 | } 373 | 374 | return buf, nil 375 | } 376 | 377 | func pint8(val uint8, buf []byte) []byte { 378 | buf[0] = val 379 | return buf[1:] 380 | } 381 | 382 | func pint16(val uint16, buf []byte) []byte { 383 | buf[0] = uint8(val) 384 | buf[1] = uint8(val >> 8) 385 | return buf[2:] 386 | } 387 | 388 | func pint32(val uint32, buf []byte) []byte { 389 | buf[0] = uint8(val) 390 | buf[1] = uint8(val >> 8) 391 | buf[2] = uint8(val >> 16) 392 | buf[3] = uint8(val >> 24) 393 | return buf[4:] 394 | } 395 | 396 | func pint64(val uint64, buf []byte) []byte { 397 | buf[0] = uint8(val) 398 | buf[1] = uint8(val >> 8) 399 | buf[2] = uint8(val >> 16) 400 | buf[3] = uint8(val >> 24) 401 | buf[4] = uint8(val >> 32) 402 | buf[5] = uint8(val >> 40) 403 | buf[6] = uint8(val >> 48) 404 | buf[7] = uint8(val >> 56) 405 | return buf[8:] 406 | } 407 | 408 | func pstr(val string, buf []byte) []byte { 409 | n := uint16(len(val)) 410 | buf = pint16(n, buf) 411 | b := []byte(val) 412 | copy(buf, b) 413 | return buf[n:] 414 | } 415 | 416 | func pqid(val *Qid, buf []byte) []byte { 417 | buf = pint8(val.Type, buf) 418 | buf = pint32(val.Version, buf) 419 | buf = pint64(val.Path, buf) 420 | 421 | return buf 422 | } 423 | 424 | func statsz(d *Dir, dotu bool) int { 425 | sz := 2 + 2 + 4 + 13 + 4 + 4 + 4 + 8 + 2 + 2 + 2 + 2 + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid) 426 | if dotu { 427 | sz += 2 + 4 + 4 + 4 + len(d.Ext) 428 | } 429 | return sz 430 | } 431 | 432 | func pstat(d *Dir, buf []byte, dotu bool) []byte { 433 | sz := statsz(d, dotu) 434 | buf = pint16(uint16(sz-2), buf) 435 | buf = pint16(d.Type, buf) 436 | buf = pint32(d.Dev, buf) 437 | buf = pqid(&d.Qid, buf) 438 | buf = pint32(d.Mode, buf) 439 | buf = pint32(d.Atime, buf) 440 | buf = pint32(d.Mtime, buf) 441 | buf = pint64(d.Length, buf) 442 | buf = pstr(d.Name, buf) 443 | buf = pstr(d.Uid, buf) 444 | buf = pstr(d.Gid, buf) 445 | buf = pstr(d.Muid, buf) 446 | if dotu { 447 | buf = pstr(d.Ext, buf) 448 | buf = pint32(d.Uidnum, buf) 449 | buf = pint32(d.Gidnum, buf) 450 | buf = pint32(d.Muidnum, buf) 451 | } 452 | 453 | return buf 454 | } 455 | 456 | // Converts a Dir value to its on-the-wire representation and writes it to 457 | // the buf. Returns the number of bytes written, 0 if there is not enough space. 458 | func PackDir(d *Dir, dotu bool) []byte { 459 | sz := statsz(d, dotu) 460 | buf := make([]byte, sz) 461 | pstat(d, buf, dotu) 462 | return buf 463 | } 464 | 465 | // Converts the on-the-wire representation of a stat to Stat value. 466 | // Returns an error if the conversion is impossible, otherwise 467 | // a pointer to a Stat value. 468 | func UnpackDir(buf []byte, dotu bool) (d *Dir, b []byte, amt int, err error) { 469 | sz := 2 + 2 + 4 + 13 + 4 + /* size[2] type[2] dev[4] qid[13] mode[4] */ 470 | 4 + 4 + 8 + /* atime[4] mtime[4] length[8] */ 471 | 2 + 2 + 2 + 2 /* name[s] uid[s] gid[s] muid[s] */ 472 | 473 | if dotu { 474 | sz += 2 + 4 + 4 + 4 /* extension[s] n_uid[4] n_gid[4] n_muid[4] */ 475 | } 476 | 477 | if len(buf) < sz { 478 | s := fmt.Sprintf("short buffer: Need %d and have %v", sz, len(buf)) 479 | return nil, nil, 0, &Error{s, EINVAL} 480 | } 481 | 482 | d = new(Dir) 483 | b, err = gstat(buf, d, dotu) 484 | if err != nil { 485 | return nil, nil, 0, err 486 | } 487 | 488 | return d, b, len(buf) - len(b), nil 489 | 490 | } 491 | 492 | // ChangeMode returns true if Dir contains a mode change value. This should be used in 493 | // conjunction with Twstat and Rwstat. 494 | func (d *Dir) ChangeMode() bool { 495 | return d.Mode != ^uint32(0) 496 | } 497 | 498 | // ChangeTime returns true if Dir contains an mtime change value. This should be used in 499 | // conjunction with Twstat and Rwstat. 500 | func (d *Dir) ChangeMtime() bool { 501 | return d.Mtime != ^uint32(0) 502 | } 503 | 504 | // ChangeLength returns true if Dir contains a length change value. This should be used in 505 | // conjunction with Twstat and Rwstat. 506 | func (d *Dir) ChangeLength() bool { 507 | return d.Length != ^uint64(0) 508 | } 509 | 510 | // ChangeName returns true if Dir contains a name change value. This should be used in 511 | // conjunction with Twstat and Rwstat to implement rename. 512 | func (d *Dir) ChangeName() bool { 513 | return d.Name != "" 514 | } 515 | 516 | // ChangeGID returns true if Dir contains a gid change value. This should be used in 517 | // conjunction with Twstat and Rwstat. 518 | func (d *Dir) ChangeGID() bool { 519 | return d.Gid != "" 520 | } 521 | 522 | // ChangeIllegalFields returns true if Dir contains values that would request illegal fields to be 523 | // changed; these are type, dev, and qid. The size field is ignored because it's not kept in tact. 524 | // Any 9p server should return error to a Wstat request when this method returns true. 525 | func (d *Dir) ChangeIllegalFields() bool { 526 | return d.Type != ^uint16(0) || d.Dev != ^uint32(0) || d.Qid.Type != ^uint8(0) || d.Qid.Version != ^uint32(0) || d.Qid.Path != ^uint64(0) || d.Atime != ^uint32(0) || d.Uid != "" || d.Muid != "" 527 | } 528 | 529 | // Allocates a new Fcall. 530 | func NewFcall(sz uint32) *Fcall { 531 | fc := new(Fcall) 532 | fc.Buf = make([]byte, sz) 533 | 534 | return fc 535 | } 536 | 537 | // Sets the tag of a Fcall. 538 | func SetTag(fc *Fcall, tag uint16) { 539 | fc.Tag = tag 540 | pint16(tag, fc.Pkt[5:]) 541 | } 542 | 543 | func packCommon(fc *Fcall, size int, id uint8) ([]byte, error) { 544 | size += 4 + 1 + 2 /* size[4] id[1] tag[2] */ 545 | if len(fc.Buf) < int(size) { 546 | return nil, &Error{"buffer too small", EINVAL} 547 | } 548 | 549 | fc.Size = uint32(size) 550 | fc.Type = id 551 | fc.Tag = NOTAG 552 | p := fc.Buf 553 | p = pint32(uint32(size), p) 554 | p = pint8(id, p) 555 | p = pint16(NOTAG, p) 556 | fc.Pkt = fc.Buf[0:size] 557 | 558 | return p, nil 559 | } 560 | 561 | func (err *Error) Error() string { 562 | if err != nil { 563 | return fmt.Sprintf("%s: %d", err.Err, err.Errornum) 564 | } 565 | 566 | return "" 567 | } 568 | 569 | // NewWstatDir creates a dir struct with all 570 | // entries set to 'off', i.e. the server Wstat 571 | // operation will be a no op unless you change 572 | // something. It will be correct for both 9p2000 573 | // and 9p2000.u 574 | func NewWstatDir() *Dir { 575 | // Ignore Size, and 9p servers should too, 576 | // because this field isn't kept in tact: a 577 | // maximal value is truncated. 578 | return &Dir{ 579 | Type: ^uint16(0), 580 | Dev: ^uint32(0), 581 | Qid: Qid{Type: ^uint8(0), Version: ^uint32(0), Path: ^uint64(0)}, 582 | Mode: ^uint32(0), 583 | Length: ^uint64(0), 584 | Atime: ^uint32(0), 585 | Mtime: ^uint32(0), 586 | Name: "", 587 | Uid: "", 588 | Gid: "", 589 | Muid: "", 590 | Uidnum: ^uint32(0), 591 | Gidnum: ^uint32(0), 592 | Muidnum: ^uint32(0), 593 | } 594 | } 595 | -------------------------------------------------------------------------------- /p/packr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package p 6 | 7 | // Create a Rversion message in the specified Fcall. 8 | func PackRversion(fc *Fcall, msize uint32, version string) error { 9 | size := 4 + 2 + len(version) /* msize[4] version[s] */ 10 | p, err := packCommon(fc, size, Rversion) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | fc.Msize = msize 16 | fc.Version = version 17 | p = pint32(msize, p) 18 | p = pstr(version, p) 19 | 20 | return nil 21 | } 22 | 23 | // Create a Rauth message in the specified Fcall. 24 | func PackRauth(fc *Fcall, aqid *Qid) error { 25 | size := 13 /* aqid[13] */ 26 | p, err := packCommon(fc, size, Rauth) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | fc.Qid = *aqid 32 | p = pqid(aqid, p) 33 | return nil 34 | } 35 | 36 | // Create a Rerror message in the specified Fcall. If dotu is true, 37 | // the function will create a 9P2000.u message. If false, nerror is 38 | // ignored. 39 | func PackRerror(fc *Fcall, error string, errornum uint32, dotu bool) error { 40 | size := 2 + len(error) /* ename[s] */ 41 | if dotu { 42 | size += 4 /* ecode[4] */ 43 | } 44 | 45 | p, err := packCommon(fc, size, Rerror) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | fc.Error = error 51 | p = pstr(error, p) 52 | if dotu { 53 | fc.Errornum = errornum 54 | p = pint32(errornum, p) 55 | } 56 | 57 | return nil 58 | } 59 | 60 | // Create a Rflush message in the specified Fcall. 61 | func PackRflush(fc *Fcall) error { 62 | _, err := packCommon(fc, 0, Rflush) 63 | 64 | return err 65 | } 66 | 67 | // Create a Rattach message in the specified Fcall. 68 | func PackRattach(fc *Fcall, aqid *Qid) error { 69 | size := 13 /* aqid[13] */ 70 | p, err := packCommon(fc, size, Rattach) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | fc.Qid = *aqid 76 | p = pqid(aqid, p) 77 | return nil 78 | } 79 | 80 | // Create a Rwalk message in the specified Fcall. 81 | func PackRwalk(fc *Fcall, wqids []Qid) error { 82 | nwqid := len(wqids) 83 | size := 2 + nwqid*13 /* nwqid[2] nwname*wqid[13] */ 84 | p, err := packCommon(fc, size, Rwalk) 85 | if err != nil { 86 | return err 87 | } 88 | 89 | p = pint16(uint16(nwqid), p) 90 | fc.Wqid = make([]Qid, nwqid) 91 | for i := 0; i < nwqid; i++ { 92 | fc.Wqid[i] = wqids[i] 93 | p = pqid(&wqids[i], p) 94 | } 95 | 96 | return nil 97 | } 98 | 99 | // Create a Ropen message in the specified Fcall. 100 | func PackRopen(fc *Fcall, qid *Qid, iounit uint32) error { 101 | size := 13 + 4 /* qid[13] iounit[4] */ 102 | p, err := packCommon(fc, size, Ropen) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | fc.Qid = *qid 108 | fc.Iounit = iounit 109 | p = pqid(qid, p) 110 | p = pint32(iounit, p) 111 | return nil 112 | } 113 | 114 | // Create a Rcreate message in the specified Fcall. 115 | func PackRcreate(fc *Fcall, qid *Qid, iounit uint32) error { 116 | size := 13 + 4 /* qid[13] iounit[4] */ 117 | p, err := packCommon(fc, size, Rcreate) 118 | if err != nil { 119 | return err 120 | } 121 | 122 | fc.Qid = *qid 123 | fc.Iounit = iounit 124 | p = pqid(qid, p) 125 | p = pint32(iounit, p) 126 | return nil 127 | } 128 | 129 | // Initializes the specified Fcall value to contain Rread message. 130 | // The user should copy the returned data to the slice pointed by 131 | // fc.Data and call SetRreadCount to update the data size to the 132 | // actual value. 133 | func InitRread(fc *Fcall, count uint32) error { 134 | size := int(4 + count) /* count[4] data[count] */ 135 | p, err := packCommon(fc, size, Rread) 136 | if err != nil { 137 | return err 138 | } 139 | 140 | fc.Count = count 141 | fc.Data = p[4 : fc.Count+4] 142 | p = pint32(count, p) 143 | return nil 144 | } 145 | 146 | // Updates the size of the data returned by Rread. Expects that 147 | // the Fcall value is already initialized by InitRread. 148 | func SetRreadCount(fc *Fcall, count uint32) { 149 | /* we need to update both the packet size as well as the data count */ 150 | size := 4 + 1 + 2 + 4 + count /* size[4] id[1] tag[2] count[4] data[count] */ 151 | pint32(size, fc.Pkt) 152 | pint32(count, fc.Pkt[7:]) 153 | fc.Size = size 154 | fc.Count = count 155 | fc.Pkt = fc.Pkt[0:size] 156 | fc.Data = fc.Data[0:count] 157 | fc.Size = size 158 | } 159 | 160 | // Create a Rread message in the specified Fcall. 161 | func PackRread(fc *Fcall, data []byte) error { 162 | count := uint32(len(data)) 163 | err := InitRread(fc, count) 164 | if err != nil { 165 | return err 166 | } 167 | 168 | copy(fc.Data, data) 169 | return nil 170 | } 171 | 172 | // Create a Rwrite message in the specified Fcall. 173 | func PackRwrite(fc *Fcall, count uint32) error { 174 | p, err := packCommon(fc, 4, Rwrite) /* count[4] */ 175 | if err != nil { 176 | return err 177 | } 178 | 179 | fc.Count = count 180 | 181 | p = pint32(count, p) 182 | return nil 183 | } 184 | 185 | // Create a Rclunk message in the specified Fcall. 186 | func PackRclunk(fc *Fcall) error { 187 | _, err := packCommon(fc, 0, Rclunk) 188 | return err 189 | } 190 | 191 | // Create a Rremove message in the specified Fcall. 192 | func PackRremove(fc *Fcall) error { 193 | _, err := packCommon(fc, 0, Rremove) 194 | return err 195 | } 196 | 197 | // Create a Rstat message in the specified Fcall. If dotu is true, the 198 | // function will create a 9P2000.u stat representation that includes 199 | // st.Nuid, st.Ngid, st.Nmuid and st.Ext. Otherwise these values will be 200 | // ignored. 201 | func PackRstat(fc *Fcall, d *Dir, dotu bool) error { 202 | stsz := statsz(d, dotu) 203 | size := 2 + stsz /* stat[n] */ 204 | p, err := packCommon(fc, size, Rstat) 205 | if err != nil { 206 | return err 207 | } 208 | 209 | p = pint16(uint16(stsz), p) 210 | p = pstat(d, p, dotu) 211 | fc.Dir = *d 212 | return nil 213 | } 214 | 215 | // Create a Rwstat message in the specified Fcall. 216 | func PackRwstat(fc *Fcall) error { 217 | _, err := packCommon(fc, 0, Rwstat) 218 | return err 219 | } 220 | -------------------------------------------------------------------------------- /p/packt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package p 6 | 7 | // Create a Tversion message in the specified Fcall. 8 | func PackTversion(fc *Fcall, msize uint32, version string) error { 9 | size := 4 + 2 + len(version) /* msize[4] version[s] */ 10 | p, err := packCommon(fc, size, Tversion) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | fc.Msize = msize 16 | fc.Version = version 17 | p = pint32(msize, p) 18 | p = pstr(version, p) 19 | 20 | return nil 21 | } 22 | 23 | // Create a Tauth message in the specified Fcall. 24 | func PackTauth(fc *Fcall, fid uint32, uname string, aname string, unamenum uint32, dotu bool) error { 25 | size := 4 + 2 + 2 + len(uname) + len(aname) /* fid[4] uname[s] aname[s] */ 26 | if dotu { 27 | size += 4 /* n_uname[4] */ 28 | } 29 | 30 | p, err := packCommon(fc, size, Tauth) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | fc.Fid = fid 36 | fc.Uname = uname 37 | fc.Aname = aname 38 | p = pint32(fid, p) 39 | p = pstr(uname, p) 40 | p = pstr(aname, p) 41 | if dotu { 42 | fc.Unamenum = unamenum 43 | p = pint32(unamenum, p) 44 | } 45 | 46 | return nil 47 | } 48 | 49 | // Create a Tflush message in the specified Fcall. 50 | func PackTflush(fc *Fcall, oldtag uint16) error { 51 | p, err := packCommon(fc, 2, Tflush) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | fc.Oldtag = oldtag 57 | p = pint16(oldtag, p) 58 | return nil 59 | } 60 | 61 | // Create a Tattach message in the specified Fcall. If dotu is true, 62 | // the function will create 9P2000.u including the nuname value, otherwise 63 | // nuname is ignored. 64 | func PackTattach(fc *Fcall, fid uint32, afid uint32, uname string, aname string, unamenum uint32, dotu bool) error { 65 | size := 4 + 4 + 2 + len(uname) + 2 + len(aname) /* fid[4] afid[4] uname[s] aname[s] */ 66 | if dotu { 67 | size += 4 68 | } 69 | 70 | p, err := packCommon(fc, size, Tattach) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | fc.Fid = fid 76 | fc.Afid = afid 77 | fc.Uname = uname 78 | fc.Aname = aname 79 | p = pint32(fid, p) 80 | p = pint32(afid, p) 81 | p = pstr(uname, p) 82 | p = pstr(aname, p) 83 | if dotu { 84 | fc.Unamenum = unamenum 85 | p = pint32(unamenum, p) 86 | } 87 | 88 | return nil 89 | } 90 | 91 | // Create a Twalk message in the specified Fcall. 92 | func PackTwalk(fc *Fcall, fid uint32, newfid uint32, wnames []string) error { 93 | nwname := len(wnames) 94 | size := 4 + 4 + 2 + nwname*2 /* fid[4] newfid[4] nwname[2] nwname*wname[s] */ 95 | for i := 0; i < nwname; i++ { 96 | size += len(wnames[i]) 97 | } 98 | 99 | p, err := packCommon(fc, size, Twalk) 100 | if err != nil { 101 | return err 102 | } 103 | 104 | fc.Fid = fid 105 | fc.Newfid = newfid 106 | p = pint32(fid, p) 107 | p = pint32(newfid, p) 108 | p = pint16(uint16(nwname), p) 109 | fc.Wname = make([]string, nwname) 110 | for i := 0; i < nwname; i++ { 111 | fc.Wname[i] = wnames[i] 112 | p = pstr(wnames[i], p) 113 | } 114 | 115 | return nil 116 | } 117 | 118 | // Create a Topen message in the specified Fcall. 119 | func PackTopen(fc *Fcall, fid uint32, mode uint8) error { 120 | size := 4 + 1 /* fid[4] mode[1] */ 121 | p, err := packCommon(fc, size, Topen) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | fc.Fid = fid 127 | fc.Mode = mode 128 | p = pint32(fid, p) 129 | p = pint8(mode, p) 130 | return nil 131 | } 132 | 133 | // Create a Tcreate message in the specified Fcall. If dotu is true, 134 | // the function will create a 9P2000.u message that includes ext. 135 | // Otherwise the ext value is ignored. 136 | func PackTcreate(fc *Fcall, fid uint32, name string, perm uint32, mode uint8, ext string, dotu bool) error { 137 | size := 4 + 2 + len(name) + 4 + 1 /* fid[4] name[s] perm[4] mode[1] */ 138 | 139 | if dotu { 140 | size += 2 + len(ext) 141 | } 142 | 143 | p, err := packCommon(fc, size, Tcreate) 144 | if err != nil { 145 | return err 146 | } 147 | 148 | fc.Fid = fid 149 | fc.Name = name 150 | fc.Perm = perm 151 | fc.Mode = mode 152 | p = pint32(fid, p) 153 | p = pstr(name, p) 154 | p = pint32(perm, p) 155 | p = pint8(mode, p) 156 | 157 | if dotu { 158 | fc.Ext = ext 159 | p = pstr(ext, p) 160 | } 161 | 162 | return nil 163 | } 164 | 165 | // Create a Tread message in the specified Fcall. 166 | func PackTread(fc *Fcall, fid uint32, offset uint64, count uint32) error { 167 | size := 4 + 8 + 4 /* fid[4] offset[8] count[4] */ 168 | p, err := packCommon(fc, size, Tread) 169 | if err != nil { 170 | return err 171 | } 172 | 173 | fc.Fid = fid 174 | fc.Offset = offset 175 | fc.Count = count 176 | p = pint32(fid, p) 177 | p = pint64(offset, p) 178 | p = pint32(count, p) 179 | return nil 180 | } 181 | 182 | // Create a Twrite message in the specified Fcall. 183 | func PackTwrite(fc *Fcall, fid uint32, offset uint64, count uint32, data []byte) error { 184 | c := len(data) 185 | size := 4 + 8 + 4 + c /* fid[4] offset[8] count[4] data[count] */ 186 | p, err := packCommon(fc, size, Twrite) 187 | if err != nil { 188 | return err 189 | } 190 | 191 | fc.Fid = fid 192 | fc.Offset = offset 193 | fc.Count = count 194 | p = pint32(fid, p) 195 | p = pint64(offset, p) 196 | p = pint32(count, p) 197 | fc.Data = p 198 | copy(fc.Data, data) 199 | return nil 200 | } 201 | 202 | // Create a Tclunk message in the specified Fcall. 203 | func PackTclunk(fc *Fcall, fid uint32) error { 204 | p, err := packCommon(fc, 4, Tclunk) /* fid[4] */ 205 | if err != nil { 206 | return err 207 | } 208 | 209 | fc.Fid = fid 210 | p = pint32(fid, p) 211 | return nil 212 | } 213 | 214 | // Create a Tremove message in the specified Fcall. 215 | func PackTremove(fc *Fcall, fid uint32) error { 216 | p, err := packCommon(fc, 4, Tremove) /* fid[4] */ 217 | if err != nil { 218 | return err 219 | } 220 | 221 | fc.Fid = fid 222 | p = pint32(fid, p) 223 | return nil 224 | } 225 | 226 | // Create a Tstat message in the specified Fcall. 227 | func PackTstat(fc *Fcall, fid uint32) error { 228 | p, err := packCommon(fc, 4, Tstat) /* fid[4] */ 229 | if err != nil { 230 | return err 231 | } 232 | 233 | fc.Fid = fid 234 | p = pint32(fid, p) 235 | return nil 236 | } 237 | 238 | // Create a Twstat message in the specified Fcall. If dotu is true 239 | // the function will create 9P2000.u message, otherwise the 9P2000.u 240 | // specific fields from the Stat value will be ignored. 241 | func PackTwstat(fc *Fcall, fid uint32, d *Dir, dotu bool) error { 242 | stsz := statsz(d, dotu) 243 | size := 4 + 2 + stsz /* fid[4] stat[n] */ 244 | p, err := packCommon(fc, size, Twstat) 245 | if err != nil { 246 | return err 247 | } 248 | 249 | fc.Fid = fid 250 | fc.Dir = *d 251 | p = pint32(fid, p) 252 | p = pint16(uint16(stsz), p) 253 | p = pstat(d, p, dotu) 254 | return nil 255 | } 256 | -------------------------------------------------------------------------------- /p/srv/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package srv 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "fmt" 10 | "log" 11 | "net" 12 | ) 13 | 14 | func (srv *Srv) NewConn(c net.Conn) { 15 | conn := new(Conn) 16 | conn.Srv = srv 17 | conn.Msize = srv.Msize 18 | conn.Dotu = srv.Dotu 19 | conn.Debuglevel = srv.Debuglevel 20 | conn.conn = c 21 | conn.Fidpool = make(map[uint32]*Fid) 22 | conn.Reqs = make(map[uint16]*Req) 23 | conn.Reqout = make(chan *Req, srv.Maxpend) 24 | conn.done = make(chan bool) 25 | conn.rchan = make(chan *p.Fcall, 64) 26 | 27 | srv.Lock() 28 | if srv.conns == nil { 29 | srv.conns = make(map[*Conn]*Conn) 30 | } 31 | srv.conns[conn] = conn 32 | srv.Unlock() 33 | 34 | conn.Id = c.RemoteAddr().String() 35 | if op, ok := (conn.Srv.ops).(ConnOps); ok { 36 | op.ConnOpened(conn) 37 | } 38 | 39 | if sop, ok := (interface{}(conn)).(StatsOps); ok { 40 | sop.statsRegister() 41 | } 42 | 43 | go conn.recv() 44 | go conn.send() 45 | } 46 | 47 | func (conn *Conn) close() { 48 | conn.done <- true 49 | conn.Srv.Lock() 50 | delete(conn.Srv.conns, conn) 51 | conn.Srv.Unlock() 52 | 53 | if sop, ok := (interface{}(conn)).(StatsOps); ok { 54 | sop.statsUnregister() 55 | } 56 | if op, ok := (conn.Srv.ops).(ConnOps); ok { 57 | op.ConnClosed(conn) 58 | } 59 | 60 | /* call FidDestroy for all remaining fids */ 61 | if op, ok := (conn.Srv.ops).(FidOps); ok { 62 | for _, fid := range conn.Fidpool { 63 | op.FidDestroy(fid) 64 | } 65 | } 66 | } 67 | 68 | func (conn *Conn) recv() { 69 | var err error 70 | var n int 71 | 72 | buf := make([]byte, conn.Msize*8) 73 | pos := 0 74 | for { 75 | if len(buf) < int(conn.Msize) { 76 | b := make([]byte, conn.Msize*8) 77 | copy(b, buf[0:pos]) 78 | buf = b 79 | b = nil 80 | } 81 | 82 | n, err = conn.conn.Read(buf[pos:]) 83 | if err != nil || n == 0 { 84 | conn.close() 85 | return 86 | } 87 | 88 | pos += n 89 | for pos > 4 { 90 | sz, _ := p.Gint32(buf) 91 | if sz > conn.Msize { 92 | log.Println("bad client connection: ", conn.conn.RemoteAddr()) 93 | conn.conn.Close() 94 | conn.close() 95 | return 96 | } 97 | if pos < int(sz) { 98 | if len(buf) < int(sz) { 99 | b := make([]byte, conn.Msize*8) 100 | copy(b, buf[0:pos]) 101 | buf = b 102 | b = nil 103 | } 104 | 105 | break 106 | } 107 | fc, err, fcsize := p.Unpack(buf, conn.Dotu) 108 | if err != nil { 109 | log.Println(fmt.Sprintf("invalid packet : %v %v", err, buf)) 110 | conn.conn.Close() 111 | conn.close() 112 | return 113 | } 114 | 115 | tag := fc.Tag 116 | req := new(Req) 117 | select { 118 | case req.Rc = <-conn.rchan: 119 | break 120 | default: 121 | req.Rc = p.NewFcall(conn.Msize) 122 | } 123 | 124 | req.Conn = conn 125 | req.Tc = fc 126 | // req.Rc = rc 127 | if conn.Debuglevel > 0 { 128 | conn.logFcall(req.Tc) 129 | if conn.Debuglevel&DbgPrintPackets != 0 { 130 | log.Println(">->", conn.Id, fmt.Sprint(req.Tc.Pkt)) 131 | } 132 | 133 | if conn.Debuglevel&DbgPrintFcalls != 0 { 134 | log.Println(">>>", conn.Id, req.Tc.String()) 135 | } 136 | } 137 | 138 | conn.Lock() 139 | conn.nreqs++ 140 | conn.tsz += uint64(fc.Size) 141 | conn.npend++ 142 | if conn.npend > conn.maxpend { 143 | conn.maxpend = conn.npend 144 | } 145 | 146 | req.next = conn.Reqs[tag] 147 | conn.Reqs[tag] = req 148 | process := req.next == nil 149 | if req.next != nil { 150 | req.next.prev = req 151 | } 152 | conn.Unlock() 153 | if process { 154 | // Tversion may change some attributes of the 155 | // connection, so we block on it. Otherwise, 156 | // we may loop back to reading and that is a race. 157 | // This fix brought to you by the race detector. 158 | if req.Tc.Type == p.Tversion { 159 | req.process() 160 | } else { 161 | go req.process() 162 | } 163 | } 164 | 165 | buf = buf[fcsize:] 166 | pos -= fcsize 167 | } 168 | } 169 | } 170 | 171 | func (conn *Conn) send() { 172 | for { 173 | select { 174 | case <-conn.done: 175 | return 176 | 177 | case req := <-conn.Reqout: 178 | p.SetTag(req.Rc, req.Tc.Tag) 179 | conn.Lock() 180 | conn.rsz += uint64(req.Rc.Size) 181 | conn.npend-- 182 | conn.Unlock() 183 | if conn.Debuglevel > 0 { 184 | conn.logFcall(req.Rc) 185 | if conn.Debuglevel&DbgPrintPackets != 0 { 186 | log.Println("<-<", conn.Id, fmt.Sprint(req.Rc.Pkt)) 187 | } 188 | 189 | if conn.Debuglevel&DbgPrintFcalls != 0 { 190 | log.Println("<<<", conn.Id, req.Rc.String()) 191 | } 192 | } 193 | 194 | for buf := req.Rc.Pkt; len(buf) > 0; { 195 | n, err := conn.conn.Write(buf) 196 | if err != nil { 197 | /* just close the socket, will get signal on conn.done */ 198 | log.Println("error while writing") 199 | conn.conn.Close() 200 | break 201 | } 202 | 203 | buf = buf[n:] 204 | } 205 | 206 | select { 207 | case conn.rchan <- req.Rc: 208 | break 209 | default: 210 | } 211 | } 212 | } 213 | 214 | panic("unreached") 215 | } 216 | 217 | func (conn *Conn) RemoteAddr() net.Addr { 218 | return conn.conn.RemoteAddr() 219 | } 220 | 221 | func (conn *Conn) LocalAddr() net.Addr { 222 | return conn.conn.LocalAddr() 223 | } 224 | 225 | func (conn *Conn) logFcall(fc *p.Fcall) { 226 | if conn.Debuglevel&DbgLogPackets != 0 { 227 | pkt := make([]byte, len(fc.Pkt)) 228 | copy(pkt, fc.Pkt) 229 | conn.Srv.Log.Log(pkt, conn, DbgLogPackets) 230 | } 231 | 232 | if conn.Debuglevel&DbgLogFcalls != 0 { 233 | f := new(p.Fcall) 234 | *f = *fc 235 | f.Pkt = nil 236 | conn.Srv.Log.Log(f, conn, DbgLogFcalls) 237 | } 238 | } 239 | 240 | func (srv *Srv) StartNetListener(ntype, addr string) error { 241 | l, err := net.Listen(ntype, addr) 242 | if err != nil { 243 | return &p.Error{err.Error(), p.EIO} 244 | } 245 | 246 | return srv.StartListener(l) 247 | } 248 | 249 | // Start listening on the specified network and address for incoming 250 | // connections. Once a connection is established, create a new Conn 251 | // value, read messages from the socket, send them to the specified 252 | // server, and send back responses received from the server. 253 | func (srv *Srv) StartListener(l net.Listener) error { 254 | for { 255 | c, err := l.Accept() 256 | if err != nil { 257 | return &p.Error{err.Error(), p.EIO} 258 | } 259 | 260 | srv.NewConn(c) 261 | } 262 | return nil 263 | } 264 | -------------------------------------------------------------------------------- /p/srv/examples/clonefs/clonefs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // A synthetic filesystem emulating a persistent cloning interface 6 | // from Plan 9. Reading the /clone file creates new entries in the filesystem 7 | // each containing unique information/data. Clone files remember what it written 8 | // to them. Removing a clone file does what is expected. 9 | 10 | package main 11 | 12 | import ( 13 | "github.com/lionkov/go9p/p" 14 | "github.com/lionkov/go9p/p/srv" 15 | "flag" 16 | "fmt" 17 | "log" 18 | "os" 19 | "strconv" 20 | "time" 21 | ) 22 | 23 | type ClFile struct { 24 | srv.File 25 | created string 26 | id int 27 | data []byte 28 | } 29 | 30 | type Clone struct { 31 | srv.File 32 | clones int 33 | } 34 | 35 | var addr = flag.String("addr", ":5640", "network address") 36 | var debug = flag.Bool("d", false, "print debug messages") 37 | 38 | var root *srv.File 39 | 40 | func (cl *ClFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 41 | var b []byte 42 | if len(cl.data) == 0 { 43 | str := strconv.Itoa(cl.id) + " created on:" + cl.created 44 | b = []byte(str) 45 | } else { 46 | b = cl.data 47 | } 48 | n := len(b) 49 | if offset >= uint64(n) { 50 | return 0, nil 51 | } 52 | 53 | b = b[int(offset):n] 54 | n -= int(offset) 55 | if len(buf) < n { 56 | n = len(buf) 57 | } 58 | 59 | copy(buf[offset:int(offset)+n], b[offset:]) 60 | return n, nil 61 | } 62 | 63 | func (cl *ClFile) Write(fid *srv.FFid, data []byte, offset uint64) (int, error) { 64 | n := uint64(len(cl.data)) 65 | nlen := offset + uint64(len(data)) 66 | if nlen > n { 67 | ndata := make([]byte, nlen) 68 | copy(ndata, cl.data[0:n]) 69 | cl.data = ndata 70 | } 71 | 72 | copy(cl.data[offset:], data[offset:]) 73 | return len(data), nil 74 | } 75 | 76 | func (cl *ClFile) Wstat(fid *srv.FFid, dir *p.Dir) error { 77 | return nil 78 | } 79 | 80 | func (cl *ClFile) Remove(fid *srv.FFid) error { 81 | return nil 82 | } 83 | 84 | func (cl *Clone) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 85 | // we only allow a single read from us, change the offset and we're done 86 | if offset > uint64(0) { 87 | return 0, nil 88 | } 89 | 90 | cl.clones += 1 91 | ncl := new(ClFile) 92 | ncl.id = cl.clones 93 | ncl.created = time.Now().String() 94 | name := strconv.Itoa(ncl.id) 95 | 96 | err := ncl.Add(root, name, p.OsUsers.Uid2User(os.Geteuid()), nil, 0666, ncl) 97 | if err != nil { 98 | return 0, &p.Error{"can not create file", 0} 99 | } 100 | 101 | b := []byte(name) 102 | if len(buf) < len(b) { 103 | // cleanup 104 | ncl.File.Remove() 105 | return 0, &p.Error{"not enough buffer space for result", 0} 106 | } 107 | 108 | copy(buf, b) 109 | return len(b), nil 110 | } 111 | 112 | func main() { 113 | var err error 114 | var cl *Clone 115 | var s *srv.Fsrv 116 | 117 | flag.Parse() 118 | user := p.OsUsers.Uid2User(os.Geteuid()) 119 | root = new(srv.File) 120 | err = root.Add(nil, "/", user, nil, p.DMDIR|0777, nil) 121 | if err != nil { 122 | goto error 123 | } 124 | 125 | cl = new(Clone) 126 | err = cl.Add(root, "clone", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, cl) 127 | if err != nil { 128 | goto error 129 | } 130 | 131 | s = srv.NewFileSrv(root) 132 | s.Dotu = true 133 | 134 | if *debug { 135 | s.Debuglevel = 1 136 | } 137 | 138 | s.Start(s) 139 | err = s.StartNetListener("tcp", *addr) 140 | if err != nil { 141 | goto error 142 | } 143 | return 144 | 145 | error: 146 | log.Println(fmt.Sprintf("Error: %s", err)) 147 | } 148 | -------------------------------------------------------------------------------- /p/srv/examples/ramfs/ramfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "github.com/lionkov/go9p/p/srv" 10 | "flag" 11 | "fmt" 12 | "log" 13 | "os" 14 | ) 15 | 16 | type Ramfs struct { 17 | srv *srv.Fsrv 18 | user p.User 19 | group p.Group 20 | blksz int 21 | blkchan chan []byte 22 | zero []byte // blksz array of zeroes 23 | } 24 | 25 | type RFile struct { 26 | srv.File 27 | data [][]byte 28 | } 29 | 30 | var addr = flag.String("addr", ":5640", "network address") 31 | var debug = flag.Int("d", 0, "debuglevel") 32 | var blksize = flag.Int("b", 8192, "block size") 33 | var logsz = flag.Int("l", 2048, "log size") 34 | var rsrv Ramfs 35 | 36 | func (f *RFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 37 | f.Lock() 38 | defer f.Unlock() 39 | 40 | if offset > f.Length { 41 | return 0, nil 42 | } 43 | 44 | count := uint32(len(buf)) 45 | if offset+uint64(count) > f.Length { 46 | count = uint32(f.Length - offset) 47 | } 48 | 49 | for n, off, b := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz), buf[0:count]; len(b) > 0; n++ { 50 | m := rsrv.blksz - int(off) 51 | if m > len(b) { 52 | m = len(b) 53 | } 54 | 55 | blk := rsrv.zero 56 | if len(f.data[n]) != 0 { 57 | blk = f.data[n] 58 | } 59 | 60 | // log.Stderr("read block", n, "off", off, "len", m, "l", len(blk), "ll", len(b)) 61 | copy(b, blk[off:off+uint64(m)]) 62 | b = b[m:] 63 | off = 0 64 | } 65 | 66 | return int(count), nil 67 | } 68 | 69 | func (f *RFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 70 | f.Lock() 71 | defer f.Unlock() 72 | 73 | // make sure the data array is big enough 74 | sz := offset + uint64(len(buf)) 75 | if f.Length < sz { 76 | f.expand(sz) 77 | } 78 | 79 | count := 0 80 | for n, off := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz); len(buf) > 0; n++ { 81 | blk := f.data[n] 82 | if len(blk) == 0 { 83 | select { 84 | case blk = <-rsrv.blkchan: 85 | break 86 | default: 87 | blk = make([]byte, rsrv.blksz) 88 | } 89 | 90 | // if off>0 { 91 | copy(blk, rsrv.zero /*[0:off]*/) 92 | // } 93 | 94 | f.data[n] = blk 95 | } 96 | 97 | m := copy(blk[off:], buf) 98 | buf = buf[m:] 99 | count += m 100 | off = 0 101 | } 102 | 103 | return count, nil 104 | } 105 | 106 | func (f *RFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) { 107 | ff := new(RFile) 108 | err := ff.Add(&f.File, name, rsrv.user, rsrv.group, perm, ff) 109 | return &ff.File, err 110 | } 111 | 112 | func (f *RFile) Remove(fid *srv.FFid) error { 113 | f.trunc(0) 114 | 115 | return nil 116 | } 117 | 118 | func (f *RFile) Wstat(fid *srv.FFid, dir *p.Dir) error { 119 | var uid, gid uint32 120 | 121 | f.Lock() 122 | defer f.Unlock() 123 | 124 | up := rsrv.srv.Upool 125 | uid = dir.Uidnum 126 | gid = dir.Gidnum 127 | if uid == p.NOUID && dir.Uid != "" { 128 | user := up.Uname2User(dir.Uid) 129 | if user == nil { 130 | return srv.Enouser 131 | } 132 | 133 | f.Uidnum = uint32(user.Id()) 134 | } 135 | 136 | if gid == p.NOUID && dir.Gid != "" { 137 | group := up.Gname2Group(dir.Gid) 138 | if group == nil { 139 | return srv.Enouser 140 | } 141 | 142 | f.Gidnum = uint32(group.Id()) 143 | } 144 | 145 | if dir.Mode != 0xFFFFFFFF { 146 | f.Mode = (f.Mode &^ 0777) | (dir.Mode & 0777) 147 | } 148 | 149 | if dir.Name != "" { 150 | if err := f.Rename(dir.Name); err != nil { 151 | return err 152 | } 153 | } 154 | 155 | if dir.Length != 0xFFFFFFFFFFFFFFFF { 156 | f.trunc(dir.Length) 157 | } 158 | 159 | return nil 160 | } 161 | 162 | // called with f locked 163 | func (f *RFile) trunc(sz uint64) { 164 | if f.Length == sz { 165 | return 166 | } 167 | 168 | if f.Length > sz { 169 | f.shrink(sz) 170 | } else { 171 | f.expand(sz) 172 | } 173 | } 174 | 175 | // called with f lock held 176 | func (f *RFile) shrink(sz uint64) { 177 | blknum := sz / uint64(rsrv.blksz) 178 | off := sz % uint64(rsrv.blksz) 179 | if off > 0 { 180 | if len(f.data[blknum]) > 0 { 181 | copy(f.data[blknum][off:], rsrv.zero) 182 | } 183 | 184 | blknum++ 185 | } 186 | 187 | for i := blknum; i < uint64(len(f.data)); i++ { 188 | if len(f.data[i]) == 0 { 189 | continue 190 | } 191 | 192 | select { 193 | case rsrv.blkchan <- f.data[i]: 194 | break 195 | default: 196 | } 197 | } 198 | 199 | f.data = f.data[0:blknum] 200 | f.Length = sz 201 | } 202 | 203 | func (f *RFile) expand(sz uint64) { 204 | blknum := sz / uint64(rsrv.blksz) 205 | if sz%uint64(rsrv.blksz) != 0 { 206 | blknum++ 207 | } 208 | 209 | data := make([][]byte, blknum) 210 | if f.data != nil { 211 | copy(data, f.data) 212 | } 213 | 214 | f.data = data 215 | f.Length = sz 216 | } 217 | 218 | func main() { 219 | var err error 220 | var l *p.Logger 221 | 222 | flag.Parse() 223 | rsrv.user = p.OsUsers.Uid2User(os.Geteuid()) 224 | rsrv.group = p.OsUsers.Gid2Group(os.Getegid()) 225 | rsrv.blksz = *blksize 226 | rsrv.blkchan = make(chan []byte, 2048) 227 | rsrv.zero = make([]byte, rsrv.blksz) 228 | 229 | root := new(RFile) 230 | err = root.Add(nil, "/", rsrv.user, nil, p.DMDIR|0777, root) 231 | if err != nil { 232 | goto error 233 | } 234 | 235 | l = p.NewLogger(*logsz) 236 | rsrv.srv = srv.NewFileSrv(&root.File) 237 | rsrv.srv.Dotu = true 238 | rsrv.srv.Debuglevel = *debug 239 | rsrv.srv.Start(rsrv.srv) 240 | rsrv.srv.Id = "ramfs" 241 | rsrv.srv.Log = l 242 | 243 | err = rsrv.srv.StartNetListener("tcp", *addr) 244 | if err != nil { 245 | goto error 246 | } 247 | return 248 | 249 | error: 250 | log.Println(fmt.Sprintf("Error: %s", err)) 251 | } 252 | -------------------------------------------------------------------------------- /p/srv/examples/timefs/timefs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "github.com/lionkov/go9p/p/srv" 10 | "flag" 11 | "fmt" 12 | "log" 13 | "os" 14 | "time" 15 | ) 16 | 17 | type Time struct { 18 | srv.File 19 | } 20 | type InfTime struct { 21 | srv.File 22 | } 23 | 24 | var addr = flag.String("addr", ":5640", "network address") 25 | var debug = flag.Bool("d", false, "print debug messages") 26 | var debugall = flag.Bool("D", false, "print packets as well as debug messages") 27 | 28 | func (*InfTime) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 29 | // push out time ignoring offset (infinite read) 30 | t := time.Now().String() + "\n" 31 | b := []byte(t) 32 | ml := len(b) 33 | if ml > len(buf) { 34 | ml = len(buf) 35 | } 36 | 37 | copy(buf, b[0:ml]) 38 | return ml, nil 39 | } 40 | 41 | func (*Time) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 42 | b := []byte(time.Now().String()) 43 | have := len(b) 44 | off := int(offset) 45 | 46 | if off >= have { 47 | return 0, nil 48 | } 49 | 50 | return copy(buf, b[off:]), nil 51 | } 52 | 53 | func main() { 54 | var err error 55 | var tm *Time 56 | var ntm *InfTime 57 | var s *srv.Fsrv 58 | 59 | flag.Parse() 60 | user := p.OsUsers.Uid2User(os.Geteuid()) 61 | root := new(srv.File) 62 | err = root.Add(nil, "/", user, nil, p.DMDIR|0555, nil) 63 | if err != nil { 64 | goto error 65 | } 66 | 67 | tm = new(Time) 68 | err = tm.Add(root, "time", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, tm) 69 | if err != nil { 70 | goto error 71 | } 72 | ntm = new(InfTime) 73 | err = ntm.Add(root, "inftime", p.OsUsers.Uid2User(os.Geteuid()), nil, 0444, ntm) 74 | if err != nil { 75 | goto error 76 | } 77 | 78 | s = srv.NewFileSrv(root) 79 | s.Dotu = true 80 | 81 | if *debug { 82 | s.Debuglevel = 1 83 | } 84 | if *debugall { 85 | s.Debuglevel = 2 86 | } 87 | 88 | s.Start(s) 89 | err = s.StartNetListener("tcp", *addr) 90 | if err != nil { 91 | goto error 92 | } 93 | 94 | return 95 | 96 | error: 97 | log.Println(fmt.Sprintf("Error: %s", err)) 98 | } 99 | -------------------------------------------------------------------------------- /p/srv/examples/tlsramfs/tlsramfs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Listen on SSL connection, can be used as an example with p/clnt/examples/tls.go 6 | // Sample certificate was copied from the Go source code 7 | package main 8 | 9 | import ( 10 | "github.com/lionkov/go9p/p" 11 | "github.com/lionkov/go9p/p/srv" 12 | "crypto/rand" 13 | "crypto/rsa" 14 | "crypto/tls" 15 | "encoding/hex" 16 | "flag" 17 | "fmt" 18 | "log" 19 | "math/big" 20 | "os" 21 | ) 22 | 23 | type Ramfs struct { 24 | srv *srv.Fsrv 25 | user p.User 26 | group p.Group 27 | blksz int 28 | blkchan chan []byte 29 | zero []byte // blksz array of zeroes 30 | } 31 | 32 | type RFile struct { 33 | srv.File 34 | data [][]byte 35 | } 36 | 37 | var addr = flag.String("addr", ":5640", "network address") 38 | var debug = flag.Int("d", 0, "debuglevel") 39 | var blksize = flag.Int("b", 8192, "block size") 40 | var logsz = flag.Int("l", 2048, "log size") 41 | var rsrv Ramfs 42 | 43 | func (f *RFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 44 | f.Lock() 45 | defer f.Unlock() 46 | 47 | if offset > f.Length { 48 | return 0, nil 49 | } 50 | 51 | count := uint32(len(buf)) 52 | if offset+uint64(count) > f.Length { 53 | count = uint32(f.Length - offset) 54 | } 55 | 56 | for n, off, b := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz), buf[0:count]; len(b) > 0; n++ { 57 | m := rsrv.blksz - int(off) 58 | if m > len(b) { 59 | m = len(b) 60 | } 61 | 62 | blk := rsrv.zero 63 | if len(f.data[n]) != 0 { 64 | blk = f.data[n] 65 | } 66 | 67 | // log.Stderr("read block", n, "off", off, "len", m, "l", len(blk), "ll", len(b)) 68 | copy(b, blk[off:off+uint64(m)]) 69 | b = b[m:] 70 | off = 0 71 | } 72 | 73 | return int(count), nil 74 | } 75 | 76 | func (f *RFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) { 77 | f.Lock() 78 | defer f.Unlock() 79 | 80 | // make sure the data array is big enough 81 | sz := offset + uint64(len(buf)) 82 | if f.Length < sz { 83 | f.expand(sz) 84 | } 85 | 86 | count := 0 87 | for n, off := offset/uint64(rsrv.blksz), offset%uint64(rsrv.blksz); len(buf) > 0; n++ { 88 | blk := f.data[n] 89 | if len(blk) == 0 { 90 | select { 91 | case blk = <-rsrv.blkchan: 92 | break 93 | default: 94 | blk = make([]byte, rsrv.blksz) 95 | } 96 | 97 | // if off>0 { 98 | copy(blk, rsrv.zero /*[0:off]*/) 99 | // } 100 | 101 | f.data[n] = blk 102 | } 103 | 104 | m := copy(blk[off:], buf) 105 | buf = buf[m:] 106 | count += m 107 | off = 0 108 | } 109 | 110 | return count, nil 111 | } 112 | 113 | func (f *RFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) { 114 | ff := new(RFile) 115 | err := ff.Add(&f.File, name, rsrv.user, rsrv.group, perm, ff) 116 | return &ff.File, err 117 | } 118 | 119 | func (f *RFile) Remove(fid *srv.FFid) error { 120 | f.trunc(0) 121 | 122 | return nil 123 | } 124 | 125 | func (f *RFile) Wstat(fid *srv.FFid, dir *p.Dir) error { 126 | var uid, gid uint32 127 | 128 | f.Lock() 129 | defer f.Unlock() 130 | 131 | up := rsrv.srv.Upool 132 | uid = dir.Uidnum 133 | gid = dir.Gidnum 134 | if uid == p.NOUID && dir.Uid != "" { 135 | user := up.Uname2User(dir.Uid) 136 | if user == nil { 137 | return srv.Enouser 138 | } 139 | 140 | f.Uidnum = uint32(user.Id()) 141 | } 142 | 143 | if gid == p.NOUID && dir.Gid != "" { 144 | group := up.Gname2Group(dir.Gid) 145 | if group == nil { 146 | return srv.Enouser 147 | } 148 | 149 | f.Gidnum = uint32(group.Id()) 150 | } 151 | 152 | if dir.Mode != 0xFFFFFFFF { 153 | f.Mode = (f.Mode &^ 0777) | (dir.Mode & 0777) 154 | } 155 | 156 | if dir.Name != "" { 157 | if err := f.Rename(dir.Name); err != nil { 158 | return err 159 | } 160 | } 161 | 162 | if dir.Length != 0xFFFFFFFFFFFFFFFF { 163 | f.trunc(dir.Length) 164 | } 165 | 166 | return nil 167 | } 168 | 169 | // called with f locked 170 | func (f *RFile) trunc(sz uint64) { 171 | if f.Length == sz { 172 | return 173 | } 174 | 175 | if f.Length > sz { 176 | f.shrink(sz) 177 | } else { 178 | f.expand(sz) 179 | } 180 | } 181 | 182 | // called with f lock held 183 | func (f *RFile) shrink(sz uint64) { 184 | blknum := sz / uint64(rsrv.blksz) 185 | off := sz % uint64(rsrv.blksz) 186 | if off > 0 { 187 | if len(f.data[blknum]) > 0 { 188 | copy(f.data[blknum][off:], rsrv.zero) 189 | } 190 | 191 | blknum++ 192 | } 193 | 194 | for i := blknum; i < uint64(len(f.data)); i++ { 195 | if len(f.data[i]) == 0 { 196 | continue 197 | } 198 | 199 | select { 200 | case rsrv.blkchan <- f.data[i]: 201 | break 202 | default: 203 | } 204 | } 205 | 206 | f.data = f.data[0:blknum] 207 | f.Length = sz 208 | } 209 | 210 | func (f *RFile) expand(sz uint64) { 211 | blknum := sz / uint64(rsrv.blksz) 212 | if sz%uint64(rsrv.blksz) != 0 { 213 | blknum++ 214 | } 215 | 216 | data := make([][]byte, blknum) 217 | if f.data != nil { 218 | copy(data, f.data) 219 | } 220 | 221 | f.data = data 222 | f.Length = sz 223 | } 224 | 225 | func main() { 226 | var err error 227 | 228 | flag.Parse() 229 | rsrv.user = p.OsUsers.Uid2User(os.Geteuid()) 230 | rsrv.group = p.OsUsers.Gid2Group(os.Getegid()) 231 | rsrv.blksz = *blksize 232 | rsrv.blkchan = make(chan []byte, 2048) 233 | rsrv.zero = make([]byte, rsrv.blksz) 234 | 235 | root := new(RFile) 236 | err = root.Add(nil, "/", rsrv.user, nil, p.DMDIR|0777, root) 237 | if err != nil { 238 | log.Println(fmt.Sprintf("Error: %s", err)) 239 | return 240 | } 241 | 242 | l := p.NewLogger(*logsz) 243 | rsrv.srv = srv.NewFileSrv(&root.File) 244 | rsrv.srv.Dotu = true 245 | rsrv.srv.Debuglevel = *debug 246 | rsrv.srv.Start(rsrv.srv) 247 | rsrv.srv.Id = "ramfs" 248 | rsrv.srv.Log = l 249 | 250 | cert := make([]tls.Certificate, 1) 251 | cert[0].Certificate = [][]byte{testCertificate} 252 | cert[0].PrivateKey = testPrivateKey 253 | 254 | ls, oerr := tls.Listen("tcp", *addr, &tls.Config{ 255 | Rand: rand.Reader, 256 | Certificates: cert, 257 | CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA}, 258 | InsecureSkipVerify: true, 259 | }) 260 | if oerr != nil { 261 | log.Println("can't listen:", oerr) 262 | return 263 | } 264 | 265 | err = rsrv.srv.StartListener(ls) 266 | if err != nil { 267 | log.Println(fmt.Sprintf("Error: %s", err)) 268 | return 269 | } 270 | return 271 | } 272 | 273 | // Copied from crypto/tls/handshake_server_test.go from the Go repository 274 | func bigFromString(s string) *big.Int { 275 | ret := new(big.Int) 276 | ret.SetString(s, 10) 277 | return ret 278 | } 279 | 280 | func fromHex(s string) []byte { 281 | b, _ := hex.DecodeString(s) 282 | return b 283 | } 284 | 285 | var testCertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9") 286 | 287 | var testPrivateKey = &rsa.PrivateKey{ 288 | PublicKey: rsa.PublicKey{ 289 | N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"), 290 | E: 65537, 291 | }, 292 | D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"), 293 | Primes: []*big.Int{ 294 | bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"), 295 | bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"), 296 | }, 297 | } 298 | -------------------------------------------------------------------------------- /p/srv/examples/ufs/.gitignore: -------------------------------------------------------------------------------- 1 | /ufs 2 | -------------------------------------------------------------------------------- /p/srv/examples/ufs/ufs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "log" 10 | 11 | "github.com/lionkov/go9p/p/srv/ufs" 12 | ) 13 | 14 | var ( 15 | debug = flag.Int("d", 0, "print debug messages") 16 | addr = flag.String("addr", ":5640", "network address") 17 | user = flag.String("user", "", "user name") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | ufs := ufs.New() 23 | ufs.Dotu = true 24 | ufs.Id = "ufs" 25 | ufs.Debuglevel = *debug 26 | ufs.Start(ufs) 27 | if *user != "" { 28 | u := ufs.Upool.Uname2User(*user) 29 | if u == nil { 30 | log.Printf("Warning: Adding %v failed", *user) 31 | } 32 | } 33 | 34 | err := ufs.StartNetListener("tcp", *addr) 35 | if err != nil { 36 | log.Println(err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /p/srv/fcall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package srv 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | ) 10 | 11 | func (srv *Srv) version(req *Req) { 12 | tc := req.Tc 13 | conn := req.Conn 14 | 15 | if tc.Msize < p.IOHDRSZ { 16 | req.RespondError(&p.Error{"msize too small", p.EINVAL}) 17 | return 18 | } 19 | 20 | if tc.Msize < conn.Msize { 21 | conn.Msize = tc.Msize 22 | } 23 | 24 | conn.Dotu = tc.Version == "9P2000.u" && srv.Dotu 25 | ver := "9P2000" 26 | if conn.Dotu { 27 | ver = "9P2000.u" 28 | } 29 | 30 | /* make sure that the responses of all current requests will be ignored */ 31 | conn.Lock() 32 | for tag, r := range conn.Reqs { 33 | if tag == p.NOTAG { 34 | continue 35 | } 36 | 37 | for rr := r; rr != nil; rr = rr.next { 38 | rr.Lock() 39 | rr.status |= reqFlush 40 | rr.Unlock() 41 | } 42 | } 43 | conn.Unlock() 44 | 45 | req.RespondRversion(conn.Msize, ver) 46 | } 47 | 48 | func (srv *Srv) getUser(req *Req) p.User { 49 | tc := req.Tc 50 | conn := req.Conn 51 | 52 | var user p.User = nil 53 | 54 | if conn.Dotu { 55 | // from the 9p2000.u specification: 56 | // A numeric uname field has been added to the attach and auth 57 | // messages in order to provide hints to map a string to a 58 | // numeric id if such a facility is not available. The numeric 59 | // uname should be given preference over the uname string 60 | // unless n_uname is unspecified (~0). 61 | if tc.Unamenum != p.NOUID { 62 | user = srv.Upool.Uid2User(int(tc.Unamenum)) 63 | } else if tc.Uname != "" { 64 | user = srv.Upool.Uname2User(tc.Uname) 65 | } 66 | } else if tc.Uname != "" { 67 | user = srv.Upool.Uname2User(tc.Uname) 68 | } 69 | 70 | return user 71 | } 72 | 73 | func (srv *Srv) auth(req *Req) { 74 | tc := req.Tc 75 | conn := req.Conn 76 | if tc.Afid == p.NOFID { 77 | req.RespondError(Eunknownfid) 78 | return 79 | } 80 | 81 | req.Afid = conn.FidNew(tc.Afid) 82 | if req.Afid == nil { 83 | req.RespondError(Einuse) 84 | return 85 | } 86 | 87 | user := srv.getUser(req) 88 | if user == nil { 89 | req.RespondError(Enouser) 90 | return 91 | } 92 | 93 | req.Afid.User = user 94 | req.Afid.Type = p.QTAUTH 95 | if aop, ok := (srv.ops).(AuthOps); ok { 96 | aqid, err := aop.AuthInit(req.Afid, tc.Aname) 97 | if err != nil { 98 | req.RespondError(err) 99 | } else { 100 | aqid.Type |= p.QTAUTH // just in case 101 | req.RespondRauth(aqid) 102 | } 103 | } else { 104 | req.RespondError(Enoauth) 105 | } 106 | 107 | } 108 | 109 | func (srv *Srv) authPost(req *Req) { 110 | if req.Rc != nil && req.Rc.Type == p.Rauth { 111 | req.Afid.IncRef() 112 | } 113 | } 114 | 115 | func (srv *Srv) attach(req *Req) { 116 | tc := req.Tc 117 | conn := req.Conn 118 | if tc.Fid == p.NOFID { 119 | req.RespondError(Eunknownfid) 120 | return 121 | } 122 | 123 | req.Fid = conn.FidNew(tc.Fid) 124 | if req.Fid == nil { 125 | req.RespondError(Einuse) 126 | return 127 | } 128 | 129 | if tc.Afid != p.NOFID { 130 | req.Afid = conn.FidGet(tc.Afid) 131 | if req.Afid == nil { 132 | req.RespondError(Eunknownfid) 133 | } 134 | } 135 | 136 | user := srv.getUser(req) 137 | if user == nil { 138 | req.RespondError(Enouser) 139 | return 140 | } 141 | 142 | req.Fid.User = user 143 | if aop, ok := (srv.ops).(AuthOps); ok { 144 | err := aop.AuthCheck(req.Fid, req.Afid, tc.Aname) 145 | if err != nil { 146 | req.RespondError(err) 147 | return 148 | } 149 | } 150 | 151 | (srv.ops).(ReqOps).Attach(req) 152 | } 153 | 154 | func (srv *Srv) attachPost(req *Req) { 155 | if req.Rc != nil && req.Rc.Type == p.Rattach { 156 | req.Fid.Type = req.Rc.Qid.Type 157 | req.Fid.IncRef() 158 | } 159 | } 160 | 161 | func (srv *Srv) flush(req *Req) { 162 | conn := req.Conn 163 | tag := req.Tc.Oldtag 164 | p.PackRflush(req.Rc) 165 | conn.Lock() 166 | r := conn.Reqs[tag] 167 | if r != nil { 168 | rp := &r.flushreq 169 | for *rp != nil { 170 | rp = &((*rp).flushreq) 171 | } 172 | 173 | *rp = req 174 | } 175 | conn.Unlock() 176 | 177 | if r == nil { 178 | // there are no requests with that tag 179 | req.Respond() 180 | return 181 | } 182 | 183 | r.Lock() 184 | status := r.status 185 | if (status & (reqWork | reqSaved)) == 0 { 186 | /* the request is not worked on yet */ 187 | r.status |= reqFlush 188 | } 189 | r.Unlock() 190 | 191 | if (status & (reqWork | reqSaved)) == 0 { 192 | r.Respond() 193 | } else { 194 | if op, ok := (srv.ops).(FlushOp); ok { 195 | op.Flush(r) 196 | } 197 | } 198 | } 199 | 200 | func (srv *Srv) walk(req *Req) { 201 | conn := req.Conn 202 | tc := req.Tc 203 | fid := req.Fid 204 | 205 | /* we can't walk regular files, only clone them */ 206 | if len(tc.Wname) > 0 && (fid.Type&p.QTDIR) == 0 { 207 | req.RespondError(Enotdir) 208 | return 209 | } 210 | 211 | /* we can't walk open files */ 212 | if fid.opened { 213 | req.RespondError(Ebaduse) 214 | return 215 | } 216 | 217 | if tc.Fid != tc.Newfid { 218 | req.Newfid = conn.FidNew(tc.Newfid) 219 | if req.Newfid == nil { 220 | req.RespondError(Einuse) 221 | return 222 | } 223 | 224 | req.Newfid.User = fid.User 225 | req.Newfid.Type = fid.Type 226 | } else { 227 | req.Newfid = req.Fid 228 | req.Newfid.IncRef() 229 | } 230 | 231 | (req.Conn.Srv.ops).(ReqOps).Walk(req) 232 | } 233 | 234 | func (srv *Srv) walkPost(req *Req) { 235 | rc := req.Rc 236 | if rc == nil || rc.Type != p.Rwalk || req.Newfid == nil { 237 | return 238 | } 239 | 240 | n := len(rc.Wqid) 241 | if n > 0 { 242 | req.Newfid.Type = rc.Wqid[n-1].Type 243 | } else { 244 | req.Newfid.Type = req.Fid.Type 245 | } 246 | 247 | // Don't retain the fid if only a partial walk succeeded 248 | if n != len(req.Tc.Wname) { 249 | return 250 | } 251 | 252 | if req.Newfid.fid != req.Fid.fid { 253 | req.Newfid.IncRef() 254 | } 255 | } 256 | 257 | func (srv *Srv) open(req *Req) { 258 | fid := req.Fid 259 | tc := req.Tc 260 | if fid.opened { 261 | req.RespondError(Eopen) 262 | return 263 | } 264 | 265 | if (fid.Type&p.QTDIR) != 0 && tc.Mode != p.OREAD { 266 | req.RespondError(Eperm) 267 | return 268 | } 269 | 270 | fid.Omode = tc.Mode 271 | (req.Conn.Srv.ops).(ReqOps).Open(req) 272 | } 273 | 274 | func (srv *Srv) openPost(req *Req) { 275 | if req.Fid != nil { 276 | req.Fid.opened = req.Rc != nil && req.Rc.Type == p.Ropen 277 | } 278 | } 279 | 280 | func (srv *Srv) create(req *Req) { 281 | fid := req.Fid 282 | tc := req.Tc 283 | if fid.opened { 284 | req.RespondError(Eopen) 285 | return 286 | } 287 | 288 | if (fid.Type & p.QTDIR) == 0 { 289 | req.RespondError(Enotdir) 290 | return 291 | } 292 | 293 | /* can't open directories for other than reading */ 294 | if (tc.Perm&p.DMDIR) != 0 && tc.Mode != p.OREAD { 295 | req.RespondError(Eperm) 296 | return 297 | } 298 | 299 | /* can't create special files if not 9P2000.u */ 300 | if (tc.Perm&(p.DMNAMEDPIPE|p.DMSYMLINK|p.DMLINK|p.DMDEVICE|p.DMSOCKET)) != 0 && !req.Conn.Dotu { 301 | req.RespondError(Eperm) 302 | return 303 | } 304 | 305 | fid.Omode = tc.Mode 306 | (req.Conn.Srv.ops).(ReqOps).Create(req) 307 | } 308 | 309 | func (srv *Srv) createPost(req *Req) { 310 | if req.Rc != nil && req.Rc.Type == p.Rcreate && req.Fid != nil { 311 | req.Fid.Type = req.Rc.Qid.Type 312 | req.Fid.opened = true 313 | } 314 | } 315 | 316 | func (srv *Srv) read(req *Req) { 317 | tc := req.Tc 318 | fid := req.Fid 319 | if tc.Count+p.IOHDRSZ > req.Conn.Msize { 320 | req.RespondError(Etoolarge) 321 | return 322 | } 323 | 324 | if (fid.Type & p.QTAUTH) != 0 { 325 | var n int 326 | 327 | rc := req.Rc 328 | err := p.InitRread(rc, tc.Count) 329 | if err != nil { 330 | req.RespondError(err) 331 | return 332 | } 333 | 334 | if op, ok := (req.Conn.Srv.ops).(AuthOps); ok { 335 | n, err = op.AuthRead(fid, tc.Offset, rc.Data) 336 | if err != nil { 337 | req.RespondError(err) 338 | return 339 | } 340 | 341 | p.SetRreadCount(rc, uint32(n)) 342 | req.Respond() 343 | } else { 344 | req.RespondError(Enotimpl) 345 | } 346 | 347 | return 348 | } 349 | 350 | if !fid.opened || (fid.Omode&3) == p.OWRITE { 351 | req.RespondError(Ebaduse) 352 | return 353 | } 354 | 355 | if (fid.Type & p.QTDIR) != 0 { 356 | fid.Lock() 357 | if tc.Offset == 0 { 358 | fid.Diroffset = 0 359 | } else if tc.Offset != fid.Diroffset { 360 | // This used to be an error, at this 361 | // level. But maybe the provider can handle 362 | // offsets that change. In one version of 9p 363 | // we were able to support arbitrary 364 | // offsets. At the least, we're going to let 365 | // the provider decide if this is an error. 366 | fid.Diroffset = tc.Offset 367 | } 368 | fid.Unlock() 369 | } 370 | 371 | (req.Conn.Srv.ops).(ReqOps).Read(req) 372 | } 373 | 374 | func (srv *Srv) readPost(req *Req) { 375 | if req.Rc != nil && req.Rc.Type == p.Rread && (req.Fid.Type&p.QTDIR) != 0 { 376 | req.Fid.Lock() 377 | req.Fid.Diroffset += uint64(req.Rc.Count) 378 | req.Fid.Unlock() 379 | } 380 | } 381 | 382 | func (srv *Srv) write(req *Req) { 383 | fid := req.Fid 384 | tc := req.Tc 385 | if (fid.Type & p.QTAUTH) != 0 { 386 | tc := req.Tc 387 | if op, ok := (req.Conn.Srv.ops).(AuthOps); ok { 388 | n, err := op.AuthWrite(req.Fid, tc.Offset, tc.Data) 389 | if err != nil { 390 | req.RespondError(err) 391 | } else { 392 | req.RespondRwrite(uint32(n)) 393 | } 394 | } else { 395 | req.RespondError(Enotimpl) 396 | } 397 | 398 | return 399 | } 400 | 401 | if !fid.opened || (fid.Type&p.QTDIR) != 0 || (fid.Omode&3) == p.OREAD { 402 | req.RespondError(Ebaduse) 403 | return 404 | } 405 | 406 | if tc.Count+p.IOHDRSZ > req.Conn.Msize { 407 | req.RespondError(Etoolarge) 408 | return 409 | } 410 | 411 | (req.Conn.Srv.ops).(ReqOps).Write(req) 412 | } 413 | 414 | func (srv *Srv) clunk(req *Req) { 415 | fid := req.Fid 416 | if (fid.Type & p.QTAUTH) != 0 { 417 | if op, ok := (req.Conn.Srv.ops).(AuthOps); ok { 418 | op.AuthDestroy(fid) 419 | req.RespondRclunk() 420 | } else { 421 | req.RespondError(Enotimpl) 422 | } 423 | 424 | return 425 | } 426 | 427 | (req.Conn.Srv.ops).(ReqOps).Clunk(req) 428 | } 429 | 430 | func (srv *Srv) clunkPost(req *Req) { 431 | if req.Rc != nil && req.Rc.Type == p.Rclunk && req.Fid != nil { 432 | req.Fid.DecRef() 433 | } 434 | } 435 | 436 | func (srv *Srv) remove(req *Req) { (req.Conn.Srv.ops).(ReqOps).Remove(req) } 437 | 438 | func (srv *Srv) removePost(req *Req) { 439 | if req.Rc != nil && req.Fid != nil { 440 | req.Fid.DecRef() 441 | } 442 | } 443 | 444 | func (srv *Srv) stat(req *Req) { (req.Conn.Srv.ops).(ReqOps).Stat(req) } 445 | 446 | func (srv *Srv) wstat(req *Req) { 447 | /* 448 | fid := req.Fid 449 | d := &req.Tc.Dir 450 | if d.Type != uint16(0xFFFF) || d.Dev != uint32(0xFFFFFFFF) || d.Version != uint32(0xFFFFFFFF) || 451 | d.Path != uint64(0xFFFFFFFFFFFFFFFF) { 452 | req.RespondError(Eperm) 453 | return 454 | } 455 | 456 | if (d.Mode != 0xFFFFFFFF) && (((fid.Type&p.QTDIR) != 0 && (d.Mode&p.DMDIR) == 0) || 457 | ((d.Type&p.QTDIR) == 0 && (d.Mode&p.DMDIR) != 0)) { 458 | req.RespondError(Edirchange) 459 | return 460 | } 461 | */ 462 | 463 | (req.Conn.Srv.ops).(ReqOps).Wstat(req) 464 | } 465 | -------------------------------------------------------------------------------- /p/srv/file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package srv 6 | 7 | import ( 8 | "github.com/lionkov/go9p/p" 9 | "log" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // The FStatOp interface provides a single operation (Stat) that will be 15 | // called before a file stat is sent back to the client. If implemented, 16 | // the operation should update the data in the File struct. 17 | type FStatOp interface { 18 | Stat(fid *FFid) error 19 | } 20 | 21 | // The FWstatOp interface provides a single operation (Wstat) that will be 22 | // called when the client requests the File metadata to be modified. If 23 | // implemented, the operation will be called when Twstat message is received. 24 | // If not implemented, "permission denied" error will be sent back. If the 25 | // operation returns an Error, the error is send back to the client. 26 | type FWstatOp interface { 27 | Wstat(*FFid, *p.Dir) error 28 | } 29 | 30 | // If the FReadOp interface is implemented, the Read operation will be called 31 | // to read from the file. If not implemented, "permission denied" error will 32 | // be send back. The operation returns the number of bytes read, or the 33 | // error occured while reading. 34 | type FReadOp interface { 35 | Read(fid *FFid, buf []byte, offset uint64) (int, error) 36 | } 37 | 38 | // If the FWriteOp interface is implemented, the Write operation will be called 39 | // to write to the file. If not implemented, "permission denied" error will 40 | // be send back. The operation returns the number of bytes written, or the 41 | // error occured while writing. 42 | type FWriteOp interface { 43 | Write(fid *FFid, data []byte, offset uint64) (int, error) 44 | } 45 | 46 | // If the FCreateOp interface is implemented, the Create operation will be called 47 | // when the client attempts to create a file in the File implementing the interface. 48 | // If not implemented, "permission denied" error will be send back. If successful, 49 | // the operation should call (*File)Add() to add the created file to the directory. 50 | // The operation returns the created file, or the error occured while creating it. 51 | type FCreateOp interface { 52 | Create(fid *FFid, name string, perm uint32) (*File, error) 53 | } 54 | 55 | // If the FRemoveOp interface is implemented, the Remove operation will be called 56 | // when the client attempts to create a file in the File implementing the interface. 57 | // If not implemented, "permission denied" error will be send back. 58 | // The operation returns nil if successful, or the error that occured while removing 59 | // the file. 60 | type FRemoveOp interface { 61 | Remove(*FFid) error 62 | } 63 | 64 | type FOpenOp interface { 65 | Open(fid *FFid, mode uint8) error 66 | } 67 | 68 | type FClunkOp interface { 69 | Clunk(fid *FFid) error 70 | } 71 | 72 | type FDestroyOp interface { 73 | FidDestroy(fid *FFid) 74 | } 75 | 76 | type FFlags int 77 | 78 | const ( 79 | Fremoved FFlags = 1 << iota 80 | ) 81 | 82 | // The File type represents a file (or directory) served by the file server. 83 | type File struct { 84 | sync.Mutex 85 | p.Dir 86 | flags FFlags 87 | 88 | Parent *File // parent 89 | next, prev *File // siblings, guarded by parent.Lock 90 | cfirst, clast *File // children (if directory) 91 | Ops interface{} 92 | } 93 | 94 | type FFid struct { 95 | F *File 96 | Fid *Fid 97 | dirs []*File // used for readdir 98 | } 99 | 100 | // The Fsrv can be used to create file servers that serve 101 | // simple trees of synthetic files. 102 | type Fsrv struct { 103 | Srv 104 | Root *File 105 | } 106 | 107 | var lock sync.Mutex 108 | var qnext uint64 109 | var Eexist = &p.Error{"file already exists", p.EEXIST} 110 | var Enoent = &p.Error{"file not found", p.ENOENT} 111 | var Enotempty = &p.Error{"directory not empty", p.EPERM} 112 | 113 | // Creates a file server with root as root directory 114 | func NewFileSrv(root *File) *Fsrv { 115 | srv := new(Fsrv) 116 | srv.Root = root 117 | root.Parent = root // make sure we can .. in root 118 | 119 | return srv 120 | } 121 | 122 | // Initializes the fields of a file and add it to a directory. 123 | // Returns nil if successful, or an error. 124 | func (f *File) Add(dir *File, name string, uid p.User, gid p.Group, mode uint32, ops interface{}) error { 125 | 126 | lock.Lock() 127 | qpath := qnext 128 | qnext++ 129 | lock.Unlock() 130 | 131 | f.Qid.Type = uint8(mode >> 24) 132 | f.Qid.Version = 0 133 | f.Qid.Path = qpath 134 | f.Mode = mode 135 | f.Atime = uint32(time.Now().Unix()) 136 | f.Mtime = f.Atime 137 | f.Length = 0 138 | f.Name = name 139 | if uid != nil { 140 | f.Uid = uid.Name() 141 | f.Uidnum = uint32(uid.Id()) 142 | } else { 143 | f.Uid = "none" 144 | f.Uidnum = p.NOUID 145 | } 146 | 147 | if gid != nil { 148 | f.Gid = gid.Name() 149 | f.Gidnum = uint32(gid.Id()) 150 | } else { 151 | f.Gid = "none" 152 | f.Gidnum = p.NOUID 153 | } 154 | 155 | f.Muid = "" 156 | f.Muidnum = p.NOUID 157 | f.Ext = "" 158 | 159 | if dir != nil { 160 | f.Parent = dir 161 | dir.Lock() 162 | for p := dir.cfirst; p != nil; p = p.next { 163 | if name == p.Name { 164 | dir.Unlock() 165 | return Eexist 166 | } 167 | } 168 | 169 | if dir.clast != nil { 170 | dir.clast.next = f 171 | } else { 172 | dir.cfirst = f 173 | } 174 | 175 | f.prev = dir.clast 176 | f.next = nil 177 | dir.clast = f 178 | dir.Unlock() 179 | } else { 180 | f.Parent = f 181 | } 182 | 183 | f.Ops = ops 184 | return nil 185 | } 186 | 187 | // Removes a file from its parent directory. 188 | func (f *File) Remove() { 189 | f.Lock() 190 | if (f.flags & Fremoved) != 0 { 191 | f.Unlock() 192 | return 193 | } 194 | 195 | f.flags |= Fremoved 196 | f.Unlock() 197 | 198 | p := f.Parent 199 | p.Lock() 200 | if f.next != nil { 201 | f.next.prev = f.prev 202 | } else { 203 | p.clast = f.prev 204 | } 205 | 206 | if f.prev != nil { 207 | f.prev.next = f.next 208 | } else { 209 | p.cfirst = f.next 210 | } 211 | 212 | f.next = nil 213 | f.prev = nil 214 | p.Unlock() 215 | } 216 | 217 | func (f *File) Rename(name string) error { 218 | p := f.Parent 219 | p.Lock() 220 | defer p.Unlock() 221 | for c := p.cfirst; c != nil; c = c.next { 222 | if name == c.Name { 223 | return Eexist 224 | } 225 | } 226 | 227 | f.Name = name 228 | return nil 229 | } 230 | 231 | // Looks for a file in a directory. Returns nil if the file is not found. 232 | func (p *File) Find(name string) *File { 233 | var f *File 234 | 235 | p.Lock() 236 | for f = p.cfirst; f != nil; f = f.next { 237 | if name == f.Name { 238 | break 239 | } 240 | } 241 | p.Unlock() 242 | return f 243 | } 244 | 245 | // Checks if the specified user has permission to perform 246 | // certain operation on a file. Perm contains one or more 247 | // of p.DMREAD, p.DMWRITE, and p.DMEXEC. 248 | func (f *File) CheckPerm(user p.User, perm uint32) bool { 249 | if user == nil { 250 | return false 251 | } 252 | 253 | perm &= 7 254 | 255 | /* other permissions */ 256 | fperm := f.Mode & 7 257 | if (fperm & perm) == perm { 258 | return true 259 | } 260 | 261 | /* user permissions */ 262 | if f.Uid == user.Name() || f.Uidnum == uint32(user.Id()) { 263 | fperm |= (f.Mode >> 6) & 7 264 | } 265 | 266 | if (fperm & perm) == perm { 267 | return true 268 | } 269 | 270 | /* group permissions */ 271 | groups := user.Groups() 272 | if groups != nil && len(groups) > 0 { 273 | for i := 0; i < len(groups); i++ { 274 | if f.Gid == groups[i].Name() || f.Gidnum == uint32(groups[i].Id()) { 275 | fperm |= (f.Mode >> 3) & 7 276 | break 277 | } 278 | } 279 | } 280 | 281 | if (fperm & perm) == perm { 282 | return true 283 | } 284 | 285 | return false 286 | } 287 | 288 | func (s *Fsrv) Attach(req *Req) { 289 | fid := new(FFid) 290 | fid.F = s.Root 291 | fid.Fid = req.Fid 292 | req.Fid.Aux = fid 293 | req.RespondRattach(&s.Root.Qid) 294 | } 295 | 296 | func (*Fsrv) Walk(req *Req) { 297 | fid := req.Fid.Aux.(*FFid) 298 | tc := req.Tc 299 | 300 | if req.Newfid.Aux == nil { 301 | nfid := new(FFid) 302 | nfid.Fid = req.Newfid 303 | req.Newfid.Aux = nfid 304 | } 305 | 306 | nfid := req.Newfid.Aux.(*FFid) 307 | wqids := make([]p.Qid, len(tc.Wname)) 308 | i := 0 309 | f := fid.F 310 | for ; i < len(tc.Wname); i++ { 311 | if tc.Wname[i] == ".." { 312 | // handle dotdot 313 | f = f.Parent 314 | wqids[i] = f.Qid 315 | continue 316 | } 317 | if (wqids[i].Type & p.QTDIR) > 0 { 318 | if !f.CheckPerm(req.Fid.User, p.DMEXEC) { 319 | break 320 | } 321 | } 322 | 323 | p := f.Find(tc.Wname[i]) 324 | if p == nil { 325 | break 326 | } 327 | 328 | f = p 329 | wqids[i] = f.Qid 330 | } 331 | 332 | if len(tc.Wname) > 0 && i == 0 { 333 | req.RespondError(Enoent) 334 | return 335 | } 336 | 337 | nfid.F = f 338 | req.RespondRwalk(wqids[0:i]) 339 | } 340 | 341 | func mode2Perm(mode uint8) uint32 { 342 | var perm uint32 = 0 343 | 344 | switch mode & 3 { 345 | case p.OREAD: 346 | perm = p.DMREAD 347 | case p.OWRITE: 348 | perm = p.DMWRITE 349 | case p.ORDWR: 350 | perm = p.DMREAD | p.DMWRITE 351 | } 352 | 353 | if (mode & p.OTRUNC) != 0 { 354 | perm |= p.DMWRITE 355 | } 356 | 357 | return perm 358 | } 359 | 360 | func (*Fsrv) Open(req *Req) { 361 | fid := req.Fid.Aux.(*FFid) 362 | tc := req.Tc 363 | 364 | if !fid.F.CheckPerm(req.Fid.User, mode2Perm(tc.Mode)) { 365 | req.RespondError(Eperm) 366 | return 367 | } 368 | 369 | if op, ok := (fid.F.Ops).(FOpenOp); ok { 370 | err := op.Open(fid, tc.Mode) 371 | if err != nil { 372 | req.RespondError(err) 373 | return 374 | } 375 | } 376 | req.RespondRopen(&fid.F.Qid, 0) 377 | } 378 | 379 | func (*Fsrv) Create(req *Req) { 380 | fid := req.Fid.Aux.(*FFid) 381 | tc := req.Tc 382 | 383 | dir := fid.F 384 | if !dir.CheckPerm(req.Fid.User, p.DMWRITE) { 385 | req.RespondError(Eperm) 386 | return 387 | } 388 | 389 | if cop, ok := (dir.Ops).(FCreateOp); ok { 390 | f, err := cop.Create(fid, tc.Name, tc.Perm) 391 | if err != nil { 392 | req.RespondError(err) 393 | } else { 394 | fid.F = f 395 | req.RespondRcreate(&fid.F.Qid, 0) 396 | } 397 | } else { 398 | req.RespondError(Eperm) 399 | } 400 | } 401 | 402 | func (*Fsrv) Read(req *Req) { 403 | var i, n int 404 | var err error 405 | 406 | fid := req.Fid.Aux.(*FFid) 407 | f := fid.F 408 | tc := req.Tc 409 | rc := req.Rc 410 | p.InitRread(rc, tc.Count) 411 | 412 | if f.Mode&p.DMDIR != 0 { 413 | // directory 414 | f.Lock() 415 | if tc.Offset == 0 { 416 | var g *File 417 | for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next { 418 | } 419 | 420 | fid.dirs = make([]*File, n) 421 | for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next { 422 | fid.dirs[n] = g 423 | } 424 | } 425 | 426 | n = 0 427 | b := rc.Data 428 | // only return whole entries. 429 | for i = 0; i < len(fid.dirs); i++ { 430 | g := fid.dirs[i] 431 | g.Lock() 432 | if (g.flags & Fremoved) != 0 { 433 | g.Unlock() 434 | continue 435 | } 436 | 437 | nd := p.PackDir(&g.Dir, req.Conn.Dotu) 438 | g.Unlock() 439 | 440 | if len(nd) > len(b) { 441 | break 442 | } 443 | copy(b, nd) 444 | b = b[len(nd):] 445 | n += len(nd) 446 | } 447 | fid.dirs = fid.dirs[i:] 448 | f.Unlock() 449 | } else { 450 | // file 451 | if rop, ok := f.Ops.(FReadOp); ok { 452 | n, err = rop.Read(fid, rc.Data, tc.Offset) 453 | if err != nil { 454 | req.RespondError(err) 455 | return 456 | } 457 | } else { 458 | req.RespondError(Eperm) 459 | return 460 | } 461 | } 462 | 463 | p.SetRreadCount(rc, uint32(n)) 464 | req.Respond() 465 | } 466 | 467 | func (*Fsrv) Write(req *Req) { 468 | fid := req.Fid.Aux.(*FFid) 469 | f := fid.F 470 | tc := req.Tc 471 | 472 | if wop, ok := (f.Ops).(FWriteOp); ok { 473 | n, err := wop.Write(fid, tc.Data, tc.Offset) 474 | if err != nil { 475 | req.RespondError(err) 476 | } else { 477 | req.RespondRwrite(uint32(n)) 478 | } 479 | } else { 480 | req.RespondError(Eperm) 481 | } 482 | 483 | } 484 | 485 | func (*Fsrv) Clunk(req *Req) { 486 | fid := req.Fid.Aux.(*FFid) 487 | 488 | if op, ok := (fid.F.Ops).(FClunkOp); ok { 489 | err := op.Clunk(fid) 490 | if err != nil { 491 | req.RespondError(err) 492 | } 493 | } 494 | req.RespondRclunk() 495 | } 496 | 497 | func (*Fsrv) Remove(req *Req) { 498 | fid := req.Fid.Aux.(*FFid) 499 | f := fid.F 500 | f.Lock() 501 | if f.cfirst != nil { 502 | f.Unlock() 503 | req.RespondError(Enotempty) 504 | return 505 | } 506 | f.Unlock() 507 | 508 | if rop, ok := (f.Ops).(FRemoveOp); ok { 509 | err := rop.Remove(fid) 510 | if err != nil { 511 | req.RespondError(err) 512 | } else { 513 | f.Remove() 514 | req.RespondRremove() 515 | } 516 | } else { 517 | log.Println("remove not implemented") 518 | req.RespondError(Eperm) 519 | } 520 | } 521 | 522 | func (*Fsrv) Stat(req *Req) { 523 | fid := req.Fid.Aux.(*FFid) 524 | f := fid.F 525 | 526 | if sop, ok := (f.Ops).(FStatOp); ok { 527 | err := sop.Stat(fid) 528 | if err != nil { 529 | req.RespondError(err) 530 | } else { 531 | req.RespondRstat(&f.Dir) 532 | } 533 | } else { 534 | req.RespondRstat(&f.Dir) 535 | } 536 | } 537 | 538 | func (*Fsrv) Wstat(req *Req) { 539 | tc := req.Tc 540 | fid := req.Fid.Aux.(*FFid) 541 | f := fid.F 542 | 543 | if wop, ok := (f.Ops).(FWstatOp); ok { 544 | err := wop.Wstat(fid, &tc.Dir) 545 | if err != nil { 546 | req.RespondError(err) 547 | } else { 548 | req.RespondRwstat() 549 | } 550 | } else { 551 | req.RespondError(Eperm) 552 | } 553 | } 554 | 555 | func (*Fsrv) Flush(req *Req) { 556 | req.Flush() 557 | } 558 | 559 | func (*Fsrv) FidDestroy(ffid *Fid) { 560 | if ffid.Aux == nil { 561 | return 562 | } 563 | fid := ffid.Aux.(*FFid) 564 | f := fid.F 565 | 566 | if f == nil { 567 | return // otherwise errs in bad walks 568 | } 569 | 570 | if op, ok := (f.Ops).(FDestroyOp); ok { 571 | op.FidDestroy(fid) 572 | } 573 | } 574 | -------------------------------------------------------------------------------- /p/srv/respond.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package srv 6 | 7 | import "fmt" 8 | import "github.com/lionkov/go9p/p" 9 | 10 | // Respond to the request with Rerror message 11 | func (req *Req) RespondError(err interface{}) { 12 | switch e := err.(type) { 13 | case *p.Error: 14 | p.PackRerror(req.Rc, e.Err, uint32(e.Errornum), req.Conn.Dotu) 15 | case error: 16 | p.PackRerror(req.Rc, e.Error(), uint32(p.EIO), req.Conn.Dotu) 17 | default: 18 | p.PackRerror(req.Rc, fmt.Sprintf("%v", e), uint32(p.EIO), req.Conn.Dotu) 19 | } 20 | 21 | req.Respond() 22 | } 23 | 24 | // Respond to the request with Rversion message 25 | func (req *Req) RespondRversion(msize uint32, version string) { 26 | err := p.PackRversion(req.Rc, msize, version) 27 | if err != nil { 28 | req.RespondError(err) 29 | } else { 30 | req.Respond() 31 | } 32 | } 33 | 34 | // Respond to the request with Rauth message 35 | func (req *Req) RespondRauth(aqid *p.Qid) { 36 | err := p.PackRauth(req.Rc, aqid) 37 | if err != nil { 38 | req.RespondError(err) 39 | } else { 40 | req.Respond() 41 | } 42 | } 43 | 44 | // Respond to the request with Rflush message 45 | func (req *Req) RespondRflush() { 46 | err := p.PackRflush(req.Rc) 47 | if err != nil { 48 | req.RespondError(err) 49 | } else { 50 | req.Respond() 51 | } 52 | } 53 | 54 | // Respond to the request with Rattach message 55 | func (req *Req) RespondRattach(aqid *p.Qid) { 56 | err := p.PackRattach(req.Rc, aqid) 57 | if err != nil { 58 | req.RespondError(err) 59 | } else { 60 | req.Respond() 61 | } 62 | } 63 | 64 | // Respond to the request with Rwalk message 65 | func (req *Req) RespondRwalk(wqids []p.Qid) { 66 | err := p.PackRwalk(req.Rc, wqids) 67 | if err != nil { 68 | req.RespondError(err) 69 | } else { 70 | req.Respond() 71 | } 72 | } 73 | 74 | // Respond to the request with Ropen message 75 | func (req *Req) RespondRopen(qid *p.Qid, iounit uint32) { 76 | err := p.PackRopen(req.Rc, qid, iounit) 77 | if err != nil { 78 | req.RespondError(err) 79 | } else { 80 | req.Respond() 81 | } 82 | } 83 | 84 | // Respond to the request with Rcreate message 85 | func (req *Req) RespondRcreate(qid *p.Qid, iounit uint32) { 86 | err := p.PackRcreate(req.Rc, qid, iounit) 87 | if err != nil { 88 | req.RespondError(err) 89 | } else { 90 | req.Respond() 91 | } 92 | } 93 | 94 | // Respond to the request with Rread message 95 | func (req *Req) RespondRread(data []byte) { 96 | err := p.PackRread(req.Rc, data) 97 | if err != nil { 98 | req.RespondError(err) 99 | } else { 100 | req.Respond() 101 | } 102 | } 103 | 104 | // Respond to the request with Rwrite message 105 | func (req *Req) RespondRwrite(count uint32) { 106 | err := p.PackRwrite(req.Rc, count) 107 | if err != nil { 108 | req.RespondError(err) 109 | } else { 110 | req.Respond() 111 | } 112 | } 113 | 114 | // Respond to the request with Rclunk message 115 | func (req *Req) RespondRclunk() { 116 | err := p.PackRclunk(req.Rc) 117 | if err != nil { 118 | req.RespondError(err) 119 | } else { 120 | req.Respond() 121 | } 122 | } 123 | 124 | // Respond to the request with Rremove message 125 | func (req *Req) RespondRremove() { 126 | err := p.PackRremove(req.Rc) 127 | if err != nil { 128 | req.RespondError(err) 129 | } else { 130 | req.Respond() 131 | } 132 | } 133 | 134 | // Respond to the request with Rstat message 135 | func (req *Req) RespondRstat(st *p.Dir) { 136 | err := p.PackRstat(req.Rc, st, req.Conn.Dotu) 137 | if err != nil { 138 | req.RespondError(err) 139 | } else { 140 | req.Respond() 141 | } 142 | } 143 | 144 | // Respond to the request with Rwstat message 145 | func (req *Req) RespondRwstat() { 146 | err := p.PackRwstat(req.Rc) 147 | if err != nil { 148 | req.RespondError(err) 149 | } else { 150 | req.Respond() 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /p/srv/srv.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // The srv package provides definitions and functions used to implement 6 | // a 9P2000 file server. 7 | package srv 8 | 9 | import ( 10 | "github.com/lionkov/go9p/p" 11 | "net" 12 | "sync" 13 | ) 14 | 15 | type reqStatus int 16 | 17 | const ( 18 | reqFlush reqStatus = (1 << iota) /* request is flushed (no response will be sent) */ 19 | reqWork /* goroutine is currently working on it */ 20 | reqResponded /* response is already produced */ 21 | reqSaved /* no response was produced after the request is worked on */ 22 | ) 23 | 24 | // Debug flags 25 | const ( 26 | DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr 27 | DbgPrintPackets // print the raw packets on stderr 28 | DbgLogFcalls // keep the last N 9P messages (can be accessed over http) 29 | DbgLogPackets // keep the last N 9P messages (can be accessed over http) 30 | ) 31 | 32 | var Eunknownfid error = &p.Error{"unknown fid", p.EINVAL} 33 | var Enoauth error = &p.Error{"no authentication required", p.EINVAL} 34 | var Einuse error = &p.Error{"fid already in use", p.EINVAL} 35 | var Ebaduse error = &p.Error{"bad use of fid", p.EINVAL} 36 | var Eopen error = &p.Error{"fid already opened", p.EINVAL} 37 | var Enotdir error = &p.Error{"not a directory", p.ENOTDIR} 38 | var Eperm error = &p.Error{"permission denied", p.EPERM} 39 | var Etoolarge error = &p.Error{"i/o count too large", p.EINVAL} 40 | var Ebadoffset error = &p.Error{"bad offset in directory read", p.EINVAL} 41 | var Edirchange error = &p.Error{"cannot convert between files and directories", p.EINVAL} 42 | var Enouser error = &p.Error{"unknown user", p.EINVAL} 43 | var Enotimpl error = &p.Error{"not implemented", p.EINVAL} 44 | 45 | // Authentication operations. The file server should implement them if 46 | // it requires user authentication. The authentication in 9P2000 is 47 | // done by creating special authentication fids and performing I/O 48 | // operations on them. Once the authentication is done, the authentication 49 | // fid can be used by the user to get access to the actual files. 50 | type AuthOps interface { 51 | // AuthInit is called when the user starts the authentication 52 | // process on Fid afid. The user that is being authenticated 53 | // is referred by afid.User. The function should return the Qid 54 | // for the authentication file, or an Error if the user can't be 55 | // authenticated 56 | AuthInit(afid *Fid, aname string) (*p.Qid, error) 57 | 58 | // AuthDestroy is called when an authentication fid is destroyed. 59 | AuthDestroy(afid *Fid) 60 | 61 | // AuthCheck is called after the authentication process is finished 62 | // when the user tries to attach to the file server. If the function 63 | // returns nil, the authentication was successful and the user has 64 | // permission to access the files. 65 | AuthCheck(fid *Fid, afid *Fid, aname string) error 66 | 67 | // AuthRead is called when the user attempts to read data from an 68 | // authentication fid. 69 | AuthRead(afid *Fid, offset uint64, data []byte) (count int, err error) 70 | 71 | // AuthWrite is called when the user attempts to write data to an 72 | // authentication fid. 73 | AuthWrite(afid *Fid, offset uint64, data []byte) (count int, err error) 74 | } 75 | 76 | // Connection operations. These should be implemented if the file server 77 | // needs to be called when a connection is opened or closed. 78 | type ConnOps interface { 79 | ConnOpened(*Conn) 80 | ConnClosed(*Conn) 81 | } 82 | 83 | // Fid operations. This interface should be implemented if the file server 84 | // needs to be called when a Fid is destroyed. 85 | type FidOps interface { 86 | FidDestroy(*Fid) 87 | } 88 | 89 | // Request operations. This interface should be implemented if the file server 90 | // needs to bypass the default request process, or needs to perform certain 91 | // operations before the (any) request is processed, or before (any) response 92 | // sent back to the client. 93 | type ReqProcessOps interface { 94 | // Called when a new request is received from the client. If the 95 | // interface is not implemented, (req *Req) srv.Process() method is 96 | // called. If the interface is implemented, it is the user's 97 | // responsibility to call srv.Process. If srv.Process isn't called, 98 | // Fid, Afid and Newfid fields in Req are not set, and the ReqOps 99 | // methods are not called. 100 | ReqProcess(*Req) 101 | 102 | // Called when a request is responded, i.e. when (req *Req)srv.Respond() 103 | // is called and before the response is sent. If the interface is not 104 | // implemented, (req *Req) srv.PostProcess() method is called to finalize 105 | // the request. If the interface is implemented and ReqProcess calls 106 | // the srv.Process method, ReqRespond should call the srv.PostProcess 107 | // method. 108 | ReqRespond(*Req) 109 | } 110 | 111 | // Flush operation. This interface should be implemented if the file server 112 | // can flush pending requests. If the interface is not implemented, requests 113 | // that were passed to the file server implementation won't be flushed. 114 | // The flush method should call the (req *Req) srv.Flush() method if the flush 115 | // was successful so the request can be marked appropriately. 116 | type FlushOp interface { 117 | Flush(*Req) 118 | } 119 | 120 | // Request operations. This interface should be implemented by all file servers. 121 | // The operations correspond directly to most of the 9P2000 message types. 122 | type ReqOps interface { 123 | Attach(*Req) 124 | Walk(*Req) 125 | Open(*Req) 126 | Create(*Req) 127 | Read(*Req) 128 | Write(*Req) 129 | Clunk(*Req) 130 | Remove(*Req) 131 | Stat(*Req) 132 | Wstat(*Req) 133 | } 134 | 135 | type StatsOps interface { 136 | statsRegister() 137 | statsUnregister() 138 | } 139 | 140 | // The Srv type contains the basic fields used to control the 9P2000 141 | // file server. Each file server implementation should create a value 142 | // of Srv type, initialize the values it cares about and pass the 143 | // struct to the (Srv *) srv.Start(ops) method together with the object 144 | // that implements the file server operations. 145 | type Srv struct { 146 | sync.Mutex 147 | Id string // Used for debugging and stats 148 | Msize uint32 // Maximum size of the 9P2000 messages supported by the server 149 | Dotu bool // If true, the server supports the 9P2000.u extension 150 | Debuglevel int // debug level 151 | Upool p.Users // Interface for finding users and groups known to the file server 152 | Maxpend int // Maximum pending outgoing requests 153 | Log *p.Logger 154 | 155 | ops interface{} // operations 156 | conns map[*Conn]*Conn // List of connections 157 | } 158 | 159 | // The Conn type represents a connection from a client to the file server 160 | type Conn struct { 161 | sync.Mutex 162 | Srv *Srv 163 | Msize uint32 // maximum size of 9P2000 messages for the connection 164 | Dotu bool // if true, both the client and the server speak 9P2000.u 165 | Id string // used for debugging and stats 166 | Debuglevel int 167 | 168 | conn net.Conn 169 | Fidpool map[uint32]*Fid 170 | Reqs map[uint16]*Req // all outstanding requests 171 | 172 | Reqout chan *Req 173 | rchan chan *p.Fcall 174 | done chan bool 175 | 176 | // stats 177 | nreqs int // number of requests processed by the server 178 | tsz uint64 // total size of the T messages received 179 | rsz uint64 // total size of the R messages sent 180 | npend int // number of currently pending messages 181 | maxpend int // maximum number of pending messages 182 | nreads int // number of reads 183 | nwrites int // number of writes 184 | } 185 | 186 | // The Fid type identifies a file on the file server. 187 | // A new Fid is created when the user attaches to the file server (the Attach 188 | // operation), or when Walk-ing to a file. The Fid values are created 189 | // automatically by the srv implementation. The FidDestroy operation is called 190 | // when a Fid is destroyed. 191 | type Fid struct { 192 | sync.Mutex 193 | fid uint32 194 | refcount int 195 | opened bool // True if the Fid is opened 196 | Fconn *Conn // Connection the Fid belongs to 197 | Omode uint8 // Open mode (p.O* flags), if the fid is opened 198 | Type uint8 // Fid type (p.QT* flags) 199 | Diroffset uint64 // If directory, the next valid read position 200 | User p.User // The Fid's user 201 | Aux interface{} // Can be used by the file server implementation for per-Fid data 202 | } 203 | 204 | // The Req type represents a 9P2000 request. Each request has a 205 | // T-message (Tc) and a R-message (Rc). If the ReqProcessOps don't 206 | // override the default behavior, the implementation initializes Fid, 207 | // Afid and Newfid values and automatically keeps track on when the Fids 208 | // should be destroyed. 209 | type Req struct { 210 | sync.Mutex 211 | Tc *p.Fcall // Incoming 9P2000 message 212 | Rc *p.Fcall // Outgoing 9P2000 response 213 | Fid *Fid // The Fid value for all messages that contain fid[4] 214 | Afid *Fid // The Fid value for the messages that contain afid[4] (Tauth and Tattach) 215 | Newfid *Fid // The Fid value for the messages that contain newfid[4] (Twalk) 216 | Conn *Conn // Connection that the request belongs to 217 | 218 | status reqStatus 219 | flushreq *Req 220 | prev, next *Req 221 | } 222 | 223 | // The Start method should be called once the file server implementor 224 | // initializes the Srv struct with the preferred values. It sets default 225 | // values to the fields that are not initialized and creates the goroutines 226 | // required for the server's operation. The method receives an empty 227 | // interface value, ops, that should implement the interfaces the file server is 228 | // interested in. Ops must implement the ReqOps interface. 229 | func (srv *Srv) Start(ops interface{}) bool { 230 | if _, ok := (ops).(ReqOps); !ok { 231 | return false 232 | } 233 | 234 | srv.ops = ops 235 | if srv.Upool == nil { 236 | srv.Upool = p.OsUsers 237 | } 238 | 239 | if srv.Msize < p.IOHDRSZ { 240 | srv.Msize = p.MSIZE 241 | } 242 | 243 | if srv.Log == nil { 244 | srv.Log = p.NewLogger(1024) 245 | } 246 | 247 | if sop, ok := (interface{}(srv)).(StatsOps); ok { 248 | sop.statsRegister() 249 | } 250 | 251 | return true 252 | } 253 | 254 | func (srv *Srv) String() string { 255 | return srv.Id 256 | } 257 | 258 | func (req *Req) process() { 259 | req.Lock() 260 | flushed := (req.status & reqFlush) != 0 261 | if !flushed { 262 | req.status |= reqWork 263 | } 264 | req.Unlock() 265 | 266 | if flushed { 267 | req.Respond() 268 | } 269 | 270 | if rop, ok := (req.Conn.Srv.ops).(ReqProcessOps); ok { 271 | rop.ReqProcess(req) 272 | } else { 273 | req.Process() 274 | } 275 | 276 | req.Lock() 277 | req.status &= ^reqWork 278 | if !(req.status&reqResponded != 0) { 279 | req.status |= reqSaved 280 | } 281 | req.Unlock() 282 | } 283 | 284 | // Performs the default processing of a request. Initializes 285 | // the Fid, Afid and Newfid fields and calls the appropriate 286 | // ReqOps operation for the message. The file server implementer 287 | // should call it only if the file server implements the ReqProcessOps 288 | // within the ReqProcess operation. 289 | func (req *Req) Process() { 290 | conn := req.Conn 291 | srv := conn.Srv 292 | tc := req.Tc 293 | 294 | if tc.Fid != p.NOFID && tc.Type != p.Tattach { 295 | srv.Lock() 296 | req.Fid = conn.FidGet(tc.Fid) 297 | srv.Unlock() 298 | if req.Fid == nil { 299 | req.RespondError(Eunknownfid) 300 | return 301 | } 302 | } 303 | 304 | switch req.Tc.Type { 305 | default: 306 | req.RespondError(&p.Error{"unknown message type", p.EINVAL}) 307 | 308 | case p.Tversion: 309 | srv.version(req) 310 | 311 | case p.Tauth: 312 | srv.auth(req) 313 | 314 | case p.Tattach: 315 | srv.attach(req) 316 | 317 | case p.Tflush: 318 | srv.flush(req) 319 | 320 | case p.Twalk: 321 | srv.walk(req) 322 | 323 | case p.Topen: 324 | srv.open(req) 325 | 326 | case p.Tcreate: 327 | srv.create(req) 328 | 329 | case p.Tread: 330 | srv.read(req) 331 | 332 | case p.Twrite: 333 | srv.write(req) 334 | 335 | case p.Tclunk: 336 | srv.clunk(req) 337 | 338 | case p.Tremove: 339 | srv.remove(req) 340 | 341 | case p.Tstat: 342 | srv.stat(req) 343 | 344 | case p.Twstat: 345 | srv.wstat(req) 346 | } 347 | } 348 | 349 | // Performs the post processing required if the (*Req) Process() method 350 | // is called for a request. The file server implementer should call it 351 | // only if the file server implements the ReqProcessOps within the 352 | // ReqRespond operation. 353 | func (req *Req) PostProcess() { 354 | srv := req.Conn.Srv 355 | 356 | /* call the post-handlers (if needed) */ 357 | switch req.Tc.Type { 358 | case p.Tauth: 359 | srv.authPost(req) 360 | 361 | case p.Tattach: 362 | srv.attachPost(req) 363 | 364 | case p.Twalk: 365 | srv.walkPost(req) 366 | 367 | case p.Topen: 368 | srv.openPost(req) 369 | 370 | case p.Tcreate: 371 | srv.createPost(req) 372 | 373 | case p.Tread: 374 | srv.readPost(req) 375 | 376 | case p.Tclunk: 377 | srv.clunkPost(req) 378 | 379 | case p.Tremove: 380 | srv.removePost(req) 381 | } 382 | 383 | if req.Fid != nil { 384 | req.Fid.DecRef() 385 | req.Fid = nil 386 | } 387 | 388 | if req.Afid != nil { 389 | req.Afid.DecRef() 390 | req.Afid = nil 391 | } 392 | 393 | if req.Newfid != nil { 394 | req.Newfid.DecRef() 395 | req.Newfid = nil 396 | } 397 | } 398 | 399 | // The Respond method sends response back to the client. The req.Rc value 400 | // should be initialized and contain valid 9P2000 message. In most cases 401 | // the file server implementer shouldn't call this method directly. Instead 402 | // one of the RespondR* methods should be used. 403 | func (req *Req) Respond() { 404 | var flushreqs *Req 405 | 406 | conn := req.Conn 407 | req.Lock() 408 | status := req.status 409 | req.status |= reqResponded 410 | req.status &= ^reqWork 411 | req.Unlock() 412 | 413 | if (status & reqResponded) != 0 { 414 | return 415 | } 416 | 417 | /* remove the request and all requests flushing it */ 418 | conn.Lock() 419 | nextreq := req.prev 420 | if nextreq != nil { 421 | nextreq.next = nil 422 | // if there are flush requests, move them to the next request 423 | if req.flushreq != nil { 424 | var p *Req = nil 425 | r := nextreq.flushreq 426 | for ; r != nil; p, r = r, r.flushreq { 427 | } 428 | 429 | if p == nil { 430 | nextreq.flushreq = req.flushreq 431 | } else { 432 | p.next = req.flushreq 433 | } 434 | } 435 | 436 | flushreqs = nil 437 | } else { 438 | delete(conn.Reqs, req.Tc.Tag) 439 | flushreqs = req.flushreq 440 | } 441 | conn.Unlock() 442 | 443 | if rop, ok := (req.Conn.Srv.ops).(ReqProcessOps); ok { 444 | rop.ReqRespond(req) 445 | } else { 446 | req.PostProcess() 447 | } 448 | 449 | if (status & reqFlush) == 0 { 450 | conn.Reqout <- req 451 | } 452 | 453 | // process the next request with the same tag (if available) 454 | if nextreq != nil { 455 | go nextreq.process() 456 | } 457 | 458 | // respond to the flush messages 459 | // can't send the responses directly to conn.reqout, because the 460 | // the flushes may be in a tag group too 461 | for freq := flushreqs; freq != nil; freq = freq.flushreq { 462 | freq.Respond() 463 | } 464 | } 465 | 466 | // Should be called to cancel a request. Should only be called 467 | // from the Flush operation if the FlushOp is implemented. 468 | func (req *Req) Flush() { 469 | req.Lock() 470 | req.status |= reqFlush 471 | req.Unlock() 472 | req.Respond() 473 | } 474 | 475 | // Lookup a Fid struct based on the 32-bit identifier sent over the wire. 476 | // Returns nil if the fid is not found. Increases the reference count of 477 | // the returned fid. The user is responsible to call DecRef once it no 478 | // longer needs it. 479 | func (conn *Conn) FidGet(fidno uint32) *Fid { 480 | conn.Lock() 481 | fid, present := conn.Fidpool[fidno] 482 | conn.Unlock() 483 | if present { 484 | fid.IncRef() 485 | } 486 | 487 | return fid 488 | } 489 | 490 | // Creates a new Fid struct for the fidno integer. Returns nil 491 | // if the Fid for that number already exists. The returned fid 492 | // has reference count set to 1. 493 | func (conn *Conn) FidNew(fidno uint32) *Fid { 494 | conn.Lock() 495 | _, present := conn.Fidpool[fidno] 496 | if present { 497 | conn.Unlock() 498 | return nil 499 | } 500 | 501 | fid := new(Fid) 502 | fid.fid = fidno 503 | fid.refcount = 1 504 | fid.Fconn = conn 505 | conn.Fidpool[fidno] = fid 506 | conn.Unlock() 507 | 508 | return fid 509 | } 510 | 511 | func (conn *Conn) String() string { 512 | return conn.Srv.Id + "/" + conn.Id 513 | } 514 | 515 | // Increase the reference count for the fid. 516 | func (fid *Fid) IncRef() { 517 | fid.Lock() 518 | fid.refcount++ 519 | fid.Unlock() 520 | } 521 | 522 | // Decrease the reference count for the fid. When the 523 | // reference count reaches 0, the fid is no longer valid. 524 | func (fid *Fid) DecRef() { 525 | fid.Lock() 526 | fid.refcount-- 527 | n := fid.refcount 528 | fid.Unlock() 529 | 530 | if n > 0 { 531 | return 532 | } 533 | 534 | conn := fid.Fconn 535 | conn.Lock() 536 | delete(conn.Fidpool, fid.fid) 537 | conn.Unlock() 538 | 539 | if fop, ok := (conn.Srv.ops).(FidOps); ok { 540 | fop.FidDestroy(fid) 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /p/srv/stats_http.go: -------------------------------------------------------------------------------- 1 | // +build httpstats 2 | 3 | package srv 4 | 5 | import ( 6 | "github.com/lionkov/go9p/p" 7 | "fmt" 8 | "io" 9 | "net/http" 10 | "sync" 11 | ) 12 | 13 | var mux sync.RWMutex 14 | var stat map[string]http.Handler 15 | var once sync.Once 16 | 17 | func register(s string, h http.Handler) { 18 | mux.Lock() 19 | if stat == nil { 20 | stat = make(map[string]http.Handler) 21 | } 22 | 23 | if h == nil { 24 | delete(stat, s) 25 | } else { 26 | stat[s] = h 27 | } 28 | mux.Unlock() 29 | } 30 | func (srv *Srv) statsRegister() { 31 | once.Do(func() { 32 | http.HandleFunc("/go9p/", StatsHandler) 33 | go http.ListenAndServe(":6060", nil) 34 | }) 35 | 36 | register("/go9p/srv/"+srv.Id, srv) 37 | } 38 | 39 | func (srv *Srv) statsUnregister() { 40 | register("/go9p/srv/"+srv.Id, nil) 41 | } 42 | 43 | func (srv *Srv) ServeHTTP(c http.ResponseWriter, r *http.Request) { 44 | io.WriteString(c, fmt.Sprintf("

Server %s

", srv.Id)) 45 | defer io.WriteString(c, "") 46 | 47 | // connections 48 | io.WriteString(c, "

Connections

") 49 | srv.Lock() 50 | defer srv.Unlock() 51 | if len(srv.conns) == 0 { 52 | io.WriteString(c, "none") 53 | return 54 | } 55 | 56 | for _, conn := range srv.conns { 57 | io.WriteString(c, fmt.Sprintf("%s
", srv.Id, conn.Id, conn.Id)) 58 | } 59 | } 60 | 61 | func (conn *Conn) statsRegister() { 62 | register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, conn) 63 | } 64 | 65 | func (conn *Conn) statsUnregister() { 66 | register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, nil) 67 | } 68 | 69 | func (conn *Conn) ServeHTTP(c http.ResponseWriter, r *http.Request) { 70 | io.WriteString(c, fmt.Sprintf("

Connection %s/%s

", conn.Srv.Id, conn.Id)) 71 | defer io.WriteString(c, "") 72 | 73 | // statistics 74 | conn.Lock() 75 | io.WriteString(c, fmt.Sprintf("

Number of processed requests: %d", conn.nreqs)) 76 | io.WriteString(c, fmt.Sprintf("
Sent %v bytes", conn.rsz)) 77 | io.WriteString(c, fmt.Sprintf("
Received %v bytes", conn.tsz)) 78 | io.WriteString(c, fmt.Sprintf("
Pending requests: %d max %d", conn.npend, conn.maxpend)) 79 | io.WriteString(c, fmt.Sprintf("
Number of reads: %d", conn.nreads)) 80 | io.WriteString(c, fmt.Sprintf("
Number of writes: %d", conn.nwrites)) 81 | conn.Unlock() 82 | 83 | // fcalls 84 | if conn.Debuglevel&DbgLogFcalls != 0 { 85 | fs := conn.Srv.Log.Filter(conn, DbgLogFcalls) 86 | io.WriteString(c, fmt.Sprintf("

Last %d 9P messages

", len(fs))) 87 | for i, l := range fs { 88 | fc := l.Data.(*p.Fcall) 89 | if fc.Type == 0 { 90 | continue 91 | } 92 | 93 | lbl := "" 94 | if fc.Type%2 == 0 { 95 | // try to find the response for the T message 96 | for j := i + 1; j < len(fs); j++ { 97 | rc := fs[j].Data.(*p.Fcall) 98 | if rc.Tag == fc.Tag { 99 | lbl = fmt.Sprintf("", j) 100 | break 101 | } 102 | } 103 | } else { 104 | // try to find the request for the R message 105 | for j := i - 1; j >= 0; j-- { 106 | tc := fs[j].Data.(*p.Fcall) 107 | if tc.Tag == fc.Tag { 108 | lbl = fmt.Sprintf("", j) 109 | break 110 | } 111 | } 112 | } 113 | 114 | io.WriteString(c, fmt.Sprintf("
%d: %s%s", i, i, fc, lbl)) 115 | } 116 | } 117 | } 118 | 119 | func StatsHandler(c http.ResponseWriter, r *http.Request) { 120 | mux.RLock() 121 | if v, ok := stat[r.URL.Path]; ok { 122 | v.ServeHTTP(c, r) 123 | } else if r.URL.Path == "/go9p/" { 124 | io.WriteString(c, fmt.Sprintf("

On offer:


")) 125 | for v := range stat { 126 | io.WriteString(c, fmt.Sprintf("%s
", v, v)) 127 | } 128 | io.WriteString(c, "") 129 | } 130 | mux.RUnlock() 131 | } 132 | -------------------------------------------------------------------------------- /p/srv/ufs/ufs.go: -------------------------------------------------------------------------------- 1 | package ufs 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "os/user" 10 | "path" 11 | "path/filepath" 12 | "sort" 13 | "strconv" 14 | "strings" 15 | "syscall" 16 | "time" 17 | 18 | "github.com/lionkov/go9p/p" 19 | "github.com/lionkov/go9p/p/srv" 20 | ) 21 | 22 | type Fid struct { 23 | path string 24 | file *os.File 25 | dirs []os.FileInfo 26 | diroffset uint64 27 | direntends []int 28 | dirents []byte 29 | st os.FileInfo 30 | } 31 | 32 | type Ufs struct { 33 | srv.Srv 34 | Root string 35 | } 36 | 37 | var root = flag.String("root", "/", "root filesystem") 38 | var Enoent = &p.Error{"file not found", p.ENOENT} 39 | 40 | func toError(err error) *p.Error { 41 | var ecode uint32 42 | 43 | ename := err.Error() 44 | if e, ok := err.(syscall.Errno); ok { 45 | ecode = uint32(e) 46 | } else { 47 | ecode = p.EIO 48 | } 49 | 50 | return &p.Error{ename, ecode} 51 | } 52 | 53 | // IsBlock reports if the file is a block device 54 | func isBlock(d os.FileInfo) bool { 55 | sysif := d.Sys() 56 | if sysif == nil { 57 | return false 58 | } 59 | stat := sysif.(*syscall.Stat_t) 60 | return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK 61 | } 62 | 63 | // IsChar reports if the file is a character device 64 | func isChar(d os.FileInfo) bool { 65 | sysif := d.Sys() 66 | if sysif == nil { 67 | return false 68 | } 69 | stat := sysif.(*syscall.Stat_t) 70 | return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR 71 | } 72 | 73 | func (fid *Fid) stat() *p.Error { 74 | var err error 75 | 76 | fid.st, err = os.Lstat(fid.path) 77 | if err != nil { 78 | return toError(err) 79 | } 80 | 81 | return nil 82 | } 83 | 84 | func omode2uflags(mode uint8) int { 85 | ret := int(0) 86 | switch mode & 3 { 87 | case p.OREAD: 88 | ret = os.O_RDONLY 89 | break 90 | 91 | case p.ORDWR: 92 | ret = os.O_RDWR 93 | break 94 | 95 | case p.OWRITE: 96 | ret = os.O_WRONLY 97 | break 98 | 99 | case p.OEXEC: 100 | ret = os.O_RDONLY 101 | break 102 | } 103 | 104 | if mode&p.OTRUNC != 0 { 105 | ret |= os.O_TRUNC 106 | } 107 | 108 | return ret 109 | } 110 | 111 | func dir2Qid(d os.FileInfo) *p.Qid { 112 | var qid p.Qid 113 | sysif := d.Sys() 114 | if sysif == nil { 115 | return nil 116 | } 117 | stat := sysif.(*syscall.Stat_t) 118 | 119 | qid.Path = stat.Ino 120 | qid.Version = uint32(d.ModTime().UnixNano() / 1000000) 121 | qid.Type = dir2QidType(d) 122 | 123 | return &qid 124 | } 125 | 126 | func dir2QidType(d os.FileInfo) uint8 { 127 | ret := uint8(0) 128 | if d.IsDir() { 129 | ret |= p.QTDIR 130 | } 131 | 132 | if d.Mode()&os.ModeSymlink != 0 { 133 | ret |= p.QTSYMLINK 134 | } 135 | 136 | return ret 137 | } 138 | 139 | func dir2Npmode(d os.FileInfo, dotu bool) uint32 { 140 | ret := uint32(d.Mode() & 0777) 141 | if d.IsDir() { 142 | ret |= p.DMDIR 143 | } 144 | 145 | if dotu { 146 | mode := d.Mode() 147 | if mode&os.ModeSymlink != 0 { 148 | ret |= p.DMSYMLINK 149 | } 150 | 151 | if mode&os.ModeSocket != 0 { 152 | ret |= p.DMSOCKET 153 | } 154 | 155 | if mode&os.ModeNamedPipe != 0 { 156 | ret |= p.DMNAMEDPIPE 157 | } 158 | 159 | if mode&os.ModeDevice != 0 { 160 | ret |= p.DMDEVICE 161 | } 162 | 163 | if mode&os.ModeSetuid != 0 { 164 | ret |= p.DMSETUID 165 | } 166 | 167 | if mode&os.ModeSetgid != 0 { 168 | ret |= p.DMSETGID 169 | } 170 | } 171 | 172 | return ret 173 | } 174 | 175 | // Dir is an instantiation of the p.Dir structure 176 | // that can act as a receiver for local methods. 177 | type Dir struct { 178 | p.Dir 179 | } 180 | 181 | func dir2Dir(s string, d os.FileInfo, dotu bool, upool p.Users) (*p.Dir, error) { 182 | sysif := d.Sys() 183 | if sysif == nil { 184 | return nil, &os.PathError{"dir2Dir", s, nil} 185 | } 186 | var sysMode *syscall.Stat_t 187 | switch t := sysif.(type) { 188 | case *syscall.Stat_t: 189 | sysMode = t 190 | default: 191 | return nil, &os.PathError{"dir2Dir: sysif has wrong type", s, nil} 192 | } 193 | 194 | dir := new(Dir) 195 | dir.Qid = *dir2Qid(d) 196 | dir.Mode = dir2Npmode(d, dotu) 197 | dir.Atime = uint32(atime(sysMode).Unix()) 198 | dir.Mtime = uint32(d.ModTime().Unix()) 199 | dir.Length = uint64(d.Size()) 200 | dir.Name = s[strings.LastIndex(s, "/")+1:] 201 | 202 | if dotu { 203 | dir.dotu(s, d, upool, sysMode) 204 | return &dir.Dir, nil 205 | } 206 | 207 | unixUid := int(sysMode.Uid) 208 | unixGid := int(sysMode.Gid) 209 | dir.Uid = strconv.Itoa(unixUid) 210 | dir.Gid = strconv.Itoa(unixGid) 211 | dir.Muid = "none" 212 | 213 | // BUG(akumar): LookupId will never find names for 214 | // groups, as it only operates on user ids. 215 | u, err := user.LookupId(dir.Uid) 216 | if err == nil { 217 | dir.Uid = u.Username 218 | } 219 | g, err := user.LookupId(dir.Gid) 220 | if err == nil { 221 | dir.Gid = g.Username 222 | } 223 | 224 | return &dir.Dir, nil 225 | } 226 | 227 | func (dir *Dir) dotu(path string, d os.FileInfo, upool p.Users, sysMode *syscall.Stat_t) { 228 | u := upool.Uid2User(int(sysMode.Uid)) 229 | g := upool.Gid2Group(int(sysMode.Gid)) 230 | dir.Uid = u.Name() 231 | if dir.Uid == "" { 232 | dir.Uid = "none" 233 | } 234 | 235 | dir.Gid = g.Name() 236 | if dir.Gid == "" { 237 | dir.Gid = "none" 238 | } 239 | dir.Muid = "none" 240 | dir.Ext = "" 241 | dir.Uidnum = uint32(u.Id()) 242 | dir.Gidnum = uint32(g.Id()) 243 | dir.Muidnum = p.NOUID 244 | if d.Mode()&os.ModeSymlink != 0 { 245 | var err error 246 | dir.Ext, err = os.Readlink(path) 247 | if err != nil { 248 | dir.Ext = "" 249 | } 250 | } else if isBlock(d) { 251 | dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF) 252 | } else if isChar(d) { 253 | dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF) 254 | } 255 | } 256 | 257 | func (*Ufs) ConnOpened(conn *srv.Conn) { 258 | if conn.Srv.Debuglevel > 0 { 259 | log.Println("connected") 260 | } 261 | } 262 | 263 | func (*Ufs) ConnClosed(conn *srv.Conn) { 264 | if conn.Srv.Debuglevel > 0 { 265 | log.Println("disconnected") 266 | } 267 | } 268 | 269 | func (*Ufs) FidDestroy(sfid *srv.Fid) { 270 | var fid *Fid 271 | 272 | if sfid.Aux == nil { 273 | return 274 | } 275 | 276 | fid = sfid.Aux.(*Fid) 277 | if fid.file != nil { 278 | fid.file.Close() 279 | } 280 | } 281 | 282 | func (u *Ufs) Attach(req *srv.Req) { 283 | if req.Afid != nil { 284 | req.RespondError(srv.Enoauth) 285 | return 286 | } 287 | 288 | tc := req.Tc 289 | fid := new(Fid) 290 | 291 | // You can think of the ufs.Root as a 'chroot' of a sort. 292 | // client attaches are not allowed to go outside the 293 | // directory represented by ufs.Root 294 | fid.path = path.Join(u.Root, tc.Aname) 295 | 296 | req.Fid.Aux = fid 297 | err := fid.stat() 298 | if err != nil { 299 | req.RespondError(err) 300 | return 301 | } 302 | 303 | qid := dir2Qid(fid.st) 304 | req.RespondRattach(qid) 305 | } 306 | 307 | func (*Ufs) Flush(req *srv.Req) {} 308 | 309 | func (*Ufs) Walk(req *srv.Req) { 310 | fid := req.Fid.Aux.(*Fid) 311 | tc := req.Tc 312 | 313 | err := fid.stat() 314 | if err != nil { 315 | req.RespondError(err) 316 | return 317 | } 318 | 319 | if req.Newfid.Aux == nil { 320 | req.Newfid.Aux = new(Fid) 321 | } 322 | 323 | nfid := req.Newfid.Aux.(*Fid) 324 | wqids := make([]p.Qid, len(tc.Wname)) 325 | path := fid.path 326 | i := 0 327 | for ; i < len(tc.Wname); i++ { 328 | p := path + "/" + tc.Wname[i] 329 | st, err := os.Lstat(p) 330 | if err != nil { 331 | if i == 0 { 332 | req.RespondError(Enoent) 333 | return 334 | } 335 | 336 | break 337 | } 338 | 339 | wqids[i] = *dir2Qid(st) 340 | path = p 341 | } 342 | 343 | nfid.path = path 344 | req.RespondRwalk(wqids[0:i]) 345 | } 346 | 347 | func (*Ufs) Open(req *srv.Req) { 348 | fid := req.Fid.Aux.(*Fid) 349 | tc := req.Tc 350 | err := fid.stat() 351 | if err != nil { 352 | req.RespondError(err) 353 | return 354 | } 355 | 356 | var e error 357 | fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0) 358 | if e != nil { 359 | req.RespondError(toError(e)) 360 | return 361 | } 362 | 363 | req.RespondRopen(dir2Qid(fid.st), 0) 364 | } 365 | 366 | func (*Ufs) Create(req *srv.Req) { 367 | fid := req.Fid.Aux.(*Fid) 368 | tc := req.Tc 369 | err := fid.stat() 370 | if err != nil { 371 | req.RespondError(err) 372 | return 373 | } 374 | 375 | path := fid.path + "/" + tc.Name 376 | var e error = nil 377 | var file *os.File = nil 378 | switch { 379 | case tc.Perm&p.DMDIR != 0: 380 | e = os.Mkdir(path, os.FileMode(tc.Perm&0777)) 381 | 382 | case tc.Perm&p.DMSYMLINK != 0: 383 | e = os.Symlink(tc.Ext, path) 384 | 385 | case tc.Perm&p.DMLINK != 0: 386 | n, e := strconv.ParseUint(tc.Ext, 10, 0) 387 | if e != nil { 388 | break 389 | } 390 | 391 | ofid := req.Conn.FidGet(uint32(n)) 392 | if ofid == nil { 393 | req.RespondError(srv.Eunknownfid) 394 | return 395 | } 396 | 397 | e = os.Link(ofid.Aux.(*Fid).path, path) 398 | ofid.DecRef() 399 | 400 | case tc.Perm&p.DMNAMEDPIPE != 0: 401 | case tc.Perm&p.DMDEVICE != 0: 402 | req.RespondError(&p.Error{"not implemented", p.EIO}) 403 | return 404 | 405 | default: 406 | var mode uint32 = tc.Perm & 0777 407 | if req.Conn.Dotu { 408 | if tc.Perm&p.DMSETUID > 0 { 409 | mode |= syscall.S_ISUID 410 | } 411 | if tc.Perm&p.DMSETGID > 0 { 412 | mode |= syscall.S_ISGID 413 | } 414 | } 415 | file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode)) 416 | } 417 | 418 | if file == nil && e == nil { 419 | file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0) 420 | } 421 | 422 | if e != nil { 423 | req.RespondError(toError(e)) 424 | return 425 | } 426 | 427 | fid.path = path 428 | fid.file = file 429 | err = fid.stat() 430 | if err != nil { 431 | req.RespondError(err) 432 | return 433 | } 434 | 435 | req.RespondRcreate(dir2Qid(fid.st), 0) 436 | } 437 | 438 | func (u *Ufs) Read(req *srv.Req) { 439 | dbg := u.Debuglevel&srv.DbgLogFcalls != 0 440 | fid := req.Fid.Aux.(*Fid) 441 | tc := req.Tc 442 | rc := req.Rc 443 | err := fid.stat() 444 | if err != nil { 445 | req.RespondError(err) 446 | return 447 | } 448 | 449 | p.InitRread(rc, tc.Count) 450 | var count int 451 | var e error 452 | if fid.st.IsDir() { 453 | if tc.Offset == 0 { 454 | var e error 455 | // If we got here, it was open. Can't really seek 456 | // in most cases, just close and reopen it. 457 | fid.file.Close() 458 | if fid.file, e = os.OpenFile(fid.path, omode2uflags(req.Fid.Omode), 0); e != nil { 459 | req.RespondError(toError(e)) 460 | return 461 | } 462 | 463 | if fid.dirs, e = fid.file.Readdir(-1); e != nil { 464 | req.RespondError(toError(e)) 465 | return 466 | } 467 | 468 | if dbg { 469 | log.Printf("Read: read %d entries", len(fid.dirs)) 470 | } 471 | fid.dirents = nil 472 | fid.direntends = nil 473 | for i := 0; i < len(fid.dirs); i++ { 474 | path := fid.path + "/" + fid.dirs[i].Name() 475 | st, err := dir2Dir(path, fid.dirs[i], req.Conn.Dotu, req.Conn.Srv.Upool) 476 | if err != nil { 477 | if dbg { 478 | log.Printf("dbg: stat of %v: %v", path, err) 479 | } 480 | continue 481 | } 482 | if dbg { 483 | log.Printf("Stat: %v is %v", path, st) 484 | } 485 | b := p.PackDir(st, req.Conn.Dotu) 486 | fid.dirents = append(fid.dirents, b...) 487 | count += len(b) 488 | fid.direntends = append(fid.direntends, count) 489 | if dbg { 490 | log.Printf("fid.direntends is %v\n", fid.direntends) 491 | } 492 | } 493 | } 494 | 495 | switch { 496 | case tc.Offset > uint64(len(fid.dirents)): 497 | count = 0 498 | case len(fid.dirents[tc.Offset:]) > int(tc.Count): 499 | count = int(tc.Count) 500 | default: 501 | count = len(fid.dirents[tc.Offset:]) 502 | } 503 | 504 | if dbg { 505 | log.Printf("readdir: count %v @ offset %v", count, tc.Offset) 506 | } 507 | nextend := sort.SearchInts(fid.direntends, int(tc.Offset)+count) 508 | if nextend < len(fid.direntends) { 509 | if fid.direntends[nextend] > int(tc.Offset)+count { 510 | if nextend > 0 { 511 | count = fid.direntends[nextend-1] - int(tc.Offset) 512 | } else { 513 | count = 0 514 | } 515 | } 516 | } 517 | if dbg { 518 | log.Printf("readdir: count adjusted %v @ offset %v", count, tc.Offset) 519 | } 520 | if count == 0 && int(tc.Offset) < len(fid.dirents) && len(fid.dirents) > 0 { 521 | req.RespondError(&p.Error{"too small read size for dir entry", p.EINVAL}) 522 | return 523 | } 524 | copy(rc.Data, fid.dirents[tc.Offset:int(tc.Offset)+count]) 525 | } else { 526 | count, e = fid.file.ReadAt(rc.Data, int64(tc.Offset)) 527 | if e != nil && e != io.EOF { 528 | req.RespondError(toError(e)) 529 | return 530 | } 531 | } 532 | 533 | p.SetRreadCount(rc, uint32(count)) 534 | req.Respond() 535 | } 536 | 537 | func (*Ufs) Write(req *srv.Req) { 538 | fid := req.Fid.Aux.(*Fid) 539 | tc := req.Tc 540 | err := fid.stat() 541 | if err != nil { 542 | req.RespondError(err) 543 | return 544 | } 545 | 546 | n, e := fid.file.WriteAt(tc.Data, int64(tc.Offset)) 547 | if e != nil { 548 | req.RespondError(toError(e)) 549 | return 550 | } 551 | 552 | req.RespondRwrite(uint32(n)) 553 | } 554 | 555 | func (*Ufs) Clunk(req *srv.Req) { req.RespondRclunk() } 556 | 557 | func (*Ufs) Remove(req *srv.Req) { 558 | fid := req.Fid.Aux.(*Fid) 559 | err := fid.stat() 560 | if err != nil { 561 | req.RespondError(err) 562 | return 563 | } 564 | 565 | e := os.Remove(fid.path) 566 | if e != nil { 567 | req.RespondError(toError(e)) 568 | return 569 | } 570 | 571 | req.RespondRremove() 572 | } 573 | 574 | func (*Ufs) Stat(req *srv.Req) { 575 | fid := req.Fid.Aux.(*Fid) 576 | if err := fid.stat(); err != nil { 577 | req.RespondError(err) 578 | return 579 | } 580 | 581 | st, err := dir2Dir(fid.path, fid.st, req.Conn.Dotu, req.Conn.Srv.Upool) 582 | if err != nil { 583 | req.RespondError(err) 584 | return 585 | } 586 | req.RespondRstat(st) 587 | } 588 | 589 | func lookup(uid string, group bool) (uint32, *p.Error) { 590 | if uid == "" { 591 | return p.NOUID, nil 592 | } 593 | usr, e := user.Lookup(uid) 594 | if e != nil { 595 | return p.NOUID, toError(e) 596 | } 597 | conv := usr.Uid 598 | if group { 599 | conv = usr.Gid 600 | } 601 | u, e := strconv.Atoi(conv) 602 | if e != nil { 603 | return p.NOUID, toError(e) 604 | } 605 | return uint32(u), nil 606 | } 607 | 608 | func (u *Ufs) Wstat(req *srv.Req) { 609 | var changed bool 610 | fid := req.Fid.Aux.(*Fid) 611 | err := fid.stat() 612 | if err != nil { 613 | req.RespondError(err) 614 | return 615 | } 616 | 617 | dir := &req.Tc.Dir 618 | if dir.Mode != 0xFFFFFFFF { 619 | changed = true 620 | mode := dir.Mode & 0777 621 | if req.Conn.Dotu { 622 | if dir.Mode&p.DMSETUID > 0 { 623 | mode |= syscall.S_ISUID 624 | } 625 | if dir.Mode&p.DMSETGID > 0 { 626 | mode |= syscall.S_ISGID 627 | } 628 | } 629 | e := os.Chmod(fid.path, os.FileMode(mode)) 630 | if e != nil { 631 | req.RespondError(toError(e)) 632 | return 633 | } 634 | } 635 | 636 | uid, gid := p.NOUID, p.NOUID 637 | if req.Conn.Dotu { 638 | uid = dir.Uidnum 639 | gid = dir.Gidnum 640 | } 641 | 642 | // Try to find local uid, gid by name. 643 | if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu { 644 | changed = true 645 | uid, err = lookup(dir.Uid, false) 646 | if err != nil { 647 | req.RespondError(err) 648 | return 649 | } 650 | 651 | // BUG(akumar): Lookup will never find gids 652 | // corresponding to group names, because 653 | // it only operates on user names. 654 | gid, err = lookup(dir.Gid, true) 655 | if err != nil { 656 | req.RespondError(err) 657 | return 658 | } 659 | } 660 | 661 | if uid != p.NOUID || gid != p.NOUID { 662 | changed = true 663 | e := os.Chown(fid.path, int(uid), int(gid)) 664 | if e != nil { 665 | req.RespondError(toError(e)) 666 | return 667 | } 668 | } 669 | 670 | if dir.Name != "" { 671 | changed = true 672 | // If we path.Join dir.Name to / before adding it to 673 | // the fid path, that ensures nobody gets to walk out of the 674 | // root of this server. 675 | newname := path.Join(path.Dir(fid.path), path.Join("/", dir.Name)) 676 | 677 | // absolute renaming. Ufs can do this, so let's support it. 678 | // We'll allow an absolute path in the Name and, if it is, 679 | // we will make it relative to root. This is a gigantic performance 680 | // improvement in systems that allow it. 681 | if filepath.IsAbs(dir.Name) { 682 | newname = path.Join(u.Root, dir.Name) 683 | } 684 | 685 | err := syscall.Rename(fid.path, newname) 686 | if err != nil { 687 | req.RespondError(toError(err)) 688 | return 689 | } 690 | fid.path = newname 691 | } 692 | 693 | if dir.Length != 0xFFFFFFFFFFFFFFFF { 694 | changed = true 695 | e := os.Truncate(fid.path, int64(dir.Length)) 696 | if e != nil { 697 | req.RespondError(toError(e)) 698 | return 699 | } 700 | } 701 | 702 | // If either mtime or atime need to be changed, then 703 | // we must change both. 704 | if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) { 705 | changed = true 706 | mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0) 707 | if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat { 708 | st, e := os.Stat(fid.path) 709 | if e != nil { 710 | req.RespondError(toError(e)) 711 | return 712 | } 713 | switch cmt { 714 | case true: 715 | mt = st.ModTime() 716 | default: 717 | at = atime(st.Sys().(*syscall.Stat_t)) 718 | } 719 | } 720 | e := os.Chtimes(fid.path, at, mt) 721 | if e != nil { 722 | req.RespondError(toError(e)) 723 | return 724 | } 725 | } 726 | 727 | if !changed && fid.file != nil { 728 | fid.file.Sync() 729 | } 730 | req.RespondRwstat() 731 | } 732 | 733 | func New() *Ufs { 734 | return &Ufs{Root: *root} 735 | } 736 | -------------------------------------------------------------------------------- /p/srv/ufs/ufs_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ufs 6 | 7 | import ( 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | func atime(stat *syscall.Stat_t) time.Time { 13 | return time.Unix(stat.Atimespec.Unix()) 14 | } 15 | -------------------------------------------------------------------------------- /p/srv/ufs/ufs_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ufs 6 | 7 | import ( 8 | "syscall" 9 | "time" 10 | ) 11 | 12 | func atime(stat *syscall.Stat_t) time.Time { 13 | return time.Unix(stat.Atim.Unix()) 14 | } 15 | -------------------------------------------------------------------------------- /p/unpack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go9p Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package p 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // Creates a Fcall value from the on-the-wire representation. If 12 | // dotu is true, reads 9P2000.u messages. Returns the unpacked message, 13 | // error and how many bytes from the buffer were used by the message. 14 | func Unpack(buf []byte, dotu bool) (fc *Fcall, err error, fcsz int) { 15 | var m uint16 16 | 17 | if len(buf) < 7 { 18 | return nil, &Error{"buffer too short", EINVAL}, 0 19 | } 20 | 21 | fc = new(Fcall) 22 | fc.Fid = NOFID 23 | fc.Afid = NOFID 24 | fc.Newfid = NOFID 25 | 26 | p := buf 27 | fc.Size, p = gint32(p) 28 | fc.Type, p = gint8(p) 29 | fc.Tag, p = gint16(p) 30 | 31 | if int(fc.Size) > len(buf) || fc.Size < 7 { 32 | return nil, &Error{fmt.Sprintf("buffer too short: %d expected %d", 33 | len(buf), fc.Size), 34 | EINVAL}, 35 | 0 36 | } 37 | 38 | p = p[0 : fc.Size-7] 39 | fc.Pkt = buf[0:fc.Size] 40 | fcsz = int(fc.Size) 41 | if fc.Type < Tversion || fc.Type >= Tlast { 42 | return nil, &Error{"invalid id", EINVAL}, 0 43 | } 44 | 45 | var sz uint32 46 | if dotu { 47 | sz = minFcusize[fc.Type-Tversion] 48 | } else { 49 | sz = minFcsize[fc.Type-Tversion] 50 | } 51 | 52 | if fc.Size < sz { 53 | goto szerror 54 | } 55 | 56 | err = nil 57 | switch fc.Type { 58 | default: 59 | return nil, &Error{"invalid message id", EINVAL}, 0 60 | 61 | case Tversion, Rversion: 62 | fc.Msize, p = gint32(p) 63 | fc.Version, p = gstr(p) 64 | if p == nil { 65 | goto szerror 66 | } 67 | 68 | case Tauth: 69 | fc.Afid, p = gint32(p) 70 | fc.Uname, p = gstr(p) 71 | if p == nil { 72 | goto szerror 73 | } 74 | 75 | fc.Aname, p = gstr(p) 76 | if p == nil { 77 | goto szerror 78 | } 79 | 80 | fc.Unamenum = NOUID 81 | if dotu && len(p) > 0 { 82 | fc.Unamenum, p = gint32(p) 83 | } 84 | 85 | case Rauth, Rattach: 86 | p = gqid(p, &fc.Qid) 87 | 88 | case Tflush: 89 | fc.Oldtag, p = gint16(p) 90 | 91 | case Tattach: 92 | fc.Fid, p = gint32(p) 93 | fc.Afid, p = gint32(p) 94 | fc.Uname, p = gstr(p) 95 | if p == nil { 96 | goto szerror 97 | } 98 | 99 | fc.Aname, p = gstr(p) 100 | if p == nil { 101 | goto szerror 102 | } 103 | 104 | fc.Unamenum = NOUID 105 | if dotu && len(p) > 0 { 106 | fc.Unamenum, p = gint32(p) 107 | } 108 | 109 | case Rerror: 110 | fc.Error, p = gstr(p) 111 | if p == nil { 112 | goto szerror 113 | } 114 | if dotu { 115 | fc.Errornum, p = gint32(p) 116 | } else { 117 | fc.Errornum = 0 118 | } 119 | 120 | case Twalk: 121 | fc.Fid, p = gint32(p) 122 | fc.Newfid, p = gint32(p) 123 | m, p = gint16(p) 124 | fc.Wname = make([]string, m) 125 | for i := 0; i < int(m); i++ { 126 | fc.Wname[i], p = gstr(p) 127 | if p == nil { 128 | goto szerror 129 | } 130 | } 131 | 132 | case Rwalk: 133 | m, p = gint16(p) 134 | fc.Wqid = make([]Qid, m) 135 | for i := 0; i < int(m); i++ { 136 | p = gqid(p, &fc.Wqid[i]) 137 | } 138 | 139 | case Topen: 140 | fc.Fid, p = gint32(p) 141 | fc.Mode, p = gint8(p) 142 | 143 | case Ropen, Rcreate: 144 | p = gqid(p, &fc.Qid) 145 | fc.Iounit, p = gint32(p) 146 | 147 | case Tcreate: 148 | fc.Fid, p = gint32(p) 149 | fc.Name, p = gstr(p) 150 | if p == nil { 151 | goto szerror 152 | } 153 | fc.Perm, p = gint32(p) 154 | fc.Mode, p = gint8(p) 155 | if dotu { 156 | fc.Ext, p = gstr(p) 157 | if p == nil { 158 | goto szerror 159 | } 160 | } 161 | 162 | case Tread: 163 | fc.Fid, p = gint32(p) 164 | fc.Offset, p = gint64(p) 165 | fc.Count, p = gint32(p) 166 | 167 | case Rread: 168 | fc.Count, p = gint32(p) 169 | if len(p) < int(fc.Count) { 170 | goto szerror 171 | } 172 | fc.Data = p 173 | p = p[fc.Count:] 174 | 175 | case Twrite: 176 | fc.Fid, p = gint32(p) 177 | fc.Offset, p = gint64(p) 178 | fc.Count, p = gint32(p) 179 | if len(p) != int(fc.Count) { 180 | fc.Data = make([]byte, fc.Count) 181 | copy(fc.Data, p) 182 | p = p[len(p):] 183 | } else { 184 | fc.Data = p 185 | p = p[fc.Count:] 186 | } 187 | 188 | case Rwrite: 189 | fc.Count, p = gint32(p) 190 | 191 | case Tclunk, Tremove, Tstat: 192 | fc.Fid, p = gint32(p) 193 | 194 | case Rstat: 195 | m, p = gint16(p) 196 | p, err = gstat(p, &fc.Dir, dotu) 197 | if err != nil { 198 | return nil, err, 0 199 | } 200 | 201 | case Twstat: 202 | fc.Fid, p = gint32(p) 203 | m, p = gint16(p) 204 | p, _ = gstat(p, &fc.Dir, dotu) 205 | 206 | case Rflush, Rclunk, Rremove, Rwstat: 207 | } 208 | 209 | if len(p) > 0 { 210 | goto szerror 211 | } 212 | 213 | return 214 | 215 | szerror: 216 | return nil, &Error{"invalid size", EINVAL}, 0 217 | } 218 | --------------------------------------------------------------------------------