1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "fs_manager/mount.h"
17#include <cerrno>
18#include <fcntl.h>
19#include <string>
20#include <sys/mount.h>
21#include <sys/stat.h>
22#include <sys/wait.h>
23#include <unistd.h>
24#include <vector>
25#include <linux/fs.h>
26#include "log/dump.h"
27#include "log/log.h"
28#include "utils.h"
29
30namespace Updater {
31using Updater::Utils::SplitString;
32static std::string g_defaultUpdaterFstab = "";
33static Fstab *g_fstab = nullptr;
34static const std::string PARTITION_PATH = "/dev/block/by-name";
35
36static std::string GetFstabFile()
37{
38    /* check vendor fstab files from specific directory */
39    std::vector<const std::string> specificFstabFiles = {"/vendor/etc/fstab.updater"};
40    for (auto& fstabFile : specificFstabFiles) {
41        if (access(fstabFile.c_str(), F_OK) == 0) {
42            return fstabFile;
43        }
44    }
45    return "";
46}
47
48#ifndef UPDATE_PATCH_SHARED
49MountStatus GetMountStatusForPath(const std::string &path)
50{
51    FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
52    if (item == nullptr) {
53        return MountStatus::MOUNT_ERROR;
54    }
55    return GetMountStatusForMountPoint(item->mountPoint);
56}
57#endif
58
59void LoadFstab()
60{
61    std::string fstabFile = g_defaultUpdaterFstab;
62    if (fstabFile.empty()) {
63        fstabFile = GetFstabFile();
64        if (fstabFile.empty()) {
65            fstabFile = "/etc/fstab.updater";
66        }
67    }
68    if (g_fstab != nullptr) {
69        ReleaseFstab(g_fstab);
70        g_fstab = nullptr;
71    }
72    // Clear fstab before read fstab file.
73    if ((g_fstab = ReadFstabFromFile(fstabFile.c_str(), false)) == nullptr) {
74        LOG(WARNING) << "Read " << fstabFile << " failed";
75        return;
76    }
77
78    LOG(DEBUG) << "Updater filesystem config info:";
79    for (FstabItem *item = g_fstab->head; item != nullptr; item = item->next) {
80        LOG(DEBUG) << "\tDevice: " << item->deviceName;
81        LOG(DEBUG) << "\tMount point : " << item->mountPoint;
82        LOG(DEBUG) << "\tFs type : " << item->fsType;
83        LOG(DEBUG) << "\tMount options: " << item->mountOptions;
84    }
85}
86
87void LoadSpecificFstab(const std::string &fstabName)
88{
89    g_defaultUpdaterFstab = fstabName;
90    LoadFstab();
91    g_defaultUpdaterFstab = "";
92}
93
94int UmountForPath(const std::string& path)
95{
96    if (g_fstab == nullptr) {
97        LOG(ERROR) << "fstab is not loaded, g_fstab is null.";
98        return -1;
99    }
100
101    FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
102    if (item == nullptr) {
103        LOG(ERROR) << "Cannot find fstab item for " << path << " to umount.";
104        return -1;
105    }
106
107    LOG(DEBUG) << "Umount for path " << path;
108    MountStatus rc = GetMountStatusForMountPoint(item->mountPoint);
109    if (rc == MOUNT_ERROR) {
110        return -1;
111    } else if (rc == MOUNT_UMOUNTED) {
112        return 0;
113    } else {
114        if (path == "/data") {
115            Utils::SetParameter("updater.data.ready", "0");
116        }
117        int ret = umount(item->mountPoint);
118        if (ret == -1) {
119            LOG(ERROR) << "Umount " << item->mountPoint << "failed: " << errno;
120            return -1;
121        }
122    }
123    return 0;
124}
125
126static int MountNtfsWithRetry(std::string source, std::string target)
127{
128    char *argv[] = {const_cast<char *>("system/bin/mount.ntfs"),
129        const_cast<char *>(source.c_str()), const_cast<char *>(target.c_str()), nullptr};
130    int num = 0;
131    do {
132        pid_t child = fork();
133        if (child == 0) {
134            if (execv(argv[0], argv)) {
135                _exit(-1);
136            }
137        }
138        int status = -1;
139        if (waitpid(child, &status, 0) < 0) {
140            LOG(ERROR) << "waitpid failed, " << child;
141        }
142        if (WIFEXITED(status)) {
143            LOG(ERROR) << "child terminated by exit " << WEXITSTATUS(status);
144        } else if (WIFSIGNALED(status)) {
145            LOG(ERROR) << "child terminated by signal " << WTERMSIG(status);
146        } else if (WIFSTOPPED(status)) {
147            LOG(ERROR) << "child stopped by signal " << WSTOPSIG(status);
148        }
149
150        if (status == 0) {
151            Utils::UsSleep(100); // 100 : Wait interval
152            LOG(INFO) << "success to mount " << source << " on " << target;
153            return 0;
154        } else {
155            if ((errno == ENOENT) || (errno == ENODEV) || (errno == ENOMEDIUM)) {
156                LOG(ERROR) << "SD card never insert, dont try again, failed to mount " << source << " on " << target;
157                return -1;
158            }
159        }
160        num++;
161        LOG(ERROR) << "failed to mount " << source << " on " << target << ", errno is " << errno;
162    } while (num < 3); // 3 : retry three times
163    return -1;
164}
165
166int MountSdcard(std::string &path, std::string &mountPoint)
167{
168    if (path.empty() || mountPoint.empty()) {
169        LOG(ERROR) << "path or mountPoint is null, mount fail";
170        return -1;
171    }
172    MountStatus rc = GetMountStatusForMountPoint(mountPoint.c_str());
173    if (rc == MountStatus::MOUNT_ERROR) {
174        return -1;
175    } else if (rc == MountStatus::MOUNT_MOUNTED) {
176        LOG(INFO) << path << " already mounted";
177        return 0;
178    }
179    const std::vector<const char *> fileSystemType = {"ext4", "vfat", "exfat"};
180    for (auto type : fileSystemType) {
181        if (mount(path.c_str(), mountPoint.c_str(), type, 0, nullptr) == 0) {
182            LOG(INFO) << "mount success, sdcard type is " << type;
183            return 0;
184        }
185    }
186    if (MountNtfsWithRetry(path, mountPoint) == 0) {
187        LOG(INFO) << "mount success, sdcard type is ntfs";
188        return 0;
189    }
190    return -1;
191}
192
193int MountForPath(const std::string &path)
194{
195    if (g_fstab == nullptr) {
196        LOG(ERROR) << "fstab is not loaded, g_fstab is null.";
197        return -1;
198    }
199
200    FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
201    int ret = -1;
202    if (item == nullptr) {
203        LOG(ERROR) << "Cannot find fstab item for " << path << " to mount.";
204        return -1;
205    }
206
207    LOG(DEBUG) << "Mount for path " << path;
208    MountStatus rc = GetMountStatusForMountPoint(item->mountPoint);
209    if (rc == MountStatus::MOUNT_ERROR) {
210        LOG(ERROR) << "GetMountStatusForMountPoint ret is MOUNT_ERROR";
211        ret = -1;
212    } else if (rc == MountStatus::MOUNT_MOUNTED) {
213        LOG(INFO) << path << " already mounted";
214        ret = 0;
215    } else {
216        ret = MountOneItem(item);
217    }
218    return ret;
219}
220
221void ErasePartition(const std::string &devPath)
222{
223    std::string realPath {};
224    if (!Utils::PathToRealPath(devPath, realPath)) {
225        LOG(ERROR) << "realpath failed:" << devPath;
226        return;
227    }
228    int fd = open(realPath.c_str(), O_RDWR | O_LARGEFILE);
229    if (fd == -1) {
230        LOG(ERROR) << "open failed:" << realPath;
231        return;
232    }
233
234    uint64_t size = 0;
235    int ret = ioctl(fd, BLKGETSIZE64, &size);
236    if (ret < 0) {
237        LOG(ERROR) << "get partition size failed:" << size;
238        close(fd);
239        return;
240    }
241
242    LOG(INFO) << "erase partition size:" << size;
243
244    uint64_t range[] { 0, size };
245    ret = ioctl(fd, BLKDISCARD, &range);
246    if (ret < 0) {
247        LOG(ERROR) << "erase partition failed";
248    }
249    close(fd);
250
251    return;
252}
253
254int FormatPartition(const std::string &path, bool isZeroErase)
255{
256    if (g_fstab == nullptr) {
257        LOG(ERROR) << "fstab is not loaded, g_fstab is null.";
258        return -1;
259    }
260
261    FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
262    if (item == nullptr) {
263        LOG(ERROR) << "Cannot find fstab item for " << path << " to format.";
264        return -1;
265    }
266
267    if (strcmp(item->mountPoint, "/") == 0) {
268        /* Can not format root */
269        return 0;
270    }
271
272    if (!IsSupportedFilesystem(item->fsType)) {
273        LOG(ERROR) << "Try to format " << item->mountPoint << " with unsupported file system type: " << item->fsType;
274        return -1;
275    }
276
277    // Umount first
278    if (UmountForPath(path) != 0) {
279        return -1;
280    }
281
282    if (isZeroErase) {
283        ErasePartition(item->deviceName);
284    }
285
286    int ret = DoFormat(item->deviceName, item->fsType);
287    if (ret != 0) {
288        LOG(ERROR) << "Format " << path << " failed";
289    }
290    return ((ret != 0) ? -1 : 0);
291}
292
293int SetupPartitions(bool isMountData)
294{
295    UPDATER_INIT_RECORD;
296    if (!Utils::IsUpdaterMode()) {
297        LOG(ERROR) << "live update mode";
298        return 0;
299    }
300
301    if (g_fstab == NULL || g_fstab->head == NULL) {
302        LOG(ERROR) << "Fstab is invalid";
303        UPDATER_LAST_WORD(-1);
304        return -1;
305    }
306    for (const FstabItem *item = g_fstab->head; item != nullptr; item = item->next) {
307        std::string mountPoint(item->mountPoint);
308        std::string fsType(item->fsType);
309        if (mountPoint == "/" || mountPoint == "/tmp" || fsType == "none" ||
310            mountPoint == "/sdcard") {
311            continue;
312        }
313
314        if (mountPoint == "/data" && isMountData) {
315            if (MountForPath(mountPoint) != 0) {
316                LOG(ERROR) << "Expected partition " << mountPoint << " is not mounted.";
317                UPDATER_LAST_WORD(-1);
318                return -1;
319            }
320            Utils::SetParameter("updater.data.ready", "1");
321            LOG(INFO) << "mount data not fail";
322            continue;
323        }
324        if (UmountForPath(mountPoint) != 0) {
325            LOG(ERROR) << "Umount " << mountPoint << " failed";
326            UPDATER_LAST_WORD(-1);
327            return -1;
328        }
329    }
330    return 0;
331}
332
333const std::string GetBlockDeviceByMountPoint(const std::string &mountPoint)
334{
335    if (mountPoint.empty()) {
336        LOG(ERROR) << "mountPoint empty error.";
337        return "";
338    }
339    std::string blockDevice = PARTITION_PATH + mountPoint;
340    if (mountPoint[0] != '/') {
341        blockDevice = PARTITION_PATH + "/" + mountPoint;
342    }
343    if (g_fstab != nullptr) {
344        FstabItem *item = FindFstabItemForMountPoint(*g_fstab, mountPoint.c_str());
345        if (item != NULL) {
346            blockDevice = item->deviceName;
347        }
348    }
349    return blockDevice;
350}
351
352const std::vector<std::string> GetBlockDevicesByMountPoint(const std::string &mountPoint)
353{
354    std::vector<std::string> blockDevices;
355    if (mountPoint.empty() || g_fstab == nullptr) {
356        LOG(ERROR) << "mountPoint or g_fstab empty error.";
357        return blockDevices;
358    }
359    for (FstabItem *item = g_fstab->head; item != NULL; item = item->next) {
360        if ((item->mountPoint != NULL) && item->mountPoint == mountPoint) {
361            blockDevices.push_back(item->deviceName);
362        }
363    }
364
365    if (blockDevices.empty()) {
366        std::string blockDevice = PARTITION_PATH + mountPoint;
367        if (mountPoint[0] != '/') {
368            blockDevice = PARTITION_PATH + "/" + mountPoint;
369        }
370        blockDevices.push_back(blockDevice);
371    }
372    return blockDevices;
373}
374} // updater
375