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