1 /*
2  * Copyright (C) 2021 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 "file_source_stream.h"
17 
18 #include <cerrno>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 
23 #include "directory_ex.h"
24 #include "file_packer_stream.h"
25 #include "image_log.h"
26 #include "image_utils.h"
27 #include "media_errors.h"
28 
29 #if !defined(_WIN32) && !defined(_APPLE) &&!defined(IOS_PLATFORM) &&!defined(ANDROID_PLATFORM)
30 #include <sys/mman.h>
31 #define SUPPORT_MMAP
32 #endif
33 
34 const unsigned HMDFS_IOC = 0xf2;
35 #define HMDFS_IOC_GET_LOCATION _IOR(HMDFS_IOC, 7, unsigned int)
36 
37 #undef LOG_DOMAIN
38 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
39 
40 #undef LOG_TAG
41 #define LOG_TAG "FileSourceStream"
42 
43 namespace OHOS {
44 namespace Media {
45 using namespace std;
46 using namespace ImagePlugin;
47 
48 constexpr int INVALID_POSITION = -1;
49 constexpr int IOCTL_SUCCESS = 0;
50 constexpr int LOCAL_FILE_POSITION = 1;
51 
FileSourceStream(std::FILE * file,size_t size,size_t offset,size_t original,bool useMmap,int originalFd)52 FileSourceStream::FileSourceStream(std::FILE *file, size_t size, size_t offset, size_t original,
53                                    bool useMmap, int originalFd)
54     : filePtr_(file), fileSize_(size), fileOffset_(offset), fileOriginalOffset_(original),
55     useMmap_(useMmap)
56 {
57     originalFd_ = originalFd;
58 }
59 
FileSourceStream(std::FILE * file,size_t size,size_t offset,size_t original,bool useMmap,const std::string & originalPath)60 FileSourceStream::FileSourceStream(std::FILE *file, size_t size, size_t offset, size_t original,
61                                    bool useMmap, const std::string &originalPath)
62     : filePtr_(file), fileSize_(size), fileOffset_(offset), fileOriginalOffset_(original),
63     useMmap_(useMmap)
64 {
65     originalPath_ = originalPath;
66 }
67 
~FileSourceStream()68 FileSourceStream::~FileSourceStream()
69 {
70     IMAGE_LOGD("[FileSourceStream]destructor enter.");
71     if (filePtr_ != nullptr) {
72         fclose(filePtr_);
73         filePtr_ = nullptr;
74     }
75     ResetReadBuffer();
76 }
77 
CreateSourceStream(const string & pathName)78 unique_ptr<FileSourceStream> FileSourceStream::CreateSourceStream(const string &pathName)
79 {
80     string realPath;
81     if (!PathToRealPath(pathName, realPath)) {
82         IMAGE_LOGE("[FileSourceStream]input the file path exception, errno:%{public}d.", errno);
83         return nullptr;
84     }
85     int fd = open(realPath.c_str(), O_RDONLY);
86     bool useMmap = false;
87     if (fd >= 0) {
88         useMmap = ShouldUseMmap(fd);
89         close(fd);
90     }
91     FILE *filePtr = fopen(realPath.c_str(), "rb");
92     if (filePtr == nullptr) {
93         IMAGE_LOGE("[FileSourceStream]open file fail.");
94         return nullptr;
95     }
96     size_t size = 0;
97     if (!ImageUtils::GetFileSize(realPath, size)) {
98         IMAGE_LOGE("[FileSourceStream]get the file size fail");
99         fclose(filePtr);
100         return nullptr;
101     }
102     int64_t offset = ftell(filePtr);
103     if (offset < 0) {
104         IMAGE_LOGE("[FileSourceStream]get the position fail.");
105         fclose(filePtr);
106         return nullptr;
107     }
108     return make_unique<FileSourceStream>(filePtr, size, offset, offset, useMmap, realPath);
109 }
110 
CreateSourceStream(const int fd)111 unique_ptr<FileSourceStream> FileSourceStream::CreateSourceStream(const int fd)
112 {
113     int dupFd = dup(fd);
114     if (dupFd < 0) {
115         IMAGE_LOGE("[FileSourceStream]Fail to dup fd.");
116         return nullptr;
117     }
118 
119     FILE *filePtr = fdopen(dupFd, "rb");
120     if (filePtr == nullptr) {
121         IMAGE_LOGE("[FileSourceStream]open file fail.");
122         return nullptr;
123     }
124     bool useMmap = ShouldUseMmap(fd);
125     size_t size = 0;
126     if (!ImageUtils::GetFileSize(dupFd, size)) {
127         IMAGE_LOGE("[FileSourceStream]get the file size fail. dupFd=%{public}d", dupFd);
128         fclose(filePtr);
129         return nullptr;
130     }
131 
132     int ret = fseek(filePtr, 0, SEEK_SET);
133     if (ret != 0) {
134         IMAGE_LOGE("[FileSourceStream]Go to 0 position fail, ret:%{public}d.", ret);
135     }
136 
137     int64_t offset = ftell(filePtr);
138     if (offset < 0) {
139         IMAGE_LOGE("[FileSourceStream]get the position fail.");
140         fclose(filePtr);
141         return nullptr;
142     }
143     return make_unique<FileSourceStream>(filePtr, size, offset, offset, useMmap, dupFd);
144 }
145 
CreateSourceStream(const int fd,int32_t offset,int32_t length)146 unique_ptr<FileSourceStream> FileSourceStream::CreateSourceStream(
147     const int fd, int32_t offset, int32_t length)
148 {
149     int dupFd = dup(fd);
150     if (dupFd < 0) {
151         IMAGE_LOGE("[FileSourceStream]Fail to dup fd.");
152         return nullptr;
153     }
154 
155     FILE *filePtr = fdopen(dupFd, "rb");
156     if (filePtr == nullptr) {
157         IMAGE_LOGE("[FileSourceStream]open file fail.");
158         return nullptr;
159     }
160     bool useMmap = ShouldUseMmap(fd);
161     int ret = fseek(filePtr, offset, SEEK_SET);
162     if (ret != 0) {
163         IMAGE_LOGE("[FileSourceStream]Go to %{public}d position fail, ret:%{public}d.", offset, ret);
164         return nullptr;
165     }
166     return make_unique<FileSourceStream>(filePtr, length, offset, offset, useMmap, dupFd);
167 }
168 
Read(uint32_t desiredSize,DataStreamBuffer & outData)169 bool FileSourceStream::Read(uint32_t desiredSize, DataStreamBuffer &outData)
170 {
171     if (desiredSize == 0 || filePtr_ == nullptr) {
172         IMAGE_LOGE("[FileSourceStream]read stream input parameter exception.");
173         return false;
174     }
175     if (!GetData(desiredSize, outData)) {
176         IMAGE_LOGI("[FileSourceStream]read dataStreamBuffer fail.");
177         return false;
178     }
179     fileOffset_ += outData.dataSize;
180     return true;
181 }
182 
Peek(uint32_t desiredSize,DataStreamBuffer & outData)183 bool FileSourceStream::Peek(uint32_t desiredSize, DataStreamBuffer &outData)
184 {
185     if (desiredSize == 0 || filePtr_ == nullptr) {
186         IMAGE_LOGE("[FileSourceStream]peek stream input parameter exception.");
187         return false;
188     }
189     if (!GetData(desiredSize, outData)) {
190         IMAGE_LOGD("[FileSourceStream]peek dataStreamBuffer fail, desiredSize:%{public}u", desiredSize);
191         return false;
192     }
193     int ret = fseek(filePtr_, fileOffset_, SEEK_SET);
194     if (ret != 0) {
195         IMAGE_LOGE("[FileSourceStream]go to original position fail, ret:%{public}d.", ret);
196         return false;
197     }
198     return true;
199 }
200 
Read(uint32_t desiredSize,uint8_t * outBuffer,uint32_t bufferSize,uint32_t & readSize)201 bool FileSourceStream::Read(uint32_t desiredSize, uint8_t *outBuffer, uint32_t bufferSize, uint32_t &readSize)
202 {
203     if (desiredSize == 0 || outBuffer == nullptr || desiredSize > bufferSize || desiredSize > fileSize_) {
204         IMAGE_LOGE("[FileSourceStream]input parameter exception, desiredSize:%{public}u,"
205             "bufferSize:%{public}u,fileSize_:%{public}zu.", desiredSize, bufferSize, fileSize_);
206         return false;
207     }
208     if (!GetData(desiredSize, outBuffer, bufferSize, readSize)) {
209         IMAGE_LOGD("[FileSourceStream]read outBuffer fail.");
210         return false;
211     }
212     fileOffset_ += readSize;
213     return true;
214 }
215 
Peek(uint32_t desiredSize,uint8_t * outBuffer,uint32_t bufferSize,uint32_t & readSize)216 bool FileSourceStream::Peek(uint32_t desiredSize, uint8_t *outBuffer, uint32_t bufferSize, uint32_t &readSize)
217 {
218     if (desiredSize == 0 || outBuffer == nullptr || desiredSize > bufferSize || desiredSize > fileSize_) {
219         IMAGE_LOGE("[FileSourceStream]input parameter exception, desiredSize:%{public}u,"
220             "bufferSize:%{public}u, fileSize_:%{public}zu.", desiredSize, bufferSize, fileSize_);
221         return false;
222     }
223     if (!GetData(desiredSize, outBuffer, bufferSize, readSize)) {
224         IMAGE_LOGI("[FileSourceStream]peek outBuffer fail.");
225         return false;
226     }
227     if (filePtr_ == nullptr) {
228         return false;
229     }
230     int ret = fseek(filePtr_, fileOffset_, SEEK_SET);
231     if (ret != 0) {
232         IMAGE_LOGE("[FileSourceStream]go to original position fail, ret:%{public}d.", ret);
233         return false;
234     }
235     return true;
236 }
237 
Seek(uint32_t position)238 bool FileSourceStream::Seek(uint32_t position)
239 {
240     if (position > fileSize_) {
241         IMAGE_LOGE("[FileSourceStream]Seek the position greater than the file size, position:%{public}u.",
242             position);
243         return false;
244     }
245     if (filePtr_ == nullptr) {
246         return false;
247     }
248     size_t targetPosition = position + fileOriginalOffset_;
249     fileOffset_ = ((targetPosition < fileSize_) ? targetPosition : fileSize_);
250     int ret = fseek(filePtr_, fileOffset_, SEEK_SET);
251     if (ret != 0) {
252         IMAGE_LOGE("[FileSourceStream]go to offset position fail, ret:%{public}d.", ret);
253         return false;
254     }
255     return true;
256 }
257 
Tell()258 uint32_t FileSourceStream::Tell()
259 {
260     return fileOffset_ - fileOriginalOffset_;
261 }
262 
GetData(uint32_t desiredSize,uint8_t * outBuffer,uint32_t bufferSize,uint32_t & readSize)263 bool FileSourceStream::GetData(uint32_t desiredSize, uint8_t *outBuffer, uint32_t bufferSize, uint32_t &readSize)
264 {
265     if (fileSize_ == fileOffset_) {
266         IMAGE_LOGE("[FileSourceStream]read data finish, offset:%{public}zu ,dataSize%{public}zu.",
267             fileOffset_, fileSize_);
268         return false;
269     }
270     if (filePtr_ == nullptr) {
271         return false;
272     }
273     if (desiredSize > (fileSize_ - fileOffset_)) {
274         desiredSize = fileSize_ - fileOffset_;
275     }
276     size_t bytesRead = fread(outBuffer, sizeof(outBuffer[0]), desiredSize, filePtr_);
277     if (bytesRead < desiredSize) {
278         IMAGE_LOGD("read outBuffer end, bytesRead:%{public}zu, desiredSize:%{public}u, fileSize_:%{public}zu,"
279             "fileOffset_:%{public}zu", bytesRead, desiredSize, fileSize_, fileOffset_);
280         int fRes = ferror(filePtr_);
281         if (fRes) {
282             IMAGE_LOGD("fread failed, ferror:%{public}d", fRes);
283             return false;
284         }
285     }
286     readSize = bytesRead;
287     return true;
288 }
289 
GetData(uint32_t desiredSize,DataStreamBuffer & outData)290 bool FileSourceStream::GetData(uint32_t desiredSize, DataStreamBuffer &outData)
291 {
292     if (fileSize_ == fileOffset_) {
293         IMAGE_LOGE("[FileSourceStream]read finish, offset:%{public}zu ,dataSize%{public}zu.",
294             fileOffset_, fileSize_);
295         return false;
296     }
297 
298     if (desiredSize == 0 || desiredSize > MALLOC_MAX_LENTH) {
299         IMAGE_LOGE("[FileSourceStream]Invalid value, desiredSize out of size.");
300         return false;
301     }
302     if (filePtr_ == nullptr) {
303         return false;
304     }
305 
306     ResetReadBuffer();
307     readBuffer_ = static_cast<uint8_t *>(malloc(desiredSize));
308     if (readBuffer_ == nullptr) {
309         IMAGE_LOGE("[FileSourceStream]malloc the desiredSize fail.");
310         return false;
311     }
312     outData.bufferSize = desiredSize;
313     if (desiredSize > (fileSize_ - fileOffset_)) {
314         desiredSize = fileSize_ - fileOffset_;
315     }
316     size_t bytesRead = fread(readBuffer_, sizeof(uint8_t), desiredSize, filePtr_);
317     if (bytesRead < desiredSize) {
318         IMAGE_LOGD("read outData end, bytesRead:%{public}zu, desiredSize:%{public}u, fileSize_:%{public}zu,"
319             "fileOffset_:%{public}zu", bytesRead, desiredSize, fileSize_, fileOffset_);
320         int fRes = ferror(filePtr_);
321         if (fRes) {
322             IMAGE_LOGD("fread failed, ferror:%{public}d", fRes);
323             free(readBuffer_);
324             readBuffer_ = nullptr;
325             return false;
326         }
327     }
328     outData.inputStreamBuffer = static_cast<uint8_t *>(readBuffer_);
329     outData.dataSize = bytesRead;
330     return true;
331 }
332 
GetStreamSize()333 size_t FileSourceStream::GetStreamSize()
334 {
335     return fileSize_ - fileOriginalOffset_;
336 }
337 
DupFd(FILE * f,int & res)338 static bool DupFd(FILE *f, int &res)
339 {
340     res = fileno(f);
341     if (res < 0) {
342         IMAGE_LOGE("[FileSourceStream]Fail to fileno fd.");
343         return false;
344     }
345     res = dup(res);
346     if (res < 0) {
347         IMAGE_LOGE("[FileSourceStream]Fail to dup fd.");
348         return false;
349     }
350     return true;
351 }
352 
GetDataPtr()353 uint8_t *FileSourceStream::GetDataPtr()
354 {
355     return GetDataPtr(false);
356 }
357 
GetDataPtr(bool populate)358 uint8_t *FileSourceStream::GetDataPtr(bool populate)
359 {
360     if (fileData_ != nullptr) {
361         return fileData_;
362     }
363 
364     if (!useMmap_) {
365         uint32_t size = static_cast<uint32_t>(GetStreamSize());
366         uint8_t* buffer = new (std::nothrow) uint8_t [size];
367         if (buffer == nullptr) {
368             IMAGE_LOGE("[FileSourceStream] Failed to new stream buffer.");
369             return nullptr;
370         }
371         uint32_t savedPosition = Tell();
372         if (!Seek(0)) {
373             IMAGE_LOGE("[FileSourceStream] GetDataPtr seek start failed.");
374             delete[] buffer;
375             return nullptr;
376         }
377         uint32_t readSize = 0;
378         bool retRead = Read(size, buffer, size, readSize);
379         if (!Seek(savedPosition) || !retRead) {
380             IMAGE_LOGE("[FileSourceStream] GetDataPtr read failed.");
381             delete[] buffer;
382             return nullptr;
383         }
384         IMAGE_LOGD("[FileSourceStream] UseMmap is false, read buffer success.");
385         fileData_ = buffer;
386         return fileData_;
387     }
388 
389 #ifdef SUPPORT_MMAP
390     if (filePtr_ == nullptr) {
391         return nullptr;
392     }
393     if (!DupFd(filePtr_, mmapFd_)) {
394         return nullptr;
395     }
396     auto mmptr = ::mmap(nullptr, fileSize_ - fileOriginalOffset_, PROT_READ,
397         populate ? MAP_SHARED | MAP_POPULATE : MAP_SHARED, mmapFd_, fileOriginalOffset_);
398     if (mmptr == MAP_FAILED) {
399         IMAGE_LOGE("[FileSourceStream] mmap failed, errno:%{public}d", errno);
400         close(mmapFd_);
401         return nullptr;
402     }
403     fileData_ = static_cast<uint8_t*>(mmptr);
404 #endif
405     return fileData_;
406 }
407 
GetStreamType()408 uint32_t FileSourceStream::GetStreamType()
409 {
410     return ImagePlugin::FILE_STREAM_TYPE;
411 }
412 
ResetReadBuffer()413 void FileSourceStream::ResetReadBuffer()
414 {
415     if (readBuffer_ != nullptr) {
416         free(readBuffer_);
417         readBuffer_ = nullptr;
418     }
419     if (fileData_ != nullptr && !mmapFdPassedOn_ && useMmap_) {
420 #ifdef SUPPORT_MMAP
421         ::munmap(fileData_, fileSize_ - fileOriginalOffset_);
422         close(mmapFd_);
423 #endif
424     }
425     if (!useMmap_) {
426         delete [] fileData_;
427     }
428     fileData_ = nullptr;
429 }
430 
ToOutputDataStream()431 OutputDataStream* FileSourceStream::ToOutputDataStream()
432 {
433     int dupFd = -1;
434     if (filePtr_ == nullptr) {
435         return nullptr;
436     }
437     if (DupFd(filePtr_, dupFd)) {
438         IMAGE_LOGE("[FileSourceStream] ToOutputDataStream fd failed");
439         return nullptr;
440     }
441     return new (std::nothrow) FilePackerStream(dupFd);
442 }
443 
GetMMapFd()444 int FileSourceStream::GetMMapFd()
445 {
446     mmapFdPassedOn_ = true;
447     return mmapFd_;
448 }
449 
ShouldUseMmap(int fd)450 bool FileSourceStream::ShouldUseMmap(int fd)
451 {
452     int location = INVALID_POSITION;
453     bool useMmap = false;
454     int err = ioctl(fd, HMDFS_IOC_GET_LOCATION, &location);
455     if (err != IOCTL_SUCCESS) {
456         IMAGE_LOGD("[FileSourceStream] ioctl failed, error: %{public}d, errno: %{public}d.", err, errno);
457         return useMmap;
458     }
459 
460     if (location == LOCAL_FILE_POSITION) {
461         useMmap = true;
462     }
463     IMAGE_LOGD("[FileSourceStream] File position: %{public}d.", location);
464     return useMmap;
465 }
466 
467 } // namespace Media
468 } // namespace OHOS
469