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