📄 scp.c
字号:
} return str;}/* * Determine whether a string is entirely composed of dots. */static int is_dots(char *str){ return str[strspn(str, ".")] == '\0';}/* * Wait for a response from the other side. * Return 0 if ok, -1 if error. */static int response(void){ char ch, resp, rbuf[2048]; int p; if (ssh_scp_recv((unsigned char *) &resp, 1) <= 0) bump("Lost connection"); p = 0; switch (resp) { case 0: /* ok */ return (0); default: rbuf[p++] = resp; /* fallthrough */ case 1: /* error */ case 2: /* fatal error */ do { if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0) bump("Protocol error: Lost connection"); rbuf[p++] = ch; } while (p < sizeof(rbuf) && ch != '\n'); rbuf[p - 1] = '\0'; if (resp == 1) tell_user(stderr, "%s\n", rbuf); else bump("%s", rbuf); errs++; return (-1); }}int sftp_recvdata(char *buf, int len){ return ssh_scp_recv((unsigned char *) buf, len);}int sftp_senddata(char *buf, int len){ back->send(backhandle, buf, len); return 1;}/* ---------------------------------------------------------------------- * sftp-based replacement for the hacky `pscp -ls'. */static int sftp_ls_compare(const void *av, const void *bv){ const struct fxp_name *a = (const struct fxp_name *) av; const struct fxp_name *b = (const struct fxp_name *) bv; return strcmp(a->filename, b->filename);}void scp_sftp_listdir(char *dirname){ struct fxp_handle *dirh; struct fxp_names *names; struct fxp_name *ournames; struct sftp_packet *pktin; struct sftp_request *req, *rreq; int nnames, namesize; int i; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; return; } printf("Listing directory %s\n", dirname); sftp_register(req = fxp_opendir_send(dirname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); dirh = fxp_opendir_recv(pktin, rreq); if (dirh == NULL) { printf("Unable to open %s: %s\n", dirname, fxp_error()); } else { nnames = namesize = 0; ournames = NULL; while (1) { sftp_register(req = fxp_readdir_send(dirh)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); names = fxp_readdir_recv(pktin, rreq); if (names == NULL) { if (fxp_error_type() == SSH_FX_EOF) break; printf("Reading directory %s: %s\n", dirname, fxp_error()); break; } if (names->nnames == 0) { fxp_free_names(names); break; } if (nnames + names->nnames >= namesize) { namesize += names->nnames + 128; ournames = sresize(ournames, namesize, struct fxp_name); } for (i = 0; i < names->nnames; i++) ournames[nnames++] = names->names[i]; names->nnames = 0; /* prevent free_names */ fxp_free_names(names); } sftp_register(req = fxp_close_send(dirh)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); fxp_close_recv(pktin, rreq); /* * Now we have our filenames. Sort them by actual file * name, and then output the longname parts. */ qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare); /* * And print them. */ for (i = 0; i < nnames; i++) printf("%s\n", ournames[i].longname); }}/* ---------------------------------------------------------------------- * Helper routines that contain the actual SCP protocol elements, * implemented both as SCP1 and SFTP. */static struct scp_sftp_dirstack { struct scp_sftp_dirstack *next; struct fxp_name *names; int namepos, namelen; char *dirpath; char *wildcard; int matched_something; /* wildcard match set was non-empty */} *scp_sftp_dirstack_head;static char *scp_sftp_remotepath, *scp_sftp_currentname;static char *scp_sftp_wildcard;static int scp_sftp_targetisdir, scp_sftp_donethistarget;static int scp_sftp_preserve, scp_sftp_recursive;static unsigned long scp_sftp_mtime, scp_sftp_atime;static int scp_has_times;static struct fxp_handle *scp_sftp_filehandle;static struct fxp_xfer *scp_sftp_xfer;static uint64 scp_sftp_fileoffset;void scp_source_setup(char *target, int shouldbedir){ if (using_sftp) { /* * Find out whether the target filespec is in fact a * directory. */ struct sftp_packet *pktin; struct sftp_request *req, *rreq; struct fxp_attrs attrs; int ret; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; return; } sftp_register(req = fxp_stat_send(target)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); ret = fxp_stat_recv(pktin, rreq, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) scp_sftp_targetisdir = 0; else scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0; if (shouldbedir && !scp_sftp_targetisdir) { bump("pscp: remote filespec %s: not a directory\n", target); } scp_sftp_remotepath = dupstr(target); scp_has_times = 0; } else { (void) response(); }}int scp_send_errmsg(char *str){ if (using_sftp) { /* do nothing; we never need to send our errors to the server */ } else { back->send(backhandle, "\001", 1);/* scp protocol error prefix */ back->send(backhandle, str, strlen(str)); } return 0; /* can't fail */}int scp_send_filetimes(unsigned long mtime, unsigned long atime){ if (using_sftp) { scp_sftp_mtime = mtime; scp_sftp_atime = atime; scp_has_times = 1; return 0; } else { char buf[80]; sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime); back->send(backhandle, buf, strlen(buf)); return response(); }}int scp_send_filename(char *name, unsigned long size, int modes){ if (using_sftp) { char *fullname; struct sftp_packet *pktin; struct sftp_request *req, *rreq; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); } else { fullname = dupstr(scp_sftp_remotepath); } sftp_register(req = fxp_open_send(fullname, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); scp_sftp_filehandle = fxp_open_recv(pktin, rreq); if (!scp_sftp_filehandle) { tell_user(stderr, "pscp: unable to open %s: %s", fullname, fxp_error()); errs++; return 1; } scp_sftp_fileoffset = uint64_make(0, 0); scp_sftp_xfer = xfer_upload_init(scp_sftp_filehandle, scp_sftp_fileoffset); sfree(fullname); return 0; } else { char buf[40]; sprintf(buf, "C%04o %lu ", modes, size); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); return response(); }}int scp_send_filedata(char *data, int len){ if (using_sftp) { int ret; struct sftp_packet *pktin; if (!scp_sftp_filehandle) { return 1; } while (!xfer_upload_ready(scp_sftp_xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); if (!ret) { tell_user(stderr, "error while writing: %s\n", fxp_error()); errs++; return 1; } } xfer_upload_data(scp_sftp_xfer, data, len); scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len); return 0; } else { int bufsize = back->send(backhandle, data, len); /* * If the network transfer is backing up - that is, the * remote site is not accepting data as fast as we can * produce it - then we must loop on network events until * we have space in the buffer again. */ while (bufsize > MAX_SCP_BUFSIZE) { if (ssh_sftp_loop_iteration() < 0) return 1; bufsize = back->sendbuffer(backhandle); } return 0; }}int scp_send_finish(void){ if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req, *rreq; int ret; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); xfer_upload_gotpkt(scp_sftp_xfer, pktin); } xfer_cleanup(scp_sftp_xfer); if (!scp_sftp_filehandle) { return 1; } if (scp_has_times) { attrs.flags = SSH_FILEXFER_ATTR_ACMODTIME; attrs.atime = scp_sftp_atime; attrs.mtime = scp_sftp_mtime; sftp_register(req = fxp_fsetstat_send(scp_sftp_filehandle, attrs)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); ret = fxp_fsetstat_recv(pktin, rreq); if (!ret) { tell_user(stderr, "unable to set file times: %s\n", fxp_error()); errs++; } } sftp_register(req = fxp_close_send(scp_sftp_filehandle)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); fxp_close_recv(pktin, rreq); scp_has_times = 0; return 0; } else { back->send(backhandle, "", 1); return response(); }}char *scp_save_remotepath(void){ if (using_sftp) return scp_sftp_remotepath; else return NULL;}void scp_restore_remotepath(char *data){ if (using_sftp) scp_sftp_remotepath = data;}int scp_send_dirname(char *name, int modes){ if (using_sftp) { char *fullname; char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req, *rreq; int ret; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); } else { fullname = dupstr(scp_sftp_remotepath); } /* * We don't worry about whether we managed to create the * directory, because if it exists already it's OK just to * use it. Instead, we will stat it afterwards, and if it * exists and is a directory we will assume we were either * successful or it didn't matter. */ sftp_register(req = fxp_mkdir_send(fullname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); ret = fxp_mkdir_recv(pktin, rreq); if (!ret) err = fxp_error(); else err = "server reported no error"; sftp_register(req = fxp_stat_send(fullname)); rreq = sftp_find_request(pktin = sftp_recv()); assert(rreq == req); ret = fxp_stat_recv(pktin, rreq, &attrs); if (!ret || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) || !(attrs.permissions & 0040000)) { tell_user(stderr, "unable to create directory %s: %s", fullname, err); errs++; return 1; } scp_sftp_remotepath = fullname; return 0; } else { char buf[40]; sprintf(buf, "D%04o 0 ", modes); back->send(backhandle, buf, strlen(buf)); back->send(backhandle, name, strlen(name)); back->send(backhandle, "\n", 1); return response(); }}int scp_send_enddir(void){ if (using_sftp) { sfree(scp_sftp_remotepath); return 0; } else { back->send(backhandle, "E\n", 2); return response(); }}/* * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init. * That's bad. The difference is that scp_sink_setup is called once * right at the start, whereas scp_sink_init is called to * initialise every level of recursion in the protocol. */int scp_sink_setup(char *source, int preserve, int recursive){ if (using_sftp) { char *newsource; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; return 1; } /* * It's possible that the source string we've been given * contains a wildcard. If so, we must split the directory * away from the wildcard itself (throwing an error if any * wildcardness comes before the final slash) and arrange * things so that a dirstack entry will be set up. */ newsource = snewn(1+strlen(source), char); if (!wc_unescape(newsource, source)) { /* Yes, here we go; it's a wildcard. Bah. */ char *dupsource, *lastpart, *dirpart, *wildcard; dupsource = dupstr(source); lastpart = stripslashes(dupsource, 0); wildcard = dupstr(lastpart); *lastpart = '\0'; if (*dupsource && dupsource[1]) { /* * The remains of dupsource are at least two * characters long, meaning the pathname wasn't * empty or just `/'. Hence, we remove the trailing * slash. */ lastpart[-1] = '\0'; } else if (!*dupsource) { /* * The remains of dupsource are _empty_ - the whole * pathname was a wildcard. Hence we need to * replace it with ".". */ sfree(dupsource); dupsource = dupstr("."); } /* * Now we have separated our string into dupsource (the * directory part) and wildcard. Both of these will * need freeing at some point. Next step is to remove * wildcard escapes from the directory part, throwing * an error if it contains a real wildcard. */ dirpart = snewn(1+strlen(dupsource), char); if (!wc_unescape(dirpart, dupsource)) { tell_user(stderr, "%s: multiple-level wildcards unsupported", source); errs++; sfree(dirpart); sfree(wildcard); sfree(dupsource); return 1; } /* * Now we have dirpart (unescaped, ie a valid remote * path), and wildcard (a wildcard). This will be * sufficient to arrange a dirstack entry. */ scp_sftp_remotepath = dirpart; scp_sftp_wildcard = wildcard; sfree(dupsource); } else { scp_sftp_remotepath = newsource; scp_sftp_wildcard = NULL; } scp_sftp_preserve = preserve; scp_sftp_recursive = recursive; scp_sftp_donethistarget = 0; scp_sftp_dirstack_head = NULL; } return 0;}int scp_sink_init(void){ if (!using_sftp) { back->send(backhandle, "", 1); } return 0;}#define SCP_SINK_FILE 1#define SCP_SINK_DIR 2#define SCP_SINK_ENDDIR 3#define SCP_SINK_RETRY 4 /* not an action; just try again */struct scp_sink_action { int action; /* FILE, DIR, ENDDIR */ char *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ int mode; /* access mode (not ENDDIR) */ unsigned long size; /* file size (not ENDDIR) */ int settime; /* 1 if atime and mtime are filled */ unsigned long atime, mtime; /* access times for the file */};int scp_get_sink_action(struct scp_sink_action *act){ if (using_sftp) { char *fname; int must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req, *rreq; int ret; if (!scp_sftp_dirstack_head) { if (!scp_sftp_donethistarget) { /* * Simple case: we are only dealing with one file. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -