Here's a patch that adds support for the creation of hard links over
SFTP.
Hard links are not used very often nowdays, but they do still have
their uses and this is currently the most often requested improvement
for SSHFS.
To detect hard links the st_nlink, st_dev and st_ino attributes are
usually used. I'll also post patches adding extensions for these and
other attributes.
Please consider adding these to the OpenSSH sftp-server/client
implementations. Comments are welcome.
Thanks,
Miklos
Index: ssh/sftp-client.c
==================================================================---
ssh.orig/sftp-client.c 2009-02-10 14:54:58.000000000 +0100
+++ ssh/sftp-client.c 2009-02-10 15:15:08.000000000 +0100
@@ -63,6 +63,7 @@ struct sftp_conn {
#define SFTP_EXT_POSIX_RENAME 0x00000001
#define SFTP_EXT_STATVFS 0x00000002
#define SFTP_EXT_FSTATVFS 0x00000004
+#define SFTP_EXT_LINK 0x00000008
u_int exts;
};
@@ -328,10 +329,14 @@ do_init(int fd_in, int fd_out, u_int tra
strcmp(value, "2") == 0) {
exts |= SFTP_EXT_STATVFS;
known = 1;
- } if (strcmp(name, "fstatvfs at openssh.com") == 0 &&
+ } else if (strcmp(name, "fstatvfs at openssh.com") == 0 &&
strcmp(value, "2") == 0) {
exts |= SFTP_EXT_FSTATVFS;
known = 1;
+ } else if (strcmp(name, "link at openssh.com") == 0 &&
+ strcmp(value, "1") == 0) {
+ exts |= SFTP_EXT_LINK;
+ known = 1;
}
if (known) {
debug2("Server supports extension \"%s\" revision %s",
@@ -731,6 +736,39 @@ do_rename(struct sftp_conn *conn, char *
newpath, fx2txt(status));
return(status);
+}
+
+int
+do_link(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_LINK) == 0) {
+ error("Server does not support link at openssh.com extension");
+ return -1;
+ }
+
+ buffer_put_char(&msg, SSH2_FXP_EXTENDED);
+ buffer_put_int(&msg, id);
+ buffer_put_cstring(&msg, "link at openssh.com");
+ buffer_put_cstring(&msg, oldpath);
+ buffer_put_cstring(&msg, newpath);
+ send_msg(conn->fd_out, &msg);
+ debug3("Sent message link at openssh.com \"%s\" ->
\"%s\"",
+ oldpath, newpath);
+ buffer_free(&msg);
+
+ status = get_status(conn->fd_in, id);
+ if (status != SSH2_FX_OK)
+ error("Couldn't link file \"%s\" to \"%s\":
%s", oldpath,
+ newpath, fx2txt(status));
+
+ return(status);
}
int
Index: ssh/sftp-client.h
==================================================================---
ssh.orig/sftp-client.h 2009-02-10 14:54:58.000000000 +0100
+++ ssh/sftp-client.h 2009-02-10 15:15:08.000000000 +0100
@@ -94,6 +94,9 @@ int do_statvfs(struct sftp_conn *, const
/* Rename 'oldpath' to 'newpath' */
int do_rename(struct sftp_conn *, char *, char *);
+/* Link 'oldpath' to 'newpath' */
+int do_link(struct sftp_conn *, char *, char *);
+
/* Rename 'oldpath' to 'newpath' */
int do_symlink(struct sftp_conn *, char *, char *);
Index: ssh/sftp-server.c
==================================================================---
ssh.orig/sftp-server.c 2009-02-10 14:54:58.000000000 +0100
+++ ssh/sftp-server.c 2009-02-10 15:15:08.000000000 +0100
@@ -523,6 +523,9 @@ process_init(void)
/* fstatvfs extension */
buffer_put_cstring(&msg, "fstatvfs at openssh.com");
buffer_put_cstring(&msg, "2"); /* version */
+ /* link extension */
+ buffer_put_cstring(&msg, "link at openssh.com");
+ buffer_put_cstring(&msg, "1"); /* version */
send_msg(&msg);
buffer_free(&msg);
}
@@ -1153,6 +1156,23 @@ process_extended_fstatvfs(u_int32_t id)
}
static void
+process_extended_link(u_int32_t id)
+{
+ char *oldpath, *newpath;
+
+ oldpath = get_string(NULL);
+ newpath = get_string(NULL);
+ debug3("request %u: link", id);
+ logit("link old \"%s\" new \"%s\"", oldpath,
newpath);
+ if (link(oldpath, newpath) == -1)
+ send_status(id, errno_to_portable(errno));
+ else
+ send_status(id, SSH2_FX_OK);
+ xfree(oldpath);
+ xfree(newpath);
+}
+
+static void
process_extended(void)
{
u_int32_t id;
@@ -1166,6 +1186,8 @@ process_extended(void)
process_extended_statvfs(id);
else if (strcmp(request, "fstatvfs at openssh.com") == 0)
process_extended_fstatvfs(id);
+ else if (strcmp(request, "link at openssh.com") == 0)
+ process_extended_link(id);
else
send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
xfree(request);
Index: ssh/sftp.c
==================================================================---
ssh.orig/sftp.c 2009-02-10 14:54:58.000000000 +0100
+++ ssh/sftp.c 2009-02-10 15:15:19.000000000 +0100
@@ -98,6 +98,7 @@ int remote_glob(struct sftp_conn *, cons
#define I_GET 5
#define I_HELP 6
#define I_LCHDIR 7
+#define I_LINK 25
#define I_LLS 8
#define I_LMKDIR 9
#define I_LPWD 10
@@ -135,6 +136,7 @@ static const struct CMD cmds[] = {
{ "help", I_HELP },
{ "lcd", I_LCHDIR },
{ "lchdir", I_LCHDIR },
+ { "link", I_LINK },
{ "lls", I_LLS },
{ "lmkdir", I_LMKDIR },
{ "ln", I_SYMLINK },
@@ -198,6 +200,7 @@ help(void)
"get [-P] remote-path [local-path] Download file\n"
"help Display this help text\n"
"lcd path Change local directory to
'path'\n"
+ "link oldpath newpath Create hard link to remote
file\n"
"lls [ls-options [path]] Display local directory
listing\n"
"lmkdir path Create local directory\n"
"ln oldpath newpath Symlink remote file\n"
@@ -1111,6 +1114,7 @@ parse_args(const char **cpp, int *pflag,
}
break;
case I_RENAME:
+ case I_LINK:
case I_SYMLINK:
if (argc - optidx < 2) {
error("You must specify two paths after a %s "
@@ -1250,6 +1254,11 @@ parse_dispatch_command(struct sftp_conn
path2 = make_absolute(path2, *pwd);
err = do_rename(conn, path1, path2);
break;
+ case I_LINK:
+ path1 = make_absolute(path1, *pwd);
+ path2 = make_absolute(path2, *pwd);
+ err = do_link(conn, path1, path2);
+ break;
case I_SYMLINK:
path2 = make_absolute(path2, *pwd);
err = do_symlink(conn, path1, path2);
Index: ssh/PROTOCOL
==================================================================---
ssh.orig/PROTOCOL 2009-02-10 14:54:58.000000000 +0100
+++ ssh/PROTOCOL 2009-02-10 15:22:05.000000000 +0100
@@ -240,4 +240,20 @@ The values of the f_flag bitmask are as
Both the "statvfs at openssh.com" and "fstatvfs at
openssh.com" extensions are
advertised in the SSH_FXP_VERSION hello with version "2".
+10. sftp: Extension request "link at openssh.com"
+
+This request is for creating a hard link to a regular file. This
+request is implemented as a SSH_FXP_EXTENDED request with the
+following format:
+
+ uint32 id
+ string "link at openssh.com"
+ string oldpath
+ string newpath
+
+On receiving this request the server will perform the operation
+link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message.
+This extension is advertised in the SSH_FXP_VERSION hello with version
+"1".
+
$OpenBSD: PROTOCOL,v 1.11 2008/07/05 05:16:01 djm Exp $