Skip to content

Commit

Permalink
fix Specials restore (missing autodetection of compression etc., so o…
Browse files Browse the repository at this point in the history
…ld default=gzip does not work), use findBackupArchive and genericRestoreFromArchive to make it work identically to other restores
  • Loading branch information
hg42 committed Aug 25, 2024
1 parent eb921e4 commit 2199f3a
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ open class RestoreAppAction(context: Context, work: AppActionWork?, shell: Shell
}

@Throws(RestoreFailedException::class, CryptoSetupException::class)
private fun genericRestoreFromArchive(
fun genericRestoreFromArchive(
dataType: String,
archive: StorageFile,
targetPath: String,
Expand Down
127 changes: 65 additions & 62 deletions src/main/java/com/machiav3lli/backup/actions/RestoreSpecialAction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ import com.machiav3lli.backup.items.RootFile
import com.machiav3lli.backup.items.StorageFile
import com.machiav3lli.backup.tasks.AppActionWork
import com.machiav3lli.backup.utils.CryptoSetupException
import com.machiav3lli.backup.utils.suUnpackTo
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
import org.apache.commons.io.FileUtils
import timber.log.Timber
import java.io.File
Expand Down Expand Up @@ -62,70 +60,74 @@ class RestoreSpecialAction(context: Context, work: AppActionWork?, shell: ShellH
work?.setOperation("s")
val metaInfo = app.packageInfo as SpecialInfo
val tempPath = RootFile(context.cacheDir, backup.packageName)
val backupFilename = getBackupArchiveFilename(
BACKUP_DIR_DATA,
backup.isCompressed,
backup.compressionType,
backup.isEncrypted
)
val backupArchiveFile = backupDir.findFile(backupFilename)
?: throw RestoreFailedException("Backup archive at $backupFilename is missing")
try {
TarArchiveInputStream(
openArchiveFile(backupArchiveFile, backup.isCompressed, backup.compressionType, backup.isEncrypted, backup.iv)
).use { archiveStream ->
tempPath.mkdir()
// Extract the contents to a temporary directory
archiveStream.suUnpackTo(tempPath, isOldVersion(backup))

// check if all expected files are there
val filesInBackup = tempPath.listFiles()
val expectedFiles = metaInfo.specialFiles
.map { pathname: String? -> RootFile(pathname ?: "") }
.toTypedArray()
if (filesInBackup != null
&& (filesInBackup.size != expectedFiles.size
|| !areBasefilesSubsetOf(expectedFiles, filesInBackup))
) {
val errorMessage =
"$app: Backup is missing files. Found ${filesInBackup.map { it.absolutePath }}; needed: ${expectedFiles.map { it.absolutePath }}"
Timber.e(errorMessage)
throw RestoreFailedException(errorMessage, null)
}
val commands = mutableListOf<String?>()
for (restoreFile in expectedFiles) {
val (uid, gid, con) = try {
shell.suGetOwnerGroupContext(restoreFile.absolutePath)
} catch (e: Throwable) {
// fallback to permissions of parent directory
shell.suGetOwnerGroupContext(
restoreFile.parentFile?.absolutePath
?: restoreFile.toPath().parent.toString()
)
}
commands.add(
"$utilBoxQ mv -f ${
quote(
File(
tempPath,
restoreFile.name
)
)
} ${quote(restoreFile)}"
)
commands.add(
"$utilBoxQ chown $uid:$gid ${quote(restoreFile)}"
)
commands.add(
if (con == "?") //TODO hg42: when does it happen? maybe if selinux not supported on storage?
null // "" ; restorecon -RF -v ${quote(restoreFile)}" //TODO hg42 doesn't seem to work, probably because selinux unsupported in this case
else
"chcon -R -h -v '$con' ${quote(restoreFile)}"

val dataType = BACKUP_DIR_DATA
val backupArchive = findBackupArchive(dataType, backup, backupDir)
tempPath.mkdir()
val extractTo = tempPath.absolutePath

genericRestoreFromArchive(
dataType,
backupArchive.file,
extractTo,
backupArchive.isCompressed,
backupArchive.compressionType,
backupArchive.isEncrypted,
backup.iv,
RootFile(context.cacheDir),
isOldVersion(backup)
)

// check if all expected files are there
val filesInBackup = tempPath.listFiles()
val expectedFiles = metaInfo.specialFiles
.map { pathname: String? -> RootFile(pathname ?: "") }
.toTypedArray()
if (filesInBackup != null
&& (filesInBackup.size != expectedFiles.size
|| !areBasefilesSubsetOf(expectedFiles, filesInBackup))
) {
val errorMessage =
"$app: Backup is missing files. Found ${filesInBackup.map { it.absolutePath }}; needed: ${expectedFiles.map { it.absolutePath }}"
Timber.e(errorMessage)
throw RestoreFailedException(errorMessage, null)
}
val commands = mutableListOf<String?>()
for (restoreFile in expectedFiles) {
val (uid, gid, con) = try {
shell.suGetOwnerGroupContext(restoreFile.absolutePath)
} catch (e: Throwable) {
// fallback to permissions of parent directory
shell.suGetOwnerGroupContext(
restoreFile.parentFile?.absolutePath
?: restoreFile.toPath().parent.toString()
)
}
val command = commands.filterNotNull().joinToString(" ; ") // no dependency
runAsRoot(command)
commands.add(
"$utilBoxQ mv -f ${
quote(
File(
tempPath,
restoreFile.name
)
)
} ${quote(restoreFile)}"
)
commands.add(
"$utilBoxQ chown $uid:$gid ${quote(restoreFile)}"
)
commands.add(
if (con == "?") //TODO hg42: when does it happen? maybe if selinux not supported on storage?
null // "" ; restorecon -RF -v ${quote(restoreFile)}" //TODO hg42 doesn't seem to work, probably because selinux unsupported in this case
else
"chcon -R -h -v '$con' ${quote(restoreFile)}"
)
}

val command = commands.filterNotNull().joinToString(" ; ") // no dependency
runAsRoot(command)

if (app.packageName == "special.smsmms.json") {
for (filePath in metaInfo.specialFiles) {
RestoreSMSMMSJSONAction.restoreData(context, filePath)
Expand All @@ -136,6 +138,7 @@ class RestoreSpecialAction(context: Context, work: AppActionWork?, shell: ShellH
RestoreCallLogsJSONAction.restoreData(context, filePath)
}
}

} catch (e: RuntimeException) {
throw RestoreFailedException("${e.message}", e)
} catch (e: ShellCommandFailedException) {
Expand Down

0 comments on commit 2199f3a

Please sign in to comment.