diff --git a/ChangeLog b/ChangeLog index 38d6be3d5..a8a1a94e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +20010308 + - OpenBSD CVS Sync + - djm@cvs.openbsd.org 2001/03/07 10:11:23 + [sftp-client.c sftp-client.h sftp-int.c sftp-server.c sftp.1 sftp.c sftp.h] + Support for new draft (draft-ietf-secsh-filexfer-01). New symlink handling + functions and small protocol change. + 20010307 - (bal) OpenBSD CVS Sync - deraadt@cvs.openbsd.org 2001/03/06 06:11:18 @@ -4430,4 +4437,4 @@ - Wrote replacements for strlcpy and mkdtemp - Released 1.0pre1 -$Id: ChangeLog,v 1.924 2001/03/07 10:38:19 djm Exp $ +$Id: ChangeLog,v 1.925 2001/03/07 23:08:49 djm Exp $ diff --git a/sftp-client.c b/sftp-client.c index 5242cab03..d1e4ebacc 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -29,7 +29,7 @@ /* XXX: copy between two remote sites */ #include "includes.h" -RCSID("$OpenBSD: sftp-client.c,v 1.10 2001/02/14 09:46:03 djm Exp $"); +RCSID("$OpenBSD: sftp-client.c,v 1.11 2001/03/07 10:11:22 djm Exp $"); #include "ssh.h" #include "buffer.h" @@ -247,7 +247,8 @@ do_init(int fd_in, int fd_out) } buffer_free(&msg); - return(0); + + return(version); } int @@ -483,8 +484,7 @@ do_realpath(int fd_in, int fd_out, char *path) Attrib *a; expected_id = id = msg_id++; - send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, - strlen(path)); + send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path)); buffer_init(&msg); @@ -548,6 +548,79 @@ do_rename(int fd_in, int fd_out, char *oldpath, char *newpath) return(status); } +int +do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath) +{ + Buffer msg; + u_int status, id; + + buffer_init(&msg); + + /* Send rename request */ + id = msg_id++; + buffer_put_char(&msg, SSH2_FXP_SYMLINK); + buffer_put_int(&msg, id); + buffer_put_cstring(&msg, oldpath); + buffer_put_cstring(&msg, newpath); + send_msg(fd_out, &msg); + debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, + newpath); + buffer_free(&msg); + + status = get_status(fd_in, id); + if (status != SSH2_FX_OK) + error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, + fx2txt(status)); + + return(status); +} + +char * +do_readlink(int fd_in, int fd_out, char *path) +{ + Buffer msg; + u_int type, expected_id, count, id; + char *filename, *longname; + Attrib *a; + + expected_id = id = msg_id++; + send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path)); + + buffer_init(&msg); + + get_msg(fd_in, &msg); + type = buffer_get_char(&msg); + id = buffer_get_int(&msg); + + if (id != expected_id) + fatal("ID mismatch (%d != %d)", id, expected_id); + + if (type == SSH2_FXP_STATUS) { + u_int status = buffer_get_int(&msg); + + error("Couldn't readlink: %s", fx2txt(status)); + return(NULL); + } else if (type != SSH2_FXP_NAME) + fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", + SSH2_FXP_NAME, type); + + count = buffer_get_int(&msg); + if (count != 1) + fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); + + filename = buffer_get_string(&msg, NULL); + longname = buffer_get_string(&msg, NULL); + a = decode_attrib(&msg); + + debug3("SSH_FXP_READLINK %s -> %s", path, filename); + + xfree(longname); + + buffer_free(&msg); + + return(filename); +} + int do_download(int fd_in, int fd_out, char *remote_path, char *local_path, int pflag) diff --git a/sftp-client.h b/sftp-client.h index 838b46b0b..e836c0d66 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.2 2001/03/07 10:11:23 djm Exp $ */ /* * Copyright (c) 2001 Damien Miller. All rights reserved. @@ -26,7 +26,10 @@ /* Client side of SSH2 filexfer protocol */ -/* Initialiase a SSH filexfer connection */ +/* + * Initialiase a SSH filexfer connection. Returns -1 on error or + * protocol version on success. + */ int do_init(int fd_in, int fd_out); /* Close file referred to by 'handle' */ @@ -67,6 +70,12 @@ char *do_realpath(int fd_in, int fd_out, char *path); /* Rename 'oldpath' to 'newpath' */ int do_rename(int fd_in, int fd_out, char *oldpath, char *newpath); +/* Rename 'oldpath' to 'newpath' */ +int do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath); + +/* Return target of symlink 'path' - caller must free result */ +char *do_readlink(int fd_in, int fd_out, char *path); + /* XXX: add callbacks to do_download/do_upload so we can do progress meter */ /* diff --git a/sftp-int.c b/sftp-int.c index 7aa7abdb9..6f5b3677a 100644 --- a/sftp-int.c +++ b/sftp-int.c @@ -28,7 +28,7 @@ /* XXX: recursive operations */ #include "includes.h" -RCSID("$OpenBSD: sftp-int.c,v 1.25 2001/03/06 06:11:44 deraadt Exp $"); +RCSID("$OpenBSD: sftp-int.c,v 1.26 2001/03/07 10:11:23 djm Exp $"); #include "buffer.h" #include "xmalloc.h" @@ -40,7 +40,11 @@ RCSID("$OpenBSD: sftp-int.c,v 1.25 2001/03/06 06:11:44 deraadt Exp $"); #include "sftp-client.h" #include "sftp-int.h" -extern FILE* infile; +/* File to read commands from */ +extern FILE *infile; + +/* Version of server we are speaking to */ +int version; /* Seperators for interactive commands */ #define WHITESPACE " \t\r\n" @@ -66,6 +70,7 @@ extern FILE* infile; #define I_RM 18 #define I_RMDIR 19 #define I_SHELL 20 +#define I_SYMLINK 21 struct CMD { const char *c; @@ -86,6 +91,7 @@ const struct CMD cmds[] = { { "lchdir", I_LCHDIR }, { "lls", I_LLS }, { "lmkdir", I_LMKDIR }, + { "ln", I_SYMLINK }, { "lpwd", I_LPWD }, { "ls", I_LS }, { "lumask", I_LUMASK }, @@ -96,6 +102,7 @@ const struct CMD cmds[] = { { "rename", I_RENAME }, { "rm", I_RM }, { "rmdir", I_RMDIR }, + { "symlink", I_SYMLINK }, { "!", I_SHELL }, { "?", I_HELP }, { NULL, -1} @@ -113,6 +120,7 @@ help(void) printf("help Display this help text\n"); printf("get remote-path [local-path] Download file\n"); printf("lls [ls-options [path]] Display local directory listing\n"); + printf("ln oldpath newpath Symlink remote file\n"); printf("lmkdir path Create local directory\n"); printf("lpwd Print local working directory\n"); printf("ls [path] Display remote directory listing\n"); @@ -125,6 +133,7 @@ help(void) printf("rename oldpath newpath Rename remote file\n"); printf("rmdir path Remove remote directory\n"); printf("rm path Delete remote file\n"); + printf("symlink oldpath newpath Symlink remote file\n"); printf("!command Execute 'command' in local shell\n"); printf("! Escape to local shell\n"); printf("? Synonym for help\n"); @@ -356,7 +365,7 @@ parse_args(const char **cpp, int *pflag, unsigned long *n_arg, return(-1); break; case I_RENAME: - /* Get first pathname (mandatory) */ + case I_SYMLINK: if (get_pathname(&cp, path1)) return(-1); if (get_pathname(&cp, path2)) @@ -468,6 +477,16 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) path2 = make_absolute(path2, *pwd); err = do_rename(in, out, path1, path2); break; + case I_SYMLINK: + if (version < 3) { + error("The server (version %d) does not support " + "this operation", version); + err = -1; + } else { + path2 = make_absolute(path2, *pwd); + err = do_symlink(in, out, path1, path2); + } + break; case I_RM: path1 = make_absolute(path1, *pwd); err = do_rm(in, out, path1); @@ -624,6 +643,10 @@ interactive_loop(int fd_in, int fd_out) char *pwd; char cmd[2048]; + version = do_init(fd_in, fd_out); + if (version == -1) + fatal("Couldn't initialise connection to server"); + pwd = do_realpath(fd_in, fd_out, "."); if (pwd == NULL) fatal("Need cwd"); diff --git a/sftp-server.c b/sftp-server.c index 6575eb941..178084577 100644 --- a/sftp-server.c +++ b/sftp-server.c @@ -22,7 +22,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: sftp-server.c,v 1.22 2001/03/03 22:07:50 deraadt Exp $"); +RCSID("$OpenBSD: sftp-server.c,v 1.23 2001/03/07 10:11:23 djm Exp $"); #include "buffer.h" #include "bufaux.h" @@ -49,6 +49,9 @@ char *__progname; Buffer iqueue; Buffer oqueue; +/* Version of client */ +int version; + /* portable attibutes, etc. */ typedef struct Stat Stat; @@ -266,12 +269,29 @@ void send_status(u_int32_t id, u_int32_t error) { Buffer msg; + const char *status_messages[] = { + "Success", /* SSH_FX_OK */ + "End of file", /* SSH_FX_EOF */ + "No such file", /* SSH_FX_NO_SUCH_FILE */ + "Permission denied", /* SSH_FX_PERMISSION_DENIED */ + "Failure", /* SSH_FX_FAILURE */ + "Bad message", /* SSH_FX_BAD_MESSAGE */ + "No connection", /* SSH_FX_NO_CONNECTION */ + "Connection lost", /* SSH_FX_CONNECTION_LOST */ + "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ + "Unknown error" /* Others */ + }; TRACE("sent status id %d error %d", id, error); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_int(&msg, id); buffer_put_int(&msg, error); + if (version >= 3) { + buffer_put_cstring(&msg, + status_messages[MIN(error,SSH2_FX_MAX)]); + buffer_put_cstring(&msg, ""); + } send_msg(&msg); buffer_free(&msg); } @@ -347,8 +367,8 @@ void process_init(void) { Buffer msg; - int version = buffer_get_int(&iqueue); + version = buffer_get_int(&iqueue); TRACE("client version %d", version); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_VERSION); @@ -859,6 +879,51 @@ process_rename(void) xfree(newpath); } +void +process_readlink(void) +{ + u_int32_t id; + char link[MAXPATHLEN]; + char *path; + + id = get_int(); + path = get_string(NULL); + TRACE("readlink id %d path %s", id, path); + if (readlink(path, link, sizeof(link) - 1) == -1) + send_status(id, errno_to_portable(errno)); + else { + Stat s; + + link[sizeof(link) - 1] = '\0'; + attrib_clear(&s.attrib); + s.name = s.long_name = link; + send_names(id, 1, &s); + } + xfree(path); +} + +void +process_symlink(void) +{ + u_int32_t id; + struct stat st; + char *oldpath, *newpath; + int ret, status = SSH2_FX_FAILURE; + + id = get_int(); + oldpath = get_string(NULL); + newpath = get_string(NULL); + TRACE("symlink id %d old %s new %s", id, oldpath, newpath); + /* fail if 'newpath' exists */ + if (stat(newpath, &st) == -1) { + ret = symlink(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } + send_status(id, status); + xfree(oldpath); + xfree(newpath); +} + void process_extended(void) { @@ -944,6 +1009,12 @@ process(void) case SSH2_FXP_RENAME: process_rename(); break; + case SSH2_FXP_READLINK: + process_readlink(); + break; + case SSH2_FXP_SYMLINK: + process_symlink(); + break; case SSH2_FXP_EXTENDED: process_extended(); break; diff --git a/sftp.1 b/sftp.1 index 7792283ee..826a0ddf4 100644 --- a/sftp.1 +++ b/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.11 2001/03/06 15:10:42 deraadt Exp $ +.\" $OpenBSD: sftp.1,v 1.12 2001/03/07 10:11:23 djm Exp $ .\" .\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" @@ -58,7 +58,7 @@ instead of Since it lacks user interaction it should be used in conjuction with a non-interactive authentication. Sftp will abort if any of the following commands fail: -.Pa get, put, rename, rm, mkdir, chdir, lchdir +.Pa get, put, rename, ln, rm, mkdir, chdir, lchdir and .Pa lmkdir. .It Fl C @@ -131,6 +131,11 @@ is not specified. .It Ic lmkdir Ar path Create local directory specified by .Ar path . +.It Ic ln Ar oldpath Ar newpath +Create a symbolic link from +.Ar oldpath +to +.Ar newpath . .It Ic lpwd Print local working directory. .It Ic ls Op Ar path @@ -152,8 +157,8 @@ Create remote directory specified by .Xc Upload .Ar local-path -and store it on the remote machine. If the remote path name is not specified, -it is given the same name it has on the local machine. If the +and store it on the remote machine. If the remote path name is not +specified, it is given the same name it has on the local machine. If the .Fl P flag is specified, then the file's full permission and access time are copied too. @@ -172,6 +177,11 @@ Remove remote directory specified by .It Ic rm Ar path Delete remote file specified by .Ar path . +.It Ic symlink Ar oldpath Ar newpath +Create a symbolic link from +.Ar oldpath +to +.Ar newpath . .It Ic ! Ar command Execute .Ar command diff --git a/sftp.c b/sftp.c index c22825093..4569daccc 100644 --- a/sftp.c +++ b/sftp.c @@ -24,7 +24,7 @@ #include "includes.h" -RCSID("$OpenBSD: sftp.c,v 1.10 2001/03/06 06:11:44 deraadt Exp $"); +RCSID("$OpenBSD: sftp.c,v 1.11 2001/03/07 10:11:23 djm Exp $"); /* XXX: commandline mode */ /* XXX: copy between two remote hosts (commandline) */ @@ -256,8 +256,6 @@ main(int argc, char **argv) connect_to_server(make_ssh_args(NULL), &in, &out, &sshpid); - do_init(in, out); - interactive_loop(in, out); #if !defined(USE_PIPES) diff --git a/sftp.h b/sftp.h index 763056f57..2ad95864b 100644 --- a/sftp.h +++ b/sftp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.h,v 1.2 2001/01/29 01:58:18 niklas Exp $ */ +/* $OpenBSD: sftp.h,v 1.3 2001/03/07 10:11:23 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -25,11 +25,11 @@ */ /* - * draft-ietf-secsh-filexfer-00.txt + * draft-ietf-secsh-filexfer-01.txt */ /* version */ -#define SSH2_FILEXFER_VERSION 2 +#define SSH2_FILEXFER_VERSION 3 /* client to server */ #define SSH2_FXP_INIT 1 @@ -49,6 +49,8 @@ #define SSH2_FXP_REALPATH 16 #define SSH2_FXP_STAT 17 #define SSH2_FXP_RENAME 18 +#define SSH2_FXP_READLINK 19 +#define SSH2_FXP_SYMLINK 20 /* server to client */ #define SSH2_FXP_VERSION 2 @@ -86,3 +88,4 @@ #define SSH2_FX_NO_CONNECTION 6 #define SSH2_FX_CONNECTION_LOST 7 #define SSH2_FX_OP_UNSUPPORTED 8 +#define SSH2_FX_MAX 8