|
|
|
/* fstatat() fdopendir() */
|
|
|
|
#if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE-0 < 700
|
|
|
|
#undef _XOPEN_SOURCE
|
|
|
|
#define _XOPEN_SOURCE 700
|
|
|
|
/* NetBSD dirent.h improperly hides fdopendir() (POSIX.1-2008) declaration
|
|
|
|
* which should be visible with _XOPEN_SOURCE 700 or _POSIX_C_SOURCE 200809L */
|
|
|
|
#ifdef __NetBSD__
|
|
|
|
#define _NETBSD_SOURCE
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "first.h"
|
|
|
|
|
|
|
|
#include "sys-time.h"
|
|
|
|
|
|
|
|
#include "base.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "buffer.h"
|
|
|
|
#include "connections.h"/* joblist_append() */
|
|
|
|
#include "fdevent.h"
|
|
|
|
#include "http_chunk.h"
|
|
|
|
#include "http_header.h"
|
|
|
|
#include "keyvalue.h"
|
|
|
|
#include "response.h"
|
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
|
|
|
|
#include "stat_cache.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#ifdef AT_FDCWD
|
|
|
|
#ifndef _ATFILE_SOURCE
|
|
|
|
#define _ATFILE_SOURCE
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _D_EXACT_NAMLEN
|
|
|
|
#ifdef _DIRENT_HAVE_D_NAMLEN
|
|
|
|
#define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
|
|
|
|
#else
|
|
|
|
#define _D_EXACT_NAMLEN(d) (strlen ((d)->d_name))
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* this is a dirlisting for a lighttpd plugin
|
|
|
|
*
|
|
|
|
* Notes:
|
|
|
|
* - mod_dirlisting is a dir list implementation. One size does not fit all.
|
|
|
|
* mod_dirlisting aims to be somewhat flexible, but if special behavior
|
|
|
|
* is needed, use custom CGI/FastCGI/SCGI to handle dir listing instead.
|
|
|
|
* - backend daemon could implement custom caching
|
|
|
|
* - backend daemon could monitor directory for changes (e.g. inotify)
|
|
|
|
* - backend daemon or scripts could trigger when directory is modified
|
|
|
|
* and could regenerate index.html (and mod_indexfile could be used
|
|
|
|
* instead of mod_dirlisting)
|
|
|
|
* - basic alphabetical sorting (in C locale) is done on server side
|
|
|
|
* in case client does not execute javascript
|
|
|
|
* (otherwise, response could be streamed, which is not done)
|
|
|
|
* (possible future dir-listing.* config option)
|
|
|
|
* - reading entire directory into memory for sorting large directory
|
|
|
|
* can lead to large memory usage if many simultaneous requests occur
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct dirlist_cache {
|
|
|
|
int32_t max_age;
|
|
|
|
buffer *path;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char dir_listing;
|
|
|
|
char hide_dot_files;
|
|
|
|
char hide_readme_file;
|
|
|
|
char encode_readme;
|
|
|
|
char hide_header_file;
|
|
|
|
char encode_header;
|
|
|
|
char auto_layout;
|
|
|
|
|
|
|
|
pcre_keyvalue_buffer *excludes;
|
|
|
|
|
|
|
|
const buffer *show_readme;
|
|
|
|
const buffer *show_header;
|
|
|
|
const buffer *external_css;
|
|
|
|
const buffer *external_js;
|
|
|
|
const buffer *encoding;
|
|
|
|
const buffer *set_footer;
|
|
|
|
const struct dirlist_cache *cache;
|
|
|
|
} plugin_config;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
PLUGIN_DATA;
|
|
|
|
plugin_config defaults;
|
|
|
|
plugin_config conf;
|
|
|
|
int processing;
|
|
|
|
} plugin_data;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint32_t namelen;
|
|
|
|
unix_time64_t mtime;
|
|
|
|
off_t size;
|
|
|
|
} dirls_entry_t;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
dirls_entry_t **ent;
|
|
|
|
uint32_t used;
|
|
|
|
uint32_t size;
|
|
|
|
} dirls_list_t;
|
|
|
|
|
|
|
|
#define DIRLIST_ENT_NAME(ent) ((char*)(ent) + sizeof(dirls_entry_t))
|
|
|
|
#define DIRLIST_BLOB_SIZE 16
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
DIR *dp;
|
|
|
|
dirls_list_t dirs;
|
|
|
|
dirls_list_t files;
|
|
|
|
char *path;
|
|
|
|
char *path_file;
|
|
|
|
int dfd; /*(dirfd() owned by (DIR *))*/
|
|
|
|
uint32_t name_max;
|
|
|
|
plugin_config conf;
|
|
|
|
} handler_ctx;
|
|
|
|
|
|
|
|
#define DIRLIST_BATCH 32
|
|
|
|
static int dirlist_max_in_progress;
|
|
|
|
|
|
|
|
|
|
|
|
static handler_ctx * mod_dirlisting_handler_ctx_init (plugin_data * const p) {
|
|
|
|
handler_ctx *hctx = calloc(1, sizeof(*hctx));
|
|
|
|
force_assert(hctx);
|
|
|
|
memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
|
|
|
|
return hctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mod_dirlisting_handler_ctx_free (handler_ctx *hctx) {
|
|
|
|
if (hctx->dp)
|
|
|
|
closedir(hctx->dp);
|
|
|
|
if (hctx->files.ent) {
|
|
|
|
dirls_entry_t ** const ent = hctx->files.ent;
|
|
|
|
for (uint32_t i = 0, used = hctx->files.used; i < used; ++i)
|
|
|
|
free(ent[i]);
|
|
|
|
free(ent);
|
|
|
|
}
|
|
|
|
if (hctx->dirs.ent) {
|
|
|
|
dirls_entry_t ** const ent = hctx->dirs.ent;
|
|
|
|
for (uint32_t i = 0, used = hctx->dirs.used; i < used; ++i)
|
|
|
|
free(ent[i]);
|
|
|
|
free(ent);
|
|
|
|
}
|
|
|
|
free(hctx->path);
|
|
|
|
free(hctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct dirlist_cache * mod_dirlisting_parse_cache(server *srv, const array *a) {
|
|
|
|
const data_unset *du;
|
|
|
|
|
|
|
|
du = array_get_element_klen(a, CONST_STR_LEN("max-age"));
|
|
|
|
const int32_t max_age = config_plugin_value_to_int32(du, 15);
|
|
|
|
|
|
|
|
buffer *path = NULL;
|
|
|
|
du = array_get_element_klen(a, CONST_STR_LEN("path"));
|
|
|
|
if (NULL == du) {
|
|
|
|
if (0 != max_age) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"dir-listing.cache must include \"path\"");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (du->type != TYPE_STRING) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"dir-listing.cache \"path\" must have string value");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
path = &((data_string *)du)->value;
|
|
|
|
if (!stat_cache_path_isdir(path)) {
|
|
|
|
if (errno == ENOTDIR) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"dir-listing.cache \"path\" => \"%s\" is not a dir",
|
|
|
|
path->ptr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"dir-listing.cache \"path\" => \"%s\" does not exist",
|
|
|
|
path->ptr);
|
|
|
|
/*(warning; not returning NULL)*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dirlist_cache * const cache = calloc(1, sizeof(struct dirlist_cache));
|
|
|
|
force_assert(cache);
|
|
|
|
cache->max_age = max_age;
|
|
|
|
cache->path = path;
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static pcre_keyvalue_buffer * mod_dirlisting_parse_excludes(server *srv, const array *a) {
|
|
|
|
const int pcre_jit =
|
|
|
|
!srv->srvconf.feature_flags
|
|
|
|
|| config_plugin_value_tobool(
|
|
|
|
array_get_element_klen(srv->srvconf.feature_flags,
|
|
|
|
CONST_STR_LEN("server.pcre_jit")), 1);
|
|
|
|
pcre_keyvalue_buffer * const kvb = pcre_keyvalue_buffer_init();
|
|
|
|
buffer empty = { NULL, 0, 0 };
|
|
|
|
for (uint32_t j = 0; j < a->used; ++j) {
|
|
|
|
const data_string *ds = (data_string *)a->data[j];
|
|
|
|
if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->value, &empty,
|
|
|
|
pcre_jit)) {
|
|
|
|
log_error(srv->errh, __FILE__, __LINE__,
|
|
|
|
"pcre_compile failed for %s", ds->key.ptr);
|
|
|
|
pcre_keyvalue_buffer_free(kvb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return kvb;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __COVERITY__
|
|
|
|
#include "burl.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int mod_dirlisting_exclude(pcre_keyvalue_buffer * const kvb, const char * const name, const uint32_t len) {
|
|
|
|
/*(re-use keyvalue.[ch] for match-only;
|
|
|
|
* must have been configured with empty kvb 'value' during init)*/
|
|
|
|
buffer input = { NULL, len+1, 0 };
|
|
|
|
*(const char **)&input.ptr = name;
|
|
|
|
pcre_keyvalue_ctx ctx = { NULL, NULL, 0, -1 };
|
|
|
|
#ifdef __COVERITY__
|
|
|
|
/*(again, must have been configured w/ empty kvb 'value' during init)*/
|
|
|
|
struct cond_match_t cache;
|
|
|
|
memset(&cache, 0, sizeof(cache));
|
|
|
|
struct burl_parts_t bp;
|
|
|
|
memset(&bp, 0, sizeof(bp));
|
|
|
|
ctx.cache = &cache;
|
|
|
|
ctx.burl = &bp;
|
|
|
|
#endif
|
|
|
|
/*(fail closed (simulate match to exclude) if there is an error)*/
|
|
|
|
return HANDLER_ERROR == pcre_keyvalue_buffer_process(kvb,&ctx,&input,NULL)
|
|
|
|
|| -1 != ctx.m;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
INIT_FUNC(mod_dirlisting_init) {
|
|
|
|
return calloc(1, sizeof(plugin_data));
|
|
|
|
}
|
|
|
|
|
|
|
|
FREE_FUNC(mod_dirlisting_free) {
|
|
|
|
plugin_data * const p = p_d;
|
|
|
|
if (NULL == p->cvlist) return;
|
|
|
|
/* (init i to 0 if global context; to 1 to skip empty global context) */
|
|
|
|
for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
|
|
|
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
|
|
|
|
for (; -1 != cpv->k_id; ++cpv) {
|
|
|
|
switch (cpv->k_id) {
|
|
|
|
case 2: /* dir-listing.exclude */
|
|
|
|
if (cpv->vtype != T_CONFIG_LOCAL) continue;
|
|
|
|
pcre_keyvalue_buffer_free(cpv->v.v);
|
|
|
|
break;
|
|
|
|
case 15: /* dir-listing.cache */
|
|
|
|
if (cpv->vtype != T_CONFIG_LOCAL) continue;
|
|
|
|
free(cpv->v.v);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mod_dirlisting_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
|
|
|
|
switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
|
|
|
|
case 0: /* dir-listing.activate */
|
|
|
|
case 1: /* server.dir-listing *//*(historical)*/
|
|
|
|
pconf->dir_listing = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 2: /* dir-listing.exclude */
|
|
|
|
if (cpv->vtype == T_CONFIG_LOCAL)
|
|
|
|
pconf->excludes = cpv->v.v;
|
|
|
|
break;
|
|
|
|
case 3: /* dir-listing.hide-dotfiles */
|
|
|
|
pconf->hide_dot_files = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 4: /* dir-listing.external-css */
|
|
|
|
pconf->external_css = cpv->v.b;
|
|
|
|
break;
|
|
|
|
case 5: /* dir-listing.external-js */
|
|
|
|
pconf->external_js = cpv->v.b;
|
|
|
|
break;
|
|
|
|
case 6: /* dir-listing.encoding */
|
|
|
|
pconf->encoding = cpv->v.b;
|
|
|
|
break;
|
|
|
|
case 7: /* dir-listing.show-readme */
|
|
|
|
pconf->show_readme = cpv->v.b;
|
|
|
|
break;
|
|
|
|
case 8: /* dir-listing.hide-readme-file */
|
|
|
|
pconf->hide_readme_file = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 9: /* dir-listing.show-header */
|
|
|
|
pconf->show_header = cpv->v.b;
|
|
|
|
break;
|
|
|
|
case 10:/* dir-listing.hide-header-file */
|
|
|
|
pconf->hide_header_file = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 11:/* dir-listing.set-footer */
|
|
|
|
pconf->set_footer = cpv->v.b;
|
|
|
|
break;
|
|
|
|
case 12:/* dir-listing.encode-readme */
|
|
|
|
pconf->encode_readme = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 13:/* dir-listing.encode-header */
|
|
|
|
pconf->encode_header = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 14:/* dir-listing.auto-layout */
|
|
|
|
pconf->auto_layout = (char)cpv->v.u;
|
|
|
|
break;
|
|
|
|
case 15:/* dir-listing.cache */
|
|
|
|
if (cpv->vtype == T_CONFIG_LOCAL)
|
|
|
|
pconf->cache = cpv->v.v;
|
|
|
|
break;
|
|
|
|
default:/* should not happen */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mod_dirlisting_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
|
|
|
|
do {
|
|
|
|
mod_dirlisting_merge_config_cpv(pconf, cpv);
|
|
|
|
} while ((++cpv)->k_id != -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mod_dirlisting_patch_config(request_st * const r, plugin_data * const p) {
|
|
|
|
memcpy(&p->conf, &p->defaults, sizeof(plugin_config));
|
|
|
|
for (int i = 1, used = p->nconfig; i < used; ++i) {
|
|
|
|
if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
|
|
|
|
mod_dirlisting_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) {
|
|
|
|
static const config_plugin_keys_t cpk[] = {
|
|
|
|
{ CONST_STR_LEN("dir-listing.activate"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("server.dir-listing"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.exclude"),
|
|
|
|
T_CONFIG_ARRAY_VLIST,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.hide-dotfiles"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.external-css"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.external-js"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.encoding"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.show-readme"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.hide-readme-file"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.show-header"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.hide-header-file"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.set-footer"),
|
|
|
|
T_CONFIG_STRING,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.encode-readme"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.encode-header"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.auto-layout"),
|
|
|
|
T_CONFIG_BOOL,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ CONST_STR_LEN("dir-listing.cache"),
|
|
|
|
T_CONFIG_ARRAY_KVANY,
|
|
|
|
T_CONFIG_SCOPE_CONNECTION }
|
|
|
|
,{ NULL, 0,
|
|
|
|
T_CONFIG_UNSET,
|
|
|
|
T_CONFIG_SCOPE_UNSET }
|
|
|
|
};
|
|
|
|
|
|
|
|
plugin_data * const p = p_d;
|
|
|
|
if (!config_plugin_values_init(srv, p, cpk, "mod_dirlisting"))
|
|
|
|
return HANDLER_ERROR;
|
|
|
|
|
|
|
|
/* process and validate config directives
|
|
|
|
* (init i to 0 if global context; to 1 to skip empty global context) */
|
|
|
|
for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
|
|
|
|
config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
|
|
|
|
for (; -1 != cpv->k_id; ++cpv) {
|
|
|
|
switch (cpv->k_id) {
|
|
|
|
case 0: /* dir-listing.activate */
|
|
|
|
case 1: /* server.dir-listing *//*(historical)*/
|
|
|
|
break;
|
|
|
|
case 2: /* dir-listing.exclude */
|
|
|
|
cpv->v.v = mod_dirlisting_parse_excludes(srv, cpv->v.a);
|
|
|
|
if (NULL == cpv->v.v) return HANDLER_ERROR;
|
|
|
|
cpv->vtype = T_CONFIG_LOCAL;
|
|
|
|
break;
|
|
|
|
case 3: /* dir-listing.hide-dotfiles */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
break;
|
|
|
|
case 4: /* dir-listing.external-css */
|
|
|
|
case 5: /* dir-listing.external-js */
|
|
|
|
case 6: /* dir-listing.encoding */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
if (buffer_is_blank(cpv->v.b))
|
|
|
|
cpv->v.b = NULL;
|
|
|
|
break;
|
|
|
|
case 7: /* dir-listing.show-readme */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
if (!buffer_is_blank(cpv->v.b)) {
|
|
|
|
buffer *b;
|
|
|
|
*(const buffer **)&b = cpv->v.b;
|
|
|
|
if (buffer_is_equal_string(b, CONST_STR_LEN("enable")))
|
|
|
|
buffer_copy_string_len(b, CONST_STR_LEN("README.txt"));
|
|
|
|
else if (buffer_is_equal_string(b,CONST_STR_LEN("disable")))
|
|
|
|
buffer_clear(b);
|
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
else
|
|
|
|
cpv->v.b = NULL;
|
|
|
|
break;
|
|
|
|
case 8: /* dir-listing.hide-readme-file */
|
|
|
|
break;
|
|
|
|
case 9: /* dir-listing.show-header */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
if (!buffer_is_blank(cpv->v.b)) {
|
|
|
|
buffer *b;
|
|
|
|
*(const buffer **)&b = cpv->v.b;
|
|
|
|
if (buffer_is_equal_string(b, CONST_STR_LEN("enable")))
|
|
|
|
buffer_copy_string_len(b, CONST_STR_LEN("HEADER.txt"));
|
|
|
|
else if (buffer_is_equal_string(b,CONST_STR_LEN("disable")))
|
|
|
|
buffer_clear(b);
|
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
else
|
|
|
|
cpv->v.b = NULL;
|
|
|
|
break;
|
|
|
|
case 10:/* dir-listing.hide-header-file */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
break;
|
|
|
|
case 11:/* dir-listing.set-footer */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
if (buffer_is_blank(cpv->v.b))
|
|
|
|
cpv->v.b = NULL;
|
|
|
|
break;
|
|
|
|
case 12:/* dir-listing.encode-readme */
|
|
|
|
case 13:/* dir-listing.encode-header */
|
|
|
|
case 14:/* dir-listing.auto-layout */
|
|
|
|
break;
|
|
|
|
case 15:/* dir-listing.cache */
|
|
|
|
cpv->v.v = mod_dirlisting_parse_cache(srv, cpv->v.a);
|
|
|
|
if (NULL == cpv->v.v) return HANDLER_ERROR;
|
|
|
|
cpv->vtype = T_CONFIG_LOCAL;
|
|
|
|
break;
|
|
|
|
default:/* should not happen */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dirlist_max_in_progress = srv->max_conns >> 4;
|
|
|
|
if (0 == dirlist_max_in_progress) dirlist_max_in_progress = 1;
|
|
|
|
|
|
|
|
p->defaults.dir_listing = 0;
|
|
|
|
p->defaults.hide_dot_files = 1;
|
|
|
|
p->defaults.hide_readme_file = 0;
|
|
|
|
p->defaults.hide_header_file = 0;
|
|
|
|
p->defaults.encode_readme = 1;
|
|
|
|
p->defaults.encode_header = 1;
|
|
|
|
p->defaults.auto_layout = 1;
|
|
|
|
|
|
|
|
/* initialize p->defaults from global config context */
|
|
|
|
if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
|
|
|
|
const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
|
|
|
|
if (-1 != cpv->k_id)
|
|
|
|
mod_dirlisting_merge_config(&p->defaults, cpv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return HANDLER_GO_ON;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* simple combsort algorithm */
|
|
|
|
static void http_dirls_sort(dirls_entry_t **ent, int num) {
|
|
|
|
int gap = num;
|
|
|
|
int i, j;
|
|
|
|
int swapped;
|
|
|
|
dirls_entry_t *tmp;
|
|
|
|
|
|
|
|
do {
|
|
|
|
gap = (gap * 10) / 13;
|
|
|
|
if (gap == 9 || gap == 10)
|
|
|
|
gap = 11;
|
|
|
|
if (gap < 1)
|
|
|
|
gap = 1;
|
|
|
|
swapped = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num - gap; i++) {
|
|
|
|
j = i + gap;
|
|
|
|
if (strcmp(DIRLIST_ENT_NAME(ent[i]), DIRLIST_ENT_NAME(ent[j])) > 0) {
|
|
|
|
tmp = ent[i];
|
|
|
|
ent[i] = ent[j];
|
|
|
|
ent[j] = tmp;
|
|
|
|
swapped = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (gap > 1 || swapped);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* buffer must be able to hold "999.9K"
|
|
|
|
* conversion is simple but not perfect
|
|
|
|
*/
|
|
|
|
static size_t http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) {
|
|
|
|
int remain;
|
|
|
|
int u = -1; /* u will always increment at least once */
|
|
|
|
size_t buflen;
|
|
|
|
|
|
|
|
if (0 < size && size < 100)
|
|
|
|
size += 99;
|
|
|
|
|
|
|
|
do {
|
|
|
|
remain = (int)(size & 1023);
|
|
|
|
size >>= 10;
|
|
|
|
++u;
|
|
|
|
} while (size & ~1023);
|
|
|
|
|
|
|
|
remain /= 100;
|
|
|
|
if (remain > 9)
|
|
|
|
remain = 9;
|
|
|
|
if (size > 999) {
|
|
|
|
size = 0;
|
|
|
|
remain = 9;
|
|
|
|
u++;
|
|
|
|
}
|
|
|
|
|
|
|
|
buflen = li_itostrn(buf, bufsz, size);
|
|
|
|
if (buflen + 3 >= bufsz) return buflen;
|
|
|
|
buf[buflen+0] = '.';
|
|
|
|
buf[buflen+1] = remain + '0';
|
|
|
|
buf[buflen+2] = "KMGTPE"[u]; /* Kilo, Mega, Giga, Tera, Peta, Exa */
|
|
|
|
buf[buflen+3] = '\0';
|
|
|
|
|
|
|
|
return buflen + 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void http_list_directory_include_file(request_st * const r, const handler_ctx * const p, int is_header) {
|
|
|
|
const buffer *path;
|
|
|
|
int encode = 0;
|
|
|
|
if (is_header) {
|
|
|
|
path = p->conf.show_header;
|
|
|
|
encode = p->conf.encode_header;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
path = p->conf.show_readme;
|
|
|
|
encode = p->conf.encode_readme;
|
|
|
|
}
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
if (NULL == path) return;
|
|
|
|
|
|
|
|
uint32_t len = 0;
|
|
|
|
if (path->ptr[0] != '/') { /* temporarily extend r->physical.path */
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
len = buffer_clen(&r->physical.path);
|
|
|
|
buffer_append_path_len(&r->physical.path, BUF_PTR_LEN(path));
|
|
|
|
path = &r->physical.path;
|
|
|
|
}
|
|
|
|
stat_cache_entry * const sce =
|
|
|
|
stat_cache_get_entry_open(path, r->conf.follow_symlink);
|
|
|
|
if (len)
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
buffer_truncate(&r->physical.path, len);
|
|
|
|
if (NULL == sce || sce->fd < 0 || 0 != sce->st.st_size)
|
|
|
|
return;
|
|
|
|
|
|
|
|
chunkqueue * const cq = &r->write_queue;
|
|
|
|
if (encode) {
|
|
|
|
if (is_header)
|
|
|
|
chunkqueue_append_mem(cq, CONST_STR_LEN("<pre class=\"header\">"));
|
|
|
|
else
|
|
|
|
chunkqueue_append_mem(cq, CONST_STR_LEN("<pre class=\"readme\">"));
|
|
|
|
|
|
|
|
/* Note: encoding a very large file may cause lighttpd to pause handling
|
|
|
|
* other requests while lighttpd encodes the file, especially if file is
|
|
|
|
* on a remote filesystem */
|
|
|
|
|
|
|
|
/* encoding can consume 6x file size in worst case scenario,
|
|
|
|
* so send encoded contents of files > 32k to tempfiles) */
|
|
|
|
buffer * const tb = r->tmp_buf;
|
|
|
|
buffer * const out = sce->st.st_size <= 32768
|
|
|
|
? chunkqueue_append_buffer_open(cq)
|
|
|
|
: tb;
|
|
|
|
buffer_clear(out);
|
|
|
|
const int fd = sce->fd;
|
|
|
|
ssize_t rd;
|
|
|
|
char buf[8192];
|
|
|
|
while ((rd = read(fd, buf, sizeof(buf))) > 0) {
|
|
|
|
buffer_append_string_encoded(out, buf, (size_t)rd, ENCODING_MINIMAL_XML);
|
|
|
|
if (out == tb) {
|
|
|
|
if (0 != chunkqueue_append_mem_to_tempfile(cq,
|
[multiple] reduce redundant NULL buffer checks
This commit is a large set of code changes and results in removal of
hundreds, perhaps thousands, of CPU instructions, a portion of which
are on hot code paths.
Most (buffer *) used by lighttpd are not NULL, especially since buffers
were inlined into numerous larger structs such as request_st and chunk.
In the small number of instances where that is not the case, a NULL
check is often performed earlier in a function where that buffer is
later used with a buffer_* func. In the handful of cases that remained,
a NULL check was added, e.g. with r->http_host and r->conf.server_tag.
- check for empty strings at config time and set value to NULL if blank
string will be ignored at runtime; at runtime, simple pointer check
for NULL can be used to check for a value that has been set and is not
blank ("")
- use buffer_is_blank() instead of buffer_string_is_empty(),
and use buffer_is_unset() instead of buffer_is_empty(),
where buffer is known not to be NULL so that NULL check can be skipped
- use buffer_clen() instead of buffer_string_length() when buffer is
known not to be NULL (to avoid NULL check at runtime)
- use buffer_truncate() instead of buffer_string_set_length() to
truncate string, and use buffer_extend() to extend
Examples where buffer known not to be NULL:
- cpv->v.b from config_plugin_values_init is not NULL if T_CONFIG_BOOL
(though we might set it to NULL if buffer_is_blank(cpv->v.b))
- address of buffer is arg (&foo)
(compiler optimizer detects this in most, but not all, cases)
- buffer is checked for NULL earlier in func
- buffer is accessed in same scope without a NULL check (e.g. b->ptr)
internal behavior change:
callers must not pass a NULL buffer to some funcs.
- buffer_init_buffer() requires non-null args
- buffer_copy_buffer() requires non-null args
- buffer_append_string_buffer() requires non-null args
- buffer_string_space() requires non-null arg
1 year ago
|
|
|
BUF_PTR_LEN(out),
|
|
|
|
r->conf.errh))
|
|
|
|
break;
|
|
|
|
buffer_clear(out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (out != tb)
|
|
|
|
chunkqueue_append_buffer_commit(cq);
|
|
|
|
|
|
|
|
chunkqueue_append_mem(cq, CONST_STR_LEN("</pre>"));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
(void)http_chunk_append_file_ref(r, sce);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* portions copied from mod_status
|
|
|
|
* modified and specialized for stable dirlist sorting by name */
|
|
|
|
static const char js_simple_table_resort[] = \
|
|
|
|
"var click_column;\n" \
|
|
|
|
"var name_column = 0;\n" \
|
|
|
|
"var date_column = 1;\n" \
|
|
|
|
"var size_column = 2;\n" \
|
|
|
|
"var type_column = 3;\n" \
|
|
|
|
"var prev_span = null;\n" \
|
|
|
|
"\n" \
|
|
|
|
"if (typeof(String.prototype.localeCompare) === 'undefined') {\n" \
|
|
|
|
" String.prototype.localeCompare = function(str, locale, options) {\n" \
|
|
|
|
" return ((this == str) ? 0 : ((this > str) ? 1 : -1));\n" \
|
|
|
|
" };\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"if (typeof(String.prototype.toLocaleUpperCase) === 'undefined') {\n" \
|
|
|
|
" String.prototype.toLocaleUpperCase = function() {\n" \
|
|
|
|
" return this.toUpperCase();\n" \
|
|
|
|
" };\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function get_inner_text(el) {\n" \
|
|
|
|
" if((typeof el == 'string')||(typeof el == 'undefined'))\n" \
|
|
|
|
" return el;\n" \
|
|
|
|
" if(el.innerText)\n" \
|
|
|
|
" return el.innerText;\n" \
|
|
|
|
" else {\n" \
|
|
|
|
" var str = \"\";\n" \
|
|
|
|
" var cs = el.childNodes;\n" \
|
|
|
|
" var l = cs.length;\n" \
|
|
|
|
" for (var i=0;i<l;i++) {\n" \
|
|
|
|
" if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" \
|
|
|
|
" else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" \
|
|
|
|
" }\n" \
|
|
|
|
" }\n" \
|
|
|
|
" return str;\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function isdigit(c) {\n" \
|
|
|
|
" return (c >= '0' && c <= '9');\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function unit_multiplier(unit) {\n" \
|
|
|
|
" return (unit=='K') ? 1000\n" \
|
|
|
|
" : (unit=='M') ? 1000000\n" \
|
|
|
|
" : (unit=='G') ? 1000000000\n" \
|
|
|
|
" : (unit=='T') ? 1000000000000\n" \
|
|
|
|
" : (unit=='P') ? 1000000000000000\n" \
|
|
|
|
" : (unit=='E') ? 1000000000000000000 : 1;\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"var li_date_regex=/(\\d{4})-(\\w{3})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/;\n" \
|
|
|
|
"\n" \
|
|
|
|
"var li_mon = ['Jan','Feb','Mar','Apr','May','Jun',\n" \
|
|
|
|
" 'Jul','Aug','Sep','Oct','Nov','Dec'];\n" \
|
|
|
|
"\n" \
|
|
|
|
"function li_mon_num(mon) {\n" \
|
|
|
|
" var i; for (i = 0; i < 12 && mon != li_mon[i]; ++i); return i;\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function li_date_cmp(s1, s2) {\n" \
|
|
|
|
" var dp1 = li_date_regex.exec(s1)\n" \
|
|
|
|
" var dp2 = li_date_regex.exec(s2)\n" \
|
|
|
|
" for (var i = 1; i < 7; ++i) {\n" \
|
|
|
|
" var cmp = (2 != i)\n" \
|
|
|
|
" ? parseInt(dp1[i]) - parseInt(dp2[i])\n" \
|
|
|
|
" : li_mon_num(dp1[2]) - li_mon_num(dp2[2]);\n" \
|
|
|
|
" if (0 != cmp) return cmp;\n" \
|
|
|
|
" }\n" \
|
|
|
|
" return 0;\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function sortfn_then_by_name(a,b,sort_column) {\n" \
|
|
|
|
" if (sort_column == name_column || sort_column == type_column) {\n" \
|
|
|
|
" var ad = (a.cells[type_column].innerHTML === 'Directory');\n" \
|
|
|
|
" var bd = (b.cells[type_column].innerHTML === 'Directory');\n" \
|
|
|
|
" if (ad != bd) return (ad ? -1 : 1);\n" \
|
|
|
|
" }\n" \
|
|
|
|
" var at = get_inner_text(a.cells[sort_column]);\n" \
|
|
|
|
" var bt = get_inner_text(b.cells[sort_column]);\n" \
|
|
|
|
" var cmp;\n" \
|
|
|
|
" if (sort_column == name_column) {\n" \
|
|
|
|
" if (at == '..') return -1;\n" \
|
|
|
|
" if (bt == '..') return 1;\n" \
|
|
|
|
" }\n" \
|
|
|
|
" if (a.cells[sort_column].className == 'int') {\n" \
|
|
|
|
" cmp = parseInt(at)-parseInt(bt);\n" \
|
|
|
|
" } else if (sort_column == date_column) {\n" \
|
|
|
|
" var ad = isdigit(at.substr(0,1));\n" \
|
|
|
|
" var bd = isdigit(bt.substr(0,1));\n" \
|
|
|
|
" if (ad != bd) return (!ad ? -1 : 1);\n" \
|
|
|
|
" cmp = li_date_cmp(at,bt);\n" \
|
|
|
|
" } else if (sort_column == size_column) {\n" \
|
|
|
|
" var ai = parseInt(at, 10) * unit_multiplier(at.substr(-1,1));\n" \
|
|
|
|
" var bi = parseInt(bt, 10) * unit_multiplier(bt.substr(-1,1));\n" \
|
|
|
|
" if (at.substr(0,1) == '-') ai = -1;\n" \
|
|
|
|
" if (bt.substr(0,1) == '-') bi = -1;\n" \
|
|
|
|
" cmp = ai - bi;\n" \
|
|
|
|
" } else {\n" \
|
|
|
|
" cmp = at.toLocaleUpperCase().localeCompare(bt.toLocaleUpperCase());\n" \
|
|
|
|
" if (0 != cmp) return cmp;\n" \
|
|
|
|
" cmp = at.localeCompare(bt);\n" \
|
|
|
|
" }\n" \
|
|
|
|
" if (0 != cmp || sort_column == name_column) return cmp;\n" \
|
|
|
|
" return sortfn_then_by_name(a,b,name_column);\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function sortfn(a,b) {\n" \
|
|
|
|
" return sortfn_then_by_name(a,b,click_column);\n" \
|
|
|
|
"}\n" \
|
|
|
|
"\n" \
|
|
|
|
"function resort(lnk) {\n" \
|
|
|
|
" var span = lnk.childNodes[1];\n" \
|
|
|
|
" var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" \
|
|
|
|
" var rows = new Array();\n" \
|
|
|
|
" for (var j=1;j<table.rows.length;j++)\n" \
|
|