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 0f5f58bb 2016-11-16 stephen.d "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 0f5f58bb 2016-11-16 stephen.d )
13 4b33cdd0 2015-11-30 stephen.d
14 0f5f58bb 2016-11-16 stephen.d const (
15 0f5f58bb 2016-11-16 stephen.d // channelMessageHeaderSize is the overhead for sending the size of a
16 0f5f58bb 2016-11-16 stephen.d // message on the wire.
17 0f5f58bb 2016-11-16 stephen.d 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 0f5f58bb 2016-11-16 stephen.d //
122 0f5f58bb 2016-11-16 stephen.d // If the incoming message overflows the msize, Overflow(err) will return
123 0f5f58bb 2016-11-16 stephen.d // 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 4b33cdd0 2015-11-30 stephen.d log.Printf("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 0f5f58bb 2016-11-16 stephen.d 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 0f5f58bb 2016-11-16 stephen.d if err := ch.maybeTruncate(fcall); err != nil {
160 0f5f58bb 2016-11-16 stephen.d return err
161 0f5f58bb 2016-11-16 stephen.d }
162 0f5f58bb 2016-11-16 stephen.d
163 4b33cdd0 2015-11-30 stephen.d return nil
164 4b33cdd0 2015-11-30 stephen.d }
165 4b33cdd0 2015-11-30 stephen.d
166 0f5f58bb 2016-11-16 stephen.d // WriteFcall writes the message to the connection.
167 0f5f58bb 2016-11-16 stephen.d //
168 0f5f58bb 2016-11-16 stephen.d // If a message destined for the wire will overflow MSize, an Overflow error
169 0f5f58bb 2016-11-16 stephen.d // may be returned. For Twrite calls, the buffer will simply be truncated to
170 0f5f58bb 2016-11-16 stephen.d // the optimal msize, with the caller detecting this condition with
171 0f5f58bb 2016-11-16 stephen.d // 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 4b33cdd0 2015-11-30 stephen.d log.Printf("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 0f5f58bb 2016-11-16 stephen.d if err := ch.maybeTruncate(fcall); err != nil {
191 0f5f58bb 2016-11-16 stephen.d return err
192 0f5f58bb 2016-11-16 stephen.d }
193 0f5f58bb 2016-11-16 stephen.d
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 0f5f58bb 2016-11-16 stephen.d // maybeTruncate will truncate the message to fit into msize on the wire, if
207 0f5f58bb 2016-11-16 stephen.d // possible, or modify the message to ensure the response won't overflow.
208 0f5f58bb 2016-11-16 stephen.d //
209 0f5f58bb 2016-11-16 stephen.d // If the message cannot be truncated, an error will be returned and the
210 0f5f58bb 2016-11-16 stephen.d // message should not be sent.
211 0f5f58bb 2016-11-16 stephen.d //
212 0f5f58bb 2016-11-16 stephen.d // A nil return value means the message can be sent without
213 0f5f58bb 2016-11-16 stephen.d func (ch *channel) maybeTruncate(fcall *Fcall) error {
214 0f5f58bb 2016-11-16 stephen.d
215 0f5f58bb 2016-11-16 stephen.d // for certain message types, just remove the extra bytes from the data portion.
216 0f5f58bb 2016-11-16 stephen.d switch msg := fcall.Message.(type) {
217 0f5f58bb 2016-11-16 stephen.d // TODO(stevvooe): There is one more problematic message type:
218 0f5f58bb 2016-11-16 stephen.d //
219 0f5f58bb 2016-11-16 stephen.d // Rread: while we can employ the same truncation fix as Twrite, we
220 0f5f58bb 2016-11-16 stephen.d // need to make it observable to upstream handlers.
221 0f5f58bb 2016-11-16 stephen.d
222 0f5f58bb 2016-11-16 stephen.d case MessageTread:
223 0f5f58bb 2016-11-16 stephen.d // We can rewrite msg.Count so that a return message will be under
224 0f5f58bb 2016-11-16 stephen.d // msize. This is more defensive than anything but will ensure that
225 0f5f58bb 2016-11-16 stephen.d // calls don't fail on sloppy servers.
226 0f5f58bb 2016-11-16 stephen.d
227 0f5f58bb 2016-11-16 stephen.d // first, craft the shape of the response message
228 0f5f58bb 2016-11-16 stephen.d resp := newFcall(fcall.Tag, MessageRread{})
229 0f5f58bb 2016-11-16 stephen.d overflow := uint32(ch.msgmsize(resp)) + msg.Count - uint32(ch.msize)
230 0f5f58bb 2016-11-16 stephen.d
231 0f5f58bb 2016-11-16 stephen.d if msg.Count < overflow {
232 0f5f58bb 2016-11-16 stephen.d // Let the bad thing happen; msize too small to even support valid
233 0f5f58bb 2016-11-16 stephen.d // rewrite. This will result in a Terror from the server-side or
234 0f5f58bb 2016-11-16 stephen.d // just work.
235 0f5f58bb 2016-11-16 stephen.d return nil
236 0f5f58bb 2016-11-16 stephen.d }
237 0f5f58bb 2016-11-16 stephen.d
238 0f5f58bb 2016-11-16 stephen.d msg.Count -= overflow
239 0f5f58bb 2016-11-16 stephen.d fcall.Message = msg
240 0f5f58bb 2016-11-16 stephen.d
241 0f5f58bb 2016-11-16 stephen.d return nil
242 0f5f58bb 2016-11-16 stephen.d case MessageTwrite:
243 0f5f58bb 2016-11-16 stephen.d // If we are going to overflow the msize, we need to truncate the write to
244 0f5f58bb 2016-11-16 stephen.d // appropriate size or throw an error in all other conditions.
245 0f5f58bb 2016-11-16 stephen.d size := ch.msgmsize(fcall)
246 0f5f58bb 2016-11-16 stephen.d if size <= ch.msize {
247 0f5f58bb 2016-11-16 stephen.d return nil
248 0f5f58bb 2016-11-16 stephen.d }
249 0f5f58bb 2016-11-16 stephen.d
250 0f5f58bb 2016-11-16 stephen.d // overflow the msize, including the channel message size fields.
251 0f5f58bb 2016-11-16 stephen.d overflow := size - ch.msize
252 0f5f58bb 2016-11-16 stephen.d
253 0f5f58bb 2016-11-16 stephen.d if len(msg.Data) < overflow {
254 0f5f58bb 2016-11-16 stephen.d // paranoid: if msg.Data is not big enough to handle the
255 0f5f58bb 2016-11-16 stephen.d // overflow, we should get an overflow error. MSize would have
256 0f5f58bb 2016-11-16 stephen.d // to be way too small to be realistic.
257 0f5f58bb 2016-11-16 stephen.d return overflowErr{size: overflow}
258 0f5f58bb 2016-11-16 stephen.d }
259 0f5f58bb 2016-11-16 stephen.d
260 0f5f58bb 2016-11-16 stephen.d // The truncation is reflected in the return message (Rwrite) by
261 0f5f58bb 2016-11-16 stephen.d // the server, so we don't need a return value or error condition
262 0f5f58bb 2016-11-16 stephen.d // to communicate it.
263 0f5f58bb 2016-11-16 stephen.d msg.Data = msg.Data[:len(msg.Data)-overflow]
264 0f5f58bb 2016-11-16 stephen.d fcall.Message = msg // since we have a local copy
265 0f5f58bb 2016-11-16 stephen.d
266 0f5f58bb 2016-11-16 stephen.d return nil
267 0f5f58bb 2016-11-16 stephen.d default:
268 0f5f58bb 2016-11-16 stephen.d size := ch.msgmsize(fcall)
269 0f5f58bb 2016-11-16 stephen.d if size > ch.msize {
270 0f5f58bb 2016-11-16 stephen.d // overflow the msize, including the channel message size fields.
271 0f5f58bb 2016-11-16 stephen.d return overflowErr{size: size - ch.msize}
272 0f5f58bb 2016-11-16 stephen.d }
273 0f5f58bb 2016-11-16 stephen.d
274 0f5f58bb 2016-11-16 stephen.d return nil
275 0f5f58bb 2016-11-16 stephen.d }
276 0f5f58bb 2016-11-16 stephen.d
277 0f5f58bb 2016-11-16 stephen.d }
278 0f5f58bb 2016-11-16 stephen.d
279 0f5f58bb 2016-11-16 stephen.d // msgmsize returns the on-wire msize of the Fcall, including the size header.
280 0f5f58bb 2016-11-16 stephen.d // Typically, this can be used to detect whether or not the message overflows
281 0f5f58bb 2016-11-16 stephen.d // the msize buffer.
282 0f5f58bb 2016-11-16 stephen.d func (ch *channel) msgmsize(fcall *Fcall) int {
283 0f5f58bb 2016-11-16 stephen.d return channelMessageHeaderSize + ch.codec.Size(fcall)
284 0f5f58bb 2016-11-16 stephen.d }
285 0f5f58bb 2016-11-16 stephen.d
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 }