xref: /6.6.0/couchstore/src/os_win.cc (revision cf120ada)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17#include "couchstore_config.h"
18
19#include "internal.h"
20
21#include <errno.h>
22#include <fcntl.h>
23#include <io.h>
24#include <platform/cbassert.h>
25#include <share.h>
26#include <sys/types.h>
27
28#undef LOG_IO
29#ifdef LOG_IO
30#include <cstdio>
31#endif
32
33static DWORD save_windows_error(couchstore_error_info_t *errinfo) {
34    DWORD err = GetLastError();
35    if (errinfo) {
36        errinfo->error = err;
37    }
38
39    return err;
40}
41
42static HANDLE handle_to_win(couch_file_handle handle)
43{
44    return (HANDLE)(intptr_t)handle;
45}
46
47static couch_file_handle win_to_handle(HANDLE hdl)
48{
49    return (couch_file_handle)(intptr_t)hdl;
50}
51
52class WindowsFileOps : public FileOpsInterface {
53public:
54    WindowsFileOps() {}
55
56    couch_file_handle constructor(couchstore_error_info_t* errinfo) override ;
57    couchstore_error_t open(couchstore_error_info_t* errinfo,
58                            couch_file_handle* handle, const char* path,
59                            int oflag) override;
60    couchstore_error_t close(couchstore_error_info_t* errinfo,
61                             couch_file_handle handle) override;
62    couchstore_error_t set_periodic_sync(couch_file_handle handle,
63                                         uint64_t period_bytes) override;
64    ssize_t pread(couchstore_error_info_t* errinfo,
65                  couch_file_handle handle, void* buf, size_t nbytes,
66                  cs_off_t offset) override;
67    ssize_t pwrite(couchstore_error_info_t* errinfo,
68                   couch_file_handle handle, const void* buf,
69                   size_t nbytes, cs_off_t offset) override;
70    cs_off_t goto_eof(couchstore_error_info_t* errinfo,
71                      couch_file_handle handle) override;
72    couchstore_error_t sync(couchstore_error_info_t* errinfo,
73                            couch_file_handle handle) override;
74    couchstore_error_t advise(couchstore_error_info_t* errinfo,
75                              couch_file_handle handle, cs_off_t offset,
76                              cs_off_t len,
77                              couchstore_file_advice_t advice) override;
78    void destructor(couch_file_handle handle) override;
79
80private:
81    // State of a single file handle, as returned by open().
82    struct File {
83        File(HANDLE fh = INVALID_HANDLE_VALUE) : fh(fh) {
84        }
85
86        /// File handle to operate on.
87        HANDLE fh;
88
89        /**
90         * If non-zero, specifies that sync() should automatically be called
91         * after every N bytes are written.
92         */
93        uint64_t periodic_sync_bytes = 0;
94
95        /// Count of how many bytes have been written since the last sync().
96        uint64_t bytes_written_since_last_sync = 0;
97    };
98
99    static File* to_file(couch_file_handle handle)
100    {
101        return reinterpret_cast<File*>(handle);
102    }
103};
104
105ssize_t WindowsFileOps::pread(couchstore_error_info_t* errinfo,
106                              couch_file_handle handle,
107                              void* buf,
108                              size_t nbyte,
109                              cs_off_t offset)
110{
111#ifdef LOG_IO
112    fprintf(stderr, "PREAD  %8llx -- %8llx  (%6.1f kbytes)\n", offset,
113            offset+nbyte, nbyte/1024.0);
114#endif
115    auto* file = to_file(handle);
116    BOOL rv;
117    DWORD bytesread;
118    OVERLAPPED winoffs;
119    memset(&winoffs, 0, sizeof(winoffs));
120    winoffs.Offset = offset & 0xFFFFFFFF;
121    winoffs.OffsetHigh = (offset >> 32) & 0x7FFFFFFF;
122    rv = ReadFile(file->fh, buf, nbyte, &bytesread, &winoffs);
123    if(!rv) {
124        save_windows_error(errinfo);
125        return (ssize_t) COUCHSTORE_ERROR_READ;
126    }
127    return bytesread;
128}
129
130ssize_t WindowsFileOps::pwrite(couchstore_error_info_t *errinfo,
131                               couch_file_handle handle,
132                               const void *buf,
133                               size_t nbyte,
134                               cs_off_t offset)
135{
136#ifdef LOG_IO
137    fprintf(stderr, "PWRITE %8llx -- %8llx  (%6.1f kbytes)\n", offset,
138            offset+nbyte, nbyte/1024.0);
139#endif
140    auto* file = to_file(handle);
141    BOOL rv;
142    DWORD byteswritten;
143    OVERLAPPED winoffs;
144    memset(&winoffs, 0, sizeof(winoffs));
145    winoffs.Offset = offset & 0xFFFFFFFF;
146    winoffs.OffsetHigh = (offset >> 32) & 0x7FFFFFFF;
147    rv = WriteFile(file->fh, buf, nbyte, &byteswritten, &winoffs);
148    if(!rv) {
149        save_windows_error(errinfo);
150        return (ssize_t) COUCHSTORE_ERROR_WRITE;
151    }
152
153    file->bytes_written_since_last_sync += rv;
154    if ((file->periodic_sync_bytes > 0) &&
155        (file->bytes_written_since_last_sync >= file->periodic_sync_bytes)) {
156        couchstore_error_t sync_rv = sync(errinfo, handle);
157        file->bytes_written_since_last_sync = 0;
158        if (sync_rv != COUCHSTORE_SUCCESS) {
159            return sync_rv;
160        }
161    }
162
163    return byteswritten;
164}
165
166couchstore_error_t WindowsFileOps::open(couchstore_error_info_t *errinfo,
167                                        couch_file_handle* handle,
168                                        const char* path,
169                                        int oflag)
170{
171    auto* file = to_file(*handle);
172    if (file) {
173        cb_assert(file->fh == INVALID_HANDLE_VALUE);
174        delete file;
175        *handle = nullptr;
176    }
177
178    int creationflag = OPEN_EXISTING;
179    if(oflag & O_CREAT) {
180        creationflag = OPEN_ALWAYS;
181    }
182
183    HANDLE os_handle = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
184                                   FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ,
185                                   NULL, creationflag, 0, NULL);
186
187    if(os_handle == INVALID_HANDLE_VALUE) {
188        DWORD last_error = save_windows_error(errinfo);
189        if(last_error == ERROR_FILE_NOT_FOUND ||
190                last_error == ERROR_SUCCESS) {
191            return COUCHSTORE_ERROR_NO_SUCH_FILE;
192        };
193        return COUCHSTORE_ERROR_OPEN_FILE;
194    }
195    /* Tell the caller about the new handle (file descriptor) */
196    file = new File(os_handle);
197    *handle = reinterpret_cast<couch_file_handle>(file);
198    return COUCHSTORE_SUCCESS;
199}
200
201couchstore_error_t WindowsFileOps::close(couchstore_error_info_t* errinfo,
202                                         couch_file_handle handle)
203{
204    auto* file = to_file(handle);
205    if(!CloseHandle(file->fh)) {
206        save_windows_error(errinfo);
207        return COUCHSTORE_ERROR_FILE_CLOSE;
208    }
209    return COUCHSTORE_SUCCESS;
210}
211
212couchstore_error_t WindowsFileOps::set_periodic_sync(couch_file_handle handle,
213                                                     uint64_t period_bytes) {
214    auto* file = to_file(handle);
215    file->periodic_sync_bytes = period_bytes;
216    return COUCHSTORE_SUCCESS;
217}
218
219cs_off_t WindowsFileOps::goto_eof(couchstore_error_info_t* errinfo,
220                                  couch_file_handle handle)
221{
222    auto* file = to_file(handle);
223    LARGE_INTEGER size;
224    if(!GetFileSizeEx(file->fh, &size)) {
225        save_windows_error(errinfo);
226        return (cs_off_t) COUCHSTORE_ERROR_READ;
227    }
228    return size.QuadPart;
229}
230
231
232couchstore_error_t WindowsFileOps::sync(couchstore_error_info_t* errinfo,
233                                        couch_file_handle handle)
234{
235    auto* file = to_file(handle);
236
237    if (!FlushFileBuffers(file->fh)) {
238        save_windows_error(errinfo);
239        return COUCHSTORE_ERROR_WRITE;
240    }
241
242    return COUCHSTORE_SUCCESS;
243}
244
245couch_file_handle WindowsFileOps::constructor(couchstore_error_info_t* errinfo)
246{
247
248    /*  We don't have a file descriptor till couch_open runs,
249        so return an invalid value for now. */
250    return reinterpret_cast<couch_file_handle>(new File());
251}
252
253void WindowsFileOps::destructor(couch_file_handle handle)
254{
255    auto* file = to_file(handle);
256    delete file;
257}
258
259couchstore_error_t WindowsFileOps::advise(couchstore_error_info_t* errinfo,
260                                          couch_file_handle handle,
261                                          cs_off_t offset,
262                                          cs_off_t len,
263                                          couchstore_file_advice_t advice)
264{
265    return COUCHSTORE_SUCCESS;
266}
267
268WindowsFileOps default_file_ops;
269
270FileOpsInterface* couchstore_get_default_file_ops(void)
271{
272    return &default_file_ops;
273}
274
275FileOpsInterface* create_default_file_ops(void)
276{
277    return new WindowsFileOps();
278}
279