From c00249fc17600315ca0025164c5d8bf469e49b79 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 09:38:28 -0400
Subject: [PATCH 01/14] Island: Change FileNotFoundError import in
 LocalStorageFileRepository

---
 .../cc/repository/local_storage_file_repository.py           | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/monkey/monkey_island/cc/repository/local_storage_file_repository.py b/monkey/monkey_island/cc/repository/local_storage_file_repository.py
index 4c7662b0c..cf0444abe 100644
--- a/monkey/monkey_island/cc/repository/local_storage_file_repository.py
+++ b/monkey/monkey_island/cc/repository/local_storage_file_repository.py
@@ -4,10 +4,11 @@ from pathlib import Path
 from typing import BinaryIO
 
 from common.utils.file_utils import get_all_regular_files_in_directory
+from monkey_island.cc import repository
 from monkey_island.cc.repository import RemovalError, RetrievalError, StorageError
 from monkey_island.cc.server_utils.file_utils import create_secure_directory
 
-from . import IFileRepository, i_file_repository
+from . import IFileRepository
 
 logger = logging.getLogger(__name__)
 
@@ -49,7 +50,7 @@ class LocalStorageFileRepository(IFileRepository):
             return open(safe_file_path, "rb")
         except FileNotFoundError as err:
             # Wrap Python's FileNotFound error, which is-an OSError, in repository.FileNotFoundError
-            raise i_file_repository.FileNotFoundError(
+            raise repository.FileNotFoundError(
                 f'The requested file "{unsafe_file_name}" does not exist: {err}'
             )
         except Exception as err:

From bc0c4a1c8e697b1e27dee331fd72a4302f87b633 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 09:49:12 -0400
Subject: [PATCH 02/14] Island: Add FileRepositoryLoggingDecorator

---
 .../monkey_island/cc/repository/__init__.py   |  1 +
 .../file_repository_logging_decorator.py      | 31 +++++++++++++++++++
 2 files changed, 32 insertions(+)
 create mode 100644 monkey/monkey_island/cc/repository/file_repository_logging_decorator.py

diff --git a/monkey/monkey_island/cc/repository/__init__.py b/monkey/monkey_island/cc/repository/__init__.py
index 153d57e27..3b18883c2 100644
--- a/monkey/monkey_island/cc/repository/__init__.py
+++ b/monkey/monkey_island/cc/repository/__init__.py
@@ -1,6 +1,7 @@
 from .errors import RemovalError, RetrievalError, StorageError
 from .i_file_repository import FileNotFoundError, IFileRepository
 from .local_storage_file_repository import LocalStorageFileRepository
+from .file_repository_logging_decorator import FileRepositoryLoggingDecorator
 from .i_agent_binary_repository import IAgentBinaryRepository
 from .agent_binary_repository import AgentBinaryRepository
 from .i_agent_configuration_repository import IAgentConfigurationRepository
diff --git a/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py b/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
new file mode 100644
index 000000000..47ba16ca1
--- /dev/null
+++ b/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
@@ -0,0 +1,31 @@
+import logging
+from typing import BinaryIO
+
+from . import IFileRepository
+
+logger = logging.getLogger(__name__)
+
+
+class FileRepositoryLoggingDecorator(IFileRepository):
+    def __init__(self, file_repository: IFileRepository):
+        self._file_repository = file_repository
+
+    def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
+        logger.info(f"Saving file {unsafe_file_name}")
+
+        return self._file_repository.save_file(unsafe_file_name, file_contents)
+
+    def open_file(self, unsafe_file_name: str) -> BinaryIO:
+        logger.info(f"Opening file {unsafe_file_name}")
+
+        return self._file_repository.open_file(unsafe_file_name)
+
+    def delete_file(self, unsafe_file_name: str):
+        logger.info(f"Deleting file {unsafe_file_name}")
+
+        return self._file_repository.delete_file(unsafe_file_name)
+
+    def delete_all_files(self):
+        logger.info("Deleting all files in the repository")
+
+        return self._file_repository.delete_all_files()

From e3b5e0c01aebe68493253a9988d8b0b97b5ee07f Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 09:53:18 -0400
Subject: [PATCH 03/14] Island: Add FileRepositoryLockingDecorator

---
 .../monkey_island/cc/repository/__init__.py   |  1 +
 .../file_repository_locking_decorator.py      | 26 +++++++++++++++++++
 2 files changed, 27 insertions(+)
 create mode 100644 monkey/monkey_island/cc/repository/file_repository_locking_decorator.py

diff --git a/monkey/monkey_island/cc/repository/__init__.py b/monkey/monkey_island/cc/repository/__init__.py
index 3b18883c2..6f683cc1a 100644
--- a/monkey/monkey_island/cc/repository/__init__.py
+++ b/monkey/monkey_island/cc/repository/__init__.py
@@ -1,6 +1,7 @@
 from .errors import RemovalError, RetrievalError, StorageError
 from .i_file_repository import FileNotFoundError, IFileRepository
 from .local_storage_file_repository import LocalStorageFileRepository
+from .file_repository_locking_decorator import FileRepositoryLockingDecorator
 from .file_repository_logging_decorator import FileRepositoryLoggingDecorator
 from .i_agent_binary_repository import IAgentBinaryRepository
 from .agent_binary_repository import AgentBinaryRepository
diff --git a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
new file mode 100644
index 000000000..7539d4fc1
--- /dev/null
+++ b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
@@ -0,0 +1,26 @@
+from multiprocessing import Lock
+from typing import BinaryIO
+
+from . import IFileRepository
+
+
+class FileRepositoryLockingDecorator(IFileRepository):
+    def __init__(self, file_repository: IFileRepository):
+        self._file_repository = file_repository
+        self._lock = Lock()
+
+    def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
+        with self._lock:
+            return self._file_repository.save_file(unsafe_file_name, file_contents)
+
+    def open_file(self, unsafe_file_name: str) -> BinaryIO:
+        with self._lock:
+            return self._file_repository.open_file(unsafe_file_name)
+
+    def delete_file(self, unsafe_file_name: str):
+        with self._lock:
+            return self._file_repository.delete_file(unsafe_file_name)
+
+    def delete_all_files(self):
+        with self._lock:
+            return self._file_repository.delete_all_files()

From d4883c6e4420c86abc43a8109498773e8e303c48 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 10:32:12 -0400
Subject: [PATCH 04/14] Island: Specify "read-only" in
 IFileRepository.open_file() docstring

---
 monkey/monkey_island/cc/repository/i_file_repository.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/repository/i_file_repository.py b/monkey/monkey_island/cc/repository/i_file_repository.py
index baa52fdda..bedc9cfdb 100644
--- a/monkey/monkey_island/cc/repository/i_file_repository.py
+++ b/monkey/monkey_island/cc/repository/i_file_repository.py
@@ -27,7 +27,7 @@ class IFileRepository(metaclass=abc.ABCMeta):
     @abc.abstractmethod
     def open_file(self, unsafe_file_name: str) -> BinaryIO:
         """
-        Open a file and return a file-like object
+        Open a file and return a read-only file-like object
 
         :param unsafe_file_name: An unsanitized file name that identifies the file to be opened
         :return: A file-like object providing access to the file's contents

From 9affe10f67c4b15c48703d2dfd83e2f05763a80e Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 10:33:06 -0400
Subject: [PATCH 05/14] Island: Add FileRepositoryCachingDecorator

---
 .../monkey_island/cc/repository/__init__.py   |  1 +
 .../file_repository_caching_decorator.py      | 37 +++++++++++++
 .../test_file_repository_caching_decorator.py | 55 +++++++++++++++++++
 3 files changed, 93 insertions(+)
 create mode 100644 monkey/monkey_island/cc/repository/file_repository_caching_decorator.py
 create mode 100644 monkey/tests/unit_tests/monkey_island/cc/repository/test_file_repository_caching_decorator.py

diff --git a/monkey/monkey_island/cc/repository/__init__.py b/monkey/monkey_island/cc/repository/__init__.py
index 6f683cc1a..d90f6aac6 100644
--- a/monkey/monkey_island/cc/repository/__init__.py
+++ b/monkey/monkey_island/cc/repository/__init__.py
@@ -1,6 +1,7 @@
 from .errors import RemovalError, RetrievalError, StorageError
 from .i_file_repository import FileNotFoundError, IFileRepository
 from .local_storage_file_repository import LocalStorageFileRepository
+from .file_repository_caching_decorator import FileRepositoryCachingDecorator
 from .file_repository_locking_decorator import FileRepositoryLockingDecorator
 from .file_repository_logging_decorator import FileRepositoryLoggingDecorator
 from .i_agent_binary_repository import IAgentBinaryRepository
diff --git a/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py b/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py
new file mode 100644
index 000000000..76739716f
--- /dev/null
+++ b/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py
@@ -0,0 +1,37 @@
+import io
+import shutil
+from functools import lru_cache
+from typing import BinaryIO
+
+from . import IFileRepository
+
+
+class FileRepositoryCachingDecorator(IFileRepository):
+    def __init__(self, file_repository: IFileRepository):
+        self._file_repository = file_repository
+
+    def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
+        self._open_file.cache_clear()
+        return self._file_repository.save_file(unsafe_file_name, file_contents)
+
+    def open_file(self, unsafe_file_name: str) -> BinaryIO:
+        original_file = self._open_file(unsafe_file_name)
+        file_copy = io.BytesIO()
+
+        shutil.copyfileobj(original_file, file_copy)
+        original_file.seek(0)
+        file_copy.seek(0)
+
+        return file_copy
+
+    @lru_cache(maxsize=16)
+    def _open_file(self, unsafe_file_name: str) -> BinaryIO:
+        return self._file_repository.open_file(unsafe_file_name)
+
+    def delete_file(self, unsafe_file_name: str):
+        self._open_file.cache_clear()
+        return self._file_repository.delete_file(unsafe_file_name)
+
+    def delete_all_files(self):
+        self._open_file.cache_clear()
+        return self._file_repository.delete_all_files()
diff --git a/monkey/tests/unit_tests/monkey_island/cc/repository/test_file_repository_caching_decorator.py b/monkey/tests/unit_tests/monkey_island/cc/repository/test_file_repository_caching_decorator.py
new file mode 100644
index 000000000..14fdf8c27
--- /dev/null
+++ b/monkey/tests/unit_tests/monkey_island/cc/repository/test_file_repository_caching_decorator.py
@@ -0,0 +1,55 @@
+import io
+
+import pytest
+from tests.monkey_island import SingleFileRepository
+
+from monkey_island.cc import repository
+from monkey_island.cc.repository import FileRepositoryCachingDecorator
+
+
+@pytest.fixture
+def file_repository():
+    return FileRepositoryCachingDecorator(SingleFileRepository())
+
+
+def test_open_cache_file(file_repository):
+    file_name = "test.txt"
+    file_contents = b"Hello World!"
+
+    file_repository.save_file(file_name, io.BytesIO(file_contents))
+    assert file_repository.open_file(file_name).read() == file_contents
+    assert file_repository.open_file(file_name).read() == file_contents
+
+
+def test_overwrite_file(file_repository):
+    file_name = "test.txt"
+    file_contents_1 = b"Hello World!"
+    file_contents_2 = b"Goodbye World!"
+
+    file_repository.save_file(file_name, io.BytesIO(file_contents_1))
+    assert file_repository.open_file(file_name).read() == file_contents_1
+
+    file_repository.save_file(file_name, io.BytesIO(file_contents_2))
+    assert file_repository.open_file(file_name).read() == file_contents_2
+
+
+def test_delete_file(file_repository):
+    file_name = "test.txt"
+    file_contents = b"Hello World!"
+
+    file_repository.save_file(file_name, io.BytesIO(file_contents))
+    file_repository.delete_file(file_name)
+
+    with pytest.raises(repository.FileNotFoundError):
+        file_repository.open_file(file_name)
+
+
+def test_delete_all_files(file_repository):
+    file_name = "test.txt"
+    file_contents = b"Hello World!"
+
+    file_repository.save_file(file_name, io.BytesIO(file_contents))
+    file_repository.delete_all_files()
+
+    with pytest.raises(repository.FileNotFoundError):
+        file_repository.open_file(file_name)

From a329177b499fc32980bf4c13a9ca8bcf5550ec4e Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 19:59:50 -0400
Subject: [PATCH 06/14] Island: Decorate LocalStorageFileRepository

---
 monkey/monkey_island/cc/services/initialize.py | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py
index 28a88558c..dad5b92c7 100644
--- a/monkey/monkey_island/cc/services/initialize.py
+++ b/monkey/monkey_island/cc/services/initialize.py
@@ -12,6 +12,9 @@ from common.utils.file_utils import get_binary_io_sha256_hash
 from monkey_island.cc.repository import (
     AgentBinaryRepository,
     FileAgentConfigurationRepository,
+    FileRepositoryCachingDecorator,
+    FileRepositoryLockingDecorator,
+    FileRepositoryLoggingDecorator,
     FileSimulationRepository,
     IAgentBinaryRepository,
     IAgentConfigurationRepository,
@@ -62,7 +65,8 @@ def _register_conventions(container: DIContainer, data_dir: Path):
 
 def _register_repositories(container: DIContainer, data_dir: Path):
     container.register_instance(
-        IFileRepository, LocalStorageFileRepository(data_dir / "runtime_data")
+        IFileRepository,
+        _decorate_file_repository(LocalStorageFileRepository(data_dir / "runtime_data")),
     )
     container.register_instance(IAgentBinaryRepository, _build_agent_binary_repository())
     container.register_instance(
@@ -71,6 +75,12 @@ def _register_repositories(container: DIContainer, data_dir: Path):
     container.register_instance(ISimulationRepository, container.resolve(FileSimulationRepository))
 
 
+def _decorate_file_repository(file_repository: IFileRepository) -> IFileRepository:
+    return FileRepositoryLockingDecorator(
+        FileRepositoryCachingDecorator(FileRepositoryLoggingDecorator(file_repository))
+    )
+
+
 def _build_agent_binary_repository():
     file_repository = LocalStorageFileRepository(AGENT_BINARIES_PATH)
     agent_binary_repository = AgentBinaryRepository(file_repository)

From 6cc3689ab4ec4342b37297f39f7a3b6498aef771 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 20:02:53 -0400
Subject: [PATCH 07/14] Island: Remove unnecesary logging from
 LocalStorageFileRepository

Most logging is now handled by FileRepositoryLoggingDecorator, which
makes the logging reusable across different implementations of
`IFileRepository`.
---
 .../cc/repository/local_storage_file_repository.py           | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/monkey/monkey_island/cc/repository/local_storage_file_repository.py b/monkey/monkey_island/cc/repository/local_storage_file_repository.py
index cf0444abe..58fd20bdb 100644
--- a/monkey/monkey_island/cc/repository/local_storage_file_repository.py
+++ b/monkey/monkey_island/cc/repository/local_storage_file_repository.py
@@ -36,7 +36,6 @@ class LocalStorageFileRepository(IFileRepository):
         try:
             safe_file_path = self._get_safe_file_path(unsafe_file_name)
 
-            logger.debug(f"Saving file contents to {safe_file_path}")
             with open(safe_file_path, "wb") as dest:
                 shutil.copyfileobj(file_contents, dest)
         except Exception as err:
@@ -45,8 +44,6 @@ class LocalStorageFileRepository(IFileRepository):
     def open_file(self, unsafe_file_name: str) -> BinaryIO:
         try:
             safe_file_path = self._get_safe_file_path(unsafe_file_name)
-
-            logger.debug(f"Opening {safe_file_path}")
             return open(safe_file_path, "rb")
         except FileNotFoundError as err:
             # Wrap Python's FileNotFound error, which is-an OSError, in repository.FileNotFoundError
@@ -61,8 +58,6 @@ class LocalStorageFileRepository(IFileRepository):
     def delete_file(self, unsafe_file_name: str):
         try:
             safe_file_path = self._get_safe_file_path(unsafe_file_name)
-
-            logger.debug(f"Deleting {safe_file_path}")
             safe_file_path.unlink()
         except FileNotFoundError:
             # This method is idempotent.

From 3c85a897027152774b55469dde3a5c6e15ae66b8 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sat, 2 Jul 2022 20:04:10 -0400
Subject: [PATCH 08/14] Island: Change log level in
 FileRepositoryLoggingDecorator to debug

Most log statements in repositories should probably be at debug.
Services and resources could potentially log at info.
---
 .../repository/file_repository_logging_decorator.py  | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py b/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
index 47ba16ca1..5365faf4b 100644
--- a/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
+++ b/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
@@ -11,21 +11,17 @@ class FileRepositoryLoggingDecorator(IFileRepository):
         self._file_repository = file_repository
 
     def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
-        logger.info(f"Saving file {unsafe_file_name}")
-
+        logger.debug(f"Saving file {unsafe_file_name}")
         return self._file_repository.save_file(unsafe_file_name, file_contents)
 
     def open_file(self, unsafe_file_name: str) -> BinaryIO:
-        logger.info(f"Opening file {unsafe_file_name}")
-
+        logger.debug(f"Opening file {unsafe_file_name}")
         return self._file_repository.open_file(unsafe_file_name)
 
     def delete_file(self, unsafe_file_name: str):
-        logger.info(f"Deleting file {unsafe_file_name}")
-
+        logger.debug(f"Deleting file {unsafe_file_name}")
         return self._file_repository.delete_file(unsafe_file_name)
 
     def delete_all_files(self):
-        logger.info("Deleting all files in the repository")
-
+        logger.debug("Deleting all files in the repository")
         return self._file_repository.delete_all_files()

From 9cb79c119ce55a043a5faacfbcfe1b1f20480420 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sun, 3 Jul 2022 09:35:19 -0400
Subject: [PATCH 09/14] Island: Decorate the FileRepository for
 AgentBinaryRepository

---
 monkey/monkey_island/cc/services/initialize.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py
index dad5b92c7..7616a191e 100644
--- a/monkey/monkey_island/cc/services/initialize.py
+++ b/monkey/monkey_island/cc/services/initialize.py
@@ -82,7 +82,7 @@ def _decorate_file_repository(file_repository: IFileRepository) -> IFileReposito
 
 
 def _build_agent_binary_repository():
-    file_repository = LocalStorageFileRepository(AGENT_BINARIES_PATH)
+    file_repository = _decorate_file_repository(LocalStorageFileRepository(AGENT_BINARIES_PATH))
     agent_binary_repository = AgentBinaryRepository(file_repository)
 
     _log_agent_binary_hashes(agent_binary_repository)

From 18f995919d04a2f599f2139fbd8adbe763be2d54 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Sun, 3 Jul 2022 09:38:31 -0400
Subject: [PATCH 10/14] Island: Add TODO about read/write lock

---
 .../cc/repository/file_repository_locking_decorator.py        | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
index 7539d4fc1..18a559f78 100644
--- a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
+++ b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
@@ -3,6 +3,10 @@ from typing import BinaryIO
 
 from . import IFileRepository
 
+# TODO: Use a read-write lock. See:
+#           - https://pypi.org/project/readerwriterlock/
+#           - https://www.oreilly.com/library/view/python-cookbook/0596001673/ch06s04.html
+
 
 class FileRepositoryLockingDecorator(IFileRepository):
     def __init__(self, file_repository: IFileRepository):

From df8132c86c348db2696e640dbb324af54d875f48 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Mon, 4 Jul 2022 07:14:11 -0400
Subject: [PATCH 11/14] Island: Change the order of FileRepository decorators

The caching decorator was suppressing debug log messages. Reversing
their order resolves the issue.
---
 monkey/monkey_island/cc/services/initialize.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py
index 7616a191e..b5a16ec9f 100644
--- a/monkey/monkey_island/cc/services/initialize.py
+++ b/monkey/monkey_island/cc/services/initialize.py
@@ -77,7 +77,7 @@ def _register_repositories(container: DIContainer, data_dir: Path):
 
 def _decorate_file_repository(file_repository: IFileRepository) -> IFileRepository:
     return FileRepositoryLockingDecorator(
-        FileRepositoryCachingDecorator(FileRepositoryLoggingDecorator(file_repository))
+        FileRepositoryLoggingDecorator(FileRepositoryCachingDecorator(file_repository))
     )
 
 

From 48cc6d6dd2defc5f39d22e825b925a407a82a280 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Tue, 5 Jul 2022 10:42:06 -0400
Subject: [PATCH 12/14] Island: Add readerwriterlock as a dependency

---
 monkey/monkey_island/Pipfile      |  1 +
 monkey/monkey_island/Pipfile.lock | 14 +++++++++++---
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/monkey/monkey_island/Pipfile b/monkey/monkey_island/Pipfile
index fbc8b16ae..052a46039 100644
--- a/monkey/monkey_island/Pipfile
+++ b/monkey/monkey_island/Pipfile
@@ -30,6 +30,7 @@ pywin32 = {version = "*", sys_platform = "== 'win32'"} # Lock file is not create
 pefile = {version = "*", sys_platform = "== 'win32'"} # Pyinstaller requirement on windows
 marshmallow = "*"
 marshmallow-enum = "*"
+readerwriterlock = "*"
 
 [dev-packages]
 virtualenv = ">=20.0.26"
diff --git a/monkey/monkey_island/Pipfile.lock b/monkey/monkey_island/Pipfile.lock
index c3e59c1d5..67647710a 100644
--- a/monkey/monkey_island/Pipfile.lock
+++ b/monkey/monkey_island/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "04efa1f593acdfcdc48b7089108921a46421acbacec80d8a664ec674b221dd4b"
+            "sha256": "91b8cfcf1408b3709300f47d420c550fe355df76ad396e455049fef1cceca3ad"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -338,7 +338,7 @@
                 "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
                 "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
             ],
-            "markers": "python_full_version >= '3.5.0'",
+            "markers": "python_version >= '3.5'",
             "version": "==3.3"
         },
         "importlib-metadata": {
@@ -792,6 +792,14 @@
             "markers": "sys_platform == 'win32'",
             "version": "==0.2.0"
         },
+        "readerwriterlock": {
+            "hashes": [
+                "sha256:8c4b704e60d15991462081a27ef46762fea49b478aa4426644f2146754759ca7",
+                "sha256:b7c4cc003435d7a8ff15b312b0a62a88d9800ba6164af88991f87f8b748f9bea"
+            ],
+            "index": "pypi",
+            "version": "==1.0.9"
+        },
         "requests": {
             "hashes": [
                 "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f",
@@ -1117,7 +1125,7 @@
                 "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
                 "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
             ],
-            "markers": "python_full_version >= '3.5.0'",
+            "markers": "python_version >= '3.5'",
             "version": "==3.3"
         },
         "imagesize": {

From a8b54f69f9771d8ba111a34e9cb57b500397202b Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Tue, 5 Jul 2022 11:17:03 -0400
Subject: [PATCH 13/14] Island: Use read/write lock in
 FileRepositoryLockingDecorator

---
 .../file_repository_locking_decorator.py        | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
index 18a559f78..0e7ee8a50 100644
--- a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
+++ b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
@@ -1,30 +1,27 @@
-from multiprocessing import Lock
 from typing import BinaryIO
 
-from . import IFileRepository
+from readerwriterlock import rwlock
 
-# TODO: Use a read-write lock. See:
-#           - https://pypi.org/project/readerwriterlock/
-#           - https://www.oreilly.com/library/view/python-cookbook/0596001673/ch06s04.html
+from . import IFileRepository
 
 
 class FileRepositoryLockingDecorator(IFileRepository):
     def __init__(self, file_repository: IFileRepository):
         self._file_repository = file_repository
-        self._lock = Lock()
+        self._rwlock = rwlock.RWLockFair()
 
     def save_file(self, unsafe_file_name: str, file_contents: BinaryIO):
-        with self._lock:
+        with self._rwlock.gen_wlock():
             return self._file_repository.save_file(unsafe_file_name, file_contents)
 
     def open_file(self, unsafe_file_name: str) -> BinaryIO:
-        with self._lock:
+        with self._rwlock.gen_rlock():
             return self._file_repository.open_file(unsafe_file_name)
 
     def delete_file(self, unsafe_file_name: str):
-        with self._lock:
+        with self._rwlock.gen_wlock():
             return self._file_repository.delete_file(unsafe_file_name)
 
     def delete_all_files(self):
-        with self._lock:
+        with self._rwlock.gen_wlock():
             return self._file_repository.delete_all_files()

From 545f35710f9c1fc716df01eda83a4fffabac0d70 Mon Sep 17 00:00:00 2001
From: Mike Salvatore <mike.s.salvatore@gmail.com>
Date: Tue, 5 Jul 2022 11:24:34 -0400
Subject: [PATCH 14/14] Island: Add class docstrings for IFileRepository
 decorators

---
 .../cc/repository/file_repository_caching_decorator.py        | 4 ++++
 .../cc/repository/file_repository_locking_decorator.py        | 4 ++++
 .../cc/repository/file_repository_logging_decorator.py        | 4 ++++
 3 files changed, 12 insertions(+)

diff --git a/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py b/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py
index 76739716f..0e026d035 100644
--- a/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py
+++ b/monkey/monkey_island/cc/repository/file_repository_caching_decorator.py
@@ -7,6 +7,10 @@ from . import IFileRepository
 
 
 class FileRepositoryCachingDecorator(IFileRepository):
+    """
+    An IFileRepository decorator that provides caching for other IFileRepositories.
+    """
+
     def __init__(self, file_repository: IFileRepository):
         self._file_repository = file_repository
 
diff --git a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
index 0e7ee8a50..1bd76b4dd 100644
--- a/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
+++ b/monkey/monkey_island/cc/repository/file_repository_locking_decorator.py
@@ -6,6 +6,10 @@ from . import IFileRepository
 
 
 class FileRepositoryLockingDecorator(IFileRepository):
+    """
+    An IFileRepository decorator that makes other IFileRepositories thread-safe.
+    """
+
     def __init__(self, file_repository: IFileRepository):
         self._file_repository = file_repository
         self._rwlock = rwlock.RWLockFair()
diff --git a/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py b/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
index 5365faf4b..2bf8bfe6e 100644
--- a/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
+++ b/monkey/monkey_island/cc/repository/file_repository_logging_decorator.py
@@ -7,6 +7,10 @@ logger = logging.getLogger(__name__)
 
 
 class FileRepositoryLoggingDecorator(IFileRepository):
+    """
+    An IFileRepository decorator that provides debug logging for other IFileRepositories.
+    """
+
     def __init__(self, file_repository: IFileRepository):
         self._file_repository = file_repository