phpseclib / phpseclib

PHP Secure Communications Library
http://phpseclib.com/
MIT License
5.35k stars 887 forks source link

SFTP rename doesn't work when path has space #1391

Open beargroup-jay opened 5 years ago

beargroup-jay commented 5 years ago

Reading and writing files to an SFTP remote path with a space works fine, but renaming a file via SFTP::rename('/files/test.xml', '/files/test spaces/test.xml') does not work.

I'm not familiar with the low level sftp protocol, so have not been able to get into the nitty gritty of the packed data and raw packet data that's being sent, but it is easily reproducible for me using phpseclib 2.0.11.

SFTP php

terrafrost commented 5 years ago

It works fine for me:

$sftp->chdir('test');
$sftp->put('test.xml', 'zzzz');
$sftp->rename('/home/vagrant/test/test.xml', '/home/vagrant/test/sub dir/test.xml');

print_r($sftp->nlist('sub dir'));

I guess my two thoughts are... maybe look at the SFTP errors. eg.

print_r($sftp->getSFTPErrors());

Failing that maybe get the SFTP logs and then post them here. You can get them by doing define('NET_SFTP_LOGGING', 2); at the top and then echo $sftp->getSFTPLog(); after you attempt the rename.

beargroup-jay commented 5 years ago

Thanks for the pointers, also fwiw the same call with a path that doesn't have spaces succeeds for me and I can't see anything obvious in the error messages. The main difference between what you ran and mine is you use an absolute path, but since it works for me with relative paths and no spaces I don't think that would be the issue?

Additional context that I am not sure is relevant, but this library is being included by Magento 2 and used in a module, the point where the error happens seems pretty clear cut use of rename so again I don't suspect that comes into play, but am not seeing anything obvious for why it wouldn't work. This is php7.1 also, the composer.lock for the library is:

        {
            "name": "phpseclib/phpseclib",
            "version": "2.0.11",
            "source": {
                "type": "git",
                "url": "https://github.com/phpseclib/phpseclib.git",
                "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/7053f06f91b3de78e143d430e55a8f7889efc08b",
                "reference": "7053f06f91b3de78e143d430e55a8f7889efc08b",
                "shasum": ""
            },

For the logs, $sftp->getSFTPErrors() is an array:

[0] => NET_SFTP_STATUS_EOF: End of file
[1] => NET_SFTP_STATUS_FAILURE: Failure

and the snipped log:

-> NET_SFTP_INIT (0.0006s)
00000000  00:00:00:03                                      ....

<- NET_SFTP_VERSION (0.0945s)
00000000  00:00:00:03:00:00:00:18:70:6f:73:69:78:2d:72:65  ........posix-re
00000010  6e:61:6d:65:40:6f:70:65:6e:73:73:68:2e:63:6f:6d  name@openssh.com
00000020  00:00:00:01:31:00:00:00:13:73:74:61:74:76:66:73  ....1....statvfs
00000030  40:6f:70:65:6e:73:73:68:2e:63:6f:6d:00:00:00:01  @openssh.com....
00000040  32:00:00:00:14:66:73:74:61:74:76:66:73:40:6f:70  2....fstatvfs@op
00000050  65:6e:73:73:68:2e:63:6f:6d:00:00:00:01:32:00:00  enssh.com....2..
00000060  00:14:68:61:72:64:6c:69:6e:6b:40:6f:70:65:6e:73  ..hardlink@opens
00000070  73:68:2e:63:6f:6d:00:00:00:01:31:00:00:00:11:66  sh.com....1....f
00000080  73:79:6e:63:40:6f:70:65:6e:73:73:68:2e:63:6f:6d  sync@openssh.com
00000090  00:00:00:01:31                                   ....1

-> NET_SFTP_REALPATH (0.0007s)
00000000  00:00:00:01:2e                                   .....

<- NET_SFTP_NAME (0.0933s)
00000000  00:00:00:01:00:00:00:01:2f:00:00:00:01:2f:00:00  ......../..../..
00000010  00:00                                            ..

-> NET_SFTP_OPENDIR (0.0007s)
00000000  00:00:00:06:2f:66:69:6c:65:73                    ..../files

<- NET_SFTP_HANDLE (0.098s)
00000000  00:00:00:04:00:00:00:00                          ........

-> NET_SFTP_CLOSE (0.0011s)
00000000  00:00:00:04:00:00:00:00                          ........

<- NET_SFTP_STATUS (0.105s)
00000000  00:00:00:00:00:00:00:07:53:75:63:63:65:73:73:00  ........Success.
00000010  00:00:00                                         ...

-> NET_SFTP_OPENDIR (0.0009s)
00000000  00:00:00:06:2f:66:69:6c:65:73                    ..../files

<- NET_SFTP_HANDLE (0.0988s)
00000000  00:00:00:04:00:00:00:00                          ........

-> NET_SFTP_READDIR (0.0011s)
00000000  00:00:00:04:00:00:00:00                          ........

<- NET_SFTP_NAME (0.1902s)
00000000  00:00:00:64:00:00:00:01:2e:00:00:00:39:64:72:77  ...d........9drw
00000010  78:72:77:78:72:2d:78:20:20:20:20:34:20:31:30:30  xrwxr-x    4 100

<snip a bunch of file names>

00004230  00:00:03:ee:00:00:81:a4:56:f8:a7:27:56:f8:a7:28  ........V..'V..(

-> NET_SFTP_READDIR (0.0008s)
00000000  00:00:00:04:00:00:00:00                          ........

<- NET_SFTP_NAME (0.1055s)
00000000  00:00:00:64:00:00:00:30:44:69:61:6d:6f:6e:64:62  ...d...0Diamondb

<snip a bunch more names, this happens until it has listed all the files in the dir>

000034a0  00:0f:00:00:00:00:00:02:b5:b7:00:00:03:eb:00:00  ................
000034b0  03:eb:00:00:81:a4:5a:b1:84:9a:5a:b1:84:9a        ......Z...Z...

-> NET_SFTP_READDIR (0.0008s)
00000000  00:00:00:04:00:00:00:00                          ........

<- NET_SFTP_STATUS (0.0935s)
00000000  00:00:00:01:00:00:00:0b:45:6e:64:20:6f:66:20:66  ........End of f
00000010  69:6c:65:00:00:00:00                             ile....

-> NET_SFTP_CLOSE (0.0008s)
00000000  00:00:00:04:00:00:00:00                          ........

<- NET_SFTP_STATUS (0.0958s)
00000000  00:00:00:00:00:00:00:07:53:75:63:63:65:73:73:00  ........Success.
00000010  00:00:00                                         ...

-> NET_SFTP_OPEN (0.0007s)
00000000  00:00:00:18:2f:66:69:6c:65:73:2f:74:65:73:74:5f  ..../files/test_
00000010  70:72:6f:64:75:63:74:73:2e:78:6d:6c:00:00:00:01  products.xml....
00000020  00:00:00:00                                      ....

<- NET_SFTP_HANDLE (0.093s)
00000000  00:00:00:04:00:00:00:00                          ........

-> NET_SFTP_READ (0.0008s)
00000000  00:00:00:04:00:00:00:00:00:00:00:00:00:00:00:00  ................
00000010  00:00:80:00                                      ....

<snip logs NET_SFTP_READ ~32 times before>

<- NET_SFTP_DATA (0.0656s)
00000000  00:00:03:8b:3c:3f:78:6d:6c:20:76:65:72:73:69:6f  .....?xml versio
00000010  6e:3d:22:31:2e:30:22:20:65:6e:63:6f:64:69:6e:67  n="1.0" encoding
00000020  3d:22:55:54:46:2d:38:22:3f:3e:0a:3c:49:74:65:6d  ="UTF-8"?>..Item

<snip rest of xml file>

00000380  74:65:6d:3e:0a:3c:2f:49:74:65:6d:73:3e:0a:0a     tem>../Items>..

<- NET_SFTP_STATUS (0.0002s)
00000000  00:00:00:01:00:00:00:0b:45:6e:64:20:6f:66:20:66  ........End of f
00000010  69:6c:65:00:00:00:00                             ile....

<snip ~32 NET_SFTP_STATUS>

<- NET_SFTP_STATUS (0.0014s)
00000000  00:00:00:01:00:00:00:0b:45:6e:64:20:6f:66:20:66  ........End of f
00000010  69:6c:65:00:00:00:00                             ile....

-> NET_SFTP_CLOSE (0.0007s)
00000000  00:00:00:04:00:00:00:00                          ........

<- NET_SFTP_STATUS (0.0939s)
00000000  00:00:00:00:00:00:00:07:53:75:63:63:65:73:73:00  ........Success.
00000010  00:00:00                                         ...

-> NET_SFTP_RENAME (0.0021s)
00000000  00:00:00:18:2f:66:69:6c:65:73:2f:74:65:73:74:5f  ..../files/test_
00000010  70:72:6f:64:75:63:74:73:2e:78:6d:6c:00:00:00:24  products.xml...$
00000020  2f:66:69:6c:65:73:2f:74:65:73:74:20:73:70:61:63  /files/test spac
00000030  65:73:2f:74:65:73:74:5f:70:72:6f:64:75:63:74:73  es/test_products
00000040  2e:78:6d:6c                                      .xml

<- NET_SFTP_STATUS (0.0034s)
00000000  00:00:00:04:00:00:00:07:46:61:69:6c:75:72:65:00  ........Failure.
00000010  00:00:00                                         ...
terrafrost commented 5 years ago

The main difference between what you ran and mine is you use an absolute path, but since it works for me with relative paths and no spaces I don't think that would be the issue?

Can you post the SFTP logs of it with relative paths? phpseclib passes the absolute path even when the relative path is used. It does this because SFTP doesn't really have a notion of chdir and phpseclib just emulates it under the hood. So if you're trying to use a relative path that's relative to anything outside of your home directory it's not going to work.

Your SFTP logs:

-> NET_SFTP_RENAME (0.0021s)
00000000  00:00:00:18:2f:66:69:6c:65:73:2f:74:65:73:74:5f  ..../files/test_
00000010  70:72:6f:64:75:63:74:73:2e:78:6d:6c:00:00:00:24  products.xml...$
00000020  2f:66:69:6c:65:73:2f:74:65:73:74:20:73:70:61:63  /files/test spac
00000030  65:73:2f:74:65:73:74:5f:70:72:6f:64:75:63:74:73  es/test_products
00000040  2e:78:6d:6c                                      .xml

<- NET_SFTP_STATUS (0.0034s)
00000000  00:00:00:04:00:00:00:07:46:61:69:6c:75:72:65:00  ........Failure.
00000010  00:00:00                                         ...

My SFTP logs:

-> NET_SFTP_RENAME (0s)
00000000  00:00:00:1b:2f:68:6f:6d:65:2f:76:61:67:72:61:6e  ..../home/vagran
00000010  74:2f:74:65:73:74:2f:74:65:73:74:2e:78:6d:6c:00  t/test/test.xml.
00000020  00:00:23:2f:68:6f:6d:65:2f:76:61:67:72:61:6e:74  ..#/home/vagrant
00000030  2f:74:65:73:74:2f:73:75:62:20:64:69:72:2f:74:65  /test/sub dir/te
00000040  73:74:2e:78:6d:6c                                st.xml

<- NET_SFTP_STATUS (0.0003s)
00000000  00:00:00:00:00:00:00:07:53:75:63:63:65:73:73:00  ........Success.
00000010  00:00:00                                         ...

Yours returns a failure and mine returns a success. Both use absolute paths.

The first four bytes before each path represent the length of the string.

So yah - I think what I'm going to need to see is how this compares to your relative path thing. I'll see what the differences are and see if they can be fixed!