From f5ac9376b1890df8b631f487dcb1f69bcaccf502 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Mon, 6 Mar 2023 09:17:28 -0800 Subject: [PATCH] Allow copy and move file operations to opt-in to overwrite. (#1120) - Allow create, copy and move file operations to opt-in to overwrite - Use this when extracting packaged properties to avoid it getting stuck in the images dir - Better error handling in common file operation methods --- python/common/src/piscsi/file_cmds.py | 68 ++++++++++++++++++------ python/common/src/piscsi/return_codes.py | 2 + python/web/src/return_code_mapper.py | 4 ++ 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/python/common/src/piscsi/file_cmds.py b/python/common/src/piscsi/file_cmds.py index 5987f1c7..a64298ae 100644 --- a/python/common/src/piscsi/file_cmds.py +++ b/python/common/src/piscsi/file_cmds.py @@ -166,7 +166,15 @@ class FileCmds: parameters = {"file_path": file_path} if file_path.exists(): - file_path.unlink() + try: + file_path.unlink() + except OSError as error: + logging.error(error) + return { + "status": False, + "return_code": ReturnCodes.DELETEFILE_UNABLE_TO_DELETE, + "parameters": parameters, + } return { "status": True, "return_code": ReturnCodes.DELETEFILE_SUCCESS, @@ -179,18 +187,28 @@ class FileCmds: } # noinspection PyMethodMayBeStatic - def rename_file(self, file_path, target_path): + def rename_file(self, file_path, target_path, overwrite_target=False): """ Takes: - (Path) file_path for the file to rename - (Path) target_path for the name to rename + - optional (bool) overwrite_target Returns (dict) with (bool) status, (str) msg, (dict) parameters """ parameters = {"target_path": target_path} if not target_path.parent.exists(): target_path.parent.mkdir(parents=True) - if target_path.parent.exists() and not target_path.exists(): - file_path.rename(target_path) + + if overwrite_target or not target_path.exists(): + try: + file_path.rename(target_path) + except OSError as error: + logging.error(error) + return { + "status": False, + "return_code": ReturnCodes.RENAMEFILE_UNABLE_TO_MOVE, + "parameters": parameters, + } return { "status": True, "return_code": ReturnCodes.RENAMEFILE_SUCCESS, @@ -198,23 +216,33 @@ class FileCmds: } return { "status": False, - "return_code": ReturnCodes.RENAMEFILE_UNABLE_TO_MOVE, + "return_code": ReturnCodes.WRITEFILE_COULD_NOT_OVERWRITE, "parameters": parameters, } # noinspection PyMethodMayBeStatic - def copy_file(self, file_path, target_path): + def copy_file(self, file_path, target_path, overwrite_target=False): """ Takes: - (Path) file_path for the file to copy from - (Path) target_path for the name to copy to + - optional (bool) overwrite_target Returns (dict) with (bool) status, (str) msg, (dict) parameters """ parameters = {"target_path": target_path} if not target_path.parent.exists(): target_path.parent.mkdir(parents=True) - if target_path.parent.exists() and not target_path.exists(): - copyfile(str(file_path), str(target_path)) + + if overwrite_target or not target_path.exists(): + try: + copyfile(str(file_path), str(target_path)) + except OSError as error: + logging.error(error) + return { + "status": False, + "return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE, + "parameters": parameters, + } return { "status": True, "return_code": ReturnCodes.WRITEFILE_SUCCESS, @@ -222,32 +250,41 @@ class FileCmds: } return { "status": False, - "return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE, + "return_code": ReturnCodes.WRITEFILE_COULD_NOT_OVERWRITE, "parameters": parameters, } - def create_empty_image(self, target_path, size): + def create_empty_image(self, target_path, size, overwrite_target=False): """ - Takes (Path) target_path and (int) size in bytes - Creates a new empty binary file to use as image + Creates a new empty binary file to use as image. + Takes: + - (Path) target_path + - (int) size in bytes + - optional (bool) overwrite_target Returns (dict) with (bool) status, (str) msg, (dict) parameters """ parameters = {"target_path": target_path} if not target_path.parent.exists(): target_path.parent.mkdir(parents=True) - if target_path.parent.exists() and not target_path.exists(): + + if overwrite_target or not target_path.exists(): try: with open(f"{target_path}", "wb") as out: out.seek(size - 1) out.write(b"\0") except OSError as error: - return {"status": False, "msg": str(error)} + logging.error(error) + return { + "status": False, + "return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE, + "parameters": parameters, + } return {"status": True, "msg": ""} return { "status": False, - "return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE, + "return_code": ReturnCodes.WRITEFILE_COULD_NOT_OVERWRITE, "parameters": parameters, } @@ -282,6 +319,7 @@ class FileCmds: if self.rename_file( Path(file["absolute_path"]), prop_path, + overwrite_target=True, ): properties_files_moved.append( { diff --git a/python/common/src/piscsi/return_codes.py b/python/common/src/piscsi/return_codes.py index d1ea9764..cfd8265c 100644 --- a/python/common/src/piscsi/return_codes.py +++ b/python/common/src/piscsi/return_codes.py @@ -9,12 +9,14 @@ class ReturnCodes: DELETEFILE_SUCCESS = 0 DELETEFILE_FILE_NOT_FOUND = 1 + DELETEFILE_UNABLE_TO_DELETE = 2 RENAMEFILE_SUCCESS = 10 RENAMEFILE_UNABLE_TO_MOVE = 11 DOWNLOADFILETOISO_SUCCESS = 20 DOWNLOADTODIR_SUCCESS = 30 WRITEFILE_SUCCESS = 40 WRITEFILE_COULD_NOT_WRITE = 41 + WRITEFILE_COULD_NOT_OVERWRITE = 42 READCONFIG_SUCCESS = 50 READCONFIG_COULD_NOT_READ = 51 READCONFIG_INVALID_CONFIG_FILE_FORMAT = 52 diff --git a/python/web/src/return_code_mapper.py b/python/web/src/return_code_mapper.py index 6ff3ba15..f6c6e273 100644 --- a/python/web/src/return_code_mapper.py +++ b/python/web/src/return_code_mapper.py @@ -14,6 +14,8 @@ class ReturnCodeMapper: _("File deleted: %(file_path)s"), ReturnCodes.DELETEFILE_FILE_NOT_FOUND: _("File to delete not found: %(file_path)s"), + ReturnCodes.DELETEFILE_UNABLE_TO_DELETE: + _("Could not delete file: %(file_path)s"), ReturnCodes.RENAMEFILE_SUCCESS: _("File moved to: %(target_path)s"), ReturnCodes.RENAMEFILE_UNABLE_TO_MOVE: @@ -26,6 +28,8 @@ class ReturnCodeMapper: _("File created: %(target_path)s"), ReturnCodes.WRITEFILE_COULD_NOT_WRITE: _("Could not create file: %(target_path)s"), + ReturnCodes.WRITEFILE_COULD_NOT_OVERWRITE: + _("A file with name %(target_path)s already exists"), ReturnCodes.READCONFIG_SUCCESS: _("Loaded configurations from: %(file_name)s"), ReturnCodes.READCONFIG_COULD_NOT_READ: