• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 #include "mapped_file.h"
16 
17 #include <sys/mman.h>
18 #include "common_mapped_file_errors.h"
19 #include "file_ex.h"
20 #include "utils_log.h"
21 
22 namespace OHOS {
23 namespace Utils {
24 off_t MappedFile::pageSize_ = static_cast<off_t>(sysconf(_SC_PAGESIZE));
25 
MappedFile(std::string & path,MapMode mode,off_t offset,off_t size,const char * hint)26 MappedFile::MappedFile(std::string& path, MapMode mode, off_t offset, off_t size, const char *hint)
27     :path_(path), size_(size), offset_(offset), mode_(mode), hint_(hint) {}
28 
ValidMappedSize(off_t & targetSize,const struct stat & stb)29 bool MappedFile::ValidMappedSize(off_t& targetSize, const struct stat& stb)
30 {
31     off_t max = RoundSize(stb.st_size) - offset_; // Avoid mapped size excessing
32                                                   // that of the file more than a page,
33     if (max > 0) {                                // since write operation on it may raise signal 7.
34         targetSize = targetSize > max ? max : targetSize;
35     } else {
36         return false;
37     }
38 
39     return true;
40 }
NormalizePath()41 bool MappedFile::NormalizePath()
42 {
43     char canonicalPath[PATH_MAX];
44     if (realpath(path_.c_str(), canonicalPath) == nullptr) {
45         if (errno != ENOENT) {
46             UTILS_LOGE("%{public}s get realpath failed.", __FUNCTION__);
47             return false;
48         }
49     } else {
50         path_ = canonicalPath;
51     }
52 
53     return true;
54 }
55 
NormalizeSize()56 bool MappedFile::NormalizeSize()
57 {
58     if (size_ == 0 || size_ < DEFAULT_LENGTH) {
59         UTILS_LOGE("%{public}s: Failed. Invalid mapping size: %{public}lld",
60                    __FUNCTION__, static_cast<long long>(size_));
61         return false;
62     }
63 
64     errno = 0;
65     if (!NormalizePath()) {
66         UTILS_LOGE("%{public}s normalize path failed. %{public}s", __FUNCTION__, strerror(errno));
67         return false;
68     }
69     if (!FileExists(path_)) {
70         if ((mode_ & MapMode::CREATE_IF_ABSENT) == MapMode::DEFAULT) {
71             UTILS_LOGE("%{public}s: Failed. %{public}s", __FUNCTION__, strerror(errno));
72             return false;
73         }
74 
75         if (size_ == DEFAULT_LENGTH) {
76             size_ = PageSize();
77         }
78 
79         isNewFile_ = true;
80     } else {
81         struct stat stb = {0};
82         // Calculate specified mapping size
83         if (stat(path_.c_str(), &stb) != 0) {
84             UTILS_LOGW("%{public}s: Failed. Get file size failed! Mapped size will be that of a page.", __FUNCTION__);
85             size_ = PageSize();
86         }
87 
88         if (size_ == DEFAULT_LENGTH) {
89             size_ = stb.st_size;
90         }
91 
92         // Get valid size
93         if (!ValidMappedSize(size_, stb)) {
94             UTILS_LOGE("%{public}s: Failed. Invalid params. Specified size: %{public}lld, File size: %{public}lld", \
95                        __FUNCTION__, static_cast<long long>(size_), static_cast<long long>(stb.st_size));
96             return false;
97         }
98     }
99 
100     return true;
101 }
102 
NormalizeMode()103 void MappedFile::NormalizeMode()
104 {
105     mode_ &= (MapMode::PRIVATE | MapMode::READ_ONLY | MapMode::CREATE_IF_ABSENT);
106 
107     openFlag_ = O_CLOEXEC;
108     if (mode_ == MapMode::DEFAULT) {
109         mapFlag_ = MAP_SHARED;
110         mapProt_ = PROT_READ | PROT_WRITE;
111         openFlag_ |= O_RDWR;
112     } else {
113         if ((mode_ & MapMode::PRIVATE) != MapMode::DEFAULT) {
114             mapFlag_ = MAP_PRIVATE;
115         } else {
116             mapFlag_ = MAP_SHARED;
117         }
118 
119         if ((mode_ & MapMode::READ_ONLY) != MapMode::DEFAULT) {
120             mapProt_ = PROT_READ;
121             openFlag_ |= O_RDONLY;
122         } else {
123             mapProt_ = PROT_READ | PROT_WRITE;
124             openFlag_ |= O_RDWR;
125         }
126 
127         if ((mode_ & MapMode::CREATE_IF_ABSENT) != MapMode::DEFAULT) {
128             openFlag_ |= O_CREAT;
129         }
130     }
131 }
132 
Normalize()133 ErrCode MappedFile::Normalize()
134 {
135     if (isNormed_) {
136         UTILS_LOGD("%{public}s: Already normalized.", __FUNCTION__);
137         return ERR_INVALID_OPERATION;
138     }
139 
140     // resolve params for mapping region
141     // offset
142     if (offset_ < 0 || (offset_ % PageSize() != 0)) {
143         UTILS_LOGE("%{public}s: Failed. Invalid offset: %{public}lld", __FUNCTION__, static_cast<long long>(offset_));
144         return ERR_INVALID_VALUE;
145     }
146 
147     // size
148     if (!NormalizeSize()) {
149         UTILS_LOGE("%{public}s: Failed. Cannot normalize size.", __FUNCTION__);
150         return ERR_INVALID_VALUE;
151     }
152 
153     // Set open flags, mapping types and protections
154     NormalizeMode();
155 
156     isNormed_ = true;
157     return MAPPED_FILE_ERR_OK;
158 }
159 
OpenFile()160 bool MappedFile::OpenFile()
161 {
162     int fd = open(path_.c_str(), openFlag_, S_IRWXU | S_IRGRP | S_IROTH);
163     if (fd == -1) {
164         UTILS_LOGE("%{public}s: Failed. Cannot open file - %{public}s.", __FUNCTION__, strerror(errno));
165         return false;
166     }
167 
168     if (isNewFile_) {
169         if (!NormalizePath()) {
170             UTILS_LOGE("%{public}s normalize path failed. %{public}s", __FUNCTION__, strerror(errno));
171             return false;
172         }
173         if (ftruncate(fd, EndOffset() + 1) == -1) {
174             UTILS_LOGD("%{public}s: Failed. Cannot change file size: %{public}s.", __FUNCTION__, strerror(errno));
175             if (close(fd) == -1) {
176                 UTILS_LOGW("%{public}s: Failed. Cannot close the file: %{public}s.", \
177                            __FUNCTION__, strerror(errno));
178             }
179             if (unlink(path_.c_str()) == -1) {
180                 UTILS_LOGW("%{public}s: Failed. Cannot unlink the file: %{public}s.", \
181                            __FUNCTION__, strerror(errno));
182             }
183             return false;
184         }
185         isNewFile_ = false;
186     }
187 
188     fd_ = fd;
189     return true;
190 }
191 
Map()192 ErrCode MappedFile::Map()
193 {
194     if (isMapped_) {
195         UTILS_LOGD("%{public}s: Failed. Already mapped.", __FUNCTION__);
196         return ERR_INVALID_OPERATION;
197     }
198 
199     // Normalize params
200     ErrCode res = Normalize();
201     if (res != MAPPED_FILE_ERR_OK && res != ERR_INVALID_OPERATION) {
202         UTILS_LOGD("%{public}s: Normalize Failed.", __FUNCTION__);
203         return res;
204     }
205 
206     // Open file to get its fd
207     if (fd_ == -1) {
208         if (!OpenFile()) {
209             UTILS_LOGD("%{public}s: Open Failed.", __FUNCTION__);
210             return MAPPED_FILE_ERR_FAILED;
211         }
212     }
213 
214     // Try map
215     void* data = MAP_FAILED;
216     do {
217         data = mmap(reinterpret_cast<void*>(const_cast<char *>(hint_)),
218                     static_cast<size_t>(size_),
219                     mapProt_,
220                     mapFlag_,
221                     fd_,
222                     offset_);
223         if (data == MAP_FAILED && hint_ != nullptr) {
224             UTILS_LOGW("%{public}s: Mapping Failed. %{public}s, retry with a null hint.", \
225                        __FUNCTION__, strerror(errno));
226             hint_ = nullptr;
227         } else {
228             break;
229         }
230     } while (true);
231 
232     if (data == MAP_FAILED) {
233         UTILS_LOGE("%{public}s: Mapping Failed. %{public}s", __FUNCTION__, strerror(errno));
234         return MAPPED_FILE_ERR_FAILED;
235     }
236 
237     rStart_ = reinterpret_cast<char*>(data);
238     // set region boundary.
239     rEnd_ = rStart_ + (RoundSize(size_) - 1LL);
240     // set segment start
241     data_ = rStart_;
242     isMapped_ = true;
243 
244     return MAPPED_FILE_ERR_OK;
245 }
246 
Unmap()247 ErrCode MappedFile::Unmap()
248 {
249     if (!isMapped_) {
250         UTILS_LOGD("%{public}s: Failed. Already unmapped.", __FUNCTION__);
251         return ERR_INVALID_OPERATION;
252     }
253 
254     if (!isNormed_) {
255         UTILS_LOGW("%{public}s. Try unmapping with params changed.", __FUNCTION__);
256     }
257 
258     if (munmap(rStart_, static_cast<size_t>(size_)) == -1) {
259         UTILS_LOGD("%{public}s: Failed. %{public}s.", __FUNCTION__, strerror(errno));
260         return MAPPED_FILE_ERR_FAILED;
261     }
262 
263     rStart_ = nullptr;
264     rEnd_ = nullptr;
265     data_ = nullptr;
266     isMapped_ = false;
267     return MAPPED_FILE_ERR_OK;
268 }
269 
SyncFileSize(off_t newSize)270 bool MappedFile::SyncFileSize(off_t newSize)
271 {
272     if (newSize > size_) {
273         struct stat stb = {0};
274         if (stat(path_.c_str(), &stb) != 0) {
275             UTILS_LOGD("%{public}s: Failed. Cannot get file size: %{public}s.", __FUNCTION__, strerror(errno));
276             return false;
277         } else if (offset_ + newSize <= stb.st_size) {
278             UTILS_LOGW("%{public}s: Failed. Unextend file size, no need to sync.", __FUNCTION__);
279         } else {
280             if (ftruncate(fd_, offset_ + newSize) == -1) {
281                 UTILS_LOGD("%{public}s: Failed. Cannot extend file size: %{public}s.", __FUNCTION__, strerror(errno));
282                 return false;
283             }
284         }
285     }
286 
287     return true;
288 }
289 
Resize(off_t newSize,bool sync)290 ErrCode MappedFile::Resize(off_t newSize, bool sync)
291 {
292     if (newSize == DEFAULT_LENGTH) {
293         struct stat stb = {0};
294         if (stat(path_.c_str(), &stb) != 0) {
295             UTILS_LOGW("%{public}s: Failed. Get file size failed! Mapped size will be that of a page.", __FUNCTION__);
296             newSize = PageSize();
297         }
298 
299         if (newSize == DEFAULT_LENGTH) {
300             newSize = stb.st_size;
301         }
302     }
303 
304     if (newSize == 0 || newSize < DEFAULT_LENGTH || newSize == size_) {
305         UTILS_LOGD("%{public}s: Failed. Cannot remap with the same /negative size.", __FUNCTION__);
306         return ERR_INVALID_OPERATION;
307     }
308 
309     if (!isMapped_) {
310         UTILS_LOGD("%{public}s: Failed. Invalid status. mapped:%{public}d, normed:%{public}d", \
311                    __FUNCTION__, isMapped_, isNormed_);
312         return ERR_INVALID_OPERATION;
313     }
314 
315     if (sync) {
316         if (!SyncFileSize(newSize)) {
317             UTILS_LOGD("%{public}s: Sync Failed.", __FUNCTION__);
318             return MAPPED_FILE_ERR_FAILED;
319         }
320     } else {
321         struct stat stb = {0};
322         if (stat(path_.c_str(), &stb) != 0) {
323             UTILS_LOGW("%{public}s: Failed. Cannot get file size: %{public}s.", __FUNCTION__, strerror(errno));
324             return ERR_INVALID_OPERATION;
325         }
326 
327         if (!ValidMappedSize(newSize, stb)) {
328             UTILS_LOGD("%{public}s: Failed. Invalid params.", __FUNCTION__);
329             return ERR_INVALID_VALUE;
330         }
331     }
332 
333     void* newData = mremap(rStart_, static_cast<size_t>(size_), static_cast<size_t>(newSize), MREMAP_MAYMOVE);
334     if (newData == MAP_FAILED) {
335         UTILS_LOGD("%{public}s: Failed. %{public}s", __FUNCTION__, strerror(errno));
336         return MAPPED_FILE_ERR_FAILED;
337     }
338 
339     rStart_ = reinterpret_cast<char*>(newData);
340     size_ = newSize;
341     // set region boundary.
342     rEnd_ = rStart_ + RoundSize(size_) - 1;
343     // set segment start.
344     data_ = rStart_;
345 
346     return MAPPED_FILE_ERR_OK;
347 }
348 
Resize()349 ErrCode MappedFile::Resize()
350 {
351     if (isMapped_) {
352         UTILS_LOGD("%{public}s: Failed. No param changes detected or unmapping required before resize.", __FUNCTION__);
353         return ERR_INVALID_OPERATION;
354     }
355 
356     int res = Map();
357     if (res != MAPPED_FILE_ERR_OK) {
358         UTILS_LOGD("%{public}s: Failed. Remapping failed.", __FUNCTION__);
359         return res;
360     }
361 
362     return MAPPED_FILE_ERR_OK;
363 }
364 
TurnNext()365 ErrCode MappedFile::TurnNext()
366 {
367     if (!isNormed_) {
368         UTILS_LOGD("%{public}s: Failed. Cannot turnNext with params changed.", __FUNCTION__);
369         return ERR_INVALID_OPERATION;
370     }
371 
372     struct stat stb = {0};
373     int ret = stat(path_.c_str(), &stb);
374     if (ret != 0) {
375         UTILS_LOGD("%{public}s: Failed. Get file size failed.", __FUNCTION__);
376         return MAPPED_FILE_ERR_FAILED;
377     }
378     if (EndOffset() + 1 >= stb.st_size) {
379         UTILS_LOGD("%{public}s: Failed. No contents remained.", __FUNCTION__);
380         return ERR_INVALID_OPERATION;
381     }
382 
383     // save original params
384     off_t oldSize = size_;
385     off_t oldOff = offset_;
386     const char* oldHint = hint_;
387 
388     // if mapped, rStart_ and rEnd_ are viable
389     if (isMapped_) {
390         char* curEnd = End();
391         // case 1: remap needed
392         if (curEnd == rEnd_) {
393             // check if larger than exact file size.
394             if (EndOffset() + 1 + size_ > stb.st_size) {
395                 size_ = stb.st_size - EndOffset() - 1;
396             }
397             isNormed_ = false;
398             hint_ = rStart_;
399             offset_ += oldSize;
400 
401             ErrCode res = Unmap();
402             if (res == MAPPED_FILE_ERR_OK) {
403                 res = Resize();
404             }
405             if (res != MAPPED_FILE_ERR_OK) {
406                 UTILS_LOGE("%{public}s Failed. Fail to UnMap/Resize.", __FUNCTION__);
407                 // restore
408                 offset_ = oldOff;
409                 size_ = oldSize;
410                 hint_ = oldHint;
411                 isNormed_ = true;
412             }
413             return res;
414         }
415 
416         // case 2: no need to remap, but to adjust boundary.
417         if (curEnd + oldSize > rEnd_) { // otherwise keep original "size_"
418             size_ = rEnd_ - curEnd;
419         }
420         data_ += oldSize;
421         offset_ += oldSize;
422         return MAPPED_FILE_ERR_OK;
423     }
424 
425     // if not mapped, turnNext() will set offset to next page of PageSize()
426     offset_ += PageSize();
427     isNormed_ = false;
428 
429     return MAPPED_FILE_ERR_OK;
430 }
431 
Reset()432 void MappedFile::Reset()
433 {
434     isNormed_ = false;
435     isMapped_ = false;
436     isNewFile_ = false;
437 
438     rStart_ = nullptr;
439     rEnd_ = nullptr;
440     data_ = nullptr;
441     path_ = "";
442     size_ = DEFAULT_LENGTH;
443     offset_ = 0;
444     mode_ = MapMode::DEFAULT;
445     fd_ = -1;
446     mapProt_ = 0;
447     mapFlag_ = 0;
448     openFlag_ = 0;
449     hint_ = nullptr;
450 }
451 
Clear(bool force)452 ErrCode MappedFile::Clear(bool force)
453 {
454     if (isMapped_) {
455         ErrCode res = Unmap();
456         if (!force && res != MAPPED_FILE_ERR_OK) {
457             UTILS_LOGD("%{public}s failed. UnMapping Failed.", __FUNCTION__);
458             return res;
459         }
460     }
461 
462     if (fd_ != -1 && close(fd_) == -1) {
463         UTILS_LOGD("%{public}s: Failed. Cannot close the file: %{public}s.", \
464                    __FUNCTION__, strerror(errno));
465         return MAPPED_FILE_ERR_FAILED;
466     }
467     Reset();
468     return MAPPED_FILE_ERR_OK;
469 }
470 
~MappedFile()471 MappedFile::~MappedFile()
472 {
473     if (isMapped_) {
474         ErrCode res = Unmap();
475         if (res != MAPPED_FILE_ERR_OK) {
476             UTILS_LOGW("%{public}s: File unmapping failed, it will be automatically  \
477                        unmapped when the process is terminated.", __FUNCTION__);
478         }
479     }
480 
481     if (fd_ != -1 && close(fd_) == -1) {
482         UTILS_LOGE("%{public}s: Failed. Cannot close the file: %{public}s.", \
483                    __FUNCTION__, strerror(errno));
484     }
485 }
486 
MappedFile(MappedFile && other)487 MappedFile::MappedFile(MappedFile&& other) noexcept
488     : data_(other.data_), rStart_(other.rStart_), rEnd_(other.rEnd_), isMapped_(other.isMapped_),
489     isNormed_(other.isNormed_), isNewFile_(other.isNewFile_), path_(std::move(other.path_)), size_(other.size_),
490     offset_(other.offset_), mode_(other.mode_), fd_(other.fd_), mapProt_(other.mapProt_), mapFlag_(other.mapFlag_),
491     openFlag_(other.openFlag_), hint_(other.hint_)
492 {
493     other.Reset();
494 }
495 
operator =(MappedFile && other)496 MappedFile& MappedFile::operator=(MappedFile&& other) noexcept
497 {
498     Clear(true);
499 
500     data_ = other.data_;
501     rStart_ = other.rStart_;
502     rEnd_ = other.rEnd_;
503     isMapped_ = other.isMapped_;
504     isNormed_ = other.isNormed_;
505     isNewFile_ = other.isNewFile_;
506     path_ = other.path_;
507     size_ = other.size_;
508     offset_ = other.offset_;
509     mode_ = other.mode_;
510     fd_ = other.fd_;
511     mapProt_ = other.mapProt_;
512     mapFlag_ = other.mapFlag_;
513     openFlag_ = other.openFlag_;
514     hint_ = other.hint_;
515 
516     other.Reset();
517 
518     return *this;
519 }
520 
ChangeOffset(off_t val)521 bool MappedFile::ChangeOffset(off_t val)
522 {
523     if (offset_ != val) {
524         if (!isMapped_ || Unmap() == MAPPED_FILE_ERR_OK) {
525             offset_ = val;
526             isNormed_ = false;
527 
528             return true;
529         }
530 
531         UTILS_LOGW("%{public}s: Change params failed. Unmapping failed.", __FUNCTION__);
532     }
533     return false;
534 }
535 
ChangeSize(off_t val)536 bool MappedFile::ChangeSize(off_t val)
537 {
538     if ((val > 0 || val == DEFAULT_LENGTH) && size_ != val) {
539         if (!isMapped_ || Unmap() == MAPPED_FILE_ERR_OK) {
540             size_ = val;
541             isNormed_ = false;
542 
543             return true;
544         }
545 
546         UTILS_LOGW("%{public}s: Change params failed. Unmapping failed.", __FUNCTION__);
547     }
548     return false;
549 }
550 
ChangePath(const std::string & val)551 bool MappedFile::ChangePath(const std::string& val)
552 {
553     if (path_ != val) {
554         if (!isMapped_ || Unmap() == MAPPED_FILE_ERR_OK) {
555             if (fd_ != -1 && close(fd_) == -1) {
556                 UTILS_LOGW("%{public}s: Failed. Cannot close the file: %{public}s.", \
557                            __FUNCTION__, strerror(errno));
558                 return false;
559             }
560             path_ = val;
561             isNormed_ = false;
562             fd_ = -1;
563 
564             return true;
565         } else {
566             UTILS_LOGW("%{public}s: Change params failed. Unmapping failed.", __FUNCTION__);
567         }
568     }
569     return false;
570 }
571 
ChangeHint(const char * val)572 bool MappedFile::ChangeHint(const char* val)
573 {
574     if (hint_ != val) {
575         if (!isMapped_ || Unmap() == MAPPED_FILE_ERR_OK) {
576             hint_ = val;
577             isNormed_ = false;
578 
579             return true;
580         } else {
581             UTILS_LOGW("%{public}s: Change params failed. Unmapping failed.", __FUNCTION__);
582         }
583     }
584     return false;
585 }
586 
ChangeMode(MapMode val)587 bool MappedFile::ChangeMode(MapMode val)
588 {
589     if (mode_ != val) {
590         if (!isMapped_ || Unmap() == MAPPED_FILE_ERR_OK) {
591             mode_ = val;
592             isNormed_ = false;
593 
594             return true;
595         } else {
596             UTILS_LOGW("%{public}s: Change params failed. Unmapping failed.", __FUNCTION__);
597         }
598     }
599     return false;
600 }
601 
602 } // namespace Utils
603 } // namespace OHOS