* 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 "fs_manager/partitions.h"
#include <cstdlib>
#include <string>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include "log/log.h"
#include "partition_const.h"
#include "scope_guard.h"
#include "securec.h"
namespace Updater {
static struct Disk *g_disks;
static int DeviceStat(const BlockDevice &dev, struct stat &devStat)
{
int ret = 0;
if (!stat (dev.devPath.c_str(), &devStat)) {
ret = 1;
}
if (stat (dev.devPath.c_str(), &devStat) != EOK) {
LOG(ERROR) << "stat error: " << errno << std::endl;
ret = 0;
}
return ret;
}
static int DeviceProbeType(BlockDevice &dev)
{
struct stat devStat {};
int devMajor;
int devMinor;
BlockSpecific *specific = BLOCK_SPECIFIC(&dev);
if (DeviceStat(dev, devStat) == 0) {
return 0;
}
devMajor = static_cast<int>(major (devStat.st_rdev));
specific->major = devMajor;
devMinor = static_cast<int>(minor (devStat.st_rdev));
specific->minor = devMinor;
bool a1 = SCSI_BLK_MAJOR(devMajor) && (devMinor % 0x10 == 0);
bool a2 = devMajor == SDMMC_MAJOR && (devMinor % 0x08 == 0);
if (a1) {
dev.type = DEVICE_SCSI;
}
if (a2) {
dev.type = DEVICE_EMMC;
}
if (!a1 && !a2) {
dev.type = DEVICE_UNKNOWN;
}
return 1;
}
static std::string LastComponent(const std::string &path)
{
std::string tmp = "";
if (path == MMC_PATH) {
tmp = MMC_DEV;
}
if (path == SDA_PATH) {
tmp = SDA_DEV;
}
if (path == SDB_PATH) {
tmp = SDB_DEV;
}
return tmp;
}
static bool ReadDeviceSysfsFile(BlockDevice &dev, const std::string &file, std::string &strl)
{
FILE *f = nullptr;
char nameBuf[DEVPATH_SIZE];
char buf[BUFFER_SIZE];
if (snprintf_s(nameBuf, DEVPATH_SIZE, DEVPATH_SIZE - 1, "/sys/block/%s/device/%s",
LastComponent(dev.devPath).c_str(), file.c_str()) == -1) {
return false;
}
char realPath[PATH_MAX] = {0};
if (realpath(nameBuf, realPath) == nullptr) {
return false;
}
if ((f = fopen(realPath, "r")) == nullptr) {
return false;
}
if (fgets(buf, BUFFER_SIZE, f) == nullptr) {
fclose(f);
return false;
}
strl = buf;
fclose(f);
return true;
}
static bool SdmmcGetProductInfo(BlockDevice &dev, std::string &type, std::string &name)
{
std::string typeStr = "type";
std::string nameStr = "name";
bool ret = ReadDeviceSysfsFile(dev, typeStr, type);
bool red = ReadDeviceSysfsFile(dev, nameStr, name);
return (ret || red);
}
bool SetBlockDeviceMode(BlockDevice &dev)
{
BlockSpecific *specific = BLOCK_SPECIFIC(&dev);
specific->fd = open(dev.devPath.c_str(), RW_MODE);
if (specific->fd == -1) {
LOG(WARNING) << "Open " << dev.devPath << " with read-write failed, try read-only mode";
specific->fd = open(dev.devPath.c_str(), RD_MODE);
bool a1 = dev.readOnly;
dev.readOnly = 1;
if (specific->fd == -1) {
LOG(ERROR) << "Open " << dev.devPath << " with read-only mode failed: " << errno;
dev.readOnly = a1;
return false;
}
fdsan_exchange_owner_tag(specific->fd, 0, FDSAN_UPDATER_TAG);
} else {
fdsan_exchange_owner_tag(specific->fd, 0, FDSAN_UPDATER_TAG);
dev.readOnly = 0;
}
return true;
}
static int BlockDeviceClose(const BlockDevice &dev)
{
BlockSpecific* specific = BLOCK_SPECIFIC(&dev);
if (fsync(specific->fd) < 0 || fdsan_close_with_tag(specific->fd, FDSAN_UPDATER_TAG) < 0) {
return 0;
}
return 1;
}
static std::string ReadPartitionFromSys(const std::string &devname, const std::string &partn,
const std::string &type, const std::string &table)
{
FILE *f = nullptr;
char buf[BUFFER_SIZE] = {0};
std::string devPath;
std::string partString = "";
devPath = "/sys/block/" + devname + "/" + partn + "/" + type;
if (partn.empty()) {
devPath = "/sys/block/" + devname + "/" + type;
}
if (devPath.length() >= DEVPATH_SIZE) {
LOG(ERROR) << "devPath is invalid";
return partString;
}
if ((f = fopen(devPath.c_str(), "r")) == nullptr) {
return partString;
}
ON_SCOPE_EXIT(fclosef) {
fclose(f);
};
while (!feof(f)) {
if (fgets(buf, BUFFER_SIZE, f) == nullptr) {
return partString;
}
if (type == "uevent" && strstr(buf, table.c_str()) != nullptr) {
partString = std::string(buf + table.size(), sizeof(buf) - table.size());
if (!partString.empty()) {
partString.pop_back();
}
return partString;
} else if (type == "start" || type == "size") {
partString = std::string(buf, sizeof(buf) - 1);
LOG(INFO) << type << " partInf: " << std::string(buf, sizeof(buf) - 1);
return partString;
}
if (memset_s(buf, sizeof(buf), 0, sizeof(buf)) != EOK) {
return partString;
}
}
return partString;
}
static int InitGeneric(BlockDevice &dev, const std::string modelName)
{
struct stat devStat {};
if (DeviceStat(dev, devStat) == 0) {
LOG(ERROR) << "device stat error ";
return 0;
}
if (!SetBlockDeviceMode(dev)) {
LOG(ERROR) << "device authority error ";
return 0;
}
const std::string devName = LastComponent(dev.devPath);
std::string partSize = ReadPartitionFromSys(devName, "", "size", "");
if (partSize.empty()) {
return 0;
}
int devSize = atoi(partSize.c_str());
dev.length = devSize;
dev.sectorSize = SECTOR_SIZE_DEFAULT;
dev.physSectorSize = SECTOR_SIZE_DEFAULT;
dev.model = modelName;
BlockDeviceClose (dev);
dev.fd = -1;
return 1;
}
static int InitSdmmc(BlockDevice &dev)
{
std::string type = "";
std::string name = "";
std::string id = "";
bool a1 = SdmmcGetProductInfo(dev, type, name);
if (a1) {
id = type + name;
}
if (!a1) {
id = "Generic SD/MMC Storage Card";
return 0;
}
return InitGeneric(dev, id);
}
static BlockDevice* NewBlockDevice(const std::string &path)
{
BlockDevice *dev = nullptr;
BlockSpecific *specific = nullptr;
dev = static_cast<BlockDevice*>(calloc(1, sizeof (BlockDevice)));
if (dev == nullptr) {
LOG(ERROR) << "calloc errno " << errno;
return nullptr;
}
dev->devPath = path;
dev->specific = static_cast<BlockSpecific*>(calloc(1, sizeof (BlockSpecific)));
if (!dev->specific) {
LOG(ERROR) << "calloc errno " << errno;
free(dev);
return nullptr;
}
specific = BLOCK_SPECIFIC(dev);
dev->readOnly = 0;
dev->sectorSize = 0;
dev->physSectorSize = 0;
int ret = 0;
bool a1 = DeviceProbeType(*dev);
if (a1) {
if (dev->type == DEVICE_EMMC) {
ret = InitSdmmc(*dev);
if (ret == 0) {
LOG(ERROR) << "Init sdmmc error";
}
}
if (dev->type != DEVICE_EMMC) {
LOG(WARNING) << "Unsupported device type";
}
}
if (!a1) {
LOG(ERROR) << "Device probe error";
}
if (ret == 0) {
free(dev->specific);
free(dev);
dev = nullptr;
}
return dev;
}
static Disk* NewBlockDisk(const BlockDevice &dev, const DiskType diskType)
{
Disk *disk = nullptr;
disk = static_cast<Disk*>(calloc (1, sizeof (Disk)));
if (disk == nullptr) {
LOG(ERROR) << "Allocate memory for disk failed: " << errno;
return nullptr;
}
disk->dev = (BlockDevice*)&dev;
disk->type = diskType;
disk->partsum = 0;
disk->partList.clear();
return disk;
}
int DiskAlloc(const std::string &path)
{
struct Disk *disk = nullptr;
struct BlockDevice *dev = nullptr;
dev = NewBlockDevice(path);
if (dev == nullptr) {
LOG(ERROR) << "NewBlockDevice nullptr ";
return 0;
}
disk = NewBlockDisk(*dev, GPT);
if (disk == nullptr) {
free(dev);
LOG(ERROR) << "NewBlockDevice nullptr ";
return 0;
}
g_disks = disk;
return 1;
}
static struct Partition* NewPartition(const BlockDevice &dev, int partn)
{
Partition* part = (Partition*) calloc (1, sizeof (Partition));
if (part == nullptr) {
LOG(ERROR) << "Allocate memory for partition failed.";
return nullptr;
}
const std::string devName = LastComponent(dev.devPath);
char partName[64] = {0};
if (devName == MMC_DEV) {
if (snprintf_s(partName, sizeof(partName), sizeof(partName) - 1, "%sp%d", devName.c_str(), partn) == -1) {
free(part);
return nullptr;
}
}
if (devName != MMC_DEV && ((devName == SDA_DEV) || (devName == SDB_DEV)) &&
snprintf_s(partName, sizeof(partName), sizeof(partName) - 1, "%s%d", devName.c_str(), partn) == -1) {
free(part);
return nullptr;
}
std::string strstart = ReadPartitionFromSys(devName, partName, "start", "");
if (strstart.empty()) {
free(part);
return nullptr;
}
part->start = static_cast<size_t>(atoi(strstart.c_str()));
std::string strsize = ReadPartitionFromSys(devName, partName, "size", "");
if (strsize.empty()) {
free(part);
return nullptr;
}
part->length = static_cast<size_t>(atoi(strsize.c_str()));
std::string strdevname = ReadPartitionFromSys(devName, partName, "uevent", "DEVNAME=");
part->devName = partName;
if (!strdevname.empty()) {
part->devName = strdevname;
}
std::string strpartname = ReadPartitionFromSys(devName, partName, "uevent", "PARTNAME=");
part->partName = partName;
if (!strpartname.empty()) {
part->partName = strpartname;
}
part->partNum = partn;
part->type = NORMAL;
part->fsType = "";
part->changeType = NORMAL_CHANGE;
return part;
}
struct Partition* GetPartition(const Disk &disk, int partn)
{
struct Partition *part = nullptr;
if (partn == 0) {
return nullptr;
}
if (disk.partList.empty()) {
return nullptr;
}
for (auto& p : disk.partList) {
if (p->partNum == partn) {
part = p;
break;
}
}
return part;
}
int ProbeAllPartitions()
{
int i = 0;
struct Disk* disk = nullptr;
disk = g_disks;
if (disk == nullptr) {
return 0;
}
int partSum = DEFAULT_PARTSUM;
struct Partition* part = nullptr;
for (i = 1; i < partSum; i++) {
part = NewPartition(*(disk->dev), i);
if (!part) {
LOG(ERROR) << "Create new partition failed.";
break;
}
disk->partList.push_back(part);
disk->partsum++;
}
return disk->partsum;
}
Disk* GetRegisterBlockDisk(const std::string &path)
{
if (g_disks == nullptr) {
return nullptr;
}
Disk *p = nullptr;
if (g_disks->dev->devPath == path) {
p = g_disks;
}
return p;
}
int GetPartitionNumByPartName(const std::string &partname, const PartitonList &plist)
{
int ret = 0;
for (const auto &p : plist) {
if (p->partName == partname) {
ret = p->partNum;
break;
}
}
return ret;
}
}