1 /*------------------------------------------------------------------------------
2  *
3  * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4  * The YADIFA TM software product is provided under the BSD 3-clause license:
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *        * Redistributions of source code must retain the above copyright
11  *          notice, this list of conditions and the following disclaimer.
12  *        * Redistributions in binary form must reproduce the above copyright
13  *          notice, this list of conditions and the following disclaimer in the
14  *          documentation and/or other materials provided with the distribution.
15  *        * Neither the name of EURid nor the names of its contributors may be
16  *          used to endorse or promote products derived from this software
17  *          without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  *------------------------------------------------------------------------------
32  *
33  */
34 
35 /** @defgroup streaming Streams
36  *  @ingroup dnscore
37  *  @brief
38  *
39  *
40  *
41  * @{
42  *
43  *----------------------------------------------------------------------------*/
44 
45 #include "dnscore/dnscore-config.h"
46 
47 #if HAS_MREMAP
48 #define _GNU_SOURCE 1
49 #endif
50 
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 #include <unistd.h>
57 #include <sys/mman.h>
58 
59 #include "dnscore/mapped-file.h"
60 #include "dnscore/fdtools.h"
61 #include "dnscore/zalloc.h"
62 #include "dnscore/mutex.h"
63 
64 //extern logger_handle *g_system_logger;
65 //#define MODULE_MSG_HANDLE g_system_logger
66 
67 #define MAPPED_FILE_TAG 0x454c494650414d4d // MMAPFILE_TAG
68 
69 ssize_t g_page_size = 0;
70 ssize_t g_page_mask = 0;
71 
72 static void
mapped_file_update_system_consts()73 mapped_file_update_system_consts()
74 {
75 #ifndef WIN32
76     if(g_page_size == 0)
77     {
78         g_page_size = sysconf(_SC_PAGE_SIZE);
79         if(g_page_size < 0)
80         {
81             g_page_size = 4096;
82         }
83         g_page_mask = g_page_size - 1;
84     }
85 #else
86     g_page_size = 4096;
87     g_page_mask = g_page_size - 1;
88 #endif
89 }
90 
91 struct mapped_file_t_ // matches the file_t_ signature, appends its own
92 {
93     const struct file_vtbl *vtbl;
94     u8 *address;
95     ssize_t size;
96     ssize_t real_size;  // for non file-backed maps
97     off_t position;
98     group_mutex_t mtx;
99     int fd;
100     int prot;
101     int flags;
102 };
103 
104 typedef struct mapped_file_t_* mapped_file_t;
105 
106 static int
mapped_file_resize_internal(mapped_file_t mf,size_t required_new_size)107 mapped_file_resize_internal(mapped_file_t mf, size_t required_new_size)
108 {
109     u8 *address;
110 
111     if((ssize_t)required_new_size <= mf->real_size)
112     {
113         mf->size = required_new_size;
114         return SUCCESS;
115     }
116 
117     size_t new_real_size = MAX(mf->real_size * 2, 0x1000000);
118 
119 #if HAS_MREMAP
120     if(mf->address != NULL)
121     {
122         address = mremap(mf->address, mf->real_size, new_real_size, MREMAP_MAYMOVE);
123 
124         if(address == (u8*)MAP_FAILED)
125         {
126             return ERRNO_ERROR;
127         }
128     }
129     else
130     {
131         address = mmap(mf->address, new_real_size, mf->prot, mf->flags, mf->fd, 0);
132 
133         if(address == (u8*)MAP_FAILED)
134         {
135             return ERRNO_ERROR;
136         }
137     }
138 #else
139     address = mmap(mf->address, new_real_size, mf->prot, mf->flags, mf->fd, 0);
140 
141     if(address == (u8*)MAP_FAILED)
142     {
143         return ERRNO_ERROR;
144     }
145 
146     if(mf->address != NULL)
147     {
148         memcpy(address, mf->address, MIN(mf->size, (ssize_t)required_new_size));
149         munmap(mf->address, mf->size);
150     }
151 #endif
152 
153     mf->address = address;
154     mf->size = required_new_size;
155     mf->real_size = new_real_size;
156 
157     return SUCCESS;
158 }
159 
160 static ssize_t
mapped_file_read(file_t f,void * buffer,ssize_t size)161 mapped_file_read(file_t f, void *buffer, ssize_t size)
162 {
163     mapped_file_t mf = (mapped_file_t)f;
164 
165     if((mf->prot & PROT_READ) == 0)
166     {
167         return INVALID_STATE_ERROR;
168     }
169 
170     group_mutex_double_lock(&mf->mtx, GROUP_MUTEX_READ, GROUP_MUTEX_WRITE);
171     ssize_t avail = mf->size - mf->position;
172 
173     if(avail < size)
174     {
175         size = avail;
176     }
177     memcpy(buffer, &mf->address[mf->position], size);
178 
179     group_mutex_exchange_locks(&mf->mtx, GROUP_MUTEX_READ, GROUP_MUTEX_WRITE);
180     mf->position += size;
181     group_mutex_exchange_locks(&mf->mtx, GROUP_MUTEX_WRITE, GROUP_MUTEX_READ);
182 
183     group_mutex_double_unlock(&mf->mtx, GROUP_MUTEX_READ, GROUP_MUTEX_WRITE);
184 
185     return size;
186 }
187 
188 static ssize_t
mapped_file_write(file_t f,const void * buffer,ssize_t size)189 mapped_file_write(file_t f, const void *buffer, ssize_t size)
190 {
191     mapped_file_t mf = (mapped_file_t)f;
192 
193     if((mf->prot & PROT_WRITE) == 0)
194     {
195         return INVALID_STATE_ERROR;
196     }
197 
198     group_mutex_lock(&mf->mtx, GROUP_MUTEX_WRITE);
199     ssize_t avail = mf->size - mf->position;
200 
201     if(avail < size)
202     {
203         // grow the file
204         if(mf->fd >= 0)
205         {
206             ftruncate(mf->fd, mf->position + size);
207         }
208 
209         ssize_t ret = mapped_file_resize_internal(mf, mf->position + size);
210 
211         if(FAIL(ret))
212         {
213             group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
214 
215             return ret;
216         }
217     }
218 
219     memcpy(&mf->address[mf->position], buffer, size);
220 
221     mf->position += size;
222 
223     group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
224 
225     return size;
226 }
227 
228 static ssize_t
mapped_file_seek(file_t f,ssize_t position,int whence)229 mapped_file_seek(file_t f, ssize_t position, int whence)
230 {
231     mapped_file_t mf = (mapped_file_t)f;
232 
233     if(mf->prot == PROT_NONE)
234     {
235         return INVALID_STATE_ERROR;
236     }
237 
238     group_mutex_lock(&mf->mtx, GROUP_MUTEX_WRITE);
239 
240     switch(whence)
241     {
242         case SEEK_SET:
243         {
244             mf->position = position;
245 
246             group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
247 
248             return position;
249         }
250         case SEEK_CUR:
251         {
252             if(mf->position + position >= 0)
253             {
254                 position = (mf->position += position);
255 
256                 group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
257 
258                 return position;
259             }
260             else
261             {
262                 mf->position = 0;
263 
264                 group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
265 
266                 return 0;
267             }
268         }
269         case SEEK_END:
270         {
271             if(mf->size + position >= 0)
272             {
273                 position = mf->position = mf->size + position;
274                 group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
275                 return position;
276             }
277             else
278             {
279                 mf->position = 0;
280                 group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
281                 return 0;
282             }
283         }
284         default:
285         {
286             group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
287             return ERROR;
288         }
289     }
290 }
291 
292 static ssize_t
mapped_file_tell(file_t f)293 mapped_file_tell(file_t f)
294 {
295     mapped_file_t mf = (mapped_file_t)f;
296 
297     if(mf->prot == PROT_NONE)
298     {
299         return INVALID_STATE_ERROR;
300     }
301 
302     ssize_t ret;
303 
304     group_mutex_lock(&mf->mtx, GROUP_MUTEX_READ);
305     ret = mf->position;
306     group_mutex_unlock(&mf->mtx, GROUP_MUTEX_READ);
307 
308     return ret;
309 }
310 
311 static ya_result
mapped_file_flush(file_t f)312 mapped_file_flush(file_t f)
313 {
314     mapped_file_t mf = (mapped_file_t)f;
315 
316     if(mf->prot == PROT_NONE)
317     {
318         return INVALID_STATE_ERROR;
319     }
320 
321     group_mutex_lock(&mf->mtx, GROUP_MUTEX_READ);
322 
323     int ret = msync(mf->address, mf->size, MS_SYNC);
324 
325     group_mutex_unlock(&mf->mtx, GROUP_MUTEX_READ);
326 
327     if(FAIL(ret))
328     {
329         ret = ERRNO_ERROR;
330     }
331 
332     return ret;
333 }
334 
335 static int
mapped_file_close(file_t f)336 mapped_file_close(file_t f)
337 {
338     mapped_file_t mf = (mapped_file_t)f;
339 
340     if(mf->prot == PROT_NONE)
341     {
342         return INVALID_STATE_ERROR;
343     }
344 
345     group_mutex_lock(&mf->mtx, GROUP_MUTEX_WRITE);
346 
347     if(mf->address != NULL)
348     {
349         munmap(mf->address, mf->real_size);
350         mf->address = NULL;
351         mf->size = 0;
352     }
353 
354     group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
355 
356     if(mf->fd >= 0)
357     {
358         close_ex(mf->fd);
359         mf->fd = -2;
360     }
361     mf->vtbl = NULL;
362     ZFREE_OBJECT(mf);
363 
364     return SUCCESS;
365 }
366 
367 static ssize_t
mapped_file_size(file_t f)368 mapped_file_size(file_t f)
369 {
370     mapped_file_t mf = (mapped_file_t)f;
371 
372     if(mf->prot == PROT_NONE)
373     {
374         return INVALID_STATE_ERROR;
375     }
376 
377     ssize_t ret;
378 
379     group_mutex_lock(&mf->mtx, GROUP_MUTEX_READ);
380     ret = mf->size;
381     group_mutex_unlock(&mf->mtx, GROUP_MUTEX_READ);
382 
383     return ret;
384 }
385 
386 static int
mapped_file_resize(file_t f,ssize_t size)387 mapped_file_resize(file_t f, ssize_t size)
388 {
389     mapped_file_t mf = (mapped_file_t)f;
390 
391     if(mf->prot == PROT_NONE)
392     {
393         return INVALID_STATE_ERROR;
394     }
395 
396     ya_result ret = SUCCESS;
397 
398     group_mutex_lock(&mf->mtx, GROUP_MUTEX_WRITE);
399 
400     if(mf->size != size)
401     {
402         // grow the file
403         if(mf->fd >= 0)
404         {
405             if(ISOK(ret = ftruncate(mf->fd, size)))
406             {
407                 ret = mapped_file_resize_internal(mf, size);
408             }
409         }
410         else
411         {
412             ret = mapped_file_resize_internal(mf, size);
413         }
414     }
415 
416     group_mutex_unlock(&mf->mtx, GROUP_MUTEX_WRITE);
417 
418     return ret;
419 }
420 
421 static const struct file_vtbl mapped_file_vtbl =
422 {
423     mapped_file_read,
424     mapped_file_write,
425     mapped_file_seek,
426     mapped_file_tell,
427     mapped_file_flush,
428     mapped_file_close,
429     mapped_file_size,
430     mapped_file_resize
431 };
432 
433 /*
434 file_t
435 mapped_file_open_ex(const char *filename, int flags, mode_t mode, ya_result *ret)
436 */
437 
438 ya_result
mapped_file_open_ex(file_t * fp,const char * filename,int flags)439 mapped_file_open_ex(file_t *fp, const char *filename, int flags)
440 {
441     mapped_file_update_system_consts();
442 
443     if(fp == NULL)
444     {
445         return UNEXPECTED_NULL_ARGUMENT_ERROR;
446     }
447 
448     int fd = open_ex(filename, flags);
449     if(FAIL(fd))
450     {
451         return ERRNO_ERROR;
452     }
453 
454     struct stat st;
455     if(fstat(fd, &st) < 0)
456     {
457         close_ex(fd);
458         return ERRNO_ERROR;
459     }
460 
461     int prot;
462 
463     if((flags & O_RDWR) == O_RDWR)
464     {
465         prot = PROT_READ|PROT_WRITE;
466     }
467     else if((flags & O_RDONLY) == O_RDONLY)
468     {
469         prot = PROT_READ;
470     }
471     else if((flags & O_WRONLY) == O_WRONLY)
472     {
473         prot = PROT_WRITE;
474     }
475     else
476     {
477         prot = PROT_NONE;
478     }
479 
480     int mmap_flags = MAP_SHARED;
481 
482     u8 *address = NULL;
483 
484     ssize_t real_size = 0;
485 
486     if(st.st_size > 0)
487     {
488         real_size = (st.st_size + 0xffffffLL) & ~0xffffffLL;
489         address = mmap(NULL, real_size, prot, mmap_flags, fd, 0);
490 
491         if(address == (u8*)MAP_FAILED)
492         {
493             close_ex(fd);
494             return ERRNO_ERROR;
495         }
496     }
497 
498     mapped_file_t mf;
499     ZALLOC_OBJECT_OR_DIE(mf,struct mapped_file_t_, MAPPED_FILE_TAG);
500     mf->vtbl = &mapped_file_vtbl;
501     mf->address = address;
502     mf->size = st.st_size;
503     mf->real_size = real_size;
504     mf->position = 0;
505     group_mutex_init(&mf->mtx);
506     mf->fd = fd;
507     mf->prot = prot;
508     mf->flags = mmap_flags;
509 
510     *fp = (file_t)mf;
511 
512     return SUCCESS;
513 }
514 
515 ya_result
mapped_file_create_ex(file_t * fp,const char * filename,int flags,mode_t mode)516 mapped_file_create_ex(file_t *fp, const char *filename, int flags, mode_t mode)
517 {
518     mapped_file_update_system_consts();
519 
520     if(fp == NULL)
521     {
522         return UNEXPECTED_NULL_ARGUMENT_ERROR;
523     }
524 
525     int fd = open_create_ex(filename, flags, mode);
526 
527     if(FAIL(fd))
528     {
529         return ERRNO_ERROR;
530     }
531 
532     struct stat st;
533     if(fstat(fd, &st) < 0)
534     {
535         close_ex(fd);
536         return ERRNO_ERROR;
537     }
538 
539     int prot;
540 
541     if((flags & O_RDWR) == O_RDWR)
542     {
543         prot = PROT_READ|PROT_WRITE;
544     }
545     else if((flags & O_RDONLY) == O_RDONLY)
546     {
547         prot = PROT_READ;
548     }
549     else if((flags & O_WRONLY) == O_WRONLY)
550     {
551         prot = PROT_WRITE;
552     }
553     else
554     {
555         prot = PROT_NONE;
556     }
557 
558     int mmap_flags = MAP_SHARED;
559 
560     u8 *address = NULL;
561     size_t real_size = 0;
562 
563     if(st.st_size > 0)
564     {
565         real_size = (st.st_size + 0xffffffLL) & ~0xffffffLL;
566         address = mmap(NULL, real_size, prot, mmap_flags, fd, 0);
567 
568         if(address == (u8*)MAP_FAILED)
569         {
570             close_ex(fd);
571             return ERRNO_ERROR;
572         }
573     }
574 
575     mapped_file_t mf;
576     ZALLOC_OBJECT_OR_DIE(mf,struct mapped_file_t_, MAPPED_FILE_TAG);
577     mf->vtbl = &mapped_file_vtbl;
578     mf->address = address;
579     mf->size = st.st_size;
580     mf->real_size = real_size;
581     mf->position = 0;
582     group_mutex_init(&mf->mtx);
583     mf->fd = fd;
584     mf->prot = prot;
585     mf->flags = mmap_flags;
586 
587     *fp = (file_t)mf;
588 
589     return SUCCESS;
590 }
591 
592 ya_result
mapped_file_create_volatile(file_t * fp,const char * filename,size_t base_size)593 mapped_file_create_volatile(file_t *fp, const char *filename, size_t base_size)
594 {
595     (void)filename;
596 
597     mapped_file_update_system_consts();
598 
599     if(fp == NULL)
600     {
601         return UNEXPECTED_NULL_ARGUMENT_ERROR;
602     }
603 
604     int fd = -1;
605 
606     int prot = PROT_READ|PROT_WRITE;
607 
608     int mmap_flags = MAP_ANONYMOUS|MAP_PRIVATE;
609 
610     u8 *address = NULL;
611     size_t real_size = 0;
612 
613     if(base_size > 0)
614     {
615         real_size = (base_size + 0xffffffLL) & ~0xffffffLL;
616 
617         address = mmap(NULL, real_size, prot, mmap_flags, fd, 0);
618 
619         if(address == (u8*)MAP_FAILED)
620         {
621             close_ex(fd);
622             return ERRNO_ERROR;
623         }
624     }
625 
626     mapped_file_t mf;
627     ZALLOC_OBJECT_OR_DIE(mf,struct mapped_file_t_, MAPPED_FILE_TAG);
628     mf->vtbl = &mapped_file_vtbl;
629     mf->address = address;
630     mf->size = 0;
631     mf->real_size = real_size;
632     mf->position = 0;
633     group_mutex_init(&mf->mtx);
634     mf->fd = fd;
635     mf->prot = prot;
636     mf->flags = mmap_flags;
637 
638     *fp = (file_t)mf;
639 
640     return SUCCESS;
641 }
642 
643 ya_result
mapped_file_get_buffer(file_t f,void ** address,ssize_t * size)644 mapped_file_get_buffer(file_t f, void **address, ssize_t *size)
645 {
646     mapped_file_t mf = (mapped_file_t)f;
647 
648     if(f->vtbl == &mapped_file_vtbl)
649     {
650         if(address != NULL)
651         {
652             *address = mf->address;
653         }
654         if(size != NULL)
655         {
656             *size = mf->size;
657         }
658 
659         return SUCCESS;
660     }
661     else
662     {
663         return INVALID_ARGUMENT_ERROR;
664     }
665 }
666 
667 ya_result
mapped_file_get_buffer_const(file_t f,const void ** address,ssize_t * size)668 mapped_file_get_buffer_const(file_t f, const void **address, ssize_t *size)
669 {
670     mapped_file_t mf = (mapped_file_t)f;
671 
672     if(f->vtbl == &mapped_file_vtbl)
673     {
674         if(address != NULL)
675         {
676             *address = mf->address;
677         }
678         if(size != NULL)
679         {
680             *size = mf->size;
681         }
682 
683         return SUCCESS;
684     }
685     else
686     {
687         return INVALID_ARGUMENT_ERROR;
688     }
689 }
690 
691 /** @} */
692