Browse Source

[mod_magnet] sync script load w/ stat_cache

read file and use luaL_loadbuffer()
eliminate TOC-TOU race w/ independent stat() in stat_cache_get_entry()

restructure script_cache_get_script() into smaller funcs
master
Glenn Strauss 6 months ago
parent
commit
2d898bf9a9
  1. 12
      src/mod_magnet.c
  2. 161
      src/mod_magnet_cache.c

12
src/mod_magnet.c

@ -870,6 +870,18 @@ static handler_t magnet_attract(request_st * const r, plugin_data * const p, buf
/* get the script-context */
L = script_cache_get_script(&p->cache, name, r->conf.etag_flags);
if (NULL == L) {
log_perror(r->conf.errh, __FILE__, __LINE__,
"loading script %s failed", name->ptr);
if (p->conf.stage != -1) { /* skip for response-start */
r->http_status = 500;
r->handler_module = NULL;
}
return HANDLER_FINISHED;
}
if (lua_isstring(L, -1)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"loading script %s failed: %s", name->ptr, lua_tostring(L, -1));

161
src/mod_magnet_cache.c

@ -1,11 +1,11 @@
#include "first.h"
#include "mod_magnet_cache.h"
#include "log.h"
#include "stat_cache.h"
#include "sys-time.h"
#include <errno.h>
#include <stdlib.h>
#include <unistd.h> /* read() */
#include <lualib.h>
#include <lauxlib.h>
@ -22,7 +22,8 @@ __attribute_cold__
static void script_free(script *sc)
{
if (!sc) return;
lua_pop(sc->L, 1); /* the function copy */
if (lua_gettop(sc->L) != 0)
lua_pop(sc->L, 1); /* the function copy */
lua_close(sc->L);
free(sc->name.ptr);
free(sc->etag.ptr);
@ -46,67 +47,99 @@ void script_cache_free_data(script_cache *p)
free(p->ptr);
}
__attribute_cold__
__attribute_noinline__
static lua_State *script_cache_load_script(script * const sc, int etag_flags)
{
/* read file and use luaL_loadbuffer()
* eliminate TOC-TOU race w/ independent stat() in stat_cache_get_entry() */
stat_cache_entry * const sce = stat_cache_get_entry_open(&sc->name, 1);
buffer_clear(&sc->etag);
if (NULL == sce || sce->fd < 0) {
/*(sce->fd < 0 might indicate empty file, which is not a valid script)*/
if (NULL != sce) errno = EBADF;
return NULL;
}
const buffer * const etag = stat_cache_etag_get(sce, etag_flags);
if (etag)
buffer_copy_buffer(&sc->etag, etag);
const off_t sz = sce->st.st_size;
char * const buf = malloc(sz);
force_assert(buf);
ssize_t rd = 0;
off_t off = 0;
do {
rd = read(sce->fd, buf+off, (size_t)(sz-off));
} while (rd > 0 ? (off += rd) != sz : rd < 0 && errno == EINTR);
if (off != sz) { /*(file truncated?)*/
if (rd >= 0) errno = EIO;
return NULL;
}
int rc = luaL_loadbuffer(sc->L, buf, (size_t)sz, sc->name.ptr);
free(buf);
if (0 != rc) {
/* oops, an error, return it */
return sc->L;
}
force_assert(lua_isfunction(sc->L, -1));
return sc->L;
}
__attribute_cold__
static lua_State *script_cache_new_script(script_cache * const cache, const buffer * const name, int etag_flags)
{
script * const sc = script_init();
if (cache->used == cache->size) {
cache->size += 16;
cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr)));
}
cache->ptr[cache->used++] = sc;
buffer_copy_buffer(&sc->name, name);
sc->L = luaL_newstate();
luaL_openlibs(sc->L);
return script_cache_load_script(sc, etag_flags);
}
static lua_State *script_cache_check_script(script * const sc, int etag_flags)
{
if (lua_gettop(sc->L) == 0)
return script_cache_load_script(sc, etag_flags);
force_assert(lua_gettop(sc->L) == 1);
force_assert(lua_isfunction(sc->L, -1));
stat_cache_entry * const sce = stat_cache_get_entry(&sc->name);
if (NULL == sce) {
lua_pop(sc->L, 1); /* pop the old function */
return script_cache_load_script(sc, etag_flags);
}
const buffer * const etag = stat_cache_etag_get(sce, etag_flags);
if (NULL == etag || !buffer_is_equal(&sc->etag, etag)) {
if (0 == etag_flags)
return sc->L;
/* the etag is outdated, reload the function */
lua_pop(sc->L, 1);
return script_cache_load_script(sc, etag_flags);
}
return sc->L;
}
lua_State *script_cache_get_script(script_cache *cache, const buffer *name, int etag_flags)
{
script *sc = NULL;
stat_cache_entry *sce;
for (uint32_t i = 0; i < cache->used; ++i, sc = NULL) {
sc = cache->ptr[i];
if (!buffer_is_equal(name, &sc->name)) continue;
if (lua_gettop(sc->L) == 0) break;
force_assert(lua_gettop(sc->L) == 1);
sce = stat_cache_get_entry(&sc->name);
if (NULL == sce) {
lua_pop(sc->L, 1); /* pop the old function */
break;
}
const buffer *etag = stat_cache_etag_get(sce, etag_flags);
if (NULL == etag || !buffer_is_equal(&sc->etag, etag)) {
/* the etag is outdated, reload the function */
lua_pop(sc->L, 1);
break;
}
force_assert(lua_isfunction(sc->L, -1));
return sc->L;
}
/* if the script was script already loaded but either got changed or
* failed to load last time */
if (sc == NULL) {
sc = script_init();
if (cache->used == cache->size) {
cache->size += 16;
cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr)));
}
cache->ptr[cache->used++] = sc;
buffer_copy_buffer(&sc->name, name);
sc->L = luaL_newstate();
luaL_openlibs(sc->L);
}
buffer_clear(&sc->etag);
if (0 != luaL_loadfile(sc->L, name->ptr)) {
/* oops, an error, return it */
return sc->L;
}
sce = stat_cache_get_entry(&sc->name);
if (sce) {
const buffer *etag = stat_cache_etag_get(sce, etag_flags);
if (etag)
buffer_copy_buffer(&sc->etag, etag);
}
force_assert(lua_isfunction(sc->L, -1));
return sc->L;
for (uint32_t i = 0; i < cache->used; ++i) {
script * const sc = cache->ptr[i];
if (buffer_is_equal(&sc->name, name))
return script_cache_check_script(sc, etag_flags);
}
return script_cache_new_script(cache, name, etag_flags);
}

Loading…
Cancel
Save