• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fcntl.h>
18 #include <memory>
19 #include <string>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include "file_metadata_stream.h"
26 #include "image_log.h"
27 #include "image_utils.h"
28 #include "metadata_stream.h"
29 
30 #undef LOG_DOMAIN
31 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
32 
33 #undef LOG_TAG
34 #define LOG_TAG "FileMetadataStream"
35 
36 namespace OHOS {
37 namespace Media {
FWrite(const void * src,size_t size,ssize_t nmemb,FILE * file)38 ssize_t FileWrapper::FWrite(const void *src, size_t size, ssize_t nmemb, FILE *file)
39 {
40     return ::fwrite(src, size, nmemb, file);
41 }
42 
FRead(void * destv,size_t size,ssize_t nmemb,FILE * file)43 ssize_t FileWrapper::FRead(void *destv, size_t size, ssize_t nmemb, FILE *file)
44 {
45     return ::fread(destv, size, nmemb, file);
46 }
47 
FileMetadataStream(const std::string & filePath,const std::string & originalPath)48 FileMetadataStream::FileMetadataStream(const std::string &filePath, const std::string &originalPath)
49 {
50     initPath_ = INIT_FROM_PATH;
51     Initialize(filePath);
52     originalPath_ = originalPath;
53 }
54 
FileMetadataStream(int fileDescriptor,int originalFd)55 FileMetadataStream::FileMetadataStream(int fileDescriptor, int originalFd)
56 {
57     initPath_ = INIT_FROM_FD;
58     Initialize("", fileDescriptor);
59     originalFd_ = originalFd;
60 }
61 
FileMetadataStream(const std::string & filePath,std::unique_ptr<FileWrapper> fileWrapper)62 FileMetadataStream::FileMetadataStream(const std::string &filePath, std::unique_ptr<FileWrapper> fileWrapper)
63     : fp_(nullptr), fileWrapper_(std::move(fileWrapper))
64 {
65     initPath_ = INIT_FROM_PATH;
66     Initialize(filePath);
67 }
68 
~FileMetadataStream()69 FileMetadataStream::~FileMetadataStream()
70 {
71     Close();
72 }
73 
Initialize(const std::string & filePath,int fileDescriptor)74 void FileMetadataStream::Initialize(const std::string &filePath, int fileDescriptor)
75 {
76     this->fp_ = nullptr;
77     this->filePath_ = filePath;
78     this->dupFD_ = dup(fileDescriptor);
79     if (!fileWrapper_) {
80         this->fileWrapper_ = std::make_unique<FileWrapper>();
81     }
82     mappedMemory_ = nullptr;
83 }
84 
85 // Error handling function
HandleFileError(const std::string & operation,const std::string & filePath,int fileDescriptor,ssize_t result,ssize_t expectedSize)86 void HandleFileError(const std::string &operation, const std::string &filePath, int fileDescriptor, ssize_t result,
87     ssize_t expectedSize)
88 {
89     std::string buf(METADATA_STREAM_ERROR_BUFFER_SIZE, '\0');
90     strerror_r(errno, &buf[0], buf.size());
91 
92     if (fileDescriptor != -1) { // If the operation is through a file descriptor
93         IMAGE_LOGE("%{public}s file failed: %{public}d, reason: "
94             "%{public}s. result is %{public}zd, expected size is %{public}zd",
95             operation.c_str(), fileDescriptor, buf.c_str(), result, expectedSize);
96     } else { // If the operation is through a file path
97         IMAGE_LOGE("%{public}s file failed: %{public}s, reason: "
98             "%{public}s. result is %{public}zd, expected size is %{public}zd",
99             operation.c_str(), filePath.c_str(), buf.c_str(), result, expectedSize);
100     }
101 }
102 
Write(byte * data,ssize_t size)103 ssize_t FileMetadataStream::Write(byte *data, ssize_t size)
104 {
105     if (fp_ == nullptr) {
106         HandleFileError("Write", filePath_, -1, -1, size);
107         return -1;
108     }
109 
110     ssize_t result = fileWrapper_->FWrite(data, 1, size, fp_);
111     if (result != size || (ferror(fp_) != 0)) {
112         HandleFileError("Write", filePath_, (initPath_ == INIT_FROM_FD) ? dupFD_ : -1, result, size);
113         return -1;
114     }
115 
116     return result;
117 }
118 
Read(byte * buf,ssize_t size)119 ssize_t FileMetadataStream::Read(byte *buf, ssize_t size)
120 {
121     if (fp_ == nullptr) {
122         HandleFileError("Read", filePath_, -1, -1, size);
123         return -1;
124     }
125     if (size == 0) {
126         return 0;
127     }
128 
129     ssize_t result = fileWrapper_->FRead(buf, 1, size, fp_);
130     if (result == 0 && ferror(fp_) != 0) {
131         HandleFileError("Read", filePath_, (initPath_ == INIT_FROM_FD) ? dupFD_ : -1, result, size);
132         return -1;
133     }
134 
135     return result;
136 }
137 
ReadByte()138 int FileMetadataStream::ReadByte()
139 {
140     if (fp_ == nullptr) {
141         HandleFileError("ReadByte", filePath_, -1, -1, 1);
142         return -1;
143     }
144 
145     int byte = fgetc(fp_);
146     if (byte == EOF) {
147         HandleFileError("ReadByte", filePath_, (initPath_ == INIT_FROM_FD) ? dupFD_ : -1, byte, 1);
148         return -1;
149     }
150 
151     return byte;
152 }
153 
Seek(long offset,SeekPos pos)154 long FileMetadataStream::Seek(long offset, SeekPos pos)
155 {
156     if (fp_ == nullptr) {
157         HandleFileError("Seek", filePath_, -1, -1, offset);
158         return -1;
159     }
160 
161     int origin;
162     switch (pos) {
163         case SeekPos::BEGIN:
164             origin = SEEK_SET;
165             break;
166         case SeekPos::CURRENT:
167             origin = SEEK_CUR;
168             break;
169         case SeekPos::END:
170             origin = SEEK_END;
171             break;
172         default:
173             return -1;
174     }
175 
176     int result = fseek(fp_, offset, origin);
177     if (result != 0) {
178         HandleFileError("Seek", filePath_, (initPath_ == INIT_FROM_FD) ? dupFD_ : -1, result, offset);
179         return -1;
180     }
181 
182     return ftell(fp_);
183 }
184 
Tell()185 long FileMetadataStream::Tell()
186 {
187     if (fp_ == nullptr) {
188         if (initPath_ == INIT_FROM_FD) {
189             IMAGE_LOGE("Tell file failed: %{public}d, reason: %{public}s", dupFD_, "fp is nullptr");
190         } else if (initPath_ == INIT_FROM_PATH) {
191             IMAGE_LOGE("Tell file failed: %{public}s, reason: %{public}s", filePath_.c_str(), "fp is nullptr");
192         } else if (initPath_ == INIT_FROM_UNKNOWN) {
193             IMAGE_LOGE("Tell file failed: %{public}s, reason: %{public}s", "initPath is INIT_FROM_UNKNOWN",
194                 "fp is nullptr");
195         }
196         IMAGE_LOGE("Tell file failed: %{public}s, reason: %{public}s", filePath_.c_str(), "fp is nullptr");
197         return -1;
198     }
199 
200     return ftell(fp_);
201 }
202 
IsEof()203 bool FileMetadataStream::IsEof()
204 {
205     if (fp_ == nullptr) {
206         HandleFileError("Check EOF", "", -1, -1, -1);
207         return true;
208     }
209 
210     if (ferror(fp_) != 0) {
211         HandleFileError("Check EOF", "", fileno(fp_), -1, -1);
212         return true;
213     }
214 
215     long currentPos = ftell(fp_);
216     if (Seek(0, SeekPos::END) == -1) {
217         return true;
218     }
219 
220     long fileSize = ftell(fp_);
221 
222     bool isEof = currentPos == fileSize;
223 
224     if (Seek(currentPos, SeekPos::BEGIN) == -1) {
225         return true;
226     }
227 
228     return isEof;
229 }
230 
IsOpen()231 bool FileMetadataStream::IsOpen()
232 {
233     return fp_ != nullptr;
234 }
235 
Close()236 void FileMetadataStream::Close()
237 {
238     ReleaseAddr();
239 
240     // If the file is not open, return directly
241     if (fp_ != nullptr) {
242         fclose(fp_);
243         fp_ = nullptr;
244     }
245 
246     // Reset all member variables
247     if (initPath_ == INIT_FROM_FD) {
248         IMAGE_LOGD("File closed: %{public}d", dupFD_);
249     } else if (initPath_ == INIT_FROM_PATH) {
250         IMAGE_LOGD("File closed: %{public}s", filePath_.c_str());
251     }
252 
253     // Close the file
254     if (dupFD_ != -1) {
255         close(dupFD_);
256         dupFD_ = -1;
257     }
258     initPath_ = INIT_FROM_UNKNOWN;
259 }
260 
OpenFromFD(const char * modeStr)261 bool FileMetadataStream::OpenFromFD(const char *modeStr)
262 {
263     if (dupFD_ == -1) {
264         HandleFileError("Open file", filePath_, -1, -1, -1);
265         return false;
266     }
267 
268     // Decide how to create FILE* fp based on the mode parameter
269     fp_ = fdopen(dupFD_, modeStr);
270     if (fp_ == NULL || ferror(fp_) != 0) {
271         HandleFileError("Open file", filePath_, dupFD_, -1, -1);
272         return false;
273     }
274     IMAGE_LOGD("File opened: %{public}d", dupFD_);
275 
276     return true;
277 }
278 
OpenFromPath(const char * modeStr)279 bool FileMetadataStream::OpenFromPath(const char *modeStr)
280 {
281     IMAGE_LOGD("Open file: %{public}s, modeStr: %{public}s", filePath_.c_str(), modeStr);
282     fp_ = fopen(filePath_.c_str(), modeStr);
283     if (fp_ == nullptr) {
284         HandleFileError("Open file", filePath_, -1, -1, -1);
285         return false;
286     }
287     IMAGE_LOGD("File opened: %{public}s", filePath_.c_str());
288     return true;
289 }
290 
Open(OpenMode mode)291 bool FileMetadataStream::Open(OpenMode mode)
292 {
293     if (initPath_ == INIT_FROM_UNKNOWN) {
294         IMAGE_LOGE("initPath is INIT_FROM_UNKNOWN. It seems that the file has "
295             "been closed before.");
296         return false;
297     }
298 
299     const char *modeStr;
300     switch (mode) {
301         case OpenMode::Read:
302             modeStr = "r";
303             break;
304         case OpenMode::ReadWrite:
305             modeStr = "r+";
306             break;
307         default:
308             return false;
309     }
310 
311     bool openResult = false;
312     if (initPath_ == INIT_FROM_FD) {
313         IMAGE_LOGD("initPath is INIT_FROM_FD");
314         openResult = OpenFromFD(modeStr);
315     }
316     if (initPath_ == INIT_FROM_PATH) {
317         IMAGE_LOGD("initPath is INIT_FROM_PATH");
318         openResult = OpenFromPath(modeStr);
319     }
320 
321     if (!openResult) {
322         return false;
323     }
324 
325     return true;
326 }
327 
GetAddr(bool isWriteable)328 byte *FileMetadataStream::GetAddr(bool isWriteable)
329 {
330     // If there is already a memory map, return it directly
331     if (mappedMemory_ != nullptr) {
332         IMAGE_LOGE("mmap: There is already a memory mapping, return it directly");
333         return (byte *)mappedMemory_;
334     }
335 
336     // If the file is not open, open it first
337     if (fp_ == nullptr) {
338         HandleFileError("Get memory address", filePath_, -1, -1, -1);
339         return nullptr;
340     }
341 
342     // Get the file descriptor from the file pointer
343     int fileDescriptor = fileno(fp_);
344 
345     // Create a memory map
346     fileSize_ = GetSize();
347     if (fileSize_ <= 0) {
348         mappedMemory_ = nullptr;
349     } else {
350         mappedMemory_ = ::mmap(nullptr, static_cast<size_t>(fileSize_), isWriteable ?
351             (PROT_READ | PROT_WRITE) : PROT_READ, MAP_SHARED, fileDescriptor, 0);
352         if (mappedMemory_ == static_cast<void *>(MAP_FAILED)) {
353             HandleFileError("Create memory mapping", filePath_, fileDescriptor, -1, -1);
354             mappedMemory_ = nullptr;
355         }
356     }
357     IMAGE_LOGD("mmap: Memory mapping created: %{public}s, size: %{public}zu", filePath_.c_str(), fileSize_);
358     return (byte *)mappedMemory_;
359 }
360 
ReleaseAddr()361 bool FileMetadataStream::ReleaseAddr()
362 {
363     if (mappedMemory_ == nullptr) {
364         return true;
365     }
366 
367     if (fileSize_ <= 0) {
368         mappedMemory_ = nullptr;
369         return true;
370     }
371     // Delete the memory map
372     if (munmap(mappedMemory_, static_cast<size_t>(fileSize_)) == -1) {
373         // Memory mapping failed
374         HandleFileError("Remove memory mapping", filePath_, -1, -1, -1);
375         return false;
376     }
377     IMAGE_LOGD("munmap: Memory mapping removed: %{public}s, size: %{public}zu", filePath_.c_str(), fileSize_);
378 
379     mappedMemory_ = nullptr;
380     return true;
381 }
382 
Flush()383 bool FileMetadataStream::Flush()
384 {
385     if (fp_ == nullptr) {
386         HandleFileError("Flush file", filePath_, -1, -1, -1);
387         return false;
388     }
389 
390     if (fflush(fp_) != 0) {
391         HandleFileError("Flush file", filePath_, fileno(fp_), -1, -1);
392         return false;
393     }
394 
395     return true;
396 }
397 
TruncateFile(size_t totalBytesWritten,MetadataStream & src,ssize_t src_cur)398 bool FileMetadataStream::TruncateFile(size_t totalBytesWritten, MetadataStream &src, ssize_t src_cur)
399 {
400     int fileDescriptor = fileno(fp_);
401     if (ftruncate(fileDescriptor, totalBytesWritten) == -1) {
402         HandleFileError("Truncate file", filePath_, fileDescriptor, -1, totalBytesWritten);
403         src.Seek(src_cur, SeekPos::BEGIN); // Restore the position of src
404         return false;
405     }
406     return true;
407 }
408 
CopyDataFromSource(MetadataStream & src,ssize_t & totalBytesWritten)409 bool FileMetadataStream::CopyDataFromSource(MetadataStream &src, ssize_t &totalBytesWritten)
410 {
411     ssize_t buffer_size = std::min((ssize_t)METADATA_STREAM_COPY_FROM_BUFFER_SIZE, src.GetSize());
412     if (buffer_size > METADATA_STREAM_COPY_FROM_BUFFER_SIZE) {
413         return false;
414     }
415     byte *tempBuffer = new (std::nothrow) byte[buffer_size];
416     if (tempBuffer == nullptr) {
417         // Handle memory allocation failure
418         HandleFileError("Memory allocation", filePath_, -1, -1, buffer_size);
419         return false;
420     }
421 
422     Seek(0, SeekPos::BEGIN);
423     src.Seek(0, SeekPos::BEGIN); // Set the position of src to 0
424 
425     bool result = ReadFromSourceAndWriteToFile(src, tempBuffer, buffer_size, totalBytesWritten);
426     delete[] tempBuffer;
427     return result;
428 }
429 
ReadFromSourceAndWriteToFile(MetadataStream & src,byte * tempBuffer,ssize_t buffer_size,ssize_t & totalBytesWritten)430 bool FileMetadataStream::ReadFromSourceAndWriteToFile(MetadataStream &src, byte *tempBuffer, ssize_t buffer_size,
431     ssize_t &totalBytesWritten)
432 {
433     while (!src.IsEof()) {
434         ssize_t bytesRead = src.Read(tempBuffer, buffer_size);
435         if (bytesRead > 0) {
436             ssize_t bytesWritten = Write(tempBuffer, bytesRead);
437             if (bytesWritten == -1) {
438                 // Write failed
439                 HandleFileError("Write file", filePath_, fileno(fp_), bytesWritten, bytesRead);
440                 return false;
441             }
442             totalBytesWritten += bytesWritten;
443         }
444         if (bytesRead < 0 && !src.IsEof()) {
445             HandleFileError("Read file", filePath_, -1, bytesRead, buffer_size);
446             return false;
447         }
448     }
449     return true;
450 }
451 
CopyFrom(MetadataStream & src)452 bool FileMetadataStream::CopyFrom(MetadataStream &src)
453 {
454     ssize_t oldSize = GetSize();
455     if (!src.IsOpen()) {
456         IMAGE_LOGE("transfer: Source file is not open");
457         return false;
458     }
459 
460     if (!IsOpen()) {
461         IMAGE_LOGE("transfer: File is not open: %{public}s", filePath_.c_str());
462         return false;
463     }
464 
465     ssize_t totalBytesWritten = 0;
466     ssize_t src_cur = src.Tell(); // Temporarily store the position of src
467 
468     if (!CopyDataFromSource(src, totalBytesWritten)) {
469         return false;
470     }
471 
472     IMAGE_LOGD("transfer: Write file done: %{public}s, size: %{public}zu", filePath_.c_str(), totalBytesWritten);
473 
474     // Flush the file
475     if (!Flush()) {
476         return false;
477     }
478 
479     // Truncate the file only if totalBytesWritten is less than oldSize
480     if (totalBytesWritten < oldSize) {
481         if (!TruncateFile(totalBytesWritten, src, src_cur)) {
482             return false;
483         }
484     }
485 
486     return true;
487 }
488 
GetSize()489 ssize_t FileMetadataStream::GetSize()
490 {
491     if (fp_ == nullptr) {
492         HandleFileError("GetSize", filePath_, -1, -1, -1);
493         return -1;
494     }
495     ssize_t oldPos = Tell();
496     if (fseek(fp_, 0, SEEK_END) != 0) {
497         std::string errstr(METADATA_STREAM_ERROR_BUFFER_SIZE, '\0');
498         strerror_r(errno, &errstr[0], METADATA_STREAM_ERROR_BUFFER_SIZE);
499         IMAGE_LOGE("Failed to seek to the end of the file: %{public}s", errstr.c_str());
500         return -1;
501     }
502 
503     ssize_t fileSize = ftell(fp_);
504 
505     if (fseek(fp_, oldPos, SEEK_SET) != 0) { // Restore the file pointer to its original position
506         std::string errstr(METADATA_STREAM_ERROR_BUFFER_SIZE, '\0');
507         strerror_r(errno, &errstr[0], METADATA_STREAM_ERROR_BUFFER_SIZE);
508         IMAGE_LOGE("Failed to restore the file position: %{public}s", errstr.c_str());
509         return -1;
510     }
511 
512     return fileSize;
513 }
514 
515 } // namespace Media
516 } // namespace OHOS
517