1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "config.h"
3 #include <assert.h>
4 #include <sys/types.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 
8 #include "internal.h"
9 
10 #undef LOG_IO
11 #ifdef LOG_IO
12 #include <stdio.h>
13 #endif
14 
save_errno(couchstore_error_info_t *errinfo)15 static void save_errno(couchstore_error_info_t *errinfo) {
16     if (errinfo) {
17         errinfo->error = errno;
18     }
19 }
20 
handle_to_fd(couch_file_handle handle)21 static int handle_to_fd(couch_file_handle handle)
22 {
23     return (int)(intptr_t)handle;
24 }
25 
fd_to_handle(int fd)26 static couch_file_handle fd_to_handle(int fd)
27 {
28     return (couch_file_handle)(intptr_t)fd;
29 }
30 
couch_pread(couchstore_error_info_t *errinfo, couch_file_handle handle, void *buf, size_t nbyte, cs_off_t offset)31 static ssize_t couch_pread(couchstore_error_info_t *errinfo,
32                            couch_file_handle handle,
33                            void *buf,
34                            size_t nbyte,
35                            cs_off_t offset)
36 {
37 #ifdef LOG_IO
38     fprintf(stderr, "PREAD  %8llx -- %8llx  (%6.1f kbytes)\n", offset, offset+nbyte, nbyte/1024.0);
39 #endif
40     int fd = handle_to_fd(handle);
41     ssize_t rv;
42     do {
43         rv = pread(fd, buf, nbyte, offset);
44     } while (rv == -1 && errno == EINTR);
45 
46     if (rv < 0) {
47         save_errno(errinfo);
48         return (ssize_t) COUCHSTORE_ERROR_READ;
49     }
50     return rv;
51 }
52 
couch_pwrite(couchstore_error_info_t *errinfo, couch_file_handle handle, const void *buf, size_t nbyte, cs_off_t offset)53 static ssize_t couch_pwrite(couchstore_error_info_t *errinfo,
54                             couch_file_handle handle,
55                             const void *buf,
56                             size_t nbyte,
57                             cs_off_t offset)
58 {
59 #ifdef LOG_IO
60     fprintf(stderr, "PWRITE %8llx -- %8llx  (%6.1f kbytes)\n", offset, offset+nbyte, nbyte/1024.0);
61 #endif
62     int fd = handle_to_fd(handle);
63     ssize_t rv;
64     do {
65         rv = pwrite(fd, buf, nbyte, offset);
66     } while (rv == -1 && errno == EINTR);
67 
68     if (rv < 0) {
69         save_errno(errinfo);
70         return (ssize_t) COUCHSTORE_ERROR_WRITE;
71     }
72     return rv;
73 }
74 
couch_open(couchstore_error_info_t *errinfo, couch_file_handle* handle, const char *path, int oflag)75 static couchstore_error_t couch_open(couchstore_error_info_t *errinfo,
76                                      couch_file_handle* handle,
77                                      const char *path,
78                                      int oflag)
79 {
80     int fd;
81     do {
82         fd = open(path, oflag | O_LARGEFILE, 0666);
83     } while (fd == -1 && errno == EINTR);
84 
85     if (fd < 0) {
86         save_errno(errinfo);
87         if (errno == ENOENT) {
88             return COUCHSTORE_ERROR_NO_SUCH_FILE;
89         } else {
90             return COUCHSTORE_ERROR_OPEN_FILE;
91         }
92     }
93     /* Tell the caller about the new handle (file descriptor) */
94     *handle = fd_to_handle(fd);
95     return COUCHSTORE_SUCCESS;
96 }
97 
couch_close(couchstore_error_info_t *errinfo, couch_file_handle handle)98 static void couch_close(couchstore_error_info_t *errinfo,
99                         couch_file_handle handle)
100 {
101     int fd = handle_to_fd(handle);
102     int rv = 0;
103 
104     if (fd != -1) {
105         do {
106             assert(fd >= 3);
107             rv = close(fd);
108         } while (rv == -1 && errno == EINTR);
109     }
110     if (rv < 0) {
111         save_errno(errinfo);
112     }
113 }
114 
couch_goto_eof(couchstore_error_info_t *errinfo, couch_file_handle handle)115 static cs_off_t couch_goto_eof(couchstore_error_info_t *errinfo,
116                                couch_file_handle handle)
117 {
118     int fd = handle_to_fd(handle);
119     cs_off_t rv = lseek(fd, 0, SEEK_END);
120     if (rv < 0) {
121         save_errno(errinfo);
122     }
123     return rv;
124 }
125 
126 
couch_sync(couchstore_error_info_t *errinfo, couch_file_handle handle)127 static couchstore_error_t couch_sync(couchstore_error_info_t *errinfo,
128                                      couch_file_handle handle)
129 {
130     int fd = handle_to_fd(handle);
131     int rv;
132     do {
133         rv = fdatasync(fd);
134     } while (rv == -1 && errno == EINTR);
135 
136     if (rv == -1) {
137         save_errno(errinfo);
138         return COUCHSTORE_ERROR_WRITE;
139     }
140 
141     return COUCHSTORE_SUCCESS;
142 }
143 
couch_constructor(couchstore_error_info_t *errinfo, void* cookie)144 static couch_file_handle couch_constructor(couchstore_error_info_t *errinfo,
145                                            void* cookie)
146 {
147     (void) cookie;
148     (void)errinfo;
149     /*
150     ** We don't have a file descriptor till couch_open runs, so return
151     ** an invalid value for now.
152     */
153     return fd_to_handle(-1);
154 }
155 
couch_destructor(couchstore_error_info_t *errinfo, couch_file_handle handle)156 static void couch_destructor(couchstore_error_info_t *errinfo,
157                              couch_file_handle handle)
158 {
159     /* nothing to do here */
160     (void)handle;
161     (void)errinfo;
162 }
163 
couch_advise(couchstore_error_info_t *errinfo, couch_file_handle handle, cs_off_t offset, cs_off_t len, couchstore_file_advice_t advice)164 static couchstore_error_t couch_advise(couchstore_error_info_t *errinfo,
165                                        couch_file_handle handle,
166                                        cs_off_t offset,
167                                        cs_off_t len,
168                                        couchstore_file_advice_t advice)
169 {
170 #ifdef POSIX_FADV_NORMAL
171     int fd = handle_to_fd(handle);
172     int error = posix_fadvise(fd, offset, len, (int) advice);
173     if (error != 0) {
174         save_errno(errinfo);
175     }
176     switch(error) {
177         case EINVAL:
178         case ESPIPE:
179             return COUCHSTORE_ERROR_INVALID_ARGUMENTS;
180             break;
181         case EBADF:
182             return COUCHSTORE_ERROR_OPEN_FILE;
183             break;
184     }
185 #else
186     (void) handle; (void)offset; (void)len; (void)advice;
187     (void)errinfo;
188 #endif
189     return COUCHSTORE_SUCCESS;
190 }
191 
192 static const couch_file_ops default_file_ops = {
193     (uint64_t)5,
194     couch_constructor,
195     couch_open,
196     couch_close,
197     couch_pread,
198     couch_pwrite,
199     couch_goto_eof,
200     couch_sync,
201     couch_advise,
202     couch_destructor,
203     NULL
204 };
205 
206 LIBCOUCHSTORE_API
couchstore_get_default_file_ops(void)207 const couch_file_ops *couchstore_get_default_file_ops(void)
208 {
209     return &default_file_ops;
210 }
211