History

Attempted to improve handling of different format fields, treating certain types as special cases and allowing lengths to refer to earlier fields in a format list.

David Boddie committed 8 years ago

Commit 6add7821 parent 4da8c8f1 file · +264 −138Browse files

Unified · Split

styx.py+264−138
@@ -76,6 +76,67 @@ def encode_qid(qid): · 75 unmodified lines
7676
7777 return struct.pack("<BIQ", *qid)
7878
79+def decode_format(stream, obj):
80+
81+ for name, length in obj.format:
82+
83+ if length == "s":
84+ # UTF-8 string
85+ value = decode_string(stream)
86+ elif length == "n":
87+ # n bytes of raw data
88+ value = decode_data(stream)
89+ elif length == 1:
90+ value = struct.unpack("<B", stream.recv(1))[0]
91+ elif length == 2:
92+ value = struct.unpack("<H", stream.recv(2))[0]
93+ elif length == 4:
94+ value = struct.unpack("<I", stream.recv(4))[0]
95+ elif length == 8:
96+ value = struct.unpack("<Q", stream.recv(8))[0]
97+ elif name == "qid":
98+ value = decode_qid(stream)
99+ elif name == "stat":
100+ value = Stat()
101+ value.decode(stream)
102+ elif length in obj.__dict__:
103+ value = stream.recv(obj.__dict__[length])
104+ else:
105+ raise StyxError("decode_format: Unknown field length specifier: %s" % length)
106+
107+ obj.__dict__[name] = value
108+
109+def encode_format(stream, obj):
110+
111+ data = ""
112+
113+ for name, length in obj.format:
114+
115+ value = obj.__dict__[name]
116+
117+ if length == "s":
118+ # UTF-8 string
119+ data += encode_string(value)
120+ elif length == "n":
121+ # n bytes of raw data
122+ data += encode_data(value)
123+ elif length == 1:
124+ data += struct.pack("<B", value)
125+ elif length == 2:
126+ data += struct.pack("<H", value)
127+ elif length == 4:
128+ data += struct.pack("<I", value)
129+ elif length == 8:
130+ data += struct.pack("<Q", value)
131+ elif name == "qid":
132+ data += encode_qid(value)
133+ elif name == "stat":
134+ data += value.encode()
135+ else:
136+ data += value
137+
138+ return data
139+
79140
80141 class StyxError(Exception):
81142 pass
@@ -88,33 +149,6 @@ class StyxMessage: · 6 unmodified lines
88149 self.size = size
89150 self.tag = tag
90151
91− def decode(self, size, tag, stream):
92−
93− self.init(size, tag)
94−
95− for name, length in self.format:
96−
97− if length == "s":
98− # UTF-8 string
99− value = decode_string(stream)
100− elif length == "n":
101− # n bytes of raw data
102− value = decode_data(stream)
103− elif length == 2:
104− value = struct.unpack("<H", stream.recv(2))[0]
105− elif length == 4:
106− value = struct.unpack("<I", stream.recv(4))[0]
107− elif length == 8:
108− value = struct.unpack("<Q", stream.recv(8))[0]
109− elif name == "qid":
110− value = decode_qid(stream)
111− else:
112− raise StyxError("Unknown field length specifier: %s" % length)
113−
114− self.__dict__[name] = value
115−
116− return self
117−
118152 def __repr__(self):
119153
120154 s = self.msg_name + "(tag=" + repr(self.tag)
@@ -124,41 +158,21 @@ class StyxMessage: · 3 unmodified lines
124158
125159 return s + ")"
126160
161+ def decode(self, size, tag, stream):
162+
163+ self.init(size, tag)
164+ decode_format(stream, self)
165+ return self
166+
127167 def encode(self, stream):
128168
129169 # Start with the message type and tag before adding all the other pieces.
130− data = struct.pack("<bH", self.code, self.tag)
131−
132− data += self._encode_format(stream)
170+ data = struct.pack("<BH", self.code, self.tag)
171+ data += encode_format(stream, self)
133172
134173 # Finally, prepend the length to the byte string and send it.
135174 data = struct.pack("<I", len(data) + 4) + data
136175 stream.sendall(data)
137−
138− def _encode_format(self, stream):
139−
140− data = ""
141−
142− for name, length in self.format:
143−
144− value = self.__dict__[name]
145−
146− if length == "s":
147− # UTF-8 string
148− data += encode_string(value)
149− elif length == "n":
150− # n bytes of raw data
151− data += encode_data(value)
152− elif length == 2:
153− data += struct.pack("<H", value)
154− elif length == 4:
155− data += struct.pack("<I", value)
156− elif length == 8:
157− data += struct.pack("<Q", value)
158− elif name == "qid":
159− data += encode_qid(value)
160−
161− return data
162176
163177
164178 class Tversion(StyxMessage):
@@ -212,77 +226,16 @@ class Rattach(StyxMessage): · 47 unmodified lines
212226 self.qid = qid
213227
214228
215−class Tstat(StyxMessage):
216−
217− msg_name = "Tstat"
218− code = 124
219− format = [("fid", 4)]
220−
221− def __init__(self, tag = None, fid = None):
222−
223− self.tag = tag
224− self.fid = fid
225−
226−class Rstat(StyxMessage):
229+class Rerror(StyxMessage):
227230
228− msg_name = "Rstat"
229− code = 125
230− # Because the format of this message describes the stat information as
231− # stat[n] it includes a 16-bit length, but the stat object itself
232− # includes its own length field.
233− format = [
234− ("stat_size", 2), ("size", 2), ("type", 2), ("dev", 4), ("qid", 13),
235− ("mode", 4), ("atime", 4), ("mtime", 4), ("length", 8), ("name", "s"),
236− ("uid", "s"), ("gid", "s"), ("muid", "s")
237− ]
231+ msg_name = "Rerror"
232+ code = 107
233+ format = [("ename", "s")]
238234
239− def __init__(self, tag = None, stat = None):
235+ def __init__(self, tag = None, ename = None):
240236
241237 self.tag = tag
242− self.stat = stat
243−
244− def encode(self, stream):
245−
246− data = struct.pack("<bH", self.code, self.tag)
247−
248− # Encode the stat structure.
249− data += self.stat.encode()
250−
251− data = struct.pack("<I", len(data) + 4) + data
252− stream.sendall(data)
253−
254−
255−class Stat:
256−
257− def __init__(self, type, dev, qid, mode, atime, mtime, length, name, uid,
258− gid, muid):
259−
260− self.type = type
261− self.dev = dev
262− self.qid = qid
263− self.mode = mode
264− self.atime = atime
265− self.mtime = mtime
266− self.length = length
267− self.name = name
268− self.uid = uid
269− self.gid = gid
270− self.muid = muid
271−
272− def encode(self):
273−
274− args = (self.type, self.dev) + self.qid + (self.mode, self.atime, self.mtime, self.length)
275− data = struct.pack("<HIBIQIIIQ", *args)
276− data += encode_string(self.name)
277− data += encode_string(self.uid)
278− data += encode_string(self.gid)
279− data += encode_string(self.muid)
280−
281− # Prepend the size of the fields within the stat structure.
282− data = encode_data(data)
283−
284− # Prepend the size of the stat structure itself.
285− return encode_data(data)
238+ self.ename = ename
286239
287240
288241 class Twalk(StyxMessage):
@@ -291,12 +244,13 @@ class Twalk(StyxMessage): · 2 unmodified lines
291244 code = 110
292245 format = [("fid", 4), ("newfid", 4), ("nwname", 2)] # nwname of wname[s]
293246
294− def __init__(self, tag = None, fid = None, newfid = None, wname = None):
247+ def __init__(self, tag = None, fid = None, newfid = None, wname = []):
295248
296249 self.tag = tag
297250 self.fid = fid
298251 self.newfid = newfid
299252 self.wname = wname
253+ self.nwname = len(wname)
300254
301255 def decode(self, size, tag, stream):
302256
@@ -309,14 +263,16 @@ class Twalk(StyxMessage): · 6 unmodified lines
309263 self.wname.append(decode_string(stream))
310264 i += 1
311265
266+ self.format.append(("wname", "nwname"))
267+
312268 return self
313269
314270 def encode(self, stream):
315271
316− data = struct.pack("<bH", self.code, self.tag)
272+ data = struct.pack("<BH", self.code, self.tag)
317273
318274 self.nwname = len(self.wname)
319− data += self._encode_format(stream)
275+ data += encode_format(stream, self)
320276
321277 # Encode the names separately.
322278 for name in self.wname:
@@ -325,17 +281,17 @@ class Twalk(StyxMessage): · 2 unmodified lines
325281 data = struct.pack("<I", len(data) + 4) + data
326282 stream.sendall(data)
327283
328−
329284 class Rwalk(StyxMessage):
330285
331286 msg_name = "Rwalk"
332287 code = 111
333288 format = [("nwqid", 2)] # nwqid of wqid[13]
334289
335− def __init__(self, tag = None, wqid = None):
290+ def __init__(self, tag = None, wqid = []):
336291
337292 self.tag = tag
338293 self.wqid = wqid
294+ self.nwqid = len(wqid)
339295
340296 def decode(self, size, tag, stream):
341297
@@ -348,14 +304,16 @@ class Rwalk(StyxMessage): · 6 unmodified lines
348304 self.wqid.append(decode_qid(stream))
349305 i += 1
350306
307+ self.format.append(("wqid", "nwqid"))
308+
351309 return self
352310
353311 def encode(self, stream):
354312
355− data = struct.pack("<bH", self.code, self.tag)
313+ data = struct.pack("<BH", self.code, self.tag)
356314
357315 self.nwqid = len(self.wqid)
358− data += self._encode_format(stream)
316+ data += encode_format(stream, self)
359317
360318 # Encode the qids separately.
361319 for qid in self.wqid:
@@ -365,6 +323,171 @@ class Rwalk(StyxMessage): · 3 unmodified lines
365323 stream.sendall(data)
366324
367325
326+class Topen(StyxMessage):
327+
328+ msg_name = "Topen"
329+ code = 112
330+ format = [("fid", 4), ("mode", 1)]
331+
332+ def __init__(self, tag = None, fid = None, mode = None):
333+
334+ self.tag = tag
335+ self.fid = fid
336+ self.mode = mode
337+
338+class Ropen(StyxMessage):
339+
340+ msg_name = "Ropen"
341+ code = 113
342+ format = [("qid", 13), ("iounit", 4)]
343+
344+ def __init__(self, tag = None, qid = None, iounit = None):
345+
346+ self.tag = tag
347+ self.qid = qid
348+ self.iounit = iounit
349+
350+
351+class Tread(StyxMessage):
352+
353+ msg_name = "Tread"
354+ code = 116
355+ format = [("fid", 4), ("offset", 8), ("count", 4)]
356+
357+ def __init__(self, tag = None, fid = None, offset = 0, count = 0):
358+
359+ self.tag = tag
360+ self.fid = fid
361+ self.offset = offset
362+ self.count = count
363+
364+class Rread(StyxMessage):
365+
366+ msg_name = "Rread"
367+ code = 117
368+ format = [("count", 4), ("data", "count")]
369+
370+ def __init__(self, tag = None, data = ""):
371+
372+ self.tag = tag
373+ self.data = data
374+ self.count = len(data)
375+
376+
377+class Tclunk(StyxMessage):
378+
379+ msg_name = "Tclunk"
380+ code = 120
381+ format = [("fid", 4)]
382+
383+ def __init__(self, tag = None, fid = None):
384+
385+ self.tag = tag
386+ self.fid = fid
387+
388+class Rclunk(StyxMessage):
389+
390+ msg_name = "Rclunk"
391+ code = 121
392+ format = []
393+
394+ def __init__(self, tag = None):
395+
396+ self.tag = tag
397+
398+
399+class Tstat(StyxMessage):
400+
401+ msg_name = "Tstat"
402+ code = 124
403+ format = [("fid", 4)]
404+
405+ def __init__(self, tag = None, fid = None):
406+
407+ self.tag = tag
408+ self.fid = fid
409+
410+class Rstat(StyxMessage):
411+
412+ msg_name = "Rstat"
413+ code = 125
414+ # Because the format of this message describes the stat information as
415+ # stat[n] it includes a 16-bit length, but the stat object itself
416+ # includes its own length field.
417+ format = [("stat_size", 2), ("stat", "stat")]
418+
419+ def __init__(self, tag = None, stat = None):
420+
421+ self.tag = tag
422+ self.stat = stat
423+
424+ def __repr__(self):
425+
426+ return self.msg_name + "(tag=%s, stat=%s)" % (repr(self.tag), repr(self.stat))
427+
428+ def encode(self, stream):
429+
430+ # Encode the stat structure.
431+ stat_data = self.stat.encode()
432+
433+ # Prepend the size of the stat structure itself.
434+ stat_data = encode_data(stat_data)
435+
436+ data = struct.pack("<BH", self.code, self.tag) + stat_data
437+
438+ data = struct.pack("<I", len(data) + 4) + data
439+ stream.sendall(data)
440+
441+
442+class Stat:
443+
444+ format = [
445+ ("size", 2), ("type", 2), ("dev", 4), ("qid", 13), ("mode", 4),
446+ ("atime", 4), ("mtime", 4), ("length", 8), ("name", "s"), ("uid", "s"),
447+ ("gid", "s"), ("muid", "s")
448+ ]
449+
450+ def __init__(self, type = 0, dev = 0, qid = (0, 0, 0), mode = 0,
451+ atime = 0, mtime = 0, length = 0, name = u"", uid = u"",
452+ gid = "", muid = u""):
453+
454+ self.type = type
455+ self.dev = dev
456+ self.qid = qid
457+ self.mode = mode
458+ self.atime = atime
459+ self.mtime = mtime
460+ self.length = length
461+ self.name = name
462+ self.uid = uid
463+ self.gid = gid
464+ self.muid = muid
465+
466+ def __repr__(self):
467+
468+ pieces = []
469+ for name, length in self.format:
470+ pieces.append("%s=%s" % (name, repr(self.__dict__[name])))
471+
472+ return "Stat(" + ", ".join(pieces) + ")"
473+
474+ def decode(self, stream):
475+
476+ decode_format(stream, self)
477+
478+ def encode(self):
479+
480+ args = (self.type, self.dev) + self.qid + (self.mode, self.atime, self.mtime, self.length)
481+ data = struct.pack("<HIBIQIIIQ", *args)
482+ data += encode_string(self.name)
483+ data += encode_string(self.uid)
484+ data += encode_string(self.gid)
485+ data += encode_string(self.muid)
486+
487+ # Prepend the size of the fields within the stat structure.
488+ return encode_data(data)
489+
490+
368491 MessageTypes = {
369492 Tversion.code: Tversion,
370493 Rversion.code: Rversion,
@@ -373,21 +496,21 @@ MessageTypes = { · 2 unmodified lines
373496 Tattach.code: Tattach,
374497 Rattach.code: Rattach,
375498 # Terror.code: Terror,
376−# Rerror.code: Rerror,
499+ Rerror.code: Rerror,
377500 # Tflush.code: Tflush,
378501 # Rflush.code: Rflush,
379502 Twalk.code: Twalk,
380503 Rwalk.code: Rwalk,
381−# Topen.code: Topen,
382−# Ropen.code: Ropen,
504+ Topen.code: Topen,
505+ Ropen.code: Ropen,
383506 # Tcreate.code: Tcreate,
384507 # Rcreate.code: Rcreate,
385−# Tread.code: Tread,
386−# Rread.code: Rread,
508+ Tread.code: Tread,
509+ Rread.code: Rread,
387510 # Twrite.code: Twrite,
388511 # Rwrite.code: Rwrite,
389−# Tclunk.code: Tclunk,
390−# Rclunk.code: Rclunk,
512+ Tclunk.code: Tclunk,
513+ Rclunk.code: Rclunk,
391514 # Tremove.code: Tremove,
392515 # Rremove.code: Rremove,
393516 Tstat.code: Tstat,
@@ -412,5 +535,8 @@ def decode(sock = None, data = None): · 18 unmodified lines
412535
413536 # Find the relevant message class to handle this type and create an
414537 # instance of it to parse the message data.
415− Message = MessageTypes[message_type]
416− return Message().decode(size, tag, stream)
538+ try:
539+ Message = MessageTypes[message_type]
540+ return Message().decode(size, tag, stream)
541+ except:
542+ return size, message_type, tag, stream