Unified · Split
| @@ -45,122 +45,339 @@ class SocketReceiver: · 44 unmodified lines | |||
| 45 | return data | 45 | return data |
| 46 | 46 | ||
| 47 | 47 | ||
| 48 | class StyxMessage: | 48 | def decode_string(stream): |
| 49 | 49 | ||
| 50 | def decode_string(self, stream): | 50 | length = struct.unpack("<H", stream.recv(2))[0] |
| 51 | 51 | if length > 0: | |
| 52 | length = struct.unpack("<H", stream.recv(2))[0] | ||
| 53 | return stream.recv(length).decode("utf8") | 52 | return stream.recv(length).decode("utf8") |
| 53 | else: | ||
| 54 | return u"" | ||
| 54 | 55 | ||
| 56 | def encode_string(string): | ||
| 57 | |||
| 58 | utf8 = string.encode("utf8") | ||
| 59 | return struct.pack("<H", len(utf8)) + utf8 | ||
| 60 | |||
| 61 | def decode_data(stream): | ||
| 62 | |||
| 63 | n = struct.unpack("<H", stream.recv(2))[0] | ||
| 64 | return stream.recv(n) | ||
| 65 | |||
| 66 | def encode_data(data): | ||
| 67 | |||
| 68 | return struct.pack("<H", len(data)) + data | ||
| 69 | |||
| 70 | def decode_qid(stream): | ||
| 71 | |||
| 72 | # Store a tuple of values: file/dir, qid version, qid path. | ||
| 73 | return struct.unpack("<BIQ", stream.recv(13)) | ||
| 74 | |||
| 75 | def encode_qid(qid): | ||
| 76 | |||
| 77 | return struct.pack("<BIQ", *qid) | ||
| 78 | |||
| 79 | |||
| 80 | class StyxError(Exception): | ||
| 81 | pass | ||
| 82 | |||
| 83 | |||
| 84 | class StyxMessage: | ||
| 85 | |||
| 55 | def init(self, size, tag): | 86 | def init(self, size, tag): |
| 56 | 87 | ||
| 57 | self.size = size | 88 | self.size = size |
| 58 | self.tag = tag | 89 | self.tag = tag |
| 59 | 90 | ||
| 60 | def encode_string(self, string): | 91 | def decode(self, size, tag, stream): |
| 61 | |||
| 62 | utf8 = string.encode("utf8") | ||
| 63 | return struct.pack("<H", len(utf8)) + utf8 | ||
| 64 | 92 | ||
| 65 | def _parse_format(self, stream): | 93 | self.init(size, tag) |
| 66 | |||
| 67 | self.fields = { | ||
| 68 | "size": repr(self.size), | ||
| 69 | "tag": repr(self.tag) | ||
| 70 | } | ||
| 71 | 94 | ||
| 72 | for piece in self.format.split()[3:]: | 95 | for name, length in self.format: |
| 73 | 96 | ||
| 74 | begin = piece.find("[") | 97 | if length == "s": |
| 75 | 98 | # UTF-8 string | |
| 76 | if begin != -1: | 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) | ||
| 77 | 113 | ||
| 78 | name = piece[:begin] | 114 | self.__dict__[name] = value |
| 79 | end = piece.find("]", begin) | 115 | |
| 80 | length = piece[begin + 1:end] | ||
| 81 | |||
| 82 | if length == "s": | ||
| 83 | # UTF-8 string | ||
| 84 | value = self.decode_string(stream) | ||
| 85 | elif length == "n": | ||
| 86 | # n bytes of raw data | ||
| 87 | n = struct.unpack("<H", stream.recv(2)) | ||
| 88 | value = stream.recv(n) | ||
| 89 | elif length == "2": | ||
| 90 | value = struct.unpack("<H", stream.recv(2)) | ||
| 91 | elif length == "4": | ||
| 92 | value = struct.unpack("<I", stream.recv(4)) | ||
| 93 | else: | ||
| 94 | raise | ||
| 95 | |||
| 96 | self.fields[name] = repr(value) | ||
| 97 | self.__dict__[name] = value | ||
| 98 | |||
| 99 | def parse(self, size, tag, stream): | ||
| 100 | |||
| 101 | self.init(size, tag) | ||
| 102 | self._parse_format(stream) | ||
| 103 | return self | 116 | return self |
| 104 | 117 | ||
| 105 | def __repr__(self): | 118 | def __repr__(self): |
| 106 | 119 | ||
| 107 | return self.repr_format % self.fields | 120 | s = self.msg_name + "(tag=" + repr(self.tag) |
| 121 | |||
| 122 | for name, length in self.format: | ||
| 123 | s += ", " + "%s=%s" % (name, repr(self.__dict__[name])) | ||
| 124 | |||
| 125 | return s + ")" | ||
| 126 | |||
| 127 | def encode(self, stream): | ||
| 128 | |||
| 129 | # 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) | ||
| 133 | |||
| 134 | # Finally, prepend the length to the byte string and send it. | ||
| 135 | data = struct.pack("<I", len(data) + 4) + data | ||
| 136 | 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 | ||
| 108 | 162 | ||
| 109 | 163 | ||
| 110 | class Tversion(StyxMessage): | 164 | class Tversion(StyxMessage): |
| 111 | 165 | ||
| 166 | msg_name = "Tversion" | ||
| 112 | code = 100 | 167 | code = 100 |
| 113 | format = "size[4] Tversion tag[2] msize[4] version[s]" | 168 | format = [("msize", 4), ("version", "s")] |
| 114 | repr_format = "Tversion(tag=%(tag)s, msize=%(msize)s, version=%(version)s)" | ||
| 115 | 169 | ||
| 116 | def __init__(self, tag = None, msize = 0, version = ""): | 170 | def __init__(self, tag = None, msize = 0, version = ""): |
| 117 | 171 | ||
| 118 | self.tag = tag | 172 | self.tag = tag |
| 119 | self.msize = msize | 173 | self.msize = msize |
| 120 | self.version = version | 174 | self.version = version |
| 121 | |||
| 122 | def write(self, stream): | ||
| 123 | |||
| 124 | data = struct.pack("<bHI", self.code, self.tag, self.msize) | ||
| 125 | data += self.encode_string(self.version) | ||
| 126 | data = struct.pack("<I", len(data) + 4) + data | ||
| 127 | stream.sendall(data) | ||
| 128 | |||
| 129 | 175 | ||
| 130 | class Rversion(StyxMessage): | 176 | class Rversion(StyxMessage): |
| 131 | 177 | ||
| 178 | msg_name = "Rversion" | ||
| 132 | code = 101 | 179 | code = 101 |
| 133 | format = "size[4] Rversion tag[2] msize[4] version[s]" | 180 | format = [("msize", 4), ("version", "s")] |
| 134 | repr_format = "Rversion(tag=%(tag)s, msize=%(msize)s, version=%(version)s)" | ||
| 135 | 181 | ||
| 136 | def __init__(self, tag = None, msize = 0, version = ""): | 182 | def __init__(self, tag = None, msize = 0, version = ""): |
| 137 | 183 | ||
| 138 | self.tag = tag | 184 | self.tag = tag |
| 139 | self.msize = msize | 185 | self.msize = msize |
| 140 | self.version = version | 186 | self.version = version |
| 187 | |||
| 188 | |||
| 189 | class Tattach(StyxMessage): | ||
| 190 | |||
| 191 | msg_name = "Tattach" | ||
| 192 | code = 104 | ||
| 193 | format = [("fid", 4), ("afid", 4), ("uname", "s"), ("aname", "s")] | ||
| 194 | |||
| 195 | def __init__(self, tag = None, fid = None, afid = None, uname = None, aname = None): | ||
| 196 | |||
| 197 | self.tag = tag | ||
| 198 | self.fid = fid | ||
| 199 | self.afid = afid | ||
| 200 | self.uname = uname | ||
| 201 | self.aname = aname | ||
| 202 | |||
| 203 | class Rattach(StyxMessage): | ||
| 204 | |||
| 205 | msg_name = "Rattach" | ||
| 206 | code = 105 | ||
| 207 | format = [("qid", 13)] | ||
| 208 | |||
| 209 | def __init__(self, tag = None, qid = None): | ||
| 210 | |||
| 211 | self.tag = tag | ||
| 212 | self.qid = qid | ||
| 213 | |||
| 214 | |||
| 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): | ||
| 227 | |||
| 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 | ] | ||
| 141 | 238 | ||
| 142 | def write(self, stream): | 239 | def __init__(self, tag = None, stat = None): |
| 143 | 240 | ||
| 144 | data = struct.pack("<bHI", self.code, self.tag, self.msize) | 241 | self.tag = tag |
| 145 | data += self.encode_string(self.version) | 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 | |||
| 146 | data = struct.pack("<I", len(data) + 4) + data | 251 | data = struct.pack("<I", len(data) + 4) + data |
| 147 | stream.sendall(data) | 252 | stream.sendall(data) |
| 148 | 253 | ||
| 149 | 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) | ||
| 286 | |||
| 287 | |||
| 288 | class Twalk(StyxMessage): | ||
| 289 | |||
| 290 | msg_name = "Twalk" | ||
| 291 | code = 110 | ||
| 292 | format = [("fid", 4), ("newfid", 4), ("nwname", 2)] # nwname of wname[s] | ||
| 293 | |||
| 294 | def __init__(self, tag = None, fid = None, newfid = None, wname = None): | ||
| 295 | |||
| 296 | self.tag = tag | ||
| 297 | self.fid = fid | ||
| 298 | self.newfid = newfid | ||
| 299 | self.wname = wname | ||
| 300 | |||
| 301 | def decode(self, size, tag, stream): | ||
| 302 | |||
| 303 | StyxMessage.decode(self, size, tag, stream) | ||
| 304 | |||
| 305 | # Decode the names. | ||
| 306 | self.wname = [] | ||
| 307 | i = 0 | ||
| 308 | while i < self.nwname: | ||
| 309 | self.wname.append(decode_string(stream)) | ||
| 310 | i += 1 | ||
| 311 | |||
| 312 | return self | ||
| 313 | |||
| 314 | def encode(self, stream): | ||
| 315 | |||
| 316 | data = struct.pack("<bH", self.code, self.tag) | ||
| 317 | |||
| 318 | self.nwname = len(self.wname) | ||
| 319 | data += self._encode_format(stream) | ||
| 320 | |||
| 321 | # Encode the names separately. | ||
| 322 | for name in self.wname: | ||
| 323 | data += encode_string(name) | ||
| 324 | |||
| 325 | data = struct.pack("<I", len(data) + 4) + data | ||
| 326 | stream.sendall(data) | ||
| 327 | |||
| 328 | |||
| 329 | class Rwalk(StyxMessage): | ||
| 330 | |||
| 331 | msg_name = "Rwalk" | ||
| 332 | code = 111 | ||
| 333 | format = [("nwqid", 2)] # nwqid of wqid[13] | ||
| 334 | |||
| 335 | def __init__(self, tag = None, wqid = None): | ||
| 336 | |||
| 337 | self.tag = tag | ||
| 338 | self.wqid = wqid | ||
| 339 | |||
| 340 | def decode(self, size, tag, stream): | ||
| 341 | |||
| 342 | StyxMessage.decode(self, size, tag, stream) | ||
| 343 | |||
| 344 | # Decode the qids. | ||
| 345 | self.wqid = [] | ||
| 346 | i = 0 | ||
| 347 | while i < self.nwqid: | ||
| 348 | self.wqid.append(decode_qid(stream)) | ||
| 349 | i += 1 | ||
| 350 | |||
| 351 | return self | ||
| 352 | |||
| 353 | def encode(self, stream): | ||
| 354 | |||
| 355 | data = struct.pack("<bH", self.code, self.tag) | ||
| 356 | |||
| 357 | self.nwqid = len(self.wqid) | ||
| 358 | data += self._encode_format(stream) | ||
| 359 | |||
| 360 | # Encode the qids separately. | ||
| 361 | for qid in self.wqid: | ||
| 362 | data += encode_qid(qid) | ||
| 363 | |||
| 364 | data = struct.pack("<I", len(data) + 4) + data | ||
| 365 | stream.sendall(data) | ||
| 366 | |||
| 150 | 367 | ||
| 151 | MessageTypes = { | 368 | MessageTypes = { |
| 152 | Tversion.code: Tversion, | 369 | Tversion.code: Tversion, |
| 153 | Rversion.code: Rversion, | 370 | Rversion.code: Rversion, |
| 154 | # Tauth.code: Tauth, | 371 | # Tauth.code: Tauth, |
| 155 | # Rauth.code: Rauth, | 372 | # Rauth.code: Rauth, |
| 156 | # Tattach.code: Tattach, | 373 | Tattach.code: Tattach, |
| 157 | # Rattach.code: Rattach, | 374 | Rattach.code: Rattach, |
| 158 | # Terror.code: Terror, | 375 | # Terror.code: Terror, |
| 159 | # Rerror.code: Rerror, | 376 | # Rerror.code: Rerror, |
| 160 | # Tflush.code: Tflush, | 377 | # Tflush.code: Tflush, |
| 161 | # Rflush.code: Rflush, | 378 | # Rflush.code: Rflush, |
| 162 | # Twalk.code: Twalk, | 379 | Twalk.code: Twalk, |
| 163 | # Rwalk.code: Rwalk, | 380 | Rwalk.code: Rwalk, |
| 164 | # Topen.code: Topen, | 381 | # Topen.code: Topen, |
| 165 | # Ropen.code: Ropen, | 382 | # Ropen.code: Ropen, |
| 166 | # Tcreate.code: Tcreate, | 383 | # Tcreate.code: Tcreate, |
| @@ -173,14 +390,13 @@ MessageTypes = { · 6 unmodified lines | |||
| 173 | # Rclunk.code: Rclunk, | 390 | # Rclunk.code: Rclunk, |
| 174 | # Tremove.code: Tremove, | 391 | # Tremove.code: Tremove, |
| 175 | # Rremove.code: Rremove, | 392 | # Rremove.code: Rremove, |
| 176 | # Tstat.code: Tstat, | 393 | Tstat.code: Tstat, |
| 177 | # Rstat.code: Rstat, | 394 | Rstat.code: Rstat, |
| 178 | # Twstat.code: Twstat, | 395 | # Twstat.code: Twstat, |
| 179 | # Rwstat.code: Rwstat | 396 | # Rwstat.code: Rwstat |
| 180 | } | 397 | } |
| 181 | 398 | ||
| 182 | 399 | def decode(sock = None, data = None): | |
| 183 | def parse(sock = None, data = None): | ||
| 184 | 400 | ||
| 185 | if sock: | 401 | if sock: |
| 186 | stream = SocketReceiver(sock) | 402 | stream = SocketReceiver(sock) |
| @@ -197,4 +413,4 @@ def parse(sock = None, data = None): · 10 unmodified lines | |||
| 197 | # Find the relevant message class to handle this type and create an | 413 | # Find the relevant message class to handle this type and create an |
| 198 | # instance of it to parse the message data. | 414 | # instance of it to parse the message data. |
| 199 | Message = MessageTypes[message_type] | 415 | Message = MessageTypes[message_type] |
| 200 | return Message().parse(size, tag, stream) | 416 | return Message().decode(size, tag, stream) |