1 #include "fs.h" 2 3 #include "fastboot.h" 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/stat.h> 11 #include <sys/types.h> 12 #ifndef WIN32 13 #include <sys/wait.h> 14 #else 15 #include <tchar.h> 16 #include <windows.h> 17 #endif 18 #include <unistd.h> 19 #include <vector> 20 21 #include <android-base/errors.h> 22 #include <android-base/file.h> 23 #include <android-base/stringprintf.h> 24 #include <android-base/unique_fd.h> 25 26 using android::base::GetExecutableDirectory; 27 using android::base::StringPrintf; 28 using android::base::unique_fd; 29 30 #ifdef WIN32 31 static int exec_cmd(const char* path, const char** argv, const char** envp) { 32 std::string cmd; 33 int i = 0; 34 while (argv[i] != nullptr) { 35 cmd += argv[i++]; 36 cmd += " "; 37 } 38 cmd = cmd.substr(0, cmd.size() - 1); 39 40 STARTUPINFO si; 41 PROCESS_INFORMATION pi; 42 DWORD exit_code = 0; 43 44 ZeroMemory(&si, sizeof(si)); 45 si.cb = sizeof(si); 46 ZeroMemory(&pi, sizeof(pi)); 47 48 std::string env_str; 49 if (envp != nullptr) { 50 while (*envp != nullptr) { 51 env_str += std::string(*envp) + std::string("\0", 1); 52 envp++; 53 } 54 } 55 56 if (!CreateProcessA(nullptr, // No module name (use command line) 57 const_cast<char*>(cmd.c_str()), // Command line 58 nullptr, // Process handle not inheritable 59 nullptr, // Thread handle not inheritable 60 FALSE, // Set handle inheritance to FALSE 61 0, // No creation flags 62 env_str.empty() ? nullptr : LPSTR(env_str.c_str()), 63 nullptr, // Use parent's starting directory 64 &si, // Pointer to STARTUPINFO structure 65 &pi) // Pointer to PROCESS_INFORMATION structure 66 ) { 67 fprintf(stderr, "CreateProcess failed: %s\n", 68 android::base::SystemErrorCodeToString(GetLastError()).c_str()); 69 return -1; 70 } 71 72 WaitForSingleObject(pi.hProcess, INFINITE); 73 74 GetExitCodeProcess(pi.hProcess, &exit_code); 75 76 CloseHandle(pi.hProcess); 77 CloseHandle(pi.hThread); 78 79 if (exit_code != 0) { 80 fprintf(stderr, "%s failed: %lu\n", path, exit_code); 81 return -1; 82 } 83 return 0; 84 } 85 #else 86 static int exec_cmd(const char* path, const char** argv, const char** envp) { 87 int status; 88 pid_t child; 89 if ((child = fork()) == 0) { 90 execve(path, const_cast<char**>(argv), const_cast<char**>(envp)); 91 _exit(EXIT_FAILURE); 92 } 93 if (child < 0) { 94 fprintf(stderr, "%s failed with fork %s\n", path, strerror(errno)); 95 return -1; 96 } 97 if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) { 98 fprintf(stderr, "%s failed with waitpid %s\n", path, strerror(errno)); 99 return -1; 100 } 101 int ret = -1; 102 if (WIFEXITED(status)) { 103 ret = WEXITSTATUS(status); 104 } 105 106 if (ret != 0) { 107 fprintf(stderr, "%s failed with status %d\n", path, ret); 108 return -1; 109 } 110 return 0; 111 } 112 #endif 113 114 static int generate_ext4_image(const char* fileName, long long partSize, 115 const std::string& initial_dir, unsigned eraseBlkSize, 116 unsigned logicalBlkSize) { 117 static constexpr int block_size = 4096; 118 const std::string exec_dir = android::base::GetExecutableDirectory(); 119 120 const std::string mke2fs_path = exec_dir + "/mke2fs"; 121 std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), "-t", "ext4", "-b"}; 122 123 std::string block_size_str = std::to_string(block_size); 124 mke2fs_args.push_back(block_size_str.c_str()); 125 126 std::string ext_attr = "android_sparse"; 127 if (eraseBlkSize != 0 && logicalBlkSize != 0) { 128 int raid_stride = logicalBlkSize / block_size; 129 int raid_stripe_width = eraseBlkSize / block_size; 130 // stride should be the max of 8kb and logical block size 131 if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size; 132 // stripe width should be >= stride 133 if (raid_stripe_width < raid_stride) raid_stripe_width = raid_stride; 134 ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width); 135 } 136 mke2fs_args.push_back("-E"); 137 mke2fs_args.push_back(ext_attr.c_str()); 138 mke2fs_args.push_back("-O"); 139 mke2fs_args.push_back("uninit_bg"); 140 mke2fs_args.push_back(fileName); 141 142 std::string size_str = std::to_string(partSize / block_size); 143 mke2fs_args.push_back(size_str.c_str()); 144 mke2fs_args.push_back(nullptr); 145 146 const std::string mke2fs_env = "MKE2FS_CONFIG=" + GetExecutableDirectory() + "/mke2fs.conf"; 147 std::vector<const char*> mke2fs_envp = {mke2fs_env.c_str(), nullptr}; 148 149 int ret = exec_cmd(mke2fs_args[0], mke2fs_args.data(), mke2fs_envp.data()); 150 if (ret != 0) { 151 return -1; 152 } 153 154 if (initial_dir.empty()) { 155 return 0; 156 } 157 158 const std::string e2fsdroid_path = exec_dir + "/e2fsdroid"; 159 std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(), 160 fileName, nullptr}; 161 162 return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr); 163 } 164 165 static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir, 166 unsigned /* unused */, unsigned /* unused */) 167 { 168 const std::string exec_dir = android::base::GetExecutableDirectory(); 169 const std::string mkf2fs_path = exec_dir + "/make_f2fs"; 170 std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()}; 171 172 mkf2fs_args.push_back("-S"); 173 std::string size_str = std::to_string(partSize); 174 mkf2fs_args.push_back(size_str.c_str()); 175 mkf2fs_args.push_back("-f"); 176 mkf2fs_args.push_back("-O"); 177 mkf2fs_args.push_back("encrypt"); 178 mkf2fs_args.push_back("-O"); 179 mkf2fs_args.push_back("quota"); 180 mkf2fs_args.push_back("-O"); 181 mkf2fs_args.push_back("verity"); 182 mkf2fs_args.push_back(fileName); 183 mkf2fs_args.push_back(nullptr); 184 185 int ret = exec_cmd(mkf2fs_args[0], mkf2fs_args.data(), nullptr); 186 if (ret != 0) { 187 return -1; 188 } 189 190 if (initial_dir.empty()) { 191 return 0; 192 } 193 194 const std::string sload_path = exec_dir + "/sload_f2fs"; 195 std::vector<const char*> sload_args = {sload_path.c_str(), "-S", 196 "-f", initial_dir.c_str(), fileName, nullptr}; 197 198 return exec_cmd(sload_args[0], sload_args.data(), nullptr); 199 } 200 201 static const struct fs_generator { 202 const char* fs_type; //must match what fastboot reports for partition type 203 204 //returns 0 or error value 205 int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir, 206 unsigned eraseBlkSize, unsigned logicalBlkSize); 207 208 } generators[] = { 209 { "ext4", generate_ext4_image}, 210 { "f2fs", generate_f2fs_image}, 211 }; 212 213 const struct fs_generator* fs_get_generator(const std::string& fs_type) { 214 for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) { 215 if (fs_type == generators[i].fs_type) { 216 return generators + i; 217 } 218 } 219 return nullptr; 220 } 221 222 int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize, 223 const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize) 224 { 225 return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize); 226 } 227