• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "partition.h"
16 
17 #include <cstdio>
18 #include <fcntl.h>
19 #include <map>
20 #include <sys/ioctl.h>
21 #include <sys/mount.h>
22 #include <sys/stat.h>
23 #include <sys/statfs.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <vector>
27 #include <linux/fs.h>
28 #include "flash_service.h"
29 #include "utils.h"
30 
31 namespace flashd {
~Partition()32 Partition::~Partition()
33 {
34     if (fd_ != -1) {
35         close(fd_);
36     }
37 }
38 
Load()39 int Partition::Load()
40 {
41     std::vector<std::string> table;
42     std::string info = ReadPartitionSysInfo(devName_, "partition", table);
43     FLASHING_CHECK(!info.empty(), return -1, "Can not get partition for %s", devName_.c_str());
44     partNumber_ = atoi(info.c_str());
45 
46     ReadPartitionSysInfo(devName_, "uevent", table);
47     info = FlashService::GetParamFromTable(table, "DEVNAME=");
48     FLASHING_CHECK(info == devName_, return -1, "Failed to check dev name %s %s", info.c_str(), devName_.c_str());
49 
50     partName_ = FlashService::GetParamFromTable(table, "PARTNAME=");
51     if (partName_.empty()) {
52         partName_ = devName_;
53     }
54     int ret = Partition::GetMountInfo();
55     FLASHING_LOGI("Partition info devName \"%s\" partPath_ \"%s\" partName_ \"%s\"",
56         devName_.c_str(), partPath_.c_str(), partName_.c_str());
57     if (!mountPoint_.empty()) {
58         FLASHING_LOGI("Partition mount info mount \"%s\" fs %s flags: %s",
59             mountPoint_.c_str(), fsType_.c_str(), mountFlags_.c_str());
60     }
61     return ret;
62 }
63 
DoFlash(const std::string & fileName)64 int Partition::DoFlash(const std::string &fileName)
65 {
66     int ret = Open();
67     FLASHING_CHECK(ret == 0, return ret, "Can not open partiton %s for flash", partName_.c_str());
68 
69     auto inputFd = open(fileName.c_str(), O_RDONLY);
70     FLASHING_CHECK(inputFd > 0,
71         flash_->RecordMsg(updater::ERROR, "Can not open image \"%s\" error: %s", fileName.c_str(), strerror(errno));
72         return FLASHING_NOPERMISSION, "Can not open image \"%s\" error: %s", fileName.c_str(), strerror(errno));
73 
74     struct stat st {};
75     size_t fileSize = 1;
76     if (fstat(inputFd, &st) >= 0) {
77         fileSize = st.st_size;
78     }
79 
80     std::vector<uint8_t> content(BUFFER_SIZE);
81     ssize_t readLen = read(inputFd, content.data(), content.size());
82     FLASHING_CHECK(readLen >= 0, close(inputFd);
83         return FLASHING_PART_WRITE_ERROR, "Failed to flash data of len %d", readLen);
84     ret = WriteRowData(inputFd, fileSize, content, readLen);
85     close(inputFd);
86     FLASHING_CHECK(ret == 0, return ret, "Failed to write %s", fileName.c_str());
87     FLASHING_LOGI("DoFlash partition %s image:%s success", partPath_.c_str(), fileName.c_str());
88     return ret;
89 }
90 
DoErase()91 int Partition::DoErase()
92 {
93     int ret = Open();
94     FLASHING_CHECK(ret == 0, return ret, "Can not open partiton %s for erase", partName_.c_str());
95 
96     if (!IsBlockDevice(fd_)) {
97         return 0;
98     }
99 
100     uint64_t size = GetBlockDeviceSize(fd_);
101     FLASHING_LOGI("DoErase partition %s size %luM", partPath_.c_str(), size);
102     uint64_t range[2] = {0};
103     range[1] = size;
104     ret = ioctl(fd_, BLKSECDISCARD, &range);
105     FLASHING_LOGI("DoErase partition %s ret %d", partPath_.c_str(), ret);
106     if (ret < 0) {
107         range[0] = 0;
108         range[1] = size;
109 #ifndef UPDATER_UT
110         ret = ioctl(fd_, BLKDISCARD, &range);
111 #endif
112         FLASHING_CHECK(ret >= 0,
113             flash_->RecordMsg(updater::ERROR, "Failed to erase \"%s\" error: %s", partName_.c_str(), strerror(errno));
114             return ret, "Failed to erase %s error: %s", partName_.c_str(), strerror(errno));
115         std::vector<char> buffer(BLOCK_SIZE, 0);
116 #ifndef UPDATER_UT
117         ret = updater::utils::WriteFully(fd_, buffer.data(), buffer.size());
118 #endif
119         FLASHING_CHECK(ret == 0, return FLASHING_PART_WRITE_ERROR, "Failed to flash data errno %d", errno);
120         fsync(fd_);
121     }
122     FLASHING_LOGI("DoErase partition %s erase success", partPath_.c_str());
123     return 0;
124 }
125 
DoFormat(const std::string & fsType)126 int Partition::DoFormat(const std::string &fsType)
127 {
128     int ret = DoUmount();
129     FLASHING_CHECK(ret == 0, return FLASHING_PART_WRITE_ERROR, "Failed to umount partition");
130     FLASHING_LOGI("DoFormat partition %s format %s", partName_.c_str(), fsType_.c_str());
131 
132     std::vector<std::string> formatCmds {};
133     ret = BuildCommandParam(fsType, formatCmds);
134     FLASHING_CHECK(ret == 0, return FLASHING_PART_WRITE_ERROR, "Failed to get param");
135     ret = FlashService::ExecCommand(formatCmds);
136     FLASHING_CHECK(ret == 0,
137         flash_->RecordMsg(updater::ERROR, "Failed to format \"%s\" error: %s", partName_.c_str(), strerror(ret));
138         return FLASHING_PART_WRITE_ERROR, "Failed to format \"%s\" error: %s", partPath_.c_str(), strerror(ret));
139 
140     fsType_ = fsType;
141     ret = DoMount();
142     FLASHING_CHECK(ret == 0,
143         flash_->RecordMsg(updater::ERROR, "Failed to mount \"%s\" error: %s", partName_.c_str(), strerror(ret));
144         return FLASHING_PART_WRITE_ERROR, "Failed to mount \"%s\"", partPath_.c_str());
145     FLASHING_LOGI("DoFormat partition %s format %s success", partName_.c_str(), fsType_.c_str());
146     return ret;
147 }
148 
DoResize(uint32_t blocks)149 int Partition::DoResize(uint32_t blocks)
150 {
151     int ret = 0;
152     bool needResize = false;
153     if (!mountPoint_.empty()) {
154         needResize = FlashService::CheckFreeSpace(mountPoint_, blocks);
155     }
156     if (!needResize) {
157         FLASHING_LOGI("No need to resize partition %s", partName_.c_str());
158         return 0;
159     }
160     ret = DoUmount();
161     FLASHING_CHECK(ret == 0, return FLASHING_PART_WRITE_ERROR, "Failed to umount partition");
162 
163     std::vector<std::string> formatCmds;
164     formatCmds.push_back(RESIZE_TOOL);
165     formatCmds.push_back(partPath_);
166     ret = FlashService::ExecCommand(formatCmds);
167     FLASHING_CHECK(ret == 0,
168         flash_->RecordMsg(updater::ERROR, "Failed to resize \"%s\" error: %s", partName_.c_str(), strerror(ret));
169         return FLASHING_PART_WRITE_ERROR, "Failed to resize \"%s\" error: %s", partName_.c_str(), strerror(ret));
170     ret = DoMount();
171     FLASHING_CHECK(ret == 0,
172         flash_->RecordMsg(updater::ERROR, "Failed to mount \"%s\" error: %s", partName_.c_str(), strerror(ret));
173         return FLASHING_PART_WRITE_ERROR, "Failed to mount \"%s\"", partPath_.c_str());
174     FLASHING_LOGI("Resize partition %s success", partName_.c_str());
175     return 0;
176 }
177 
Open()178 int Partition::Open()
179 {
180     if (fd_ <= 0) {
181         fd_ = open(partPath_.c_str(), O_RDWR);
182     }
183     FLASHING_CHECK(fd_ > 0,
184         flash_->RecordMsg(updater::ERROR,
185         "Can not open partiton \"%s\" error: %s", partName_.c_str(), strerror(errno));
186         return FLASHING_NOPERMISSION,
187         "Can open partition %s error %s", partPath_.c_str(), strerror(errno));
188     return 0;
189 }
190 
WriteRowData(int inputFd,size_t fileSize,std::vector<uint8_t> & buffer,size_t dataSize)191 int Partition::WriteRowData(int inputFd, size_t fileSize, std::vector<uint8_t> &buffer, size_t dataSize)
192 {
193     size_t dataLen = dataSize;
194     size_t totalWrite = 0;
195     do {
196 #ifndef UPDATER_UT
197         ssize_t writeLen = write(fd_, buffer.data(), dataLen);
198 #else
199         ssize_t writeLen = dataLen;
200 #endif
201         FLASHING_CHECK(writeLen >= 0,
202             return FLASHING_PART_WRITE_ERROR, "Failed to write data of len %d", dataLen);
203         totalWrite += writeLen;
204 
205         // continue read and write
206         ssize_t ret = read(inputFd, buffer.data(), dataSize);
207         FLASHING_CHECK(ret > 0, return -1, "Failed to read data %d %d", errno, buffer.size());
208         flash_->PostProgress(UPDATEMOD_FLASH, writeLen, nullptr);
209         dataLen = ret;
210     } while (1);
211     fsync(fd_);
212     return 0;
213 }
214 
IsBlockDevice(int fd) const215 int Partition::IsBlockDevice(int fd) const
216 {
217     struct stat st {};
218     int ret = fstat(fd, &st);
219     FLASHING_CHECK(ret >= 0, return 0, "Invalid get fstate %d", errno);
220     return S_ISBLK(st.st_mode);
221 }
222 
GetBlockDeviceSize(int fd) const223 uint64_t Partition::GetBlockDeviceSize(int fd) const
224 {
225     uint64_t size = 0;
226     int ret = ioctl(fd, BLKGETSIZE64, &size);
227     return (ret == 0) ? size : 0;
228 }
229 
ReadPartitionSysInfo(const std::string & partition,const std::string & type,std::vector<std::string> & table)230 std::string Partition::ReadPartitionSysInfo(const std::string &partition,
231     const std::string &type, std::vector<std::string> &table)
232 {
233     std::vector<char> buffer(DEVICE_PATH_SIZE, 0);
234     int ret = snprintf_s(buffer.data(), DEVICE_PATH_SIZE, DEVICE_PATH_SIZE - 1,
235         "/sys/block/%s/%s/%s", device_->GetDeviceName().c_str(), partition.c_str(), type.c_str());
236     FLASHING_CHECK(ret != -1, return "", "Failed to snprintf_s %s", device_->GetDeviceName().c_str());
237     return FlashService::ReadSysInfo(buffer.data(), type, table);
238 }
239 
GetPartitionName() const240 const std::string Partition::GetPartitionName() const
241 {
242     return partName_;
243 }
244 
GetMountInfo()245 int Partition::GetMountInfo()
246 {
247     auto file = std::unique_ptr<FILE, decltype(&fclose)>(fopen("/proc/mounts", "r"), fclose);
248     FLASHING_CHECK(file != nullptr, return -1, "Failed to open mounts ");
249     std::vector<char> buffer(LINE_BUFFER_SIZE, 0);
250     std::vector<char> mount(DEVICE_PATH_SIZE, 0);
251     std::vector<char> fsType(DEVICE_PATH_SIZE, 0);
252     std::vector<char> dev(DEVICE_PATH_SIZE, 0);
253     std::vector<char> flags(DEVICE_PATH_SIZE, 0);
254     while (fgets(buffer.data(), LINE_BUFFER_SIZE, file.get()) != nullptr) {
255         // clang-format off
256         int ret = sscanf_s(buffer.data(), "%255s %255s %255s %255s %*d %*d\n",
257             dev.data(), DEVICE_PATH_SIZE - 1,
258             mount.data(), DEVICE_PATH_SIZE - 1,
259             fsType.data(), DEVICE_PATH_SIZE - 1,
260             flags.data(), DEVICE_PATH_SIZE - 1);
261         // clang-format on
262         if (ret <= 0) {
263             break;
264         }
265         struct stat st {};
266         if (lstat(dev.data(), &st) < 0) {
267             continue;
268         }
269         if (S_ISLNK(st.st_mode)) {
270             readlink(dev.data(), dev.data(), DEVICE_PATH_SIZE);
271         }
272         if (strncmp(dev.data(), partPath_.c_str(), partPath_.size()) == 0 ||
273             (partName_ == "system" && strncmp(mount.data(), "/", strlen("/")) == 0)) {
274             mountPoint_.assign(mount.data());
275             fsType_.assign(fsType.data());
276             mountFlags_.assign(flags.data());
277             break;
278         }
279         memset_s(buffer.data(), LINE_BUFFER_SIZE, 0, LINE_BUFFER_SIZE);
280     }
281     return 0;
282 }
283 
DoUmount()284 int Partition::DoUmount()
285 {
286     if (mountPoint_.empty()) {
287         return 0;
288     }
289 #ifndef UPDATER_UT
290     int ret = umount2(mountPoint_.c_str(), MNT_FORCE);
291     FLASHING_CHECK(ret == 0,
292         flash_->RecordMsg(updater::ERROR, "Failed to umount \"%s\" error: %s", partName_.c_str(), strerror(errno));
293         return FLASHING_PART_WRITE_ERROR, "Failed to umount \"%s\" error: %s", partName_.c_str(), strerror(errno));
294 #endif
295     return 0;
296 }
297 
DoMount()298 int Partition::DoMount()
299 {
300     if (mountPoint_.empty()) {
301         return 0;
302     }
303     struct stat st {};
304     int ret = lstat(mountPoint_.c_str(), &st);
305     FLASHING_CHECK(ret >= 0, return ret, "Failed to fstat %s error %s", mountPoint_.c_str(), strerror(errno));
306 
307     if (S_ISLNK(st.st_mode)) {
308         unlink(mountPoint_.c_str());
309     }
310     mkdir(mountPoint_.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
311 
312     // get mount flags
313     std::string data;
314     uint32_t flags = GetMountFlags(mountFlags_, data);
315     errno = 0;
316 
317     while ((ret = mount(partPath_.c_str(), mountPoint_.c_str(), fsType_.c_str(), flags, data.c_str()) != 0)) {
318 #ifdef UPDATER_UT
319         ret = 0;
320 #endif
321         if (errno == EAGAIN) {
322             continue;
323         } else {
324             break;
325         }
326     }
327     return ret;
328 }
329 
BuildCommandParam(const std::string & fsType,std::vector<std::string> & formatCmds) const330 int Partition::BuildCommandParam(const std::string &fsType, std::vector<std::string> &formatCmds) const
331 {
332     std::map<std::string, std::string> fsToolsMap = {
333         { "ext4", FORMAT_TOOL_FOR_EXT4 },
334         { "f2fs", FORMAT_TOOL_FOR_F2FS },
335     };
336     auto it = fsToolsMap.find(fsType);
337     FLASHING_CHECK(it != fsToolsMap.end(),
338         flash_->RecordMsg(updater::ERROR,  "Not support fs type %s", fsType.c_str());
339         return FLASHING_FSTYPE_NOT_SUPPORT, "Not support fs type %s", fsType.c_str());
340 
341     if (fsType == "ext4") {
342         formatCmds.push_back(it->second);
343         formatCmds.push_back("-F");
344         formatCmds.push_back("-t");
345         formatCmds.push_back(fsType);
346         formatCmds.push_back("-b");
347         formatCmds.push_back(std::to_string(DEFAULT_BLOCK_SIZE));
348         formatCmds.push_back(partPath_);
349     } else if (fsType == "f2fs") {
350         formatCmds.push_back(it->second);
351         formatCmds.push_back(partPath_);
352     }
353     return 0;
354 }
355 
IsOnlyErase() const356 bool Partition::IsOnlyErase() const
357 {
358     std::vector<std::string> rawPartName = {
359         "boot", "fastboot", "kernel", "misc", "system"
360     };
361     auto it = std::find (rawPartName.begin(), rawPartName.end(), partName_);
362     return it != rawPartName.end();
363 }
364 
GetMountFlags(const std::string & mountFlagsStr,std::string & data) const365 uint32_t Partition::GetMountFlags(const std::string &mountFlagsStr, std::string &data) const
366 {
367     static std::map<std::string, uint32_t> mntInfo = {
368         { "ro", MS_RDONLY },
369         { "rw", 0 },
370         { ",sync", MS_SYNCHRONOUS },
371         { ",dirsync", MS_DIRSYNC },
372         { ",mand", MS_MANDLOCK },
373         { ",lazytime", MS_LAZYTIME },
374         { ",nosuid", MS_NOSUID },
375         { ",nodev", MS_NODEV },
376         { ",noexec", MS_NOEXEC },
377         { ",noatime", MS_NOATIME },
378         { ",nodiratime", MS_NODIRATIME },
379         { ",relatime", MS_RELATIME },
380         { ",remount", MS_REMOUNT },
381         { ",bind", MS_BIND },
382         { ",rec", MS_REC },
383         { ",unbindable", MS_UNBINDABLE },
384         { ",private", MS_PRIVATE },
385         { ",slave", MS_SLAVE },
386         { ",shared", MS_SHARED },
387         { ",defaults", 0 },
388     };
389 
390     // get rw flags
391     uint32_t flags = 0;
392     std::string::size_type found = std::string::npos;
393     std::string::size_type start = 0;
394     while (true) {
395         found = mountFlagsStr.find_first_of(",", start + 1);
396         std::string option = mountFlagsStr.substr(start, found - start);
397         auto iter = mntInfo.find(option);
398         if (iter != mntInfo.end()) {
399             flags |= iter->second;
400         } else {
401             data.append(option);
402             data.append(",");
403         }
404         if (found == std::string::npos) {
405             break;
406         }
407         start = found;
408     }
409     data.pop_back(); // Remove last ','
410     return flags;
411 }
412 }
413