diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 9a89f5ef..aadf6897 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -2148,11 +2148,12 @@ webdav_if_match_or_unmodified_since (connection * const con, struct stat *st) static void -webdav_response_etag (connection * const con, struct stat *st) +webdav_response_etag (server *srv, connection * const con, struct stat *st) { if (0 != con->etag_flags) { buffer *etagb = con->physical.etag; etag_create(etagb, st, con->etag_flags); + stat_cache_update_entry(srv,CONST_BUF_LEN(con->physical.path),st,etagb); etag_mutate(etagb, etagb); http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), @@ -2184,10 +2185,12 @@ webdav_parse_Depth (connection * const con) static int webdav_unlinkat (const plugin_config * const pconf, const buffer * const uri, - const int dfd, const char * const d_name) + const int dfd, const char * const d_name, size_t len) { - if (0 == unlinkat(dfd, d_name, 0)) + if (0 == unlinkat(dfd, d_name, 0)) { + stat_cache_delete_entry(pconf->srv, d_name, len); return webdav_prop_delete_uri(pconf, uri); + } switch(errno) { case EACCES: case EPERM: return 403; /* Forbidden */ @@ -2201,8 +2204,10 @@ static int webdav_delete_file (const plugin_config * const pconf, const physical_st * const dst) { - if (0 == unlink(dst->path->ptr)) + if (0 == unlink(dst->path->ptr)) { + stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(dst->path)); return webdav_prop_delete_uri(pconf, dst->rel_path); + } switch(errno) { case EACCES: case EPERM: return 403; /* Forbidden */ @@ -2265,7 +2270,8 @@ webdav_delete_dir (const plugin_config * const pconf, multi_status |= webdav_delete_dir(pconf, dst, b, flags); } else { - int status = webdav_unlinkat(pconf, dst->rel_path, dfd, de->d_name); + int status = + webdav_unlinkat(pconf, dst->rel_path, dfd, de->d_name, len); if (0 != status) { webdav_xml_response_status(b, dst->rel_path, status); multi_status = 1; @@ -2398,7 +2404,12 @@ webdav_copytmp_rename (const plugin_config * const pconf, AT_FDCWD, dst->path->ptr, overwrite ? 0 : RENAME_NOREPLACE)) #endif + { + /* unconditional stat cache deletion + * (not worth extra syscall/race to detect overwritten or not) */ + stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(dst->path)); return 0; + } else { const int errnum = errno; unlink(tmpb->ptr); @@ -2447,6 +2458,10 @@ webdav_copymove_file (const plugin_config * const pconf, * though unlikely if locks are used since locks have not yet been * released. */ if (overwrite) unlink(src->path->ptr); + /* unconditional stat cache deletion + * (not worth extra syscall/race to detect overwritten or not) */ + stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(dst->path)); + stat_cache_delete_entry(pconf->srv, CONST_BUF_LEN(src->path)); webdav_prop_move_uri(pconf, src->rel_path, dst->rel_path); return 0; } @@ -3976,6 +3991,8 @@ mod_webdav_delete (connection * const con, const plugin_config * const pconf) } buffer_free(ms); + /* invalidate stat cache of src if DELETE, whether or not successful */ + stat_cache_delete_dir(pconf->srv, CONST_BUF_LEN(con->physical.path)); } else if (con->physical.path->ptr[con->physical.path->used - 2] == '/') http_status_set_error(con, 403); @@ -4120,7 +4137,7 @@ mod_webdav_put_0 (connection * const con, const plugin_config * const pconf) if (0 != con->etag_flags) { /*(skip sending etag if fstat() error; not expected)*/ struct stat st; - if (0 == fstat(fd, &st)) webdav_response_etag(con, &st); + if (0 == fstat(fd, &st)) webdav_response_etag(pconf->srv, con, &st); } close(fd); http_status_set_fin(con, 201); /* Created */ @@ -4236,6 +4253,7 @@ mod_webdav_put_prep (connection * const con, const plugin_config * const pconf) #if (defined(__linux__) || defined(__CYGWIN__)) && defined(O_TMPFILE) static int mod_webdav_put_linkat_rename (connection * const con, + const plugin_config * const pconf, const char * const pathtemp) { chunkqueue * const cq = con->request_content_queue; @@ -4269,7 +4287,8 @@ mod_webdav_put_linkat_rename (connection * const con, if (0 != con->etag_flags && http_status_get(con) < 300) { /*(201, 204)*/ /*(skip sending etag if fstat() error; not expected)*/ - if (0 == fstat(c->file.fd, &st)) webdav_response_etag(con, &st); + if (0 == fstat(c->file.fd, &st)) + webdav_response_etag(pconf->srv, con, &st); } chunkqueue_mark_written(cq, c->file.length); @@ -4284,7 +4303,9 @@ mod_webdav_put_linkat_rename (connection * const con, __attribute_cold__ static handler_t -mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, const buffer * const h) +mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, + const plugin_config *pconf, + const buffer * const h) { /* historical code performed very limited range parse (repeated here) */ /* we only support - ... */ @@ -4333,7 +4354,7 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con, con if (!http_status_is_set(con)) { http_status_set_fin(con, 204); /* No Content */ - if (0 != con->etag_flags) webdav_response_etag(con, &st); + if (0 != con->etag_flags) webdav_response_etag(pconf->srv, con, &st); } return HANDLER_FINISHED; @@ -4366,7 +4387,8 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Content-Range")); if (NULL != h) - return mod_webdav_put_deprecated_unsafe_partial_put_compat(con, h); + return + mod_webdav_put_deprecated_unsafe_partial_put_compat(con,pconf,h); } /* construct temporary filename in same directory as target @@ -4415,7 +4437,7 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) if (!mod_webdav_write_single_file_chunk(con, cq)) return HANDLER_FINISHED; } - if (mod_webdav_put_linkat_rename(con, pathtemp)) + if (mod_webdav_put_linkat_rename(con, pconf, pathtemp)) return HANDLER_FINISHED; /* attempt traditional copy (below) if linkat() failed for any reason */ } @@ -4451,7 +4473,7 @@ mod_webdav_put (connection * const con, const plugin_config * const pconf) ? 204 /* No Content */ : 201); /* Created */ if (0 == rename(pathtemp, con->physical.path->ptr)) { - if (0 != con->etag_flags) webdav_response_etag(con, &st); + if (0 != con->etag_flags) webdav_response_etag(pconf->srv,con,&st); } else { if (errno == EISDIR) @@ -4745,6 +4767,9 @@ mod_webdav_copymove_b (connection * const con, const plugin_config * const pconf webdav_xml_doc_multistatus(con, pconf, ms); /* 207 Multi-status */ } buffer_free(ms); + /* invalidate stat cache of src if MOVE, whether or not successful */ + if (con->request.http_method == HTTP_METHOD_MOVE) + stat_cache_delete_dir(pconf->srv,CONST_BUF_LEN(con->physical.path)); return HANDLER_FINISHED; } else if (con->physical.path->ptr[con->physical.path->used - 2] == '/') { @@ -5307,7 +5332,7 @@ mod_webdav_lock (connection * const con, const plugin_config * const pconf) lockstr, sizeof(lockstr)-1); webdav_xml_doc_lock_acquired(con, pconf, &lockdata); if (0 != con->etag_flags && !S_ISDIR(st.st_mode)) - webdav_response_etag(con, &st); + webdav_response_etag(pconf->srv, con, &st); http_status_set_fin(con, created ? 201 : 200); /* Created | OK */ } else /*(database error obtaining lock)*/