Skip to content

files_lock: renaming a locked file is always refused (PERMISSION_UPDATE & PERMISSION_READ = 0) and reported as NotPermittedException #66620

Closed

Description

@PeterPablo

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:

  1. open a .md note in the Nextcloud Notes app → it uses Direct Editing → Text creates a files_lock row
    (type = 1, owner Text);
  2. 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);
  3. now try to rename the note — in the same app, with an empty target directory and full permissions
    (permissions = 27 on the file, 31 on 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 simply PERMISSION_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 Locked with the lock owner, the way
    copy/copyFromStorage/file_put_contents already do through ManuallyLockedException.

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

Activity

  1. PeterPablo commented on Nov 14, 2026

    @PeterPablo
    Author

    Wrong tracker — files_lock has its own repository; the report continues as nextcloud/files_lock#1437. Closing this one to keep a single thread.

    #AI-ASSISTED

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions