| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "base/logging.h" |
| #include "base/unix_file/mapped_file.h" |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <algorithm> |
| #include <string> |
| |
| namespace unix_file { |
| |
| MappedFile::~MappedFile() { |
| } |
| |
| int MappedFile::Close() { |
| if (IsMapped()) { |
| Unmap(); |
| } |
| return FdFile::Close(); |
| } |
| |
| bool MappedFile::MapReadOnly() { |
| CHECK(IsOpened()); |
| CHECK(!IsMapped()); |
| struct stat st; |
| int result = TEMP_FAILURE_RETRY(fstat(Fd(), &st)); |
| if (result == -1) { |
| PLOG(WARNING) << "Failed to stat file '" << GetPath() << "'"; |
| return false; |
| } |
| file_size_ = st.st_size; |
| do { |
| mapped_file_ = mmap(NULL, file_size_, PROT_READ, MAP_PRIVATE, Fd(), 0); |
| } while (mapped_file_ == MAP_FAILED && errno == EINTR); |
| if (mapped_file_ == MAP_FAILED) { |
| PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size " |
| << file_size_ << " bytes to memory"; |
| return false; |
| } |
| map_mode_ = kMapReadOnly; |
| return true; |
| } |
| |
| bool MappedFile::MapReadWrite(int64_t file_size) { |
| CHECK(IsOpened()); |
| CHECK(!IsMapped()); |
| int result = TEMP_FAILURE_RETRY(ftruncate64(Fd(), file_size)); |
| if (result == -1) { |
| PLOG(ERROR) << "Failed to truncate file '" << GetPath() |
| << "' to size " << file_size; |
| return false; |
| } |
| file_size_ = file_size; |
| do { |
| mapped_file_ = |
| mmap(NULL, file_size_, PROT_READ | PROT_WRITE, MAP_SHARED, Fd(), 0); |
| } while (mapped_file_ == MAP_FAILED && errno == EINTR); |
| if (mapped_file_ == MAP_FAILED) { |
| PLOG(WARNING) << "Failed to mmap file '" << GetPath() << "' of size " |
| << file_size_ << " bytes to memory"; |
| return false; |
| } |
| map_mode_ = kMapReadWrite; |
| return true; |
| } |
| |
| bool MappedFile::Unmap() { |
| CHECK(IsMapped()); |
| int result = TEMP_FAILURE_RETRY(munmap(mapped_file_, file_size_)); |
| if (result == -1) { |
| PLOG(WARNING) << "Failed unmap file '" << GetPath() << "' of size " |
| << file_size_; |
| return false; |
| } else { |
| mapped_file_ = NULL; |
| file_size_ = -1; |
| return true; |
| } |
| } |
| |
| int64_t MappedFile::Read(char* buf, int64_t byte_count, int64_t offset) const { |
| if (IsMapped()) { |
| if (offset < 0) { |
| errno = EINVAL; |
| return -errno; |
| } |
| int64_t read_size = std::max(static_cast<int64_t>(0), |
| std::min(byte_count, file_size_ - offset)); |
| if (read_size > 0) { |
| memcpy(buf, data() + offset, read_size); |
| } |
| return read_size; |
| } else { |
| return FdFile::Read(buf, byte_count, offset); |
| } |
| } |
| |
| int MappedFile::SetLength(int64_t new_length) { |
| CHECK(!IsMapped()); |
| return FdFile::SetLength(new_length); |
| } |
| |
| int64_t MappedFile::GetLength() const { |
| if (IsMapped()) { |
| return file_size_; |
| } else { |
| return FdFile::GetLength(); |
| } |
| } |
| |
| int MappedFile::Flush() { |
| int rc = IsMapped() ? TEMP_FAILURE_RETRY(msync(mapped_file_, file_size_, 0)) : FdFile::Flush(); |
| return rc == -1 ? -errno : 0; |
| } |
| |
| int64_t MappedFile::Write(const char* buf, int64_t byte_count, int64_t offset) { |
| if (IsMapped()) { |
| CHECK_EQ(kMapReadWrite, map_mode_); |
| if (offset < 0) { |
| errno = EINVAL; |
| return -errno; |
| } |
| int64_t write_size = std::max(static_cast<int64_t>(0), |
| std::min(byte_count, file_size_ - offset)); |
| if (write_size > 0) { |
| memcpy(data() + offset, buf, write_size); |
| } |
| return write_size; |
| } else { |
| return FdFile::Write(buf, byte_count, offset); |
| } |
| } |
| |
| int64_t MappedFile::size() const { |
| return GetLength(); |
| } |
| |
| bool MappedFile::IsMapped() const { |
| return mapped_file_ != NULL && mapped_file_ != MAP_FAILED; |
| } |
| |
| char* MappedFile::data() const { |
| CHECK(IsMapped()); |
| return static_cast<char*>(mapped_file_); |
| } |
| |
| } // namespace unix_file |