X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/32874aeac8dacbca26663777b39a79efc5d8dc4b..41d3adbbfed77605e4a397ce199c5751c35bf94b:/sftp.c diff --git a/sftp.c b/sftp.c index ff9e0431..9ffd19c9 100644 --- a/sftp.c +++ b/sftp.c @@ -7,13 +7,10 @@ #include #include +#include "misc.h" #include "int64.h" #include "sftp.h" -#define smalloc malloc -#define srealloc realloc -#define sfree free - #define GET_32BIT(cp) \ (((unsigned long)(unsigned char)(cp)[0] << 24) | \ ((unsigned long)(unsigned char)(cp)[1] << 16) | \ @@ -33,6 +30,9 @@ struct sftp_packet { int type; }; +static const char *fxp_error_message; +static int fxp_errtype; + /* ---------------------------------------------------------------------- * SFTP packet construction functions. */ @@ -103,6 +103,31 @@ static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data) sftp_pkt_addstring_start(pkt); sftp_pkt_addstring_str(pkt, data); } +static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs) +{ + sftp_pkt_adduint32(pkt, attrs.flags); + if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { + sftp_pkt_adduint32(pkt, attrs.size.hi); + sftp_pkt_adduint32(pkt, attrs.size.lo); + } + if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) { + sftp_pkt_adduint32(pkt, attrs.uid); + sftp_pkt_adduint32(pkt, attrs.gid); + } + if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) { + sftp_pkt_adduint32(pkt, attrs.permissions); + } + if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) { + sftp_pkt_adduint32(pkt, attrs.atime); + sftp_pkt_adduint32(pkt, attrs.mtime); + } + if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) { + /* + * We currently don't support sending any extended + * attributes. + */ + } +} /* ---------------------------------------------------------------------- * SFTP packet decode functions. @@ -234,9 +259,6 @@ static char *mkstr(char *s, int len) * SFTP primitives. */ -static const char *fxp_error_message; -static int fxp_errtype; - /* * Deal with (and free) an FXP_STATUS packet. Return 1 if * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error). @@ -314,12 +336,14 @@ int fxp_init(void) } if (pktin->type != SSH_FXP_VERSION) { fxp_internal_error("did not receive FXP_VERSION"); + sftp_pkt_free(pktin); return 0; } remotever = sftp_pkt_getuint32(pktin); if (remotever > SFTP_PROTO_VERSION) { fxp_internal_error ("remote protocol is more advanced than we support"); + sftp_pkt_free(pktin); return 0; } /* @@ -347,9 +371,14 @@ char *fxp_realpath(char *path) sftp_pkt_addstring_str(pktout, path); sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return NULL; + } id = sftp_pkt_getuint32(pktin); if (id != 0x123) { fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); return NULL; } if (pktin->type == SSH_FXP_NAME) { @@ -360,11 +389,13 @@ char *fxp_realpath(char *path) count = sftp_pkt_getuint32(pktin); if (count != 1) { fxp_internal_error("REALPATH returned name count != 1\n"); + sftp_pkt_free(pktin); return NULL; } sftp_pkt_getstring(pktin, &path, &len); if (!path) { fxp_internal_error("REALPATH returned malformed FXP_NAME\n"); + sftp_pkt_free(pktin); return NULL; } path = mkstr(path, len); @@ -372,6 +403,7 @@ char *fxp_realpath(char *path) return path; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return NULL; } } @@ -391,9 +423,14 @@ struct fxp_handle *fxp_open(char *path, int type) sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */ sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return NULL; + } id = sftp_pkt_getuint32(pktin); if (id != 0x567) { fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); return NULL; } if (pktin->type == SSH_FXP_HANDLE) { @@ -404,6 +441,7 @@ struct fxp_handle *fxp_open(char *path, int type) sftp_pkt_getstring(pktin, &hstring, &len); if (!hstring) { fxp_internal_error("OPEN returned malformed FXP_HANDLE\n"); + sftp_pkt_free(pktin); return NULL; } handle = smalloc(sizeof(struct fxp_handle)); @@ -413,6 +451,7 @@ struct fxp_handle *fxp_open(char *path, int type) return handle; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return NULL; } } @@ -430,9 +469,14 @@ struct fxp_handle *fxp_opendir(char *path) sftp_pkt_addstring(pktout, path); sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return NULL; + } id = sftp_pkt_getuint32(pktin); if (id != 0x456) { fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); return NULL; } if (pktin->type == SSH_FXP_HANDLE) { @@ -443,6 +487,7 @@ struct fxp_handle *fxp_opendir(char *path) sftp_pkt_getstring(pktin, &hstring, &len); if (!hstring) { fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n"); + sftp_pkt_free(pktin); return NULL; } handle = smalloc(sizeof(struct fxp_handle)); @@ -452,6 +497,7 @@ struct fxp_handle *fxp_opendir(char *path) return handle; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return NULL; } } @@ -470,16 +516,266 @@ void fxp_close(struct fxp_handle *handle) sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return; + } id = sftp_pkt_getuint32(pktin); if (id != 0x789) { fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); return; } fxp_got_status(pktin); + sftp_pkt_free(pktin); sfree(handle->hstring); sfree(handle); } +int fxp_mkdir(char *path) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_MKDIR); + sftp_pkt_adduint32(pktout, 0x234); /* request id */ + sftp_pkt_addstring(pktout, path); + sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */ + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x234) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + id = fxp_got_status(pktin); + sftp_pkt_free(pktin); + if (id != 1) { + return 0; + } + return 1; +} + +int fxp_rmdir(char *path) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_RMDIR); + sftp_pkt_adduint32(pktout, 0x345); /* request id */ + sftp_pkt_addstring(pktout, path); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x345) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + id = fxp_got_status(pktin); + sftp_pkt_free(pktin); + if (id != 1) { + return 0; + } + return 1; +} + +int fxp_remove(char *fname) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_REMOVE); + sftp_pkt_adduint32(pktout, 0x678); /* request id */ + sftp_pkt_addstring(pktout, fname); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x678) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + id = fxp_got_status(pktin); + sftp_pkt_free(pktin); + if (id != 1) { + return 0; + } + return 1; +} + +int fxp_rename(char *srcfname, char *dstfname) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_RENAME); + sftp_pkt_adduint32(pktout, 0x678); /* request id */ + sftp_pkt_addstring(pktout, srcfname); + sftp_pkt_addstring(pktout, dstfname); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x678) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + id = fxp_got_status(pktin); + sftp_pkt_free(pktin); + if (id != 1) { + return 0; + } + return 1; +} + +/* + * Retrieve the attributes of a file. We have fxp_stat which works + * on filenames, and fxp_fstat which works on open file handles. + */ +int fxp_stat(char *fname, struct fxp_attrs *attrs) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_STAT); + sftp_pkt_adduint32(pktout, 0x678); /* request id */ + sftp_pkt_addstring(pktout, fname); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x678) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + + if (pktin->type == SSH_FXP_ATTRS) { + *attrs = sftp_pkt_getattrs(pktin); + sftp_pkt_free(pktin); + return 1; + } else { + fxp_got_status(pktin); + sftp_pkt_free(pktin); + return 0; + } +} + +int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_FSTAT); + sftp_pkt_adduint32(pktout, 0x678); /* request id */ + sftp_pkt_addstring_start(pktout); + sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x678) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + + if (pktin->type == SSH_FXP_ATTRS) { + *attrs = sftp_pkt_getattrs(pktin); + sftp_pkt_free(pktin); + return 1; + } else { + fxp_got_status(pktin); + sftp_pkt_free(pktin); + return 0; + } +} + +/* + * Set the attributes of a file. + */ +int fxp_setstat(char *fname, struct fxp_attrs attrs) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_SETSTAT); + sftp_pkt_adduint32(pktout, 0x678); /* request id */ + sftp_pkt_addstring(pktout, fname); + sftp_pkt_addattrs(pktout, attrs); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x678) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + id = fxp_got_status(pktin); + sftp_pkt_free(pktin); + if (id != 1) { + return 0; + } + return 1; +} +int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs) +{ + struct sftp_packet *pktin, *pktout; + int id; + + pktout = sftp_pkt_init(SSH_FXP_FSETSTAT); + sftp_pkt_adduint32(pktout, 0x678); /* request id */ + sftp_pkt_addstring_start(pktout); + sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); + sftp_pkt_addattrs(pktout, attrs); + sftp_send(pktout); + pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } + id = sftp_pkt_getuint32(pktin); + if (id != 0x678) { + fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); + return 0; + } + id = fxp_got_status(pktin); + sftp_pkt_free(pktin); + if (id != 1) { + return 0; + } + return 1; +} + /* * Read from a file. Returns the number of bytes read, or -1 on an * error, or possibly 0 if EOF. (I'm not entirely sure whether it @@ -500,9 +796,14 @@ int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, sftp_pkt_adduint32(pktout, len); sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return -1; + } id = sftp_pkt_getuint32(pktin); if (id != 0xBCD) { fxp_internal_error("request ID mismatch"); + sftp_pkt_free(pktin); return -1; } if (pktin->type == SSH_FXP_DATA) { @@ -513,14 +814,16 @@ int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, if (rlen > len || rlen < 0) { fxp_internal_error("READ returned more bytes than requested"); + sftp_pkt_free(pktin); return -1; } memcpy(buffer, str, rlen); - sfree(pktin); + sftp_pkt_free(pktin); return rlen; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return -1; } } @@ -539,9 +842,14 @@ struct fxp_names *fxp_readdir(struct fxp_handle *handle) sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen); sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return NULL; + } id = sftp_pkt_getuint32(pktin); if (id != 0xABC) { fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); return NULL; } if (pktin->type == SSH_FXP_NAME) { @@ -559,9 +867,11 @@ struct fxp_names *fxp_readdir(struct fxp_handle *handle) ret->names[i].longname = mkstr(str, len); ret->names[i].attrs = sftp_pkt_getattrs(pktin); } + sftp_pkt_free(pktin); return ret; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return NULL; } } @@ -584,12 +894,18 @@ int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset, sftp_pkt_addstring_data(pktout, buffer, len); sftp_send(pktout); pktin = sftp_recv(); + if (!pktin) { + fxp_internal_error("did not receive a valid SFTP packet\n"); + return 0; + } id = sftp_pkt_getuint32(pktin); if (id != 0xDCB) { fxp_internal_error("request ID mismatch\n"); + sftp_pkt_free(pktin); return 0; } fxp_got_status(pktin); + sftp_pkt_free(pktin); return fxp_errtype == SSH_FX_OK; }