fix(files): file purchased content into FileBrowser folders again
Every paid download logged "filing into filebrowser/Music/... failed (non-fatal): Permission denied". The purchase played in-app but never appeared in Files. FileBrowser's folders belong to its rootless container range (host uid 100000, mode 755). This service is host uid 1000, outside that range, so it can read them but not create files in them. New container::filebrowser::save_new_file: - Writes directly when the folder allows it. - Otherwise writes through `podman unshare`, where that uid range is ours: to a temp file, then chowned to the folder's owner, set to 0644, and hard-linked into place. FileBrowser never sees a partial file and an existing file is never replaced. A missing folder is created and given its parent's owner. No sudo. - Keeps the "name (2).ext" de-duplication the RPC did inline. Checked the unshare script on amishparadise in a scratch folder owned like FileBrowser's: new folder + file OK, owner/mode right, no clobber, no temp file left, and the service can read the result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -678,28 +678,12 @@ impl RpcHandler {
|
||||
.unwrap_or("download")
|
||||
.to_string();
|
||||
let dir = self.config.data_dir.join("filebrowser").join(folder);
|
||||
if let Err(e) = tokio::fs::create_dir_all(&dir).await {
|
||||
tracing::warn!("paid download: cannot create {}: {e}", dir.display());
|
||||
} else {
|
||||
// Don't clobber an existing file of the same name: "x.jpg"
|
||||
// → "x (2).jpg" etc.
|
||||
let mut target = dir.join(&base);
|
||||
let (stem, ext) = match base.rsplit_once('.') {
|
||||
Some((s, e)) if !s.is_empty() => (s.to_string(), format!(".{e}")),
|
||||
_ => (base.clone(), String::new()),
|
||||
};
|
||||
let mut n = 2;
|
||||
while target.exists() {
|
||||
target = dir.join(format!("{stem} ({n}){ext}"));
|
||||
n += 1;
|
||||
}
|
||||
match tokio::fs::write(&target, &bytes).await {
|
||||
Ok(()) => tracing::info!("paid download: filed into {}", target.display()),
|
||||
Err(e) => tracing::warn!(
|
||||
"paid download: filing into {} failed (non-fatal): {e}",
|
||||
target.display()
|
||||
),
|
||||
}
|
||||
match crate::container::filebrowser::save_new_file(&dir, &base, &bytes).await {
|
||||
Ok(path) => tracing::info!("paid download: filed into {}", path.display()),
|
||||
Err(e) => tracing::warn!(
|
||||
"paid download: filing into {} failed (non-fatal): {e:#}",
|
||||
dir.display()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user