From 6d8cb762ffca1bd151d98e0cec5b66b966965014 Mon Sep 17 00:00:00 2001 From: Eric Callahan Date: Fri, 15 Dec 2023 10:53:09 -0500 Subject: [PATCH] file_manager: relax registration requirements Use EAFP techniques to test for directory read permission during registration. If access fails continue with registration. This allows users to potentially fix an issue without restarting. Inotify failures always require a restart to resolve. Signed-off-by: Eric Callahan --- .../components/file_manager/file_manager.py | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/moonraker/components/file_manager/file_manager.py b/moonraker/components/file_manager/file_manager.py index 731547d03..e93dec798 100644 --- a/moonraker/components/file_manager/file_manager.py +++ b/moonraker/components/file_manager/file_manager.py @@ -268,15 +268,29 @@ def register_directory(self, f"Supplied path ({path}) for ({root}) is invalid. Make sure\n" "that the path exists and is not the file system root.") return False - permissions = os.R_OK + # Check Folder Permissions + missing_perms = [] + try: + # Test read + os.listdir(path) + except PermissionError: + missing_perms.append("READ") + except Exception: + logging.exception(f"Error testing read access for root {root}") if full_access: - permissions |= os.W_OK + if ( + os.access in os.supports_effective_ids and + not os.access(path, os.W_OK, effective_ids=True) + ): + missing_perms.append("WRITE") self.full_access_roots.add(root) - if not os.access(path, permissions): - self.server.add_warning( - f"Moonraker does not have permission to access path " - f"({path}) for ({root}).") - return False + if missing_perms: + mpstr = " | ".join(missing_perms) + self.server.add_log_rollover_item( + f"fm_reg_perms_{root}", + f"file_manager: Moonraker has detected the following missing " + f"permissions for root folder '{root}': {mpstr}" + ) if path != self.file_paths.get(root, ""): self.file_paths[root] = path self.server.register_static_file_handler(root, path) @@ -834,8 +848,14 @@ def _parse_upload_args(self, if unzip_ufp: filename = os.path.splitext(filename)[0] + ".gcode" dest_path = os.path.splitext(dest_path)[0] + ".gcode" - if os.path.isfile(dest_path) and not os.access(dest_path, os.W_OK): - raise self.server.error(f"File is read-only: {dest_path}") + if ( + os.path.isfile(dest_path) and + os.access in os.supports_effective_ids and + not os.access(dest_path, os.W_OK, effective_ids=True) + ): + logging.info( + f"Destination file exists and appears to be read-only: {dest_path}" + ) return { 'root': root, 'filename': filename, @@ -1811,17 +1831,41 @@ def add_root_watch(self, root: str, root_path: str) -> None: try: root_node = InotifyRootNode(self, root, root_path) except Exception: + logging.exception(f"Inotify: failed to create root node '{root}'") + self.server.add_warning( + f"file_manager: Failed to create inotify root node {root}. " + "See moonraker.log for details.", + log=False + ) return self.watched_roots[root] = root_node if self.initialized: - mevts = root_node.scan_node() + try: + mevts = root_node.scan_node() + except Exception: + logging.exception(f"Inotify: failed to scan root '{root}'") + self.server.add_warning( + f"file_manager: Failed to scan inotify root node '{root}'. " + "See moonraker.log for details.", + log=False + ) + return self.log_nodes() self.event_loop.register_callback( self._notify_root_updated, mevts, root, root_path) def initialize(self) -> None: for root, node in self.watched_roots.items(): - evts = node.scan_node() + try: + evts = node.scan_node() + except Exception: + logging.exception(f"Inotify: failed to scan root '{root}'") + self.server.add_warning( + f"file_manager: Failed to scan inotify root node '{root}'. " + "See moonraker.log for details.", + log=False + ) + continue if not evts: continue root_path = node.get_path()