1 | /* -*- c-basic-offset: 2 -*- */ |
2 | /* |
3 | Copyright(C) 2011-2016 Brazil |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License version 2.1 as published by the Free Software Foundation. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with this library; if not, write to the Free Software |
16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 | */ |
18 | |
19 | #include "file-impl.hpp" |
20 | |
21 | #include <sys/types.h> |
22 | #include <sys/stat.h> |
23 | |
24 | #ifdef WIN32 |
25 | # ifdef min |
26 | # undef min |
27 | # endif // min |
28 | # ifdef max |
29 | # undef max |
30 | # endif // max |
31 | #else // WIN32 |
32 | # include <fcntl.h> |
33 | # include <sys/mman.h> |
34 | # include <unistd.h> |
35 | #endif // WIN32 |
36 | |
37 | #include <algorithm> |
38 | #include <limits> |
39 | |
40 | /* Must be the same value as GRN_OPEN_CREATE_MODE */ |
41 | #ifdef WIN32 |
42 | # define GRN_IO_FILE_CREATE_MODE (GENERIC_READ | GENERIC_WRITE) |
43 | #else /* WIN32 */ |
44 | # define GRN_IO_FILE_CREATE_MODE 0640 |
45 | #endif /* WIN32 */ |
46 | |
47 | namespace grn { |
48 | namespace dat { |
49 | |
50 | #ifdef WIN32 |
51 | |
52 | FileImpl::FileImpl() |
53 | : ptr_(NULL), |
54 | size_(0), |
55 | file_(INVALID_HANDLE_VALUE), |
56 | map_(INVALID_HANDLE_VALUE), |
57 | addr_(NULL) {} |
58 | |
59 | FileImpl::~FileImpl() { |
60 | if (addr_ != NULL) { |
61 | ::UnmapViewOfFile(addr_); |
62 | } |
63 | |
64 | if (map_ != INVALID_HANDLE_VALUE) { |
65 | ::CloseHandle(map_); |
66 | } |
67 | |
68 | if (file_ != INVALID_HANDLE_VALUE) { |
69 | ::CloseHandle(file_); |
70 | } |
71 | } |
72 | |
73 | #else // WIN32 |
74 | |
75 | FileImpl::FileImpl() |
76 | : ptr_(NULL), |
77 | size_(0), |
78 | fd_(-1), |
79 | addr_(MAP_FAILED), |
80 | length_(0) {} |
81 | |
82 | FileImpl::~FileImpl() { |
83 | if (addr_ != MAP_FAILED) { |
84 | ::munmap(addr_, length_); |
85 | } |
86 | |
87 | if (fd_ != -1) { |
88 | ::close(fd_); |
89 | } |
90 | } |
91 | |
92 | #endif // WIN32 |
93 | |
94 | void FileImpl::create(const char *path, UInt64 size) { |
95 | GRN_DAT_THROW_IF(PARAM_ERROR, size == 0); |
96 | GRN_DAT_THROW_IF(PARAM_ERROR, |
97 | size > static_cast<UInt64>(std::numeric_limits< ::size_t>::max())); |
98 | |
99 | FileImpl new_impl; |
100 | new_impl.create_(path, size); |
101 | new_impl.swap(this); |
102 | } |
103 | |
104 | void FileImpl::open(const char *path) { |
105 | GRN_DAT_THROW_IF(PARAM_ERROR, path == NULL); |
106 | GRN_DAT_THROW_IF(PARAM_ERROR, path[0] == '\0'); |
107 | |
108 | FileImpl new_impl; |
109 | new_impl.open_(path); |
110 | new_impl.swap(this); |
111 | } |
112 | |
113 | void FileImpl::close() { |
114 | FileImpl new_impl; |
115 | new_impl.swap(this); |
116 | } |
117 | |
118 | #ifdef WIN32 |
119 | |
120 | void FileImpl::swap(FileImpl *rhs) { |
121 | std::swap(ptr_, rhs->ptr_); |
122 | std::swap(size_, rhs->size_); |
123 | std::swap(file_, rhs->file_); |
124 | std::swap(map_, rhs->map_); |
125 | std::swap(addr_, rhs->addr_); |
126 | } |
127 | |
128 | void FileImpl::flush() { |
129 | if (!addr_) { |
130 | return; |
131 | } |
132 | |
133 | BOOL succeeded = ::FlushViewOfFile(addr_, static_cast<SIZE_T>(size_)); |
134 | GRN_DAT_THROW_IF(IO_ERROR, !succeeded); |
135 | |
136 | SYSTEMTIME system_time; |
137 | GetSystemTime(&system_time); |
138 | FILETIME file_time; |
139 | succeeded = SystemTimeToFileTime(&system_time, &file_time); |
140 | GRN_DAT_THROW_IF(IO_ERROR, !succeeded); |
141 | |
142 | succeeded = SetFileTime(file_, NULL, NULL, &file_time); |
143 | GRN_DAT_THROW_IF(IO_ERROR, !succeeded); |
144 | } |
145 | |
146 | void FileImpl::create_(const char *path, UInt64 size) { |
147 | if ((path != NULL) && (path[0] != '\0')) { |
148 | file_ = ::CreateFileA(path, GRN_IO_FILE_CREATE_MODE, |
149 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
150 | NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
151 | GRN_DAT_THROW_IF(IO_ERROR, file_ == INVALID_HANDLE_VALUE); |
152 | |
153 | const LONG size_low = static_cast<LONG>(size & 0xFFFFFFFFU); |
154 | LONG size_high = static_cast<LONG>(size >> 32); |
155 | const DWORD file_pos = ::SetFilePointer(file_, size_low, &size_high, |
156 | FILE_BEGIN); |
157 | GRN_DAT_THROW_IF(IO_ERROR, (file_pos == INVALID_SET_FILE_POINTER) && |
158 | (::GetLastError() != 0)); |
159 | GRN_DAT_THROW_IF(IO_ERROR, ::SetEndOfFile(file_) == 0); |
160 | |
161 | map_ = ::CreateFileMapping(file_, NULL, PAGE_READWRITE, 0, 0, NULL); |
162 | GRN_DAT_THROW_IF(IO_ERROR, map_ == INVALID_HANDLE_VALUE); |
163 | } else { |
164 | const DWORD size_low = static_cast<DWORD>(size & 0xFFFFFFFFU); |
165 | const DWORD size_high = static_cast<DWORD>(size >> 32); |
166 | |
167 | map_ = ::CreateFileMapping(file_, NULL, PAGE_READWRITE, |
168 | size_high, size_low, NULL); |
169 | GRN_DAT_THROW_IF(IO_ERROR, map_ == INVALID_HANDLE_VALUE); |
170 | } |
171 | |
172 | addr_ = ::MapViewOfFile(map_, FILE_MAP_WRITE, 0, 0, 0); |
173 | GRN_DAT_THROW_IF(IO_ERROR, addr_ == NULL); |
174 | |
175 | ptr_ = addr_; |
176 | size_ = static_cast< ::size_t>(size); |
177 | } |
178 | |
179 | void FileImpl::open_(const char *path) { |
180 | #ifdef _MSC_VER |
181 | struct __stat64 st; |
182 | GRN_DAT_THROW_IF(IO_ERROR, ::_stat64(path, &st) == -1); |
183 | #else // _MSC_VER |
184 | struct _stat st; |
185 | GRN_DAT_THROW_IF(IO_ERROR, ::_stat(path, &st) == -1); |
186 | #endif // _MSC_VER |
187 | GRN_DAT_THROW_IF(IO_ERROR, st.st_size == 0); |
188 | GRN_DAT_THROW_IF(IO_ERROR, |
189 | static_cast<UInt64>(st.st_size) > std::numeric_limits< ::size_t>::max()); |
190 | |
191 | file_ = ::CreateFileA(path, GRN_IO_FILE_CREATE_MODE, |
192 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
193 | NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
194 | GRN_DAT_THROW_IF(IO_ERROR, file_ == NULL); |
195 | |
196 | map_ = ::CreateFileMapping(file_, NULL, PAGE_READWRITE, 0, 0, NULL); |
197 | GRN_DAT_THROW_IF(IO_ERROR, map_ == NULL); |
198 | |
199 | addr_ = ::MapViewOfFile(map_, FILE_MAP_WRITE, 0, 0, 0); |
200 | GRN_DAT_THROW_IF(IO_ERROR, addr_ == NULL); |
201 | |
202 | ptr_ = addr_; |
203 | size_ = static_cast< ::size_t>(st.st_size); |
204 | } |
205 | |
206 | #else // WIN32 |
207 | |
208 | void FileImpl::swap(FileImpl *rhs) { |
209 | std::swap(ptr_, rhs->ptr_); |
210 | std::swap(size_, rhs->size_); |
211 | std::swap(fd_, rhs->fd_); |
212 | std::swap(addr_, rhs->addr_); |
213 | std::swap(length_, rhs->length_); |
214 | } |
215 | |
216 | void FileImpl::flush() { |
217 | if (!addr_) { |
218 | return; |
219 | } |
220 | |
221 | int result = ::msync(addr_, length_, MS_SYNC); |
222 | GRN_DAT_THROW_IF(IO_ERROR, result != 0); |
223 | } |
224 | |
225 | void FileImpl::create_(const char *path, UInt64 size) { |
226 | GRN_DAT_THROW_IF(PARAM_ERROR, |
227 | size > static_cast<UInt64>(std::numeric_limits< ::off_t>::max())); |
228 | |
229 | if ((path != NULL) && (path[0] != '\0')) { |
230 | fd_ = ::open(path, O_RDWR | O_CREAT | O_TRUNC, GRN_IO_FILE_CREATE_MODE); |
231 | GRN_DAT_THROW_IF(IO_ERROR, fd_ == -1); |
232 | |
233 | const ::off_t file_size = static_cast< ::off_t>(size); |
234 | GRN_DAT_THROW_IF(IO_ERROR, ::ftruncate(fd_, file_size) == -1); |
235 | } |
236 | |
237 | #ifdef MAP_ANONYMOUS |
238 | const int flags = (fd_ == -1) ? (MAP_PRIVATE | MAP_ANONYMOUS) : MAP_SHARED; |
239 | #else // MAP_ANONYMOUS |
240 | const int flags = (fd_ == -1) ? (MAP_PRIVATE | MAP_ANON) : MAP_SHARED; |
241 | #endif // MAP_ANONYMOUS |
242 | |
243 | length_ = static_cast< ::size_t>(size); |
244 | #ifdef USE_MAP_HUGETLB |
245 | addr_ = ::mmap(NULL, length_, PROT_READ | PROT_WRITE, |
246 | flags | MAP_HUGETLB, fd_, 0); |
247 | #endif // USE_MAP_HUGETLB |
248 | if (addr_ == MAP_FAILED) { |
249 | addr_ = ::mmap(NULL, length_, PROT_READ | PROT_WRITE, flags, fd_, 0); |
250 | GRN_DAT_THROW_IF(IO_ERROR, addr_ == MAP_FAILED); |
251 | } |
252 | |
253 | ptr_ = addr_; |
254 | size_ = length_; |
255 | } |
256 | |
257 | void FileImpl::open_(const char *path) { |
258 | struct stat st; |
259 | GRN_DAT_THROW_IF(IO_ERROR, ::stat(path, &st) == -1); |
260 | GRN_DAT_THROW_IF(IO_ERROR, (st.st_mode & S_IFMT) != S_IFREG); |
261 | GRN_DAT_THROW_IF(IO_ERROR, st.st_size == 0); |
262 | GRN_DAT_THROW_IF(IO_ERROR, |
263 | static_cast<UInt64>(st.st_size) > std::numeric_limits< ::size_t>::max()); |
264 | |
265 | fd_ = ::open(path, O_RDWR); |
266 | GRN_DAT_THROW_IF(IO_ERROR, fd_ == -1); |
267 | |
268 | length_ = static_cast<std::size_t>(st.st_size); |
269 | addr_ = ::mmap(NULL, length_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); |
270 | GRN_DAT_THROW_IF(IO_ERROR, addr_ == MAP_FAILED); |
271 | |
272 | ptr_ = addr_; |
273 | size_ = length_; |
274 | } |
275 | |
276 | #endif // WIN32 |
277 | |
278 | } // namespace dat |
279 | } // namespace grn |
280 | |