fs.cpp revision 0c04f782865e493945aee466c4fff4e73c36a659
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 26using android::base::GetExecutableDirectory; 27using android::base::StringPrintf; 28using android::base::unique_fd; 29 30#ifdef WIN32 31static int exec_e2fs_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 return exit_code != 0; 80} 81#else 82static int exec_e2fs_cmd(const char* path, const char** argv, const char** envp) { 83 int status; 84 pid_t child; 85 if ((child = fork()) == 0) { 86 execve(path, const_cast<char**>(argv), const_cast<char**>(envp)); 87 _exit(EXIT_FAILURE); 88 } 89 if (child < 0) { 90 fprintf(stderr, "%s failed with fork %s\n", path, strerror(errno)); 91 return -1; 92 } 93 if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) { 94 fprintf(stderr, "%s failed with waitpid %s\n", path, strerror(errno)); 95 return -1; 96 } 97 int ret = -1; 98 if (WIFEXITED(status)) { 99 ret = WEXITSTATUS(status); 100 if (ret != 0) { 101 fprintf(stderr, "%s failed with status %d\n", path, ret); 102 } 103 } 104 return ret; 105} 106#endif 107 108static int generate_ext4_image(const char* fileName, long long partSize, 109 const std::string& initial_dir, unsigned eraseBlkSize, 110 unsigned logicalBlkSize) { 111 static constexpr int block_size = 4096; 112 const std::string exec_dir = android::base::GetExecutableDirectory(); 113 114 const std::string mke2fs_path = exec_dir + "/mke2fs"; 115 std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), "-t", "ext4", "-b"}; 116 117 std::string block_size_str = std::to_string(block_size); 118 mke2fs_args.push_back(block_size_str.c_str()); 119 120 std::string ext_attr = "android_sparse"; 121 if (eraseBlkSize != 0 && logicalBlkSize != 0) { 122 int raid_stride = logicalBlkSize / block_size; 123 int raid_stripe_width = eraseBlkSize / block_size; 124 // stride should be the max of 8kb and logical block size 125 if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size; 126 // stripe width should be >= stride 127 if (raid_stripe_width < raid_stride) raid_stripe_width = raid_stride; 128 ext_attr += StringPrintf(",stride=%d,stripe-width=%d", raid_stride, raid_stripe_width); 129 } 130 mke2fs_args.push_back("-E"); 131 mke2fs_args.push_back(ext_attr.c_str()); 132 mke2fs_args.push_back("-O"); 133 mke2fs_args.push_back("uninit_bg"); 134 mke2fs_args.push_back(fileName); 135 136 std::string size_str = std::to_string(partSize / block_size); 137 mke2fs_args.push_back(size_str.c_str()); 138 mke2fs_args.push_back(nullptr); 139 140 const std::string mke2fs_env = "MKE2FS_CONFIG=" + GetExecutableDirectory() + "/mke2fs.conf"; 141 std::vector<const char*> mke2fs_envp = {mke2fs_env.c_str(), nullptr}; 142 143 int ret = exec_e2fs_cmd(mke2fs_args[0], mke2fs_args.data(), mke2fs_envp.data()); 144 if (ret != 0) { 145 fprintf(stderr, "mke2fs failed: %d\n", ret); 146 return -1; 147 } 148 149 if (initial_dir.empty()) { 150 return 0; 151 } 152 153 const std::string e2fsdroid_path = exec_dir + "/e2fsdroid"; 154 std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(), 155 fileName, nullptr}; 156 157 ret = exec_e2fs_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr); 158 if (ret != 0) { 159 fprintf(stderr, "e2fsdroid failed: %d\n", ret); 160 return -1; 161 } 162 163 return 0; 164} 165 166static int generate_f2fs_image(const char* fileName, long long partSize, const std::string& initial_dir, 167 unsigned /* unused */, unsigned /* unused */) 168{ 169#ifndef WIN32 170 const std::string exec_dir = android::base::GetExecutableDirectory(); 171 const std::string mkf2fs_path = exec_dir + "/make_f2fs"; 172 std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()}; 173 174 mkf2fs_args.push_back("-S"); 175 std::string size_str = std::to_string(partSize); 176 mkf2fs_args.push_back(size_str.c_str()); 177 mkf2fs_args.push_back("-f"); 178 mkf2fs_args.push_back("-O"); 179 mkf2fs_args.push_back("encrypt"); 180 mkf2fs_args.push_back("-O"); 181 mkf2fs_args.push_back("quota"); 182 mkf2fs_args.push_back(fileName); 183 mkf2fs_args.push_back(nullptr); 184 185 int ret = exec_e2fs_cmd(mkf2fs_args[0], mkf2fs_args.data(), nullptr); 186 if (ret != 0) { 187 fprintf(stderr, "mkf2fs failed: %d\n", ret); 188 return -1; 189 } 190 191 if (!initial_dir.empty()) { 192 fprintf(stderr, "sload.f2s not supported yet\n"); 193 return 0; 194 } 195 return 0; 196#else 197 UNUSED(fileName, partSize, initial_dir); 198 fprintf(stderr, "make_f2fs not supported on Windows\n"); 199 return -1; 200#endif 201} 202 203static const struct fs_generator { 204 const char* fs_type; //must match what fastboot reports for partition type 205 206 //returns 0 or error value 207 int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir, 208 unsigned eraseBlkSize, unsigned logicalBlkSize); 209 210} generators[] = { 211 { "ext4", generate_ext4_image}, 212 { "f2fs", generate_f2fs_image}, 213}; 214 215const struct fs_generator* fs_get_generator(const std::string& fs_type) { 216 for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) { 217 if (fs_type == generators[i].fs_type) { 218 return generators + i; 219 } 220 } 221 return nullptr; 222} 223 224int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize, 225 const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize) 226{ 227 return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize); 228} 229