diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index ff80bee6..fb5e639c 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -17,8 +17,10 @@ #include "log.h" #include "buffer.h" #include "fdevent.h" +#include "http_chunk.h" #include "http_header.h" #include "keyvalue.h" +#include "response.h" #include "plugin.h" @@ -72,8 +74,6 @@ typedef struct { PLUGIN_DATA; plugin_config defaults; plugin_config conf; - - buffer tmp_buf; } plugin_data; static pcre_keyvalue_buffer * mod_dirlisting_parse_excludes(server *srv, const array *a) { @@ -115,7 +115,6 @@ INIT_FUNC(mod_dirlisting_init) { FREE_FUNC(mod_dirlisting_free) { plugin_data * const p = p_d; - free(p->tmp_buf.ptr); 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) { @@ -371,24 +370,19 @@ static void http_dirls_sort(dirls_entry_t **ent, int num) { /* buffer must be able to hold "999.9K" * conversion is simple but not perfect */ -static int http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) { - const char unit[] = " KMGTPE"; /* Kilo, Mega, Giga, Tera, Peta, Exa */ - const char *u = unit; /* u will always increment at least once */ +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 (size < 100) + if (0 < size && size < 100) size += 99; - if (size < 100) - size = 0; - while (1) { - remain = (int) size & 1023; + do { + remain = (int)(size & 1023); size >>= 10; - u++; - if ((size & (~0 ^ 1023)) == 0) - break; - } + ++u; + } while (size & ~1023); remain /= 100; if (remain > 9) @@ -403,37 +397,76 @@ static int http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) { if (buflen + 3 >= bufsz) return buflen; buf[buflen+0] = '.'; buf[buflen+1] = remain + '0'; - buf[buflen+2] = *u; + 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(buffer *out, int symlinks, const buffer *path, const char *classname, int encode) { - int fd = fdevent_open_cloexec(path->ptr, symlinks, O_RDONLY, 0); - ssize_t rd; - char buf[8192]; +static void http_list_directory_include_file(request_st * const r, plugin_data * 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; + } - if (-1 == fd) return; + uint32_t len = 0; + if (path->ptr[0] != '/') { /* temporarily extend r->physical.path */ + len = buffer_string_length(&r->physical.path); + buffer_append_path_len(&r->physical.path, CONST_BUF_LEN(path)); + path = &r->physical.path; + } + stat_cache_entry * const sce = + stat_cache_get_entry_open(path, r->conf.follow_symlink); + if (len) + buffer_string_set_length(&r->physical.path, len); + if (NULL == sce || sce->fd < 0 || 0 != sce->st.st_size) + return; - if (encode) { - buffer_append_string_len(out, CONST_STR_LEN("
")); - } + chunkqueue * const cq = &r->write_queue; + if (encode) { + if (is_header) + chunkqueue_append_mem(cq, CONST_STR_LEN("")); + else + chunkqueue_append_mem(cq, CONST_STR_LEN("")); + } + else { + http_chunk_append_file_ref(r, sce); + } } /* portions copied from mod_status @@ -643,9 +676,11 @@ static void http_dirlist_append_js_table_resort (buffer * const b, const request buffer_append_string_len(b, CONST_STR_LEN(");\n\n// -->\n\n\n")); } -static void http_list_directory_header(const request_st * const r, plugin_data * const p, buffer * const out) { +static void http_list_directory_header(request_st * const r, plugin_data * const p) { + chunkqueue * const cq = &r->write_queue; if (p->conf.auto_layout) { + buffer * const out = chunkqueue_append_buffer_open(cq); buffer_append_string_len(out, CONST_STR_LEN( "\n" "\n" @@ -702,21 +737,14 @@ static void http_list_directory_header(const request_st * const r, plugin_data * } buffer_append_string_len(out, CONST_STR_LEN("\n\n")); + chunkqueue_append_buffer_commit(cq); } if (!buffer_string_is_empty(p->conf.show_header)) { - /* if we have a HEADER file, display it in */ - - const buffer *hb = p->conf.show_header; - if (hb->ptr[0] != '/') { - hb = &p->tmp_buf; - buffer_copy_buffer(&p->tmp_buf, &r->physical.path); - buffer_append_path_len(&p->tmp_buf, CONST_BUF_LEN(p->conf.show_header)); - } - - http_list_directory_include_file(out, r->conf.follow_symlink, hb, "header", p->conf.encode_header); + http_list_directory_include_file(r, p, 1);/*0 for readme; 1 for header*/ } + buffer * const out = chunkqueue_append_buffer_open(cq); buffer_append_string_len(out, CONST_STR_LEN("")); - while ((rd = read(fd, buf, sizeof(buf))) > 0) { - if (encode) { - buffer_append_string_encoded(out, buf, (size_t)rd, ENCODING_MINIMAL_XML); - } else { - buffer_append_string_len(out, buf, (size_t)rd); - } - } - close(fd); + /* 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 */ - if (encode) { - buffer_append_string_len(out, CONST_STR_LEN("")); - } + /* 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, + CONST_BUF_LEN(out), + r->conf.errh)) + break; + buffer_clear(out); + } + } + if (out != tb) + chunkqueue_append_buffer_commit(cq); + + chunkqueue_append_mem(cq, CONST_STR_LEN("Index of ")); buffer_append_string_encoded(out, CONST_BUF_LEN(&r->uri.path), ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN( @@ -743,40 +771,37 @@ static void http_list_directory_header(const request_st * const r, plugin_data * "\n" )); } + chunkqueue_append_buffer_commit(cq); } -static void http_list_directory_footer(const request_st * const r, plugin_data * const p, buffer * const out) { +static void http_list_directory_footer(request_st * const r, plugin_data * const p) { - buffer_append_string_len(out, CONST_STR_LEN( + chunkqueue * const cq = &r->write_queue; + chunkqueue_append_mem(cq, CONST_STR_LEN( "\n" "\n" "\n" )); if (!buffer_string_is_empty(p->conf.show_readme)) { - /* if we have a README file, display it in */ - - const buffer *rb = p->conf.show_readme; - if (rb->ptr[0] != '/') { - rb = &p->tmp_buf; - buffer_copy_buffer(&p->tmp_buf, &r->physical.path); - buffer_append_path_len(&p->tmp_buf, CONST_BUF_LEN(p->conf.show_readme)); - } - - http_list_directory_include_file(out, r->conf.follow_symlink, rb, "readme", p->conf.encode_readme); + http_list_directory_include_file(r, p, 0);/*0 for readme; 1 for header*/ } - if(p->conf.auto_layout) { + if (p->conf.auto_layout) { + buffer * const out = chunkqueue_append_buffer_open(cq); + const buffer * const footer = + !buffer_string_is_empty(p->conf.set_footer) + ? p->conf.set_footer + : !buffer_string_is_empty(r->conf.server_tag) + ? r->conf.server_tag + : NULL; buffer_append_string_len(out, CONST_STR_LEN( "
" )); - if (!buffer_string_is_empty(p->conf.set_footer)) { - buffer_append_string_buffer(out, p->conf.set_footer); - } else { - buffer_append_string_buffer(out, r->conf.server_tag); - } + if (footer) + buffer_append_string_buffer(out, footer); buffer_append_string_len(out, CONST_STR_LEN( "\n" @@ -794,11 +819,12 @@ static void http_list_directory_footer(const request_st * const r, plugin_data * "\n" "