unsigned long long TWFunc::Get_Folder_Size(const string& Path, bool Display_Error) { DIR* d; struct dirent* de; struct stat st; unsigned long long dusize = 0; unsigned long long dutemp = 0; d = opendir(Path.c_str()); if (d == NULL) { LOGERR("error opening '%s'\n", Path.c_str()); LOGERR("error: %s\n", strerror(errno)); return 0; } while ((de = readdir(d)) != NULL) { if (de->d_type == DT_DIR && strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0 && strcmp(de->d_name, "lost+found") != 0) { dutemp = Get_Folder_Size((Path + "/" + de->d_name), Display_Error); dusize += dutemp; dutemp = 0; } else if (de->d_type == DT_REG) { stat((Path + "/" + de->d_name).c_str(), &st); dusize += (unsigned long long)(st.st_size); } } closedir(d); return dusize; }
// print backup stats summary at end of a backup void show_backup_stats(const char* backup_path) { long long total_msec = timenow_msec() - nandroid_start_msec; long long minutes = total_msec / 60000LL; long long seconds = (total_msec % 60000LL) / 1000LL; unsigned long long final_size = Get_Folder_Size(backup_path); long double compression; if (Backup_Size == 0 || final_size == 0 || nandroid_get_default_backup_format() != NANDROID_BACKUP_FORMAT_TGZ) compression = 0; else compression = 1 - ((long double)(final_size) / (long double)(Backup_Size)); ui_print("\nBackup complete!\n"); ui_print("Backup time: %02lld:%02lld mn\n", minutes, seconds); ui_print("Backup size: %.2LfMb\n", (long double) final_size / 1048576); // print compression % only if it is a tar / tar.gz backup // keep also for tar to show it is 0% compression if (default_backup_handler != dedupe_compress_wrapper) ui_print("Compression: %.2Lf%%\n", compression * 100); }
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; }
int Generate_File_Lists(const char* Path) { DIR* d; struct dirent* de; struct stat st; char FileName[PATH_MAX]; // Skip /data/media if (is_data_media() && strlen(Path) >= 11 && strncmp(Path, "/data/media", 11) == 0) return 0; // Skip google cached music if (strstr(Path, "data/data/com.google.android.music/files") != NULL) return 0; d = opendir(Path); if (d == NULL) { LOGE("error opening '%s'\n", Path); return -1; } while ((de = readdir(d)) != NULL) { sprintf(FileName, "%s/", Path); strcat(FileName, de->d_name); if (is_data_media() && strlen(FileName) >= 11 && strncmp(FileName, "/data/media", 11) == 0) continue; // Skip /data/media if (de->d_type == DT_DIR && strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) { unsigned long long folder_size = Get_Folder_Size(FileName); if (Makelist_Current_Size + folder_size > MAX_ARCHIVE_SIZE) { if (Generate_File_Lists(FileName) < 0) return -1; } else { strcat(FileName, "/"); if (Add_Item(FileName) < 0) return -1; Makelist_Current_Size += folder_size; } } else if (de->d_type == DT_REG || de->d_type == DT_LNK) { stat(FileName, &st); if (Makelist_Current_Size != 0 && Makelist_Current_Size + st.st_size > MAX_ARCHIVE_SIZE) { Makelist_File_Count++; Makelist_Current_Size = 0; } if (Add_Item(FileName) < 0) return -1; Makelist_Current_Size += st.st_size; if (st.st_size > 2147483648LL) LOGE("There is a file that is larger than 2GB in the file system\n'%s'\nThis file may not restore properly\n", FileName); } } closedir(d); return 0; }