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 #ifndef FRAMEWORKS_INNERKITSIMPL_ACCESSOR_INCLUDE_FILE_METADATA_STREAM_H
17 #define FRAMEWORKS_INNERKITSIMPL_ACCESSOR_INCLUDE_FILE_METADATA_STREAM_H
18 
19 #include <cstddef>
20 #include <cstdint>
21 #include <fstream>
22 #include <memory>
23 #include <sys/stat.h>
24 #include <vector>
25 
26 #include "metadata_stream.h"
27 
28 namespace OHOS {
29 namespace Media {
30 #if defined(FRAMEWORKS_INNERKITSIMPL_ACCESSOR_INCLUDE_FILE_METADATA_STREAM_TESTS_PRIVATE)
31 #define FRAMEWORKS_INNERKITSIMPL_ACCESSOR_INCLUDE_FILE_METADATA_STREAM_PRIVATE_UNLESS_TESTED public
32 #else
33 #define FRAMEWORKS_INNERKITSIMPL_ACCESSOR_INCLUDE_FILE_METADATA_STREAM_PRIVATE_UNLESS_TESTED private
34 #endif
35 
36 /**
37  * This is a helper class for FileMetadataStream. It is used for testing whether exceptions are
38  * properly handled during file read/write errors.
39  */
40 class FileWrapper {
41 public:
~FileWrapper()42     virtual ~FileWrapper() {}
43     /* *
44      * @brief Simulates the fwrite function.
45      * @param src Pointer to the array of elements to be written.
46      * @param size Size in bytes of each element to be written.
47      * @param nmemb Number of elements, each one with a size of 'size' bytes.
48      * @param file Pointer to a FILE object that specifies an output stream.
49      * @return The total number of elements successfully written.
50      */
51     virtual ssize_t FWrite(const void *src, size_t size, ssize_t nmemb, FILE *file);
52 
53     /* *
54      * @brief Simulates the fread function.
55      * @param destv Pointer to a block of memory with a size of at least (size*nmemb) bytes.
56      * @param size Size in bytes of each element to be read.
57      * @param nmemb Number of elements, each one with a size of 'size' bytes.
58      * @param file Pointer to a FILE object that specifies an input stream.
59      * @return The total number of elements successfully read.
60      */
61     virtual ssize_t FRead(void *destv, size_t size, ssize_t nmemb, FILE *file);
62 };
63 
64 /**
65  * @class FileMetadataStream
66  * @brief A class that represents a file-based image stream.
67  * @note This class is not thread-safe. If you need to use the same FileMetadataStream instance from multiple threads,
68  * you must ensure that all access to the instance is properly synchronized.
69  */
70 class FileMetadataStream : public MetadataStream {
71 public:
72     /* *
73      * @brief Constructs a new FileMetadataStream object from a file descriptor.
74      * @param fileDescriptor The file descriptor.
75      */
76     FileMetadataStream(int fileDescriptor, int originalFd = METADATA_STREAM_INVALID_FD);
77 
78     /* *
79      * @brief Constructs a new FileMetadataStream object from a file path.
80      * @param filePath The file path.
81      */
82     FileMetadataStream(const std::string &filePath, const std::string &originalPath = METADATA_STREAM_INVALID_PATH);
83 
84     /* *
85      * @brief Destructs the FileMetadataStream object.
86      */
87     virtual ~FileMetadataStream();
88 
89     /* *
90      * @brief Writes data to the FileMetadataStream.
91      * @param data The data to be written.
92      * @param size The size of the data. On a 32-bit system, the maximum size
93      * that can be written at once is 2GB. On a 64-bit system, the maximum size
94      * depends on the type of ssize_t. If ssize_t is 32-bit, the maximum size
95      * is 2GB. If ssize_t is 64-bit, the maximum size is 8ZB.
96      * @return The number of bytes written. Returns -1 if an error occurred
97      * during writing.
98      */
99     virtual ssize_t Write(byte *data, ssize_t size) override;
100 
101     /* *
102      * @brief Reads data from the FileMetadataStream.
103      * @param buf The buffer to store the data.
104      * @param size The size of the data.
105      * @return The number of bytes read. Returns -1 if a read error occurred
106      * or the pointer was not initialized.
107      */
108     virtual ssize_t Read(byte *buf, ssize_t size) override;
109 
110     /* *
111      * @brief Reads a byte from the FileMetadataStream.
112      * @return The byte read.
113      */
114     virtual int ReadByte() override;
115 
116     /* *
117      * @brief Seeks to a specific position in the FileMetadataStream.
118      * @param offset The offset.
119      * @param pos The starting position of the offset.
120      * @return The new position in the stream. Returns -1 if an error occurred during seeking.
121      */
122     virtual long Seek(long offset, SeekPos pos) override;
123 
124     /* *
125      * @brief Gets the current position in the FileMetadataStream.
126      * @return The current position.
127      */
128     virtual long Tell() override;
129 
130     /* *
131      * @brief Checks if the end of the FileMetadataStream has been reached.
132      * @return true if the end has been reached, false otherwise.
133      */
134     virtual bool IsEof() override;
135 
136     /* *
137      * @brief Checks if the FileMetadataStream is open.
138      * @return true if it is open, false otherwise.
139      */
140     virtual bool IsOpen() override;
141 
142     /* *
143      * @brief Opens the FileMetadataStream with a specific mode.
144      * The Open operation will reset the read and write position of the file.
145      * A file object can only be opened and closed once.
146      * If the file fails to open from the file descriptor or path, it will
147      * return false. If it fails to seek to the end of the file or restore
148      * the file position, it will return false.
149      * @param mode The mode to open the FileMetadataStream. It can be OpenMode::Read
150      * or OpenMode::ReadWrite.
151      * @return true if it opens successfully, false otherwise.
152      */
153     virtual bool Open(OpenMode mode = OpenMode::ReadWrite) override;
154 
155     /* *
156      * @brief Flushes the FileMetadataStream.
157      * The scenarios for using Flush are described in MetadataStream::Flush.
158      * @return true if it flushes successfully, false otherwise.
159      */
160     virtual bool Flush() override;
161 
162     /* *
163      * @brief Creates a memory map of the file.
164      * @param isWriteable If true, the created memory map will be writable;
165      * otherwise, it will be read-only. Writing to a read-only pointer will
166      * result in a segmentation fault.
167      * @return A pointer to the memory map if it is created successfully,
168      * nullptr otherwise.
169      */
170     virtual byte *GetAddr(bool isWriteable = false) override;
171 
172     /* *
173      * @brief Should call Open first. Transfers the content of the source
174      * MetadataStream to the current FileMetadataStream.
175      * Note the buffer size in CopyFrom is currently 4K. When reading from SSDs,
176      * reading only 4K at a time may not fully utilize the IO transfer capabilities.
177      * If performance issues are identified in later testing, this can be adjusted
178      * to a multiple of 4K.
179      * @param src The source MetadataStream.
180      */
181     bool CopyFrom(MetadataStream &src) override;
182 
183     /* *
184      * @brief Get the size of the FileMetadataStream.
185      * @return The size of the FileMetadataStream.
186      * @note If this function is called frequently, it is recommended to cache the size to improve performance.
187      */
188     ssize_t GetSize() override;
189 
190 private:
191     /* *
192      * @brief Closes the FileMetadataStream.
193      */
194     virtual void Close() override;
195 
196     /* *
197      * @brief Releases a memory map.
198      * @param mmap The pointer to the memory map that needs to be released.
199      * @return true if the memory map is released successfully, false otherwise.
200      */
201     bool ReleaseAddr();
202 
203     /* *
204      * @brief Constructs a new FileMetadataStream object from a file path and a
205      * file wrapper.
206      * @param filePath The file path.
207      * @param fileWrapper The file wrapper.
208      */
209     FileMetadataStream(const std::string &filePath, std::unique_ptr<FileWrapper> fileWrapper);
210 
211     /* *
212      * @brief Opens the FileMetadataStream from a file descriptor.
213      * @param modeStr The mode string.
214      * @return true if it opens successfully, false otherwise.
215      */
216     bool OpenFromFD(const char *modeStr);
217 
218     /* *
219      * @brief Opens the FileMetadataStream from a file path.
220      * @param modeStr The mode string.
221      * @return true if it opens successfully, false otherwise.
222      */
223     bool OpenFromPath(const char *modeStr);
224 
225     /* *
226      * @brief Copies data from the source MetadataStream to the current file.
227      * @param src The source MetadataStream.
228      * @param totalBytesWritten The total number of bytes written to the current file.
229      * This function reads data from the source MetadataStream and writes it to the current file.
230      * It uses a temporary buffer of size min(IMAGE_STREAM_PAGE_SIZE, src.GetSize()).
231      * The function continues to read and write data until it reaches the end of the source MetadataStream.
232      * If a write operation fails, it handles the error and returns false.
233      * If a read operation fails and it's not because of reaching the end of the source MetadataStream,
234      * it returns false.
235      * @return true if the data is copied successfully, false otherwise.
236      */
237     bool CopyDataFromSource(MetadataStream &src, ssize_t &totalBytesWritten);
238 
239     /* *
240      * @brief Handles the error when writing data to the current file fails.
241      * @param src The source MetadataStream.
242      * @param src_cur The current position of the source MetadataStream.
243      */
244     void HandleWriteError(MetadataStream &src, ssize_t src_cur);
245 
246     /* *
247      * @brief Truncates the current file to the specified size.
248      * @param totalBytesWritten The new size of the file.
249      * @param src The source MetadataStream.
250      * @param src_cur The current position of the source MetadataStream.
251      * @return true if the file is truncated successfully, false otherwise.
252      */
253     bool TruncateFile(size_t totalBytesWritten, MetadataStream &src, ssize_t src_cur);
254     bool ReadFromSourceAndWriteToFile(MetadataStream &src, byte *tempBuffer, ssize_t buffer_size,
255         ssize_t &totalBytesWritten);
256 
257     /* *
258      * @brief Initializes the FileMetadataStream with a file path and a file descriptor.
259      * @param filePath The path of the file to be opened. Default is an empty string.
260      * @param fileDescriptor The file descriptor of the file to be opened. Default is -1.
261      */
262     void Initialize(const std::string &filePath = "", int fileDescriptor = -1);
263 
264     FILE *fp_;                                 // File descriptor
265     int dupFD_;                                // Duplicated file descriptor
266     std::string filePath_;                     // File path
267     void *mappedMemory_;                       // Address of memory mapping
268     std::unique_ptr<FileWrapper> fileWrapper_; // File wrapper class, used for testing
269 
270     enum {
271         INIT_FROM_FD,
272         INIT_FROM_PATH,
273         INIT_FROM_UNKNOWN,
274     } initPath_;
275 };
276 } // namespace Media
277 } // namespace OHOS
278 #endif // FRAMEWORKS_INNERKITSIMPL_ACCESSOR_INCLUDE_FILE_METADATA_STREAM_H
279