summaryrefslogtreecommitdiffstats
path: root/scripts/remote/ratpfs.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/remote/ratpfs.py')
-rw-r--r--scripts/remote/ratpfs.py189
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