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