Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP 7 SSH2.SFTP stat() bug work around

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 )

like image 801
mwex501 Avatar asked Dec 01 '16 22:12

mwex501


People also ask

What does the method ssh2_sftp_* () return?

This method returns an SSH2 SFTP resource for use with all other ssh2_sftp_* () methods and the ssh2.sftp:// fopen wrapper, or false on failure. throw new Exception("Could not connect to $host on port $port."); throw new Exception("Could not authenticate with username $username " . "and password $password.");

How to add SSH2 extension in PHP7?

2- Download the php7 branch source of php ssh2 from 3- unzip the zip file. unzip pecl-networking-ssh2-php7.zip 5- Prepare build environment for this extension using phpize. It will create all required file and folder structure and configure file for our extension sudo make install. Hope this will solve your problem.

What is the best way to speed up SFTP uploads with libssh2?

If you set the chunk size to for example 1Mb, libssh2 will send that chunk in multiple packets of 32K and then wait for a response, making the upload much faster. throw new Exception("Could not initialize SFTP subsystem.");

How to install php-ssh2 on Linux?

1- Install Linux package lib-ssh2 on your machine which is the only dependency for php-ssh2. Download the source from lib ssh2 official site and install . 3- unzip the zip file. 5- Prepare build environment for this extension using phpize.


1 Answers

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.

like image 118
neubert Avatar answered Jan 01 '23 02:01

neubert