1/*
2 * Copyright (c) 2024-2024 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 <cinttypes>
17
18#include "securec.h"
19#include "signature_info.h"
20#include "signature_tools_log.h"
21#include "digest_common.h"
22//#include "verify_hap_openssl_utils.h"
23#include "random_access_file.h"
24
25namespace OHOS {
26namespace SignatureTools {
27int32_t RandomAccessFile::memoryPageSize = sysconf(_SC_PAGESIZE);
28
29RandomAccessFile::RandomAccessFile()
30    : fd(-1), fileLength(0)
31{
32}
33
34RandomAccessFile::~RandomAccessFile()
35{
36    if (fd != -1) {
37        close(fd);
38    }
39}
40
41bool RandomAccessFile::Init(const std::string& filePath)
42{
43    fd = open(filePath.c_str(), O_RDWR);
44    if (fd == -1) {
45        PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "open file: " + filePath + " failed");
46        return false;
47    }
48
49    if (memoryPageSize <= 0) {
50        PrintErrorNumberMsg("IO_ERROR", IO_ERROR,
51                            "getting pagesize failed. memoryPageSize: " + std::to_string(memoryPageSize));
52        return false;
53    }
54
55    struct stat file_stat;
56    if (fstat(fd, &file_stat) == -1) {
57        PrintErrorNumberMsg("IO_ERROR", IO_ERROR,
58                            filePath + "get status failed");
59        return false;
60    }
61    fileLength = file_stat.st_size;
62
63    return true;
64}
65
66int64_t RandomAccessFile::GetLength() const
67{
68    return fileLength;
69}
70
71bool RandomAccessFile::CheckLittleEndian()
72{
73    union LittleEndian {
74        int32_t number;
75        char ch;
76    } t;
77    t.number = 1;
78    return (t.ch == 1);
79}
80
81int32_t RandomAccessFile::DoMMap(int32_t bufCapacity, int64_t offset, MmapInfo& mmapInfo)
82{
83    if (!CheckLittleEndian()) {
84        SIGNATURE_TOOLS_LOGE("CheckLittleEndian: failed");
85        return MMAP_FAILED;
86    }
87
88    // Starting address for memory mapping
89    mmapInfo.mapAddr = reinterpret_cast<char*>(MAP_FAILED);
90    if (fd == -1) {
91        SIGNATURE_TOOLS_LOGE("random access file's fd is -1, the file is closed");
92        return FILE_IS_CLOSE;
93    }
94    if (offset < 0 || offset > fileLength - bufCapacity) {
95        SIGNATURE_TOOLS_LOGE("offset is less than 0 OR read offset is out of range. offset: %" PRId64
96                             ", range: %" PRId64, offset, fileLength - bufCapacity);
97        return READ_OFFSET_OUT_OF_RANGE;
98    }
99    // Memory mapped file offset, 0 OR an integer multiple of 4K
100    mmapInfo.mmapPosition = (offset / memoryPageSize) * memoryPageSize;
101    // How many more bytes can be read from the current mapped memory page to find
102    mmapInfo.readMoreLen = static_cast<int>(offset - mmapInfo.mmapPosition);
103    mmapInfo.mmapSize = bufCapacity + mmapInfo.readMoreLen;
104    mmapInfo.mapAddr = reinterpret_cast<char*>(mmap(nullptr, mmapInfo.mmapSize, PROT_READ | PROT_WRITE,
105                                                    MAP_SHARED | MAP_POPULATE, fd, mmapInfo.mmapPosition));
106    if (mmapInfo.mapAddr == MAP_FAILED) {
107        SIGNATURE_TOOLS_LOGE("mmap failed");
108        return MMAP_FAILED;
109    }
110    return 0;
111}
112
113int32_t RandomAccessFile::ReadFileFullyFromOffset(char buf[], int64_t offset, int64_t bufCapacity)
114{
115    if (buf == nullptr) {
116        SIGNATURE_TOOLS_LOGE("The dest buffer is null");
117        return DEST_BUFFER_IS_NULL;
118    }
119
120    MmapInfo mmapInfo;
121    int32_t ret = DoMMap(bufCapacity, offset, mmapInfo);
122    if (ret < 0) {
123        return ret;
124    }
125
126    if (memcpy_s(buf, bufCapacity, mmapInfo.mapAddr + mmapInfo.readMoreLen,
127                 mmapInfo.mmapSize - mmapInfo.readMoreLen) != EOK) {
128        munmap(mmapInfo.mapAddr, mmapInfo.mmapSize);
129        SIGNATURE_TOOLS_LOGE("memcpy_s error, errno: %d", IO_ERROR);
130        return MMAP_COPY_FAILED;
131    }
132    munmap(mmapInfo.mapAddr, mmapInfo.mmapSize);
133    return bufCapacity;
134}
135
136int32_t RandomAccessFile::ReadFileFullyFromOffset(ByteBuffer& buffer, int64_t offset)
137{
138    if (!buffer.HasRemaining()) {
139        SIGNATURE_TOOLS_LOGE("The dest buffer has not remaining");
140        return DEST_BUFFER_IS_NULL;
141    }
142
143    MmapInfo mmapInfo;
144    int32_t bufCapacity = buffer.GetCapacity();
145    int64_t ret = DoMMap(bufCapacity, offset, mmapInfo);
146    if (ret < 0) {
147        return ret;
148    }
149
150    buffer.PutData(0, mmapInfo.mapAddr + mmapInfo.readMoreLen, bufCapacity);
151    munmap(mmapInfo.mapAddr, mmapInfo.mmapSize);
152    return bufCapacity;
153}
154
155int32_t RandomAccessFile::WriteToFile(ByteBuffer& buffer, int64_t position, int64_t length)
156{
157    // write file, file length may change
158    int64_t remainLength = fileLength - position;
159    fileLength = (length <= remainLength) ? fileLength : (fileLength + (length - remainLength));
160    // update file length
161    if (ftruncate(fd, fileLength) == -1) {
162        SIGNATURE_TOOLS_LOGE("RandomAccessFile ftruncate error: %s", strerror(errno));
163        return IO_ERROR;
164    }
165
166    int32_t bufCapacity = buffer.GetCapacity();
167    if (bufCapacity == 0) {
168        SIGNATURE_TOOLS_LOGE("The dest buffer capacity is 0");
169        return DEST_BUFFER_IS_NULL;
170    }
171
172    MmapInfo mmapInfo;
173
174    int32_t ret = DoMMap(bufCapacity, position, mmapInfo);
175    if (ret < 0) {
176        SIGNATURE_TOOLS_LOGE("DoMMap failed: %d", ret);
177        return ret;
178    }
179
180    if (memcpy_s(mmapInfo.mapAddr + mmapInfo.readMoreLen,
181                 mmapInfo.mmapSize - mmapInfo.readMoreLen,
182        buffer.GetBufferPtr(), bufCapacity) != RET_OK) {
183        SIGNATURE_TOOLS_LOGE("memcpy_s error, errno: %d", IO_ERROR);
184        return MMAP_COPY_FAILED;
185    }
186    munmap(mmapInfo.mapAddr, mmapInfo.mmapSize);
187    return bufCapacity;
188}
189
190bool RandomAccessFile::ReadFileFromOffsetAndDigestUpdate(const DigestParameter& digestParam,
191                                                         int32_t chunkSize, int64_t offset)
192{
193    MmapInfo mmapInfo;
194    int32_t ret = DoMMap(chunkSize, offset, mmapInfo);
195    if (ret < 0) {
196        SIGNATURE_TOOLS_LOGE("DoMMap failed: %d", ret);
197        return false;
198    }
199    unsigned char* content = reinterpret_cast<unsigned char*>(mmapInfo.mapAddr + mmapInfo.readMoreLen);
200    bool res = DigestCommon::DigestUpdate(digestParam, content, chunkSize);
201    munmap(mmapInfo.mapAddr, mmapInfo.mmapSize);
202    return res;
203}
204}
205}