-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
120 lines (100 loc) · 2.47 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package p9
import (
"errors"
"net"
"sync/atomic"
"github.com/DeedleFake/p9/proto"
)
var (
// ErrUnsupportedVersion is returned from a handshake attempt that
// fails due to a version mismatch.
ErrUnsupportedVersion = errors.New("unsupported version")
)
// Client provides functionality for sending requests to and receiving
// responses from a 9P server.
//
// A Client must be closed when it is no longer going to be used in
// order to free up the related resources.
type Client struct {
*proto.Client
fid uint32
}
// NewClient returns a client that communicates using c. The Client
// will close c when the Client is closed.
func NewClient(c net.Conn) *Client {
return &Client{Client: proto.NewClient(Proto(), c)}
}
// Dial is a convenience function that dials and creates a client in
// the same step.
func Dial(network, addr string) (*Client, error) {
pc, err := proto.Dial(Proto(), network, addr)
if err != nil {
return nil, err
}
return &Client{Client: pc}, nil
}
func (c *Client) nextFID() uint32 {
return atomic.AddUint32(&c.fid, 1) - 1
}
// Handshake performs an initial handshake to establish the maximum
// allowed message size. A handshake must be performed before any
// other request types may be sent.
func (c *Client) Handshake(msize uint32) (uint32, error) {
rsp, err := c.Send(&Tversion{
Msize: msize,
Version: Version,
})
if err != nil {
return 0, err
}
version := rsp.(*Rversion)
if version.Version != Version {
return 0, ErrUnsupportedVersion
}
c.SetMsize(version.Msize)
return version.Msize, nil
}
// Auth requests an auth file from the server, returning a Remote
// representing it or an error if one occurred.
func (c *Client) Auth(user, aname string) (*Remote, error) {
fid := c.nextFID()
rsp, err := c.Send(&Tauth{
AFID: fid,
Uname: user,
Aname: aname,
})
if err != nil {
return nil, err
}
rauth := rsp.(*Rauth)
return &Remote{
client: c,
fid: fid,
qid: rauth.AQID,
}, nil
}
// Attach attaches to a filesystem provided by the connected server
// with the given attributes. If no authentication has been done,
// afile may be nil.
func (c *Client) Attach(afile *Remote, user, aname string) (*Remote, error) {
fid := c.nextFID()
afid := NoFID
if afile != nil {
afid = afile.fid
}
rsp, err := c.Send(&Tattach{
FID: fid,
AFID: afid,
Uname: user,
Aname: aname,
})
if err != nil {
return nil, err
}
attach := rsp.(*Rattach)
return &Remote{
client: c,
fid: fid,
qid: attach.QID,
}, nil
}