155 lines
4.3 KiB
C
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
|