Files
Seregon 5e3b38902c Add EPSV/OPTS/SITE/CLNT, MLSD, HTTP stats
Adds several FTP protocol improvements and a new HTTP stats API.

FTP:
- Added EPSV, OPTS, SITE and CLNT command handlers and registered them in the command table.
- Implemented MLSD (machine-readable directory list) and redirected MLST to STAT.
- Changed USER/PASS to accept immediate login for better compatibility with Android clients.
- Relaxed PORT IP validation and added debug logging for PORT targets.
- Open data connections before sending 150 and add short pacing delay to avoid merged 150+226 replies (LIST/NLST/MLSD).
- Implemented EPSV passive socket setup and OPTS handling (UTF8/MLST) and SITE CHMOD no-op.
- Implemented atomic STOR uploads using a temp file and rename on success; cleanup on failure.
- Improved active-mode connect behavior with a short send timeout and logging; simplified data close path to match upstream behavior.
- Accept bare LF line endings from some clients and make command lookup case-insensitive.
- Enabled TCP_NODELAY on control socket and turned on FTP command logging; bumped RELEASE_VERSION to 1.2.3.

HTTP / Server infrastructure:
- Extended http_response to support in-memory streaming, segment splicing, and added APIs (set_body_ref, set_body_splice).
- Modified static serving to use splicing for injected HTML and fall back to body ref for binary assets (PNG); added image/png content-type for .png.
- Added a new /api/stats endpoint providing JSON with disk usage, items in directory, boot/uptime, and CPU temperature where available, plus numerous helper functions for gathering and formatting stats.

Build / repo:
- Updated .gitignore, added web/zftpd-logo.png and bumped embedded web resources (large generated resource changes not detailed here).

Overall these changes improve client compatibility (especially mobile/Android and GoldHEN clients), make uploads atomic, add diagnostics via the HTTP API, and enhance HTTP response streaming.
2026-02-25 23:49:34 +01:00

140 lines
3.0 KiB
C

#include "pal_alloc.h"
#include <pthread.h>
#include <stdint.h>
#include <string.h>
#define THREADS 6
#define ITERS 4000
#define SLOTS 256
#define TEST_ARENA_SIZE (64U * 1024U * 1024U)
static _Alignas(4096) uint8_t g_test_arena[TEST_ARENA_SIZE];
static uint32_t xs32(uint32_t *s)
{
uint32_t x = *s;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
*s = x;
return x;
}
typedef struct {
uint32_t seed;
} worker_arg_t;
static void *worker(void *arg)
{
worker_arg_t *w = (worker_arg_t *)arg;
void *slots[SLOTS] = {0};
size_t sizes[SLOTS] = {0};
for (int i = 0; i < ITERS; i++) {
uint32_t r = xs32(&w->seed);
uint32_t idx = r % SLOTS;
if (slots[idx] != NULL) {
uint8_t *p = (uint8_t *)slots[idx];
size_t sz = sizes[idx];
if (sz > 0U) {
if (p[0] != (uint8_t)(idx & 0xFFU)) {
return (void *)1;
}
if (p[sz - 1U] != (uint8_t)((idx ^ 0xAAU) & 0xFFU)) {
return (void *)2;
}
}
pal_free(slots[idx]);
slots[idx] = NULL;
sizes[idx] = 0U;
} else {
size_t sz = (size_t)(r % 65536U) + 1U;
void *p = pal_malloc(sz);
if (p == NULL) {
return (void *)3;
}
memset(p, (int)(idx & 0xFFU), 1U);
((uint8_t *)p)[sz - 1U] = (uint8_t)((idx ^ 0xAAU) & 0xFFU);
slots[idx] = p;
sizes[idx] = sz;
}
}
for (uint32_t i = 0; i < SLOTS; i++) {
if (slots[i] != NULL) {
pal_free(slots[i]);
}
}
return NULL;
}
int main(void)
{
if (pal_alloc_init(g_test_arena, sizeof(g_test_arena)) != 0) {
return 10;
}
pal_alloc_reset_stats();
pthread_t th[THREADS];
worker_arg_t args[THREADS];
for (int i = 0; i < THREADS; i++) {
args[i].seed = (uint32_t)(0xC001D00DU ^ (uint32_t)i);
if (pthread_create(&th[i], NULL, worker, &args[i]) != 0) {
return 11;
}
}
for (int i = 0; i < THREADS; i++) {
void *ret = NULL;
(void)pthread_join(th[i], &ret);
if (ret != NULL) {
return 12;
}
}
void *p = pal_calloc(128U, 16U);
if (p == NULL) {
return 13;
}
for (size_t i = 0U; i < 2048U; i++) {
if (((uint8_t *)p)[i] != 0U) {
return 14;
}
}
p = pal_realloc(p, 8192U);
if (p == NULL) {
return 15;
}
pal_free(p);
void *a = pal_aligned_alloc(4096U, 123U);
if (a == NULL) {
return 16;
}
if (((uintptr_t)a & (4096U - 1U)) != 0U) {
return 17;
}
pal_free(a);
void *m = NULL;
if (pal_posix_memalign(&m, 64U, 77U) != 0) {
return 18;
}
if (((uintptr_t)m & 63U) != 0U) {
return 19;
}
pal_free(m);
pal_alloc_stats_t st;
pal_alloc_get_stats(&st);
if (st.failures != 0U) {
return 20;
}
if (st.bytes_in_use != 0U) {
return 21;
}
return 0;
}