- djm@cvs.openbsd.org 2010/12/04 00:18:01
     [sftp-server.c sftp.1 sftp-client.h sftp.c PROTOCOL sftp-client.c]
     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; based on a patch from
     miklos AT szeredi.hu in bz#1555; ok markus@
diff --git a/sftp-client.c b/sftp-client.c
index 4e009ef..caa384b 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.c,v 1.93 2010/09/22 22:58:51 djm Exp $ */
+/* $OpenBSD: sftp-client.c,v 1.94 2010/12/04 00:18:01 djm Exp $ */
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
  *
@@ -75,6 +75,7 @@
 #define SFTP_EXT_POSIX_RENAME	0x00000001
 #define SFTP_EXT_STATVFS	0x00000002
 #define SFTP_EXT_FSTATVFS	0x00000004
+#define SFTP_EXT_HARDLINK	0x00000008
 	u_int exts;
 	u_int64_t limit_kbps;
 	struct bwlimit bwlimit_in, bwlimit_out;
@@ -378,10 +379,14 @@
 		    strcmp(value, "2") == 0) {
 			ret->exts |= SFTP_EXT_STATVFS;
 			known = 1;
-		} if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
+		} else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
 		    strcmp(value, "2") == 0) {
 			ret->exts |= SFTP_EXT_FSTATVFS;
 			known = 1;
+		} else if (strcmp(name, "hardlink@openssh.com") == 0 &&
+		    strcmp(value, "1") == 0) {
+			ret->exts |= SFTP_EXT_HARDLINK;
+			known = 1;
 		}
 		if (known) {
 			debug2("Server supports extension \"%s\" revision %s",
@@ -795,6 +800,39 @@
 }
 
 int
+do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)
+{
+	Buffer msg;
+	u_int status, id;
+
+	buffer_init(&msg);
+
+	/* Send link request */
+	id = conn->msg_id++;
+	if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
+		error("Server does not support hardlink@openssh.com extension");
+		return -1;
+	}
+
+	buffer_put_char(&msg, SSH2_FXP_EXTENDED);
+	buffer_put_int(&msg, id);
+	buffer_put_cstring(&msg, "hardlink@openssh.com");
+	buffer_put_cstring(&msg, oldpath);
+	buffer_put_cstring(&msg, newpath);
+	send_msg(conn, &msg);
+	debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
+	       oldpath, newpath);
+	buffer_free(&msg);
+
+	status = get_status(conn, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
+		    newpath, fx2txt(status));
+
+	return(status);
+}
+
+int
 do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
 {
 	Buffer msg;