/* * AUTHOR * * Rob Mueller <cpan@robm.fastmail.fm> * * COPYRIGHT AND LICENSE * * Copyright (C) 2003 by FastMail IP Partners * * This library is free software; you can redistribute it and/or modify * it under the same terms as Perl itself. * */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <time.h> #include <errno.h> #include <stdarg.h> #include "mmap_cache.h" #include "mmap_cache_internals.h" char* _mmc_get_def_share_filename(mmap_cache * cache) { return def_share_file; } int mmc_open_cache_file(mmap_cache* cache, int * do_init) { int res, i, fh; void * tmp; struct stat statbuf; /* Check if file exists */ res = stat(cache->share_file, &statbuf); /* Remove if different size or remove requested */ if (!res && (cache->init_file || (statbuf.st_size != cache->c_size))) { res = remove(cache->share_file); if (res == -1 && errno != ENOENT) { return _mmc_set_error(cache, errno, "Unlink of existing share file %s failed", cache->share_file); } } /* Create file if it doesn't exist */ *do_init = 0; res = stat(cache->share_file, &statbuf); if (res == -1) { mode_t permissions = (mode_t)cache->permissions; res = open(cache->share_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | O_APPEND, permissions); if (res == -1) { return _mmc_set_error(cache, errno, "Create of share file %s failed", cache->share_file); } /* Fill file with 0's */ tmp = calloc(1, cache->c_page_size); if (!tmp) { return _mmc_set_error(cache, errno, "Calloc of tmp space failed"); } for (i = 0; i < cache->c_num_pages; i++) { int written = write(res, tmp, cache->c_page_size); if (written < 0) { free(tmp); return _mmc_set_error(cache, errno, "Write to share file %s failed", cache->share_file); } if (written < cache->c_page_size) { free(tmp); return _mmc_set_error(cache, 0, "Write to share file %s failed; short write (%d of %d bytes written)", cache->share_file, written, cache->c_page_size); } } free(tmp); /* Later on initialise page structures */ *do_init = 1; close(res); } /* Open for reading/writing */ fh = open(cache->share_file, O_RDWR); if (fh == -1) { return _mmc_set_error(cache, errno, "Open of share file %s failed", cache->share_file); } /* Automatically close cache fd on exec */ fcntl(fh, F_SETFD, FD_CLOEXEC); cache->fh = fh; return 0; } /* * mmc_map_memory(mmap_cache * cache) * * maps the cache file into memory, and sets cache->mm_var as needed. */ int mmc_map_memory(mmap_cache* cache) { /* Map file into memory */ cache->mm_var = mmap(0, cache->c_size, PROT_READ | PROT_WRITE, MAP_SHARED, cache->fh, 0); if (cache->mm_var == (void *)MAP_FAILED) { _mmc_set_error(cache, errno, "Mmap of shared file %s failed", cache->share_file); mmc_close_fh(cache); return -1; } return 0; } /* * mmc_unmap_memory(mmap_cache * cache) * * Unmaps cache->mm_var */ int mmc_unmap_memory(mmap_cache* cache) { int res = munmap(cache->mm_var, cache->c_size); if (res == -1) { return _mmc_set_error(cache, errno, "Munmap of shared file %s failed", cache->share_file); } return res; } int mmc_close_fh(mmap_cache* cache) { int res = close(cache->fh); return res; } int mmc_lock_page(mmap_cache* cache, MU64 p_offset) { struct flock lock; int old_alarm, alarm_left = 10; int lock_res = -1; /* Setup fcntl locking structure */ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = p_offset; lock.l_len = cache->c_page_size; if (cache->catch_deadlocks) old_alarm = alarm(alarm_left); while (lock_res != 0) { /* Lock the page (block till done, signal, or timeout) */ lock_res = fcntl(cache->fh, F_SETLKW, &lock); /* Continue immediately if success */ if (lock_res == 0) { if (cache->catch_deadlocks) alarm(old_alarm); break; } /* Turn off alarm for a moment */ if (cache->catch_deadlocks) alarm_left = alarm(0); /* Some signal interrupted, and it wasn't the alarm? Rerun lock */ if (lock_res == -1 && errno == EINTR && alarm_left) { if (cache->catch_deadlocks) alarm(alarm_left); continue; } /* Lock failed? */ _mmc_set_error(cache, errno, "Lock failed"); if (cache->catch_deadlocks) alarm(old_alarm); return -1; } return 0; } int mmc_unlock_page(mmap_cache * cache) { struct flock lock; /* Setup fcntl locking structure */ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = cache->p_offset; lock.l_len = cache->c_page_size; /* And unlock page */ fcntl(cache->fh, F_SETLKW, &lock); /* Set to bad value while page not locked */ cache->p_cur = NOPAGE; return 0; } /* * int _mmc_set_error(mmap_cache *cache, int err, char * error_string, ...) * * Set internal error string/state * */ int _mmc_set_error(mmap_cache *cache, int err, char * error_string, ...) { va_list ap; static char errbuf[1024]; va_start(ap, error_string); /* Make sure it's terminated */ errbuf[1023] = '\0'; /* Start with error string passed */ vsnprintf(errbuf, 1023, error_string, ap); /* Add system error code if passed */ if (err) { strncat(errbuf, ": ", 1023); strncat(errbuf, strerror(err), 1023); } /* Save in cache object */ cache->last_error = errbuf; va_end(ap); return -1; }