• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************************************
2  * IOWOW library
3  *
4  * MIT License
5  *
6  * Copyright (c) 2012-2020 Softmotions Ltd <info@softmotions.com>
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in all
16  * copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  *************************************************************************************************/
26 
27 #include "iwcfg.h"
28 #include "iwutils.h"
29 #include "iwlog.h"
30 #include "iwexfile.h"
31 
32 #include <pthread.h>
33 #ifdef _WIN32
34 #include "win32/mman/mman.h"
35 #else
36 #include <sys/mman.h>
37 #endif
38 
39 struct MMAPSLOT;
40 typedef struct IWFS_EXT_IMPL {
41   IWFS_FILE file;            /**< Underlying file */
42   IWDLSNR *dlsnr;            /**< Data events listener */
43   pthread_rwlock_t *rwlock;  /**< Thread RW lock */
44   struct MMAPSLOT *mmslots;  /**< Memory mapping slots */
45   void *rspolicy_ctx;        /**< Custom opaque data for policy functions */
46   IW_EXT_RSPOLICY rspolicy;  /**< File resize policy function ptr */
47   uint64_t fsize;            /**< Current file size */
48   uint64_t maxoff;           /**< Maximum allowed file offset. Unlimited if zero.
49                                   If maximum offset is reached `IWFS_ERROR_MAXOFF` will be reported. */
50   size_t psize;              /**< System page size */
51   HANDLE fh;                 /**< File handle */
52   iwfs_omode omode;          /**< File open mode */
53   bool use_locks;            /**< Use rwlocks to guard method access */
54 } EXF;
55 
56 typedef struct MMAPSLOT {
57   off_t off;     /**< Offset to a memory mapped region */
58   size_t len;    /**< Actual size of memory mapped region. */
59   size_t maxlen; /**< Maximum length of memory mapped region */
60   iwfs_ext_mmap_opts_t mmopts;
61   struct MMAPSLOT *prev; /**< Previous mmap slot. */
62   struct MMAPSLOT *next; /**< Next mmap slot. */
63   uint8_t *mmap;          /**< Pointer to a mmaped address space
64                                in the case if file data is memory mapped. */
65 } MMAPSLOT;
66 
_exfile_wlock(IWFS_EXT * f)67 IW_INLINE iwrc _exfile_wlock(IWFS_EXT *f) {
68   struct IWFS_EXT_IMPL *impl = f->impl;
69   if (impl) {
70     if (!impl->use_locks) return 0;
71     if (impl->rwlock) {
72       int rv = pthread_rwlock_wrlock(impl->rwlock);
73       return rv ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rv) : 0;
74     }
75   }
76   return IW_ERROR_INVALID_STATE;
77 }
78 
_exfile_rlock(IWFS_EXT * f)79 IW_INLINE iwrc _exfile_rlock(IWFS_EXT *f) {
80   struct IWFS_EXT_IMPL *impl = f->impl;
81   if (impl) {
82     if (!impl->use_locks) return 0;
83     if (impl->rwlock) {
84       int rv = pthread_rwlock_rdlock(impl->rwlock);
85       return rv ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rv) : 0;
86     }
87   }
88   return IW_ERROR_INVALID_STATE;
89 }
90 
_exfile_unlock(IWFS_EXT * f)91 IW_INLINE iwrc _exfile_unlock(IWFS_EXT *f) {
92   struct IWFS_EXT_IMPL *impl = f->impl;
93   if (impl) {
94     if (!impl->use_locks) return 0;
95     if (impl->rwlock) {
96       int rv = pthread_rwlock_unlock(impl->rwlock);
97       return rv ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rv) : 0;
98     }
99   }
100   return IW_ERROR_INVALID_STATE;
101 }
102 
_exfile_unlock2(EXF * impl)103 IW_INLINE iwrc _exfile_unlock2(EXF *impl) {
104   if (impl) {
105     if (!impl->use_locks) return 0;
106     if (impl->rwlock) {
107       int rv = pthread_rwlock_unlock(impl->rwlock);
108       return rv ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rv) : 0;
109     }
110   }
111   return IW_ERROR_INVALID_STATE;
112 }
113 
_exfile_destroylocks(EXF * impl)114 static iwrc _exfile_destroylocks(EXF *impl) {
115   if (!impl) return IW_ERROR_INVALID_STATE;
116   if (!impl->rwlock) return 0;
117   int rv = pthread_rwlock_destroy(impl->rwlock);
118   free(impl->rwlock);
119   impl->rwlock = 0;
120   return rv ? iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rv) : 0;
121 }
122 
_exfile_initmmap_slot_lw(struct IWFS_EXT * f,MMAPSLOT * s)123 static iwrc _exfile_initmmap_slot_lw(struct IWFS_EXT *f, MMAPSLOT *s) {
124   assert(f && s);
125   size_t nlen;
126   EXF *impl = f->impl;
127   if (s->off >= impl->fsize) {
128     nlen = 0;
129   } else {
130     nlen = MIN(s->maxlen, impl->fsize - s->off);
131   }
132   if (nlen == s->len) {
133     return 0;
134   }
135   if (s->len) {  // unmap me first
136     assert(s->mmap);
137     if (!(s->mmopts & IWFS_MMAP_PRIVATE) && msync(s->mmap, s->len, 0) == -1) {
138       s->len = 0;
139       return iwrc_set_errno(IW_ERROR_ERRNO, errno);
140     }
141     if (munmap(s->mmap, s->len) == -1) {
142       s->len = 0;
143       return iwrc_set_errno(IW_ERROR_ERRNO, errno);
144     }
145     s->len = 0;
146   }
147   if (nlen > 0) {
148     int flags = (s->mmopts & IWFS_MMAP_PRIVATE)
149 #ifdef MAP_NORESERVE
150                 ? (MAP_PRIVATE + MAP_NORESERVE)
151 #else
152                 ? MAP_PRIVATE
153 #endif
154                 : MAP_SHARED;
155     int prot = (impl->omode & IWFS_OWRITE) ? (PROT_WRITE + PROT_READ) : (PROT_READ);
156     s->len = nlen;
157     s->mmap = mmap(s->mmap, s->len, prot, flags, impl->fh, s->off); // -V774
158     if (s->mmap == MAP_FAILED) {
159       iwrc rc = iwrc_set_errno(IW_ERROR_ERRNO, errno);
160       iwlog_ecode_error3(rc);
161       return rc;
162     }
163   }
164   return 0;
165 }
166 
_exfile_initmmap_lw(struct IWFS_EXT * f)167 static iwrc _exfile_initmmap_lw(struct IWFS_EXT *f) {
168   assert(f);
169   iwrc rc = 0;
170   EXF *impl = f->impl;
171   assert(!(impl->fsize & (impl->psize - 1)));
172   MMAPSLOT *s = impl->mmslots;
173   while (s) {
174     rc = _exfile_initmmap_slot_lw(f, s);
175     if (rc) {
176       break;
177     }
178     s = s->next;
179   }
180   return rc;
181 }
182 
_exfile_truncate_lw(struct IWFS_EXT * f,off_t size)183 static iwrc _exfile_truncate_lw(struct IWFS_EXT *f, off_t size) {
184   assert(f && f->impl);
185   iwrc rc = 0;
186   EXF *impl = f->impl;
187   iwfs_omode omode = impl->omode;
188   uint64_t old_size = impl->fsize;
189   bool rsh = false;
190 
191   size = IW_ROUNDUP(size, impl->psize);
192   if (old_size == size) {
193     return 0;
194   }
195 
196   if (old_size < size) {
197     if (!(omode & IWFS_OWRITE)) {
198       return IW_ERROR_READONLY;
199     }
200     if (impl->maxoff && size > impl->maxoff) {
201       return IWFS_ERROR_MAXOFF;
202     }
203     if (impl->dlsnr) {
204       rc = impl->dlsnr->onresize(impl->dlsnr, old_size, size, 0, &rsh);
205       RCGO(rc, truncfail);
206     }
207     if (!rsh) {
208       impl->fsize = (uint64_t) size;
209       rc = iwp_fallocate(impl->fh, size);
210       RCGO(rc, truncfail);
211       rc = _exfile_initmmap_lw(f);
212     }
213   } else if (old_size > size) {
214     if (!(omode & IWFS_OWRITE)) {
215       return IW_ERROR_READONLY;
216     }
217     if (impl->dlsnr) {
218       rc = impl->dlsnr->onresize(impl->dlsnr, old_size, size, 0, &rsh);
219       RCGO(rc, truncfail);
220     }
221     if (!rsh) {
222       impl->fsize = (uint64_t) size;
223       rc = _exfile_initmmap_lw(f);
224       RCGO(rc, truncfail);
225       rc = iwp_ftruncate(impl->fh, size);
226       RCGO(rc, truncfail);
227     }
228   }
229   return rc;
230 
231 truncfail:
232   impl->fsize = old_size;
233   IWRC(_exfile_initmmap_lw(f), rc);
234   return rc;
235 }
236 
_exfile_ensure_size_lw(struct IWFS_EXT * f,off_t sz)237 static iwrc _exfile_ensure_size_lw(struct IWFS_EXT *f, off_t sz) {
238   EXF *impl = f->impl;
239   assert(impl && impl->rspolicy);
240   if (impl->fsize >= sz) {
241     return 0;
242   }
243   off_t nsz = impl->rspolicy(sz, impl->fsize, f, &impl->rspolicy_ctx);
244   if (nsz < sz || ((uint64_t) nsz & (impl->psize - 1))) {
245     return IWFS_ERROR_RESIZE_POLICY_FAIL;
246   }
247   if (impl->maxoff && nsz > impl->maxoff) {
248     nsz = impl->maxoff;
249     if (nsz < sz) {
250       return IWFS_ERROR_MAXOFF;
251     }
252   }
253   return _exfile_truncate_lw(f, nsz);
254 }
255 
_exfile_sync(struct IWFS_EXT * f,iwfs_sync_flags flags)256 static iwrc _exfile_sync(struct IWFS_EXT *f, iwfs_sync_flags flags) {
257   iwrc rc = _exfile_rlock(f);
258   RCRET(rc);
259   EXF *impl = f->impl;
260   int mflags = MS_SYNC;
261   MMAPSLOT *s = impl->mmslots;
262   while (s) {
263     if (s->mmap && s->mmap != MAP_FAILED
264         && !(s->mmopts & IWFS_MMAP_PRIVATE)
265         && msync(s->mmap, s->len, mflags) == -1) {
266       rc = iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
267     }
268     s = s->next;
269   }
270   IWRC(impl->file.sync(&impl->file, flags), rc);
271   IWRC(_exfile_unlock2(impl), rc);
272   return rc;
273 }
274 
_exfile_write(struct IWFS_EXT * f,off_t off,const void * buf,size_t siz,size_t * sp)275 static iwrc _exfile_write(struct IWFS_EXT *f, off_t off, const void *buf, size_t siz, size_t *sp) {
276   MMAPSLOT *s;
277   EXF *impl = f->impl;
278   off_t end = off + siz;
279   off_t wp = siz, len;
280 
281   *sp = 0;
282   if (off < 0 || end < 0) {
283     return IW_ERROR_OUT_OF_BOUNDS;
284   }
285   if (impl->maxoff && off + siz > impl->maxoff) {
286     return IWFS_ERROR_MAXOFF;
287   }
288   iwrc rc = _exfile_rlock(f);
289   RCRET(rc);
290   if (end > impl->fsize) {
291     rc = _exfile_unlock2(impl);
292     RCGO(rc, end);
293     rc = _exfile_wlock(f);
294     RCGO(rc, end);
295     if (end > impl->fsize) {
296       rc = _exfile_ensure_size_lw(f, end);
297       RCGO(rc, finish);
298     }
299   }
300   s = impl->mmslots;
301   while (s && wp > 0) {
302     if (!s->len || wp + off <= s->off) {
303       break;
304     }
305     if (s->off > off) {
306       len = MIN(wp, s->off - off);
307       rc = impl->file.write(&impl->file, off, (const char *) buf + (siz - wp), (size_t) len, sp);
308       RCGO(rc, finish);
309       wp = wp - *sp;
310       off = off + *sp;
311     }
312     if (wp > 0 && s->off <= off && s->off + s->len > off) {
313       len = MIN(wp, s->off + s->len - off);
314       if (impl->dlsnr) {
315         rc = impl->dlsnr->onwrite(impl->dlsnr, off - s->off, (const char *) buf + (siz - wp), len, 0);
316         RCGO(rc, finish);
317       }
318       memcpy(s->mmap + (off - s->off), (const char *) buf + (siz - wp), (size_t) len);
319       wp -= len;
320       off += len;
321     }
322     s = s->next;
323   }
324   if (wp > 0) {
325     rc = impl->file.write(&impl->file, off, (const char *) buf + (siz - wp), (size_t) wp, sp);
326     RCGO(rc, finish);
327     wp = wp - *sp;
328   }
329   *sp = siz - wp;
330 finish:
331   IWRC(_exfile_unlock2(impl), rc);
332 end:
333   if (rc) {
334     *sp = 0;
335   }
336   return rc;
337 }
338 
_exfile_read(struct IWFS_EXT * f,off_t off,void * buf,size_t siz,size_t * sp)339 static iwrc _exfile_read(struct IWFS_EXT *f, off_t off, void *buf, size_t siz, size_t *sp) {
340   MMAPSLOT *s;
341   EXF *impl;
342   off_t end = off + siz;
343   off_t rp = siz, len;
344   *sp = 0;
345   if (off < 0 || end < 0) {
346     return IW_ERROR_OUT_OF_BOUNDS;
347   }
348   iwrc rc = _exfile_rlock(f);
349   RCRET(rc);
350   impl = f->impl;
351   s = impl->mmslots;
352   if (end > impl->fsize) {
353     siz = (size_t)(impl->fsize - off);
354     rp = siz;
355   }
356   while (s && rp > 0) {
357     if (!s->len || rp + off <= s->off) {
358       break;
359     }
360     if (s->off > off) {
361       len = MIN(rp, s->off - off);
362       rc = impl->file.read(&impl->file, off, (char *) buf + (siz - rp), (size_t) len, sp);
363       RCGO(rc, finish);
364       rp = rp - *sp;
365       off = off + *sp;
366     }
367     if (rp > 0 && s->off <= off && s->off + s->len > off) {
368       len = MIN(rp, s->off + s->len - off);
369       memcpy((char *) buf + (siz - rp), s->mmap + (off - s->off), (size_t) len);
370       rp -= len;
371       off += len;
372     }
373     s = s->next;
374   }
375   if (rp > 0) {
376     rc = impl->file.read(&impl->file, off, (char *) buf + (siz - rp), (size_t) rp, sp);
377     RCGO(rc, finish);
378     rp = rp - *sp;
379   }
380   *sp = siz - rp;
381 finish:
382   if (rc) {
383     *sp = 0;
384   }
385   IWRC(_exfile_unlock2(impl), rc);
386   return rc;
387 }
388 
_exfile_state(struct IWFS_EXT * f,IWFS_EXT_STATE * state)389 static iwrc _exfile_state(struct IWFS_EXT *f, IWFS_EXT_STATE *state) {
390   iwrc rc = _exfile_rlock(f);
391   RCRET(rc);
392   EXF *xf = f->impl;
393   IWRC(xf->file.state(&xf->file, &state->file), rc);
394   state->fsize = f->impl->fsize;
395   IWRC(_exfile_unlock(f), rc);
396   return rc;
397 }
398 
_exfile_copy(struct IWFS_EXT * f,off_t off,size_t siz,off_t noff)399 static iwrc _exfile_copy(struct IWFS_EXT *f, off_t off, size_t siz, off_t noff) {
400   iwrc rc = _exfile_rlock(f);
401   RCRET(rc);
402   EXF *impl = f->impl;
403   MMAPSLOT *s = impl->mmslots;
404   if (s && s->mmap && s->off == 0 && s->len >= noff + siz) { // fully mmaped file
405     rc = _exfile_ensure_size_lw(f, noff + siz);
406     RCRET(rc);
407     if (impl->dlsnr) {
408       rc = impl->dlsnr->onwrite(impl->dlsnr, noff, s->mmap + off, siz, 0);
409       RCRET(rc);
410     }
411     memmove(s->mmap + noff, s->mmap + off, siz);
412   } else {
413     IWRC(impl->file.copy(&impl->file, off, siz, noff), rc);
414   }
415   IWRC(_exfile_unlock(f), rc);
416   return rc;
417 }
418 
_exfile_remove_mmap_lw(struct IWFS_EXT * f,off_t off)419 static iwrc _exfile_remove_mmap_lw(struct IWFS_EXT *f, off_t off) {
420   iwrc rc = 0;
421   EXF *impl = f->impl;
422   MMAPSLOT *s = impl->mmslots;
423   while (s) {
424     if (s->off == off) {
425       break;
426     }
427     s = s->next;
428   }
429   if (!s) {
430     rc = IWFS_ERROR_NOT_MMAPED;
431     goto finish;
432   }
433   if (impl->mmslots == s) {
434     if (s->next) {
435       s->next->prev = s->prev;
436     }
437     impl->mmslots = s->next;
438   } else if (impl->mmslots->prev == s) {
439     s->prev->next = 0;
440     impl->mmslots->prev = s->prev;
441   } else {
442     s->prev->next = s->next;
443     s->next->prev = s->prev;
444   }
445   if (s->len) {
446     if (munmap(s->mmap, s->len)) {
447       rc = iwrc_set_errno(IW_ERROR_ERRNO, errno);
448       goto finish;
449     }
450   }
451 finish:
452   free(s);
453   return rc;
454 }
455 
_exfile_close(struct IWFS_EXT * f)456 static iwrc _exfile_close(struct IWFS_EXT *f) {
457   if (!f || !f->impl) {
458     return 0;
459   }
460   iwrc rc = _exfile_wlock(f);
461   RCRET(rc);
462   EXF *impl = f->impl;
463   if (impl->dlsnr) {
464     rc = impl->dlsnr->onclosing(impl->dlsnr);
465   }
466   MMAPSLOT *s = impl->mmslots, *next;
467   while (s) {
468     next = s->next;
469     IWRC(_exfile_remove_mmap_lw(f, s->off), rc);
470     s = next;
471   }
472   IWRC(impl->file.close(&impl->file), rc);
473   f->impl = 0;
474   if (impl->rspolicy) {  // dispose resize policy function
475     impl->rspolicy(-1, impl->fsize, f, &impl->rspolicy_ctx);
476   }
477   IWRC(_exfile_unlock2(impl), rc);
478   IWRC(_exfile_destroylocks(impl), rc);
479   free(impl);
480   return rc;
481 }
482 
_exfile_ensure_size(struct IWFS_EXT * f,off_t sz)483 static iwrc _exfile_ensure_size(struct IWFS_EXT *f, off_t sz) {
484   iwrc rc = _exfile_rlock(f);
485   RCRET(rc);
486   if (f->impl->fsize >= sz) {
487     return _exfile_unlock2(f->impl);
488   }
489   rc = _exfile_unlock2(f->impl);
490   RCRET(rc);
491   rc = _exfile_wlock(f);
492   RCRET(rc);
493   rc = _exfile_ensure_size_lw(f, sz);
494   IWRC(_exfile_unlock2(f->impl), rc);
495   return rc;
496 }
497 
_exfile_truncate(struct IWFS_EXT * f,off_t sz)498 static iwrc _exfile_truncate(struct IWFS_EXT *f, off_t sz) {
499   iwrc rc = _exfile_wlock(f);
500   RCRET(rc);
501   rc = _exfile_truncate_lw(f, sz);
502   IWRC(_exfile_unlock(f), rc);
503   return rc;
504 }
505 
_exfile_add_mmap_lw(struct IWFS_EXT * f,off_t off,size_t maxlen,iwfs_ext_mmap_opts_t mmopts)506 static iwrc _exfile_add_mmap_lw(struct IWFS_EXT *f, off_t off, size_t maxlen, iwfs_ext_mmap_opts_t mmopts) {
507   size_t tmp;
508   iwrc rc = 0;
509   MMAPSLOT *ns = 0;
510   EXF *impl = f->impl;
511 
512   if ((uint64_t) off & (impl->psize - 1)) {
513     rc = IW_ERROR_NOT_ALIGNED;
514     goto finish;
515   }
516   if (OFF_T_MAX - off < maxlen) {
517     maxlen = (size_t)(OFF_T_MAX - off);
518   }
519   tmp = IW_ROUNDUP(maxlen, impl->psize);
520   if (tmp < maxlen || OFF_T_MAX - off < tmp) {
521     maxlen = IW_ROUNDOWN(maxlen, impl->psize);
522   } else {
523     maxlen = tmp;
524   }
525   if (!maxlen) {
526     rc = IW_ERROR_OUT_OF_BOUNDS;
527     goto finish;
528   }
529   assert(!(maxlen & (impl->psize - 1)));
530   ns = calloc(1, sizeof(*ns));
531   if (!ns) {
532     rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
533     goto finish;
534   }
535   ns->off = off;
536   ns->len = 0;
537   ns->maxlen = maxlen;
538   ns->mmopts = mmopts;
539   rc = _exfile_initmmap_slot_lw(f, ns);
540   RCGO(rc, finish);
541   if (impl->mmslots == 0) {
542     ns->next = 0;
543     ns->prev = ns;
544     impl->mmslots = ns;
545   } else {
546     MMAPSLOT *s = impl->mmslots;
547     while (s) {
548       off_t e1 = s->off + s->maxlen;
549       off_t e2 = ns->off + ns->maxlen;
550       if (IW_RANGES_OVERLAP(s->off, e1, ns->off, e2)) {
551         rc = IWFS_ERROR_MMAP_OVERLAP;
552         goto finish;
553       }
554       if (ns->off < s->off) {
555         break;
556       }
557       s = s->next;
558     }
559     if (s) {  // insert before
560       ns->next = s;
561       ns->prev = s->prev;
562       s->prev->next = ns;
563       s->prev = ns;
564       if (s == impl->mmslots) {
565         impl->mmslots = ns;
566         ns->prev->next = 0;
567       }
568     } else {  // insert at the end
569       s = impl->mmslots;
570       ns->next = 0;
571       ns->prev = s->prev;
572       s->prev->next = ns;
573       s->prev = ns;
574     }
575   }
576 finish:
577   if (rc) {
578     if (ns) {
579       if (impl->mmslots == ns) {
580         impl->mmslots = 0;
581       }
582       free(ns);
583     }
584   }
585   return rc;
586 }
587 
_exfile_add_mmap(struct IWFS_EXT * f,off_t off,size_t maxlen,iwfs_ext_mmap_opts_t mmopts)588 static iwrc _exfile_add_mmap(struct IWFS_EXT *f, off_t off, size_t maxlen, iwfs_ext_mmap_opts_t mmopts) {
589   assert(f && off >= 0);
590   iwrc rc = _exfile_wlock(f);
591   RCRET(rc);
592   rc = _exfile_add_mmap_lw(f, off, maxlen, mmopts);
593   IWRC(_exfile_unlock(f), rc);
594   return rc;
595 }
596 
_exfile_acquire_mmap(struct IWFS_EXT * f,off_t off,uint8_t ** mm,size_t * sp)597 iwrc _exfile_acquire_mmap(struct IWFS_EXT *f, off_t off, uint8_t **mm, size_t *sp) {
598   assert(f && mm && off >= 0);
599   iwrc rc = _exfile_rlock(f);
600   if (IW_UNLIKELY(rc)) {
601     *mm = 0;
602     if (sp) {
603       *sp = 0;
604     }
605     return rc;
606   }
607   MMAPSLOT *s = f->impl->mmslots;
608   while (s) {
609     if (s->off == off) {
610       if (s->len) {
611         *mm = s->mmap;
612         if (sp) {
613           *sp = s->len;
614         }
615         return 0;
616       }
617       break;
618     }
619     s = s->next;
620   }
621   *mm = 0;
622   if (sp) {
623     *sp = 0;
624   }
625   return IWFS_ERROR_NOT_MMAPED;
626 }
627 
_exfile_probe_mmap_lr(struct IWFS_EXT * f,off_t off,uint8_t ** mm,size_t * sp)628 iwrc _exfile_probe_mmap_lr(struct IWFS_EXT *f, off_t off, uint8_t **mm, size_t *sp) {
629   assert(f && mm && off >= 0);
630   if (sp) {
631     *sp = 0;
632   }
633   *mm = 0;
634   iwrc rc = 0;
635   EXF *impl = f->impl;
636   MMAPSLOT *s = impl->mmslots;
637   while (s) {
638     if (s->off == off) {
639       if (!s->len) {
640         rc = IWFS_ERROR_NOT_MMAPED;
641         break;
642       }
643       *mm = s->mmap;
644       if (sp) {
645         *sp = s->len;
646       }
647       break;
648     }
649     s = s->next;
650   }
651   if (!rc && !*mm) {
652     rc = IWFS_ERROR_NOT_MMAPED;
653   }
654   return rc;
655 }
656 
_exfile_probe_mmap(struct IWFS_EXT * f,off_t off,uint8_t ** mm,size_t * sp)657 iwrc _exfile_probe_mmap(struct IWFS_EXT *f, off_t off, uint8_t **mm, size_t *sp) {
658   iwrc rc = _exfile_rlock(f);
659   RCRET(rc);
660   rc = _exfile_probe_mmap_lr(f, off, mm, sp);
661   IWRC(_exfile_unlock(f), rc);
662   return rc;
663 }
664 
_exfile_release_mmap(struct IWFS_EXT * f)665 iwrc _exfile_release_mmap(struct IWFS_EXT *f) {
666   assert(f);
667   return _exfile_unlock(f);
668 }
669 
_exfile_remove_mmap(struct IWFS_EXT * f,off_t off)670 static iwrc _exfile_remove_mmap(struct IWFS_EXT *f, off_t off) {
671   assert(f && off >= 0);
672   iwrc rc = _exfile_wlock(f);
673   RCRET(rc);
674   rc = _exfile_remove_mmap_lw(f, off);
675   IWRC(_exfile_unlock(f), rc);
676   return rc;
677 }
678 
_exfile_sync_mmap_lr(struct IWFS_EXT * f,off_t off,iwfs_sync_flags flags)679 static iwrc _exfile_sync_mmap_lr(struct IWFS_EXT *f, off_t off, iwfs_sync_flags flags) {
680   assert(f && off >= 0);
681   iwrc rc = 0;
682   EXF *impl = f->impl;
683   int mflags = MS_SYNC;
684   MMAPSLOT *s = impl->mmslots;
685   while (s) {
686     if (s->off == off) {
687       if (s->len == 0) {
688         rc = IWFS_ERROR_NOT_MMAPED;
689         break;
690       }
691       if (s->mmap && s->mmap != MAP_FAILED) {
692         if (!(s->mmopts & IWFS_MMAP_PRIVATE)
693             && msync(s->mmap, s->len, mflags) == -1) {
694           rc = iwrc_set_errno(IW_ERROR_IO_ERRNO, errno);
695         }
696         break;
697       }
698     }
699     s = s->next;
700   }
701   if (!s) {
702     rc = IWFS_ERROR_NOT_MMAPED;
703   }
704   return rc;
705 }
706 
_exfile_sync_mmap(struct IWFS_EXT * f,off_t off,iwfs_sync_flags flags)707 static iwrc _exfile_sync_mmap(struct IWFS_EXT *f, off_t off, iwfs_sync_flags flags) {
708   iwrc rc = _exfile_rlock(f);
709   RCRET(rc);
710   rc = _exfile_sync_mmap_lr(f, off, flags);
711   IWRC(_exfile_unlock(f), rc);
712   return rc;
713 }
714 
_exfile_remap_all(struct IWFS_EXT * f)715 static iwrc _exfile_remap_all(struct IWFS_EXT *f) {
716   assert(f);
717   iwrc rc = _exfile_wlock(f);
718   RCRET(rc);
719   rc = _exfile_initmmap_lw(f);
720   IWRC(_exfile_unlock(f), rc);
721   return rc;
722 }
723 
_exfile_default_szpolicy(off_t nsize,off_t csize,struct IWFS_EXT * f,void ** ctx)724 static off_t _exfile_default_szpolicy(off_t nsize, off_t csize, struct IWFS_EXT *f, void **ctx) {
725   if (nsize == -1) {
726     return 0;
727   }
728   return IW_ROUNDUP(nsize, f->impl->psize);
729 }
730 
iw_exfile_szpolicy_fibo(off_t nsize,off_t csize,struct IWFS_EXT * f,void ** _ctx)731 off_t iw_exfile_szpolicy_fibo(off_t nsize, off_t csize, struct IWFS_EXT *f, void **_ctx) {
732   struct _FIBO_CTX {
733     off_t prev_sz;
734   } *ctx = *_ctx;
735   if (nsize == -1) {
736     if (ctx) {
737       free(ctx);
738       *_ctx = 0;
739     }
740     return 0;
741   }
742   if (!ctx) {
743     *_ctx = ctx = calloc(1, sizeof(*ctx));
744     if (!ctx) {
745       return _exfile_default_szpolicy(nsize, csize, f, _ctx);
746     }
747   }
748   uint64_t res = (uint64_t) csize + ctx->prev_sz;
749   res = MAX(res, nsize);
750   res = IW_ROUNDUP(res, f->impl->psize);
751   if (res > OFF_T_MAX) {
752     res = OFF_T_MAX;
753   }
754   ctx->prev_sz = csize;
755   return res;
756 }
757 
iw_exfile_szpolicy_mul(off_t nsize,off_t csize,struct IWFS_EXT * f,void ** _ctx)758 off_t iw_exfile_szpolicy_mul(off_t nsize, off_t csize, struct IWFS_EXT *f, void **_ctx) {
759   IW_RNUM *mul = *_ctx;
760   if (nsize == -1) {
761     return 0;
762   }
763   if (!mul || !mul->dn || mul->n < mul->dn) {
764     iwlog_error2(
765       "Invalid iw_exfile_szpolicy_mul context arguments, fallback to the "
766       "default resize policy");
767     return _exfile_default_szpolicy(nsize, csize, f, _ctx);
768   }
769   uint64_t ret = (uint64_t) nsize;
770   ret /= mul->dn;
771   ret *= mul->n;
772   ret = IW_ROUNDUP(ret, f->impl->psize);
773   if (ret > OFF_T_MAX) {
774     ret = OFF_T_MAX;
775   }
776   return ret;
777 }
778 
_exfile_initlocks(IWFS_EXT * f)779 static iwrc _exfile_initlocks(IWFS_EXT *f) {
780   assert(f && f->impl);
781   assert(!f->impl->rwlock);
782   EXF *impl = f->impl;
783   if (!impl->use_locks) {
784     return 0;
785   }
786   impl->rwlock = calloc(1, sizeof(*impl->rwlock));
787   if (!impl->rwlock) {
788     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
789   }
790   int rv = pthread_rwlock_init(impl->rwlock, (void *) 0);
791   if (rv) {
792     free(impl->rwlock);
793     impl->rwlock = 0;
794     return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rv);
795   }
796   return 0;
797 }
798 
iwfs_exfile_open(IWFS_EXT * f,const IWFS_EXT_OPTS * opts)799 iwrc iwfs_exfile_open(IWFS_EXT *f, const IWFS_EXT_OPTS *opts) {
800   assert(f);
801   assert(opts);
802   iwrc rc = 0;
803   const char *path = opts->file.path;
804 
805   memset(f, 0, sizeof(*f));
806 
807   rc = iwfs_exfile_init();
808   RCGO(rc, finish);
809 
810   f->close = _exfile_close;
811   f->read = _exfile_read;
812   f->write = _exfile_write;
813   f->sync = _exfile_sync;
814   f->state = _exfile_state;
815   f->copy =  _exfile_copy;
816 
817   f->ensure_size = _exfile_ensure_size;
818 
819   f->truncate = _exfile_truncate;
820   f->truncate_unsafe = _exfile_truncate_lw;
821 
822   f->add_mmap = _exfile_add_mmap;
823   f->add_mmap_unsafe = _exfile_add_mmap_lw;
824 
825   f->remove_mmap = _exfile_remove_mmap;
826   f->remove_mmap_unsafe = _exfile_remove_mmap_lw;
827 
828   f->probe_mmap = _exfile_probe_mmap;
829   f->probe_mmap_unsafe = _exfile_probe_mmap_lr;
830 
831   f->sync_mmap = _exfile_sync_mmap;
832   f->sync_mmap_unsafe = _exfile_sync_mmap_lr;
833 
834   f->acquire_mmap = _exfile_acquire_mmap;
835   f->release_mmap = _exfile_release_mmap;
836   f->remap_all = _exfile_remap_all;
837 
838   if (!path) {
839     return IW_ERROR_INVALID_ARGS;
840   }
841 
842   EXF *impl = f->impl = calloc(1, sizeof(EXF));
843   if (!impl) {
844     return iwrc_set_errno(IW_ERROR_ALLOC, errno);
845   }
846 
847   impl->dlsnr = opts->file.dlsnr;
848   impl->psize = iwp_alloc_unit();
849   impl->rspolicy = opts->rspolicy ? opts->rspolicy : _exfile_default_szpolicy;
850   impl->rspolicy_ctx = opts->rspolicy_ctx;
851   impl->use_locks = opts->use_locks;
852   if (opts->maxoff >= impl->psize) {
853     impl->maxoff = IW_ROUNDOWN(opts->maxoff, impl->psize);
854   }
855   rc = _exfile_initlocks(f);
856   RCGO(rc, finish);
857 
858   rc = iwfs_file_open(&impl->file, &opts->file);
859   RCGO(rc, finish);
860 
861   IWFS_FILE_STATE fstate;
862   rc = impl->file.state(&impl->file, &fstate);
863   RCGO(rc, finish);
864 
865   IWP_FILE_STAT fstat;
866   rc = iwp_fstat(fstate.opts.path, &fstat);
867   RCGO(rc, finish);
868 
869   impl->fsize = fstat.size;
870   impl->omode = fstate.opts.omode;
871   impl->fh = fstate.fh;
872 
873   if (impl->fsize < opts->initial_size) {
874     rc = _exfile_truncate_lw(f, opts->initial_size);
875   } else if (impl->fsize & (impl->psize - 1)) {  // not a page aligned
876     rc = _exfile_truncate_lw(f, impl->fsize);
877   }
878 finish:
879   if (rc) {
880     if (f->impl) {
881       _exfile_destroylocks(f->impl);
882       free(f->impl);
883       f->impl = 0;
884     }
885   }
886   return rc;
887 }
888 
_exfile_ecodefn(locale_t locale,uint32_t ecode)889 static const char *_exfile_ecodefn(locale_t locale, uint32_t ecode) {
890   if (!(ecode > _IWFS_EXT_ERROR_START && ecode < _IWFS_EXT_ERROR_END)) {
891     return 0;
892   }
893   switch (ecode) {
894     case IWFS_ERROR_MMAP_OVERLAP:
895       return "Region is mmaped already, mmaping overlaps. "
896              "(IWFS_ERROR_MMAP_OVERLAP)";
897     case IWFS_ERROR_NOT_MMAPED:
898       return "Region is not mmaped. (IWFS_ERROR_NOT_MMAPED)";
899     case IWFS_ERROR_RESIZE_POLICY_FAIL:
900       return "Invalid result of resize policy function. "
901              "(IWFS_ERROR_RESIZE_POLICY_FAIL)";
902     case IWFS_ERROR_MAXOFF:
903       return "Maximum file offset reached. (IWFS_ERROR_MAXOFF)";
904     default:
905       break;
906   }
907   return 0;
908 }
909 
iwfs_exfile_init(void)910 iwrc iwfs_exfile_init(void) {
911   static int _exfile_initialized = 0;
912   iwrc rc = iw_init();
913   RCRET(rc);
914   if (!__sync_bool_compare_and_swap(&_exfile_initialized, 0, 1)) {
915     return 0;  // initialized already
916   }
917   return iwlog_register_ecodefn(_exfile_ecodefn);
918 }
919