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