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 26namespace OHOS { 27namespace Utils { 28off_t MappedFile::pageSize_ = static_cast<off_t>(sysconf(_SC_PAGESIZE)); 29 30MappedFile::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 33bool 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} 45bool 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 60bool 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 107void 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 137ErrCode 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 164bool 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 196ErrCode 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 251ErrCode 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 274bool 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 294ErrCode 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 353ErrCode 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 369ErrCode 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 436void 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 456ErrCode 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 475MappedFile::~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 491MappedFile::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 500MappedFile& 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 525bool 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 540bool 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 555bool 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 576bool 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 591bool 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