/* * sophia database * sphia.org * * Copyright (c) Dmitry Simonenko * BSD License */ #include #include #include #include #include int sp_fileexists(char *path) { struct stat st; int rc = lstat(path, &st); return rc == 0; } int sp_filerm(char *path) { return unlink(path); } static inline ssize_t sp_mapsizeof(char *path) { struct stat st; int rc = lstat(path, &st); if (spunlikely(rc == -1)) return -1; return st.st_size; } static inline int sp_mapresize(spfile *f, size_t size) { int rc = ftruncate(f->fd, size); if (spunlikely(rc == -1)) return -1; f->size = size; return 0; } static inline int sp_map(spfile *f, int flags) { void *p = mmap(NULL, f->size, flags, MAP_SHARED, f->fd, 0); if (p == MAP_FAILED) return -1; f->map = p; return 0; } static inline int sp_unmap(spfile *f) { int rc; if (f->map) { rc = munmap(f->map, f->size); f->map = NULL; return rc; } return 0; } static inline int sp_mapopenof(spfile *f, char *path, int flags, uint64_t size) { f->fd = open(path, flags, 0600); if (spunlikely(f->fd == -1)) return -1; f->file = sp_strdup(f->a, path); if (spunlikely(f->file == NULL)) { close(f->fd); f->fd = -1; return -1; } f->used = 0; f->creat = (flags & O_CREAT ? 1 : 0); int rc; if (! f->creat) { ssize_t sz = sp_mapsizeof(path); if (spunlikely(sz == -1)) goto err; f->size = sz; rc = sp_map(f, PROT_READ); if (spunlikely(rc == -1)) goto err; return 0; } f->size = 0; rc = sp_mapresize(f, size); if (spunlikely(rc == -1)) goto err; rc = sp_map(f, PROT_READ|PROT_WRITE); if (spunlikely(rc == -1)) goto err; return 0; err: close(f->fd); f->fd = -1; sp_free(f->a, f->file); f->file = NULL; return -1; } int sp_mapopen(spfile *f, char *path) { return sp_mapopenof(f, path, O_RDONLY, 0); } int sp_mapnew(spfile *f, char *path, uint64_t size) { return sp_mapopenof(f, path, O_RDWR|O_CREAT, size); } static inline int sp_mapsync(spfile *f) { return msync(f->map, f->size, MS_SYNC); } int sp_mapunlink(spfile *f) { return sp_filerm(f->file); } static inline int sp_mapcut(spfile *f) { if (f->creat == 0) return 0; int rc = sp_mapsync(f); if (spunlikely(rc == -1)) return -1; rc = sp_unmap(f); if (spunlikely(rc == -1)) return -1; rc = sp_mapresize(f, f->used); if (spunlikely(rc == -1)) return -1; return 0; } static inline int sp_fileclose(spfile *f) { /* leave file incomplete */ if (splikely(f->file)) { sp_free(f->a, f->file); f->file = NULL; } int rc; if (spunlikely(f->fd != -1)) { rc = close(f->fd); if (spunlikely(rc == -1)) return -1; f->fd = -1; } return 0; } static inline int sp_filecomplete(spfile *f) { f->creat = 0; char path[1024]; snprintf(path, sizeof(path), "%s", f->file); char *ext = strstr(path, ".incomplete"); if (spunlikely(ext == NULL)) return 0; *ext = 0; int rc = rename(f->file, path); if (spunlikely(rc == -1)) return -1; char *p = sp_strdup(f->a, path); if (spunlikely(p == NULL)) return -1; sp_free(f->a, f->file); f->file = p; return 0; } int sp_mapunmap(spfile *f) { return sp_unmap(f); } int sp_mapclose(spfile *f) { int rc = sp_mapcut(f); if (spunlikely(rc == -1)) return -1; if (f->map) { rc = sp_unmap(f); if (spunlikely(rc == -1)) return -1; } return sp_fileclose(f); } int sp_mapcomplete(spfile *f) { if (f->creat == 0) return 0; /* sync and truncate file by used size */ int rc = sp_mapcut(f); if (spunlikely(rc == -1)) return -1; /* remove .incomplete part */ rc = sp_filecomplete(f); if (spunlikely(rc == -1)) return -1; return sp_map(f, PROT_READ); } int sp_mapensure(spfile *f, uint64_t size, float grow) { if (splikely((f->used + size) <= f->size)) return 0; int rc = sp_unmap(f); if (spunlikely(rc == -1)) return -1; uint64_t nsz = f->used + f->size * grow; if (spunlikely(nsz < size)) nsz = f->used + size; assert(nsz >= (f->used + size)); rc = sp_mapresize(f, nsz); if (spunlikely(rc == -1)) return -1; return sp_map(f, PROT_READ|PROT_WRITE); } static inline int sp_logopenof(spfile *f, char *path, int flags) { f->creat = 1; f->fd = open(path, flags, 0600); if (spunlikely(f->fd == -1)) return -1; f->file = sp_strdup(f->a, path); if (spunlikely(f->file == NULL)) { close(f->fd); f->fd = -1; return -1; } f->size = 0; f->used = 0; return 0; } int sp_lognew(spfile *f, char *dir, uint32_t epoch) { char path[1024]; snprintf(path, sizeof(path), "%s/%"PRIu32".log.incomplete", dir, epoch); int rc = sp_logopenof(f, path, O_WRONLY|O_APPEND|O_CREAT); if (spunlikely(rc == -1)) return -1; // posix_fadvise(seq) return 0; } int sp_logcontinue(spfile *f, char *dir, uint32_t epoch) { char path[1024]; snprintf(path, sizeof(path), "%s/%"PRIu32".log.incomplete", dir, epoch); int rc = sp_logopenof(f, path, O_WRONLY|O_APPEND); if (spunlikely(rc == -1)) return -1; return 0; } int sp_logclose(spfile *f) { return sp_fileclose(f); } static inline int sp_logsync(spfile *f) { return (f->creat ? fsync(f->fd) : 0); } int sp_logcomplete(spfile *f) { int rc = sp_logsync(f); if (spunlikely(rc == -1)) return -1; return sp_filecomplete(f); } int sp_logcompleteforce(spfile *f) { int rc = sp_logsync(f); if (spunlikely(rc == -1)) return -1; int oldcreat = f->creat; f->creat = 1; rc = sp_filecomplete(f); f->creat = oldcreat; return rc; } int sp_logunlink(spfile *f) { return sp_filerm(f->file); } int sp_logwrite(spfile *f, void *buf, size_t size) { size_t n = 0; do { ssize_t r; do { r = write(f->fd, (char*)buf + n, size - n); } while (r == -1 && errno == EINTR); if (r <= 0) return -1; n += r; } while (n != size); f->used += size; return 0; } int sp_logput(spfile *f, spbatch *b) { register struct iovec *v = b->iov; register uint64_t size = 0; register int n = b->iovc; do { int r; do { r = writev(f->fd, v, n); } while (r == -1 && errno == EINTR); if (r < 0) { b->iovc = 0; return -1; } size += r; while (n > 0) { if (v->iov_len > (size_t)r) { v->iov_base = (char*)v->iov_base + r; v->iov_len -= r; break; } else { r -= v->iov_len; v++; n--; } } } while (n > 0); f->used += size; b->iovc = 0; return 0; } int sp_logrlb(spfile *f) { int rc = ftruncate(f->fd, f->svp); if (spunlikely(rc == -1)) return -1; f->used = f->svp; f->svp = 0; return lseek(f->fd, f->used, SEEK_SET); } int sp_logeof(spfile *f) { sp_filesvp(f); speofh eof = { SPEOF }; int rc = sp_logwrite(f, &eof, sizeof(eof)); if (spunlikely(rc == -1)) { sp_logrlb(f); return -1; } return 0; } int sp_lockfile(spfile *f, char *path) { f->creat = 0; f->fd = open(path, O_CREAT|O_WRONLY, 0600); if (spunlikely(f->fd == -1)) return -1; f->file = sp_strdup(f->a, path); if (spunlikely(f->file == NULL)) { close(f->fd); f->fd = -1; return -1; } struct flock l; memset(&l, 0, sizeof(l)); l.l_whence = SEEK_SET; l.l_start = 0; l.l_len = 0; l.l_type = F_WRLCK; int rc = fcntl(f->fd, F_SETLK, &l); if (spunlikely(rc == -1)) { sp_fileclose(f); return 1; } return 0; } int sp_unlockfile(spfile *f) { if (spunlikely(f->fd == -1)) return 0; struct flock l; memset(&l, 0, sizeof(l)); l.l_whence = SEEK_SET; l.l_start = 0; l.l_len = 0; l.l_type = F_UNLCK; fcntl(f->fd, F_SETLK, &l); unlink(f->file); return sp_fileclose(f); }