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 <cerrno>
16#include <cstring>
17#include <linux/blkpg.h>
18#include <linux/fs.h>
19#include <libgen.h>
20#include <string>
21#include <sys/ioctl.h>
22#include <sys/types.h>
23#include <unistd.h>
24#include "fs_manager/cmp_partition.h"
25#include "fs_manager/mount.h"
26#include "fs_manager/partitions.h"
27#include "log/log.h"
28#include "misc_info/misc_info.h"
29#include "partition_const.h"
30#include "scope_guard.h"
31#include "securec.h"
32
33namespace Updater {
34namespace {
35constexpr const char *USERDATA_PARTNAME = "userdata";
36constexpr const char *UPDATER_PARTNAME = "updater";
37}
38
39static int BlkpgPartCommand(const Partition &part, struct blkpg_partition &pg, int op)
40{
41    struct blkpg_ioctl_arg args {};
42    args.op = op;
43    args.flags = 0;
44    args.datalen = static_cast<int>(sizeof(struct blkpg_partition));
45    args.data = static_cast<void *>(&pg);
46
47    int ret = 0;
48#ifndef UPDATER_UT
49    ret = ioctl(part.partfd, BLKPG, &args);
50#endif
51    if (ret < 0) {
52        LOG(ERROR) << "ioctl of partition " << part.partName << " with operation " << op << " failed";
53    }
54    return ret;
55}
56
57static int DoUmountDiskPartition(const Partition &part)
58{
59    std::string partName = std::string("/") + part.partName;
60    int ret = UmountForPath(partName);
61    if (ret == -1) {
62        LOG(ERROR) << "Umount " << partName << " failed: " << errno;
63        return 0;
64    }
65    return 1;
66}
67
68static void DoFsync(const BlockDevice &dev)
69{
70    BlockSpecific* bs = BLOCK_SPECIFIC(&dev);
71    int status;
72
73    while (true) {
74        status = fsync (bs->fd);
75        if (status >= 0) {
76            break;
77        }
78    }
79}
80
81static int BlockSync(const Disk &disk)
82{
83    if (disk.dev->readOnly) {
84        return 0;
85    }
86    DoFsync(*(disk.dev));
87    return 1;
88}
89
90static int BlkpgRemovePartition(const Partition &part)
91{
92    struct blkpg_partition blkPart {};
93    if (memset_s(&blkPart, sizeof(blkPart), 0, sizeof(blkPart)) != EOK) {
94        return -1;
95    }
96    blkPart.pno = part.partNum;
97    return BlkpgPartCommand(part, blkPart, BLKPG_DEL_PARTITION);
98}
99
100static int BlockDiskOpen(Disk &disk)
101{
102    disk.dev->fd = open(disk.dev->devPath.c_str(), RW_MODE);
103    if (disk.dev->fd < 0) {
104        LOG(WARNING) << "open fail: " << disk.dev->devPath << errno;
105    }
106    return disk.dev->fd;
107}
108
109static void BlockDiskClose(Disk &disk)
110{
111    if (disk.dev != nullptr) {
112        if (disk.dev->fd > 0) {
113            close(disk.dev->fd);
114            disk.dev->fd = -1;
115        }
116    }
117}
118
119static bool DoRmPartition(const Disk &disk, int partn)
120{
121    Partition *part = nullptr;
122    part = GetPartition(disk, partn);
123    if (part == nullptr) {
124        LOG(ERROR) << "Cannot get partition info for partition number: " << partn;
125        return false;
126    }
127
128    if (disk.dev->fd < 0) {
129        return false;
130    }
131    part->partfd = disk.dev->fd;
132    int ret = BlkpgRemovePartition(*part);
133    part->partfd = -1;
134    if (ret < 0) {
135        LOG(ERROR) << "Delete part failed";
136        return false;
137    }
138    return true;
139}
140
141static int BlkpgAddPartition(Partition &part)
142{
143    struct blkpg_partition blkPart {};
144    if (memset_s(&blkPart, sizeof(blkPart), 0, sizeof(blkPart)) != EOK) {
145        return 0;
146    }
147    blkPart.start = static_cast<long long>(part.start * SECTOR_SIZE_DEFAULT);
148    LOG(INFO) << "blkPart.start " << blkPart.start;
149    blkPart.length = static_cast<long long>(part.length * SECTOR_SIZE_DEFAULT);
150    LOG(INFO) << "blkPart.length " << blkPart.length;
151    blkPart.pno = part.partNum;
152    LOG(INFO) << "blkPart.pno " << blkPart.pno;
153    if (strncpy_s(blkPart.devname, BLKPG_DEVNAMELTH, part.devName.c_str(), part.devName.size()) != EOK) {
154        return 0;
155    }
156    LOG(INFO) << "blkPart.devname " << blkPart.devname;
157    if (strncpy_s(blkPart.volname, BLKPG_VOLNAMELTH, part.partName.c_str(), part.partName.size()) != EOK) {
158        return 0;
159    }
160    LOG(INFO) << "blkPart.volname " << blkPart.volname;
161    if (BlkpgPartCommand(part, blkPart, BLKPG_ADD_PARTITION) < 0) {
162        return 0;
163    }
164    return 1;
165}
166
167static bool DoAddPartition(const Disk &disk, Partition &part)
168{
169    if (disk.dev->fd < 0) {
170        return false;
171    }
172
173    part.partfd = disk.dev->fd;
174    int ret = BlkpgAddPartition(part);
175    part.partfd = -1;
176    if (ret == 0) {
177        LOG(ERROR) << "Add partition failed";
178        return false;
179    }
180    return true;
181}
182
183static void DestroyDiskPartitions(Disk &disk)
184{
185    if (!disk.partList.empty()) {
186        for (auto& p : disk.partList) {
187            if (p != nullptr) {
188                free(p);
189            }
190        }
191    }
192    disk.partList.clear();
193}
194
195static void DestroyDiskDevices(const Disk &disk)
196{
197    if (disk.dev != nullptr) {
198        if (disk.dev->specific != nullptr) {
199            free(disk.dev->specific);
200        }
201        free(disk.dev);
202    }
203}
204
205static bool WriteMiscMsgWithOffset(const std::string &msg, int32_t offset)
206{
207    const std::string miscDevPath = GetBlockDeviceByMountPoint("/misc");
208    char realPath[PATH_MAX] = {0};
209    if (realpath(miscDevPath.c_str(), realPath) == nullptr) {
210        LOG(ERROR) << "realPath is NULL";
211        return false;
212    }
213    FILE *fp = fopen(realPath, "rb+");
214    if (fp == nullptr) {
215        LOG(ERROR) << "fopen error " << errno;
216        return false;
217    }
218
219    ON_SCOPE_EXIT(flosefp) {
220        fclose(fp);
221    };
222
223    if (fseek(fp, offset, SEEK_SET) != 0) {
224        LOG(ERROR) << "fseek error";
225        return false;
226    }
227
228    if (fwrite(msg.c_str(), msg.length() + 1, 1, fp) < 0) {
229        LOG(ERROR) << "fwrite error " << errno;
230        return false;
231    }
232
233    int fd = fileno(fp);
234    fsync(fd);
235    return true;
236}
237
238static bool WriteDiskPartitionToMisc(PartitonList &nlist)
239{
240    if (nlist.empty()) {
241        return false;
242    }
243    char blkdevparts[MISC_RECORD_UPDATE_PARTITIONS_SIZE] = "mmcblk0:";
244    std::sort(nlist.begin(), nlist.end(), [](const struct Partition *a, const struct Partition *b) {
245            return (a->start < b->start);
246    }); // Sort in ascending order
247    char tmp[SMALL_BUFFER_SIZE] = {0};
248    size_t size = 0;
249    for (auto& p : nlist) {
250        if (memset_s(tmp, sizeof(tmp), 0, sizeof(tmp)) != EOK) {
251            return false;
252        }
253        if (p->partName == "userdata") {
254            if (snprintf_s(tmp, sizeof(tmp), sizeof(tmp) - 1, "-(%s),",
255                p->partName.c_str()) == -1) {
256                    return false;
257                }
258        } else {
259            size = static_cast<size_t>(p->length * SECTOR_SIZE_DEFAULT / DEFAULT_SIZE_1MB);
260            if (snprintf_s(tmp, sizeof(tmp), sizeof(tmp) - 1, "%luM(%s),",
261                size, p->partName.c_str()) == -1) {
262                    return false;
263                }
264        }
265        if (strncat_s(blkdevparts, MISC_RECORD_UPDATE_PARTITIONS_SIZE - 1, tmp, strlen(tmp)) != EOK) {
266            LOG(ERROR) << "Block device name overflow";
267            return false;
268        }
269    }
270
271    blkdevparts[strlen(blkdevparts) - 1] = '\0';
272    LOG(INFO) << "blkdevparts is " << blkdevparts;
273
274    return WriteMiscMsgWithOffset(std::string(blkdevparts), MISC_RECORD_UPDATE_PARTITIONS_OFFSET);
275}
276
277static bool AddPartitions(const Disk &disk, const PartitonList &ulist, int &partitionAddedCounter)
278{
279    if (!ulist.empty()) {
280        int userNum = GetPartitionNumByPartName(USERDATA_PARTNAME, disk.partList);
281        int step = 1;
282        char pdevname[DEVPATH_SIZE] = {0};
283        for (auto& p2 : ulist) {
284            if (p2->partName == USERDATA_PARTNAME) {
285                LOG(INFO) << "Change userdata image is not support.";
286                continue;
287            }
288            if (p2->partName == UPDATER_PARTNAME) {
289                LOG(ERROR) << "Change updater image is not supported.";
290                continue;
291            }
292            p2->partNum = userNum + step;
293            if (snprintf_s(pdevname, sizeof(pdevname), sizeof(pdevname) - 1, "%sp%d", MMC_DEV, p2->partNum) == -1) {
294                return false;
295            }
296            p2->devName.clear();
297            p2->devName = pdevname;
298            LOG(INFO) << "Adding partition " << p2->partName;
299            if (!DoAddPartition (disk, *p2)) {
300                LOG(ERROR) << "Add partition fail for " << p2->partName;
301                return false;
302            }
303            step++;
304            partitionAddedCounter++;
305        }
306    }
307    return true;
308}
309
310static bool RemovePartitions(const Disk &disk, int &partitionRemovedCounter)
311{
312    PartitonList pList = disk.partList;
313    for (const auto &it : pList) {
314        if (it->changeType == NOT_CHANGE) {
315            continue;
316        }
317        if (it->partName == UPDATER_PARTNAME) {
318            LOG(ERROR) << "Cannot delete updater partition.";
319            continue;
320        }
321
322        if (it->partName == USERDATA_PARTNAME) {
323            LOG(INFO) << "Cannot delete userdata partition.";
324            continue;
325        }
326        if (DoUmountDiskPartition(*it) == 0) {
327            continue;
328        }
329        LOG(INFO) << "Removing partition " << it->partName;
330        if (!DoRmPartition (disk, it->partNum)) {
331            LOG(ERROR) << "Remove partition failed.";
332            return false;
333        }
334        partitionRemovedCounter++;
335    }
336    return true;
337}
338
339int CheckDevicePartitions(const std::string &path)
340{
341    if (DiskAlloc(path) == 0) {
342        LOG(ERROR) << "path not exist" << path;
343        return 0;
344    }
345    if (ProbeAllPartitions() == 0) {
346        LOG(ERROR) << "partition sum  is zero!";
347        return 0;
348    }
349    return 1;
350}
351
352int AdjustPartitions(Disk *disk, int &partitionChangedCounter)
353{
354    PartitonList ulist;
355    ulist.clear();
356
357    if (disk == nullptr || BlockDiskOpen(*disk) < 0) {
358        return 0;
359    }
360
361    if (GetRegisterUpdaterPartitionList(ulist) == 0) {
362        LOG(ERROR) << "get updater list fail!";
363        return 0;
364    }
365
366    if (!RemovePartitions(*disk, partitionChangedCounter)) {
367        return 0;
368    }
369
370    BlockSync(*disk);
371    if (!AddPartitions(*disk, ulist, partitionChangedCounter)) {
372        return 0;
373    }
374    BlockSync(*disk);
375    return 1;
376}
377
378int DoPartitions(PartitonList &nlist)
379{
380    LOG(INFO) << "do_partitions start";
381    if (nlist.empty()) {
382        LOG(ERROR) << "newpartitionlist is empty ";
383        return 0;
384    }
385
386    const std::string path = MMC_PATH;
387    if (CheckDevicePartitions(path) == 0) {
388        return 0;
389    }
390
391    Disk *disk = GetRegisterBlockDisk(path);
392    if (disk == nullptr) {
393        LOG(ERROR) << "getRegisterdisk fail! ";
394        return 0;
395    }
396    if (RegisterUpdaterPartitionList(nlist, disk->partList) == 0) {
397        LOG(ERROR) << "register updater list fail!";
398        free(disk);
399        return 0;
400    }
401
402    ON_SCOPE_EXIT(clearresource) {
403        BlockDiskClose(*disk);
404        DestroyDiskPartitions(*disk);
405        DestroyDiskDevices(*disk);
406        free(disk);
407    };
408
409    int partitionChangedCounter = 1;
410    if (AdjustPartitions(disk, partitionChangedCounter) == 0) {
411        return 0;
412    }
413
414    (void)WriteDiskPartitionToMisc(nlist);
415    return partitionChangedCounter;
416}
417} // Updater
418
419