Repository navigation
Description
Renaming a locked file is always refused, and the failure is reported as a permission error
apps/files_lock/lib/Storage/LockWrapper.php gates rename() like this (shipped with Nextcloud 35.0.2, files_lock 35.0.1):
return $this->checkPermissions($sourceParent, Constants::PERMISSION_DELETE)
&& $this->checkPermissions($source, Constants::PERMISSION_UPDATE & Constants::PERMISSION_READ)
&& $this->checkPermissions($target, $permissions)
&& parent::rename($source, $target);PERMISSION_UPDATE & PERMISSION_READ is 2 & 1 = 0 — almost certainly meant as |. That matters because
checkPermissions() treats the mask as a permission level, not a bit set:
if (!$this->isPathLocked($ownerId, $path, $viewerId, $lock)) {
return true; // unlocked: the mask is never looked at
}
switch ($permissions) {
case Constants::PERMISSION_READ: return true; // 1
case Constants::PERMISSION_DELETE: // 8
case Constants::PERMISSION_UPDATE: throw new ManuallyLockedException(…); // 2
default: return false; // ← 0 lands here
}So for an unlocked file the 0 is harmless (early return true), and for a locked file the same 0 returns
false — i.e. any lock makes a rename impossible, including a rename of a file nobody else is editing, and the
check never even reaches the PERMISSION_DELETE/PERMISSION_UPDATE branches that exist to throw the honest
ManuallyLockedException. Expected behaviour would be: a locked file refuses the rename with 423 and the lock owner,
not false.
Why it is easy to hit
This is the visible half of the stale-lock problem (nextcloud/text#9042). Steps, on a default setup:
- open a
.mdnote in the Nextcloud Notes app → it uses Direct Editing → Text creates afiles_lockrow
(type = 1, ownerText); - close the editor. The app calls
POST /apps/text/session/<id>/close, which requires a logged-in user session and
answers 403 to a token-driven client, so the row survives (measured: 3/3, and the same client gets 200 on
sync/push/save); - now try to rename the note — in the same app, with an empty target directory and full permissions
(permissions = 27on the file,31on the folder):
16:19:20 PUT /apps/notes/api/v1/notes/958914 200 ← edit is fine
16:19:38 PUT /apps/notes/api/v1/notes/958914 500 ← rename
OCP\Files\NotPermittedException
"Could not move /<user>/files/Notes/Neue Notiz.md to /<user>/files/Notes/tasks 2026.md"
Diagnostics in nextcloud.log for the same operation, at level info, name the lock properly:
"files/Notes/Neue Notiz.md" is locked, existing lock on file: files_lock/393bd18e-…
so the lock is known — the rename path just cannot say so.
Expected
PERMISSION_UPDATE | PERMISSION_READ(or simplyPERMISSION_UPDATE, since the source must be deletable for a move)
in that expression;- and, independent of the mask, a rename of a locked file should surface
423 Lockedwith the lock owner, the way
copy/copyFromStorage/file_put_contentsalready do throughManuallyLockedException.
Environment
Nextcloud 35.0.2 (docker, Apache), MySQL, files_lock 35.0.1, clients: Nextcloud Notes for Android 35.0.0 and the web UI.
Independent of the storage backend and of occ files:scan; the cache rows were verified healthy
(permissions 27/31) while the rename still failed.
#AI-ASSISTED
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
PeterPablo commentedon Nov 14, 2026
Wrong tracker —
files_lockhas its own repository; the report continues as nextcloud/files_lock#1437. Closing this one to keep a single thread.#AI-ASSISTED