summaryrefslogtreecommitdiffstats
path: root/partition.cpp
diff options
context:
space:
mode:
authorbigbiff <bigbiff@teamw.in>2015-12-13 00:30:21 +0100
committerEthan Yonker <dees_troy@teamw.in>2016-08-01 05:20:18 +0200
commitce8f83c48d200106ff61ad530c863b15c16949d9 (patch)
tree2908e48dfb44eae96c49113a177108f3cbb714f2 /partition.cpp
parentBlack screen patch for some HiSilicon devices (FBIOPAN_DISPLAY) (diff)
downloadandroid_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.tar
android_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.tar.gz
android_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.tar.bz2
android_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.tar.lz
android_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.tar.xz
android_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.tar.zst
android_bootable_recovery-ce8f83c48d200106ff61ad530c863b15c16949d9.zip
Diffstat (limited to 'partition.cpp')
-rw-r--r--partition.cpp269
1 files changed, 168 insertions, 101 deletions
diff --git a/partition.cpp b/partition.cpp
index 1261a2a26..bf87de38a 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <dirent.h>
#include <libgen.h>
+#include <zlib.h>
#include <iostream>
#include <sstream>
#include <sys/param.h>
@@ -46,6 +47,7 @@
#include "infomanager.hpp"
#include "set_metadata.h"
#include "gui/gui.hpp"
+#include "adbbu/libtwadbbu.hpp"
extern "C" {
#include "mtdutils/mtdutils.h"
#include "mtdutils/mounts.h"
@@ -208,7 +210,7 @@ TWPartition::TWPartition() {
Backup_Name = "";
Backup_FileName = "";
MTD_Name = "";
- Backup_Method = NONE;
+ Backup_Method = BM_NONE;
Can_Encrypt_Backup = false;
Use_Userdata_Encryption = false;
Has_Data_Media = false;
@@ -801,16 +803,16 @@ void TWPartition::Setup_File_System(bool Display_Error) {
Make_Dir(Mount_Point, Display_Error);
Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
Backup_Name = Display_Name;
- Backup_Method = FILES;
+ Backup_Method = BM_FILES;
}
void TWPartition::Setup_Image(bool Display_Error) {
Display_Name = Mount_Point.substr(1, Mount_Point.size() - 1);
Backup_Name = Display_Name;
if (Current_File_System == "emmc")
- Backup_Method = DD;
+ Backup_Method = BM_DD;
else if (Current_File_System == "mtd" || Current_File_System == "bml")
- Backup_Method = FLASH_UTILS;
+ Backup_Method = BM_FLASH_UTILS;
else
LOGINFO("Unhandled file system '%s' on image '%s'\n", Current_File_System.c_str(), Display_Name.c_str());
if (Find_Partition_Size()) {
@@ -1598,14 +1600,13 @@ bool TWPartition::Resize() {
return false;
}
-bool TWPartition::Backup(const string& backup_folder, pid_t &tar_fork_pid, ProgressTracking *progress) {
- if (Backup_Method == FILES) {
- return Backup_Tar(backup_folder, progress, tar_fork_pid);
- }
- else if (Backup_Method == DD)
- return Backup_Image(backup_folder, progress);
- else if (Backup_Method == FLASH_UTILS)
- return Backup_Dump_Image(backup_folder, progress);
+bool TWPartition::Backup(PartitionSettings *part_settings, pid_t *tar_fork_pid) {
+ if (Backup_Method == BM_FILES)
+ return Backup_Tar(part_settings, tar_fork_pid);
+ else if (Backup_Method == BM_DD)
+ return Backup_Image(part_settings);
+ else if (Backup_Method == BM_FLASH_UTILS)
+ return Backup_Dump_Image(part_settings);
LOGERR("Unknown backup method for '%s'\n", Mount_Point.c_str());
return false;
}
@@ -1658,53 +1659,49 @@ bool TWPartition::Check_MD5(string restore_folder) {
return false;
}
-bool TWPartition::Restore(const string& restore_folder, ProgressTracking *progress) {
- string Restore_File_System;
-
+bool TWPartition::Restore(PartitionSettings *part_settings) {
TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Display_Name, gui_parse_text("{@restoring_hdr}"));
- LOGINFO("Restore filename is: %s\n", Backup_FileName.c_str());
+ LOGINFO("Restore filename is: %s/%s\n", part_settings->Restore_Name.c_str(), part_settings->Backup_FileName.c_str());
- Restore_File_System = Get_Restore_File_System(restore_folder);
+ string Restore_File_System = Get_Restore_File_System(part_settings);
if (Is_File_System(Restore_File_System))
- return Restore_Tar(restore_folder, Restore_File_System, progress);
- else if (Is_Image(Restore_File_System)) {
- return Restore_Image(restore_folder, Restore_File_System, progress);
- }
+ return Restore_Tar(part_settings);
+ else if (Is_Image(Restore_File_System))
+ return Restore_Image(part_settings);
LOGERR("Unknown restore method for '%s'\n", Mount_Point.c_str());
return false;
}
-string TWPartition::Get_Restore_File_System(const string& restore_folder) {
+string TWPartition::Get_Restore_File_System(PartitionSettings *part_settings) {
size_t first_period, second_period;
string Restore_File_System;
// Parse backup filename to extract the file system before wiping
- first_period = Backup_FileName.find(".");
+ first_period = part_settings->Backup_FileName.find(".");
if (first_period == string::npos) {
LOGERR("Unable to find file system (first period).\n");
return string();
}
- Restore_File_System = Backup_FileName.substr(first_period + 1, Backup_FileName.size() - first_period - 1);
+ Restore_File_System = part_settings->Backup_FileName.substr(first_period + 1, part_settings->Backup_FileName.size() - first_period - 1);
second_period = Restore_File_System.find(".");
if (second_period == string::npos) {
LOGERR("Unable to find file system (second period).\n");
return string();
}
Restore_File_System.resize(second_period);
- LOGINFO("Restore file system is: '%s'.\n", Restore_File_System.c_str());
return Restore_File_System;
}
string TWPartition::Backup_Method_By_Name() {
- if (Backup_Method == NONE)
+ if (Backup_Method == BM_NONE)
return "none";
- else if (Backup_Method == FILES)
+ else if (Backup_Method == BM_FILES)
return "files";
- else if (Backup_Method == DD)
+ else if (Backup_Method == BM_DD)
return "dd";
- else if (Backup_Method == FLASH_UTILS)
+ else if (Backup_Method == BM_FLASH_UTILS)
return "flash_utils";
else
return "undefined";
@@ -2141,7 +2138,7 @@ bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unu
return false;
}
-bool TWPartition::Backup_Tar(const string& backup_folder, ProgressTracking *progress, pid_t &tar_fork_pid) {
+bool TWPartition::Backup_Tar(PartitionSettings *part_settings, pid_t *tar_fork_pid) {
string Full_FileName;
twrpTar tar;
@@ -2169,72 +2166,114 @@ bool TWPartition::Backup_Tar(const string& backup_folder, ProgressTracking *prog
#endif
Backup_FileName = Backup_Name + "." + Current_File_System + ".win";
- Full_FileName = backup_folder + "/" + Backup_FileName;
+ Full_FileName = part_settings->Full_Backup_Path + Backup_FileName;
tar.has_data_media = Has_Data_Media;
- Full_FileName = backup_folder + "/" + Backup_FileName;
+ tar.part_settings = part_settings;
tar.setdir(Backup_Path);
tar.setfn(Full_FileName);
tar.setsize(Backup_Size);
tar.partition_name = Backup_Name;
- tar.backup_folder = backup_folder;
- if (tar.createTarFork(progress, tar_fork_pid) != 0)
+ tar.backup_folder = part_settings->Full_Backup_Path;
+ if (tar.createTarFork(tar_fork_pid) != 0)
return false;
return true;
}
-bool TWPartition::Backup_Image(const string& backup_folder, ProgressTracking *progress) {
- string Full_FileName;
+bool TWPartition::Backup_Image(PartitionSettings *part_settings) {
+ string Full_FileName, adb_file_name;
+ int adb_control_bu_fd, compressed;
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, gui_parse_text("{@backing}"));
gui_msg(Msg("backing_up=Backing up {1}...")(Backup_Display_Name));
Backup_FileName = Backup_Name + "." + Current_File_System + ".win";
- Full_FileName = backup_folder + "/" + Backup_FileName;
- if (!Raw_Read_Write(Actual_Block_Device, Full_FileName, Backup_Size, progress))
- return false;
+ if (part_settings->adbbackup) {
+ Full_FileName = TW_ADB_BACKUP;
+ adb_file_name = part_settings->Full_Backup_Path + "/" + Backup_FileName;
+ }
+ else
+ Full_FileName = part_settings->Full_Backup_Path + "/" + Backup_FileName;
- tw_set_default_metadata(Full_FileName.c_str());
- if (TWFunc::Get_File_Size(Full_FileName) == 0) {
- gui_msg(Msg(msg::kError, "backup_size=Backup file size for '{1}' is 0 bytes.")(Full_FileName));
+ part_settings->total_restore_size = Backup_Size;
+
+ if (part_settings->adbbackup) {
+ if (!twadbbu::Write_TWIMG(adb_file_name, Backup_Size))
+ return false;
+ }
+
+ if (!Raw_Read_Write(part_settings))
return false;
+
+ if (part_settings->adbbackup) {
+ if (!twadbbu::Write_TWEOF())
+ return false;
}
return true;
}
-bool TWPartition::Raw_Read_Write(const string& input_file, const string& output_file, const unsigned long long input_size, ProgressTracking *progress) {
- unsigned long long RW_Block_Size, Remain;
- int src_fd = -1, dest_fd = -1, bs;
+bool TWPartition::Raw_Read_Write(PartitionSettings *part_settings) {
+ unsigned long long RW_Block_Size, Remain = Backup_Size;
+ int src_fd = -1, dest_fd = -1;
+ ssize_t bs;
bool ret = false;
void* buffer = NULL;
unsigned long long backedup_size = 0;
+ string srcfn, destfn;
- RW_Block_Size = 1048576LLU; // 1MB
- Remain = input_size;
+ if (part_settings->PM_Method == PM_BACKUP) {
+ srcfn = Actual_Block_Device;
+ if (part_settings->adbbackup)
+ destfn = TW_ADB_BACKUP;
+ else
+ destfn = part_settings->Full_Backup_Path + part_settings->Backup_FileName;
+ }
+ else {
+ destfn = Actual_Block_Device;
+ if (part_settings->adbbackup) {
+ srcfn = TW_ADB_RESTORE;
+ } else {
+ srcfn = part_settings->Restore_Name + "/" + part_settings->Backup_FileName;
+ Remain = TWFunc::Get_File_Size(srcfn);
+ }
+ }
- src_fd = open(input_file.c_str(), O_RDONLY | O_LARGEFILE);
+ src_fd = open(srcfn.c_str(), O_RDONLY | O_LARGEFILE);
if (src_fd < 0) {
- gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(input_file)(strerror(errno)));
+ gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(srcfn.c_str())(strerror(errno)));
return false;
}
- dest_fd = open(output_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR);
+
+ dest_fd = open(destfn.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR);
if (dest_fd < 0) {
- gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(output_file)(strerror(errno)));
+ gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(destfn.c_str())(strerror(errno)));
goto exit;
}
- bs = (int)(RW_Block_Size);
+
+ LOGINFO("Reading '%s', writing '%s'\n", srcfn.c_str(), destfn.c_str());
+
+ if (part_settings->adbbackup) {
+ RW_Block_Size = MAX_ADB_READ;
+ bs = MAX_ADB_READ;
+ }
+ else {
+ RW_Block_Size = 1048576LLU; // 1MB
+ bs = (ssize_t)(RW_Block_Size);
+ }
+
buffer = malloc((size_t)bs);
if (!buffer) {
LOGINFO("Raw_Read_Write failed to malloc\n");
goto exit;
}
- LOGINFO("Reading '%s', writing '%s'\n", input_file.c_str(), output_file.c_str());
- if (progress)
- progress->SetPartitionSize(input_size);
+
+ if (part_settings->progress)
+ part_settings->progress->SetPartitionSize(part_settings->total_restore_size);
+
while (Remain > 0) {
if (Remain < RW_Block_Size)
- bs = (int)(Remain);
- if (read(src_fd, buffer, bs) != bs) {
+ bs = (ssize_t)(Remain);
+ if (read(src_fd, buffer, bs) != bs) {
LOGINFO("Error reading source fd (%s)\n", strerror(errno));
goto exit;
}
@@ -2243,14 +2282,14 @@ bool TWPartition::Raw_Read_Write(const string& input_file, const string& output_
goto exit;
}
backedup_size += (unsigned long long)(bs);
- Remain -= (unsigned long long)(bs);
- if (progress)
- progress->UpdateSize(backedup_size);
+ Remain = Remain - (unsigned long long)(bs);
+ if (part_settings->progress)
+ part_settings->progress->UpdateSize(backedup_size);
if (PartitionManager.Check_Backup_Cancel() != 0)
goto exit;
}
- if (progress)
- progress->UpdateDisplayDetails(true);
+ if (part_settings->progress)
+ part_settings->progress->UpdateDisplayDetails(true);
fsync(dest_fd);
ret = true;
exit:
@@ -2263,19 +2302,22 @@ exit:
return ret;
}
-bool TWPartition::Backup_Dump_Image(const string& backup_folder, ProgressTracking *progress) {
+bool TWPartition::Backup_Dump_Image(PartitionSettings *part_settings) {
string Full_FileName, Command;
+ int use_compression, adb_control_bu_fd;
+ unsigned long long compressed;
TWFunc::GUI_Operation_Text(TW_BACKUP_TEXT, Display_Name, gui_parse_text("{@backing}"));
gui_msg(Msg("backing_up=Backing up {1}...")(Backup_Display_Name));
- if (progress)
- progress->SetPartitionSize(Backup_Size);
+ if (part_settings->progress)
+ part_settings->progress->SetPartitionSize(Backup_Size);
Backup_FileName = Backup_Name + "." + Current_File_System + ".win";
- Full_FileName = backup_folder + "/" + Backup_FileName;
+ Full_FileName = part_settings->Full_Backup_Path + "/" + Backup_FileName;
Command = "dump_image " + MTD_Name + " '" + Full_FileName + "'";
+
LOGINFO("Backup command: '%s'\n", Command.c_str());
TWFunc::Exec_Cmd(Command);
tw_set_default_metadata(Full_FileName.c_str());
@@ -2284,23 +2326,26 @@ bool TWPartition::Backup_Dump_Image(const string& backup_folder, ProgressTrackin
gui_msg(Msg(msg::kError, "backup_size=Backup file size for '{1}' is 0 bytes.")(Full_FileName));
return false;
}
- if (progress)
- progress->UpdateSize(Backup_Size);
+ if (part_settings->progress)
+ part_settings->progress->UpdateSize(Backup_Size);
+
return true;
}
-unsigned long long TWPartition::Get_Restore_Size(const string& restore_folder) {
- InfoManager restore_info(restore_folder + "/" + Backup_Name + ".info");
- if (restore_info.LoadValues() == 0) {
- if (restore_info.GetValue("backup_size", Restore_Size) == 0) {
- LOGINFO("Read info file, restore size is %llu\n", Restore_Size);
- return Restore_Size;
+unsigned long long TWPartition::Get_Restore_Size(PartitionSettings *part_settings) {
+ if (!part_settings->adbbackup) {
+ InfoManager restore_info(part_settings->Restore_Name + "/" + Backup_Name + ".info");
+ if (restore_info.LoadValues() == 0) {
+ if (restore_info.GetValue("backup_size", Restore_Size) == 0) {
+ LOGINFO("Read info file, restore size is %llu\n", Restore_Size);
+ return Restore_Size;
+ }
}
}
- string Full_FileName, Restore_File_System = Get_Restore_File_System(restore_folder);
- Full_FileName = restore_folder + "/" + Backup_FileName;
+ string Full_FileName, Restore_File_System = Get_Restore_File_System(part_settings);
+ Full_FileName = part_settings->Restore_Name + "/" + Backup_FileName;
if (Is_Image(Restore_File_System)) {
Restore_Size = TWFunc::Get_File_Size(Full_FileName);
return Restore_Size;
@@ -2309,7 +2354,7 @@ unsigned long long TWPartition::Get_Restore_Size(const string& restore_folder) {
twrpTar tar;
tar.setdir(Backup_Path);
tar.setfn(Full_FileName);
- tar.backup_name = Backup_Name;
+ tar.backup_name = Full_FileName;
#ifndef TW_EXCLUDE_ENCRYPTED_BACKUPS
string Password;
DataManager::GetValue("tw_restore_password", Password);
@@ -2317,14 +2362,16 @@ unsigned long long TWPartition::Get_Restore_Size(const string& restore_folder) {
tar.setpassword(Password);
#endif
tar.partition_name = Backup_Name;
- tar.backup_folder = restore_folder;
+ tar.backup_folder = part_settings->Restore_Name;
+ tar.part_settings = part_settings;
Restore_Size = tar.get_size();
return Restore_Size;
}
-bool TWPartition::Restore_Tar(const string& restore_folder, const string& Restore_File_System, ProgressTracking *progress) {
+bool TWPartition::Restore_Tar(PartitionSettings *part_settings) {
string Full_FileName;
bool ret = false;
+ string Restore_File_System = Get_Restore_File_System(part_settings);
if (Has_Android_Secure) {
if (!Wipe_AndSec())
@@ -2347,8 +2394,9 @@ bool TWPartition::Restore_Tar(const string& restore_folder, const string& Restor
if (!ReMount_RW(true))
return false;
- Full_FileName = restore_folder + "/" + Backup_FileName;
+ Full_FileName = part_settings->Restore_Name + "/" + part_settings->Backup_FileName;
twrpTar tar;
+ tar.part_settings = part_settings;
tar.setdir(Backup_Path);
tar.setfn(Full_FileName);
tar.backup_name = Backup_Name;
@@ -2358,8 +2406,8 @@ bool TWPartition::Restore_Tar(const string& restore_folder, const string& Restor
if (!Password.empty())
tar.setpassword(Password);
#endif
- progress->SetPartitionSize(Get_Restore_Size(restore_folder));
- if (tar.extractTarFork(progress) != 0)
+ part_settings->progress->SetPartitionSize(Get_Restore_Size(part_settings));
+ if (tar.extractTarFork() != 0)
ret = false;
else
ret = true;
@@ -2389,19 +2437,30 @@ bool TWPartition::Restore_Tar(const string& restore_folder, const string& Restor
return ret;
}
-bool TWPartition::Restore_Image(const string& restore_folder, const string& Restore_File_System, ProgressTracking *progress) {
+bool TWPartition::Restore_Image(PartitionSettings *part_settings) {
string Full_FileName;
+ string Restore_File_System = Get_Restore_File_System(part_settings);
TWFunc::GUI_Operation_Text(TW_RESTORE_TEXT, Backup_Display_Name, gui_parse_text("{@restoring_hdr}"));
gui_msg(Msg("restoring=Restoring {1}...")(Backup_Display_Name));
- Full_FileName = restore_folder + "/" + Backup_FileName;
+
+ if (part_settings->adbbackup)
+ Full_FileName = TW_ADB_RESTORE;
+ else
+ Full_FileName = part_settings->Full_Backup_Path + part_settings->Backup_FileName;
if (Restore_File_System == "emmc") {
- unsigned long long file_size = (unsigned long long)(TWFunc::Get_File_Size(Full_FileName));
- if (!Raw_Read_Write(Full_FileName, Actual_Block_Device, file_size, progress))
+ if (!part_settings->adbbackup)
+ part_settings->total_restore_size = (uint64_t)(TWFunc::Get_File_Size(Full_FileName));
+ if (!Raw_Read_Write(part_settings))
return false;
} else if (Restore_File_System == "mtd" || Restore_File_System == "bml") {
- if (!Flash_Image_FI(Full_FileName, progress))
+ if (!Flash_Image_FI(Full_FileName, part_settings->progress))
+ return false;
+ }
+
+ if (part_settings->adbbackup) {
+ if (!twadbbu::Write_TWEOF())
return false;
}
return true;
@@ -2544,12 +2603,14 @@ uint64_t TWPartition::Get_Max_FileSize() {
return maxFileSize - 1;
}
-bool TWPartition::Flash_Image(const string& Filename) {
- string Restore_File_System;
+bool TWPartition::Flash_Image(PartitionSettings *part_settings) {
+ string Restore_File_System, full_filename;
+
+ full_filename = part_settings->Restore_Name + "/" + part_settings->Backup_FileName;
- LOGINFO("Image filename is: %s\n", Filename.c_str());
+ LOGINFO("Image filename is: %s\n", part_settings->Backup_FileName.c_str());
- if (Backup_Method == FILES) {
+ if (Backup_Method == BM_FILES) {
LOGERR("Cannot flash images to file systems\n");
return false;
} else if (!Can_Flash_Img) {
@@ -2560,22 +2621,23 @@ bool TWPartition::Flash_Image(const string& Filename) {
LOGERR("Unable to find partition size for '%s'\n", Mount_Point.c_str());
return false;
}
- unsigned long long image_size = TWFunc::Get_File_Size(Filename);
+ unsigned long long image_size = TWFunc::Get_File_Size(full_filename);
if (image_size > Size) {
LOGINFO("Size (%llu bytes) of image '%s' is larger than target device '%s' (%llu bytes)\n",
- image_size, Filename.c_str(), Actual_Block_Device.c_str(), Size);
+ image_size, part_settings->Backup_FileName.c_str(), Actual_Block_Device.c_str(), Size);
gui_err("img_size_err=Size of image is larger than target device");
return false;
}
- if (Backup_Method == DD) {
- if (Is_Sparse_Image(Filename)) {
- return Flash_Sparse_Image(Filename);
+ if (Backup_Method == BM_DD) {
+ if (!part_settings->adbbackup) {
+ if (Is_Sparse_Image(full_filename)) {
+ return Flash_Sparse_Image(full_filename);
+ }
}
- unsigned long long file_size = (unsigned long long)(TWFunc::Get_File_Size(Filename));
- ProgressTracking pt(file_size);
- return Raw_Read_Write(Filename, Actual_Block_Device, file_size, &pt);
- } else if (Backup_Method == FLASH_UTILS) {
- return Flash_Image_FI(Filename, NULL);
+ unsigned long long file_size = (unsigned long long)(TWFunc::Get_File_Size(full_filename));
+ return Raw_Read_Write(part_settings);
+ } else if (Backup_Method == BM_FLASH_UTILS) {
+ return Flash_Image_FI(full_filename, NULL);
}
}
@@ -2590,6 +2652,7 @@ bool TWPartition::Is_Sparse_Image(const string& Filename) {
gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(Filename)(strerror(errno)));
return false;
}
+
if (read(fd, &magic, sizeof(magic)) != sizeof(magic)) {
gui_msg(Msg(msg::kError, "error_opening_strerr=Error opening: '{1}' ({2})")(Filename)(strerror(errno)));
close(fd);
@@ -2637,6 +2700,10 @@ void TWPartition::Change_Mount_Read_Only(bool new_value) {
Mount_Read_Only = new_value;
}
+bool TWPartition::Is_Read_Only() {
+ return Mount_Read_Only;
+}
+
int TWPartition::Check_Lifetime_Writes() {
bool original_read_only = Mount_Read_Only;
int ret = 1;