190 lines
6.1 KiB
Python
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
|