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 "fs_manager/partitions.h"
16#include <cstdlib>
17#include <string>
18#include <sys/stat.h>
19#include <sys/sysmacros.h>
20#include <unistd.h>
21#include "log/log.h"
22#include "partition_const.h"
23#include "scope_guard.h"
24#include "securec.h"
25
26namespace Updater {
27static struct Disk *g_disks;
28static int DeviceStat(const BlockDevice &dev, struct stat &devStat)
29{
30    int ret = 0;
31    if (!stat (dev.devPath.c_str(), &devStat)) {
32        ret = 1;
33    }
34    if (stat (dev.devPath.c_str(), &devStat) != EOK) {
35        LOG(ERROR) << "stat error: " << errno << std::endl;
36        ret = 0;
37    }
38    return ret;
39}
40
41static int DeviceProbeType(BlockDevice &dev)
42{
43    struct stat devStat {};
44    int devMajor;
45    int devMinor;
46    BlockSpecific *specific = BLOCK_SPECIFIC(&dev);
47    if (DeviceStat(dev, devStat) == 0) {
48        return 0;
49    }
50
51    devMajor = static_cast<int>(major (devStat.st_rdev));
52    specific->major = devMajor;
53    devMinor = static_cast<int>(minor (devStat.st_rdev));
54    specific->minor = devMinor;
55    bool a1 = SCSI_BLK_MAJOR(devMajor) && (devMinor % 0x10 == 0);
56    bool a2 = devMajor == SDMMC_MAJOR && (devMinor % 0x08 == 0);
57    if (a1) {
58        dev.type = DEVICE_SCSI;
59    }
60    if (a2) {
61        dev.type = DEVICE_EMMC;
62    }
63    if (!a1 && !a2) {
64        dev.type = DEVICE_UNKNOWN;
65    }
66    return 1;
67}
68
69static std::string LastComponent(const std::string &path)
70{
71    std::string tmp = "";
72    if (path == MMC_PATH) {
73        tmp = MMC_DEV;
74    }
75    if (path == SDA_PATH) {
76        tmp = SDA_DEV;
77    }
78    if (path == SDB_PATH) {
79        tmp = SDB_DEV;
80    }
81    return tmp;
82}
83
84static bool ReadDeviceSysfsFile(BlockDevice &dev, const std::string &file, std::string &strl)
85{
86    FILE *f = nullptr;
87    char nameBuf[DEVPATH_SIZE];
88    char buf[BUFFER_SIZE];
89
90    if (snprintf_s(nameBuf, DEVPATH_SIZE, DEVPATH_SIZE - 1, "/sys/block/%s/device/%s",
91        LastComponent(dev.devPath).c_str(), file.c_str()) == -1) {
92        return false;
93    }
94    char realPath[PATH_MAX] = {0};
95    if (realpath(nameBuf, realPath) == nullptr) {
96        return false;
97    }
98    if ((f = fopen(realPath, "r")) == nullptr) {
99        return false;
100    }
101
102    if (fgets(buf, BUFFER_SIZE, f) == nullptr) {
103        fclose(f);
104        return false;
105    }
106    strl = buf;
107    fclose(f);
108    return true;
109}
110
111static bool SdmmcGetProductInfo(BlockDevice &dev, std::string &type, std::string &name)
112{
113    std::string typeStr = "type";
114    std::string nameStr = "name";
115
116    bool ret = ReadDeviceSysfsFile(dev, typeStr, type);
117    bool red = ReadDeviceSysfsFile(dev, nameStr, name);
118    return (ret || red);
119}
120
121bool SetBlockDeviceMode(BlockDevice &dev)
122{
123    BlockSpecific *specific = BLOCK_SPECIFIC(&dev);
124
125    specific->fd = open(dev.devPath.c_str(), RW_MODE);
126    if (specific->fd == -1) {
127        LOG(WARNING) << "Open " << dev.devPath << " with read-write failed, try read-only mode";
128        specific->fd = open(dev.devPath.c_str(), RD_MODE);
129        bool a1 = dev.readOnly;
130        dev.readOnly = 1;
131        if (specific->fd == -1) {
132            LOG(ERROR) << "Open " << dev.devPath << " with read-only mode failed: " << errno;
133            dev.readOnly = a1;
134            return false;
135        }
136    } else {
137        dev.readOnly = 0;
138    }
139    return true;
140}
141
142static int BlockDeviceClose(const BlockDevice &dev)
143{
144    BlockSpecific* specific = BLOCK_SPECIFIC(&dev);
145    if (fsync(specific->fd) < 0 || close(specific->fd) < 0) {
146        return 0;
147    }
148    return 1;
149}
150
151static std::string ReadPartitionFromSys(const std::string &devname, const std::string &partn,
152    const std::string &type, const std::string &table)
153{
154    FILE *f = nullptr;
155    char buf[BUFFER_SIZE] = {0};
156    std::string devPath;
157    std::string partString = "";
158    devPath = "/sys/block/" + devname + "/" + partn + "/" + type;
159    if (partn.empty()) {
160        devPath = "/sys/block/" + devname + "/" + type;
161    }
162
163    if (devPath.length() >= DEVPATH_SIZE) {
164        LOG(ERROR) << "devPath is invalid";
165        return partString;
166    }
167
168    if ((f = fopen(devPath.c_str(), "r")) == nullptr) {
169        return partString;
170    }
171
172    ON_SCOPE_EXIT(fclosef) {
173        fclose(f);
174    };
175
176    while (!feof(f)) {
177        if (fgets(buf, BUFFER_SIZE, f) == nullptr) {
178            return partString;
179        }
180        if (type == "uevent" && strstr(buf, table.c_str()) != nullptr) {
181            partString = std::string(buf + table.size(), sizeof(buf) - table.size());
182            if (!partString.empty()) {
183                partString.pop_back();
184            }
185            return partString;
186        } else if (type == "start" || type == "size") {
187            partString = std::string(buf, sizeof(buf) - 1);
188            LOG(INFO) << type << " partInf: " << std::string(buf, sizeof(buf) - 1);
189            return partString;
190        }
191        if (memset_s(buf, sizeof(buf), 0, sizeof(buf)) != EOK) {
192            return partString;
193        }
194    }
195
196    return partString;
197}
198
199static int InitGeneric(BlockDevice &dev, const std::string modelName)
200{
201    struct stat devStat {};
202    if (DeviceStat(dev, devStat) == 0) {
203        LOG(ERROR) << "device stat error ";
204        return 0;
205    }
206    if (!SetBlockDeviceMode(dev)) {
207        LOG(ERROR) << "device authority error ";
208        return 0;
209    }
210
211    const std::string devName = LastComponent(dev.devPath);
212    std::string partSize = ReadPartitionFromSys(devName, "", "size", "");
213    if (partSize.empty()) {
214        return 0;
215    }
216    int devSize = atoi(partSize.c_str());
217    dev.length = devSize;
218    dev.sectorSize = SECTOR_SIZE_DEFAULT;
219    dev.physSectorSize = SECTOR_SIZE_DEFAULT;
220    dev.model = modelName;
221    BlockDeviceClose (dev);
222    dev.fd = -1;
223    return 1;
224}
225
226static int InitSdmmc(BlockDevice &dev)
227{
228    std::string type = "";
229    std::string name = "";
230    std::string id = "";
231    bool a1 = SdmmcGetProductInfo(dev, type, name);
232    if (a1) {
233        id = type + name;
234    }
235    if (!a1) {
236        id = "Generic SD/MMC Storage Card";
237        return 0;
238    }
239    return InitGeneric(dev, id);
240}
241
242static BlockDevice* NewBlockDevice(const std::string &path)
243{
244    BlockDevice *dev = nullptr;
245    BlockSpecific *specific = nullptr;
246
247    dev = static_cast<BlockDevice*>(calloc(1, sizeof (BlockDevice)));
248    if (dev == nullptr) {
249        LOG(ERROR) << "calloc errno " << errno;
250        return nullptr;
251    }
252
253    dev->devPath = path;
254    dev->specific = static_cast<BlockSpecific*>(calloc(1, sizeof (BlockSpecific)));
255    if (!dev->specific) {
256        LOG(ERROR) << "calloc errno " << errno;
257        free(dev);
258        return nullptr;
259    }
260
261    specific = BLOCK_SPECIFIC(dev);
262    dev->readOnly = 0;
263    dev->sectorSize = 0;
264    dev->physSectorSize = 0;
265
266    int ret = 0;
267    bool a1 = DeviceProbeType(*dev);
268    if (a1) {
269        if (dev->type == DEVICE_EMMC)  {
270            ret = InitSdmmc(*dev);
271            if (ret == 0) {
272                LOG(ERROR) << "Init sdmmc error";
273            }
274        }
275        if (dev->type != DEVICE_EMMC) {
276            LOG(WARNING) << "Unsupported device type";
277        }
278    }
279    if (!a1) {
280        LOG(ERROR) << "Device probe error";
281    }
282
283    if (ret == 0) {
284        free(dev->specific);
285        free(dev);
286        dev = nullptr;
287    }
288    return dev;
289}
290
291static Disk* NewBlockDisk(const BlockDevice &dev, const DiskType diskType)
292{
293    Disk *disk = nullptr;
294
295    disk = static_cast<Disk*>(calloc (1, sizeof (Disk)));
296    if (disk == nullptr) {
297        LOG(ERROR) << "Allocate memory for disk failed: " << errno;
298        return nullptr;
299    }
300
301    disk->dev = (BlockDevice*)&dev;
302    disk->type = diskType;
303    disk->partsum = 0;
304    disk->partList.clear();
305    return disk;
306}
307
308int DiskAlloc(const std::string &path)
309{
310    struct Disk *disk = nullptr;
311    struct BlockDevice *dev = nullptr;
312    dev = NewBlockDevice(path);
313    if (dev == nullptr) {
314        LOG(ERROR) << "NewBlockDevice nullptr ";
315        return 0;
316    }
317
318    disk = NewBlockDisk(*dev, GPT);
319    if (disk == nullptr) {
320        LOG(ERROR) << "NewBlockDevice nullptr ";
321        return 0;
322    }
323    g_disks = disk;
324    return 1;
325}
326
327static struct Partition* NewPartition(const BlockDevice &dev, int partn)
328{
329    Partition* part = (Partition*) calloc (1, sizeof (Partition));
330    if (part == nullptr) {
331        LOG(ERROR) << "Allocate memory for partition failed.";
332        return nullptr;
333    }
334    const std::string devName = LastComponent(dev.devPath);
335    char partName[64] = {0};
336    if (devName == MMC_DEV) {
337        if (snprintf_s(partName, sizeof(partName), sizeof(partName) - 1, "%sp%d", devName.c_str(), partn) == -1) {
338            free(part);
339            return nullptr;
340        }
341    }
342    if (devName != MMC_DEV && ((devName == SDA_DEV) || (devName == SDB_DEV)) &&
343        snprintf_s(partName, sizeof(partName), sizeof(partName) - 1, "%s%d", devName.c_str(), partn) == -1) {
344        free(part);
345        return nullptr;
346    }
347
348    std::string strstart = ReadPartitionFromSys(devName, partName, "start", "");
349    if (strstart.empty()) {
350        free(part);
351        return nullptr;
352    }
353    part->start = static_cast<size_t>(atoi(strstart.c_str()));
354
355    std::string strsize = ReadPartitionFromSys(devName, partName, "size", "");
356    if (strsize.empty()) {
357        free(part);
358        return nullptr;
359    }
360    part->length = static_cast<size_t>(atoi(strsize.c_str()));
361
362    std::string strdevname = ReadPartitionFromSys(devName, partName, "uevent", "DEVNAME=");
363    part->devName = partName;
364    if (!strdevname.empty()) {
365        part->devName = strdevname;
366    }
367    std::string strpartname = ReadPartitionFromSys(devName, partName, "uevent", "PARTNAME=");
368    part->partName = partName;
369    if (!strpartname.empty()) {
370        part->partName = strpartname;
371    }
372
373    part->partNum = partn;
374    part->type = NORMAL;
375    part->fsType = "";
376    part->changeType = NORMAL_CHANGE;
377    return part;
378}
379
380struct Partition* GetPartition(const Disk &disk, int partn)
381{
382    struct Partition *part = nullptr;
383    if (partn == 0) {
384        return nullptr;
385    }
386    if (disk.partList.empty()) {
387        return nullptr;
388    }
389    for (auto& p : disk.partList) {
390        if (p->partNum == partn) {
391            part = p;
392            break;
393        }
394    }
395    return part;
396}
397
398int ProbeAllPartitions()
399{
400    int i = 0;
401    struct Disk* disk = nullptr;
402    disk = g_disks;
403    if (disk == nullptr) {
404        return 0;
405    }
406    int partSum = DEFAULT_PARTSUM;
407    struct Partition* part = nullptr;
408    for (i = 1; i < partSum; i++) {
409        part = NewPartition(*(disk->dev), i);
410        if (!part) {
411            LOG(ERROR) << "Create new partition failed.";
412            break;
413        }
414        disk->partList.push_back(part);
415        disk->partsum++;
416    }
417    return disk->partsum;
418}
419
420Disk* GetRegisterBlockDisk(const std::string &path)
421{
422    if (g_disks == nullptr) {
423        return nullptr;
424    }
425    Disk *p = nullptr;
426    if (g_disks->dev->devPath == path) {
427        p = g_disks;
428    }
429    return p;
430}
431
432int GetPartitionNumByPartName(const std::string &partname, const PartitonList &plist)
433{
434    int ret = 0;
435    for (const auto &p : plist) {
436        if (p->partName == partname) {
437            ret = p->partNum;
438            break;
439        }
440    }
441    return ret;
442}
443} // namespace Updater
444