1 #include "iwrdb.h"
2 #include "iwp.h"
3 #include "iwlog.h"
4 #include "iwfile.h"
5
6 #include <sys/types.h>
7 #include <fcntl.h>
8 #include <pthread.h>
9
10 #ifndef O_CLOEXEC
11 #define O_CLOEXEC 0
12 #endif
13
14 #include "iwcfg.h"
15
16 #define _ENSURE_OPEN(db_) \
17 if (!(db_) || INVALIDHANDLE((db_)->fh)) return IW_ERROR_INVALID_STATE
18
19 struct _IWRDB {
20 HANDLE fh;
21 iwrdb_oflags_t oflags;
22 pthread_rwlock_t *cwl;
23 char *path;
24 uint8_t *buf;
25 size_t bufsz;
26 off_t bp;
27 off_t end;
28 };
29
_wlock(IWRDB db)30 IW_INLINE iwrc _wlock(IWRDB db) {
31 int rci = db->cwl ? pthread_rwlock_wrlock(db->cwl) : 0;
32 return (rci ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci) : 0);
33 }
34
_rlock(IWRDB db)35 IW_INLINE iwrc _rlock(IWRDB db) {
36 int rci = db->cwl ? pthread_rwlock_rdlock(db->cwl) : 0;
37 return (rci ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci) : 0);
38 }
39
_unlock(IWRDB db)40 IW_INLINE iwrc _unlock(IWRDB db) {
41 int rci = db->cwl ? pthread_rwlock_unlock(db->cwl) : 0;
42 return (rci ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci) : 0);
43 }
44
_initlocks(IWRDB db)45 static iwrc _initlocks(IWRDB db) {
46 if (db->oflags & IWRDB_NOLOCKS) {
47 db->cwl = 0;
48 return 0;
49 }
50 db->cwl = malloc(sizeof(*db->cwl));
51 if (!db->cwl) {
52 return iwrc_set_errno(IW_ERROR_ALLOC, errno);
53 }
54
55 pthread_rwlockattr_t attr;
56 pthread_rwlockattr_init(&attr);
57 #if defined __linux__ && (defined __USE_UNIX98 || defined __USE_XOPEN2K)
58 pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
59 #endif
60 int rci = pthread_rwlock_init(db->cwl, &attr);
61 if (rci) {
62 free(db->cwl);
63 db->cwl = 0;
64 return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
65 }
66 return 0;
67 }
68
_destroy_locks(IWRDB db)69 static iwrc _destroy_locks(IWRDB db) {
70 iwrc rc = 0;
71 if (!db->cwl) {
72 return 0;
73 }
74 int rci = pthread_rwlock_destroy(db->cwl);
75 if (rci) {
76 IWRC(iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci), rc);
77 }
78 free(db->cwl);
79 db->cwl = 0;
80 return rc;
81 }
82
_flush_lw(IWRDB db)83 static iwrc _flush_lw(IWRDB db) {
84 if (db->bp < 1) {
85 return 0;
86 }
87 iwrc rc = iwp_write(db->fh, db->buf, db->bp);
88 RCRET(rc);
89 db->end += db->bp;
90 db->bp = 0;
91 return 0;
92 }
93
_append_lw(IWRDB db,const void * data,int len,uint64_t * oref)94 static iwrc _append_lw(IWRDB db, const void *data, int len, uint64_t *oref) {
95 iwrc rc = 0;
96 *oref = 0;
97
98 if (db->bufsz && (db->bp + len > db->bufsz)) {
99 rc = _flush_lw(db);
100 if (rc) {
101 return rc;
102 }
103 }
104 if (!db->bufsz || (db->bp + len > db->bufsz)) {
105 *oref = db->end + 1;
106 rc = iwp_write(db->fh, data, len);
107 RCRET(rc);
108 db->end += len;
109 } else {
110 memcpy(db->buf + db->bp, data, len);
111 *oref = db->end + db->bp + 1;
112 db->bp += len;
113 assert(db->bp <= db->bufsz);
114 }
115 if (db->bufsz && (db->bp == db->bufsz)) {
116 return _flush_lw(db);
117 }
118 return rc;
119 }
120
iwrdb_open(const char * path,iwrdb_oflags_t oflags,size_t bufsz,IWRDB * odb)121 iwrc iwrdb_open(const char *path, iwrdb_oflags_t oflags, size_t bufsz, IWRDB *odb) {
122 assert(path && odb);
123 iwrc rc = 0;
124 IWRDB db = 0;
125 *odb = 0;
126
127 #ifndef _WIN32
128 HANDLE fh = open(path, O_CREAT | O_RDWR | O_CLOEXEC, IWFS_DEFAULT_FILEMODE);
129 if (INVALIDHANDLE(fh)) {
130 return iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
131 }
132 #else
133 HANDLE fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
134 FILE_SHARE_READ | FILE_SHARE_WRITE,
135 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
136 if (INVALIDHANDLE(fh)) {
137 return iwrc_set_werror(IW_ERROR_IO_ERRNO, GetLastError());
138 }
139 #endif
140
141 db = calloc(1, sizeof(*db));
142 if (!db) {
143 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
144 goto finish;
145 }
146 *odb = db;
147 db->oflags = oflags;
148 db->path = strdup(path);
149 db->fh = fh;
150 if (bufsz) {
151 db->buf = malloc(bufsz);
152 if (!db->buf) {
153 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
154 goto finish;
155 }
156 db->bufsz = bufsz;
157 }
158 rc = iwp_lseek(db->fh, 0, IWP_SEEK_END, &db->end);
159 RCGO(rc, finish);
160 rc = _initlocks(db);
161
162 finish:
163 if (rc && db) {
164 IWRC(iwrdb_close(&db), rc);
165 }
166 return rc;
167 }
168
iwrdb_sync(IWRDB db)169 iwrc iwrdb_sync(IWRDB db) {
170 iwrc rc;
171 _ENSURE_OPEN(db);
172 rc = _wlock(db);
173 RCRET(rc);
174 rc = _flush_lw(db);
175 RCGO(rc, finish);
176 rc = iwp_fsync(db->fh);
177 RCGO(rc, finish);
178
179 finish:
180 IWRC(_unlock(db), rc);
181 return rc;
182 }
183
iwrdb_close(IWRDB * rdb)184 iwrc iwrdb_close(IWRDB *rdb) {
185 iwrc rc = 0;
186 IWRDB db;
187 if (!rdb || !*rdb) {
188 return 0;
189 }
190 db = *rdb;
191 if (!INVALIDHANDLE(db->fh)) {
192 IWRC(iwrdb_sync(db), rc);
193 IWRC(iwp_closefh(db->fh), rc);
194 }
195 db->fh = INVALID_HANDLE_VALUE;
196 IWRC(_destroy_locks(db), rc);
197 free(db->path);
198 free(db->buf);
199 free(db);
200 *rdb = 0;
201 return rc;
202 }
203
iwrdb_append(IWRDB db,const void * data,int len,uint64_t * oref)204 iwrc iwrdb_append(IWRDB db, const void *data, int len, uint64_t *oref) {
205 _ENSURE_OPEN(db);
206 iwrc rc = _wlock(db);
207 if (rc) {
208 return rc;
209 }
210 rc = _append_lw(db, data, len, oref);
211 IWRC(_unlock(db), rc);
212 return rc;
213 }
214
iwrdb_patch(IWRDB db,uint64_t ref,off_t skip,const void * data,int len)215 iwrc iwrdb_patch(IWRDB db, uint64_t ref, off_t skip, const void *data, int len) {
216 iwrc rc;
217 size_t sz;
218 ssize_t sz2;
219 ssize_t tw = len;
220 uint8_t *rp = (uint8_t*) data;
221 ssize_t off = ref - 1 + skip;
222
223 _ENSURE_OPEN(db);
224 if (!ref || (off < 0) || (skip < 0)) {
225 return IW_ERROR_INVALID_ARGS;
226 }
227 rc = _wlock(db);
228 if (rc) {
229 return rc;
230 }
231 if (off + len > db->end + db->bp) {
232 rc = IW_ERROR_OUT_OF_BOUNDS;
233 goto finish;
234 }
235 if (off < db->end) {
236 sz2 = MIN(len, db->end - off);
237 rc = iwp_pwrite(db->fh, off, rp, sz2, &sz);
238 RCGO(rc, finish);
239 tw -= sz;
240 rp += sz;
241 off += sz;
242 }
243 if (tw > 0) {
244 sz = off - db->end;
245 memcpy(db->buf + sz, rp, tw);
246 }
247 finish:
248 IWRC(_unlock(db), rc);
249 return rc;
250 }
251
iwrdb_read(IWRDB db,uint64_t ref,off_t skip,void * buf,int len,size_t * sp)252 iwrc iwrdb_read(IWRDB db, uint64_t ref, off_t skip, void *buf, int len, size_t *sp) {
253 iwrc rc;
254 size_t sz;
255 ssize_t sz2;
256 ssize_t tr = len;
257 uint8_t *wp = buf;
258 ssize_t off = ref - 1 + skip;
259
260 *sp = 0;
261 _ENSURE_OPEN(db);
262 if (!ref || (skip < 0) || (len < 0)) {
263 return IW_ERROR_INVALID_ARGS;
264 }
265 rc = _rlock(db);
266 if (rc) {
267 return rc;
268 }
269 if (off + len > db->end + db->bp) {
270 off_t l = db->end + db->bp - off;
271 if (l < 0) {
272 rc = IW_ERROR_OUT_OF_BOUNDS;
273 goto finish;
274 }
275 }
276 if (off < db->end) {
277 sz2 = MIN(len, db->end - off);
278 rc = iwp_pread(db->fh, off, wp, sz2, &sz);
279 RCGO(rc, finish);
280 tr -= sz;
281 wp += sz;
282 off += sz;
283 }
284 if ((tr > 0) && (db->bp > 0)) {
285 sz = off - db->end;
286 memcpy(wp, db->buf + sz, tr);
287 }
288 *sp = len;
289
290 finish:
291 IWRC(_unlock(db), rc);
292 return rc;
293 }
294