Wednesday, January 9, 2013

Android: Adding scp/sftp support to dropbear and mounting with sshfs

I have recently received an android smartphone, and one of the first things I did with it was to root it :). This allows power-usres to get the most out of their hardware.

The next thing on my list was to set up a SSH server and to be able to transfer files between my Linux system and my phone (by the way, I'm running Android 4.1 and it seems USB mass storage support has been removed. MTP/PTP modes have either horrible transfer speed or are poorly supported in Ubuntu).

With the above in mind, the plan was to:
  • Enable tethering on the phone
  • Run a SSH server to support issuing remote commands and file transfer (FTP might have been an alternative, but I'm a SSH adept).
Browsing the market I found SSHDroid which does all that it advertised. Problem is - the free version conflicts with my add-blocking apps and requests that they are disabled to run.

For me, this is a big nuisance, so I kept looking. I found Dropbear SSH Server 2, which is completely free, but doesn't support scp/sftp.

So, I wanted scp/sftp support, so I started to work on a solution.

If, after starting Dropbear server you try to transfer a file via scp you get this error (pris is the name of my phone and is mapped to an IP address via the /etc/hosts file):
adrianp@frost:~$ scp test.log root@pris:/storage/sdcard0/
Welcome to DropBear SSH Server II!
root@pris's password:
sh: scp: not found
lost connection
then, you are in the same situation I was...

For scp/sftp to work, the process needs to have access to the scp/sftp-server binaries on your android system. But it seems dropbear doesn't come with those binaries. But searching around the system, the binaries are available in the SSHDroid package.

So, I was doing the following steps to make those binaries available to the whole system (needs a rooted system with busybox installed):

root@android:/data/local # scp
sh: scp: not found
127|root@android:/data/local # find / -name scp 2>/dev/null

1|root@android:/data/local # /data/data/
usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
           [-l limit] [-o ssh_option] [-P port] [-S program]
           [[user@]host1:]file1 [...] [[user@]host2:]file2

1|root@android:/data/local # ls -l /system/xbin/                              
-rwxr-xr-x root     shell       59760 2012-09-28 12:15 dexdump
-rwsr-sr-x root     root        91992 2012-12-21 10:02 su
root@android:/data/local # echo $PATH                                         
root@android:/data/local # mount -o remount,rw /system
root@android:/data/local # ln -s /data/data/ /system/xbin/scp

root@android:/data/local # ln -s /data/data/ /system/xbin/ssh
root@android:/data/local # ln -s /data/data/ /system/xbin/sftp-server
root@android:/data/local # mount -o remount,ro /system
At this point, you can use scp to transfer files from your computer to your Android device. However, you can't mount it with sshfs yet.

The problem when mounting it is that sshfs tries to use the sftp server, and by default it tries to call it from /usr/libexec/sftp-server. This path does not exist on your android device, and you will need to instruct sftp to use /system/xbin/sftp-server instead. You can do this with the following command:

sudo sshfs -o sftp_server=/system/xbin/sftp-server  root@pris:/storage /media/pris
Of course, in order to mount the device as a regular user and to be able to transfer files, you will need to prepare your mount point and your fstab entry:

adrianp@frost:~/temp$ sudo mkdir -p /media/pris
adrianp@frost:~/temp$ sudo chown root:fuse /media/pris
adrianp@frost:~/temp$ sudo chmod g+w /media/pris
adrianp@frost:~/temp$ cat /etc/fstab | grep pris
sshfs#root@pris:/storage /media/pris fuse user,fsname=sshfs#root@pris:/storage,noauto,sftp_server=/system/xbin/sftp-server 0 0
You will now be able to mount the device via the command line (or in Nautilus). It will ask for your ssh password, and then it will display the files as if they were local. In terms of performance, I get about 4.6MB/s reads and 4.1MB/s write speed (over USB).  Compared to ~240kB/s read/write in MTP mode, I would say I get quite a performance boost!

Remember, in order to follow the steps above you will need:
  • a rooted android device
  • busybox installed
  • SSHDroid installed (it will remain installed even if you don't start the ssh service. You can possibly uninstall it if you replace the "ln -s" commands with "cp" instead)
  • Dropbear II (and started)
  • either a wifi connection between your PC and android device, or USB tethering (it's what I'm using)


László said...

You're the man, thanks so much! I can finally scp back and forth and use sshfs with my phone!

Adrian Popa said...

Glad it helped.
I'm currently running a variant of the above instructions with the change that I moved sftp-server to /usr/libexec instead of /system/xbin. This allows me to skip specifying sftp-server in the mount command.

The problem is the system deletes /usr/libexec on reboot, so I need to recreate it...

George Profenza said...

This is great! I've just this now and it works like a charm.

Multumesc :)

Adrian Popa said...

I have mostly abandoned mounting over ssh and I'm currently using FTP for more performance. Over USB tethering I can now get about 10MB/s transfer rates (compared to 4.5MB/s with ssh).

I am using ES Explorer's FTP server (if you create a shortcut on the desktop you can enable it without turning on Wifi).

Lehin' said...

Great post. I had to add `,allow_other` option to fstab, and my `ssh` binary was in the `/data/dropbear` folder, but otherwise worked perfectly.