/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <cerrno> #include <cstring> #include <linux/blkpg.h> #include <linux/fs.h> #include <libgen.h> #include <string> #include <sys/ioctl.h> #include <sys/types.h> #include <unistd.h> #include "fs_manager/cmp_partition.h" #include "fs_manager/mount.h" #include "fs_manager/partitions.h" #include "log/log.h" #include "misc_info/misc_info.h" #include "partition_const.h" #include "scope_guard.h" #include "securec.h" namespace Updater { namespace { constexpr const char *USERDATA_PARTNAME = "userdata"; constexpr const char *UPDATER_PARTNAME = "updater"; } static int BlkpgPartCommand(const Partition &part, struct blkpg_partition &pg, int op) { struct blkpg_ioctl_arg args {}; args.op = op; args.flags = 0; args.datalen = static_cast<int>(sizeof(struct blkpg_partition)); args.data = static_cast<void *>(&pg); int ret = 0; #ifndef UPDATER_UT ret = ioctl(part.partfd, BLKPG, &args); #endif if (ret < 0) { LOG(ERROR) << "ioctl of partition " << part.partName << " with operation " << op << " failed"; } return ret; } static int DoUmountDiskPartition(const Partition &part) { std::string partName = std::string("/") + part.partName; int ret = UmountForPath(partName); if (ret == -1) { LOG(ERROR) << "Umount " << partName << " failed: " << errno; return 0; } return 1; } static void DoFsync(const BlockDevice &dev) { BlockSpecific* bs = BLOCK_SPECIFIC(&dev); int status; while (true) { status = fsync (bs->fd); if (status >= 0) { break; } } } static int BlockSync(const Disk &disk) { if (disk.dev->readOnly) { return 0; } DoFsync(*(disk.dev)); return 1; } static int BlkpgRemovePartition(const Partition &part) { struct blkpg_partition blkPart {}; if (memset_s(&blkPart, sizeof(blkPart), 0, sizeof(blkPart)) != EOK) { return -1; } blkPart.pno = part.partNum; return BlkpgPartCommand(part, blkPart, BLKPG_DEL_PARTITION); } static int BlockDiskOpen(Disk &disk) { disk.dev->fd = open(disk.dev->devPath.c_str(), RW_MODE); if (disk.dev->fd < 0) { LOG(WARNING) << "open fail: " << disk.dev->devPath << errno; } return disk.dev->fd; } static void BlockDiskClose(Disk &disk) { if (disk.dev != nullptr) { if (disk.dev->fd > 0) { close(disk.dev->fd); disk.dev->fd = -1; } } } static bool DoRmPartition(const Disk &disk, int partn) { Partition *part = nullptr; part = GetPartition(disk, partn); if (part == nullptr) { LOG(ERROR) << "Cannot get partition info for partition number: " << partn; return false; } if (disk.dev->fd < 0) { return false; } part->partfd = disk.dev->fd; int ret = BlkpgRemovePartition(*part); part->partfd = -1; if (ret < 0) { LOG(ERROR) << "Delete part failed"; return false; } return true; } static int BlkpgAddPartition(Partition &part) { struct blkpg_partition blkPart {}; if (memset_s(&blkPart, sizeof(blkPart), 0, sizeof(blkPart)) != EOK) { return 0; } blkPart.start = static_cast<long long>(part.start * SECTOR_SIZE_DEFAULT); LOG(INFO) << "blkPart.start " << blkPart.start; blkPart.length = static_cast<long long>(part.length * SECTOR_SIZE_DEFAULT); LOG(INFO) << "blkPart.length " << blkPart.length; blkPart.pno = part.partNum; LOG(INFO) << "blkPart.pno " << blkPart.pno; if (strncpy_s(blkPart.devname, BLKPG_DEVNAMELTH, part.devName.c_str(), part.devName.size()) != EOK) { return 0; } LOG(INFO) << "blkPart.devname " << blkPart.devname; if (strncpy_s(blkPart.volname, BLKPG_VOLNAMELTH, part.partName.c_str(), part.partName.size()) != EOK) { return 0; } LOG(INFO) << "blkPart.volname " << blkPart.volname; if (BlkpgPartCommand(part, blkPart, BLKPG_ADD_PARTITION) < 0) { return 0; } return 1; } static bool DoAddPartition(const Disk &disk, Partition &part) { if (disk.dev->fd < 0) { return false; } part.partfd = disk.dev->fd; int ret = BlkpgAddPartition(part); part.partfd = -1; if (ret == 0) { LOG(ERROR) << "Add partition failed"; return false; } return true; } static void DestroyDiskPartitions(Disk &disk) { if (!disk.partList.empty()) { for (auto& p : disk.partList) { if (p != nullptr) { free(p); } } } disk.partList.clear(); } static void DestroyDiskDevices(const Disk &disk) { if (disk.dev != nullptr) { if (disk.dev->specific != nullptr) { free(disk.dev->specific); } free(disk.dev); } } static bool WriteMiscMsgWithOffset(const std::string &msg, int32_t offset) { const std::string miscDevPath = GetBlockDeviceByMountPoint("/misc"); char *realPath = realpath(miscDevPath.c_str(), NULL); if (realPath == nullptr) { LOG(ERROR) << "realPath is NULL"; return false; } FILE *fp = fopen(realPath, "rb+"); free(realPath); if (fp == nullptr) { LOG(ERROR) << "fopen error " << errno; return false; } ON_SCOPE_EXIT(flosefp) { fclose(fp); }; if (fseek(fp, offset, SEEK_SET) != 0) { LOG(ERROR) << "fseek error"; return false; } if (fwrite(msg.c_str(), msg.length() + 1, 1, fp) < 0) { LOG(ERROR) << "fwrite error " << errno; return false; } int fd = fileno(fp); fsync(fd); return true; } static bool WriteDiskPartitionToMisc(PartitonList &nlist) { if (nlist.empty()) { return false; } char blkdevparts[MISC_RECORD_UPDATE_PARTITIONS_SIZE] = "mmcblk0:"; std::sort(nlist.begin(), nlist.end(), [](const struct Partition *a, const struct Partition *b) { return (a->start < b->start); }); // Sort in ascending order char tmp[SMALL_BUFFER_SIZE] = {0}; size_t size = 0; for (auto& p : nlist) { if (memset_s(tmp, sizeof(tmp), 0, sizeof(tmp)) != EOK) { return false; } if (p->partName == "userdata") { if (snprintf_s(tmp, sizeof(tmp), sizeof(tmp) - 1, "-(%s),", p->partName.c_str()) == -1) { return false; } } else { size = static_cast<size_t>(p->length * SECTOR_SIZE_DEFAULT / DEFAULT_SIZE_1MB); if (snprintf_s(tmp, sizeof(tmp), sizeof(tmp) - 1, "%luM(%s),", size, p->partName.c_str()) == -1) { return false; } } if (strncat_s(blkdevparts, MISC_RECORD_UPDATE_PARTITIONS_SIZE - 1, tmp, strlen(tmp)) != EOK) { LOG(ERROR) << "Block device name overflow"; return false; } } blkdevparts[strlen(blkdevparts) - 1] = '\0'; LOG(INFO) << "blkdevparts is " << blkdevparts; return WriteMiscMsgWithOffset(std::string(blkdevparts), MISC_RECORD_UPDATE_PARTITIONS_OFFSET); } static bool AddPartitions(const Disk &disk, const PartitonList &ulist, int &partitionAddedCounter) { if (!ulist.empty()) { int userNum = GetPartitionNumByPartName(USERDATA_PARTNAME, disk.partList); int step = 1; char pdevname[DEVPATH_SIZE] = {0}; for (auto& p2 : ulist) { if (p2->partName == USERDATA_PARTNAME) { LOG(INFO) << "Change userdata image is not support."; continue; } if (p2->partName == UPDATER_PARTNAME) { LOG(ERROR) << "Change updater image is not supported."; continue; } p2->partNum = userNum + step; if (snprintf_s(pdevname, sizeof(pdevname), sizeof(pdevname) - 1, "%sp%d", MMC_DEV, p2->partNum) == -1) { return false; } p2->devName.clear(); p2->devName = pdevname; LOG(INFO) << "Adding partition " << p2->partName; if (!DoAddPartition (disk, *p2)) { LOG(ERROR) << "Add partition fail for " << p2->partName; return false; } step++; partitionAddedCounter++; } } return true; } static bool RemovePartitions(const Disk &disk, int &partitionRemovedCounter) { PartitonList pList = disk.partList; for (const auto &it : pList) { if (it->changeType == NOT_CHANGE) { continue; } if (it->partName == UPDATER_PARTNAME) { LOG(ERROR) << "Cannot delete updater partition."; continue; } if (it->partName == USERDATA_PARTNAME) { LOG(INFO) << "Cannot delete userdata partition."; continue; } if (DoUmountDiskPartition(*it) == 0) { continue; } LOG(INFO) << "Removing partition " << it->partName; if (!DoRmPartition (disk, it->partNum)) { LOG(ERROR) << "Remove partition failed."; return false; } partitionRemovedCounter++; } return true; } int CheckDevicePartitions(const std::string &path) { if (DiskAlloc(path) == 0) { LOG(ERROR) << "path not exist" << path; return 0; } if (ProbeAllPartitions() == 0) { LOG(ERROR) << "partition sum is zero!"; return 0; } return 1; } int AdjustPartitions(Disk *disk, int &partitionChangedCounter) { PartitonList ulist; ulist.clear(); if (disk == nullptr || BlockDiskOpen(*disk) < 0) { return 0; } if (GetRegisterUpdaterPartitionList(ulist) == 0) { LOG(ERROR) << "get updater list fail!"; return 0; } if (!RemovePartitions(*disk, partitionChangedCounter)) { return 0; } BlockSync(*disk); if (!AddPartitions(*disk, ulist, partitionChangedCounter)) { return 0; } BlockSync(*disk); return 1; } int DoPartitions(PartitonList &nlist) { LOG(INFO) << "do_partitions start"; if (nlist.empty()) { LOG(ERROR) << "newpartitionlist is empty "; return 0; } const std::string path = MMC_PATH; if (CheckDevicePartitions(path) == 0) { return 0; } Disk *disk = GetRegisterBlockDisk(path); if (disk == nullptr) { LOG(ERROR) << "getRegisterdisk fail! "; return 0; } if (RegisterUpdaterPartitionList(nlist, disk->partList) == 0) { LOG(ERROR) << "register updater list fail!"; free(disk); return 0; } ON_SCOPE_EXIT(clearresource) { BlockDiskClose(*disk); DestroyDiskPartitions(*disk); DestroyDiskDevices(*disk); free(disk); }; int partitionChangedCounter = 1; if (AdjustPartitions(disk, partitionChangedCounter) == 0) { return 0; } (void)WriteDiskPartitionToMisc(nlist); return partitionChangedCounter; } } // Updater