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