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