📄 pscp.c
字号:
scp_sftp_donethistarget = 1;
} else {
/*
* Even simpler case: one file _which we've done_.
* Return 1 (finished).
*/
return 1;
}
} else {
/*
* We're now in the middle of stepping through a list
* of names returned from fxp_readdir(); so let's carry
* on.
*/
struct scp_sftp_dirstack *head = scp_sftp_dirstack_head;
while (head->namepos < head->namelen &&
(is_dots(head->names[head->namepos].filename) ||
(head->wildcard &&
!wc_match(head->wildcard,
head->names[head->namepos].filename))))
head->namepos++; /* skip . and .. */
if (head->namepos < head->namelen) {
head->matched_something = 1;
fname = dupcat(head->dirpath, "/",
head->names[head->namepos++].filename,
NULL);
must_free_fname = 1;
} else {
/*
* We've come to the end of the list; pop it off
* the stack and return an ENDDIR action (or RETRY
* if this was a wildcard match).
*/
if (head->wildcard) {
act->action = SCP_SINK_RETRY;
if (!head->matched_something) {
tell_user(stderr, "pscp: wildcard '%s' matched "
"no files", head->wildcard);
errs++;
}
sfree(head->wildcard);
} else {
act->action = SCP_SINK_ENDDIR;
}
sfree(head->dirpath);
sfree(head->names);
scp_sftp_dirstack_head = head->next;
sfree(head);
return 0;
}
}
/*
* Now we have a filename. Stat it, and see if it's a file
* or a directory.
*/
sftp_register(req = fxp_stat_send(fname));
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)) {
tell_user(stderr, "unable to identify %s: %s", fname,
ret ? "file type not supplied" : fxp_error());
errs++;
return 1;
}
if (attrs.permissions & 0040000) {
struct scp_sftp_dirstack *newitem;
struct fxp_handle *dirhandle;
int nnames, namesize;
struct fxp_name *ournames;
struct fxp_names *names;
/*
* It's a directory. If we're not in recursive mode,
* this merits a complaint (which is fatal if the name
* was specified directly, but not if it was matched by
* a wildcard).
*
* We skip this complaint completely if
* scp_sftp_wildcard is set, because that's an
* indication that we're not actually supposed to
* _recursively_ transfer the dir, just scan it for
* things matching the wildcard.
*/
if (!scp_sftp_recursive && !scp_sftp_wildcard) {
tell_user(stderr, "pscp: %s: is a directory", fname);
errs++;
if (must_free_fname) sfree(fname);
if (scp_sftp_dirstack_head) {
act->action = SCP_SINK_RETRY;
return 0;
} else {
return 1;
}
}
/*
* Otherwise, the fun begins. We must fxp_opendir() the
* directory, slurp the filenames into memory, return
* SCP_SINK_DIR (unless this is a wildcard match), and
* set targetisdir. The next time we're called, we will
* run through the list of filenames one by one,
* matching them against a wildcard if present.
*
* If targetisdir is _already_ set (meaning we're
* already in the middle of going through another such
* list), we must push the other (target,namelist) pair
* on a stack.
*/
sftp_register(req = fxp_opendir_send(fname));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
dirhandle = fxp_opendir_recv(pktin, rreq);
if (!dirhandle) {
tell_user(stderr, "scp: unable to open directory %s: %s",
fname, fxp_error());
if (must_free_fname) sfree(fname);
errs++;
return 1;
}
nnames = namesize = 0;
ournames = NULL;
while (1) {
int i;
sftp_register(req = fxp_readdir_send(dirhandle));
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;
tell_user(stderr, "scp: reading directory %s: %s\n",
fname, fxp_error());
if (must_free_fname) sfree(fname);
sfree(ournames);
errs++;
return 1;
}
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++) {
if (!strcmp(names->names[i].filename, ".") ||
!strcmp(names->names[i].filename, "..")) {
/*
* . and .. are normal consequences of
* reading a directory, and aren't worth
* complaining about.
*/
} else if (!vet_filename(names->names[i].filename)) {
tell_user(stderr, "ignoring potentially dangerous server-"
"supplied filename '%s'\n",
names->names[i].filename);
} else
ournames[nnames++] = names->names[i];
}
names->nnames = 0; /* prevent free_names */
fxp_free_names(names);
}
sftp_register(req = fxp_close_send(dirhandle));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
fxp_close_recv(pktin, rreq);
newitem = snew(struct scp_sftp_dirstack);
newitem->next = scp_sftp_dirstack_head;
newitem->names = ournames;
newitem->namepos = 0;
newitem->namelen = nnames;
if (must_free_fname)
newitem->dirpath = fname;
else
newitem->dirpath = dupstr(fname);
if (scp_sftp_wildcard) {
newitem->wildcard = scp_sftp_wildcard;
newitem->matched_something = 0;
scp_sftp_wildcard = NULL;
} else {
newitem->wildcard = NULL;
}
scp_sftp_dirstack_head = newitem;
if (newitem->wildcard) {
act->action = SCP_SINK_RETRY;
} else {
act->action = SCP_SINK_DIR;
act->buf = dupstr(stripslashes(fname, 0));
act->name = act->buf;
act->size = uint64_make(0,0); /* duhh, it's a directory */
act->mode = 07777 & attrs.permissions;
if (scp_sftp_preserve &&
(attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
act->atime = attrs.atime;
act->mtime = attrs.mtime;
act->settime = 1;
} else
act->settime = 0;
}
return 0;
} else {
/*
* It's a file. Return SCP_SINK_FILE.
*/
act->action = SCP_SINK_FILE;
act->buf = dupstr(stripslashes(fname, 0));
act->name = act->buf;
if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
act->size = attrs.size;
} else
act->size = uint64_make(ULONG_MAX,ULONG_MAX); /* no idea */
act->mode = 07777 & attrs.permissions;
if (scp_sftp_preserve &&
(attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
act->atime = attrs.atime;
act->mtime = attrs.mtime;
act->settime = 1;
} else
act->settime = 0;
if (must_free_fname)
scp_sftp_currentname = fname;
else
scp_sftp_currentname = dupstr(fname);
return 0;
}
} else {
int done = 0;
int i, bufsize;
int action;
char ch;
act->settime = 0;
act->buf = NULL;
bufsize = 0;
while (!done) {
if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)
return 1;
if (ch == '\n')
bump("Protocol error: Unexpected newline");
i = 0;
action = ch;
do {
if (ssh_scp_recv((unsigned char *) &ch, 1) <= 0)
bump("Lost connection");
if (i >= bufsize) {
bufsize = i + 128;
act->buf = sresize(act->buf, bufsize, char);
}
act->buf[i++] = ch;
} while (ch != '\n');
act->buf[i - 1] = '\0';
switch (action) {
case '\01': /* error */
tell_user(stderr, "%s\n", act->buf);
errs++;
continue; /* go round again */
case '\02': /* fatal error */
bump("%s", act->buf);
case 'E':
back->send(backhandle, "", 1);
act->action = SCP_SINK_ENDDIR;
return 0;
case 'T':
if (sscanf(act->buf, "%ld %*d %ld %*d",
&act->mtime, &act->atime) == 2) {
act->settime = 1;
back->send(backhandle, "", 1);
continue; /* go round again */
}
bump("Protocol error: Illegal time format");
case 'C':
case 'D':
act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
break;
default:
bump("Protocol error: Expected control record");
}
/*
* We will go round this loop only once, unless we hit
* `continue' above.
*/
done = 1;
}
/*
* If we get here, we must have seen SCP_SINK_FILE or
* SCP_SINK_DIR.
*/
{
char sizestr[40];
if (sscanf(act->buf, "%o %s %n", &act->mode, sizestr, &i) != 2)
bump("Protocol error: Illegal file descriptor format");
act->size = uint64_from_decimal(sizestr);
act->name = act->buf + i;
return 0;
}
}
}
int scp_accept_filexfer(void)
{
if (using_sftp) {
struct sftp_packet *pktin;
struct sftp_request *req, *rreq;
sftp_register(req = fxp_open_send(scp_sftp_currentname, SSH_FXF_READ));
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",
scp_sftp_currentname, fxp_error());
errs++;
return 1;
}
scp_sftp_fileoffset = uint64_make(0, 0);
scp_sftp_xfer = xfer_download_init(scp_sftp_filehandle,
scp_sftp_fileoffset);
sfree(scp_sftp_currentname);
return 0;
} else {
back->send(backhandle, "", 1);
return 0; /* can't fail */
}
}
int scp_recv_filedata(char *data, int len)
{
if (using_sftp) {
struct sftp_packet *pktin;
int ret, actuallen;
void *vbuf;
xfer_download_queue(scp_sftp_xfer);
pktin = sftp_recv();
ret = xfer_download_gotpkt(scp_sftp_xfer, pktin);
if (ret < 0) {
tell_user(stderr, "pscp: error while reading: %s", fxp_error());
errs++;
return -1;
}
if (xfer_download_data(scp_sftp_xfer, &vbuf, &actuallen)) {
/*
* This assertion relies on the fact that the natural
* block size used in the xfer manager is at most that
* used in this module. I don't like crossing layers in
* this way, but it'll do for now.
*/
assert(actuallen <= len);
memcpy(data, vbuf, actuallen);
sfree(vbuf);
} else
actuallen = 0;
scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, actuallen);
return actuallen;
} else {
return ssh_scp_recv((unsigned char *) data, len);
}
}
int scp_finish_filerecv(void)
{
if (using_sftp) {
struct sftp_packet *pktin;
struct sftp_request *req, *rreq;
/*
* Ensure that xfer_done() will work correctly, so we can
* clean up any outstanding requests from the file
* transfer.
*/
xfer_set_error(scp_sftp_xfer);
while (!xfer_done(scp_sftp_xfer)) {
void *vbuf;
int len;
pktin = sftp_recv();
xfer_download_gotpkt(scp_sftp_xfer, pktin);
if (xfer_download_data(scp_sftp_xfer, &vbuf, &len))
sfree(vbuf);
}
xfer_cleanup(scp_sftp_xfer);
sftp_register(req = fxp_close_send(scp_sftp_filehandle));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
fxp_close_recv(pktin, rreq);
return 0;
} else {
back->send(backhandle, "", 1);
return response();
}
}
/* ----------------------------------------------------------------------
* Send an error message to the other side and to the screen.
* Increment error counter.
*/
static void run_err(const char *fmt, ...)
{
char *str, *str2;
va_list ap;
va_start(ap, fmt);
errs++;
str = dupvprintf(fmt, ap);
str2 = dupcat("scp: ", str, "\n", NULL);
sfree(str);
scp_send_errmsg(str2);
tell_user(stderr, "%s", str2);
va_end(ap);
sfree(str2);
}
/*
* Execute the source part of the SCP protocol.
*/
static void source(char *src)
{
uint64 size;
unsigned long mtime, atime;
char *last;
RFile *f;
int attr;
uint64 i;
uint64 stat_bytes;
time_t stat_starttime, stat_lasttime;
attr = file_type(src);
if (attr == FILE_TYPE_NONEXISTENT ||
attr == FILE_TYPE_WEIRD) {
run_err("%s: %s file or directory", src,
(attr == FILE_TYPE_WEIRD ? "Not a" : "No such"));
return;
}
if (attr == FILE_TYPE_DIRECTORY) {
if (recursive) {
/*
* Avoid . and .. directories.
*/
char *p;
p = strrchr(src, '/');
if (!p)
p = strrchr(src, '\\');
if (!p)
p = src;
else
p++;
if (!strcmp(p, ".") || !strcmp(p, ".."))
/* skip . and .. */ ;
else
rsource(src);
} else {
run_err("%s: not a regular file", src);
}
return;
}
if ((last = strrchr(src, '/')) == NULL)
last = src;
else
last++;
if (strrchr(last, '\\') != NULL)
last = strrchr(last, '\\') + 1;
if (last == src && strchr(src, ':') != NULL)
last = strchr(src, ':') + 1;
f = open_existing_file(src, &size, &mtime, &atime);
if (f == NULL) {
run_err("%s: Cannot open file", src);
return;
}
if (preserve) {
if (scp_send_filetimes(mtime, atime))
return;
}
if (verbose) {
char sizestr[40];
uint64_decimal(size, sizestr);
tell_user(stderr, "Sending file %s, size=%s", last, sizestr);
}
if (scp_send_filename(last, size, 0644))
return;
stat_bytes = uint64_make(0,0);
stat_starttime = time(NULL);
stat_lasttime = 0;
for (i = uint64_make(0,0);
uint64_compare(i,size) < 0;
i = uint64_add32(i,4096)) {
char transbuf[4096];
int j, k = 4096;
if (uint64_compare(uint64_add32(i, k),size) > 0) /* i + k > size */
k = (uint64_subtract(size, i)).lo; /* k = size - i; */
if ((j = read_from_file(f, transbuf, k)) != k) {
if (statistics)
printf("\n");
bump("%s: Read error", src);
}
if (scp_send_filedata(transbuf, k))
bump("%s: Network error occurred", src);
if (statistics) {
stat_bytes = uint64_add32(stat_bytes, k);
if (time(NULL) != stat_lasttime ||
(uint64_compare(uint64_add32(i, k), size) == 0)) {
stat_lasttime = time(NULL);
print_stats(last, size, stat_bytes,
stat_starttime, stat_lasttime);
}
}
}
close_rfile(f);
(void) scp_send_finish();
}
/*
* Recursively send the contents of a directory.
*/
static void rsource(char *src)
{
char *last;
char *save_target;
DirHandle *dir;
if ((last = strrchr(src, '/')) == NULL)
last = src;
else
last++;
if (strrchr(last, '\\') != NULL)
last = strrchr(last, '\\') + 1;
if (last == src && strchr(src, ':') != NULL)
last = strchr(src, ':') + 1;
/* maybe send filetime */
save_target = scp_save_remotepath();
if (verbose)
tell_user(stderr, "Entering directory: %s", last);
if (scp_send_dirname(last, 0755))
return;
dir = open_directory(src);
if (dir != NULL) {
char *filename;
while ((filename = read_filename(dir)) != NULL) {
char *foundfile = dupcat(src, "/", filename, NULL);
source(foundfile);
sfree(foundfile);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -