=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/Attic/sftp-int.c,v retrieving revision 1.22.2.2 retrieving revision 1.22.2.3 diff -u -r1.22.2.2 -r1.22.2.3 --- src/usr.bin/ssh/Attic/sftp-int.c 2001/02/19 17:19:23 1.22.2.2 +++ src/usr.bin/ssh/Attic/sftp-int.c 2001/03/21 19:46:29 1.22.2.3 @@ -22,14 +22,14 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* XXX: finish implementation of all commands */ -/* XXX: do fnmatch() instead of using raw pathname */ /* XXX: globbed ls */ /* XXX: recursive operations */ #include "includes.h" -RCSID("$OpenBSD: sftp-int.c,v 1.22.2.2 2001/02/19 17:19:23 jason Exp $"); +RCSID("$OpenBSD: sftp-int.c,v 1.22.2.3 2001/03/21 19:46:29 jason Exp $"); +#include + #include "buffer.h" #include "xmalloc.h" #include "log.h" @@ -37,9 +37,16 @@ #include "sftp.h" #include "sftp-common.h" +#include "sftp-glob.h" #include "sftp-client.h" #include "sftp-int.h" +/* File to read commands from */ +extern FILE *infile; + +/* Version of server we are speaking to */ +int version; + /* Seperators for interactive commands */ #define WHITESPACE " \t\r\n" @@ -64,6 +71,8 @@ #define I_RM 18 #define I_RMDIR 19 #define I_SHELL 20 +#define I_SYMLINK 21 +#define I_VERSION 22 struct CMD { const char *c; @@ -84,6 +93,7 @@ { "lchdir", I_LCHDIR }, { "lls", I_LLS }, { "lmkdir", I_LMKDIR }, + { "ln", I_SYMLINK }, { "lpwd", I_LPWD }, { "ls", I_LS }, { "lumask", I_LUMASK }, @@ -94,6 +104,8 @@ { "rename", I_RENAME }, { "rm", I_RM }, { "rmdir", I_RMDIR }, + { "symlink", I_SYMLINK }, + { "version", I_VERSION }, { "!", I_SHELL }, { "?", I_HELP }, { NULL, -1} @@ -111,6 +123,7 @@ printf("help Display this help text\n"); printf("get remote-path [local-path] Download file\n"); printf("lls [ls-options [path]] Display local directory listing\n"); + printf("ln oldpath newpath Symlink remote file\n"); printf("lmkdir path Create local directory\n"); printf("lpwd Print local working directory\n"); printf("ls [path] Display remote directory listing\n"); @@ -123,6 +136,8 @@ printf("rename oldpath newpath Rename remote file\n"); printf("rmdir path Remove remote directory\n"); printf("rm path Delete remote file\n"); + printf("symlink oldpath newpath Symlink remote file\n"); + printf("version Show SFTP version\n"); printf("!command Execute 'command' in local shell\n"); printf("! Escape to local shell\n"); printf("? Synonym for help\n"); @@ -182,18 +197,51 @@ } char * +path_append(char *p1, char *p2) +{ + char *ret; + int len = strlen(p1) + strlen(p2) + 2; + + ret = xmalloc(len); + strlcpy(ret, p1, len); + strlcat(ret, "/", len); + strlcat(ret, p2, len); + + 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 @@ -236,7 +284,7 @@ /* Check for quoted filenames */ if (*cp == '\"' || *cp == '\'') { quot = *cp++; - + end = strchr(cp, quot); if (end == NULL) { error("Unterminated quote"); @@ -268,28 +316,185 @@ } int -infer_path(const char *p, char **ifp) +is_dir(char *path) { - char *cp; + struct stat sb; - debug("XXX: P = \"%s\"", p); + /* XXX: report errors? */ + if (stat(path, &sb) == -1) + return(0); - cp = strrchr(p, '/'); - if (cp == NULL) { - *ifp = xstrdup(p); + 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 parse_args(const char **cpp, int *pflag, unsigned long *n_arg, char **path1, char **path2) { @@ -349,12 +554,9 @@ /* Try to get second pathname (optional) */ if (get_pathname(&cp, path2)) return(-1); - /* Otherwise try to guess it from first path */ - if (*path2 == NULL && infer_path(*path1, path2)) - return(-1); break; case I_RENAME: - /* Get first pathname (mandatory) */ + case I_SYMLINK: if (get_pathname(&cp, path1)) return(-1); if (get_pathname(&cp, path2)) @@ -427,6 +629,7 @@ case I_PWD: case I_LPWD: case I_HELP: + case I_VERSION: break; default: fatal("Command not implemented"); @@ -440,63 +643,85 @@ parse_dispatch_command(int in, int out, const char *cmd, char **pwd) { char *path1, *path2, *tmp; - int pflag, cmdnum; + int pflag, cmdnum, i; unsigned long n_arg; Attrib a, *aa; - char path_buf[PATH_MAX]; + char path_buf[MAXPATHLEN]; + int err = 0; + glob_t g; 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: - path1 = make_absolute(path1, *pwd); - do_download(in, out, path1, path2, pflag); + err = process_get(in, out, path1, path2, *pwd, pflag); break; case I_PUT: - path2 = make_absolute(path2, *pwd); - do_upload(in, out, path1, path2, pflag); - break; - case I_RENAME: + err = process_put(in, out, path1, path2, *pwd, pflag); + break; + case I_RENAME: path1 = make_absolute(path1, *pwd); path2 = make_absolute(path2, *pwd); - do_rename(in, out, path1, path2); + err = do_rename(in, out, path1, path2); break; + case I_SYMLINK: + if (version < 3) { + error("The server (version %d) does not support " + "this operation", version); + err = -1; + } else { + path2 = make_absolute(path2, *pwd); + err = do_symlink(in, out, path1, path2); + } + break; case I_RM: path1 = make_absolute(path1, *pwd); - do_rm(in, out, path1); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + printf("Removing %s\n", g.gl_pathv[i]); + if (do_rm(in, out, g.gl_pathv[i]) == -1) + err = -1; + } break; case I_MKDIR: path1 = make_absolute(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = 0777; - do_mkdir(in, out, path1, &a); + err = do_mkdir(in, out, path1, &a); break; case I_RMDIR: path1 = make_absolute(path1, *pwd); - do_rmdir(in, out, path1); + err = do_rmdir(in, out, path1); break; case I_CHDIR: path1 = make_absolute(path1, *pwd); - if ((tmp = do_realpath(in, out, path1)) == NULL) + if ((tmp = do_realpath(in, out, path1)) == NULL) { + 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; } if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { error("Can't change directory: Can't check target"); xfree(tmp); + err = 1; break; } if (!S_ISDIR(aa->perm)) { error("Can't change directory: \"%s\" is not " "a directory", tmp); xfree(tmp); + err = 1; break; } xfree(*pwd); @@ -512,9 +737,9 @@ 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) && + if ((aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && !S_ISDIR(aa->perm)) { error("Can't ls: \"%s\" is not a directory", path1); break; @@ -522,14 +747,18 @@ do_ls(in, out, path1); break; case I_LCHDIR: - if (chdir(path1) == -1) + if (chdir(path1) == -1) { error("Couldn't change local directory to " "\"%s\": %s", path1, strerror(errno)); + err = 1; + } break; case I_LMKDIR: - if (mkdir(path1, 0777) == -1) + if (mkdir(path1, 0777) == -1) { error("Couldn't create local directory " "\"%s\": %s", path1, strerror(errno)); + err = 1; + } break; case I_LLS: local_do_ls(cmd); @@ -546,40 +775,52 @@ attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = n_arg; - do_setstat(in, out, path1, &a); + remote_glob(in, out, path1, GLOB_NOCHECK, NULL, &g); + for(i = 0; g.gl_pathv[i]; i++) { + printf("Changing mode on %s\n", g.gl_pathv[i]); + do_setstat(in, out, g.gl_pathv[i], &a); + } break; case I_CHOWN: path1 = make_absolute(path1, *pwd); - if (!(aa = do_stat(in, out, path1))) - break; - if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { - error("Can't get current ownership of " - "remote file \"%s\"", path1); - break; + 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], 0))) + continue; + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + continue; + } + printf("Changing owner on %s\n", g.gl_pathv[i]); + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa->uid = n_arg; + do_setstat(in, out, g.gl_pathv[i], aa); } - aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; - aa->uid = n_arg; - do_setstat(in, out, path1, aa); break; case I_CHGRP: path1 = make_absolute(path1, *pwd); - if (!(aa = do_stat(in, out, path1))) - break; - if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { - error("Can't get current ownership of " - "remote file \"%s\"", path1); - break; + 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], 0))) + continue; + if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { + error("Can't get current ownership of " + "remote file \"%s\"", g.gl_pathv[i]); + continue; + } + printf("Changing group on %s\n", g.gl_pathv[i]); + aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; + aa->gid = n_arg; + do_setstat(in, out, g.gl_pathv[i], aa); } - aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; - aa->gid = n_arg; - do_setstat(in, out, path1, aa); break; case I_PWD: printf("Remote working directory: %s\n", *pwd); break; case I_LPWD: if (!getcwd(path_buf, sizeof(path_buf))) - error("Couldn't get local cwd: %s\n", + error("Couldn't get local cwd: %s", strerror(errno)); else printf("Local working directory: %s\n", @@ -590,14 +831,24 @@ case I_HELP: help(); break; + case I_VERSION: + printf("SFTP protocol version %d\n", version); + break; default: fatal("%d is not implemented", cmdnum); } + if (g.gl_pathc) + globfree(&g); if (path1) xfree(path1); if (path2) xfree(path2); + + /* If an error occurs in batch mode we should abort. */ + if (infile != stdin && err > 0) + return -1; + return(0); } @@ -607,12 +858,16 @@ char *pwd; char cmd[2048]; + version = do_init(fd_in, fd_out); + if (version == -1) + fatal("Couldn't initialise connection to server"); + pwd = do_realpath(fd_in, fd_out, "."); if (pwd == NULL) fatal("Need cwd"); setvbuf(stdout, NULL, _IOLBF, 0); - setvbuf(stdin, NULL, _IOLBF, 0); + setvbuf(infile, NULL, _IOLBF, 0); for(;;) { char *cp; @@ -620,13 +875,16 @@ printf("sftp> "); /* XXX: use libedit */ - if (fgets(cmd, sizeof(cmd), stdin) == NULL) { + if (fgets(cmd, sizeof(cmd), infile) == NULL) { printf("\n"); break; - } + } else if (infile != stdin) /* Bluff typing */ + printf("%s", cmd); + cp = strrchr(cmd, '\n'); if (cp) *cp = '\0'; + if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd)) break; }