Skip to content

[Bug]: SFTPReadStream returns no data after seeking back from EOF (movie previews fail on SFTP storage) #66001

Closed

Description

@tcarrondo

⚠️ This issue respects the following points: ⚠️

  • This is not a troubleshooting question, general support matter, or webserver/proxy problem, but likely a bug.
  • This issue is not already reported on Github OR solved at the Community Help Forum (I've searched!).
  • I'm using a maintained major version of Nextcloud Server and tested against the latest patch level.
  • I agree to follow Nextcloud's Code of Conduct.
  • I've tried my best to provide clear reproduction steps that someone unfamiliar with this bug could use to reproduce it.

Bug description

SFTPReadStream never clears its $eof flag when seeking. Once a read has reached the end of the remote file, every later fseek() reports success and ftell() returns the new position, but all reads return an empty string.

The most visible effect is the movie preview on SFTP external storage. OC\Preview\Movie::getSparseFile() walks the MP4 atoms with stream_get_contents($content, …, $offset) to find the moov atom. When moov is at the end of the file (typical for phone recordings), that walk reads up to EOF. The following fseek($content, 0) + stream_copy_to_stream() then copies 0 bytes, so ffmpeg gets a sparse file that is all zeros and fails ("Invalid data found when processing input"). The log shows Movie preview generation process failed, file=… again on every preview request.

seekTo() resets the positions and the buffer, but not $this->eof:

https://github.com/nextcloud/server/blob/master/apps/files_external/lib/Lib/Storage/SFTPReadStream.php

Steps to reproduce

  1. Mount an SFTP external storage.
  2. Upload an MP4 larger than 10 MB with the moov atom at the end (e.g. a phone video, or ffmpeg -i in.mp4 -c copy -movflags -faststart out.mp4).
  3. Open the folder so a preview is requested, or call \OC\Preview\Movie::getThumbnail() on the file.
  4. The preview fails, and Movie preview generation process failed is logged.

Minimal check of the stream itself (in a script run as the web user):

$c = $node->fopen('r');                        // file on the SFTP storage
stream_get_contents($c, 8, $node->getSize() - 8); // read up to EOF
fseek($c, 0);
var_dump(feof($c), strlen(fread($c, 8192)));     // bool(true), int(0)

Expected behavior

After a seek, reads return the data at the new position, and the preview of the video is generated.

With a fresh stream for the copy, the same sparse file gives a valid image, and with $this->eof = false; in seekTo() the stream reads again after the seek.

Nextcloud Server version

35

Operating system

Other

PHP engine version

PHP 8.5

Web server

Nginx

Database engine version

MariaDB

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

None

Are you using the Nextcloud Server Encryption module?

Encryption is Disabled

What user-backends are you using?

  • Default user-backend (database)

Additional info

Seen on 35.0.2 (official Docker image, fpm), and the code on master is the same. I will open a PR with the one-line fix and a unit test for SFTPReadStream.

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

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions