Skip to content

[Bug]: oc_share table has duplicate rows for the same share #53970

Description

@Antreesy

⚠️ This issue respects the following points: ⚠️

Bug description

There might be a race condition in ShareProvider, where several requests in parallel check if share exists in DB, and create it otherwise. In this case, there could be several new entries created, each with unique id, but all pointing to the same share.
If user tries to modify/leave share, it will modify the first row only, keeping the second intact. That way, you can never get rid of it.

Steps to reproduce

Don't have clear steps, but example behaviour sounds logical:

  1. Sharing a file in Talk creates a parent entry with share_type 10 and file_target /{TALK_PLACEHOLDER}/file.md
  2. User access conversation with shared file as last message, filesystem is mounted from conversations request and chat messages request
  3. This creates two entries in DB with share_type 11 and file_target /Talk/file.md (best reproducible when PHP debugger was enabled, but also occurs on prod/daily instances)
    3.1. Worse case if user at some point changed the attachments folder, so initial entry is share_type 11 and file_target /Talk/file.md, and duplicates are share_type 11 and file_target /SomeOtherPath/file.md. Modifications will touch only first occurence of /SomeOtherPath/file.md

Expected behavior

Some sort of transactional lock (to write in DB only once) in place

Nextcloud Server version

master

Details

Operating system

None

PHP engine version

None

Web server

None

Database engine version

None

Is this bug present after an update or on a fresh install?

None

Are you using the Nextcloud Server Encryption module?

None

What user-backends are you using?

  • Default user-backend (database)
  • LDAP/ Active Directory
  • SSO - SAML
  • Other

Configuration report

List of activated Apps

Nextcloud Signing status

Nextcloud Logs

Additional info

No response

Activity

  1. added this to the Nextcloud 32 milestone on Jul 16, 2025
  2. Antreesy commented on Jul 16, 2025

    @Antreesy
    ContributorAuthor

    cc @nickvergessen @miaulalala @provokateurin to consider as part of planned performance improvements

  3. nickvergessen commented on Jul 22, 2025

    @nickvergessen
    Member

    Few notes:

    1. The query at
      ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY)))
      is not hitting an index:
    MariaDB [oc]> EXPLAIN SELECT id FROM oc_share WHERE share_type = 2 AND share_with = '…' AND parent = 42 AND item_type IN ('file', 'folder');
    +------+-------------+----------+------+---------------------------------------------------------------------+--------------+---------+-------+------+-------------+
    | id   | select_type | table    | type | possible_keys                                                       | key          | key_len | ref   | rows | Extra       |
    +------+-------------+----------+------+---------------------------------------------------------------------+--------------+---------+-------+------+-------------+
    |    1 | SIMPLE      | oc_share | ref  | item_share_type_index,share_with_index,parent_index,share_type_with | parent_index | 9       | const | 1    | Using where |
    +------+-------------+----------+------+---------------------------------------------------------------------+--------------+---------+-------+------+-------------+
    1 row in set (0.000 sec)
    
    1. The formatShareAttributes between the read and write is creating more chances for concurrency, could be moved before the SELECT to reduce chance for concurrency
    2. There is no lock/transaction around this
  4. nickvergessen commented on Jul 22, 2025

    @nickvergessen
    Member

    But even without the query is still not using an index on prod

  5. bobobo-git commented on Mar 3, 2026

    @bobobo-git

    maybe related.

    if you set your nextcloud to "allow users to set custom share link tokens" you can set a custom share link token in the sharing context of the object. BUT it isn't checked if the custom token ist already set for another object. a unique constraint could be enough here. though, i don't know about other dependencies .. so this may not be a correct solution .

    and it would be nice if a real deletion of an object could trigger a deletion of the share entry too.

  6. nickvergessen commented on Mar 3, 2026

    @nickvergessen
    Member

    a unique constraint could be enough here.

    That would only work, when all shares would have a token. But user, group and many other shares dont

  7. mosi-kha commented on May 22, 2026

    @mosi-kha
    Contributor

    @nickvergessen @Antreesy
    I traced this and found the duplicate USERROOM rows originate from SharedMount::verifyMountPoint:

    if ($newMountPoint !== $share->getTarget()) {
    $this->updateFileTarget($newMountPoint, $share);
    }

    When a share provider sets a non-final target (e.g. a placeholder path that gets rewritten per-user via VerifyMountPointEvent), $newMountPoint !== $share->getTarget() evaluates true on every mount build, so updateFileTarget() is invoked → IManager::moveShare() → RoomShareProvider::move().

    move() does a check-then-insert on oc_share without a unique constraint or ON CONFLICT handling. Under concurrent PROPFINDs (or PROPFIND + a parallel notification/sync poll triggered by SetupManager invalidating the mount cache on ShareCreatedEvent), both calls SELECT empty before either commits, then both INSERT — producing duplicate USERROOM rows with the same parent, share_with, file_target, and stime.

    Reproducible by sharing a file into a Talk room (TYPE_ROOM share) and triggering two PROPFINDs on the recipient's mount near-simultaneously.

    Possible fixes: unique index on (share_type, parent, share_with), or INSERT … ON CONFLICT DO NOTHING in move() / deleteFromSelf().

    I can send a PR for possible fixes.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions