1*134e1779SJakub Wojciech Klama#! /usr/bin/env python 2*134e1779SJakub Wojciech Klama 3*134e1779SJakub Wojciech Klama""" 4*134e1779SJakub Wojciech KlamaProtocol definitions for python based lib9p server/client. 5*134e1779SJakub Wojciech Klama 6*134e1779SJakub Wojciech KlamaThe sub-namespace td has type definitions (qid, stat) and values 7*134e1779SJakub Wojciech Klamathat are "#define" constants in C code (e.g., DMDIR, QTFILE, etc). 8*134e1779SJakub Wojciech KlamaThis also contains the byte values for protocol codes like Tversion, 9*134e1779SJakub Wojciech KlamaRversion, Rerror, and so on. 10*134e1779SJakub Wojciech Klama 11*134e1779SJakub Wojciech Klama >>> td.Tversion 12*134e1779SJakub Wojciech Klama 100 13*134e1779SJakub Wojciech Klama >>> td.Rlerror 14*134e1779SJakub Wojciech Klama 7 15*134e1779SJakub Wojciech Klama 16*134e1779SJakub Wojciech KlamaThe qid and stat types are PFOD classes and generate instances that 17*134e1779SJakub Wojciech Klamaare a cross between namedtuple and OrderedDictionary (see pfod.py 18*134e1779SJakub Wojciech Klamafor details): 19*134e1779SJakub Wojciech Klama 20*134e1779SJakub Wojciech Klama >>> td.qid(type=td.QTFILE, path=2, version=1) 21*134e1779SJakub Wojciech Klama qid(type=0, version=1, path=2) 22*134e1779SJakub Wojciech Klama 23*134e1779SJakub Wojciech KlamaThe td.stat() type output is pretty long, since it has all the 24*134e1779SJakub Wojciech Klamadotu-specific members (used only when packing for dotu/dotl and 25*134e1779SJakub Wojciech Klamaset only when unpacking those), so here's just one field: 26*134e1779SJakub Wojciech Klama 27*134e1779SJakub Wojciech Klama >>> td.stat(*(15 * [0])).mode 28*134e1779SJakub Wojciech Klama 0 29*134e1779SJakub Wojciech Klama >>> import pprint; pprint.pprint(td.stat()._fields) 30*134e1779SJakub Wojciech Klama ('type', 31*134e1779SJakub Wojciech Klama 'dev', 32*134e1779SJakub Wojciech Klama 'qid', 33*134e1779SJakub Wojciech Klama 'mode', 34*134e1779SJakub Wojciech Klama 'atime', 35*134e1779SJakub Wojciech Klama 'mtime', 36*134e1779SJakub Wojciech Klama 'length', 37*134e1779SJakub Wojciech Klama 'name', 38*134e1779SJakub Wojciech Klama 'uid', 39*134e1779SJakub Wojciech Klama 'gid', 40*134e1779SJakub Wojciech Klama 'muid', 41*134e1779SJakub Wojciech Klama 'extension', 42*134e1779SJakub Wojciech Klama 'n_uid', 43*134e1779SJakub Wojciech Klama 'n_gid', 44*134e1779SJakub Wojciech Klama 'n_muid') 45*134e1779SJakub Wojciech Klama 46*134e1779SJakub Wojciech KlamaStat objects sent across the protocol must first be encoded into 47*134e1779SJakub Wojciech Klamawirestat objects, which are basically size-counted pre-sequenced 48*134e1779SJakub Wojciech Klamastat objects. The pre-sequencing uses: 49*134e1779SJakub Wojciech Klama 50*134e1779SJakub Wojciech Klama >>> td.stat_seq 51*134e1779SJakub Wojciech Klama Sequencer('stat') 52*134e1779SJakub Wojciech Klama 53*134e1779SJakub Wojciech KlamaFor parsing bytes returned in a Tread on a directory, td.wirestat_seq 54*134e1779SJakub Wojciech Klamais the sequencer. However, most users should rely on the packers and 55*134e1779SJakub Wojciech Klamaunpackers in each protocol (see {pack,unpack}_wirestat below). 56*134e1779SJakub Wojciech Klama 57*134e1779SJakub Wojciech Klama >>> td.wirestat_seq 58*134e1779SJakub Wojciech Klama Sequencer('wirestat') 59*134e1779SJakub Wojciech Klama 60*134e1779SJakub Wojciech KlamaThere is a dictionary fcall_to_name that maps from byte value 61*134e1779SJakub Wojciech Klamato protocol code. Names map to themselves as well: 62*134e1779SJakub Wojciech Klama 63*134e1779SJakub Wojciech Klama >>> fcall_names[101] 64*134e1779SJakub Wojciech Klama 'Rversion' 65*134e1779SJakub Wojciech Klama >>> fcall_names['Tversion'] 66*134e1779SJakub Wojciech Klama 'Tversion' 67*134e1779SJakub Wojciech Klama 68*134e1779SJakub Wojciech KlamaThe sub-namespace rrd has request (Tversion, Topen, etc) and 69*134e1779SJakub Wojciech Klamaresponse (Rversion, Ropen, etc) data definitions. Each of these 70*134e1779SJakub Wojciech Klamais a PFOD class: 71*134e1779SJakub Wojciech Klama 72*134e1779SJakub Wojciech Klama >>> rrd.Tversion(1000, 'hello', tag=0) 73*134e1779SJakub Wojciech Klama Tversion(tag=0, msize=1000, version='hello') 74*134e1779SJakub Wojciech Klama 75*134e1779SJakub Wojciech KlamaThe function p9_version() looks up the instance of each supported 76*134e1779SJakub Wojciech Klamaprotocol, or raises a KeyError when given an invalid protocol. 77*134e1779SJakub Wojciech KlamaThe names may be spelled in any mixture of cases. 78*134e1779SJakub Wojciech Klama 79*134e1779SJakub Wojciech KlamaThe names plain, dotu, and dotl are predefined as the three 80*134e1779SJakub Wojciech Klamasupported protocols: 81*134e1779SJakub Wojciech Klama 82*134e1779SJakub Wojciech Klama >>> p9_version('invalid') 83*134e1779SJakub Wojciech Klama Traceback (most recent call last): 84*134e1779SJakub Wojciech Klama ... 85*134e1779SJakub Wojciech Klama KeyError: 'invalid' 86*134e1779SJakub Wojciech Klama >>> p9_version('9p2000') == plain 87*134e1779SJakub Wojciech Klama True 88*134e1779SJakub Wojciech Klama >>> p9_version('9P2000') == plain 89*134e1779SJakub Wojciech Klama True 90*134e1779SJakub Wojciech Klama >>> p9_version('9P2000.u') == dotu 91*134e1779SJakub Wojciech Klama True 92*134e1779SJakub Wojciech Klama >>> p9_version('9p2000.L') == dotl 93*134e1779SJakub Wojciech Klama True 94*134e1779SJakub Wojciech Klama 95*134e1779SJakub Wojciech KlamaProtocol instances have a pack() method that encodes a set of 96*134e1779SJakub Wojciech Klamaarguments into a packet. To know what to encode, pack() must 97*134e1779SJakub Wojciech Klamareceive an fcall value and a dictionary containing argument 98*134e1779SJakub Wojciech Klamavalues, or something equivalent. The required argument values 99*134e1779SJakub Wojciech Klamadepend on the fcall. For instance, a Tversion fcall needs three 100*134e1779SJakub Wojciech Klamaarguments: the version name, the tag, and the msize (these of 101*134e1779SJakub Wojciech Klamacourse are the pre-filled fields in a Tversion PFOD instance). 102*134e1779SJakub Wojciech Klama 103*134e1779SJakub Wojciech Klama >>> args = {'version': '!', 'tag': 1, 'msize': 1000} 104*134e1779SJakub Wojciech Klama >>> pkt = dotu.pack(fcall='Tversion', args=args) 105*134e1779SJakub Wojciech Klama >>> len(pkt) 106*134e1779SJakub Wojciech Klama 14 107*134e1779SJakub Wojciech Klama 108*134e1779SJakub Wojciech KlamaThe length of string '!' is 1, and the packet (or wire) format of 109*134e1779SJakub Wojciech Klamaa Tversion request is: 110*134e1779SJakub Wojciech Klama 111*134e1779SJakub Wojciech Klama size[4] fcall[1] tag[2] msize[4] version[s] 112*134e1779SJakub Wojciech Klama 113*134e1779SJakub Wojciech Klamawhich corresponds to a struct's IBHIH (for the fixed size parts) 114*134e1779SJakub Wojciech Klamafollowed by 1 B (for the string). The overall packet is 14 bytes 115*134e1779SJakub Wojciech Klamalong, so we have size=9, fcall=100, tag=1, msize=1000, and the 116*134e1779SJakub Wojciech Klamaversion string is length=1, value=33 (ord('!')). 117*134e1779SJakub Wojciech Klama 118*134e1779SJakub Wojciech Klama >>> import struct 119*134e1779SJakub Wojciech Klama >>> struct.unpack('<IBHIHB', pkt) 120*134e1779SJakub Wojciech Klama (14, 100, 1, 1000, 1, 33) 121*134e1779SJakub Wojciech Klama 122*134e1779SJakub Wojciech KlamaOf course, this packed a completely bogus "version" string, but 123*134e1779SJakub Wojciech Klamathat's what we told it to do. Protocol instances remember their 124*134e1779SJakub Wojciech Klamaversion, so we can get it right by omitting the version from the 125*134e1779SJakub Wojciech Klamaarguments: 126*134e1779SJakub Wojciech Klama 127*134e1779SJakub Wojciech Klama >>> dotu.version 128*134e1779SJakub Wojciech Klama '9P2000.u' 129*134e1779SJakub Wojciech Klama >>> args = {'tag': 99, 'msize': 1000} 130*134e1779SJakub Wojciech Klama >>> pkt = dotu.pack(fcall='Tversion', args=args) 131*134e1779SJakub Wojciech Klama >>> len(pkt) 132*134e1779SJakub Wojciech Klama 21 133*134e1779SJakub Wojciech Klama 134*134e1779SJakub Wojciech KlamaThe fcall can be supplied numerically: 135*134e1779SJakub Wojciech Klama 136*134e1779SJakub Wojciech Klama >>> pkt2 = dotu.pack(fcall=td.Tversion, args=args) 137*134e1779SJakub Wojciech Klama >>> pkt == pkt2 138*134e1779SJakub Wojciech Klama True 139*134e1779SJakub Wojciech Klama 140*134e1779SJakub Wojciech KlamaInstead of providing an fcall you can provide an instance of 141*134e1779SJakub Wojciech Klamathe appropriate PFOD. In this case pack() finds the type from 142*134e1779SJakub Wojciech Klamathe PFOD instance. As usual, the version parameter is filled in 143*134e1779SJakub Wojciech Klamafor you: 144*134e1779SJakub Wojciech Klama 145*134e1779SJakub Wojciech Klama >>> pkt2 = dotu.pack(rrd.Tversion(tag=99, msize=1000)) 146*134e1779SJakub Wojciech Klama >>> pkt == pkt2 147*134e1779SJakub Wojciech Klama True 148*134e1779SJakub Wojciech Klama 149*134e1779SJakub Wojciech KlamaNote that it's up to you to check the other end's version and 150*134e1779SJakub Wojciech Klamaswitch to a "lower" protocol as needed. Each instance does provide 151*134e1779SJakub Wojciech Klamaa downgrade_to() method that gets you a possibly-downgraded instance. 152*134e1779SJakub Wojciech KlamaThis will fail if you are actually trying to upgrade, and also if 153*134e1779SJakub Wojciech Klamayou provide a bogus version: 154*134e1779SJakub Wojciech Klama 155*134e1779SJakub Wojciech Klama >>> dotu.downgrade_to('9P2000.L') 156*134e1779SJakub Wojciech Klama Traceback (most recent call last): 157*134e1779SJakub Wojciech Klama ... 158*134e1779SJakub Wojciech Klama KeyError: '9P2000.L' 159*134e1779SJakub Wojciech Klama >>> dotu.downgrade_to('we never heard of this protocol') 160*134e1779SJakub Wojciech Klama Traceback (most recent call last): 161*134e1779SJakub Wojciech Klama ... 162*134e1779SJakub Wojciech Klama KeyError: 'we never heard of this protocol' 163*134e1779SJakub Wojciech Klama 164*134e1779SJakub Wojciech KlamaHence you might use: 165*134e1779SJakub Wojciech Klama 166*134e1779SJakub Wojciech Klama try: 167*134e1779SJakub Wojciech Klama proto = protocol.dotl.downgrade(vstr) 168*134e1779SJakub Wojciech Klama except KeyError: 169*134e1779SJakub Wojciech Klama pkt = protocol.plain.pack(fcall='Rerror', 170*134e1779SJakub Wojciech Klama args={'tag': tag, 'errstr': 'unknown protocol version ' 171*134e1779SJakub Wojciech Klama '{0!r}'.format(vstr)}) 172*134e1779SJakub Wojciech Klama else: 173*134e1779SJakub Wojciech Klama pkt = proto.pack(fcall='Rversion', args={'tag': tag, 'msize': msize}) 174*134e1779SJakub Wojciech Klama 175*134e1779SJakub Wojciech KlamaWhen using a PFOD instance, it is slightly more efficient to use 176*134e1779SJakub Wojciech Klamapack_from(): 177*134e1779SJakub Wojciech Klama 178*134e1779SJakub Wojciech Klama try: 179*134e1779SJakub Wojciech Klama proto = protocol.dotl.downgrade(vstr) 180*134e1779SJakub Wojciech Klama reply = protocol.rrd.Rversion(tag=tag, msize=msize) 181*134e1779SJakub Wojciech Klama except KeyError: 182*134e1779SJakub Wojciech Klama proto = protocol.plain 183*134e1779SJakub Wojciech Klama reply = protocol.rrd.Rerror(tag=tag, 184*134e1779SJakub Wojciech Klama errstr='unknown protocol version {0!r}'.format(vstr)) 185*134e1779SJakub Wojciech Klama pkt = proto.pack_from(reply) 186*134e1779SJakub Wojciech Klama 187*134e1779SJakub Wojciech Klamadoes the equivalent of the try/except/else variant. Note that 188*134e1779SJakub Wojciech Klamathe protocol.rrd.Rversion() instance has version=None. Like 189*134e1779SJakub Wojciech Klamaproto.pack, the pack_from will detect this "missing" value and 190*134e1779SJakub Wojciech Klamafill it in. 191*134e1779SJakub Wojciech Klama 192*134e1779SJakub Wojciech KlamaBecause errors vary (one should use Rlerror for dotl and Rerror 193*134e1779SJakub Wojciech Klamafor dotu and plain), and it's convenient to use an Exception 194*134e1779SJakub Wojciech Klamainstance for an error, all protocols provide .error(). This 195*134e1779SJakub Wojciech Klamabuilds the appropriate kind of error response, extracting and 196*134e1779SJakub Wojciech Klamaconverting errno's and error messages as appropriate. 197*134e1779SJakub Wojciech Klama 198*134e1779SJakub Wojciech KlamaIf <err> is an instance of Exception, err.errno provides the errnum 199*134e1779SJakub Wojciech Klamaor ecode value (if used, for dotu and dotl) and err.strerror as the 200*134e1779SJakub Wojciech Klamaerrstr value (if used, for plain 9p2000). Otherwise err should be 201*134e1779SJakub Wojciech Klamaan integer, and we'll use os.strerror() to get a message. 202*134e1779SJakub Wojciech Klama 203*134e1779SJakub Wojciech KlamaWhen using plain 9P2000 this sends error *messages*: 204*134e1779SJakub Wojciech Klama 205*134e1779SJakub Wojciech Klama >>> import errno, os 206*134e1779SJakub Wojciech Klama >>> utf8 = os.strerror(errno.ENOENT).encode('utf-8') 207*134e1779SJakub Wojciech Klama >>> pkt = None 208*134e1779SJakub Wojciech Klama >>> try: 209*134e1779SJakub Wojciech Klama ... os.open('presumably this file does not exist here', 0) 210*134e1779SJakub Wojciech Klama ... except OSError as err: 211*134e1779SJakub Wojciech Klama ... pkt = plain.error(1, err) 212*134e1779SJakub Wojciech Klama ... 213*134e1779SJakub Wojciech Klama >>> pkt[-len(utf8):] == utf8 214*134e1779SJakub Wojciech Klama True 215*134e1779SJakub Wojciech Klama >>> pkt2 = plain.error(1, errno.ENOENT) 216*134e1779SJakub Wojciech Klama >>> pkt == pkt2 217*134e1779SJakub Wojciech Klama True 218*134e1779SJakub Wojciech Klama 219*134e1779SJakub Wojciech KlamaWhen using 9P2000.u it sends the error code as well, and when 220*134e1779SJakub Wojciech Klamausing 9P2000.L it sends only the error code (and more error 221*134e1779SJakub Wojciech Klamacodes can pass through): 222*134e1779SJakub Wojciech Klama 223*134e1779SJakub Wojciech Klama >>> len(pkt) 224*134e1779SJakub Wojciech Klama 34 225*134e1779SJakub Wojciech Klama >>> len(dotu.error(1, errno.ENOENT)) 226*134e1779SJakub Wojciech Klama 38 227*134e1779SJakub Wojciech Klama >>> len(dotl.error(1, errno.ENOENT)) 228*134e1779SJakub Wojciech Klama 11 229*134e1779SJakub Wojciech Klama 230*134e1779SJakub Wojciech KlamaFor even more convenience (and another slight speed hack), the 231*134e1779SJakub Wojciech Klamaprotocol has member functions for each valid pfod, which 232*134e1779SJakub Wojciech Klamaeffectively do a pack_from of a pfod built from the arguments. In 233*134e1779SJakub Wojciech Klamathe above example this is not very useful (because we want two 234*134e1779SJakub Wojciech Klamadifferent replies), but for Rlink, for instance, which has only 235*134e1779SJakub Wojciech Klamaa tag, a server might implement Tlink() as: 236*134e1779SJakub Wojciech Klama 237*134e1779SJakub Wojciech Klama def do_Tlink(proto, data): # data will be a protocol.rrd.Tlink(...) 238*134e1779SJakub Wojciech Klama tag = data.tag 239*134e1779SJakub Wojciech Klama dfid = data.dfid 240*134e1779SJakub Wojciech Klama fid = data.fid 241*134e1779SJakub Wojciech Klama name = data.name 242*134e1779SJakub Wojciech Klama ... some code to set up for doing the link link ... 243*134e1779SJakub Wojciech Klama try: 244*134e1779SJakub Wojciech Klama os.link(path1, path2) 245*134e1779SJakub Wojciech Klama except OSError as err: 246*134e1779SJakub Wojciech Klama return proto.error(tag, err) 247*134e1779SJakub Wojciech Klama else: 248*134e1779SJakub Wojciech Klama return proto.Rlink(tag) 249*134e1779SJakub Wojciech Klama 250*134e1779SJakub Wojciech Klama >>> pkt = dotl.Rlink(12345) 251*134e1779SJakub Wojciech Klama >>> struct.unpack('<IBH', pkt) 252*134e1779SJakub Wojciech Klama (7, 71, 12345) 253*134e1779SJakub Wojciech Klama 254*134e1779SJakub Wojciech KlamaSimilarly, a client can build a Tversion packet quite trivially: 255*134e1779SJakub Wojciech Klama 256*134e1779SJakub Wojciech Klama >>> vpkt = dotl.Tversion(tag=0, msize=12345) 257*134e1779SJakub Wojciech Klama 258*134e1779SJakub Wojciech KlamaTo see that this is a valid version packet, let's unpack its bytes. 259*134e1779SJakub Wojciech KlamaThe overall length is 21 bytes: 4 bytes of size, 1 byte of code 100 260*134e1779SJakub Wojciech Klamafor Tversion, 2 bytes of tag, 4 bytes of msize, 2 bytes of string 261*134e1779SJakub Wojciech Klamalength, and 8 bytes of string '9P2000.L'. 262*134e1779SJakub Wojciech Klama 263*134e1779SJakub Wojciech Klama >>> tup = struct.unpack('<IBHIH8B', vpkt) 264*134e1779SJakub Wojciech Klama >>> tup[0:5] 265*134e1779SJakub Wojciech Klama (21, 100, 0, 12345, 8) 266*134e1779SJakub Wojciech Klama >>> ''.join(chr(i) for i in tup[5:]) 267*134e1779SJakub Wojciech Klama '9P2000.L' 268*134e1779SJakub Wojciech Klama 269*134e1779SJakub Wojciech KlamaOf course, since you can *pack*, you can also *unpack*. It's 270*134e1779SJakub Wojciech Klamapossible that the incoming packet is malformed. If so, this 271*134e1779SJakub Wojciech Klamaraises various errors (see below). 272*134e1779SJakub Wojciech Klama 273*134e1779SJakub Wojciech KlamaUnpack is actually a two step process: first we unpack a header 274*134e1779SJakub Wojciech Klama(where the size is already removed and is implied by len(data)), 275*134e1779SJakub Wojciech Klamathen we unpack the data within the packet. You can invoke the 276*134e1779SJakub Wojciech Klamafirst step separately. Furthermore, there's a noerror argument 277*134e1779SJakub Wojciech Klamathat leaves some fields set to None or empty strings, if the 278*134e1779SJakub Wojciech Klamapacket is too short. (Note that we need a hack for py2k vs py3k 279*134e1779SJakub Wojciech Klamastrings here, for doctests. Also, encoding 12345 into a byte 280*134e1779SJakub Wojciech Klamastring produces '90', by ASCII luck!) 281*134e1779SJakub Wojciech Klama 282*134e1779SJakub Wojciech Klama >>> pkt = pkt[4:] # strip generated size 283*134e1779SJakub Wojciech Klama >>> import sys 284*134e1779SJakub Wojciech Klama >>> py3k = sys.version_info[0] >= 3 285*134e1779SJakub Wojciech Klama >>> b2s = lambda x: x.decode('utf-8') if py3k else x 286*134e1779SJakub Wojciech Klama >>> d = plain.unpack_header(pkt[0:1], noerror=True) 287*134e1779SJakub Wojciech Klama >>> d.data = b2s(d.data) 288*134e1779SJakub Wojciech Klama >>> d 289*134e1779SJakub Wojciech Klama Header(size=5, dsize=0, fcall=71, data='') 290*134e1779SJakub Wojciech Klama >>> d = plain.unpack_header(pkt[0:2], noerror=True) 291*134e1779SJakub Wojciech Klama >>> d.data = b2s(d.data) 292*134e1779SJakub Wojciech Klama >>> d 293*134e1779SJakub Wojciech Klama Header(size=6, dsize=1, fcall=71, data='9') 294*134e1779SJakub Wojciech Klama 295*134e1779SJakub Wojciech KlamaWithout noerror=True a short packet raises a SequenceError: 296*134e1779SJakub Wojciech Klama 297*134e1779SJakub Wojciech Klama >>> plain.unpack_header(pkt[0:0]) # doctest: +IGNORE_EXCEPTION_DETAIL 298*134e1779SJakub Wojciech Klama Traceback (most recent call last): 299*134e1779SJakub Wojciech Klama ... 300*134e1779SJakub Wojciech Klama SequenceError: out of data while unpacking 'fcall' 301*134e1779SJakub Wojciech Klama 302*134e1779SJakub Wojciech KlamaOf course, a normal packet decodes fine: 303*134e1779SJakub Wojciech Klama 304*134e1779SJakub Wojciech Klama >>> d = plain.unpack_header(pkt) 305*134e1779SJakub Wojciech Klama >>> d.data = b2s(d.data) 306*134e1779SJakub Wojciech Klama >>> d 307*134e1779SJakub Wojciech Klama Header(size=7, dsize=2, fcall=71, data='90') 308*134e1779SJakub Wojciech Klama 309*134e1779SJakub Wojciech Klamabut one that is too *long* potentially raises a SequencError. 310*134e1779SJakub Wojciech Klama(This is impossible for a header, though, since the size and 311*134e1779SJakub Wojciech Klamadata size are both implied: either there is an fcall code, and 312*134e1779SJakub Wojciech Klamathe rest of the bytes are "data", or there isn't and the packet 313*134e1779SJakub Wojciech Klamais too short. So we can only demonstrate this for regular 314*134e1779SJakub Wojciech Klamaunpack; see below.) 315*134e1779SJakub Wojciech Klama 316*134e1779SJakub Wojciech KlamaNote that all along, this has been decoding Rlink (fcall=71), 317*134e1779SJakub Wojciech Klamawhich is not valid for plain 9P2000 protocol. It's up to the 318*134e1779SJakub Wojciech Klamacaller to check: 319*134e1779SJakub Wojciech Klama 320*134e1779SJakub Wojciech Klama >>> plain.supports(71) 321*134e1779SJakub Wojciech Klama False 322*134e1779SJakub Wojciech Klama 323*134e1779SJakub Wojciech Klama >>> plain.unpack(pkt) # doctest: +IGNORE_EXCEPTION_DETAIL 324*134e1779SJakub Wojciech Klama Traceback (most recent call last): 325*134e1779SJakub Wojciech Klama ... 326*134e1779SJakub Wojciech Klama SequenceError: invalid fcall 'Rlink' for 9P2000 327*134e1779SJakub Wojciech Klama >>> dotl.unpack(pkt) 328*134e1779SJakub Wojciech Klama Rlink(tag=12345) 329*134e1779SJakub Wojciech Klama 330*134e1779SJakub Wojciech KlamaHowever, the unpack() method DOES check that the fcall type is 331*134e1779SJakub Wojciech Klamavalid, even if you supply noerror=True. This is because we can 332*134e1779SJakub Wojciech Klamaonly really decode the header, not the data, if the fcall is 333*134e1779SJakub Wojciech Klamainvalid: 334*134e1779SJakub Wojciech Klama 335*134e1779SJakub Wojciech Klama >>> plain.unpack(pkt, noerror=True) # doctest: +IGNORE_EXCEPTION_DETAIL 336*134e1779SJakub Wojciech Klama Traceback (most recent call last): 337*134e1779SJakub Wojciech Klama ... 338*134e1779SJakub Wojciech Klama SequenceError: invalid fcall 'Rlink' for 9P2000 339*134e1779SJakub Wojciech Klama 340*134e1779SJakub Wojciech KlamaThe same applies to much-too-short packets even if noerror is set. 341*134e1779SJakub Wojciech KlamaSpecifically, if the (post-"size") header shortens down to the empty 342*134e1779SJakub Wojciech Klamastring, the fcall will be None: 343*134e1779SJakub Wojciech Klama 344*134e1779SJakub Wojciech Klama >>> dotl.unpack(b'', noerror=True) # doctest: +IGNORE_EXCEPTION_DETAIL 345*134e1779SJakub Wojciech Klama Traceback (most recent call last): 346*134e1779SJakub Wojciech Klama ... 347*134e1779SJakub Wojciech Klama SequenceError: invalid fcall None for 9P2000.L 348*134e1779SJakub Wojciech Klama 349*134e1779SJakub Wojciech KlamaIf there is at least a full header, though, noerror will do the obvious: 350*134e1779SJakub Wojciech Klama 351*134e1779SJakub Wojciech Klama >>> dotl.unpack(pkt[0:1], noerror=True) 352*134e1779SJakub Wojciech Klama Rlink(tag=None) 353*134e1779SJakub Wojciech Klama >>> dotl.unpack(pkt[0:2], noerror=True) 354*134e1779SJakub Wojciech Klama Rlink(tag=None) 355*134e1779SJakub Wojciech Klama 356*134e1779SJakub Wojciech KlamaIf the packet is too long, noerror suppresses the SequenceError: 357*134e1779SJakub Wojciech Klama 358*134e1779SJakub Wojciech Klama >>> dotl.unpack(pkt + b'x') # doctest: +IGNORE_EXCEPTION_DETAIL 359*134e1779SJakub Wojciech Klama Traceback (most recent call last): 360*134e1779SJakub Wojciech Klama ... 361*134e1779SJakub Wojciech Klama SequenceError: 1 byte(s) unconsumed 362*134e1779SJakub Wojciech Klama >>> dotl.unpack(pkt + b'x', noerror=True) 363*134e1779SJakub Wojciech Klama Rlink(tag=12345) 364*134e1779SJakub Wojciech Klama 365*134e1779SJakub Wojciech KlamaTo pack a stat object when producing data for reading a directory, 366*134e1779SJakub Wojciech Klamause pack_wirestat. This puts a size in front of the packed stat 367*134e1779SJakub Wojciech Klamadata (they're represented this way in read()-of-directory data, 368*134e1779SJakub Wojciech Klamabut not elsewhere). 369*134e1779SJakub Wojciech Klama 370*134e1779SJakub Wojciech KlamaTo unpack the result of a Tstat or a read() on a directory, use 371*134e1779SJakub Wojciech Klamaunpack_wirestat. The stat values are variable length so this 372*134e1779SJakub Wojciech Klamaworks with offsets. If the packet is truncated, you'll get a 373*134e1779SJakub Wojciech KlamaSequenceError, but just as for header unpacking, you can use 374*134e1779SJakub Wojciech Klamanoerror to suppress this. 375*134e1779SJakub Wojciech Klama 376*134e1779SJakub Wojciech Klama(First, we'll need to build some valid packet data.) 377*134e1779SJakub Wojciech Klama 378*134e1779SJakub Wojciech Klama >>> statobj = td.stat(type=0,dev=0,qid=td.qid(0,0,0),mode=0, 379*134e1779SJakub Wojciech Klama ... atime=0,mtime=0,length=0,name=b'foo',uid=b'0',gid=b'0',muid=b'0') 380*134e1779SJakub Wojciech Klama >>> data = plain.pack_wirestat(statobj) 381*134e1779SJakub Wojciech Klama >>> len(data) 382*134e1779SJakub Wojciech Klama 55 383*134e1779SJakub Wojciech Klama 384*134e1779SJakub Wojciech KlamaNow we can unpack it: 385*134e1779SJakub Wojciech Klama 386*134e1779SJakub Wojciech Klama >>> newobj, offset = plain.unpack_wirestat(data, 0) 387*134e1779SJakub Wojciech Klama >>> newobj == statobj 388*134e1779SJakub Wojciech Klama True 389*134e1779SJakub Wojciech Klama >>> offset 390*134e1779SJakub Wojciech Klama 55 391*134e1779SJakub Wojciech Klama 392*134e1779SJakub Wojciech KlamaSince the packed data do not include the dotu extensions, we get 393*134e1779SJakub Wojciech Klamaa SequenceError if we try to unpack with dotu or dotl: 394*134e1779SJakub Wojciech Klama 395*134e1779SJakub Wojciech Klama >>> dotu.unpack_wirestat(data, 0) # doctest: +IGNORE_EXCEPTION_DETAIL 396*134e1779SJakub Wojciech Klama Traceback (most recent call last): 397*134e1779SJakub Wojciech Klama ... 398*134e1779SJakub Wojciech Klama SequenceError: out of data while unpacking 'extension' 399*134e1779SJakub Wojciech Klama 400*134e1779SJakub Wojciech KlamaWhen using noerror, the returned new offset will be greater 401*134e1779SJakub Wojciech Klamathan the length of the packet, after a failed unpack, and some 402*134e1779SJakub Wojciech Klamaelements may be None: 403*134e1779SJakub Wojciech Klama 404*134e1779SJakub Wojciech Klama >>> newobj, offset = plain.unpack_wirestat(data[0:10], 0, noerror=True) 405*134e1779SJakub Wojciech Klama >>> offset 406*134e1779SJakub Wojciech Klama 55 407*134e1779SJakub Wojciech Klama >>> newobj.length is None 408*134e1779SJakub Wojciech Klama True 409*134e1779SJakub Wojciech Klama 410*134e1779SJakub Wojciech KlamaSimilarly, use unpack_dirent to unpack the result of a dot-L 411*134e1779SJakub Wojciech Klamareaddir(), using offsets. (Build them with pack_dirent.) 412*134e1779SJakub Wojciech Klama 413*134e1779SJakub Wojciech Klama >>> dirent = td.dirent(qid=td.qid(1,2,3),offset=0, 414*134e1779SJakub Wojciech Klama ... type=td.DT_REG,name=b'foo') 415*134e1779SJakub Wojciech Klama >>> pkt = dotl.pack_dirent(dirent) 416*134e1779SJakub Wojciech Klama >>> len(pkt) 417*134e1779SJakub Wojciech Klama 27 418*134e1779SJakub Wojciech Klama 419*134e1779SJakub Wojciech Klamaand then: 420*134e1779SJakub Wojciech Klama 421*134e1779SJakub Wojciech Klama >>> newde, offset = dotl.unpack_dirent(pkt, 0) 422*134e1779SJakub Wojciech Klama >>> newde == dirent 423*134e1779SJakub Wojciech Klama True 424*134e1779SJakub Wojciech Klama >>> offset 425*134e1779SJakub Wojciech Klama 27 426*134e1779SJakub Wojciech Klama 427*134e1779SJakub Wojciech Klama""" 428*134e1779SJakub Wojciech Klama 429*134e1779SJakub Wojciech Klamafrom __future__ import print_function 430*134e1779SJakub Wojciech Klama 431*134e1779SJakub Wojciech Klamaimport collections 432*134e1779SJakub Wojciech Klamaimport os 433*134e1779SJakub Wojciech Klamaimport re 434*134e1779SJakub Wojciech Klamaimport sys 435*134e1779SJakub Wojciech Klama 436*134e1779SJakub Wojciech Klamaimport p9err 437*134e1779SJakub Wojciech Klamaimport pfod 438*134e1779SJakub Wojciech Klamaimport sequencer 439*134e1779SJakub Wojciech Klama 440*134e1779SJakub Wojciech KlamaSequenceError = sequencer.SequenceError 441*134e1779SJakub Wojciech Klama 442*134e1779SJakub Wojciech Klamafcall_names = {} 443*134e1779SJakub Wojciech Klama 444*134e1779SJakub Wojciech Klama# begin ??? 445*134e1779SJakub Wojciech Klama# to interfere with (eg) the size part of the packet: 446*134e1779SJakub Wojciech Klama# pkt = proto.pack(fcall=protocol.td.Tversion, 447*134e1779SJakub Wojciech Klama# size=123, # wrong 448*134e1779SJakub Wojciech Klama# args={ 'tag': 1, msize: 1000, version: '9p2000.u' }) 449*134e1779SJakub Wojciech Klama# a standard Twrite: 450*134e1779SJakub Wojciech Klama# pkt = proto.pack(fcall=protocol.td.Twrite, 451*134e1779SJakub Wojciech Klama# args={ 'tag': 1, 'fid': 2, 'offset': 0, 'data': b'rawdata' }) 452*134e1779SJakub Wojciech Klama# or: 453*134e1779SJakub Wojciech Klama# pkt = proto.pack(fcall=protocol.td.Twrite, 454*134e1779SJakub Wojciech Klama# data=proto.Twrite(tag=1, fid=2, offset=0, data=b'rawdata' }) 455*134e1779SJakub Wojciech Klama# a broken Twrite: 456*134e1779SJakub Wojciech Klama# pkt = proto.pack(fcall=protocol.td.Twrite, 457*134e1779SJakub Wojciech Klama# args={ 'tag': 1, 'fid': 2, 'offset': 0, 'count': 99, 458*134e1779SJakub Wojciech Klama# 'data': b'rawdata' }) -- XXX won't work (yet?) 459*134e1779SJakub Wojciech Klama# 460*134e1779SJakub Wojciech Klama# build a QID: (td => typedefs and defines) 461*134e1779SJakub Wojciech Klama# qid = protocol.td.qid(type=protocol.td.QTFILE, version=1, path=2) 462*134e1779SJakub Wojciech Klama# build the Twrite data as a data structure: 463*134e1779SJakub Wojciech Klama# wrdata = protocol.td.Twrite(tag=1, fid=2, offset=0, data=b'rawdata') 464*134e1779SJakub Wojciech Klama# 465*134e1779SJakub Wojciech Klama# turn incoming byte stream data into a Header and remaining data: 466*134e1779SJakub Wojciech Klama# foo = proto.pack(data) 467*134e1779SJakub Wojciech Klama 468*134e1779SJakub Wojciech Klamaclass _PackInfo(object): 469*134e1779SJakub Wojciech Klama """ 470*134e1779SJakub Wojciech Klama Essentially just a Sequencer, except that we remember 471*134e1779SJakub Wojciech Klama if there are any :auto annotations on any of the coders, 472*134e1779SJakub Wojciech Klama and we check for coders that are string coders ('data[size]'). 473*134e1779SJakub Wojciech Klama 474*134e1779SJakub Wojciech Klama This could in theory be a recursive check, but in practice 475*134e1779SJakub Wojciech Klama all the automatics are at the top level, and we have no mechanism 476*134e1779SJakub Wojciech Klama to pass down inner automatics. 477*134e1779SJakub Wojciech Klama """ 478*134e1779SJakub Wojciech Klama def __init__(self, seq): 479*134e1779SJakub Wojciech Klama self.seq = seq 480*134e1779SJakub Wojciech Klama self.autos = None 481*134e1779SJakub Wojciech Klama for pair in seq: # (cond, code) pair 482*134e1779SJakub Wojciech Klama sub = pair[1] 483*134e1779SJakub Wojciech Klama if sub.aux is None: 484*134e1779SJakub Wojciech Klama continue 485*134e1779SJakub Wojciech Klama assert sub.aux == 'auto' or sub.aux == 'len' 486*134e1779SJakub Wojciech Klama if self.autos is None: 487*134e1779SJakub Wojciech Klama self.autos = [] 488*134e1779SJakub Wojciech Klama self.autos.append(pair) 489*134e1779SJakub Wojciech Klama 490*134e1779SJakub Wojciech Klama def __repr__(self): 491*134e1779SJakub Wojciech Klama return '{0}({1!r})'.format(self.__class__.__name__, self.seq) 492*134e1779SJakub Wojciech Klama 493*134e1779SJakub Wojciech Klama def pack(self, auto_vars, conditions, data, rodata): 494*134e1779SJakub Wojciech Klama """ 495*134e1779SJakub Wojciech Klama Pack data. Insert automatic and/or counted variables 496*134e1779SJakub Wojciech Klama automatically, if they are not already set in the data. 497*134e1779SJakub Wojciech Klama 498*134e1779SJakub Wojciech Klama If rodata ("read-only data") is True we make sure not 499*134e1779SJakub Wojciech Klama to modify the caller's data. Since data is a PFOD rather 500*134e1779SJakub Wojciech Klama than a normal ordered dictionary, we use _copy(). 501*134e1779SJakub Wojciech Klama """ 502*134e1779SJakub Wojciech Klama if self.autos: 503*134e1779SJakub Wojciech Klama for cond, sub in self.autos: 504*134e1779SJakub Wojciech Klama # False conditionals don't need to be filled-in. 505*134e1779SJakub Wojciech Klama if cond is not None and not conditions[cond]: 506*134e1779SJakub Wojciech Klama continue 507*134e1779SJakub Wojciech Klama if sub.aux == 'auto': 508*134e1779SJakub Wojciech Klama # Automatic variable, e.g., version. The 509*134e1779SJakub Wojciech Klama # sub-coder's name ('version') is the test item. 510*134e1779SJakub Wojciech Klama if data.get(sub.name) is None: 511*134e1779SJakub Wojciech Klama if rodata: 512*134e1779SJakub Wojciech Klama data = data._copy() 513*134e1779SJakub Wojciech Klama rodata = False 514*134e1779SJakub Wojciech Klama data[sub.name] = auto_vars[sub.name] 515*134e1779SJakub Wojciech Klama else: 516*134e1779SJakub Wojciech Klama # Automatic length, e.g., data[count]. The 517*134e1779SJakub Wojciech Klama # sub-coders's repeat item ('count') is the 518*134e1779SJakub Wojciech Klama # test item. Of course, it's possible that 519*134e1779SJakub Wojciech Klama # the counted item is missing as well. If so 520*134e1779SJakub Wojciech Klama # we just leave both None and take the 521*134e1779SJakub Wojciech Klama # encoding error. 522*134e1779SJakub Wojciech Klama assert sub.aux == 'len' 523*134e1779SJakub Wojciech Klama if data.get(sub.repeat) is not None: 524*134e1779SJakub Wojciech Klama continue 525*134e1779SJakub Wojciech Klama item = data.get(sub.name) 526*134e1779SJakub Wojciech Klama if item is not None: 527*134e1779SJakub Wojciech Klama if rodata: 528*134e1779SJakub Wojciech Klama data = data._copy() 529*134e1779SJakub Wojciech Klama rodata = False 530*134e1779SJakub Wojciech Klama data[sub.repeat] = len(item) 531*134e1779SJakub Wojciech Klama return self.seq.pack(data, conditions) 532*134e1779SJakub Wojciech Klama 533*134e1779SJakub Wojciech Klamaclass _P9Proto(object): 534*134e1779SJakub Wojciech Klama def __init__(self, auto_vars, conditions, p9_data, pfods, index): 535*134e1779SJakub Wojciech Klama self.auto_vars = auto_vars # currently, just version 536*134e1779SJakub Wojciech Klama self.conditions = conditions # '.u' 537*134e1779SJakub Wojciech Klama self.pfods = pfods # dictionary, maps pfod to packinfo 538*134e1779SJakub Wojciech Klama self.index = index # for comparison: plain < dotu < dotl 539*134e1779SJakub Wojciech Klama 540*134e1779SJakub Wojciech Klama self.use_rlerror = rrd.Rlerror in pfods 541*134e1779SJakub Wojciech Klama 542*134e1779SJakub Wojciech Klama for dtype in pfods: 543*134e1779SJakub Wojciech Klama name = dtype.__name__ 544*134e1779SJakub Wojciech Klama # For each Txxx/Rxxx, define a self.<name>() to 545*134e1779SJakub Wojciech Klama # call self.pack_from(). 546*134e1779SJakub Wojciech Klama # 547*134e1779SJakub Wojciech Klama # The packinfo is from _Packinfo(seq); the fcall and 548*134e1779SJakub Wojciech Klama # seq come from p9_data.protocol[<name>]. 549*134e1779SJakub Wojciech Klama proto_tuple = p9_data.protocol[name] 550*134e1779SJakub Wojciech Klama assert dtype == proto_tuple[0] 551*134e1779SJakub Wojciech Klama packinfo = pfods[dtype] 552*134e1779SJakub Wojciech Klama # in theory we can do this with no names using nested 553*134e1779SJakub Wojciech Klama # lambdas, but that's just too confusing, so let's 554*134e1779SJakub Wojciech Klama # do it with nested functions instead. 555*134e1779SJakub Wojciech Klama def builder(constructor=dtype, packinfo=packinfo): 556*134e1779SJakub Wojciech Klama "return function that calls _pack_from with built PFOD" 557*134e1779SJakub Wojciech Klama def invoker(self, *args, **kwargs): 558*134e1779SJakub Wojciech Klama "build PFOD and call _pack_from" 559*134e1779SJakub Wojciech Klama return self._pack_from(constructor(*args, **kwargs), 560*134e1779SJakub Wojciech Klama rodata=False, caller=None, 561*134e1779SJakub Wojciech Klama packinfo=packinfo) 562*134e1779SJakub Wojciech Klama return invoker 563*134e1779SJakub Wojciech Klama func = builder() 564*134e1779SJakub Wojciech Klama func.__name__ = name 565*134e1779SJakub Wojciech Klama func.__doc__ = 'pack from {0}'.format(name) 566*134e1779SJakub Wojciech Klama setattr(self.__class__, name, func) 567*134e1779SJakub Wojciech Klama 568*134e1779SJakub Wojciech Klama def __repr__(self): 569*134e1779SJakub Wojciech Klama return '{0}({1!r})'.format(self.__class__.__name__, self.version) 570*134e1779SJakub Wojciech Klama 571*134e1779SJakub Wojciech Klama def __str__(self): 572*134e1779SJakub Wojciech Klama return self.version 573*134e1779SJakub Wojciech Klama 574*134e1779SJakub Wojciech Klama # define rich-comparison operators, so we can, e.g., test vers > plain 575*134e1779SJakub Wojciech Klama def __lt__(self, other): 576*134e1779SJakub Wojciech Klama return self.index < other.index 577*134e1779SJakub Wojciech Klama def __le__(self, other): 578*134e1779SJakub Wojciech Klama return self.index <= other.index 579*134e1779SJakub Wojciech Klama def __eq__(self, other): 580*134e1779SJakub Wojciech Klama return self.index == other.index 581*134e1779SJakub Wojciech Klama def __ne__(self, other): 582*134e1779SJakub Wojciech Klama return self.index != other.index 583*134e1779SJakub Wojciech Klama def __gt__(self, other): 584*134e1779SJakub Wojciech Klama return self.index > other.index 585*134e1779SJakub Wojciech Klama def __ge__(self, other): 586*134e1779SJakub Wojciech Klama return self.index >= other.index 587*134e1779SJakub Wojciech Klama 588*134e1779SJakub Wojciech Klama def downgrade_to(self, other_name): 589*134e1779SJakub Wojciech Klama """ 590*134e1779SJakub Wojciech Klama Downgrade from this protocol to a not-greater one. 591*134e1779SJakub Wojciech Klama 592*134e1779SJakub Wojciech Klama Raises KeyError if other_name is not a valid protocol, 593*134e1779SJakub Wojciech Klama or this is not a downgrade (with setting back to self 594*134e1779SJakub Wojciech Klama considered a valid "downgrade", i.e., we're doing subseteq 595*134e1779SJakub Wojciech Klama rather than subset). 596*134e1779SJakub Wojciech Klama """ 597*134e1779SJakub Wojciech Klama if not isinstance(other_name, str) and isinstance(other_name, bytes): 598*134e1779SJakub Wojciech Klama other_name = other_name.decode('utf-8', 'surrogateescape') 599*134e1779SJakub Wojciech Klama other = p9_version(other_name) 600*134e1779SJakub Wojciech Klama if other > self: 601*134e1779SJakub Wojciech Klama raise KeyError(other_name) 602*134e1779SJakub Wojciech Klama return other 603*134e1779SJakub Wojciech Klama 604*134e1779SJakub Wojciech Klama def error(self, tag, err): 605*134e1779SJakub Wojciech Klama "produce Rerror or Rlerror, whichever is appropriate" 606*134e1779SJakub Wojciech Klama if isinstance(err, Exception): 607*134e1779SJakub Wojciech Klama errnum = err.errno 608*134e1779SJakub Wojciech Klama errmsg = err.strerror 609*134e1779SJakub Wojciech Klama else: 610*134e1779SJakub Wojciech Klama errnum = err 611*134e1779SJakub Wojciech Klama errmsg = os.strerror(errnum) 612*134e1779SJakub Wojciech Klama if self.use_rlerror: 613*134e1779SJakub Wojciech Klama return self.Rlerror(tag=tag, ecode=p9err.to_dotl(errnum)) 614*134e1779SJakub Wojciech Klama return self.Rerror(tag=tag, errstr=errmsg, 615*134e1779SJakub Wojciech Klama errnum=p9err.to_dotu(errnum)) 616*134e1779SJakub Wojciech Klama 617*134e1779SJakub Wojciech Klama def pack(self, *args, **kwargs): 618*134e1779SJakub Wojciech Klama "pack up a pfod or fcall-and-arguments" 619*134e1779SJakub Wojciech Klama fcall = kwargs.pop('fcall', None) 620*134e1779SJakub Wojciech Klama if fcall is None: 621*134e1779SJakub Wojciech Klama # Called without fcall=... 622*134e1779SJakub Wojciech Klama # This requires that args have one argument that 623*134e1779SJakub Wojciech Klama # is the PFOD; kwargs should be empty (but we'll take 624*134e1779SJakub Wojciech Klama # data=pfod as well). The size is implied, and 625*134e1779SJakub Wojciech Klama # fcall comes from the pfod. 626*134e1779SJakub Wojciech Klama data = kwargs.pop('data', None) 627*134e1779SJakub Wojciech Klama if data is None: 628*134e1779SJakub Wojciech Klama if len(args) != 1: 629*134e1779SJakub Wojciech Klama raise TypeError('pack() with no fcall requires 1 argument') 630*134e1779SJakub Wojciech Klama data = args[0] 631*134e1779SJakub Wojciech Klama if len(kwargs): 632*134e1779SJakub Wojciech Klama raise TypeError('pack() got an unexpected keyword argument ' 633*134e1779SJakub Wojciech Klama '{0}'.format(kwargs.popitem()[0])) 634*134e1779SJakub Wojciech Klama return self._pack_from(data, True, 'pack', None) 635*134e1779SJakub Wojciech Klama 636*134e1779SJakub Wojciech Klama # Called as pack(fcall=whatever, data={...}). 637*134e1779SJakub Wojciech Klama # The data argument must be a dictionary since we're going to 638*134e1779SJakub Wojciech Klama # apply ** to it in the call to build the PFOD. Note that 639*134e1779SJakub Wojciech Klama # it could already be a PFOD, which is OK, but we're going to 640*134e1779SJakub Wojciech Klama # copy it to a new one regardless (callers that have a PFOD 641*134e1779SJakub Wojciech Klama # should use pack_from instead). 642*134e1779SJakub Wojciech Klama if len(args): 643*134e1779SJakub Wojciech Klama raise TypeError('pack() got unexpected arguments ' 644*134e1779SJakub Wojciech Klama '{0!r}'.format(args)) 645*134e1779SJakub Wojciech Klama data = kwargs.pop('args', None) 646*134e1779SJakub Wojciech Klama if len(kwargs): 647*134e1779SJakub Wojciech Klama raise TypeError('pack() got an unexpected keyword argument ' 648*134e1779SJakub Wojciech Klama '{0}'.format(kwargs.popitem()[0])) 649*134e1779SJakub Wojciech Klama if not isinstance(data, dict): 650*134e1779SJakub Wojciech Klama raise TypeError('pack() with fcall and data ' 651*134e1779SJakub Wojciech Klama 'requires data to be a dictionary') 652*134e1779SJakub Wojciech Klama try: 653*134e1779SJakub Wojciech Klama name = fcall_names[fcall] 654*134e1779SJakub Wojciech Klama except KeyError: 655*134e1779SJakub Wojciech Klama raise TypeError('pack(): {0} is not a valid ' 656*134e1779SJakub Wojciech Klama 'fcall value'.format(fcall)) 657*134e1779SJakub Wojciech Klama cls = getattr(rrd, name) 658*134e1779SJakub Wojciech Klama data = cls(**data) 659*134e1779SJakub Wojciech Klama return self._pack_from(data, False, 'pack', None) 660*134e1779SJakub Wojciech Klama 661*134e1779SJakub Wojciech Klama def pack_from(self, data): 662*134e1779SJakub Wojciech Klama "pack from pfod data, using its type to determine fcall" 663*134e1779SJakub Wojciech Klama return self._pack_from(data, True, 'pack_from', None) 664*134e1779SJakub Wojciech Klama 665*134e1779SJakub Wojciech Klama def _pack_from(self, data, rodata, caller, packinfo): 666*134e1779SJakub Wojciech Klama """ 667*134e1779SJakub Wojciech Klama Internal pack(): called from both invokers (self.Tversion, 668*134e1779SJakub Wojciech Klama self.Rwalk, etc.) and from pack and pack_from methods. 669*134e1779SJakub Wojciech Klama "caller" says which. If rodata is True we're not supposed to 670*134e1779SJakub Wojciech Klama modify the incoming data, as it may belong to someone 671*134e1779SJakub Wojciech Klama else. Some calls to pack() build a PFOD and hence pass in 672*134e1779SJakub Wojciech Klama False. 673*134e1779SJakub Wojciech Klama 674*134e1779SJakub Wojciech Klama The predefined invokers pass in a preconstructed PFOD, 675*134e1779SJakub Wojciech Klama *and* set rodata=False, *and* provide a packinfo, so that 676*134e1779SJakub Wojciech Klama we never have to copy, nor look up the packinfo. 677*134e1779SJakub Wojciech Klama """ 678*134e1779SJakub Wojciech Klama if caller is not None: 679*134e1779SJakub Wojciech Klama assert caller in ('pack', 'pack_from') and packinfo is None 680*134e1779SJakub Wojciech Klama # Indirect call from pack_from(), or from pack() after 681*134e1779SJakub Wojciech Klama # pack() built a PFOD. We make sure this kind of PFOD 682*134e1779SJakub Wojciech Klama # is allowed for this protocol. 683*134e1779SJakub Wojciech Klama packinfo = self.pfods.get(data.__class__, None) 684*134e1779SJakub Wojciech Klama if packinfo is None: 685*134e1779SJakub Wojciech Klama raise TypeError('{0}({1!r}): invalid ' 686*134e1779SJakub Wojciech Klama 'input'.format(caller, data)) 687*134e1779SJakub Wojciech Klama 688*134e1779SJakub Wojciech Klama # Pack the data 689*134e1779SJakub Wojciech Klama pkt = packinfo.pack(self.auto_vars, self.conditions, data, rodata) 690*134e1779SJakub Wojciech Klama 691*134e1779SJakub Wojciech Klama fcall = data.__class__.__name__ 692*134e1779SJakub Wojciech Klama fcall_code = getattr(td, fcall) 693*134e1779SJakub Wojciech Klama 694*134e1779SJakub Wojciech Klama # That's the inner data; now we must add the header, 695*134e1779SJakub Wojciech Klama # with fcall (translated back to byte code value) and 696*134e1779SJakub Wojciech Klama # outer data. The size is implied by len(pkt). There 697*134e1779SJakub Wojciech Klama # are no other auto variables, and no conditions. 698*134e1779SJakub Wojciech Klama # 699*134e1779SJakub Wojciech Klama # NB: the size includes the size of the header itself 700*134e1779SJakub Wojciech Klama # and the fcall code byte, plus the size of the data. 701*134e1779SJakub Wojciech Klama data = _9p_data.header_pfod(size=4 + 1 + len(pkt), dsize=len(pkt), 702*134e1779SJakub Wojciech Klama fcall=fcall_code, data=pkt) 703*134e1779SJakub Wojciech Klama empty = None # logically should be {}, but not actually used below 704*134e1779SJakub Wojciech Klama pkt = _9p_data.header_pack_seq.pack(data, empty) 705*134e1779SJakub Wojciech Klama return pkt 706*134e1779SJakub Wojciech Klama 707*134e1779SJakub Wojciech Klama @staticmethod 708*134e1779SJakub Wojciech Klama def unpack_header(bstring, noerror=False): 709*134e1779SJakub Wojciech Klama """ 710*134e1779SJakub Wojciech Klama Unpack header. 711*134e1779SJakub Wojciech Klama 712*134e1779SJakub Wojciech Klama We know that our caller has already stripped off the 713*134e1779SJakub Wojciech Klama overall size field (4 bytes), leaving us with the fcall 714*134e1779SJakub Wojciech Klama (1 byte) and data (len(bstring)-1 bytes). If len(bstring) 715*134e1779SJakub Wojciech Klama is 0, this is an invalid header: set dsize to 0 and let 716*134e1779SJakub Wojciech Klama fcall become None, if noerror is set. 717*134e1779SJakub Wojciech Klama """ 718*134e1779SJakub Wojciech Klama vdict = _9p_data.header_pfod() 719*134e1779SJakub Wojciech Klama vdict['size'] = len(bstring) + 4 720*134e1779SJakub Wojciech Klama vdict['dsize'] = max(0, len(bstring) - 1) 721*134e1779SJakub Wojciech Klama _9p_data.header_unpack_seq.unpack(vdict, None, bstring, noerror) 722*134e1779SJakub Wojciech Klama return vdict 723*134e1779SJakub Wojciech Klama 724*134e1779SJakub Wojciech Klama def unpack(self, bstring, noerror=False): 725*134e1779SJakub Wojciech Klama "produce filled PFOD from fcall in packet" 726*134e1779SJakub Wojciech Klama vdict = self.unpack_header(bstring, noerror) 727*134e1779SJakub Wojciech Klama # NB: vdict['dsize'] is used internally during unpack, to 728*134e1779SJakub Wojciech Klama # find out how many bytes to copy to vdict['data'], but by 729*134e1779SJakub Wojciech Klama # the time unpack is done, we no longer need it. 730*134e1779SJakub Wojciech Klama # 731*134e1779SJakub Wojciech Klama # size = vdict['size'] 732*134e1779SJakub Wojciech Klama # dsize = vdict['dsize'] 733*134e1779SJakub Wojciech Klama fcall = vdict['fcall'] 734*134e1779SJakub Wojciech Klama data = vdict['data'] 735*134e1779SJakub Wojciech Klama # Note: it's possible for size and/or fcall to be None, 736*134e1779SJakub Wojciech Klama # when noerror is true. However, if we support fcall, then 737*134e1779SJakub Wojciech Klama # clearly fcall is not None; and since fcall follows size, 738*134e1779SJakub Wojciech Klama # we can always proceed if we support fcall. 739*134e1779SJakub Wojciech Klama if self.supports(fcall): 740*134e1779SJakub Wojciech Klama fcall = fcall_names[fcall] 741*134e1779SJakub Wojciech Klama cls = getattr(rrd, fcall) 742*134e1779SJakub Wojciech Klama seq = self.pfods[cls].seq 743*134e1779SJakub Wojciech Klama elif fcall == td.Rlerror: 744*134e1779SJakub Wojciech Klama # As a special case for diod, we accept Rlerror even 745*134e1779SJakub Wojciech Klama # if it's not formally part of the protocol. 746*134e1779SJakub Wojciech Klama cls = rrd.Rlerror 747*134e1779SJakub Wojciech Klama seq = dotl.pfods[rrd.Rlerror].seq 748*134e1779SJakub Wojciech Klama else: 749*134e1779SJakub Wojciech Klama fcall = fcall_names.get(fcall, fcall) 750*134e1779SJakub Wojciech Klama raise SequenceError('invalid fcall {0!r} for ' 751*134e1779SJakub Wojciech Klama '{1}'.format(fcall, self)) 752*134e1779SJakub Wojciech Klama vdict = cls() 753*134e1779SJakub Wojciech Klama seq.unpack(vdict, self.conditions, data, noerror) 754*134e1779SJakub Wojciech Klama return vdict 755*134e1779SJakub Wojciech Klama 756*134e1779SJakub Wojciech Klama def pack_wirestat(self, statobj): 757*134e1779SJakub Wojciech Klama """ 758*134e1779SJakub Wojciech Klama Pack a stat object to appear as data returned by read() 759*134e1779SJakub Wojciech Klama on a directory. Essentially, we prefix the data with a size. 760*134e1779SJakub Wojciech Klama """ 761*134e1779SJakub Wojciech Klama data = td.stat_seq.pack(statobj, self.conditions) 762*134e1779SJakub Wojciech Klama return td.wirestat_seq.pack({'size': len(data), 'data': data}, {}) 763*134e1779SJakub Wojciech Klama 764*134e1779SJakub Wojciech Klama def unpack_wirestat(self, bstring, offset, noerror=False): 765*134e1779SJakub Wojciech Klama """ 766*134e1779SJakub Wojciech Klama Produce the next td.stat object from byte-string, 767*134e1779SJakub Wojciech Klama returning it and new offset. 768*134e1779SJakub Wojciech Klama """ 769*134e1779SJakub Wojciech Klama statobj = td.stat() 770*134e1779SJakub Wojciech Klama d = { 'size': None } 771*134e1779SJakub Wojciech Klama newoff = td.wirestat_seq.unpack_from(d, self.conditions, bstring, 772*134e1779SJakub Wojciech Klama offset, noerror) 773*134e1779SJakub Wojciech Klama size = d['size'] 774*134e1779SJakub Wojciech Klama if size is None: # implies noerror; newoff==offset+2 775*134e1779SJakub Wojciech Klama return statobj, newoff 776*134e1779SJakub Wojciech Klama # We now have size and data. If noerror, data might be 777*134e1779SJakub Wojciech Klama # too short, in which case we'll unpack a partial statobj. 778*134e1779SJakub Wojciech Klama # Or (with or without noeror), data might be too long, so 779*134e1779SJakub Wojciech Klama # that while len(data) == size, not all the data get used. 780*134e1779SJakub Wojciech Klama # That may be allowed by the protocol: it's not clear. 781*134e1779SJakub Wojciech Klama data = d['data'] 782*134e1779SJakub Wojciech Klama used = td.stat_seq.unpack_from(statobj, self.conditions, data, 783*134e1779SJakub Wojciech Klama 0, noerror) 784*134e1779SJakub Wojciech Klama # if size != used ... then what? 785*134e1779SJakub Wojciech Klama return statobj, newoff 786*134e1779SJakub Wojciech Klama 787*134e1779SJakub Wojciech Klama def pack_dirent(self, dirent): 788*134e1779SJakub Wojciech Klama """ 789*134e1779SJakub Wojciech Klama Dirents (dot-L only) are easy to pack, but we provide 790*134e1779SJakub Wojciech Klama this function for symmetry. (Should we raise an error 791*134e1779SJakub Wojciech Klama if called on plain or dotu?) 792*134e1779SJakub Wojciech Klama """ 793*134e1779SJakub Wojciech Klama return td.dirent_seq.pack(dirent, self.conditions) 794*134e1779SJakub Wojciech Klama 795*134e1779SJakub Wojciech Klama def unpack_dirent(self, bstring, offset, noerror=False): 796*134e1779SJakub Wojciech Klama """ 797*134e1779SJakub Wojciech Klama Produces the next td.dirent object from byte-string, 798*134e1779SJakub Wojciech Klama returning it and new offset. 799*134e1779SJakub Wojciech Klama """ 800*134e1779SJakub Wojciech Klama deobj = td.dirent() 801*134e1779SJakub Wojciech Klama offset = td.dirent_seq.unpack_from(deobj, self.conditions, bstring, 802*134e1779SJakub Wojciech Klama offset, noerror) 803*134e1779SJakub Wojciech Klama return deobj, offset 804*134e1779SJakub Wojciech Klama 805*134e1779SJakub Wojciech Klama def supports(self, fcall): 806*134e1779SJakub Wojciech Klama """ 807*134e1779SJakub Wojciech Klama Return True if and only if this protocol supports the 808*134e1779SJakub Wojciech Klama given fcall. 809*134e1779SJakub Wojciech Klama 810*134e1779SJakub Wojciech Klama >>> plain.supports(100) 811*134e1779SJakub Wojciech Klama True 812*134e1779SJakub Wojciech Klama >>> plain.supports('Tversion') 813*134e1779SJakub Wojciech Klama True 814*134e1779SJakub Wojciech Klama >>> plain.supports('Rlink') 815*134e1779SJakub Wojciech Klama False 816*134e1779SJakub Wojciech Klama """ 817*134e1779SJakub Wojciech Klama fcall = fcall_names.get(fcall, None) 818*134e1779SJakub Wojciech Klama if fcall is None: 819*134e1779SJakub Wojciech Klama return False 820*134e1779SJakub Wojciech Klama cls = getattr(rrd, fcall) 821*134e1779SJakub Wojciech Klama return cls in self.pfods 822*134e1779SJakub Wojciech Klama 823*134e1779SJakub Wojciech Klama def get_version(self, as_bytes=True): 824*134e1779SJakub Wojciech Klama "get Plan 9 protocol version, as string or (default) as bytes" 825*134e1779SJakub Wojciech Klama ret = self.auto_vars['version'] 826*134e1779SJakub Wojciech Klama if as_bytes and not isinstance(ret, bytes): 827*134e1779SJakub Wojciech Klama ret = ret.encode('utf-8') 828*134e1779SJakub Wojciech Klama return ret 829*134e1779SJakub Wojciech Klama 830*134e1779SJakub Wojciech Klama @property 831*134e1779SJakub Wojciech Klama def version(self): 832*134e1779SJakub Wojciech Klama "Plan 9 protocol version" 833*134e1779SJakub Wojciech Klama return self.get_version(as_bytes=False) 834*134e1779SJakub Wojciech Klama 835*134e1779SJakub Wojciech KlamaDEBUG = False 836*134e1779SJakub Wojciech Klama 837*134e1779SJakub Wojciech Klama# This defines a special en/decoder named "s" using a magic 838*134e1779SJakub Wojciech Klama# builtin. This and stat are the only variable-length 839*134e1779SJakub Wojciech Klama# decoders, and this is the only recursively-variable-length 840*134e1779SJakub Wojciech Klama# one (i.e., stat decoding is effectively fixed size once we 841*134e1779SJakub Wojciech Klama# handle strings). So this magic avoids the need for recursion. 842*134e1779SJakub Wojciech Klama# 843*134e1779SJakub Wojciech Klama# Note that _string_ is, in effect, size[2] orig_var[size]. 844*134e1779SJakub Wojciech Klama_STRING_MAGIC = '_string_' 845*134e1779SJakub Wojciech KlamaSDesc = "typedef s: " + _STRING_MAGIC 846*134e1779SJakub Wojciech Klama 847*134e1779SJakub Wojciech Klama# This defines an en/decoder for type "qid", 848*134e1779SJakub Wojciech Klama# which en/decodes 1 byte called type, 4 called version, and 849*134e1779SJakub Wojciech Klama# 8 called path (for a total of 13 bytes). 850*134e1779SJakub Wojciech Klama# 851*134e1779SJakub Wojciech Klama# It also defines QTDIR, QTAPPEND, etc. (These are not used 852*134e1779SJakub Wojciech Klama# for en/decode, or at least not yet.) 853*134e1779SJakub Wojciech KlamaQIDDesc = """\ 854*134e1779SJakub Wojciech Klamatypedef qid: type[1] version[4] path[8] 855*134e1779SJakub Wojciech Klama 856*134e1779SJakub Wojciech Klama #define QTDIR 0x80 857*134e1779SJakub Wojciech Klama #define QTAPPEND 0x40 858*134e1779SJakub Wojciech Klama #define QTEXCL 0x20 859*134e1779SJakub Wojciech Klama #define QTMOUNT 0x10 860*134e1779SJakub Wojciech Klama #define QTAUTH 0x08 861*134e1779SJakub Wojciech Klama #define QTTMP 0x04 862*134e1779SJakub Wojciech Klama #define QTSYMLINK 0x02 863*134e1779SJakub Wojciech Klama #define QTFILE 0x00 864*134e1779SJakub Wojciech Klama""" 865*134e1779SJakub Wojciech Klama 866*134e1779SJakub Wojciech Klama# This defines a stat decoder, which has a 9p2000 standard front, 867*134e1779SJakub Wojciech Klama# followed by an optional additional portion. 868*134e1779SJakub Wojciech Klama# 869*134e1779SJakub Wojciech Klama# The constants are named DMDIR etc. 870*134e1779SJakub Wojciech KlamaSTATDesc = """ 871*134e1779SJakub Wojciech Klamatypedef stat: type[2] dev[4] qid[qid] mode[4] atime[4] mtime[4] \ 872*134e1779SJakub Wojciech Klamalength[8] name[s] uid[s] gid[s] muid[s] \ 873*134e1779SJakub Wojciech Klama{.u: extension[s] n_uid[4] n_gid[4] n_muid[4] } 874*134e1779SJakub Wojciech Klama 875*134e1779SJakub Wojciech Klama #define DMDIR 0x80000000 876*134e1779SJakub Wojciech Klama #define DMAPPEND 0x40000000 877*134e1779SJakub Wojciech Klama #define DMMOUNT 0x10000000 878*134e1779SJakub Wojciech Klama #define DMAUTH 0x08000000 879*134e1779SJakub Wojciech Klama #define DMTMP 0x04000000 880*134e1779SJakub Wojciech Klama #define DMSYMLINK 0x02000000 881*134e1779SJakub Wojciech Klama /* 9P2000.u extensions */ 882*134e1779SJakub Wojciech Klama #define DMDEVICE 0x00800000 883*134e1779SJakub Wojciech Klama #define DMNAMEDPIPE 0x00200000 884*134e1779SJakub Wojciech Klama #define DMSOCKET 0x00100000 885*134e1779SJakub Wojciech Klama #define DMSETUID 0x00080000 886*134e1779SJakub Wojciech Klama #define DMSETGID 0x00040000 887*134e1779SJakub Wojciech Klama""" 888*134e1779SJakub Wojciech Klama 889*134e1779SJakub Wojciech Klama# This defines a wirestat decoder. A wirestat is a size and then 890*134e1779SJakub Wojciech Klama# a (previously encoded, or future-decoded) stat. 891*134e1779SJakub Wojciech KlamaWirestatDesc = """ 892*134e1779SJakub Wojciech Klamatypedef wirestat: size[2] data[size] 893*134e1779SJakub Wojciech Klama""" 894*134e1779SJakub Wojciech Klama 895*134e1779SJakub Wojciech Klama# This defines a dirent decoder, which has a dot-L specific format. 896*134e1779SJakub Wojciech Klama# 897*134e1779SJakub Wojciech Klama# The dirent type fields are defined as DT_* (same as BSD and Linux). 898*134e1779SJakub Wojciech KlamaDirentDesc = """ 899*134e1779SJakub Wojciech Klamatypedef dirent: qid[qid] offset[8] type[1] name[s] 900*134e1779SJakub Wojciech Klama 901*134e1779SJakub Wojciech Klama #define DT_UNKNOWN 0 902*134e1779SJakub Wojciech Klama #define DT_FIFO 1 903*134e1779SJakub Wojciech Klama #define DT_CHR 2 904*134e1779SJakub Wojciech Klama #define DT_DIR 4 905*134e1779SJakub Wojciech Klama #define DT_BLK 6 906*134e1779SJakub Wojciech Klama #define DT_REG 8 907*134e1779SJakub Wojciech Klama #define DT_LNK 10 908*134e1779SJakub Wojciech Klama #define DT_SOCK 12 909*134e1779SJakub Wojciech Klama #define DT_WHT 14 910*134e1779SJakub Wojciech Klama""" 911*134e1779SJakub Wojciech Klama 912*134e1779SJakub Wojciech Klama# N.B.: this is largely a slightly more rigidly formatted variant of 913*134e1779SJakub Wojciech Klama# the contents of: 914*134e1779SJakub Wojciech Klama# https://github.com/chaos/diod/blob/master/protocol.md 915*134e1779SJakub Wojciech Klama# 916*134e1779SJakub Wojciech Klama# Note that <name> = <value>: ... assigns names for the fcall 917*134e1779SJakub Wojciech Klama# fcall (function call) table. Names without "= value" are 918*134e1779SJakub Wojciech Klama# assumed to be the previous value +1 (and the two names are 919*134e1779SJakub Wojciech Klama# also checked to make sure they are Tfoo,Rfoo). 920*134e1779SJakub Wojciech KlamaProtocolDesc = """\ 921*134e1779SJakub Wojciech KlamaRlerror.L = 7: tag[2] ecode[4] 922*134e1779SJakub Wojciech Klama ecode is a numerical Linux errno 923*134e1779SJakub Wojciech Klama 924*134e1779SJakub Wojciech KlamaTstatfs.L = 8: tag[2] fid[4] 925*134e1779SJakub Wojciech KlamaRstatfs.L: tag[2] type[4] bsize[4] blocks[8] bfree[8] bavail[8] \ 926*134e1779SJakub Wojciech Klama files[8] ffree[8] fsid[8] namelen[4] 927*134e1779SJakub Wojciech Klama Rstatfs corresponds to Linux statfs structure: 928*134e1779SJakub Wojciech Klama struct statfs { 929*134e1779SJakub Wojciech Klama long f_type; /* type of file system */ 930*134e1779SJakub Wojciech Klama long f_bsize; /* optimal transfer block size */ 931*134e1779SJakub Wojciech Klama long f_blocks; /* total data blocks in file system */ 932*134e1779SJakub Wojciech Klama long f_bfree; /* free blocks in fs */ 933*134e1779SJakub Wojciech Klama long f_bavail; /* free blocks avail to non-superuser */ 934*134e1779SJakub Wojciech Klama long f_files; /* total file nodes in file system */ 935*134e1779SJakub Wojciech Klama long f_ffree; /* free file nodes in fs */ 936*134e1779SJakub Wojciech Klama fsid_t f_fsid; /* file system id */ 937*134e1779SJakub Wojciech Klama long f_namelen; /* maximum length of filenames */ 938*134e1779SJakub Wojciech Klama }; 939*134e1779SJakub Wojciech Klama 940*134e1779SJakub Wojciech Klama This comes from nowhere obvious... 941*134e1779SJakub Wojciech Klama #define FSTYPE 0x01021997 942*134e1779SJakub Wojciech Klama 943*134e1779SJakub Wojciech KlamaTlopen.L = 12: tag[2] fid[4] flags[4] 944*134e1779SJakub Wojciech KlamaRlopen.L: tag[2] qid[qid] iounit[4] 945*134e1779SJakub Wojciech Klama lopen prepares fid for file (or directory) I/O. 946*134e1779SJakub Wojciech Klama 947*134e1779SJakub Wojciech Klama flags contains Linux open(2) flag bits, e.g., O_RDONLY, O_RDWR, O_WRONLY. 948*134e1779SJakub Wojciech Klama 949*134e1779SJakub Wojciech Klama #define L_O_CREAT 000000100 950*134e1779SJakub Wojciech Klama #define L_O_EXCL 000000200 951*134e1779SJakub Wojciech Klama #define L_O_NOCTTY 000000400 952*134e1779SJakub Wojciech Klama #define L_O_TRUNC 000001000 953*134e1779SJakub Wojciech Klama #define L_O_APPEND 000002000 954*134e1779SJakub Wojciech Klama #define L_O_NONBLOCK 000004000 955*134e1779SJakub Wojciech Klama #define L_O_DSYNC 000010000 956*134e1779SJakub Wojciech Klama #define L_O_FASYNC 000020000 957*134e1779SJakub Wojciech Klama #define L_O_DIRECT 000040000 958*134e1779SJakub Wojciech Klama #define L_O_LARGEFILE 000100000 959*134e1779SJakub Wojciech Klama #define L_O_DIRECTORY 000200000 960*134e1779SJakub Wojciech Klama #define L_O_NOFOLLOW 000400000 961*134e1779SJakub Wojciech Klama #define L_O_NOATIME 001000000 962*134e1779SJakub Wojciech Klama #define L_O_CLOEXEC 002000000 963*134e1779SJakub Wojciech Klama #define L_O_SYNC 004000000 964*134e1779SJakub Wojciech Klama #define L_O_PATH 010000000 965*134e1779SJakub Wojciech Klama #define L_O_TMPFILE 020000000 966*134e1779SJakub Wojciech Klama 967*134e1779SJakub Wojciech KlamaTlcreate.L = 14: tag[2] fid[4] name[s] flags[4] mode[4] gid[4] 968*134e1779SJakub Wojciech KlamaRlcreate.L: tag[2] qid[qid] iounit[4] 969*134e1779SJakub Wojciech Klama lcreate creates a regular file name in directory fid and prepares 970*134e1779SJakub Wojciech Klama it for I/O. 971*134e1779SJakub Wojciech Klama 972*134e1779SJakub Wojciech Klama fid initially represents the parent directory of the new file. 973*134e1779SJakub Wojciech Klama After the call it represents the new file. 974*134e1779SJakub Wojciech Klama 975*134e1779SJakub Wojciech Klama flags contains Linux open(2) flag bits (including O_CREAT). 976*134e1779SJakub Wojciech Klama 977*134e1779SJakub Wojciech Klama mode contains Linux creat(2) mode (permissions) bits. 978*134e1779SJakub Wojciech Klama 979*134e1779SJakub Wojciech Klama gid is the effective gid of the caller. 980*134e1779SJakub Wojciech Klama 981*134e1779SJakub Wojciech KlamaTsymlink.L = 16: tag[2] dfid[4] name[s] symtgt[s] gid[4] 982*134e1779SJakub Wojciech KlamaRsymlink.L: tag[2] qid[qid] 983*134e1779SJakub Wojciech Klama symlink creates a symbolic link name in directory dfid. The 984*134e1779SJakub Wojciech Klama link will point to symtgt. 985*134e1779SJakub Wojciech Klama 986*134e1779SJakub Wojciech Klama gid is the effective group id of the caller. 987*134e1779SJakub Wojciech Klama 988*134e1779SJakub Wojciech Klama The qid for the new symbolic link is returned in the reply. 989*134e1779SJakub Wojciech Klama 990*134e1779SJakub Wojciech KlamaTmknod.L = 18: tag[2] dfid[4] name[s] mode[4] major[4] minor[4] gid[4] 991*134e1779SJakub Wojciech KlamaRmknod.L: tag[2] qid[qid] 992*134e1779SJakub Wojciech Klama mknod creates a device node name in directory dfid with major 993*134e1779SJakub Wojciech Klama and minor numbers. 994*134e1779SJakub Wojciech Klama 995*134e1779SJakub Wojciech Klama mode contains Linux mknod(2) mode bits. (Note that these 996*134e1779SJakub Wojciech Klama include the S_IFMT bits which may be S_IFBLK, S_IFCHR, or 997*134e1779SJakub Wojciech Klama S_IFSOCK.) 998*134e1779SJakub Wojciech Klama 999*134e1779SJakub Wojciech Klama gid is the effective group id of the caller. 1000*134e1779SJakub Wojciech Klama 1001*134e1779SJakub Wojciech Klama The qid for the new device node is returned in the reply. 1002*134e1779SJakub Wojciech Klama 1003*134e1779SJakub Wojciech KlamaTrename.L = 20: tag[2] fid[4] dfid[4] name[s] 1004*134e1779SJakub Wojciech KlamaRrename.L: tag[2] 1005*134e1779SJakub Wojciech Klama rename renames a file system object referenced by fid, to name 1006*134e1779SJakub Wojciech Klama in the directory referenced by dfid. 1007*134e1779SJakub Wojciech Klama 1008*134e1779SJakub Wojciech Klama This operation will eventually be replaced by renameat. 1009*134e1779SJakub Wojciech Klama 1010*134e1779SJakub Wojciech KlamaTreadlink.L = 22: tag[2] fid[4] 1011*134e1779SJakub Wojciech KlamaRreadlink.L: tag[2] target[s] 1012*134e1779SJakub Wojciech Klama readlink returns the contents of teh symbolic link referenced by fid. 1013*134e1779SJakub Wojciech Klama 1014*134e1779SJakub Wojciech KlamaTgetattr.L = 24: tag[2] fid[4] request_mask[8] 1015*134e1779SJakub Wojciech KlamaRgetattr.L: tag[2] valid[8] qid[qid] mode[4] uid[4] gid[4] nlink[8] \ 1016*134e1779SJakub Wojciech Klama rdev[8] size[8] blksize[8] blocks[8] \ 1017*134e1779SJakub Wojciech Klama atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] \ 1018*134e1779SJakub Wojciech Klama ctime_sec[8] ctime_nsec[8] btime_sec[8] btime_nsec[8] \ 1019*134e1779SJakub Wojciech Klama gen[8] data_version[8] 1020*134e1779SJakub Wojciech Klama 1021*134e1779SJakub Wojciech Klama getattr gets attributes of a file system object referenced by fid. 1022*134e1779SJakub Wojciech Klama The response is intended to follow pretty closely the fields 1023*134e1779SJakub Wojciech Klama returned by the stat(2) system call: 1024*134e1779SJakub Wojciech Klama 1025*134e1779SJakub Wojciech Klama struct stat { 1026*134e1779SJakub Wojciech Klama dev_t st_dev; /* ID of device containing file */ 1027*134e1779SJakub Wojciech Klama ino_t st_ino; /* inode number */ 1028*134e1779SJakub Wojciech Klama mode_t st_mode; /* protection */ 1029*134e1779SJakub Wojciech Klama nlink_t st_nlink; /* number of hard links */ 1030*134e1779SJakub Wojciech Klama uid_t st_uid; /* user ID of owner */ 1031*134e1779SJakub Wojciech Klama gid_t st_gid; /* group ID of owner */ 1032*134e1779SJakub Wojciech Klama dev_t st_rdev; /* device ID (if special file) */ 1033*134e1779SJakub Wojciech Klama off_t st_size; /* total size, in bytes */ 1034*134e1779SJakub Wojciech Klama blksize_t st_blksize; /* blocksize for file system I/O */ 1035*134e1779SJakub Wojciech Klama blkcnt_t st_blocks; /* number of 512B blocks allocated */ 1036*134e1779SJakub Wojciech Klama time_t st_atime; /* time of last access */ 1037*134e1779SJakub Wojciech Klama time_t st_mtime; /* time of last modification */ 1038*134e1779SJakub Wojciech Klama time_t st_ctime; /* time of last status change */ 1039*134e1779SJakub Wojciech Klama }; 1040*134e1779SJakub Wojciech Klama 1041*134e1779SJakub Wojciech Klama The differences are: 1042*134e1779SJakub Wojciech Klama 1043*134e1779SJakub Wojciech Klama * st_dev is omitted 1044*134e1779SJakub Wojciech Klama * st_ino is contained in the path component of qid 1045*134e1779SJakub Wojciech Klama * times are nanosecond resolution 1046*134e1779SJakub Wojciech Klama * btime, gen and data_version fields are reserved for future use 1047*134e1779SJakub Wojciech Klama 1048*134e1779SJakub Wojciech Klama Not all fields are valid in every call. request_mask is a bitmask 1049*134e1779SJakub Wojciech Klama indicating which fields are requested. valid is a bitmask 1050*134e1779SJakub Wojciech Klama indicating which fields are valid in the response. The mask 1051*134e1779SJakub Wojciech Klama values are as follows: 1052*134e1779SJakub Wojciech Klama 1053*134e1779SJakub Wojciech Klama #define GETATTR_MODE 0x00000001 1054*134e1779SJakub Wojciech Klama #define GETATTR_NLINK 0x00000002 1055*134e1779SJakub Wojciech Klama #define GETATTR_UID 0x00000004 1056*134e1779SJakub Wojciech Klama #define GETATTR_GID 0x00000008 1057*134e1779SJakub Wojciech Klama #define GETATTR_RDEV 0x00000010 1058*134e1779SJakub Wojciech Klama #define GETATTR_ATIME 0x00000020 1059*134e1779SJakub Wojciech Klama #define GETATTR_MTIME 0x00000040 1060*134e1779SJakub Wojciech Klama #define GETATTR_CTIME 0x00000080 1061*134e1779SJakub Wojciech Klama #define GETATTR_INO 0x00000100 1062*134e1779SJakub Wojciech Klama #define GETATTR_SIZE 0x00000200 1063*134e1779SJakub Wojciech Klama #define GETATTR_BLOCKS 0x00000400 1064*134e1779SJakub Wojciech Klama 1065*134e1779SJakub Wojciech Klama #define GETATTR_BTIME 0x00000800 1066*134e1779SJakub Wojciech Klama #define GETATTR_GEN 0x00001000 1067*134e1779SJakub Wojciech Klama #define GETATTR_DATA_VERSION 0x00002000 1068*134e1779SJakub Wojciech Klama 1069*134e1779SJakub Wojciech Klama #define GETATTR_BASIC 0x000007ff /* Mask for fields up to BLOCKS */ 1070*134e1779SJakub Wojciech Klama #define GETATTR_ALL 0x00003fff /* Mask for All fields above */ 1071*134e1779SJakub Wojciech Klama 1072*134e1779SJakub Wojciech KlamaTsetattr.L = 26: tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8] \ 1073*134e1779SJakub Wojciech Klama atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] 1074*134e1779SJakub Wojciech KlamaRsetattr.L: tag[2] 1075*134e1779SJakub Wojciech Klama setattr sets attributes of a file system object referenced by 1076*134e1779SJakub Wojciech Klama fid. As with getattr, valid is a bitmask selecting which 1077*134e1779SJakub Wojciech Klama fields to set, which can be any combination of: 1078*134e1779SJakub Wojciech Klama 1079*134e1779SJakub Wojciech Klama mode - Linux chmod(2) mode bits. 1080*134e1779SJakub Wojciech Klama 1081*134e1779SJakub Wojciech Klama uid, gid - New owner, group of the file as described in Linux chown(2). 1082*134e1779SJakub Wojciech Klama 1083*134e1779SJakub Wojciech Klama size - New file size as handled by Linux truncate(2). 1084*134e1779SJakub Wojciech Klama 1085*134e1779SJakub Wojciech Klama atime_sec, atime_nsec - Time of last file access. 1086*134e1779SJakub Wojciech Klama 1087*134e1779SJakub Wojciech Klama mtime_sec, mtime_nsec - Time of last file modification. 1088*134e1779SJakub Wojciech Klama 1089*134e1779SJakub Wojciech Klama The valid bits are defined as follows: 1090*134e1779SJakub Wojciech Klama 1091*134e1779SJakub Wojciech Klama #define SETATTR_MODE 0x00000001 1092*134e1779SJakub Wojciech Klama #define SETATTR_UID 0x00000002 1093*134e1779SJakub Wojciech Klama #define SETATTR_GID 0x00000004 1094*134e1779SJakub Wojciech Klama #define SETATTR_SIZE 0x00000008 1095*134e1779SJakub Wojciech Klama #define SETATTR_ATIME 0x00000010 1096*134e1779SJakub Wojciech Klama #define SETATTR_MTIME 0x00000020 1097*134e1779SJakub Wojciech Klama #define SETATTR_CTIME 0x00000040 1098*134e1779SJakub Wojciech Klama #define SETATTR_ATIME_SET 0x00000080 1099*134e1779SJakub Wojciech Klama #define SETATTR_MTIME_SET 0x00000100 1100*134e1779SJakub Wojciech Klama 1101*134e1779SJakub Wojciech Klama If a time bit is set without the corresponding SET bit, the 1102*134e1779SJakub Wojciech Klama current system time on the server is used instead of the value 1103*134e1779SJakub Wojciech Klama sent in the request. 1104*134e1779SJakub Wojciech Klama 1105*134e1779SJakub Wojciech KlamaTxattrwalk.L = 30: tag[2] fid[4] newfid[4] name[s] 1106*134e1779SJakub Wojciech KlamaRxattrwalk.L: tag[2] size[8] 1107*134e1779SJakub Wojciech Klama xattrwalk gets a newfid pointing to xattr name. This fid can 1108*134e1779SJakub Wojciech Klama later be used to read the xattr value. If name is NULL newfid 1109*134e1779SJakub Wojciech Klama can be used to get the list of extended attributes associated 1110*134e1779SJakub Wojciech Klama with the file system object. 1111*134e1779SJakub Wojciech Klama 1112*134e1779SJakub Wojciech KlamaTxattrcreate.L = 32: tag[2] fid[4] name[s] attr_size[8] flags[4] 1113*134e1779SJakub Wojciech KlamaRxattrcreate.L: tag[2] 1114*134e1779SJakub Wojciech Klama xattrcreate gets a fid pointing to the xattr name. This fid 1115*134e1779SJakub Wojciech Klama can later be used to set the xattr value. 1116*134e1779SJakub Wojciech Klama 1117*134e1779SJakub Wojciech Klama flag is derived from set Linux setxattr. The manpage says 1118*134e1779SJakub Wojciech Klama 1119*134e1779SJakub Wojciech Klama The flags parameter can be used to refine the semantics of 1120*134e1779SJakub Wojciech Klama the operation. XATTR_CREATE specifies a pure create, 1121*134e1779SJakub Wojciech Klama which fails if the named attribute exists already. 1122*134e1779SJakub Wojciech Klama XATTR_REPLACE specifies a pure replace operation, which 1123*134e1779SJakub Wojciech Klama fails if the named attribute does not already exist. By 1124*134e1779SJakub Wojciech Klama default (no flags), the extended attribute will be created 1125*134e1779SJakub Wojciech Klama if need be, or will simply replace the value if the 1126*134e1779SJakub Wojciech Klama attribute exists. 1127*134e1779SJakub Wojciech Klama 1128*134e1779SJakub Wojciech Klama The actual setxattr operation happens when the fid is clunked. 1129*134e1779SJakub Wojciech Klama At that point the written byte count and the attr_size 1130*134e1779SJakub Wojciech Klama specified in TXATTRCREATE should be same otherwise an error 1131*134e1779SJakub Wojciech Klama will be returned. 1132*134e1779SJakub Wojciech Klama 1133*134e1779SJakub Wojciech KlamaTreaddir.L = 40: tag[2] fid[4] offset[8] count[4] 1134*134e1779SJakub Wojciech KlamaRreaddir.L: tag[2] count[4] data[count] 1135*134e1779SJakub Wojciech Klama readdir requests that the server return directory entries from 1136*134e1779SJakub Wojciech Klama the directory represented by fid, previously opened with 1137*134e1779SJakub Wojciech Klama lopen. offset is zero on the first call. 1138*134e1779SJakub Wojciech Klama 1139*134e1779SJakub Wojciech Klama Directory entries are represented as variable-length records: 1140*134e1779SJakub Wojciech Klama qid[qid] offset[8] type[1] name[s] 1141*134e1779SJakub Wojciech Klama At most count bytes will be returned in data. If count is not 1142*134e1779SJakub Wojciech Klama zero in the response, more data is available. On subsequent 1143*134e1779SJakub Wojciech Klama calls, offset is the offset returned in the last directory 1144*134e1779SJakub Wojciech Klama entry of the previous call. 1145*134e1779SJakub Wojciech Klama 1146*134e1779SJakub Wojciech KlamaTfsync.L = 50: tag[2] fid[4] 1147*134e1779SJakub Wojciech KlamaRfsync.L: tag[2] 1148*134e1779SJakub Wojciech Klama fsync tells the server to flush any cached data associated 1149*134e1779SJakub Wojciech Klama with fid, previously opened with lopen. 1150*134e1779SJakub Wojciech Klama 1151*134e1779SJakub Wojciech KlamaTlock.L = 52: tag[2] fid[4] type[1] flags[4] start[8] length[8] \ 1152*134e1779SJakub Wojciech Klama proc_id[4] client_id[s] 1153*134e1779SJakub Wojciech KlamaRlock.L: tag[2] status[1] 1154*134e1779SJakub Wojciech Klama lock is used to acquire or release a POSIX record lock on fid 1155*134e1779SJakub Wojciech Klama and has semantics similar to Linux fcntl(F_SETLK). 1156*134e1779SJakub Wojciech Klama 1157*134e1779SJakub Wojciech Klama type has one of the values: 1158*134e1779SJakub Wojciech Klama 1159*134e1779SJakub Wojciech Klama #define LOCK_TYPE_RDLCK 0 1160*134e1779SJakub Wojciech Klama #define LOCK_TYPE_WRLCK 1 1161*134e1779SJakub Wojciech Klama #define LOCK_TYPE_UNLCK 2 1162*134e1779SJakub Wojciech Klama 1163*134e1779SJakub Wojciech Klama start, length, and proc_id correspond to the analagous fields 1164*134e1779SJakub Wojciech Klama passed to Linux fcntl(F_SETLK): 1165*134e1779SJakub Wojciech Klama 1166*134e1779SJakub Wojciech Klama struct flock { 1167*134e1779SJakub Wojciech Klama short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */ 1168*134e1779SJakub Wojciech Klama short l_whence;/* How to intrprt l_start: SEEK_SET,SEEK_CUR,SEEK_END */ 1169*134e1779SJakub Wojciech Klama off_t l_start; /* Starting offset for lock */ 1170*134e1779SJakub Wojciech Klama off_t l_len; /* Number of bytes to lock */ 1171*134e1779SJakub Wojciech Klama pid_t l_pid; /* PID of process blocking our lock (F_GETLK only) */ 1172*134e1779SJakub Wojciech Klama }; 1173*134e1779SJakub Wojciech Klama 1174*134e1779SJakub Wojciech Klama flags bits are: 1175*134e1779SJakub Wojciech Klama 1176*134e1779SJakub Wojciech Klama #define LOCK_SUCCESS 0 1177*134e1779SJakub Wojciech Klama #define LOCK_BLOCKED 1 1178*134e1779SJakub Wojciech Klama #define LOCK_ERROR 2 1179*134e1779SJakub Wojciech Klama #define LOCK_GRACE 3 1180*134e1779SJakub Wojciech Klama 1181*134e1779SJakub Wojciech Klama The Linux v9fs client implements the fcntl(F_SETLKW) 1182*134e1779SJakub Wojciech Klama (blocking) lock request by calling lock with 1183*134e1779SJakub Wojciech Klama LOCK_FLAGS_BLOCK set. If the response is LOCK_BLOCKED, 1184*134e1779SJakub Wojciech Klama it retries the lock request in an interruptible loop until 1185*134e1779SJakub Wojciech Klama status is no longer LOCK_BLOCKED. 1186*134e1779SJakub Wojciech Klama 1187*134e1779SJakub Wojciech Klama The Linux v9fs client translates BSD advisory locks (flock) to 1188*134e1779SJakub Wojciech Klama whole-file POSIX record locks. v9fs does not implement 1189*134e1779SJakub Wojciech Klama mandatory locks and will return ENOLCK if use is attempted. 1190*134e1779SJakub Wojciech Klama 1191*134e1779SJakub Wojciech Klama Because of POSIX record lock inheritance and upgrade 1192*134e1779SJakub Wojciech Klama properties, pass-through servers must be implemented 1193*134e1779SJakub Wojciech Klama carefully. 1194*134e1779SJakub Wojciech Klama 1195*134e1779SJakub Wojciech KlamaTgetlock.L = 54: tag[2] fid[4] type[1] start[8] length[8] proc_id[4] \ 1196*134e1779SJakub Wojciech Klama client_id[s] 1197*134e1779SJakub Wojciech KlamaRgetlock.L: tag[2] type[1] start[8] length[8] proc_id[4] client_id[s] 1198*134e1779SJakub Wojciech Klama getlock tests for the existence of a POSIX record lock and has 1199*134e1779SJakub Wojciech Klama semantics similar to Linux fcntl(F_GETLK). 1200*134e1779SJakub Wojciech Klama 1201*134e1779SJakub Wojciech Klama As with lock, type has one of the values defined above, and 1202*134e1779SJakub Wojciech Klama start, length, and proc_id correspond to the analagous fields 1203*134e1779SJakub Wojciech Klama in struct flock passed to Linux fcntl(F_GETLK), and client_Id 1204*134e1779SJakub Wojciech Klama is an additional mechanism for uniquely identifying the lock 1205*134e1779SJakub Wojciech Klama requester and is set to the nodename by the Linux v9fs client. 1206*134e1779SJakub Wojciech Klama 1207*134e1779SJakub Wojciech KlamaTlink.L = 70: tag[2] dfid[4] fid[4] name[s] 1208*134e1779SJakub Wojciech KlamaRlink.L: tag[2] 1209*134e1779SJakub Wojciech Klama link creates a hard link name in directory dfid. The link 1210*134e1779SJakub Wojciech Klama target is referenced by fid. 1211*134e1779SJakub Wojciech Klama 1212*134e1779SJakub Wojciech KlamaTmkdir.L = 72: tag[2] dfid[4] name[s] mode[4] gid[4] 1213*134e1779SJakub Wojciech KlamaRmkdir.L: tag[2] qid[qid] 1214*134e1779SJakub Wojciech Klama mkdir creates a new directory name in parent directory dfid. 1215*134e1779SJakub Wojciech Klama 1216*134e1779SJakub Wojciech Klama mode contains Linux mkdir(2) mode bits. 1217*134e1779SJakub Wojciech Klama 1218*134e1779SJakub Wojciech Klama gid is the effective group ID of the caller. 1219*134e1779SJakub Wojciech Klama 1220*134e1779SJakub Wojciech Klama The qid of the new directory is returned in the response. 1221*134e1779SJakub Wojciech Klama 1222*134e1779SJakub Wojciech KlamaTrenameat.L = 74: tag[2] olddirfid[4] oldname[s] newdirfid[4] newname[s] 1223*134e1779SJakub Wojciech KlamaRrenameat.L: tag[2] 1224*134e1779SJakub Wojciech Klama Change the name of a file from oldname to newname, possible 1225*134e1779SJakub Wojciech Klama moving it from old directory represented by olddirfid to new 1226*134e1779SJakub Wojciech Klama directory represented by newdirfid. 1227*134e1779SJakub Wojciech Klama 1228*134e1779SJakub Wojciech Klama If the server returns ENOTSUPP, the client should fall back to 1229*134e1779SJakub Wojciech Klama the rename operation. 1230*134e1779SJakub Wojciech Klama 1231*134e1779SJakub Wojciech KlamaTunlinkat.L = 76: tag[2] dirfd[4] name[s] flags[4] 1232*134e1779SJakub Wojciech KlamaRunlinkat.L: tag[2] 1233*134e1779SJakub Wojciech Klama Unlink name from directory represented by dirfd. If the file 1234*134e1779SJakub Wojciech Klama is represented by a fid, that fid is not clunked. If the 1235*134e1779SJakub Wojciech Klama server returns ENOTSUPP, the client should fall back to the 1236*134e1779SJakub Wojciech Klama remove operation. 1237*134e1779SJakub Wojciech Klama 1238*134e1779SJakub Wojciech Klama There seems to be only one defined flag: 1239*134e1779SJakub Wojciech Klama 1240*134e1779SJakub Wojciech Klama #define AT_REMOVEDIR 0x200 1241*134e1779SJakub Wojciech Klama 1242*134e1779SJakub Wojciech KlamaTversion = 100: tag[2] msize[4] version[s]:auto 1243*134e1779SJakub Wojciech KlamaRversion: tag[2] msize[4] version[s] 1244*134e1779SJakub Wojciech Klama 1245*134e1779SJakub Wojciech Klama negotiate protocol version 1246*134e1779SJakub Wojciech Klama 1247*134e1779SJakub Wojciech Klama version establishes the msize, which is the maximum message 1248*134e1779SJakub Wojciech Klama size inclusive of the size value that can be handled by both 1249*134e1779SJakub Wojciech Klama client and server. 1250*134e1779SJakub Wojciech Klama 1251*134e1779SJakub Wojciech Klama It also establishes the protocol version. For 9P2000.L 1252*134e1779SJakub Wojciech Klama version must be the string 9P2000.L. 1253*134e1779SJakub Wojciech Klama 1254*134e1779SJakub Wojciech KlamaTauth = 102: tag[2] afid[4] uname[s] aname[s] n_uname[4] 1255*134e1779SJakub Wojciech KlamaRauth: tag[2] aqid[qid] 1256*134e1779SJakub Wojciech Klama auth initiates an authentication handshake for n_uname. 1257*134e1779SJakub Wojciech Klama Rlerror is returned if authentication is not required. If 1258*134e1779SJakub Wojciech Klama successful, afid is used to read/write the authentication 1259*134e1779SJakub Wojciech Klama handshake (protocol does not specify what is read/written), 1260*134e1779SJakub Wojciech Klama and afid is presented in the attach. 1261*134e1779SJakub Wojciech Klama 1262*134e1779SJakub Wojciech KlamaTattach = 104: tag[2] fid[4] afid[4] uname[s] aname[s] {.u: n_uname[4] } 1263*134e1779SJakub Wojciech KlamaRattach: tag[2] qid[qid] 1264*134e1779SJakub Wojciech Klama attach introduces a new user to the server, and establishes 1265*134e1779SJakub Wojciech Klama fid as the root for that user on the file tree selected by 1266*134e1779SJakub Wojciech Klama aname. 1267*134e1779SJakub Wojciech Klama 1268*134e1779SJakub Wojciech Klama afid can be NOFID (~0) or the fid from a previous auth 1269*134e1779SJakub Wojciech Klama handshake. The afid can be clunked immediately after the 1270*134e1779SJakub Wojciech Klama attach. 1271*134e1779SJakub Wojciech Klama 1272*134e1779SJakub Wojciech Klama #define NOFID 0xffffffff 1273*134e1779SJakub Wojciech Klama 1274*134e1779SJakub Wojciech Klama n_uname, if not set to NONUNAME (~0), is the uid of the 1275*134e1779SJakub Wojciech Klama user and is used in preference to uname. Note that it appears 1276*134e1779SJakub Wojciech Klama in both .u and .L (unlike most .u-specific features). 1277*134e1779SJakub Wojciech Klama 1278*134e1779SJakub Wojciech Klama #define NONUNAME 0xffffffff 1279*134e1779SJakub Wojciech Klama 1280*134e1779SJakub Wojciech Klama v9fs has several modes of access which determine how it uses 1281*134e1779SJakub Wojciech Klama attach. In the default access=user, an initial attach is sent 1282*134e1779SJakub Wojciech Klama for the user provided in the uname=name mount option, and for 1283*134e1779SJakub Wojciech Klama each user that accesses the file system thereafter. For 1284*134e1779SJakub Wojciech Klama access=, only the initial attach is sent for and all other 1285*134e1779SJakub Wojciech Klama users are denied access by the client. 1286*134e1779SJakub Wojciech Klama 1287*134e1779SJakub Wojciech KlamaRerror = 107: tag[2] errstr[s] {.u: errnum[4] } 1288*134e1779SJakub Wojciech Klama 1289*134e1779SJakub Wojciech KlamaTflush = 108: tag[2] oldtag[2] 1290*134e1779SJakub Wojciech KlamaRflush: tag[2] 1291*134e1779SJakub Wojciech Klama flush aborts an in-flight request referenced by oldtag, if any. 1292*134e1779SJakub Wojciech Klama 1293*134e1779SJakub Wojciech KlamaTwalk = 110: tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) 1294*134e1779SJakub Wojciech KlamaRwalk: tag[2] nwqid[2] nwqid*(wqid[qid]) 1295*134e1779SJakub Wojciech Klama walk is used to descend a directory represented by fid using 1296*134e1779SJakub Wojciech Klama successive path elements provided in the wname array. If 1297*134e1779SJakub Wojciech Klama succesful, newfid represents the new path. 1298*134e1779SJakub Wojciech Klama 1299*134e1779SJakub Wojciech Klama fid can be cloned to newfid by calling walk with nwname set to 1300*134e1779SJakub Wojciech Klama zero. 1301*134e1779SJakub Wojciech Klama 1302*134e1779SJakub Wojciech Klama if nwname==0, fid need not represent a directory. 1303*134e1779SJakub Wojciech Klama 1304*134e1779SJakub Wojciech KlamaTopen = 112: tag[2] fid[4] mode[1] 1305*134e1779SJakub Wojciech KlamaRopen: tag[2] qid[qid] iounit[4] 1306*134e1779SJakub Wojciech Klama open prepares fid for file (or directory) I/O. 1307*134e1779SJakub Wojciech Klama 1308*134e1779SJakub Wojciech Klama mode is: 1309*134e1779SJakub Wojciech Klama #define OREAD 0 /* open for read */ 1310*134e1779SJakub Wojciech Klama #define OWRITE 1 /* open for write */ 1311*134e1779SJakub Wojciech Klama #define ORDWR 2 /* open for read and write */ 1312*134e1779SJakub Wojciech Klama #define OEXEC 3 /* open for execute */ 1313*134e1779SJakub Wojciech Klama 1314*134e1779SJakub Wojciech Klama #define OTRUNC 16 /* truncate (illegal if OEXEC) */ 1315*134e1779SJakub Wojciech Klama #define OCEXEC 32 /* close on exec (nonsensical) */ 1316*134e1779SJakub Wojciech Klama #define ORCLOSE 64 /* remove on close */ 1317*134e1779SJakub Wojciech Klama #define ODIRECT 128 /* direct access (.u extension?) */ 1318*134e1779SJakub Wojciech Klama 1319*134e1779SJakub Wojciech KlamaTcreate = 114: tag[2] fid[4] name[s] perm[4] mode[1] {.u: extension[s] } 1320*134e1779SJakub Wojciech KlamaRcreate: tag[2] qid[qid] iounit[4] 1321*134e1779SJakub Wojciech Klama create is similar to open; however, the incoming fid is the 1322*134e1779SJakub Wojciech Klama diretory in which the file is to be created, and on success, 1323*134e1779SJakub Wojciech Klama return, the fid refers to the then-created file. 1324*134e1779SJakub Wojciech Klama 1325*134e1779SJakub Wojciech KlamaTread = 116: tag[2] fid[4] offset[8] count[4] 1326*134e1779SJakub Wojciech KlamaRread: tag[2] count[4] data[count] 1327*134e1779SJakub Wojciech Klama perform a read on the file represented by fid. Note that in 1328*134e1779SJakub Wojciech Klama v9fs, a read(2) or write(2) system call for a chunk of the 1329*134e1779SJakub Wojciech Klama file that won't fit in a single request is broken up into 1330*134e1779SJakub Wojciech Klama multiple requests. 1331*134e1779SJakub Wojciech Klama 1332*134e1779SJakub Wojciech Klama Under 9P2000.L, read cannot be used on directories. See readdir. 1333*134e1779SJakub Wojciech Klama 1334*134e1779SJakub Wojciech KlamaTwrite = 118: tag[2] fid[4] offset[8] count[4] data[count] 1335*134e1779SJakub Wojciech KlamaRwrite: tag[2] count[4] 1336*134e1779SJakub Wojciech Klama perform a write on the file represented by fid. Note that in 1337*134e1779SJakub Wojciech Klama v9fs, a read(2) or write(2) system call for a chunk of the 1338*134e1779SJakub Wojciech Klama file that won't fit in a single request is broken up into 1339*134e1779SJakub Wojciech Klama multiple requests. 1340*134e1779SJakub Wojciech Klama 1341*134e1779SJakub Wojciech Klama write cannot be used on directories. 1342*134e1779SJakub Wojciech Klama 1343*134e1779SJakub Wojciech KlamaTclunk = 120: tag[2] fid[4] 1344*134e1779SJakub Wojciech KlamaRclunk: tag[2] 1345*134e1779SJakub Wojciech Klama clunk signifies that fid is no longer needed by the client. 1346*134e1779SJakub Wojciech Klama 1347*134e1779SJakub Wojciech KlamaTremove = 122: tag[2] fid[4] 1348*134e1779SJakub Wojciech KlamaRremove: tag[2] 1349*134e1779SJakub Wojciech Klama remove removes the file system object represented by fid. 1350*134e1779SJakub Wojciech Klama 1351*134e1779SJakub Wojciech Klama The fid is always clunked (even on error). 1352*134e1779SJakub Wojciech Klama 1353*134e1779SJakub Wojciech KlamaTstat = 124: tag[2] fid[4] 1354*134e1779SJakub Wojciech KlamaRstat: tag[2] size[2] data[size] 1355*134e1779SJakub Wojciech Klama 1356*134e1779SJakub Wojciech KlamaTwstat = 126: tag[2] fid[4] size[2] data[size] 1357*134e1779SJakub Wojciech KlamaRwstat: tag[2] 1358*134e1779SJakub Wojciech Klama""" 1359*134e1779SJakub Wojciech Klama 1360*134e1779SJakub Wojciech Klamaclass _Token(object): 1361*134e1779SJakub Wojciech Klama r""" 1362*134e1779SJakub Wojciech Klama A scanned token. 1363*134e1779SJakub Wojciech Klama 1364*134e1779SJakub Wojciech Klama Tokens have a type (tok.ttype) and value (tok.value). The value 1365*134e1779SJakub Wojciech Klama is generally the token itself, although sometimes a prefix and/or 1366*134e1779SJakub Wojciech Klama suffix has been removed (for 'label', 'word*', ':aux', and 1367*134e1779SJakub Wojciech Klama '[type]' tokens). If prefix and/or suffix are removed, the full 1368*134e1779SJakub Wojciech Klama original token is 1369*134e1779SJakub Wojciech Klama in its .orig. 1370*134e1779SJakub Wojciech Klama 1371*134e1779SJakub Wojciech Klama Tokens are: 1372*134e1779SJakub Wojciech Klama - 'word', 'word*', or 'label': 1373*134e1779SJakub Wojciech Klama '[.\w]+' followed by optional '*' or ':': 1374*134e1779SJakub Wojciech Klama 1375*134e1779SJakub Wojciech Klama - 'aux': ':' followed by '\w+' (used for :auto annotation) 1376*134e1779SJakub Wojciech Klama 1377*134e1779SJakub Wojciech Klama - 'type': 1378*134e1779SJakub Wojciech Klama open bracket '[', followed by '\w+' or '\d+' (only one of these), 1379*134e1779SJakub Wojciech Klama followed by close bracket ']' 1380*134e1779SJakub Wojciech Klama 1381*134e1779SJakub Wojciech Klama - '(', ')', '{', '}': themeselves 1382*134e1779SJakub Wojciech Klama 1383*134e1779SJakub Wojciech Klama Each token can have arbitrary leading white space (which is 1384*134e1779SJakub Wojciech Klama discarded). 1385*134e1779SJakub Wojciech Klama 1386*134e1779SJakub Wojciech Klama (Probably should return ':' as a char and handle it in parser, 1387*134e1779SJakub Wojciech Klama but oh well.) 1388*134e1779SJakub Wojciech Klama """ 1389*134e1779SJakub Wojciech Klama def __init__(self, ttype, value, orig=None): 1390*134e1779SJakub Wojciech Klama self.ttype = ttype 1391*134e1779SJakub Wojciech Klama self.value = value 1392*134e1779SJakub Wojciech Klama self.orig = value if orig is None else orig 1393*134e1779SJakub Wojciech Klama if self.ttype == 'type' and self.value.isdigit(): 1394*134e1779SJakub Wojciech Klama self.ival = int(self.value) 1395*134e1779SJakub Wojciech Klama else: 1396*134e1779SJakub Wojciech Klama self.ival = None 1397*134e1779SJakub Wojciech Klama def __str__(self): 1398*134e1779SJakub Wojciech Klama return self.orig 1399*134e1779SJakub Wojciech Klama 1400*134e1779SJakub Wojciech Klama_Token.tok_expr = re.compile(r'\s*([.\w]+(?:\*|:)?' 1401*134e1779SJakub Wojciech Klama r'|:\w+' 1402*134e1779SJakub Wojciech Klama r'|\[(?:\w+|\d+)\]' 1403*134e1779SJakub Wojciech Klama r'|[(){}])') 1404*134e1779SJakub Wojciech Klama 1405*134e1779SJakub Wojciech Klamadef _scan(string): 1406*134e1779SJakub Wojciech Klama """ 1407*134e1779SJakub Wojciech Klama Tokenize a string. 1408*134e1779SJakub Wojciech Klama 1409*134e1779SJakub Wojciech Klama Note: This raises a ValueError with the position of any unmatched 1410*134e1779SJakub Wojciech Klama character in the string. 1411*134e1779SJakub Wojciech Klama """ 1412*134e1779SJakub Wojciech Klama tlist = [] 1413*134e1779SJakub Wojciech Klama 1414*134e1779SJakub Wojciech Klama # make sure entire string is tokenized properly 1415*134e1779SJakub Wojciech Klama pos = 0 1416*134e1779SJakub Wojciech Klama for item in _Token.tok_expr.finditer(string): 1417*134e1779SJakub Wojciech Klama span = item.span() 1418*134e1779SJakub Wojciech Klama if span[0] != pos: 1419*134e1779SJakub Wojciech Klama print('error: unmatched character(s) in input\n{0}\n{1}^'.format( 1420*134e1779SJakub Wojciech Klama string, ' ' * pos)) 1421*134e1779SJakub Wojciech Klama raise ValueError('unmatched lexeme', pos) 1422*134e1779SJakub Wojciech Klama pos = span[1] 1423*134e1779SJakub Wojciech Klama tlist.append(item.group(1)) 1424*134e1779SJakub Wojciech Klama if pos != len(string): 1425*134e1779SJakub Wojciech Klama print('error: unmatched character(s) in input\n{0}\n{1}^'.format( 1426*134e1779SJakub Wojciech Klama string, ' ' * pos)) 1427*134e1779SJakub Wojciech Klama raise ValueError('unmatched lexeme', pos) 1428*134e1779SJakub Wojciech Klama 1429*134e1779SJakub Wojciech Klama # classify each token, stripping decorations 1430*134e1779SJakub Wojciech Klama result = [] 1431*134e1779SJakub Wojciech Klama for item in tlist: 1432*134e1779SJakub Wojciech Klama if item in ('(', ')', '{', '}'): 1433*134e1779SJakub Wojciech Klama tok = _Token(item, item) 1434*134e1779SJakub Wojciech Klama elif item[0] == ':': 1435*134e1779SJakub Wojciech Klama tok = _Token('aux', item[1:], item) 1436*134e1779SJakub Wojciech Klama elif item.endswith(':'): 1437*134e1779SJakub Wojciech Klama tok = _Token('label', item[0:-1], item) 1438*134e1779SJakub Wojciech Klama elif item.endswith('*'): 1439*134e1779SJakub Wojciech Klama tok = _Token('word*', item[0:-1], item) 1440*134e1779SJakub Wojciech Klama elif item[0] == '[': 1441*134e1779SJakub Wojciech Klama # integer or named type 1442*134e1779SJakub Wojciech Klama if item[-1] != ']': 1443*134e1779SJakub Wojciech Klama raise ValueError('internal error: "{0}" is not [...]'.format( 1444*134e1779SJakub Wojciech Klama item)) 1445*134e1779SJakub Wojciech Klama tok = _Token('type', item[1:-1], item) 1446*134e1779SJakub Wojciech Klama else: 1447*134e1779SJakub Wojciech Klama tok = _Token('word', item) 1448*134e1779SJakub Wojciech Klama result.append(tok) 1449*134e1779SJakub Wojciech Klama return result 1450*134e1779SJakub Wojciech Klama 1451*134e1779SJakub Wojciech Klamadef _debug_print_sequencer(seq): 1452*134e1779SJakub Wojciech Klama """for debugging""" 1453*134e1779SJakub Wojciech Klama print('sequencer is {0!r}'.format(seq), file=sys.stderr) 1454*134e1779SJakub Wojciech Klama for i, enc in enumerate(seq): 1455*134e1779SJakub Wojciech Klama print(' [{0:d}] = {1}'.format(i, enc), file=sys.stderr) 1456*134e1779SJakub Wojciech Klama 1457*134e1779SJakub Wojciech Klamadef _parse_expr(seq, string, typedefs): 1458*134e1779SJakub Wojciech Klama """ 1459*134e1779SJakub Wojciech Klama Parse "expression-ish" items, which is a list of: 1460*134e1779SJakub Wojciech Klama name[type] 1461*134e1779SJakub Wojciech Klama name*(subexpr) (a literal asterisk) 1462*134e1779SJakub Wojciech Klama { label ... } 1463*134e1779SJakub Wojciech Klama 1464*134e1779SJakub Wojciech Klama The "type" may be an integer or a second name. In the case 1465*134e1779SJakub Wojciech Klama of a second name it must be something from <typedefs>. 1466*134e1779SJakub Wojciech Klama 1467*134e1779SJakub Wojciech Klama The meaning of name[integer] is that we are going to encode 1468*134e1779SJakub Wojciech Klama or decode a fixed-size field of <integer> bytes, using the 1469*134e1779SJakub Wojciech Klama given name. 1470*134e1779SJakub Wojciech Klama 1471*134e1779SJakub Wojciech Klama For name[name2], we can look up name2 in our typedefs table. 1472*134e1779SJakub Wojciech Klama The only real typedefs's used here are "stat" and "s"; each 1473*134e1779SJakub Wojciech Klama of these expands to a variable-size encode/decode. See the 1474*134e1779SJakub Wojciech Klama special case below, though. 1475*134e1779SJakub Wojciech Klama 1476*134e1779SJakub Wojciech Klama The meaning of name*(...) is: the earlier name will have been 1477*134e1779SJakub Wojciech Klama defined by an earlier _parse_expr for this same line. That 1478*134e1779SJakub Wojciech Klama earlier name provides a repeat-count. 1479*134e1779SJakub Wojciech Klama 1480*134e1779SJakub Wojciech Klama Inside the parens we get a name[type] sub-expressino. This may 1481*134e1779SJakub Wojciech Klama not recurse further, so we can use a pretty cheesy parser. 1482*134e1779SJakub Wojciech Klama 1483*134e1779SJakub Wojciech Klama As a special case, given name[name2], we first check whether 1484*134e1779SJakub Wojciech Klama name2 is an earlier name a la name*(...). Here the meaning 1485*134e1779SJakub Wojciech Klama is much like name2*(name[1]), except that the result is a 1486*134e1779SJakub Wojciech Klama simple byte string, rather than an array. 1487*134e1779SJakub Wojciech Klama 1488*134e1779SJakub Wojciech Klama The meaning of "{ label ... " is that everything following up 1489*134e1779SJakub Wojciech Klama to "}" is optional and used only with 9P2000.u and/or 9P2000.L. 1490*134e1779SJakub Wojciech Klama Inside the {...} pair is the usual set of tokens, but again 1491*134e1779SJakub Wojciech Klama {...} cannot recurse. 1492*134e1779SJakub Wojciech Klama 1493*134e1779SJakub Wojciech Klama The parse fills in a Sequencer instance, and returns a list 1494*134e1779SJakub Wojciech Klama of the parsed names. 1495*134e1779SJakub Wojciech Klama """ 1496*134e1779SJakub Wojciech Klama names = [] 1497*134e1779SJakub Wojciech Klama cond = None 1498*134e1779SJakub Wojciech Klama 1499*134e1779SJakub Wojciech Klama tokens = collections.deque(_scan(string)) 1500*134e1779SJakub Wojciech Klama 1501*134e1779SJakub Wojciech Klama def get_subscripted(tokens): 1502*134e1779SJakub Wojciech Klama """ 1503*134e1779SJakub Wojciech Klama Allows name[integer] and name1[name2] only; returns 1504*134e1779SJakub Wojciech Klama tuple after stripping off both tokens, or returns None 1505*134e1779SJakub Wojciech Klama and does not strip tokens. 1506*134e1779SJakub Wojciech Klama """ 1507*134e1779SJakub Wojciech Klama if len(tokens) == 0 or tokens[0].ttype != 'word': 1508*134e1779SJakub Wojciech Klama return None 1509*134e1779SJakub Wojciech Klama if len(tokens) > 1 and tokens[1].ttype == 'type': 1510*134e1779SJakub Wojciech Klama word = tokens.popleft() 1511*134e1779SJakub Wojciech Klama return word, tokens.popleft() 1512*134e1779SJakub Wojciech Klama return None 1513*134e1779SJakub Wojciech Klama 1514*134e1779SJakub Wojciech Klama def lookup(name, typeinfo, aux=None): 1515*134e1779SJakub Wojciech Klama """ 1516*134e1779SJakub Wojciech Klama Convert cond (if not None) to its .value, so that instead 1517*134e1779SJakub Wojciech Klama of (x, '.u') we get '.u'. 1518*134e1779SJakub Wojciech Klama 1519*134e1779SJakub Wojciech Klama Convert typeinfo to an encdec. Typeinfo may be 1/2/4/8, or 1520*134e1779SJakub Wojciech Klama one of our typedef names. If it's a typedef name it will 1521*134e1779SJakub Wojciech Klama normally correspond to an EncDecTyped, but we have one special 1522*134e1779SJakub Wojciech Klama case for string types, and another for using an earlier-defined 1523*134e1779SJakub Wojciech Klama variable. 1524*134e1779SJakub Wojciech Klama """ 1525*134e1779SJakub Wojciech Klama condval = None if cond is None else cond.value 1526*134e1779SJakub Wojciech Klama if typeinfo.ival is None: 1527*134e1779SJakub Wojciech Klama try: 1528*134e1779SJakub Wojciech Klama cls, sub = typedefs[typeinfo.value] 1529*134e1779SJakub Wojciech Klama except KeyError: 1530*134e1779SJakub Wojciech Klama raise ValueError('unknown type name {0}'.format(typeinfo)) 1531*134e1779SJakub Wojciech Klama # the type name is typeinfo.value; the corresponding 1532*134e1779SJakub Wojciech Klama # pfod class is cls; the *variable* name is name; 1533*134e1779SJakub Wojciech Klama # and the sub-sequence is sub. But if cls is None 1534*134e1779SJakub Wojciech Klama # then it's our string type. 1535*134e1779SJakub Wojciech Klama if cls is None: 1536*134e1779SJakub Wojciech Klama encdec = sequencer.EncDecSimple(name, _STRING_MAGIC, aux) 1537*134e1779SJakub Wojciech Klama else: 1538*134e1779SJakub Wojciech Klama encdec = sequencer.EncDecTyped(cls, name, sub, aux) 1539*134e1779SJakub Wojciech Klama else: 1540*134e1779SJakub Wojciech Klama if typeinfo.ival not in (1, 2, 4, 8): 1541*134e1779SJakub Wojciech Klama raise ValueError('bad integer code in {0}'.format(typeinfo)) 1542*134e1779SJakub Wojciech Klama encdec = sequencer.EncDecSimple(name, typeinfo.ival, aux) 1543*134e1779SJakub Wojciech Klama return condval, encdec 1544*134e1779SJakub Wojciech Klama 1545*134e1779SJakub Wojciech Klama def emit_simple(name, typeinfo, aux=None): 1546*134e1779SJakub Wojciech Klama """ 1547*134e1779SJakub Wojciech Klama Emit name[type]. We may be inside a conditional; if so 1548*134e1779SJakub Wojciech Klama cond is not None. 1549*134e1779SJakub Wojciech Klama """ 1550*134e1779SJakub Wojciech Klama condval, encdec = lookup(name, typeinfo, aux) 1551*134e1779SJakub Wojciech Klama seq.append_encdec(condval, encdec) 1552*134e1779SJakub Wojciech Klama names.append(name) 1553*134e1779SJakub Wojciech Klama 1554*134e1779SJakub Wojciech Klama def emit_repeat(name1, name2, typeinfo): 1555*134e1779SJakub Wojciech Klama """ 1556*134e1779SJakub Wojciech Klama Emit name1*(name2[type]). 1557*134e1779SJakub Wojciech Klama 1558*134e1779SJakub Wojciech Klama Note that the conditional is buried in the sub-coder for 1559*134e1779SJakub Wojciech Klama name2. It must be passed through anyway in case the sub- 1560*134e1779SJakub Wojciech Klama coder is only partly conditional. If the sub-coder is 1561*134e1779SJakub Wojciech Klama fully conditional, each sub-coding uses or produces no 1562*134e1779SJakub Wojciech Klama bytes and hence the array itself is effectively conditional 1563*134e1779SJakub Wojciech Klama as well (it becomes name1 * [None]). 1564*134e1779SJakub Wojciech Klama 1565*134e1779SJakub Wojciech Klama We don't (currently) have any auxiliary data for arrays. 1566*134e1779SJakub Wojciech Klama """ 1567*134e1779SJakub Wojciech Klama if name1 not in names: 1568*134e1779SJakub Wojciech Klama raise ValueError('{0}*({1}[{2}]): ' 1569*134e1779SJakub Wojciech Klama '{0} undefined'.format(name1, name2, 1570*134e1779SJakub Wojciech Klama typeinfo.value)) 1571*134e1779SJakub Wojciech Klama condval, encdec = lookup(name2, typeinfo) 1572*134e1779SJakub Wojciech Klama encdec = sequencer.EncDecA(name1, name2, encdec) 1573*134e1779SJakub Wojciech Klama seq.append_encdec(condval, encdec) 1574*134e1779SJakub Wojciech Klama names.append(name2) 1575*134e1779SJakub Wojciech Klama 1576*134e1779SJakub Wojciech Klama def emit_bytes_repeat(name1, name2): 1577*134e1779SJakub Wojciech Klama """ 1578*134e1779SJakub Wojciech Klama Emit name1[name2], e.g., data[count]. 1579*134e1779SJakub Wojciech Klama """ 1580*134e1779SJakub Wojciech Klama condval = None if cond is None else cond.value 1581*134e1779SJakub Wojciech Klama # Note that the two names are reversed when compared to 1582*134e1779SJakub Wojciech Klama # count*(data[type]). The "sub-coder" is handled directly 1583*134e1779SJakub Wojciech Klama # by EncDecA, hence is None. 1584*134e1779SJakub Wojciech Klama # 1585*134e1779SJakub Wojciech Klama # As a peculiar side effect, all bytes-repeats cause the 1586*134e1779SJakub Wojciech Klama # count itself to become automatic (to have an aux of 'len'). 1587*134e1779SJakub Wojciech Klama encdec = sequencer.EncDecA(name2, name1, None, 'len') 1588*134e1779SJakub Wojciech Klama seq.append_encdec(condval, encdec) 1589*134e1779SJakub Wojciech Klama names.append(name1) 1590*134e1779SJakub Wojciech Klama 1591*134e1779SJakub Wojciech Klama supported_conditions = ('.u') 1592*134e1779SJakub Wojciech Klama while tokens: 1593*134e1779SJakub Wojciech Klama token = tokens.popleft() 1594*134e1779SJakub Wojciech Klama if token.ttype == 'label': 1595*134e1779SJakub Wojciech Klama raise ValueError('misplaced label') 1596*134e1779SJakub Wojciech Klama if token.ttype == 'aux': 1597*134e1779SJakub Wojciech Klama raise ValueError('misplaced auxiliary') 1598*134e1779SJakub Wojciech Klama if token.ttype == '{': 1599*134e1779SJakub Wojciech Klama if cond is not None: 1600*134e1779SJakub Wojciech Klama raise ValueError('nested "{"') 1601*134e1779SJakub Wojciech Klama if len(tokens) == 0: 1602*134e1779SJakub Wojciech Klama raise ValueError('unclosed "{"') 1603*134e1779SJakub Wojciech Klama cond = tokens.popleft() 1604*134e1779SJakub Wojciech Klama if cond.ttype != 'label': 1605*134e1779SJakub Wojciech Klama raise ValueError('"{" not followed by cond label') 1606*134e1779SJakub Wojciech Klama if cond.value not in supported_conditions: 1607*134e1779SJakub Wojciech Klama raise ValueError('unsupported condition "{0}"'.format( 1608*134e1779SJakub Wojciech Klama cond.value)) 1609*134e1779SJakub Wojciech Klama continue 1610*134e1779SJakub Wojciech Klama if token.ttype == '}': 1611*134e1779SJakub Wojciech Klama if cond is None: 1612*134e1779SJakub Wojciech Klama raise ValueError('closing "}" w/o opening "{"') 1613*134e1779SJakub Wojciech Klama cond = None 1614*134e1779SJakub Wojciech Klama continue 1615*134e1779SJakub Wojciech Klama if token.ttype == 'word*': 1616*134e1779SJakub Wojciech Klama if len(tokens) == 0 or tokens[0].ttype != '(': 1617*134e1779SJakub Wojciech Klama raise ValueError('{0} not followed by (...)'.format(token)) 1618*134e1779SJakub Wojciech Klama tokens.popleft() 1619*134e1779SJakub Wojciech Klama repeat = get_subscripted(tokens) 1620*134e1779SJakub Wojciech Klama if repeat is None: 1621*134e1779SJakub Wojciech Klama raise ValueError('parse error after {0}('.format(token)) 1622*134e1779SJakub Wojciech Klama if len(tokens) == 0 or tokens[0].ttype != ')': 1623*134e1779SJakub Wojciech Klama raise ValueError('missing ")" after {0}({1}{2}'.format( 1624*134e1779SJakub Wojciech Klama token, repeat[0], repeat[1])) 1625*134e1779SJakub Wojciech Klama tokens.popleft() 1626*134e1779SJakub Wojciech Klama # N.B.: a repeat cannot have an auxiliary info (yet?). 1627*134e1779SJakub Wojciech Klama emit_repeat(token.value, repeat[0].value, repeat[1]) 1628*134e1779SJakub Wojciech Klama continue 1629*134e1779SJakub Wojciech Klama if token.ttype == 'word': 1630*134e1779SJakub Wojciech Klama # Special case: _STRING_MAGIC turns into a string 1631*134e1779SJakub Wojciech Klama # sequencer. This should be used with just one 1632*134e1779SJakub Wojciech Klama # typedef (typedef s: _string_). 1633*134e1779SJakub Wojciech Klama if token.value == _STRING_MAGIC: 1634*134e1779SJakub Wojciech Klama names.append(_STRING_MAGIC) # XXX temporary 1635*134e1779SJakub Wojciech Klama continue 1636*134e1779SJakub Wojciech Klama if len(tokens) == 0 or tokens[0].ttype != 'type': 1637*134e1779SJakub Wojciech Klama raise ValueError('parse error after {0}'.format(token)) 1638*134e1779SJakub Wojciech Klama type_or_size = tokens.popleft() 1639*134e1779SJakub Wojciech Klama # Check for name[name2] where name2 is a word (not a 1640*134e1779SJakub Wojciech Klama # number) that is in the names[] array. 1641*134e1779SJakub Wojciech Klama if type_or_size.value in names: 1642*134e1779SJakub Wojciech Klama # NB: this cannot have auxiliary info. 1643*134e1779SJakub Wojciech Klama emit_bytes_repeat(token.value, type_or_size.value) 1644*134e1779SJakub Wojciech Klama continue 1645*134e1779SJakub Wojciech Klama if len(tokens) > 0 and tokens[0].ttype == 'aux': 1646*134e1779SJakub Wojciech Klama aux = tokens.popleft() 1647*134e1779SJakub Wojciech Klama if aux.value != 'auto': 1648*134e1779SJakub Wojciech Klama raise ValueError('{0}{1}: only know "auto", not ' 1649*134e1779SJakub Wojciech Klama '{2}'.format(token, type_or_size, 1650*134e1779SJakub Wojciech Klama aux.value)) 1651*134e1779SJakub Wojciech Klama emit_simple(token.value, type_or_size, aux.value) 1652*134e1779SJakub Wojciech Klama else: 1653*134e1779SJakub Wojciech Klama emit_simple(token.value, type_or_size) 1654*134e1779SJakub Wojciech Klama continue 1655*134e1779SJakub Wojciech Klama raise ValueError('"{0}" not valid here"'.format(token)) 1656*134e1779SJakub Wojciech Klama 1657*134e1779SJakub Wojciech Klama if cond is not None: 1658*134e1779SJakub Wojciech Klama raise ValueError('unclosed "}"') 1659*134e1779SJakub Wojciech Klama 1660*134e1779SJakub Wojciech Klama return names 1661*134e1779SJakub Wojciech Klama 1662*134e1779SJakub Wojciech Klamaclass _ProtoDefs(object): 1663*134e1779SJakub Wojciech Klama def __init__(self): 1664*134e1779SJakub Wojciech Klama # Scan our typedefs. This may execute '#define's as well. 1665*134e1779SJakub Wojciech Klama self.typedefs = {} 1666*134e1779SJakub Wojciech Klama self.defines = {} 1667*134e1779SJakub Wojciech Klama typedef_re = re.compile(r'\s*typedef\s+(\w+)\s*:\s*(.*)') 1668*134e1779SJakub Wojciech Klama self.parse_lines('SDesc', SDesc, typedef_re, self.handle_typedef) 1669*134e1779SJakub Wojciech Klama self.parse_lines('QIDDesc', QIDDesc, typedef_re, self.handle_typedef) 1670*134e1779SJakub Wojciech Klama self.parse_lines('STATDesc', STATDesc, typedef_re, self.handle_typedef) 1671*134e1779SJakub Wojciech Klama self.parse_lines('WirestatDesc', WirestatDesc, typedef_re, 1672*134e1779SJakub Wojciech Klama self.handle_typedef) 1673*134e1779SJakub Wojciech Klama self.parse_lines('DirentDesc', DirentDesc, typedef_re, 1674*134e1779SJakub Wojciech Klama self.handle_typedef) 1675*134e1779SJakub Wojciech Klama 1676*134e1779SJakub Wojciech Klama # Scan protocol (the bulk of the work). This, too, may 1677*134e1779SJakub Wojciech Klama # execute '#define's. 1678*134e1779SJakub Wojciech Klama self.protocol = {} 1679*134e1779SJakub Wojciech Klama proto_re = re.compile(r'(\*?\w+)(\.\w+)?\s*(?:=\s*(\d+))?\s*:\s*(.*)') 1680*134e1779SJakub Wojciech Klama self.prev_proto_value = None 1681*134e1779SJakub Wojciech Klama self.parse_lines('ProtocolDesc', ProtocolDesc, 1682*134e1779SJakub Wojciech Klama proto_re, self.handle_proto_def) 1683*134e1779SJakub Wojciech Klama 1684*134e1779SJakub Wojciech Klama self.setup_header() 1685*134e1779SJakub Wojciech Klama 1686*134e1779SJakub Wojciech Klama # set these up for export() 1687*134e1779SJakub Wojciech Klama self.plain = {} 1688*134e1779SJakub Wojciech Klama self.dotu = {} 1689*134e1779SJakub Wojciech Klama self.dotl = {} 1690*134e1779SJakub Wojciech Klama 1691*134e1779SJakub Wojciech Klama def parse_lines(self, name, text, regexp, match_handler): 1692*134e1779SJakub Wojciech Klama """ 1693*134e1779SJakub Wojciech Klama Parse a sequence of lines. Match each line using the 1694*134e1779SJakub Wojciech Klama given regexp, or (first) as a #define line. Note that 1695*134e1779SJakub Wojciech Klama indented lines are either #defines or are commentary! 1696*134e1779SJakub Wojciech Klama 1697*134e1779SJakub Wojciech Klama If hnadling raises a ValueError, we complain and include 1698*134e1779SJakub Wojciech Klama the appropriate line offset. Then we sys.exit(1) (!). 1699*134e1779SJakub Wojciech Klama """ 1700*134e1779SJakub Wojciech Klama define = re.compile(r'\s*#define\s+(\w+)\s+([^/]*)' 1701*134e1779SJakub Wojciech Klama r'(\s*/\*.*\*/)?\s*$') 1702*134e1779SJakub Wojciech Klama for lineoff, line in enumerate(text.splitlines()): 1703*134e1779SJakub Wojciech Klama try: 1704*134e1779SJakub Wojciech Klama match = define.match(line) 1705*134e1779SJakub Wojciech Klama if match: 1706*134e1779SJakub Wojciech Klama self.handle_define(*match.groups()) 1707*134e1779SJakub Wojciech Klama continue 1708*134e1779SJakub Wojciech Klama match = regexp.match(line) 1709*134e1779SJakub Wojciech Klama if match: 1710*134e1779SJakub Wojciech Klama match_handler(*match.groups()) 1711*134e1779SJakub Wojciech Klama continue 1712*134e1779SJakub Wojciech Klama if len(line) and not line[0].isspace(): 1713*134e1779SJakub Wojciech Klama raise ValueError('unhandled line: {0}'.format(line)) 1714*134e1779SJakub Wojciech Klama except ValueError as err: 1715*134e1779SJakub Wojciech Klama print('Internal error while parsing {0}:\n' 1716*134e1779SJakub Wojciech Klama ' {1}\n' 1717*134e1779SJakub Wojciech Klama '(at line offset +{2}, discounting \\-newline)\n' 1718*134e1779SJakub Wojciech Klama 'The original line in question reads:\n' 1719*134e1779SJakub Wojciech Klama '{3}'.format(name, err.args[0], lineoff, line), 1720*134e1779SJakub Wojciech Klama file=sys.stderr) 1721*134e1779SJakub Wojciech Klama sys.exit(1) 1722*134e1779SJakub Wojciech Klama 1723*134e1779SJakub Wojciech Klama def handle_define(self, name, value, comment): 1724*134e1779SJakub Wojciech Klama """ 1725*134e1779SJakub Wojciech Klama Handle #define match. 1726*134e1779SJakub Wojciech Klama 1727*134e1779SJakub Wojciech Klama The regexp has three fields, matching the name, value, 1728*134e1779SJakub Wojciech Klama and possibly-empty comment; these are our arguments. 1729*134e1779SJakub Wojciech Klama """ 1730*134e1779SJakub Wojciech Klama # Obnoxious: int(,0) requires new 0o syntax in py3k; 1731*134e1779SJakub Wojciech Klama # work around by trying twice, once with base 0, then again 1732*134e1779SJakub Wojciech Klama # with explicit base 8 if the first attempt fails. 1733*134e1779SJakub Wojciech Klama try: 1734*134e1779SJakub Wojciech Klama value = int(value, 0) 1735*134e1779SJakub Wojciech Klama except ValueError: 1736*134e1779SJakub Wojciech Klama value = int(value, 8) 1737*134e1779SJakub Wojciech Klama if DEBUG: 1738*134e1779SJakub Wojciech Klama print('define: defining {0} as {1:x}'.format(name, value), 1739*134e1779SJakub Wojciech Klama file=sys.stderr) 1740*134e1779SJakub Wojciech Klama if name in self.defines: 1741*134e1779SJakub Wojciech Klama raise ValueError('redefining {0}'.format(name)) 1742*134e1779SJakub Wojciech Klama self.defines[name] = (value, comment) 1743*134e1779SJakub Wojciech Klama 1744*134e1779SJakub Wojciech Klama def handle_typedef(self, name, expr): 1745*134e1779SJakub Wojciech Klama """ 1746*134e1779SJakub Wojciech Klama Handle typedef match. 1747*134e1779SJakub Wojciech Klama 1748*134e1779SJakub Wojciech Klama The regexp has just two fields, the name and the expression 1749*134e1779SJakub Wojciech Klama to parse (note that the expression must fit all on one line, 1750*134e1779SJakub Wojciech Klama using backslach-newline if needed). 1751*134e1779SJakub Wojciech Klama 1752*134e1779SJakub Wojciech Klama Typedefs may refer back to existing typedefs, so we pass 1753*134e1779SJakub Wojciech Klama self.typedefs to _parse_expr(). 1754*134e1779SJakub Wojciech Klama """ 1755*134e1779SJakub Wojciech Klama seq = sequencer.Sequencer(name) 1756*134e1779SJakub Wojciech Klama fields = _parse_expr(seq, expr, self.typedefs) 1757*134e1779SJakub Wojciech Klama # Check for special string magic typedef. (The name 1758*134e1779SJakub Wojciech Klama # probably should be just 's' but we won't check that 1759*134e1779SJakub Wojciech Klama # here.) 1760*134e1779SJakub Wojciech Klama if len(fields) == 1 and fields[0] == _STRING_MAGIC: 1761*134e1779SJakub Wojciech Klama cls = None 1762*134e1779SJakub Wojciech Klama else: 1763*134e1779SJakub Wojciech Klama cls = pfod.pfod(name, fields) 1764*134e1779SJakub Wojciech Klama if DEBUG: 1765*134e1779SJakub Wojciech Klama print('typedef: {0} = {1!r}; '.format(name, fields), 1766*134e1779SJakub Wojciech Klama end='', file=sys.stderr) 1767*134e1779SJakub Wojciech Klama _debug_print_sequencer(seq) 1768*134e1779SJakub Wojciech Klama if name in self.typedefs: 1769*134e1779SJakub Wojciech Klama raise ValueError('redefining {0}'.format(name)) 1770*134e1779SJakub Wojciech Klama self.typedefs[name] = cls, seq 1771*134e1779SJakub Wojciech Klama 1772*134e1779SJakub Wojciech Klama def handle_proto_def(self, name, proto_version, value, expr): 1773*134e1779SJakub Wojciech Klama """ 1774*134e1779SJakub Wojciech Klama Handle protocol definition. 1775*134e1779SJakub Wojciech Klama 1776*134e1779SJakub Wojciech Klama The regexp matched: 1777*134e1779SJakub Wojciech Klama - The name of the protocol option such as Tversion, 1778*134e1779SJakub Wojciech Klama Rversion, Rlerror, etc. 1779*134e1779SJakub Wojciech Klama - The protocol version, if any (.u or .L). 1780*134e1779SJakub Wojciech Klama - The value, if specified. If no value is specified 1781*134e1779SJakub Wojciech Klama we use "the next value". 1782*134e1779SJakub Wojciech Klama - The expression to parse. 1783*134e1779SJakub Wojciech Klama 1784*134e1779SJakub Wojciech Klama As with typedefs, the expression must fit all on one 1785*134e1779SJakub Wojciech Klama line. 1786*134e1779SJakub Wojciech Klama """ 1787*134e1779SJakub Wojciech Klama if value: 1788*134e1779SJakub Wojciech Klama value = int(value) 1789*134e1779SJakub Wojciech Klama elif self.prev_proto_value is not None: 1790*134e1779SJakub Wojciech Klama value = self.prev_proto_value + 1 1791*134e1779SJakub Wojciech Klama else: 1792*134e1779SJakub Wojciech Klama raise ValueError('{0}: missing protocol value'.format(name)) 1793*134e1779SJakub Wojciech Klama if value < 0 or value > 255: 1794*134e1779SJakub Wojciech Klama raise ValueError('{0}: protocol value {1} out of ' 1795*134e1779SJakub Wojciech Klama 'range'.format(name, value)) 1796*134e1779SJakub Wojciech Klama self.prev_proto_value = value 1797*134e1779SJakub Wojciech Klama 1798*134e1779SJakub Wojciech Klama seq = sequencer.Sequencer(name) 1799*134e1779SJakub Wojciech Klama fields = _parse_expr(seq, expr, self.typedefs) 1800*134e1779SJakub Wojciech Klama cls = pfod.pfod(name, fields) 1801*134e1779SJakub Wojciech Klama if DEBUG: 1802*134e1779SJakub Wojciech Klama print('proto: {0} = {1}; '.format(name, value), 1803*134e1779SJakub Wojciech Klama end='', file=sys.stderr) 1804*134e1779SJakub Wojciech Klama _debug_print_sequencer(seq) 1805*134e1779SJakub Wojciech Klama if name in self.protocol: 1806*134e1779SJakub Wojciech Klama raise ValueError('redefining {0}'.format(name)) 1807*134e1779SJakub Wojciech Klama self.protocol[name] = cls, value, proto_version, seq 1808*134e1779SJakub Wojciech Klama 1809*134e1779SJakub Wojciech Klama def setup_header(self): 1810*134e1779SJakub Wojciech Klama """ 1811*134e1779SJakub Wojciech Klama Handle header definition. 1812*134e1779SJakub Wojciech Klama 1813*134e1779SJakub Wojciech Klama This is a bit gimmicky and uses some special cases, 1814*134e1779SJakub Wojciech Klama because data is sized to dsize which is effectively 1815*134e1779SJakub Wojciech Klama just size - 5. We can't express this in our mini language, 1816*134e1779SJakub Wojciech Klama so we just hard-code the sequencer and pfod. 1817*134e1779SJakub Wojciech Klama 1818*134e1779SJakub Wojciech Klama In addition, the unpacker never gets the original packet's 1819*134e1779SJakub Wojciech Klama size field, only the fcall and the data. 1820*134e1779SJakub Wojciech Klama """ 1821*134e1779SJakub Wojciech Klama self.header_pfod = pfod.pfod('Header', 'size dsize fcall data') 1822*134e1779SJakub Wojciech Klama 1823*134e1779SJakub Wojciech Klama seq = sequencer.Sequencer('Header-pack') 1824*134e1779SJakub Wojciech Klama # size: 4 bytes 1825*134e1779SJakub Wojciech Klama seq.append_encdec(None, sequencer.EncDecSimple('size', 4, None)) 1826*134e1779SJakub Wojciech Klama # fcall: 1 byte 1827*134e1779SJakub Wojciech Klama seq.append_encdec(None, sequencer.EncDecSimple('fcall', 1, None)) 1828*134e1779SJakub Wojciech Klama # data: string of length dsize 1829*134e1779SJakub Wojciech Klama seq.append_encdec(None, sequencer.EncDecA('dsize', 'data', None)) 1830*134e1779SJakub Wojciech Klama if DEBUG: 1831*134e1779SJakub Wojciech Klama print('Header-pack:', file=sys.stderr) 1832*134e1779SJakub Wojciech Klama _debug_print_sequencer(seq) 1833*134e1779SJakub Wojciech Klama self.header_pack_seq = seq 1834*134e1779SJakub Wojciech Klama 1835*134e1779SJakub Wojciech Klama seq = sequencer.Sequencer('Header-unpack') 1836*134e1779SJakub Wojciech Klama seq.append_encdec(None, sequencer.EncDecSimple('fcall', 1, None)) 1837*134e1779SJakub Wojciech Klama seq.append_encdec(None, sequencer.EncDecA('dsize', 'data', None)) 1838*134e1779SJakub Wojciech Klama if DEBUG: 1839*134e1779SJakub Wojciech Klama print('Header-unpack:', file=sys.stderr) 1840*134e1779SJakub Wojciech Klama _debug_print_sequencer(seq) 1841*134e1779SJakub Wojciech Klama self.header_unpack_seq = seq 1842*134e1779SJakub Wojciech Klama 1843*134e1779SJakub Wojciech Klama def export(self, mod): 1844*134e1779SJakub Wojciech Klama """ 1845*134e1779SJakub Wojciech Klama Dump results of internal parsing process 1846*134e1779SJakub Wojciech Klama into our module namespace. 1847*134e1779SJakub Wojciech Klama 1848*134e1779SJakub Wojciech Klama Note that we do not export the 's' typedef, which 1849*134e1779SJakub Wojciech Klama did not define a data structure. 1850*134e1779SJakub Wojciech Klama 1851*134e1779SJakub Wojciech Klama Check for name collisions while we're at it. 1852*134e1779SJakub Wojciech Klama """ 1853*134e1779SJakub Wojciech Klama namespace = type('td', (object,), {}) 1854*134e1779SJakub Wojciech Klama 1855*134e1779SJakub Wojciech Klama # Export the typedefs (qid, stat). 1856*134e1779SJakub Wojciech Klama setattr(mod, 'td', namespace) 1857*134e1779SJakub Wojciech Klama for key in self.typedefs: 1858*134e1779SJakub Wojciech Klama cls = self.typedefs[key][0] 1859*134e1779SJakub Wojciech Klama if cls is None: 1860*134e1779SJakub Wojciech Klama continue 1861*134e1779SJakub Wojciech Klama setattr(namespace, key, cls) 1862*134e1779SJakub Wojciech Klama 1863*134e1779SJakub Wojciech Klama # Export two sequencers for en/decoding stat fields 1864*134e1779SJakub Wojciech Klama # (needed for reading directories and doing Twstat). 1865*134e1779SJakub Wojciech Klama setattr(namespace, 'stat_seq', self.typedefs['stat'][1]) 1866*134e1779SJakub Wojciech Klama setattr(namespace, 'wirestat_seq', self.typedefs['wirestat'][1]) 1867*134e1779SJakub Wojciech Klama 1868*134e1779SJakub Wojciech Klama # Export the similar dirent decoder. 1869*134e1779SJakub Wojciech Klama setattr(namespace, 'dirent_seq', self.typedefs['dirent'][1]) 1870*134e1779SJakub Wojciech Klama 1871*134e1779SJakub Wojciech Klama # Export the #define values 1872*134e1779SJakub Wojciech Klama for key, val in self.defines.items(): 1873*134e1779SJakub Wojciech Klama if hasattr(namespace, key): 1874*134e1779SJakub Wojciech Klama print('{0!r} is both a #define and a typedef'.format(key)) 1875*134e1779SJakub Wojciech Klama raise AssertionError('bad internal names') 1876*134e1779SJakub Wojciech Klama setattr(namespace, key, val[0]) 1877*134e1779SJakub Wojciech Klama 1878*134e1779SJakub Wojciech Klama # Export Tattach, Rattach, Twrite, Rversion, etc values. 1879*134e1779SJakub Wojciech Klama # Set up fcall_names[] table to map from value back to name. 1880*134e1779SJakub Wojciech Klama # We also map fcall names to themselves, so given either a 1881*134e1779SJakub Wojciech Klama # name or a byte code we can find out whether it's a valid 1882*134e1779SJakub Wojciech Klama # fcall. 1883*134e1779SJakub Wojciech Klama for key, val in self.protocol.items(): 1884*134e1779SJakub Wojciech Klama if hasattr(namespace, key): 1885*134e1779SJakub Wojciech Klama prev_def = '#define' if key in self.defines else 'typedef' 1886*134e1779SJakub Wojciech Klama print('{0!r} is both a {1} and a protocol ' 1887*134e1779SJakub Wojciech Klama 'value'.format(key, prev_def)) 1888*134e1779SJakub Wojciech Klama raise AssertionError('bad internal names') 1889*134e1779SJakub Wojciech Klama setattr(namespace, key, val[1]) 1890*134e1779SJakub Wojciech Klama fcall_names[key] = key 1891*134e1779SJakub Wojciech Klama fcall_names[val[1]] = key 1892*134e1779SJakub Wojciech Klama 1893*134e1779SJakub Wojciech Klama # Hook up PFOD's for each protocol object -- for 1894*134e1779SJakub Wojciech Klama # Tversion/Rversion, Twrite/Rwrite, Tlopen/Rlopen, etc. 1895*134e1779SJakub Wojciech Klama # They go in the rrd name-space, and also in dictionaries 1896*134e1779SJakub Wojciech Klama # per-protocol here, with the lookup pointing to a _PackInfo 1897*134e1779SJakub Wojciech Klama # for the corresponding sequencer. 1898*134e1779SJakub Wojciech Klama # 1899*134e1779SJakub Wojciech Klama # Note that each protocol PFOD is optionally annotated with 1900*134e1779SJakub Wojciech Klama # its specific version. We know that .L > .u > plain; but 1901*134e1779SJakub Wojciech Klama # all the "lesser" PFODs are available to all "greater" 1902*134e1779SJakub Wojciech Klama # protocols at all times. 1903*134e1779SJakub Wojciech Klama # 1904*134e1779SJakub Wojciech Klama # (This is sort-of-wrong for Rerror vs Rlerror, but we 1905*134e1779SJakub Wojciech Klama # don't bother to exclude Rerror from .L.) 1906*134e1779SJakub Wojciech Klama # 1907*134e1779SJakub Wojciech Klama # The PFODs themselves were already created, at parse time. 1908*134e1779SJakub Wojciech Klama namespace = type('rrd', (object,), {}) 1909*134e1779SJakub Wojciech Klama setattr(mod, 'rrd', namespace) 1910*134e1779SJakub Wojciech Klama for key, val in self.protocol.items(): 1911*134e1779SJakub Wojciech Klama cls = val[0] 1912*134e1779SJakub Wojciech Klama proto_version = val[2] 1913*134e1779SJakub Wojciech Klama seq = val[3] 1914*134e1779SJakub Wojciech Klama packinfo = _PackInfo(seq) 1915*134e1779SJakub Wojciech Klama if proto_version is None: 1916*134e1779SJakub Wojciech Klama # all three protocols have it 1917*134e1779SJakub Wojciech Klama self.plain[cls] = packinfo 1918*134e1779SJakub Wojciech Klama self.dotu[cls] = packinfo 1919*134e1779SJakub Wojciech Klama self.dotl[cls] = packinfo 1920*134e1779SJakub Wojciech Klama elif proto_version == '.u': 1921*134e1779SJakub Wojciech Klama # only .u and .L have it 1922*134e1779SJakub Wojciech Klama self.dotu[cls] = packinfo 1923*134e1779SJakub Wojciech Klama self.dotl[cls] = packinfo 1924*134e1779SJakub Wojciech Klama elif proto_version == '.L': 1925*134e1779SJakub Wojciech Klama # only .L has it 1926*134e1779SJakub Wojciech Klama self.dotl[cls] = packinfo 1927*134e1779SJakub Wojciech Klama else: 1928*134e1779SJakub Wojciech Klama raise AssertionError('unknown protocol {1} for ' 1929*134e1779SJakub Wojciech Klama '{0}'.format(key, proto_version)) 1930*134e1779SJakub Wojciech Klama setattr(namespace, key, cls) 1931*134e1779SJakub Wojciech Klama 1932*134e1779SJakub Wojciech Klama_9p_data = _ProtoDefs() 1933*134e1779SJakub Wojciech Klama_9p_data.export(sys.modules[__name__]) 1934*134e1779SJakub Wojciech Klama 1935*134e1779SJakub Wojciech Klama# Currently we look up by text-string, in lowercase. 1936*134e1779SJakub Wojciech Klama_9p_versions = { 1937*134e1779SJakub Wojciech Klama '9p2000': _P9Proto({'version': '9P2000'}, 1938*134e1779SJakub Wojciech Klama {'.u': False}, 1939*134e1779SJakub Wojciech Klama _9p_data, 1940*134e1779SJakub Wojciech Klama _9p_data.plain, 1941*134e1779SJakub Wojciech Klama 0), 1942*134e1779SJakub Wojciech Klama '9p2000.u': _P9Proto({'version': '9P2000.u'}, 1943*134e1779SJakub Wojciech Klama {'.u': True}, 1944*134e1779SJakub Wojciech Klama _9p_data, 1945*134e1779SJakub Wojciech Klama _9p_data.dotu, 1946*134e1779SJakub Wojciech Klama 1), 1947*134e1779SJakub Wojciech Klama '9p2000.l': _P9Proto({'version': '9P2000.L'}, 1948*134e1779SJakub Wojciech Klama {'.u': True}, 1949*134e1779SJakub Wojciech Klama _9p_data, 1950*134e1779SJakub Wojciech Klama _9p_data.dotl, 1951*134e1779SJakub Wojciech Klama 2), 1952*134e1779SJakub Wojciech Klama} 1953*134e1779SJakub Wojciech Klamadef p9_version(vers_string): 1954*134e1779SJakub Wojciech Klama """ 1955*134e1779SJakub Wojciech Klama Return protocol implementation of given version. Raises 1956*134e1779SJakub Wojciech Klama KeyError if the version is invalid. Note that the KeyError 1957*134e1779SJakub Wojciech Klama will be on a string-ified, lower-cased version of the vers_string 1958*134e1779SJakub Wojciech Klama argument, even if it comes in as a bytes instance in py3k. 1959*134e1779SJakub Wojciech Klama """ 1960*134e1779SJakub Wojciech Klama if not isinstance(vers_string, str) and isinstance(vers_string, bytes): 1961*134e1779SJakub Wojciech Klama vers_string = vers_string.decode('utf-8', 'surrogateescape') 1962*134e1779SJakub Wojciech Klama return _9p_versions[vers_string.lower()] 1963*134e1779SJakub Wojciech Klama 1964*134e1779SJakub Wojciech Klamaplain = p9_version('9p2000') 1965*134e1779SJakub Wojciech Klamadotu = p9_version('9p2000.u') 1966*134e1779SJakub Wojciech Klamadotl = p9_version('9p2000.L') 1967*134e1779SJakub Wojciech Klama 1968*134e1779SJakub Wojciech Klamadef qid_type2name(qidtype): 1969*134e1779SJakub Wojciech Klama """ 1970*134e1779SJakub Wojciech Klama Convert qid type field to printable string. 1971*134e1779SJakub Wojciech Klama 1972*134e1779SJakub Wojciech Klama >>> qid_type2name(td.QTDIR) 1973*134e1779SJakub Wojciech Klama 'dir' 1974*134e1779SJakub Wojciech Klama >>> qid_type2name(td.QTAPPEND) 1975*134e1779SJakub Wojciech Klama 'append-only' 1976*134e1779SJakub Wojciech Klama >>> qid_type2name(0xff) 1977*134e1779SJakub Wojciech Klama 'invalid(0xff)' 1978*134e1779SJakub Wojciech Klama """ 1979*134e1779SJakub Wojciech Klama try: 1980*134e1779SJakub Wojciech Klama # Is it ever OK to have multiple bits set, 1981*134e1779SJakub Wojciech Klama # e.g., both QTAPPEND and QTEXCL? 1982*134e1779SJakub Wojciech Klama return { 1983*134e1779SJakub Wojciech Klama td.QTDIR: 'dir', 1984*134e1779SJakub Wojciech Klama td.QTAPPEND: 'append-only', 1985*134e1779SJakub Wojciech Klama td.QTEXCL: 'exclusive', 1986*134e1779SJakub Wojciech Klama td.QTMOUNT: 'mount', 1987*134e1779SJakub Wojciech Klama td.QTAUTH: 'auth', 1988*134e1779SJakub Wojciech Klama td.QTTMP: 'tmp', 1989*134e1779SJakub Wojciech Klama td.QTSYMLINK: 'symlink', 1990*134e1779SJakub Wojciech Klama td.QTFILE: 'file', 1991*134e1779SJakub Wojciech Klama }[qidtype] 1992*134e1779SJakub Wojciech Klama except KeyError: 1993*134e1779SJakub Wojciech Klama pass 1994*134e1779SJakub Wojciech Klama return 'invalid({0:#x})'.format(qidtype) 1995*134e1779SJakub Wojciech Klama 1996*134e1779SJakub Wojciech Klamaif __name__ == '__main__': 1997*134e1779SJakub Wojciech Klama import doctest 1998*134e1779SJakub Wojciech Klama doctest.testmod() 1999