// // Copyright (C) 2012 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 "update_engine/payload_consumer/file_descriptor.h" #include #include #include #include #include #include #include #include "update_engine/common/utils.h" namespace chromeos_update_engine { EintrSafeFileDescriptor::~EintrSafeFileDescriptor() { if (IsOpen()) { Close(); } } bool EintrSafeFileDescriptor::Open(const char* path, int flags, mode_t mode) { CHECK_EQ(fd_, -1); return ((fd_ = HANDLE_EINTR(open(path, flags, mode))) >= 0); } bool EintrSafeFileDescriptor::Open(const char* path, int flags) { CHECK_EQ(fd_, -1); return ((fd_ = HANDLE_EINTR(open(path, flags))) >= 0); } ssize_t EintrSafeFileDescriptor::Read(void* buf, size_t count) { CHECK_GE(fd_, 0); return HANDLE_EINTR(read(fd_, buf, count)); } ssize_t EintrSafeFileDescriptor::Write(const void* buf, size_t count) { CHECK_GE(fd_, 0); // Attempt repeated writes, as long as some progress is being made. char* char_buf = const_cast(reinterpret_cast(buf)); ssize_t written = 0; while (count > 0) { ssize_t ret = HANDLE_EINTR(write(fd_, char_buf, count)); // Fail on either an error or no progress. if (ret <= 0) return (written ? written : ret); written += ret; count -= ret; char_buf += ret; } return written; } off64_t EintrSafeFileDescriptor::Seek(off64_t offset, int whence) { CHECK_GE(fd_, 0); return lseek64(fd_, offset, whence); } uint64_t EintrSafeFileDescriptor::BlockDevSize() { if (fd_ < 0) return 0; struct stat stbuf; if (fstat(fd_, &stbuf) < 0) { PLOG(ERROR) << "Error stat-ing fd " << fd_; return 0; } if (!S_ISBLK(stbuf.st_mode)) return 0; off_t block_size = utils::BlockDevSize(fd_); return block_size < 0 ? 0 : block_size; } bool EintrSafeFileDescriptor::BlkIoctl(int request, uint64_t start, uint64_t length, int* result) { // If the ioctl BLKZEROOUT is not defined, just fail to perform any of these // operations. #ifndef BLKZEROOUT return false; #else // defined(BLKZEROOUT) DCHECK(request == BLKDISCARD || request == BLKZEROOUT || request == BLKSECDISCARD); // On some devices, the BLKDISCARD will actually read back as zeros, instead // of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the // case, so we issue a BLKDISCARD in those cases to speed up the writes. unsigned int arg; if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg) request = BLKDISCARD; // Ensure the |fd_| is in O_DIRECT mode during this operation, so the write // cache for this region is invalidated. This is required since otherwise // reading back this region could consume stale data from the cache. int flags = fcntl(fd_, F_GETFL, 0); if (flags == -1) { PLOG(WARNING) << "Couldn't get flags on fd " << fd_; return false; } if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags | O_DIRECT) == -1) { PLOG(WARNING) << "Couldn't set O_DIRECT on fd " << fd_; return false; } uint64_t range[2] = {start, length}; *result = ioctl(fd_, request, range); if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags) == -1) { PLOG(WARNING) << "Couldn't remove O_DIRECT on fd " << fd_; return false; } return true; #endif // defined(BLKZEROOUT) } bool EintrSafeFileDescriptor::Flush() { CHECK_GE(fd_, 0); // Implemented as a No-Op, as delta_performer typically uses |O_DSYNC|, except // in interactive settings. fsync(fd_); return true; } bool EintrSafeFileDescriptor::Close() { if (fd_ < 0) { return false; } // https://stackoverflow.com/questions/705454/does-linux-guarantee-the-contents-of-a-file-is-flushed-to-disc-after-close // |close()| doesn't imply |fsync()|, we need to do it manually. fsync(fd_); if (IGNORE_EINTR(close(fd_))) return false; fd_ = -1; return true; } } // namespace chromeos_update_engine