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 
GetMountStatusForPath(const std::string & path)48 MountStatus GetMountStatusForPath(const std::string &path)
49 {
50     FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
51     if (item == nullptr) {
52         return MountStatus::MOUNT_ERROR;
53     }
54     return GetMountStatusForMountPoint(item->mountPoint);
55 }
56 
LoadFstab()57 void LoadFstab()
58 {
59     std::string fstabFile = g_defaultUpdaterFstab;
60     if (fstabFile.empty()) {
61         fstabFile = GetFstabFile();
62         if (fstabFile.empty()) {
63             fstabFile = "/etc/fstab.updater";
64         }
65     }
66     if (g_fstab != nullptr) {
67         ReleaseFstab(g_fstab);
68         g_fstab = nullptr;
69     }
70     // Clear fstab before read fstab file.
71     if ((g_fstab = ReadFstabFromFile(fstabFile.c_str(), false)) == nullptr) {
72         LOG(WARNING) << "Read " << fstabFile << " failed";
73         return;
74     }
75 
76     LOG(DEBUG) << "Updater filesystem config info:";
77     for (FstabItem *item = g_fstab->head; item != nullptr; item = item->next) {
78         LOG(DEBUG) << "\tDevice: " << item->deviceName;
79         LOG(DEBUG) << "\tMount point : " << item->mountPoint;
80         LOG(DEBUG) << "\tFs type : " << item->fsType;
81         LOG(DEBUG) << "\tMount options: " << item->mountOptions;
82     }
83 }
84 
LoadSpecificFstab(const std::string & fstabName)85 void LoadSpecificFstab(const std::string &fstabName)
86 {
87     g_defaultUpdaterFstab = fstabName;
88     LoadFstab();
89     g_defaultUpdaterFstab = "";
90 }
91 
UmountForPath(const std::string & path)92 int UmountForPath(const std::string& path)
93 {
94     if (g_fstab == nullptr) {
95         LOG(ERROR) << "fstab is not loaded, g_fstab is null.";
96         return -1;
97     }
98 
99     FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
100     if (item == nullptr) {
101         LOG(ERROR) << "Cannot find fstab item for " << path << " to umount.";
102         return -1;
103     }
104 
105     LOG(DEBUG) << "Umount for path " << path;
106     MountStatus rc = GetMountStatusForMountPoint(item->mountPoint);
107     if (rc == MOUNT_ERROR) {
108         return -1;
109     } else if (rc == MOUNT_UMOUNTED) {
110         return 0;
111     } else {
112         if (path == "/data") {
113             Utils::SetParameter("updater.data.ready", "0");
114         }
115         int ret = umount(item->mountPoint);
116         if (ret == -1) {
117             LOG(ERROR) << "Umount " << item->mountPoint << "failed: " << errno;
118             return -1;
119         }
120     }
121     return 0;
122 }
LoopToMount(char * argv[],std::string source,std::string target)123 static int LoopToMount(char *argv[], std::string source, std::string target)
124 {
125     int num = 0;
126     do {
127         pid_t child = fork();
128         if (child == 0) {
129             if (execv(argv[0], argv)) {
130                 _exit(-1);
131             }
132         }
133         int status = -1;
134         if (waitpid(child, &status, 0) < 0) {
135             LOG(ERROR) << "waitpid failed, " << child;
136         }
137         if (WIFEXITED(status)) {
138             LOG(ERROR) << "child terminated by exit " << WEXITSTATUS(status);
139         } else if (WIFSIGNALED(status)) {
140             LOG(ERROR) << "child terminated by signal " << WTERMSIG(status);
141         } else if (WIFSTOPPED(status)) {
142             LOG(ERROR) << "child stopped by signal " << WSTOPSIG(status);
143         }
144 
145         if (status == 0) {
146             Utils::UsSleep(100); // 100 : Wait interval
147             LOG(INFO) << "success to mount " << source << " on " << target;
148             return 0;
149         } else {
150             if ((errno == ENOENT) || (errno == ENODEV) || (errno == ENOMEDIUM)) {
151                 LOG(ERROR) << "SD card never insert, dont try again, failed to mount " << source << " on " << target;
152                 return -1;
153             }
154         }
155         num++;
156         LOG(ERROR) << "failed to mount " << source << " on " << target << ", errno is " << errno;
157     } while (num < 3); // 3 : retry three times
158     return -1;
159 }
160 
MountNtfsWithRetry(std::string source,std::string target)161 static int MountNtfsWithRetry(std::string source, std::string target)
162 {
163     char *argv[] = {const_cast<char *>("system/bin/mount.ntfs"),
164         const_cast<char *>(source.c_str()), const_cast<char *>(target.c_str()), nullptr};
165     return LoopToMount(argv, source, target);
166 }
167 
MountExfatWithRetry(std::string source,std::string target)168 static int MountExfatWithRetry(std::string source, std::string target)
169 {
170     char *argv[] = {const_cast<char *>("system/bin/mount.exfat"),
171         const_cast<char *>(source.c_str()), const_cast<char *>(target.c_str()), nullptr};
172     return LoopToMount(argv, source, target);
173 }
174 
MountSdcard(std::string & path,std::string & mountPoint)175 int MountSdcard(std::string &path, std::string &mountPoint)
176 {
177     if (path.empty() || mountPoint.empty()) {
178         LOG(ERROR) << "path or mountPoint is null, mount fail";
179         return -1;
180     }
181     MountStatus rc = GetMountStatusForMountPoint(mountPoint.c_str());
182     if (rc == MountStatus::MOUNT_ERROR) {
183         return -1;
184     } else if (rc == MountStatus::MOUNT_MOUNTED) {
185         LOG(INFO) << path << " already mounted";
186         return 0;
187     }
188     const std::vector<const char *> fileSystemType = {"ext4", "vfat", "exfat"};
189     for (auto type : fileSystemType) {
190         if (mount(path.c_str(), mountPoint.c_str(), type, 0, nullptr) == 0) {
191             LOG(INFO) << "mount success, sdcard type is " << type;
192             return 0;
193         }
194     }
195     if (MountNtfsWithRetry(path, mountPoint) == 0) {
196         LOG(INFO) << "mount success, sdcard type is ntfs";
197         return 0;
198     }
199     if (MountExfatWithRetry(path, mountPoint) == 0) {
200         LOG(INFO) << "mount success, sdcard type is exfat";
201         return 0;
202     }
203     return -1;
204 }
205 
MountForPath(const std::string & path)206 int MountForPath(const std::string &path)
207 {
208     if (g_fstab == nullptr) {
209         LOG(ERROR) << "fstab is not loaded, g_fstab is null.";
210         return -1;
211     }
212 
213     FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
214     int ret = -1;
215     if (item == nullptr) {
216         LOG(ERROR) << "Cannot find fstab item for " << path << " to mount.";
217         return -1;
218     }
219 
220     LOG(DEBUG) << "Mount for path " << path;
221     MountStatus rc = GetMountStatusForMountPoint(item->mountPoint);
222     if (rc == MountStatus::MOUNT_ERROR) {
223         LOG(ERROR) << "GetMountStatusForMountPoint ret is MOUNT_ERROR";
224         ret = -1;
225     } else if (rc == MountStatus::MOUNT_MOUNTED) {
226         LOG(INFO) << path << " already mounted";
227         ret = 0;
228     } else {
229         ret = MountOneItem(item);
230     }
231     return ret;
232 }
233 
ErasePartition(const std::string & devPath)234 void ErasePartition(const std::string &devPath)
235 {
236     std::string realPath {};
237     if (!Utils::PathToRealPath(devPath, realPath)) {
238         LOG(ERROR) << "realpath failed:" << devPath;
239         return;
240     }
241     int fd = open(realPath.c_str(), O_RDWR | O_LARGEFILE);
242     if (fd == -1) {
243         LOG(ERROR) << "open failed:" << realPath;
244         return;
245     }
246 
247     uint64_t size = 0;
248     int ret = ioctl(fd, BLKGETSIZE64, &size);
249     if (ret < 0) {
250         LOG(ERROR) << "get partition size failed:" << size;
251         close(fd);
252         return;
253     }
254 
255     LOG(INFO) << "erase partition size:" << size;
256 
257     uint64_t range[] { 0, size };
258     ret = ioctl(fd, BLKDISCARD, &range);
259     if (ret < 0) {
260         LOG(ERROR) << "erase partition failed";
261     }
262     close(fd);
263 
264     return;
265 }
266 
FormatPartition(const std::string & path,bool isZeroErase)267 int FormatPartition(const std::string &path, bool isZeroErase)
268 {
269     if (g_fstab == nullptr) {
270         LOG(ERROR) << "fstab is not loaded, g_fstab is null.";
271         return -1;
272     }
273 
274     FstabItem *item = FindFstabItemForPath(*g_fstab, path.c_str());
275     if (item == nullptr) {
276         LOG(ERROR) << "Cannot find fstab item for " << path << " to format.";
277         return -1;
278     }
279 
280     if (strcmp(item->mountPoint, "/") == 0) {
281         /* Can not format root */
282         return 0;
283     }
284 
285     if (!IsSupportedFilesystem(item->fsType)) {
286         LOG(ERROR) << "Try to format " << item->mountPoint << " with unsupported file system type: " << item->fsType;
287         return -1;
288     }
289 
290     // Umount first
291     if (UmountForPath(path) != 0) {
292         return -1;
293     }
294 
295     if (isZeroErase) {
296         ErasePartition(item->deviceName);
297     }
298 
299     int ret = DoFormat(item->deviceName, item->fsType);
300     if (ret != 0) {
301         LOG(ERROR) << "Format " << path << " failed";
302     }
303     return ((ret != 0) ? -1 : 0);
304 }
305 
SetupPartitions(bool isMountData)306 int SetupPartitions(bool isMountData)
307 {
308     UPDATER_INIT_RECORD;
309     if (!Utils::IsUpdaterMode()) {
310         LOG(ERROR) << "live update mode";
311         return 0;
312     }
313 
314     if (g_fstab == NULL || g_fstab->head == NULL) {
315         LOG(ERROR) << "Fstab is invalid";
316         UPDATER_LAST_WORD(-1);
317         return -1;
318     }
319     for (const FstabItem *item = g_fstab->head; item != nullptr; item = item->next) {
320         std::string mountPoint(item->mountPoint);
321         std::string fsType(item->fsType);
322         if (mountPoint == "/" || mountPoint == "/tmp" || fsType == "none" ||
323             mountPoint == "/sdcard") {
324             continue;
325         }
326 
327         if (mountPoint == "/data" && isMountData) {
328             if (MountForPath(mountPoint) != 0) {
329                 LOG(ERROR) << "Expected partition " << mountPoint << " is not mounted.";
330                 UPDATER_LAST_WORD(-1);
331                 return -1;
332             }
333             Utils::SetParameter("updater.data.ready", "1");
334             LOG(INFO) << "mount data not fail";
335             continue;
336         }
337         if (UmountForPath(mountPoint) != 0) {
338             LOG(ERROR) << "Umount " << mountPoint << " failed";
339             UPDATER_LAST_WORD(-1);
340             return -1;
341         }
342     }
343     return 0;
344 }
345 
GetBlockDeviceByMountPoint(const std::string & mountPoint)346 const std::string GetBlockDeviceByMountPoint(const std::string &mountPoint)
347 {
348     if (mountPoint.empty()) {
349         LOG(ERROR) << "mountPoint empty error.";
350         return "";
351     }
352     std::string blockDevice = PARTITION_PATH + mountPoint;
353     if (mountPoint[0] != '/') {
354         blockDevice = PARTITION_PATH + "/" + mountPoint;
355     }
356     if (g_fstab != nullptr) {
357         FstabItem *item = FindFstabItemForMountPoint(*g_fstab, mountPoint.c_str());
358         if (item != NULL) {
359             blockDevice = item->deviceName;
360         }
361     }
362     return blockDevice;
363 }
364 
GetBlockDevicesByMountPoint(const std::string & mountPoint)365 const std::vector<std::string> GetBlockDevicesByMountPoint(const std::string &mountPoint)
366 {
367     std::vector<std::string> blockDevices;
368     if (mountPoint.empty() || g_fstab == nullptr) {
369         LOG(ERROR) << "mountPoint or g_fstab empty error.";
370         return blockDevices;
371     }
372     for (FstabItem *item = g_fstab->head; item != NULL; item = item->next) {
373         if ((item->mountPoint != NULL) && item->mountPoint == mountPoint) {
374             blockDevices.push_back(item->deviceName);
375         }
376     }
377 
378     if (blockDevices.empty()) {
379         std::string blockDevice = PARTITION_PATH + mountPoint;
380         if (mountPoint[0] != '/') {
381             blockDevice = PARTITION_PATH + "/" + mountPoint;
382         }
383         blockDevices.push_back(blockDevice);
384     }
385     return blockDevices;
386 }
387 } // updater
388