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 
30 namespace Updater {
31 using Updater::Utils::SplitString;
32 static std::string g_defaultUpdaterFstab = "";
33 static Fstab *g_fstab = nullptr;
34 static const std::string PARTITION_PATH = "/dev/block/by-name";
35 
GetFstabFile()36 static 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
GetMountStatusForPath(const std::string &path)49 MountStatus 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 
LoadFstab()59 void 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 
LoadSpecificFstab(const std::string &fstabName)87 void LoadSpecificFstab(const std::string &fstabName)
88 {
89     g_defaultUpdaterFstab = fstabName;
90     LoadFstab();
91     g_defaultUpdaterFstab = "";
92 }
93 
UmountForPath(const std::string& path)94 int 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 
MountNtfsWithRetry(std::string source, std::string target)126 static 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 
MountSdcard(std::string &path, std::string &mountPoint)166 int 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 
MountForPath(const std::string &path)193 int 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 
ErasePartition(const std::string &devPath)221 void 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 
FormatPartition(const std::string &path, bool isZeroErase)254 int 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 
SetupPartitions(bool isMountData)293 int 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 
GetBlockDeviceByMountPoint(const std::string &mountPoint)333 const 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 
GetBlockDevicesByMountPoint(const std::string &mountPoint)352 const 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