diff --git a/ChangeLog b/ChangeLog index 191ee72e8..7f972e306 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,10 @@ - markus@cvs.openbsd.org 2001/03/15 22:07:08 [session.c] pass Session to do_child + KNF + - djm@cvs.openbsd.org 2001/03/16 08:16:18 + [sftp-client.c sftp-client.h sftp-glob.c sftp-int.c] + Revise globbing for get/put to be more shell-like. In particular, + "get/put file* directory/" now works. ok markus@ 20010315 - OpenBSD CVS Sync @@ -4570,4 +4574,4 @@ - Wrote replacements for strlcpy and mkdtemp - Released 1.0pre1 -$Id: ChangeLog,v 1.963 2001/03/17 00:32:57 mouring Exp $ +$Id: ChangeLog,v 1.964 2001/03/17 00:34:46 mouring Exp $ diff --git a/sftp-client.c b/sftp-client.c index 87f3053f6..b0007a734 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.13 2001/03/14 08:57:14 markus Exp $"); +RCSID("$OpenBSD: sftp-client.c,v 1.14 2001/03/16 08:16:17 djm Exp $"); #include "ssh.h" #include "buffer.h" @@ -180,7 +180,7 @@ get_handle(int fd, u_int expected_id, u_int *len) } Attrib * -get_decode_stat(int fd, u_int expected_id) +get_decode_stat(int fd, u_int expected_id, int quiet) { Buffer msg; u_int type, id; @@ -198,7 +198,10 @@ get_decode_stat(int fd, u_int expected_id) if (type == SSH2_FXP_STATUS) { int status = buffer_get_int(&msg); - error("Couldn't stat remote file: %s", fx2txt(status)); + if (quiet) + debug("Couldn't stat remote file: %s", fx2txt(status)); + else + error("Couldn't stat remote file: %s", fx2txt(status)); return(NULL); } else if (type != SSH2_FXP_ATTRS) { fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d", @@ -455,34 +458,33 @@ do_rmdir(int fd_in, int fd_out, char *path) } Attrib * -do_stat(int fd_in, int fd_out, char *path) +do_stat(int fd_in, int fd_out, char *path, int quiet) { u_int id; id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path)); - return(get_decode_stat(fd_in, id)); + return(get_decode_stat(fd_in, id, quiet)); } Attrib * -do_lstat(int fd_in, int fd_out, char *path) +do_lstat(int fd_in, int fd_out, char *path, int quiet) { u_int id; id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); - return(get_decode_stat(fd_in, id)); + return(get_decode_stat(fd_in, id, quiet)); } Attrib * -do_fstat(int fd_in, int fd_out, char *handle, - u_int handle_len) +do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet) { u_int id; id = msg_id++; send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); - return(get_decode_stat(fd_in, id)); + return(get_decode_stat(fd_in, id, quiet)); } int @@ -677,7 +679,7 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, Attrib junk, *a; int status; - a = do_stat(fd_in, fd_out, remote_path); + a = do_stat(fd_in, fd_out, remote_path, 0); if (a == NULL) return(-1); @@ -687,11 +689,17 @@ do_download(int fd_in, int fd_out, char *remote_path, char *local_path, else mode = 0666; + if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && + (a->perm & S_IFDIR)) { + error("Cannot download a directory: %s", remote_path); + return(-1); + } + local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); if (local_fd == -1) { error("Couldn't open local file \"%s\" for writing: %s", local_path, strerror(errno)); - return(errno); + return(-1); } buffer_init(&msg); diff --git a/sftp-client.h b/sftp-client.h index e7ba02ad6..a935736e3 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-client.h,v 1.3 2001/03/13 22:42:54 djm Exp $ */ +/* $OpenBSD: sftp-client.h,v 1.4 2001/03/16 08:16:18 djm Exp $ */ /* * Copyright (c) 2001 Damien Miller. All rights reserved. @@ -62,14 +62,14 @@ int do_mkdir(int fd_in, int fd_out, char *path, Attrib *a); int do_rmdir(int fd_in, int fd_out, char *path); /* Get file attributes of 'path' (follows symlinks) */ -Attrib *do_stat(int fd_in, int fd_out, char *path); +Attrib *do_stat(int fd_in, int fd_out, char *path, int quiet); /* Get file attributes of 'path' (does not follow symlinks) */ -Attrib *do_lstat(int fd_in, int fd_out, char *path); +Attrib *do_lstat(int fd_in, int fd_out, char *path, int quiet); /* Get file attributes of open file 'handle' */ -Attrib *do_fstat(int fd_in, int fd_out, char *handle, - u_int handle_len); +Attrib *do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, + int quiet); /* Set file attributes of 'path' */ int do_setstat(int fd_in, int fd_out, char *path, Attrib *a); diff --git a/sftp-glob.c b/sftp-glob.c index aec6d2734..79dca00f1 100644 --- a/sftp-glob.c +++ b/sftp-glob.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sftp-glob.c,v 1.1 2001/03/13 22:42:54 djm Exp $"); +RCSID("$OpenBSD: sftp-glob.c,v 1.2 2001/03/16 08:16:18 djm Exp $"); #include "ssh.h" #include "buffer.h" @@ -119,7 +119,7 @@ int fudge_lstat(const char *path, struct stat *st) { Attrib *a; - if (!(a = do_lstat(cur.fd_in, cur.fd_out, (char*)path))) + if (!(a = do_lstat(cur.fd_in, cur.fd_out, (char*)path, 0))) return(-1); attrib_to_stat(a, st); @@ -131,7 +131,7 @@ int fudge_stat(const char *path, struct stat *st) { Attrib *a; - if (!(a = do_stat(cur.fd_in, cur.fd_out, (char*)path))) + if (!(a = do_stat(cur.fd_in, cur.fd_out, (char*)path, 0))) return(-1); attrib_to_stat(a, st); diff --git a/sftp-int.c b/sftp-int.c index cf86012ea..fdadf23b2 100644 --- a/sftp-int.c +++ b/sftp-int.c @@ -26,7 +26,7 @@ /* XXX: recursive operations */ #include "includes.h" -RCSID("$OpenBSD: sftp-int.c,v 1.28 2001/03/14 15:15:58 markus Exp $"); +RCSID("$OpenBSD: sftp-int.c,v 1.29 2001/03/16 08:16:18 djm Exp $"); #include "buffer.h" #include "xmalloc.h" @@ -194,19 +194,51 @@ local_do_ls(const char *args) } } +char * +path_append(char *p1, char *p2) +{ + char *ret; + + ret = xmalloc(strlen(p1) + strlen(p2) + 2); + strcpy(ret, p1); + strcat(ret, "/"); + strcat(ret, p2); + + return(ret); +} + char * make_absolute(char *p, char *pwd) { - char buf[2048]; + char *abs; /* Derelativise */ if (p && p[0] != '/') { - snprintf(buf, sizeof(buf), "%s/%s", pwd, p); + abs = path_append(pwd, p); xfree(p); - p = xstrdup(buf); + return(abs); + } else + return(p); +} + +int +infer_path(const char *p, char **ifp) +{ + char *cp; + + cp = strrchr(p, '/'); + if (cp == NULL) { + *ifp = xstrdup(p); + return(0); } - return(p); + if (!cp[1]) { + error("Invalid path"); + return(-1); + } + + *ifp = xstrdup(cp + 1); + return(0); } int @@ -281,23 +313,182 @@ get_pathname(const char **cpp, char **path) } int -infer_path(const char *p, char **ifp) +is_dir(char *path) { - char *cp; + struct stat sb; - cp = strrchr(p, '/'); - if (cp == NULL) { - *ifp = xstrdup(p); + /* XXX: report errors? */ + if (stat(path, &sb) == -1) return(0); + + return(sb.st_mode & S_IFDIR); +} + +int +remote_is_dir(int in, int out, char *path) +{ + Attrib *a; + + /* XXX: report errors? */ + if ((a = do_stat(in, out, path, 1)) == NULL) + return(0); + if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) + return(0); + return(a->perm & S_IFDIR); +} + +int +process_get(int in, int out, char *src, char *dst, char *pwd, int pflag) +{ + char *abs_src = NULL; + char *abs_dst = NULL; + char *tmp; + glob_t g; + int err = 0; + int i; + + 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(in, out, abs_src, 0, NULL, &g)) { + error("File \"%s\" not found.", abs_src); + err = -1; + goto out; } - if (!cp[1]) { - error("Invalid path"); - return(-1); + /* Only one match, dst may be file, directory or unspecified */ + if (g.gl_pathv[0] && g.gl_matchc == 1) { + if (dst) { + /* If directory specified, append filename */ + 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 = xstrdup(dst); + } else if (infer_path(g.gl_pathv[0], &abs_dst)) { + err = -1; + goto out; + } + printf("Fetching %s to %s\n", g.gl_pathv[0], abs_dst); + err = do_download(in, out, g.gl_pathv[0], abs_dst, pflag); + goto out; } - *ifp = xstrdup(cp + 1); - return(0); + /* Multiple matches, dst may be directory or unspecified */ + if (dst && !is_dir(dst)) { + error("Multiple files match, but \"%s\" is not a directory", + dst); + err = -1; + goto out; + } + + for(i = 0; g.gl_pathv[i]; i++) { + if (infer_path(g.gl_pathv[i], &tmp)) { + err = -1; + goto out; + } + if (dst) { + abs_dst = path_append(dst, tmp); + xfree(tmp); + } else + abs_dst = tmp; + + printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); + if (do_download(in, out, g.gl_pathv[i], abs_dst, pflag) == -1) + err = -1; + xfree(abs_dst); + abs_dst = NULL; + } + +out: + xfree(abs_src); + if (abs_dst) + xfree(abs_dst); + globfree(&g); + return(err); +} + +int +process_put(int in, int out, char *src, char *dst, char *pwd, int pflag) +{ + char *tmp_dst = NULL; + char *abs_dst = NULL; + char *tmp; + glob_t g; + int err = 0; + int i; + + if (dst) { + tmp_dst = xstrdup(dst); + tmp_dst = make_absolute(tmp_dst, pwd); + } + + memset(&g, '\0', sizeof(g)); + debug3("Looking up %s", src); + if (glob(src, 0, NULL, &g)) { + error("File \"%s\" not found.", src); + err = -1; + goto out; + } + + /* Only one match, dst may be file, directory or unspecified */ + if (g.gl_pathv[0] && g.gl_matchc == 1) { + if (tmp_dst) { + /* If directory specified, append filename */ + if (remote_is_dir(in, out, tmp_dst)) { + if (infer_path(g.gl_pathv[0], &tmp)) { + err = 1; + goto out; + } + abs_dst = path_append(tmp_dst, tmp); + xfree(tmp); + } else + abs_dst = xstrdup(tmp_dst); + } else if (infer_path(g.gl_pathv[0], &abs_dst)) { + err = -1; + goto out; + } + printf("Uploading %s to %s\n", g.gl_pathv[0], abs_dst); + err = do_upload(in, out, g.gl_pathv[0], abs_dst, pflag); + goto out; + } + + /* Multiple matches, dst may be directory or unspecified */ + if (tmp_dst && !remote_is_dir(in, out, tmp_dst)) { + error("Multiple files match, but \"%s\" is not a directory", + tmp_dst); + err = -1; + goto out; + } + + for(i = 0; g.gl_pathv[i]; i++) { + if (infer_path(g.gl_pathv[i], &tmp)) { + err = -1; + goto out; + } + if (tmp_dst) { + abs_dst = path_append(tmp_dst, tmp); + xfree(tmp); + } else + abs_dst = make_absolute(tmp, pwd); + + printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); + if (do_upload(in, out, g.gl_pathv[i], abs_dst, pflag) == -1) + err = -1; + } + +out: + if (abs_dst) + xfree(abs_dst); + if (tmp_dst) + xfree(tmp_dst); + return(err); } int @@ -459,66 +650,17 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) path1 = path2 = NULL; cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2); + memset(&g, 0, sizeof(g)); + /* Perform command */ switch (cmdnum) { case -1: break; case I_GET: - memset(&g, 0, sizeof(g)); - if (!remote_glob(in, out, path1, 0, NULL, &g)) { - if (path2) { - /* XXX: target should be directory */ - error("You cannot specify a target when " - "downloading multiple files"); - err = -1; - break; - } - for(i = 0; g.gl_pathv[i]; i++) { - if (!infer_path(g.gl_pathv[i], &path2)) { - printf("Fetching %s\n", g.gl_pathv[i]); - if (do_download(in, out, g.gl_pathv[i], - path2, pflag) == -1) - err = -1; - free(path2); - path2 = NULL; - } else - err = -1; - } - } else { - if (!path2 && infer_path(path1, &path2)) { - err = -1; - break; - } - err = do_download(in, out, path1, path2, pflag); - } + err = process_get(in, out, path1, path2, *pwd, pflag); break; case I_PUT: - if (!glob(path1, 0, NULL, &g)) { - if (path2) { - error("You cannot specify a target when " - "uploading multiple files"); - err = -1; - break; - } - for(i = 0; g.gl_pathv[i]; i++) { - if (!infer_path(g.gl_pathv[i], &path2)) { - path2 = make_absolute(path2, *pwd); - printf("Uploading %s\n", g.gl_pathv[i]); - if (do_upload(in, out, g.gl_pathv[i], - path2, pflag) == -1) - err = -1; - free(path2); - path2 = NULL; - } else - err = -1; - } - } else { - if (!path2 && infer_path(path1, &path2)) { - err = -1; - break; - } - err = do_upload(in, out, path1, path2, pflag); - } + err = process_put(in, out, path1, path2, *pwd, pflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd); @@ -561,7 +703,7 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) err = 1; break; } - if ((aa = do_stat(in, out, tmp)) == NULL) { + if ((aa = do_stat(in, out, tmp, 0)) == NULL) { xfree(tmp); err = 1; break; @@ -592,7 +734,7 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) break; xfree(path1); path1 = tmp; - if ((aa = do_stat(in, out, path1)) == NULL) + if ((aa = do_stat(in, out, path1, 0)) == NULL) break; if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && !S_ISDIR(aa->perm)) { @@ -640,7 +782,7 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) path1 = make_absolute(path1, *pwd); remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); for(i = 0; g.gl_pathv[i]; i++) { - if (!(aa = do_stat(in, out, g.gl_pathv[i]))) + if (!(aa = do_stat(in, out, g.gl_pathv[i], 0))) continue; if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { error("Can't get current ownership of " @@ -657,7 +799,7 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) path1 = make_absolute(path1, *pwd); remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); for(i = 0; g.gl_pathv[i]; i++) { - if (!(aa = do_stat(in, out, g.gl_pathv[i]))) + if (!(aa = do_stat(in, out, g.gl_pathv[i], 0))) continue; if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { error("Can't get current ownership of " @@ -693,6 +835,8 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) fatal("%d is not implemented", cmdnum); } + if (g.gl_pathc) + globfree(&g); if (path1) xfree(path1); if (path2)