• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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