// Windows implementation of pread/pwrite. Note that these DO move the file descriptor's read/write // position, but do so atomically. static ssize_t pread(int fd, void* data, size_t byte_count, off64_t offset) {
ScopedEvent event; if (event.handle() == INVALID_HANDLE_VALUE) {
PLOG(ERROR) << "Could not create event handle.";
errno = EIO; returnstatic_cast<ssize_t>(-1);
}
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
DWORD bytes_read = 0;
OVERLAPPED overlapped = {};
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
overlapped.hEvent = event.handle(); if (!ReadFile(handle, data, static_cast<DWORD>(byte_count), &bytes_read, &overlapped)) { // If the read failed with other than ERROR_IO_PENDING, return an error. // ERROR_IO_PENDING signals the write was begun asynchronously. // Block until the asynchronous operation has finished or fails, and return // result accordingly. if (::GetLastError() != ERROR_IO_PENDING ||
!::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE)) { // In case someone tries to read errno (since this is masquerading as a POSIX call).
errno = EIO; returnstatic_cast<ssize_t>(-1);
}
} returnstatic_cast<ssize_t>(bytes_read);
}
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
DWORD bytes_written = 0;
OVERLAPPED overlapped = {};
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
overlapped.hEvent = event.handle(); if (!::WriteFile(handle, buf, count, &bytes_written, &overlapped)) { // If the write failed with other than ERROR_IO_PENDING, return an error. // ERROR_IO_PENDING signals the write was begun asynchronously. // Block until the asynchronous operation has finished or fails, and return // result accordingly. if (::GetLastError() != ERROR_IO_PENDING ||
!::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE)) { // In case someone tries to read errno (since this is masquerading as a POSIX call).
errno = EIO; returnstatic_cast<ssize_t>(-1);
}
} returnstatic_cast<ssize_t>(bytes_written);
}
file_path_ = path; if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) { // Start in the base state (not flushed, not closed).
guard_state_ = GuardState::kBase;
} else { // We are not concerned with read-only files. In that case, proper flushing and closing is // not important.
guard_state_ = GuardState::kNoCheck;
} returntrue;
}
int FdFile::Close() { #ifdefined(__BIONIC__) int result = android_fdsan_close_with_tag(fd_, GetFdFileOwnerTag(this)); #else int result = close(fd_); #endif
// Test here, so the file is closed and not leaked. if (kCheckSafeUsage) {
DCHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
<< " has not been flushed before closing.";
moveUp(GuardState::kClosed, nullptr);
}
#ifdefined(__linux__) // close always succeeds on linux, even if failure is reported.
UNUSED(result); #else if (result == -1) { return -errno;
} #endif
fd_ = kInvalidFd;
file_path_ = ""; return0;
}
int FdFile::Flush(bool flush_metadata) {
DCHECK(flush_metadata || !read_only_mode_);
#ifdef __linux__ int rc; if (flush_metadata) {
rc = TEMP_FAILURE_RETRY(fsync(fd_));
} else {
rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
} #else int rc = TEMP_FAILURE_RETRY(fsync(fd_)); #endif
bool FdFile::Rename(const std::string& new_path) { if (kCheckSafeUsage) { // Filesystems that use delayed allocation (e.g., ext4) may journal a rename before a data // update is written to disk. Therefore on system crash, the data update may not persist. // Guard against this by ensuring the file has been flushed prior to rename. if (guard_state_ < GuardState::kFlushed) {
LOG(ERROR) << "File " << file_path_ << " has not been flushed before renaming.";
}
DCHECK_GE(guard_state_, GuardState::kFlushed);
}
if (!FilePathMatchesFd()) {
LOG(ERROR) << "Failed rename because the file descriptor is not backed by the expected file "
<< "path: " << file_path_; returnfalse;
}
std::string old_path = file_path_; int rc = std::rename(old_path.c_str(), new_path.c_str()); if (rc != 0) {
LOG(ERROR) << "Rename from '" << old_path << "' to '" << new_path << "' failed."; returnfalse;
}
file_path_ = new_path;
// Rename modifies the directory entries mapped within the parent directory file descriptor(s), // rather than the file, so flushing the file will not persist the change to disk. Therefore, we // flush the parent directory file descriptor(s).
std::string old_dir = android::base::Dirname(old_path);
std::string new_dir = android::base::Dirname(new_path);
std::vector<std::string> sync_dirs = {new_dir}; if (new_dir != old_dir) {
sync_dirs.emplace_back(old_dir);
} for (auto& dirname : sync_dirs) {
FdFile dir = FdFile(dirname, O_RDONLY, /*check_usage=*/false);
rc = dir.Flush(/*flush_metadata=*/true); if (rc != 0) {
LOG(ERROR) << "Flushing directory '" << dirname << "' during rename failed."; returnfalse;
}
rc = dir.Close(); if (rc != 0) {
LOG(ERROR) << "Closing directory '" << dirname << "' during rename failed."; returnfalse;
}
} returntrue;
}
#ifdef __linux__ bool FdFile::SparseWrite(const uint8_t* data,
size_t size, const std::vector<uint8_t>& zeroes) {
DCHECK_GE(zeroes.size(), size); if (memcmp(zeroes.data(), data, size) == 0 && AllowSparseFiles()) { // These bytes are all zeroes, skip them by moving the file offset via lseek SEEK_CUR (available // since linux kernel 3.1). if (TEMP_FAILURE_RETRY(lseek(Fd(), size, SEEK_CUR)) < 0) { returnfalse;
}
} else { if (!WriteFully(data, size)) { returnfalse;
}
} returntrue;
}
bool FdFile::UserspaceSparseCopy(const FdFile* input_file,
off_t off,
size_t size,
size_t fs_blocksize) { // Map the input file. We will begin the copy 'off' bytes into the map.
art::MemMap::Init();
std::string error_msg;
art::MemMap mmap = art::MemMap::MapFile(off + size,
PROT_READ,
MAP_PRIVATE,
input_file->Fd(), /*start=*/0, /*low_4gb=*/false,
input_file->GetPath().c_str(),
&error_msg); if (!mmap.IsValid()) {
LOG(ERROR) << "Failed to mmap " << input_file->GetPath() << " for copying: " << error_msg; returnfalse;
}
#ifdef __linux__
off_t current_offset = TEMP_FAILURE_RETRY(lseek(Fd(), 0, SEEK_CUR)); if (GetLength() > current_offset) { // Copying to an existing region of the destination file is not supported. The current // implementation would incorrectly preserve all existing data regions within the output file // which match the locations of holes within the input file.
LOG(ERROR) << "Cannot copy into an existing region of the destination file.";
errno = EINVAL; returnfalse;
} struct stat output_stat; if (TEMP_FAILURE_RETRY(fstat(Fd(), &output_stat)) < 0) { returnfalse;
} const off_t fs_blocksize = output_stat.st_blksize; if (!art::IsAlignedParam(current_offset, fs_blocksize)) { // The input region is copied (skipped or written) in chunks of the output file's blocksize. For // those chunks to be represented as holes or data, they should land as aligned blocks in the // output file. Therefore, here we enforce that the current output offset is aligned.
LOG(ERROR) << "Copy destination FD offset (" << current_offset << ") must be aligned with"
<< " blocksize (" << fs_blocksize << ").";
errno = EINVAL; returnfalse;
} const size_t end_length = GetLength() + sz; if (!UserspaceSparseCopy(input_file, off, sz, fs_blocksize)) { returnfalse;
} // In case the last blocks of the input file were a hole, fix the length to what would have been // set if they had been data. if (SetLength(end_length) != 0) { returnfalse;
} #else if (lseek(input_file->Fd(), off, SEEK_SET) != off) { returnfalse;
}
constexpr size_t kMaxBufferSize = 16 * ::art::KB; const size_t buffer_size = std::min<uint64_t>(size, kMaxBufferSize);
art::UniqueCPtr<void> buffer(malloc(buffer_size)); if (buffer == nullptr) {
errno = ENOMEM; returnfalse;
} while (size != 0) {
size_t chunk_size = std::min<uint64_t>(buffer_size, size); if (!input_file->ReadFully(buffer.get(), chunk_size) ||
!WriteFully(buffer.get(), chunk_size)) { returnfalse;
}
size -= chunk_size;
} #endif returntrue;
}
bool FdFile::FilePathMatchesFd() { if (file_path_.empty()) { returnfalse;
} // Try to figure out whether file_path_ is still referring to the one on disk. bool is_current = false; struct stat this_stat, current_stat; int cur_fd = TEMP_FAILURE_RETRY(open(file_path_.c_str(), O_RDONLY | O_CLOEXEC)); if (cur_fd > 0) { // File still exists. if (fstat(fd_, &this_stat) == 0 && fstat(cur_fd, ¤t_stat) == 0) {
is_current = (this_stat.st_dev == current_stat.st_dev) &&
(this_stat.st_ino == current_stat.st_ino);
}
close(cur_fd);
} return is_current;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.