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