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