Hardlink support in sshfs

February 19, 2011

linux

OpenSSH 5.7 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 work (better) with sshfs.

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

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,
None