- 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/ChangeLog b/ChangeLog
index a8aeacc..4e60f13 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,13 @@
[auth-rsa.c]
move check for revoked keys to run earlier (in auth_rsa_key_allowed)
bz#1829; patch from ldv AT altlinux.org; ok markus@
+ - 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@
20101204
- (djm) [openbsd-compat/bindresvport.c] Use arc4random_uniform(range)
diff --git a/PROTOCOL b/PROTOCOL
index 5d2a711..c281960 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -275,4 +275,20 @@
Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are
advertised in the SSH_FXP_VERSION hello with version "2".
-$OpenBSD: PROTOCOL,v 1.16 2010/08/31 11:54:45 djm Exp $
+10. sftp: Extension request "hardlink@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 "hardlink@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.17 2010/12/04 00:18:01 djm Exp $
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;
diff --git a/sftp-client.h b/sftp-client.h
index 145fc38..aef54ef 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.h,v 1.19 2010/09/22 22:58:51 djm Exp $ */
+/* $OpenBSD: sftp-client.h,v 1.20 2010/12/04 00:18:01 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -94,6 +94,9 @@
/* Rename 'oldpath' to 'newpath' */
int do_rename(struct sftp_conn *, char *, char *);
+/* Link 'oldpath' to 'newpath' */
+int do_hardlink(struct sftp_conn *, char *, char *);
+
/* Rename 'oldpath' to 'newpath' */
int do_symlink(struct sftp_conn *, char *, char *);
diff --git a/sftp-server.c b/sftp-server.c
index 47edcd0..b268d08 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-server.c,v 1.92 2010/11/04 02:45:34 djm Exp $ */
+/* $OpenBSD: sftp-server.c,v 1.93 2010/12/04 00:18:01 djm Exp $ */
/*
* Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
*
@@ -535,6 +535,9 @@
/* fstatvfs extension */
buffer_put_cstring(&msg, "fstatvfs@openssh.com");
buffer_put_cstring(&msg, "2"); /* version */
+ /* hardlink extension */
+ buffer_put_cstring(&msg, "hardlink@openssh.com");
+ buffer_put_cstring(&msg, "1"); /* version */
send_msg(&msg);
buffer_free(&msg);
}
@@ -1223,6 +1226,27 @@
}
static void
+process_extended_hardlink(u_int32_t id)
+{
+ char *oldpath, *newpath;
+ int ret, status;
+
+ oldpath = get_string(NULL);
+ newpath = get_string(NULL);
+ debug3("request %u: hardlink", id);
+ logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
+ if (readonly)
+ status = SSH2_FX_PERMISSION_DENIED;
+ else {
+ ret = link(oldpath, newpath);
+ status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
+ }
+ send_status(id, status);
+ xfree(oldpath);
+ xfree(newpath);
+}
+
+static void
process_extended(void)
{
u_int32_t id;
@@ -1236,6 +1260,8 @@
process_extended_statvfs(id);
else if (strcmp(request, "fstatvfs@openssh.com") == 0)
process_extended_fstatvfs(id);
+ else if (strcmp(request, "hardlink@openssh.com") == 0)
+ process_extended_hardlink(id);
else
send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
xfree(request);
diff --git a/sftp.1 b/sftp.1
index 3bb0c06..89b5d35 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: sftp.1,v 1.87 2010/11/18 15:01:00 jmc Exp $
+.\" $OpenBSD: sftp.1,v 1.88 2010/12/04 00:18:01 djm Exp $
.\"
.\" Copyright (c) 2001 Damien Miller. All rights reserved.
.\"
@@ -22,7 +22,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: November 18 2010 $
+.Dd $Mdocdate: December 4 2010 $
.Dt SFTP 1
.Os
.Sh NAME
@@ -128,7 +128,7 @@
.Ic get , put , rename , ln ,
.Ic rm , mkdir , chdir , ls ,
.Ic lchdir , chmod , chown ,
-.Ic chgrp , lpwd , df ,
+.Ic chgrp , lpwd , df , symlink ,
and
.Ic lmkdir .
Termination on error can be suppressed on a command by command basis by
@@ -392,11 +392,19 @@
.It Ic lmkdir Ar path
Create local directory specified by
.Ar path .
-.It Ic ln Ar oldpath Ar newpath
-Create a symbolic link from
+.It Xo Ic ln
+.Op Fl s
+.Ar oldpath
+.Ar newpath
+.Xc
+Create a link from
.Ar oldpath
to
.Ar newpath .
+If the
+.Fl s
+flag is specified the created link is a symbolic link, otherwise it is
+a hard link.
.It Ic lpwd
Print local working directory.
.It Xo Ic ls
diff --git a/sftp.c b/sftp.c
index d605505..ab667f5 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp.c,v 1.131 2010/10/23 22:06:12 sthen Exp $ */
+/* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
@@ -132,6 +132,7 @@
#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
@@ -176,7 +177,7 @@
{ "lchdir", I_LCHDIR, LOCAL },
{ "lls", I_LLS, LOCAL },
{ "lmkdir", I_LMKDIR, LOCAL },
- { "ln", I_SYMLINK, REMOTE },
+ { "ln", I_LINK, REMOTE },
{ "lpwd", I_LPWD, LOCAL },
{ "ls", I_LS, REMOTE },
{ "lumask", I_LUMASK, NOARGS },
@@ -240,7 +241,7 @@
"lcd path Change local directory to 'path'\n"
"lls [ls-options [path]] Display local directory listing\n"
"lmkdir path Create local directory\n"
- "ln oldpath newpath Symlink remote file\n"
+ "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
"lpwd Print local working directory\n"
"ls [-1afhlnrSt] [path] Display remote directory listing\n"
"lumask umask Set local umask to 'umask'\n"
@@ -377,6 +378,30 @@
}
static int
+parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
+{
+ extern int opterr, optind, optopt, optreset;
+ int ch;
+
+ optind = optreset = 1;
+ opterr = 0;
+
+ *sflag = 0;
+ while ((ch = getopt(argc, argv, "s")) != -1) {
+ switch (ch) {
+ case 's':
+ *sflag = 1;
+ break;
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
+}
+
+static int
parse_ls_flags(char **argv, int argc, int *lflag)
{
extern int opterr, optind, optopt, optreset;
@@ -1088,7 +1113,7 @@
static int
parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
- int *hflag, unsigned long *n_arg, char **path1, char **path2)
+ int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
{
const char *cmd, *cp = *cpp;
char *cp2, **argv;
@@ -1138,7 +1163,8 @@
switch (cmdnum) {
case I_GET:
case I_PUT:
- if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1)
+ if ((optidx = parse_getput_flags(cmd, argv, argc,
+ pflag, rflag)) == -1)
return -1;
/* Get first pathname (mandatory) */
if (argc - optidx < 1) {
@@ -1154,8 +1180,11 @@
undo_glob_escape(*path2);
}
break;
- case I_RENAME:
+ case I_LINK:
+ if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
+ return -1;
case I_SYMLINK:
+ case I_RENAME:
if (argc - optidx < 2) {
error("You must specify two paths after a %s "
"command.", cmd);
@@ -1258,7 +1287,8 @@
int err_abort)
{
char *path1, *path2, *tmp;
- int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i;
+ int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
+ int cmdnum, i;
unsigned long n_arg = 0;
Attrib a, *aa;
char path_buf[MAXPATHLEN];
@@ -1266,8 +1296,8 @@
glob_t g;
path1 = path2 = NULL;
- cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg,
- &path1, &path2);
+ cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
+ &sflag, &n_arg, &path1, &path2);
if (iflag != 0)
err_abort = 0;
@@ -1295,8 +1325,11 @@
err = do_rename(conn, path1, path2);
break;
case I_SYMLINK:
+ sflag = 1;
+ case I_LINK:
+ path1 = make_absolute(path1, *pwd);
path2 = make_absolute(path2, *pwd);
- err = do_symlink(conn, path1, path2);
+ err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
break;
case I_RM:
path1 = make_absolute(path1, *pwd);