1// Copyright 2015 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "file.h" 6 7#include <errno.h> 8#include <fcntl.h> 9#ifdef __linux__ 10#include <linux/fs.h> 11#endif // __linux__ 12#include <string.h> 13#include <sys/ioctl.h> 14#include <sys/stat.h> 15#include <sys/types.h> 16#include <unistd.h> 17 18// TEMP_FAILURE_RETRY is defined by some versions of <unistd.h>. 19#ifndef TEMP_FAILURE_RETRY 20#include <utils/Compat.h> 21#endif 22 23#include <algorithm> 24 25namespace bsdiff { 26 27std::unique_ptr<File> File::FOpen(const char* pathname, int flags) { 28 int fd = TEMP_FAILURE_RETRY(open(pathname, flags, 0644)); 29 if (fd < 0) 30 return std::unique_ptr<File>(); 31 return std::unique_ptr<File>(new File(fd)); 32} 33 34File::~File() { 35 Close(); 36} 37 38bool File::Read(void* buf, size_t count, size_t* bytes_read) { 39 if (fd_ < 0) { 40 errno = EBADF; 41 return false; 42 } 43 ssize_t rc = TEMP_FAILURE_RETRY(read(fd_, buf, count)); 44 if (rc == -1) 45 return false; 46 *bytes_read = static_cast<size_t>(rc); 47 return true; 48} 49 50bool File::Write(const void* buf, size_t count, size_t* bytes_written) { 51 if (fd_ < 0) { 52 errno = EBADF; 53 return false; 54 } 55 ssize_t rc = TEMP_FAILURE_RETRY(write(fd_, buf, count)); 56 if (rc == -1) 57 return false; 58 *bytes_written = static_cast<size_t>(rc); 59 return true; 60} 61 62bool File::Seek(off_t pos) { 63 if (fd_ < 0) { 64 errno = EBADF; 65 return false; 66 } 67 // fseek() uses a long value for the offset which could be smaller than off_t. 68 if (pos > std::numeric_limits<long>::max()) { 69 errno = EOVERFLOW; 70 return false; 71 } 72 off_t newpos = lseek(fd_, pos, SEEK_SET); 73 if (newpos < 0) 74 return false; 75 if (newpos != pos) { 76 errno = EINVAL; 77 return false; 78 } 79 return true; 80} 81 82bool File::Close() { 83 if (fd_ < 0) { 84 errno = EBADF; 85 return false; 86 } 87 bool success = close(fd_) == 0; 88 if (!success && errno == EINTR) 89 success = true; 90 fd_ = -1; 91 return success; 92} 93 94bool File::GetSize(uint64_t* size) { 95 struct stat stbuf; 96 if (fstat(fd_, &stbuf) == -1) 97 return false; 98 if (S_ISREG(stbuf.st_mode)) { 99 *size = stbuf.st_size; 100 return true; 101 } 102 if (S_ISBLK(stbuf.st_mode)) { 103#if defined(BLKGETSIZE64) 104 return ioctl(fd_, BLKGETSIZE64, size); 105#elif defined(DKIOCGETBLOCKCOUNT) 106 uint64_t sectors = 0; 107 if (ioctl(fd_, DKIOCGETBLOCKCOUNT, §ors) == 0) { 108 *size = sectors << 9; 109 return true; 110 } 111 return false; 112#else 113 // Fall back to doing seeks to know the EOF. 114 off_t pos = lseek(fd_, 0, SEEK_CUR); 115 if (pos == -1) 116 return false; 117 off_t end_pos = lseek(fd_, 0, SEEK_END); 118 if (end_pos == -1) 119 return false; 120 *size = end_pos; 121 lseek(fd_, 0, SEEK_END); 122 return true; 123#endif 124 } 125 return false; 126} 127 128File::File(int fd) 129 : fd_(fd) {} 130 131} // namespace bsdiff 132