nextcloud / nextcloud/server

Re-shared external storage (SMB) not always rescanned for recipients

Open
#63,253 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

0. Needs triage enhancement
Dominant language
PHP
Stars
36.9k
Forks
5.2k
Avg merge
2d 3h
Merged PRs (30d)
713

Description

Steps to reproduce
  1. User A adds an SMB external storage mount.
  2. User A shares a folder from that mount directly with User B.
  3. Something changes on the SMB server outside of Nextcloud.
  4. User A browses the folder → gets rescanned correctly.
  5. User B browses the re-share → does not get rescanned (stale listing).
Root cause

SharedStorage::getWatcher() decides between a real Watcher and a
NullWatcher purely from string metadata on the share's cache entry:

https://github.com/nextcloud/server/blob/master/apps/files_sharing/lib/SharedStorage.php#L453-L477

$node = $this->getShare()->getNodeCacheEntry();
if ($node instanceof CacheEntry) {
    $storageId = $node->getData()['storage_string_id'] ?? null;
    if ($storageId !== null && !(str_starts_with($storageId, 'home::') || str_starts_with($storageId, 'object::user'))) {
        // real watcher, rescans happen
    }
}
// falls through here -> NullWatcher, rescans never happen
$this->watcher = new NullWatcher();

getNodeCacheEntry() returns null, or a CacheEntry missing the
storage_string_id key, for several legitimate code paths:

  • DefaultShareProvider::getSharesBy() / getShareById() / getSharesByPath()
    never populate a node cache entry at all (no filecache/storages join).
  • Even DefaultShareProvider::_getSharedWith() (used for the normal
    getMountsForUser/getMountsForPath mount setup) can come back without
    storage_string_id if the source node wasn't in filecache yet at
    share-fetch time.

Whenever that happens, the code silently assumes "home storage" and installs
a NullWatcher, meaning the share never gets checked against the real
backend for the lifetime of that storage object/request.

This is the same fragile spot as #50235, which was patched defensively in
#50769 (?? null to avoid the crash) — that fixed the exception but left the
underlying "silently never rescan" behavior in place.

Suggested fix

Fall back to a live, deterministic check of the real underlying storage
($this->nonMaskedStorage->instanceOfStorage(IHomeStorage::class), after
$this->init()) when the cache-entry metadata is missing or incomplete,
instead of assuming home storage. This mirrors the existing fallback pattern
in SharedStorage::getSourceRootInfo(). A FailedStorage guard is needed so
a deleted owner / offline storage still safely yields NullWatcher.

I'm happy to submit a PR for this if the approach sounds right.

This issue has been created in cooperation with Claude Sonnet 5 but it has been reviewed and submitted by me - human.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in apps/files_sharing/lib/SharedStorage.php at getWatcher(), then compare its metadata path with the fallback pattern in getSourceRootInfo(). Trace initialization of nonMaskedStorage and the FailedStorage case, and verify the behavior when storage_string_id is absent. Done means a re-shared SMB folder is rescanned for recipients while deleted or unavailable owner storage still uses NullWatcher safely.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend, cloud
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.