X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/9954aaa37368a233dc614d610ca68c2ce5a7e8cd..7d2c1789c5a8ccf8a767fb11082bff34c1b7c5aa:/sftp.c diff --git a/sftp.c b/sftp.c index 44913492..e1f60173 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) | \ @@ -65,7 +62,6 @@ static struct sftp_packet *sftp_pkt_init(int pkt_type) pkt->length = 0; pkt->maxlen = 0; sftp_pkt_addbyte(pkt, (unsigned char) pkt_type); - fxp_error_message = NULL; return pkt; } static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value) @@ -107,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. @@ -315,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; } /* @@ -348,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) { @@ -361,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); @@ -373,6 +403,7 @@ char *fxp_realpath(char *path) return path; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return NULL; } } @@ -392,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) { @@ -405,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)); @@ -414,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; } } @@ -431,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) { @@ -444,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)); @@ -453,6 +497,7 @@ struct fxp_handle *fxp_opendir(char *path) return handle; } else { fxp_got_status(pktin); + sftp_pkt_free(pktin); return NULL; } } @@ -471,12 +516,18 @@ 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); } @@ -488,17 +539,22 @@ int fxp_mkdir(char *path) pktout = sftp_pkt_init(SSH_FXP_MKDIR); sftp_pkt_adduint32(pktout, 0x234); /* request id */ - sftp_pkt_addstring_start(pktout); - sftp_pkt_addstring_data(pktout, path, strlen(path)); + 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; } @@ -512,39 +568,208 @@ int fxp_rmdir(char *path) pktout = sftp_pkt_init(SSH_FXP_RMDIR); sftp_pkt_adduint32(pktout, 0x345); /* request id */ - sftp_pkt_addstring_start(pktout); - sftp_pkt_addstring_data(pktout, path, strlen(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 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_rm(char *fname) +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, fname, strlen(fname)); + 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; } @@ -571,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) { @@ -584,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; } } @@ -610,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) { @@ -630,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; } } @@ -655,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; } @@ -678,3 +923,28 @@ void fxp_free_names(struct fxp_names *names) 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 = smalloc(sizeof(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) +{ + int i; + + sfree(name->filename); + sfree(name->longname); + sfree(name); +}