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