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