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