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