Blame


1 4b33cdd0 2015-11-30 stephen.d package p9p
2 4b33cdd0 2015-11-30 stephen.d
3 4b33cdd0 2015-11-30 stephen.d import (
4 4b33cdd0 2015-11-30 stephen.d "bufio"
5 c74282f8 2016-11-16 noreply "context"
6 4b33cdd0 2015-11-30 stephen.d "encoding/binary"
7 4b33cdd0 2015-11-30 stephen.d "io"
8 4b33cdd0 2015-11-30 stephen.d "io/ioutil"
9 4b33cdd0 2015-11-30 stephen.d "log"
10 4b33cdd0 2015-11-30 stephen.d "net"
11 4b33cdd0 2015-11-30 stephen.d "time"
12 c74282f8 2016-11-16 noreply )
13 4b33cdd0 2015-11-30 stephen.d
14 c74282f8 2016-11-16 noreply const (
15 c74282f8 2016-11-16 noreply // channelMessageHeaderSize is the overhead for sending the size of a
16 c74282f8 2016-11-16 noreply // message on the wire.
17 c74282f8 2016-11-16 noreply channelMessageHeaderSize = 4
18 4b33cdd0 2015-11-30 stephen.d )
19 4b33cdd0 2015-11-30 stephen.d
20 4b33cdd0 2015-11-30 stephen.d // Channel defines the operations necessary to implement a 9p message channel
21 4b33cdd0 2015-11-30 stephen.d // interface. Typically, message channels do no protocol processing except to
22 4b33cdd0 2015-11-30 stephen.d // send and receive message frames.
23 4b33cdd0 2015-11-30 stephen.d type Channel interface {
24 4b33cdd0 2015-11-30 stephen.d // ReadFcall reads one fcall frame into the provided fcall structure. The
25 4b33cdd0 2015-11-30 stephen.d // Fcall may be cleared whether there is an error or not. If the operation
26 4b33cdd0 2015-11-30 stephen.d // is successful, the contents of the fcall will be populated in the
27 4b33cdd0 2015-11-30 stephen.d // argument. ReadFcall cannot be called concurrently with other calls to
28 4b33cdd0 2015-11-30 stephen.d // ReadFcall. This both to preserve message ordering and to allow lockless
29 4b33cdd0 2015-11-30 stephen.d // buffer reusage.
30 4b33cdd0 2015-11-30 stephen.d ReadFcall(ctx context.Context, fcall *Fcall) error
31 4b33cdd0 2015-11-30 stephen.d
32 4b33cdd0 2015-11-30 stephen.d // WriteFcall writes the provided fcall to the channel. WriteFcall cannot
33 4b33cdd0 2015-11-30 stephen.d // be called concurrently with other calls to WriteFcall.
34 4b33cdd0 2015-11-30 stephen.d WriteFcall(ctx context.Context, fcall *Fcall) error
35 4b33cdd0 2015-11-30 stephen.d
36 4b33cdd0 2015-11-30 stephen.d // MSize returns the current msize for the channel.
37 4b33cdd0 2015-11-30 stephen.d MSize() int
38 4b33cdd0 2015-11-30 stephen.d
39 4b33cdd0 2015-11-30 stephen.d // SetMSize sets the maximum message size for the channel. This must never
40 4b33cdd0 2015-11-30 stephen.d // be called currently with ReadFcall or WriteFcall.
41 4b33cdd0 2015-11-30 stephen.d SetMSize(msize int)
42 4b33cdd0 2015-11-30 stephen.d }
43 4b33cdd0 2015-11-30 stephen.d
44 a0568195 2016-05-23 stevvooe // NewChannel returns a new channel to read and write Fcalls with the provided
45 a0568195 2016-05-23 stevvooe // connection and message size.
46 4b33cdd0 2015-11-30 stephen.d func NewChannel(conn net.Conn, msize int) Channel {
47 4b33cdd0 2015-11-30 stephen.d return newChannel(conn, codec9p{}, msize)
48 4b33cdd0 2015-11-30 stephen.d }
49 4b33cdd0 2015-11-30 stephen.d
50 4b33cdd0 2015-11-30 stephen.d const (
51 e797c539 2016-05-18 stevvooe defaultRWTimeout = 30 * time.Second // default read/write timeout if not set in context
52 4b33cdd0 2015-11-30 stephen.d )
53 4b33cdd0 2015-11-30 stephen.d
54 4b33cdd0 2015-11-30 stephen.d // channel provides bidirectional protocol framing for 9p over net.Conn.
55 4b33cdd0 2015-11-30 stephen.d // Operations are not thread-safe but reads and writes may be carried out
56 4b33cdd0 2015-11-30 stephen.d // concurrently, supporting separate read and write loops.
57 4b33cdd0 2015-11-30 stephen.d //
58 4b33cdd0 2015-11-30 stephen.d // Lifecyle
59 4b33cdd0 2015-11-30 stephen.d //
60 4b33cdd0 2015-11-30 stephen.d // A connection, or message channel abstraction, has a lifecycle delineated by
61 4b33cdd0 2015-11-30 stephen.d // Tversion/Rversion request response cycles. For now, this is part of the
62 4b33cdd0 2015-11-30 stephen.d // channel itself but doesn't necessarily influence the channels state, except
63 4b33cdd0 2015-11-30 stephen.d // the msize. Visually, it might look something like this:
64 4b33cdd0 2015-11-30 stephen.d //
65 4b33cdd0 2015-11-30 stephen.d // [Established] -> [Version] -> [Session] -> [Version]---+
66 4b33cdd0 2015-11-30 stephen.d // ^ |
67 4b33cdd0 2015-11-30 stephen.d // |_________________________________|
68 4b33cdd0 2015-11-30 stephen.d //
69 4b33cdd0 2015-11-30 stephen.d // The connection is established, then we negotiate a version, run a session,
70 4b33cdd0 2015-11-30 stephen.d // then negotiate a version and so on. For most purposes, we are likely going
71 4b33cdd0 2015-11-30 stephen.d // to terminate the connection after the session but we may want to support
72 4b33cdd0 2015-11-30 stephen.d // connection pooling. Pooling may result in possible security leaks if the
73 4b33cdd0 2015-11-30 stephen.d // connections are shared among contexts, since the version is negotiated at
74 4b33cdd0 2015-11-30 stephen.d // the start of the session. To avoid this, we can actually use a "tombstone"
75 4b33cdd0 2015-11-30 stephen.d // version message which clears the server's session state without starting a
76 4b33cdd0 2015-11-30 stephen.d // new session. The next version message would then prepare the session
77 4b33cdd0 2015-11-30 stephen.d // without leaking any Fid's.
78 4b33cdd0 2015-11-30 stephen.d type channel struct {
79 4b33cdd0 2015-11-30 stephen.d conn net.Conn
80 4b33cdd0 2015-11-30 stephen.d codec Codec
81 4b33cdd0 2015-11-30 stephen.d brd *bufio.Reader
82 4b33cdd0 2015-11-30 stephen.d bwr *bufio.Writer
83 4b33cdd0 2015-11-30 stephen.d closed chan struct{}
84 4b33cdd0 2015-11-30 stephen.d msize int
85 4b33cdd0 2015-11-30 stephen.d rdbuf []byte
86 4b33cdd0 2015-11-30 stephen.d }
87 4b33cdd0 2015-11-30 stephen.d
88 4b33cdd0 2015-11-30 stephen.d func newChannel(conn net.Conn, codec Codec, msize int) *channel {
89 4b33cdd0 2015-11-30 stephen.d return &channel{
90 4b33cdd0 2015-11-30 stephen.d conn: conn,
91 4b33cdd0 2015-11-30 stephen.d codec: codec,
92 4b33cdd0 2015-11-30 stephen.d brd: bufio.NewReaderSize(conn, msize), // msize may not be optimal buffer size
93 4b33cdd0 2015-11-30 stephen.d bwr: bufio.NewWriterSize(conn, msize),
94 4b33cdd0 2015-11-30 stephen.d closed: make(chan struct{}),
95 4b33cdd0 2015-11-30 stephen.d msize: msize,
96 4b33cdd0 2015-11-30 stephen.d rdbuf: make([]byte, msize),
97 4b33cdd0 2015-11-30 stephen.d }
98 4b33cdd0 2015-11-30 stephen.d }
99 4b33cdd0 2015-11-30 stephen.d
100 4b33cdd0 2015-11-30 stephen.d func (ch *channel) MSize() int {
101 4b33cdd0 2015-11-30 stephen.d return ch.msize
102 4b33cdd0 2015-11-30 stephen.d }
103 4b33cdd0 2015-11-30 stephen.d
104 4b33cdd0 2015-11-30 stephen.d // setmsize resizes the buffers for use with a separate msize. This call must
105 4b33cdd0 2015-11-30 stephen.d // be protected by a mutex or made before passing to other goroutines.
106 4b33cdd0 2015-11-30 stephen.d func (ch *channel) SetMSize(msize int) {
107 4b33cdd0 2015-11-30 stephen.d // NOTE(stevvooe): We cannot safely resize the buffered reader and writer.
108 4b33cdd0 2015-11-30 stephen.d // Proceed assuming that original size is sufficient.
109 4b33cdd0 2015-11-30 stephen.d
110 4b33cdd0 2015-11-30 stephen.d ch.msize = msize
111 4b33cdd0 2015-11-30 stephen.d if msize < len(ch.rdbuf) {
112 4b33cdd0 2015-11-30 stephen.d // just change the cap
113 4b33cdd0 2015-11-30 stephen.d ch.rdbuf = ch.rdbuf[:msize]
114 4b33cdd0 2015-11-30 stephen.d return
115 4b33cdd0 2015-11-30 stephen.d }
116 4b33cdd0 2015-11-30 stephen.d
117 4b33cdd0 2015-11-30 stephen.d ch.rdbuf = make([]byte, msize)
118 4b33cdd0 2015-11-30 stephen.d }
119 4b33cdd0 2015-11-30 stephen.d
120 4b33cdd0 2015-11-30 stephen.d // ReadFcall reads the next message from the channel into fcall.
121 c74282f8 2016-11-16 noreply //
122 c74282f8 2016-11-16 noreply // If the incoming message overflows the msize, Overflow(err) will return
123 c74282f8 2016-11-16 noreply // nonzero with the number of bytes overflowed.
124 4b33cdd0 2015-11-30 stephen.d func (ch *channel) ReadFcall(ctx context.Context, fcall *Fcall) error {
125 4b33cdd0 2015-11-30 stephen.d select {
126 4b33cdd0 2015-11-30 stephen.d case <-ctx.Done():
127 4b33cdd0 2015-11-30 stephen.d return ctx.Err()
128 4b33cdd0 2015-11-30 stephen.d case <-ch.closed:
129 4b33cdd0 2015-11-30 stephen.d return ErrClosed
130 4b33cdd0 2015-11-30 stephen.d default:
131 4b33cdd0 2015-11-30 stephen.d }
132 4b33cdd0 2015-11-30 stephen.d
133 4b33cdd0 2015-11-30 stephen.d deadline, ok := ctx.Deadline()
134 4b33cdd0 2015-11-30 stephen.d if !ok {
135 4b33cdd0 2015-11-30 stephen.d deadline = time.Now().Add(defaultRWTimeout)
136 4b33cdd0 2015-11-30 stephen.d }
137 4b33cdd0 2015-11-30 stephen.d
138 4b33cdd0 2015-11-30 stephen.d if err := ch.conn.SetReadDeadline(deadline); err != nil {
139 2da28ffa 2017-11-10 noreply log.Printf("p9p: transport: error setting read deadline on %v: %v", ch.conn.RemoteAddr(), err)
140 4b33cdd0 2015-11-30 stephen.d }
141 4b33cdd0 2015-11-30 stephen.d
142 4b33cdd0 2015-11-30 stephen.d n, err := readmsg(ch.brd, ch.rdbuf)
143 4b33cdd0 2015-11-30 stephen.d if err != nil {
144 4b33cdd0 2015-11-30 stephen.d // TODO(stevvooe): There may be more we can do here to detect partial
145 4b33cdd0 2015-11-30 stephen.d // reads. For now, we just propagate the error untouched.
146 4b33cdd0 2015-11-30 stephen.d return err
147 4b33cdd0 2015-11-30 stephen.d }
148 4b33cdd0 2015-11-30 stephen.d
149 4b33cdd0 2015-11-30 stephen.d if n > len(ch.rdbuf) {
150 c74282f8 2016-11-16 noreply return overflowErr{size: n - len(ch.rdbuf)}
151 4b33cdd0 2015-11-30 stephen.d }
152 4b33cdd0 2015-11-30 stephen.d
153 4b33cdd0 2015-11-30 stephen.d // clear out the fcall
154 4b33cdd0 2015-11-30 stephen.d *fcall = Fcall{}
155 4b33cdd0 2015-11-30 stephen.d if err := ch.codec.Unmarshal(ch.rdbuf[:n], fcall); err != nil {
156 4b33cdd0 2015-11-30 stephen.d return err
157 4b33cdd0 2015-11-30 stephen.d }
158 4b33cdd0 2015-11-30 stephen.d
159 c74282f8 2016-11-16 noreply if err := ch.maybeTruncate(fcall); err != nil {
160 c74282f8 2016-11-16 noreply return err
161 c74282f8 2016-11-16 noreply }
162 c74282f8 2016-11-16 noreply
163 4b33cdd0 2015-11-30 stephen.d return nil
164 4b33cdd0 2015-11-30 stephen.d }
165 4b33cdd0 2015-11-30 stephen.d
166 c74282f8 2016-11-16 noreply // WriteFcall writes the message to the connection.
167 c74282f8 2016-11-16 noreply //
168 c74282f8 2016-11-16 noreply // If a message destined for the wire will overflow MSize, an Overflow error
169 c74282f8 2016-11-16 noreply // may be returned. For Twrite calls, the buffer will simply be truncated to
170 c74282f8 2016-11-16 noreply // the optimal msize, with the caller detecting this condition with
171 c74282f8 2016-11-16 noreply // Rwrite.Count.
172 4b33cdd0 2015-11-30 stephen.d func (ch *channel) WriteFcall(ctx context.Context, fcall *Fcall) error {
173 4b33cdd0 2015-11-30 stephen.d select {
174 4b33cdd0 2015-11-30 stephen.d case <-ctx.Done():
175 4b33cdd0 2015-11-30 stephen.d return ctx.Err()
176 4b33cdd0 2015-11-30 stephen.d case <-ch.closed:
177 4b33cdd0 2015-11-30 stephen.d return ErrClosed
178 4b33cdd0 2015-11-30 stephen.d default:
179 4b33cdd0 2015-11-30 stephen.d }
180 4b33cdd0 2015-11-30 stephen.d
181 4b33cdd0 2015-11-30 stephen.d deadline, ok := ctx.Deadline()
182 4b33cdd0 2015-11-30 stephen.d if !ok {
183 4b33cdd0 2015-11-30 stephen.d deadline = time.Now().Add(defaultRWTimeout)
184 4b33cdd0 2015-11-30 stephen.d }
185 4b33cdd0 2015-11-30 stephen.d
186 4b33cdd0 2015-11-30 stephen.d if err := ch.conn.SetWriteDeadline(deadline); err != nil {
187 2da28ffa 2017-11-10 noreply log.Printf("p9p: transport: error setting read deadline on %v: %v", ch.conn.RemoteAddr(), err)
188 4b33cdd0 2015-11-30 stephen.d }
189 4b33cdd0 2015-11-30 stephen.d
190 c74282f8 2016-11-16 noreply if err := ch.maybeTruncate(fcall); err != nil {
191 c74282f8 2016-11-16 noreply return err
192 c74282f8 2016-11-16 noreply }
193 c74282f8 2016-11-16 noreply
194 4b33cdd0 2015-11-30 stephen.d p, err := ch.codec.Marshal(fcall)
195 4b33cdd0 2015-11-30 stephen.d if err != nil {
196 4b33cdd0 2015-11-30 stephen.d return err
197 4b33cdd0 2015-11-30 stephen.d }
198 4b33cdd0 2015-11-30 stephen.d
199 4b33cdd0 2015-11-30 stephen.d if err := sendmsg(ch.bwr, p); err != nil {
200 4b33cdd0 2015-11-30 stephen.d return err
201 4b33cdd0 2015-11-30 stephen.d }
202 4b33cdd0 2015-11-30 stephen.d
203 4b33cdd0 2015-11-30 stephen.d return ch.bwr.Flush()
204 4b33cdd0 2015-11-30 stephen.d }
205 4b33cdd0 2015-11-30 stephen.d
206 c74282f8 2016-11-16 noreply // maybeTruncate will truncate the message to fit into msize on the wire, if
207 c74282f8 2016-11-16 noreply // possible, or modify the message to ensure the response won't overflow.
208 c74282f8 2016-11-16 noreply //
209 c74282f8 2016-11-16 noreply // If the message cannot be truncated, an error will be returned and the
210 c74282f8 2016-11-16 noreply // message should not be sent.
211 c74282f8 2016-11-16 noreply //
212 c74282f8 2016-11-16 noreply // A nil return value means the message can be sent without
213 c74282f8 2016-11-16 noreply func (ch *channel) maybeTruncate(fcall *Fcall) error {
214 c74282f8 2016-11-16 noreply
215 c74282f8 2016-11-16 noreply // for certain message types, just remove the extra bytes from the data portion.
216 c74282f8 2016-11-16 noreply switch msg := fcall.Message.(type) {
217 c74282f8 2016-11-16 noreply // TODO(stevvooe): There is one more problematic message type:
218 c74282f8 2016-11-16 noreply //
219 c74282f8 2016-11-16 noreply // Rread: while we can employ the same truncation fix as Twrite, we
220 c74282f8 2016-11-16 noreply // need to make it observable to upstream handlers.
221 c74282f8 2016-11-16 noreply
222 c74282f8 2016-11-16 noreply case MessageTread:
223 c74282f8 2016-11-16 noreply // We can rewrite msg.Count so that a return message will be under
224 c74282f8 2016-11-16 noreply // msize. This is more defensive than anything but will ensure that
225 c74282f8 2016-11-16 noreply // calls don't fail on sloppy servers.
226 c74282f8 2016-11-16 noreply
227 c74282f8 2016-11-16 noreply // first, craft the shape of the response message
228 c74282f8 2016-11-16 noreply resp := newFcall(fcall.Tag, MessageRread{})
229 c74282f8 2016-11-16 noreply overflow := uint32(ch.msgmsize(resp)) + msg.Count - uint32(ch.msize)
230 c74282f8 2016-11-16 noreply
231 c74282f8 2016-11-16 noreply if msg.Count < overflow {
232 c74282f8 2016-11-16 noreply // Let the bad thing happen; msize too small to even support valid
233 c74282f8 2016-11-16 noreply // rewrite. This will result in a Terror from the server-side or
234 c74282f8 2016-11-16 noreply // just work.
235 c74282f8 2016-11-16 noreply return nil
236 c74282f8 2016-11-16 noreply }
237 c74282f8 2016-11-16 noreply
238 c74282f8 2016-11-16 noreply msg.Count -= overflow
239 c74282f8 2016-11-16 noreply fcall.Message = msg
240 c74282f8 2016-11-16 noreply
241 c74282f8 2016-11-16 noreply return nil
242 c74282f8 2016-11-16 noreply case MessageTwrite:
243 c74282f8 2016-11-16 noreply // If we are going to overflow the msize, we need to truncate the write to
244 c74282f8 2016-11-16 noreply // appropriate size or throw an error in all other conditions.
245 c74282f8 2016-11-16 noreply size := ch.msgmsize(fcall)
246 c74282f8 2016-11-16 noreply if size <= ch.msize {
247 c74282f8 2016-11-16 noreply return nil
248 c74282f8 2016-11-16 noreply }
249 c74282f8 2016-11-16 noreply
250 c74282f8 2016-11-16 noreply // overflow the msize, including the channel message size fields.
251 c74282f8 2016-11-16 noreply overflow := size - ch.msize
252 c74282f8 2016-11-16 noreply
253 c74282f8 2016-11-16 noreply if len(msg.Data) < overflow {
254 c74282f8 2016-11-16 noreply // paranoid: if msg.Data is not big enough to handle the
255 c74282f8 2016-11-16 noreply // overflow, we should get an overflow error. MSize would have
256 c74282f8 2016-11-16 noreply // to be way too small to be realistic.
257 c74282f8 2016-11-16 noreply return overflowErr{size: overflow}
258 c74282f8 2016-11-16 noreply }
259 c74282f8 2016-11-16 noreply
260 c74282f8 2016-11-16 noreply // The truncation is reflected in the return message (Rwrite) by
261 c74282f8 2016-11-16 noreply // the server, so we don't need a return value or error condition
262 c74282f8 2016-11-16 noreply // to communicate it.
263 c74282f8 2016-11-16 noreply msg.Data = msg.Data[:len(msg.Data)-overflow]
264 c74282f8 2016-11-16 noreply fcall.Message = msg // since we have a local copy
265 c74282f8 2016-11-16 noreply
266 c74282f8 2016-11-16 noreply return nil
267 c74282f8 2016-11-16 noreply default:
268 c74282f8 2016-11-16 noreply size := ch.msgmsize(fcall)
269 c74282f8 2016-11-16 noreply if size > ch.msize {
270 c74282f8 2016-11-16 noreply // overflow the msize, including the channel message size fields.
271 c74282f8 2016-11-16 noreply return overflowErr{size: size - ch.msize}
272 c74282f8 2016-11-16 noreply }
273 c74282f8 2016-11-16 noreply
274 c74282f8 2016-11-16 noreply return nil
275 c74282f8 2016-11-16 noreply }
276 c74282f8 2016-11-16 noreply
277 c74282f8 2016-11-16 noreply }
278 c74282f8 2016-11-16 noreply
279 c74282f8 2016-11-16 noreply // msgmsize returns the on-wire msize of the Fcall, including the size header.
280 c74282f8 2016-11-16 noreply // Typically, this can be used to detect whether or not the message overflows
281 c74282f8 2016-11-16 noreply // the msize buffer.
282 c74282f8 2016-11-16 noreply func (ch *channel) msgmsize(fcall *Fcall) int {
283 c74282f8 2016-11-16 noreply return channelMessageHeaderSize + ch.codec.Size(fcall)
284 c74282f8 2016-11-16 noreply }
285 c74282f8 2016-11-16 noreply
286 4b33cdd0 2015-11-30 stephen.d // readmsg reads a 9p message into p from rd, ensuring that all bytes are
287 4b33cdd0 2015-11-30 stephen.d // consumed from the size header. If the size header indicates the message is
288 4b33cdd0 2015-11-30 stephen.d // larger than p, the entire message will be discarded, leaving a truncated
289 4b33cdd0 2015-11-30 stephen.d // portion in p. Any error should be treated as a framing error unless n is
290 4b33cdd0 2015-11-30 stephen.d // zero. The caller must check that n is less than or equal to len(p) to
291 4b33cdd0 2015-11-30 stephen.d // ensure that a valid message has been read.
292 4b33cdd0 2015-11-30 stephen.d func readmsg(rd io.Reader, p []byte) (n int, err error) {
293 4b33cdd0 2015-11-30 stephen.d var msize uint32
294 4b33cdd0 2015-11-30 stephen.d
295 4b33cdd0 2015-11-30 stephen.d if err := binary.Read(rd, binary.LittleEndian, &msize); err != nil {
296 4b33cdd0 2015-11-30 stephen.d return 0, err
297 4b33cdd0 2015-11-30 stephen.d }
298 4b33cdd0 2015-11-30 stephen.d
299 4b33cdd0 2015-11-30 stephen.d n += binary.Size(msize)
300 4b33cdd0 2015-11-30 stephen.d mbody := int(msize) - 4
301 4b33cdd0 2015-11-30 stephen.d
302 4b33cdd0 2015-11-30 stephen.d if mbody < len(p) {
303 4b33cdd0 2015-11-30 stephen.d p = p[:mbody]
304 4b33cdd0 2015-11-30 stephen.d }
305 4b33cdd0 2015-11-30 stephen.d
306 4b33cdd0 2015-11-30 stephen.d np, err := io.ReadFull(rd, p)
307 4b33cdd0 2015-11-30 stephen.d if err != nil {
308 4b33cdd0 2015-11-30 stephen.d return np + n, err
309 4b33cdd0 2015-11-30 stephen.d }
310 4b33cdd0 2015-11-30 stephen.d n += np
311 4b33cdd0 2015-11-30 stephen.d
312 4b33cdd0 2015-11-30 stephen.d if mbody > len(p) {
313 4b33cdd0 2015-11-30 stephen.d // message has been read up to len(p) but we must consume the entire
314 4b33cdd0 2015-11-30 stephen.d // message. This is an error condition but is non-fatal if we can
315 4b33cdd0 2015-11-30 stephen.d // consume msize bytes.
316 4b33cdd0 2015-11-30 stephen.d nn, err := io.CopyN(ioutil.Discard, rd, int64(mbody-len(p)))
317 4b33cdd0 2015-11-30 stephen.d n += int(nn)
318 4b33cdd0 2015-11-30 stephen.d if err != nil {
319 4b33cdd0 2015-11-30 stephen.d return n, err
320 4b33cdd0 2015-11-30 stephen.d }
321 4b33cdd0 2015-11-30 stephen.d }
322 4b33cdd0 2015-11-30 stephen.d
323 4b33cdd0 2015-11-30 stephen.d return n, nil
324 4b33cdd0 2015-11-30 stephen.d }
325 4b33cdd0 2015-11-30 stephen.d
326 4b33cdd0 2015-11-30 stephen.d // sendmsg writes a message of len(p) to wr with a 9p size header. All errors
327 4b33cdd0 2015-11-30 stephen.d // should be considered terminal.
328 4b33cdd0 2015-11-30 stephen.d func sendmsg(wr io.Writer, p []byte) error {
329 4b33cdd0 2015-11-30 stephen.d size := uint32(len(p) + 4) // message size plus 4-bytes for size.
330 4b33cdd0 2015-11-30 stephen.d if err := binary.Write(wr, binary.LittleEndian, size); err != nil {
331 5eb5d0b1 2016-05-19 stevvooe return err
332 4b33cdd0 2015-11-30 stephen.d }
333 4b33cdd0 2015-11-30 stephen.d
334 4b33cdd0 2015-11-30 stephen.d // This assume partial writes to wr aren't possible. Not sure if this
335 4b33cdd0 2015-11-30 stephen.d // valid. Matters during timeout retries.
336 4b33cdd0 2015-11-30 stephen.d if n, err := wr.Write(p); err != nil {
337 4b33cdd0 2015-11-30 stephen.d return err
338 4b33cdd0 2015-11-30 stephen.d } else if n < len(p) {
339 4b33cdd0 2015-11-30 stephen.d return io.ErrShortWrite
340 4b33cdd0 2015-11-30 stephen.d }
341 4b33cdd0 2015-11-30 stephen.d
342 4b33cdd0 2015-11-30 stephen.d return nil
343 4b33cdd0 2015-11-30 stephen.d }