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