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