Ask the file, not its folder, why the write was refused

The 403 probe read `oc:permissions` off the parent collection and warned
when `W` was missing. But Nextcloud reports `W` on files and `CK` on
collections, so a directory legitimately lacks `W`: the warning fired on
a healthy share and pointed at a mount that was fine.

Probe the file itself. Its permissions answer the question that matters,
and the status distinguishes the two cases the parent could not: a 404
means the sidecar does not exist and the refusal was about creating it,
while a 200 without `W` means it exists and cannot be updated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 20:38:48 +02:00
co-authored by Claude Opus 5
parent 6621c11ad6
commit 18f20170b3
+12 -9
View File
@@ -373,16 +373,18 @@ impl RemoteBackend for NextcloudBackend {
// make anything worse and it is the difference between a // make anything worse and it is the difference between a
// server-side fix and a client-side one. // server-side fix and a client-side one.
if status == reqwest::StatusCode::FORBIDDEN { if status == reqwest::StatusCode::FORBIDDEN {
let parent = path // The *file*, not the folder. `W` is reported on files and
.as_str() // `CK` on collections, so a directory legitimately lacks `W`
.rsplit_once('/') // and reading that as read-only would send someone to change a
.map(|(head, _)| head) // mount that is fine. If the file exists without `W` the
.unwrap_or(""); // refusal is an update it will not allow; a 404 here means it
// does not exist and the refusal was about creating it.
let target = path.as_str().to_string();
let probe = self let probe = self
.client .client
.request( .request(
reqwest::Method::from_bytes(b"PROPFIND").expect("valid method"), reqwest::Method::from_bytes(b"PROPFIND").expect("valid method"),
self.url_for(&RemotePath::new(parent)), self.url_for(path),
) )
.basic_auth(&self.login, Some(&self.password)) .basic_auth(&self.login, Some(&self.password))
.header("Depth", "0") .header("Depth", "0")
@@ -394,6 +396,7 @@ impl RemoteBackend for NextcloudBackend {
.await; .await;
match probe { match probe {
Ok(r) => { Ok(r) => {
let status = r.status();
let text = r.text().await.unwrap_or_default(); let text = r.text().await.unwrap_or_default();
let perms = text let perms = text
.split("<oc:permissions>") .split("<oc:permissions>")
@@ -401,11 +404,11 @@ impl RemoteBackend for NextcloudBackend {
.and_then(|t| t.split('<').next()) .and_then(|t| t.split('<').next())
.unwrap_or("(not reported)"); .unwrap_or("(not reported)");
log::warn!( log::warn!(
" parent {parent} permissions: {perms} \ " target {target} -> {status} permissions: {perms} \
(W = write, C = create; absent means read-only)" (on a file W = update; a 404 means it does not exist yet)"
); );
} }
Err(e) => log::warn!(" could not read parent permissions: {e}"), Err(e) => log::warn!(" could not read {target} permissions: {e}"),
} }
} }