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
16#include "applypatch/partition_record.h"
17#include <cerrno>
18#include <fcntl.h>
19#include <unistd.h>
20#include "fs_manager/mount.h"
21#include "log/log.h"
22#include "securec.h"
23
24namespace Updater {
25PartitionRecord &PartitionRecord::GetInstance()
26{
27    static PartitionRecord partitionRecord;
28    return partitionRecord;
29}
30bool PartitionRecord::IsPartitionUpdated(const std::string &partitionName)
31{
32    auto miscBlockDevice = GetMiscPartitionPath();
33    uint8_t buffer[PARTITION_UPDATER_RECORD_MSG_SIZE];
34    if (!miscBlockDevice.empty()) {
35        char *realPath = realpath(miscBlockDevice.c_str(), NULL);
36        if (realPath == nullptr) {
37            LOG(ERROR) << "realPath is NULL" << " : " << strerror(errno);
38            return false;
39        }
40        int fd = open(realPath, O_RDONLY | O_EXCL | O_CLOEXEC | O_BINARY);
41        free(realPath);
42        if (fd < 0) {
43            LOG(ERROR) << "PartitionRecord: Open misc to recording partition failed" << " : " << strerror(errno);
44            return false;
45        }
46        if (lseek(fd, PARTITION_RECORD_START, SEEK_CUR) < 0) {
47            LOG(ERROR) << "PartitionRecord: Seek misc to specific offset failed" << " : " << strerror(errno);
48            close(fd);
49            return false;
50        }
51        if (read(fd, buffer, PARTITION_UPDATER_RECORD_MSG_SIZE) != PARTITION_UPDATER_RECORD_MSG_SIZE) {
52            LOG(ERROR) << "PartitionRecord: Read from misc partition failed" << " : " << strerror(errno);
53            close(fd);
54            return false;
55        }
56        for (uint8_t *p = buffer; p < buffer + PARTITION_UPDATER_RECORD_MSG_SIZE; p += sizeof(PartitionRecordInfo)) {
57            PartitionRecordInfo *pri = reinterpret_cast<PartitionRecordInfo*>(p);
58            if (strcmp(pri->partitionName, partitionName.c_str()) == 0) {
59                LOG(DEBUG) << "PartitionRecord: Found " << partitionName << " record in misc partition";
60                LOG(DEBUG) << "PartitionRecord: update status: " << pri->updated;
61                close(fd);
62                return pri->updated;
63            }
64        }
65        fsync(fd);
66        close(fd);
67        LOG(INFO) << "PartitionRecord: Cannot found " << partitionName << " record in misc partition";
68    }
69    return false;
70}
71
72bool PartitionRecord::RecordPartitionSetOffset(int fd)
73{
74    off_t newOffset = 0;
75    if (lseek(fd, PARTITION_RECORD_OFFSET, SEEK_SET) < 0) {
76        LOG(ERROR) << "PartitionRecord: Seek misc to record offset failed: " << strerror(errno);
77        return false;
78    }
79    if (read(fd, &newOffset, sizeof(off_t)) != static_cast<ssize_t>(sizeof(off_t))) {
80        LOG(ERROR) << "PartitionRecord: Read offset failed: " << strerror(errno);
81        return false;
82    }
83    offset_ = newOffset;
84    if (lseek(fd, PARTITION_RECORD_START + offset_, SEEK_SET) < 0) {
85        LOG(ERROR) << "PartitionRecord: Seek misc to specific offset failed: " << strerror(errno);
86        return false;
87    }
88    return true;
89}
90
91bool PartitionRecord::RecordPartitionSetInfo(const std::string &partitionName, bool updated, int fd)
92{
93    (void)memset_s(&info_, sizeof(info_), 0, sizeof(info_));
94    if (strncpy_s(info_.partitionName, PARTITION_NAME_LEN, partitionName.c_str(), PARTITION_NAME_LEN - 1) != EOK) {
95        LOG(ERROR) << "PartitionRecord: strncpy_s failed: " << strerror(errno);
96        return false;
97    }
98    info_.updated = updated;
99    if (write(fd, &info_, sizeof(PartitionRecordInfo)) != static_cast<ssize_t>(sizeof(PartitionRecordInfo))) {
100        LOG(ERROR) << "PartitionRecord: write failed: " << strerror(errno);
101        return false;
102    }
103    offset_ += static_cast<off_t>(sizeof(PartitionRecordInfo));
104    if (lseek(fd, PARTITION_RECORD_OFFSET, SEEK_SET) < 0) {
105        LOG(ERROR) << "PartitionRecord: Seek misc to record offset failed: " << strerror(errno);
106        return false;
107    }
108    if (write(fd, &offset_, sizeof(off_t)) != static_cast<ssize_t>(sizeof(off_t))) {
109        LOG(ERROR) << "PartitionRecord: write misc to record offset failed: " << strerror(errno);
110        return false;
111    }
112    return true;
113}
114
115bool PartitionRecord::RecordPartitionUpdateStatus(const std::string &partitionName, bool updated)
116{
117    auto miscBlockDevice = GetMiscPartitionPath();
118    if (!miscBlockDevice.empty()) {
119        char *realPath = realpath(miscBlockDevice.c_str(), NULL);
120        if (realPath == nullptr) {
121            LOG(ERROR) << "realPath is NULL" << " : " << strerror(errno);
122            return false;
123        }
124        int fd = open(realPath, O_RDWR | O_EXCL | O_CLOEXEC | O_BINARY);
125        free(realPath);
126        if (fd < 0) {
127            LOG(ERROR) << "PartitionRecord: Open misc to recording partition failed" << " : " << strerror(errno);
128            return false;
129        }
130        if (!RecordPartitionSetOffset(fd)) {
131            close(fd);
132            return false;
133        }
134        if (offset_ + static_cast<off_t>(sizeof(PartitionRecordInfo)) < PARTITION_UPDATER_RECORD_SIZE) {
135            if (!RecordPartitionSetInfo(partitionName, updated, fd)) {
136                close(fd);
137                return false;
138            }
139            LOG(DEBUG) << "PartitionRecord: offset is " << offset_;
140        } else {
141            LOG(WARNING) << "PartitionRecord: partition record overflow, offset = " << offset_;
142            close(fd);
143            return false;
144        }
145        LOG(DEBUG) << "PartitionRecord: record " << partitionName << " successfully.";
146        fsync(fd);
147        close(fd);
148    }
149    return true;
150}
151
152bool PartitionRecord::ClearRecordPartitionOffset()
153{
154    auto miscBlockDevice = GetMiscPartitionPath();
155    if (!miscBlockDevice.empty()) {
156        int fd = open(miscBlockDevice.c_str(), O_RDWR | O_EXCL | O_CLOEXEC | O_BINARY);
157        if (fd < 0) {
158            LOG(ERROR) << "Open misc to recording partition failed" << " : " << strerror(errno);
159            return false;
160        }
161        if (lseek(fd, PARTITION_RECORD_OFFSET, SEEK_SET) < 0) {
162            LOG(ERROR) << "Seek misc to specific offset failed" << " : " << strerror(errno);
163            close(fd);
164            return false;
165        }
166
167        off_t initOffset = 0;
168        if (write(fd, &initOffset, sizeof(off_t)) != static_cast<ssize_t>(sizeof(off_t))) {
169            LOG(ERROR) << "StartUpdater: Write misc initOffset 0 failed" << " : " << strerror(errno);
170            close(fd);
171            return false;
172        }
173        fsync(fd);
174        close(fd);
175    }
176    return true;
177}
178
179std::string PartitionRecord::GetMiscPartitionPath(const std::string &misc)
180{
181    auto miscBlockDevice = GetBlockDeviceByMountPoint(misc);
182    if (miscBlockDevice.empty()) {
183        LOG(WARNING) << "Can not find misc partition";
184    }
185    return miscBlockDevice;
186}
187} // namespace Updater
188