711 lines · 16.8 KB · Python
RawDownload
# styx.py - Classes to decode and encode Styx (9P2000) messages.## Copyright (C) 2018 David Boddie <david@boddie.org.uk>## This program is free software: you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation, either version 3 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program.  If not, see <http://www.gnu.org/licenses/>. # Using the information from the Inferno man 5 pages. import struct class StringReceiver:     def __init__(self, string):        self.string = string        self.ptr = 0        def recv(self, n):        end = self.ptr + n        data = self.string[self.ptr:end]        self.ptr = end        return data  class SocketReceiver:     def __init__(self, sock):        self.sock = sock        def recv(self, n):        data = b""        while len(data) < n:            data += self.sock.recv(n - len(data))                return data  def decode_string(stream):     length = struct.unpack("<H", stream.recv(2))[0]    if length > 0:        return stream.recv(length).decode("utf8")    else:        return u""    def encode_string(string):     utf8 = string.encode("utf8")    return struct.pack("<H", len(utf8)) + utf8 def decode_data(stream):     n = struct.unpack("<H", stream.recv(2))[0]    return stream.recv(n) def encode_data(data):     return struct.pack("<H", len(data)) + data def decode_qid(stream):     # Store a tuple of values: file/dir, qid version, qid path.    return struct.unpack("<BIQ", stream.recv(13)) def encode_qid(qid):     return struct.pack("<BIQ", *qid) def decode_format(stream, obj):     for name, length in obj.format:            if length == "s":            # UTF-8 string            value = decode_string(stream)        elif length == "n":            # n bytes of raw data            value = decode_data(stream)        elif length == 1:            value = struct.unpack("<B", stream.recv(1))[0]        elif length == 2:            value = struct.unpack("<H", stream.recv(2))[0]        elif length == 4:            value = struct.unpack("<I", stream.recv(4))[0]        elif length == 8:            value = struct.unpack("<Q", stream.recv(8))[0]        elif name == "qid":            value = decode_qid(stream)        elif name == "stat":            value = Stat()            value.decode(stream)        elif length in obj.__dict__:            value = stream.recv(obj.__dict__[length])        else:            raise StyxError("decode_format: Unknown field length specifier: %s" % length)                obj.__dict__[name] = value def encode_format(stream, obj):     data = b""        for name, length in obj.format:            value = obj.__dict__[name]                if length == "s":            # UTF-8 string            data += encode_string(value)        elif length == "n":            # n bytes of raw data            data += encode_data(value)        elif length == 1:            data += struct.pack("<B", value)        elif length == 2:            data += struct.pack("<H", value)        elif length == 4:            data += struct.pack("<I", value)        elif length == 8:            data += struct.pack("<Q", value)        elif name == "qid":            data += encode_qid(value)        elif name == "stat":            data += value.encode()        else:            data += value        return data  class StyxError(Exception):    pass  class StyxMessage:     def init(self, size, tag):            self.size = size        self.tag = tag        def __repr__(self):            s = self.msg_name + "(tag=" + repr(self.tag)                for name, length in self.format:            s += ", " + "%s=%s" % (name, repr(self.__dict__[name]))                return s + ")"        def decode(self, size, tag, stream):            self.init(size, tag)        decode_format(stream, self)        return self        def encode(self, stream):            # Start with the message type and tag before adding all the other pieces.        data = struct.pack("<BH", self.code, self.tag)        data += encode_format(stream, self)                # Finally, prepend the length to the byte string and send it.        data = struct.pack("<I", len(data) + 4) + data        stream.sendall(data)  class Tversion(StyxMessage):     msg_name = "Tversion"    code = 100    format = [("msize", 4), ("version", "s")]        def __init__(self, tag = None, msize = 0, version = ""):            self.tag = tag        self.msize = msize        self.version = version class Rversion(StyxMessage):     msg_name = "Rversion"    code = 101    format = [("msize", 4), ("version", "s")]        def __init__(self, tag = None, msize = 0, version = ""):            self.tag = tag        self.msize = msize        self.version = version  class Tattach(StyxMessage):     msg_name = "Tattach"    code = 104    format = [("fid", 4), ("afid", 4), ("uname", "s"), ("aname", "s")]        NOFID = 0xffffffff        def __init__(self, tag = None, fid = None, afid = None, uname = None, aname = None):            self.tag = tag        self.fid = fid        self.afid = afid        self.uname = uname        self.aname = aname class Rattach(StyxMessage):     msg_name = "Rattach"    code = 105    format = [("qid", 13)]        def __init__(self, tag = None, qid = None):            self.tag = tag        self.qid = qid  class Rerror(StyxMessage):     msg_name = "Rerror"    code = 107    format = [("ename", "s")]        def __init__(self, tag = None, ename = None):            self.tag = tag        self.ename = ename  class Tflush(StyxMessage):     msg_name = "Tflush"    code = 108    format = [("oldtag", 4)]        def __init__(self, tag = None, fid = None):            self.tag = tag        self.fid = fid class Rflush(StyxMessage):     msg_name = "Rflush"    code = 109    format = []        def __init__(self, tag = None):            self.tag = tag  class Twalk(StyxMessage):     msg_name = "Twalk"    code = 110    format = [("fid", 4), ("newfid", 4), ("nwname", 2)] # nwname of wname[s]        def __init__(self, tag = None, fid = None, newfid = None, wname = []):            self.tag = tag        self.fid = fid        self.newfid = newfid        self.wname = wname        self.nwname = len(wname)        def decode(self, size, tag, stream):            StyxMessage.decode(self, size, tag, stream)                # Decode the names.        self.wname = []        i = 0        while i < self.nwname:            self.wname.append(decode_string(stream))            i += 1                return self        def encode(self, stream):            data = struct.pack("<BH", self.code, self.tag)                self.nwname = len(self.wname)        data += encode_format(stream, self)                # Encode the names separately.        for name in self.wname:            data += encode_string(name)                data = struct.pack("<I", len(data) + 4) + data        stream.sendall(data) class Rwalk(StyxMessage):     msg_name = "Rwalk"    code = 111    format = [("nwqid", 2)] # nwqid of wqid[13]        def __init__(self, tag = None, wqid = []):            self.tag = tag        self.wqid = wqid        self.nwqid = len(wqid)        def decode(self, size, tag, stream):            StyxMessage.decode(self, size, tag, stream)                # Decode the qids.        self.wqid = []        i = 0        while i < self.nwqid:            self.wqid.append(decode_qid(stream))            i += 1                return self        def encode(self, stream):            data = struct.pack("<BH", self.code, self.tag)                self.nwqid = len(self.wqid)        data += encode_format(stream, self)                # Encode the qids separately.        for qid in self.wqid:            data += encode_qid(qid)                data = struct.pack("<I", len(data) + 4) + data        stream.sendall(data)  class Topen(StyxMessage):     msg_name = "Topen"    code = 112    format = [("fid", 4), ("mode", 1)]        def __init__(self, tag = None, fid = None, mode = None):            self.tag = tag        self.fid = fid        self.mode = mode class Ropen(StyxMessage):     msg_name = "Ropen"    code = 113    format = [("qid", 13), ("iounit", 4)]        def __init__(self, tag = None, qid = None, iounit = None):            self.tag = tag        self.qid = qid        self.iounit = iounit  class Tcreate(StyxMessage):     msg_name = "Tcreate"    code = 114    format = [("fid", 4), ("name", "s"), ("perm", 4), ("mode", 1)]        def __init__(self, tag = None, fid = None, name = "", perm = 0, mode = None):            self.tag = tag        self.fid = fid        self.name = name        self.perm = perm        self.mode = mode class Rcreate(StyxMessage):     msg_name = "Rcreate"    code = 115    format = [("qid", 13), ("iounit", 4)]        def __init__(self, tag = None, qid = None, iounit = None):            self.tag = tag        self.qid = qid        self.iounit = iounit  class Tread(StyxMessage):     msg_name = "Tread"    code = 116    format = [("fid", 4), ("offset", 8), ("count", 4)]        def __init__(self, tag = None, fid = None, offset = 0, count = 0):            self.tag = tag        self.fid = fid        self.offset = offset        self.count = count class Rread(StyxMessage):     msg_name = "Rread"    code = 117    format = [("count", 4), ("data", "count")]        def __init__(self, tag = None, data = ""):            self.tag = tag        self.data = data        self.count = len(data)  class Twrite(StyxMessage):     msg_name = "Twrite"    code = 118    format = [("fid", 4), ("offset", 8), ("count", 4), ("data", "count")]        def __init__(self, tag = None, fid = None, offset = 0, data = ""):            self.tag = tag        self.fid = fid        self.offset = offset        self.count = len(data)        self.data = data class Rwrite(StyxMessage):     msg_name = "Rwrite"    code = 119    format = [("count", 4)]        def __init__(self, tag = None, count = 0):            self.tag = tag        self.count = count  class Tclunk(StyxMessage):     msg_name = "Tclunk"    code = 120    format = [("fid", 4)]        def __init__(self, tag = None, fid = None):            self.tag = tag        self.fid = fid class Rclunk(StyxMessage):     msg_name = "Rclunk"    code = 121    format = []        def __init__(self, tag = None):            self.tag = tag  class Tremove(StyxMessage):     msg_name = "Tremove"    code = 122    format = [("fid", 4)]        def __init__(self, tag = None, fid = None):            self.tag = tag        self.fid = fid class Rremove(StyxMessage):     msg_name = "Rremove"    code = 123    format = []        def __init__(self, tag = None):            self.tag = tag  class Tstat(StyxMessage):     msg_name = "Tstat"    code = 124    format = [("fid", 4)]        def __init__(self, tag = None, fid = None):

Lines 1–500 of 711.