Diffstat (limited to 'cache.c')
| -rw-r--r-- | cache.c | 214 |
1 files changed, 89 insertions, 125 deletions
@@ -1,6 +1,6 @@ /* cache.c: cache management * - * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -15,25 +15,22 @@ #include "cgit.h" #include "cache.h" -#include "html.h" -#ifdef HAVE_LINUX_SENDFILE -#include <sys/sendfile.h> -#endif #define CACHE_BUFSIZE (1024 * 4) struct cache_slot { const char *key; - size_t keylen; + int keylen; int ttl; cache_fill_fn fn; + void *cbdata; int cache_fd; int lock_fd; - int stdout_fd; const char *cache_name; const char *lock_name; int match; struct stat cache_st; + struct stat lock_st; int bufsize; char buf[CACHE_BUFSIZE]; }; @@ -45,7 +42,7 @@ struct cache_slot { static int open_slot(struct cache_slot *slot) { char *bufz; - ssize_t bufkeylen = -1; + int bufkeylen = -1; slot->cache_fd = open(slot->cache_name, O_RDONLY); if (slot->cache_fd == -1) @@ -62,9 +59,8 @@ static int open_slot(struct cache_slot *slot) if (bufz) bufkeylen = bufz - slot->buf; - if (slot->key) - slot->match = bufkeylen == slot->keylen && - !memcmp(slot->key, slot->buf, bufkeylen + 1); + slot->match = bufkeylen == slot->keylen && + !memcmp(slot->key, slot->buf, bufkeylen + 1); return 0; } @@ -85,45 +81,22 @@ static int close_slot(struct cache_slot *slot) /* Print the content of the active cache slot (but skip the key). */ static int print_slot(struct cache_slot *slot) { - off_t off; -#ifdef HAVE_LINUX_SENDFILE - off_t size; -#endif + ssize_t i, j; - off = slot->keylen + 1; - -#ifdef HAVE_LINUX_SENDFILE - size = slot->cache_st.st_size; + i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); + if (i != slot->keylen + 1) + return errno; do { - ssize_t ret; - ret = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off); - if (ret < 0) { - if (errno == EAGAIN || errno == EINTR) - continue; - /* Fall back to read/write on EINVAL or ENOSYS */ - if (errno == EINVAL || errno == ENOSYS) - break; - return errno; - } - if (off == size) - return 0; - } while (1); -#endif + i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); + if (i > 0) + j = xwrite(STDOUT_FILENO, slot->buf, i); + } while (i > 0 && j == i); - if (lseek(slot->cache_fd, off, SEEK_SET) != off) + if (i < 0 || j != i) return errno; - - do { - ssize_t ret; - ret = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); - if (ret < 0) - return errno; - if (ret == 0) - return 0; - if (write_in_full(STDOUT_FILENO, slot->buf, ret) < 0) - return errno; - } while (1); + else + return 0; } /* Check if the slot has expired */ @@ -132,7 +105,7 @@ static int is_expired(struct cache_slot *slot) if (slot->ttl < 0) return 0; else - return slot->cache_st.st_mtime + slot->ttl * 60 < time(NULL); + return slot->cache_st.st_mtime + slot->ttl*60 < time(NULL); } /* Check if the slot has been modified since we opened it. @@ -168,25 +141,10 @@ static int close_lock(struct cache_slot *slot) */ static int lock_slot(struct cache_slot *slot) { - struct flock lock = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - .l_start = 0, - .l_len = 0, - }; - - slot->lock_fd = open(slot->lock_name, O_RDWR | O_CREAT, - S_IRUSR | S_IWUSR); + slot->lock_fd = open(slot->lock_name, O_RDWR|O_CREAT|O_EXCL, + S_IRUSR|S_IWUSR); if (slot->lock_fd == -1) return errno; - if (fcntl(slot->lock_fd, F_SETLK, &lock) < 0) { - int saved_errno = errno; - close(slot->lock_fd); - slot->lock_fd = -1; - return saved_errno; - } - if (ftruncate(slot->lock_fd, 0) < 0) - return errno; if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) return errno; return 0; @@ -205,13 +163,6 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot) else err = unlink(slot->lock_name); - /* Restore stdout and close the temporary FD. */ - if (slot->stdout_fd >= 0) { - dup2(slot->stdout_fd, STDOUT_FILENO); - close(slot->stdout_fd); - slot->stdout_fd = -1; - } - if (err) return errno; @@ -223,9 +174,11 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot) */ static int fill_slot(struct cache_slot *slot) { + int tmp; + /* Preserve stdout */ - slot->stdout_fd = dup(STDOUT_FILENO); - if (slot->stdout_fd == -1) + tmp = dup(STDOUT_FILENO); + if (tmp == -1) return errno; /* Redirect stdout to lockfile */ @@ -233,14 +186,14 @@ static int fill_slot(struct cache_slot *slot) return errno; /* Generate cache content */ - slot->fn(); + slot->fn(slot->cbdata); - /* Make sure any buffered data is flushed to the file */ - if (fflush(stdout)) + /* Restore stdout */ + if (dup2(tmp, STDOUT_FILENO) == -1) return errno; - /* update stat info */ - if (fstat(slot->lock_fd, &slot->cache_st)) + /* Close the temporary filedescriptor */ + if (close(tmp)) return errno; return 0; @@ -261,7 +214,7 @@ unsigned long hash_str(const char *str) if (!s) return h; - while (*s) { + while(*s) { h *= FNV_PRIME; h ^= *s++; } @@ -315,7 +268,7 @@ static int process_slot(struct cache_slot *slot) /* If the cache slot does not exist (or its key doesn't match the * current key), lets try to create a new cache slot for this * request. If this fails (for whatever reason), lets just generate - * the content without caching it and fool the caller to believe + * the content without caching it and fool the caller to belive * everything worked out (but print a warning on stdout). */ @@ -323,7 +276,7 @@ static int process_slot(struct cache_slot *slot) if ((err = lock_slot(slot)) != 0) { cache_log("[cgit] Unable to lock slot %s: %s (%d)\n", slot->lock_name, strerror(err), err); - slot->fn(); + slot->fn(slot->cbdata); return 0; } @@ -332,7 +285,7 @@ static int process_slot(struct cache_slot *slot) slot->lock_name, strerror(err), err); unlock_slot(slot, 0); close_lock(slot); - slot->fn(); + slot->fn(slot->cbdata); return 0; } // We've got a valid cache slot in the lock file, which @@ -356,64 +309,69 @@ static int process_slot(struct cache_slot *slot) /* Print cached content to stdout, generate the content if necessary. */ int cache_process(int size, const char *path, const char *key, int ttl, - cache_fill_fn fn) + cache_fill_fn fn, void *cbdata) { unsigned long hash; - int i; - struct strbuf filename = STRBUF_INIT; - struct strbuf lockname = STRBUF_INIT; + int len, i; + char filename[1024]; + char lockname[1024 + 5]; /* 5 = ".lock" */ struct cache_slot slot; - int result; /* If the cache is disabled, just generate the content */ - if (size <= 0 || ttl == 0) { - fn(); + if (size <= 0) { + fn(cbdata); return 0; } /* Verify input, calculate filenames */ if (!path) { cache_log("[cgit] Cache path not specified, caching is disabled\n"); - fn(); + fn(cbdata); + return 0; + } + len = strlen(path); + if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */ + cache_log("[cgit] Cache path too long, caching is disabled: %s\n", + path); + fn(cbdata); return 0; } if (!key) key = ""; hash = hash_str(key) % size; - strbuf_addstr(&filename, path); - strbuf_ensure_end(&filename, '/'); - for (i = 0; i < 8; i++) { - strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf)); + strcpy(filename, path); + if (filename[len - 1] != '/') + filename[len++] = '/'; + for(i = 0; i < 8; i++) { + sprintf(filename + len++, "%x", + (unsigned char)(hash & 0xf)); hash >>= 4; } - strbuf_addbuf(&lockname, &filename); - strbuf_addstr(&lockname, ".lock"); + filename[len] = '\0'; + strcpy(lockname, filename); + strcpy(lockname + len, ".lock"); slot.fn = fn; + slot.cbdata = cbdata; slot.ttl = ttl; - slot.stdout_fd = -1; - slot.cache_name = filename.buf; - slot.lock_name = lockname.buf; + slot.cache_name = filename; + slot.lock_name = lockname; slot.key = key; slot.keylen = strlen(key); - result = process_slot(&slot); - - strbuf_release(&filename); - strbuf_release(&lockname); - return result; + return process_slot(&slot); } /* Return a strftime formatted date/time * NB: the result from this function is to shared memory */ -static char *sprintftime(const char *format, time_t time) +char *sprintftime(const char *format, time_t time) { static char buf[64]; - struct tm tm; + struct tm *tm; if (!time) return NULL; - gmtime_r(&time, &tm); - strftime(buf, sizeof(buf)-1, format, &tm); + tm = gmtime(&time); + strftime(buf, sizeof(buf)-1, format, tm); return buf; } @@ -422,14 +380,19 @@ int cache_ls(const char *path) DIR *dir; struct dirent *ent; int err = 0; - struct cache_slot slot = { NULL }; - struct strbuf fullname = STRBUF_INIT; - size_t prefixlen; + struct cache_slot slot; + char fullname[1024]; + char *name; if (!path) { cache_log("[cgit] cache path not specified\n"); return -1; } + if (strlen(path) > 1024 - 10) { + cache_log("[cgit] cache path too long: %s\n", + path); + return -1; + } dir = opendir(path); if (!dir) { err = errno; @@ -437,30 +400,31 @@ int cache_ls(const char *path) path, strerror(err), err); return err; } - strbuf_addstr(&fullname, path); - strbuf_ensure_end(&fullname, '/'); - prefixlen = fullname.len; - while ((ent = readdir(dir)) != NULL) { + strcpy(fullname, path); + name = fullname + strlen(path); + if (*(name - 1) != '/') { + *name++ = '/'; + *name = '\0'; + } + slot.cache_name = fullname; + while((ent = readdir(dir)) != NULL) { if (strlen(ent->d_name) != 8) continue; - strbuf_setlen(&fullname, prefixlen); - strbuf_addstr(&fullname, ent->d_name); - slot.cache_name = fullname.buf; + strcpy(name, ent->d_name); if ((err = open_slot(&slot)) != 0) { cache_log("[cgit] unable to open path %s: %s (%d)\n", - fullname.buf, strerror(err), err); + fullname, strerror(err), err); continue; } - htmlf("%s %s %10"PRIuMAX" %s\n", - fullname.buf, - sprintftime("%Y-%m-%d %H:%M:%S", - slot.cache_st.st_mtime), - (uintmax_t)slot.cache_st.st_size, - slot.buf); + printf("%s %s %10zd %s\n", + name, + sprintftime("%Y-%m-%d %H:%M:%S", + slot.cache_st.st_mtime), + slot.cache_st.st_size, + slot.buf); close_slot(&slot); } closedir(dir); - strbuf_release(&fullname); return 0; } |