diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2010-12-04 00:18:02 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2010-12-04 00:18:02 +0000 |
commit | 3bf9288cc7c36161011d1f665b678d5914f7d6ed (patch) | |
tree | f506f1fae3b1f4bfd0ab0636e6d9424939bc69f3 /usr.bin/ssh | |
parent | 054dc1c0cd58a51d35229b6bd5bd8cf4f60c342d (diff) |
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@
Diffstat (limited to 'usr.bin/ssh')
-rw-r--r-- | usr.bin/ssh/PROTOCOL | 18 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-client.c | 42 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-client.h | 5 | ||||
-rw-r--r-- | usr.bin/ssh/sftp-server.c | 28 | ||||
-rw-r--r-- | usr.bin/ssh/sftp.1 | 18 | ||||
-rw-r--r-- | usr.bin/ssh/sftp.c | 53 |
6 files changed, 144 insertions, 20 deletions
diff --git a/usr.bin/ssh/PROTOCOL b/usr.bin/ssh/PROTOCOL index 5d2a7118a92..c2819601137 100644 --- a/usr.bin/ssh/PROTOCOL +++ b/usr.bin/ssh/PROTOCOL @@ -275,4 +275,20 @@ The values of the f_flag bitmask are as follows: 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/usr.bin/ssh/sftp-client.c b/usr.bin/ssh/sftp-client.c index 55ddece06ce..42979bbd493 100644 --- a/usr.bin/ssh/sftp-client.c +++ b/usr.bin/ssh/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> * @@ -68,6 +68,7 @@ struct sftp_conn { #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; @@ -371,10 +372,14 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, 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", @@ -788,6 +793,39 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath) } 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/usr.bin/ssh/sftp-client.h b/usr.bin/ssh/sftp-client.h index 145fc38ee11..aef54ef49dd 100644 --- a/usr.bin/ssh/sftp-client.h +++ b/usr.bin/ssh/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 @@ int do_statvfs(struct sftp_conn *, const char *, struct sftp_statvfs *, int); /* 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/usr.bin/ssh/sftp-server.c b/usr.bin/ssh/sftp-server.c index 1e5b0d9d295..b16c9036307 100644 --- a/usr.bin/ssh/sftp-server.c +++ b/usr.bin/ssh/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. * @@ -526,6 +526,9 @@ process_init(void) /* 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); } @@ -1195,6 +1198,27 @@ process_extended_fstatvfs(u_int32_t id) } 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; @@ -1208,6 +1232,8 @@ process_extended(void) 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/usr.bin/ssh/sftp.1 b/usr.bin/ssh/sftp.1 index 3bb0c06462c..89b5d3544dd 100644 --- a/usr.bin/ssh/sftp.1 +++ b/usr.bin/ssh/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 @@ commands fail: .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 @@ characters and may match multiple files. .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/usr.bin/ssh/sftp.c b/usr.bin/ssh/sftp.c index 466a0c15afb..0a5c6b52402 100644 --- a/usr.bin/ssh/sftp.c +++ b/usr.bin/ssh/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> * @@ -109,6 +109,7 @@ int remote_glob(struct sftp_conn *, const char *, int, #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 @@ -153,7 +154,7 @@ static const struct CMD cmds[] = { { "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 }, @@ -217,7 +218,7 @@ help(void) "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" @@ -354,6 +355,30 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, } 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; @@ -1065,7 +1090,7 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, 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; @@ -1115,7 +1140,8 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 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) { @@ -1131,8 +1157,11 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, 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); @@ -1235,7 +1264,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 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]; @@ -1243,8 +1273,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 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; @@ -1272,8 +1302,11 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, 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); |