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