bool TWPartition::Update_Size(bool Display_Error) { bool ret = false, Was_Already_Mounted = false; if (!Can_Be_Mounted && !Is_Encrypted) return false; Was_Already_Mounted = Is_Mounted(); if (Removable || Is_Encrypted) { if (!Mount(false)) return true; } else if (!Mount(Display_Error)) return false; ret = Get_Size_Via_statfs(Display_Error); if (!ret || Size == 0) { if (!Get_Size_Via_df(Display_Error)) { if (!Was_Already_Mounted) UnMount(false); return false; } } if (Has_Data_Media) { if (Mount(Display_Error)) { unsigned long long data_media_used, actual_data; Used = TWFunc::Get_Folder_Size("/data", Display_Error); data_media_used = TWFunc::Get_Folder_Size("/data/media", Display_Error); actual_data = Used - data_media_used; Backup_Size = actual_data; int bak = (int)(Backup_Size / 1048576LLU); int total = (int)(Size / 1048576LLU); int us = (int)(Used / 1048576LLU); int fre = (int)(Free / 1048576LLU); int datmed = (int)(data_media_used / 1048576LLU); LOGI("Data backup size is %iMB, size: %iMB, used: %iMB, free: %iMB, in data/media: %iMB.\n", bak, total, us, fre, datmed); } else { if (!Was_Already_Mounted) UnMount(false); return false; } } else if (Has_Android_Secure) { if (Mount(Display_Error)) Backup_Size = TWFunc::Get_Folder_Size(Backup_Path, Display_Error); else { if (!Was_Already_Mounted) UnMount(false); return false; } } if (!Was_Already_Mounted) UnMount(false); return true; }
// check size of archive files to get total backed up data size // find all backup image files of a given partition and increment Backup_Size // Backup_Size is set to 0 at start of nandroid_restore() process so that we do not print size progress on void check_restore_size(const char* backup_file_image, const char* backup_path) { // refresh target partition size if (Get_Size_Via_statfs(backup_path) != 0) { Backup_Size = 0; return; } Before_Used_Size = Used_Size; char tmp[PATH_MAX]; char filename[PATH_MAX]; char** files; int numFiles = 0; sprintf(tmp, "%s/", DirName(backup_file_image)); files = gather_files(tmp, "", &numFiles); // if it's a twrp multi volume backup, ensure we remove trailing 000: strlen("000") = 3 if (strlen(backup_file_image) > strlen("win000") && strcmp(backup_file_image + strlen(backup_file_image) - strlen("win000"), "win000") == 0) snprintf(tmp, strlen(backup_file_image) - 3, "%s", backup_file_image); else strcpy(tmp, backup_file_image); sprintf(filename, "%s", BaseName(tmp)); int i; unsigned long fsize; for(i = 0; i < numFiles; i++) { if (strstr(files[i], filename) != NULL) { fsize = Get_File_Size(files[i]); // check if it is a compressed archive and increase size by 45% // this needs a better implementation to do later if (is_gzip_file(files[i]) > 0) fsize += (fsize * 45) / 100; Backup_Size += fsize; } } free_string_array(files); }
int twrp_backup(const char* backup_path) { // keep this for extra security and keep close to stock code // refresh_default_backup_handler() mounts /sdcard. We stat it in nandroid_backup_partition_extended() for callback nandroid_backup_bitfield = 0; refresh_default_backup_handler(); if (ensure_path_mounted(backup_path) != 0) return print_and_error("Can't mount backup path.\n", NANDROID_ERROR_GENERAL); int ret; struct statfs s; // refresh size stats for backup_path // this will also ensure volume for backup path != NULL if (0 != Get_Size_Via_statfs(backup_path)) return print_and_error("Unable to stat backup path.\n", NANDROID_ERROR_GENERAL); // estimate backup size and ensure we have enough free space available on backup_path if (check_backup_size(backup_path) < 0) return print_and_error("Not enough free space: backup cancelled.\n", NANDROID_ERROR_GENERAL); // moved after backup size check to fix pause before showing low space prompt // this is caused by friendly log view triggering on ui_set_background(BACKGROUND_ICON_INSTALLING) call // also, it is expected to have the background installing icon when we actually start backup ui_set_background(BACKGROUND_ICON_INSTALLING); nandroid_start_msec = timenow_msec(); // starts backup monitoring timer for total backup time #ifdef PHILZ_TOUCH_RECOVERY last_key_ev = nandroid_start_msec; // support dim screen timeout during nandroid operation #endif char tmp[PATH_MAX]; ensure_directory(backup_path, 0755); if (backup_boot && volume_for_path(BOOT_PARTITION_MOUNT_POINT) != NULL && 0 != (ret = nandroid_backup_partition(backup_path, BOOT_PARTITION_MOUNT_POINT))) return print_and_error(NULL, ret); if (backup_recovery && volume_for_path("/recovery") != NULL && 0 != (ret = nandroid_backup_partition(backup_path, "/recovery"))) return print_and_error(NULL, ret); #ifdef BOARD_USE_MTK_LAYOUT if ((backup_boot || backup_recovery) && volume_for_path("/uboot") != NULL && 0 != (ret = nandroid_backup_partition(backup_path, "/uboot"))) return print_and_error(NULL, ret); #endif Volume *vol = volume_for_path("/efs"); if (backup_efs && NULL != vol) { if (0 != (ret = nandroid_backup_partition(backup_path, "/efs"))) return print_and_error(NULL, ret); } vol = volume_for_path("/misc"); if (backup_misc && NULL != vol) { if (0 != (ret = nandroid_backup_partition(backup_path, "/misc"))) return print_and_error(NULL, ret); } vol = volume_for_path("/modem"); if (backup_modem && NULL != vol) { if (0 != (ret = nandroid_backup_partition(backup_path, "/modem"))) return print_and_error(NULL, ret); } vol = volume_for_path("/radio"); if (backup_radio && NULL != vol) { if (0 != (ret = nandroid_backup_partition(backup_path, "/radio"))) return print_and_error(NULL, ret); } if (backup_system && 0 != (ret = nandroid_backup_partition(backup_path, "/system"))) return print_and_error(NULL, ret); vol = volume_for_path("/preload"); if (backup_preload && NULL != vol) { if (0 != (ret = nandroid_backup_partition(backup_path, "/preload"))) return print_and_error(NULL, ret); } if (backup_data && 0 != (ret = nandroid_backup_partition(backup_path, "/data"))) return print_and_error(NULL, ret); if (has_datadata()) { if (backup_data && 0 != (ret = nandroid_backup_partition(backup_path, "/datadata"))) return print_and_error(NULL, ret); } // handle .android_secure on external and internal storage set_android_secure_path(tmp); if (backup_data && android_secure_ext) { if (0 != (ret = nandroid_backup_partition_extended(backup_path, tmp, 0))) return print_and_error(NULL, ret); } if (backup_cache && 0 != (ret = nandroid_backup_partition_extended(backup_path, "/cache", 0))) return print_and_error(NULL, ret); if (backup_sdext) { if (0 != ensure_path_mounted("/sd-ext")) { LOGI("No sd-ext found. Skipping backup of sd-ext.\n"); } else if (0 != (ret = nandroid_backup_partition(backup_path, "/sd-ext"))) { return print_and_error(NULL, ret); } } // handle extra partitions int i; int extra_partitions_num = get_extra_partitions_state(); for (i = 0; i < extra_partitions_num; ++i) { if (extra_partition[i].backup_state && 0 != (ret = nandroid_backup_partition(backup_path, extra_partition[i].mount_point))) return print_and_error(NULL, ret); } if (enable_md5sum.value) { if (0 != (ret = gen_twrp_md5sum(backup_path))) return print_and_error(NULL, ret); } sprintf(tmp, "chmod -R 777 %s", backup_path); __system(tmp); finish_nandroid_job(); show_backup_stats(backup_path); if (reboot_after_nandroid) reboot_main_system(ANDROID_RB_RESTART, 0, 0); return 0; }
int check_backup_size(const char* backup_path) { // these are the size stats for backup_path we previously refreshed by calling Get_Size_Via_statfs() int total_mb = (int)(Total_Size / 1048576LLU); int used_mb = (int)(Used_Size / 1048576LLU); int free_mb = (int)(Free_Size / 1048576LLU); int free_percent = free_mb * 100 / total_mb; Before_Used_Size = Used_Size; // save Used_Size to refresh data written stats later Backup_Size = 0; // supported nandroid partitions char* Base_Partitions_List[BASE_PARTITIONS_NUM] = { "/recovery", BOOT_PARTITION_MOUNT_POINT, "/wimax", "/modem", "/radio", "/efs", "/misc", "/system", "/preload", "/data", "/datadata", "/cache", "/sd-ext" }; int items_num = BASE_PARTITIONS_NUM + MAX_EXTRA_NANDROID_PARTITIONS + 1; char* Partitions_List[items_num]; int i; for (i = 0; i < BASE_PARTITIONS_NUM; ++i) { Partitions_List[i] = Base_Partitions_List[i]; } int extra_partitions_num = get_extra_partitions_state(); for (i = 0; i < extra_partitions_num; ++i) { Partitions_List[BASE_PARTITIONS_NUM + i] = extra_partition[i].mount_point; } Partitions_List[BASE_PARTITIONS_NUM + extra_partitions_num] = NULL; int preload_status = 0; if ((is_custom_backup && backup_preload) || (!is_custom_backup && nandroid_add_preload.value)) preload_status = 1; int Base_Partitions_Backup_Status[] = { backup_recovery, backup_boot, backup_wimax, backup_modem, backup_radio, backup_efs, backup_misc, backup_system, preload_status, backup_data, backup_data, backup_cache, backup_sdext, }; LOGI("Checking needed space for backup '%s'\n", backup_path); // calculate needed space for backup // assume recovery and wimax always use a raw backup mode (Is_Image() = 0) char skipped_parts[1024] = ""; int ret = 0; Volume* vol; for (i = 0; Partitions_List[i] != NULL; ++i) { if (i >= BASE_PARTITIONS_NUM) { if (!extra_partition[i - BASE_PARTITIONS_NUM].backup_state) continue; } else if (!Base_Partitions_Backup_Status[i]) { continue; } // size of /data will be calculated later for /data/media devices to subtract sdcard size from it if (strcmp(Partitions_List[i], "/data") == 0 && is_data_media()) continue; // redundant but keep for compatibility: // has_datadata() does a volume_for_path() != NULL check if (strcmp(Partitions_List[i], "/datadata") == 0 && !has_datadata()) continue; vol = volume_for_path(Partitions_List[i]); if (vol == NULL) continue; if (Is_Image(Partitions_List[i]) == 0) { if (Find_Partition_Size(Partitions_List[i]) == 0) { Backup_Size += Total_Size; LOGI("%s backup size (/proc)=%lluMb\n", Partitions_List[i], Total_Size / 1048576LLU); // debug } else { ret++; strcat(skipped_parts, " - "); strcat(skipped_parts, Partitions_List[i]); } } else if (Is_File_System(Partitions_List[i]) == 0) { // Get_Size_Via_statfs() will ensure vol->mount_point != NULL if (0 == ensure_path_mounted(vol->mount_point) && 0 == Get_Size_Via_statfs(vol->mount_point)) { Backup_Size += Used_Size; LOGI("%s backup size (stat)=%lluMb\n", Partitions_List[i], Used_Size / 1048576LLU); // debug } else { ret++; strcat(skipped_parts, " - "); strcat(skipped_parts, Partitions_List[i]); } } else { ret++; strcat(skipped_parts, " - Unknown file system: "); strcat(skipped_parts, Partitions_List[i]); } } // handle special partitions and folders: // handle /data and /data/media partitions size for /data/media devices unsigned long long data_backup_size = 0; unsigned long long data_used_bytes = 0; unsigned long long data_media_size = 0; if (is_data_media() && (backup_data || backup_data_media)) { if (0 == ensure_path_mounted("/data") && 0 == Get_Size_Via_statfs("/data")) { data_media_size = Get_Folder_Size("/data/media"); data_used_bytes = Get_Folder_Size("/data"); data_backup_size = data_used_bytes - data_media_size; LOGI("/data: tot size=%lluMb, free=%lluMb, backup size=%lluMb, used=%lluMb, media=%lluMb\n", Total_Size/1048576LLU, Free_Size/1048576LLU, data_backup_size/1048576LLU, data_used_bytes/1048576LLU, data_media_size/1048576LLU); } else { if (backup_data) { strcat(skipped_parts, " - /data"); ret++; } if (backup_data_media) { strcat(skipped_parts, " - /data/media"); ret++; } } } if (backup_data) Backup_Size += data_backup_size; // check if we are also backing up /data/media // if backup_path is same as /data/media, ignore this as it will not be processed by nandroid_backup_datamedia() if (backup_data_media && !is_data_media_volume_path(backup_path)) { Backup_Size += data_media_size; LOGI("included /data/media size\n"); // debug } // .android_secure size calculation // set_android_secure_path() will mount tmp so no need to remount before calling Get_Folder_Size(tmp) char tmp[PATH_MAX]; set_android_secure_path(tmp); if (backup_data && android_secure_ext) { unsigned long long andsec_size; andsec_size = Get_Folder_Size(tmp); Backup_Size += andsec_size; LOGI("%s backup size=%lluMb\n", tmp, andsec_size / 1048576LLU); // debug } // check if we have the needed space int backup_size_mb = (int)(Backup_Size / 1048576LLU); ui_print("\n>> Free space: %dMb (%d%%)\n", free_mb, free_percent); ui_print(">> Needed space: %dMb\n", backup_size_mb); if (ret) ui_print(">> Unknown partitions size (%d):%s\n", ret, skipped_parts); // dedupe wrapper needs less space than actual backup size (incremental backups) // only check free space in Mb if we use tar or tar.gz as a default format // also, add extra 50 Mb for security measures if (free_percent < 3 || (default_backup_handler != dedupe_compress_wrapper && free_mb < backup_size_mb + 50)) { LOGW("Low space for backup!\n"); if (!ui_is_initialized()) { // do not prompt when it is an "adb shell nandroid backup" command LOGW("\n>>> Backup could fail with I/O error!! <<<\n\n"); } else if (nand_prompt_on_low_space.value && !confirm_selection("Low free space! Continue anyway?", "Yes - Continue Nandroid Job")) return -1; } return 0; }