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