├── .gitignore
├── LICENSE
├── README.md
├── clnt_clnt.go
├── clnt_close.go
├── clnt_mount.go
├── clnt_open.go
├── clnt_pool.go
├── clnt_read.go
├── clnt_remove.go
├── clnt_stat.go
├── clnt_stats_http.go
├── clnt_tag.go
├── clnt_walk.go
├── clnt_write.go
├── debug.go
├── fmt.go
├── go9p_test.go
├── log.go
├── osusers.go
├── p9.go
├── packr.go
├── packt.go
├── srv_conn.go
├── srv_fcall.go
├── srv_file.go
├── srv_pipe.go
├── srv_respond.go
├── srv_srv.go
├── srv_stats_http.go
├── stats.go
├── ufs.go
├── ufs
└── ufs.go
└── unpack.go
/.gitignore:
--------------------------------------------------------------------------------
1 | *.orig
2 | *.rej
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is go9p done in a way that I can understand.
2 |
3 | To install:
4 | export GOPATH=~rminnich/go
5 | go get -a github.com/rminnich/go9p
6 | go get -a github.com/rminnich/go9p/ufs
7 | go install -a github.com/rminnich/go9p/ufs
8 |
9 | ~/go/bin/ufs
10 |
11 |
12 |
--------------------------------------------------------------------------------
/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 go9provides definitions and functions used to implement
6 | // a 9P2000 file client.
7 | package go9p
8 |
9 | import (
10 | "fmt"
11 | "log"
12 | "net"
13 | "sync"
14 | "sync/atomic"
15 | )
16 |
17 | // The Clnt type represents a 9P2000 client. The client is connected to
18 | // a 9P2000 file server and its methods can be used to access and manipulate
19 | // the files exported by the server.
20 | type Clnt struct {
21 | sync.Mutex
22 | Debuglevel int // =0 don't print anything, >0 print Fcalls, >1 print raw packets
23 | Msize uint32 // Maximum size of the 9P messages
24 | Dotu bool // If true, 9P2000.u protocol is spoken
25 | Root *Fid // Fid that points to the rood directory
26 | Id string // Used when printing debug messages
27 | Log *Logger
28 |
29 | conn net.Conn
30 | tagpool *pool
31 | fidpool *pool
32 | reqout chan *Req
33 | done chan bool
34 | reqfirst *Req
35 | reqlast *Req
36 | err error
37 |
38 | reqchan chan *Req
39 | tchan chan *Fcall
40 |
41 | next, prev *Clnt
42 | }
43 |
44 | // A Fid type represents a file on the server. Fids are used for the
45 | // low level methods that correspond directly to the 9P2000 message requests
46 | type Fid struct {
47 | sync.Mutex
48 | Clnt *Clnt // Client the fid belongs to
49 | Iounit uint32
50 | Qid // The Qid description for the file
51 | Mode uint8 // Open mode (one of O* values) (if file is open)
52 | Fid uint32 // Fid number
53 | User // The user the fid belongs to
54 | walked bool // true if the fid points to a walked file on the server
55 | }
56 |
57 | // The file is similar to the Fid, but is used in the high-level client
58 | // interface. We expose the Fid so that client code can use Remove
59 | // on a fid, the same way a kernel can.
60 | type File struct {
61 | Fid *Fid
62 | offset uint64
63 | }
64 |
65 | type Req struct {
66 | sync.Mutex
67 | Clnt *Clnt
68 | Tc *Fcall
69 | Rc *Fcall
70 | Err error
71 | Done chan *Req
72 | tag uint16
73 | prev, next *Req
74 | fid *Fid
75 | }
76 |
77 | type ClntList struct {
78 | sync.Mutex
79 | clntList, clntLast *Clnt
80 | }
81 |
82 | var clnts *ClntList
83 | var DefaultDebuglevel int
84 | var DefaultLogger *Logger
85 |
86 | func (clnt *Clnt) Rpcnb(r *Req) error {
87 | var tag uint16
88 |
89 | if r.Tc.Type == Tversion {
90 | tag = NOTAG
91 | } else {
92 | tag = r.tag
93 | }
94 |
95 | SetTag(r.Tc, tag)
96 | clnt.Lock()
97 | if clnt.err != nil {
98 | clnt.Unlock()
99 | return clnt.err
100 | }
101 |
102 | if clnt.reqlast != nil {
103 | clnt.reqlast.next = r
104 | } else {
105 | clnt.reqfirst = r
106 | }
107 |
108 | r.prev = clnt.reqlast
109 | clnt.reqlast = r
110 | clnt.Unlock()
111 |
112 | clnt.reqout <- r
113 | return nil
114 | }
115 |
116 | func (clnt *Clnt) Rpc(tc *Fcall) (rc *Fcall, err error) {
117 | r := clnt.ReqAlloc()
118 | r.Tc = tc
119 | r.Done = make(chan *Req)
120 | err = clnt.Rpcnb(r)
121 | if err != nil {
122 | return
123 | }
124 |
125 | <-r.Done
126 | rc = r.Rc
127 | err = r.Err
128 | clnt.ReqFree(r)
129 | return
130 | }
131 |
132 | func (clnt *Clnt) recv() {
133 | var err error
134 | var buf []byte
135 |
136 | err = nil
137 | pos := 0
138 | for {
139 | // Connect can change the client Msize.
140 | clntmsize := int(atomic.LoadUint32(&clnt.Msize))
141 | if len(buf) < clntmsize {
142 | b := make([]byte, clntmsize*8)
143 | copy(b, buf[0:pos])
144 | buf = b
145 | b = nil
146 | }
147 |
148 | n, oerr := clnt.conn.Read(buf[pos:])
149 | if oerr != nil || n == 0 {
150 | err = &Error{oerr.Error(), EIO}
151 | clnt.Lock()
152 | clnt.err = err
153 | clnt.Unlock()
154 | goto closed
155 | }
156 |
157 | pos += n
158 | for pos > 4 {
159 | sz, _ := Gint32(buf)
160 | if pos < int(sz) {
161 | if len(buf) < int(sz) {
162 | b := make([]byte, atomic.LoadUint32(&clnt.Msize)*8)
163 | copy(b, buf[0:pos])
164 | buf = b
165 | b = nil
166 | }
167 |
168 | break
169 | }
170 |
171 | fc, err, fcsize := Unpack(buf, clnt.Dotu)
172 | clnt.Lock()
173 | if err != nil {
174 | clnt.err = err
175 | clnt.conn.Close()
176 | clnt.Unlock()
177 | goto closed
178 | }
179 |
180 | if clnt.Debuglevel > 0 {
181 | clnt.logFcall(fc)
182 | if clnt.Debuglevel&DbgPrintPackets != 0 {
183 | log.Println("}-}", clnt.Id, fmt.Sprint(fc.Pkt))
184 | }
185 |
186 | if clnt.Debuglevel&DbgPrintFcalls != 0 {
187 | log.Println("}}}", clnt.Id, fc.String())
188 | }
189 | }
190 |
191 | var r *Req = nil
192 | for r = clnt.reqfirst; r != nil; r = r.next {
193 | if r.Tc.Tag == fc.Tag {
194 | break
195 | }
196 | }
197 |
198 | if r == nil {
199 | clnt.err = &Error{"unexpected response", EINVAL}
200 | clnt.conn.Close()
201 | clnt.Unlock()
202 | goto closed
203 | }
204 |
205 | r.Rc = fc
206 | if r.prev != nil {
207 | r.prev.next = r.next
208 | } else {
209 | clnt.reqfirst = r.next
210 | }
211 |
212 | if r.next != nil {
213 | r.next.prev = r.prev
214 | } else {
215 | clnt.reqlast = r.prev
216 | }
217 | clnt.Unlock()
218 |
219 | if r.Tc.Type != r.Rc.Type-1 {
220 | if r.Rc.Type != Rerror {
221 | r.Err = &Error{"invalid response", EINVAL}
222 | log.Println(fmt.Sprintf("TTT %v", r.Tc))
223 | log.Println(fmt.Sprintf("RRR %v", r.Rc))
224 | } else {
225 | if r.Err == nil {
226 | r.Err = &Error{r.Rc.Error, r.Rc.Errornum}
227 | }
228 | }
229 | }
230 |
231 | if r.Done != nil {
232 | r.Done <- r
233 | }
234 |
235 | pos -= fcsize
236 | buf = buf[fcsize:]
237 | }
238 | }
239 |
240 | closed:
241 | clnt.done <- true
242 |
243 | /* send error to all pending requests */
244 | clnt.Lock()
245 | r := clnt.reqfirst
246 | clnt.reqfirst = nil
247 | clnt.reqlast = nil
248 | if err == nil {
249 | err = clnt.err
250 | }
251 | clnt.Unlock()
252 | for ; r != nil; r = r.next {
253 | r.Err = err
254 | if r.Done != nil {
255 | r.Done <- r
256 | }
257 | }
258 |
259 | clnts.Lock()
260 | if clnt.prev != nil {
261 | clnt.prev.next = clnt.next
262 | } else {
263 | clnts.clntList = clnt.next
264 | }
265 |
266 | if clnt.next != nil {
267 | clnt.next.prev = clnt.prev
268 | } else {
269 | clnts.clntLast = clnt.prev
270 | }
271 | clnts.Unlock()
272 |
273 | if sop, ok := (interface{}(clnt)).(StatsOps); ok {
274 | sop.statsUnregister()
275 | }
276 | }
277 |
278 | func (clnt *Clnt) send() {
279 | for {
280 | select {
281 | case <-clnt.done:
282 | return
283 |
284 | case req := <-clnt.reqout:
285 | if clnt.Debuglevel > 0 {
286 | clnt.logFcall(req.Tc)
287 | if clnt.Debuglevel&DbgPrintPackets != 0 {
288 | log.Println("{-{", clnt.Id, fmt.Sprint(req.Tc.Pkt))
289 | }
290 |
291 | if clnt.Debuglevel&DbgPrintFcalls != 0 {
292 | log.Println("{{{", clnt.Id, req.Tc.String())
293 | }
294 | }
295 |
296 | for buf := req.Tc.Pkt; len(buf) > 0; {
297 | n, err := clnt.conn.Write(buf)
298 | if err != nil {
299 | /* just close the socket, will get signal on clnt.done */
300 | clnt.conn.Close()
301 | break
302 | }
303 |
304 | buf = buf[n:]
305 | }
306 | }
307 | }
308 | }
309 |
310 | // Creates and initializes a new Clnt object. Doesn't send any data
311 | // on the wire.
312 | func NewClnt(c net.Conn, msize uint32, dotu bool) *Clnt {
313 | clnt := new(Clnt)
314 | clnt.conn = c
315 | clnt.Msize = msize
316 | clnt.Dotu = dotu
317 | clnt.Debuglevel = DefaultDebuglevel
318 | clnt.Log = DefaultLogger
319 | clnt.Id = c.RemoteAddr().String() + ":"
320 | clnt.tagpool = newPool(uint32(NOTAG))
321 | clnt.fidpool = newPool(NOFID)
322 | clnt.reqout = make(chan *Req)
323 | clnt.done = make(chan bool)
324 | clnt.reqchan = make(chan *Req, 16)
325 | clnt.tchan = make(chan *Fcall, 16)
326 |
327 | go clnt.recv()
328 | go clnt.send()
329 |
330 | clnts.Lock()
331 | if clnts.clntLast != nil {
332 | clnts.clntLast.next = clnt
333 | } else {
334 | clnts.clntList = clnt
335 | }
336 |
337 | clnt.prev = clnts.clntLast
338 | clnts.clntLast = clnt
339 | clnts.Unlock()
340 |
341 | if sop, ok := (interface{}(clnt)).(StatsOps); ok {
342 | sop.statsRegister()
343 | }
344 |
345 | return clnt
346 | }
347 |
348 | // Establishes a new socket connection to the 9P server and creates
349 | // a client object for it. Negotiates the dialect and msize for the
350 | // connection. Returns a Clnt object, or Error.
351 | func Connect(c net.Conn, msize uint32, dotu bool) (*Clnt, error) {
352 | clnt := NewClnt(c, msize, dotu)
353 | ver := "9P2000"
354 | if clnt.Dotu {
355 | ver = "9P2000.u"
356 | }
357 |
358 | clntmsize := atomic.LoadUint32(&clnt.Msize)
359 | tc := NewFcall(clntmsize)
360 | err := PackTversion(tc, clntmsize, ver)
361 | if err != nil {
362 | return nil, err
363 | }
364 |
365 | rc, err := clnt.Rpc(tc)
366 | if err != nil {
367 | return nil, err
368 | }
369 |
370 | if rc.Msize < atomic.LoadUint32(&clnt.Msize) {
371 | atomic.StoreUint32(&clnt.Msize, rc.Msize)
372 | }
373 |
374 | clnt.Dotu = rc.Version == "9P2000.u" && clnt.Dotu
375 | return clnt, nil
376 | }
377 |
378 | // Creates a new Fid object for the client
379 | func (clnt *Clnt) FidAlloc() *Fid {
380 | fid := new(Fid)
381 | fid.Fid = clnt.fidpool.getId()
382 | fid.Clnt = clnt
383 |
384 | return fid
385 | }
386 |
387 | func (clnt *Clnt) NewFcall() *Fcall {
388 | select {
389 | case tc := <-clnt.tchan:
390 | return tc
391 | default:
392 | }
393 | return NewFcall(atomic.LoadUint32(&clnt.Msize))
394 | }
395 |
396 | func (clnt *Clnt) FreeFcall(fc *Fcall) {
397 | if fc != nil && len(fc.Buf) >= int(atomic.LoadUint32(&clnt.Msize)) {
398 | select {
399 | case clnt.tchan <- fc:
400 | break
401 | default:
402 | }
403 | }
404 | }
405 |
406 | func (clnt *Clnt) ReqAlloc() *Req {
407 | var req *Req
408 | select {
409 | case req = <-clnt.reqchan:
410 | break
411 | default:
412 | req = new(Req)
413 | req.Clnt = clnt
414 | req.tag = uint16(clnt.tagpool.getId())
415 | }
416 | return req
417 | }
418 |
419 | func (clnt *Clnt) ReqFree(req *Req) {
420 | clnt.FreeFcall(req.Tc)
421 | req.Tc = nil
422 | req.Rc = nil
423 | req.Err = nil
424 | req.Done = nil
425 | req.next = nil
426 | req.prev = nil
427 |
428 | select {
429 | case clnt.reqchan <- req:
430 | break
431 | default:
432 | clnt.tagpool.putId(uint32(req.tag))
433 | }
434 | }
435 |
436 | func (clnt *Clnt) logFcall(fc *Fcall) {
437 | if clnt.Debuglevel&DbgLogPackets != 0 {
438 | pkt := make([]byte, len(fc.Pkt))
439 | copy(pkt, fc.Pkt)
440 | clnt.Log.Log(pkt, clnt, DbgLogPackets)
441 | }
442 |
443 | if clnt.Debuglevel&DbgLogFcalls != 0 {
444 | f := new(Fcall)
445 | *f = *fc
446 | f.Pkt = nil
447 | clnt.Log.Log(f, clnt, DbgLogFcalls)
448 | }
449 | }
450 |
451 | // FidFile returns a File that represents the given Fid, initially at the given
452 | // offset.
453 | func FidFile(fid *Fid, offset uint64) *File {
454 | return &File{fid, offset}
455 | }
456 |
457 | func init() {
458 | clnts = new(ClntList)
459 | if sop, ok := (interface{}(clnts)).(StatsOps); ok {
460 | sop.statsRegister()
461 | }
462 | }
463 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | // Clunks a fid. Returns nil if successful.
8 | func (clnt *Clnt) Clunk(fid *Fid) (err error) {
9 | err = nil
10 | if fid.walked {
11 | tc := clnt.NewFcall()
12 | err := PackTclunk(tc, fid.Fid)
13 | if err != nil {
14 | return err
15 | }
16 |
17 | _, err = clnt.Rpc(tc)
18 | }
19 |
20 | clnt.fidpool.putId(fid.Fid)
21 | fid.walked = false
22 | fid.Fid = NOFID
23 | return
24 | }
25 |
26 | // Closes a file. Returns nil if successful.
27 | func (file *File) Close() error {
28 | // Should we cancel all pending requests for the File
29 | return file.Fid.Clnt.Clunk(file.Fid)
30 | }
31 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "net"
9 | )
10 |
11 | // Creates an authentication fid for the specified user. Returns the fid, if
12 | // successful, or an Error.
13 | func (clnt *Clnt) Auth(user User, aname string) (*Fid, error) {
14 | fid := clnt.FidAlloc()
15 | tc := clnt.NewFcall()
16 | err := PackTauth(tc, fid.Fid, user.Name(), aname, uint32(user.Id()), clnt.Dotu)
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | _, err = clnt.Rpc(tc)
22 | if err != nil {
23 | return nil, err
24 | }
25 |
26 | fid.User = user
27 | fid.walked = true
28 | return fid, nil
29 | }
30 |
31 | // Creates a fid for the specified user that points to the root
32 | // of the file server's file tree. Returns a Fid pointing to the root,
33 | // if successful, or an Error.
34 | func (clnt *Clnt) Attach(afid *Fid, user User, aname string) (*Fid, error) {
35 | var afno uint32
36 |
37 | if afid != nil {
38 | afno = afid.Fid
39 | } else {
40 | afno = NOFID
41 | }
42 |
43 | fid := clnt.FidAlloc()
44 | tc := clnt.NewFcall()
45 | err := PackTattach(tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), clnt.Dotu)
46 | if err != nil {
47 | return nil, err
48 | }
49 |
50 | rc, err := clnt.Rpc(tc)
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | fid.Qid = rc.Qid
56 | fid.User = user
57 | fid.walked = true
58 | return fid, nil
59 | }
60 |
61 | // Connects to a file server and attaches to it as the specified user.
62 | func Mount(ntype, addr, aname string, msize uint32, user User) (*Clnt, error) {
63 | c, e := net.Dial(ntype, addr)
64 | if e != nil {
65 | return nil, &Error{e.Error(), EIO}
66 | }
67 |
68 | return MountConn(c, aname, msize, user)
69 | }
70 |
71 | func MountConn(c net.Conn, aname string, msize uint32, user User) (*Clnt, error) {
72 | clnt, err := Connect(c, msize+IOHDRSZ, true)
73 | if err != nil {
74 | return nil, err
75 | }
76 |
77 | fid, err := clnt.Attach(nil, user, aname)
78 | if err != nil {
79 | clnt.Unmount()
80 | return nil, err
81 | }
82 |
83 | clnt.Root = fid
84 | return clnt, nil
85 | }
86 |
87 | // Closes the connection to the file sever.
88 | func (clnt *Clnt) Unmount() {
89 | clnt.Lock()
90 | clnt.err = &Error{"connection closed", EIO}
91 | clnt.conn.Close()
92 | clnt.Unlock()
93 | }
94 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "strings"
9 | )
10 |
11 | // Opens the file associated with the fid. Returns nil if
12 | // the operation is successful.
13 | func (clnt *Clnt) Open(fid *Fid, mode uint8) error {
14 | tc := clnt.NewFcall()
15 | err := PackTopen(tc, fid.Fid, mode)
16 | if err != nil {
17 | return err
18 | }
19 |
20 | rc, err := clnt.Rpc(tc)
21 | if err != nil {
22 | return err
23 | }
24 |
25 | fid.Qid = rc.Qid
26 | fid.Iounit = rc.Iounit
27 | if fid.Iounit == 0 || fid.Iounit > clnt.Msize-IOHDRSZ {
28 | fid.Iounit = clnt.Msize - IOHDRSZ
29 | }
30 | fid.Mode = mode
31 | return nil
32 | }
33 |
34 | // Creates a file in the directory associated with the fid. Returns nil
35 | // if the operation is successful.
36 | func (clnt *Clnt) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error {
37 | tc := clnt.NewFcall()
38 | err := PackTcreate(tc, fid.Fid, name, perm, mode, ext, clnt.Dotu)
39 | if err != nil {
40 | return err
41 | }
42 |
43 | rc, err := clnt.Rpc(tc)
44 | if err != nil {
45 | return err
46 | }
47 |
48 | fid.Qid = rc.Qid
49 | fid.Iounit = rc.Iounit
50 | if fid.Iounit == 0 || fid.Iounit > clnt.Msize-IOHDRSZ {
51 | fid.Iounit = clnt.Msize - IOHDRSZ
52 | }
53 | fid.Mode = mode
54 | return nil
55 | }
56 |
57 | // Creates and opens a named file.
58 | // Returns the file if the operation is successful, or an Error.
59 | func (clnt *Clnt) FCreate(path string, perm uint32, mode uint8) (*File, error) {
60 | n := strings.LastIndex(path, "/")
61 | if n < 0 {
62 | n = 0
63 | }
64 |
65 | fid, err := clnt.FWalk(path[0:n])
66 | if err != nil {
67 | return nil, err
68 | }
69 |
70 | if path[n] == '/' {
71 | n++
72 | }
73 |
74 | err = clnt.Create(fid, path[n:], perm, mode, "")
75 | if err != nil {
76 | clnt.Clunk(fid)
77 | return nil, err
78 | }
79 |
80 | return &File{fid, 0}, nil
81 | }
82 |
83 | // Opens a named file. Returns the opened file, or an Error.
84 | func (clnt *Clnt) FOpen(path string, mode uint8) (*File, error) {
85 | fid, err := clnt.FWalk(path)
86 | if err != nil {
87 | return nil, err
88 | }
89 |
90 | err = clnt.Open(fid, mode)
91 | if err != nil {
92 | clnt.Clunk(fid)
93 | return nil, err
94 | }
95 |
96 | return &File{fid, 0}, nil
97 | }
98 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import "sync"
8 |
9 | var m2id = [...]uint8{
10 | 0, 1, 0, 2, 0, 1, 0, 3,
11 | 0, 1, 0, 2, 0, 1, 0, 4,
12 | 0, 1, 0, 2, 0, 1, 0, 3,
13 | 0, 1, 0, 2, 0, 1, 0, 5,
14 | 0, 1, 0, 2, 0, 1, 0, 3,
15 | 0, 1, 0, 2, 0, 1, 0, 4,
16 | 0, 1, 0, 2, 0, 1, 0, 3,
17 | 0, 1, 0, 2, 0, 1, 0, 6,
18 | 0, 1, 0, 2, 0, 1, 0, 3,
19 | 0, 1, 0, 2, 0, 1, 0, 4,
20 | 0, 1, 0, 2, 0, 1, 0, 3,
21 | 0, 1, 0, 2, 0, 1, 0, 5,
22 | 0, 1, 0, 2, 0, 1, 0, 3,
23 | 0, 1, 0, 2, 0, 1, 0, 4,
24 | 0, 1, 0, 2, 0, 1, 0, 3,
25 | 0, 1, 0, 2, 0, 1, 0, 7,
26 | 0, 1, 0, 2, 0, 1, 0, 3,
27 | 0, 1, 0, 2, 0, 1, 0, 4,
28 | 0, 1, 0, 2, 0, 1, 0, 3,
29 | 0, 1, 0, 2, 0, 1, 0, 5,
30 | 0, 1, 0, 2, 0, 1, 0, 3,
31 | 0, 1, 0, 2, 0, 1, 0, 4,
32 | 0, 1, 0, 2, 0, 1, 0, 3,
33 | 0, 1, 0, 2, 0, 1, 0, 6,
34 | 0, 1, 0, 2, 0, 1, 0, 3,
35 | 0, 1, 0, 2, 0, 1, 0, 4,
36 | 0, 1, 0, 2, 0, 1, 0, 3,
37 | 0, 1, 0, 2, 0, 1, 0, 5,
38 | 0, 1, 0, 2, 0, 1, 0, 3,
39 | 0, 1, 0, 2, 0, 1, 0, 4,
40 | 0, 1, 0, 2, 0, 1, 0, 3,
41 | 0, 1, 0, 2, 0, 1, 0, 0,
42 | }
43 |
44 | type pool struct {
45 | sync.Mutex
46 | need int
47 | nchan chan uint32
48 | maxid uint32
49 | imap []byte
50 | }
51 |
52 | func newPool(maxid uint32) *pool {
53 | p := new(pool)
54 | p.maxid = maxid
55 | p.nchan = make(chan uint32)
56 |
57 | return p
58 | }
59 |
60 | func (p *pool) getId() uint32 {
61 | var n uint32 = 0
62 | var ret uint32
63 |
64 | p.Lock()
65 | for n = 0; n < uint32(len(p.imap)); n++ {
66 | if p.imap[n] != 0xFF {
67 | break
68 | }
69 | }
70 |
71 | if int(n) >= len(p.imap) {
72 | m := uint32(len(p.imap) + 32)
73 | if uint32(m*8) > p.maxid {
74 | m = p.maxid/8 + 1
75 | }
76 |
77 | b := make([]byte, m)
78 | copy(b, p.imap)
79 | p.imap = b
80 | }
81 |
82 | if n >= uint32(len(p.imap)) {
83 | p.need++
84 | p.Unlock()
85 | ret = <-p.nchan
86 | } else {
87 | ret = uint32(m2id[p.imap[n]])
88 | p.imap[n] |= 1 << ret
89 | ret += n * 8
90 | p.Unlock()
91 | }
92 |
93 | return ret
94 | }
95 |
96 | func (p *pool) putId(id uint32) {
97 | p.Lock()
98 | if p.need > 0 {
99 | p.nchan <- id
100 | p.need--
101 | p.Unlock()
102 | return
103 | }
104 |
105 | p.imap[id/8] &= ^(1 << (id % 8))
106 | p.Unlock()
107 | }
108 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "io"
9 | )
10 |
11 | // Reads count bytes starting from offset from the file associated with the fid.
12 | // Returns a slice with the data read, if the operation was successful, or an
13 | // Error.
14 | func (clnt *Clnt) Read(fid *Fid, offset uint64, count uint32) ([]byte, error) {
15 | if count > fid.Iounit {
16 | count = fid.Iounit
17 | }
18 |
19 | tc := clnt.NewFcall()
20 | err := PackTread(tc, fid.Fid, offset, count)
21 | if err != nil {
22 | return nil, err
23 | }
24 |
25 | rc, err := clnt.Rpc(tc)
26 | if err != nil {
27 | return nil, err
28 | }
29 |
30 | return rc.Data, nil
31 | }
32 |
33 | // Reads up to len(buf) bytes from the File. Returns the number
34 | // of bytes read, or an Error.
35 | func (file *File) Read(buf []byte) (int, error) {
36 | n, err := file.ReadAt(buf, int64(file.offset))
37 | if err == nil {
38 | file.offset += uint64(n)
39 | }
40 |
41 | return n, err
42 | }
43 |
44 | // Reads up to len(buf) bytes from the file starting from offset.
45 | // Returns the number of bytes read, or an Error.
46 | func (file *File) ReadAt(buf []byte, offset int64) (int, error) {
47 | b, err := file.Fid.Clnt.Read(file.Fid, uint64(offset), uint32(len(buf)))
48 | if err != nil {
49 | return 0, err
50 | }
51 |
52 | if len(b) == 0 {
53 | return 0, io.EOF
54 | }
55 |
56 | copy(buf, b)
57 | return len(b), nil
58 | }
59 |
60 | // Reads exactly len(buf) bytes from the File starting from offset.
61 | // Returns the number of bytes read (could be less than len(buf) if
62 | // end-of-file is reached), or an Error.
63 | func (file *File) Readn(buf []byte, offset uint64) (int, error) {
64 | ret := 0
65 | for len(buf) > 0 {
66 | n, err := file.ReadAt(buf, int64(offset))
67 | if err != nil {
68 | return 0, err
69 | }
70 |
71 | if n == 0 {
72 | break
73 | }
74 |
75 | buf = buf[n:]
76 | offset += uint64(n)
77 | ret += n
78 | }
79 |
80 | return ret, nil
81 | }
82 |
83 | // Reads the content of the directory associated with the File.
84 | // Returns an array of maximum num entries (if num is 0, returns
85 | // all entries from the directory). If the operation fails, returns
86 | // an Error.
87 | func (file *File) Readdir(num int) ([]*Dir, error) {
88 | buf := make([]byte, file.Fid.Clnt.Msize-IOHDRSZ)
89 | dirs := make([]*Dir, 32)
90 | pos := 0
91 | offset := file.offset
92 | defer func() {
93 | file.offset = offset
94 | }()
95 | for {
96 | n, err := file.Read(buf)
97 | if err != nil && err != io.EOF {
98 | return nil, err
99 | }
100 |
101 | if n == 0 {
102 | break
103 | }
104 |
105 | for b := buf[0:n]; len(b) > 0; {
106 | d, _, _, perr := 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 | b = b[d.Size+2:]
117 | offset += uint64(d.Size + 2)
118 | if pos >= len(dirs) {
119 | s := make([]*Dir, len(dirs)+32)
120 | copy(s, dirs)
121 | dirs = s
122 | }
123 |
124 | dirs[pos] = d
125 | pos++
126 | if num != 0 && pos >= num {
127 | break
128 | }
129 | }
130 | }
131 |
132 | return dirs[0:pos], nil
133 | }
134 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | // Removes the file associated with the Fid. Returns nil if the
8 | // operation is successful.
9 | func (clnt *Clnt) Remove(fid *Fid) error {
10 | tc := clnt.NewFcall()
11 | err := PackTremove(tc, fid.Fid)
12 | if err != nil {
13 | return err
14 | }
15 |
16 | _, err = clnt.Rpc(tc)
17 | clnt.fidpool.putId(fid.Fid)
18 | fid.Fid = NOFID
19 |
20 | return err
21 | }
22 |
23 | // Removes the named file. Returns nil if the operation is successful.
24 | func (clnt *Clnt) FRemove(path string) error {
25 | var err error
26 | fid, err := clnt.FWalk(path)
27 | if err != nil {
28 | return err
29 | }
30 |
31 | err = clnt.Remove(fid)
32 | return err
33 | }
34 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | // Returns the metadata for the file associated with the Fid, or an Error.
8 | func (clnt *Clnt) Stat(fid *Fid) (*Dir, error) {
9 | tc := clnt.NewFcall()
10 | err := PackTstat(tc, fid.Fid)
11 | if err != nil {
12 | return nil, err
13 | }
14 |
15 | rc, err := clnt.Rpc(tc)
16 | if err != nil {
17 | return nil, err
18 | }
19 |
20 | return &rc.Dir, nil
21 | }
22 |
23 | // Returns the metadata for a named file, or an Error.
24 | func (clnt *Clnt) FStat(path string) (*Dir, error) {
25 | fid, err := clnt.FWalk(path)
26 | if err != nil {
27 | return nil, err
28 | }
29 |
30 | d, err := clnt.Stat(fid)
31 | clnt.Clunk(fid)
32 | return d, err
33 | }
34 |
35 | // Modifies the data of the file associated with the Fid, or an Error.
36 | func (clnt *Clnt) Wstat(fid *Fid, dir *Dir) error {
37 | tc := clnt.NewFcall()
38 | err := PackTwstat(tc, fid.Fid, dir, clnt.Dotu)
39 | if err != nil {
40 | return err
41 | }
42 |
43 | _, err = clnt.Rpc(tc)
44 | return err
45 | }
46 |
--------------------------------------------------------------------------------
/clnt_stats_http.go:
--------------------------------------------------------------------------------
1 | // +build httpstats
2 |
3 | package go9p
4 |
5 | import (
6 | "fmt"
7 | "io"
8 | "net/http"
9 | )
10 |
11 | func (clnt *Clnt) ServeHTTP(c http.ResponseWriter, r *http.Request) {
12 | io.WriteString(c, fmt.Sprintf("
Client %s
", clnt.Id))
13 | defer io.WriteString(c, "")
14 |
15 | // fcalls
16 | if clnt.Debuglevel&DbgLogFcalls != 0 {
17 | fs := clnt.Log.Filter(clnt, DbgLogFcalls)
18 | io.WriteString(c, fmt.Sprintf("Last %d 9P messages
", len(fs)))
19 | for _, l := range fs {
20 | fc := l.Data.(*Fcall)
21 | if fc.Type != 0 {
22 | io.WriteString(c, fmt.Sprintf("
%s", fc))
23 | }
24 | }
25 | }
26 | }
27 |
28 | func clntServeHTTP(c http.ResponseWriter, r *http.Request) {
29 | io.WriteString(c, fmt.Sprintf(""))
30 | defer io.WriteString(c, "")
31 |
32 | clnts.Lock()
33 | if clnts.clntList == nil {
34 | io.WriteString(c, "no clients")
35 | }
36 |
37 | for clnt := clnts.clntList; clnt != nil; clnt = clnt.next {
38 | io.WriteString(c, fmt.Sprintf("%s
", clnt.Id, clnt.Id))
39 | }
40 | clnts.Unlock()
41 | }
42 |
43 | func (clnt *Clnt) statsRegister() {
44 | http.Handle("/go9p/clnt/"+clnt.Id, clnt)
45 | }
46 |
47 | func (clnt *Clnt) statsUnregister() {
48 | http.Handle("/go9p/clnt/"+clnt.Id, nil)
49 | }
50 |
51 | func (c *ClntList) statsRegister() {
52 | http.HandleFunc("/go9p/clnt", clntServeHTTP)
53 | }
54 |
55 | func (c *ClntList) statsUnregister() {
56 | http.HandleFunc("/go9p/clnt", nil)
57 | }
58 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | type Tag struct {
8 | clnt *Clnt
9 | tag uint16
10 | reqchan chan *Req
11 | respchan chan *Req
12 | donechan chan bool
13 | }
14 |
15 | func (clnt *Clnt) TagAlloc(reqchan chan *Req) *Tag {
16 | tag := new(Tag)
17 | tag.clnt = clnt
18 | tag.tag = uint16(clnt.tagpool.getId())
19 | tag.reqchan = reqchan
20 | tag.respchan = make(chan *Req, 16)
21 | tag.donechan = make(chan bool)
22 | go tag.reqproc()
23 |
24 | return tag
25 | }
26 |
27 | func (clnt *Clnt) TagFree(tag *Tag) {
28 | tag.donechan <- true
29 | clnt.tagpool.putId(uint32(tag.tag))
30 | }
31 |
32 | func (tag *Tag) reqAlloc() *Req {
33 | r := new(Req)
34 | r.tag = tag.tag
35 | r.Clnt = tag.clnt
36 | r.Done = tag.respchan
37 | r.Tc = tag.clnt.NewFcall()
38 |
39 | return r
40 | }
41 |
42 | func (tag *Tag) ReqFree(r *Req) {
43 | tag.clnt.FreeFcall(r.Tc)
44 | }
45 |
46 | func (tag *Tag) reqproc() {
47 | for {
48 | select {
49 | case <-tag.donechan:
50 | return
51 |
52 | case r := <-tag.respchan:
53 | rc := r.Rc
54 | fid := r.fid
55 | err := r.Rc.Type == Rerror
56 |
57 | switch r.Tc.Type {
58 | case Tauth:
59 | if err {
60 | fid.User = nil
61 | }
62 |
63 | case Tattach:
64 | if !err {
65 | fid.Qid = rc.Qid
66 | } else {
67 | fid.User = nil
68 | }
69 |
70 | case Twalk:
71 | if !err {
72 | fid.walked = true
73 | if len(rc.Wqid) > 0 {
74 | fid.Qid = rc.Wqid[len(rc.Wqid)-1]
75 | }
76 | } else {
77 | fid.User = nil
78 | }
79 |
80 | case Topen:
81 | case Tcreate:
82 | if !err {
83 | fid.Iounit = rc.Iounit
84 | fid.Qid = rc.Qid
85 | } else {
86 | fid.Mode = 0
87 | }
88 |
89 | case Tclunk:
90 | case Tremove:
91 | tag.clnt.fidpool.putId(fid.Fid)
92 | }
93 |
94 | tag.reqchan <- r
95 | }
96 | }
97 | }
98 |
99 | func (tag *Tag) Auth(afid *Fid, user User, aname string) error {
100 | req := tag.reqAlloc()
101 | req.fid = afid
102 | err := PackTauth(req.Tc, afid.Fid, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu)
103 | if err != nil {
104 | return err
105 | }
106 |
107 | afid.User = user
108 | return tag.clnt.Rpcnb(req)
109 | }
110 |
111 | func (tag *Tag) Attach(fid, afid *Fid, user User, aname string) error {
112 | var afno uint32
113 |
114 | if afid != nil {
115 | afno = afid.Fid
116 | } else {
117 | afno = NOFID
118 | }
119 |
120 | req := tag.reqAlloc()
121 | req.fid = fid
122 | err := PackTattach(req.Tc, fid.Fid, afno, user.Name(), aname, uint32(user.Id()), tag.clnt.Dotu)
123 | if err != nil {
124 | return err
125 | }
126 |
127 | fid.User = user
128 | return tag.clnt.Rpcnb(req)
129 | }
130 |
131 | func (tag *Tag) Walk(fid *Fid, newfid *Fid, wnames []string) error {
132 | req := tag.reqAlloc()
133 | req.fid = newfid
134 | if len(wnames) == 0 {
135 | newfid.Qid = fid.Qid
136 | }
137 |
138 | err := PackTwalk(req.Tc, fid.Fid, newfid.Fid, wnames)
139 | if err != nil {
140 | return err
141 | }
142 |
143 | newfid.User = fid.User
144 | return tag.clnt.Rpcnb(req)
145 | }
146 |
147 | func (tag *Tag) Open(fid *Fid, mode uint8) error {
148 | req := tag.reqAlloc()
149 | req.fid = fid
150 | err := PackTopen(req.Tc, fid.Fid, mode)
151 | if err != nil {
152 | return err
153 | }
154 |
155 | fid.Mode = mode
156 | return tag.clnt.Rpcnb(req)
157 | }
158 |
159 | func (tag *Tag) Create(fid *Fid, name string, perm uint32, mode uint8, ext string) error {
160 | req := tag.reqAlloc()
161 | req.fid = fid
162 | err := PackTcreate(req.Tc, fid.Fid, name, perm, mode, ext, tag.clnt.Dotu)
163 | if err != nil {
164 | return err
165 | }
166 |
167 | fid.Mode = mode
168 | return tag.clnt.Rpcnb(req)
169 | }
170 |
171 | func (tag *Tag) Read(fid *Fid, offset uint64, count uint32) error {
172 | req := tag.reqAlloc()
173 | req.fid = fid
174 | err := PackTread(req.Tc, fid.Fid, offset, count)
175 | if err != nil {
176 | return err
177 | }
178 |
179 | return tag.clnt.Rpcnb(req)
180 | }
181 |
182 | func (tag *Tag) Write(fid *Fid, data []byte, offset uint64) error {
183 | req := tag.reqAlloc()
184 | req.fid = fid
185 | err := PackTwrite(req.Tc, fid.Fid, offset, uint32(len(data)), data)
186 | if err != nil {
187 | return err
188 | }
189 |
190 | return tag.clnt.Rpcnb(req)
191 | }
192 |
193 | func (tag *Tag) Clunk(fid *Fid) error {
194 | req := tag.reqAlloc()
195 | req.fid = fid
196 | err := PackTclunk(req.Tc, fid.Fid)
197 | if err != nil {
198 | return err
199 | }
200 |
201 | return tag.clnt.Rpcnb(req)
202 | }
203 |
204 | func (tag *Tag) Remove(fid *Fid) error {
205 | req := tag.reqAlloc()
206 | req.fid = fid
207 | err := PackTremove(req.Tc, fid.Fid)
208 | if err != nil {
209 | return err
210 | }
211 |
212 | return tag.clnt.Rpcnb(req)
213 | }
214 |
215 | func (tag *Tag) Stat(fid *Fid) error {
216 | req := tag.reqAlloc()
217 | req.fid = fid
218 | err := PackTstat(req.Tc, fid.Fid)
219 | if err != nil {
220 | return err
221 | }
222 |
223 | return tag.clnt.Rpcnb(req)
224 | }
225 |
226 | func (tag *Tag) Wstat(fid *Fid, dir *Dir) error {
227 | req := tag.reqAlloc()
228 | req.fid = fid
229 | err := PackTwstat(req.Tc, fid.Fid, dir, tag.clnt.Dotu)
230 | if err != nil {
231 | return err
232 | }
233 |
234 | return tag.clnt.Rpcnb(req)
235 | }
236 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "strings"
9 | )
10 |
11 | // Starting from the file associated with fid, walks all wnames in
12 | // sequence and associates the resulting file with newfid. If no wnames
13 | // were walked successfully, an Error is returned. Otherwise a slice with a
14 | // Qid for each walked name is returned.
15 | func (clnt *Clnt) Walk(fid *Fid, newfid *Fid, wnames []string) ([]Qid, error) {
16 | tc := clnt.NewFcall()
17 | err := PackTwalk(tc, fid.Fid, newfid.Fid, wnames)
18 | if err != nil {
19 | return nil, err
20 | }
21 |
22 | rc, err := clnt.Rpc(tc)
23 | if err != nil {
24 | return nil, err
25 | }
26 |
27 | newfid.walked = true
28 | return rc.Wqid, nil
29 | }
30 |
31 | // Walks to a named file. Returns a Fid associated with the file,
32 | // or an Error.
33 | func (clnt *Clnt) FWalk(path string) (*Fid, error) {
34 | var err error = nil
35 |
36 | var i, m int
37 | for i = 0; i < len(path); i++ {
38 | if path[i] != '/' {
39 | break
40 | }
41 | }
42 |
43 | if i > 0 {
44 | path = path[i:]
45 | }
46 |
47 | wnames := strings.Split(path, "/")
48 | newfid := clnt.FidAlloc()
49 | fid := clnt.Root
50 | newfid.User = fid.User
51 |
52 | /* get rid of the empty names */
53 | for i, m = 0, 0; i < len(wnames); i++ {
54 | if wnames[i] != "" {
55 | wnames[m] = wnames[i]
56 | m++
57 | }
58 | }
59 |
60 | wnames = wnames[0:m]
61 | for {
62 | n := len(wnames)
63 | if n > 16 {
64 | n = 16
65 | }
66 |
67 | tc := clnt.NewFcall()
68 | err = PackTwalk(tc, fid.Fid, newfid.Fid, wnames[0:n])
69 | if err != nil {
70 | goto error
71 | }
72 |
73 | var rc *Fcall
74 | rc, err = clnt.Rpc(tc)
75 | if err != nil {
76 | goto error
77 | }
78 |
79 | newfid.walked = true
80 | if len(rc.Wqid) != n {
81 | err = &Error{"file not found", ENOENT}
82 | goto error
83 | }
84 |
85 | if len(rc.Wqid) > 0 {
86 | newfid.Qid = rc.Wqid[len(rc.Wqid)-1]
87 | } else {
88 | newfid.Qid = fid.Qid
89 | }
90 |
91 | wnames = wnames[n:]
92 | fid = newfid
93 | if len(wnames) == 0 {
94 | break
95 | }
96 | }
97 |
98 | return newfid, nil
99 |
100 | error:
101 | clnt.Clunk(newfid)
102 | return nil, err
103 | }
104 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | // Write up to len(data) bytes starting from offset. Returns the
8 | // number of bytes written, or an Error.
9 | func (clnt *Clnt) Write(fid *Fid, data []byte, offset uint64) (int, error) {
10 | if uint32(len(data)) > fid.Iounit {
11 | data = data[0:fid.Iounit]
12 | }
13 |
14 | tc := clnt.NewFcall()
15 | err := PackTwrite(tc, fid.Fid, offset, uint32(len(data)), data)
16 | if err != nil {
17 | return 0, err
18 | }
19 |
20 | rc, err := clnt.Rpc(tc)
21 | if err != nil {
22 | return 0, err
23 | }
24 |
25 | return int(rc.Count), nil
26 | }
27 |
28 | // Writes up to len(buf) bytes to a file. Returns the number of
29 | // bytes written, or an Error.
30 | func (file *File) Write(buf []byte) (int, error) {
31 | n, err := file.WriteAt(buf, int64(file.offset))
32 | if err == nil {
33 | file.offset += uint64(n)
34 | }
35 |
36 | return n, err
37 | }
38 |
39 | // Writes up to len(buf) bytes starting from offset. Returns the number
40 | // of bytes written, or an Error.
41 | func (file *File) WriteAt(buf []byte, offset int64) (int, error) {
42 | return file.Fid.Clnt.Write(file.Fid, buf, uint64(offset))
43 | }
44 |
45 | // Writes exactly len(buf) bytes starting from offset. Returns the number of
46 | // bytes written. If Error is returned the number of bytes can be less
47 | // than len(buf).
48 | func (file *File) Writen(buf []byte, offset uint64) (int, error) {
49 | ret := 0
50 | for len(buf) > 0 {
51 | n, err := file.WriteAt(buf, int64(offset))
52 | if err != nil {
53 | return ret, err
54 | }
55 |
56 | if n == 0 {
57 | break
58 | }
59 |
60 | buf = buf[n:]
61 | offset += uint64(n)
62 | ret += n
63 | }
64 |
65 | return ret, nil
66 | }
67 |
--------------------------------------------------------------------------------
/debug.go:
--------------------------------------------------------------------------------
1 | package go9p
2 |
3 | // Debug flags
4 | const (
5 | DbgPrintFcalls = (1 << iota) // print all 9P messages on stderr
6 | DbgPrintPackets // print the raw packets on stderr
7 | DbgLogFcalls // keep the last N 9P messages (can be accessed over http)
8 | DbgLogPackets // keep the last N 9P messages (can be accessed over http)
9 | )
10 |
--------------------------------------------------------------------------------
/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 go9p
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 |
--------------------------------------------------------------------------------
/go9p_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 go9p
6 |
7 | import (
8 | "flag"
9 | "fmt"
10 | "io/ioutil"
11 | "net"
12 | "os"
13 | "path"
14 | "syscall"
15 | "testing"
16 | )
17 |
18 | const numDir = 16384
19 |
20 | var addr = flag.String("addr", ":5640", "network address")
21 | var pipefsaddr = flag.String("pipefsaddr", ":5641", "pipefs network address")
22 | var debug = flag.Int("debug", 0, "print debug messages")
23 | var root = flag.String("root", "/", "root filesystem")
24 |
25 | // Two files, dotu was true.
26 | var testunpackbytes = []byte{
27 | 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,
28 | }
29 |
30 | func TestUnpackDir(t *testing.T) {
31 | b := testunpackbytes
32 | for len(b) > 0 {
33 | var err error
34 | if _, b, _, err = UnpackDir(b, true); err != nil {
35 | t.Fatalf("Unpackdir: %v", err)
36 | }
37 | }
38 | }
39 |
40 | func TestAttachOpenReaddir(t *testing.T) {
41 | var err error
42 | flag.Parse()
43 | ufs := new(Ufs)
44 | ufs.Dotu = false
45 | ufs.Id = "ufs"
46 | ufs.Root = *root
47 | ufs.Debuglevel = *debug
48 | ufs.Start(ufs)
49 |
50 | t.Log("ufs starting\n")
51 | // determined by build tags
52 | //extraFuncs()
53 | go func() {
54 | if err = ufs.StartNetListener("tcp", *addr); err != nil {
55 | t.Fatalf("Can not start listener: %v", err)
56 | }
57 | }()
58 | /* this may take a few tries ... */
59 | var conn net.Conn
60 | for i := 0; i < 16; i++ {
61 | if conn, err = net.Dial("tcp", *addr); err != nil {
62 | t.Logf("Try go connect, %d'th try, %v", i, err)
63 | } else {
64 | t.Logf("Got a conn, %v\n", conn)
65 | break
66 | }
67 | }
68 | if err != nil {
69 | t.Fatalf("Connect failed after many tries ...")
70 | }
71 |
72 | root := OsUsers.Uid2User(0)
73 |
74 | dir, err := ioutil.TempDir("", "go9p")
75 | if err != nil {
76 | t.Fatalf("got %v, want nil", err)
77 | }
78 | defer os.RemoveAll(dir)
79 |
80 | // Now create a whole bunch of files to test readdir
81 | for i := 0; i < numDir; i++ {
82 | f := fmt.Sprintf(path.Join(dir, fmt.Sprintf("%d", i)))
83 | if err := ioutil.WriteFile(f, []byte(f), 0600); err != nil {
84 | t.Fatalf("Create %v: got %v, want nil", f, err)
85 | }
86 | }
87 |
88 | var clnt *Clnt
89 | for i := 0; i < 16; i++ {
90 | clnt, err = Mount("tcp", *addr, dir, 8192, root)
91 | }
92 | if err != nil {
93 | t.Fatalf("Connect failed: %v\n", err)
94 | }
95 |
96 | defer clnt.Unmount()
97 | t.Logf("attached, rootfid %v\n", clnt.Root)
98 |
99 | dirfid := clnt.FidAlloc()
100 | if _, err = clnt.Walk(clnt.Root, dirfid, []string{"."}); err != nil {
101 | t.Fatalf("%v", err)
102 | }
103 | if err = clnt.Open(dirfid, 0); err != nil {
104 | t.Fatalf("%v", err)
105 | }
106 | var b []byte
107 | var i, amt int
108 | var offset uint64
109 | for i < numDir {
110 | if b, err = clnt.Read(dirfid, offset, 64*1024); err != nil {
111 | t.Fatalf("%v", err)
112 | }
113 | for b != nil && len(b) > 0 {
114 | if _, b, amt, err = UnpackDir(b, ufs.Dotu); err != nil {
115 | break
116 | } else {
117 | i++
118 | offset += uint64(amt)
119 | }
120 | }
121 | }
122 | if i != numDir {
123 | t.Fatalf("Reading %v: got %d entries, wanted %d", dir, i, numDir)
124 | }
125 |
126 | // Alternate form, using readdir and File
127 | var dirfile *File
128 | if dirfile, err = clnt.FOpen(".", OREAD); err != nil {
129 | t.Fatalf("%v", err)
130 | }
131 | i, amt, offset = 0, 0, 0
132 | for i < numDir {
133 | if d, err := dirfile.Readdir(numDir); err != nil {
134 | t.Fatalf("%v", err)
135 | } else {
136 | i += len(d)
137 | }
138 | }
139 | if i != numDir {
140 | t.Fatalf("Readdir %v: got %d entries, wanted %d", dir, i, numDir)
141 | }
142 |
143 | // now test partial reads.
144 | // Read 128 bytes at a time. Remember the last successful offset.
145 | // if UnpackDir fails, read again from that offset
146 | t.Logf("NOW TRY PARTIAL")
147 | i, amt, offset = 0, 0, 0
148 | for {
149 | var b []byte
150 | var d *Dir
151 | if b, err = clnt.Read(dirfid, offset, 128); err != nil {
152 | t.Fatalf("%v", err)
153 | }
154 | if len(b) == 0 {
155 | break
156 | }
157 | t.Logf("b %v\n", b)
158 | for b != nil && len(b) > 0 {
159 | t.Logf("len(b) %v\n", len(b))
160 | if d, b, amt, err = UnpackDir(b, ufs.Dotu); err != nil {
161 | // this error is expected ...
162 | t.Logf("unpack failed (it's ok!). retry at offset %v\n",
163 | offset)
164 | break
165 | } else {
166 | t.Logf("d %v\n", d)
167 | offset += uint64(amt)
168 | }
169 | }
170 | }
171 |
172 | t.Logf("NOW TRY WAY TOO SMALL")
173 | i, amt, offset = 0, 0, 0
174 | for {
175 | var b []byte
176 | if b, err = clnt.Read(dirfid, offset, 32); err != nil {
177 | t.Logf("dirread fails as expected: %v\n", err)
178 | break
179 | }
180 | if offset == 0 && len(b) == 0 {
181 | t.Fatalf("too short dirread returns 0 (no error)")
182 | }
183 | if len(b) == 0 {
184 | break
185 | }
186 | // todo: add entry accumulation and validation here..
187 | offset += uint64(len(b))
188 | }
189 | }
190 |
191 | var f *File
192 | var b = make([]byte, 1048576/8)
193 |
194 | // Not sure we want this, and the test has issues. Revive it if we ever find a use for it.
195 | func TestPipefs(t *testing.T) {
196 | pipefs := new(Pipefs)
197 | pipefs.Dotu = false
198 | pipefs.Msize = 1048576
199 | pipefs.Id = "pipefs"
200 | pipefs.Root = *root
201 | pipefs.Debuglevel = *debug
202 | pipefs.Start(pipefs)
203 |
204 | t.Logf("pipefs starting\n")
205 | d, err := ioutil.TempDir("", "TestPipeFS")
206 | if err != nil {
207 | t.Fatalf("%v", err)
208 | }
209 | defer func() {
210 | if err := os.Remove(d); err != nil {
211 | t.Fatalf("%v", err)
212 | }
213 | }()
214 | fn := path.Join(d, "fifo")
215 | if err := syscall.Mkfifo(fn, 0600); err != nil {
216 | t.Fatalf("%v", err)
217 | }
218 | defer func() {
219 | if err := os.Remove(fn); err != nil {
220 | t.Fatalf("%v", err)
221 | }
222 | }()
223 | // determined by build tags
224 | //extraFuncs()
225 | go func() {
226 | err := pipefs.StartNetListener("tcp", *pipefsaddr)
227 | if err != nil {
228 | t.Fatalf("StartNetListener failed: %v\n", err)
229 | }
230 | }()
231 | root := OsUsers.Uid2User(0)
232 |
233 | var c *Clnt
234 | for i := 0; i < 16; i++ {
235 | c, err = Mount("tcp", *pipefsaddr, "/", uint32(len(b)), root)
236 | }
237 | if err != nil {
238 | t.Fatalf("Connect failed: %v\n", err)
239 | }
240 | t.Logf("Connected to %v\n", *c)
241 | if f, err = c.FOpen(fn, ORDWR); err != nil {
242 | t.Fatalf("Open failed: %v\n", err)
243 | } else {
244 | for i := 0; i < 1048576/8; i++ {
245 | b[i] = byte(i)
246 | }
247 | t.Logf("f %v \n", f)
248 | if n, err := f.Write(b); err != nil {
249 | t.Fatalf("write failed: %v\n", err)
250 | } else {
251 | t.Logf("Wrote %v bytes\n", n)
252 | }
253 | if n, err := f.Read(b); err != nil {
254 | t.Fatalf("read failed: %v\n", err)
255 | } else {
256 | t.Logf("read %v bytes\n", n)
257 | }
258 |
259 | }
260 | }
261 |
262 | func BenchmarkPipeFS(bb *testing.B) {
263 | for i := 0; i < bb.N; i++ {
264 | if _, err := f.Write(b); err != nil {
265 | bb.Errorf("write failed: %v\n", err)
266 | }
267 | if _, err := f.Read(b); err != nil {
268 | bb.Errorf("read failed: %v\n", err)
269 | }
270 | }
271 | }
272 |
--------------------------------------------------------------------------------
/log.go:
--------------------------------------------------------------------------------
1 | package go9p
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 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import "sync"
8 |
9 | var once sync.Once
10 |
11 | type osUser struct {
12 | uid int
13 | }
14 |
15 | type osUsers struct {
16 | users map[int]*osUser
17 | groups map[int]*osGroup
18 | sync.Mutex
19 | }
20 |
21 | // Simple Users implementation that fakes looking up users and groups
22 | // by uid only. The names and groups memberships are empty
23 | var OsUsers *osUsers
24 |
25 | func (u *osUser) Name() string { return "" }
26 |
27 | func (u *osUser) Id() int { return u.uid }
28 |
29 | func (u *osUser) Groups() []Group { return nil }
30 |
31 | func (u *osUser) IsMember(g Group) bool { return false }
32 |
33 | type osGroup struct {
34 | gid int
35 | }
36 |
37 | func (g *osGroup) Name() string { return "" }
38 |
39 | func (g *osGroup) Id() int { return g.gid }
40 |
41 | func (g *osGroup) Members() []User { return nil }
42 |
43 | func initOsusers() {
44 | OsUsers = new(osUsers)
45 | OsUsers.users = make(map[int]*osUser)
46 | OsUsers.groups = make(map[int]*osGroup)
47 | }
48 |
49 | func (up *osUsers) Uid2User(uid int) User {
50 | once.Do(initOsusers)
51 | OsUsers.Lock()
52 | defer OsUsers.Unlock()
53 | user, present := OsUsers.users[uid]
54 | if present {
55 | return user
56 | }
57 |
58 | user = new(osUser)
59 | user.uid = uid
60 | OsUsers.users[uid] = user
61 | return user
62 | }
63 |
64 | func (up *osUsers) Uname2User(uname string) User {
65 | // unimplemented
66 | return nil
67 | }
68 |
69 | func (up *osUsers) Gid2Group(gid int) Group {
70 | once.Do(initOsusers)
71 | OsUsers.Lock()
72 | group, present := OsUsers.groups[gid]
73 | if present {
74 | OsUsers.Unlock()
75 | return group
76 | }
77 |
78 | group = new(osGroup)
79 | group.gid = gid
80 | OsUsers.groups[gid] = group
81 | OsUsers.Unlock()
82 | return group
83 | }
84 |
85 | func (up *osUsers) Gname2Group(gname string) Group {
86 | return nil
87 | }
88 |
--------------------------------------------------------------------------------
/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 go9provides the definitions and functions used to implement
6 | // the 9P2000 protocol.
7 | // TODO.
8 | // All the packet conversion code in this file is crap and needs a rewrite.
9 | package go9p
10 |
11 | import (
12 | "fmt"
13 | )
14 |
15 | // 9P2000 message types
16 | const (
17 | Tversion = 100 + iota
18 | Rversion
19 | Tauth
20 | Rauth
21 | Tattach
22 | Rattach
23 | Terror
24 | Rerror
25 | Tflush
26 | Rflush
27 | Twalk
28 | Rwalk
29 | Topen
30 | Ropen
31 | Tcreate
32 | Rcreate
33 | Tread
34 | Rread
35 | Twrite
36 | Rwrite
37 | Tclunk
38 | Rclunk
39 | Tremove
40 | Rremove
41 | Tstat
42 | Rstat
43 | Twstat
44 | Rwstat
45 | Tlast
46 | )
47 |
48 | const (
49 | MSIZE = 1048576 + IOHDRSZ // default message size (1048576+IOHdrSz)
50 | IOHDRSZ = 24 // the non-data size of the Twrite messages
51 | PORT = 564 // default port for 9P file servers
52 | )
53 |
54 | // Qid types
55 | const (
56 | QTDIR = 0x80 // directories
57 | QTAPPEND = 0x40 // append only files
58 | QTEXCL = 0x20 // exclusive use files
59 | QTMOUNT = 0x10 // mounted channel
60 | QTAUTH = 0x08 // authentication file
61 | QTTMP = 0x04 // non-backed-up file
62 | QTSYMLINK = 0x02 // symbolic link (Unix, 9P2000.u)
63 | QTLINK = 0x01 // hard link (Unix, 9P2000.u)
64 | QTFILE = 0x00
65 | )
66 |
67 | // Flags for the mode field in Topen and Tcreate messages
68 | const (
69 | OREAD = 0 // open read-only
70 | OWRITE = 1 // open write-only
71 | ORDWR = 2 // open read-write
72 | OEXEC = 3 // execute (== read but check execute permission)
73 | OTRUNC = 16 // or'ed in (except for exec), truncate file first
74 | OCEXEC = 32 // or'ed in, close on exec
75 | ORCLOSE = 64 // or'ed in, remove on close
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 | EEXIST = 17
110 | ENOTDIR = 20
111 | EINVAL = 22
112 | )
113 |
114 | // Error represents a 9P2000 (and 9P2000.u) error
115 | type Error struct {
116 | Err string // textual representation of the error
117 | Errornum uint32 // numeric representation of the error (9P2000.u)
118 | }
119 |
120 | // File identifier
121 | type Qid struct {
122 | Type uint8 // type of the file (high 8 bits of the mode)
123 | Version uint32 // version number for the path
124 | Path uint64 // server's unique identification of the file
125 | }
126 |
127 | // Dir describes a file
128 | type Dir struct {
129 | Size uint16 // size-2 of the Dir on the wire
130 | Type uint16
131 | Dev uint32
132 | Qid // file's Qid
133 | Mode uint32 // permissions and flags
134 | Atime uint32 // last access time in seconds
135 | Mtime uint32 // last modified time in seconds
136 | Length uint64 // file length in bytes
137 | Name string // file name
138 | Uid string // owner name
139 | Gid string // group name
140 | Muid string // name of the last user that modified the file
141 |
142 | /* 9P2000.u extension */
143 | Ext string // special file's descriptor
144 | Uidnum uint32 // owner ID
145 | Gidnum uint32 // group ID
146 | Muidnum uint32 // ID of the last user that modified the file
147 | }
148 |
149 | // Fcall represents a 9P2000 message
150 | type Fcall struct {
151 | Size uint32 // size of the message
152 | Type uint8 // message type
153 | Fid uint32 // file identifier
154 | Tag uint16 // message tag
155 | Msize uint32 // maximum message size (used by Tversion, Rversion)
156 | Version string // protocol version (used by Tversion, Rversion)
157 | Oldtag uint16 // tag of the message to flush (used by Tflush)
158 | Error string // error (used by Rerror)
159 | Qid // file Qid (used by Rauth, Rattach, Ropen, Rcreate)
160 | Iounit uint32 // maximum bytes read without breaking in multiple messages (used by Ropen, Rcreate)
161 | Afid uint32 // authentication fid (used by Tauth, Tattach)
162 | Uname string // user name (used by Tauth, Tattach)
163 | Aname string // attach name (used by Tauth, Tattach)
164 | Perm uint32 // file permission (mode) (used by Tcreate)
165 | Name string // file name (used by Tcreate)
166 | Mode uint8 // open mode (used by Topen, Tcreate)
167 | Newfid uint32 // the fid that represents the file walked to (used by Twalk)
168 | Wname []string // list of names to walk (used by Twalk)
169 | Wqid []Qid // list of Qids for the walked files (used by Rwalk)
170 | Offset uint64 // offset in the file to read/write from/to (used by Tread, Twrite)
171 | Count uint32 // number of bytes read/written (used by Tread, Rread, Twrite, Rwrite)
172 | Data []uint8 // data read/to-write (used by Rread, Twrite)
173 | Dir // file description (used by Rstat, Twstat)
174 |
175 | /* 9P2000.u extensions */
176 | Errornum uint32 // error code, 9P2000.u only (used by Rerror)
177 | Ext string // special file description, 9P2000.u only (used by Tcreate)
178 | Unamenum uint32 // user ID, 9P2000.u only (used by Tauth, Tattach)
179 |
180 | Pkt []uint8 // raw packet data
181 | Buf []uint8 // buffer to put the raw data in
182 | }
183 |
184 | // Interface for accessing users and groups
185 | type Users interface {
186 | Uid2User(uid int) User
187 | Uname2User(uname string) User
188 | Gid2Group(gid int) Group
189 | Gname2Group(gname string) Group
190 | }
191 |
192 | // Represents a user
193 | type User interface {
194 | Name() string // user name
195 | Id() int // user id
196 | Groups() []Group // groups the user belongs to (can return nil)
197 | IsMember(g Group) bool // returns true if the user is member of the specified group
198 | }
199 |
200 | // Represents a group of users
201 | type Group interface {
202 | Name() string // group name
203 | Id() int // group id
204 | Members() []User // list of members that belong to the group (can return nil)
205 | }
206 |
207 | // minimum size of a 9P2000 message for a type
208 | var minFcsize = [...]uint32{
209 | 6, /* Tversion msize[4] version[s] */
210 | 6, /* Rversion msize[4] version[s] */
211 | 8, /* Tauth fid[4] uname[s] aname[s] */
212 | 13, /* Rauth aqid[13] */
213 | 12, /* Tattach fid[4] afid[4] uname[s] aname[s] */
214 | 13, /* Rattach qid[13] */
215 | 0, /* Terror */
216 | 2, /* Rerror ename[s] (ecode[4]) */
217 | 2, /* Tflush oldtag[2] */
218 | 0, /* Rflush */
219 | 10, /* Twalk fid[4] newfid[4] nwname[2] */
220 | 2, /* Rwalk nwqid[2] */
221 | 5, /* Topen fid[4] mode[1] */
222 | 17, /* Ropen qid[13] iounit[4] */
223 | 11, /* Tcreate fid[4] name[s] perm[4] mode[1] */
224 | 17, /* Rcreate qid[13] iounit[4] */
225 | 16, /* Tread fid[4] offset[8] count[4] */
226 | 4, /* Rread count[4] */
227 | 16, /* Twrite fid[4] offset[8] count[4] */
228 | 4, /* Rwrite count[4] */
229 | 4, /* Tclunk fid[4] */
230 | 0, /* Rclunk */
231 | 4, /* Tremove fid[4] */
232 | 0, /* Rremove */
233 | 4, /* Tstat fid[4] */
234 | 4, /* Rstat stat[n] */
235 | 8, /* Twstat fid[4] stat[n] */
236 | 0, /* Rwstat */
237 | 20, /* Tbread fileid[8] offset[8] count[4] */
238 | 4, /* Rbread count[4] */
239 | 20, /* Tbwrite fileid[8] offset[8] count[4] */
240 | 4, /* Rbwrite count[4] */
241 | 16, /* Tbtrunc fileid[8] offset[8] */
242 | 0, /* Rbtrunc */
243 | }
244 |
245 | // minimum size of a 9P2000.u message for a type
246 | var minFcusize = [...]uint32{
247 | 6, /* Tversion msize[4] version[s] */
248 | 6, /* Rversion msize[4] version[s] */
249 | 12, /* Tauth fid[4] uname[s] aname[s] */
250 | 13, /* Rauth aqid[13] */
251 | 16, /* Tattach fid[4] afid[4] uname[s] aname[s] */
252 | 13, /* Rattach qid[13] */
253 | 0, /* Terror */
254 | 6, /* Rerror ename[s] (ecode[4]) */
255 | 2, /* Tflush oldtag[2] */
256 | 0, /* Rflush */
257 | 10, /* Twalk fid[4] newfid[4] nwname[2] */
258 | 2, /* Rwalk nwqid[2] */
259 | 5, /* Topen fid[4] mode[1] */
260 | 17, /* Ropen qid[13] iounit[4] */
261 | 13, /* Tcreate fid[4] name[s] perm[4] mode[1] */
262 | 17, /* Rcreate qid[13] iounit[4] */
263 | 16, /* Tread fid[4] offset[8] count[4] */
264 | 4, /* Rread count[4] */
265 | 16, /* Twrite fid[4] offset[8] count[4] */
266 | 4, /* Rwrite count[4] */
267 | 4, /* Tclunk fid[4] */
268 | 0, /* Rclunk */
269 | 4, /* Tremove fid[4] */
270 | 0, /* Rremove */
271 | 4, /* Tstat fid[4] */
272 | 4, /* Rstat stat[n] */
273 | 8, /* Twstat fid[4] stat[n] */
274 | 20, /* Tbread fileid[8] offset[8] count[4] */
275 | 4, /* Rbread count[4] */
276 | 20, /* Tbwrite fileid[8] offset[8] count[4] */
277 | 4, /* Rbwrite count[4] */
278 | 16, /* Tbtrunc fileid[8] offset[8] */
279 | 0, /* Rbtrunc */
280 | }
281 |
282 | func gint8(buf []byte) (uint8, []byte) { return buf[0], buf[1:] }
283 |
284 | func gint16(buf []byte) (uint16, []byte) {
285 | return uint16(buf[0]) | (uint16(buf[1]) << 8), buf[2:]
286 | }
287 |
288 | func gint32(buf []byte) (uint32, []byte) {
289 | return uint32(buf[0]) | (uint32(buf[1]) << 8) | (uint32(buf[2]) << 16) |
290 | (uint32(buf[3]) << 24),
291 | buf[4:]
292 | }
293 |
294 | func Gint32(buf []byte) (uint32, []byte) { return gint32(buf) }
295 |
296 | func gint64(buf []byte) (uint64, []byte) {
297 | return uint64(buf[0]) | (uint64(buf[1]) << 8) | (uint64(buf[2]) << 16) |
298 | (uint64(buf[3]) << 24) | (uint64(buf[4]) << 32) | (uint64(buf[5]) << 40) |
299 | (uint64(buf[6]) << 48) | (uint64(buf[7]) << 56),
300 | buf[8:]
301 | }
302 |
303 | func gstr(buf []byte) (string, []byte) {
304 | var n uint16
305 |
306 | if buf == nil || len(buf) < 2 {
307 | return "", nil
308 | }
309 |
310 | n, buf = gint16(buf)
311 |
312 | if int(n) > len(buf) {
313 | return "", nil
314 | }
315 |
316 | return string(buf[0:n]), buf[n:]
317 | }
318 |
319 | func gqid(buf []byte, qid *Qid) []byte {
320 | qid.Type, buf = gint8(buf)
321 | qid.Version, buf = gint32(buf)
322 | qid.Path, buf = gint64(buf)
323 |
324 | return buf
325 | }
326 |
327 | func gstat(buf []byte, d *Dir, dotu bool) ([]byte, error) {
328 | sz := len(buf)
329 | d.Size, buf = gint16(buf)
330 | d.Type, buf = gint16(buf)
331 | d.Dev, buf = gint32(buf)
332 | buf = gqid(buf, &d.Qid)
333 | d.Mode, buf = gint32(buf)
334 | d.Atime, buf = gint32(buf)
335 | d.Mtime, buf = gint32(buf)
336 | d.Length, buf = gint64(buf)
337 | d.Name, buf = gstr(buf)
338 | if buf == nil {
339 | s := fmt.Sprintf("Buffer too short for basic 9p: need %d, have %d",
340 | 49, sz)
341 | return nil, &Error{s, EINVAL}
342 | }
343 |
344 | d.Uid, buf = gstr(buf)
345 | if buf == nil {
346 | return nil, &Error{"d.Uid failed", EINVAL}
347 | }
348 | d.Gid, buf = gstr(buf)
349 | if buf == nil {
350 | return nil, &Error{"d.Gid failed", EINVAL}
351 | }
352 |
353 | d.Muid, buf = gstr(buf)
354 | if buf == nil {
355 | return nil, &Error{"d.Muid failed", EINVAL}
356 | }
357 |
358 | if dotu {
359 | d.Ext, buf = gstr(buf)
360 | if buf == nil {
361 | return nil, &Error{"d.Ext failed", EINVAL}
362 | }
363 |
364 | d.Uidnum, buf = gint32(buf)
365 | d.Gidnum, buf = gint32(buf)
366 | d.Muidnum, buf = gint32(buf)
367 | } else {
368 | d.Uidnum = NOUID
369 | d.Gidnum = NOUID
370 | d.Muidnum = NOUID
371 | }
372 |
373 | return buf, nil
374 | }
375 |
376 | func pint8(val uint8, buf []byte) []byte {
377 | buf[0] = val
378 | return buf[1:]
379 | }
380 |
381 | func pint16(val uint16, buf []byte) []byte {
382 | buf[0] = uint8(val)
383 | buf[1] = uint8(val >> 8)
384 | return buf[2:]
385 | }
386 |
387 | func pint32(val uint32, buf []byte) []byte {
388 | buf[0] = uint8(val)
389 | buf[1] = uint8(val >> 8)
390 | buf[2] = uint8(val >> 16)
391 | buf[3] = uint8(val >> 24)
392 | return buf[4:]
393 | }
394 |
395 | func pint64(val uint64, buf []byte) []byte {
396 | buf[0] = uint8(val)
397 | buf[1] = uint8(val >> 8)
398 | buf[2] = uint8(val >> 16)
399 | buf[3] = uint8(val >> 24)
400 | buf[4] = uint8(val >> 32)
401 | buf[5] = uint8(val >> 40)
402 | buf[6] = uint8(val >> 48)
403 | buf[7] = uint8(val >> 56)
404 | return buf[8:]
405 | }
406 |
407 | func pstr(val string, buf []byte) []byte {
408 | n := uint16(len(val))
409 | buf = pint16(n, buf)
410 | b := []byte(val)
411 | copy(buf, b)
412 | return buf[n:]
413 | }
414 |
415 | func pqid(val *Qid, buf []byte) []byte {
416 | buf = pint8(val.Type, buf)
417 | buf = pint32(val.Version, buf)
418 | buf = pint64(val.Path, buf)
419 |
420 | return buf
421 | }
422 |
423 | func statsz(d *Dir, dotu bool) int {
424 | 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)
425 | if dotu {
426 | sz += 2 + 4 + 4 + 4 + len(d.Ext)
427 | }
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 | // Allocates a new Fcall.
493 | func NewFcall(sz uint32) *Fcall {
494 | fc := new(Fcall)
495 | fc.Buf = make([]byte, sz)
496 |
497 | return fc
498 | }
499 |
500 | // Sets the tag of a Fcall.
501 | func SetTag(fc *Fcall, tag uint16) {
502 | fc.Tag = tag
503 | pint16(tag, fc.Pkt[5:])
504 | }
505 |
506 | func packCommon(fc *Fcall, size int, id uint8) ([]byte, error) {
507 | size += 4 + 1 + 2 /* size[4] id[1] tag[2] */
508 | if len(fc.Buf) < int(size) {
509 | return nil, &Error{"buffer too small", EINVAL}
510 | }
511 |
512 | fc.Size = uint32(size)
513 | fc.Type = id
514 | fc.Tag = NOTAG
515 | p := fc.Buf
516 | p = pint32(uint32(size), p)
517 | p = pint8(id, p)
518 | p = pint16(NOTAG, p)
519 | fc.Pkt = fc.Buf[0:size]
520 |
521 | return p, nil
522 | }
523 |
524 | func (err *Error) Error() string {
525 | if err != nil {
526 | return err.Err
527 | }
528 |
529 | return ""
530 | }
531 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import "fmt"
8 |
9 | // Create a Rversion message in the specified Fcall.
10 | func PackRversion(fc *Fcall, msize uint32, version string) error {
11 | size := 4 + 2 + len(version) /* msize[4] version[s] */
12 | p, err := packCommon(fc, size, Rversion)
13 | if err != nil {
14 | return err
15 | }
16 |
17 | fc.Msize = msize
18 | fc.Version = version
19 | p = pint32(msize, p)
20 | p = pstr(version, p)
21 |
22 | return nil
23 | }
24 |
25 | // Create a Rauth message in the specified Fcall.
26 | func PackRauth(fc *Fcall, aqid *Qid) error {
27 | size := 13 /* aqid[13] */
28 | p, err := packCommon(fc, size, Rauth)
29 | if err != nil {
30 | return err
31 | }
32 |
33 | fc.Qid = *aqid
34 | p = pqid(aqid, p)
35 | return nil
36 | }
37 |
38 | // Create a Rerror message in the specified Fcall. If dotu is true,
39 | // the function will create a 9P2000.u message. If false, errornum is
40 | // ignored.
41 | // The Akaros global is hurl-inducing but this whole blob of code
42 | // needs a redo. dotu is even worse, since it bakes in ONE PARTICULAR
43 | // EXTENSION ...
44 |
45 | func PackRerror(fc *Fcall, error string, errornum uint32, dotu bool) error {
46 | if *Akaros {
47 | error = fmt.Sprintf("%04X %v", errornum, error)
48 | }
49 |
50 | size := 2 + len(error) /* ename[s] */
51 | if dotu {
52 | size += 4 /* ecode[4] */
53 | }
54 |
55 | p, err := packCommon(fc, size, Rerror)
56 | if err != nil {
57 | return err
58 | }
59 |
60 | fc.Error = error
61 | p = pstr(error, p)
62 | if dotu {
63 | fc.Errornum = errornum
64 | p = pint32(errornum, p)
65 | }
66 |
67 | return nil
68 | }
69 |
70 | // Create a Rflush message in the specified Fcall.
71 | func PackRflush(fc *Fcall) error {
72 | _, err := packCommon(fc, 0, Rflush)
73 |
74 | return err
75 | }
76 |
77 | // Create a Rattach message in the specified Fcall.
78 | func PackRattach(fc *Fcall, aqid *Qid) error {
79 | size := 13 /* aqid[13] */
80 | p, err := packCommon(fc, size, Rattach)
81 | if err != nil {
82 | return err
83 | }
84 |
85 | fc.Qid = *aqid
86 | p = pqid(aqid, p)
87 | return nil
88 | }
89 |
90 | // Create a Rwalk message in the specified Fcall.
91 | func PackRwalk(fc *Fcall, wqids []Qid) error {
92 | nwqid := len(wqids)
93 | size := 2 + nwqid*13 /* nwqid[2] nwname*wqid[13] */
94 | p, err := packCommon(fc, size, Rwalk)
95 | if err != nil {
96 | return err
97 | }
98 |
99 | p = pint16(uint16(nwqid), p)
100 | fc.Wqid = make([]Qid, nwqid)
101 | for i := 0; i < nwqid; i++ {
102 | fc.Wqid[i] = wqids[i]
103 | p = pqid(&wqids[i], p)
104 | }
105 |
106 | return nil
107 | }
108 |
109 | // Create a Ropen message in the specified Fcall.
110 | func PackRopen(fc *Fcall, qid *Qid, iounit uint32) error {
111 | size := 13 + 4 /* qid[13] iounit[4] */
112 | p, err := packCommon(fc, size, Ropen)
113 | if err != nil {
114 | return err
115 | }
116 |
117 | fc.Qid = *qid
118 | fc.Iounit = iounit
119 | p = pqid(qid, p)
120 | p = pint32(iounit, p)
121 | return nil
122 | }
123 |
124 | // Create a Rcreate message in the specified Fcall.
125 | func PackRcreate(fc *Fcall, qid *Qid, iounit uint32) error {
126 | size := 13 + 4 /* qid[13] iounit[4] */
127 | p, err := packCommon(fc, size, Rcreate)
128 | if err != nil {
129 | return err
130 | }
131 |
132 | fc.Qid = *qid
133 | fc.Iounit = iounit
134 | p = pqid(qid, p)
135 | p = pint32(iounit, p)
136 | return nil
137 | }
138 |
139 | // Initializes the specified Fcall value to contain Rread message.
140 | // The user should copy the returned data to the slice pointed by
141 | // fc.Data and call SetRreadCount to update the data size to the
142 | // actual value.
143 | func InitRread(fc *Fcall, count uint32) error {
144 | size := int(4 + count) /* count[4] data[count] */
145 | p, err := packCommon(fc, size, Rread)
146 | if err != nil {
147 | return err
148 | }
149 |
150 | fc.Count = count
151 | fc.Data = p[4 : fc.Count+4]
152 | p = pint32(count, p)
153 | return nil
154 | }
155 |
156 | // Updates the size of the data returned by Rread. Expects that
157 | // the Fcall value is already initialized by InitRread.
158 | func SetRreadCount(fc *Fcall, count uint32) {
159 | /* we need to update both the packet size as well as the data count */
160 | size := 4 + 1 + 2 + 4 + count /* size[4] id[1] tag[2] count[4] data[count] */
161 | pint32(size, fc.Pkt)
162 | pint32(count, fc.Pkt[7:])
163 | fc.Size = size
164 | fc.Count = count
165 | fc.Pkt = fc.Pkt[0:size]
166 | fc.Data = fc.Data[0:count]
167 | fc.Size = size
168 | }
169 |
170 | // Create a Rread message in the specified Fcall.
171 | func PackRread(fc *Fcall, data []byte) error {
172 | count := uint32(len(data))
173 | err := InitRread(fc, count)
174 | if err != nil {
175 | return err
176 | }
177 |
178 | copy(fc.Data, data)
179 | return nil
180 | }
181 |
182 | // Create a Rwrite message in the specified Fcall.
183 | func PackRwrite(fc *Fcall, count uint32) error {
184 | p, err := packCommon(fc, 4, Rwrite) /* count[4] */
185 | if err != nil {
186 | return err
187 | }
188 |
189 | fc.Count = count
190 |
191 | p = pint32(count, p)
192 | return nil
193 | }
194 |
195 | // Create a Rclunk message in the specified Fcall.
196 | func PackRclunk(fc *Fcall) error {
197 | _, err := packCommon(fc, 0, Rclunk)
198 | return err
199 | }
200 |
201 | // Create a Rremove message in the specified Fcall.
202 | func PackRremove(fc *Fcall) error {
203 | _, err := packCommon(fc, 0, Rremove)
204 | return err
205 | }
206 |
207 | // Create a Rstat message in the specified Fcall. If dotu is true, the
208 | // function will create a 9P2000.u stat representation that includes
209 | // st.Nuid, st.Ngid, st.Nmuid and st.Ext. Otherwise these values will be
210 | // ignored.
211 | func PackRstat(fc *Fcall, d *Dir, dotu bool) error {
212 | stsz := statsz(d, dotu)
213 | size := 2 + stsz /* stat[n] */
214 | p, err := packCommon(fc, size, Rstat)
215 | if err != nil {
216 | return err
217 | }
218 |
219 | p = pint16(uint16(stsz), p)
220 | p = pstat(d, p, dotu)
221 | fc.Dir = *d
222 | return nil
223 | }
224 |
225 | // Create a Rwstat message in the specified Fcall.
226 | func PackRwstat(fc *Fcall) error {
227 | _, err := packCommon(fc, 0, Rwstat)
228 | return err
229 | }
230 |
--------------------------------------------------------------------------------
/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 go9p
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 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "fmt"
9 | "log"
10 | "net"
11 | )
12 |
13 | func (srv *Srv) NewConn(c net.Conn) {
14 | conn := new(Conn)
15 | conn.Srv = srv
16 | conn.Msize = srv.Msize
17 | conn.Dotu = srv.Dotu
18 | conn.Debuglevel = srv.Debuglevel
19 | conn.conn = c
20 | conn.fidpool = make(map[uint32]*SrvFid)
21 | conn.reqs = make(map[uint16]*SrvReq)
22 | conn.reqout = make(chan *SrvReq, srv.Maxpend)
23 | conn.done = make(chan bool)
24 | conn.rchan = make(chan *Fcall, 64)
25 |
26 | srv.Lock()
27 | if srv.conns == nil {
28 | srv.conns = make(map[*Conn]*Conn)
29 | }
30 | srv.conns[conn] = conn
31 | srv.Unlock()
32 |
33 | conn.Id = c.RemoteAddr().String()
34 | if op, ok := (conn.Srv.ops).(ConnOps); ok {
35 | op.ConnOpened(conn)
36 | }
37 |
38 | if sop, ok := (interface{}(conn)).(StatsOps); ok {
39 | sop.statsRegister()
40 | }
41 |
42 | go conn.recv()
43 | go conn.send()
44 | }
45 |
46 | func (conn *Conn) close() {
47 | conn.done <- true
48 | conn.Srv.Lock()
49 | delete(conn.Srv.conns, conn)
50 | conn.Srv.Unlock()
51 |
52 | if sop, ok := (interface{}(conn)).(StatsOps); ok {
53 | sop.statsUnregister()
54 | }
55 | if op, ok := (conn.Srv.ops).(ConnOps); ok {
56 | op.ConnClosed(conn)
57 | }
58 |
59 | /* call FidDestroy for all remaining fids */
60 | if op, ok := (conn.Srv.ops).(SrvFidOps); ok {
61 | for _, fid := range conn.fidpool {
62 | op.FidDestroy(fid)
63 | }
64 | }
65 | }
66 |
67 | func (conn *Conn) recv() {
68 | var err error
69 | var n int
70 |
71 | buf := make([]byte, conn.Msize*8)
72 | pos := 0
73 | for {
74 | if len(buf) < int(conn.Msize) {
75 | b := make([]byte, conn.Msize*8)
76 | copy(b, buf[0:pos])
77 | buf = b
78 | b = nil
79 | }
80 |
81 | n, err = conn.conn.Read(buf[pos:])
82 | if err != nil || n == 0 {
83 | conn.close()
84 | return
85 | }
86 |
87 | pos += n
88 | for pos > 4 {
89 | sz, _ := Gint32(buf)
90 | if sz > conn.Msize {
91 | log.Println("bad client connection: ", conn.conn.RemoteAddr())
92 | conn.conn.Close()
93 | conn.close()
94 | return
95 | }
96 | if pos < int(sz) {
97 | if len(buf) < int(sz) {
98 | b := make([]byte, conn.Msize*8)
99 | copy(b, buf[0:pos])
100 | buf = b
101 | b = nil
102 | }
103 |
104 | break
105 | }
106 | fc, err, fcsize := Unpack(buf, conn.Dotu)
107 | if err != nil {
108 | log.Println(fmt.Sprintf("invalid packet : %v %v", err, buf))
109 | conn.conn.Close()
110 | conn.close()
111 | return
112 | }
113 |
114 | tag := fc.Tag
115 | req := new(SrvReq)
116 | select {
117 | case req.Rc = <-conn.rchan:
118 | break
119 | default:
120 | req.Rc = NewFcall(conn.Msize)
121 | }
122 |
123 | req.Conn = conn
124 | req.Tc = fc
125 | // req.Rc = rc
126 | if conn.Debuglevel > 0 {
127 | conn.logFcall(req.Tc)
128 | if conn.Debuglevel&DbgPrintPackets != 0 {
129 | log.Println(">->", conn.Id, fmt.Sprint(req.Tc.Pkt))
130 | }
131 |
132 | if conn.Debuglevel&DbgPrintFcalls != 0 {
133 | log.Println(">>>", conn.Id, req.Tc.String())
134 | }
135 | }
136 |
137 | conn.Lock()
138 | conn.nreqs++
139 | conn.tsz += uint64(fc.Size)
140 | conn.npend++
141 | if conn.npend > conn.maxpend {
142 | conn.maxpend = conn.npend
143 | }
144 |
145 | req.next = conn.reqs[tag]
146 | conn.reqs[tag] = req
147 | process := req.next == nil
148 | if req.next != nil {
149 | req.next.prev = req
150 | }
151 | conn.Unlock()
152 | if process {
153 | // Tversion may change some attributes of the
154 | // connection, so we block on it. Otherwise,
155 | // we may loop back to reading and that is a race.
156 | // This fix brought to you by the race detector.
157 | if req.Tc.Type == Tversion {
158 | req.process()
159 | } else {
160 | go req.process()
161 | }
162 | }
163 |
164 | buf = buf[fcsize:]
165 | pos -= fcsize
166 | }
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 | 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 *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(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 &Error{err.Error(), 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 &Error{err.Error(), EIO}
258 | }
259 |
260 | srv.NewConn(c)
261 | }
262 | return nil
263 | }
264 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | func (srv *Srv) version(req *SrvReq) {
8 | tc := req.Tc
9 | conn := req.Conn
10 |
11 | if tc.Msize < IOHDRSZ {
12 | req.RespondError(&Error{"msize too small", EINVAL})
13 | return
14 | }
15 |
16 | if tc.Msize < conn.Msize {
17 | conn.Msize = tc.Msize
18 | }
19 |
20 | conn.Dotu = tc.Version == "9P2000.u" && srv.Dotu
21 | ver := "9P2000"
22 | if conn.Dotu {
23 | ver = "9P2000.u"
24 | }
25 |
26 | /* make sure that the responses of all current requests will be ignored */
27 | conn.Lock()
28 | for tag, r := range conn.reqs {
29 | if tag == NOTAG {
30 | continue
31 | }
32 |
33 | for rr := r; rr != nil; rr = rr.next {
34 | rr.Lock()
35 | rr.status |= reqFlush
36 | rr.Unlock()
37 | }
38 | }
39 | conn.Unlock()
40 |
41 | req.RespondRversion(conn.Msize, ver)
42 | }
43 |
44 | func (srv *Srv) auth(req *SrvReq) {
45 | tc := req.Tc
46 | conn := req.Conn
47 | if tc.Afid == NOFID {
48 | req.RespondError(Eunknownfid)
49 | return
50 | }
51 |
52 | req.Afid = conn.FidNew(tc.Afid)
53 | if req.Afid == nil {
54 | req.RespondError(Einuse)
55 | return
56 | }
57 |
58 | var user User = nil
59 | if tc.Unamenum != NOUID || conn.Dotu {
60 | user = srv.Upool.Uid2User(int(tc.Unamenum))
61 | } else if tc.Uname != "" {
62 | user = srv.Upool.Uname2User(tc.Uname)
63 | }
64 |
65 | if user == nil {
66 | req.RespondError(Enouser)
67 | return
68 | }
69 |
70 | req.Afid.User = user
71 | req.Afid.Type = QTAUTH
72 | if aop, ok := (srv.ops).(AuthOps); ok {
73 | aqid, err := aop.AuthInit(req.Afid, tc.Aname)
74 | if err != nil {
75 | req.RespondError(err)
76 | } else {
77 | aqid.Type |= QTAUTH // just in case
78 | req.RespondRauth(aqid)
79 | }
80 | } else {
81 | req.RespondError(Enoauth)
82 | }
83 |
84 | }
85 |
86 | func (srv *Srv) authPost(req *SrvReq) {
87 | if req.Rc != nil && req.Rc.Type == Rauth {
88 | req.Afid.IncRef()
89 | }
90 | }
91 |
92 | func (srv *Srv) attach(req *SrvReq) {
93 | tc := req.Tc
94 | conn := req.Conn
95 | if tc.Fid == NOFID {
96 | req.RespondError(Eunknownfid)
97 | return
98 | }
99 |
100 | req.Fid = conn.FidNew(tc.Fid)
101 | if req.Fid == nil {
102 | req.RespondError(Einuse)
103 | return
104 | }
105 |
106 | if tc.Afid != NOFID {
107 | req.Afid = conn.FidGet(tc.Afid)
108 | if req.Afid == nil {
109 | req.RespondError(Eunknownfid)
110 | }
111 | }
112 |
113 | var user User = nil
114 | if tc.Unamenum != NOUID || conn.Dotu {
115 | user = srv.Upool.Uid2User(int(tc.Unamenum))
116 | } else if tc.Uname != "" {
117 | user = srv.Upool.Uname2User(tc.Uname)
118 | }
119 |
120 | if user == nil {
121 | req.RespondError(Enouser)
122 | return
123 | }
124 |
125 | req.Fid.User = user
126 | if aop, ok := (srv.ops).(AuthOps); ok {
127 | err := aop.AuthCheck(req.Fid, req.Afid, tc.Aname)
128 | if err != nil {
129 | req.RespondError(err)
130 | return
131 | }
132 | }
133 |
134 | (srv.ops).(SrvReqOps).Attach(req)
135 | }
136 |
137 | func (srv *Srv) attachPost(req *SrvReq) {
138 | if req.Rc != nil && req.Rc.Type == Rattach {
139 | req.Fid.Type = req.Rc.Qid.Type
140 | req.Fid.IncRef()
141 | }
142 | }
143 |
144 | func (srv *Srv) flush(req *SrvReq) {
145 | conn := req.Conn
146 | tag := req.Tc.Oldtag
147 | PackRflush(req.Rc)
148 | conn.Lock()
149 | r := conn.reqs[tag]
150 | if r != nil {
151 | req.flushreq = r.flushreq
152 | r.flushreq = req
153 | }
154 | conn.Unlock()
155 |
156 | if r == nil {
157 | // there are no requests with that tag
158 | req.Respond()
159 | return
160 | }
161 |
162 | r.Lock()
163 | status := r.status
164 | if (status & (reqWork | reqSaved)) == 0 {
165 | /* the request is not worked on yet */
166 | r.status |= reqFlush
167 | }
168 | r.Unlock()
169 |
170 | if (status & (reqWork | reqSaved)) == 0 {
171 | r.Respond()
172 | } else {
173 | if op, ok := (srv.ops).(FlushOp); ok {
174 | op.Flush(r)
175 | }
176 | }
177 | }
178 |
179 | func (srv *Srv) walk(req *SrvReq) {
180 | conn := req.Conn
181 | tc := req.Tc
182 | fid := req.Fid
183 |
184 | /* we can't walk regular files, only clone them */
185 | if len(tc.Wname) > 0 && (fid.Type&QTDIR) == 0 {
186 | req.RespondError(Enotdir)
187 | return
188 | }
189 |
190 | /* we can't walk open files */
191 | if fid.opened {
192 | req.RespondError(Ebaduse)
193 | return
194 | }
195 |
196 | if tc.Fid != tc.Newfid {
197 | req.Newfid = conn.FidNew(tc.Newfid)
198 | if req.Newfid == nil {
199 | req.RespondError(Einuse)
200 | return
201 | }
202 |
203 | req.Newfid.User = fid.User
204 | req.Newfid.Type = fid.Type
205 | } else {
206 | req.Newfid = req.Fid
207 | req.Newfid.IncRef()
208 | }
209 |
210 | (req.Conn.Srv.ops).(SrvReqOps).Walk(req)
211 | }
212 |
213 | func (srv *Srv) walkPost(req *SrvReq) {
214 | rc := req.Rc
215 | if rc == nil || rc.Type != Rwalk || req.Newfid == nil {
216 | return
217 | }
218 |
219 | n := len(rc.Wqid)
220 | if n > 0 {
221 | req.Newfid.Type = rc.Wqid[n-1].Type
222 | } else {
223 | req.Newfid.Type = req.Fid.Type
224 | }
225 |
226 | // Don't retain the fid if only a partial walk succeeded
227 | if n != len(req.Tc.Wname) {
228 | return
229 | }
230 |
231 | if req.Newfid.fid != req.Fid.fid {
232 | req.Newfid.IncRef()
233 | }
234 | }
235 |
236 | func (srv *Srv) open(req *SrvReq) {
237 | fid := req.Fid
238 | tc := req.Tc
239 | if fid.opened {
240 | req.RespondError(Eopen)
241 | return
242 | }
243 |
244 | if (fid.Type&QTDIR) != 0 && tc.Mode != OREAD {
245 | req.RespondError(Eperm)
246 | return
247 | }
248 |
249 | fid.Omode = tc.Mode
250 | (req.Conn.Srv.ops).(SrvReqOps).Open(req)
251 | }
252 |
253 | func (srv *Srv) openPost(req *SrvReq) {
254 | if req.Fid != nil {
255 | req.Fid.opened = req.Rc != nil && req.Rc.Type == Ropen
256 | }
257 | }
258 |
259 | func (srv *Srv) create(req *SrvReq) {
260 | fid := req.Fid
261 | tc := req.Tc
262 | if fid.opened {
263 | req.RespondError(Eopen)
264 | return
265 | }
266 |
267 | if (fid.Type & QTDIR) == 0 {
268 | req.RespondError(Enotdir)
269 | return
270 | }
271 |
272 | /* can't open directories for other than reading */
273 | if (tc.Perm&DMDIR) != 0 && tc.Mode != OREAD {
274 | req.RespondError(Eperm)
275 | return
276 | }
277 |
278 | /* can't create special files if not 9P2000.u */
279 | if (tc.Perm&(DMNAMEDPIPE|DMSYMLINK|DMLINK|DMDEVICE|DMSOCKET)) != 0 && !req.Conn.Dotu {
280 | req.RespondError(Eperm)
281 | return
282 | }
283 |
284 | fid.Omode = tc.Mode
285 | (req.Conn.Srv.ops).(SrvReqOps).Create(req)
286 | }
287 |
288 | func (srv *Srv) createPost(req *SrvReq) {
289 | if req.Rc != nil && req.Rc.Type == Rcreate && req.Fid != nil {
290 | req.Fid.Type = req.Rc.Qid.Type
291 | req.Fid.opened = true
292 | }
293 | }
294 |
295 | func (srv *Srv) read(req *SrvReq) {
296 | tc := req.Tc
297 | fid := req.Fid
298 | if tc.Count+IOHDRSZ > req.Conn.Msize {
299 | req.RespondError(Etoolarge)
300 | return
301 | }
302 |
303 | if (fid.Type & QTAUTH) != 0 {
304 | var n int
305 |
306 | rc := req.Rc
307 | err := InitRread(rc, tc.Count)
308 | if err != nil {
309 | req.RespondError(err)
310 | return
311 | }
312 |
313 | if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
314 | n, err = op.AuthRead(fid, tc.Offset, rc.Data)
315 | if err != nil {
316 | req.RespondError(err)
317 | return
318 | }
319 |
320 | SetRreadCount(rc, uint32(n))
321 | req.Respond()
322 | } else {
323 | req.RespondError(Enotimpl)
324 | }
325 |
326 | return
327 | }
328 |
329 | if (fid.Type & QTDIR) != 0 {
330 | if tc.Offset == 0 {
331 | fid.Diroffset = 0
332 | } else if tc.Offset != fid.Diroffset {
333 | fid.Diroffset = tc.Offset
334 | }
335 | }
336 |
337 | (req.Conn.Srv.ops).(SrvReqOps).Read(req)
338 | }
339 |
340 | func (srv *Srv) readPost(req *SrvReq) {
341 | if req.Rc != nil && req.Rc.Type == Rread && (req.Fid.Type&QTDIR) != 0 {
342 | req.Fid.Diroffset += uint64(req.Rc.Count)
343 | }
344 | }
345 |
346 | func (srv *Srv) write(req *SrvReq) {
347 | fid := req.Fid
348 | tc := req.Tc
349 | if (fid.Type & QTAUTH) != 0 {
350 | tc := req.Tc
351 | if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
352 | n, err := op.AuthWrite(req.Fid, tc.Offset, tc.Data)
353 | if err != nil {
354 | req.RespondError(err)
355 | } else {
356 | req.RespondRwrite(uint32(n))
357 | }
358 | } else {
359 | req.RespondError(Enotimpl)
360 | }
361 |
362 | return
363 | }
364 |
365 | if !fid.opened || (fid.Type&QTDIR) != 0 || (fid.Omode&3) == OREAD {
366 | req.RespondError(Ebaduse)
367 | return
368 | }
369 |
370 | if tc.Count+IOHDRSZ > req.Conn.Msize {
371 | req.RespondError(Etoolarge)
372 | return
373 | }
374 |
375 | (req.Conn.Srv.ops).(SrvReqOps).Write(req)
376 | }
377 |
378 | func (srv *Srv) clunk(req *SrvReq) {
379 | fid := req.Fid
380 | if (fid.Type & QTAUTH) != 0 {
381 | if op, ok := (req.Conn.Srv.ops).(AuthOps); ok {
382 | op.AuthDestroy(fid)
383 | req.RespondRclunk()
384 | } else {
385 | req.RespondError(Enotimpl)
386 | }
387 |
388 | return
389 | }
390 |
391 | (req.Conn.Srv.ops).(SrvReqOps).Clunk(req)
392 | }
393 |
394 | func (srv *Srv) clunkPost(req *SrvReq) {
395 | if req.Rc != nil && req.Rc.Type == Rclunk && req.Fid != nil {
396 | req.Fid.DecRef()
397 | }
398 | }
399 |
400 | func (srv *Srv) remove(req *SrvReq) { (req.Conn.Srv.ops).(SrvReqOps).Remove(req) }
401 |
402 | func (srv *Srv) removePost(req *SrvReq) {
403 | if req.Rc != nil && req.Fid != nil {
404 | req.Fid.DecRef()
405 | }
406 | }
407 |
408 | func (srv *Srv) stat(req *SrvReq) { (req.Conn.Srv.ops).(SrvReqOps).Stat(req) }
409 |
410 | func (srv *Srv) wstat(req *SrvReq) {
411 | /*
412 | fid := req.Fid
413 | d := &req.Tc.Dir
414 | if d.Type != uint16(0xFFFF) || d.Dev != uint32(0xFFFFFFFF) || d.Version != uint32(0xFFFFFFFF) ||
415 | d.Path != uint64(0xFFFFFFFFFFFFFFFF) {
416 | req.RespondError(Eperm)
417 | return
418 | }
419 |
420 | if (d.Mode != 0xFFFFFFFF) && (((fid.Type&QTDIR) != 0 && (d.Mode&DMDIR) == 0) ||
421 | ((d.Type&QTDIR) == 0 && (d.Mode&DMDIR) != 0)) {
422 | req.RespondError(Edirchange)
423 | return
424 | }
425 | */
426 |
427 | (req.Conn.Srv.ops).(SrvReqOps).Wstat(req)
428 | }
429 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "log"
9 | "sync"
10 | "time"
11 | )
12 |
13 | // The FStatOp interface provides a single operation (Stat) that will be
14 | // called before a file stat is sent back to the client. If implemented,
15 | // the operation should update the data in the srvFile struct.
16 | type FStatOp interface {
17 | Stat(fid *FFid) error
18 | }
19 |
20 | // The FWstatOp interface provides a single operation (Wstat) that will be
21 | // called when the client requests the srvFile metadata to be modified. If
22 | // implemented, the operation will be called when Twstat message is received.
23 | // If not implemented, "permission denied" error will be sent back. If the
24 | // operation returns an Error, the error is send back to the client.
25 | type FWstatOp interface {
26 | Wstat(*FFid, *Dir) error
27 | }
28 |
29 | // If the FReadOp interface is implemented, the Read operation will be called
30 | // to read from the file. If not implemented, "permission denied" error will
31 | // be send back. The operation returns the number of bytes read, or the
32 | // error occured while reading.
33 | type FReadOp interface {
34 | Read(fid *FFid, buf []byte, offset uint64) (int, error)
35 | }
36 |
37 | // If the FWriteOp interface is implemented, the Write operation will be called
38 | // to write to the file. If not implemented, "permission denied" error will
39 | // be send back. The operation returns the number of bytes written, or the
40 | // error occured while writing.
41 | type FWriteOp interface {
42 | Write(fid *FFid, data []byte, offset uint64) (int, error)
43 | }
44 |
45 | // If the FCreateOp interface is implemented, the Create operation will be called
46 | // when the client attempts to create a file in the srvFile implementing the interface.
47 | // If not implemented, "permission denied" error will be send back. If successful,
48 | // the operation should call (*File)Add() to add the created file to the directory.
49 | // The operation returns the created file, or the error occured while creating it.
50 | type FCreateOp interface {
51 | Create(fid *FFid, name string, perm uint32) (*srvFile, error)
52 | }
53 |
54 | // If the FRemoveOp interface is implemented, the Remove operation will be called
55 | // when the client attempts to create a file in the srvFile implementing the interface.
56 | // If not implemented, "permission denied" error will be send back.
57 | // The operation returns nil if successful, or the error that occured while removing
58 | // the file.
59 | type FRemoveOp interface {
60 | Remove(*FFid) error
61 | }
62 |
63 | type FOpenOp interface {
64 | Open(fid *FFid, mode uint8) error
65 | }
66 |
67 | type FClunkOp interface {
68 | Clunk(fid *FFid) error
69 | }
70 |
71 | type FDestroyOp interface {
72 | FidDestroy(fid *FFid)
73 | }
74 |
75 | type FFlags int
76 |
77 | const (
78 | Fremoved FFlags = 1 << iota
79 | )
80 |
81 | // The srvFile type represents a file (or directory) served by the file server.
82 | type srvFile struct {
83 | sync.Mutex
84 | Dir
85 | flags FFlags
86 |
87 | Parent *srvFile // parent
88 | next, prev *srvFile // siblings, guarded by parent.Lock
89 | cfirst, clast *srvFile // children (if directory)
90 | ops interface{}
91 | }
92 |
93 | type FFid struct {
94 | F *srvFile
95 | Fid *SrvFid
96 | dirs []*srvFile // used for readdir
97 | dirents []byte // serialized version of dirs
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 *srvFile
105 | }
106 |
107 | var lock sync.Mutex
108 | var qnext uint64
109 | var Eexist = &Error{"file already exists", EEXIST}
110 | var Enoent = &Error{"file not found", ENOENT}
111 | var Enotempty = &Error{"directory not empty", EPERM}
112 |
113 | // Creates a file server with root as root directory
114 | func NewsrvFileSrv(root *srvFile) *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 *srvFile) Add(dir *srvFile, name string, uid User, gid 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 = 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 = NOUID
153 | }
154 |
155 | f.Muid = ""
156 | f.Muidnum = 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 *srvFile) 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 *srvFile) 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 *srvFile) Find(name string) *srvFile {
233 | var f *srvFile
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 DMREAD, DMWRITE, and DMEXEC.
248 | func (f *srvFile) CheckPerm(user 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 *SrvReq) {
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 *SrvReq) {
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([]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 & QTDIR) > 0 {
318 | if !f.CheckPerm(req.Fid.User, 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 OREAD:
346 | perm = DMREAD
347 | case OWRITE:
348 | perm = DMWRITE
349 | case ORDWR:
350 | perm = DMREAD | DMWRITE
351 | }
352 |
353 | if (mode & OTRUNC) != 0 {
354 | perm |= DMWRITE
355 | }
356 |
357 | return perm
358 | }
359 |
360 | func (*Fsrv) Open(req *SrvReq) {
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 *SrvReq) {
380 | fid := req.Fid.Aux.(*FFid)
381 | tc := req.Tc
382 |
383 | dir := fid.F
384 | if !dir.CheckPerm(req.Fid.User, 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 *SrvReq) {
403 | var 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 | InitRread(rc, tc.Count)
411 |
412 | if f.Mode&DMDIR != 0 {
413 | // Get all the directory entries and
414 | // serialize them all into an output buffer.
415 | // This greatly simplifies the directory read.
416 | if tc.Offset == 0 {
417 | var g *srvFile
418 | fid.dirents = nil
419 | f.Lock()
420 | for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next {
421 | }
422 | fid.dirs = make([]*srvFile, n)
423 | for n, g = 0, f.cfirst; g != nil; n, g = n+1, g.next {
424 | fid.dirs[n] = g
425 | fid.dirents = append(fid.dirents,
426 | PackDir(&g.Dir, req.Conn.Dotu)...)
427 | }
428 | f.Unlock()
429 | }
430 |
431 | switch {
432 | case tc.Offset > uint64(len(fid.dirents)):
433 | n = 0
434 | case len(fid.dirents[tc.Offset:]) > int(tc.Size):
435 | n = int(tc.Size)
436 | default:
437 | n = len(fid.dirents[tc.Offset:])
438 | }
439 | copy(rc.Data, fid.dirents[tc.Offset:int(tc.Offset)+1+n])
440 |
441 | } else {
442 | // file
443 | if rop, ok := f.ops.(FReadOp); ok {
444 | n, err = rop.Read(fid, rc.Data, tc.Offset)
445 | if err != nil {
446 | req.RespondError(err)
447 | return
448 | }
449 | } else {
450 | req.RespondError(Eperm)
451 | return
452 | }
453 | }
454 |
455 | SetRreadCount(rc, uint32(n))
456 | req.Respond()
457 | }
458 |
459 | func (*Fsrv) Write(req *SrvReq) {
460 | fid := req.Fid.Aux.(*FFid)
461 | f := fid.F
462 | tc := req.Tc
463 |
464 | if wop, ok := (f.ops).(FWriteOp); ok {
465 | n, err := wop.Write(fid, tc.Data, tc.Offset)
466 | if err != nil {
467 | req.RespondError(err)
468 | } else {
469 | req.RespondRwrite(uint32(n))
470 | }
471 | } else {
472 | req.RespondError(Eperm)
473 | }
474 |
475 | }
476 |
477 | func (*Fsrv) Clunk(req *SrvReq) {
478 | fid := req.Fid.Aux.(*FFid)
479 |
480 | if op, ok := (fid.F.ops).(FClunkOp); ok {
481 | err := op.Clunk(fid)
482 | if err != nil {
483 | req.RespondError(err)
484 | }
485 | }
486 | req.RespondRclunk()
487 | }
488 |
489 | func (*Fsrv) Remove(req *SrvReq) {
490 | fid := req.Fid.Aux.(*FFid)
491 | f := fid.F
492 | f.Lock()
493 | if f.cfirst != nil {
494 | f.Unlock()
495 | req.RespondError(Enotempty)
496 | return
497 | }
498 | f.Unlock()
499 |
500 | if rop, ok := (f.ops).(FRemoveOp); ok {
501 | err := rop.Remove(fid)
502 | if err != nil {
503 | req.RespondError(err)
504 | } else {
505 | f.Remove()
506 | req.RespondRremove()
507 | }
508 | } else {
509 | log.Println("remove not implemented")
510 | req.RespondError(Eperm)
511 | }
512 | }
513 |
514 | func (*Fsrv) Stat(req *SrvReq) {
515 | fid := req.Fid.Aux.(*FFid)
516 | f := fid.F
517 |
518 | if sop, ok := (f.ops).(FStatOp); ok {
519 | err := sop.Stat(fid)
520 | if err != nil {
521 | req.RespondError(err)
522 | } else {
523 | req.RespondRstat(&f.Dir)
524 | }
525 | } else {
526 | req.RespondRstat(&f.Dir)
527 | }
528 | }
529 |
530 | func (*Fsrv) Wstat(req *SrvReq) {
531 | tc := req.Tc
532 | fid := req.Fid.Aux.(*FFid)
533 | f := fid.F
534 |
535 | if wop, ok := (f.ops).(FWstatOp); ok {
536 | err := wop.Wstat(fid, &tc.Dir)
537 | if err != nil {
538 | req.RespondError(err)
539 | } else {
540 | req.RespondRwstat()
541 | }
542 | } else {
543 | req.RespondError(Eperm)
544 | }
545 | }
546 |
547 | func (*Fsrv) FidDestroy(ffid *SrvFid) {
548 | if ffid.Aux == nil {
549 | return
550 | }
551 | fid := ffid.Aux.(*FFid)
552 | f := fid.F
553 |
554 | if f == nil {
555 | return // otherwise errs in bad walks
556 | }
557 |
558 | if op, ok := (f.ops).(FDestroyOp); ok {
559 | op.FidDestroy(fid)
560 | }
561 | }
562 |
--------------------------------------------------------------------------------
/srv_pipe.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 go9p
6 |
7 | import (
8 | "fmt"
9 | "log"
10 | "os"
11 | "strconv"
12 | "syscall"
13 | )
14 |
15 | type pipeFid struct {
16 | path string
17 | file *os.File
18 | dirs []os.FileInfo
19 | dirents []byte
20 | diroffset uint64
21 | st os.FileInfo
22 | data []uint8
23 | eof bool
24 | }
25 |
26 | type Pipefs struct {
27 | Srv
28 | Root string
29 | }
30 |
31 | func (fid *pipeFid) stat() *Error {
32 | var err error
33 |
34 | fid.st, err = os.Lstat(fid.path)
35 | if err != nil {
36 | return toError(err)
37 | }
38 |
39 | return nil
40 | }
41 |
42 | // Dir is an instantiation of the p.Dir structure
43 | // that can act as a receiver for local methods.
44 | type pipeDir struct {
45 | Dir
46 | }
47 |
48 | func (dir *pipeDir) dotu(path string, d os.FileInfo, upool Users, sysMode *syscall.Stat_t) {
49 | u := upool.Uid2User(int(sysMode.Uid))
50 | g := upool.Gid2Group(int(sysMode.Gid))
51 | dir.Uid = u.Name()
52 | if dir.Uid == "" {
53 | dir.Uid = "none"
54 | }
55 |
56 | dir.Gid = g.Name()
57 | if dir.Gid == "" {
58 | dir.Gid = "none"
59 | }
60 | dir.Muid = "none"
61 | dir.Ext = ""
62 | dir.Uidnum = uint32(u.Id())
63 | dir.Gidnum = uint32(g.Id())
64 | dir.Muidnum = NOUID
65 | if d.Mode()&os.ModeSymlink != 0 {
66 | var err error
67 | dir.Ext, err = os.Readlink(path)
68 | if err != nil {
69 | dir.Ext = ""
70 | }
71 | } else if isBlock(d) {
72 | dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
73 | } else if isChar(d) {
74 | dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
75 | }
76 | }
77 |
78 | func (*Pipefs) ConnOpened(conn *Conn) {
79 | if conn.Srv.Debuglevel > 0 {
80 | log.Println("connected")
81 | }
82 | }
83 |
84 | func (*Pipefs) ConnClosed(conn *Conn) {
85 | if conn.Srv.Debuglevel > 0 {
86 | log.Println("disconnected")
87 | }
88 | }
89 |
90 | func (*Pipefs) FidDestroy(sfid *SrvFid) {
91 | var fid *pipeFid
92 |
93 | if sfid.Aux == nil {
94 | return
95 | }
96 |
97 | fid = sfid.Aux.(*pipeFid)
98 | if fid.file != nil {
99 | fid.file.Close()
100 | }
101 | }
102 |
103 | func (pipe *Pipefs) Attach(req *SrvReq) {
104 | if req.Afid != nil {
105 | req.RespondError(Enoauth)
106 | return
107 | }
108 |
109 | tc := req.Tc
110 | fid := new(pipeFid)
111 | if len(tc.Aname) == 0 {
112 | fid.path = pipe.Root
113 | } else {
114 | fid.path = tc.Aname
115 | }
116 |
117 | req.Fid.Aux = fid
118 | err := fid.stat()
119 | if err != nil {
120 | req.RespondError(err)
121 | return
122 | }
123 |
124 | qid := dir2Qid(fid.st)
125 | req.RespondRattach(qid)
126 | }
127 |
128 | func (*Pipefs) Flush(req *SrvReq) {}
129 |
130 | func (*Pipefs) Walk(req *SrvReq) {
131 | fid := req.Fid.Aux.(*pipeFid)
132 | tc := req.Tc
133 |
134 | err := fid.stat()
135 | if err != nil {
136 | req.RespondError(err)
137 | return
138 | }
139 |
140 | if req.Newfid.Aux == nil {
141 | req.Newfid.Aux = new(pipeFid)
142 | }
143 |
144 | nfid := req.Newfid.Aux.(*pipeFid)
145 | wqids := make([]Qid, len(tc.Wname))
146 | path := fid.path
147 | i := 0
148 | for ; i < len(tc.Wname); i++ {
149 | p := path + "/" + tc.Wname[i]
150 | st, err := os.Lstat(p)
151 | if err != nil {
152 | if i == 0 {
153 | req.RespondError(Enoent)
154 | return
155 | }
156 |
157 | break
158 | }
159 |
160 | wqids[i] = *dir2Qid(st)
161 | path = p
162 | }
163 |
164 | nfid.path = path
165 | req.RespondRwalk(wqids[0:i])
166 | }
167 |
168 | func (*Pipefs) Open(req *SrvReq) {
169 | fid := req.Fid.Aux.(*pipeFid)
170 | tc := req.Tc
171 | err := fid.stat()
172 | if err != nil {
173 | req.RespondError(err)
174 | return
175 | }
176 |
177 | var e error
178 | fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0)
179 | if e != nil {
180 | req.RespondError(toError(e))
181 | return
182 | }
183 |
184 | req.RespondRopen(dir2Qid(fid.st), 0)
185 | }
186 |
187 | func (*Pipefs) Create(req *SrvReq) {
188 | fid := req.Fid.Aux.(*pipeFid)
189 | tc := req.Tc
190 | err := fid.stat()
191 | if err != nil {
192 | req.RespondError(err)
193 | return
194 | }
195 |
196 | path := fid.path + "/" + tc.Name
197 | var e error = nil
198 | var file *os.File = nil
199 | switch {
200 | case tc.Perm&DMDIR != 0:
201 | e = os.Mkdir(path, os.FileMode(tc.Perm&0777))
202 |
203 | case tc.Perm&DMSYMLINK != 0:
204 | e = os.Symlink(tc.Ext, path)
205 |
206 | case tc.Perm&DMLINK != 0:
207 | n, e := strconv.ParseUint(tc.Ext, 10, 0)
208 | if e != nil {
209 | break
210 | }
211 |
212 | ofid := req.Conn.FidGet(uint32(n))
213 | if ofid == nil {
214 | req.RespondError(Eunknownfid)
215 | return
216 | }
217 |
218 | e = os.Link(ofid.Aux.(*pipeFid).path, path)
219 | ofid.DecRef()
220 |
221 | case tc.Perm&DMNAMEDPIPE != 0:
222 | case tc.Perm&DMDEVICE != 0:
223 | req.RespondError(&Error{"not implemented", EIO})
224 | return
225 |
226 | default:
227 | var mode uint32 = tc.Perm & 0777
228 | if req.Conn.Dotu {
229 | if tc.Perm&DMSETUID > 0 {
230 | mode |= syscall.S_ISUID
231 | }
232 | if tc.Perm&DMSETGID > 0 {
233 | mode |= syscall.S_ISGID
234 | }
235 | }
236 | file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode))
237 | }
238 |
239 | if file == nil && e == nil {
240 | file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0)
241 | }
242 |
243 | if e != nil {
244 | req.RespondError(toError(e))
245 | return
246 | }
247 |
248 | fid.path = path
249 | fid.file = file
250 | err = fid.stat()
251 | if err != nil {
252 | req.RespondError(err)
253 | return
254 | }
255 |
256 | req.RespondRcreate(dir2Qid(fid.st), 0)
257 | }
258 |
259 | func min(a, b int) int {
260 | if a < b {
261 | return a
262 | }
263 | return b
264 | }
265 |
266 | func (*Pipefs) Read(req *SrvReq) {
267 | fid := req.Fid.Aux.(*pipeFid)
268 | tc := req.Tc
269 | rc := req.Rc
270 | err := fid.stat()
271 | if err != nil {
272 | req.RespondError(err)
273 | return
274 | }
275 |
276 | InitRread(rc, tc.Count)
277 | var count int
278 | var e error
279 | if fid.st.IsDir() {
280 | if tc.Offset == 0 {
281 | var e error
282 | // If we got here, it was open. Can't really seek
283 | // in most cases, just close and reopen it.
284 | fid.file.Close()
285 | if fid.file, e = os.OpenFile(fid.path, omode2uflags(req.Fid.Omode), 0); e != nil {
286 | req.RespondError(toError(e))
287 | return
288 | }
289 |
290 | if fid.dirs, e = fid.file.Readdir(-1); e != nil {
291 | req.RespondError(toError(e))
292 | return
293 | }
294 | fid.dirents = nil
295 |
296 | for i := 0; i < len(fid.dirs); i++ {
297 | path := fid.path + "/" + fid.dirs[i].Name()
298 | st, _ := dir2Dir(path, fid.dirs[i], req.Conn.Dotu, req.Conn.Srv.Upool)
299 | if st == nil {
300 | continue
301 | }
302 | b := PackDir(st, req.Conn.Dotu)
303 | fid.dirents = append(fid.dirents, b...)
304 | count += len(b)
305 | }
306 | }
307 | switch {
308 | case tc.Offset > uint64(len(fid.dirents)):
309 | count = 0
310 | case len(fid.dirents[tc.Offset:]) > int(tc.Count):
311 | count = int(tc.Count)
312 | default:
313 | count = len(fid.dirents[tc.Offset:])
314 | }
315 |
316 | copy(rc.Data, fid.dirents[tc.Offset:int(tc.Offset)+count])
317 |
318 | } else {
319 | if fid.eof {
320 | req.RespondError(toError(e))
321 | return
322 | }
323 | length := min(len(rc.Data), len(fid.data))
324 | count = length
325 | copy(rc.Data, fid.data[:length])
326 | fid.data = fid.data[length:]
327 | }
328 |
329 | SetRreadCount(rc, uint32(count))
330 | req.Respond()
331 | }
332 |
333 | func (*Pipefs) Write(req *SrvReq) {
334 | fid := req.Fid.Aux.(*pipeFid)
335 | tc := req.Tc
336 | err := fid.stat()
337 | if err != nil {
338 | req.RespondError(err)
339 | return
340 | }
341 |
342 | fid.data = append(fid.data, tc.Data...)
343 |
344 | req.RespondRwrite(uint32(len(tc.Data)))
345 | }
346 |
347 | func (*Pipefs) Clunk(req *SrvReq) { req.RespondRclunk() }
348 |
349 | func (*Pipefs) Remove(req *SrvReq) {
350 | fid := req.Fid.Aux.(*pipeFid)
351 | err := fid.stat()
352 | if err != nil {
353 | req.RespondError(err)
354 | return
355 | }
356 |
357 | e := os.Remove(fid.path)
358 | if e != nil {
359 | req.RespondError(toError(e))
360 | return
361 | }
362 |
363 | req.RespondRremove()
364 | }
365 |
366 | func (*Pipefs) Stat(req *SrvReq) {
367 | fid := req.Fid.Aux.(*pipeFid)
368 | err := fid.stat()
369 | if err != nil {
370 | req.RespondError(err)
371 | return
372 | }
373 |
374 | st, derr := dir2Dir(fid.path, fid.st, req.Conn.Dotu, req.Conn.Srv.Upool)
375 | if st == nil {
376 | req.RespondError(derr)
377 | return
378 | }
379 |
380 | req.RespondRstat(st)
381 | }
382 |
383 | func (*Pipefs) Wstat(req *SrvReq) {
384 | req.RespondError(Eperm)
385 | }
386 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import "fmt"
8 |
9 | // SrvRequest operations. This interface should be implemented by all file servers.
10 | // The operations correspond directly to most of the 9P2000 message types.
11 | type SrvReqOps interface {
12 | Attach(*SrvReq)
13 | Walk(*SrvReq)
14 | Open(*SrvReq)
15 | Create(*SrvReq)
16 | Read(*SrvReq)
17 | Write(*SrvReq)
18 | Clunk(*SrvReq)
19 | Remove(*SrvReq)
20 | Stat(*SrvReq)
21 | Wstat(*SrvReq)
22 | }
23 |
24 | // Respond to the request with Rerror message
25 | func (req *SrvReq) RespondError(err interface{}) {
26 | switch e := err.(type) {
27 | case *Error:
28 | PackRerror(req.Rc, e.Error(), uint32(e.Errornum), req.Conn.Dotu)
29 | case error:
30 | PackRerror(req.Rc, e.Error(), uint32(EIO), req.Conn.Dotu)
31 | default:
32 | PackRerror(req.Rc, fmt.Sprintf("%v", e), uint32(EIO), req.Conn.Dotu)
33 | }
34 |
35 | req.Respond()
36 | }
37 |
38 | // Respond to the request with Rversion message
39 | func (req *SrvReq) RespondRversion(msize uint32, version string) {
40 | err := PackRversion(req.Rc, msize, version)
41 | if err != nil {
42 | req.RespondError(err)
43 | } else {
44 | req.Respond()
45 | }
46 | }
47 |
48 | // Respond to the request with Rauth message
49 | func (req *SrvReq) RespondRauth(aqid *Qid) {
50 | err := PackRauth(req.Rc, aqid)
51 | if err != nil {
52 | req.RespondError(err)
53 | } else {
54 | req.Respond()
55 | }
56 | }
57 |
58 | // Respond to the request with Rflush message
59 | func (req *SrvReq) RespondRflush() {
60 | err := PackRflush(req.Rc)
61 | if err != nil {
62 | req.RespondError(err)
63 | } else {
64 | req.Respond()
65 | }
66 | }
67 |
68 | // Respond to the request with Rattach message
69 | func (req *SrvReq) RespondRattach(aqid *Qid) {
70 | err := PackRattach(req.Rc, aqid)
71 | if err != nil {
72 | req.RespondError(err)
73 | } else {
74 | req.Respond()
75 | }
76 | }
77 |
78 | // Respond to the request with Rwalk message
79 | func (req *SrvReq) RespondRwalk(wqids []Qid) {
80 | err := PackRwalk(req.Rc, wqids)
81 | if err != nil {
82 | req.RespondError(err)
83 | } else {
84 | req.Respond()
85 | }
86 | }
87 |
88 | // Respond to the request with Ropen message
89 | func (req *SrvReq) RespondRopen(qid *Qid, iounit uint32) {
90 | err := PackRopen(req.Rc, qid, iounit)
91 | if err != nil {
92 | req.RespondError(err)
93 | } else {
94 | req.Respond()
95 | }
96 | }
97 |
98 | // Respond to the request with Rcreate message
99 | func (req *SrvReq) RespondRcreate(qid *Qid, iounit uint32) {
100 | err := PackRcreate(req.Rc, qid, iounit)
101 | if err != nil {
102 | req.RespondError(err)
103 | } else {
104 | req.Respond()
105 | }
106 | }
107 |
108 | // Respond to the request with Rread message
109 | func (req *SrvReq) RespondRread(data []byte) {
110 | err := PackRread(req.Rc, data)
111 | if err != nil {
112 | req.RespondError(err)
113 | } else {
114 | req.Respond()
115 | }
116 | }
117 |
118 | // Respond to the request with Rwrite message
119 | func (req *SrvReq) RespondRwrite(count uint32) {
120 | err := PackRwrite(req.Rc, count)
121 | if err != nil {
122 | req.RespondError(err)
123 | } else {
124 | req.Respond()
125 | }
126 | }
127 |
128 | // Respond to the request with Rclunk message
129 | func (req *SrvReq) RespondRclunk() {
130 | err := PackRclunk(req.Rc)
131 | if err != nil {
132 | req.RespondError(err)
133 | } else {
134 | req.Respond()
135 | }
136 | }
137 |
138 | // Respond to the request with Rremove message
139 | func (req *SrvReq) RespondRremove() {
140 | err := PackRremove(req.Rc)
141 | if err != nil {
142 | req.RespondError(err)
143 | } else {
144 | req.Respond()
145 | }
146 | }
147 |
148 | // Respond to the request with Rstat message
149 | func (req *SrvReq) RespondRstat(st *Dir) {
150 | err := PackRstat(req.Rc, st, req.Conn.Dotu)
151 | if err != nil {
152 | req.RespondError(err)
153 | } else {
154 | req.Respond()
155 | }
156 | }
157 |
158 | // Respond to the request with Rwstat message
159 | func (req *SrvReq) RespondRwstat() {
160 | err := PackRwstat(req.Rc)
161 | if err != nil {
162 | req.RespondError(err)
163 | } else {
164 | req.Respond()
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/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 go9provides definitions and functions used to implement
6 | // a 9P2000 file server.
7 | package go9p
8 |
9 | import (
10 | "net"
11 | "sync"
12 | )
13 |
14 | type reqStatus int
15 |
16 | const (
17 | reqFlush reqStatus = (1 << iota) /* request is flushed (no response will be sent) */
18 | reqWork /* goroutine is currently working on it */
19 | reqResponded /* response is already produced */
20 | reqSaved /* no response was produced after the request is worked on */
21 | )
22 |
23 | var Eunknownfid error = &Error{"unknown fid", EINVAL}
24 | var Enoauth error = &Error{"no authentication required", EINVAL}
25 | var Einuse error = &Error{"fid already in use", EINVAL}
26 | var Ebaduse error = &Error{"bad use of fid", EINVAL}
27 | var Eopen error = &Error{"fid already opened", EINVAL}
28 | var Enotdir error = &Error{"not a directory", ENOTDIR}
29 | var Eperm error = &Error{"permission denied", EPERM}
30 | var Etoolarge error = &Error{"i/o count too large", EINVAL}
31 | var Ebadoffset error = &Error{"bad offset in directory read", EINVAL}
32 | var Edirchange error = &Error{"cannot convert between files and directories", EINVAL}
33 | var Enouser error = &Error{"unknown user", EINVAL}
34 | var Enotimpl error = &Error{"not implemented", EINVAL}
35 |
36 | // Authentication operations. The file server should implement them if
37 | // it requires user authentication. The authentication in 9P2000 is
38 | // done by creating special authentication fids and performing I/O
39 | // operations on them. Once the authentication is done, the authentication
40 | // fid can be used by the user to get access to the actual files.
41 | type AuthOps interface {
42 | // AuthInit is called when the user starts the authentication
43 | // process on SrvFid afid. The user that is being authenticated
44 | // is referred by afid.User. The function should return the Qid
45 | // for the authentication file, or an Error if the user can't be
46 | // authenticated
47 | AuthInit(afid *SrvFid, aname string) (*Qid, error)
48 |
49 | // AuthDestroy is called when an authentication fid is destroyed.
50 | AuthDestroy(afid *SrvFid)
51 |
52 | // AuthCheck is called after the authentication process is finished
53 | // when the user tries to attach to the file server. If the function
54 | // returns nil, the authentication was successful and the user has
55 | // permission to access the files.
56 | AuthCheck(fid *SrvFid, afid *SrvFid, aname string) error
57 |
58 | // AuthRead is called when the user attempts to read data from an
59 | // authentication fid.
60 | AuthRead(afid *SrvFid, offset uint64, data []byte) (count int, err error)
61 |
62 | // AuthWrite is called when the user attempts to write data to an
63 | // authentication fid.
64 | AuthWrite(afid *SrvFid, offset uint64, data []byte) (count int, err error)
65 | }
66 |
67 | // Connection operations. These should be implemented if the file server
68 | // needs to be called when a connection is opened or closed.
69 | type ConnOps interface {
70 | ConnOpened(*Conn)
71 | ConnClosed(*Conn)
72 | }
73 |
74 | // SrvFid operations. This interface should be implemented if the file server
75 | // needs to be called when a SrvFid is destroyed.
76 | type SrvFidOps interface {
77 | FidDestroy(*SrvFid)
78 | }
79 |
80 | // Request operations. This interface should be implemented if the file server
81 | // needs to bypass the default request process, or needs to perform certain
82 | // operations before the (any) request is processed, or before (any) response
83 | // sent back to the client.
84 | type SrvReqProcessOps interface {
85 | // Called when a new request is received from the client. If the
86 | // interface is not implemented, (req *SrvReq) srv.Process() method is
87 | // called. If the interface is implemented, it is the user's
88 | // responsibility to call srv.Process. If srv.Process isn't called,
89 | // SrvFid, Afid and Newfid fields in SrvReq are not set, and the SrvReqOps
90 | // methods are not called.
91 | SrvReqProcess(*SrvReq)
92 |
93 | // Called when a request is responded, i.e. when (req *SrvReq)srv.Respond()
94 | // is called and before the response is sent. If the interface is not
95 | // implemented, (req *SrvReq) srv.PostProcess() method is called to finalize
96 | // the request. If the interface is implemented and SrvReqProcess calls
97 | // the srv.Process method, SrvReqRespond should call the srv.PostProcess
98 | // method.
99 | SrvReqRespond(*SrvReq)
100 | }
101 |
102 | // Flush operation. This interface should be implemented if the file server
103 | // can flush pending requests. If the interface is not implemented, requests
104 | // that were passed to the file server implementation won't be flushed.
105 | // The flush method should call the (req *SrvReq) srv.Flush() method if the flush
106 | // was successful so the request can be marked appropriately.
107 | type FlushOp interface {
108 | Flush(*SrvReq)
109 | }
110 |
111 | // The Srv type contains the basic fields used to control the 9P2000
112 | // file server. Each file server implementation should create a value
113 | // of Srv type, initialize the values it cares about and pass the
114 | // struct to the (Srv *) srv.Start(ops) method together with the object
115 | // that implements the file server operations.
116 | type Srv struct {
117 | sync.Mutex
118 | Id string // Used for debugging and stats
119 | Msize uint32 // Maximum size of the 9P2000 messages supported by the server
120 | Dotu bool // If true, the server supports the 9P2000.u extension
121 | Debuglevel int // debug level
122 | Upool Users // Interface for finding users and groups known to the file server
123 | Maxpend int // Maximum pending outgoing requests
124 | Log *Logger
125 |
126 | ops interface{} // operations
127 | conns map[*Conn]*Conn // List of connections
128 | }
129 |
130 | // The Conn type represents a connection from a client to the file server
131 | type Conn struct {
132 | sync.Mutex
133 | Srv *Srv
134 | Msize uint32 // maximum size of 9P2000 messages for the connection
135 | Dotu bool // if true, both the client and the server speak 9P2000.u
136 | Id string // used for debugging and stats
137 | Debuglevel int
138 |
139 | conn net.Conn
140 | fidpool map[uint32]*SrvFid
141 | reqs map[uint16]*SrvReq // all outstanding requests
142 |
143 | reqout chan *SrvReq
144 | rchan chan *Fcall
145 | done chan bool
146 |
147 | // stats
148 | nreqs int // number of requests processed by the server
149 | tsz uint64 // total size of the T messages received
150 | rsz uint64 // total size of the R messages sent
151 | npend int // number of currently pending messages
152 | maxpend int // maximum number of pending messages
153 | nreads int // number of reads
154 | nwrites int // number of writes
155 | }
156 |
157 | // The SrvFid type identifies a file on the file server.
158 | // A new SrvFid is created when the user attaches to the file server (the Attach
159 | // operation), or when Walk-ing to a file. The SrvFid values are created
160 | // automatically by the srv implementation. The SrvFidDestroy operation is called
161 | // when a SrvFid is destroyed.
162 | type SrvFid struct {
163 | sync.Mutex
164 | fid uint32
165 | refcount int
166 | opened bool // True if the SrvFid is opened
167 | Fconn *Conn // Connection the SrvFid belongs to
168 | Omode uint8 // Open mode (O* flags), if the fid is opened
169 | Type uint8 // SrvFid type (QT* flags)
170 | Diroffset uint64 // If directory, the next valid read position
171 | Dirents []byte // If directory, the serialized dirents
172 | User User // The SrvFid's user
173 | Aux interface{} // Can be used by the file server implementation for per-SrvFid data
174 | }
175 |
176 | // The SrvReq type represents a 9P2000 request. Each request has a
177 | // T-message (Tc) and a R-message (Rc). If the SrvReqProcessOps don't
178 | // override the default behavior, the implementation initializes SrvFid,
179 | // Afid and Newfid values and automatically keeps track on when the SrvFids
180 | // should be destroyed.
181 | type SrvReq struct {
182 | sync.Mutex
183 | Tc *Fcall // Incoming 9P2000 message
184 | Rc *Fcall // Outgoing 9P2000 response
185 | Fid *SrvFid // The SrvFid value for all messages that contain fid[4]
186 | Afid *SrvFid // The SrvFid value for the messages that contain afid[4] (Tauth and Tattach)
187 | Newfid *SrvFid // The SrvFid value for the messages that contain newfid[4] (Twalk)
188 | Conn *Conn // Connection that the request belongs to
189 |
190 | status reqStatus
191 | flushreq *SrvReq
192 | prev, next *SrvReq
193 | }
194 |
195 | // The Start method should be called once the file server implementor
196 | // initializes the Srv struct with the preferred values. It sets default
197 | // values to the fields that are not initialized and creates the goroutines
198 | // required for the server's operation. The method receives an empty
199 | // interface value, ops, that should implement the interfaces the file server is
200 | // interested in. Ops must implement the SrvReqOps interface.
201 | func (srv *Srv) Start(ops interface{}) bool {
202 | if _, ok := (ops).(SrvReqOps); !ok {
203 | return false
204 | }
205 |
206 | srv.ops = ops
207 | if srv.Upool == nil {
208 | srv.Upool = OsUsers
209 | }
210 |
211 | if srv.Msize < IOHDRSZ {
212 | srv.Msize = MSIZE
213 | }
214 |
215 | if srv.Log == nil {
216 | srv.Log = NewLogger(1024)
217 | }
218 |
219 | if sop, ok := (interface{}(srv)).(StatsOps); ok {
220 | sop.statsRegister()
221 | }
222 |
223 | return true
224 | }
225 |
226 | func (srv *Srv) String() string {
227 | return srv.Id
228 | }
229 |
230 | func (req *SrvReq) process() {
231 | req.Lock()
232 | flushed := (req.status & reqFlush) != 0
233 | if !flushed {
234 | req.status |= reqWork
235 | }
236 | req.Unlock()
237 |
238 | if flushed {
239 | req.Respond()
240 | }
241 |
242 | if rop, ok := (req.Conn.Srv.ops).(SrvReqProcessOps); ok {
243 | rop.SrvReqProcess(req)
244 | } else {
245 | req.Process()
246 | }
247 |
248 | req.Lock()
249 | req.status &= ^reqWork
250 | if !(req.status&reqResponded != 0) {
251 | req.status |= reqSaved
252 | }
253 | req.Unlock()
254 | }
255 |
256 | // Performs the default processing of a request. Initializes
257 | // the SrvFid, Afid and Newfid fields and calls the appropriate
258 | // SrvReqOps operation for the message. The file server implementer
259 | // should call it only if the file server implements the SrvReqProcessOps
260 | // within the SrvReqProcess operation.
261 | func (req *SrvReq) Process() {
262 | conn := req.Conn
263 | srv := conn.Srv
264 | tc := req.Tc
265 |
266 | if tc.Fid != NOFID && tc.Type != Tattach {
267 | srv.Lock()
268 | req.Fid = conn.FidGet(tc.Fid)
269 | srv.Unlock()
270 | if req.Fid == nil {
271 | req.RespondError(Eunknownfid)
272 | return
273 | }
274 | }
275 |
276 | switch req.Tc.Type {
277 | default:
278 | req.RespondError(&Error{"unknown message type", EINVAL})
279 |
280 | case Tversion:
281 | srv.version(req)
282 |
283 | case Tauth:
284 | srv.auth(req)
285 |
286 | case Tattach:
287 | srv.attach(req)
288 |
289 | case Tflush:
290 | srv.flush(req)
291 |
292 | case Twalk:
293 | srv.walk(req)
294 |
295 | case Topen:
296 | srv.open(req)
297 |
298 | case Tcreate:
299 | srv.create(req)
300 |
301 | case Tread:
302 | srv.read(req)
303 |
304 | case Twrite:
305 | srv.write(req)
306 |
307 | case Tclunk:
308 | srv.clunk(req)
309 |
310 | case Tremove:
311 | srv.remove(req)
312 |
313 | case Tstat:
314 | srv.stat(req)
315 |
316 | case Twstat:
317 | srv.wstat(req)
318 | }
319 | }
320 |
321 | // Performs the post processing required if the (*SrvReq) Process() method
322 | // is called for a request. The file server implementer should call it
323 | // only if the file server implements the SrvReqProcessOps within the
324 | // SrvReqRespond operation.
325 | func (req *SrvReq) PostProcess() {
326 | srv := req.Conn.Srv
327 |
328 | /* call the post-handlers (if needed) */
329 | switch req.Tc.Type {
330 | case Tauth:
331 | srv.authPost(req)
332 |
333 | case Tattach:
334 | srv.attachPost(req)
335 |
336 | case Twalk:
337 | srv.walkPost(req)
338 |
339 | case Topen:
340 | srv.openPost(req)
341 |
342 | case Tcreate:
343 | srv.createPost(req)
344 |
345 | case Tread:
346 | srv.readPost(req)
347 |
348 | case Tclunk:
349 | srv.clunkPost(req)
350 |
351 | case Tremove:
352 | srv.removePost(req)
353 | }
354 |
355 | if req.Fid != nil {
356 | req.Fid.DecRef()
357 | req.Fid = nil
358 | }
359 |
360 | if req.Afid != nil {
361 | req.Afid.DecRef()
362 | req.Afid = nil
363 | }
364 |
365 | if req.Newfid != nil {
366 | req.Newfid.DecRef()
367 | req.Newfid = nil
368 | }
369 | }
370 |
371 | // The Respond method sends response back to the client. The req.Rc value
372 | // should be initialized and contain valid 9P2000 message. In most cases
373 | // the file server implementer shouldn't call this method directly. Instead
374 | // one of the RespondR* methods should be used.
375 | func (req *SrvReq) Respond() {
376 | var flushreqs *SrvReq
377 |
378 | conn := req.Conn
379 | req.Lock()
380 | status := req.status
381 | req.status |= reqResponded
382 | req.status &= ^reqWork
383 | req.Unlock()
384 |
385 | if (status & reqResponded) != 0 {
386 | return
387 | }
388 |
389 | /* remove the request and all requests flushing it */
390 | conn.Lock()
391 | nextreq := req.prev
392 | if nextreq != nil {
393 | nextreq.next = nil
394 | // if there are flush requests, move them to the next request
395 | if req.flushreq != nil {
396 | var p *SrvReq = nil
397 | r := nextreq.flushreq
398 | for ; r != nil; p, r = r, r.flushreq {
399 | }
400 |
401 | if p == nil {
402 | nextreq.flushreq = req.flushreq
403 | } else {
404 | nextreq = req.flushreq
405 | }
406 | }
407 |
408 | flushreqs = nil
409 | } else {
410 | delete(conn.reqs, req.Tc.Tag)
411 | flushreqs = req.flushreq
412 | }
413 | conn.Unlock()
414 |
415 | if rop, ok := (req.Conn.Srv.ops).(SrvReqProcessOps); ok {
416 | rop.SrvReqRespond(req)
417 | } else {
418 | req.PostProcess()
419 | }
420 |
421 | if (status & reqFlush) == 0 {
422 | conn.reqout <- req
423 | }
424 |
425 | // process the next request with the same tag (if available)
426 | if nextreq != nil {
427 | go nextreq.process()
428 | }
429 |
430 | // respond to the flush messages
431 | // can't send the responses directly to conn.reqout, because the
432 | // the flushes may be in a tag group too
433 | for freq := flushreqs; freq != nil; freq = freq.flushreq {
434 | freq.Respond()
435 | }
436 | }
437 |
438 | // Should be called to cancel a request. Should only be called
439 | // from the Flush operation if the FlushOp is implemented.
440 | func (req *SrvReq) Flush() {
441 | req.Lock()
442 | req.status |= reqFlush
443 | req.Unlock()
444 | req.Respond()
445 | }
446 |
447 | // Lookup a SrvFid struct based on the 32-bit identifier sent over the wire.
448 | // Returns nil if the fid is not found. Increases the reference count of
449 | // the returned fid. The user is responsible to call DecRef once it no
450 | // longer needs it.
451 | func (conn *Conn) FidGet(fidno uint32) *SrvFid {
452 | conn.Lock()
453 | fid, present := conn.fidpool[fidno]
454 | conn.Unlock()
455 | if present {
456 | fid.IncRef()
457 | }
458 |
459 | return fid
460 | }
461 |
462 | // Creates a new SrvFid struct for the fidno integer. Returns nil
463 | // if the SrvFid for that number already exists. The returned fid
464 | // has reference count set to 1.
465 | func (conn *Conn) FidNew(fidno uint32) *SrvFid {
466 | conn.Lock()
467 | _, present := conn.fidpool[fidno]
468 | if present {
469 | conn.Unlock()
470 | return nil
471 | }
472 |
473 | fid := new(SrvFid)
474 | fid.fid = fidno
475 | fid.refcount = 1
476 | fid.Fconn = conn
477 | conn.fidpool[fidno] = fid
478 | conn.Unlock()
479 |
480 | return fid
481 | }
482 |
483 | func (conn *Conn) String() string {
484 | return conn.Srv.Id + "/" + conn.Id
485 | }
486 |
487 | // Increase the reference count for the fid.
488 | func (fid *SrvFid) IncRef() {
489 | fid.Lock()
490 | fid.refcount++
491 | fid.Unlock()
492 | }
493 |
494 | // Decrease the reference count for the fid. When the
495 | // reference count reaches 0, the fid is no longer valid.
496 | func (fid *SrvFid) DecRef() {
497 | fid.Lock()
498 | fid.refcount--
499 | n := fid.refcount
500 | fid.Unlock()
501 |
502 | if n > 0 {
503 | return
504 | }
505 |
506 | conn := fid.Fconn
507 | conn.Lock()
508 | delete(conn.fidpool, fid.fid)
509 | conn.Unlock()
510 |
511 | if fop, ok := (conn.Srv.ops).(SrvFidOps); ok {
512 | fop.FidDestroy(fid)
513 | }
514 | }
515 |
--------------------------------------------------------------------------------
/srv_stats_http.go:
--------------------------------------------------------------------------------
1 | // +build httpstats
2 |
3 | package go9p
4 |
5 | import (
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "sync"
10 | )
11 |
12 | var mux sync.RWMutex
13 | var stat map[string]http.Handler
14 | var httponce sync.Once
15 |
16 | func register(s string, h http.Handler) {
17 | mux.Lock()
18 | if stat == nil {
19 | stat = make(map[string]http.Handler)
20 | }
21 |
22 | if h == nil {
23 | delete(stat, s)
24 | } else {
25 | stat[s] = h
26 | }
27 | mux.Unlock()
28 | }
29 | func (srv *Srv) statsRegister() {
30 | httponce.Do(func() {
31 | http.HandleFunc("/go9p/", StatsHandler)
32 | go http.ListenAndServe(":6060", nil)
33 | })
34 |
35 | register("/go9p/srv/"+srv.Id, srv)
36 | }
37 |
38 | func (srv *Srv) statsUnregister() {
39 | register("/go9p/srv/"+srv.Id, nil)
40 | }
41 |
42 | func (srv *Srv) ServeHTTP(c http.ResponseWriter, r *http.Request) {
43 | io.WriteString(c, fmt.Sprintf("Server %s
", srv.Id))
44 | defer io.WriteString(c, "")
45 |
46 | // connections
47 | io.WriteString(c, "Connections
")
48 | srv.Lock()
49 | defer srv.Unlock()
50 | if len(srv.conns) == 0 {
51 | io.WriteString(c, "none")
52 | return
53 | }
54 |
55 | for _, conn := range srv.conns {
56 | io.WriteString(c, fmt.Sprintf("%s
", srv.Id, conn.Id, conn.Id))
57 | }
58 | }
59 |
60 | func (conn *Conn) statsRegister() {
61 | register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, conn)
62 | }
63 |
64 | func (conn *Conn) statsUnregister() {
65 | register("/go9p/srv/"+conn.Srv.Id+"/conn/"+conn.Id, nil)
66 | }
67 |
68 | func (conn *Conn) ServeHTTP(c http.ResponseWriter, r *http.Request) {
69 | io.WriteString(c, fmt.Sprintf("
Connection %s/%s
", conn.Srv.Id, conn.Id))
70 | defer io.WriteString(c, "")
71 |
72 | // statistics
73 | conn.Lock()
74 | io.WriteString(c, fmt.Sprintf("Number of processed requests: %d", conn.nreqs))
75 | io.WriteString(c, fmt.Sprintf("
Sent %v bytes", conn.rsz))
76 | io.WriteString(c, fmt.Sprintf("
Received %v bytes", conn.tsz))
77 | io.WriteString(c, fmt.Sprintf("
Pending requests: %d max %d", conn.npend, conn.maxpend))
78 | io.WriteString(c, fmt.Sprintf("
Number of reads: %d", conn.nreads))
79 | io.WriteString(c, fmt.Sprintf("
Number of writes: %d", conn.nwrites))
80 | conn.Unlock()
81 |
82 | // fcalls
83 | if conn.Debuglevel&DbgLogFcalls != 0 {
84 | fs := conn.Srv.Log.Filter(conn, DbgLogFcalls)
85 | io.WriteString(c, fmt.Sprintf("
Last %d 9P messages
", len(fs)))
86 | for i, l := range fs {
87 | fc := l.Data.(*Fcall)
88 | if fc.Type == 0 {
89 | continue
90 | }
91 |
92 | lbl := ""
93 | if fc.Type%2 == 0 {
94 | // try to find the response for the T message
95 | for j := i + 1; j < len(fs); j++ {
96 | rc := fs[j].Data.(*Fcall)
97 | if rc.Tag == fc.Tag {
98 | lbl = fmt.Sprintf("➴", j)
99 | break
100 | }
101 | }
102 | } else {
103 | // try to find the request for the R message
104 | for j := i - 1; j >= 0; j-- {
105 | tc := fs[j].Data.(*Fcall)
106 | if tc.Tag == fc.Tag {
107 | lbl = fmt.Sprintf("➶", j)
108 | break
109 | }
110 | }
111 | }
112 |
113 | io.WriteString(c, fmt.Sprintf("
%d: %s%s", i, i, fc, lbl))
114 | }
115 | }
116 | }
117 |
118 | func StatsHandler(c http.ResponseWriter, r *http.Request) {
119 | mux.RLock()
120 | if v, ok := stat[r.URL.Path]; ok {
121 | v.ServeHTTP(c, r)
122 | } else if r.URL.Path == "/go9p/" {
123 | io.WriteString(c, fmt.Sprintf("
On offer:
"))
124 | for v := range stat {
125 | io.WriteString(c, fmt.Sprintf("%s
", v, v))
126 | }
127 | io.WriteString(c, "")
128 | }
129 | mux.RUnlock()
130 | }
131 |
--------------------------------------------------------------------------------
/stats.go:
--------------------------------------------------------------------------------
1 | package go9p
2 |
3 | type StatsOps interface {
4 | statsRegister()
5 | statsUnregister()
6 | }
7 |
--------------------------------------------------------------------------------
/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 go9p
6 |
7 | import (
8 | "flag"
9 | "fmt"
10 | "io"
11 | "log"
12 | "os"
13 | "os/user"
14 | "path"
15 | "sort"
16 | "strconv"
17 | "strings"
18 | "syscall"
19 | "time"
20 | )
21 |
22 | type ufsFid struct {
23 | path string
24 | file *os.File
25 | dirs []os.FileInfo
26 | direntends []int
27 | dirents []byte
28 | diroffset uint64
29 | st os.FileInfo
30 | }
31 |
32 | type Ufs struct {
33 | Srv
34 | Root string
35 | }
36 |
37 | func toError(err error) *Error {
38 | var ecode uint32
39 |
40 | ename := err.Error()
41 | if e, ok := err.(syscall.Errno); ok {
42 | ecode = uint32(e)
43 | } else {
44 | ecode = EIO
45 | }
46 |
47 | return &Error{ename, ecode}
48 | }
49 |
50 | // IsBlock reports if the file is a block device
51 | func isBlock(d os.FileInfo) bool {
52 | stat := d.Sys().(*syscall.Stat_t)
53 | return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
54 | }
55 |
56 | // IsChar reports if the file is a character device
57 | func isChar(d os.FileInfo) bool {
58 | stat := d.Sys().(*syscall.Stat_t)
59 | return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
60 | }
61 |
62 | func (fid *ufsFid) stat() *Error {
63 | var err error
64 |
65 | fid.st, err = os.Lstat(fid.path)
66 | if err != nil {
67 | return toError(err)
68 | }
69 |
70 | return nil
71 | }
72 |
73 | func omode2uflags(mode uint8) int {
74 | ret := int(0)
75 | switch mode & 3 {
76 | case OREAD:
77 | ret = os.O_RDONLY
78 | break
79 |
80 | case ORDWR:
81 | ret = os.O_RDWR
82 | break
83 |
84 | case OWRITE:
85 | ret = os.O_WRONLY
86 | break
87 |
88 | case OEXEC:
89 | ret = os.O_RDONLY
90 | break
91 | }
92 |
93 | if mode&OTRUNC != 0 {
94 | ret |= os.O_TRUNC
95 | }
96 |
97 | return ret
98 | }
99 |
100 | func dir2Qid(d os.FileInfo) *Qid {
101 | var qid Qid
102 |
103 | qid.Path = d.Sys().(*syscall.Stat_t).Ino
104 | qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
105 | qid.Type = dir2QidType(d)
106 |
107 | return &qid
108 | }
109 |
110 | func dir2QidType(d os.FileInfo) uint8 {
111 | ret := uint8(0)
112 | if d.IsDir() {
113 | ret |= QTDIR
114 | }
115 |
116 | if d.Mode()&os.ModeSymlink != 0 {
117 | ret |= QTSYMLINK
118 | }
119 |
120 | return ret
121 | }
122 |
123 | func dir2Npmode(d os.FileInfo, dotu bool) uint32 {
124 | ret := uint32(d.Mode() & 0777)
125 | if d.IsDir() {
126 | ret |= DMDIR
127 | }
128 |
129 | if dotu {
130 | mode := d.Mode()
131 | if mode&os.ModeSymlink != 0 {
132 | ret |= DMSYMLINK
133 | }
134 |
135 | if mode&os.ModeSocket != 0 {
136 | ret |= DMSOCKET
137 | }
138 |
139 | if mode&os.ModeNamedPipe != 0 {
140 | ret |= DMNAMEDPIPE
141 | }
142 |
143 | if mode&os.ModeDevice != 0 {
144 | ret |= DMDEVICE
145 | }
146 |
147 | if mode&os.ModeSetuid != 0 {
148 | ret |= DMSETUID
149 | }
150 |
151 | if mode&os.ModeSetgid != 0 {
152 | ret |= DMSETGID
153 | }
154 | }
155 |
156 | return ret
157 | }
158 |
159 | // Dir is an instantiation of the p.Dir structure
160 | // that can act as a receiver for local methods.
161 | type ufsDir struct {
162 | Dir
163 | }
164 |
165 | func dir2Dir(path string, d os.FileInfo, dotu bool, upool Users) (*Dir, error) {
166 | if r := recover(); r != nil {
167 | fmt.Print("stat failed: ", r)
168 | return nil, &os.PathError{"dir2Dir", path, nil}
169 | }
170 | sysif := d.Sys()
171 | if sysif == nil {
172 | return nil, &os.PathError{"dir2Dir: sysif is nil", path, nil}
173 | }
174 | sysMode := sysif.(*syscall.Stat_t)
175 |
176 | dir := new(ufsDir)
177 | dir.Qid = *dir2Qid(d)
178 | dir.Mode = dir2Npmode(d, dotu)
179 | dir.Atime = uint32(0 /*atime(sysMode).Unix()*/)
180 | dir.Mtime = uint32(d.ModTime().Unix())
181 | dir.Length = uint64(d.Size())
182 | dir.Name = path[strings.LastIndex(path, "/")+1:]
183 |
184 | if dotu {
185 | dir.dotu(path, d, upool, sysMode)
186 | return &dir.Dir, nil
187 | }
188 |
189 | unixUid := int(sysMode.Uid)
190 | unixGid := int(sysMode.Gid)
191 | dir.Uid = strconv.Itoa(unixUid)
192 | dir.Gid = strconv.Itoa(unixGid)
193 |
194 | // BUG(akumar): LookupId will never find names for
195 | // groups, as it only operates on user ids.
196 | u, err := user.LookupId(dir.Uid)
197 | if err == nil {
198 | dir.Uid = u.Username
199 | }
200 | g, err := user.LookupId(dir.Gid)
201 | if err == nil {
202 | dir.Gid = g.Username
203 | }
204 |
205 | /* For Akaros, we use the Muid as the link value. */
206 | if *Akaros && (d.Mode()&os.ModeSymlink != 0) {
207 | dir.Muid, err = os.Readlink(path)
208 | if err == nil {
209 | dir.Mode |= DMSYMLINK
210 | }
211 | }
212 | return &dir.Dir, nil
213 | }
214 |
215 | func (dir *ufsDir) dotu(path string, d os.FileInfo, upool Users, sysMode *syscall.Stat_t) {
216 | u := upool.Uid2User(int(sysMode.Uid))
217 | g := upool.Gid2Group(int(sysMode.Gid))
218 | dir.Uid = u.Name()
219 | if dir.Uid == "" {
220 | dir.Uid = "none"
221 | }
222 |
223 | dir.Gid = g.Name()
224 | if dir.Gid == "" {
225 | dir.Gid = "none"
226 | }
227 | dir.Muid = "none"
228 | dir.Ext = ""
229 | dir.Uidnum = uint32(u.Id())
230 | dir.Gidnum = uint32(g.Id())
231 | dir.Muidnum = NOUID
232 | if d.Mode()&os.ModeSymlink != 0 {
233 | var err error
234 | dir.Ext, err = os.Readlink(path)
235 | if err != nil {
236 | dir.Ext = ""
237 | }
238 | } else if isBlock(d) {
239 | dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
240 | } else if isChar(d) {
241 | dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
242 | }
243 | }
244 |
245 | func (*Ufs) ConnOpened(conn *Conn) {
246 | if conn.Srv.Debuglevel > 0 {
247 | log.Println("connected")
248 | }
249 | }
250 |
251 | func (*Ufs) ConnClosed(conn *Conn) {
252 | if conn.Srv.Debuglevel > 0 {
253 | log.Println("disconnected")
254 | }
255 | }
256 |
257 | func (*Ufs) FidDestroy(sfid *SrvFid) {
258 | var fid *ufsFid
259 |
260 | if sfid.Aux == nil {
261 | return
262 | }
263 |
264 | fid = sfid.Aux.(*ufsFid)
265 | if fid.file != nil {
266 | fid.file.Close()
267 | }
268 | }
269 |
270 | func (ufs *Ufs) Attach(req *SrvReq) {
271 | if req.Afid != nil {
272 | req.RespondError(Enoauth)
273 | return
274 | }
275 |
276 | tc := req.Tc
277 | fid := new(ufsFid)
278 | // You can think of the ufs.Root as a 'chroot' of a sort.
279 | // clients attach are not allowed to go outside the
280 | // directory represented by ufs.Root
281 | fid.path = path.Join(ufs.Root, tc.Aname)
282 |
283 | req.Fid.Aux = fid
284 | err := fid.stat()
285 | if err != nil {
286 | req.RespondError(err)
287 | return
288 | }
289 |
290 | qid := dir2Qid(fid.st)
291 | req.RespondRattach(qid)
292 | }
293 |
294 | func (*Ufs) Flush(req *SrvReq) {}
295 |
296 | func (*Ufs) Walk(req *SrvReq) {
297 | fid := req.Fid.Aux.(*ufsFid)
298 | tc := req.Tc
299 |
300 | err := fid.stat()
301 | if err != nil {
302 | req.RespondError(err)
303 | return
304 | }
305 |
306 | if req.Newfid.Aux == nil {
307 | req.Newfid.Aux = new(ufsFid)
308 | }
309 |
310 | nfid := req.Newfid.Aux.(*ufsFid)
311 | wqids := make([]Qid, len(tc.Wname))
312 | path := fid.path
313 | i := 0
314 | for ; i < len(tc.Wname); i++ {
315 | p := path + "/" + tc.Wname[i]
316 | st, err := os.Lstat(p)
317 | if err != nil {
318 | if i == 0 {
319 | req.RespondError(Enoent)
320 | return
321 | }
322 |
323 | break
324 | }
325 |
326 | wqids[i] = *dir2Qid(st)
327 | path = p
328 | }
329 |
330 | nfid.path = path
331 | req.RespondRwalk(wqids[0:i])
332 | }
333 |
334 | func (*Ufs) Open(req *SrvReq) {
335 | fid := req.Fid.Aux.(*ufsFid)
336 | tc := req.Tc
337 | err := fid.stat()
338 | if err != nil {
339 | req.RespondError(err)
340 | return
341 | }
342 |
343 | var e error
344 | fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0)
345 | if e != nil {
346 | req.RespondError(toError(e))
347 | return
348 | }
349 |
350 | req.RespondRopen(dir2Qid(fid.st), 0)
351 | }
352 |
353 | func (*Ufs) Create(req *SrvReq) {
354 | fid := req.Fid.Aux.(*ufsFid)
355 | tc := req.Tc
356 | err := fid.stat()
357 | if err != nil {
358 | req.RespondError(err)
359 | return
360 | }
361 |
362 | path := fid.path + "/" + tc.Name
363 | var e error = nil
364 | var file *os.File = nil
365 | switch {
366 | case tc.Perm&DMDIR != 0:
367 | e = os.Mkdir(path, os.FileMode(tc.Perm&0777))
368 |
369 | case tc.Perm&DMSYMLINK != 0:
370 | e = os.Symlink(tc.Ext, path)
371 |
372 | case tc.Perm&DMLINK != 0:
373 | n, e := strconv.ParseUint(tc.Ext, 10, 0)
374 | if e != nil {
375 | break
376 | }
377 |
378 | ofid := req.Conn.FidGet(uint32(n))
379 | if ofid == nil {
380 | req.RespondError(Eunknownfid)
381 | return
382 | }
383 |
384 | e = os.Link(ofid.Aux.(*ufsFid).path, path)
385 | ofid.DecRef()
386 |
387 | case tc.Perm&DMNAMEDPIPE != 0:
388 | case tc.Perm&DMDEVICE != 0:
389 | req.RespondError(&Error{"not implemented", EIO})
390 | return
391 |
392 | default:
393 | var mode uint32 = tc.Perm & 0777
394 | if req.Conn.Dotu {
395 | if tc.Perm&DMSETUID > 0 {
396 | mode |= syscall.S_ISUID
397 | }
398 | if tc.Perm&DMSETGID > 0 {
399 | mode |= syscall.S_ISGID
400 | }
401 | }
402 | file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode))
403 | }
404 |
405 | if file == nil && e == nil {
406 | file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0)
407 | }
408 |
409 | if e != nil {
410 | req.RespondError(toError(e))
411 | return
412 | }
413 |
414 | fid.path = path
415 | fid.file = file
416 | err = fid.stat()
417 | if err != nil {
418 | req.RespondError(err)
419 | return
420 | }
421 |
422 | req.RespondRcreate(dir2Qid(fid.st), 0)
423 | }
424 |
425 | func (*Ufs) Read(req *SrvReq) {
426 | fid := req.Fid.Aux.(*ufsFid)
427 | tc := req.Tc
428 | rc := req.Rc
429 | err := fid.stat()
430 | if err != nil {
431 | req.RespondError(err)
432 | return
433 | }
434 |
435 | InitRread(rc, tc.Count)
436 | var count int
437 | var e error
438 | if fid.st.IsDir() {
439 | if tc.Offset == 0 {
440 | var e error
441 | // If we got here, it was open. Can't really seek
442 | // in most cases, just close and reopen it.
443 | fid.file.Close()
444 | if fid.file, e = os.OpenFile(fid.path, omode2uflags(req.Fid.Omode), 0); e != nil {
445 | req.RespondError(toError(e))
446 | return
447 | }
448 |
449 | if fid.dirs, e = fid.file.Readdir(-1); e != nil {
450 | req.RespondError(toError(e))
451 | return
452 | }
453 |
454 | fid.dirents = nil
455 | fid.direntends = nil
456 | for i := 0; i < len(fid.dirs); i++ {
457 | path := fid.path + "/" + fid.dirs[i].Name()
458 | st, _ := dir2Dir(path, fid.dirs[i], req.Conn.Dotu, req.Conn.Srv.Upool)
459 | if st == nil {
460 | continue
461 | }
462 | b := PackDir(st, req.Conn.Dotu)
463 | fid.dirents = append(fid.dirents, b...)
464 | count += len(b)
465 | fid.direntends = append(fid.direntends, count)
466 | }
467 | }
468 |
469 | switch {
470 | case tc.Offset > uint64(len(fid.dirents)):
471 | count = 0
472 | case len(fid.dirents[tc.Offset:]) > int(tc.Count):
473 | count = int(tc.Count)
474 | default:
475 | count = len(fid.dirents[tc.Offset:])
476 | }
477 |
478 | if !*Akaros {
479 | nextend := sort.SearchInts(fid.direntends, int(tc.Offset)+count)
480 | if nextend < len(fid.direntends) {
481 | if fid.direntends[nextend] > int(tc.Offset)+count {
482 | if nextend > 0 {
483 | count = fid.direntends[nextend-1] - int(tc.Offset)
484 | } else {
485 | count = 0
486 | }
487 | }
488 | }
489 | if count == 0 && int(tc.Offset) < len(fid.dirents) && len(fid.dirents) > 0 {
490 | req.RespondError(&Error{"too small read size for dir entry", EINVAL})
491 | return
492 | }
493 | }
494 |
495 | copy(rc.Data, fid.dirents[tc.Offset:int(tc.Offset)+count])
496 |
497 | } else {
498 | count, e = fid.file.ReadAt(rc.Data, int64(tc.Offset))
499 | if e != nil && e != io.EOF {
500 | req.RespondError(toError(e))
501 | return
502 | }
503 |
504 | }
505 |
506 | SetRreadCount(rc, uint32(count))
507 | req.Respond()
508 | }
509 |
510 | func (*Ufs) Write(req *SrvReq) {
511 | fid := req.Fid.Aux.(*ufsFid)
512 | tc := req.Tc
513 | err := fid.stat()
514 | if err != nil {
515 | req.RespondError(err)
516 | return
517 | }
518 |
519 | n, e := fid.file.WriteAt(tc.Data, int64(tc.Offset))
520 | if e != nil {
521 | req.RespondError(toError(e))
522 | return
523 | }
524 |
525 | req.RespondRwrite(uint32(n))
526 | }
527 |
528 | func (*Ufs) Clunk(req *SrvReq) { req.RespondRclunk() }
529 |
530 | func (*Ufs) Remove(req *SrvReq) {
531 | fid := req.Fid.Aux.(*ufsFid)
532 | err := fid.stat()
533 | if err != nil {
534 | req.RespondError(err)
535 | return
536 | }
537 |
538 | e := os.Remove(fid.path)
539 | if e != nil {
540 | req.RespondError(toError(e))
541 | return
542 | }
543 |
544 | req.RespondRremove()
545 | }
546 |
547 | func (*Ufs) Stat(req *SrvReq) {
548 | fid := req.Fid.Aux.(*ufsFid)
549 | err := fid.stat()
550 | if err != nil {
551 | req.RespondError(err)
552 | return
553 | }
554 |
555 | st, derr := dir2Dir(fid.path, fid.st, req.Conn.Dotu, req.Conn.Srv.Upool)
556 | if st == nil {
557 | req.RespondError(derr)
558 | return
559 | }
560 |
561 | req.RespondRstat(st)
562 | }
563 |
564 | func lookup(uid string, group bool) (uint32, *Error) {
565 | if uid == "" {
566 | return NOUID, nil
567 | }
568 | usr, e := user.Lookup(uid)
569 | if e != nil {
570 | return NOUID, toError(e)
571 | }
572 | conv := usr.Uid
573 | if group {
574 | conv = usr.Gid
575 | }
576 | u, e := strconv.Atoi(conv)
577 | if e != nil {
578 | return NOUID, toError(e)
579 | }
580 | return uint32(u), nil
581 | }
582 |
583 | func (u *Ufs) Wstat(req *SrvReq) {
584 | fid := req.Fid.Aux.(*ufsFid)
585 | err := fid.stat()
586 | if err != nil {
587 | req.RespondError(err)
588 | return
589 | }
590 |
591 | dir := &req.Tc.Dir
592 | if dir.Mode != 0xFFFFFFFF {
593 | mode := dir.Mode & 0777
594 | if req.Conn.Dotu {
595 | if dir.Mode&DMSETUID > 0 {
596 | mode |= syscall.S_ISUID
597 | }
598 | if dir.Mode&DMSETGID > 0 {
599 | mode |= syscall.S_ISGID
600 | }
601 | }
602 | e := os.Chmod(fid.path, os.FileMode(mode))
603 | if e != nil {
604 | req.RespondError(toError(e))
605 | return
606 | }
607 | }
608 |
609 | uid, gid := NOUID, NOUID
610 | if req.Conn.Dotu {
611 | uid = dir.Uidnum
612 | gid = dir.Gidnum
613 | }
614 |
615 | // Try to find local uid, gid by name.
616 | if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
617 | uid, err = lookup(dir.Uid, false)
618 | if err != nil {
619 | req.RespondError(err)
620 | return
621 | }
622 |
623 | // BUG(akumar): Lookup will never find gids
624 | // corresponding to group names, because
625 | // it only operates on user names.
626 | gid, err = lookup(dir.Gid, true)
627 | if err != nil {
628 | req.RespondError(err)
629 | return
630 | }
631 | }
632 |
633 | if uid != NOUID || gid != NOUID {
634 | e := os.Chown(fid.path, int(uid), int(gid))
635 | if e != nil {
636 | req.RespondError(toError(e))
637 | return
638 | }
639 | }
640 |
641 | if dir.Name != "" {
642 | fmt.Printf("Rename %s to %s\n", fid.path, dir.Name)
643 | // if first char is / it is relative to root, else relative to
644 | // cwd.
645 | var destpath string
646 | if dir.Name[0] == '/' {
647 | destpath = path.Join(u.Root, dir.Name)
648 | fmt.Printf("/ results in %s\n", destpath)
649 | } else {
650 | fiddir, _ := path.Split(fid.path)
651 | destpath = path.Join(fiddir, dir.Name)
652 | fmt.Printf("rel results in %s\n", destpath)
653 | }
654 | err := syscall.Rename(fid.path, destpath)
655 | fmt.Printf("rename %s to %s gets %v\n", fid.path, destpath, err)
656 | if err != nil {
657 | req.RespondError(toError(err))
658 | return
659 | }
660 | fid.path = destpath
661 | }
662 |
663 | if dir.Length != 0xFFFFFFFFFFFFFFFF {
664 | e := os.Truncate(fid.path, int64(dir.Length))
665 | if e != nil {
666 | req.RespondError(toError(e))
667 | return
668 | }
669 | }
670 |
671 | // If either mtime or atime need to be changed, then
672 | // we must change both.
673 | if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
674 | mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
675 | if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
676 | st, e := os.Stat(fid.path)
677 | if e != nil {
678 | req.RespondError(toError(e))
679 | return
680 | }
681 | switch cmt {
682 | case true:
683 | mt = st.ModTime()
684 | default:
685 | //at = time.Time(0)//atime(st.Sys().(*syscall.Stat_t))
686 | }
687 | }
688 | e := os.Chtimes(fid.path, at, mt)
689 | if e != nil {
690 | req.RespondError(toError(e))
691 | return
692 | }
693 | }
694 |
695 | req.RespondRwstat()
696 | }
697 |
698 | /* enables "Akaros" capabilities, which right now means
699 | * a sane error message format.
700 | */
701 | var Akaros = flag.Bool("akaros", false, "Akaros extensions")
702 |
--------------------------------------------------------------------------------
/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 | "fmt"
10 | "github.com/rminnich/go9p"
11 | "log"
12 | )
13 |
14 | var addr = flag.String("addr", ":5640", "network address")
15 | var debug = flag.Int("debug", 0, "print debug messages")
16 | var root = flag.String("root", "/", "root filesystem")
17 |
18 | func main() {
19 | flag.Parse()
20 | ufs := new(go9p.Ufs)
21 | ufs.Dotu = true
22 | ufs.Id = "ufs"
23 | ufs.Root = *root
24 | ufs.Debuglevel = *debug
25 | ufs.Start(ufs)
26 |
27 | fmt.Print("ufs starting\n")
28 | // determined by build tags
29 | //extraFuncs()
30 | err := ufs.StartNetListener("tcp", *addr)
31 | if err != nil {
32 | log.Println(err)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/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 go9p
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 = minFcsize[fc.Type-Tversion]
48 | } else {
49 | sz = minFcusize[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 | if dotu {
81 | if len(p) > 0 {
82 | fc.Unamenum, p = gint32(p)
83 | } else {
84 | fc.Unamenum = NOUID
85 | }
86 | } else {
87 | fc.Unamenum = NOUID
88 | }
89 |
90 | case Rauth, Rattach:
91 | p = gqid(p, &fc.Qid)
92 |
93 | case Tflush:
94 | fc.Oldtag, p = gint16(p)
95 |
96 | case Tattach:
97 | fc.Fid, p = gint32(p)
98 | fc.Afid, p = gint32(p)
99 | fc.Uname, p = gstr(p)
100 | if p == nil {
101 | goto szerror
102 | }
103 |
104 | fc.Aname, p = gstr(p)
105 | if p == nil {
106 | goto szerror
107 | }
108 |
109 | if dotu {
110 | if len(p) > 0 {
111 | fc.Unamenum, p = gint32(p)
112 | } else {
113 | fc.Unamenum = NOUID
114 | }
115 | }
116 |
117 | case Rerror:
118 | fc.Error, p = gstr(p)
119 | if p == nil {
120 | goto szerror
121 | }
122 | if dotu {
123 | fc.Errornum, p = gint32(p)
124 | } else {
125 | fc.Errornum = 0
126 | }
127 |
128 | case Twalk:
129 | fc.Fid, p = gint32(p)
130 | fc.Newfid, p = gint32(p)
131 | m, p = gint16(p)
132 | fc.Wname = make([]string, m)
133 | for i := 0; i < int(m); i++ {
134 | fc.Wname[i], p = gstr(p)
135 | if p == nil {
136 | goto szerror
137 | }
138 | }
139 |
140 | case Rwalk:
141 | m, p = gint16(p)
142 | fc.Wqid = make([]Qid, m)
143 | for i := 0; i < int(m); i++ {
144 | p = gqid(p, &fc.Wqid[i])
145 | }
146 |
147 | case Topen:
148 | fc.Fid, p = gint32(p)
149 | fc.Mode, p = gint8(p)
150 |
151 | case Ropen, Rcreate:
152 | p = gqid(p, &fc.Qid)
153 | fc.Iounit, p = gint32(p)
154 |
155 | case Tcreate:
156 | fc.Fid, p = gint32(p)
157 | fc.Name, p = gstr(p)
158 | if p == nil {
159 | goto szerror
160 | }
161 | fc.Perm, p = gint32(p)
162 | fc.Mode, p = gint8(p)
163 | if dotu {
164 | fc.Ext, p = gstr(p)
165 | if p == nil {
166 | goto szerror
167 | }
168 | }
169 |
170 | case Tread:
171 | fc.Fid, p = gint32(p)
172 | fc.Offset, p = gint64(p)
173 | fc.Count, p = gint32(p)
174 |
175 | case Rread:
176 | fc.Count, p = gint32(p)
177 | if len(p) < int(fc.Count) {
178 | goto szerror
179 | }
180 | fc.Data = p
181 | p = p[fc.Count:]
182 |
183 | case Twrite:
184 | fc.Fid, p = gint32(p)
185 | fc.Offset, p = gint64(p)
186 | fc.Count, p = gint32(p)
187 | if len(p) != int(fc.Count) {
188 | fc.Data = make([]byte, fc.Count)
189 | copy(fc.Data, p)
190 | p = p[len(p):]
191 | } else {
192 | fc.Data = p
193 | p = p[fc.Count:]
194 | }
195 |
196 | case Rwrite:
197 | fc.Count, p = gint32(p)
198 |
199 | case Tclunk, Tremove, Tstat:
200 | fc.Fid, p = gint32(p)
201 |
202 | case Rstat:
203 | m, p = gint16(p)
204 | p, err = gstat(p, &fc.Dir, dotu)
205 | if err != nil {
206 | return nil, err, 0
207 | }
208 |
209 | case Twstat:
210 | fc.Fid, p = gint32(p)
211 | m, p = gint16(p)
212 | p, _ = gstat(p, &fc.Dir, dotu)
213 |
214 | case Rflush, Rclunk, Rremove, Rwstat:
215 | }
216 |
217 | if len(p) > 0 {
218 | goto szerror
219 | }
220 |
221 | return
222 |
223 | szerror:
224 | return nil, &Error{"invalid size", EINVAL}, 0
225 | }
226 |
--------------------------------------------------------------------------------