lighttpd 1.4.x
https://www.lighttpd.net/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
9.8 KiB
483 lines
9.8 KiB
#define _GNU_SOURCE |
|
|
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <unistd.h> |
|
#include <stdio.h> |
|
#include <fcntl.h> |
|
|
|
#include "log.h" |
|
#include "file_cache.h" |
|
#include "fdevent.h" |
|
#include "etag.h" |
|
|
|
#ifdef HAVE_ATTR_ATTRIBUTES_H |
|
#include <attr/attributes.h> |
|
#endif |
|
|
|
#include "sys-mmap.h" |
|
|
|
/* NetBSD 1.3.x needs it */ |
|
#ifndef MAP_FAILED |
|
# define MAP_FAILED -1 |
|
#endif |
|
|
|
#ifndef O_LARGEFILE |
|
# define O_LARGEFILE 0 |
|
#endif |
|
|
|
#ifndef HAVE_LSTAT |
|
#define lstat stat |
|
#endif |
|
|
|
/* don't enable the dir-cache |
|
* |
|
* F_NOTIFY would be nice but only works with linux-rtsig |
|
*/ |
|
#undef USE_LINUX_SIGIO |
|
|
|
file_cache *file_cache_init(void) { |
|
file_cache *fc = NULL; |
|
|
|
fc = calloc(1, sizeof(*fc)); |
|
|
|
fc->dir_name = buffer_init(); |
|
|
|
return fc; |
|
} |
|
|
|
static file_cache_entry * file_cache_entry_init(void) { |
|
file_cache_entry *fce = NULL; |
|
|
|
fce = calloc(1, sizeof(*fce)); |
|
|
|
fce->fd = -1; |
|
fce->fde_ndx = -1; |
|
fce->name = buffer_init(); |
|
fce->etag = buffer_init(); |
|
fce->content_type = buffer_init(); |
|
|
|
return fce; |
|
} |
|
|
|
static void file_cache_entry_free(server *srv, file_cache_entry *fce) { |
|
if (!fce) return; |
|
|
|
if (fce->fd >= 0) { |
|
close(fce->fd); |
|
srv->cur_fds--; |
|
} |
|
|
|
buffer_free(fce->etag); |
|
buffer_free(fce->name); |
|
buffer_free(fce->content_type); |
|
|
|
if (fce->mmap_p) munmap(fce->mmap_p, fce->mmap_length); |
|
|
|
free(fce); |
|
} |
|
|
|
static int file_cache_entry_reset(server *srv, file_cache_entry *fce) { |
|
if (fce->fd < 0) return 0; |
|
|
|
close(fce->fd); |
|
srv->cur_fds--; |
|
|
|
#ifdef USE_LINUX_SIGIO |
|
/* doesn't work anymore */ |
|
if (fce->fde_ndx != -1) { |
|
fdevent_event_del(srv->ev, &(fce->fde_ndx), fce->fd); |
|
} |
|
#else |
|
UNUSED(srv); |
|
#endif |
|
|
|
if (fce->mmap_p) { |
|
munmap(fce->mmap_p, fce->mmap_length); |
|
fce->mmap_p = NULL; |
|
} |
|
fce->fd = -1; |
|
|
|
buffer_reset(fce->etag); |
|
buffer_reset(fce->name); |
|
buffer_reset(fce->content_type); |
|
|
|
return 0; |
|
} |
|
|
|
void file_cache_free(server *srv, file_cache *fc) { |
|
size_t i; |
|
for (i = 0; i < fc->used; i++) { |
|
file_cache_entry_free(srv, fc->ptr[i]); |
|
} |
|
|
|
free(fc->ptr); |
|
|
|
buffer_free(fc->dir_name); |
|
|
|
free(fc); |
|
|
|
} |
|
|
|
#ifdef HAVE_XATTR |
|
int fce_attr_get(buffer *buf, char *name) { |
|
int attrlen; |
|
int ret; |
|
|
|
attrlen = 1024; |
|
buffer_prepare_copy(buf, attrlen); |
|
attrlen--; |
|
if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { |
|
buf->used = attrlen + 1; |
|
buf->ptr[attrlen] = '\0'; |
|
} |
|
return ret; |
|
} |
|
#endif |
|
|
|
file_cache_entry * file_cache_get_unused_entry(server *srv) { |
|
file_cache_entry *fce = NULL; |
|
file_cache *fc = srv->file_cache; |
|
size_t i; |
|
|
|
if (fc->size == 0) { |
|
fc->size = 16; |
|
fc->ptr = calloc(fc->size, sizeof(*fc->ptr)); |
|
fc->used = 0; |
|
} |
|
for (i = 0; i < fc->used; i++) { |
|
file_cache_entry *f = fc->ptr[i]; |
|
|
|
if (f->fd == -1) { |
|
return f; |
|
} |
|
} |
|
|
|
if (fc->used < fc->size) { |
|
fce = file_cache_entry_init(); |
|
fc->ptr[fc->used++] = fce; |
|
} else { |
|
/* the cache is full, time to resize */ |
|
|
|
fc->size += 16; |
|
fc->ptr = realloc(fc->ptr, sizeof(*fc->ptr) * fc->size); |
|
|
|
fce = file_cache_entry_init(); |
|
fc->ptr[fc->used++] = fce; |
|
} |
|
|
|
return fce; |
|
} |
|
|
|
handler_t file_cache_handle_fdevent(void *_srv, void *_fce, int revent) { |
|
size_t i; |
|
server *srv = _srv; |
|
file_cache_entry *fce = _fce; |
|
file_cache *fc = srv->file_cache;; |
|
|
|
UNUSED(revent); |
|
/* */ |
|
#if 0 |
|
log_error_write(srv, __FILE__, __LINE__, "sds", "dir has changed: ", fce->fd, fce->name->ptr); |
|
#endif |
|
/* touch all files below this directory */ |
|
|
|
for (i = 0; i < fc->used; i++) { |
|
file_cache_entry *f = fc->ptr[i]; |
|
|
|
if (fce == f) continue; |
|
|
|
if (0 == strncmp(fce->name->ptr, f->name->ptr, fce->name->used - 1)) { |
|
#if 0 |
|
log_error_write(srv, __FILE__, __LINE__, "ss", "file hit: ", f->name->ptr); |
|
#endif |
|
f->is_dirty = 1; |
|
} |
|
} |
|
|
|
return HANDLER_GO_ON; |
|
} |
|
|
|
|
|
#if 0 |
|
/* dead code, might be reused somewhere again */ |
|
|
|
int file_cache_check_cache() { |
|
file_cache_entry *first_unused_fce = NULL; |
|
file_cache *fc = srv->file_cache; |
|
size_t i; |
|
|
|
/* check the cache */ |
|
for (i = 0; i < fc->used; i++) { |
|
fce = fc->ptr[i]; |
|
|
|
if (buffer_is_equal(name, fce->name)) { |
|
log_error_write(srv, __FILE__, __LINE__, "sb", "cache hit:", name); |
|
|
|
#ifdef USE_LINUX_SIGIO |
|
if (fce->is_dirty == 0) { |
|
fce->in_use++; |
|
return fce; |
|
} |
|
#endif |
|
|
|
/* get the etag information */ |
|
if (-1 == (stat(name->ptr, &(fce->st)))) { |
|
fce->in_use = 0; |
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sbs", "stat failed: ", name, strerror(errno)); |
|
|
|
file_cache_entry_reset(srv, fce); |
|
|
|
return NULL; |
|
} |
|
fce->stat_ts = srv->cur_ts; |
|
|
|
/* create etag */ |
|
etag_create(srv->file_cache_etag, &(fce->st)); |
|
|
|
if (!buffer_is_equal(srv->file_cache_etag, fce->etag)) { |
|
size_t s_len = 0, k; |
|
/* etag has changed: reopen file */ |
|
|
|
file_cache_entry_reset(srv, fce); |
|
|
|
if (-1 == (fce->fd = open(fce->name->ptr, O_RDONLY | O_LARGEFILE))) { |
|
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); |
|
|
|
buffer_reset(fce->name); |
|
return NULL; |
|
} |
|
|
|
srv->cur_fds++; |
|
|
|
buffer_copy_string_buffer(fce->etag, srv->file_cache_etag); |
|
|
|
/* determine mimetype */ |
|
buffer_reset(fce->content_type); |
|
|
|
s_len = name->used - 1; |
|
|
|
for (k = 0; k < con->conf.mimetypes->used; k++) { |
|
data_string *ds = (data_string *)con->conf.mimetypes->data[k]; |
|
size_t ct_len; |
|
|
|
ct_len = ds->key->used - 1; |
|
|
|
if (buffer_is_equal_right_len(name, ds->key, ct_len)) { |
|
buffer_copy_string_buffer(fce->content_type, ds->value); |
|
break; |
|
} |
|
} |
|
|
|
#ifdef HAVE_XATTR |
|
if (buffer_is_empty(fce->content_type)) { |
|
fce_attr_get(fce->content_type, name->ptr); |
|
} |
|
#endif |
|
} |
|
|
|
#ifdef USE_LINUX_SIGIO |
|
fce->is_dirty = 0; |
|
#endif |
|
|
|
fce->in_use++; |
|
|
|
if (fce->fd == -1) { |
|
log_error_write(srv, __FILE__, __LINE__, "sb", "fd is still -1 !", fce->name); |
|
} |
|
if (fce->st.st_size == 0) { |
|
log_error_write(srv, __FILE__, __LINE__, "sb", "size is still 0 !", fce->name); |
|
} |
|
|
|
|
|
|
|
|
|
return fce; |
|
} |
|
|
|
if (fce->in_use == 0) { |
|
if (!first_unused_fce) first_unused_fce = fce; |
|
|
|
if (srv->cur_ts - fce->stat_ts > 10) { |
|
file_cache_entry_reset(srv, fce); |
|
} |
|
} |
|
} |
|
|
|
if (first_unused_fce) { |
|
file_cache_entry_reset(srv, fce); |
|
|
|
fce = first_unused_fce; |
|
} else { |
|
/* not found, insert */ |
|
fce = file_cache_get_unused_entry(srv); |
|
} |
|
} |
|
#endif |
|
|
|
|
|
handler_t file_cache_get_entry(server *srv, connection *con, buffer *name, file_cache_entry **o_fce_ptr) { |
|
file_cache_entry *fce = NULL; |
|
file_cache_entry *o_fce = *o_fce_ptr; |
|
|
|
|
|
UNUSED(con); |
|
|
|
/* still valid ? */ |
|
if (o_fce != NULL) { |
|
if (buffer_is_equal(name, o_fce->name) && |
|
(o_fce->fd != -1) && |
|
(o_fce->stat_ts == srv->cur_ts) |
|
) { |
|
return HANDLER_GO_ON; |
|
} else { |
|
o_fce->in_use--; |
|
} |
|
file_cache_entry_reset(srv, o_fce); |
|
} |
|
|
|
|
|
fce = file_cache_get_unused_entry(srv); |
|
|
|
buffer_copy_string_buffer(fce->name, name); |
|
fce->in_use = 0; |
|
fce->fd = -1; |
|
|
|
if (-1 == (con->conf.follow_symlink ? stat(name->ptr, &(fce->st)) : lstat(name->ptr, &(fce->st)))) { |
|
int oerrno = errno; |
|
#if 0 |
|
log_error_write(srv, __FILE__, __LINE__, "sbs", "stat failed:", name, strerror(errno)); |
|
#endif |
|
file_cache_entry_reset(srv, fce); |
|
|
|
buffer_reset(fce->name); |
|
|
|
errno = oerrno; |
|
return HANDLER_ERROR; |
|
} |
|
|
|
fce->stat_ts = srv->cur_ts; |
|
|
|
if (S_ISREG(fce->st.st_mode)) { |
|
size_t k, s_len; |
|
#ifdef USE_LINUX_SIGIO |
|
file_cache_entry *dir_fce; |
|
char *slash; |
|
#endif |
|
|
|
if (-1 == (fce->fd = open(name->ptr, O_RDONLY | O_LARGEFILE))) { |
|
int oerrno = errno; |
|
if (errno == EMFILE || errno == EINTR) { |
|
return HANDLER_WAIT_FOR_FD; |
|
} |
|
|
|
log_error_write(srv, __FILE__, __LINE__, "sbs", |
|
"open failed for:", name, |
|
strerror(errno)); |
|
|
|
buffer_reset(fce->name); |
|
|
|
errno = oerrno; |
|
return HANDLER_ERROR; |
|
} |
|
|
|
srv->cur_fds++; |
|
|
|
/* determine mimetype */ |
|
buffer_reset(fce->content_type); |
|
|
|
s_len = name->used - 1; |
|
|
|
for (k = 0; k < con->conf.mimetypes->used; k++) { |
|
data_string *ds = (data_string *)con->conf.mimetypes->data[k]; |
|
size_t ct_len; |
|
|
|
if (ds->key->used == 0) continue; |
|
|
|
ct_len = ds->key->used - 1; |
|
|
|
if (s_len < ct_len) continue; |
|
|
|
if (0 == strncmp(name->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { |
|
buffer_copy_string_buffer(fce->content_type, ds->value); |
|
break; |
|
} |
|
} |
|
|
|
#ifdef HAVE_XATTR |
|
if (buffer_is_empty(fce->content_type)) { |
|
fce_attr_get(fce->content_type, name->ptr); |
|
} |
|
#endif |
|
|
|
etag_create(fce->etag, &(fce->st)); |
|
|
|
#ifdef USE_LINUX_SIGIO |
|
/* register sigio for the directory */ |
|
dir_fce = file_cache_get_unused_entry(srv); |
|
|
|
buffer_copy_string_buffer(fc->dir_name, name); |
|
|
|
/* get dirname */ |
|
if (0 == (slash = strrchr(fc->dir_name->ptr, '/'))) { |
|
SEGFAULT(); |
|
} |
|
*(slash+1) = '\0'; |
|
|
|
if (-1 == (dir_fce->fd = open(fc->dir_name->ptr, O_RDONLY))) { |
|
int oerrno = errno; |
|
log_error_write(srv, __FILE__, __LINE__, "sbs", |
|
"open failed:", fc->dir_name, strerror(errno)); |
|
|
|
errno = oerrno; |
|
return HANDLER_ERROR; |
|
} |
|
|
|
srv->cur_fds++; |
|
|
|
if (fcntl(dir_fce->fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT) < 0) { |
|
int oerrno = errno; |
|
log_error_write(srv, __FILE__, __LINE__, "ss", |
|
"fcntl failed:", strerror(errno)); |
|
|
|
close(dir_fce->fd); |
|
srv->cur_fds--; |
|
|
|
errno = oerrno; |
|
return HANDLER_ERROR; |
|
} |
|
|
|
/* ->used is not updated -> no _buffer copy */ |
|
buffer_copy_string(dir_fce->name, fc->dir_name->ptr); |
|
|
|
/* register fd-handler */ |
|
fdevent_register(srv->ev, dir_fce->fd, file_cache_handle_fdevent, dir_fce); |
|
fdevent_event_add(srv->ev, &(dir_fce->fde_ndx), dir_fce->fd, FDEVENT_IN); |
|
# if 1 |
|
log_error_write(srv, __FILE__, __LINE__, "sddb", "fdevent_event_add:", fce->fde_ndx, fce->fd, fce->name); |
|
# endif |
|
#endif |
|
} |
|
|
|
fce->in_use++; |
|
|
|
*o_fce_ptr = fce; |
|
|
|
return HANDLER_GO_ON; |
|
} |
|
|
|
int file_cache_entry_release(server *srv, connection *con, file_cache_entry *fce) { |
|
UNUSED(srv); |
|
UNUSED(con); |
|
|
|
if (fce->in_use > 0) fce->in_use--; |
|
file_cache_entry_reset(srv, fce); |
|
|
|
return 0; |
|
} |
|
|
|
|