pycfdp 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cfdp/__init__.py +106 -0
- cfdp/checksum.py +94 -0
- cfdp/constants.py +153 -0
- cfdp/core.py +895 -0
- cfdp/event.py +55 -0
- cfdp/filestore/__init__.py +2 -0
- cfdp/filestore/base.py +99 -0
- cfdp/filestore/native.py +162 -0
- cfdp/meta/__init__.py +3 -0
- cfdp/meta/datafield.py +11 -0
- cfdp/meta/filestore.py +127 -0
- cfdp/meta/message.py +659 -0
- cfdp/pdu/__init__.py +8 -0
- cfdp/pdu/ack.py +90 -0
- cfdp/pdu/eof.py +123 -0
- cfdp/pdu/filedata.py +90 -0
- cfdp/pdu/finished.py +99 -0
- cfdp/pdu/header.py +159 -0
- cfdp/pdu/keep_alive.py +108 -0
- cfdp/pdu/metadata.py +211 -0
- cfdp/pdu/nak.py +220 -0
- cfdp/pdu/prompt.py +75 -0
- cfdp/pure/__init__.py +68 -0
- cfdp/pure/config.py +141 -0
- cfdp/pure/indications.py +130 -0
- cfdp/pure/machines/__init__.py +25 -0
- cfdp/pure/machines/receiver1.py +433 -0
- cfdp/pure/machines/receiver2.py +893 -0
- cfdp/pure/machines/sender1.py +400 -0
- cfdp/pure/machines/sender2.py +774 -0
- cfdp/pure/outputs.py +203 -0
- cfdp/pure/query_port.py +48 -0
- cfdp/pure/transaction.py +43 -0
- cfdp/py.typed +0 -0
- cfdp/remote_entity_config.py +12 -0
- cfdp/shell/__init__.py +44 -0
- cfdp/shell/checksum_service.py +136 -0
- cfdp/shell/dispatcher.py +185 -0
- cfdp/shell/effect_executor.py +285 -0
- cfdp/shell/machine_registry.py +165 -0
- cfdp/shell/query_port.py +70 -0
- cfdp/shell/timer_service.py +252 -0
- cfdp/transaction_handle.py +50 -0
- cfdp/transport/__init__.py +0 -0
- cfdp/transport/base.py +12 -0
- cfdp/transport/spacepacket.py +47 -0
- cfdp/transport/udp.py +90 -0
- cfdp/transport/zmq.py +54 -0
- pycfdp-0.2.2.dist-info/METADATA +76 -0
- pycfdp-0.2.2.dist-info/RECORD +53 -0
- pycfdp-0.2.2.dist-info/WHEEL +5 -0
- pycfdp-0.2.2.dist-info/licenses/LICENSE.txt +19 -0
- pycfdp-0.2.2.dist-info/top_level.txt +1 -0
cfdp/event.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class Event:
|
|
2
|
+
def __init__(self, transaction, type, pdu=None, checksum=None):
|
|
3
|
+
self.transaction = transaction
|
|
4
|
+
self.type = type
|
|
5
|
+
# Inbound-PDU payload for a PDU-driven (receive-side) machine. The pure
|
|
6
|
+
# machine contract has a single input door, ``update_state(event)``, so
|
|
7
|
+
# everything the machine needs — including the triggering PDU — rides on
|
|
8
|
+
# the event (design "The machine contract").
|
|
9
|
+
self.pdu = pdu
|
|
10
|
+
# Result payload for the async-checksum channel (design R1 / issue #21).
|
|
11
|
+
# A ``RequestChecksum`` effect is computed off the event thread by the
|
|
12
|
+
# shell's checksum service, which injects an ``E42_CHECKSUM_READY`` event
|
|
13
|
+
# carrying the computed value here. The single input door still holds:
|
|
14
|
+
# the machine reads the result off the event, never from a return value.
|
|
15
|
+
self.checksum = checksum
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EventType:
|
|
19
|
+
E0_ENTERED_STATE = "ENTERED_STATE"
|
|
20
|
+
E1_SEND_FILE_DATA = "SEND_FILE_DATA"
|
|
21
|
+
E2_ABANDON_TRANSACTION = "ABANDON_TRANSACTION"
|
|
22
|
+
E3_NOTICE_OF_CANCELLATION = "NOTICE_OF_CANCELLATION"
|
|
23
|
+
E4_NOTICE_OF_SUSPENSION = "NOTICE_OF_SUSPENSION"
|
|
24
|
+
E5_SUSPEND_TIMERS = "SUSPEND_TIMERS"
|
|
25
|
+
E6_RESUME_TIMERS = "RESUME_TIMERS"
|
|
26
|
+
|
|
27
|
+
E10_RECEIVED_METADATA = "RECEIVED_METADATA"
|
|
28
|
+
E11_RECEIVED_FILEDATA = "RECEIVED_FILEDATA"
|
|
29
|
+
E12_RECEIVED_EOF_NO_ERROR = "RECEIVED_EOF_NO_ERROR"
|
|
30
|
+
E13_RECEIVED_EOF_CANCEL = "RECEIVED_EOF_CANCEL"
|
|
31
|
+
E14_RECEIVED_ACK_EOF = "RECEIVED_ACK_EOF"
|
|
32
|
+
E15_RECEIVED_NAK = "RECEIVED_NAK"
|
|
33
|
+
E16_RECEIVED_FINISHED_NO_ERROR = "RECEIVED_FINISHED_NO_ERROR"
|
|
34
|
+
E17_RECEIVED_FINISHED_CANCEL = "RECEIVED_FINISHED_CANCEL"
|
|
35
|
+
E18_RECEIVED_ACK_FINISHED = "RECEIVED_ACK_FINISHED"
|
|
36
|
+
|
|
37
|
+
E25_ACK_TIMEOUT = "ACK_TIMEOUT"
|
|
38
|
+
E26_NAK_TIMEOUT = "NAK_TIMEOUT"
|
|
39
|
+
E27_INACTIVITY_TIMEOUT = "INACTIVITY_TIMEOUT"
|
|
40
|
+
|
|
41
|
+
E30_RECEIVED_PUT_REQUEST = "RECEIVED_PUT_REQUEST"
|
|
42
|
+
E31_RECEIVED_SUSPEND_REQUEST = "RECEIVED_SUSPEND_REQUEST"
|
|
43
|
+
E32_RECEIVED_RESUME_REQUEST = "RECEIVED_RESUME_REQUEST"
|
|
44
|
+
E33_RECEIVED_CANCEL_REQUEST = "RECEIVED_CANCEL_REQUEST"
|
|
45
|
+
E34_RECEIVED_REPORT_REQUEST = "RECEIVED_REPORT_REQUEST"
|
|
46
|
+
|
|
47
|
+
E40_RECEIVED_FREEZE = "RECEIVED_FREEZE"
|
|
48
|
+
E41_RECEIVED_THAW = "RECEIVED_THAW"
|
|
49
|
+
|
|
50
|
+
# Async-checksum result (design R1 / issue #21). Injected by the shell's
|
|
51
|
+
# checksum service once a ``RequestChecksum`` effect has been computed off
|
|
52
|
+
# the event thread; the computed value rides on ``event.checksum``. The old
|
|
53
|
+
# machines never see this event (they compute checksum synchronously via
|
|
54
|
+
# ``Transaction.get_file_checksum``); it exists only on the pure stack.
|
|
55
|
+
E42_CHECKSUM_READY = "CHECKSUM_READY"
|
cfdp/filestore/base.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FileHandle(ABC):
|
|
5
|
+
"""
|
|
6
|
+
Abstract base class for file handle objects returned by VirtualFileStore.open()
|
|
7
|
+
and VirtualFileStore.open_tempfile(). Implementations must provide all listed
|
|
8
|
+
abstract methods.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def seek(self, offset, whence=0): ...
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def tell(self): ...
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def read(self, n=-1) -> bytes: ...
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def write(self, data: bytes): ...
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def close(self): ...
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def calculate_checksum(self, checksum_type: int) -> int: ...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class VirtualFileStore(ABC):
|
|
31
|
+
"""
|
|
32
|
+
The Virtual File Store (VFS) is an abstraction of file system. An
|
|
33
|
+
implementation must provide the methods listed below.
|
|
34
|
+
|
|
35
|
+
An implementation can map the VFS to a platform dependent native file
|
|
36
|
+
system or a packet store for example.
|
|
37
|
+
|
|
38
|
+
The VFS starts at root '/' and uses slashes for subfolders. This is how
|
|
39
|
+
pathes are to be passed to CFDP methods (eg. Put Request, Filestore
|
|
40
|
+
Requests, and Directory Listings.)
|
|
41
|
+
|
|
42
|
+
Example: '/files/a_file.txt'
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self):
|
|
47
|
+
self.config = None
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def create_file(self, filepath):
|
|
51
|
+
raise NotImplementedError
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def delete_file(self, filepath):
|
|
55
|
+
raise NotImplementedError
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def rename_file(self, filepath1, filepath2):
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def append_file(self, filepath1, filepath2):
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def replace_file(self, filepath1, filepath2):
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def create_directory(self, dirpath):
|
|
71
|
+
raise NotImplementedError
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def remove_directory(self, dirpath):
|
|
75
|
+
raise NotImplementedError
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def list_directory(self, dirpath):
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def open(self, filepath, mode="rb"):
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def open_tempfile(self):
|
|
87
|
+
raise NotImplementedError
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def is_file(self, filepath):
|
|
91
|
+
raise NotImplementedError
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def get_size(self, filepath):
|
|
95
|
+
raise NotImplementedError
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def join_path(self, *args):
|
|
99
|
+
raise NotImplementedError
|
cfdp/filestore/native.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import tempfile
|
|
4
|
+
|
|
5
|
+
from .base import FileHandle, VirtualFileStore
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NativeFileStore(VirtualFileStore):
|
|
9
|
+
"""
|
|
10
|
+
The Native File Store implements the Virtual File Store as a platform
|
|
11
|
+
dependent file system, using the Python 'os.path' library.
|
|
12
|
+
|
|
13
|
+
A rootpath is provided as class instantiation. The VFS paths are then
|
|
14
|
+
relative to this root path. For example, if root path is '/home/demo'
|
|
15
|
+
and a Put Request places a file to '/files/small.txt', then this will
|
|
16
|
+
land in '/home/demo/files/small.txt'.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, rootpath):
|
|
21
|
+
super().__init__()
|
|
22
|
+
self.rootpath = os.path.realpath(rootpath)
|
|
23
|
+
|
|
24
|
+
def get_native_path(self, path):
|
|
25
|
+
# convert from virtual filestore path to absolute native path
|
|
26
|
+
path_items = path.split("/")
|
|
27
|
+
path_items = [x for x in path_items if x != ""]
|
|
28
|
+
if path_items:
|
|
29
|
+
joined = os.path.join(*path_items)
|
|
30
|
+
else:
|
|
31
|
+
joined = ""
|
|
32
|
+
native = os.path.realpath(os.path.join(self.rootpath, joined))
|
|
33
|
+
if native != self.rootpath and not native.startswith(self.rootpath + os.sep):
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"Path traversal detected: {path!r} escapes filestore root"
|
|
36
|
+
)
|
|
37
|
+
return native
|
|
38
|
+
|
|
39
|
+
def get_virtual_path(self, path):
|
|
40
|
+
# convert from native relative path to virtual path
|
|
41
|
+
path = "/".join(os.path.normpath(path).split(os.path.sep))
|
|
42
|
+
if not path.startswith("/"):
|
|
43
|
+
path = "/" + path
|
|
44
|
+
return path
|
|
45
|
+
|
|
46
|
+
def create_file(self, filepath):
|
|
47
|
+
NativeFileHandle(self.get_native_path(filepath), "wb").close()
|
|
48
|
+
|
|
49
|
+
def delete_file(self, filepath):
|
|
50
|
+
os.remove(self.get_native_path(filepath))
|
|
51
|
+
|
|
52
|
+
def rename_file(self, filepath1, filepath2):
|
|
53
|
+
os.rename(self.get_native_path(filepath1), self.get_native_path(filepath2))
|
|
54
|
+
|
|
55
|
+
def append_file(self, filepath1, filepath2):
|
|
56
|
+
fh1 = open(self.get_native_path(filepath1), "ab")
|
|
57
|
+
fh2 = open(self.get_native_path(filepath2), "rb")
|
|
58
|
+
fh1.write(fh2.read())
|
|
59
|
+
fh1.close()
|
|
60
|
+
fh2.close()
|
|
61
|
+
|
|
62
|
+
def replace_file(self, filepath1, filepath2):
|
|
63
|
+
fh1 = open(self.get_native_path(filepath1), "wb")
|
|
64
|
+
fh2 = open(self.get_native_path(filepath2), "rb")
|
|
65
|
+
fh1.write(fh2.read())
|
|
66
|
+
fh1.close()
|
|
67
|
+
fh2.close()
|
|
68
|
+
|
|
69
|
+
def create_directory(self, dirpath):
|
|
70
|
+
os.mkdir(self.get_native_path(dirpath))
|
|
71
|
+
|
|
72
|
+
def remove_directory(self, dirpath):
|
|
73
|
+
shutil.rmtree(self.get_native_path(dirpath))
|
|
74
|
+
|
|
75
|
+
def list_directory(self, dirpath):
|
|
76
|
+
# create directory listing
|
|
77
|
+
dirpath = self.get_native_path(dirpath)
|
|
78
|
+
# get the full path w.r.t to host file system
|
|
79
|
+
dirpath, dirnames, filenames = next(os.walk(dirpath))
|
|
80
|
+
|
|
81
|
+
listing = "type,path,size,timestamp\n"
|
|
82
|
+
for dirname in dirnames:
|
|
83
|
+
fullpath = os.path.join(dirpath, dirname)
|
|
84
|
+
relpath = os.path.relpath(fullpath, self.rootpath)
|
|
85
|
+
line = "{},{},{},{}\n".format(
|
|
86
|
+
"d",
|
|
87
|
+
self.get_virtual_path(relpath),
|
|
88
|
+
os.path.getsize(fullpath),
|
|
89
|
+
os.path.getmtime(fullpath),
|
|
90
|
+
)
|
|
91
|
+
listing += line
|
|
92
|
+
for filename in filenames:
|
|
93
|
+
fullpath = os.path.join(dirpath, filename)
|
|
94
|
+
relpath = os.path.relpath(fullpath, self.rootpath)
|
|
95
|
+
line = "{},{},{},{}\n".format(
|
|
96
|
+
"f",
|
|
97
|
+
self.get_virtual_path(relpath),
|
|
98
|
+
os.path.getsize(fullpath),
|
|
99
|
+
os.path.getmtime(fullpath),
|
|
100
|
+
)
|
|
101
|
+
listing += line
|
|
102
|
+
return listing
|
|
103
|
+
|
|
104
|
+
def open(self, filepath, mode="rb"):
|
|
105
|
+
return NativeFileHandle(self.get_native_path(filepath), mode)
|
|
106
|
+
|
|
107
|
+
def open_tempfile(self):
|
|
108
|
+
return NativeTemporaryFileHandle()
|
|
109
|
+
|
|
110
|
+
def is_file(self, filepath):
|
|
111
|
+
return os.path.isfile(self.get_native_path(filepath))
|
|
112
|
+
|
|
113
|
+
def get_size(self, filepath):
|
|
114
|
+
return os.path.getsize(self.get_native_path(filepath))
|
|
115
|
+
|
|
116
|
+
def join_path(self, *args):
|
|
117
|
+
return "/".join(args)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class NativeFileHandle(FileHandle):
|
|
121
|
+
"""
|
|
122
|
+
The NativeFileHandle provides a generic interface to Python's 'open'
|
|
123
|
+
function. It also implements method for calculating the file checksum.
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__(self, filepath, mode="rb"):
|
|
128
|
+
self.handle = open(filepath, mode)
|
|
129
|
+
|
|
130
|
+
def seek(self, *args, **kwargs):
|
|
131
|
+
return self.handle.seek(*args, **kwargs)
|
|
132
|
+
|
|
133
|
+
def tell(self, *args, **kwargs):
|
|
134
|
+
return self.handle.tell(*args, **kwargs)
|
|
135
|
+
|
|
136
|
+
def read(self, *args, **kwargs):
|
|
137
|
+
return self.handle.read(*args, **kwargs)
|
|
138
|
+
|
|
139
|
+
def write(self, *args, **kwargs):
|
|
140
|
+
return self.handle.write(*args, **kwargs)
|
|
141
|
+
|
|
142
|
+
def close(self, *args, **kwargs):
|
|
143
|
+
return self.handle.close(*args, **kwargs)
|
|
144
|
+
|
|
145
|
+
def calculate_checksum(self, checksum_type: int) -> int:
|
|
146
|
+
from cfdp.checksum import calculate_checksum as _calc
|
|
147
|
+
|
|
148
|
+
self.handle.seek(0)
|
|
149
|
+
|
|
150
|
+
def _chunks():
|
|
151
|
+
while True:
|
|
152
|
+
chunk = self.handle.read(65536)
|
|
153
|
+
if not chunk:
|
|
154
|
+
break
|
|
155
|
+
yield chunk
|
|
156
|
+
|
|
157
|
+
return _calc(_chunks(), checksum_type)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class NativeTemporaryFileHandle(NativeFileHandle):
|
|
161
|
+
def __init__(self):
|
|
162
|
+
self.handle = tempfile.TemporaryFile()
|
cfdp/meta/__init__.py
ADDED
cfdp/meta/datafield.py
ADDED
cfdp/meta/filestore.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from cfdp import logger
|
|
2
|
+
from cfdp.constants import ActionCode, TypeFieldCode
|
|
3
|
+
|
|
4
|
+
from .datafield import TypeLengthValue
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FilestoreRequest(TypeLengthValue):
|
|
8
|
+
def __init__(self, action_code, first_filename, second_filename=None):
|
|
9
|
+
self.type = TypeFieldCode.FILESTORE_REQUEST
|
|
10
|
+
|
|
11
|
+
self.action_code = action_code
|
|
12
|
+
self.first_filename = first_filename
|
|
13
|
+
self.second_filename = second_filename
|
|
14
|
+
|
|
15
|
+
def encode(self):
|
|
16
|
+
data = bytes([(self.action_code << 4)])
|
|
17
|
+
|
|
18
|
+
value = self.first_filename.encode("utf-8")
|
|
19
|
+
length = len(value)
|
|
20
|
+
data += bytes([length])
|
|
21
|
+
data += value
|
|
22
|
+
|
|
23
|
+
if self.second_filename:
|
|
24
|
+
value = self.second_filename.encode("utf-8")
|
|
25
|
+
length = len(value)
|
|
26
|
+
data += bytes([length])
|
|
27
|
+
data += value
|
|
28
|
+
|
|
29
|
+
self.value = data
|
|
30
|
+
return super().encode()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def execute_filestore_requests(kernel, filestore_requests):
|
|
34
|
+
for filestore_request in filestore_requests:
|
|
35
|
+
if filestore_request.action_code == ActionCode.CREATE_FILE:
|
|
36
|
+
filename = filestore_request.first_filename.decode()
|
|
37
|
+
logger.info(f"Create file: <{filename}>")
|
|
38
|
+
try:
|
|
39
|
+
kernel.filestore.create_file(filename)
|
|
40
|
+
except Exception:
|
|
41
|
+
logger.error(f"Failed to create file <{filename}>")
|
|
42
|
+
|
|
43
|
+
elif filestore_request.action_code == ActionCode.DELETE_FILE:
|
|
44
|
+
filename = filestore_request.first_filename.decode()
|
|
45
|
+
logger.info(f"Delete file: <{filename}>")
|
|
46
|
+
try:
|
|
47
|
+
kernel.filestore.delete_file(filename)
|
|
48
|
+
except Exception:
|
|
49
|
+
logger.error(f"Failed to delete file: <{filename}>")
|
|
50
|
+
|
|
51
|
+
elif filestore_request.action_code == ActionCode.RENAME_FILE:
|
|
52
|
+
filename1 = filestore_request.first_filename.decode()
|
|
53
|
+
filename2 = filestore_request.second_filename.decode()
|
|
54
|
+
logger.info(f"Rename file: <{filename1}> to <{filename2}>")
|
|
55
|
+
try:
|
|
56
|
+
kernel.filestore.rename_file(filename1, filename2)
|
|
57
|
+
except Exception:
|
|
58
|
+
logger.error(f"Failed to rename file <{filename1}> to <{filename2}>")
|
|
59
|
+
|
|
60
|
+
elif filestore_request.action_code == ActionCode.APPEND_FILE:
|
|
61
|
+
filename1 = filestore_request.first_filename.decode()
|
|
62
|
+
filename2 = filestore_request.second_filename.decode()
|
|
63
|
+
logger.info(f"Append file <{filename1}> with <{filename2}>")
|
|
64
|
+
try:
|
|
65
|
+
kernel.filestore.append_file(filename1, filename2)
|
|
66
|
+
except Exception:
|
|
67
|
+
logger.error(f"Failed to append file <{filename1}> with <{filename2}>")
|
|
68
|
+
|
|
69
|
+
elif filestore_request.action_code == ActionCode.REPLACE_FILE:
|
|
70
|
+
filename1 = filestore_request.first_filename.decode()
|
|
71
|
+
filename2 = filestore_request.second_filename.decode()
|
|
72
|
+
logger.info(f"Replace content of file <{filename1}> by <{filename2}>")
|
|
73
|
+
try:
|
|
74
|
+
kernel.filestore.replace_file(filename1, filename2)
|
|
75
|
+
except Exception:
|
|
76
|
+
logger.error(
|
|
77
|
+
f"Failed to replace content of <{filename1}> by <{filename2}>"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
elif filestore_request.action_code == ActionCode.CREATE_DIRECTORY:
|
|
81
|
+
dirname = filestore_request.first_filename.decode()
|
|
82
|
+
logger.info(f"Create directory: <{dirname}>")
|
|
83
|
+
try:
|
|
84
|
+
kernel.filestore.create_directory(dirname)
|
|
85
|
+
except Exception:
|
|
86
|
+
logger.error(f"Failed to create directory: <{dirname}>")
|
|
87
|
+
|
|
88
|
+
elif filestore_request.action_code == ActionCode.REMOVE_DIRECTORY:
|
|
89
|
+
dirname = filestore_request.first_filename.decode()
|
|
90
|
+
logger.info(f"Remove directory: <{dirname}>")
|
|
91
|
+
try:
|
|
92
|
+
kernel.filestore.remove_directory(dirname)
|
|
93
|
+
except Exception:
|
|
94
|
+
logger.error(f"Failed to delete directory: <{dirname}>")
|
|
95
|
+
|
|
96
|
+
elif filestore_request.action_code == ActionCode.DENY_FILE:
|
|
97
|
+
filename = filestore_request.first_filename.decode()
|
|
98
|
+
logger.info(f"Deny file: <{filename}>")
|
|
99
|
+
try:
|
|
100
|
+
kernel.filestore.delete_file(filename)
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
elif filestore_request.action_code == ActionCode.DENY_DIRECTORY:
|
|
105
|
+
dirname = filestore_request.first_filename.decode()
|
|
106
|
+
logger.info(f"Deny directory: <{dirname}>")
|
|
107
|
+
try:
|
|
108
|
+
kernel.filestore.remove_directory(dirname)
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def extract_filestore_request(data):
|
|
114
|
+
action_code = data[0] >> 4
|
|
115
|
+
length_of_first_filename = data[1]
|
|
116
|
+
first_filename = data[2 : 2 + length_of_first_filename]
|
|
117
|
+
|
|
118
|
+
if action_code in [
|
|
119
|
+
ActionCode.RENAME_FILE,
|
|
120
|
+
ActionCode.APPEND_FILE,
|
|
121
|
+
ActionCode.REPLACE_FILE,
|
|
122
|
+
]:
|
|
123
|
+
second_filename = data[3 + length_of_first_filename :]
|
|
124
|
+
else:
|
|
125
|
+
second_filename = None
|
|
126
|
+
|
|
127
|
+
return FilestoreRequest(action_code, first_filename, second_filename)
|