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 25 namespace bsdiff { 26 27 std::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 34 File::~File() { 35 Close(); 36 } 37 38 bool 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 50 bool 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 62 bool 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 82 bool 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 94 bool 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 128 File::File(int fd) 129 : fd_(fd) {} 130 131 } // namespace bsdiff 132