解决openssh与ssh2之间文件传输 |
发布: 2011-01-28 09:19 |
ssh2一个商业的ssh服务协议实现,其实现的scp模块与GPL的openssh的scp不兼容,在客户端的用openssh的scp向服务端为ssh2的服务器传输文件时失败,原因也在于此。 但如果scp使用sftp传输,则无论服务端是openssh还是ssh2的,都不会有问题,这引出了openssh中的scp使用sftp协议传输的补丁要求。 根据5.4版本的scp over sftp,移植到了最新的openssh 5.7版本,初步测试没有问题, 5.4的补丁,http://www.phys98.homeip.net/~ide/aboutopenssh-e.html 5.7的补丁, [code type="diff"] diff --git a/Makefile.in b/Makefile.in index 77a78aa..462a44a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -147,8 +147,8 @@ ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) -scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o - $(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) +scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o sftp-client.o sftp-common.o sftp-glob.o + $(LD) -o $@ scp.o progressmeter.o bufaux.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) diff --git a/scp.c b/scp.c index 18b2597..c1c925f 100644 --- a/scp.c +++ b/scp.c @@ -114,12 +114,25 @@ #include "misc.h" #include "progressmeter.h" +#include "buffer.h" +#include "match.h" + +#include "sftp.h" +#include "sftp-common.h" +#include "sftp-client.h" + +int remote_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); + +#define SCP_USE_SFTP_HOSTS_ENV "SCP_USE_SFTP_HOSTS" +#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ + extern char *__progname; #define COPY_BUFLEN 16384 -int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); -int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout); +int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int use_subsys); +int do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout, int use_subsys); /* Struct for addargs */ arglist args; @@ -138,6 +151,9 @@ int verbose_mode = 0; /* This is set to zero if the progressmeter is not desired. */ int showprogress = 1; +/* SIGINT received during command processing */ +volatile sig_atomic_t interrupted = 0; + /* * This is set to non-zero if remote-remote copy should be piped * through this process. @@ -150,6 +166,11 @@ char *ssh_program = _PATH_SSH_PROGRAM; /* This is used to store the pid of ssh_program */ pid_t do_cmd_pid = -1; +/* Use sftp protocol */ +int use_sftp = 0; +int mode_sftp = 1; /* set as default by liuguangzhao@users.sf.net */ +char *scp_use_sftp_hosts = NULL; + static void killchild(int signo) { @@ -226,7 +247,7 @@ do_local_cmd(arglist *a) */ int -do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) +do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int use_subsys) { int pin[2], pout[2], reserved[2]; @@ -275,6 +296,8 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) } addargs(&args, "--"); addargs(&args, "%s", host); + if (use_subsys) + addargs(&args, "-s"); addargs(&args, "%s", cmd); execvp(ssh_program, args.list); @@ -300,7 +323,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) * This way the input and output of two commands can be connected. */ int -do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) +do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout, int use_subsys) { pid_t pid; int status; @@ -324,6 +347,8 @@ do_cmd2(char *host, char *remuser, char *cmd, int fdin, int fdout) } addargs(&args, "--"); addargs(&args, "%s", host); + if (use_subsys) + addargs(&args, "-s"); addargs(&args, "%s", cmd); execvp(ssh_program, args.list); @@ -357,12 +382,15 @@ int pflag, iamremote, iamrecursive, targetshouldbedirectory; #define CMDNEEDS 64 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ +struct sftp_conn *conn = NULL; + int response(void); -void rsource(char *, struct stat *); +void rsource(char *, struct stat *, char *); void sink(int, char *[]); -void source(int, char *[]); +void source(int, char *[], char *); void tolocal(int, char *[]); void toremote(char *, int, char *[]); +void sftp_download(struct sftp_conn *, char *, char *); void usage(void); int @@ -395,7 +423,7 @@ main(int argc, char **argv) addargs(&args, "-oClearAllForwardings=yes"); fflag = tflag = 0; - while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) + while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:sL:")) != -1) switch (ch) { /* User-visible flags. */ case '1': @@ -471,6 +499,12 @@ main(int argc, char **argv) setmode(0, O_BINARY); #endif break; + case 's': + mode_sftp = 1; + break; + case 'L': + scp_use_sftp_hosts = xstrdup(optarg); + break; default: usage(); } @@ -489,7 +523,7 @@ main(int argc, char **argv) if (fflag) { /* Follow "protocol", send data. */ (void) response(); - source(argc, argv); + source(argc, argv, NULL); exit(errs != 0); } if (tflag) { @@ -497,6 +531,10 @@ main(int argc, char **argv) sink(argc, argv); exit(errs != 0); } + + if (scp_use_sftp_hosts == NULL) + scp_use_sftp_hosts = getenv(SCP_USE_SFTP_HOSTS_ENV); + if (argc < 2) usage(); if (argc > 2) @@ -602,12 +640,12 @@ toremote(char *targ, int argc, char **argv) suser = NULL; } xasprintf(&bp, "%s -f -- %s", cmd, src); - if (do_cmd(host, suser, bp, &remin, &remout) < 0) + if (do_cmd(host, suser, bp, &remin, &remout, 0) < 0) exit(1); (void) xfree(bp); host = cleanhostname(thost); xasprintf(&bp, "%s -t -- %s", cmd, targ); - if (do_cmd2(host, tuser, bp, remin, remout) < 0) + if (do_cmd2(host, tuser, bp, remin, remout, 0) < 0) exit(1); (void) xfree(bp); (void) close(remin); @@ -652,16 +690,46 @@ 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); + int match = 0; + host = cleanhostname(thost); - if (do_cmd(host, tuser, bp, &remin, - &remout) < 0) - exit(1); - if (response() < 0) - exit(1); + + /* if (do_cmd(host, tuser, bp, &remin, */ + /* &remout) < 0) */ + /* exit(1); */ + /* if (response() < 0) */ + /* exit(1); */ + if (scp_use_sftp_hosts != NULL) + match = match_pattern_list(host, scp_use_sftp_hosts, + strlen(scp_use_sftp_hosts), 0); + if (match == 1 || (match == 0 && mode_sftp)) { + use_sftp = 1; + + if (verbose_mode) + fprintf(stderr, "Use sftp: %s\n", host); + + xasprintf(&bp, "sftp"); + if (do_cmd(host, tuser, bp, &remin, + &remout, 1) < 0) + exit(1); + conn = do_init(remin, remout, 32768, DEFAULT_NUM_REQUESTS, limit_kbps); + if (conn == NULL) { + exit(1); + } + } else { + use_sftp = 0; + + xasprintf(&bp, "%s -t -- %s", cmd, targ); + if (do_cmd(host, tuser, bp, &remin, + &remout, 0) < 0) + exit(1); + if (response() < 0) + exit(1); + } (void) xfree(bp); } - source(1, argv + i); + source(1, argv + i, targ); } } xfree(arg); @@ -672,7 +740,7 @@ tolocal(int argc, char **argv) { char *bp, *host, *src, *suser; arglist alist; - int i; + int i, match = 0; memset(&alist, '\0', sizeof(alist)); alist.list = NULL; @@ -706,20 +774,107 @@ tolocal(int argc, char **argv) } host = cleanhostname(host); xasprintf(&bp, "%s -f -- %s", cmd, src); - if (do_cmd(host, suser, bp, &remin, &remout) < 0) { - (void) xfree(bp); - ++errs; - continue; - } - xfree(bp); - sink(1, argv + argc - 1); + /* if (do_cmd(host, suser, bp, &remin, &remout) < 0) { */ + /* (void) xfree(bp); */ + /* ++errs; */ + /* continue; */ + /* } */ + + if (scp_use_sftp_hosts != NULL) + match = match_pattern_list(host, scp_use_sftp_hosts, + strlen(scp_use_sftp_hosts), 0); + if (match == 1 || (match == 0 && mode_sftp)) { + int n, targisdir = 0; + char *targ = *(argv + argc - 1); + glob_t g; + struct stat stb; + + use_sftp = 1; + + if (verbose_mode) + fprintf(stderr, "Use sftp: %s\n", host); + + xasprintf(&bp, "sftp"); + if (do_cmd(host, suser, bp, &remin, &remout, 1) < 0) { + (void) xfree(bp); + ++errs; + continue; + } + xfree(bp); + conn = do_init(remin, remout, 32768, DEFAULT_NUM_REQUESTS, limit_kbps); + if (conn == NULL) { + ++errs; + continue; + } + + n = strlen(targ); + while (n > 1 && targ[n-1] == '/') + targ[--n] = '\0'; + + remote_glob(conn, src, GLOB_NOCHECK, NULL, &g); + + if (!g.gl_pathv[0]) { + ++errs; + if (g.gl_pathc) + globfree(&g); + do_fin(conn); + continue; + } + + if (g.gl_pathv[1]) + targetshouldbedirectory = 1; + + if (targetshouldbedirectory) + verifydir(targ); + + if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) + targisdir = 1; + + for (n = 0; g.gl_pathv[n]; n++) { + if (targisdir) { + char *last, *path; + + if ((last = strrchr(g.gl_pathv[n], '/')) == NULL) + last = g.gl_pathv[n]; + else + last++; + + xasprintf(&path, "%s/%s", targ, last); + + sftp_download(conn, g.gl_pathv[n], path); + + xfree(path); + } else { + sftp_download(conn, g.gl_pathv[n], targ); + } + } + + if (g.gl_pathc) + globfree(&g); + + do_fin(conn); + } else { + use_sftp = 0; + + xasprintf(&bp, "%s -f -- %s", cmd, src); + if (do_cmd(host, suser, bp, &remin, &remout, 0) < 0) { + (void) xfree(bp); + ++errs; + continue; + } + } + xfree(bp); + sink(1, argv + argc - 1); + // xfree(bp); + // sink(1, argv + argc - 1); (void) close(remin); + (void) close(remout); remin = remout = -1; } } void -source(int argc, char **argv) +source(int argc, char **argv, char *targ) { struct stat stb; static BUF buffer; @@ -746,6 +901,11 @@ source(int argc, char **argv) syserr: run_err("%s: %s", name, strerror(errno)); goto next; } + if ((last = strrchr(name, '/')) == NULL) { + last = name; + } else { + ++last; + } if (stb.st_size < 0) { run_err("%s: %s", name, "Negative file size"); goto next; @@ -756,7 +916,23 @@ syserr: run_err("%s: %s", name, strerror(errno)); break; case S_IFDIR: if (iamrecursive) { - rsource(name, &stb); + // rsource(name, &stb); + char *path; + + xasprintf(&path, "%s/%s", targ, last); + + if (use_sftp) { + int err; + Attrib attr; + + attrib_clear(&attr); + attr.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; + attr.perm = stb.st_mode & 0777; + err = do_mkdir(conn, path, &attr, 0); + } + + rsource(name, &stb, path); + xfree(path); goto next; } /* FALLTHROUGH */ @@ -764,11 +940,39 @@ syserr: run_err("%s: %s", name, strerror(errno)); run_err("%s: not a regular file", name); goto next; } - if ((last = strrchr(name, '/')) == NULL) - last = name; - else - ++last; + /* if ((last = strrchr(name, '/')) == NULL) */ + /* last = name; */ + /* else */ + /* ++last; */ curfile = last; + + if (use_sftp) { + Attrib *attr; + int len = strlen(targ); + + attr = do_stat(conn, targ, 1); + + if (len > 1 && targ[len-1] == '/' && (!attr || !S_ISDIR(attr->perm))) { + run_err("scp: %s: %s", targ, strerror(EISDIR)); + continue; + } + + if (attr && (attr->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) + && S_ISDIR(attr->perm)){ + char *rpath; + + xasprintf(&rpath, "%s/%s", targ, last); + if (do_upload(conn, name, rpath, pflag) != 0) + errs++; + xfree(rpath); + } else { + if (do_upload(conn, name, targ , pflag) != 0) + errs++; + } + + continue; + } + if (pflag) { /* * Make it compatible with possible future @@ -842,7 +1046,7 @@ next: if (fd != -1) { } void -rsource(char *name, struct stat *statp) +rsource(char *name, struct stat *statp, char *targ) { DIR *dirp; struct dirent *dp; @@ -857,7 +1061,7 @@ rsource(char *name, struct stat *statp) last = name; else last++; - if (pflag) { + if (pflag && !use_sftp) { (void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", (u_long) statp->st_mtime, (u_long) statp->st_atime); @@ -871,11 +1075,18 @@ rsource(char *name, struct stat *statp) (u_int) (statp->st_mode & FILEMODEMASK), 0, last); if (verbose_mode) fprintf(stderr, "Entering directory: %s", path); - (void) atomicio(vwrite, remout, path, strlen(path)); - if (response() < 0) { - closedir(dirp); - return; - } + /* (void) atomicio(vwrite, remout, path, strlen(path)); */ + /* if (response() < 0) { */ + /* closedir(dirp); */ + /* return; */ + /* } */ + if (!use_sftp) { + (void) atomicio(vwrite, remout, path, strlen(path)); + if (response() < 0) { + closedir(dirp); + return; + } + } while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0) continue; @@ -887,11 +1098,15 @@ rsource(char *name, struct stat *statp) } (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); vect[0] = path; - source(1, vect); + source(1, vect, targ); } (void) closedir(dirp); - (void) atomicio(vwrite, remout, "E\n", 2); - (void) response(); + // (void) atomicio(vwrite, remout, "E\n", 2); + // (void) response(); + if (!use_sftp) { + (void) atomicio(vwrite, remout, "E\n", 2); + (void) response(); + } } void @@ -1176,6 +1391,68 @@ screwup: run_err("protocol error: %s", why); exit(1); } +void +sftp_rdownload(struct sftp_conn *conn, char *src, char *dist) +{ + int n; + SFTP_DIRENT **d; + char *path, *rpath; + + if (do_readdir(conn, src, &d) != 0) { + errs++; + return; + } + + for (n = 0; d[n] != NULL; n++) { + if (!strcmp(d[n]->filename, ".") || !strcmp(d[n]->filename, "..")) + continue; + + xasprintf(&path , "%s/%s", src , d[n]->filename); + xasprintf(&rpath, "%s/%s", dist, d[n]->filename); + + sftp_download(conn, path, rpath); + + xfree(rpath); + xfree(path); + } + + free_sftp_dirents(d); +} + +void +sftp_download(struct sftp_conn *conn, char *src, char *dist) +{ + int len; + Attrib *attr; + + len = strlen(src); + while (len > 1 && src[len-1] == '/') + src[--len] = '\0'; + + if ((attr = do_stat(conn, src, 1)) == NULL) { + errs++; + return; + } + if (!(attr->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { + return; + } + if (S_ISDIR(attr->perm)) { + if (!iamrecursive) { + return; + } + if (mkdir(dist, attr->perm | S_IRWXU) < 0) { + fprintf(stderr, "%s: %s", dist, strerror(errno)); + return; + } + + sftp_rdownload(conn, src, dist); + return; + } + + if (do_download(conn, src, dist, attr, pflag) != 0) { + errs++; + } +} int response(void) @@ -1215,7 +1492,7 @@ usage(void) { (void) fprintf(stderr, "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" - " [-l limit] [-o ssh_option] [-P port] [-S program]\n" + " [-l limit] [-o ssh_option] [-P port] [-S program] [-s]\n" " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); exit(1); } @@ -1227,7 +1504,9 @@ run_err(const char *fmt,...) va_list ap; ++errs; - if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { + if (!use_sftp) { + if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { + // if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { (void) fprintf(fp, "%c", 0x01); (void) fprintf(fp, "scp: "); va_start(ap, fmt); @@ -1235,7 +1514,8 @@ run_err(const char *fmt,...) va_end(ap); (void) fprintf(fp, "\n"); (void) fflush(fp); - } + } + } if (!iamremote) { va_start(ap, fmt); @@ -1320,10 +1600,13 @@ allocbuf(BUF *bp, int fd, int blksize) void lostconn(int signo) { - if (!iamremote) + if (!iamremote) { write(STDERR_FILENO, "lost connection\n", 16); - if (signo) + } + if (signo) { _exit(1); - else + } + else { exit(1); + } } diff --git a/sftp-client.c b/sftp-client.c index caa384b..9e3b808 100644 --- a/sftp-client.c +++ b/sftp-client.c @@ -414,6 +414,14 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, return ret; } +void +do_fin(struct sftp_conn *conn) +{ + close(conn->fd_in); + close(conn->fd_out); + + xfree(conn); +} u_int sftp_proto_version(struct sftp_conn *conn) diff --git a/sftp-client.h b/sftp-client.h index aef54ef..1219ac1 100644 --- a/sftp-client.h +++ b/sftp-client.h @@ -52,6 +52,8 @@ struct sftp_statvfs { * a pointer to a initialized sftp_conn struct on success. */ struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t); +/* Finalize a SSH filexfer connection. */ +void do_fin(struct sftp_conn *); u_int sftp_proto_version(struct sftp_conn *); diff --git a/sftp-glob.c b/sftp-glob.c index cdc2708..73ed168 100644 --- a/sftp-glob.c +++ b/sftp-glob.c @@ -47,6 +47,14 @@ static void * fudge_opendir(const char *path) { struct SFTP_OPENDIR *r; + Attrib *a; + + if (!(a = do_stat(cur.conn, (char *)path, 1))) + return(NULL); + if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) + return(NULL); + if (!S_ISDIR(a->perm)) + return(NULL); r = xmalloc(sizeof(*r)); [/code] |
原文: http://qtchina.tk/?q=node/549 |
Powered by zexport
|