]> git.proxmox.com Git - ceph.git/blob - ceph/src/fmt/src/posix.cc
import 15.2.0 Octopus source
[ceph.git] / ceph / src / fmt / src / posix.cc
1 // A C++ interface to POSIX functions.
2 //
3 // Copyright (c) 2012 - 2016, Victor Zverovich
4 // All rights reserved.
5 //
6 // For the license information refer to format.h.
7
8 // Disable bogus MSVC warnings.
9 #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
10 # define _CRT_SECURE_NO_WARNINGS
11 #endif
12
13 #include "fmt/posix.h"
14
15 #include <limits.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18
19 #ifndef _WIN32
20 # include <unistd.h>
21 #else
22 # ifndef WIN32_LEAN_AND_MEAN
23 # define WIN32_LEAN_AND_MEAN
24 # endif
25 # include <io.h>
26 # include <windows.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 } // namespace
66
67 FMT_BEGIN_NAMESPACE
68
69 buffered_file::~buffered_file() FMT_NOEXCEPT {
70 if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
71 report_system_error(errno, "cannot close file");
72 }
73
74 buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
75 FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
76 FMT_NULL);
77 if (!file_)
78 FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
79 }
80
81 void buffered_file::close() {
82 if (!file_) return;
83 int result = FMT_SYSTEM(fclose(file_));
84 file_ = FMT_NULL;
85 if (result != 0) FMT_THROW(system_error(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 buffered_file::fileno() const {
92 int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
93 if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
94 return fd;
95 }
96
97 file::file(cstring_view path, int oflag) {
98 int mode = S_IRUSR | S_IWUSR;
99 #if defined(_WIN32) && !defined(__MINGW32__)
100 fd_ = -1;
101 FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
102 #else
103 FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
104 #endif
105 if (fd_ == -1)
106 FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
107 }
108
109 file::~file() FMT_NOEXCEPT {
110 // Don't retry close in case of EINTR!
111 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
112 if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
113 report_system_error(errno, "cannot close file");
114 }
115
116 void file::close() {
117 if (fd_ == -1) return;
118 // Don't retry close in case of EINTR!
119 // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
120 int result = FMT_POSIX_CALL(close(fd_));
121 fd_ = -1;
122 if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
123 }
124
125 long long file::size() const {
126 #ifdef _WIN32
127 // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
128 // is less than 0x0500 as is the case with some default MinGW builds.
129 // Both functions support large file sizes.
130 DWORD size_upper = 0;
131 HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
132 DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
133 if (size_lower == INVALID_FILE_SIZE) {
134 DWORD error = GetLastError();
135 if (error != NO_ERROR)
136 FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
137 }
138 unsigned long long long_size = size_upper;
139 return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
140 #else
141 typedef struct stat Stat;
142 Stat file_stat = Stat();
143 if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
144 FMT_THROW(system_error(errno, "cannot get file attributes"));
145 static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
146 "return type of file::size is not large enough");
147 return file_stat.st_size;
148 #endif
149 }
150
151 std::size_t file::read(void* buffer, std::size_t count) {
152 RWResult result = 0;
153 FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
154 if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
155 return internal::to_unsigned(result);
156 }
157
158 std::size_t file::write(const void* buffer, std::size_t count) {
159 RWResult result = 0;
160 FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
161 if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
162 return internal::to_unsigned(result);
163 }
164
165 file file::dup(int fd) {
166 // Don't retry as dup doesn't return EINTR.
167 // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
168 int new_fd = FMT_POSIX_CALL(dup(fd));
169 if (new_fd == -1)
170 FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
171 return file(new_fd);
172 }
173
174 void file::dup2(int fd) {
175 int result = 0;
176 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
177 if (result == -1) {
178 FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
179 fd_, fd));
180 }
181 }
182
183 void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
184 int result = 0;
185 FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
186 if (result == -1) ec = error_code(errno);
187 }
188
189 void file::pipe(file& read_end, file& write_end) {
190 // Close the descriptors first to make sure that assignments don't throw
191 // and there are no leaks.
192 read_end.close();
193 write_end.close();
194 int fds[2] = {};
195 #ifdef _WIN32
196 // Make the default pipe capacity same as on Linux 2.6.11+.
197 enum { DEFAULT_CAPACITY = 65536 };
198 int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
199 #else
200 // Don't retry as the pipe function doesn't return EINTR.
201 // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
202 int result = FMT_POSIX_CALL(pipe(fds));
203 #endif
204 if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
205 // The following assignments don't throw because read_fd and write_fd
206 // are closed.
207 read_end = file(fds[0]);
208 write_end = file(fds[1]);
209 }
210
211 buffered_file file::fdopen(const char* mode) {
212 // Don't retry as fdopen doesn't return EINTR.
213 FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
214 if (!f)
215 FMT_THROW(
216 system_error(errno, "cannot associate stream with file descriptor"));
217 buffered_file bf(f);
218 fd_ = -1;
219 return bf;
220 }
221
222 long getpagesize() {
223 #ifdef _WIN32
224 SYSTEM_INFO si;
225 GetSystemInfo(&si);
226 return si.dwPageSize;
227 #else
228 long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
229 if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
230 return size;
231 #endif
232 }
233 FMT_END_NAMESPACE