sftp-client.c revision 76259
176259Sgreen/* 276259Sgreen * Copyright (c) 2001 Damien Miller. All rights reserved. 376259Sgreen * 476259Sgreen * Redistribution and use in source and binary forms, with or without 576259Sgreen * modification, are permitted provided that the following conditions 676259Sgreen * are met: 776259Sgreen * 1. Redistributions of source code must retain the above copyright 876259Sgreen * notice, this list of conditions and the following disclaimer. 976259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1076259Sgreen * notice, this list of conditions and the following disclaimer in the 1176259Sgreen * documentation and/or other materials provided with the distribution. 1276259Sgreen * 1376259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1476259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1576259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1676259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1776259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1876259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1976259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2076259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2176259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2276259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2376259Sgreen */ 2476259Sgreen 2576259Sgreen/* XXX: memleaks */ 2676259Sgreen/* XXX: signed vs unsigned */ 2776259Sgreen/* XXX: redesign to allow concurrent overlapped operations */ 2876259Sgreen/* XXX: we use fatal too much, error may be more appropriate in places */ 2976259Sgreen/* XXX: copy between two remote sites */ 3076259Sgreen 3176259Sgreen#include "includes.h" 3276259SgreenRCSID("$OpenBSD: sftp-client.c,v 1.16 2001/04/05 10:42:52 markus Exp $"); 3376259Sgreen 3476259Sgreen#include "ssh.h" 3576259Sgreen#include "buffer.h" 3676259Sgreen#include "bufaux.h" 3776259Sgreen#include "getput.h" 3876259Sgreen#include "xmalloc.h" 3976259Sgreen#include "log.h" 4076259Sgreen#include "atomicio.h" 4176259Sgreen#include "pathnames.h" 4276259Sgreen 4376259Sgreen#include "sftp.h" 4476259Sgreen#include "sftp-common.h" 4576259Sgreen#include "sftp-client.h" 4676259Sgreen 4776259Sgreen/* How much data to read/write at at time during copies */ 4876259Sgreen/* XXX: what should this be? */ 4976259Sgreen#define COPY_SIZE 8192 5076259Sgreen 5176259Sgreen/* Message ID */ 5276259Sgreenstatic u_int msg_id = 1; 5376259Sgreen 5476259Sgreenvoid 5576259Sgreensend_msg(int fd, Buffer *m) 5676259Sgreen{ 5776259Sgreen int mlen = buffer_len(m); 5876259Sgreen int len; 5976259Sgreen Buffer oqueue; 6076259Sgreen 6176259Sgreen buffer_init(&oqueue); 6276259Sgreen buffer_put_int(&oqueue, mlen); 6376259Sgreen buffer_append(&oqueue, buffer_ptr(m), mlen); 6476259Sgreen buffer_consume(m, mlen); 6576259Sgreen 6676259Sgreen len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue)); 6776259Sgreen if (len <= 0) 6876259Sgreen fatal("Couldn't send packet: %s", strerror(errno)); 6976259Sgreen 7076259Sgreen buffer_free(&oqueue); 7176259Sgreen} 7276259Sgreen 7376259Sgreenvoid 7476259Sgreenget_msg(int fd, Buffer *m) 7576259Sgreen{ 7676259Sgreen u_int len, msg_len; 7776259Sgreen unsigned char buf[4096]; 7876259Sgreen 7976259Sgreen len = atomicio(read, fd, buf, 4); 8076259Sgreen if (len == 0) 8176259Sgreen fatal("Connection closed"); 8276259Sgreen else if (len == -1) 8376259Sgreen fatal("Couldn't read packet: %s", strerror(errno)); 8476259Sgreen 8576259Sgreen msg_len = GET_32BIT(buf); 8676259Sgreen if (msg_len > 256 * 1024) 8776259Sgreen fatal("Received message too long %d", msg_len); 8876259Sgreen 8976259Sgreen while (msg_len) { 9076259Sgreen len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf))); 9176259Sgreen if (len == 0) 9276259Sgreen fatal("Connection closed"); 9376259Sgreen else if (len == -1) 9476259Sgreen fatal("Couldn't read packet: %s", strerror(errno)); 9576259Sgreen 9676259Sgreen msg_len -= len; 9776259Sgreen buffer_append(m, buf, len); 9876259Sgreen } 9976259Sgreen} 10076259Sgreen 10176259Sgreenvoid 10276259Sgreensend_string_request(int fd, u_int id, u_int code, char *s, 10376259Sgreen u_int len) 10476259Sgreen{ 10576259Sgreen Buffer msg; 10676259Sgreen 10776259Sgreen buffer_init(&msg); 10876259Sgreen buffer_put_char(&msg, code); 10976259Sgreen buffer_put_int(&msg, id); 11076259Sgreen buffer_put_string(&msg, s, len); 11176259Sgreen send_msg(fd, &msg); 11276259Sgreen debug3("Sent message fd %d T:%d I:%d", fd, code, id); 11376259Sgreen buffer_free(&msg); 11476259Sgreen} 11576259Sgreen 11676259Sgreenvoid 11776259Sgreensend_string_attrs_request(int fd, u_int id, u_int code, char *s, 11876259Sgreen u_int len, Attrib *a) 11976259Sgreen{ 12076259Sgreen Buffer msg; 12176259Sgreen 12276259Sgreen buffer_init(&msg); 12376259Sgreen buffer_put_char(&msg, code); 12476259Sgreen buffer_put_int(&msg, id); 12576259Sgreen buffer_put_string(&msg, s, len); 12676259Sgreen encode_attrib(&msg, a); 12776259Sgreen send_msg(fd, &msg); 12876259Sgreen debug3("Sent message fd %d T:%d I:%d", fd, code, id); 12976259Sgreen buffer_free(&msg); 13076259Sgreen} 13176259Sgreen 13276259Sgreenu_int 13376259Sgreenget_status(int fd, int expected_id) 13476259Sgreen{ 13576259Sgreen Buffer msg; 13676259Sgreen u_int type, id, status; 13776259Sgreen 13876259Sgreen buffer_init(&msg); 13976259Sgreen get_msg(fd, &msg); 14076259Sgreen type = buffer_get_char(&msg); 14176259Sgreen id = buffer_get_int(&msg); 14276259Sgreen 14376259Sgreen if (id != expected_id) 14476259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 14576259Sgreen if (type != SSH2_FXP_STATUS) 14676259Sgreen fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d", 14776259Sgreen SSH2_FXP_STATUS, type); 14876259Sgreen 14976259Sgreen status = buffer_get_int(&msg); 15076259Sgreen buffer_free(&msg); 15176259Sgreen 15276259Sgreen debug3("SSH2_FXP_STATUS %d", status); 15376259Sgreen 15476259Sgreen return(status); 15576259Sgreen} 15676259Sgreen 15776259Sgreenchar * 15876259Sgreenget_handle(int fd, u_int expected_id, u_int *len) 15976259Sgreen{ 16076259Sgreen Buffer msg; 16176259Sgreen u_int type, id; 16276259Sgreen char *handle; 16376259Sgreen 16476259Sgreen buffer_init(&msg); 16576259Sgreen get_msg(fd, &msg); 16676259Sgreen type = buffer_get_char(&msg); 16776259Sgreen id = buffer_get_int(&msg); 16876259Sgreen 16976259Sgreen if (id != expected_id) 17076259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 17176259Sgreen if (type == SSH2_FXP_STATUS) { 17276259Sgreen int status = buffer_get_int(&msg); 17376259Sgreen 17476259Sgreen error("Couldn't get handle: %s", fx2txt(status)); 17576259Sgreen return(NULL); 17676259Sgreen } else if (type != SSH2_FXP_HANDLE) 17776259Sgreen fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d", 17876259Sgreen SSH2_FXP_HANDLE, type); 17976259Sgreen 18076259Sgreen handle = buffer_get_string(&msg, len); 18176259Sgreen buffer_free(&msg); 18276259Sgreen 18376259Sgreen return(handle); 18476259Sgreen} 18576259Sgreen 18676259SgreenAttrib * 18776259Sgreenget_decode_stat(int fd, u_int expected_id, int quiet) 18876259Sgreen{ 18976259Sgreen Buffer msg; 19076259Sgreen u_int type, id; 19176259Sgreen Attrib *a; 19276259Sgreen 19376259Sgreen buffer_init(&msg); 19476259Sgreen get_msg(fd, &msg); 19576259Sgreen 19676259Sgreen type = buffer_get_char(&msg); 19776259Sgreen id = buffer_get_int(&msg); 19876259Sgreen 19976259Sgreen debug3("Received stat reply T:%d I:%d", type, id); 20076259Sgreen if (id != expected_id) 20176259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 20276259Sgreen if (type == SSH2_FXP_STATUS) { 20376259Sgreen int status = buffer_get_int(&msg); 20476259Sgreen 20576259Sgreen if (quiet) 20676259Sgreen debug("Couldn't stat remote file: %s", fx2txt(status)); 20776259Sgreen else 20876259Sgreen error("Couldn't stat remote file: %s", fx2txt(status)); 20976259Sgreen return(NULL); 21076259Sgreen } else if (type != SSH2_FXP_ATTRS) { 21176259Sgreen fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d", 21276259Sgreen SSH2_FXP_ATTRS, type); 21376259Sgreen } 21476259Sgreen a = decode_attrib(&msg); 21576259Sgreen buffer_free(&msg); 21676259Sgreen 21776259Sgreen return(a); 21876259Sgreen} 21976259Sgreen 22076259Sgreenint 22176259Sgreendo_init(int fd_in, int fd_out) 22276259Sgreen{ 22376259Sgreen int type, version; 22476259Sgreen Buffer msg; 22576259Sgreen 22676259Sgreen buffer_init(&msg); 22776259Sgreen buffer_put_char(&msg, SSH2_FXP_INIT); 22876259Sgreen buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 22976259Sgreen send_msg(fd_out, &msg); 23076259Sgreen 23176259Sgreen buffer_clear(&msg); 23276259Sgreen 23376259Sgreen get_msg(fd_in, &msg); 23476259Sgreen 23576259Sgreen /* Expecting a VERSION reply */ 23676259Sgreen if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 23776259Sgreen error("Invalid packet back from SSH2_FXP_INIT (type %d)", 23876259Sgreen type); 23976259Sgreen buffer_free(&msg); 24076259Sgreen return(-1); 24176259Sgreen } 24276259Sgreen version = buffer_get_int(&msg); 24376259Sgreen 24476259Sgreen debug2("Remote version: %d", version); 24576259Sgreen 24676259Sgreen /* Check for extensions */ 24776259Sgreen while (buffer_len(&msg) > 0) { 24876259Sgreen char *name = buffer_get_string(&msg, NULL); 24976259Sgreen char *value = buffer_get_string(&msg, NULL); 25076259Sgreen 25176259Sgreen debug2("Init extension: \"%s\"", name); 25276259Sgreen xfree(name); 25376259Sgreen xfree(value); 25476259Sgreen } 25576259Sgreen 25676259Sgreen buffer_free(&msg); 25776259Sgreen 25876259Sgreen return(version); 25976259Sgreen} 26076259Sgreen 26176259Sgreenint 26276259Sgreendo_close(int fd_in, int fd_out, char *handle, u_int handle_len) 26376259Sgreen{ 26476259Sgreen u_int id, status; 26576259Sgreen Buffer msg; 26676259Sgreen 26776259Sgreen buffer_init(&msg); 26876259Sgreen 26976259Sgreen id = msg_id++; 27076259Sgreen buffer_put_char(&msg, SSH2_FXP_CLOSE); 27176259Sgreen buffer_put_int(&msg, id); 27276259Sgreen buffer_put_string(&msg, handle, handle_len); 27376259Sgreen send_msg(fd_out, &msg); 27476259Sgreen debug3("Sent message SSH2_FXP_CLOSE I:%d", id); 27576259Sgreen 27676259Sgreen status = get_status(fd_in, id); 27776259Sgreen if (status != SSH2_FX_OK) 27876259Sgreen error("Couldn't close file: %s", fx2txt(status)); 27976259Sgreen 28076259Sgreen buffer_free(&msg); 28176259Sgreen 28276259Sgreen return(status); 28376259Sgreen} 28476259Sgreen 28576259Sgreen 28676259Sgreenint 28776259Sgreendo_lsreaddir(int fd_in, int fd_out, char *path, int printflag, 28876259Sgreen SFTP_DIRENT ***dir) 28976259Sgreen{ 29076259Sgreen Buffer msg; 29176259Sgreen u_int type, id, handle_len, i, expected_id, ents = 0; 29276259Sgreen char *handle; 29376259Sgreen 29476259Sgreen id = msg_id++; 29576259Sgreen 29676259Sgreen buffer_init(&msg); 29776259Sgreen buffer_put_char(&msg, SSH2_FXP_OPENDIR); 29876259Sgreen buffer_put_int(&msg, id); 29976259Sgreen buffer_put_cstring(&msg, path); 30076259Sgreen send_msg(fd_out, &msg); 30176259Sgreen 30276259Sgreen buffer_clear(&msg); 30376259Sgreen 30476259Sgreen handle = get_handle(fd_in, id, &handle_len); 30576259Sgreen if (handle == NULL) 30676259Sgreen return(-1); 30776259Sgreen 30876259Sgreen if (dir) { 30976259Sgreen ents = 0; 31076259Sgreen *dir = xmalloc(sizeof(**dir)); 31176259Sgreen (*dir)[0] = NULL; 31276259Sgreen } 31376259Sgreen 31476259Sgreen 31576259Sgreen for(;;) { 31676259Sgreen int count; 31776259Sgreen 31876259Sgreen id = expected_id = msg_id++; 31976259Sgreen 32076259Sgreen debug3("Sending SSH2_FXP_READDIR I:%d", id); 32176259Sgreen 32276259Sgreen buffer_clear(&msg); 32376259Sgreen buffer_put_char(&msg, SSH2_FXP_READDIR); 32476259Sgreen buffer_put_int(&msg, id); 32576259Sgreen buffer_put_string(&msg, handle, handle_len); 32676259Sgreen send_msg(fd_out, &msg); 32776259Sgreen 32876259Sgreen buffer_clear(&msg); 32976259Sgreen 33076259Sgreen get_msg(fd_in, &msg); 33176259Sgreen 33276259Sgreen type = buffer_get_char(&msg); 33376259Sgreen id = buffer_get_int(&msg); 33476259Sgreen 33576259Sgreen debug3("Received reply T:%d I:%d", type, id); 33676259Sgreen 33776259Sgreen if (id != expected_id) 33876259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 33976259Sgreen 34076259Sgreen if (type == SSH2_FXP_STATUS) { 34176259Sgreen int status = buffer_get_int(&msg); 34276259Sgreen 34376259Sgreen debug3("Received SSH2_FXP_STATUS %d", status); 34476259Sgreen 34576259Sgreen if (status == SSH2_FX_EOF) { 34676259Sgreen break; 34776259Sgreen } else { 34876259Sgreen error("Couldn't read directory: %s", 34976259Sgreen fx2txt(status)); 35076259Sgreen do_close(fd_in, fd_out, handle, handle_len); 35176259Sgreen return(status); 35276259Sgreen } 35376259Sgreen } else if (type != SSH2_FXP_NAME) 35476259Sgreen fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", 35576259Sgreen SSH2_FXP_NAME, type); 35676259Sgreen 35776259Sgreen count = buffer_get_int(&msg); 35876259Sgreen if (count == 0) 35976259Sgreen break; 36076259Sgreen debug3("Received %d SSH2_FXP_NAME responses", count); 36176259Sgreen for(i = 0; i < count; i++) { 36276259Sgreen char *filename, *longname; 36376259Sgreen Attrib *a; 36476259Sgreen 36576259Sgreen filename = buffer_get_string(&msg, NULL); 36676259Sgreen longname = buffer_get_string(&msg, NULL); 36776259Sgreen a = decode_attrib(&msg); 36876259Sgreen 36976259Sgreen if (printflag) 37076259Sgreen printf("%s\n", longname); 37176259Sgreen 37276259Sgreen if (dir) { 37376259Sgreen *dir = xrealloc(*dir, sizeof(**dir) * 37476259Sgreen (ents + 2)); 37576259Sgreen (*dir)[ents] = xmalloc(sizeof(***dir)); 37676259Sgreen (*dir)[ents]->filename = xstrdup(filename); 37776259Sgreen (*dir)[ents]->longname = xstrdup(longname); 37876259Sgreen memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 37976259Sgreen (*dir)[++ents] = NULL; 38076259Sgreen } 38176259Sgreen 38276259Sgreen xfree(filename); 38376259Sgreen xfree(longname); 38476259Sgreen } 38576259Sgreen } 38676259Sgreen 38776259Sgreen buffer_free(&msg); 38876259Sgreen do_close(fd_in, fd_out, handle, handle_len); 38976259Sgreen xfree(handle); 39076259Sgreen 39176259Sgreen return(0); 39276259Sgreen} 39376259Sgreen 39476259Sgreenint 39576259Sgreendo_ls(int fd_in, int fd_out, char *path) 39676259Sgreen{ 39776259Sgreen return(do_lsreaddir(fd_in, fd_out, path, 1, NULL)); 39876259Sgreen} 39976259Sgreen 40076259Sgreenint 40176259Sgreendo_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir) 40276259Sgreen{ 40376259Sgreen return(do_lsreaddir(fd_in, fd_out, path, 0, dir)); 40476259Sgreen} 40576259Sgreen 40676259Sgreenvoid free_sftp_dirents(SFTP_DIRENT **s) 40776259Sgreen{ 40876259Sgreen int i; 40976259Sgreen 41076259Sgreen for(i = 0; s[i]; i++) { 41176259Sgreen xfree(s[i]->filename); 41276259Sgreen xfree(s[i]->longname); 41376259Sgreen xfree(s[i]); 41476259Sgreen } 41576259Sgreen xfree(s); 41676259Sgreen} 41776259Sgreen 41876259Sgreenint 41976259Sgreendo_rm(int fd_in, int fd_out, char *path) 42076259Sgreen{ 42176259Sgreen u_int status, id; 42276259Sgreen 42376259Sgreen debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 42476259Sgreen 42576259Sgreen id = msg_id++; 42676259Sgreen send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path)); 42776259Sgreen status = get_status(fd_in, id); 42876259Sgreen if (status != SSH2_FX_OK) 42976259Sgreen error("Couldn't delete file: %s", fx2txt(status)); 43076259Sgreen return(status); 43176259Sgreen} 43276259Sgreen 43376259Sgreenint 43476259Sgreendo_mkdir(int fd_in, int fd_out, char *path, Attrib *a) 43576259Sgreen{ 43676259Sgreen u_int status, id; 43776259Sgreen 43876259Sgreen id = msg_id++; 43976259Sgreen send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path, 44076259Sgreen strlen(path), a); 44176259Sgreen 44276259Sgreen status = get_status(fd_in, id); 44376259Sgreen if (status != SSH2_FX_OK) 44476259Sgreen error("Couldn't create directory: %s", fx2txt(status)); 44576259Sgreen 44676259Sgreen return(status); 44776259Sgreen} 44876259Sgreen 44976259Sgreenint 45076259Sgreendo_rmdir(int fd_in, int fd_out, char *path) 45176259Sgreen{ 45276259Sgreen u_int status, id; 45376259Sgreen 45476259Sgreen id = msg_id++; 45576259Sgreen send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path)); 45676259Sgreen 45776259Sgreen status = get_status(fd_in, id); 45876259Sgreen if (status != SSH2_FX_OK) 45976259Sgreen error("Couldn't remove directory: %s", fx2txt(status)); 46076259Sgreen 46176259Sgreen return(status); 46276259Sgreen} 46376259Sgreen 46476259SgreenAttrib * 46576259Sgreendo_stat(int fd_in, int fd_out, char *path, int quiet) 46676259Sgreen{ 46776259Sgreen u_int id; 46876259Sgreen 46976259Sgreen id = msg_id++; 47076259Sgreen send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path)); 47176259Sgreen return(get_decode_stat(fd_in, id, quiet)); 47276259Sgreen} 47376259Sgreen 47476259SgreenAttrib * 47576259Sgreendo_lstat(int fd_in, int fd_out, char *path, int quiet) 47676259Sgreen{ 47776259Sgreen u_int id; 47876259Sgreen 47976259Sgreen id = msg_id++; 48076259Sgreen send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path)); 48176259Sgreen return(get_decode_stat(fd_in, id, quiet)); 48276259Sgreen} 48376259Sgreen 48476259SgreenAttrib * 48576259Sgreendo_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet) 48676259Sgreen{ 48776259Sgreen u_int id; 48876259Sgreen 48976259Sgreen id = msg_id++; 49076259Sgreen send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len); 49176259Sgreen return(get_decode_stat(fd_in, id, quiet)); 49276259Sgreen} 49376259Sgreen 49476259Sgreenint 49576259Sgreendo_setstat(int fd_in, int fd_out, char *path, Attrib *a) 49676259Sgreen{ 49776259Sgreen u_int status, id; 49876259Sgreen 49976259Sgreen id = msg_id++; 50076259Sgreen send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path, 50176259Sgreen strlen(path), a); 50276259Sgreen 50376259Sgreen status = get_status(fd_in, id); 50476259Sgreen if (status != SSH2_FX_OK) 50576259Sgreen error("Couldn't setstat on \"%s\": %s", path, 50676259Sgreen fx2txt(status)); 50776259Sgreen 50876259Sgreen return(status); 50976259Sgreen} 51076259Sgreen 51176259Sgreenint 51276259Sgreendo_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len, 51376259Sgreen Attrib *a) 51476259Sgreen{ 51576259Sgreen u_int status, id; 51676259Sgreen 51776259Sgreen id = msg_id++; 51876259Sgreen send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle, 51976259Sgreen handle_len, a); 52076259Sgreen 52176259Sgreen status = get_status(fd_in, id); 52276259Sgreen if (status != SSH2_FX_OK) 52376259Sgreen error("Couldn't fsetstat: %s", fx2txt(status)); 52476259Sgreen 52576259Sgreen return(status); 52676259Sgreen} 52776259Sgreen 52876259Sgreenchar * 52976259Sgreendo_realpath(int fd_in, int fd_out, char *path) 53076259Sgreen{ 53176259Sgreen Buffer msg; 53276259Sgreen u_int type, expected_id, count, id; 53376259Sgreen char *filename, *longname; 53476259Sgreen Attrib *a; 53576259Sgreen 53676259Sgreen expected_id = id = msg_id++; 53776259Sgreen send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path)); 53876259Sgreen 53976259Sgreen buffer_init(&msg); 54076259Sgreen 54176259Sgreen get_msg(fd_in, &msg); 54276259Sgreen type = buffer_get_char(&msg); 54376259Sgreen id = buffer_get_int(&msg); 54476259Sgreen 54576259Sgreen if (id != expected_id) 54676259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 54776259Sgreen 54876259Sgreen if (type == SSH2_FXP_STATUS) { 54976259Sgreen u_int status = buffer_get_int(&msg); 55076259Sgreen 55176259Sgreen error("Couldn't canonicalise: %s", fx2txt(status)); 55276259Sgreen return(NULL); 55376259Sgreen } else if (type != SSH2_FXP_NAME) 55476259Sgreen fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", 55576259Sgreen SSH2_FXP_NAME, type); 55676259Sgreen 55776259Sgreen count = buffer_get_int(&msg); 55876259Sgreen if (count != 1) 55976259Sgreen fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); 56076259Sgreen 56176259Sgreen filename = buffer_get_string(&msg, NULL); 56276259Sgreen longname = buffer_get_string(&msg, NULL); 56376259Sgreen a = decode_attrib(&msg); 56476259Sgreen 56576259Sgreen debug3("SSH_FXP_REALPATH %s -> %s", path, filename); 56676259Sgreen 56776259Sgreen xfree(longname); 56876259Sgreen 56976259Sgreen buffer_free(&msg); 57076259Sgreen 57176259Sgreen return(filename); 57276259Sgreen} 57376259Sgreen 57476259Sgreenint 57576259Sgreendo_rename(int fd_in, int fd_out, char *oldpath, char *newpath) 57676259Sgreen{ 57776259Sgreen Buffer msg; 57876259Sgreen u_int status, id; 57976259Sgreen 58076259Sgreen buffer_init(&msg); 58176259Sgreen 58276259Sgreen /* Send rename request */ 58376259Sgreen id = msg_id++; 58476259Sgreen buffer_put_char(&msg, SSH2_FXP_RENAME); 58576259Sgreen buffer_put_int(&msg, id); 58676259Sgreen buffer_put_cstring(&msg, oldpath); 58776259Sgreen buffer_put_cstring(&msg, newpath); 58876259Sgreen send_msg(fd_out, &msg); 58976259Sgreen debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath, 59076259Sgreen newpath); 59176259Sgreen buffer_free(&msg); 59276259Sgreen 59376259Sgreen status = get_status(fd_in, id); 59476259Sgreen if (status != SSH2_FX_OK) 59576259Sgreen error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, 59676259Sgreen fx2txt(status)); 59776259Sgreen 59876259Sgreen return(status); 59976259Sgreen} 60076259Sgreen 60176259Sgreenint 60276259Sgreendo_symlink(int fd_in, int fd_out, char *oldpath, char *newpath) 60376259Sgreen{ 60476259Sgreen Buffer msg; 60576259Sgreen u_int status, id; 60676259Sgreen 60776259Sgreen buffer_init(&msg); 60876259Sgreen 60976259Sgreen /* Send rename request */ 61076259Sgreen id = msg_id++; 61176259Sgreen buffer_put_char(&msg, SSH2_FXP_SYMLINK); 61276259Sgreen buffer_put_int(&msg, id); 61376259Sgreen buffer_put_cstring(&msg, oldpath); 61476259Sgreen buffer_put_cstring(&msg, newpath); 61576259Sgreen send_msg(fd_out, &msg); 61676259Sgreen debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 61776259Sgreen newpath); 61876259Sgreen buffer_free(&msg); 61976259Sgreen 62076259Sgreen status = get_status(fd_in, id); 62176259Sgreen if (status != SSH2_FX_OK) 62276259Sgreen error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, 62376259Sgreen fx2txt(status)); 62476259Sgreen 62576259Sgreen return(status); 62676259Sgreen} 62776259Sgreen 62876259Sgreenchar * 62976259Sgreendo_readlink(int fd_in, int fd_out, char *path) 63076259Sgreen{ 63176259Sgreen Buffer msg; 63276259Sgreen u_int type, expected_id, count, id; 63376259Sgreen char *filename, *longname; 63476259Sgreen Attrib *a; 63576259Sgreen 63676259Sgreen expected_id = id = msg_id++; 63776259Sgreen send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path)); 63876259Sgreen 63976259Sgreen buffer_init(&msg); 64076259Sgreen 64176259Sgreen get_msg(fd_in, &msg); 64276259Sgreen type = buffer_get_char(&msg); 64376259Sgreen id = buffer_get_int(&msg); 64476259Sgreen 64576259Sgreen if (id != expected_id) 64676259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 64776259Sgreen 64876259Sgreen if (type == SSH2_FXP_STATUS) { 64976259Sgreen u_int status = buffer_get_int(&msg); 65076259Sgreen 65176259Sgreen error("Couldn't readlink: %s", fx2txt(status)); 65276259Sgreen return(NULL); 65376259Sgreen } else if (type != SSH2_FXP_NAME) 65476259Sgreen fatal("Expected SSH2_FXP_NAME(%d) packet, got %d", 65576259Sgreen SSH2_FXP_NAME, type); 65676259Sgreen 65776259Sgreen count = buffer_get_int(&msg); 65876259Sgreen if (count != 1) 65976259Sgreen fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); 66076259Sgreen 66176259Sgreen filename = buffer_get_string(&msg, NULL); 66276259Sgreen longname = buffer_get_string(&msg, NULL); 66376259Sgreen a = decode_attrib(&msg); 66476259Sgreen 66576259Sgreen debug3("SSH_FXP_READLINK %s -> %s", path, filename); 66676259Sgreen 66776259Sgreen xfree(longname); 66876259Sgreen 66976259Sgreen buffer_free(&msg); 67076259Sgreen 67176259Sgreen return(filename); 67276259Sgreen} 67376259Sgreen 67476259Sgreenint 67576259Sgreendo_download(int fd_in, int fd_out, char *remote_path, char *local_path, 67676259Sgreen int pflag) 67776259Sgreen{ 67876259Sgreen int local_fd; 67976259Sgreen u_int expected_id, handle_len, mode, type, id; 68076259Sgreen u_int64_t offset; 68176259Sgreen char *handle; 68276259Sgreen Buffer msg; 68376259Sgreen Attrib junk, *a; 68476259Sgreen int status; 68576259Sgreen 68676259Sgreen a = do_stat(fd_in, fd_out, remote_path, 0); 68776259Sgreen if (a == NULL) 68876259Sgreen return(-1); 68976259Sgreen 69076259Sgreen /* XXX: should we preserve set[ug]id? */ 69176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 69276259Sgreen mode = S_IWRITE | (a->perm & 0777); 69376259Sgreen else 69476259Sgreen mode = 0666; 69576259Sgreen 69676259Sgreen if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 69776259Sgreen (a->perm & S_IFDIR)) { 69876259Sgreen error("Cannot download a directory: %s", remote_path); 69976259Sgreen return(-1); 70076259Sgreen } 70176259Sgreen 70276259Sgreen local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode); 70376259Sgreen if (local_fd == -1) { 70476259Sgreen error("Couldn't open local file \"%s\" for writing: %s", 70576259Sgreen local_path, strerror(errno)); 70676259Sgreen return(-1); 70776259Sgreen } 70876259Sgreen 70976259Sgreen buffer_init(&msg); 71076259Sgreen 71176259Sgreen /* Send open request */ 71276259Sgreen id = msg_id++; 71376259Sgreen buffer_put_char(&msg, SSH2_FXP_OPEN); 71476259Sgreen buffer_put_int(&msg, id); 71576259Sgreen buffer_put_cstring(&msg, remote_path); 71676259Sgreen buffer_put_int(&msg, SSH2_FXF_READ); 71776259Sgreen attrib_clear(&junk); /* Send empty attributes */ 71876259Sgreen encode_attrib(&msg, &junk); 71976259Sgreen send_msg(fd_out, &msg); 72076259Sgreen debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); 72176259Sgreen 72276259Sgreen handle = get_handle(fd_in, id, &handle_len); 72376259Sgreen if (handle == NULL) { 72476259Sgreen buffer_free(&msg); 72576259Sgreen close(local_fd); 72676259Sgreen return(-1); 72776259Sgreen } 72876259Sgreen 72976259Sgreen /* Read from remote and write to local */ 73076259Sgreen offset = 0; 73176259Sgreen for(;;) { 73276259Sgreen u_int len; 73376259Sgreen char *data; 73476259Sgreen 73576259Sgreen id = expected_id = msg_id++; 73676259Sgreen 73776259Sgreen buffer_clear(&msg); 73876259Sgreen buffer_put_char(&msg, SSH2_FXP_READ); 73976259Sgreen buffer_put_int(&msg, id); 74076259Sgreen buffer_put_string(&msg, handle, handle_len); 74176259Sgreen buffer_put_int64(&msg, offset); 74276259Sgreen buffer_put_int(&msg, COPY_SIZE); 74376259Sgreen send_msg(fd_out, &msg); 74476259Sgreen debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u", 74576259Sgreen id, (unsigned long long)offset, COPY_SIZE); 74676259Sgreen 74776259Sgreen buffer_clear(&msg); 74876259Sgreen 74976259Sgreen get_msg(fd_in, &msg); 75076259Sgreen type = buffer_get_char(&msg); 75176259Sgreen id = buffer_get_int(&msg); 75276259Sgreen debug3("Received reply T:%d I:%d", type, id); 75376259Sgreen if (id != expected_id) 75476259Sgreen fatal("ID mismatch (%d != %d)", id, expected_id); 75576259Sgreen if (type == SSH2_FXP_STATUS) { 75676259Sgreen status = buffer_get_int(&msg); 75776259Sgreen 75876259Sgreen if (status == SSH2_FX_EOF) 75976259Sgreen break; 76076259Sgreen else { 76176259Sgreen error("Couldn't read from remote " 76276259Sgreen "file \"%s\" : %s", remote_path, 76376259Sgreen fx2txt(status)); 76476259Sgreen do_close(fd_in, fd_out, handle, handle_len); 76576259Sgreen goto done; 76676259Sgreen } 76776259Sgreen } else if (type != SSH2_FXP_DATA) { 76876259Sgreen fatal("Expected SSH2_FXP_DATA(%d) packet, got %d", 76976259Sgreen SSH2_FXP_DATA, type); 77076259Sgreen } 77176259Sgreen 77276259Sgreen data = buffer_get_string(&msg, &len); 77376259Sgreen if (len > COPY_SIZE) 77476259Sgreen fatal("Received more data than asked for %d > %d", 77576259Sgreen len, COPY_SIZE); 77676259Sgreen 77776259Sgreen debug3("In read loop, got %d offset %llu", len, 77876259Sgreen (unsigned long long)offset); 77976259Sgreen if (atomicio(write, local_fd, data, len) != len) { 78076259Sgreen error("Couldn't write to \"%s\": %s", local_path, 78176259Sgreen strerror(errno)); 78276259Sgreen do_close(fd_in, fd_out, handle, handle_len); 78376259Sgreen status = -1; 78476259Sgreen xfree(data); 78576259Sgreen goto done; 78676259Sgreen } 78776259Sgreen 78876259Sgreen offset += len; 78976259Sgreen xfree(data); 79076259Sgreen } 79176259Sgreen status = do_close(fd_in, fd_out, handle, handle_len); 79276259Sgreen 79376259Sgreen /* Override umask and utimes if asked */ 79476259Sgreen if (pflag && fchmod(local_fd, mode) == -1) 79576259Sgreen error("Couldn't set mode on \"%s\": %s", local_path, 79676259Sgreen strerror(errno)); 79776259Sgreen if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 79876259Sgreen struct timeval tv[2]; 79976259Sgreen tv[0].tv_sec = a->atime; 80076259Sgreen tv[1].tv_sec = a->mtime; 80176259Sgreen tv[0].tv_usec = tv[1].tv_usec = 0; 80276259Sgreen if (utimes(local_path, tv) == -1) 80376259Sgreen error("Can't set times on \"%s\": %s", local_path, 80476259Sgreen strerror(errno)); 80576259Sgreen } 80676259Sgreen 80776259Sgreendone: 80876259Sgreen close(local_fd); 80976259Sgreen buffer_free(&msg); 81076259Sgreen xfree(handle); 81176259Sgreen return status; 81276259Sgreen} 81376259Sgreen 81476259Sgreenint 81576259Sgreendo_upload(int fd_in, int fd_out, char *local_path, char *remote_path, 81676259Sgreen int pflag) 81776259Sgreen{ 81876259Sgreen int local_fd; 81976259Sgreen u_int handle_len, id; 82076259Sgreen u_int64_t offset; 82176259Sgreen char *handle; 82276259Sgreen Buffer msg; 82376259Sgreen struct stat sb; 82476259Sgreen Attrib a; 82576259Sgreen int status; 82676259Sgreen 82776259Sgreen if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 82876259Sgreen error("Couldn't open local file \"%s\" for reading: %s", 82976259Sgreen local_path, strerror(errno)); 83076259Sgreen return(-1); 83176259Sgreen } 83276259Sgreen if (fstat(local_fd, &sb) == -1) { 83376259Sgreen error("Couldn't fstat local file \"%s\": %s", 83476259Sgreen local_path, strerror(errno)); 83576259Sgreen close(local_fd); 83676259Sgreen return(-1); 83776259Sgreen } 83876259Sgreen stat_to_attrib(&sb, &a); 83976259Sgreen 84076259Sgreen a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 84176259Sgreen a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 84276259Sgreen a.perm &= 0777; 84376259Sgreen if (!pflag) 84476259Sgreen a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 84576259Sgreen 84676259Sgreen buffer_init(&msg); 84776259Sgreen 84876259Sgreen /* Send open request */ 84976259Sgreen id = msg_id++; 85076259Sgreen buffer_put_char(&msg, SSH2_FXP_OPEN); 85176259Sgreen buffer_put_int(&msg, id); 85276259Sgreen buffer_put_cstring(&msg, remote_path); 85376259Sgreen buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); 85476259Sgreen encode_attrib(&msg, &a); 85576259Sgreen send_msg(fd_out, &msg); 85676259Sgreen debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path); 85776259Sgreen 85876259Sgreen buffer_clear(&msg); 85976259Sgreen 86076259Sgreen handle = get_handle(fd_in, id, &handle_len); 86176259Sgreen if (handle == NULL) { 86276259Sgreen close(local_fd); 86376259Sgreen buffer_free(&msg); 86476259Sgreen return(-1); 86576259Sgreen } 86676259Sgreen 86776259Sgreen /* Read from local and write to remote */ 86876259Sgreen offset = 0; 86976259Sgreen for(;;) { 87076259Sgreen int len; 87176259Sgreen char data[COPY_SIZE]; 87276259Sgreen 87376259Sgreen /* 87476259Sgreen * Can't use atomicio here because it returns 0 on EOF, thus losing 87576259Sgreen * the last block of the file 87676259Sgreen */ 87776259Sgreen do 87876259Sgreen len = read(local_fd, data, COPY_SIZE); 87976259Sgreen while ((len == -1) && (errno == EINTR || errno == EAGAIN)); 88076259Sgreen 88176259Sgreen if (len == -1) 88276259Sgreen fatal("Couldn't read from \"%s\": %s", local_path, 88376259Sgreen strerror(errno)); 88476259Sgreen if (len == 0) 88576259Sgreen break; 88676259Sgreen 88776259Sgreen buffer_clear(&msg); 88876259Sgreen buffer_put_char(&msg, SSH2_FXP_WRITE); 88976259Sgreen buffer_put_int(&msg, ++id); 89076259Sgreen buffer_put_string(&msg, handle, handle_len); 89176259Sgreen buffer_put_int64(&msg, offset); 89276259Sgreen buffer_put_string(&msg, data, len); 89376259Sgreen send_msg(fd_out, &msg); 89476259Sgreen debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u", 89576259Sgreen id, (unsigned long long)offset, len); 89676259Sgreen 89776259Sgreen status = get_status(fd_in, id); 89876259Sgreen if (status != SSH2_FX_OK) { 89976259Sgreen error("Couldn't write to remote file \"%s\": %s", 90076259Sgreen remote_path, fx2txt(status)); 90176259Sgreen do_close(fd_in, fd_out, handle, handle_len); 90276259Sgreen close(local_fd); 90376259Sgreen goto done; 90476259Sgreen } 90576259Sgreen debug3("In write loop, got %d offset %llu", len, 90676259Sgreen (unsigned long long)offset); 90776259Sgreen 90876259Sgreen offset += len; 90976259Sgreen } 91076259Sgreen 91176259Sgreen if (close(local_fd) == -1) { 91276259Sgreen error("Couldn't close local file \"%s\": %s", local_path, 91376259Sgreen strerror(errno)); 91476259Sgreen do_close(fd_in, fd_out, handle, handle_len); 91576259Sgreen status = -1; 91676259Sgreen goto done; 91776259Sgreen } 91876259Sgreen 91976259Sgreen /* Override umask and utimes if asked */ 92076259Sgreen if (pflag) 92176259Sgreen do_fsetstat(fd_in, fd_out, handle, handle_len, &a); 92276259Sgreen 92376259Sgreen status = do_close(fd_in, fd_out, handle, handle_len); 92476259Sgreen 92576259Sgreendone: 92676259Sgreen xfree(handle); 92776259Sgreen buffer_free(&msg); 92876259Sgreen return status; 92976259Sgreen} 93076259Sgreen 931