Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions static/ngx_http_zstd_static_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,17 @@ ngx_http_zstd_static_handler(ngx_http_request_t *r)
* fall back to a read+lseek pair that would mutate the shared fd
* offset. Every modern POSIX target has it; this guard is
* essentially a build-time tripwire.
*
* Skipped when the file was opened with O_DIRECT (of.is_directio, set by
* ngx_open_cached_file when "directio <size>" is configured and the file
* meets the threshold). An O_DIRECT read requires the buffer, offset and
* length all aligned to the device block size; this deliberately tiny,
* unaligned 4-byte pread would fail with EINVAL on every request, wrongly
* declining the .zst and spamming NGX_LOG_CRIT. The probe is only a
* best-effort corruption guard, so forgoing it under directio is the
* correct trade — the file is still served, just without the sanity check.
*/
if (!of.is_directio) {
if (of.size < 4) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"zstd static: \"%s\" too small to be a zstd frame "
Expand Down Expand Up @@ -289,6 +299,11 @@ ngx_http_zstd_static_handler(ngx_http_request_t *r)
return NGX_DECLINED;
}
}
} else {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
"zstd static: skipping magic probe on directio "
"file \"%s\" (O_DIRECT alignment)", path.data);
}
#endif /* NGX_HAVE_PREAD */

#endif
Expand Down
30 changes: 30 additions & 0 deletions t/01-static.t
Original file line number Diff line number Diff line change
Expand Up @@ -570,3 +570,33 @@ Accept-Encoding: gzip
Vary: Accept-Encoding
--- no_error_log
[error]



=== TEST 26: zstd_static serves a .zst under directio without a failed pread
# Regression for the O_DIRECT magic-probe bug. With "directio" active the
# open_file_cache opens the .zst with O_DIRECT; the 4-byte, unaligned
# magic pread() then fails EINVAL, wrongly declining every .zst above the
# threshold and spamming NGX_LOG_CRIT. The fix skips the probe when
# of.is_directio. Assert the precompressed frame is still served (200 +
# Content-Encoding: zstd) and no CRIT/[error] appears.
#
# NOTE: O_DIRECT support is filesystem-dependent (tmpfs may not honour it),
# so this deterministically catches the "declined + CRIT" regression only
# where O_DIRECT actually engages; everywhere else it still asserts correct
# serving. directio 1 = every file >= 1 byte is eligible (test.zst is 3717B).
--- config
location /test {
zstd_static on;
directio 1;
root ../../t/suite;
}
--- request
GET /test
--- more_headers
Accept-Encoding: zstd
--- response_headers
Content-Encoding: zstd
--- error_code: 200
--- no_error_log
[error]
Loading