@@ -1909,9 +1909,44 @@ private void bindBackupButtonLogic(MainActivity mainAct, File backupsDir, File i
19091909 String tarBin = staticTar .exists () ? staticTar .getAbsolutePath () : "tar" ;
19101910 String gzipBin = staticGzip .exists () ? staticGzip .getAbsolutePath () : "gzip" ;
19111911
1912+ // Stamp an identity manifest into the backup so a re-import is
1913+ // recognized (kind/arch) AND explicitly declares it carries NO
1914+ // integrity checksum (origin=device-backup) — we do NOT turn the
1915+ // phone into a builder. It is staged in a temp tree and packed
1916+ // FIRST (a second `-C`) so RootfsArchiveValidator reads it from
1917+ // the first tar header without decompressing the whole archive.
1918+ // See docs/ROOTFS_MANIFEST.md.
1919+ String manifestArg = null ;
1920+ File mfStageRoot = new File (requireContext ().getCacheDir (), "mfstage" );
1921+ try {
1922+ if (mfStageRoot .exists ()) {
1923+ ProcessRunner .run (new String []{"rm" , "-rf" , mfStageRoot .getAbsolutePath ()});
1924+ }
1925+ File iiabStage = new File (mfStageRoot , "installed-rootfs/iiab" );
1926+ if (iiabStage .mkdirs ()) {
1927+ String appAbi = org .iiab .controller .deploy .data .RootfsManifest .appAbiId ();
1928+ String debArch = appAbi .contains ("64" ) ? "arm64" : "armhf" ;
1929+ String built = String .format (java .util .Locale .US , "%04d.%03d" , year , dayOfYear );
1930+ String identityJson = "{\" schema\" :1,\" kind\" :\" iiab-rootfs\" ,\" arch\" :\" "
1931+ + appAbi + "\" ,\" deb_arch\" :\" " + debArch + "\" ,\" built\" :\" "
1932+ + built + "\" ,\" builder\" :\" knowledgetogo-app\" ,\" origin\" :\" device-backup\" }" ;
1933+ java .io .FileOutputStream mfo =
1934+ new java .io .FileOutputStream (new File (iiabStage , ".iiab-rootfs.json" ));
1935+ mfo .write (identityJson .getBytes ("UTF-8" ));
1936+ mfo .close ();
1937+ manifestArg = "-C '" + mfStageRoot .getAbsolutePath ()
1938+ + "' 'installed-rootfs/iiab/.iiab-rootfs.json' " ;
1939+ }
1940+ } catch (Exception mfe ) {
1941+ Log .w (TAG , "Could not stage identity manifest for backup: " + mfe .getMessage ());
1942+ manifestArg = null ;
1943+ }
1944+
19121945 // D11: single-quote the interpolated paths so the backup pipe is robust
19131946 // even if a path ever contains spaces/metacharacters (app-internal today).
1914- String cmd = "'" + tarBin + "' -cf - -C '" + iiabRootDir .getAbsolutePath ()
1947+ String cmd = "'" + tarBin + "' -cf - "
1948+ + (manifestArg != null ? manifestArg : "" )
1949+ + "-C '" + iiabRootDir .getAbsolutePath ()
19151950 + "' installed-rootfs | '" + gzipBin + "' > '" + backupFile .getAbsolutePath () + "'" ;
19161951 // D12: ProcessRunner drains stderr so a large backup with tar warnings
19171952 // cannot deadlock on a full pipe buffer.
@@ -2284,11 +2319,18 @@ private void importBackupSafely(Uri sourceUri) {
22842319 vr == org .iiab .controller .deploy .data .RootfsArchiveValidator .Result .OK ;
22852320 boolean okNoManifest =
22862321 vr == org .iiab .controller .deploy .data .RootfsArchiveValidator .Result .OK_NO_MANIFEST ;
2287- if (!okValidated && !okNoManifest ) {
2322+ boolean okNoChecksum =
2323+ vr == org .iiab .controller .deploy .data .RootfsArchiveValidator .Result .OK_NO_CHECKSUM ;
2324+ if (!okValidated && !okNoManifest && !okNoChecksum ) {
22882325 if (destFile .exists ()) destFile .delete ();
2289- final int errMsg = (vr == org .iiab .controller .deploy .data .RootfsArchiveValidator .Result .WRONG_ARCH )
2290- ? R .string .install_error_wrong_arch
2291- : R .string .install_error_not_rootfs ;
2326+ final int errMsg ;
2327+ if (vr == org .iiab .controller .deploy .data .RootfsArchiveValidator .Result .WRONG_ARCH ) {
2328+ errMsg = R .string .install_error_wrong_arch ;
2329+ } else if (vr == org .iiab .controller .deploy .data .RootfsArchiveValidator .Result .CORRUPT ) {
2330+ errMsg = R .string .install_error_corrupt ;
2331+ } else {
2332+ errMsg = R .string .install_error_not_rootfs ;
2333+ }
22922334 if (getActivity () != null ) {
22932335 getActivity ().runOnUiThread (() -> {
22942336 isImporting = false ;
@@ -2306,6 +2348,11 @@ private void importBackupSafely(Uri sourceUri) {
23062348 getActivity ().runOnUiThread (() ->
23072349 Snackbar .make (getView (), R .string .install_warn_manifest_missing , Snackbar .LENGTH_LONG ).show ());
23082350 }
2351+ // Transparency: an app-made (device) backup carries no integrity checksum.
2352+ if (okNoChecksum && getActivity () != null ) {
2353+ getActivity ().runOnUiThread (() ->
2354+ Snackbar .make (getView (), R .string .install_warn_no_checksum , Snackbar .LENGTH_LONG ).show ());
2355+ }
23092356
23102357 if (getActivity () != null ) {
23112358 getActivity ().runOnUiThread (() -> {
0 commit comments