📄 sftp.c
字号:
{
struct sftp_request *req = sftp_alloc_request();
struct sftp_packet *pktout;
pktout = sftp_pkt_init(SSH_FXP_READDIR);
sftp_pkt_adduint32(pktout, req->id);
sftp_pkt_addstring_start(pktout);
sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
sftp_send(pktout);
return req;
}
struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
struct sftp_request *req)
{
sfree(req);
if (pktin->type == SSH_FXP_NAME) {
struct fxp_names *ret;
unsigned long i;
/*
* Sanity-check the number of names. Minimum is obviously
* zero. Maximum is the remaining space in the packet
* divided by the very minimum length of a name, which is
* 12 bytes (4 for an empty filename, 4 for an empty
* longname, 4 for a set of attribute flags indicating that
* no other attributes are supplied).
*/
if (!sftp_pkt_getuint32(pktin, &i) ||
i > (pktin->length-pktin->savedpos)/12) {
fxp_internal_error("malformed FXP_NAME packet");
sftp_pkt_free(pktin);
return NULL;
}
/*
* Ensure the implicit multiplication in the snewn() call
* doesn't suffer integer overflow and cause us to malloc
* too little space.
*/
if (i > INT_MAX / sizeof(struct fxp_name)) {
fxp_internal_error("unreasonably large FXP_NAME packet");
sftp_pkt_free(pktin);
return NULL;
}
ret = snew(struct fxp_names);
ret->nnames = i;
ret->names = snewn(ret->nnames, struct fxp_name);
for (i = 0; i < ret->nnames; i++) {
char *str1, *str2;
int len1, len2;
if (!sftp_pkt_getstring(pktin, &str1, &len1) ||
!sftp_pkt_getstring(pktin, &str2, &len2) ||
!sftp_pkt_getattrs(pktin, &ret->names[i].attrs)) {
fxp_internal_error("malformed FXP_NAME packet");
while (i--) {
sfree(ret->names[i].filename);
sfree(ret->names[i].longname);
}
sfree(ret->names);
sfree(ret);
sfree(pktin);
return NULL;
}
ret->names[i].filename = mkstr(str1, len1);
ret->names[i].longname = mkstr(str2, len2);
}
sftp_pkt_free(pktin);
return ret;
} else {
fxp_got_status(pktin);
sftp_pkt_free(pktin);
return NULL;
}
}
/*
* Write to a file. Returns 0 on error, 1 on OK.
*/
struct sftp_request *fxp_write_send(struct fxp_handle *handle,
char *buffer, uint64 offset, int len)
{
struct sftp_request *req = sftp_alloc_request();
struct sftp_packet *pktout;
pktout = sftp_pkt_init(SSH_FXP_WRITE);
sftp_pkt_adduint32(pktout, req->id);
sftp_pkt_addstring_start(pktout);
sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
sftp_pkt_adduint64(pktout, offset);
sftp_pkt_addstring_start(pktout);
sftp_pkt_addstring_data(pktout, buffer, len);
sftp_send(pktout);
return req;
}
int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
{
sfree(req);
fxp_got_status(pktin);
sftp_pkt_free(pktin);
return fxp_errtype == SSH_FX_OK;
}
/*
* Free up an fxp_names structure.
*/
void fxp_free_names(struct fxp_names *names)
{
int i;
for (i = 0; i < names->nnames; i++) {
sfree(names->names[i].filename);
sfree(names->names[i].longname);
}
sfree(names->names);
sfree(names);
}
/*
* Duplicate an fxp_name structure.
*/
struct fxp_name *fxp_dup_name(struct fxp_name *name)
{
struct fxp_name *ret;
ret = snew(struct fxp_name);
ret->filename = dupstr(name->filename);
ret->longname = dupstr(name->longname);
ret->attrs = name->attrs; /* structure copy */
return ret;
}
/*
* Free up an fxp_name structure.
*/
void fxp_free_name(struct fxp_name *name)
{
sfree(name->filename);
sfree(name->longname);
sfree(name);
}
/*
* Store user data in an sftp_request structure.
*/
void *fxp_get_userdata(struct sftp_request *req)
{
return req->userdata;
}
void fxp_set_userdata(struct sftp_request *req, void *data)
{
req->userdata = data;
}
/*
* A wrapper to go round fxp_read_* and fxp_write_*, which manages
* the queueing of multiple read/write requests.
*/
struct req {
char *buffer;
int len, retlen, complete;
uint64 offset;
struct req *next, *prev;
};
struct fxp_xfer {
uint64 offset, furthestdata, filesize;
int req_totalsize, req_maxsize, eof, err;
struct fxp_handle *fh;
struct req *head, *tail;
};
static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
{
struct fxp_xfer *xfer = snew(struct fxp_xfer);
xfer->fh = fh;
xfer->offset = offset;
xfer->head = xfer->tail = NULL;
xfer->req_totalsize = 0;
xfer->req_maxsize = 16384;
xfer->err = 0;
xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
xfer->furthestdata = uint64_make(0, 0);
return xfer;
}
int xfer_done(struct fxp_xfer *xfer)
{
/*
* We're finished if we've seen EOF _and_ there are no
* outstanding requests.
*/
return (xfer->eof || xfer->err) && !xfer->head;
}
void xfer_download_queue(struct fxp_xfer *xfer)
{
while (xfer->req_totalsize < xfer->req_maxsize &&
!xfer->eof && !xfer->err) {
/*
* Queue a new read request.
*/
struct req *rr;
struct sftp_request *req;
rr = snew(struct req);
rr->offset = xfer->offset;
rr->complete = 0;
if (xfer->tail) {
xfer->tail->next = rr;
rr->prev = xfer->tail;
} else {
xfer->head = rr;
rr->prev = NULL;
}
xfer->tail = rr;
rr->next = NULL;
rr->len = 4096;
rr->buffer = snewn(rr->len, char);
sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
fxp_set_userdata(req, rr);
xfer->offset = uint64_add32(xfer->offset, rr->len);
xfer->req_totalsize += rr->len;
#ifdef DEBUG_DOWNLOAD
{ char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
#endif
}
}
struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
{
struct fxp_xfer *xfer = xfer_init(fh, offset);
xfer->eof = FALSE;
xfer_download_queue(xfer);
return xfer;
}
int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
{
struct sftp_request *rreq;
struct req *rr;
rreq = sftp_find_request(pktin);
rr = (struct req *)fxp_get_userdata(rreq);
if (!rr)
return 0; /* this packet isn't ours */
rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
#ifdef DEBUG_DOWNLOAD
printf("read request %p has returned [%d]\n", rr, rr->retlen);
#endif
if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
xfer->eof = TRUE;
rr->complete = -1;
#ifdef DEBUG_DOWNLOAD
printf("setting eof\n");
#endif
} else if (rr->retlen < 0) {
/* some error other than EOF; signal it back to caller */
xfer_set_error(xfer);
rr->complete = -1;
return -1;
}
rr->complete = 1;
/*
* Special case: if we have received fewer bytes than we
* actually read, we should do something. For the moment I'll
* just throw an ersatz FXP error to signal this; the SFTP
* draft I've got says that it can't happen except on special
* files, in which case seeking probably has very little
* meaning and so queueing an additional read request to fill
* up the gap sounds like the wrong answer. I'm not sure what I
* should be doing here - if it _was_ a special file, I suspect
* I simply shouldn't have been queueing multiple requests in
* the first place...
*/
if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
xfer->furthestdata = rr->offset;
#ifdef DEBUG_DOWNLOAD
{ char buf[40];
uint64_decimal(xfer->furthestdata, buf);
printf("setting furthestdata = %s\n", buf); }
#endif
}
if (rr->retlen < rr->len) {
uint64 filesize = uint64_add32(rr->offset,
(rr->retlen < 0 ? 0 : rr->retlen));
#ifdef DEBUG_DOWNLOAD
{ char buf[40];
uint64_decimal(filesize, buf);
printf("short block! trying filesize = %s\n", buf); }
#endif
if (uint64_compare(xfer->filesize, filesize) > 0) {
xfer->filesize = filesize;
#ifdef DEBUG_DOWNLOAD
printf("actually changing filesize\n");
#endif
}
}
if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
fxp_error_message = "received a short buffer from FXP_READ, but not"
" at EOF";
fxp_errtype = -1;
xfer_set_error(xfer);
return -1;
}
return 1;
}
void xfer_set_error(struct fxp_xfer *xfer)
{
xfer->err = 1;
}
int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
{
void *retbuf = NULL;
int retlen = 0;
/*
* Discard anything at the head of the rr queue with complete <
* 0; return the first thing with complete > 0.
*/
while (xfer->head && xfer->head->complete && !retbuf) {
struct req *rr = xfer->head;
if (rr->complete > 0) {
retbuf = rr->buffer;
retlen = rr->retlen;
#ifdef DEBUG_DOWNLOAD
printf("handing back data from read request %p\n", rr);
#endif
}
#ifdef DEBUG_DOWNLOAD
else
printf("skipping failed read request %p\n", rr);
#endif
xfer->head = xfer->head->next;
if (xfer->head)
xfer->head->prev = NULL;
else
xfer->tail = NULL;
xfer->req_totalsize -= rr->len;
sfree(rr);
}
if (retbuf) {
*buf = retbuf;
*len = retlen;
return 1;
} else
return 0;
}
struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
{
struct fxp_xfer *xfer = xfer_init(fh, offset);
/*
* We set `eof' to 1 because this will cause xfer_done() to
* return true iff there are no outstanding requests. During an
* upload, our caller will be responsible for working out
* whether all the data has been sent, so all it needs to know
* from us is whether the outstanding requests have been
* handled once that's done.
*/
xfer->eof = 1;
return xfer;
}
int xfer_upload_ready(struct fxp_xfer *xfer)
{
if (xfer->req_totalsize < xfer->req_maxsize)
return 1;
else
return 0;
}
void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
{
struct req *rr;
struct sftp_request *req;
rr = snew(struct req);
rr->offset = xfer->offset;
rr->complete = 0;
if (xfer->tail) {
xfer->tail->next = rr;
rr->prev = xfer->tail;
} else {
xfer->head = rr;
rr->prev = NULL;
}
xfer->tail = rr;
rr->next = NULL;
rr->len = len;
rr->buffer = NULL;
sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
fxp_set_userdata(req, rr);
xfer->offset = uint64_add32(xfer->offset, rr->len);
xfer->req_totalsize += rr->len;
#ifdef DEBUG_UPLOAD
{ char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
#endif
}
int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
{
struct sftp_request *rreq;
struct req *rr, *prev, *next;
int ret;
rreq = sftp_find_request(pktin);
rr = (struct req *)fxp_get_userdata(rreq);
if (!rr)
return 0; /* this packet isn't ours */
ret = fxp_write_recv(pktin, rreq);
#ifdef DEBUG_UPLOAD
printf("write request %p has returned [%d]\n", rr, ret);
#endif
/*
* Remove this one from the queue.
*/
prev = rr->prev;
next = rr->next;
if (prev)
prev->next = next;
else
xfer->head = next;
if (next)
next->prev = prev;
else
xfer->tail = prev;
xfer->req_totalsize -= rr->len;
sfree(rr);
if (!ret)
return -1;
return 1;
}
void xfer_cleanup(struct fxp_xfer *xfer)
{
struct req *rr;
while (xfer->head) {
rr = xfer->head;
xfer->head = xfer->head->next;
sfree(rr->buffer);
sfree(rr);
}
sfree(xfer);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -