[BACK]Return to sftp-client.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / crypto / external / bsd / openssh / dist

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/crypto/external/bsd/openssh/dist/sftp-client.c between version 1.5 and 1.5.10.1

version 1.5, 2011/07/25 03:03:11 version 1.5.10.1, 2017/08/15 04:40:16
Line 1 
Line 1 
 /*      $NetBSD$        */  /*      $NetBSD$        */
 /* $OpenBSD: sftp-client.c,v 1.94 2010/12/04 00:18:01 djm Exp $ */  /* $OpenBSD: sftp-client.c,v 1.126 2017/01/03 05:46:51 djm Exp $ */
 /*  /*
  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>   * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
  *   *
Line 23 
Line 23 
   
 #include "includes.h"  #include "includes.h"
 __RCSID("$NetBSD$");  __RCSID("$NetBSD$");
   
   #include <sys/param.h>  /* MIN MAX */
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/poll.h>  #include <sys/poll.h>
 #include <sys/queue.h>  #include <sys/queue.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #include <sys/time.h>  #include <sys/time.h>
 #include <sys/param.h>  
 #include <sys/statvfs.h>  #include <sys/statvfs.h>
 #include <sys/uio.h>  #include <sys/uio.h>
   
Line 38  __RCSID("$NetBSD$");
Line 39  __RCSID("$NetBSD$");
 #include <signal.h>  #include <signal.h>
 #include <stdarg.h>  #include <stdarg.h>
 #include <stdio.h>  #include <stdio.h>
   #include <stdlib.h>
 #include <string.h>  #include <string.h>
 #include <unistd.h>  #include <unistd.h>
   
 #include "xmalloc.h"  #include "xmalloc.h"
 #include "buffer.h"  #include "ssherr.h"
   #include "sshbuf.h"
 #include "log.h"  #include "log.h"
 #include "atomicio.h"  #include "atomicio.h"
 #include "progressmeter.h"  #include "progressmeter.h"
 #include "misc.h"  #include "misc.h"
   #include "utf8.h"
   
 #include "sftp.h"  #include "sftp.h"
 #include "sftp-common.h"  #include "sftp-common.h"
Line 72  struct sftp_conn {
Line 76  struct sftp_conn {
 #define SFTP_EXT_STATVFS        0x00000002  #define SFTP_EXT_STATVFS        0x00000002
 #define SFTP_EXT_FSTATVFS       0x00000004  #define SFTP_EXT_FSTATVFS       0x00000004
 #define SFTP_EXT_HARDLINK       0x00000008  #define SFTP_EXT_HARDLINK       0x00000008
   #define SFTP_EXT_FSYNC          0x00000010
         u_int exts;          u_int exts;
         u_int64_t limit_kbps;          u_int64_t limit_kbps;
         struct bwlimit bwlimit_in, bwlimit_out;          struct bwlimit bwlimit_in, bwlimit_out;
 };  };
   
 static char *  static u_char *
 get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,  get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
     const char *errfmt, ...) __attribute__((format(printf, 4, 5)));      const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
   
 /* ARGSUSED */  /* ARGSUSED */
Line 92  sftpio(void *_bwlimit, size_t amount)
Line 97  sftpio(void *_bwlimit, size_t amount)
 }  }
   
 static void  static void
 send_msg(struct sftp_conn *conn, Buffer *m)  send_msg(struct sftp_conn *conn, struct sshbuf *m)
 {  {
         u_char mlen[4];          u_char mlen[4];
         struct iovec iov[2];          struct iovec iov[2];
   
         if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)          if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH)
                 fatal("Outbound message too long %u", buffer_len(m));                  fatal("Outbound message too long %zu", sshbuf_len(m));
   
         /* Send length first */          /* Send length first */
         put_u32(mlen, buffer_len(m));          put_u32(mlen, sshbuf_len(m));
         iov[0].iov_base = mlen;          iov[0].iov_base = mlen;
         iov[0].iov_len = sizeof(mlen);          iov[0].iov_len = sizeof(mlen);
         iov[1].iov_base = buffer_ptr(m);          iov[1].iov_base = __UNCONST(sshbuf_ptr(m));
         iov[1].iov_len = buffer_len(m);          iov[1].iov_len = sshbuf_len(m);
   
         if (atomiciov6(writev, conn->fd_out, iov, 2,          if (atomiciov6(writev, conn->fd_out, iov, 2,
             conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=              conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
             buffer_len(m) + sizeof(mlen))              sshbuf_len(m) + sizeof(mlen))
                 fatal("Couldn't send packet: %s", strerror(errno));                  fatal("Couldn't send packet: %s", strerror(errno));
   
         buffer_clear(m);          sshbuf_reset(m);
 }  }
   
 static void  static void
 get_msg(struct sftp_conn *conn, Buffer *m)  get_msg(struct sftp_conn *conn, struct sshbuf *m)
 {  {
         u_int msg_len;          u_int msg_len;
           u_char *p;
           int r;
   
         buffer_append_space(m, 4);          if ((r = sshbuf_reserve(m, 4, &p)) != 0)
         if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           if (atomicio6(read, conn->fd_in, p, 4,
             conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {              conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
                 if (errno == EPIPE)                  if (errno == EPIPE)
                         fatal("Connection closed");                          fatal("Connection closed");
Line 129  get_msg(struct sftp_conn *conn, Buffer *
Line 137  get_msg(struct sftp_conn *conn, Buffer *
                         fatal("Couldn't read packet: %s", strerror(errno));                          fatal("Couldn't read packet: %s", strerror(errno));
         }          }
   
         msg_len = buffer_get_int(m);          if ((r = sshbuf_get_u32(m, &msg_len)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
         if (msg_len > SFTP_MAX_MSG_LENGTH)          if (msg_len > SFTP_MAX_MSG_LENGTH)
                 fatal("Received message too long %u", msg_len);                  fatal("Received message too long %u", msg_len);
   
         buffer_append_space(m, msg_len);          if ((r = sshbuf_reserve(m, msg_len, &p)) != 0)
         if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           if (atomicio6(read, conn->fd_in, p, msg_len,
             conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)              conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
             != msg_len) {              != msg_len) {
                 if (errno == EPIPE)                  if (errno == EPIPE)
Line 148  static void
Line 158  static void
 send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s,  send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s,
     u_int len)      u_int len)
 {  {
         Buffer msg;          struct sshbuf *msg;
           int r;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         buffer_put_char(&msg, code);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_int(&msg, id);          if ((r = sshbuf_put_u8(msg, code)) != 0 ||
         buffer_put_string(&msg, s, len);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         send_msg(conn, &msg);              (r = sshbuf_put_string(msg, s, len)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);          debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
         buffer_free(&msg);          sshbuf_free(msg);
 }  }
   
 static void  static void
 send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,  send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
     char *s, u_int len, Attrib *a)      const void *s, u_int len, Attrib *a)
 {  {
         Buffer msg;          struct sshbuf *msg;
           int r;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         buffer_put_char(&msg, code);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_int(&msg, id);          if ((r = sshbuf_put_u8(msg, code)) != 0 ||
         buffer_put_string(&msg, s, len);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         encode_attrib(&msg, a);              (r = sshbuf_put_string(msg, s, len)) != 0 ||
         send_msg(conn, &msg);              (r = encode_attrib(msg, a)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);          debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
         buffer_free(&msg);          sshbuf_free(msg);
 }  }
   
 static u_int  static u_int
 get_status(struct sftp_conn *conn, u_int expected_id)  get_status(struct sftp_conn *conn, u_int expected_id)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int type, id, status;          u_char type;
           u_int id, status;
           int r;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         get_msg(conn, &msg);                  fatal("%s: sshbuf_new failed", __func__);
         type = buffer_get_char(&msg);          get_msg(conn, msg);
         id = buffer_get_int(&msg);          if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
               (r = sshbuf_get_u32(msg, &id)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         if (id != expected_id)          if (id != expected_id)
                 fatal("ID mismatch (%u != %u)", id, expected_id);                  fatal("ID mismatch (%u != %u)", id, expected_id);
Line 192  get_status(struct sftp_conn *conn, u_int
Line 212  get_status(struct sftp_conn *conn, u_int
                 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",                  fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
                     SSH2_FXP_STATUS, type);                      SSH2_FXP_STATUS, type);
   
         status = buffer_get_int(&msg);          if ((r = sshbuf_get_u32(msg, &status)) != 0)
         buffer_free(&msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           sshbuf_free(msg);
   
         debug3("SSH2_FXP_STATUS %u", status);          debug3("SSH2_FXP_STATUS %u", status);
   
         return status;          return status;
 }  }
   
 static char *  static u_char *
 get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,  get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
     const char *errfmt, ...)      const char *errfmt, ...)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int type, id;          u_int id, status;
         char *handle, errmsg[256];          u_char type;
           u_char *handle;
           char errmsg[256];
         va_list args;          va_list args;
         int status;          int r;
   
         va_start(args, errfmt);          va_start(args, errfmt);
         if (errfmt != NULL)          if (errfmt != NULL)
                 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);                  vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
         va_end(args);          va_end(args);
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         get_msg(conn, &msg);                  fatal("%s: sshbuf_new failed", __func__);
         type = buffer_get_char(&msg);          get_msg(conn, msg);
         id = buffer_get_int(&msg);          if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
               (r = sshbuf_get_u32(msg, &id)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         if (id != expected_id)          if (id != expected_id)
                 fatal("%s: ID mismatch (%u != %u)",                  fatal("%s: ID mismatch (%u != %u)",
                     errfmt == NULL ? __func__ : errmsg, id, expected_id);                      errfmt == NULL ? __func__ : errmsg, id, expected_id);
         if (type == SSH2_FXP_STATUS) {          if (type == SSH2_FXP_STATUS) {
                 status = buffer_get_int(&msg);                  if ((r = sshbuf_get_u32(msg, &status)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                 if (errfmt != NULL)                  if (errfmt != NULL)
                         error("%s: %s", errmsg, fx2txt(status));                          error("%s: %s", errmsg, fx2txt(status));
                 buffer_free(&msg);                  sshbuf_free(msg);
                 return(NULL);                  return(NULL);
         } else if (type != SSH2_FXP_HANDLE)          } else if (type != SSH2_FXP_HANDLE)
                 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",                  fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
                     errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);                      errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
   
         handle = buffer_get_string(&msg, len);          if ((r = sshbuf_get_string(msg, &handle, len)) != 0)
         buffer_free(&msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           sshbuf_free(msg);
   
         return(handle);          return handle;
 }  }
   
 static Attrib *  static Attrib *
 get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)  get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int type, id;          u_int id;
         Attrib *a;          u_char type;
           int r;
         buffer_init(&msg);          static Attrib a;
         get_msg(conn, &msg);  
           if ((msg = sshbuf_new()) == NULL)
         type = buffer_get_char(&msg);                  fatal("%s: sshbuf_new failed", __func__);
         id = buffer_get_int(&msg);          get_msg(conn, msg);
   
           if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
               (r = sshbuf_get_u32(msg, &id)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         debug3("Received stat reply T:%u I:%u", type, id);          debug3("Received stat reply T:%u I:%u", type, id);
         if (id != expected_id)          if (id != expected_id)
                 fatal("ID mismatch (%u != %u)", id, expected_id);                  fatal("ID mismatch (%u != %u)", id, expected_id);
         if (type == SSH2_FXP_STATUS) {          if (type == SSH2_FXP_STATUS) {
                 int status = buffer_get_int(&msg);                  u_int status;
   
                   if ((r = sshbuf_get_u32(msg, &status)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                 if (quiet)                  if (quiet)
                         debug("Couldn't stat remote file: %s", fx2txt(status));                          debug("Couldn't stat remote file: %s", fx2txt(status));
                 else                  else
                         error("Couldn't stat remote file: %s", fx2txt(status));                          error("Couldn't stat remote file: %s", fx2txt(status));
                 buffer_free(&msg);                  sshbuf_free(msg);
                 return(NULL);                  return(NULL);
         } else if (type != SSH2_FXP_ATTRS) {          } else if (type != SSH2_FXP_ATTRS) {
                 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",                  fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
                     SSH2_FXP_ATTRS, type);                      SSH2_FXP_ATTRS, type);
         }          }
         a = decode_attrib(&msg);          if ((r = decode_attrib(msg, &a)) != 0) {
         buffer_free(&msg);                  error("%s: couldn't decode attrib: %s", __func__, ssh_err(r));
                   sshbuf_free(msg);
                   return NULL;
           }
           sshbuf_free(msg);
   
         return(a);          return &a;
 }  }
   
 static int  static int
 get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,  get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
     u_int expected_id, int quiet)      u_int expected_id, int quiet)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int type, id, flag;          u_char type;
           u_int id;
         buffer_init(&msg);          u_int64_t flag;
         get_msg(conn, &msg);          int r;
   
         type = buffer_get_char(&msg);          if ((msg = sshbuf_new()) == NULL)
         id = buffer_get_int(&msg);                  fatal("%s: sshbuf_new failed", __func__);
           get_msg(conn, msg);
   
           if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
               (r = sshbuf_get_u32(msg, &id)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         debug3("Received statvfs reply T:%u I:%u", type, id);          debug3("Received statvfs reply T:%u I:%u", type, id);
         if (id != expected_id)          if (id != expected_id)
                 fatal("ID mismatch (%u != %u)", id, expected_id);                  fatal("ID mismatch (%u != %u)", id, expected_id);
         if (type == SSH2_FXP_STATUS) {          if (type == SSH2_FXP_STATUS) {
                 int status = buffer_get_int(&msg);                  u_int status;
   
                   if ((r = sshbuf_get_u32(msg, &status)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                 if (quiet)                  if (quiet)
                         debug("Couldn't statvfs: %s", fx2txt(status));                          debug("Couldn't statvfs: %s", fx2txt(status));
                 else                  else
                         error("Couldn't statvfs: %s", fx2txt(status));                          error("Couldn't statvfs: %s", fx2txt(status));
                 buffer_free(&msg);                  sshbuf_free(msg);
                 return -1;                  return -1;
         } else if (type != SSH2_FXP_EXTENDED_REPLY) {          } else if (type != SSH2_FXP_EXTENDED_REPLY) {
                 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",                  fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
                     SSH2_FXP_EXTENDED_REPLY, type);                      SSH2_FXP_EXTENDED_REPLY, type);
         }          }
   
         bzero(st, sizeof(*st));          memset(st, 0, sizeof(*st));
         st->f_bsize = buffer_get_int64(&msg);          if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 ||
         st->f_frsize = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 ||
         st->f_blocks = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 ||
         st->f_bfree = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 ||
         st->f_bavail = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 ||
         st->f_files = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_files)) != 0 ||
         st->f_ffree = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 ||
         st->f_favail = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 ||
         st->f_fsid = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 ||
         flag = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &flag)) != 0 ||
         st->f_namemax = buffer_get_int64(&msg);              (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;          st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
         st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;          st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
   
         buffer_free(&msg);          sshbuf_free(msg);
   
         return 0;          return 0;
 }  }
Line 329  struct sftp_conn *
Line 374  struct sftp_conn *
 do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,  do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
     u_int64_t limit_kbps)      u_int64_t limit_kbps)
 {  {
         u_int type;          u_char type;
         Buffer msg;          struct sshbuf *msg;
         struct sftp_conn *ret;          struct sftp_conn *ret;
           int r;
   
         ret = xmalloc(sizeof(*ret));          ret = xcalloc(1, sizeof(*ret));
           ret->msg_id = 1;
         ret->fd_in = fd_in;          ret->fd_in = fd_in;
         ret->fd_out = fd_out;          ret->fd_out = fd_out;
         ret->transfer_buflen = transfer_buflen;          ret->transfer_buflen = transfer_buflen;
Line 341  do_init(int fd_in, int fd_out, u_int tra
Line 388  do_init(int fd_in, int fd_out, u_int tra
         ret->exts = 0;          ret->exts = 0;
         ret->limit_kbps = 0;          ret->limit_kbps = 0;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         buffer_put_char(&msg, SSH2_FXP_INIT);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_int(&msg, SSH2_FILEXFER_VERSION);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 ||
         send_msg(ret, &msg);              (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(ret, msg);
   
         buffer_clear(&msg);          sshbuf_reset(msg);
   
         get_msg(ret, &msg);          get_msg(ret, msg);
   
         /* Expecting a VERSION reply */          /* Expecting a VERSION reply */
         if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {          if ((r = sshbuf_get_u8(msg, &type)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           if (type != SSH2_FXP_VERSION) {
                 error("Invalid packet back from SSH2_FXP_INIT (type %u)",                  error("Invalid packet back from SSH2_FXP_INIT (type %u)",
                     type);                      type);
                 buffer_free(&msg);                  sshbuf_free(msg);
                   free(ret);
                 return(NULL);                  return(NULL);
         }          }
         ret->version = buffer_get_int(&msg);          if ((r = sshbuf_get_u32(msg, &ret->version)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         debug2("Remote version: %u", ret->version);          debug2("Remote version: %u", ret->version);
   
         /* Check for extensions */          /* Check for extensions */
         while (buffer_len(&msg) > 0) {          while (sshbuf_len(msg) > 0) {
                 char *name = buffer_get_string(&msg, NULL);                  char *name;
                 char *value = buffer_get_string(&msg, NULL);                  u_char *value;
                   size_t vlen;
                 int known = 0;                  int known = 0;
   
                   if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 ||
                       (r = sshbuf_get_string(msg, &value, &vlen)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                 if (strcmp(name, "posix-rename@openssh.com") == 0 &&                  if (strcmp(name, "posix-rename@openssh.com") == 0 &&
                     strcmp(value, "1") == 0) {                      strcmp((char *)value, "1") == 0) {
                         ret->exts |= SFTP_EXT_POSIX_RENAME;                          ret->exts |= SFTP_EXT_POSIX_RENAME;
                         known = 1;                          known = 1;
                 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&                  } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
                     strcmp(value, "2") == 0) {                      strcmp((char *)value, "2") == 0) {
                         ret->exts |= SFTP_EXT_STATVFS;                          ret->exts |= SFTP_EXT_STATVFS;
                         known = 1;                          known = 1;
                 } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&                  } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
                     strcmp(value, "2") == 0) {                      strcmp((char *)value, "2") == 0) {
                         ret->exts |= SFTP_EXT_FSTATVFS;                          ret->exts |= SFTP_EXT_FSTATVFS;
                         known = 1;                          known = 1;
                 } else if (strcmp(name, "hardlink@openssh.com") == 0 &&                  } else if (strcmp(name, "hardlink@openssh.com") == 0 &&
                     strcmp(value, "1") == 0) {                      strcmp((char *)value, "1") == 0) {
                         ret->exts |= SFTP_EXT_HARDLINK;                          ret->exts |= SFTP_EXT_HARDLINK;
                         known = 1;                          known = 1;
                   } else if (strcmp(name, "fsync@openssh.com") == 0 &&
                       strcmp((char *)value, "1") == 0) {
                           ret->exts |= SFTP_EXT_FSYNC;
                           known = 1;
                 }                  }
                 if (known) {                  if (known) {
                         debug2("Server supports extension \"%s\" revision %s",                          debug2("Server supports extension \"%s\" revision %s",
Line 390  do_init(int fd_in, int fd_out, u_int tra
Line 451  do_init(int fd_in, int fd_out, u_int tra
                 } else {                  } else {
                         debug2("Unrecognised server extension \"%s\"", name);                          debug2("Unrecognised server extension \"%s\"", name);
                 }                  }
                 xfree(name);                  free(name);
                 xfree(value);                  free(value);
         }          }
   
         buffer_free(&msg);          sshbuf_free(msg);
   
         /* Some filexfer v.0 servers don't support large packets */          /* Some filexfer v.0 servers don't support large packets */
         if (ret->version == 0)          if (ret->version == 0)
                 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);                  ret->transfer_buflen = MINIMUM(ret->transfer_buflen, 20480);
   
         ret->limit_kbps = limit_kbps;          ret->limit_kbps = limit_kbps;
         if (ret->limit_kbps > 0) {          if (ret->limit_kbps > 0) {
Line 418  sftp_proto_version(struct sftp_conn *con
Line 479  sftp_proto_version(struct sftp_conn *con
 }  }
   
 int  int
 do_close(struct sftp_conn *conn, char *handle, u_int handle_len)  do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len)
 {  {
         u_int id, status;          u_int id, status;
         Buffer msg;          struct sshbuf *msg;
           int r;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
         id = conn->msg_id++;          id = conn->msg_id++;
         buffer_put_char(&msg, SSH2_FXP_CLOSE);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 ||
         buffer_put_int(&msg, id);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_string(&msg, handle, handle_len);              (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
         send_msg(conn, &msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message SSH2_FXP_CLOSE I:%u", id);          debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
   
         status = get_status(conn, id);          status = get_status(conn, id);
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't close file: %s", fx2txt(status));                  error("Couldn't close file: %s", fx2txt(status));
   
         buffer_free(&msg);          sshbuf_free(msg);
   
         return status;          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
   
 static int  static int
 do_lsreaddir(struct sftp_conn *conn, const char *path, int printflag,  do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
     SFTP_DIRENT ***dir)      SFTP_DIRENT ***dir)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int count, type, id, handle_len, i, expected_id, ents = 0;          u_int count, id, i, expected_id, ents = 0;
         char *handle;          size_t handle_len;
           u_char type, *handle;
           int status = SSH2_FX_FAILURE;
           int r;
   
         id = conn->msg_id++;          if (dir)
                   *dir = NULL;
   
         buffer_init(&msg);          id = conn->msg_id++;
         buffer_put_char(&msg, SSH2_FXP_OPENDIR);  
         buffer_put_int(&msg, id);  
         buffer_put_cstring(&msg, path);  
         send_msg(conn, &msg);  
   
         buffer_clear(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
           if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 ||
               (r = sshbuf_put_u32(msg, id)) != 0 ||
               (r = sshbuf_put_cstring(msg, path)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
   
         handle = get_handle(conn, id, &handle_len,          handle = get_handle(conn, id, &handle_len,
             "remote readdir(\"%s\")", path);              "remote readdir(\"%s\")", path);
         if (handle == NULL)          if (handle == NULL) {
                   sshbuf_free(msg);
                 return -1;                  return -1;
           }
   
         if (dir) {          if (dir) {
                 ents = 0;                  ents = 0;
                 *dir = xmalloc(sizeof(**dir));                  *dir = xcalloc(1, sizeof(**dir));
                 (*dir)[0] = NULL;                  (*dir)[0] = NULL;
         }          }
   
Line 476  do_lsreaddir(struct sftp_conn *conn, con
Line 548  do_lsreaddir(struct sftp_conn *conn, con
   
                 debug3("Sending SSH2_FXP_READDIR I:%u", id);                  debug3("Sending SSH2_FXP_READDIR I:%u", id);
   
                 buffer_clear(&msg);                  sshbuf_reset(msg);
                 buffer_put_char(&msg, SSH2_FXP_READDIR);                  if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 ||
                 buffer_put_int(&msg, id);                      (r = sshbuf_put_u32(msg, id)) != 0 ||
                 buffer_put_string(&msg, handle, handle_len);                      (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
                 send_msg(conn, &msg);                          fatal("%s: buffer error: %s", __func__, ssh_err(r));
                   send_msg(conn, msg);
                 buffer_clear(&msg);  
                   sshbuf_reset(msg);
                 get_msg(conn, &msg);  
                   get_msg(conn, msg);
                 type = buffer_get_char(&msg);  
                 id = buffer_get_int(&msg);                  if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
                       (r = sshbuf_get_u32(msg, &id)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
                 debug3("Received reply T:%u I:%u", type, id);                  debug3("Received reply T:%u I:%u", type, id);
   
Line 495  do_lsreaddir(struct sftp_conn *conn, con
Line 569  do_lsreaddir(struct sftp_conn *conn, con
                         fatal("ID mismatch (%u != %u)", id, expected_id);                          fatal("ID mismatch (%u != %u)", id, expected_id);
   
                 if (type == SSH2_FXP_STATUS) {                  if (type == SSH2_FXP_STATUS) {
                         int status = buffer_get_int(&msg);                          u_int rstatus;
   
                         debug3("Received SSH2_FXP_STATUS %d", status);  
   
                         if (status == SSH2_FX_EOF) {                          if ((r = sshbuf_get_u32(msg, &rstatus)) != 0)
                                   fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
                           debug3("Received SSH2_FXP_STATUS %d", rstatus);
                           if (rstatus == SSH2_FX_EOF)
                                 break;                                  break;
                         } else {                          error("Couldn't read directory: %s", fx2txt(rstatus));
                                 error("Couldn't read directory: %s",                          goto out;
                                     fx2txt(status));  
                                 do_close(conn, handle, handle_len);  
                                 xfree(handle);  
                                 return(status);  
                         }  
                 } else if (type != SSH2_FXP_NAME)                  } else if (type != SSH2_FXP_NAME)
                         fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",                          fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
                             SSH2_FXP_NAME, type);                              SSH2_FXP_NAME, type);
   
                 count = buffer_get_int(&msg);                  if ((r = sshbuf_get_u32(msg, &count)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                   if (count > SSHBUF_SIZE_MAX)
                           fatal("%s: nonsensical number of entries", __func__);
                 if (count == 0)                  if (count == 0)
                         break;                          break;
                 debug3("Received %d SSH2_FXP_NAME responses", count);                  debug3("Received %d SSH2_FXP_NAME responses", count);
                 for (i = 0; i < count; i++) {                  for (i = 0; i < count; i++) {
                         char *filename, *longname;                          char *filename, *longname;
                         Attrib *a;                          Attrib a;
   
                         filename = buffer_get_string(&msg, NULL);                          if ((r = sshbuf_get_cstring(msg, &filename,
                         longname = buffer_get_string(&msg, NULL);                              NULL)) != 0 ||
                         a = decode_attrib(&msg);                              (r = sshbuf_get_cstring(msg, &longname,
                               NULL)) != 0)
                                   fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
                           if ((r = decode_attrib(msg, &a)) != 0) {
                                   error("%s: couldn't decode attrib: %s",
                                       __func__, ssh_err(r));
                                   free(filename);
                                   free(longname);
                                   sshbuf_free(msg);
                                   return -1;
                           }
   
                         if (printflag)                          if (print_flag)
                                 printf("%s\n", longname);                                  mprintf("%s\n", longname);
   
                         /*                          /*
                          * Directory entries should never contain '/'                           * Directory entries should never contain '/'
Line 535  do_lsreaddir(struct sftp_conn *conn, con
Line 620  do_lsreaddir(struct sftp_conn *conn, con
                         if (strchr(filename, '/') != NULL) {                          if (strchr(filename, '/') != NULL) {
                                 error("Server sent suspect path \"%s\" "                                  error("Server sent suspect path \"%s\" "
                                     "during readdir of \"%s\"", filename, path);                                      "during readdir of \"%s\"", filename, path);
                                 goto next;                          } else if (dir) {
                         }                                  *dir = xreallocarray(*dir, ents + 2, sizeof(**dir));
                                   (*dir)[ents] = xcalloc(1, sizeof(***dir));
                         if (dir) {  
                                 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));  
                                 (*dir)[ents] = xmalloc(sizeof(***dir));  
                                 (*dir)[ents]->filename = xstrdup(filename);                                  (*dir)[ents]->filename = xstrdup(filename);
                                 (*dir)[ents]->longname = xstrdup(longname);                                  (*dir)[ents]->longname = xstrdup(longname);
                                 memcpy(&(*dir)[ents]->a, a, sizeof(*a));                                  memcpy(&(*dir)[ents]->a, &a, sizeof(a));
                                 (*dir)[++ents] = NULL;                                  (*dir)[++ents] = NULL;
                         }                          }
  next:                          free(filename);
                         xfree(filename);                          free(longname);
                         xfree(longname);  
                 }                  }
         }          }
           status = 0;
   
         buffer_free(&msg);   out:
           sshbuf_free(msg);
         do_close(conn, handle, handle_len);          do_close(conn, handle, handle_len);
         xfree(handle);          free(handle);
   
         /* Don't return partial matches on interrupt */          if (status != 0 && dir != NULL) {
         if (interrupted && dir != NULL && *dir != NULL) {                  /* Don't return results on error */
                   free_sftp_dirents(*dir);
                   *dir = NULL;
           } else if (interrupted && dir != NULL && *dir != NULL) {
                   /* Don't return partial matches on interrupt */
                 free_sftp_dirents(*dir);                  free_sftp_dirents(*dir);
                 *dir = xmalloc(sizeof(**dir));                  *dir = xcalloc(1, sizeof(**dir));
                 **dir = NULL;                  **dir = NULL;
         }          }
   
         return 0;          return status;
 }  }
   
 int  int
Line 576  void free_sftp_dirents(SFTP_DIRENT **s)
Line 663  void free_sftp_dirents(SFTP_DIRENT **s)
 {  {
         int i;          int i;
   
           if (s == NULL)
                   return;
         for (i = 0; s[i]; i++) {          for (i = 0; s[i]; i++) {
                 xfree(s[i]->filename);                  free(s[i]->filename);
                 xfree(s[i]->longname);                  free(s[i]->longname);
                 xfree(s[i]);                  free(s[i]);
         }          }
         xfree(s);          free(s);
 }  }
   
 int  int
 do_rm(struct sftp_conn *conn, char *path)  do_rm(struct sftp_conn *conn, const char *path)
 {  {
         u_int status, id;          u_int status, id;
   
Line 596  do_rm(struct sftp_conn *conn, char *path
Line 685  do_rm(struct sftp_conn *conn, char *path
         status = get_status(conn, id);          status = get_status(conn, id);
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't delete file: %s", fx2txt(status));                  error("Couldn't delete file: %s", fx2txt(status));
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 int  int
 do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)  do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
 {  {
         u_int status, id;          u_int status, id;
   
Line 609  do_mkdir(struct sftp_conn *conn, char *p
Line 698  do_mkdir(struct sftp_conn *conn, char *p
             strlen(path), a);              strlen(path), a);
   
         status = get_status(conn, id);          status = get_status(conn, id);
         if (status != SSH2_FX_OK && printflag)          if (status != SSH2_FX_OK && print_flag)
                 error("Couldn't create directory: %s", fx2txt(status));                  error("Couldn't create directory: %s", fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 int  int
 do_rmdir(struct sftp_conn *conn, char *path)  do_rmdir(struct sftp_conn *conn, const char *path)
 {  {
         u_int status, id;          u_int status, id;
   
Line 628  do_rmdir(struct sftp_conn *conn, char *p
Line 717  do_rmdir(struct sftp_conn *conn, char *p
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't remove directory: %s", fx2txt(status));                  error("Couldn't remove directory: %s", fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 Attrib *  Attrib *
Line 667  do_lstat(struct sftp_conn *conn, const c
Line 756  do_lstat(struct sftp_conn *conn, const c
   
 #ifdef notyet  #ifdef notyet
 Attrib *  Attrib *
 do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)  do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
       int quiet)
 {  {
         u_int id;          u_int id;
   
Line 680  do_fstat(struct sftp_conn *conn, char *h
Line 770  do_fstat(struct sftp_conn *conn, char *h
 #endif  #endif
   
 int  int
 do_setstat(struct sftp_conn *conn, char *path, Attrib *a)  do_setstat(struct sftp_conn *conn, const char *path, Attrib *a)
 {  {
         u_int status, id;          u_int status, id;
   
Line 693  do_setstat(struct sftp_conn *conn, char 
Line 783  do_setstat(struct sftp_conn *conn, char 
                 error("Couldn't setstat on \"%s\": %s", path,                  error("Couldn't setstat on \"%s\": %s", path,
                     fx2txt(status));                      fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 int  int
 do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,  do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
     Attrib *a)      Attrib *a)
 {  {
         u_int status, id;          u_int status, id;
Line 710  do_fsetstat(struct sftp_conn *conn, char
Line 800  do_fsetstat(struct sftp_conn *conn, char
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't fsetstat: %s", fx2txt(status));                  error("Couldn't fsetstat: %s", fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 char *  char *
 do_realpath(struct sftp_conn *conn, const char *path)  do_realpath(struct sftp_conn *conn, const char *path)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int type, expected_id, count, id;          u_int expected_id, count, id;
         char *filename, *longname;          char *filename, *longname;
         Attrib *a;          Attrib a;
           u_char type;
           int r;
   
         expected_id = id = conn->msg_id++;          expected_id = id = conn->msg_id++;
         send_string_request(conn, id, SSH2_FXP_REALPATH, path,          send_string_request(conn, id, SSH2_FXP_REALPATH, path,
             strlen(path));              strlen(path));
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
         get_msg(conn, &msg);          get_msg(conn, msg);
         type = buffer_get_char(&msg);          if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
         id = buffer_get_int(&msg);              (r = sshbuf_get_u32(msg, &id)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         if (id != expected_id)          if (id != expected_id)
                 fatal("ID mismatch (%u != %u)", id, expected_id);                  fatal("ID mismatch (%u != %u)", id, expected_id);
   
         if (type == SSH2_FXP_STATUS) {          if (type == SSH2_FXP_STATUS) {
                 u_int status = buffer_get_int(&msg);                  u_int status;
   
                 error("Couldn't canonicalise: %s", fx2txt(status));                  if ((r = sshbuf_get_u32(msg, &status)) != 0)
                 buffer_free(&msg);                          fatal("%s: buffer error: %s", __func__, ssh_err(r));
                   error("Couldn't canonicalize: %s", fx2txt(status));
                   sshbuf_free(msg);
                 return NULL;                  return NULL;
         } else if (type != SSH2_FXP_NAME)          } else if (type != SSH2_FXP_NAME)
                 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",                  fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
                     SSH2_FXP_NAME, type);                      SSH2_FXP_NAME, type);
   
         count = buffer_get_int(&msg);          if ((r = sshbuf_get_u32(msg, &count)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
         if (count != 1)          if (count != 1)
                 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);                  fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
   
         filename = buffer_get_string(&msg, NULL);          if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
         longname = buffer_get_string(&msg, NULL);              (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
         a = decode_attrib(&msg);              (r = decode_attrib(msg, &a)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         debug3("SSH_FXP_REALPATH %s -> %s", path, filename);          debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
               (unsigned long)a.size);
   
         xfree(longname);          free(longname);
   
         buffer_free(&msg);          sshbuf_free(msg);
   
         return(filename);          return(filename);
 }  }
   
 int  int
 do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)  do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
       int force_legacy)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int status, id;          u_int status, id;
           int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
         /* Send rename request */          /* Send rename request */
         id = conn->msg_id++;          id = conn->msg_id++;
         if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {          if (use_ext) {
                 buffer_put_char(&msg, SSH2_FXP_EXTENDED);                  if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
                 buffer_put_int(&msg, id);                      (r = sshbuf_put_u32(msg, id)) != 0 ||
                 buffer_put_cstring(&msg, "posix-rename@openssh.com");                      (r = sshbuf_put_cstring(msg,
                       "posix-rename@openssh.com")) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
         } else {          } else {
                 buffer_put_char(&msg, SSH2_FXP_RENAME);                  if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 ||
                 buffer_put_int(&msg, id);                      (r = sshbuf_put_u32(msg, id)) != 0)
         }                          fatal("%s: buffer error: %s", __func__, ssh_err(r));
         buffer_put_cstring(&msg, oldpath);          }
         buffer_put_cstring(&msg, newpath);          if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
         send_msg(conn, &msg);              (r = sshbuf_put_cstring(msg, newpath)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message %s \"%s\" -> \"%s\"",          debug3("Sent message %s \"%s\" -> \"%s\"",
             (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :              use_ext ? "posix-rename@openssh.com" :
             "SSH2_FXP_RENAME", oldpath, newpath);              "SSH2_FXP_RENAME", oldpath, newpath);
         buffer_free(&msg);          sshbuf_free(msg);
   
         status = get_status(conn, id);          status = get_status(conn, id);
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,                  error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
                     newpath, fx2txt(status));                      newpath, fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 int  int
 do_hardlink(struct sftp_conn *conn, char *oldpath, char *newpath)  do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int status, id;          u_int status, id;
           int r;
   
         buffer_init(&msg);  
   
         /* Send link request */  
         id = conn->msg_id++;  
         if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {          if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
                 error("Server does not support hardlink@openssh.com extension");                  error("Server does not support hardlink@openssh.com extension");
                 return -1;                  return -1;
         }          }
   
         buffer_put_char(&msg, SSH2_FXP_EXTENDED);          if ((msg = sshbuf_new()) == NULL)
         buffer_put_int(&msg, id);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_cstring(&msg, "hardlink@openssh.com");  
         buffer_put_cstring(&msg, oldpath);          /* Send link request */
         buffer_put_cstring(&msg, newpath);          id = conn->msg_id++;
         send_msg(conn, &msg);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
               (r = sshbuf_put_u32(msg, id)) != 0 ||
               (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 ||
               (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
               (r = sshbuf_put_cstring(msg, newpath)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",          debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
                oldpath, newpath);                 oldpath, newpath);
         buffer_free(&msg);          sshbuf_free(msg);
   
         status = get_status(conn, id);          status = get_status(conn, id);
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,                  error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
                     newpath, fx2txt(status));                      newpath, fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 int  int
 do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)  do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int status, id;          u_int status, id;
           int r;
   
         if (conn->version < 3) {          if (conn->version < 3) {
                 error("This server does not support the symlink operation");                  error("This server does not support the symlink operation");
                 return(SSH2_FX_OP_UNSUPPORTED);                  return(SSH2_FX_OP_UNSUPPORTED);
         }          }
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
         /* Send symlink request */          /* Send symlink request */
         id = conn->msg_id++;          id = conn->msg_id++;
         buffer_put_char(&msg, SSH2_FXP_SYMLINK);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 ||
         buffer_put_int(&msg, id);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_cstring(&msg, oldpath);              (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
         buffer_put_cstring(&msg, newpath);              (r = sshbuf_put_cstring(msg, newpath)) != 0)
         send_msg(conn, &msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,          debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
             newpath);              newpath);
         buffer_free(&msg);          sshbuf_free(msg);
   
         status = get_status(conn, id);          status = get_status(conn, id);
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK)
                 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,                  error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
                     newpath, fx2txt(status));                      newpath, fx2txt(status));
   
         return(status);          return status == SSH2_FX_OK ? 0 : -1;
   }
   
   int
   do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len)
   {
           struct sshbuf *msg;
           u_int status, id;
           int r;
   
           /* Silently return if the extension is not supported */
           if ((conn->exts & SFTP_EXT_FSYNC) == 0)
                   return -1;
   
           /* Send fsync request */
           if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
           id = conn->msg_id++;
           if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
               (r = sshbuf_put_u32(msg, id)) != 0 ||
               (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 ||
               (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
           debug3("Sent message fsync@openssh.com I:%u", id);
           sshbuf_free(msg);
   
           status = get_status(conn, id);
           if (status != SSH2_FX_OK)
                   error("Couldn't sync file: %s", fx2txt(status));
   
           return status;
 }  }
   
 #ifdef notyet  #ifdef notyet
 char *  char *
 do_readlink(struct sftp_conn *conn, char *path)  do_readlink(struct sftp_conn *conn, const char *path)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int type, expected_id, count, id;          u_int expected_id, count, id;
         char *filename, *longname;          char *filename, *longname;
         Attrib *a;          Attrib a;
           u_char type;
           int r;
   
         expected_id = id = conn->msg_id++;          expected_id = id = conn->msg_id++;
         send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));          send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
         get_msg(conn, &msg);          get_msg(conn, msg);
         type = buffer_get_char(&msg);          if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
         id = buffer_get_int(&msg);              (r = sshbuf_get_u32(msg, &id)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         if (id != expected_id)          if (id != expected_id)
                 fatal("ID mismatch (%u != %u)", id, expected_id);                  fatal("ID mismatch (%u != %u)", id, expected_id);
   
         if (type == SSH2_FXP_STATUS) {          if (type == SSH2_FXP_STATUS) {
                 u_int status = buffer_get_int(&msg);                  u_int status;
   
                   if ((r = sshbuf_get_u32(msg, &status)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                 error("Couldn't readlink: %s", fx2txt(status));                  error("Couldn't readlink: %s", fx2txt(status));
                   sshbuf_free(msg);
                 return(NULL);                  return(NULL);
         } else if (type != SSH2_FXP_NAME)          } else if (type != SSH2_FXP_NAME)
                 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",                  fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
                     SSH2_FXP_NAME, type);                      SSH2_FXP_NAME, type);
   
         count = buffer_get_int(&msg);          if ((r = sshbuf_get_u32(msg, &count)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
         if (count != 1)          if (count != 1)
                 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);                  fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
   
         filename = buffer_get_string(&msg, NULL);          if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
         longname = buffer_get_string(&msg, NULL);              (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
         a = decode_attrib(&msg);              (r = decode_attrib(msg, &a)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
   
         debug3("SSH_FXP_READLINK %s -> %s", path, filename);          debug3("SSH_FXP_READLINK %s -> %s", path, filename);
   
         xfree(longname);          free(longname);
   
         buffer_free(&msg);          sshbuf_free(msg);
   
         return(filename);          return filename;
 }  }
 #endif  #endif
   
Line 912  int
Line 1064  int
 do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,  do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
     int quiet)      int quiet)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int id;          u_int id;
           int r;
   
         if ((conn->exts & SFTP_EXT_STATVFS) == 0) {          if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
                 error("Server does not support statvfs@openssh.com extension");                  error("Server does not support statvfs@openssh.com extension");
Line 922  do_statvfs(struct sftp_conn *conn, const
Line 1075  do_statvfs(struct sftp_conn *conn, const
   
         id = conn->msg_id++;          id = conn->msg_id++;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         buffer_clear(&msg);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_char(&msg, SSH2_FXP_EXTENDED);          sshbuf_reset(msg);
         buffer_put_int(&msg, id);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
         buffer_put_cstring(&msg, "statvfs@openssh.com");              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_cstring(&msg, path);              (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 ||
         send_msg(conn, &msg);              (r = sshbuf_put_cstring(msg, path)) != 0)
         buffer_free(&msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
           sshbuf_free(msg);
   
         return get_decode_statvfs(conn, st, id, quiet);          return get_decode_statvfs(conn, st, id, quiet);
 }  }
   
 #ifdef notyet  #ifdef notyet
 int  int
 do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,  do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
     struct sftp_statvfs *st, int quiet)      struct sftp_statvfs *st, int quiet)
 {  {
         Buffer msg;          struct sshbuf *msg;
         u_int id;          u_int id;
   
         if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {          if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
Line 949  do_fstatvfs(struct sftp_conn *conn, cons
Line 1104  do_fstatvfs(struct sftp_conn *conn, cons
   
         id = conn->msg_id++;          id = conn->msg_id++;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         buffer_clear(&msg);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_char(&msg, SSH2_FXP_EXTENDED);          sshbuf_reset(msg);
         buffer_put_int(&msg, id);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
         buffer_put_cstring(&msg, "fstatvfs@openssh.com");              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_string(&msg, handle, handle_len);              (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 ||
         send_msg(conn, &msg);              (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
         buffer_free(&msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
           sshbuf_free(msg);
   
         return get_decode_statvfs(conn, st, id, quiet);          return get_decode_statvfs(conn, st, id, quiet);
 }  }
Line 964  do_fstatvfs(struct sftp_conn *conn, cons
Line 1121  do_fstatvfs(struct sftp_conn *conn, cons
   
 static void  static void
 send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,  send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
     u_int len, char *handle, u_int handle_len)      u_int len, const u_char *handle, u_int handle_len)
 {  {
         Buffer msg;          struct sshbuf *msg;
           int r;
   
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
         buffer_clear(&msg);                  fatal("%s: sshbuf_new failed", __func__);
         buffer_put_char(&msg, SSH2_FXP_READ);          sshbuf_reset(msg);
         buffer_put_int(&msg, id);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 ||
         buffer_put_string(&msg, handle, handle_len);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_int64(&msg, offset);              (r = sshbuf_put_string(msg, handle, handle_len)) != 0 ||
         buffer_put_int(&msg, len);              (r = sshbuf_put_u64(msg, offset)) != 0 ||
         send_msg(conn, &msg);              (r = sshbuf_put_u32(msg, len)) != 0)
         buffer_free(&msg);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
           sshbuf_free(msg);
 }  }
   
 int  int
 do_download(struct sftp_conn *conn, char *remote_path, char *local_path,  do_download(struct sftp_conn *conn, const char *remote_path,
     Attrib *a, int pflag)      const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
       int fsync_flag)
 {  {
         Attrib junk;          Attrib junk;
         Buffer msg;          struct sshbuf *msg;
         char *handle;          u_char *handle;
         int local_fd, status = 0, write_error;          int local_fd = -1, write_error;
         int read_error, write_errno;          int read_error, write_errno, reordered = 0, r;
         u_int64_t offset, size;          u_int64_t offset = 0, size, highwater;
         u_int handle_len, mode, type, id, buflen, num_req, max_req;          u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK;
         off_t progress_counter;          off_t progress_counter;
           size_t handle_len;
           struct stat st;
         struct request {          struct request {
                 u_int id;                  u_int id;
                 u_int len;                  size_t len;
                 u_int64_t offset;                  u_int64_t offset;
                 TAILQ_ENTRY(request) tq;                  TAILQ_ENTRY(request) tq;
         };          };
         TAILQ_HEAD(reqhead, request) requests;          TAILQ_HEAD(reqhead, request) requests;
         struct request *req;          struct request *req;
           u_char type;
   
         status = -1;          status = -1;
         TAILQ_INIT(&requests);          TAILQ_INIT(&requests);
Line 1024  do_download(struct sftp_conn *conn, char
Line 1188  do_download(struct sftp_conn *conn, char
                 size = 0;                  size = 0;
   
         buflen = conn->transfer_buflen;          buflen = conn->transfer_buflen;
         buffer_init(&msg);          if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
           attrib_clear(&junk); /* Send empty attributes */
   
         /* Send open request */          /* Send open request */
         id = conn->msg_id++;          id = conn->msg_id++;
         buffer_put_char(&msg, SSH2_FXP_OPEN);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
         buffer_put_int(&msg, id);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_cstring(&msg, remote_path);              (r = sshbuf_put_cstring(msg, remote_path)) != 0 ||
         buffer_put_int(&msg, SSH2_FXF_READ);              (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 ||
         attrib_clear(&junk); /* Send empty attributes */              (r = encode_attrib(msg, &junk)) != 0)
         encode_attrib(&msg, &junk);                  fatal("%s: buffer error: %s", __func__, ssh_err(r));
         send_msg(conn, &msg);          send_msg(conn, msg);
         debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);          debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
   
         handle = get_handle(conn, id, &handle_len,          handle = get_handle(conn, id, &handle_len,
             "remote open(\"%s\")", remote_path);              "remote open(\"%s\")", remote_path);
         if (handle == NULL) {          if (handle == NULL) {
                 buffer_free(&msg);                  sshbuf_free(msg);
                 return(-1);                  return(-1);
         }          }
   
         local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,          local_fd = open(local_path,
             mode | S_IWRITE);              O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
         if (local_fd == -1) {          if (local_fd == -1) {
                 error("Couldn't open local file \"%s\" for writing: %s",                  error("Couldn't open local file \"%s\" for writing: %s",
                     local_path, strerror(errno));                      local_path, strerror(errno));
                 do_close(conn, handle, handle_len);                  goto fail;
                 buffer_free(&msg);          }
                 xfree(handle);          offset = highwater = 0;
                 return(-1);          if (resume_flag) {
                   if (fstat(local_fd, &st) == -1) {
                           error("Unable to stat local file \"%s\": %s",
                               local_path, strerror(errno));
                           goto fail;
                   }
                   if (st.st_size < 0) {
                           error("\"%s\" has negative size", local_path);
                           goto fail;
                   }
                   if ((u_int64_t)st.st_size > size) {
                           error("Unable to resume download of \"%s\": "
                               "local file is larger than remote", local_path);
    fail:
                           do_close(conn, handle, handle_len);
                           sshbuf_free(msg);
                           free(handle);
                           if (local_fd != -1)
                                   close(local_fd);
                           return -1;
                   }
                   offset = highwater = st.st_size;
         }          }
   
         /* Read from remote and write to local */          /* Read from remote and write to local */
         write_error = read_error = write_errno = num_req = offset = 0;          write_error = read_error = write_errno = num_req = 0;
         max_req = 1;          max_req = 1;
         progress_counter = 0;          progress_counter = offset;
   
         if (showprogress && size != 0)          if (showprogress && size != 0)
                 start_progress_meter(remote_path, size, &progress_counter);                  start_progress_meter(remote_path, size, &progress_counter);
   
         while (num_req > 0 || max_req > 0) {          while (num_req > 0 || max_req > 0) {
                 char *data;                  u_char *data;
                 u_int len;                  size_t len;
   
                 /*                  /*
                  * Simulate EOF on interrupt: stop sending new requests and                   * Simulate EOF on interrupt: stop sending new requests and
Line 1083  do_download(struct sftp_conn *conn, char
Line 1271  do_download(struct sftp_conn *conn, char
                             (unsigned long long)offset,                              (unsigned long long)offset,
                             (unsigned long long)offset + buflen - 1,                              (unsigned long long)offset + buflen - 1,
                             num_req, max_req);                              num_req, max_req);
                         req = xmalloc(sizeof(*req));                          req = xcalloc(1, sizeof(*req));
                         req->id = conn->msg_id++;                          req->id = conn->msg_id++;
                         req->len = buflen;                          req->len = buflen;
                         req->offset = offset;                          req->offset = offset;
Line 1094  do_download(struct sftp_conn *conn, char
Line 1282  do_download(struct sftp_conn *conn, char
                             req->len, handle, handle_len);                              req->len, handle, handle_len);
                 }                  }
   
                 buffer_clear(&msg);                  sshbuf_reset(msg);
                 get_msg(conn, &msg);                  get_msg(conn, msg);
                 type = buffer_get_char(&msg);                  if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
                 id = buffer_get_int(&msg);                      (r = sshbuf_get_u32(msg, &id)) != 0)
                           fatal("%s: buffer error: %s", __func__, ssh_err(r));
                 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);                  debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
   
                 /* Find the request in our queue */                  /* Find the request in our queue */
Line 1110  do_download(struct sftp_conn *conn, char
Line 1299  do_download(struct sftp_conn *conn, char
   
                 switch (type) {                  switch (type) {
                 case SSH2_FXP_STATUS:                  case SSH2_FXP_STATUS:
                         status = buffer_get_int(&msg);                          if ((r = sshbuf_get_u32(msg, &status)) != 0)
                                   fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
                         if (status != SSH2_FX_EOF)                          if (status != SSH2_FX_EOF)
                                 read_error = 1;                                  read_error = 1;
                         max_req = 0;                          max_req = 0;
                         TAILQ_REMOVE(&requests, req, tq);                          TAILQ_REMOVE(&requests, req, tq);
                         xfree(req);                          free(req);
                         num_req--;                          num_req--;
                         break;                          break;
                 case SSH2_FXP_DATA:                  case SSH2_FXP_DATA:
                         data = buffer_get_string(&msg, &len);                          if ((r = sshbuf_get_string(msg, &data, &len)) != 0)
                                   fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
                         debug3("Received data %llu -> %llu",                          debug3("Received data %llu -> %llu",
                             (unsigned long long)req->offset,                              (unsigned long long)req->offset,
                             (unsigned long long)req->offset + len - 1);                              (unsigned long long)req->offset + len - 1);
                         if (len > req->len)                          if (len > req->len)
                                 fatal("Received more data than asked for "                                  fatal("Received more data than asked for "
                                     "%u > %u", len, req->len);                                      "%zu > %zu", len, req->len);
                         if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||                          if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
                             atomicio(vwrite, local_fd, data, len) != len) &&                              atomicio(vwrite, local_fd, data, len) != len) &&
                             !write_error) {                              !write_error) {
Line 1133  do_download(struct sftp_conn *conn, char
Line 1326  do_download(struct sftp_conn *conn, char
                                 write_error = 1;                                  write_error = 1;
                                 max_req = 0;                                  max_req = 0;
                         }                          }
                           else if (!reordered && req->offset <= highwater)
                                   highwater = req->offset + len;
                           else if (!reordered && req->offset > highwater)
                                   reordered = 1;
                         progress_counter += len;                          progress_counter += len;
                         xfree(data);                          free(data);
   
                         if (len == req->len) {                          if (len == req->len) {
                                 TAILQ_REMOVE(&requests, req, tq);                                  TAILQ_REMOVE(&requests, req, tq);
                                 xfree(req);                                  free(req);
                                 num_req--;                                  num_req--;
                         } else {                          } else {
                                 /* Resend the request for the missing data */                                  /* Resend the request for the missing data */
Line 1154  do_download(struct sftp_conn *conn, char
Line 1351  do_download(struct sftp_conn *conn, char
                                     req->offset, req->len, handle, handle_len);                                      req->offset, req->len, handle, handle_len);
                                 /* Reduce the request size */                                  /* Reduce the request size */
                                 if (len < buflen)                                  if (len < buflen)
                                         buflen = MAX(MIN_READ_SIZE, len);                                          buflen = MAXIMUM(MIN_READ_SIZE, len);
                         }                          }
                         if (max_req > 0) { /* max_req = 0 iff EOF received */                          if (max_req > 0) { /* max_req = 0 iff EOF received */
                                 if (size > 0 && offset > size) {                                  if (size > 0 && offset > size) {
Line 1181  do_download(struct sftp_conn *conn, char
Line 1378  do_download(struct sftp_conn *conn, char
         /* Sanity check */          /* Sanity check */
         if (TAILQ_FIRST(&requests) != NULL)          if (TAILQ_FIRST(&requests) != NULL)
                 fatal("Transfer complete, but requests still in queue");                  fatal("Transfer complete, but requests still in queue");
           /* Truncate at highest contiguous point to avoid holes on interrupt */
           if (read_error || write_error || interrupted) {
                   if (reordered && resume_flag) {
                           error("Unable to resume download of \"%s\": "
                               "server reordered requests", local_path);
                   }
                   debug("truncating at %llu", (unsigned long long)highwater);
                   if (ftruncate(local_fd, highwater) == -1)
                           error("ftruncate \"%s\": %s", local_path,
                               strerror(errno));
           }
         if (read_error) {          if (read_error) {
                 error("Couldn't read from remote file \"%s\" : %s",                  error("Couldn't read from remote file \"%s\" : %s",
                     remote_path, fx2txt(status));                      remote_path, fx2txt(status));
                   status = -1;
                 do_close(conn, handle, handle_len);                  do_close(conn, handle, handle_len);
         } else if (write_error) {          } else if (write_error) {
                 error("Couldn't write to \"%s\": %s", local_path,                  error("Couldn't write to \"%s\": %s", local_path,
                     strerror(write_errno));                      strerror(write_errno));
                 status = -1;                  status = SSH2_FX_FAILURE;
                 do_close(conn, handle, handle_len);                  do_close(conn, handle, handle_len);
         } else {          } else {
                 status = do_close(conn, handle, handle_len);                  if (do_close(conn, handle, handle_len) != 0 || interrupted)
                           status = SSH2_FX_FAILURE;
                   else
                           status = SSH2_FX_OK;
                 /* Override umask and utimes if asked */                  /* Override umask and utimes if asked */
                 if (pflag && fchmod(local_fd, mode) == -1)                  if (preserve_flag && fchmod(local_fd, mode) == -1)
                         error("Couldn't set mode on \"%s\": %s", local_path,                          error("Couldn't set mode on \"%s\": %s", local_path,
                             strerror(errno));                              strerror(errno));
                 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {                  if (preserve_flag &&
                       (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
                         struct timeval tv[2];                          struct timeval tv[2];
                         tv[0].tv_sec = a->atime;                          tv[0].tv_sec = a->atime;
                         tv[1].tv_sec = a->mtime;                          tv[1].tv_sec = a->mtime;
Line 1207  do_download(struct sftp_conn *conn, char
Line 1418  do_download(struct sftp_conn *conn, char
                                 error("Can't set times on \"%s\": %s",                                  error("Can't set times on \"%s\": %s",
                                     local_path, strerror(errno));                                      local_path, strerror(errno));
                 }                  }
                   if (fsync_flag) {
                           debug("syncing \"%s\"", local_path);
                           if (fsync(local_fd) == -1)
                                   error("Couldn't sync file \"%s\": %s",
                                       local_path, strerror(errno));
                   }
         }          }
         close(local_fd);          close(local_fd);
         buffer_free(&msg);          sshbuf_free(msg);
         xfree(handle);          free(handle);
   
         return(status);          return(status);
 }  }
   
 static int  static int
 download_dir_internal(struct sftp_conn *conn, char *src, char *dst,  download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
     Attrib *dirattrib, int pflag, int printflag, int depth)      int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
       int resume_flag, int fsync_flag)
 {  {
         int i, ret = 0;          int i, ret = 0;
         SFTP_DIRENT **dir_entries;          SFTP_DIRENT **dir_entries;
Line 1238  download_dir_internal(struct sftp_conn *
Line 1456  download_dir_internal(struct sftp_conn *
                 error("\"%s\" is not a directory", src);                  error("\"%s\" is not a directory", src);
                 return -1;                  return -1;
         }          }
         if (printflag)          if (print_flag)
                 printf("Retrieving %s\n", src);                  mprintf("Retrieving %s\n", src);
   
         if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)          if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
                 mode = dirattrib->perm & 01777;                  mode = dirattrib->perm & 01777;
Line 1269  download_dir_internal(struct sftp_conn *
Line 1487  download_dir_internal(struct sftp_conn *
                             strcmp(filename, "..") == 0)                              strcmp(filename, "..") == 0)
                                 continue;                                  continue;
                         if (download_dir_internal(conn, new_src, new_dst,                          if (download_dir_internal(conn, new_src, new_dst,
                             &(dir_entries[i]->a), pflag, printflag,                              depth + 1, &(dir_entries[i]->a), preserve_flag,
                             depth + 1) == -1)                              print_flag, resume_flag, fsync_flag) == -1)
                                 ret = -1;                                  ret = -1;
                 } else if (S_ISREG(dir_entries[i]->a.perm) ) {                  } else if (S_ISREG(dir_entries[i]->a.perm) ) {
                         if (do_download(conn, new_src, new_dst,                          if (do_download(conn, new_src, new_dst,
                             &(dir_entries[i]->a), pflag) == -1) {                              &(dir_entries[i]->a), preserve_flag,
                               resume_flag, fsync_flag) == -1) {
                                 error("Download of file %s to %s failed",                                  error("Download of file %s to %s failed",
                                     new_src, new_dst);                                      new_src, new_dst);
                                 ret = -1;                                  ret = -1;
Line 1282  download_dir_internal(struct sftp_conn *
Line 1501  download_dir_internal(struct sftp_conn *
                 } else                  } else
                         logit("%s: not a regular file\n", new_src);                          logit("%s: not a regular file\n", new_src);
   
                 xfree(new_dst);                  free(new_dst);
                 xfree(new_src);                  free(new_src);
         }          }
   
         if (pflag) {          if (preserve_flag) {
                 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {                  if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
                         struct timeval tv[2];                          struct timeval tv[2];
                         tv[0].tv_sec = dirattrib->atime;                          tv[0].tv_sec = dirattrib->atime;
Line 1306  download_dir_internal(struct sftp_conn *
Line 1525  download_dir_internal(struct sftp_conn *
 }  }
   
 int  int
 download_dir(struct sftp_conn *conn, char *src, char *dst,  download_dir(struct sftp_conn *conn, const char *src, const char *dst,
     Attrib *dirattrib, int pflag, int printflag)      Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
       int fsync_flag)
 {  {
         char *src_canon;          char *src_canon;
         int ret;          int ret;
   
         if ((src_canon = do_realpath(conn, src)) == NULL) {          if ((src_canon = do_realpath(conn, src)) == NULL) {
                 error("Unable to canonicalise path \"%s\"", src);                  error("Unable to canonicalize path \"%s\"", src);
                 return -1;                  return -1;
         }          }
   
         ret = download_dir_internal(conn, src_canon, dst,          ret = download_dir_internal(conn, src_canon, dst, 0,
             dirattrib, pflag, printflag, 0);              dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag);
         xfree(src_canon);          free(src_canon);
         return ret;          return ret;
 }  }
   
 int  int
 do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,  do_upload(struct sftp_conn *conn, const char *local_path,
     int pflag)      const char *remote_path, int preserve_flag, int resume, int fsync_flag)
 {  {
         int local_fd;          int r, local_fd;
         int status = SSH2_FX_OK;          u_int status = SSH2_FX_OK;
         u_int handle_len, id, type;          u_int id;
         off_t offset;          u_char type;
         char *handle, *data;          off_t offset, progress_counter;
         Buffer msg;          u_char *handle, *data;
           struct sshbuf *msg;
         struct stat sb;          struct stat sb;
         Attrib a;          Attrib a, *c = NULL;
         u_int32_t startid;          u_int32_t startid;
         u_int32_t ackid;          u_int32_t ackid;
         struct outstanding_ack {          struct outstanding_ack {
Line 1345  do_upload(struct sftp_conn *conn, char *
Line 1566  do_upload(struct sftp_conn *conn, char *
         };          };
         TAILQ_HEAD(ackhead, outstanding_ack) acks;          TAILQ_HEAD(ackhead, outstanding_ack) acks;
         struct outstanding_ack *ack = NULL;          struct outstanding_ack *ack = NULL;
           size_t handle_len;
   
         TAILQ_INIT(&acks);          TAILQ_INIT(&acks);
   
Line 1369  do_upload(struct sftp_conn *conn, char *
Line 1591  do_upload(struct sftp_conn *conn, char *
         a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;          a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
         a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;          a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
         a.perm &= 0777;          a.perm &= 0777;
         if (!pflag)          if (!preserve_flag)
                 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;                  a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
   
         buffer_init(&msg);          if (resume) {
                   /* Get remote file size if it exists */
                   if ((c = do_stat(conn, remote_path, 0)) == NULL) {
                           close(local_fd);
                           return -1;
                   }
   
                   if ((off_t)c->size >= sb.st_size) {
                           error("destination file bigger or same size as "
                                 "source file");
                           close(local_fd);
                           return -1;
                   }
   
                   if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) {
                           close(local_fd);
                           return -1;
                   }
           }
   
           if ((msg = sshbuf_new()) == NULL)
                   fatal("%s: sshbuf_new failed", __func__);
   
         /* Send open request */          /* Send open request */
         id = conn->msg_id++;          id = conn->msg_id++;
         buffer_put_char(&msg, SSH2_FXP_OPEN);          if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
         buffer_put_int(&msg, id);              (r = sshbuf_put_u32(msg, id)) != 0 ||
         buffer_put_cstring(&msg, remote_path);              (r = sshbuf_put_cstring(msg, remote_path)) != 0 ||
         buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);              (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|
         encode_attrib(&msg, &a);              (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC))) != 0 ||
         send_msg(conn, &msg);              (r = encode_attrib(msg, &a)) != 0)
                   fatal("%s: buffer error: %s", __func__, ssh_err(r));
           send_msg(conn, msg);
         debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);          debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
   
         buffer_clear(&msg);          sshbuf_reset(msg);
   
         handle = get_handle(conn, id, &handle_len,          handle = get_handle(conn, id, &handle_len,
             "remote open(\"%s\")", remote_path);              "remote open(\"%s\")", remote_path);
         if (handle == NULL) {          if (handle == NULL) {
                 close(local_fd);                  close(local_fd);
                 buffer_free(&msg);                  sshbuf_free(msg);
                 return -1;                  return -1;
         }          }
   
Line 1398  do_upload(struct sftp_conn *conn, char *
Line 1643  do_upload(struct sftp_conn *conn, char *
         data = xmalloc(conn->transfer_buflen);          data = xmalloc(conn->transfer_buflen);
   
         /* Read from local and write to remote */          /* Read from local and write to remote */
         offset = 0;          offset = progress_counter = (resume ? c->size : 0);
         if (showprogress)          if (showprogress)
                 start_progress_meter(local_path, sb.st_size, &offset);                  start_progress_meter(local_path, sb.st_size,
                       &progress_counter);
   
         for (;;) {          for (;;) {
                 int len;                  int len;
Line 1422  do_upload(struct sftp_conn *conn, char *
Line 1668  do_upload(struct sftp_conn *conn, char *
                             strerror(errno));                              strerror(errno));
   
                 if (len != 0) {                  if (len != 0) {
                         ack = xmalloc(sizeof(*ack));                          ack = xcalloc(1, sizeof(*ack));
                         ack->id = ++id;                          ack->id = ++id;
                         ack->offset = offset;                          ack->offset = offset;
                         ack->len = len;                          ack->len = len;
                         TAILQ_INSERT_TAIL(&acks, ack, tq);                          TAILQ_INSERT_TAIL(&acks, ack, tq);
   
                         buffer_clear(&msg);                          sshbuf_reset(msg);
                         buffer_put_char(&msg, SSH2_FXP_WRITE);                          if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 ||
                         buffer_put_int(&msg, ack->id);                              (r = sshbuf_put_u32(msg, ack->id)) != 0 ||
                         buffer_put_string(&msg, handle, handle_len);                              (r = sshbuf_put_string(msg, handle,
                         buffer_put_int64(&msg, offset);                              handle_len)) != 0 ||
                         buffer_put_string(&msg, data, len);                              (r = sshbuf_put_u64(msg, offset)) != 0 ||
                         send_msg(conn, &msg);                              (r = sshbuf_put_string(msg, data, len)) != 0)
                                   fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
                           send_msg(conn, msg);
                         debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",                          debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
                             id, (unsigned long long)offset, len);                              id, (unsigned long long)offset, len);
                 } else if (TAILQ_FIRST(&acks) == NULL)                  } else if (TAILQ_FIRST(&acks) == NULL)
Line 1445  do_upload(struct sftp_conn *conn, char *
Line 1694  do_upload(struct sftp_conn *conn, char *
   
                 if (id == startid || len == 0 ||                  if (id == startid || len == 0 ||
                     id - ackid >= conn->num_requests) {                      id - ackid >= conn->num_requests) {
                         u_int r_id;                          u_int rid;
   
                         buffer_clear(&msg);                          sshbuf_reset(msg);
                         get_msg(conn, &msg);                          get_msg(conn, msg);
                         type = buffer_get_char(&msg);                          if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
                         r_id = buffer_get_int(&msg);                              (r = sshbuf_get_u32(msg, &rid)) != 0)
                                   fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
   
                         if (type != SSH2_FXP_STATUS)                          if (type != SSH2_FXP_STATUS)
                                 fatal("Expected SSH2_FXP_STATUS(%d) packet, "                                  fatal("Expected SSH2_FXP_STATUS(%d) packet, "
                                     "got %d", SSH2_FXP_STATUS, type);                                      "got %d", SSH2_FXP_STATUS, type);
   
                         status = buffer_get_int(&msg);                          if ((r = sshbuf_get_u32(msg, &status)) != 0)
                         debug3("SSH2_FXP_STATUS %d", status);                                  fatal("%s: buffer error: %s",
                                       __func__, ssh_err(r));
                           debug3("SSH2_FXP_STATUS %u", status);
   
                         /* Find the request in our queue */                          /* Find the request in our queue */
                         for (ack = TAILQ_FIRST(&acks);                          for (ack = TAILQ_FIRST(&acks);
                             ack != NULL && ack->id != r_id;                              ack != NULL && ack->id != rid;
                             ack = TAILQ_NEXT(ack, tq))                              ack = TAILQ_NEXT(ack, tq))
                                 ;                                  ;
                         if (ack == NULL)                          if (ack == NULL)
                                 fatal("Can't find request for ID %u", r_id);                                  fatal("Can't find request for ID %u", rid);
                         TAILQ_REMOVE(&acks, ack, tq);                          TAILQ_REMOVE(&acks, ack, tq);
                         debug3("In write loop, ack for %u %u bytes at %lld",                          debug3("In write loop, ack for %u %u bytes at %lld",
                             ack->id, ack->len, (long long)ack->offset);                              ack->id, ack->len, (long long)ack->offset);
                         ++ackid;                          ++ackid;
                         xfree(ack);                          progress_counter += ack->len;
                           free(ack);
                 }                  }
                 offset += len;                  offset += len;
                 if (offset < 0)                  if (offset < 0)
                         fatal("%s: offset < 0", __func__);                          fatal("%s: offset < 0", __func__);
         }          }
         buffer_free(&msg);          sshbuf_free(msg);
   
         if (showprogress)          if (showprogress)
                 stop_progress_meter();                  stop_progress_meter();
         xfree(data);          free(data);
   
         if (status != SSH2_FX_OK) {          if (status != SSH2_FX_OK) {
                 error("Couldn't write to remote file \"%s\": %s",                  error("Couldn't write to remote file \"%s\": %s",
                     remote_path, fx2txt(status));                      remote_path, fx2txt(status));
                 status = -1;                  status = SSH2_FX_FAILURE;
         }          }
   
         if (close(local_fd) == -1) {          if (close(local_fd) == -1) {
                 error("Couldn't close local file \"%s\": %s", local_path,                  error("Couldn't close local file \"%s\": %s", local_path,
                     strerror(errno));                      strerror(errno));
                 status = -1;                  status = SSH2_FX_FAILURE;
         }          }
   
         /* Override umask and utimes if asked */          /* Override umask and utimes if asked */
         if (pflag)          if (preserve_flag)
                 do_fsetstat(conn, handle, handle_len, &a);                  do_fsetstat(conn, handle, handle_len, &a);
   
         if (do_close(conn, handle, handle_len) != SSH2_FX_OK)          if (fsync_flag)
                 status = -1;                  (void)do_fsync(conn, handle, handle_len);
         xfree(handle);  
   
         return status;          if (do_close(conn, handle, handle_len) != 0)
                   status = SSH2_FX_FAILURE;
   
           free(handle);
   
           return status == SSH2_FX_OK ? 0 : -1;
 }  }
   
 static int  static int
 upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,  upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
     int pflag, int printflag, int depth)      int depth, int preserve_flag, int print_flag, int resume, int fsync_flag)
 {  {
         int ret = 0, status;          int ret = 0;
         DIR *dirp;          DIR *dirp;
         struct dirent *dp;          struct dirent *dp;
         char *filename, *new_src, *new_dst;          char *filename, *new_src, *new_dst;
         struct stat sb;          struct stat sb;
         Attrib a;          Attrib a, *dirattrib;
   
         if (depth >= MAX_DIR_DEPTH) {          if (depth >= MAX_DIR_DEPTH) {
                 error("Maximum directory depth exceeded: %d levels", depth);                  error("Maximum directory depth exceeded: %d levels", depth);
Line 1530  upload_dir_internal(struct sftp_conn *co
Line 1788  upload_dir_internal(struct sftp_conn *co
                 error("\"%s\" is not a directory", src);                  error("\"%s\" is not a directory", src);
                 return -1;                  return -1;
         }          }
         if (printflag)          if (print_flag)
                 printf("Entering %s\n", src);                  mprintf("Entering %s\n", src);
   
         attrib_clear(&a);          attrib_clear(&a);
         stat_to_attrib(&sb, &a);          stat_to_attrib(&sb, &a);
         a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;          a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
         a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;          a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
         a.perm &= 01777;          a.perm &= 01777;
         if (!pflag)          if (!preserve_flag)
                 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;                  a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
   
         status = do_mkdir(conn, dst, &a, 0);  
         /*          /*
          * we lack a portable status for errno EEXIST,           * sftp lacks a portable status value to match errno EEXIST,
          * so if we get a SSH2_FX_FAILURE back we must check           * so if we get a failure back then we must check whether
          * if it was created successfully.           * the path already existed and is a directory.
          */           */
         if (status != SSH2_FX_OK) {          if (do_mkdir(conn, dst, &a, 0) != 0) {
                 if (status != SSH2_FX_FAILURE)                  if ((dirattrib = do_stat(conn, dst, 0)) == NULL)
                         return -1;                          return -1;
                 if (do_stat(conn, dst, 0) == NULL)                  if (!S_ISDIR(dirattrib->perm)) {
                           error("\"%s\" exists but is not a directory", dst);
                         return -1;                          return -1;
                   }
         }          }
   
         if ((dirp = opendir(src)) == NULL) {          if ((dirp = opendir(src)) == NULL) {
                 error("Failed to open dir \"%s\": %s", src, strerror(errno));                  error("Failed to open dir \"%s\": %s", src, strerror(errno));
                 return -1;                  return -1;
         }          }
   
         while (((dp = readdir(dirp)) != NULL) && !interrupted) {          while (((dp = readdir(dirp)) != NULL) && !interrupted) {
                 if (dp->d_ino == 0)                  if (dp->d_ino == 0)
                         continue;                          continue;
Line 1576  upload_dir_internal(struct sftp_conn *co
Line 1835  upload_dir_internal(struct sftp_conn *co
                                 continue;                                  continue;
   
                         if (upload_dir_internal(conn, new_src, new_dst,                          if (upload_dir_internal(conn, new_src, new_dst,
                             pflag, printflag, depth + 1) == -1)                              depth + 1, preserve_flag, print_flag, resume,
                               fsync_flag) == -1)
                                 ret = -1;                                  ret = -1;
                 } else if (S_ISREG(sb.st_mode)) {                  } else if (S_ISREG(sb.st_mode)) {
                         if (do_upload(conn, new_src, new_dst, pflag) == -1) {                          if (do_upload(conn, new_src, new_dst,
                               preserve_flag, resume, fsync_flag) == -1) {
                                 error("Uploading of file %s to %s failed!",                                  error("Uploading of file %s to %s failed!",
                                     new_src, new_dst);                                      new_src, new_dst);
                                 ret = -1;                                  ret = -1;
                         }                          }
                 } else                  } else
                         logit("%s: not a regular file\n", filename);                          logit("%s: not a regular file\n", filename);
                 xfree(new_dst);                  free(new_dst);
                 xfree(new_src);                  free(new_src);
         }          }
   
         do_setstat(conn, dst, &a);          do_setstat(conn, dst, &a);
Line 1597  upload_dir_internal(struct sftp_conn *co
Line 1858  upload_dir_internal(struct sftp_conn *co
 }  }
   
 int  int
 upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,  upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
     int pflag)      int preserve_flag, int print_flag, int resume, int fsync_flag)
 {  {
         char *dst_canon;          char *dst_canon;
         int ret;          int ret;
   
         if ((dst_canon = do_realpath(conn, dst)) == NULL) {          if ((dst_canon = do_realpath(conn, dst)) == NULL) {
                 error("Unable to canonicalise path \"%s\"", dst);                  error("Unable to canonicalize path \"%s\"", dst);
                 return -1;                  return -1;
         }          }
   
         ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);          ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
         xfree(dst_canon);              print_flag, resume, fsync_flag);
   
           free(dst_canon);
         return ret;          return ret;
 }  }
   
 char *  char *
 path_append(char *p1, char *p2)  path_append(const char *p1, const char *p2)
 {  {
         char *ret;          char *ret;
         size_t len = strlen(p1) + strlen(p2) + 2;          size_t len = strlen(p1) + strlen(p2) + 2;

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.5.10.1

CVSweb <webmaster@jp.NetBSD.org>