From b126cf9c5a9dd82b50cb15134e151fa1286d9d07 Mon Sep 17 00:00:00 2001 From: Juan Castro Date: Wed, 30 Sep 2026 16:21:39 -0400 Subject: [PATCH] fix: carry the owner's path into WebDAV events and expand tilde before touch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebDAV emits the same GUI events the FS controllers do, and had the same confusion: the payload was masked for the actor and addressed to the owner. A recipient mounting a shared folder in Finder reproduced the disappearing-item symptom exactly. /touch prefixed `/` before expanding, so `~/AppData//f.txt` became a literal `/~/...` that no longer looked like a tilde path — the shape puter.js sends for every app-relative write answered 404. --- src/backend/controllers/fs/LegacyFSController.ts | 5 +++-- .../controllers/fs/sharedFolderWrites.http.test.ts | 11 +++++++++++ src/backend/controllers/webdav/WebDAVController.ts | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/backend/controllers/fs/LegacyFSController.ts b/src/backend/controllers/fs/LegacyFSController.ts index 302925370..a27d813c6 100644 --- a/src/backend/controllers/fs/LegacyFSController.ts +++ b/src/backend/controllers/fs/LegacyFSController.ts @@ -1050,11 +1050,12 @@ export class LegacyFSController extends PuterController { legacyCode: 'bad_request', }); - const targetPath = await expandClientPath( + const expanded = await expandClientPath( this.stores.fsEntry, - rawPath.startsWith('/') ? rawPath : `/${rawPath}`, + rawPath, actor.user?.username, ); + const targetPath = expanded.startsWith('/') ? expanded : `/${expanded}`; const parentPath = pathPosix.dirname(targetPath); if (parentPath === '/') { throw new HttpError(400, 'Cannot touch in root', { diff --git a/src/backend/controllers/fs/sharedFolderWrites.http.test.ts b/src/backend/controllers/fs/sharedFolderWrites.http.test.ts index 7b9b8a46c..63b3bc0a9 100644 --- a/src/backend/controllers/fs/sharedFolderWrites.http.test.ts +++ b/src/backend/controllers/fs/sharedFolderWrites.http.test.ts @@ -138,6 +138,17 @@ describe('writes inside a folder shared with write', () => { expect(await ownerListing(s)).toContain('from-recipient.txt'); }, 180_000); + it('touch still expands a tilde path', async () => { + const s = await shareAFolder(); + + const touched = await post('/touch', s.owner.token, { + path: '~/SharedDir/from-tilde.txt', + }); + + expect(touched.status).toBe(200); + expect(await ownerListing(s)).toContain('from-tilde.txt'); + }, 180_000); + // Addressed to the owner, so it carries the path the owner can resolve. it('publishes the owner’s real path on events about their entries', async () => { const s = await shareAFolder(); diff --git a/src/backend/controllers/webdav/WebDAVController.ts b/src/backend/controllers/webdav/WebDAVController.ts index 63a81929c..bace61112 100644 --- a/src/backend/controllers/webdav/WebDAVController.ts +++ b/src/backend/controllers/webdav/WebDAVController.ts @@ -1174,7 +1174,9 @@ export class WebDAVController extends PuterController { void Promise.resolve() .then(async () => { const response = { - ...(await toLegacyEntry(this.clients.event, entry)), + ...(await toLegacyEntry(this.clients.event, entry, { + forOwner: true, + })), ...extra, from_new_service: true, };