# Hardlink support in sshfs


[OpenSSH 5.7](http://www.openssh.com/) adds hardlink support to the sftp protocol:

> sftp(1)/sftp-server(8): add a protocol extension to support a hard
> link operation. It is available through the "ln" command in the
> client. The old "ln" behaviour of creating a symlink is available
> using its "-s" option or through the preexisting "symlink" command

This
is awesome as I can use that to make [rdup](/projects/rdup) work
(better) with [sshfs](http://fuse.sourceforge.net/sshfs.html).

I've created a patch to add hardlink support to sshfs. See below. I also
created packages for ubuntu:

* 64 bit: [sshfs_2.2-1build1_amd64.deb](/downloads/2011/sshfs/sshfs_2.2-1build1_amd64.deb)
* 32 bit: [sshfs_2.2-1build1_i386.deb](/downloads/2011/sshfs/sshfs_2.2-1build1_i386.deb)

The patched sshfs reverts to the older behavior of creating a symlink in older OpenSSH setups.

# Patch

    diff -ur sshfs-fuse-2.2/sshfs.c sshfs-fuse-2.2-hardlink/sshfs.c
    --- sshfs-fuse-2.2/sshfs.c	2008-10-20 14:10:09.000000000 +0200
    +++ sshfs-fuse-2.2-hardlink/sshfs.c	2011-02-19 13:50:39.815845053 +0100
    @@ -105,6 +105,7 @@
     #define SSH2_FXE_STATVFS_ST_RDONLY	0x00000001
     #define SSH2_FXE_STATVFS_ST_NOSUID	0x00000002
     
    +#define SFTP_EXT_HARDLINK "hardlink@openssh.com"
     #define SFTP_EXT_POSIX_RENAME "posix-rename@openssh.com"
     #define SFTP_EXT_STATVFS "statvfs@openssh.com"
     
    @@ -219,6 +220,7 @@
            char *password;
            int ext_posix_rename;
            int ext_statvfs;
    +        int ext_hardlink;
     
            /* statistics */
            uint64_t bytes_sent;
    @@ -1358,6 +1360,10 @@
                            if (strcmp(ext, SFTP_EXT_STATVFS) == 0 &&
                                strcmp(extdata, "2") == 0)
                                    sshfs.ext_statvfs = 1;
    +                        if (strcmp(ext, SFTP_EXT_HARDLINK) == 0 &&
    +                                strcmp(extdata, "1") == 0) {
    +                                sshfs.ext_hardlink = 1;
    +                        }
                    } while (buf2.len < buf2.size);
            }
            return 0;
    @@ -2015,6 +2021,19 @@
            return err;
     }
     
    +static int sshfs_ext_hardlink(const char *from, const char *to)
    +{
    +	int err;
    +	struct buffer buf;
    +	buf_init(&buf, 0);
    +	buf_add_string(&buf, SFTP_EXT_HARDLINK);
    +	buf_add_path(&buf, from);
    +	buf_add_path(&buf, to);
    +	err = sftp_request(SSH_FXP_EXTENDED, &buf, SSH_FXP_STATUS, NULL);
    +	buf_free(&buf);
    +	return err;
    +}
    +
     static void random_string(char *str, int length)
     {
            int i;
    @@ -2023,6 +2042,19 @@
            *str = '\0';
     }
     
    +static int sshfs_link(const char *from, const char *to)
    +{
    +        int err;
    +        if (sshfs.ext_hardlink) {
    +                err = sshfs_ext_hardlink(from, to);
    +        } else {
    +                /* fall back to old behavoir */
    +                err = sshfs_symlink(from, to);
    +        }
    +
    +        return err;
    +}
    +
     static int sshfs_rename(const char *from, const char *to)
     {
            int err;
    @@ -2801,6 +2833,7 @@
                    .readlink   = sshfs_readlink,
                    .mknod      = sshfs_mknod,
                    .mkdir      = sshfs_mkdir,
    +                .link       = sshfs_link,
                    .symlink    = sshfs_symlink,
                    .unlink     = sshfs_unlink,
                    .rmdir      = sshfs_rmdir,


