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