Files
mars-tinyldap/mstorage_add.c
2024-02-02 14:38:25 +00:00

155 lines
4.3 KiB
C

#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
#include "mstorage.h"
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
#define PAGEMASK ((PAGE_SIZE)-1)
unsigned long mstorage_increment=4*PAGE_SIZE;
/* Sadly, mremap is only available on Linux */
/* Please petition your congressman^Woperating system vendor to include it! */
ssize_t mstorage_reserve(mstorage_t* p,size_t n) {
if (p->mapped-p->used<n) {
size_t need;
if (!p->root) {
/* nothing allocated. mmap /dev/zero */
char* tmp;
need=(n|PAGEMASK)+1;
if (need==0) // integer overflow
return -1; // #intof1
if ((ssize_t)need < 0)
return -1; // #inttr1
#ifdef MREMAP_MAYMOVE
#ifdef MAP_ANONYMOUS
if ((tmp=mmap(0,need,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED)
return -1; // #mmap1
#else
int fd=open("/dev/zero",O_RDWR);
if (fd<0) return -1;
tmp=mmap(0,need,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
close(fd);
if (tmp==MAP_FAILED)
return -1;
#endif
#else
if (!(tmp=malloc(need)))
return -1;
#endif
p->root=tmp;
p->mapped=need;
p->used=0;
} else {
need = p->used+n;
if (need < n) // integer overflow
return -1; // #intof2
need=(need|PAGEMASK)+1+mstorage_increment;
if (need <= mstorage_increment) // integer overflow
return -1; // #intof3
if ((ssize_t)need < 0)
return -1; // #inttr2
char* tmp;
#ifdef MREMAP_MAYMOVE
tmp=mremap(p->root,p->mapped,need,MREMAP_MAYMOVE);
if (tmp==MAP_FAILED)
return -1; // #mmap2
#else
if (p->fd==-1) {
tmp=realloc(p->root,need);
if (!tmp)
return -1;
} else {
munmap(p->root,p->used);
p->root=0;
tmp=mmap(0,need,PROT_READ|PROT_WRITE,MAP_SHARED,p->fd,0);
if (tmp==MAP_FAILED) {
tmp=mmap(0,p->used,PROT_READ|PROT_WRITE,MAP_SHARED,p->fd,0);
/* this should never fail, because we mmap exactly as much as we
* had mmapped previously. We need to munmap before doing the
* new mmap, though, because we may run into the address space
* limit too early on 32-bit systems with lots of RAM */
if (tmp!=MAP_FAILED)
p->root=tmp; // fail but leave p in usable state
return -1;
}
}
#endif
p->root=tmp;
}
if (p->fd!=-1) {
/* slight complication if the storage is file based: we need to
* make sure the file size is extended, or the byte_copy will
* yield a bus error. */
if (ftruncate(p->fd,need)==-1)
return -1; // #trunc
}
p->mapped=need;
}
ssize_t l=p->used;
p->used+=n;
return l;
}
ssize_t mstorage_add(mstorage_t* p,const char* s,size_t n) {
ssize_t l = mstorage_reserve(p,n);
if (l>=0)
memcpy(p->root+l,s,n);
return l;
}
#ifdef UNITTEST
#undef UNITTEST
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include "mstorage_init.c"
#include "mstorage_unmap.c"
int main() {
mstorage_t m;
mstorage_init(&m);
// test munmap fail case in mstorage_unmap
m.root=(void*)1;
m.mapped=4096;
assert(mstorage_unmap(&m) == -1);
mstorage_init(&m);
// regular success cases
assert(mstorage_add(&m,"foo",3) == 0);
assert(mstorage_add(&m,"bar",3) == 3);
assert(mstorage_add(&m,"foo",3) == 6);
assert(m.mapped >= 9);
assert(!memcmp(m.root,"foobarfoo",9));
char* tmp=calloc(PAGE_SIZE,2);
assert(mstorage_add(&m,tmp,PAGE_SIZE*2) == 9);
// now the failure cases
assert(mstorage_unmap(&m) == 0);
assert(mstorage_add(&m,"foobarbaz",9) == 0);
assert(mstorage_add(&m, "foo", (size_t)-1) == -1); // #intof2
assert(mstorage_add(&m, "foo", (size_t)-1-4096) == -1); // #intof3
assert(mstorage_add(&m, "foo", (size_t)-1-mstorage_increment*2) == -1); // #inttr2
assert(mstorage_add(&m, "foo", ((size_t)-1)/2-mstorage_increment*2) == -1); // #mmap2
assert(mstorage_unmap(&m) == 0);
assert(mstorage_add(&m, "foo", (size_t)-1) == -1); // #intof1
assert(mstorage_add(&m, "foo", (size_t)-1-4096) == -1); // #inttr1
assert(mstorage_add(&m, "foo", ((size_t)-1)/2-4096) == -1); // #mmap1
assert(mstorage_unmap(&m) == 0);
assert((m.fd=open("Makefile",O_RDONLY)) != -1);
assert(mstorage_add(&m, tmp, PAGE_SIZE) == -1); // #trunc
assert(mstorage_unmap(&m) == -1); // drive-by 100% coverage :)
free(tmp);
return 0;
}
#endif