1 /* 2 A C++ interface to POSIX functions. 3 4 Copyright (c) 2012 - 2016, Victor Zverovich 5 All rights reserved. 6 7 For the license information refer to format.h. 8 */ 9 10 // Disable bogus MSVC warnings. 11 #ifndef _CRT_SECURE_NO_WARNINGS 12 # define _CRT_SECURE_NO_WARNINGS 13 #endif 14 15 #include "mp/posix.h" 16 17 #include <limits.h> 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 21 #ifndef _WIN32 22 # include <unistd.h> 23 #else 24 struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive- 25 # include <windows.h> 26 # include <io.h> 27 28 # define O_CREAT _O_CREAT 29 # define O_TRUNC _O_TRUNC 30 31 # ifndef S_IRUSR 32 # define S_IRUSR _S_IREAD 33 # endif 34 35 # ifndef S_IWUSR 36 # define S_IWUSR _S_IWRITE 37 # endif 38 39 # ifdef __MINGW32__ 40 # define _SH_DENYNO 0x40 41 # endif 42 43 #endif // _WIN32 44 45 #ifdef fileno 46 # undef fileno 47 #endif 48 49 namespace { 50 #ifdef _WIN32 51 // Return type of read and write functions. 52 typedef int RWResult; 53 54 // On Windows the count argument to read and write is unsigned, so convert 55 // it from size_t preventing integer overflow. 56 inline unsigned convert_rwcount(std::size_t count) { 57 return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; 58 } 59 #else 60 // Return type of read and write functions. 61 typedef ssize_t RWResult; 62 63 inline std::size_t convert_rwcount(std::size_t count) { return count; } 64 #endif 65 } 66 67 fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { 68 if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 69 fmt::report_system_error(errno, "cannot close file"); 70 } 71 72 fmt::BufferedFile::BufferedFile( 73 fmt::CStringRef filename, fmt::CStringRef mode) { 74 FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); 75 if (!file_) 76 FMT_THROW(SystemError(errno, "cannot open file {}", filename)); 77 } 78 79 void fmt::BufferedFile::close() { 80 if (!file_) 81 return; 82 int result = FMT_SYSTEM(fclose(file_)); 83 file_ = 0; 84 if (result != 0) 85 FMT_THROW(SystemError(errno, "cannot close file")); 86 } 87 88 // A macro used to prevent expansion of fileno on broken versions of MinGW. 89 #define FMT_ARGS 90 91 int fmt::BufferedFile::fileno() const { 92 int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 93 if (fd == -1) 94 FMT_THROW(SystemError(errno, "cannot get file descriptor")); 95 return fd; 96 } 97 98 fmt::File::File(fmt::CStringRef path, int oflag) { 99 int mode = S_IRUSR | S_IWUSR; 100 #if defined(_WIN32) && !defined(__MINGW32__) 101 fd_ = -1; 102 FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 103 #else 104 FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 105 #endif 106 if (fd_ == -1) 107 FMT_THROW(SystemError(errno, "cannot open file {}", path)); 108 } 109 110 fmt::File::~File() FMT_NOEXCEPT { 111 // Don't retry close in case of EINTR! 112 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 113 if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 114 fmt::report_system_error(errno, "cannot close file"); 115 } 116 117 void fmt::File::close() { 118 if (fd_ == -1) 119 return; 120 // Don't retry close in case of EINTR! 121 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 122 int result = FMT_POSIX_CALL(close(fd_)); 123 fd_ = -1; 124 if (result != 0) 125 FMT_THROW(SystemError(errno, "cannot close file")); 126 } 127 128 fmt::LongLong fmt::File::size() const { 129 #ifdef _WIN32 130 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 131 // is less than 0x0500 as is the case with some default MinGW builds. 132 // Both functions support large file sizes. 133 DWORD size_upper = 0; 134 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); 135 DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 136 if (size_lower == INVALID_FILE_SIZE) { 137 DWORD error = GetLastError(); 138 if (error != NO_ERROR) 139 FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); 140 } 141 fmt::ULongLong long_size = size_upper; 142 return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 143 #else 144 typedef struct stat Stat; 145 Stat file_stat = Stat(); 146 if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 147 FMT_THROW(SystemError(errno, "cannot get file attributes")); 148 FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), 149 "return type of File::size is not large enough"); 150 return file_stat.st_size; 151 #endif 152 } 153 154 std::size_t fmt::File::read(void *buffer, std::size_t count) { 155 RWResult result = 0; 156 FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 157 if (result < 0) 158 FMT_THROW(SystemError(errno, "cannot read from file")); 159 return internal::to_unsigned(result); 160 } 161 162 std::size_t fmt::File::write(const void *buffer, std::size_t count) { 163 RWResult result = 0; 164 FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 165 if (result < 0) 166 FMT_THROW(SystemError(errno, "cannot write to file")); 167 return internal::to_unsigned(result); 168 } 169 170 fmt::File fmt::File::dup(int fd) { 171 // Don't retry as dup doesn't return EINTR. 172 // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 173 int new_fd = FMT_POSIX_CALL(dup(fd)); 174 if (new_fd == -1) 175 FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); 176 return File(new_fd); 177 } 178 179 void fmt::File::dup2(int fd) { 180 int result = 0; 181 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 182 if (result == -1) { 183 FMT_THROW(SystemError(errno, 184 "cannot duplicate file descriptor {} to {}", fd_, fd)); 185 } 186 } 187 188 void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { 189 int result = 0; 190 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 191 if (result == -1) 192 ec = ErrorCode(errno); 193 } 194 195 void fmt::File::pipe(File &read_end, File &write_end) { 196 // Close the descriptors first to make sure that assignments don't throw 197 // and there are no leaks. 198 read_end.close(); 199 write_end.close(); 200 int fds[2] = {}; 201 #ifdef _WIN32 202 // Make the default pipe capacity same as on Linux 2.6.11+. 203 enum { DEFAULT_CAPACITY = 65536 }; 204 int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 205 #else 206 // Don't retry as the pipe function doesn't return EINTR. 207 // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 208 int result = FMT_POSIX_CALL(pipe(fds)); 209 #endif 210 if (result != 0) 211 FMT_THROW(SystemError(errno, "cannot create pipe")); 212 // The following assignments don't throw because read_fd and write_fd 213 // are closed. 214 read_end = File(fds[0]); 215 write_end = File(fds[1]); 216 } 217 218 fmt::BufferedFile fmt::File::fdopen(const char *mode) { 219 // Don't retry as fdopen doesn't return EINTR. 220 FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); 221 if (!f) 222 FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); 223 BufferedFile file(f); 224 fd_ = -1; 225 return file; 226 } 227 228 long fmt::getpagesize() { 229 #ifdef _WIN32 230 SYSTEM_INFO si; 231 GetSystemInfo(&si); 232 return si.dwPageSize; 233 #else 234 long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 235 if (size < 0) 236 FMT_THROW(SystemError(errno, "cannot get memory page size")); 237 return size; 238 #endif 239 } 240