PHP 7 SSH2.SFTP stat() bug work around
Asked Answered
R

2

5

I have an app the uses an SFTP connection to download files. It was working correctly in PHP 5.6, not so much in PHP 7. The error I get is as follows:

PHP Warning: filesize(): stat failed for ssh2.sftp ...

My code is as follows:

 public function retrieveFiles($downloadTargetFolder,$remoteFolder = '.') {

            $fileCount = 0;

                echo "\nSftpFetcher retrieveFiles\n";

        $con = ssh2_connect($this->host,$this->port) or die("Couldn't connect\n");
        if($this->pubKeyFile){
                $isAuth = ssh2_auth_pubkey_file($con, $this->user, $this->pubKeyFile, $this->privKeyFile);
        } else {
                $isAuth = ssh2_auth_password($con,  $this->user,  $this->pass);
        };


        if ($isAuth) {

                $sftp = ssh2_sftp($con);
                $rd = "ssh2.sftp://{$sftp}{$remoteFolder}";

                if (!$dir = opendir($rd)) {
                        echo "\nCould not open the remote directory\n";
                } else {
                        $files = array();
                                while (false != ($file = readdir($dir))) {
                                    if ($file == "." || $file == "..")
                                        continue;
                                    $files[] = $file;
                                }

                        if (is_array($files)) {
                            foreach ($files as $remoteFile) {
                                                echo "\ncheck file: $remoteFile vs filter: " . $this->filter."\n";
                                if ($this->filter !== null && strpos($remoteFile,$this->filter) === false) {
                                    continue;
                                }
                                                echo "file matched\n";
                                $localFile = $downloadTargetFolder . DIRECTORY_SEPARATOR . basename($remoteFile);


                                //$result = ftp_get($con,$localFile,$remoteFile,FTP_BINARY);
                                $result = true;
                                // Remote stream
                                if (!$remoteStream = @fopen($rd."/".$remoteFile, 'r')) {
                                        echo "Unable to open the remote file $remoteFolder/$remoteFile\n";
                                        $return = false;
                                } else {
                                        // Local stream
                                        if (!$localStream = @fopen($localFile, 'w')) {
                                                echo "Unable to open the local file $localFile\n";
                                                $return = false;
                                        } else {
                                                // Write from our remote stream to our local stream

                                                $read = 0;
                                                $fileSize = filesize($rd."/".$remoteFile);
                                                while ($read < $fileSize && ($buffer = fread($remoteStream, $fileSize - $read))) {
                                                        $read += strlen($buffer);
                                                        if (fwrite($localStream, $buffer) === FALSE) {
                                                                echo "Unable to write the local file $localFile\n";
                                                                $return = false;
                                                                break;
                                                        }
                                                }


                                                echo "File retrieved";
                                                // Close
                                                fclose($localStream);
                                                fclose($remoteStream);

                                        }

                                }

                                if ($result) {
                                    $fileCount++;
                                }
                            }
                        }

                        ssh2_exec($con, 'exit');
                        unset($con);
                }

        } else {
                echo "Error authenticating the user ".$this->user."\n";
        }

            return $fileCount;

    }
}

After some research I found there was an issue with stat():

http://dougal.gunters.org/blog/2016/01/18/wordpress-php7-and-updates-via-php-ssh2/ https://bugs.php.net/bug.php?id=71376

My question

Is there a workaround to allow me to download via SFTP given my current code or is there another library someone can recommend to use instead?

My PHP version: PHP 7.0.8-0ubuntu0.16.04.3 (cli) ( NTS )

Royce answered 1/12, 2016 at 22:25 Comment(0)
C
8

Quoting PHP ssh2.sftp opendir/readdir fix,

Instead of using "ssh2.sftp://$sftp" as a stream path, convert $sftp to an integer like so: "ssh2.sftp://" . intval($sftp) . "/". Then it will work just fine.

The reason for the change is as follows:

PHP 5.6.28 (and apparently 7.0.13) introduced a security fix to URL parsing, that caused the string interpolation of the $sftp resource handle to no-longer be recognized as a valid URL. In turn, that causes opendir(), readdir(), etc. to fail when you use an $sftp resource in the path string, after an upgrade to one of those PHP versions.

As for other libraries... only other library I'm aware of is phpseclib, which has an emulator of sorts for libssh2:

https://github.com/phpseclib/libssh2-compatibility-layer

That "emulator" could certainly be improved upon tho. Like a composer.json file ought to be added, etc.

Cobaltous answered 3/12, 2016 at 16:16 Comment(2)
Just ran into this and your answer saved the day. Thanks!Celiotomy
Thank you :) bizarrely had this issue after an os update, Php 5.6, intval did the trick. Now I only spent one hour on the problem as opposed to many moreCounterpoise
P
0

I had the same issue with php 8.0.

Try putting the filesize command before the fopens.

Plaudit answered 3/11, 2022 at 15:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.