diff --git a/changes-entries/core-default-handler-stale-stat.txt b/changes-entries/core-default-handler-stale-stat.txt new file mode 100644 index 00000000000..959f2e225dc --- /dev/null +++ b/changes-entries/core-default-handler-stale-stat.txt @@ -0,0 +1,7 @@ + *) core: Refresh the cached file information from the open file + descriptor in the default handler, instead of keeping the result of + the directory walk's stat() on the pathname. A rename(2) racing + with the open() could otherwise leave Last-Modified, ETag, + Content-Length and the byte range actually sent describing a + different file than the one that was opened. PR 54962. + [Ståle Kristoffersen] diff --git a/server/core.c b/server/core.c index a6928ef916c..394524012e4 100644 --- a/server/core.c +++ b/server/core.c @@ -5248,6 +5248,17 @@ static int default_handler(request_rec *r) return HTTP_FORBIDDEN; } + /* r->finfo was populated by the directory walk's stat() on the + * pathname, which can race with a rename(2) replacing the file + * before it is opened here. Refresh it from the now-open file + * descriptor so that Last-Modified, ETag, Content-Length and the + * byte range actually sent all describe the same inode, instead + * of mixing stale directory-walk metadata with the content of + * whatever file the name happened to refer to by the time it + * was opened. + */ + apr_file_info_get(&r->finfo, APR_FINFO_MIN | APR_FINFO_IDENT, fd); + ap_update_mtime(r, r->finfo.mtime); ap_set_last_modified(r); ap_set_etag_fd(r, fd);