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