mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-25 01:02:24 +00:00
Support for recursive file transfer in PSFTP.
[originally from svn r4990] [this svn revision also touched putty-wishlist]
This commit is contained in:
parent
d67bc798aa
commit
bee5812a49
@ -252,6 +252,17 @@ specify the local file name after the remote one:
|
|||||||
This will fetch the file on the server called \c{myfile.dat}, but
|
This will fetch the file on the server called \c{myfile.dat}, but
|
||||||
will save it to your local machine under the name \c{newname.dat}.
|
will save it to your local machine under the name \c{newname.dat}.
|
||||||
|
|
||||||
|
To fetch an entire directory recursively, you can use the \c{-r}
|
||||||
|
option:
|
||||||
|
|
||||||
|
\c get -r mydir
|
||||||
|
\c get -r mydir newname
|
||||||
|
|
||||||
|
(If you want to fetch a file whose name starts with a hyphen, you
|
||||||
|
may have to use the \c{--} special argument, which stops \c{get}
|
||||||
|
from interpreting anything as a switch after it. For example,
|
||||||
|
\cq{get -- -silly-name-}.)
|
||||||
|
|
||||||
\S{psftp-cmd-put} The \c{put} command: send a file to the server
|
\S{psftp-cmd-put} The \c{put} command: send a file to the server
|
||||||
|
|
||||||
To upload a file to the server from your local PC, you use the
|
To upload a file to the server from your local PC, you use the
|
||||||
@ -269,6 +280,17 @@ specify the remote file name after the local one:
|
|||||||
This will send the local file called \c{myfile.dat}, but will store
|
This will send the local file called \c{myfile.dat}, but will store
|
||||||
it on the server under the name \c{newname.dat}.
|
it on the server under the name \c{newname.dat}.
|
||||||
|
|
||||||
|
To send an entire directory recursively, you can use the \c{-r}
|
||||||
|
option:
|
||||||
|
|
||||||
|
\c put -r mydir
|
||||||
|
\c put -r mydir newname
|
||||||
|
|
||||||
|
(If you want to send a file whose name starts with a hyphen, you may
|
||||||
|
have to use the \c{--} special argument, which stops \c{put} from
|
||||||
|
interpreting anything as a switch after it. For example, \cq{put --
|
||||||
|
-silly-name-}.)
|
||||||
|
|
||||||
\S{psftp-cmd-regetput} The \c{reget} and \c{reput} commands:
|
\S{psftp-cmd-regetput} The \c{reget} and \c{reput} commands:
|
||||||
resuming file transfers
|
resuming file transfers
|
||||||
|
|
||||||
|
782
psftp.c
782
psftp.c
@ -40,6 +40,14 @@ static Config cfg;
|
|||||||
* Higher-level helper functions used in commands.
|
* Higher-level helper functions used in commands.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine whether a string is entirely composed of dots.
|
||||||
|
*/
|
||||||
|
static int is_dots(char *str)
|
||||||
|
{
|
||||||
|
return str[strspn(str, ".")] == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attempt to canonify a pathname starting from the pwd. If
|
* Attempt to canonify a pathname starting from the pwd. If
|
||||||
* canonification fails, at least fall back to returning a _valid_
|
* canonification fails, at least fall back to returning a _valid_
|
||||||
@ -168,6 +176,513 @@ static char *stripslashes(char *str, int local)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* qsort comparison routine for fxp_name structures. Sorts by real
|
||||||
|
* file name.
|
||||||
|
*/
|
||||||
|
static int sftp_name_compare(const void *av, const void *bv)
|
||||||
|
{
|
||||||
|
const struct fxp_name *const *a = (const struct fxp_name *const *) av;
|
||||||
|
const struct fxp_name *const *b = (const struct fxp_name *const *) bv;
|
||||||
|
return strcmp((*a)->filename, (*b)->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Likewise, but for a bare char *.
|
||||||
|
*/
|
||||||
|
static int bare_name_compare(const void *av, const void *bv)
|
||||||
|
{
|
||||||
|
const char **a = (const char **) av;
|
||||||
|
const char **b = (const char **) bv;
|
||||||
|
return strcmp(*a, *b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------
|
||||||
|
* The meat of the `get' and `put' commands.
|
||||||
|
*/
|
||||||
|
int sftp_get_file(char *fname, char *outfname, int recurse, int restart)
|
||||||
|
{
|
||||||
|
struct fxp_handle *fh;
|
||||||
|
struct sftp_packet *pktin;
|
||||||
|
struct sftp_request *req, *rreq;
|
||||||
|
struct fxp_xfer *xfer;
|
||||||
|
uint64 offset;
|
||||||
|
FILE *fp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In recursive mode, see if we're dealing with a directory.
|
||||||
|
* (If we're not in recursive mode, we need not even check: the
|
||||||
|
* subsequent FXP_OPEN will return a usable error message.)
|
||||||
|
*/
|
||||||
|
if (recurse) {
|
||||||
|
struct fxp_attrs attrs;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
sftp_register(req = fxp_stat_send(fname));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||||
|
if (result &&
|
||||||
|
(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
|
||||||
|
(attrs.permissions & 0040000)) {
|
||||||
|
|
||||||
|
struct fxp_handle *dirhandle;
|
||||||
|
int nnames, namesize;
|
||||||
|
struct fxp_name **ournames;
|
||||||
|
struct fxp_names *names;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, attempt to create the destination directory,
|
||||||
|
* unless it already exists.
|
||||||
|
*/
|
||||||
|
if (file_type(outfname) != FILE_TYPE_DIRECTORY &&
|
||||||
|
!create_directory(outfname)) {
|
||||||
|
printf("%s: Cannot create directory\n", outfname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now get the list of filenames in the remote
|
||||||
|
* directory.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
printf("%s: unable to open directory: %s\n",
|
||||||
|
fname, fxp_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
printf("%s: reading directory: %s\n", fname, fxp_error());
|
||||||
|
sfree(ournames);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
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 (!is_dots(names->names[i].filename))
|
||||||
|
ournames[nnames++] = fxp_dup_name(&names->names[i]);
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sort the names into a clear order. This ought to
|
||||||
|
* make things more predictable when we're doing a
|
||||||
|
* reget of the same directory, just in case two
|
||||||
|
* readdirs on the same remote directory return a
|
||||||
|
* different order.
|
||||||
|
*/
|
||||||
|
qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're in restart mode, find the last filename on
|
||||||
|
* this list that already exists. We may have to do a
|
||||||
|
* reget on _that_ file, but shouldn't have to do
|
||||||
|
* anything on the previous files.
|
||||||
|
*
|
||||||
|
* If none of them exists, of course, we start at 0.
|
||||||
|
*/
|
||||||
|
i = 0;
|
||||||
|
while (i < nnames) {
|
||||||
|
char *nextoutfname;
|
||||||
|
int ret;
|
||||||
|
nextoutfname = dir_file_cat(outfname, ournames[i]->filename);
|
||||||
|
ret = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT);
|
||||||
|
sfree(nextoutfname);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i > 0)
|
||||||
|
i--;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we're ready to recurse. Starting at ournames[i]
|
||||||
|
* and continuing on to the end of the list, we
|
||||||
|
* construct a new source and target file name, and
|
||||||
|
* call sftp_get_file again.
|
||||||
|
*/
|
||||||
|
for (; i < nnames; i++) {
|
||||||
|
char *nextfname, *nextoutfname;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
nextfname = dupcat(fname, "/", ournames[i]->filename, NULL);
|
||||||
|
nextoutfname = dir_file_cat(outfname, ournames[i]->filename);
|
||||||
|
ret = sftp_get_file(nextfname, nextoutfname, recurse, restart);
|
||||||
|
restart = FALSE; /* after first partial file, do full */
|
||||||
|
sfree(nextoutfname);
|
||||||
|
sfree(nextfname);
|
||||||
|
if (!ret) {
|
||||||
|
for (i = 0; i < nnames; i++) {
|
||||||
|
fxp_free_name(ournames[i]);
|
||||||
|
}
|
||||||
|
sfree(ournames);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Done this recursion level. Free everything.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < nnames; i++) {
|
||||||
|
fxp_free_name(ournames[i]);
|
||||||
|
}
|
||||||
|
sfree(ournames);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp_register(req = fxp_open_send(fname, SSH_FXF_READ));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
fh = fxp_open_recv(pktin, rreq);
|
||||||
|
|
||||||
|
if (!fh) {
|
||||||
|
printf("%s: %s\n", fname, fxp_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restart) {
|
||||||
|
fp = fopen(outfname, "rb+");
|
||||||
|
} else {
|
||||||
|
fp = fopen(outfname, "wb");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fp) {
|
||||||
|
printf("local: unable to open %s\n", outfname);
|
||||||
|
|
||||||
|
sftp_register(req = fxp_close_send(fh));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
fxp_close_recv(pktin, rreq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restart) {
|
||||||
|
long posn;
|
||||||
|
fseek(fp, 0L, SEEK_END);
|
||||||
|
posn = ftell(fp);
|
||||||
|
printf("reget: restarting at file position %ld\n", posn);
|
||||||
|
offset = uint64_make(0, posn);
|
||||||
|
} else {
|
||||||
|
offset = uint64_make(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("remote:%s => local:%s\n", fname, outfname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: we can use FXP_FSTAT here to get the file size, and
|
||||||
|
* thus put up a progress bar.
|
||||||
|
*/
|
||||||
|
ret = 1;
|
||||||
|
xfer = xfer_download_init(fh, offset);
|
||||||
|
while (!xfer_done(xfer)) {
|
||||||
|
void *vbuf;
|
||||||
|
int ret, len;
|
||||||
|
int wpos, wlen;
|
||||||
|
|
||||||
|
xfer_download_queue(xfer);
|
||||||
|
pktin = sftp_recv();
|
||||||
|
ret = xfer_download_gotpkt(xfer, pktin);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("error while reading: %s\n", fxp_error());
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (xfer_download_data(xfer, &vbuf, &len)) {
|
||||||
|
unsigned char *buf = (unsigned char *)vbuf;
|
||||||
|
|
||||||
|
wpos = 0;
|
||||||
|
while (wpos < len) {
|
||||||
|
wlen = fwrite(buf + wpos, 1, len - wpos, fp);
|
||||||
|
if (wlen <= 0) {
|
||||||
|
printf("error while writing local file\n");
|
||||||
|
ret = 0;
|
||||||
|
xfer_set_error(xfer);
|
||||||
|
}
|
||||||
|
wpos += wlen;
|
||||||
|
}
|
||||||
|
if (wpos < len) { /* we had an error */
|
||||||
|
ret = 0;
|
||||||
|
xfer_set_error(xfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
sfree(vbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xfer_cleanup(xfer);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
sftp_register(req = fxp_close_send(fh));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
fxp_close_recv(pktin, rreq);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sftp_put_file(char *fname, char *outfname, int recurse, int restart)
|
||||||
|
{
|
||||||
|
struct fxp_handle *fh;
|
||||||
|
struct fxp_xfer *xfer;
|
||||||
|
struct sftp_packet *pktin;
|
||||||
|
struct sftp_request *req, *rreq;
|
||||||
|
uint64 offset;
|
||||||
|
FILE *fp;
|
||||||
|
int ret, err, eof;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In recursive mode, see if we're dealing with a directory.
|
||||||
|
* (If we're not in recursive mode, we need not even check: the
|
||||||
|
* subsequent fopen will return an error message.)
|
||||||
|
*/
|
||||||
|
if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {
|
||||||
|
struct fxp_attrs attrs;
|
||||||
|
int result;
|
||||||
|
int nnames, namesize;
|
||||||
|
char *name, **ournames;
|
||||||
|
DirHandle *dh;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, attempt to create the destination directory,
|
||||||
|
* unless it already exists.
|
||||||
|
*/
|
||||||
|
sftp_register(req = fxp_stat_send(outfname));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||||
|
if (!result ||
|
||||||
|
!(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
|
||||||
|
!(attrs.permissions & 0040000)) {
|
||||||
|
sftp_register(req = fxp_mkdir_send(outfname));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
result = fxp_mkdir_recv(pktin, rreq);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
printf("%s: create directory: %s\n", outfname, fxp_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now get the list of filenames in the local directory.
|
||||||
|
*/
|
||||||
|
dh = open_directory(fname);
|
||||||
|
if (!dh) {
|
||||||
|
printf("%s: unable to open directory\n", fname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nnames = namesize = 0;
|
||||||
|
ournames = NULL;
|
||||||
|
while ((name = read_filename(dh)) != NULL) {
|
||||||
|
if (nnames >= namesize) {
|
||||||
|
namesize += 128;
|
||||||
|
ournames = sresize(ournames, namesize, char *);
|
||||||
|
}
|
||||||
|
ournames[nnames++] = name;
|
||||||
|
}
|
||||||
|
close_directory(dh);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sort the names into a clear order. This ought to make
|
||||||
|
* things more predictable when we're doing a reput of the
|
||||||
|
* same directory, just in case two readdirs on the same
|
||||||
|
* local directory return a different order.
|
||||||
|
*/
|
||||||
|
qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're in restart mode, find the last filename on this
|
||||||
|
* list that already exists. We may have to do a reput on
|
||||||
|
* _that_ file, but shouldn't have to do anything on the
|
||||||
|
* previous files.
|
||||||
|
*
|
||||||
|
* If none of them exists, of course, we start at 0.
|
||||||
|
*/
|
||||||
|
i = 0;
|
||||||
|
while (i < nnames) {
|
||||||
|
char *nextoutfname;
|
||||||
|
nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
|
||||||
|
sftp_register(req = fxp_stat_send(nextoutfname));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
result = fxp_stat_recv(pktin, rreq, &attrs);
|
||||||
|
sfree(nextoutfname);
|
||||||
|
if (!result)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i > 0)
|
||||||
|
i--;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we're ready to recurse. Starting at ournames[i]
|
||||||
|
* and continuing on to the end of the list, we
|
||||||
|
* construct a new source and target file name, and
|
||||||
|
* call sftp_put_file again.
|
||||||
|
*/
|
||||||
|
for (; i < nnames; i++) {
|
||||||
|
char *nextfname, *nextoutfname;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
nextfname = dir_file_cat(fname, ournames[i]);
|
||||||
|
nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
|
||||||
|
ret = sftp_put_file(nextfname, nextoutfname, recurse, restart);
|
||||||
|
restart = FALSE; /* after first partial file, do full */
|
||||||
|
sfree(nextoutfname);
|
||||||
|
sfree(nextfname);
|
||||||
|
if (!ret) {
|
||||||
|
for (i = 0; i < nnames; i++) {
|
||||||
|
sfree(ournames[i]);
|
||||||
|
}
|
||||||
|
sfree(ournames);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Done this recursion level. Free everything.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < nnames; i++) {
|
||||||
|
sfree(ournames[i]);
|
||||||
|
}
|
||||||
|
sfree(ournames);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(fname, "rb");
|
||||||
|
if (!fp) {
|
||||||
|
printf("local: unable to open %s\n", fname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (restart) {
|
||||||
|
sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE));
|
||||||
|
} else {
|
||||||
|
sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE |
|
||||||
|
SSH_FXF_CREAT | SSH_FXF_TRUNC));
|
||||||
|
}
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
fh = fxp_open_recv(pktin, rreq);
|
||||||
|
|
||||||
|
if (!fh) {
|
||||||
|
printf("%s: %s\n", outfname, fxp_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restart) {
|
||||||
|
char decbuf[30];
|
||||||
|
struct fxp_attrs attrs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sftp_register(req = fxp_fstat_send(fh));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
ret = fxp_fstat_recv(pktin, rreq, &attrs);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
printf("read size of %s: %s\n", outfname, fxp_error());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
|
||||||
|
printf("read size of %s: size was not given\n", outfname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
offset = attrs.size;
|
||||||
|
uint64_decimal(offset, decbuf);
|
||||||
|
printf("reput: restarting at file position %s\n", decbuf);
|
||||||
|
if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) {
|
||||||
|
printf("reput: remote file is larger than we can deal with\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (fseek(fp, offset.lo, SEEK_SET) != 0)
|
||||||
|
fseek(fp, 0, SEEK_END); /* *shrug* */
|
||||||
|
} else {
|
||||||
|
offset = uint64_make(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("local:%s => remote:%s\n", fname, outfname);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: we can use FXP_FSTAT here to get the file size, and
|
||||||
|
* thus put up a progress bar.
|
||||||
|
*/
|
||||||
|
ret = 1;
|
||||||
|
xfer = xfer_upload_init(fh, offset);
|
||||||
|
err = eof = 0;
|
||||||
|
while ((!err && !eof) || !xfer_done(xfer)) {
|
||||||
|
char buffer[4096];
|
||||||
|
int len, ret;
|
||||||
|
|
||||||
|
while (xfer_upload_ready(xfer) && !err && !eof) {
|
||||||
|
len = fread(buffer, 1, sizeof(buffer), fp);
|
||||||
|
if (len == -1) {
|
||||||
|
printf("error while reading local file\n");
|
||||||
|
err = 1;
|
||||||
|
} else if (len == 0) {
|
||||||
|
eof = 1;
|
||||||
|
} else {
|
||||||
|
xfer_upload_data(xfer, buffer, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xfer_done(xfer)) {
|
||||||
|
pktin = sftp_recv();
|
||||||
|
ret = xfer_upload_gotpkt(xfer, pktin);
|
||||||
|
if (!ret) {
|
||||||
|
printf("error while writing: %s\n", fxp_error());
|
||||||
|
err = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xfer_cleanup(xfer);
|
||||||
|
|
||||||
|
sftp_register(req = fxp_close_send(fh));
|
||||||
|
rreq = sftp_find_request(pktin = sftp_recv());
|
||||||
|
assert(rreq == req);
|
||||||
|
fxp_close_recv(pktin, rreq);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
* Actual sftp commands.
|
* Actual sftp commands.
|
||||||
*/
|
*/
|
||||||
@ -197,12 +712,6 @@ int sftp_cmd_quit(struct sftp_command *cmd)
|
|||||||
* List a directory. If no arguments are given, list pwd; otherwise
|
* List a directory. If no arguments are given, list pwd; otherwise
|
||||||
* list the directory given in words[1].
|
* list the directory given in words[1].
|
||||||
*/
|
*/
|
||||||
static int sftp_ls_compare(const void *av, const void *bv)
|
|
||||||
{
|
|
||||||
const struct fxp_name *const *a = (const struct fxp_name *const *) av;
|
|
||||||
const struct fxp_name *const *b = (const struct fxp_name *const *) bv;
|
|
||||||
return strcmp((*a)->filename, (*b)->filename);
|
|
||||||
}
|
|
||||||
int sftp_cmd_ls(struct sftp_command *cmd)
|
int sftp_cmd_ls(struct sftp_command *cmd)
|
||||||
{
|
{
|
||||||
struct fxp_handle *dirh;
|
struct fxp_handle *dirh;
|
||||||
@ -280,7 +789,7 @@ int sftp_cmd_ls(struct sftp_command *cmd)
|
|||||||
* Now we have our filenames. Sort them by actual file
|
* Now we have our filenames. Sort them by actual file
|
||||||
* name, and then output the longname parts.
|
* name, and then output the longname parts.
|
||||||
*/
|
*/
|
||||||
qsort(ournames, nnames, sizeof(*ournames), sftp_ls_compare);
|
qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And print them.
|
* And print them.
|
||||||
@ -368,124 +877,46 @@ int sftp_cmd_pwd(struct sftp_command *cmd)
|
|||||||
*/
|
*/
|
||||||
int sftp_general_get(struct sftp_command *cmd, int restart)
|
int sftp_general_get(struct sftp_command *cmd, int restart)
|
||||||
{
|
{
|
||||||
struct fxp_handle *fh;
|
char *fname, *origfname, *outfname;
|
||||||
struct sftp_packet *pktin;
|
int i, ret;
|
||||||
struct sftp_request *req, *rreq;
|
int recurse = FALSE;
|
||||||
struct fxp_xfer *xfer;
|
|
||||||
char *fname, *outfname;
|
|
||||||
uint64 offset;
|
|
||||||
FILE *fp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (back == NULL) {
|
if (back == NULL) {
|
||||||
printf("psftp: not connected to a host; use \"open host.name\"\n");
|
printf("psftp: not connected to a host; use \"open host.name\"\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->nwords < 2) {
|
i = 1;
|
||||||
|
while (i < cmd->nwords && cmd->words[i][0] == '-') {
|
||||||
|
if (!strcmp(cmd->words[i], "--")) {
|
||||||
|
/* finish processing options */
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
} else if (!strcmp(cmd->words[i], "-r")) {
|
||||||
|
recurse = TRUE;
|
||||||
|
} else {
|
||||||
|
printf("get: unrecognised option '%s'\n", cmd->words[i]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= cmd->nwords) {
|
||||||
printf("get: expects a filename\n");
|
printf("get: expects a filename\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fname = canonify(cmd->words[1]);
|
origfname = cmd->words[i++];
|
||||||
|
fname = canonify(origfname);
|
||||||
if (!fname) {
|
if (!fname) {
|
||||||
printf("%s: %s\n", cmd->words[1], fxp_error());
|
printf("%s: %s\n", origfname, fxp_error());
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
outfname = (cmd->nwords == 2 ?
|
|
||||||
stripslashes(cmd->words[1], 0) : cmd->words[2]);
|
|
||||||
|
|
||||||
sftp_register(req = fxp_open_send(fname, SSH_FXF_READ));
|
|
||||||
rreq = sftp_find_request(pktin = sftp_recv());
|
|
||||||
assert(rreq == req);
|
|
||||||
fh = fxp_open_recv(pktin, rreq);
|
|
||||||
|
|
||||||
if (!fh) {
|
|
||||||
printf("%s: %s\n", fname, fxp_error());
|
|
||||||
sfree(fname);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restart) {
|
outfname = (i >= cmd->nwords ?
|
||||||
fp = fopen(outfname, "rb+");
|
stripslashes(origfname, 0) : cmd->words[i++]);
|
||||||
} else {
|
|
||||||
fp = fopen(outfname, "wb");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fp) {
|
ret = sftp_get_file(fname, outfname, recurse, restart);
|
||||||
printf("local: unable to open %s\n", outfname);
|
|
||||||
|
|
||||||
sftp_register(req = fxp_close_send(fh));
|
|
||||||
rreq = sftp_find_request(pktin = sftp_recv());
|
|
||||||
assert(rreq == req);
|
|
||||||
fxp_close_recv(pktin, rreq);
|
|
||||||
|
|
||||||
sfree(fname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restart) {
|
|
||||||
long posn;
|
|
||||||
fseek(fp, 0L, SEEK_END);
|
|
||||||
posn = ftell(fp);
|
|
||||||
printf("reget: restarting at file position %ld\n", posn);
|
|
||||||
offset = uint64_make(0, posn);
|
|
||||||
} else {
|
|
||||||
offset = uint64_make(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("remote:%s => local:%s\n", fname, outfname);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: we can use FXP_FSTAT here to get the file size, and
|
|
||||||
* thus put up a progress bar.
|
|
||||||
*/
|
|
||||||
ret = 1;
|
|
||||||
xfer = xfer_download_init(fh, offset);
|
|
||||||
while (!xfer_done(xfer)) {
|
|
||||||
void *vbuf;
|
|
||||||
int ret, len;
|
|
||||||
int wpos, wlen;
|
|
||||||
|
|
||||||
xfer_download_queue(xfer);
|
|
||||||
pktin = sftp_recv();
|
|
||||||
ret = xfer_download_gotpkt(xfer, pktin);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
printf("error while reading: %s\n", fxp_error());
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (xfer_download_data(xfer, &vbuf, &len)) {
|
|
||||||
unsigned char *buf = (unsigned char *)vbuf;
|
|
||||||
|
|
||||||
wpos = 0;
|
|
||||||
while (wpos < len) {
|
|
||||||
wlen = fwrite(buf + wpos, 1, len - wpos, fp);
|
|
||||||
if (wlen <= 0) {
|
|
||||||
printf("error while writing local file\n");
|
|
||||||
ret = 0;
|
|
||||||
xfer_set_error(xfer);
|
|
||||||
}
|
|
||||||
wpos += wlen;
|
|
||||||
}
|
|
||||||
if (wpos < len) { /* we had an error */
|
|
||||||
ret = 0;
|
|
||||||
xfer_set_error(xfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
sfree(vbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xfer_cleanup(xfer);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
sftp_register(req = fxp_close_send(fh));
|
|
||||||
rreq = sftp_find_request(pktin = sftp_recv());
|
|
||||||
assert(rreq == req);
|
|
||||||
fxp_close_recv(pktin, rreq);
|
|
||||||
|
|
||||||
sfree(fname);
|
sfree(fname);
|
||||||
|
|
||||||
@ -508,133 +939,46 @@ int sftp_cmd_reget(struct sftp_command *cmd)
|
|||||||
*/
|
*/
|
||||||
int sftp_general_put(struct sftp_command *cmd, int restart)
|
int sftp_general_put(struct sftp_command *cmd, int restart)
|
||||||
{
|
{
|
||||||
struct fxp_handle *fh;
|
|
||||||
struct fxp_xfer *xfer;
|
|
||||||
char *fname, *origoutfname, *outfname;
|
char *fname, *origoutfname, *outfname;
|
||||||
struct sftp_packet *pktin;
|
int i, ret;
|
||||||
struct sftp_request *req, *rreq;
|
int recurse = FALSE;
|
||||||
uint64 offset;
|
|
||||||
FILE *fp;
|
|
||||||
int ret, err, eof;
|
|
||||||
|
|
||||||
if (back == NULL) {
|
if (back == NULL) {
|
||||||
printf("psftp: not connected to a host; use \"open host.name\"\n");
|
printf("psftp: not connected to a host; use \"open host.name\"\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->nwords < 2) {
|
i = 1;
|
||||||
|
while (i < cmd->nwords && cmd->words[i][0] == '-') {
|
||||||
|
if (!strcmp(cmd->words[i], "--")) {
|
||||||
|
/* finish processing options */
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
} else if (!strcmp(cmd->words[i], "-r")) {
|
||||||
|
recurse = TRUE;
|
||||||
|
} else {
|
||||||
|
printf("put: unrecognised option '%s'\n", cmd->words[i]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= cmd->nwords) {
|
||||||
printf("put: expects a filename\n");
|
printf("put: expects a filename\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fname = cmd->words[1];
|
fname = cmd->words[i++];
|
||||||
origoutfname = (cmd->nwords == 2 ?
|
origoutfname = (i >= cmd->nwords ?
|
||||||
stripslashes(cmd->words[1], 1) : cmd->words[2]);
|
stripslashes(fname, 1) : cmd->words[i++]);
|
||||||
outfname = canonify(origoutfname);
|
outfname = canonify(origoutfname);
|
||||||
if (!outfname) {
|
if (!outfname) {
|
||||||
printf("%s: %s\n", origoutfname, fxp_error());
|
printf("%s: %s\n", origoutfname, fxp_error());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = fopen(fname, "rb");
|
ret = sftp_put_file(fname, outfname, recurse, restart);
|
||||||
if (!fp) {
|
|
||||||
printf("local: unable to open %s\n", fname);
|
|
||||||
sfree(outfname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (restart) {
|
|
||||||
sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE));
|
|
||||||
} else {
|
|
||||||
sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE |
|
|
||||||
SSH_FXF_CREAT | SSH_FXF_TRUNC));
|
|
||||||
}
|
|
||||||
rreq = sftp_find_request(pktin = sftp_recv());
|
|
||||||
assert(rreq == req);
|
|
||||||
fh = fxp_open_recv(pktin, rreq);
|
|
||||||
|
|
||||||
if (!fh) {
|
|
||||||
printf("%s: %s\n", outfname, fxp_error());
|
|
||||||
sfree(outfname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restart) {
|
|
||||||
char decbuf[30];
|
|
||||||
struct fxp_attrs attrs;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
sftp_register(req = fxp_fstat_send(fh));
|
|
||||||
rreq = sftp_find_request(pktin = sftp_recv());
|
|
||||||
assert(rreq == req);
|
|
||||||
ret = fxp_fstat_recv(pktin, rreq, &attrs);
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
printf("read size of %s: %s\n", outfname, fxp_error());
|
|
||||||
sfree(outfname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
|
|
||||||
printf("read size of %s: size was not given\n", outfname);
|
|
||||||
sfree(outfname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
offset = attrs.size;
|
|
||||||
uint64_decimal(offset, decbuf);
|
|
||||||
printf("reput: restarting at file position %s\n", decbuf);
|
|
||||||
if (uint64_compare(offset, uint64_make(0, LONG_MAX)) > 0) {
|
|
||||||
printf("reput: remote file is larger than we can deal with\n");
|
|
||||||
sfree(outfname);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (fseek(fp, offset.lo, SEEK_SET) != 0)
|
|
||||||
fseek(fp, 0, SEEK_END); /* *shrug* */
|
|
||||||
} else {
|
|
||||||
offset = uint64_make(0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("local:%s => remote:%s\n", fname, outfname);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: we can use FXP_FSTAT here to get the file size, and
|
|
||||||
* thus put up a progress bar.
|
|
||||||
*/
|
|
||||||
ret = 1;
|
|
||||||
xfer = xfer_upload_init(fh, offset);
|
|
||||||
err = eof = 0;
|
|
||||||
while ((!err && !eof) || !xfer_done(xfer)) {
|
|
||||||
char buffer[4096];
|
|
||||||
int len, ret;
|
|
||||||
|
|
||||||
while (xfer_upload_ready(xfer) && !err && !eof) {
|
|
||||||
len = fread(buffer, 1, sizeof(buffer), fp);
|
|
||||||
if (len == -1) {
|
|
||||||
printf("error while reading local file\n");
|
|
||||||
err = 1;
|
|
||||||
} else if (len == 0) {
|
|
||||||
eof = 1;
|
|
||||||
} else {
|
|
||||||
xfer_upload_data(xfer, buffer, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!xfer_done(xfer)) {
|
|
||||||
pktin = sftp_recv();
|
|
||||||
ret = xfer_upload_gotpkt(xfer, pktin);
|
|
||||||
if (!ret) {
|
|
||||||
printf("error while writing: %s\n", fxp_error());
|
|
||||||
err = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xfer_cleanup(xfer);
|
|
||||||
|
|
||||||
sftp_register(req = fxp_close_send(fh));
|
|
||||||
rreq = sftp_find_request(pktin = sftp_recv());
|
|
||||||
assert(rreq == req);
|
|
||||||
fxp_close_recv(pktin, rreq);
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
sfree(outfname);
|
sfree(outfname);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user