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