9
0
Fork 0
barebox/scripts/remote/ratpfs.py

190 lines
6.1 KiB
Python

#!/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