From 099a11b01a7b53358d083967286080b7f158770b Mon Sep 17 00:00:00 2001 From: makitos666 Date: Fri, 27 Jun 2025 12:31:41 +0200 Subject: [PATCH 1/4] Update base.py I have added a better function to recover sqlite3 malformed or corrupted databases by using the ".recover" function. This command can recover more casuistics than the ".clone" --- src/mvt/ios/modules/base.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/mvt/ios/modules/base.py b/src/mvt/ios/modules/base.py index f96d99aa1..5fac871b7 100644 --- a/src/mvt/ios/modules/base.py +++ b/src/mvt/ios/modules/base.py @@ -42,7 +42,7 @@ def __init__( def _recover_sqlite_db_if_needed( self, file_path: str, forced: bool = False ) -> None: - """Tries to recover a malformed database by running a .clone command. + """Tries to recover a malformed database by running a .recover command. :param file_path: Path to the malformed database file. @@ -85,14 +85,34 @@ def _recover_sqlite_db_if_needed( bak_path = f"{file_path}.bak" shutil.move(file_path, bak_path) - ret = subprocess.call( - ["sqlite3", bak_path, f'.clone "{file_path}"'], + # First we've try to recover the database. Note that this feature needs the DBPAGER feature in sqlite3, that it is not installed in precompiled binaries from APT. + # Official webpage allows us to recover successfully https://sqlite.org/2025/sqlite-tools-linux-x64-3500100.zip + p1 = subprocess.Popen( + ["sqlite3", bak_path, ".recover"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - if ret != 0: + + out, err = p1.communicate() + + if p1.returncode != 0: raise DatabaseCorruptedError("failed to recover database") + + if b"sql error: no such table: sqlite_dbpage" in out: + raise DatabaseCorruptedError(".recover not supported in this sqlite3 installation") + + p2 = subprocess.Popen( + ["sqlite3", file_path], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + out2, err2 = p2.communicate(input=out) + if p2.returncode != 0: + raise DatabaseCorruptedError("failed to recover database") + self.log.info("Database at path %s recovered successfully!", file_path) def _open_sqlite_db(self, file_path: str) -> sqlite3.Connection: From 3bc6049a5c66e24b461dcf7974cc6d4105d95138 Mon Sep 17 00:00:00 2001 From: makitos666 Date: Fri, 27 Jun 2025 12:44:45 +0200 Subject: [PATCH 2/4] Update base.py Fixed formatting --- src/mvt/ios/modules/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mvt/ios/modules/base.py b/src/mvt/ios/modules/base.py index 5fac871b7..8a32855c5 100644 --- a/src/mvt/ios/modules/base.py +++ b/src/mvt/ios/modules/base.py @@ -99,7 +99,9 @@ def _recover_sqlite_db_if_needed( raise DatabaseCorruptedError("failed to recover database") if b"sql error: no such table: sqlite_dbpage" in out: - raise DatabaseCorruptedError(".recover not supported in this sqlite3 installation") + raise DatabaseCorruptedError( + ".recover not supported in this sqlite3 installation" + ) p2 = subprocess.Popen( ["sqlite3", file_path], From 166554745207a85d8f2c6a473c47a3575c57edda Mon Sep 17 00:00:00 2001 From: makitos666 Date: Fri, 27 Jun 2025 12:47:36 +0200 Subject: [PATCH 3/4] Update base.py Some more formatting --- src/mvt/ios/modules/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mvt/ios/modules/base.py b/src/mvt/ios/modules/base.py index 8a32855c5..b1bc295b5 100644 --- a/src/mvt/ios/modules/base.py +++ b/src/mvt/ios/modules/base.py @@ -42,7 +42,7 @@ def __init__( def _recover_sqlite_db_if_needed( self, file_path: str, forced: bool = False ) -> None: - """Tries to recover a malformed database by running a .recover command. + """Tries to recover a malformed database by running a .clone command. :param file_path: Path to the malformed database file. @@ -97,12 +97,12 @@ def _recover_sqlite_db_if_needed( if p1.returncode != 0: raise DatabaseCorruptedError("failed to recover database") - + if b"sql error: no such table: sqlite_dbpage" in out: raise DatabaseCorruptedError( ".recover not supported in this sqlite3 installation" ) - + p2 = subprocess.Popen( ["sqlite3", file_path], stdin=subprocess.PIPE, @@ -114,7 +114,7 @@ def _recover_sqlite_db_if_needed( if p2.returncode != 0: raise DatabaseCorruptedError("failed to recover database") - + self.log.info("Database at path %s recovered successfully!", file_path) def _open_sqlite_db(self, file_path: str) -> sqlite3.Connection: From 6dfca5884c22891f009f3968d4e4a5a156cbaa96 Mon Sep 17 00:00:00 2001 From: makitos666 Date: Fri, 27 Jun 2025 12:48:59 +0200 Subject: [PATCH 4/4] Update base.py Changed function comment to fit code --- src/mvt/ios/modules/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mvt/ios/modules/base.py b/src/mvt/ios/modules/base.py index b1bc295b5..7f98e12b5 100644 --- a/src/mvt/ios/modules/base.py +++ b/src/mvt/ios/modules/base.py @@ -42,7 +42,7 @@ def __init__( def _recover_sqlite_db_if_needed( self, file_path: str, forced: bool = False ) -> None: - """Tries to recover a malformed database by running a .clone command. + """Tries to recover a malformed database by running a .recover command. :param file_path: Path to the malformed database file.