diff options
Diffstat (limited to 'scripts/remote/ratpfs.py')
-rw-r--r-- | scripts/remote/ratpfs.py | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/scripts/remote/ratpfs.py b/scripts/remote/ratpfs.py new file mode 100644 index 0000000000..91ca044540 --- /dev/null +++ b/scripts/remote/ratpfs.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function + +import logging +import os +import stat +import struct +from enum import IntEnum + +from .messages import BBPacketFS, BBPacketFSReturn + +class RatpFSType(IntEnum): + invalid = 0 + mount_call = 1 + mount_return = 2 + readdir_call = 3 + readdir_return = 4 + stat_call = 5 + stat_return = 6 + open_call = 7 + open_return = 8 + read_call = 9 + read_return = 10 + write_call = 11 + write_return = 12 + close_call = 13 + close_return = 14 + truncate_call = 15 + truncate_return = 16 + + +class RatpFSError(ValueError): + pass + + +class RatpFSPacket(object): + def __init__(self, type=RatpFSType.invalid, payload="", raw=None): + if raw is not None: + type, = struct.unpack('!B', raw[:1]) + self.type = RatpFSType(type) + self.payload = raw[1:] + else: + self.type = type + self.payload = payload + + def __repr__(self): + s = "%s(" % self.__class__.__name__ + s += "TYPE=%i," % self.type + s += "PAYLOAD=%s)" % repr(self.payload) + return s + + def pack(self): + return struct.pack('!B', int(self.type))+self.payload + + +class RatpFSServer(object): + def __init__(self, path=None): + self.path = path + if path: + self.path = os.path.abspath(os.path.expanduser(path)) + self.next_handle = 1 # 0 is invalid + self.files = {} + self.mounted = False + logging.info("exporting: %s", self.path) + + def _alloc_handle(self): + handle = self.next_handle + self.next_handle += 1 + return handle + + def _resolve(self, path): + components = path.split('/') + components = [x for x in components if x and x != '..'] + return os.path.join(self.path, *components) + + def handle_stat(self, path): + + try: + logging.info("path: %r", path) + path = self._resolve(path) + logging.info("path1: %r", path) + s = os.stat(path) + except OSError as e: + return struct.pack('!BI', 0, e.errno) + if stat.S_ISREG(s.st_mode): + return struct.pack('!BI', 1, s.st_size) + elif stat.S_ISDIR(s.st_mode): + return struct.pack('!BI', 2, s.st_size) + else: + return struct.pack('!BI', 0, 0) + + def handle_open(self, params): + flags, = struct.unpack('!I', params[:4]) + flags = flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR | os.O_CREAT | + os.O_TRUNC) + path = params[4:] + try: + f = os.open(self._resolve(path), flags, 0666) + except OSError as e: + return struct.pack('!II', 0, e.errno) + h = self._alloc_handle() + self.files[h] = f + size = os.lseek(f, 0, os.SEEK_END) + return struct.pack('!II', h, size) + + def handle_read(self, params): + h, pos, size = struct.unpack('!III', params) + f = self.files[h] + os.lseek(f, pos, os.SEEK_SET) + size = min(size, 4096) + return os.read(f, size) + + def handle_write(self, params): + h, pos = struct.unpack('!II', params[:8]) + payload = params[8:] + f = self.files[h] + pos = os.lseek(f, pos, os.SEEK_SET) + assert os.write(f, payload) == len(payload) + return "" + + def handle_readdir(self, path): + res = "" + for x in os.listdir(self._resolve(path)): + res += x+'\0' + return res + + def handle_close(self, params): + h, = struct.unpack('!I', params[:4]) + os.close(self.files.pop(h)) + return "" + + def handle_truncate(self, params): + h, size = struct.unpack('!II', params) + f = self.files[h] + os.ftruncate(f, size) + return "" + + def handle(self, bbcall): + assert isinstance(bbcall, BBPacketFS) + logging.debug("bb-call: %s", bbcall) + fscall = RatpFSPacket(raw=bbcall.payload) + logging.info("fs-call: %s", fscall) + + if not self.path: + logging.warning("no filesystem exported") + fsreturn = RatpFSPacket(type=RatpFSType.invalid) + elif fscall.type == RatpFSType.mount_call: + self.mounted = True + fsreturn = RatpFSPacket(type=RatpFSType.mount_return) + elif not self.mounted: + logging.warning("filesystem not mounted") + fsreturn = RatpFSPacket(type=RatpFSType.invalid) + elif fscall.type == RatpFSType.readdir_call: + payload = self.handle_readdir(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.readdir_return, + payload=payload) + elif fscall.type == RatpFSType.stat_call: + payload = self.handle_stat(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.stat_return, + payload=payload) + elif fscall.type == RatpFSType.open_call: + payload = self.handle_open(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.open_return, + payload=payload) + elif fscall.type == RatpFSType.read_call: + payload = self.handle_read(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.read_return, + payload=payload) + elif fscall.type == RatpFSType.write_call: + payload = self.handle_write(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.write_return, + payload=payload) + elif fscall.type == RatpFSType.close_call: + payload = self.handle_close(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.close_return, + payload=payload) + elif fscall.type == RatpFSType.truncate_call: + payload = self.handle_truncate(fscall.payload) + fsreturn = RatpFSPacket(type=RatpFSType.truncate_return, + payload=payload) + else: + raise RatpFSError() + + logging.info("fs-return: %s", fsreturn) + bbreturn = BBPacketFSReturn(payload=fsreturn.pack()) + logging.debug("bb-return: %s", bbreturn) + return bbreturn |