1 /*
2  * Copyright (C) 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 <cerrno>
17 #include <cstring>
18 #include <fcntl.h>
19 #include <new>
20 #include <string>
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 
25 #include "buffer_metadata_stream.h"
26 #include "image_log.h"
27 #include "image_utils.h"
28 #include "metadata_stream.h"
29 #include "securec.h"
30 
31 #undef LOG_DOMAIN
32 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
33 
34 #undef LOG_TAG
35 #define LOG_TAG "BufferMetadataStream"
36 
37 namespace OHOS {
38 namespace Media {
BufferMetadataStream()39 BufferMetadataStream::BufferMetadataStream()
40 {
41     buffer_ = nullptr;
42     capacity_ = 0;
43     bufferSize_ = 0;
44     currentOffset_ = 0;
45     memoryMode_ = Dynamic;
46     originData_ = nullptr;
47 }
48 
BufferMetadataStream(byte * originData,size_t size,MemoryMode mode,int originalFd,const std::string & originalPath)49 BufferMetadataStream::BufferMetadataStream(byte *originData, size_t size, MemoryMode mode,
50                                            int originalFd, const std::string &originalPath)
51 {
52     buffer_ = originData;
53     this->originData_ = originData;
54     capacity_ = static_cast<long>(size);
55     bufferSize_ = static_cast<long>(size);
56     currentOffset_ = 0;
57     memoryMode_ = mode;
58     originalFd_ = originalFd;
59     originalPath_ = originalPath;
60 }
61 
~BufferMetadataStream()62 BufferMetadataStream::~BufferMetadataStream()
63 {
64     Close();
65 }
66 
Write(uint8_t * data,ssize_t size)67 ssize_t BufferMetadataStream::Write(uint8_t *data, ssize_t size)
68 {
69     // Check if the new data will fit into the current buffer
70     if (data == nullptr || size <= 0) {
71         IMAGE_LOGE("BufferMetadataStream::Write failed, data is nullptr or size less than zero");
72         return -1;
73     }
74     if (currentOffset_ + static_cast<long>(size) > capacity_) {
75         if (memoryMode_ == Fix) {
76             IMAGE_LOGE("BufferMetadataStream::Write failed, data size exceeds buffer capacity, "
77                 "currentOffset:%{public}ld, size:%{public}ld, capacity:%{public}ld",
78                 currentOffset_, static_cast<long>(size), capacity_);
79             return -1;
80         }
81 
82         // Calculate the new capacity, ensuring it is a multiple of
83         // BUFFER_IMAGE_STREAM_PAGE_SIZE
84         long newCapacity = CalculateNewCapacity(currentOffset_, size);
85         if (newCapacity > METADATA_STREAM_MAX_CAPACITY) {
86             IMAGE_LOGE("BufferMetadataStream::Write failed, new capacity exceeds maximum capacity, "
87                 "newCapacity:%{public}ld, maxCapacity:%{public}d",
88                 newCapacity, METADATA_STREAM_MAX_CAPACITY);
89             return -1;
90         }
91 
92         // Allocate the new buffer
93         byte *newBuffer = new (std::nothrow) byte[newCapacity];
94 
95         // Check if the allocation was successful
96         if (newBuffer == nullptr) {
97             IMAGE_LOGE("BufferMetadataStream::Write failed, unable to allocate new buffer");
98             return -1;
99         }
100 
101         // Removed std::fill_n for efficiency. If zero-initialization is needed,
102         // consider doing it manually where necessary.
103         // If there is existing data, copy it to the new buffer
104         if (buffer_ != nullptr) {
105             if (EOK != memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_)) {
106                 IMAGE_LOGE("BufferMetadataStream::Write failed, memcpy error");
107                 delete[] newBuffer;
108                 return -1;
109             }
110 
111             // If the old buffer was not externally allocated, delete it
112             if (originData_ != buffer_) {
113                 delete[] buffer_;
114                 buffer_ = nullptr;
115             }
116         }
117 
118         // Update the buffer and capacity
119         buffer_ = newBuffer;
120         capacity_ = newCapacity;
121 
122         // Update expansion count
123         if (memoryMode_ == Dynamic) {
124             expandCount_++;
125         }
126     }
127 
128     // Copy the new data into the buffer
129     if (EOK != memcpy_s(buffer_ + currentOffset_, capacity_ - currentOffset_, data, size)) {
130         IMAGE_LOGE("BufferMetadataStream::Write failed, memcpy error");
131         return -1;
132     }
133 
134     // Update the current offset and buffer size
135     currentOffset_ += size;
136     bufferSize_ = std::max(currentOffset_, bufferSize_);
137 
138     return size;
139 }
140 
Read(uint8_t * buf,ssize_t size)141 ssize_t BufferMetadataStream::Read(uint8_t *buf, ssize_t size)
142 {
143     if (buffer_ == nullptr || currentOffset_ == bufferSize_) {
144         return -1;
145     }
146 
147     if (currentOffset_ > bufferSize_) {
148         IMAGE_LOGE("BufferMetadataStream::Read failed, current offset exceeds buffer size, "
149             "currentOffset:%{public}ld, bufferSize:%{public}ld",
150             currentOffset_, bufferSize_);
151         return -1;
152     }
153 
154     long bytesToRead = std::min(static_cast<long>(size), bufferSize_ - currentOffset_);
155     if (IsFileChanged()) {
156         return -1;
157     }
158     memcpy_s(buf, size, buffer_ + currentOffset_, bytesToRead);
159     currentOffset_ += bytesToRead;
160     return bytesToRead;
161 }
162 
ReadByte()163 int BufferMetadataStream::ReadByte()
164 {
165     if (buffer_ == nullptr) {
166         return -1;
167     }
168     if (currentOffset_ >= bufferSize_) {
169         IMAGE_LOGE("BufferMetadataStream::ReadByte failed, current offset exceeds buffer size, "
170             "currentOffset:%{public}ld, bufferSize:%{public}ld",
171             currentOffset_, bufferSize_);
172         return -1;
173     }
174 
175     if (currentOffset_ < bufferSize_) {
176         return buffer_[currentOffset_++];
177     }
178     return -1;
179 }
180 
Seek(long offset,SeekPos pos)181 long BufferMetadataStream::Seek(long offset, SeekPos pos)
182 {
183     switch (pos) {
184         case SeekPos::BEGIN:
185             currentOffset_ = offset;
186             break;
187         case SeekPos::CURRENT:
188             currentOffset_ += offset;
189             break;
190         case SeekPos::END:
191             currentOffset_ = bufferSize_ + offset;
192             break;
193         default:
194             return -1;
195     }
196 
197     if (currentOffset_ > bufferSize_) {
198         currentOffset_ = bufferSize_;
199     }
200 
201     return currentOffset_;
202 }
203 
Tell()204 long BufferMetadataStream::Tell()
205 {
206     return currentOffset_;
207 }
208 
IsEof()209 bool BufferMetadataStream::IsEof()
210 {
211     return currentOffset_ >= bufferSize_;
212 }
213 
IsOpen()214 bool BufferMetadataStream::IsOpen()
215 {
216     return true;
217 }
218 
Close()219 void BufferMetadataStream::Close()
220 {
221     if (memoryMode_ == Dynamic && buffer_ != originData_ && buffer_ != nullptr) {
222         delete[] buffer_;
223         buffer_ = nullptr;
224     }
225     currentOffset_ = 0;
226 }
227 
Open(OpenMode mode)228 bool BufferMetadataStream::Open(OpenMode mode)
229 {
230     return true;
231 }
232 
Flush()233 bool BufferMetadataStream::Flush()
234 {
235     return true;
236 }
237 
GetAddr(bool isWriteable)238 byte *BufferMetadataStream::GetAddr(bool isWriteable)
239 {
240     return buffer_;
241 }
242 
CopyFrom(MetadataStream & src)243 bool BufferMetadataStream::CopyFrom(MetadataStream &src)
244 {
245     if (!src.IsOpen()) {
246         IMAGE_LOGE("BufferMetadataStream::CopyFrom failed, source stream is not open");
247         return false;
248     }
249     if (src.GetSize() == 0) {
250         return true;
251     }
252     if (memoryMode_ == Fix) {
253         if (src.GetSize() > static_cast<ssize_t>(capacity_)) {
254             // If the memory is fixed and the source size is too large, do not copy the data
255             IMAGE_LOGE("BufferMetadataStream::CopyFrom failed, source size is larger than capacity, "
256                 "source size:%{public}zu, capacity:%{public}ld",
257                 src.GetSize(), capacity_);
258             return false;
259         }
260     }
261 
262     // Clear the current buffer
263     if (memoryMode_ == Dynamic) {
264         if (buffer_ != nullptr && buffer_ != originData_) {
265             delete[] buffer_;
266             buffer_ = nullptr;
267         }
268         ssize_t estimatedSize = ((src.GetSize() + METADATA_STREAM_PAGE_SIZE - 1) / METADATA_STREAM_PAGE_SIZE) *
269             METADATA_STREAM_PAGE_SIZE; // Ensure it is a multiple of 32k
270         buffer_ = new (std::nothrow) byte[estimatedSize];
271         if (buffer_ == nullptr) {
272             IMAGE_LOGE("BufferMetadataStream::CopyFrom failed, insufficient memory for buffer allocation");
273             return false;
274         }
275         capacity_ = estimatedSize;
276     }
277 
278     currentOffset_ = 0;
279     bufferSize_ = 0;
280 
281     // Read data from the source ImageStream and write it to the current buffer
282     if (!ReadAndWriteData(src)) {
283         return false;
284     }
285     return true;
286 }
287 
ReadAndWriteData(MetadataStream & src)288 bool BufferMetadataStream::ReadAndWriteData(MetadataStream &src)
289 {
290     src.Seek(0, SeekPos::BEGIN);
291     ssize_t buffer_size = std::min((ssize_t)METADATA_STREAM_PAGE_SIZE, src.GetSize());
292     if (buffer_size > METADATA_STREAM_PAGE_SIZE) {
293         return false;
294     }
295     byte *tempBuffer = new (std::nothrow) byte[buffer_size];
296     if (tempBuffer == nullptr) {
297         IMAGE_LOGE("BufferMetadataStream::ReadAndWriteData failed, insufficient memory for temporary buffer");
298         return false;
299     }
300     while (!src.IsEof()) {
301         ssize_t bytesRead = src.Read(tempBuffer, buffer_size);
302         if (bytesRead > 0) {
303             if (Write(tempBuffer, bytesRead) == -1) {
304                 IMAGE_LOGE("BufferMetadataStream::ReadAndWriteData failed, unable to write to buffer");
305                 HandleWriteFailure();
306                 delete[] tempBuffer;
307                 return false;
308             }
309         }
310     }
311     delete[] tempBuffer;
312     return true;
313 }
314 
HandleWriteFailure()315 void BufferMetadataStream::HandleWriteFailure()
316 {
317     if (memoryMode_ == Dynamic && buffer_ != originData_) {
318         delete[] buffer_;
319         buffer_ = nullptr;
320         capacity_ = 0;
321     }
322     bufferSize_ = 0;
323     currentOffset_ = 0;
324 }
325 
GetSize()326 ssize_t BufferMetadataStream::GetSize()
327 {
328     return bufferSize_;
329 }
330 
Release()331 byte *BufferMetadataStream::Release()
332 {
333     byte *ret = buffer_;
334     buffer_ = nullptr;
335     capacity_ = 0;
336     bufferSize_ = 0;
337     currentOffset_ = 0;
338     return ret;
339 }
340 
CalculateNewCapacity(long currentOffset,ssize_t size)341 long BufferMetadataStream::CalculateNewCapacity(long currentOffset, ssize_t size)
342 {
343     long newCapacity;
344     switch (expandCount_) {
345         case INITIAL_EXPANSION:
346             newCapacity =
347                 ((currentOffset + size + METADATA_STREAM_INITIAL_CAPACITY - 1) / METADATA_STREAM_INITIAL_CAPACITY) *
348                 METADATA_STREAM_INITIAL_CAPACITY;
349             break;
350         case SECOND_EXPANSION:
351             newCapacity =
352                 ((currentOffset + size + METADATA_STREAM_CAPACITY_512KB - 1) / METADATA_STREAM_CAPACITY_512KB) *
353                 METADATA_STREAM_CAPACITY_512KB;
354             break;
355         case THIRD_EXPANSION:
356             newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_2MB - 1) / METADATA_STREAM_CAPACITY_2MB) *
357                 METADATA_STREAM_CAPACITY_2MB;
358             break;
359         case FOURTH_EXPANSION:
360             newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_5MB - 1) / METADATA_STREAM_CAPACITY_5MB) *
361                 METADATA_STREAM_CAPACITY_5MB;
362             break;
363         case FIFTH_EXPANSION:
364             newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_15MB - 1) / METADATA_STREAM_CAPACITY_15MB) *
365                 METADATA_STREAM_CAPACITY_15MB;
366             break;
367         default:
368             newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_30MB - 1) / METADATA_STREAM_CAPACITY_30MB) *
369                 METADATA_STREAM_CAPACITY_30MB;
370             break;
371     }
372     return newCapacity;
373 }
374 } // namespace Media
375 } // namespace OHOS
376