From 46bbbe3326d69a84d94caca656c40a4521c10c45 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Wed, 7 Oct 2009 08:21:48 +1100 Subject: - djm@cvs.openbsd.org 2009/08/12 00:13:00 [sftp.c sftp.1] support most of scp(1)'s commandline arguments in sftp(1), as a first step towards making sftp(1) a drop-in replacement for scp(1). One conflicting option (-P) has not been changed, pending further discussion. Patch from carlosvsilvapt@gmail.com as part of his work in the Google Summer of Code --- sftp.c | 57 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 21 deletions(-) (limited to 'sftp.c') diff --git a/sftp.c b/sftp.c index 66bd111b1..798a72edb 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.107 2009/02/02 11:15:14 dtucker Exp $ */ +/* $OpenBSD: sftp.c,v 1.108 2009/08/12 00:13:00 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1668,12 +1668,14 @@ usage(void) extern char *__progname; fprintf(stderr, - "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n" - " [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n" - " [-S program] [-s subsystem | sftp_server] host\n" + "usage: %s [-1246Cqv] [-B buffer_size] [-b batchfile] [-c cipher]\n" + " [-F ssh_config] [-i identify_file] [-o ssh_option]\n" + " [-P sftp_server_path] [-R num_requests] [-S program]\n" + " [-s subsystem | sftp_server] host\n" " %s [user@]host[:file ...]\n" " %s [user@]host[:dir[/]]\n" - " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); + " %s -b batchfile [user@]host\n", + __progname, __progname, __progname, __progname); exit(1); } @@ -1705,10 +1707,24 @@ main(int argc, char **argv) ll = SYSLOG_LEVEL_INFO; infile = stdin; - while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { + while ((ch = getopt(argc, argv, "1246hqvCc:i:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { + /* Passed through to ssh(1) */ + case '4': + case '6': case 'C': - addargs(&args, "-C"); + addargs(&args, "-%c", ch); + break; + /* Passed through to ssh(1) with argument */ + case 'F': + case 'c': + case 'i': + case 'o': + addargs(&args, "-%c%s", ch, optarg); + break; + case 'q': + showprogress = 0; + addargs(&args, "-%c", ch); break; case 'v': if (debug_level < 3) { @@ -1717,21 +1733,18 @@ main(int argc, char **argv) } debug_level++; break; - case 'F': - case 'o': - addargs(&args, "-%c%s", ch, optarg); - break; case '1': sshver = 1; if (sftp_server == NULL) sftp_server = _PATH_SFTP_SERVER; break; - case 's': - sftp_server = optarg; + case '2': + sshver = 2; break; - case 'S': - ssh_program = optarg; - replacearg(&args, 0, "%s", ssh_program); + case 'B': + copy_buffer_len = strtol(optarg, &cp, 10); + if (copy_buffer_len == 0 || *cp != '\0') + fatal("Invalid buffer size \"%s\"", optarg); break; case 'b': if (batchmode) @@ -1748,17 +1761,19 @@ main(int argc, char **argv) case 'P': sftp_direct = optarg; break; - case 'B': - copy_buffer_len = strtol(optarg, &cp, 10); - if (copy_buffer_len == 0 || *cp != '\0') - fatal("Invalid buffer size \"%s\"", optarg); - break; case 'R': num_requests = strtol(optarg, &cp, 10); if (num_requests == 0 || *cp != '\0') fatal("Invalid number of requests \"%s\"", optarg); break; + case 's': + sftp_server = optarg; + break; + case 'S': + ssh_program = optarg; + replacearg(&args, 0, "%s", ssh_program); + break; case 'h': default: usage(); -- cgit v1.2.3 From 282b4026cb5c5a229e2c5b33e4ad3ac31fcf3189 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Wed, 7 Oct 2009 08:23:06 +1100 Subject: - djm@cvs.openbsd.org 2009/08/13 01:11:19 [sftp.1 sftp.c] Swizzle options: "-P sftp_server_path" moves to "-D sftp_server_path", add "-P port" to match scp(1). Fortunately, the -P option is only really used by our regression scripts. part of larger patch from carlosvsilvapt@gmail.com for his Google Summer of Code work; ok deraadt markus --- ChangeLog | 7 +++++++ sftp.1 | 19 +++++++++++-------- sftp.c | 10 +++++++--- 3 files changed, 25 insertions(+), 11 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 7c305d7d6..46f5e9cfa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,13 @@ - jmc@cvs.openbsd.org 2009/08/12 06:31:42 [sftp.1] sort options; + - djm@cvs.openbsd.org 2009/08/13 01:11:19 + [sftp.1 sftp.c] + Swizzle options: "-P sftp_server_path" moves to "-D sftp_server_path", + add "-P port" to match scp(1). Fortunately, the -P option is only really + used by our regression scripts. + part of larger patch from carlosvsilvapt@gmail.com for his Google Summer + of Code work; ok deraadt markus 20091002 - (djm) [Makefile.in] Mention readconf.o in ssh-keysign's make deps. diff --git a/sftp.1 b/sftp.1 index d47ab1573..6cec52ae1 100644 --- a/sftp.1 +++ b/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.71 2009/08/12 06:31:42 jmc Exp $ +.\" $OpenBSD: sftp.1,v 1.72 2009/08/13 01:11:19 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: August 12 2009 $ +.Dd $Mdocdate: August 13 2009 $ .Dt SFTP 1 .Os .Sh NAME @@ -35,10 +35,11 @@ .Op Fl B Ar buffer_size .Op Fl b Ar batchfile .Op Fl c Ar cipher +.Op Fl D Ar sftp_server_path .Op Fl F Ar ssh_config .Op Fl i Ar identity_path .Op Fl o Ar ssh_option -.Op Fl P Ar sftp_server_path +.Op Fl P Ar port .Op Fl R Ar num_requests .Op Fl S Ar program .Op Fl s Ar subsystem | sftp_server @@ -140,6 +141,11 @@ flag). Selects the cipher to use for encrypting the data transfers. This option is directly passed to .Xr ssh 1 . +.It Fl D Ar sftp_server_path +Connect directly to a local sftp server +(rather than via +.Xr ssh 1 ) . +This option may be useful in debugging the client and server. .It Fl F Ar ssh_config Specifies an alternative per-user configuration file for @@ -215,11 +221,8 @@ For full details of the options listed below, and their possible values, see .It UserKnownHostsFile .It VerifyHostKeyDNS .El -.It Fl P Ar sftp_server_path -Connect directly to a local sftp server -(rather than via -.Xr ssh 1 ) . -This option may be useful in debugging the client and server. +.It Fl P Ar port +Specifies the port to connect to on the remote host. .It Fl q Quiet mode: disables the progress meter as well as warning and diagnostic messages from diff --git a/sftp.c b/sftp.c index 798a72edb..4c1d55389 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.108 2009/08/12 00:13:00 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.109 2009/08/13 01:11:19 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1707,7 +1707,8 @@ main(int argc, char **argv) ll = SYSLOG_LEVEL_INFO; infile = stdin; - while ((ch = getopt(argc, argv, "1246hqvCc:i:o:s:S:b:B:F:P:R:")) != -1) { + while ((ch = getopt(argc, argv, + "1246hqvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case '4': @@ -1726,6 +1727,9 @@ main(int argc, char **argv) showprogress = 0; addargs(&args, "-%c", ch); break; + case 'P': + addargs(&args, "-oPort %s", optarg); + break; case 'v': if (debug_level < 3) { addargs(&args, "-v"); @@ -1758,7 +1762,7 @@ main(int argc, char **argv) batchmode = 1; addargs(&args, "-obatchmode yes"); break; - case 'P': + case 'D': sftp_direct = optarg; break; case 'R': -- cgit v1.2.3 From c07138e6f6a9173d0315cceb123e3bc4abbd1528 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Wed, 7 Oct 2009 08:23:44 +1100 Subject: - jmc@cvs.openbsd.org 2009/08/13 13:39:54 [sftp.1 sftp.c] sync synopsis and usage(); --- ChangeLog | 3 +++ sftp.1 | 4 ++-- sftp.c | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 46f5e9cfa..9afa9e820 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,9 @@ used by our regression scripts. part of larger patch from carlosvsilvapt@gmail.com for his Google Summer of Code work; ok deraadt markus + - jmc@cvs.openbsd.org 2009/08/13 13:39:54 + [sftp.1 sftp.c] + sync synopsis and usage(); 20091002 - (djm) [Makefile.in] Mention readconf.o in ssh-keysign's make deps. diff --git a/sftp.1 b/sftp.1 index 6cec52ae1..fcd1d240a 100644 --- a/sftp.1 +++ b/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.72 2009/08/13 01:11:19 djm Exp $ +.\" $OpenBSD: sftp.1,v 1.73 2009/08/13 13:39:54 jmc Exp $ .\" .\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" @@ -37,7 +37,7 @@ .Op Fl c Ar cipher .Op Fl D Ar sftp_server_path .Op Fl F Ar ssh_config -.Op Fl i Ar identity_path +.Op Fl i Ar identity_file .Op Fl o Ar ssh_option .Op Fl P Ar port .Op Fl R Ar num_requests diff --git a/sftp.c b/sftp.c index 4c1d55389..0123fd72c 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.109 2009/08/13 01:11:19 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.110 2009/08/13 13:39:54 jmc Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1669,8 +1669,10 @@ usage(void) fprintf(stderr, "usage: %s [-1246Cqv] [-B buffer_size] [-b batchfile] [-c cipher]\n" - " [-F ssh_config] [-i identify_file] [-o ssh_option]\n" - " [-P sftp_server_path] [-R num_requests] [-S program]\n" + " [-D sftp_server_path] [-F ssh_config] " + "[-i identity_file]\n" + " [-o ssh_option] [-P port] [-R num_requests] " + "[-S program]\n" " [-s subsystem | sftp_server] host\n" " %s [user@]host[:file ...]\n" " %s [user@]host[:dir[/]]\n" -- cgit v1.2.3 From 1b0dd175377c86abb7f3a3da2e9b1a89f36c7a1a Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Wed, 7 Oct 2009 08:37:48 +1100 Subject: - djm@cvs.openbsd.org 2009/08/18 18:36:21 [sftp-client.h sftp.1 sftp-client.c sftp.c] recursive transfer support for get/put and on the commandline work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code with some tweaks by me; "go for it" deraadt@ --- ChangeLog | 5 ++ sftp-client.c | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- sftp-client.h | 21 ++++- sftp.1 | 44 ++++++++-- sftp.c | 219 +++++++++++++++++++++++++------------------------ 5 files changed, 423 insertions(+), 126 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 60eae3a42..2fedeccbd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -32,6 +32,11 @@ - dtucker@cvs.openbsd.org 2009/08/16 23:29:26 [sshd_config.5] Add PubkeyAuthentication to the list allowed in a Match block (bz #1577) + - djm@cvs.openbsd.org 2009/08/18 18:36:21 + [sftp-client.h sftp.1 sftp-client.c sftp.c] + recursive transfer support for get/put and on the commandline + work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code + with some tweaks by me; "go for it" deraadt@ 20091002 - (djm) [Makefile.in] Mention readconf.o in ssh-keysign's make deps. diff --git a/sftp-client.c b/sftp-client.c index 14c172d2f..cc4a5b15b 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.c,v 1.88 2009/08/14 18:17:49 djm Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.89 2009/08/18 18:36:20 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -36,6 +36,7 @@ #endif #include +#include #include #include #include @@ -61,6 +62,9 @@ extern int showprogress; /* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 +/* Maximum depth to descend in directory trees */ +#define MAX_DIR_DEPTH 64 + struct sftp_conn { int fd_in; int fd_out; @@ -497,6 +501,17 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, if (printflag) printf("%s\n", longname); + /* + * Directory entries should never contain '/' + * These can be used to attack recursive ops + * (e.g. send '../../../../etc/passwd') + */ + if (strchr(filename, '/') != NULL) { + error("Server sent suspect path \"%s\" " + "during readdir of \"%s\"", filename, path); + goto next; + } + if (dir) { *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xmalloc(sizeof(***dir)); @@ -505,7 +520,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag, memcpy(&(*dir)[ents]->a, a, sizeof(*a)); (*dir)[++ents] = NULL; } - + next: xfree(filename); xfree(longname); } @@ -560,7 +575,7 @@ do_rm(struct sftp_conn *conn, char *path) } int -do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) +do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag) { u_int status, id; @@ -569,7 +584,7 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) strlen(path), a); status = get_status(conn->fd_in, id); - if (status != SSH2_FX_OK) + if (status != SSH2_FX_OK && printflag) error("Couldn't create directory: %s", fx2txt(status)); return(status); @@ -908,9 +923,9 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, int do_download(struct sftp_conn *conn, char *remote_path, char *local_path, - int pflag) + Attrib *a, int pflag) { - Attrib junk, *a; + Attrib junk; Buffer msg; char *handle; int local_fd, status = 0, write_error; @@ -929,9 +944,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, TAILQ_INIT(&requests); - a = do_stat(conn, remote_path, 0); - if (a == NULL) - return(-1); + if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) + return -1; /* Do not preserve set[ug]id here, as we do not preserve ownership */ if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) @@ -1146,6 +1160,114 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path, return(status); } +static int +download_dir_internal(struct sftp_conn *conn, char *src, char *dst, + Attrib *dirattrib, int pflag, int printflag, int depth) +{ + int i, ret = 0; + SFTP_DIRENT **dir_entries; + char *filename, *new_src, *new_dst; + mode_t mode = 0777; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (dirattrib == NULL && + (dirattrib = do_stat(conn, src, 1)) == NULL) { + error("Unable to stat remote directory \"%s\"", src); + return -1; + } + if (!S_ISDIR(dirattrib->perm)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (printflag) + printf("Retrieving %s\n", src); + + if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + mode = dirattrib->perm & 01777; + else { + debug("Server did not send permissions for " + "directory \"%s\"", dst); + } + + if (mkdir(dst, mode) == -1 && errno != EEXIST) { + error("mkdir %s: %s", dst, strerror(errno)); + return -1; + } + + if (do_readdir(conn, src, &dir_entries) == -1) { + error("%s: Failed to get directory contents", src); + return -1; + } + + for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { + filename = dir_entries[i]->filename; + + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (S_ISDIR(dir_entries[i]->a.perm)) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + if (download_dir_internal(conn, new_src, new_dst, + &(dir_entries[i]->a), pflag, printflag, + depth + 1) == -1) + ret = -1; + } else if (S_ISREG(dir_entries[i]->a.perm) ) { + if (do_download(conn, new_src, new_dst, + &(dir_entries[i]->a), pflag) == -1) { + error("Download of file %s to %s failed", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file\n", new_src); + + xfree(new_dst); + xfree(new_src); + } + + if (pflag) { + if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { + struct timeval tv[2]; + tv[0].tv_sec = dirattrib->atime; + tv[1].tv_sec = dirattrib->mtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + if (utimes(dst, tv) == -1) + error("Can't set times on \"%s\": %s", + dst, strerror(errno)); + } else + debug("Server did not send times for directory " + "\"%s\"", dst); + } + + free_sftp_dirents(dir_entries); + + return ret; +} + +int +download_dir(struct sftp_conn *conn, char *src, char *dst, + Attrib *dirattrib, int pflag, int printflag) +{ + char *src_canon; + int ret; + + if ((src_canon = do_realpath(conn, src)) == NULL) { + error("Unable to canonicalise path \"%s\"", src); + return -1; + } + + ret = download_dir_internal(conn, src_canon, dst, + dirattrib, pflag, printflag, 0); + xfree(src_canon); + return ret; +} + int do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, int pflag) @@ -1328,3 +1450,123 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, return status; } + +static int +upload_dir_internal(struct sftp_conn *conn, char *src, char *dst, + int pflag, int printflag, int depth) +{ + int ret = 0, status; + DIR *dirp; + struct dirent *dp; + char *filename, *new_src, *new_dst; + struct stat sb; + Attrib a; + + if (depth >= MAX_DIR_DEPTH) { + error("Maximum directory depth exceeded: %d levels", depth); + return -1; + } + + if (stat(src, &sb) == -1) { + error("Couldn't stat directory \"%s\": %s", + src, strerror(errno)); + return -1; + } + if (!S_ISDIR(sb.st_mode)) { + error("\"%s\" is not a directory", src); + return -1; + } + if (printflag) + printf("Entering %s\n", src); + + attrib_clear(&a); + stat_to_attrib(&sb, &a); + a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; + a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; + a.perm &= 01777; + if (!pflag) + a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; + + status = do_mkdir(conn, dst, &a, 0); + /* + * we lack a portable status for errno EEXIST, + * so if we get a SSH2_FX_FAILURE back we must check + * if it was created successfully. + */ + if (status != SSH2_FX_OK) { + if (status != SSH2_FX_FAILURE) + return -1; + if (do_stat(conn, dst, 0) == NULL) + return -1; + } + + if ((dirp = opendir(src)) == NULL) { + error("Failed to open dir \"%s\": %s", src, strerror(errno)); + return -1; + } + + while (((dp = readdir(dirp)) != NULL) && !interrupted) { + if (dp->d_ino == 0) + continue; + filename = dp->d_name; + new_dst = path_append(dst, filename); + new_src = path_append(src, filename); + + if (S_ISDIR(DTTOIF(dp->d_type))) { + if (strcmp(filename, ".") == 0 || + strcmp(filename, "..") == 0) + continue; + + if (upload_dir_internal(conn, new_src, new_dst, + pflag, depth + 1, printflag) == -1) + ret = -1; + } else if (S_ISREG(DTTOIF(dp->d_type)) ) { + if (do_upload(conn, new_src, new_dst, pflag) == -1) { + error("Uploading of file %s to %s failed!", + new_src, new_dst); + ret = -1; + } + } else + logit("%s: not a regular file\n", filename); + xfree(new_dst); + xfree(new_src); + } + + do_setstat(conn, dst, &a); + + (void) closedir(dirp); + return ret; +} + +int +upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag, + int pflag) +{ + char *dst_canon; + int ret; + + if ((dst_canon = do_realpath(conn, dst)) == NULL) { + error("Unable to canonicalise path \"%s\"", dst); + return -1; + } + + ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0); + xfree(dst_canon); + return ret; +} + +char * +path_append(char *p1, char *p2) +{ + char *ret; + size_t len = strlen(p1) + strlen(p2) + 2; + + ret = xmalloc(len); + strlcpy(ret, p1, len); + if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') + strlcat(ret, "/", len); + strlcat(ret, p2, len); + + return(ret); +} + diff --git a/sftp-client.h b/sftp-client.h index edb46790f..1d08c4049 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.17 2008/06/08 20:15:29 dtucker Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller @@ -68,7 +68,7 @@ void free_sftp_dirents(SFTP_DIRENT **); int do_rm(struct sftp_conn *, char *); /* Create directory 'path' */ -int do_mkdir(struct sftp_conn *, char *, Attrib *); +int do_mkdir(struct sftp_conn *, char *, Attrib *, int); /* Remove directory 'path' */ int do_rmdir(struct sftp_conn *, char *); @@ -103,7 +103,13 @@ int do_symlink(struct sftp_conn *, char *, char *); * Download 'remote_path' to 'local_path'. Preserve permissions and times * if 'pflag' is set */ -int do_download(struct sftp_conn *, char *, char *, int); +int do_download(struct sftp_conn *, char *, char *, Attrib *, int); + +/* + * Recursively download 'remote_directory' to 'local_directory'. Preserve + * times if 'pflag' is set + */ +int download_dir(struct sftp_conn *, char *, char *, Attrib *, int, int); /* * Upload 'local_path' to 'remote_path'. Preserve permissions and times @@ -111,4 +117,13 @@ int do_download(struct sftp_conn *, char *, char *, int); */ int do_upload(struct sftp_conn *, char *, char *, int); +/* + * Recursively upload 'local_directory' to 'remote_directory'. Preserve + * times if 'pflag' is set + */ +int upload_dir(struct sftp_conn *, char *, char *, int, int); + +/* Concatenate paths, taking care of slashes. Caller must free result. */ +char *path_append(char *, char *); + #endif diff --git a/sftp.1 b/sftp.1 index fcd1d240a..21dc5d8d6 100644 --- a/sftp.1 +++ b/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.73 2009/08/13 13:39:54 jmc Exp $ +.\" $OpenBSD: sftp.1,v 1.74 2009/08/18 18:36:20 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: August 13 2009 $ +.Dd $Mdocdate: August 18 2009 $ .Dt SFTP 1 .Os .Sh NAME @@ -31,7 +31,7 @@ .Sh SYNOPSIS .Nm sftp .Bk -words -.Op Fl 1246Cqv +.Op Fl 1246Cpqrv .Op Fl B Ar buffer_size .Op Fl b Ar batchfile .Op Fl c Ar cipher @@ -223,6 +223,9 @@ For full details of the options listed below, and their possible values, see .El .It Fl P Ar port Specifies the port to connect to on the remote host. +.It Fl p +Preserves modification times, access times, and modes from the +original files transferred. .It Fl q Quiet mode: disables the progress meter as well as warning and diagnostic messages from @@ -232,6 +235,11 @@ Specify how many requests may be outstanding at any one time. Increasing this may slightly improve file transfer speed but will increase memory usage. The default is 64 outstanding requests. +.It Fl r +Recursively copy entire directories when uploading and downloading. +Note that +.Nm +does not follow symbolic links encountered in the tree traversal. .It Fl S Ar program Name of the .Ar program @@ -322,7 +330,7 @@ extension. Quit .Nm sftp . .It Xo Ic get -.Op Fl P +.Op Fl Ppr .Ar remote-path .Op Ar local-path .Xc @@ -341,10 +349,20 @@ If it does and is specified, then .Ar local-path must specify a directory. -If the -.Fl P +.Pp +If ether the +.Fl Ppr +or +.Fl p flag is specified, then full file permissions and access times are copied too. +.Pp +If the +.Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. .It Ic help Display help text. .It Ic lcd Ar path @@ -440,10 +458,20 @@ If it does and is specified, then .Ar remote-path must specify a directory. -If the +.Pp +If ether the .Fl P -flag is specified, then the file's full permission and access time are +or +.Fl p +flag is specified, then full file permissions and access times are copied too. +.Pp +If the +.Fl r +flag is specified then directories will be copied recursively. +Note that +.Nm +does not follow symbolic links when performing recursive transfers. .It Ic pwd Display remote working directory. .It Ic quit diff --git a/sftp.c b/sftp.c index 0123fd72c..75b16b27e 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.110 2009/08/13 13:39:54 jmc Exp $ */ +/* $OpenBSD: sftp.c,v 1.111 2009/08/18 18:36:21 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -35,6 +35,9 @@ #ifdef HAVE_PATHS_H # include #endif +#ifdef HAVE_LIBGEN_H +#include +#endif #ifdef USE_LIBEDIT #include #else @@ -83,6 +86,12 @@ static pid_t sshpid = -1; /* This is set to 0 if the progressmeter is not desired. */ int showprogress = 1; +/* When this option is set, we always recursively download/upload directories */ +int global_rflag = 0; + +/* When this option is set, the file transfers will always preserve times */ +int global_pflag = 0; + /* SIGINT received during command processing */ volatile sig_atomic_t interrupted = 0; @@ -216,7 +225,7 @@ help(void) "df [-hi] [path] Display statistics for current directory or\n" " filesystem containing 'path'\n" "exit Quit sftp\n" - "get [-P] remote-path [local-path] Download file\n" + "get [-Pr] remote-path [local-path] Download file\n" "help Display this help text\n" "lcd path Change local directory to 'path'\n" "lls [ls-options [path]] Display local directory listing\n" @@ -227,7 +236,7 @@ help(void) "lumask umask Set local umask to 'umask'\n" "mkdir path Create remote directory\n" "progress Toggle display of progress meter\n" - "put [-P] local-path [remote-path] Upload file\n" + "put [-Pr] local-path [remote-path] Upload file\n" "pwd Display remote working directory\n" "quit Quit sftp\n" "rename oldpath newpath Rename remote file\n" @@ -313,21 +322,6 @@ path_strip(char *path, char *strip) return (xstrdup(path)); } -static char * -path_append(char *p1, char *p2) -{ - char *ret; - size_t len = strlen(p1) + strlen(p2) + 2; - - ret = xmalloc(len); - strlcpy(ret, p1, len); - if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') - strlcat(ret, "/", len); - strlcat(ret, p2, len); - - return(ret); -} - static char * make_absolute(char *p, char *pwd) { @@ -343,27 +337,8 @@ make_absolute(char *p, char *pwd) } static int -infer_path(const char *p, char **ifp) -{ - char *cp; - - cp = strrchr(p, '/'); - if (cp == NULL) { - *ifp = xstrdup(p); - return(0); - } - - if (!cp[1]) { - error("Invalid path"); - return(-1); - } - - *ifp = xstrdup(cp + 1); - return(0); -} - -static int -parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) +parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, + int *rflag) { extern int opterr, optind, optopt, optreset; int ch; @@ -371,13 +346,17 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) optind = optreset = 1; opterr = 0; - *pflag = 0; - while ((ch = getopt(argc, argv, "Pp")) != -1) { + *rflag = *pflag = 0; + while ((ch = getopt(argc, argv, "PpRr")) != -1) { switch (ch) { case 'p': case 'P': *pflag = 1; break; + case 'r': + case 'R': + *rflag = 1; + break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; @@ -489,62 +468,79 @@ remote_is_dir(struct sftp_conn *conn, char *path) return(S_ISDIR(a->perm)); } +/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ +static int +pathname_is_dir(char *pathname) +{ + size_t l = strlen(pathname); + + return l > 0 && pathname[l - 1] == '/'; +} + static int -process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) +process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, + int pflag, int rflag) { char *abs_src = NULL; char *abs_dst = NULL; - char *tmp; glob_t g; - int err = 0; - int i; + char *filename, *tmp=NULL; + int i, err = 0; abs_src = xstrdup(src); abs_src = make_absolute(abs_src, pwd); - memset(&g, 0, sizeof(g)); + debug3("Looking up %s", abs_src); - if (remote_glob(conn, abs_src, 0, NULL, &g)) { + if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", abs_src); err = -1; goto out; } - /* If multiple matches, dst must be a directory or unspecified */ - if (g.gl_matchc > 1 && dst && !is_dir(dst)) { - error("Multiple files match, but \"%s\" is not a directory", - dst); + /* + * If multiple matches then dst must be a directory or + * unspecified. + */ + if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { + error("Multiple source paths, but destination " + "\"%s\" is not a directory", dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { - if (infer_path(g.gl_pathv[i], &tmp)) { + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + xfree(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && dst) { - /* If directory specified, append filename */ - xfree(tmp); if (is_dir(dst)) { - if (infer_path(g.gl_pathv[0], &tmp)) { - err = 1; - goto out; - } - abs_dst = path_append(dst, tmp); - xfree(tmp); - } else + abs_dst = path_append(dst, filename); + } else { abs_dst = xstrdup(dst); + } } else if (dst) { - abs_dst = path_append(dst, tmp); - xfree(tmp); - } else - abs_dst = tmp; + abs_dst = path_append(dst, filename); + } else { + abs_dst = xstrdup(filename); + } + xfree(tmp); printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1) - err = -1; + if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag, 1) == -1) + err = -1; + } else { + if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, + pflag || global_pflag) == -1) + err = -1; + } xfree(abs_dst); abs_dst = NULL; } @@ -556,14 +552,15 @@ out: } static int -process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) +process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, + int pflag, int rflag) { char *tmp_dst = NULL; char *abs_dst = NULL; - char *tmp; + char *tmp = NULL, *filename = NULL; glob_t g; int err = 0; - int i; + int i, dst_is_dir = 1; struct stat sb; if (dst) { @@ -573,16 +570,20 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) memset(&g, 0, sizeof(g)); debug3("Looking up %s", src); - if (glob(src, GLOB_NOCHECK, NULL, &g)) { + if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", src); err = -1; goto out; } + /* If we aren't fetching to pwd then stash this status for later */ + if (tmp_dst != NULL) + dst_is_dir = remote_is_dir(conn, tmp_dst); + /* If multiple matches, dst may be directory or unspecified */ - if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) { - error("Multiple files match, but \"%s\" is not a directory", - tmp_dst); + if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { + error("Multiple paths match, but destination " + "\"%s\" is not a directory", tmp_dst); err = -1; goto out; } @@ -593,38 +594,38 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) error("stat %s: %s", g.gl_pathv[i], strerror(errno)); continue; } - - if (!S_ISREG(sb.st_mode)) { - error("skipping non-regular file %s", - g.gl_pathv[i]); - continue; - } - if (infer_path(g.gl_pathv[i], &tmp)) { + + tmp = xstrdup(g.gl_pathv[i]); + if ((filename = basename(tmp)) == NULL) { + error("basename %s: %s", tmp, strerror(errno)); + xfree(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && tmp_dst) { /* If directory specified, append filename */ - if (remote_is_dir(conn, tmp_dst)) { - if (infer_path(g.gl_pathv[0], &tmp)) { - err = 1; - goto out; - } - abs_dst = path_append(tmp_dst, tmp); - xfree(tmp); - } else + if (dst_is_dir) + abs_dst = path_append(tmp_dst, filename); + else abs_dst = xstrdup(tmp_dst); - } else if (tmp_dst) { - abs_dst = path_append(tmp_dst, tmp); - xfree(tmp); - } else - abs_dst = make_absolute(tmp, pwd); + abs_dst = path_append(tmp_dst, filename); + } else { + abs_dst = make_absolute(xstrdup(filename), pwd); + } + xfree(tmp); printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); - if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1) - err = -1; + if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { + if (upload_dir(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag, 1) == -1) + err = -1; + } else { + if (do_upload(conn, g.gl_pathv[i], abs_dst, + pflag || global_pflag) == -1) + err = -1; + } } out: @@ -1065,7 +1066,7 @@ makeargv(const char *arg, int *argcp) } static int -parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, +parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int *hflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; @@ -1109,13 +1110,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, } /* Get arguments and parse flags */ - *lflag = *pflag = *hflag = *n_arg = 0; + *lflag = *pflag = *rflag = *hflag = *n_arg = 0; *path1 = *path2 = NULL; optidx = 1; switch (cmdnum) { case I_GET: case I_PUT: - if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) + if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) return -1; /* Get first pathname (mandatory) */ if (argc - optidx < 1) { @@ -1235,7 +1236,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, int err_abort) { char *path1, *path2, *tmp; - int pflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; + int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; unsigned long n_arg = 0; Attrib a, *aa; char path_buf[MAXPATHLEN]; @@ -1243,7 +1244,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, glob_t g; path1 = path2 = NULL; - cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, + cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, &path1, &path2); if (iflag != 0) @@ -1261,10 +1262,10 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, err = -1; break; case I_GET: - err = process_get(conn, path1, path2, *pwd, pflag); + err = process_get(conn, path1, path2, *pwd, pflag, rflag); break; case I_PUT: - err = process_put(conn, path1, path2, *pwd, pflag); + err = process_put(conn, path1, path2, *pwd, pflag, rflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd); @@ -1290,7 +1291,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = 0777; - err = do_mkdir(conn, path1, &a); + err = do_mkdir(conn, path1, &a, 1); break; case I_RMDIR: path1 = make_absolute(path1, *pwd); @@ -1668,7 +1669,7 @@ usage(void) extern char *__progname; fprintf(stderr, - "usage: %s [-1246Cqv] [-B buffer_size] [-b batchfile] [-c cipher]\n" + "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" " [-D sftp_server_path] [-F ssh_config] " "[-i identity_file]\n" " [-o ssh_option] [-P port] [-R num_requests] " @@ -1710,7 +1711,7 @@ main(int argc, char **argv) infile = stdin; while ((ch = getopt(argc, argv, - "1246hqvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { + "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case '4': @@ -1764,9 +1765,15 @@ main(int argc, char **argv) batchmode = 1; addargs(&args, "-obatchmode yes"); break; + case 'p': + global_pflag = 1; + break; case 'D': sftp_direct = optarg; break; + case 'r': + global_rflag = 1; + break; case 'R': num_requests = strtol(optarg, &cp, 10); if (num_requests == 0 || *cp != '\0') -- cgit v1.2.3 From 210631922f86192e99aefe6cf985155d61f44965 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 17:10:36 +1100 Subject: - djm@cvs.openbsd.org 2009/11/20 00:54:01 [sftp.c] bz#1588 change "Connecting to host..." message to "Connected to host." and delay it until after the sftp protocol connection has been established. Avoids confusing sequence of messages when the underlying ssh connection experiences problems. ok dtucker@ --- ChangeLog | 6 ++++++ sftp.c | 40 +++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 19 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 16b9c133a..6494edc0f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -72,6 +72,12 @@ Warn but do not fail if stat()ing the subsystem binary fails. This helps with chrootdirectory+forcecommand=sftp-server and restricted shells. bz #1599, ok djm. + - djm@cvs.openbsd.org 2009/11/20 00:54:01 + [sftp.c] + bz#1588 change "Connecting to host..." message to "Connected to host." + and delay it until after the sftp protocol connection has been established. + Avoids confusing sequence of messages when the underlying ssh connection + experiences problems. ok dtucker@ 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/sftp.c b/sftp.c index 75b16b27e..85e5505b5 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.111 2009/08/18 18:36:21 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.112 2009/11/20 00:54:01 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -68,18 +68,15 @@ typedef void EditLine; #include "sftp-common.h" #include "sftp-client.h" +#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ +#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ + /* File to read commands from */ FILE* infile; /* Are we in batchfile mode? */ int batchmode = 0; -/* Size of buffer used when copying files */ -size_t copy_buffer_len = 32768; - -/* Number of concurrent outstanding requests */ -size_t num_requests = 64; - /* PID of ssh transport process */ static pid_t sshpid = -1; @@ -187,7 +184,7 @@ static const struct CMD cmds[] = { { NULL, -1} }; -int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); +int interactive_loop(struct sftp_conn *, char *file1, char *file2); /* ARGSUSED */ static void @@ -1472,12 +1469,11 @@ prompt(EditLine *el) #endif int -interactive_loop(int fd_in, int fd_out, char *file1, char *file2) +interactive_loop(struct sftp_conn *conn, char *file1, char *file2) { char *pwd; char *dir = NULL; char cmd[2048]; - struct sftp_conn *conn; int err, interactive; EditLine *el = NULL; #ifdef USE_LIBEDIT @@ -1501,10 +1497,6 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2) } #endif /* USE_LIBEDIT */ - conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests); - if (conn == NULL) - fatal("Couldn't initialise connection to server"); - pwd = do_realpath(conn, "."); if (pwd == NULL) fatal("Need cwd"); @@ -1694,6 +1686,9 @@ main(int argc, char **argv) arglist args; extern int optind; extern char *optarg; + struct sftp_conn *conn; + size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; + size_t num_requests = DEFAULT_NUM_REQUESTS; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -1837,20 +1832,27 @@ main(int argc, char **argv) addargs(&args, "%s", (sftp_server != NULL ? sftp_server : "sftp")); - if (!batchmode) - fprintf(stderr, "Connecting to %s...\n", host); connect_to_server(ssh_program, args.list, &in, &out); } else { args.list = NULL; addargs(&args, "sftp-server"); - if (!batchmode) - fprintf(stderr, "Attaching to %s...\n", sftp_direct); connect_to_server(sftp_direct, args.list, &in, &out); } freeargs(&args); - err = interactive_loop(in, out, file1, file2); + conn = do_init(in, out, copy_buffer_len, num_requests); + if (conn == NULL) + fatal("Couldn't initialise connection to server"); + + if (!batchmode) { + if (sftp_direct == NULL) + fprintf(stderr, "Connected to %s.\n", host); + else + fprintf(stderr, "Attached to %s.\n", sftp_direct); + } + + err = interactive_loop(conn, file1, file2); #if !defined(USE_PIPES) shutdown(in, SHUT_RDWR); -- cgit v1.2.3 From c4dc4f5bac9e19a99a4d391e98d012c94c647ca4 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 18:50:04 +1100 Subject: - halex@cvs.openbsd.org 2009/11/22 13:18:00 [sftp.c] make passing of zero-length arguments to ssh safe by passing "-" "" rather than "-" ok dtucker@, guenther@, djm@ --- ChangeLog | 5 +++++ sftp.c | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 7a2a0e322..6d6dacd88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -87,6 +87,11 @@ to expand EXPAND_MAX_KEYS, allowing only EXPAND_MAX_KEYS-1 to actually work. Note that nothing in OpenSSH actually uses close to this limit at present. bz#1607 from Jan.Pechanec AT Sun.COM + - halex@cvs.openbsd.org 2009/11/22 13:18:00 + [sftp.c] + make passing of zero-length arguments to ssh safe by + passing "-" "" rather than "-" + ok dtucker@, guenther@, djm@ 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/sftp.c b/sftp.c index 85e5505b5..2ce7cc1e1 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.112 2009/11/20 00:54:01 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.113 2009/11/22 13:18:00 halex Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1719,7 +1719,8 @@ main(int argc, char **argv) case 'c': case 'i': case 'o': - addargs(&args, "-%c%s", ch, optarg); + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); break; case 'q': showprogress = 0; -- cgit v1.2.3 From b5082e90a13c9c9f96f1aed894f70f6f00737396 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 18:51:47 +1100 Subject: - dtucker@cvs.openbsd.org 2009/12/06 23:53:54 [sftp.c] fix potential divide-by-zero in sftp's "df" output when talking to a server that reports zero files on the filesystem (Unix filesystems always have at least the root inode). From Steve McClellan at radisys, ok djm@ --- ChangeLog | 5 +++++ sftp.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 94fb34c3b..cb64d640e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -99,6 +99,11 @@ [roaming_common.c] use socklen_t for getsockopt optlen parameter; reported by Steve.McClellan AT radisys.com, ok dtucker@ + - dtucker@cvs.openbsd.org 2009/12/06 23:53:54 + [sftp.c] + fix potential divide-by-zero in sftp's "df" output when talking to a server + that reports zero files on the filesystem (Unix filesystems always have at + least the root inode). From Steve McClellan at radisys, ok djm@ 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/sftp.c b/sftp.c index 2ce7cc1e1..1aa37423c 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.113 2009/11/22 13:18:00 halex Exp $ */ +/* $OpenBSD: sftp.c,v 1.114 2009/12/06 23:53:54 dtucker Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -846,19 +846,19 @@ do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) char s_avail[FMT_SCALED_STRSIZE]; char s_root[FMT_SCALED_STRSIZE]; char s_total[FMT_SCALED_STRSIZE]; + unsigned long long ffree; if (do_statvfs(conn, path, &st, 1) == -1) return -1; if (iflag) { + ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; printf(" Inodes Used Avail " "(root) %%Capacity\n"); printf("%11llu %11llu %11llu %11llu %3llu%%\n", (unsigned long long)st.f_files, (unsigned long long)(st.f_files - st.f_ffree), (unsigned long long)st.f_favail, - (unsigned long long)st.f_ffree, - (unsigned long long)(100 * (st.f_files - st.f_ffree) / - st.f_files)); + (unsigned long long)st.f_ffree, ffree); } else if (hflag) { strlcpy(s_used, "error", sizeof(s_used)); strlcpy(s_avail, "error", sizeof(s_avail)); -- cgit v1.2.3 From b8c884a0ba4050e4267be786414127c0f09d5544 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 18:53:43 +1100 Subject: - guenther@cvs.openbsd.org 2009/12/20 07:28:36 [ssh.c sftp.c scp.c] When passing user-controlled options with arguments to other programs, pass the option and option argument as separate argv entries and not smashed into one (e.g., as -l foo and not -lfoo). Also, always pass a "--" argument to stop option parsing, so that a positional argument that starts with a '-' isn't treated as an option. This fixes some error cases as well as the handling of hostnames and filenames that start with a '-'. Based on a diff by halex@ ok halex@ djm@ deraadt@ --- ChangeLog | 11 +++++++++++ scp.c | 21 ++++++++++++++------- sftp.c | 6 ++++-- ssh.c | 4 ++-- sshd_config.5 | 10 +++++----- 5 files changed, 36 insertions(+), 16 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 45f758529..605e0dca7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -108,6 +108,17 @@ [key.c] switch from 35 to the more common value of RSA_F4 == (2**16)+1 == 65537 for the RSA public exponent; discussed with provos; ok djm@ + - guenther@cvs.openbsd.org 2009/12/20 07:28:36 + [ssh.c sftp.c scp.c] + When passing user-controlled options with arguments to other programs, + pass the option and option argument as separate argv entries and + not smashed into one (e.g., as -l foo and not -lfoo). Also, always + pass a "--" argument to stop option parsing, so that a positional + argument that starts with a '-' isn't treated as an option. This + fixes some error cases as well as the handling of hostnames and + filenames that start with a '-'. + Based on a diff by halex@ + ok halex@ djm@ deraadt@ 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/scp.c b/scp.c index 323747806..09efb82ac 100644 --- a/scp.c +++ b/scp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scp.c,v 1.164 2008/10/10 04:55:16 stevesk Exp $ */ +/* $OpenBSD: scp.c,v 1.165 2009/12/20 07:28:36 guenther Exp $ */ /* * scp - secure remote copy. This is basically patched BSD rcp which * uses ssh to do the data transfer (instead of using rcmd). @@ -244,8 +244,11 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) close(pout[1]); replacearg(&args, 0, "%s", ssh_program); - if (remuser != NULL) - addargs(&args, "-l%s", remuser); + if (remuser != NULL) { + addargs(&args, "-l"); + addargs(&args, "%s", remuser); + } + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", cmd); @@ -337,10 +340,12 @@ main(int argc, char **argv) case 'c': case 'i': case 'F': - addargs(&args, "-%c%s", ch, optarg); + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); break; case 'P': - addargs(&args, "-p%s", optarg); + addargs(&args, "-p"); + addargs(&args, "%s", optarg); break; case 'B': addargs(&args, "-oBatchmode yes"); @@ -548,6 +553,7 @@ toremote(char *targ, int argc, char **argv) } else { host = cleanhostname(argv[i]); } + addargs(&alist, "--"); addargs(&alist, "%s", host); addargs(&alist, "%s", cmd); addargs(&alist, "%s", src); @@ -558,7 +564,7 @@ toremote(char *targ, int argc, char **argv) errs = 1; } else { /* local to remote */ if (remin == -1) { - xasprintf(&bp, "%s -t %s", cmd, targ); + xasprintf(&bp, "%s -t -- %s", cmd, targ); host = cleanhostname(thost); if (do_cmd(host, tuser, bp, &remin, &remout) < 0) @@ -591,6 +597,7 @@ tolocal(int argc, char **argv) addargs(&alist, "-r"); if (pflag) addargs(&alist, "-p"); + addargs(&alist, "--"); addargs(&alist, "%s", argv[i]); addargs(&alist, "%s", argv[argc-1]); if (do_local_cmd(&alist)) @@ -610,7 +617,7 @@ tolocal(int argc, char **argv) suser = pwd->pw_name; } host = cleanhostname(host); - xasprintf(&bp, "%s -f %s", cmd, src); + xasprintf(&bp, "%s -f -- %s", cmd, src); if (do_cmd(host, suser, bp, &remin, &remout) < 0) { (void) xfree(bp); ++errs; diff --git a/sftp.c b/sftp.c index 1aa37423c..d8728cc25 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.114 2009/12/06 23:53:54 dtucker Exp $ */ +/* $OpenBSD: sftp.c,v 1.115 2009/12/20 07:28:36 guenther Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1809,7 +1809,8 @@ main(int argc, char **argv) fprintf(stderr, "Missing username\n"); usage(); } - addargs(&args, "-l%s", userhost); + addargs(&args, "-l"); + addargs(&args, "%s", userhost); } if ((cp = colon(host)) != NULL) { @@ -1829,6 +1830,7 @@ main(int argc, char **argv) if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) addargs(&args, "-s"); + addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", (sftp_server != NULL ? sftp_server : "sftp")); diff --git a/ssh.c b/ssh.c index 90dbc69e9..6abf31b52 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.328 2009/10/28 16:38:18 reyk Exp $ */ +/* $OpenBSD: ssh.c,v 1.329 2009/12/20 07:28:36 guenther Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -528,7 +528,7 @@ main(int ac, char **av) ac -= optind; av += optind; - if (ac > 0 && !host && **av != '-') { + if (ac > 0 && !host) { if (strrchr(*av, '@')) { p = xstrdup(*av); cp = strrchr(p, '@'); diff --git a/sshd_config.5 b/sshd_config.5 index e54e70079..6d2ad9df0 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.112 2009/11/10 02:58:56 djm Exp $ -.Dd $Mdocdate: November 10 2009 $ +.\" $OpenBSD: sshd_config.5,v 1.113 2009/12/19 16:53:13 stevesk Exp $ +.Dd $Mdocdate: December 19 2009 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -182,16 +182,16 @@ PAM or though authentication styles supported in The default is .Dq yes . .It Cm ChrootDirectory -Specifies a path to +Specifies the pathname of a directory to .Xr chroot 2 to after authentication. -This path, and all its components, must be root-owned directories that are +All components of the pathname must be root-owned directories that are not writable by any other user or group. After the chroot, .Xr sshd 8 changes the working directory to the user's home directory. .Pp -The path may contain the following tokens that are expanded at runtime once +The pathname may contain the following tokens that are expanded at runtime once the connecting user has been authenticated: %% is replaced by a literal '%', %h is replaced by the home directory of the user being authenticated, and %u is replaced by the username of that user. -- cgit v1.2.3 From 909d858d6b86c789003b809a636c2c228d3f30c5 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 19:02:40 +1100 Subject: - djm@cvs.openbsd.org 2010/01/04 02:03:57 [sftp.c] Implement tab-completion of commands, local and remote filenames for sftp. Hacked on and off for some time by myself, mouring, Carlos Silva (via 2009 Google Summer of Code) and polished to a fine sheen by myself again. It should deal more-or-less correctly with the ikky corner-cases presented by quoted filenames, but the UI could still be slightly improved. In particular, it is quite slow for remote completion on large directories. bz#200; ok markus@ --- ChangeLog | 9 ++ sftp.c | 484 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 443 insertions(+), 50 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index ac3ca7b02..70e3c15e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -141,6 +141,15 @@ [sshconnect2.c] Don't escape backslashes in the SSH2 banner. bz#1533, patch from Michal Gorny via Gentoo. + - djm@cvs.openbsd.org 2010/01/04 02:03:57 + [sftp.c] + Implement tab-completion of commands, local and remote filenames for sftp. + Hacked on and off for some time by myself, mouring, Carlos Silva (via 2009 + Google Summer of Code) and polished to a fine sheen by myself again. + It should deal more-or-less correctly with the ikky corner-cases presented + by quoted filenames, but the UI could still be slightly improved. + In particular, it is quite slow for remote completion on large directories. + bz#200; ok markus@ 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/sftp.c b/sftp.c index d8728cc25..6a5ccc49d 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.115 2009/12/20 07:28:36 guenther Exp $ */ +/* $OpenBSD: sftp.c,v 1.116 2010/01/04 02:03:57 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -95,6 +95,12 @@ volatile sig_atomic_t interrupted = 0; /* I wish qsort() took a separate ctx for the comparison function...*/ int sort_flag; +/* Context used for commandline completion */ +struct complete_ctx { + struct sftp_conn *conn; + char **remote_pathp; +}; + int remote_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ @@ -145,43 +151,47 @@ extern char *__progname; struct CMD { const char *c; const int n; + const int t; }; +/* Type of completion */ +#define NOARGS 0 +#define REMOTE 1 +#define LOCAL 2 + static const struct CMD cmds[] = { - { "bye", I_QUIT }, - { "cd", I_CHDIR }, - { "chdir", I_CHDIR }, - { "chgrp", I_CHGRP }, - { "chmod", I_CHMOD }, - { "chown", I_CHOWN }, - { "df", I_DF }, - { "dir", I_LS }, - { "exit", I_QUIT }, - { "get", I_GET }, - { "mget", I_GET }, - { "help", I_HELP }, - { "lcd", I_LCHDIR }, - { "lchdir", I_LCHDIR }, - { "lls", I_LLS }, - { "lmkdir", I_LMKDIR }, - { "ln", I_SYMLINK }, - { "lpwd", I_LPWD }, - { "ls", I_LS }, - { "lumask", I_LUMASK }, - { "mkdir", I_MKDIR }, - { "progress", I_PROGRESS }, - { "put", I_PUT }, - { "mput", I_PUT }, - { "pwd", I_PWD }, - { "quit", I_QUIT }, - { "rename", I_RENAME }, - { "rm", I_RM }, - { "rmdir", I_RMDIR }, - { "symlink", I_SYMLINK }, - { "version", I_VERSION }, - { "!", I_SHELL }, - { "?", I_HELP }, - { NULL, -1} + { "bye", I_QUIT, NOARGS }, + { "cd", I_CHDIR, REMOTE }, + { "chdir", I_CHDIR, REMOTE }, + { "chgrp", I_CHGRP, REMOTE }, + { "chmod", I_CHMOD, REMOTE }, + { "chown", I_CHOWN, REMOTE }, + { "df", I_DF, REMOTE }, + { "dir", I_LS, REMOTE }, + { "exit", I_QUIT, NOARGS }, + { "get", I_GET, REMOTE }, + { "help", I_HELP, NOARGS }, + { "lcd", I_LCHDIR, LOCAL }, + { "lchdir", I_LCHDIR, LOCAL }, + { "lls", I_LLS, LOCAL }, + { "lmkdir", I_LMKDIR, LOCAL }, + { "ln", I_SYMLINK, REMOTE }, + { "lpwd", I_LPWD, LOCAL }, + { "ls", I_LS, REMOTE }, + { "lumask", I_LUMASK, NOARGS }, + { "mkdir", I_MKDIR, REMOTE }, + { "progress", I_PROGRESS, NOARGS }, + { "put", I_PUT, LOCAL }, + { "pwd", I_PWD, REMOTE }, + { "quit", I_QUIT, NOARGS }, + { "rename", I_RENAME, REMOTE }, + { "rm", I_RM, REMOTE }, + { "rmdir", I_RMDIR, REMOTE }, + { "symlink", I_SYMLINK, REMOTE }, + { "version", I_VERSION, NOARGS }, + { "!", I_SHELL, NOARGS }, + { "?", I_HELP, NOARGS }, + { NULL, -1, -1 } }; int interactive_loop(struct sftp_conn *, char *file1, char *file2); @@ -932,12 +942,23 @@ undo_glob_escape(char *s) * Split a string into an argument vector using sh(1)-style quoting, * comment and escaping rules, but with some tweaks to handle glob(3) * wildcards. + * The "sloppy" flag allows for recovery from missing terminating quote, for + * use in parsing incomplete commandlines during tab autocompletion. + * * Returns NULL on error or a NULL-terminated array of arguments. + * + * If "lastquote" is not NULL, the quoting character used for the last + * argument is placed in *lastquote ("\0", "'" or "\""). + * + * If "terminated" is not NULL, *terminated will be set to 1 when the + * last argument's quote has been properly terminated or 0 otherwise. + * This parameter is only of use if "sloppy" is set. */ #define MAXARGS 128 #define MAXARGLEN 8192 static char ** -makeargv(const char *arg, int *argcp) +makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, + u_int *terminated) { int argc, quot; size_t i, j; @@ -951,6 +972,10 @@ makeargv(const char *arg, int *argcp) error("string too long"); return NULL; } + if (terminated != NULL) + *terminated = 1; + if (lastquote != NULL) + *lastquote = '\0'; state = MA_START; i = j = 0; for (;;) { @@ -967,6 +992,8 @@ makeargv(const char *arg, int *argcp) if (state == MA_START) { argv[argc] = argvs + j; state = q; + if (lastquote != NULL) + *lastquote = arg[i]; } else if (state == MA_UNQUOTED) state = q; else if (state == q) @@ -1003,6 +1030,8 @@ makeargv(const char *arg, int *argcp) if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; } if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*' || arg[i + 1] == '\\') { @@ -1028,6 +1057,12 @@ makeargv(const char *arg, int *argcp) goto string_done; } else if (arg[i] == '\0') { if (state == MA_SQUOTE || state == MA_DQUOTE) { + if (sloppy) { + state = MA_UNQUOTED; + if (terminated != NULL) + *terminated = 0; + goto string_done; + } error("Unterminated quoted argument"); return NULL; } @@ -1041,6 +1076,8 @@ makeargv(const char *arg, int *argcp) if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; + if (lastquote != NULL) + *lastquote = '\0'; } if ((state == MA_SQUOTE || state == MA_DQUOTE) && (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { @@ -1063,8 +1100,8 @@ makeargv(const char *arg, int *argcp) } 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) +parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, + int *hflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; char *cp2, **argv; @@ -1086,7 +1123,7 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, int cp++; } - if ((argv = makeargv(cp, &argc)) == NULL) + if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) return -1; /* Figure out which command we have */ @@ -1468,10 +1505,344 @@ prompt(EditLine *el) } #endif +/* Display entries in 'list' after skipping the first 'len' chars */ +static void +complete_display(char **list, u_int len) +{ + u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; + struct winsize ws; + char *tmp; + + /* Count entries for sort and find longest */ + for (y = 0; list[y]; y++) + m = MAX(m, strlen(list[y])); + + if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + + m = m > len ? m - len : 0; + columns = width / (m + 2); + columns = MAX(columns, 1); + colspace = width / columns; + colspace = MIN(colspace, width); + + printf("\n"); + m = 1; + for (y = 0; list[y]; y++) { + llen = strlen(list[y]); + tmp = llen > len ? list[y] + len : ""; + printf("%-*s", colspace, tmp); + if (m >= columns) { + printf("\n"); + m = 1; + } else + m++; + } + printf("\n"); +} + +/* + * Given a "list" of words that begin with a common prefix of "word", + * attempt to find an autocompletion to extends "word" by the next + * characters common to all entries in "list". + */ +static char * +complete_ambiguous(const char *word, char **list, size_t count) +{ + if (word == NULL) + return NULL; + + if (count > 0) { + u_int y, matchlen = strlen(list[0]); + + /* Find length of common stem */ + for (y = 1; list[y]; y++) { + u_int x; + + for (x = 0; x < matchlen; x++) + if (list[0][x] != list[y][x]) + break; + + matchlen = x; + } + + if (matchlen > strlen(word)) { + char *tmp = xstrdup(list[0]); + + tmp[matchlen] = NULL; + return tmp; + } + } + + return xstrdup(word); +} + +/* Autocomplete a sftp command */ +static int +complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, + int terminated) +{ + u_int y, count = 0, cmdlen, tmplen; + char *tmp, **list, argterm[3]; + const LineInfo *lf; + + list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); + + /* No command specified: display all available commands */ + if (cmd == NULL) { + for (y = 0; cmds[y].c; y++) + list[count++] = xstrdup(cmds[y].c); + + list[count] = NULL; + complete_display(list, 0); + + for (y = 0; list[y] != NULL; y++) + xfree(list[y]); + xfree(list); + return count; + } + + /* Prepare subset of commands that start with "cmd" */ + cmdlen = strlen(cmd); + for (y = 0; cmds[y].c; y++) { + if (!strncasecmp(cmd, cmds[y].c, cmdlen)) + list[count++] = xstrdup(cmds[y].c); + } + list[count] = NULL; + + if (count == 0) + return 0; + + /* Complete ambigious command */ + tmp = complete_ambiguous(cmd, list, count); + if (count > 1) + complete_display(list, 0); + + for (y = 0; list[y]; y++) + xfree(list[y]); + xfree(list); + + if (tmp != NULL) { + tmplen = strlen(tmp); + cmdlen = strlen(cmd); + /* If cmd may be extended then do so */ + if (tmplen > cmdlen) + if (el_insertstr(el, tmp + cmdlen) == -1) + fatal("el_insertstr failed."); + lf = el_line(el); + /* Terminate argument cleanly */ + if (count == 1) { + y = 0; + if (!terminated) + argterm[y++] = quote; + if (lastarg || *(lf->cursor) != ' ') + argterm[y++] = ' '; + argterm[y] = '\0'; + if (y > 0 && el_insertstr(el, argterm) == -1) + fatal("el_insertstr failed."); + } + xfree(tmp); + } + + return count; +} + +/* + * Determine whether a particular sftp command's arguments (if any) + * represent local or remote files. + */ +static int +complete_is_remote(char *cmd) { + int i; + + if (cmd == NULL) + return -1; + + for (i = 0; cmds[i].c; i++) { + if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) + return cmds[i].t; + } + + return -1; +} + +/* Autocomplete a filename "file" */ +static int +complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, + char *file, int remote, int lastarg, char quote, int terminated) +{ + glob_t g; + char *tmp, *tmp2, ins[3]; + u_int i, hadglob, pwdlen, len, tmplen, filelen; + const LineInfo *lf; + + /* Glob from "file" location */ + if (file == NULL) + tmp = xstrdup("*"); + else + xasprintf(&tmp, "%s*", file); + + memset(&g, 0, sizeof(g)); + if (remote != LOCAL) { + tmp = make_absolute(tmp, remote_path); + remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + } else + glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); + + /* Determine length of pwd so we can trim completion display */ + for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { + /* Terminate counting on first unescaped glob metacharacter */ + if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { + if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') + hadglob = 1; + break; + } + if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') + tmplen++; + if (tmp[tmplen] == '/') + pwdlen = tmplen + 1; /* track last seen '/' */ + } + xfree(tmp); + + if (g.gl_matchc == 0) + goto out; + + if (g.gl_matchc > 1) + complete_display(g.gl_pathv, pwdlen); + + tmp = NULL; + /* Don't try to extend globs */ + if (file == NULL || hadglob) + goto out; + + tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); + tmp = path_strip(tmp2, remote_path); + xfree(tmp2); + + if (tmp == NULL) + goto out; + + tmplen = strlen(tmp); + filelen = strlen(file); + + if (tmplen > filelen) { + tmp2 = tmp + filelen; + len = strlen(tmp2); + /* quote argument on way out */ + for (i = 0; i < len; i++) { + ins[0] = '\\'; + ins[1] = tmp2[i]; + ins[2] = '\0'; + switch (tmp2[i]) { + case '\'': + case '"': + case '\\': + case '\t': + case ' ': + if (quote == '\0' || tmp2[i] == quote) { + if (el_insertstr(el, ins) == -1) + fatal("el_insertstr " + "failed."); + break; + } + /* FALLTHROUGH */ + default: + if (el_insertstr(el, ins + 1) == -1) + fatal("el_insertstr failed."); + break; + } + } + } + + lf = el_line(el); + /* + * XXX should we really extend here? the user may not be done if + * the filename is a directory. + */ + if (g.gl_matchc == 1) { + i = 0; + if (!terminated) + ins[i++] = quote; + if (lastarg || *(lf->cursor) != ' ') + ins[i++] = ' '; + ins[i] = '\0'; + if (i > 0 && el_insertstr(el, ins) == -1) + fatal("el_insertstr failed."); + } + xfree(tmp); + + out: + globfree(&g); + return g.gl_matchc; +} + +/* tab-completion hook function, called via libedit */ +static unsigned char +complete(EditLine *el, int ch) +{ + char **argv, *line, quote; + u_int argc, carg, cursor, len, terminated, ret = CC_ERROR; + const LineInfo *lf; + struct complete_ctx *complete_ctx; + + lf = el_line(el); + if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) + fatal("%s: el_get failed", __func__); + + /* Figure out which argument the cursor points to */ + cursor = lf->cursor - lf->buffer; + line = (char *)xmalloc(cursor + 1); + memcpy(line, lf->buffer, cursor); + line[cursor] = '\0'; + argv = makeargv(line, &carg, 1, "e, &terminated); + xfree(line); + + /* Get all the arguments on the line */ + len = lf->lastchar - lf->buffer; + line = (char *)xmalloc(len + 1); + memcpy(line, lf->buffer, len); + line[len] = '\0'; + argv = makeargv(line, &argc, 1, NULL, NULL); + + /* Ensure cursor is at EOL or a argument boundary */ + if (line[cursor] != ' ' && line[cursor] != '\0' && + line[cursor] != '\n') { + xfree(line); + return ret; + } + + if (carg == 0) { + /* Show all available commands */ + complete_cmd_parse(el, NULL, argc == carg, '\0', 1); + ret = CC_REDISPLAY; + } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { + /* Handle the command parsing */ + if (complete_cmd_parse(el, argv[0], argc == carg, + quote, terminated) != 0) + ret = CC_REDISPLAY; + } else if (carg >= 1) { + /* Handle file parsing */ + int remote = complete_is_remote(argv[0]); + char *filematch = NULL; + + if (carg > 1 && line[cursor-1] != ' ') + filematch = argv[carg - 1]; + + if (remote != 0 && + complete_match(el, complete_ctx->conn, + *complete_ctx->remote_pathp, filematch, + remote, carg == argc, quote, terminated) != 0) + ret = CC_REDISPLAY; + } + + xfree(line); + return ret; +} + int interactive_loop(struct sftp_conn *conn, char *file1, char *file2) { - char *pwd; + char *remote_path; char *dir = NULL; char cmd[2048]; int err, interactive; @@ -1480,6 +1851,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) History *hl = NULL; HistEvent hev; extern char *__progname; + struct complete_ctx complete_ctx; if (!batchmode && isatty(STDIN_FILENO)) { if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) @@ -1494,23 +1866,32 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) el_set(el, EL_TERMINAL, NULL); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); + + /* Tab Completion */ + el_set(el, EL_ADDFN, "ftp-complete", + "Context senstive argument completion", complete); + complete_ctx.conn = conn; + complete_ctx.remote_pathp = &remote_path; + el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); + el_set(el, EL_BIND, "^I", "ftp-complete", NULL); } #endif /* USE_LIBEDIT */ - pwd = do_realpath(conn, "."); - if (pwd == NULL) + remote_path = do_realpath(conn, "."); + if (remote_path == NULL) fatal("Need cwd"); if (file1 != NULL) { dir = xstrdup(file1); - dir = make_absolute(dir, pwd); + dir = make_absolute(dir, remote_path); if (remote_is_dir(conn, dir) && file2 == NULL) { printf("Changing to: %s\n", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); - if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0) { + if (parse_dispatch_command(conn, cmd, + &remote_path, 1) != 0) { xfree(dir); - xfree(pwd); + xfree(remote_path); xfree(conn); return (-1); } @@ -1521,9 +1902,10 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) snprintf(cmd, sizeof cmd, "get %s %s", dir, file2); - err = parse_dispatch_command(conn, cmd, &pwd, 1); + err = parse_dispatch_command(conn, cmd, + &remote_path, 1); xfree(dir); - xfree(pwd); + xfree(remote_path); xfree(conn); return (err); } @@ -1564,7 +1946,8 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) const char *line; int count = 0; - if ((line = el_gets(el, &count)) == NULL || count <= 0) { + if ((line = el_gets(el, &count)) == NULL || + count <= 0) { printf("\n"); break; } @@ -1584,11 +1967,12 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) interrupted = 0; signal(SIGINT, cmd_interrupt); - err = parse_dispatch_command(conn, cmd, &pwd, batchmode); + err = parse_dispatch_command(conn, cmd, &remote_path, + batchmode); if (err != 0) break; } - xfree(pwd); + xfree(remote_path); xfree(conn); #ifdef USE_LIBEDIT -- cgit v1.2.3 From e67f7db968e376c9971dd3a4d4ab3fae4a06bf78 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 19:50:02 +1100 Subject: - (dtucker) [sftp.c] ifdef out the sftp completion bits for platforms that don't have libedit. --- ChangeLog | 2 ++ sftp.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index e900cb637..d0fde0fb3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -157,6 +157,8 @@ - (dtucker) [Makefile.in added roaming_client.c roaming_serv.c] Import new files for roaming and add to Makefile. - (dtucker) [Makefile.in] .c files do not belong in the OBJ lines. + - (dtucker) [sftp.c] ifdef out the sftp completion bits for platforms that + don't have libedit. 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/sftp.c b/sftp.c index 6a5ccc49d..c994887e3 100644 --- a/sftp.c +++ b/sftp.c @@ -1503,7 +1503,6 @@ prompt(EditLine *el) { return ("sftp> "); } -#endif /* Display entries in 'list' after skipping the first 'len' chars */ static void @@ -1646,6 +1645,7 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, return count; } +#endif /* * Determine whether a particular sftp command's arguments (if any) @@ -1666,6 +1666,7 @@ complete_is_remote(char *cmd) { return -1; } +#ifdef USE_LIBEDIT /* Autocomplete a filename "file" */ static int complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, @@ -1838,6 +1839,7 @@ complete(EditLine *el, int ch) xfree(line); return ret; } +#endif /* USE_LIBEDIT */ int interactive_loop(struct sftp_conn *conn, char *file1, char *file2) -- cgit v1.2.3 From 843f0fa16d7e8aca36c1b14d933da2a1844d5a74 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 8 Jan 2010 19:56:33 +1100 Subject: - (dtucker) [sftp.c] Expand ifdef for libedit to cover complete_is_remote too. --- ChangeLog | 2 ++ sftp.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 440c8318e..60162c088 100644 --- a/ChangeLog +++ b/ChangeLog @@ -161,6 +161,8 @@ don't have libedit. - (dtucker) [configure.ac misc.c readconf.c servconf.c ssh-keyscan.c] Make RoutingDomain an unsupported option on platforms that don't have it. + - (dtucker) [sftp.c] Expand ifdef for libedit to cover complete_is_remote + too. 20091226 - (tim) [contrib/cygwin/Makefile] Install ssh-copy-id and ssh-copy-id.1 diff --git a/sftp.c b/sftp.c index c994887e3..4b12fae4b 100644 --- a/sftp.c +++ b/sftp.c @@ -1645,7 +1645,6 @@ complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, return count; } -#endif /* * Determine whether a particular sftp command's arguments (if any) @@ -1666,7 +1665,6 @@ complete_is_remote(char *cmd) { return -1; } -#ifdef USE_LIBEDIT /* Autocomplete a filename "file" */ static int complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, -- cgit v1.2.3 From 340d1688e658e85a3c45270bc3fca4e6d1aee9b1 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Sat, 9 Jan 2010 08:54:31 +1100 Subject: - dtucker@cvs.openbsd.org 2010/01/08 21:50:49 [sftp.c] Fix two warnings: possibly used unitialized and use a nul byte instead of NULL pointer. ok djm@ --- ChangeLog | 4 ++++ sftp.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 11093e7ad..b1350dc35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -154,6 +154,10 @@ [sftp-server.c] bz#1566 don't unnecessarily dup() in and out fds for sftp-server; ok markus@ + - dtucker@cvs.openbsd.org 2010/01/08 21:50:49 + [sftp.c] + Fix two warnings: possibly used unitialized and use a nul byte instead of + NULL pointer. ok djm@ - (dtucker) [Makefile.in added roaming_client.c roaming_serv.c] Import new files for roaming and add to Makefile. - (dtucker) [Makefile.in] .c files do not belong in the OBJ lines. diff --git a/sftp.c b/sftp.c index 4b12fae4b..9f5fa354d 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.116 2010/01/04 02:03:57 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.117 2010/01/08 21:50:49 dtucker Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1568,7 +1568,7 @@ complete_ambiguous(const char *word, char **list, size_t count) if (matchlen > strlen(word)) { char *tmp = xstrdup(list[0]); - tmp[matchlen] = NULL; + tmp[matchlen] = '\0'; return tmp; } } @@ -2062,7 +2062,7 @@ int main(int argc, char **argv) { int in, out, ch, err; - char *host, *userhost, *cp, *file2 = NULL; + char *host = NULL, *userhost, *cp, *file2 = NULL; int debug_level = 0, sshver = 2; char *file1 = NULL, *sftp_server = NULL; char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; -- cgit v1.2.3 From 70cc092817a61af78c751b8f7a8ac5dcabfeae00 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Sat, 9 Jan 2010 22:28:03 +1100 Subject: - dtucker@cvs.openbsd.org 2010/01/09 11:13:02 [sftp.c] Prevent sftp from derefing a null pointer when given a "-" without a command. Also, allow whitespace to follow a "-". bz#1691, path from Colin Watson via Debian. ok djm@ deraadt@ --- ChangeLog | 5 +++++ sftp.c | 11 ++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 9eda2f396..094d1bec3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,11 @@ - dtucker@cvs.openbsd.org 2010/01/09 05:17:00 [roaming_client.c] Remove a PRIu64 format string that snuck in with roaming. ok djm@ + - dtucker@cvs.openbsd.org 2010/01/09 11:13:02 + [sftp.c] + Prevent sftp from derefing a null pointer when given a "-" without a + command. Also, allow whitespace to follow a "-". bz#1691, path from + Colin Watson via Debian. ok djm@ deraadt@ 20091208 - (dtucker) OpenBSD CVS Sync diff --git a/sftp.c b/sftp.c index 9f5fa354d..78f8ca178 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.117 2010/01/08 21:50:49 dtucker Exp $ */ +/* $OpenBSD: sftp.c,v 1.118 2010/01/09 11:13:02 dtucker Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1112,17 +1112,18 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, /* Skip leading whitespace */ cp = cp + strspn(cp, WHITESPACE); - /* Ignore blank lines and lines which begin with comment '#' char */ - if (*cp == '\0' || *cp == '#') - return (0); - /* Check for leading '-' (disable error processing) */ *iflag = 0; if (*cp == '-') { *iflag = 1; cp++; + cp = cp + strspn(cp, WHITESPACE); } + /* Ignore blank lines and lines which begin with comment '#' char */ + if (*cp == '\0' || *cp == '#') + return (0); + if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) return -1; -- cgit v1.2.3 From 2901e2daebe3a0890209f31d05d5bb9338cbed5b Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Wed, 13 Jan 2010 22:44:06 +1100 Subject: - djm@cvs.openbsd.org 2010/01/13 01:40:16 [sftp.c sftp-server.c sftp.1 sftp-common.c sftp-common.h] support '-h' (human-readable units) for sftp's ls command, just like ls(1); ok dtucker@ --- ChangeLog | 4 ++++ sftp-common.c | 19 ++++++++++++++----- sftp-common.h | 4 ++-- sftp-server.c | 4 ++-- sftp.1 | 11 ++++++++--- sftp.c | 39 ++++++++++++++++++++++----------------- 6 files changed, 52 insertions(+), 29 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 7624812b1..d4210354e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,10 @@ [canohost.c ssh-keysign.c sshconnect2.c] Make HostBased authentication work with a ProxyCommand. bz #1569, patch from imorgan at nas nasa gov, ok djm@ + - djm@cvs.openbsd.org 2010/01/13 01:40:16 + [sftp.c sftp-server.c sftp.1 sftp-common.c sftp-common.h] + support '-h' (human-readable units) for sftp's ls command, just like + ls(1); ok dtucker@ 20100112 - (dtucker) OpenBSD CVS Sync diff --git a/sftp-common.c b/sftp-common.c index 7ebadcc53..7393fc6a9 100644 --- a/sftp-common.c +++ b/sftp-common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-common.c,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: sftp-common.c,v 1.21 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Damien Miller. All rights reserved. @@ -36,6 +36,7 @@ #include #include #include +#include #include "xmalloc.h" #include "buffer.h" @@ -184,7 +185,7 @@ fx2txt(int status) * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh */ char * -ls_file(const char *name, const struct stat *st, int remote) +ls_file(const char *name, const struct stat *st, int remote, int si_units) { int ulen, glen, sz = 0; struct passwd *pw; @@ -192,6 +193,7 @@ ls_file(const char *name, const struct stat *st, int remote) struct tm *ltime = localtime(&st->st_mtime); char *user, *group; char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + char sbuf[FMT_SCALED_STRSIZE]; strmode(st->st_mode, mode); if (!remote && (pw = getpwuid(st->st_uid)) != NULL) { @@ -216,8 +218,15 @@ ls_file(const char *name, const struct stat *st, int remote) tbuf[0] = '\0'; ulen = MAX(strlen(user), 8); glen = MAX(strlen(group), 8); - snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, - (u_int)st->st_nlink, ulen, user, glen, group, - (unsigned long long)st->st_size, tbuf, name); + if (si_units) { + fmt_scaled((long long)st->st_size, sbuf); + snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8s %s %s", mode, + (u_int)st->st_nlink, ulen, user, glen, group, + sbuf, tbuf, name); + } else { + snprintf(buf, sizeof buf, "%s %3u %-*s %-*s %8llu %s %s", mode, + (u_int)st->st_nlink, ulen, user, glen, group, + (unsigned long long)st->st_size, tbuf, name); + } return xstrdup(buf); } diff --git a/sftp-common.h b/sftp-common.h index 9b5848462..9ed86c070 100644 --- a/sftp-common.h +++ b/sftp-common.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-common.h,v 1.10 2006/08/03 03:34:42 deraadt Exp $ */ +/* $OpenBSD: sftp-common.h,v 1.11 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -46,6 +46,6 @@ void stat_to_attrib(const struct stat *, Attrib *); void attrib_to_stat(const Attrib *, struct stat *); Attrib *decode_attrib(Buffer *); void encode_attrib(Buffer *, const Attrib *); -char *ls_file(const char *, const struct stat *, int); +char *ls_file(const char *, const struct stat *, int, int); const char *fx2txt(int); diff --git a/sftp-server.c b/sftp-server.c index ab9391cfd..a98ac2b6d 100644 --- a/sftp-server.c +++ b/sftp-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-server.c,v 1.90 2010/01/09 00:20:26 djm Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.91 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -940,7 +940,7 @@ process_readdir(void) continue; stat_to_attrib(&st, &(stats[count].attrib)); stats[count].name = xstrdup(dp->d_name); - stats[count].long_name = ls_file(dp->d_name, &st, 0); + stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); count++; /* send up to 100 entries in one message */ /* XXX check packet size instead */ diff --git a/sftp.1 b/sftp.1 index 3ec7a0234..f6371cf54 100644 --- a/sftp.1 +++ b/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.80 2010/01/09 23:04:13 dtucker Exp $ +.\" $OpenBSD: sftp.1,v 1.81 2010/01/13 01:40:16 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: January 9 2010 $ +.Dd $Mdocdate: January 13 2010 $ .Dt SFTP 1 .Os .Sh NAME @@ -393,7 +393,7 @@ to .It Ic lpwd Print local working directory. .It Xo Ic ls -.Op Fl 1aflnrSt +.Op Fl 1aflhnrSt .Op Ar path .Xc Display a remote directory listing of either @@ -421,6 +421,11 @@ The default sort order is lexicographical. .It Fl l Display additional details including permissions and ownership information. +.It Fl h +When used with a long format option, use unit suffixes: Byte, Kilobyte, +Megabyte, Gigabyte, Terabyte, Petabyte, and Exabyte in order to reduce +the number of digits to four or fewer using powers of 2 for sizes (K=1024, +M=1048576, etc.). .It Fl n Produce a long listing with user and group information presented numerically. diff --git a/sftp.c b/sftp.c index 78f8ca178..16f84987c 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.118 2010/01/09 11:13:02 dtucker Exp $ */ +/* $OpenBSD: sftp.c,v 1.119 2010/01/13 01:40:16 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -110,16 +110,17 @@ extern char *__progname; #define WHITESPACE " \t\r\n" /* ls flags */ -#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */ -#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */ -#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */ -#define LS_NAME_SORT 0x08 /* Sort by name (default) */ -#define LS_TIME_SORT 0x10 /* Sort by mtime */ -#define LS_SIZE_SORT 0x20 /* Sort by file size */ -#define LS_REVERSE_SORT 0x40 /* Reverse sort order */ -#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */ - -#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW) +#define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ +#define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ +#define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ +#define LS_NAME_SORT 0x0008 /* Sort by name (default) */ +#define LS_TIME_SORT 0x0010 /* Sort by mtime */ +#define LS_SIZE_SORT 0x0020 /* Sort by file size */ +#define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ +#define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ +#define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ + +#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) /* Commands for interactive mode */ @@ -383,7 +384,7 @@ parse_ls_flags(char **argv, int argc, int *lflag) opterr = 0; *lflag = LS_NAME_SORT; - while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { + while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { switch (ch) { case '1': *lflag &= ~VIEW_FLAGS; @@ -399,12 +400,15 @@ parse_ls_flags(char **argv, int argc, int *lflag) case 'f': *lflag &= ~SORT_FLAGS; break; + case 'h': + *lflag |= LS_SI_UNITS; + break; case 'l': - *lflag &= ~VIEW_FLAGS; + *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_LONG_VIEW; break; case 'n': - *lflag &= ~VIEW_FLAGS; + *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; break; case 'r': @@ -716,13 +720,14 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag) xfree(tmp); if (lflag & LS_LONG_VIEW) { - if (lflag & LS_NUMERIC_VIEW) { + if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { char *lname; struct stat sb; memset(&sb, 0, sizeof(sb)); attrib_to_stat(&d[n]->a, &sb); - lname = ls_file(fname, &sb, 1); + lname = ls_file(fname, &sb, 1, + (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else @@ -824,7 +829,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, a = do_lstat(conn, g.gl_pathv[i], 1); if (a != NULL) attrib_to_stat(a, &sb); - lname = ls_file(fname, &sb, 1); + lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); printf("%s\n", lname); xfree(lname); } else { -- cgit v1.2.3 From 9c3ba07f60434e68657630fec0d7ab0dd7f984a3 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Wed, 13 Jan 2010 22:45:03 +1100 Subject: - djm@cvs.openbsd.org 2010/01/13 04:10:50 [sftp.c] don't append a space after inserting a completion of a directory (i.e. a path ending in '/') for a slightly better user experience; ok dtucker@ --- ChangeLog | 4 ++++ sftp.c | 9 +++------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 1b86fe47e..00cb316b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,10 @@ [servconf.c servconf.h sshd.c] avoid run-time failures when specifying hostkeys via a relative path by prepending the cwd in these cases; bz#1290; ok dtucker@ + - djm@cvs.openbsd.org 2010/01/13 04:10:50 + [sftp.c] + don't append a space after inserting a completion of a directory (i.e. + a path ending in '/') for a slightly better user experience; ok dtucker@ 20100112 - (dtucker) OpenBSD CVS Sync diff --git a/sftp.c b/sftp.c index 16f84987c..3bacb7d51 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.119 2010/01/13 01:40:16 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.120 2010/01/13 04:10:50 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -1760,15 +1760,12 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, } lf = el_line(el); - /* - * XXX should we really extend here? the user may not be done if - * the filename is a directory. - */ if (g.gl_matchc == 1) { i = 0; if (!terminated) ins[i++] = quote; - if (lastarg || *(lf->cursor) != ' ') + if (*(lf->cursor - 1) != '/' && + (lastarg || *(lf->cursor) != ' ')) ins[i++] = ' '; ins[i] = '\0'; if (i > 0 && el_insertstr(el, ins) == -1) -- cgit v1.2.3 From 75fe626489bbafd966332db1b4801fee0c179ffd Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 15 Jan 2010 11:42:51 +1100 Subject: - jmc@cvs.openbsd.org 2010/01/13 12:48:34 [sftp.1 sftp.c] sftp.1: put ls -h in the right place sftp.c: as above, plus add -p to get/put, and shorten their arg names to keep the help usage nicely aligned ok djm --- ChangeLog | 9 +++++++++ sftp.1 | 10 +++++----- sftp.c | 8 ++++---- 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index d10a96f55..088af5f26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +20100115 + - (dtucker) OpenBSD CVS Sync + - jmc@cvs.openbsd.org 2010/01/13 12:48:34 + [sftp.1 sftp.c] + sftp.1: put ls -h in the right place + sftp.c: as above, plus add -p to get/put, and shorten their arg names + to keep the help usage nicely aligned + ok djm + 20100114 - (djm) [platform.h] Add missing prototype for platform_krb5_get_principal_name diff --git a/sftp.1 b/sftp.1 index f6371cf54..175dc6520 100644 --- a/sftp.1 +++ b/sftp.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sftp.1,v 1.81 2010/01/13 01:40:16 djm Exp $ +.\" $OpenBSD: sftp.1,v 1.82 2010/01/13 12:48:34 jmc Exp $ .\" .\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" @@ -393,7 +393,7 @@ to .It Ic lpwd Print local working directory. .It Xo Ic ls -.Op Fl 1aflhnrSt +.Op Fl 1afhlnrSt .Op Ar path .Xc Display a remote directory listing of either @@ -418,14 +418,14 @@ List files beginning with a dot .It Fl f Do not sort the listing. The default sort order is lexicographical. -.It Fl l -Display additional details including permissions -and ownership information. .It Fl h When used with a long format option, use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte, Petabyte, and Exabyte in order to reduce the number of digits to four or fewer using powers of 2 for sizes (K=1024, M=1048576, etc.). +.It Fl l +Display additional details including permissions +and ownership information. .It Fl n Produce a long listing with user and group information presented numerically. diff --git a/sftp.c b/sftp.c index 3bacb7d51..ac6ac9fed 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.120 2010/01/13 04:10:50 djm Exp $ */ +/* $OpenBSD: sftp.c,v 1.121 2010/01/13 12:48:34 jmc Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -233,18 +233,18 @@ help(void) "df [-hi] [path] Display statistics for current directory or\n" " filesystem containing 'path'\n" "exit Quit sftp\n" - "get [-Pr] remote-path [local-path] Download file\n" + "get [-Ppr] remote [local] Download file\n" "help Display this help text\n" "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" "lpwd Print local working directory\n" - "ls [-1aflnrSt] [path] Display remote directory listing\n" + "ls [-1afhlnrSt] [path] Display remote directory listing\n" "lumask umask Set local umask to 'umask'\n" "mkdir path Create remote directory\n" "progress Toggle display of progress meter\n" - "put [-Pr] local-path [remote-path] Upload file\n" + "put [-Ppr] local [remote] Upload file\n" "pwd Display remote working directory\n" "quit Quit sftp\n" "rename oldpath newpath Rename remote file\n" -- cgit v1.2.3 From b8b17e984acab330b13c66460e6570e72c55ded9 Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Fri, 15 Jan 2010 11:46:03 +1100 Subject: - guenther@cvs.openbsd.org 2010/01/15 00:05:22 [sftp.c] Reset SIGTERM to SIG_DFL before executing ssh, so that even if sftp inherited SIGTERM as ignored it will still be able to kill the ssh it starts. ok dtucker@ --- ChangeLog | 6 ++++++ sftp.c | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 257204240..0d35871eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,12 @@ [sftp-common.c] use user_from{uid,gid} to lookup up ids since it keeps a small cache. ok djm + - guenther@cvs.openbsd.org 2010/01/15 00:05:22 + [sftp.c] + Reset SIGTERM to SIG_DFL before executing ssh, so that even if sftp + inherited SIGTERM as ignored it will still be able to kill the ssh it + starts. + ok dtucker@ 20100114 - (djm) [platform.h] Add missing prototype for diff --git a/sftp.c b/sftp.c index ac6ac9fed..e01703ba9 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.121 2010/01/13 12:48:34 jmc Exp $ */ +/* $OpenBSD: sftp.c,v 1.122 2010/01/15 00:05:22 guenther Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -2027,9 +2027,11 @@ connect_to_server(char *path, char **args, int *in, int *out) * The underlying ssh is in the same process group, so we must * ignore SIGINT if we want to gracefully abort commands, * otherwise the signal will make it to the ssh process and - * kill it too + * kill it too. Contrawise, since sftp sends SIGTERMs to the + * underlying ssh, it must *not* ignore that signal. */ signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_DFL); execvp(path, args); fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); _exit(1); -- cgit v1.2.3 From a1162985a51321158a625a214424a7ec51030a7d Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Thu, 28 Jan 2010 06:27:54 +1100 Subject: - djm@cvs.openbsd.org 2010/01/27 19:21:39 [sftp.c] add missing "p" flag to getopt optstring; bz#1704 from imorgan AT nas.nasa.gov --- ChangeLog | 4 ++++ sftp.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'sftp.c') diff --git a/ChangeLog b/ChangeLog index 05cbba64a..5cd1d8f63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,10 @@ has been prematurely deleted. spotted by imorgan AT nas.nasa.gov + - djm@cvs.openbsd.org 2010/01/27 19:21:39 + [sftp.c] + add missing "p" flag to getopt optstring; + bz#1704 from imorgan AT nas.nasa.gov 20100126 - (djm) OpenBSD CVS Sync diff --git a/sftp.c b/sftp.c index e01703ba9..d65d4ec62 100644 --- a/sftp.c +++ b/sftp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp.c,v 1.122 2010/01/15 00:05:22 guenther Exp $ */ +/* $OpenBSD: sftp.c,v 1.123 2010/01/27 19:21:39 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * @@ -2095,7 +2095,7 @@ main(int argc, char **argv) infile = stdin; while ((ch = getopt(argc, argv, - "1246hqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { + "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case '4': -- cgit v1.2.3