summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--PROTOCOL18
-rw-r--r--sftp-client.c42
-rw-r--r--sftp-client.h5
-rw-r--r--sftp-server.c28
-rw-r--r--sftp.118
-rw-r--r--sftp.c53
7 files changed, 151 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index a8aeacc56..4e60f137f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,13 @@
11 [auth-rsa.c] 11 [auth-rsa.c]
12 move check for revoked keys to run earlier (in auth_rsa_key_allowed) 12 move check for revoked keys to run earlier (in auth_rsa_key_allowed)
13 bz#1829; patch from ldv AT altlinux.org; ok markus@ 13 bz#1829; patch from ldv AT altlinux.org; ok markus@
14 - djm@cvs.openbsd.org 2010/12/04 00:18:01
15 [sftp-server.c sftp.1 sftp-client.h sftp.c PROTOCOL sftp-client.c]
16 add a protocol extension to support a hard link operation. It is
17 available through the "ln" command in the client. The old "ln"
18 behaviour of creating a symlink is available using its "-s" option
19 or through the preexisting "symlink" command; based on a patch from
20 miklos AT szeredi.hu in bz#1555; ok markus@
14 21
1520101204 2220101204
16 - (djm) [openbsd-compat/bindresvport.c] Use arc4random_uniform(range) 23 - (djm) [openbsd-compat/bindresvport.c] Use arc4random_uniform(range)
diff --git a/PROTOCOL b/PROTOCOL
index 5d2a7118a..c28196011 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -275,4 +275,20 @@ The values of the f_flag bitmask are as follows:
275Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are 275Both the "statvfs@openssh.com" and "fstatvfs@openssh.com" extensions are
276advertised in the SSH_FXP_VERSION hello with version "2". 276advertised in the SSH_FXP_VERSION hello with version "2".
277 277
278$OpenBSD: PROTOCOL,v 1.16 2010/08/31 11:54:45 djm Exp $ 27810. sftp: Extension request "hardlink@openssh.com"
279
280This request is for creating a hard link to a regular file. This
281request is implemented as a SSH_FXP_EXTENDED request with the
282following format:
283
284 uint32 id
285 string "hardlink@openssh.com"
286 string oldpath
287 string newpath
288
289On receiving this request the server will perform the operation
290link(oldpath, newpath) and will respond with a SSH_FXP_STATUS message.
291This extension is advertised in the SSH_FXP_VERSION hello with version
292"1".
293
294$OpenBSD: PROTOCOL,v 1.17 2010/12/04 00:18:01 djm Exp $
diff --git a/sftp-client.c b/sftp-client.c
index 4e009ef25..caa384b4e 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.93 2010/09/22 22:58:51 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.94 2010/12/04 00:18:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -75,6 +75,7 @@ struct sftp_conn {
75#define SFTP_EXT_POSIX_RENAME 0x00000001 75#define SFTP_EXT_POSIX_RENAME 0x00000001
76#define SFTP_EXT_STATVFS 0x00000002 76#define SFTP_EXT_STATVFS 0x00000002
77#define SFTP_EXT_FSTATVFS 0x00000004 77#define SFTP_EXT_FSTATVFS 0x00000004
78#define SFTP_EXT_HARDLINK 0x00000008
78 u_int exts; 79 u_int exts;
79 u_int64_t limit_kbps; 80 u_int64_t limit_kbps;
80 struct bwlimit bwlimit_in, bwlimit_out; 81 struct bwlimit bwlimit_in, bwlimit_out;
@@ -378,10 +379,14 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
378 strcmp(value, "2") == 0) { 379 strcmp(value, "2") == 0) {
379 ret->exts |= SFTP_EXT_STATVFS; 380 ret->exts |= SFTP_EXT_STATVFS;
380 known = 1; 381 known = 1;
381 } if (strcmp(name, "fstatvfs@openssh.com") == 0 && 382 } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
382 strcmp(value, "2") == 0) { 383 strcmp(value, "2") == 0) {
383 ret->exts |= SFTP_EXT_FSTATVFS; 384 ret->exts |= SFTP_EXT_FSTATVFS;
384 known = 1; 385 known = 1;
386 } else if (strcmp(name, "hardlink@openssh.com") == 0 &&
387 strcmp(value, "1") == 0) {
388 ret->exts |= SFTP_EXT_HARDLINK;
389 known = 1;
385 } 390 }
386 if (known) { 391 if (known) {
387 debug2("Server supports extension \"%s\" revision %s", 392 debug2("Server supports extension \"%s\" revision %s",
@@ -795,6 +800,39 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
795} 800}
796 801
797int 802int
803do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)
804{
805 Buffer msg;
806 u_int status, id;
807
808 buffer_init(&msg);
809
810 /* Send link request */
811 id = conn->msg_id++;
812 if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
813 error("Server does not support hardlink@openssh.com extension");
814 return -1;
815 }
816
817 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
818 buffer_put_int(&msg, id);
819 buffer_put_cstring(&msg, "hardlink@openssh.com");
820 buffer_put_cstring(&msg, oldpath);
821 buffer_put_cstring(&msg, newpath);
822 send_msg(conn, &msg);
823 debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
824 oldpath, newpath);
825 buffer_free(&msg);
826
827 status = get_status(conn, id);
828 if (status != SSH2_FX_OK)
829 error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
830 newpath, fx2txt(status));
831
832 return(status);
833}
834
835int
798do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath) 836do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
799{ 837{
800 Buffer msg; 838 Buffer msg;
diff --git a/sftp-client.h b/sftp-client.h
index 145fc38ee..aef54ef49 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.h,v 1.19 2010/09/22 22:58:51 djm Exp $ */ 1/* $OpenBSD: sftp-client.h,v 1.20 2010/12/04 00:18:01 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -94,6 +94,9 @@ int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int);
94/* Rename 'oldpath' to 'newpath' */ 94/* Rename 'oldpath' to 'newpath' */
95int do_rename(struct sftp_conn *, char *, char *); 95int do_rename(struct sftp_conn *, char *, char *);
96 96
97/* Link 'oldpath' to 'newpath' */
98int do_hardlink(struct sftp_conn *, char *, char *);
99
97/* Rename 'oldpath' to 'newpath' */ 100/* Rename 'oldpath' to 'newpath' */
98int do_symlink(struct sftp_conn *, char *, char *); 101int do_symlink(struct sftp_conn *, char *, char *);
99 102
diff --git a/sftp-server.c b/sftp-server.c
index 47edcd0aa..b268d0883 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-server.c,v 1.92 2010/11/04 02:45:34 djm Exp $ */ 1/* $OpenBSD: sftp-server.c,v 1.93 2010/12/04 00:18:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4 * 4 *
@@ -535,6 +535,9 @@ process_init(void)
535 /* fstatvfs extension */ 535 /* fstatvfs extension */
536 buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 536 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
537 buffer_put_cstring(&msg, "2"); /* version */ 537 buffer_put_cstring(&msg, "2"); /* version */
538 /* hardlink extension */
539 buffer_put_cstring(&msg, "hardlink@openssh.com");
540 buffer_put_cstring(&msg, "1"); /* version */
538 send_msg(&msg); 541 send_msg(&msg);
539 buffer_free(&msg); 542 buffer_free(&msg);
540} 543}
@@ -1223,6 +1226,27 @@ process_extended_fstatvfs(u_int32_t id)
1223} 1226}
1224 1227
1225static void 1228static void
1229process_extended_hardlink(u_int32_t id)
1230{
1231 char *oldpath, *newpath;
1232 int ret, status;
1233
1234 oldpath = get_string(NULL);
1235 newpath = get_string(NULL);
1236 debug3("request %u: hardlink", id);
1237 logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
1238 if (readonly)
1239 status = SSH2_FX_PERMISSION_DENIED;
1240 else {
1241 ret = link(oldpath, newpath);
1242 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1243 }
1244 send_status(id, status);
1245 xfree(oldpath);
1246 xfree(newpath);
1247}
1248
1249static void
1226process_extended(void) 1250process_extended(void)
1227{ 1251{
1228 u_int32_t id; 1252 u_int32_t id;
@@ -1236,6 +1260,8 @@ process_extended(void)
1236 process_extended_statvfs(id); 1260 process_extended_statvfs(id);
1237 else if (strcmp(request, "fstatvfs@openssh.com") == 0) 1261 else if (strcmp(request, "fstatvfs@openssh.com") == 0)
1238 process_extended_fstatvfs(id); 1262 process_extended_fstatvfs(id);
1263 else if (strcmp(request, "hardlink@openssh.com") == 0)
1264 process_extended_hardlink(id);
1239 else 1265 else
1240 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1266 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
1241 xfree(request); 1267 xfree(request);
diff --git a/sftp.1 b/sftp.1
index 3bb0c0646..89b5d3544 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.87 2010/11/18 15:01:00 jmc Exp $ 1.\" $OpenBSD: sftp.1,v 1.88 2010/12/04 00:18:01 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: November 18 2010 $ 25.Dd $Mdocdate: December 4 2010 $
26.Dt SFTP 1 26.Dt SFTP 1
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -128,7 +128,7 @@ commands fail:
128.Ic get , put , rename , ln , 128.Ic get , put , rename , ln ,
129.Ic rm , mkdir , chdir , ls , 129.Ic rm , mkdir , chdir , ls ,
130.Ic lchdir , chmod , chown , 130.Ic lchdir , chmod , chown ,
131.Ic chgrp , lpwd , df , 131.Ic chgrp , lpwd , df , symlink ,
132and 132and
133.Ic lmkdir . 133.Ic lmkdir .
134Termination on error can be suppressed on a command by command basis by 134Termination on error can be suppressed on a command by command basis by
@@ -392,11 +392,19 @@ characters and may match multiple files.
392.It Ic lmkdir Ar path 392.It Ic lmkdir Ar path
393Create local directory specified by 393Create local directory specified by
394.Ar path . 394.Ar path .
395.It Ic ln Ar oldpath Ar newpath 395.It Xo Ic ln
396Create a symbolic link from 396.Op Fl s
397.Ar oldpath
398.Ar newpath
399.Xc
400Create a link from
397.Ar oldpath 401.Ar oldpath
398to 402to
399.Ar newpath . 403.Ar newpath .
404If the
405.Fl s
406flag is specified the created link is a symbolic link, otherwise it is
407a hard link.
400.It Ic lpwd 408.It Ic lpwd
401Print local working directory. 409Print local working directory.
402.It Xo Ic ls 410.It Xo Ic ls
diff --git a/sftp.c b/sftp.c
index d605505ea..ab667f5a5 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.131 2010/10/23 22:06:12 sthen Exp $ */ 1/* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -132,6 +132,7 @@ extern char *__progname;
132#define I_GET 5 132#define I_GET 5
133#define I_HELP 6 133#define I_HELP 6
134#define I_LCHDIR 7 134#define I_LCHDIR 7
135#define I_LINK 25
135#define I_LLS 8 136#define I_LLS 8
136#define I_LMKDIR 9 137#define I_LMKDIR 9
137#define I_LPWD 10 138#define I_LPWD 10
@@ -176,7 +177,7 @@ static const struct CMD cmds[] = {
176 { "lchdir", I_LCHDIR, LOCAL }, 177 { "lchdir", I_LCHDIR, LOCAL },
177 { "lls", I_LLS, LOCAL }, 178 { "lls", I_LLS, LOCAL },
178 { "lmkdir", I_LMKDIR, LOCAL }, 179 { "lmkdir", I_LMKDIR, LOCAL },
179 { "ln", I_SYMLINK, REMOTE }, 180 { "ln", I_LINK, REMOTE },
180 { "lpwd", I_LPWD, LOCAL }, 181 { "lpwd", I_LPWD, LOCAL },
181 { "ls", I_LS, REMOTE }, 182 { "ls", I_LS, REMOTE },
182 { "lumask", I_LUMASK, NOARGS }, 183 { "lumask", I_LUMASK, NOARGS },
@@ -240,7 +241,7 @@ help(void)
240 "lcd path Change local directory to 'path'\n" 241 "lcd path Change local directory to 'path'\n"
241 "lls [ls-options [path]] Display local directory listing\n" 242 "lls [ls-options [path]] Display local directory listing\n"
242 "lmkdir path Create local directory\n" 243 "lmkdir path Create local directory\n"
243 "ln oldpath newpath Symlink remote file\n" 244 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
244 "lpwd Print local working directory\n" 245 "lpwd Print local working directory\n"
245 "ls [-1afhlnrSt] [path] Display remote directory listing\n" 246 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
246 "lumask umask Set local umask to 'umask'\n" 247 "lumask umask Set local umask to 'umask'\n"
@@ -377,6 +378,30 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
377} 378}
378 379
379static int 380static int
381parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
382{
383 extern int opterr, optind, optopt, optreset;
384 int ch;
385
386 optind = optreset = 1;
387 opterr = 0;
388
389 *sflag = 0;
390 while ((ch = getopt(argc, argv, "s")) != -1) {
391 switch (ch) {
392 case 's':
393 *sflag = 1;
394 break;
395 default:
396 error("%s: Invalid flag -%c", cmd, optopt);
397 return -1;
398 }
399 }
400
401 return optind;
402}
403
404static int
380parse_ls_flags(char **argv, int argc, int *lflag) 405parse_ls_flags(char **argv, int argc, int *lflag)
381{ 406{
382 extern int opterr, optind, optopt, optreset; 407 extern int opterr, optind, optopt, optreset;
@@ -1088,7 +1113,7 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1088 1113
1089static int 1114static int
1090parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 1115parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1091 int *hflag, unsigned long *n_arg, char **path1, char **path2) 1116 int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
1092{ 1117{
1093 const char *cmd, *cp = *cpp; 1118 const char *cmd, *cp = *cpp;
1094 char *cp2, **argv; 1119 char *cp2, **argv;
@@ -1138,7 +1163,8 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1138 switch (cmdnum) { 1163 switch (cmdnum) {
1139 case I_GET: 1164 case I_GET:
1140 case I_PUT: 1165 case I_PUT:
1141 if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) 1166 if ((optidx = parse_getput_flags(cmd, argv, argc,
1167 pflag, rflag)) == -1)
1142 return -1; 1168 return -1;
1143 /* Get first pathname (mandatory) */ 1169 /* Get first pathname (mandatory) */
1144 if (argc - optidx < 1) { 1170 if (argc - optidx < 1) {
@@ -1154,8 +1180,11 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1154 undo_glob_escape(*path2); 1180 undo_glob_escape(*path2);
1155 } 1181 }
1156 break; 1182 break;
1157 case I_RENAME: 1183 case I_LINK:
1184 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1185 return -1;
1158 case I_SYMLINK: 1186 case I_SYMLINK:
1187 case I_RENAME:
1159 if (argc - optidx < 2) { 1188 if (argc - optidx < 2) {
1160 error("You must specify two paths after a %s " 1189 error("You must specify two paths after a %s "
1161 "command.", cmd); 1190 "command.", cmd);
@@ -1258,7 +1287,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1258 int err_abort) 1287 int err_abort)
1259{ 1288{
1260 char *path1, *path2, *tmp; 1289 char *path1, *path2, *tmp;
1261 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; 1290 int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
1291 int cmdnum, i;
1262 unsigned long n_arg = 0; 1292 unsigned long n_arg = 0;
1263 Attrib a, *aa; 1293 Attrib a, *aa;
1264 char path_buf[MAXPATHLEN]; 1294 char path_buf[MAXPATHLEN];
@@ -1266,8 +1296,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1266 glob_t g; 1296 glob_t g;
1267 1297
1268 path1 = path2 = NULL; 1298 path1 = path2 = NULL;
1269 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, 1299 cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
1270 &path1, &path2); 1300 &sflag, &n_arg, &path1, &path2);
1271 1301
1272 if (iflag != 0) 1302 if (iflag != 0)
1273 err_abort = 0; 1303 err_abort = 0;
@@ -1295,8 +1325,11 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1295 err = do_rename(conn, path1, path2); 1325 err = do_rename(conn, path1, path2);
1296 break; 1326 break;
1297 case I_SYMLINK: 1327 case I_SYMLINK:
1328 sflag = 1;
1329 case I_LINK:
1330 path1 = make_absolute(path1, *pwd);
1298 path2 = make_absolute(path2, *pwd); 1331 path2 = make_absolute(path2, *pwd);
1299 err = do_symlink(conn, path1, path2); 1332 err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1300 break; 1333 break;
1301 case I_RM: 1334 case I_RM:
1302 path1 = make_absolute(path1, *pwd); 1335 path1 = make_absolute(path1, *pwd);