Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.149
1.149 ! djm 1: /* $OpenBSD: sftp-client.c,v 1.148 2021/08/07 00:09:57 djm Exp $ */
1.1 djm 2: /*
1.46 djm 3: * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
1.1 djm 4: *
1.46 djm 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 djm 8: *
1.46 djm 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 djm 16: */
17:
18: /* XXX: memleaks */
19: /* XXX: signed vs unsigned */
1.23 djm 20: /* XXX: remove all logging, only return status codes */
1.1 djm 21: /* XXX: copy between two remote sites */
22:
1.74 deraadt 23: #include <sys/types.h>
1.93 djm 24: #include <sys/poll.h>
1.21 djm 25: #include <sys/queue.h>
1.60 stevesk 26: #include <sys/stat.h>
1.71 stevesk 27: #include <sys/time.h>
1.82 djm 28: #include <sys/statvfs.h>
1.74 deraadt 29: #include <sys/uio.h>
1.66 stevesk 30:
1.89 djm 31: #include <dirent.h>
1.67 stevesk 32: #include <errno.h>
1.66 stevesk 33: #include <fcntl.h>
1.70 stevesk 34: #include <signal.h>
1.87 dtucker 35: #include <stdarg.h>
1.73 stevesk 36: #include <stdio.h>
1.109 dtucker 37: #include <stdlib.h>
1.69 stevesk 38: #include <string.h>
1.68 stevesk 39: #include <unistd.h>
1.1 djm 40:
1.74 deraadt 41: #include "xmalloc.h"
1.116 djm 42: #include "ssherr.h"
43: #include "sshbuf.h"
1.1 djm 44: #include "log.h"
45: #include "atomicio.h"
1.39 fgsch 46: #include "progressmeter.h"
1.64 djm 47: #include "misc.h"
1.124 schwarze 48: #include "utf8.h"
1.1 djm 49:
50: #include "sftp.h"
51: #include "sftp-common.h"
52: #include "sftp-client.h"
53:
1.49 djm 54: extern volatile sig_atomic_t interrupted;
1.39 fgsch 55: extern int showprogress;
56:
1.141 djm 57: /* Default size of buffer for up/download */
58: #define DEFAULT_COPY_BUFLEN 32768
59:
60: /* Default number of concurrent outstanding requests */
61: #define DEFAULT_NUM_REQUESTS 64
62:
1.59 david 63: /* Minimum amount of data to read at a time */
1.21 djm 64: #define MIN_READ_SIZE 512
65:
1.89 djm 66: /* Maximum depth to descend in directory trees */
67: #define MAX_DIR_DEPTH 64
68:
1.23 djm 69: struct sftp_conn {
70: int fd_in;
71: int fd_out;
1.141 djm 72: u_int download_buflen;
73: u_int upload_buflen;
1.23 djm 74: u_int num_requests;
75: u_int version;
76: u_int msg_id;
1.82 djm 77: #define SFTP_EXT_POSIX_RENAME 0x00000001
78: #define SFTP_EXT_STATVFS 0x00000002
79: #define SFTP_EXT_FSTATVFS 0x00000004
1.94 djm 80: #define SFTP_EXT_HARDLINK 0x00000008
1.107 djm 81: #define SFTP_EXT_FSYNC 0x00000010
1.131 djm 82: #define SFTP_EXT_LSETSTAT 0x00000020
1.141 djm 83: #define SFTP_EXT_LIMITS 0x00000040
1.81 djm 84: u_int exts;
1.93 djm 85: u_int64_t limit_kbps;
86: struct bwlimit bwlimit_in, bwlimit_out;
1.23 djm 87: };
1.4 djm 88:
1.146 djm 89: /* Tracks in-progress requests during file transfers */
90: struct request {
91: u_int id;
92: size_t len;
93: u_int64_t offset;
94: TAILQ_ENTRY(request) tq;
95: };
96: TAILQ_HEAD(requests, request);
97:
1.116 djm 98: static u_char *
99: get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
1.93 djm 100: const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
101:
102: /* ARGSUSED */
103: static int
104: sftpio(void *_bwlimit, size_t amount)
105: {
106: struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
107:
1.133 dtucker 108: refresh_progress_meter(0);
1.132 dtucker 109: if (bwlimit != NULL)
110: bandwidth_limit(bwlimit, amount);
1.93 djm 111: return 0;
112: }
1.88 djm 113:
1.17 itojun 114: static void
1.116 djm 115: send_msg(struct sftp_conn *conn, struct sshbuf *m)
1.1 djm 116: {
1.40 djm 117: u_char mlen[4];
1.65 djm 118: struct iovec iov[2];
1.40 djm 119:
1.116 djm 120: if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH)
121: fatal("Outbound message too long %zu", sshbuf_len(m));
1.40 djm 122:
123: /* Send length first */
1.116 djm 124: put_u32(mlen, sshbuf_len(m));
1.65 djm 125: iov[0].iov_base = mlen;
126: iov[0].iov_len = sizeof(mlen);
1.116 djm 127: iov[1].iov_base = (u_char *)sshbuf_ptr(m);
128: iov[1].iov_len = sshbuf_len(m);
1.74 deraadt 129:
1.132 dtucker 130: if (atomiciov6(writev, conn->fd_out, iov, 2, sftpio,
131: conn->limit_kbps > 0 ? &conn->bwlimit_out : NULL) !=
1.116 djm 132: sshbuf_len(m) + sizeof(mlen))
1.1 djm 133: fatal("Couldn't send packet: %s", strerror(errno));
134:
1.116 djm 135: sshbuf_reset(m);
1.1 djm 136: }
137:
1.17 itojun 138: static void
1.128 dtucker 139: get_msg_extended(struct sftp_conn *conn, struct sshbuf *m, int initial)
1.1 djm 140: {
1.40 djm 141: u_int msg_len;
1.116 djm 142: u_char *p;
143: int r;
1.1 djm 144:
1.144 djm 145: sshbuf_reset(m);
1.116 djm 146: if ((r = sshbuf_reserve(m, 4, &p)) != 0)
1.137 djm 147: fatal_fr(r, "reserve");
1.132 dtucker 148: if (atomicio6(read, conn->fd_in, p, 4, sftpio,
149: conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != 4) {
1.127 djm 150: if (errno == EPIPE || errno == ECONNRESET)
1.54 avsm 151: fatal("Connection closed");
152: else
153: fatal("Couldn't read packet: %s", strerror(errno));
154: }
1.1 djm 155:
1.116 djm 156: if ((r = sshbuf_get_u32(m, &msg_len)) != 0)
1.137 djm 157: fatal_fr(r, "sshbuf_get_u32");
1.128 dtucker 158: if (msg_len > SFTP_MAX_MSG_LENGTH) {
159: do_log2(initial ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_FATAL,
160: "Received message too long %u", msg_len);
161: fatal("Ensure the remote shell produces no output "
162: "for non-interactive sessions.");
163: }
1.1 djm 164:
1.116 djm 165: if ((r = sshbuf_reserve(m, msg_len, &p)) != 0)
1.137 djm 166: fatal_fr(r, "reserve");
1.132 dtucker 167: if (atomicio6(read, conn->fd_in, p, msg_len, sftpio,
168: conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL)
1.93 djm 169: != msg_len) {
1.54 avsm 170: if (errno == EPIPE)
171: fatal("Connection closed");
172: else
173: fatal("Read packet: %s", strerror(errno));
174: }
1.1 djm 175: }
176:
1.17 itojun 177: static void
1.128 dtucker 178: get_msg(struct sftp_conn *conn, struct sshbuf *m)
179: {
180: get_msg_extended(conn, m, 0);
181: }
182:
183: static void
1.116 djm 184: send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s,
1.1 djm 185: u_int len)
186: {
1.116 djm 187: struct sshbuf *msg;
188: int r;
1.1 djm 189:
1.116 djm 190: if ((msg = sshbuf_new()) == NULL)
1.137 djm 191: fatal_f("sshbuf_new failed");
1.116 djm 192: if ((r = sshbuf_put_u8(msg, code)) != 0 ||
193: (r = sshbuf_put_u32(msg, id)) != 0 ||
194: (r = sshbuf_put_string(msg, s, len)) != 0)
1.137 djm 195: fatal_fr(r, "compose");
1.116 djm 196: send_msg(conn, msg);
1.93 djm 197: debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
1.116 djm 198: sshbuf_free(msg);
1.1 djm 199: }
200:
1.17 itojun 201: static void
1.93 djm 202: send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
1.116 djm 203: const void *s, u_int len, Attrib *a)
1.1 djm 204: {
1.116 djm 205: struct sshbuf *msg;
206: int r;
1.1 djm 207:
1.116 djm 208: if ((msg = sshbuf_new()) == NULL)
1.137 djm 209: fatal_f("sshbuf_new failed");
1.116 djm 210: if ((r = sshbuf_put_u8(msg, code)) != 0 ||
211: (r = sshbuf_put_u32(msg, id)) != 0 ||
212: (r = sshbuf_put_string(msg, s, len)) != 0 ||
213: (r = encode_attrib(msg, a)) != 0)
1.137 djm 214: fatal_fr(r, "compose");
1.116 djm 215: send_msg(conn, msg);
1.148 djm 216: debug3("Sent message fd %d T:%u I:%u F:0x%04x M:%05o",
217: conn->fd_out, code, id, a->flags, a->perm);
1.116 djm 218: sshbuf_free(msg);
1.1 djm 219: }
220:
1.17 itojun 221: static u_int
1.93 djm 222: get_status(struct sftp_conn *conn, u_int expected_id)
1.1 djm 223: {
1.116 djm 224: struct sshbuf *msg;
225: u_char type;
226: u_int id, status;
227: int r;
1.1 djm 228:
1.116 djm 229: if ((msg = sshbuf_new()) == NULL)
1.137 djm 230: fatal_f("sshbuf_new failed");
1.116 djm 231: get_msg(conn, msg);
232: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
233: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 234: fatal_fr(r, "compose");
1.1 djm 235:
236: if (id != expected_id)
1.33 deraadt 237: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 238: if (type != SSH2_FXP_STATUS)
1.33 deraadt 239: fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
1.1 djm 240: SSH2_FXP_STATUS, type);
241:
1.116 djm 242: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 243: fatal_fr(r, "parse");
1.116 djm 244: sshbuf_free(msg);
1.1 djm 245:
1.33 deraadt 246: debug3("SSH2_FXP_STATUS %u", status);
1.1 djm 247:
1.93 djm 248: return status;
1.1 djm 249: }
250:
1.116 djm 251: static u_char *
252: get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len,
1.93 djm 253: const char *errfmt, ...)
1.1 djm 254: {
1.116 djm 255: struct sshbuf *msg;
256: u_int id, status;
257: u_char type;
258: u_char *handle;
259: char errmsg[256];
1.88 djm 260: va_list args;
1.116 djm 261: int r;
1.88 djm 262:
263: va_start(args, errfmt);
264: if (errfmt != NULL)
265: vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
266: va_end(args);
1.1 djm 267:
1.116 djm 268: if ((msg = sshbuf_new()) == NULL)
1.137 djm 269: fatal_f("sshbuf_new failed");
1.116 djm 270: get_msg(conn, msg);
271: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
272: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 273: fatal_fr(r, "parse");
1.1 djm 274:
275: if (id != expected_id)
1.88 djm 276: fatal("%s: ID mismatch (%u != %u)",
277: errfmt == NULL ? __func__ : errmsg, id, expected_id);
1.1 djm 278: if (type == SSH2_FXP_STATUS) {
1.116 djm 279: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 280: fatal_fr(r, "parse status");
1.88 djm 281: if (errfmt != NULL)
282: error("%s: %s", errmsg, fx2txt(status));
1.116 djm 283: sshbuf_free(msg);
1.1 djm 284: return(NULL);
285: } else if (type != SSH2_FXP_HANDLE)
1.88 djm 286: fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
287: errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
1.1 djm 288:
1.116 djm 289: if ((r = sshbuf_get_string(msg, &handle, len)) != 0)
1.137 djm 290: fatal_fr(r, "parse handle");
1.116 djm 291: sshbuf_free(msg);
1.1 djm 292:
1.116 djm 293: return handle;
1.1 djm 294: }
295:
1.149 ! djm 296: /* XXX returing &static is error-prone. Refactor to fill *Attrib argument */
1.17 itojun 297: static Attrib *
1.93 djm 298: get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
1.1 djm 299: {
1.116 djm 300: struct sshbuf *msg;
301: u_int id;
302: u_char type;
303: int r;
304: static Attrib a;
305:
306: if ((msg = sshbuf_new()) == NULL)
1.137 djm 307: fatal_f("sshbuf_new failed");
1.116 djm 308: get_msg(conn, msg);
309:
310: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
311: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 312: fatal_fr(r, "parse");
1.1 djm 313:
314: if (id != expected_id)
1.33 deraadt 315: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 316: if (type == SSH2_FXP_STATUS) {
1.116 djm 317: u_int status;
1.1 djm 318:
1.116 djm 319: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 320: fatal_fr(r, "parse status");
1.14 djm 321: if (quiet)
322: debug("Couldn't stat remote file: %s", fx2txt(status));
323: else
324: error("Couldn't stat remote file: %s", fx2txt(status));
1.116 djm 325: sshbuf_free(msg);
1.1 djm 326: return(NULL);
327: } else if (type != SSH2_FXP_ATTRS) {
1.33 deraadt 328: fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
1.1 djm 329: SSH2_FXP_ATTRS, type);
330: }
1.116 djm 331: if ((r = decode_attrib(msg, &a)) != 0) {
1.137 djm 332: error_fr(r, "decode_attrib");
1.116 djm 333: sshbuf_free(msg);
334: return NULL;
335: }
1.148 djm 336: debug3("Recevied stat reply T:%u I:%u F:0x%04x M:%05o",
337: type, id, a.flags, a.perm);
1.116 djm 338: sshbuf_free(msg);
1.1 djm 339:
1.116 djm 340: return &a;
1.1 djm 341: }
342:
1.82 djm 343: static int
1.93 djm 344: get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
345: u_int expected_id, int quiet)
1.82 djm 346: {
1.116 djm 347: struct sshbuf *msg;
348: u_char type;
349: u_int id;
350: u_int64_t flag;
351: int r;
1.82 djm 352:
1.116 djm 353: if ((msg = sshbuf_new()) == NULL)
1.137 djm 354: fatal_f("sshbuf_new failed");
1.116 djm 355: get_msg(conn, msg);
356:
357: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
358: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 359: fatal_fr(r, "parse");
1.82 djm 360:
361: debug3("Received statvfs reply T:%u I:%u", type, id);
362: if (id != expected_id)
363: fatal("ID mismatch (%u != %u)", id, expected_id);
364: if (type == SSH2_FXP_STATUS) {
1.116 djm 365: u_int status;
1.82 djm 366:
1.116 djm 367: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 368: fatal_fr(r, "parse status");
1.82 djm 369: if (quiet)
370: debug("Couldn't statvfs: %s", fx2txt(status));
371: else
372: error("Couldn't statvfs: %s", fx2txt(status));
1.116 djm 373: sshbuf_free(msg);
1.82 djm 374: return -1;
375: } else if (type != SSH2_FXP_EXTENDED_REPLY) {
376: fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
377: SSH2_FXP_EXTENDED_REPLY, type);
378: }
379:
1.114 tedu 380: memset(st, 0, sizeof(*st));
1.116 djm 381: if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 ||
382: (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 ||
383: (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 ||
384: (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 ||
385: (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 ||
386: (r = sshbuf_get_u64(msg, &st->f_files)) != 0 ||
387: (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 ||
388: (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 ||
389: (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 ||
390: (r = sshbuf_get_u64(msg, &flag)) != 0 ||
391: (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0)
1.137 djm 392: fatal_fr(r, "parse statvfs");
1.82 djm 393:
394: st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
395: st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
396:
1.116 djm 397: sshbuf_free(msg);
1.82 djm 398:
399: return 0;
400: }
401:
1.23 djm 402: struct sftp_conn *
1.93 djm 403: do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
404: u_int64_t limit_kbps)
1.1 djm 405: {
1.116 djm 406: u_char type;
407: struct sshbuf *msg;
1.23 djm 408: struct sftp_conn *ret;
1.116 djm 409: int r;
1.1 djm 410:
1.103 djm 411: ret = xcalloc(1, sizeof(*ret));
412: ret->msg_id = 1;
1.93 djm 413: ret->fd_in = fd_in;
414: ret->fd_out = fd_out;
1.141 djm 415: ret->download_buflen = ret->upload_buflen =
416: transfer_buflen ? transfer_buflen : DEFAULT_COPY_BUFLEN;
417: ret->num_requests =
418: num_requests ? num_requests : DEFAULT_NUM_REQUESTS;
1.93 djm 419: ret->exts = 0;
420: ret->limit_kbps = 0;
421:
1.116 djm 422: if ((msg = sshbuf_new()) == NULL)
1.137 djm 423: fatal_f("sshbuf_new failed");
1.116 djm 424: if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 ||
425: (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0)
1.137 djm 426: fatal_fr(r, "parse");
427:
1.116 djm 428: send_msg(ret, msg);
1.1 djm 429:
1.128 dtucker 430: get_msg_extended(ret, msg, 1);
1.1 djm 431:
1.3 stevesk 432: /* Expecting a VERSION reply */
1.116 djm 433: if ((r = sshbuf_get_u8(msg, &type)) != 0)
1.137 djm 434: fatal_fr(r, "parse type");
1.116 djm 435: if (type != SSH2_FXP_VERSION) {
1.33 deraadt 436: error("Invalid packet back from SSH2_FXP_INIT (type %u)",
1.1 djm 437: type);
1.116 djm 438: sshbuf_free(msg);
1.119 jsg 439: free(ret);
1.23 djm 440: return(NULL);
1.1 djm 441: }
1.116 djm 442: if ((r = sshbuf_get_u32(msg, &ret->version)) != 0)
1.137 djm 443: fatal_fr(r, "parse version");
1.1 djm 444:
1.93 djm 445: debug2("Remote version: %u", ret->version);
1.1 djm 446:
447: /* Check for extensions */
1.116 djm 448: while (sshbuf_len(msg) > 0) {
449: char *name;
450: u_char *value;
451: size_t vlen;
1.85 djm 452: int known = 0;
1.1 djm 453:
1.116 djm 454: if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 ||
455: (r = sshbuf_get_string(msg, &value, &vlen)) != 0)
1.137 djm 456: fatal_fr(r, "parse extension");
1.82 djm 457: if (strcmp(name, "posix-rename@openssh.com") == 0 &&
1.116 djm 458: strcmp((char *)value, "1") == 0) {
1.93 djm 459: ret->exts |= SFTP_EXT_POSIX_RENAME;
1.85 djm 460: known = 1;
461: } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
1.116 djm 462: strcmp((char *)value, "2") == 0) {
1.93 djm 463: ret->exts |= SFTP_EXT_STATVFS;
1.85 djm 464: known = 1;
1.94 djm 465: } else if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
1.116 djm 466: strcmp((char *)value, "2") == 0) {
1.93 djm 467: ret->exts |= SFTP_EXT_FSTATVFS;
1.85 djm 468: known = 1;
1.94 djm 469: } else if (strcmp(name, "hardlink@openssh.com") == 0 &&
1.116 djm 470: strcmp((char *)value, "1") == 0) {
1.94 djm 471: ret->exts |= SFTP_EXT_HARDLINK;
472: known = 1;
1.116 djm 473: } else if (strcmp(name, "fsync@openssh.com") == 0 &&
474: strcmp((char *)value, "1") == 0) {
475: ret->exts |= SFTP_EXT_FSYNC;
476: known = 1;
1.131 djm 477: } else if (strcmp(name, "lsetstat@openssh.com") == 0 &&
478: strcmp((char *)value, "1") == 0) {
479: ret->exts |= SFTP_EXT_LSETSTAT;
480: known = 1;
1.141 djm 481: } else if (strcmp(name, "limits@openssh.com") == 0 &&
482: strcmp((char *)value, "1") == 0) {
483: ret->exts |= SFTP_EXT_LIMITS;
484: known = 1;
1.85 djm 485: }
486: if (known) {
487: debug2("Server supports extension \"%s\" revision %s",
488: name, value);
489: } else {
490: debug2("Unrecognised server extension \"%s\"", name);
491: }
1.98 djm 492: free(name);
493: free(value);
1.1 djm 494: }
495:
1.116 djm 496: sshbuf_free(msg);
1.11 djm 497:
1.141 djm 498: /* Query the server for its limits */
499: if (ret->exts & SFTP_EXT_LIMITS) {
500: struct sftp_limits limits;
501: if (do_limits(ret, &limits) != 0)
502: fatal_f("limits failed");
503:
504: /* If the caller did not specify, find a good value */
505: if (transfer_buflen == 0) {
506: ret->download_buflen = limits.read_length;
507: ret->upload_buflen = limits.write_length;
508: debug("Using server download size %u", ret->download_buflen);
509: debug("Using server upload size %u", ret->upload_buflen);
510: }
511:
512: /* Use the server limit to scale down our value only */
513: if (num_requests == 0 && limits.open_handles) {
514: ret->num_requests =
515: MINIMUM(DEFAULT_NUM_REQUESTS, limits.open_handles);
516: debug("Server handle limit %llu; using %u",
1.142 djm 517: (unsigned long long)limits.open_handles,
518: ret->num_requests);
1.141 djm 519: }
520: }
521:
1.23 djm 522: /* Some filexfer v.0 servers don't support large packets */
1.141 djm 523: if (ret->version == 0) {
524: ret->download_buflen = MINIMUM(ret->download_buflen, 20480);
525: ret->upload_buflen = MINIMUM(ret->upload_buflen, 20480);
526: }
1.23 djm 527:
1.93 djm 528: ret->limit_kbps = limit_kbps;
529: if (ret->limit_kbps > 0) {
530: bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
1.141 djm 531: ret->download_buflen);
1.93 djm 532: bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
1.141 djm 533: ret->upload_buflen);
1.93 djm 534: }
535:
536: return ret;
1.23 djm 537: }
538:
539: u_int
540: sftp_proto_version(struct sftp_conn *conn)
541: {
1.93 djm 542: return conn->version;
1.1 djm 543: }
544:
545: int
1.141 djm 546: do_limits(struct sftp_conn *conn, struct sftp_limits *limits)
547: {
548: u_int id, msg_id;
549: u_char type;
550: struct sshbuf *msg;
551: int r;
552:
553: if ((conn->exts & SFTP_EXT_LIMITS) == 0) {
554: error("Server does not support limits@openssh.com extension");
555: return -1;
556: }
557:
558: if ((msg = sshbuf_new()) == NULL)
559: fatal_f("sshbuf_new failed");
560:
561: id = conn->msg_id++;
562: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
563: (r = sshbuf_put_u32(msg, id)) != 0 ||
564: (r = sshbuf_put_cstring(msg, "limits@openssh.com")) != 0)
565: fatal_fr(r, "compose");
566: send_msg(conn, msg);
567: debug3("Sent message limits@openssh.com I:%u", id);
568:
569: get_msg(conn, msg);
570:
571: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
572: (r = sshbuf_get_u32(msg, &msg_id)) != 0)
573: fatal_fr(r, "parse");
574:
575: debug3("Received limits reply T:%u I:%u", type, msg_id);
576: if (id != msg_id)
577: fatal("ID mismatch (%u != %u)", msg_id, id);
578: if (type != SSH2_FXP_EXTENDED_REPLY) {
1.143 djm 579: debug_f("expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
1.142 djm 580: SSH2_FXP_EXTENDED_REPLY, type);
1.143 djm 581: /* Disable the limits extension */
582: conn->exts &= ~SFTP_EXT_LIMITS;
583: sshbuf_free(msg);
584: return 0;
1.141 djm 585: }
586:
587: memset(limits, 0, sizeof(*limits));
588: if ((r = sshbuf_get_u64(msg, &limits->packet_length)) != 0 ||
589: (r = sshbuf_get_u64(msg, &limits->read_length)) != 0 ||
590: (r = sshbuf_get_u64(msg, &limits->write_length)) != 0 ||
591: (r = sshbuf_get_u64(msg, &limits->open_handles)) != 0)
592: fatal_fr(r, "parse limits");
593:
594: sshbuf_free(msg);
595:
596: return 0;
597: }
598:
599: int
1.116 djm 600: do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len)
1.1 djm 601: {
602: u_int id, status;
1.116 djm 603: struct sshbuf *msg;
604: int r;
1.1 djm 605:
1.116 djm 606: if ((msg = sshbuf_new()) == NULL)
1.137 djm 607: fatal_f("sshbuf_new failed");
1.1 djm 608:
1.23 djm 609: id = conn->msg_id++;
1.116 djm 610: if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 ||
611: (r = sshbuf_put_u32(msg, id)) != 0 ||
612: (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1.137 djm 613: fatal_fr(r, "parse");
1.116 djm 614: send_msg(conn, msg);
1.33 deraadt 615: debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
1.1 djm 616:
1.93 djm 617: status = get_status(conn, id);
1.1 djm 618: if (status != SSH2_FX_OK)
619: error("Couldn't close file: %s", fx2txt(status));
620:
1.116 djm 621: sshbuf_free(msg);
1.1 djm 622:
1.116 djm 623: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 624: }
625:
1.12 djm 626:
1.17 itojun 627: static int
1.116 djm 628: do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag,
1.12 djm 629: SFTP_DIRENT ***dir)
1.1 djm 630: {
1.116 djm 631: struct sshbuf *msg;
632: u_int count, id, i, expected_id, ents = 0;
633: size_t handle_len;
1.123 djm 634: u_char type, *handle;
1.111 djm 635: int status = SSH2_FX_FAILURE;
1.116 djm 636: int r;
1.111 djm 637:
638: if (dir)
639: *dir = NULL;
1.1 djm 640:
1.23 djm 641: id = conn->msg_id++;
1.1 djm 642:
1.116 djm 643: if ((msg = sshbuf_new()) == NULL)
1.137 djm 644: fatal_f("sshbuf_new failed");
1.116 djm 645: if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 ||
646: (r = sshbuf_put_u32(msg, id)) != 0 ||
647: (r = sshbuf_put_cstring(msg, path)) != 0)
1.137 djm 648: fatal_fr(r, "compose OPENDIR");
1.116 djm 649: send_msg(conn, msg);
1.1 djm 650:
1.93 djm 651: handle = get_handle(conn, id, &handle_len,
1.88 djm 652: "remote readdir(\"%s\")", path);
1.96 markus 653: if (handle == NULL) {
1.116 djm 654: sshbuf_free(msg);
1.93 djm 655: return -1;
1.96 markus 656: }
1.1 djm 657:
1.12 djm 658: if (dir) {
659: ents = 0;
1.108 djm 660: *dir = xcalloc(1, sizeof(**dir));
1.12 djm 661: (*dir)[0] = NULL;
662: }
663:
1.49 djm 664: for (; !interrupted;) {
1.23 djm 665: id = expected_id = conn->msg_id++;
1.1 djm 666:
1.33 deraadt 667: debug3("Sending SSH2_FXP_READDIR I:%u", id);
1.1 djm 668:
1.116 djm 669: sshbuf_reset(msg);
670: if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 ||
671: (r = sshbuf_put_u32(msg, id)) != 0 ||
672: (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1.137 djm 673: fatal_fr(r, "compose READDIR");
1.116 djm 674: send_msg(conn, msg);
675:
676: sshbuf_reset(msg);
677:
678: get_msg(conn, msg);
679:
680: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
681: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 682: fatal_fr(r, "parse");
1.1 djm 683:
1.33 deraadt 684: debug3("Received reply T:%u I:%u", type, id);
1.1 djm 685:
686: if (id != expected_id)
1.33 deraadt 687: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 688:
689: if (type == SSH2_FXP_STATUS) {
1.116 djm 690: u_int rstatus;
691:
692: if ((r = sshbuf_get_u32(msg, &rstatus)) != 0)
1.137 djm 693: fatal_fr(r, "parse status");
1.116 djm 694: debug3("Received SSH2_FXP_STATUS %d", rstatus);
695: if (rstatus == SSH2_FX_EOF)
1.1 djm 696: break;
1.116 djm 697: error("Couldn't read directory: %s", fx2txt(rstatus));
1.111 djm 698: goto out;
1.1 djm 699: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 700: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 701: SSH2_FXP_NAME, type);
702:
1.116 djm 703: if ((r = sshbuf_get_u32(msg, &count)) != 0)
1.137 djm 704: fatal_fr(r, "parse count");
1.126 djm 705: if (count > SSHBUF_SIZE_MAX)
1.137 djm 706: fatal_f("nonsensical number of entries");
1.7 markus 707: if (count == 0)
708: break;
1.8 stevesk 709: debug3("Received %d SSH2_FXP_NAME responses", count);
1.19 deraadt 710: for (i = 0; i < count; i++) {
1.1 djm 711: char *filename, *longname;
1.116 djm 712: Attrib a;
1.1 djm 713:
1.116 djm 714: if ((r = sshbuf_get_cstring(msg, &filename,
715: NULL)) != 0 ||
716: (r = sshbuf_get_cstring(msg, &longname,
717: NULL)) != 0)
1.137 djm 718: fatal_fr(r, "parse filenames");
1.116 djm 719: if ((r = decode_attrib(msg, &a)) != 0) {
1.137 djm 720: error_fr(r, "couldn't decode attrib");
1.116 djm 721: free(filename);
722: free(longname);
1.135 djm 723: goto out;
1.116 djm 724: }
1.1 djm 725:
1.105 djm 726: if (print_flag)
1.124 schwarze 727: mprintf("%s\n", longname);
1.12 djm 728:
1.89 djm 729: /*
730: * Directory entries should never contain '/'
731: * These can be used to attack recursive ops
732: * (e.g. send '../../../../etc/passwd')
733: */
734: if (strchr(filename, '/') != NULL) {
735: error("Server sent suspect path \"%s\" "
736: "during readdir of \"%s\"", filename, path);
1.111 djm 737: } else if (dir) {
1.118 deraadt 738: *dir = xreallocarray(*dir, ents + 2, sizeof(**dir));
1.108 djm 739: (*dir)[ents] = xcalloc(1, sizeof(***dir));
1.12 djm 740: (*dir)[ents]->filename = xstrdup(filename);
741: (*dir)[ents]->longname = xstrdup(longname);
1.116 djm 742: memcpy(&(*dir)[ents]->a, &a, sizeof(a));
1.12 djm 743: (*dir)[++ents] = NULL;
744: }
1.98 djm 745: free(filename);
746: free(longname);
1.1 djm 747: }
748: }
1.111 djm 749: status = 0;
1.1 djm 750:
1.111 djm 751: out:
1.116 djm 752: sshbuf_free(msg);
1.23 djm 753: do_close(conn, handle, handle_len);
1.98 djm 754: free(handle);
1.1 djm 755:
1.111 djm 756: if (status != 0 && dir != NULL) {
757: /* Don't return results on error */
758: free_sftp_dirents(*dir);
759: *dir = NULL;
760: } else if (interrupted && dir != NULL && *dir != NULL) {
761: /* Don't return partial matches on interrupt */
1.49 djm 762: free_sftp_dirents(*dir);
1.108 djm 763: *dir = xcalloc(1, sizeof(**dir));
1.49 djm 764: **dir = NULL;
765: }
766:
1.129 djm 767: return status == SSH2_FX_OK ? 0 : -1;
1.12 djm 768: }
769:
770: int
1.116 djm 771: do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir)
1.12 djm 772: {
1.23 djm 773: return(do_lsreaddir(conn, path, 0, dir));
1.12 djm 774: }
775:
776: void free_sftp_dirents(SFTP_DIRENT **s)
777: {
778: int i;
1.19 deraadt 779:
1.111 djm 780: if (s == NULL)
781: return;
1.19 deraadt 782: for (i = 0; s[i]; i++) {
1.98 djm 783: free(s[i]->filename);
784: free(s[i]->longname);
785: free(s[i]);
1.12 djm 786: }
1.98 djm 787: free(s);
1.12 djm 788: }
789:
790: int
1.116 djm 791: do_rm(struct sftp_conn *conn, const char *path)
1.1 djm 792: {
793: u_int status, id;
794:
795: debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
796:
1.23 djm 797: id = conn->msg_id++;
1.93 djm 798: send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
799: status = get_status(conn, id);
1.1 djm 800: if (status != SSH2_FX_OK)
801: error("Couldn't delete file: %s", fx2txt(status));
1.116 djm 802: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 803: }
804:
805: int
1.116 djm 806: do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag)
1.1 djm 807: {
808: u_int status, id;
809:
1.23 djm 810: id = conn->msg_id++;
1.93 djm 811: send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
1.1 djm 812: strlen(path), a);
813:
1.93 djm 814: status = get_status(conn, id);
1.105 djm 815: if (status != SSH2_FX_OK && print_flag)
1.1 djm 816: error("Couldn't create directory: %s", fx2txt(status));
817:
1.116 djm 818: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 819: }
820:
821: int
1.116 djm 822: do_rmdir(struct sftp_conn *conn, const char *path)
1.1 djm 823: {
824: u_int status, id;
825:
1.23 djm 826: id = conn->msg_id++;
1.93 djm 827: send_string_request(conn, id, SSH2_FXP_RMDIR, path,
1.23 djm 828: strlen(path));
1.1 djm 829:
1.93 djm 830: status = get_status(conn, id);
1.1 djm 831: if (status != SSH2_FX_OK)
832: error("Couldn't remove directory: %s", fx2txt(status));
833:
1.116 djm 834: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 835: }
836:
837: Attrib *
1.116 djm 838: do_stat(struct sftp_conn *conn, const char *path, int quiet)
1.1 djm 839: {
840: u_int id;
841:
1.23 djm 842: id = conn->msg_id++;
843:
1.93 djm 844: send_string_request(conn, id,
1.28 markus 845: conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
1.23 djm 846: path, strlen(path));
847:
1.93 djm 848: return(get_decode_stat(conn, id, quiet));
1.1 djm 849: }
850:
851: Attrib *
1.116 djm 852: do_lstat(struct sftp_conn *conn, const char *path, int quiet)
1.1 djm 853: {
854: u_int id;
855:
1.23 djm 856: if (conn->version == 0) {
857: if (quiet)
858: debug("Server version does not support lstat operation");
859: else
1.43 itojun 860: logit("Server version does not support lstat operation");
1.30 markus 861: return(do_stat(conn, path, quiet));
1.23 djm 862: }
863:
864: id = conn->msg_id++;
1.93 djm 865: send_string_request(conn, id, SSH2_FXP_LSTAT, path,
1.23 djm 866: strlen(path));
867:
1.93 djm 868: return(get_decode_stat(conn, id, quiet));
1.1 djm 869: }
870:
1.78 chl 871: #ifdef notyet
1.1 djm 872: Attrib *
1.116 djm 873: do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
874: int quiet)
1.1 djm 875: {
876: u_int id;
877:
1.23 djm 878: id = conn->msg_id++;
1.93 djm 879: send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
1.23 djm 880: handle_len);
881:
1.93 djm 882: return(get_decode_stat(conn, id, quiet));
1.1 djm 883: }
1.78 chl 884: #endif
1.1 djm 885:
886: int
1.116 djm 887: do_setstat(struct sftp_conn *conn, const char *path, Attrib *a)
1.1 djm 888: {
889: u_int status, id;
890:
1.23 djm 891: id = conn->msg_id++;
1.93 djm 892: send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
1.1 djm 893: strlen(path), a);
894:
1.93 djm 895: status = get_status(conn, id);
1.1 djm 896: if (status != SSH2_FX_OK)
897: error("Couldn't setstat on \"%s\": %s", path,
898: fx2txt(status));
899:
1.116 djm 900: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 901: }
902:
903: int
1.116 djm 904: do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
1.1 djm 905: Attrib *a)
906: {
907: u_int status, id;
908:
1.23 djm 909: id = conn->msg_id++;
1.93 djm 910: send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
1.1 djm 911: handle_len, a);
912:
1.93 djm 913: status = get_status(conn, id);
1.1 djm 914: if (status != SSH2_FX_OK)
915: error("Couldn't fsetstat: %s", fx2txt(status));
916:
1.116 djm 917: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 918: }
919:
920: char *
1.116 djm 921: do_realpath(struct sftp_conn *conn, const char *path)
1.1 djm 922: {
1.116 djm 923: struct sshbuf *msg;
924: u_int expected_id, count, id;
1.1 djm 925: char *filename, *longname;
1.116 djm 926: Attrib a;
927: u_char type;
928: int r;
1.1 djm 929:
1.23 djm 930: expected_id = id = conn->msg_id++;
1.93 djm 931: send_string_request(conn, id, SSH2_FXP_REALPATH, path,
1.23 djm 932: strlen(path));
1.1 djm 933:
1.116 djm 934: if ((msg = sshbuf_new()) == NULL)
1.137 djm 935: fatal_f("sshbuf_new failed");
1.1 djm 936:
1.116 djm 937: get_msg(conn, msg);
938: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
939: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 940: fatal_fr(r, "parse");
1.1 djm 941:
942: if (id != expected_id)
1.33 deraadt 943: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 944:
945: if (type == SSH2_FXP_STATUS) {
1.116 djm 946: u_int status;
1.1 djm 947:
1.116 djm 948: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 949: fatal_fr(r, "parse status");
1.107 djm 950: error("Couldn't canonicalize: %s", fx2txt(status));
1.116 djm 951: sshbuf_free(msg);
1.91 djm 952: return NULL;
1.1 djm 953: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 954: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 955: SSH2_FXP_NAME, type);
956:
1.116 djm 957: if ((r = sshbuf_get_u32(msg, &count)) != 0)
1.137 djm 958: fatal_fr(r, "parse count");
1.1 djm 959: if (count != 1)
960: fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
961:
1.116 djm 962: if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
963: (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
964: (r = decode_attrib(msg, &a)) != 0)
1.137 djm 965: fatal_fr(r, "parse filename/attrib");
1.1 djm 966:
1.97 dtucker 967: debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename,
1.116 djm 968: (unsigned long)a.size);
1.1 djm 969:
1.98 djm 970: free(longname);
1.1 djm 971:
1.116 djm 972: sshbuf_free(msg);
1.1 djm 973:
974: return(filename);
975: }
976:
977: int
1.116 djm 978: do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath,
1.102 djm 979: int force_legacy)
1.1 djm 980: {
1.116 djm 981: struct sshbuf *msg;
1.1 djm 982: u_int status, id;
1.116 djm 983: int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy;
1.1 djm 984:
1.116 djm 985: if ((msg = sshbuf_new()) == NULL)
1.137 djm 986: fatal_f("sshbuf_new failed");
1.1 djm 987:
988: /* Send rename request */
1.23 djm 989: id = conn->msg_id++;
1.102 djm 990: if (use_ext) {
1.116 djm 991: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
992: (r = sshbuf_put_u32(msg, id)) != 0 ||
993: (r = sshbuf_put_cstring(msg,
994: "posix-rename@openssh.com")) != 0)
1.137 djm 995: fatal_fr(r, "compose posix-rename");
1.81 djm 996: } else {
1.116 djm 997: if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 ||
998: (r = sshbuf_put_u32(msg, id)) != 0)
1.137 djm 999: fatal_fr(r, "compose rename");
1.116 djm 1000: }
1001: if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
1002: (r = sshbuf_put_cstring(msg, newpath)) != 0)
1.137 djm 1003: fatal_fr(r, "compose paths");
1.116 djm 1004: send_msg(conn, msg);
1.81 djm 1005: debug3("Sent message %s \"%s\" -> \"%s\"",
1.116 djm 1006: use_ext ? "posix-rename@openssh.com" :
1007: "SSH2_FXP_RENAME", oldpath, newpath);
1008: sshbuf_free(msg);
1.1 djm 1009:
1.93 djm 1010: status = get_status(conn, id);
1.1 djm 1011: if (status != SSH2_FX_OK)
1.23 djm 1012: error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
1.94 djm 1013: newpath, fx2txt(status));
1014:
1.116 djm 1015: return status == SSH2_FX_OK ? 0 : -1;
1.94 djm 1016: }
1017:
1018: int
1.116 djm 1019: do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
1.94 djm 1020: {
1.116 djm 1021: struct sshbuf *msg;
1.94 djm 1022: u_int status, id;
1.116 djm 1023: int r;
1.94 djm 1024:
1025: if ((conn->exts & SFTP_EXT_HARDLINK) == 0) {
1026: error("Server does not support hardlink@openssh.com extension");
1027: return -1;
1028: }
1029:
1.116 djm 1030: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1031: fatal_f("sshbuf_new failed");
1.95 markus 1032:
1033: /* Send link request */
1034: id = conn->msg_id++;
1.116 djm 1035: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1036: (r = sshbuf_put_u32(msg, id)) != 0 ||
1037: (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 ||
1038: (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
1039: (r = sshbuf_put_cstring(msg, newpath)) != 0)
1.137 djm 1040: fatal_fr(r, "compose");
1.116 djm 1041: send_msg(conn, msg);
1.94 djm 1042: debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"",
1.142 djm 1043: oldpath, newpath);
1.116 djm 1044: sshbuf_free(msg);
1.94 djm 1045:
1046: status = get_status(conn, id);
1047: if (status != SSH2_FX_OK)
1048: error("Couldn't link file \"%s\" to \"%s\": %s", oldpath,
1.23 djm 1049: newpath, fx2txt(status));
1.1 djm 1050:
1.116 djm 1051: return status == SSH2_FX_OK ? 0 : -1;
1.11 djm 1052: }
1053:
1054: int
1.116 djm 1055: do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath)
1.11 djm 1056: {
1.116 djm 1057: struct sshbuf *msg;
1.11 djm 1058: u_int status, id;
1.116 djm 1059: int r;
1.11 djm 1060:
1.23 djm 1061: if (conn->version < 3) {
1062: error("This server does not support the symlink operation");
1063: return(SSH2_FX_OP_UNSUPPORTED);
1064: }
1065:
1.116 djm 1066: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1067: fatal_f("sshbuf_new failed");
1.11 djm 1068:
1.48 djm 1069: /* Send symlink request */
1.23 djm 1070: id = conn->msg_id++;
1.116 djm 1071: if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 ||
1072: (r = sshbuf_put_u32(msg, id)) != 0 ||
1073: (r = sshbuf_put_cstring(msg, oldpath)) != 0 ||
1074: (r = sshbuf_put_cstring(msg, newpath)) != 0)
1.137 djm 1075: fatal_fr(r, "compose");
1.116 djm 1076: send_msg(conn, msg);
1.11 djm 1077: debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
1078: newpath);
1.116 djm 1079: sshbuf_free(msg);
1.11 djm 1080:
1.93 djm 1081: status = get_status(conn, id);
1.11 djm 1082: if (status != SSH2_FX_OK)
1.36 markus 1083: error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
1.23 djm 1084: newpath, fx2txt(status));
1.11 djm 1085:
1.116 djm 1086: return status == SSH2_FX_OK ? 0 : -1;
1.11 djm 1087: }
1088:
1.107 djm 1089: int
1.116 djm 1090: do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len)
1.107 djm 1091: {
1.116 djm 1092: struct sshbuf *msg;
1.107 djm 1093: u_int status, id;
1.116 djm 1094: int r;
1.107 djm 1095:
1096: /* Silently return if the extension is not supported */
1097: if ((conn->exts & SFTP_EXT_FSYNC) == 0)
1098: return -1;
1099:
1100: /* Send fsync request */
1.116 djm 1101: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1102: fatal_f("sshbuf_new failed");
1.107 djm 1103: id = conn->msg_id++;
1.116 djm 1104: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1105: (r = sshbuf_put_u32(msg, id)) != 0 ||
1106: (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 ||
1107: (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1.137 djm 1108: fatal_fr(r, "compose");
1.116 djm 1109: send_msg(conn, msg);
1.107 djm 1110: debug3("Sent message fsync@openssh.com I:%u", id);
1.116 djm 1111: sshbuf_free(msg);
1.107 djm 1112:
1113: status = get_status(conn, id);
1114: if (status != SSH2_FX_OK)
1115: error("Couldn't sync file: %s", fx2txt(status));
1116:
1.129 djm 1117: return status == SSH2_FX_OK ? 0 : -1;
1.107 djm 1118: }
1119:
1.78 chl 1120: #ifdef notyet
1.11 djm 1121: char *
1.116 djm 1122: do_readlink(struct sftp_conn *conn, const char *path)
1.11 djm 1123: {
1.116 djm 1124: struct sshbuf *msg;
1125: u_int expected_id, count, id;
1.11 djm 1126: char *filename, *longname;
1.116 djm 1127: Attrib a;
1128: u_char type;
1129: int r;
1.11 djm 1130:
1.23 djm 1131: expected_id = id = conn->msg_id++;
1.93 djm 1132: send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
1.11 djm 1133:
1.116 djm 1134: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1135: fatal_f("sshbuf_new failed");
1.11 djm 1136:
1.116 djm 1137: get_msg(conn, msg);
1138: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1139: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 1140: fatal_fr(r, "parse");
1.11 djm 1141:
1142: if (id != expected_id)
1.33 deraadt 1143: fatal("ID mismatch (%u != %u)", id, expected_id);
1.11 djm 1144:
1145: if (type == SSH2_FXP_STATUS) {
1.116 djm 1146: u_int status;
1.11 djm 1147:
1.116 djm 1148: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 1149: fatal_fr(r, "parse status");
1.11 djm 1150: error("Couldn't readlink: %s", fx2txt(status));
1.116 djm 1151: sshbuf_free(msg);
1.11 djm 1152: return(NULL);
1153: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 1154: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.11 djm 1155: SSH2_FXP_NAME, type);
1156:
1.116 djm 1157: if ((r = sshbuf_get_u32(msg, &count)) != 0)
1.137 djm 1158: fatal_fr(r, "parse count");
1.11 djm 1159: if (count != 1)
1160: fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
1161:
1.116 djm 1162: if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 ||
1163: (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 ||
1164: (r = decode_attrib(msg, &a)) != 0)
1.137 djm 1165: fatal_fr(r, "parse filenames/attrib");
1.11 djm 1166:
1167: debug3("SSH_FXP_READLINK %s -> %s", path, filename);
1168:
1.98 djm 1169: free(longname);
1.11 djm 1170:
1.116 djm 1171: sshbuf_free(msg);
1.11 djm 1172:
1.116 djm 1173: return filename;
1.82 djm 1174: }
1175: #endif
1176:
1177: int
1.84 dtucker 1178: do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
1.82 djm 1179: int quiet)
1180: {
1.116 djm 1181: struct sshbuf *msg;
1.82 djm 1182: u_int id;
1.116 djm 1183: int r;
1.82 djm 1184:
1185: if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
1186: error("Server does not support statvfs@openssh.com extension");
1187: return -1;
1188: }
1189:
1190: id = conn->msg_id++;
1191:
1.116 djm 1192: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1193: fatal_f("sshbuf_new failed");
1.116 djm 1194: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1195: (r = sshbuf_put_u32(msg, id)) != 0 ||
1196: (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 ||
1197: (r = sshbuf_put_cstring(msg, path)) != 0)
1.137 djm 1198: fatal_fr(r, "compose");
1.116 djm 1199: send_msg(conn, msg);
1200: sshbuf_free(msg);
1.82 djm 1201:
1.93 djm 1202: return get_decode_statvfs(conn, st, id, quiet);
1.82 djm 1203: }
1204:
1205: #ifdef notyet
1206: int
1.116 djm 1207: do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
1.84 dtucker 1208: struct sftp_statvfs *st, int quiet)
1.82 djm 1209: {
1.116 djm 1210: struct sshbuf *msg;
1.82 djm 1211: u_int id;
1212:
1213: if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
1214: error("Server does not support fstatvfs@openssh.com extension");
1215: return -1;
1216: }
1217:
1218: id = conn->msg_id++;
1219:
1.116 djm 1220: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1221: fatal_f("sshbuf_new failed");
1.116 djm 1222: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1223: (r = sshbuf_put_u32(msg, id)) != 0 ||
1224: (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 ||
1225: (r = sshbuf_put_string(msg, handle, handle_len)) != 0)
1.137 djm 1226: fatal_fr(r, "compose");
1.116 djm 1227: send_msg(conn, msg);
1228: sshbuf_free(msg);
1.82 djm 1229:
1.93 djm 1230: return get_decode_statvfs(conn, st, id, quiet);
1.1 djm 1231: }
1.78 chl 1232: #endif
1.1 djm 1233:
1.131 djm 1234: int
1235: do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a)
1236: {
1237: struct sshbuf *msg;
1238: u_int status, id;
1239: int r;
1240:
1241: if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) {
1242: error("Server does not support lsetstat@openssh.com extension");
1243: return -1;
1244: }
1245:
1246: id = conn->msg_id++;
1247: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1248: fatal_f("sshbuf_new failed");
1.131 djm 1249: if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1250: (r = sshbuf_put_u32(msg, id)) != 0 ||
1251: (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 ||
1252: (r = sshbuf_put_cstring(msg, path)) != 0 ||
1253: (r = encode_attrib(msg, a)) != 0)
1.137 djm 1254: fatal_fr(r, "compose");
1.131 djm 1255: send_msg(conn, msg);
1256: sshbuf_free(msg);
1257:
1258: status = get_status(conn, id);
1259: if (status != SSH2_FX_OK)
1260: error("Couldn't setstat on \"%s\": %s", path,
1261: fx2txt(status));
1262:
1263: return status == SSH2_FX_OK ? 0 : -1;
1264: }
1265:
1.21 djm 1266: static void
1.93 djm 1267: send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
1.116 djm 1268: u_int len, const u_char *handle, u_int handle_len)
1.21 djm 1269: {
1.116 djm 1270: struct sshbuf *msg;
1271: int r;
1.28 markus 1272:
1.116 djm 1273: if ((msg = sshbuf_new()) == NULL)
1.137 djm 1274: fatal_f("sshbuf_new failed");
1.116 djm 1275: if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 ||
1276: (r = sshbuf_put_u32(msg, id)) != 0 ||
1277: (r = sshbuf_put_string(msg, handle, handle_len)) != 0 ||
1278: (r = sshbuf_put_u64(msg, offset)) != 0 ||
1279: (r = sshbuf_put_u32(msg, len)) != 0)
1.137 djm 1280: fatal_fr(r, "compose");
1.116 djm 1281: send_msg(conn, msg);
1282: sshbuf_free(msg);
1.28 markus 1283: }
1.21 djm 1284:
1.144 djm 1285: static int
1286: send_open(struct sftp_conn *conn, const char *path, const char *tag,
1287: u_int openmode, Attrib *a, u_char **handlep, size_t *handle_lenp)
1288: {
1289: Attrib junk;
1290: u_char *handle;
1291: size_t handle_len;
1292: struct sshbuf *msg;
1293: int r;
1294: u_int id;
1295:
1296: *handlep = NULL;
1297: *handle_lenp = 0;
1298:
1299: if (a == NULL) {
1300: attrib_clear(&junk); /* Send empty attributes */
1301: a = &junk;
1302: }
1303: /* Send open request */
1304: if ((msg = sshbuf_new()) == NULL)
1305: fatal_f("sshbuf_new failed");
1306: id = conn->msg_id++;
1307: if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 ||
1308: (r = sshbuf_put_u32(msg, id)) != 0 ||
1309: (r = sshbuf_put_cstring(msg, path)) != 0 ||
1310: (r = sshbuf_put_u32(msg, openmode)) != 0 ||
1311: (r = encode_attrib(msg, a)) != 0)
1312: fatal_fr(r, "compose %s open", tag);
1313: send_msg(conn, msg);
1314: sshbuf_free(msg);
1315: debug3("Sent %s message SSH2_FXP_OPEN I:%u P:%s M:0x%04x",
1316: tag, id, path, openmode);
1317: if ((handle = get_handle(conn, id, &handle_len,
1318: "%s open(\"%s\")", tag, path)) == NULL)
1319: return -1;
1320: /* success */
1321: *handlep = handle;
1322: *handle_lenp = handle_len;
1323: return 0;
1324: }
1325:
1.1 djm 1326: int
1.116 djm 1327: do_download(struct sftp_conn *conn, const char *remote_path,
1328: const char *local_path, Attrib *a, int preserve_flag, int resume_flag,
1329: int fsync_flag)
1.1 djm 1330: {
1.116 djm 1331: struct sshbuf *msg;
1332: u_char *handle;
1333: int local_fd = -1, write_error;
1.134 djm 1334: int read_error, write_errno, lmodified = 0, reordered = 0, r;
1.101 djm 1335: u_int64_t offset = 0, size, highwater;
1.116 djm 1336: u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK;
1.39 fgsch 1337: off_t progress_counter;
1.116 djm 1338: size_t handle_len;
1.101 djm 1339: struct stat st;
1.146 djm 1340: struct requests requests;
1.21 djm 1341: struct request *req;
1.116 djm 1342: u_char type;
1.21 djm 1343:
1344: TAILQ_INIT(&requests);
1.1 djm 1345:
1.89 djm 1346: if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
1347: return -1;
1.1 djm 1348:
1.86 djm 1349: /* Do not preserve set[ug]id here, as we do not preserve ownership */
1.1 djm 1350: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1.38 djm 1351: mode = a->perm & 0777;
1.1 djm 1352: else
1353: mode = 0666;
1354:
1.14 djm 1355: if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1.41 djm 1356: (!S_ISREG(a->perm))) {
1357: error("Cannot download non-regular file: %s", remote_path);
1.14 djm 1358: return(-1);
1359: }
1360:
1.21 djm 1361: if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
1362: size = a->size;
1363: else
1364: size = 0;
1365:
1.141 djm 1366: buflen = conn->download_buflen;
1.1 djm 1367:
1368: /* Send open request */
1.144 djm 1369: if (send_open(conn, remote_path, "remote", SSH2_FXF_READ, NULL,
1370: &handle, &handle_len) != 0)
1371: return -1;
1.1 djm 1372:
1.105 djm 1373: local_fd = open(local_path,
1374: O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR);
1.23 djm 1375: if (local_fd == -1) {
1376: error("Couldn't open local file \"%s\" for writing: %s",
1377: local_path, strerror(errno));
1.101 djm 1378: goto fail;
1379: }
1380: offset = highwater = 0;
1.105 djm 1381: if (resume_flag) {
1.101 djm 1382: if (fstat(local_fd, &st) == -1) {
1383: error("Unable to stat local file \"%s\": %s",
1384: local_path, strerror(errno));
1385: goto fail;
1386: }
1.113 djm 1387: if (st.st_size < 0) {
1388: error("\"%s\" has negative size", local_path);
1389: goto fail;
1390: }
1391: if ((u_int64_t)st.st_size > size) {
1.101 djm 1392: error("Unable to resume download of \"%s\": "
1393: "local file is larger than remote", local_path);
1394: fail:
1395: do_close(conn, handle, handle_len);
1396: free(handle);
1.110 djm 1397: if (local_fd != -1)
1398: close(local_fd);
1.101 djm 1399: return -1;
1400: }
1401: offset = highwater = st.st_size;
1.23 djm 1402: }
1403:
1.1 djm 1404: /* Read from remote and write to local */
1.101 djm 1405: write_error = read_error = write_errno = num_req = 0;
1.21 djm 1406: max_req = 1;
1.101 djm 1407: progress_counter = offset;
1.39 fgsch 1408:
1.47 djm 1409: if (showprogress && size != 0)
1410: start_progress_meter(remote_path, size, &progress_counter);
1.39 fgsch 1411:
1.144 djm 1412: if ((msg = sshbuf_new()) == NULL)
1413: fatal_f("sshbuf_new failed");
1414:
1.21 djm 1415: while (num_req > 0 || max_req > 0) {
1.116 djm 1416: u_char *data;
1417: size_t len;
1.1 djm 1418:
1.49 djm 1419: /*
1.51 deraadt 1420: * Simulate EOF on interrupt: stop sending new requests and
1.49 djm 1421: * allow outstanding requests to drain gracefully
1422: */
1423: if (interrupted) {
1424: if (num_req == 0) /* If we haven't started yet... */
1425: break;
1426: max_req = 0;
1427: }
1428:
1.21 djm 1429: /* Send some more requests */
1430: while (num_req < max_req) {
1.28 markus 1431: debug3("Request range %llu -> %llu (%d/%d)",
1.25 itojun 1432: (unsigned long long)offset,
1433: (unsigned long long)offset + buflen - 1,
1434: num_req, max_req);
1.108 djm 1435: req = xcalloc(1, sizeof(*req));
1.23 djm 1436: req->id = conn->msg_id++;
1.21 djm 1437: req->len = buflen;
1438: req->offset = offset;
1439: offset += buflen;
1440: num_req++;
1441: TAILQ_INSERT_TAIL(&requests, req, tq);
1.93 djm 1442: send_read_request(conn, req->id, req->offset,
1.21 djm 1443: req->len, handle, handle_len);
1444: }
1.1 djm 1445:
1.116 djm 1446: sshbuf_reset(msg);
1447: get_msg(conn, msg);
1448: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1449: (r = sshbuf_get_u32(msg, &id)) != 0)
1.137 djm 1450: fatal_fr(r, "parse");
1.33 deraadt 1451: debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1.21 djm 1452:
1453: /* Find the request in our queue */
1.53 deraadt 1454: for (req = TAILQ_FIRST(&requests);
1.21 djm 1455: req != NULL && req->id != id;
1456: req = TAILQ_NEXT(req, tq))
1457: ;
1458: if (req == NULL)
1459: fatal("Unexpected reply %u", id);
1460:
1461: switch (type) {
1462: case SSH2_FXP_STATUS:
1.116 djm 1463: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 1464: fatal_fr(r, "parse status");
1.21 djm 1465: if (status != SSH2_FX_EOF)
1466: read_error = 1;
1467: max_req = 0;
1468: TAILQ_REMOVE(&requests, req, tq);
1.98 djm 1469: free(req);
1.21 djm 1470: num_req--;
1471: break;
1472: case SSH2_FXP_DATA:
1.116 djm 1473: if ((r = sshbuf_get_string(msg, &data, &len)) != 0)
1.137 djm 1474: fatal_fr(r, "parse data");
1.26 itojun 1475: debug3("Received data %llu -> %llu",
1.28 markus 1476: (unsigned long long)req->offset,
1.26 itojun 1477: (unsigned long long)req->offset + len - 1);
1.21 djm 1478: if (len > req->len)
1479: fatal("Received more data than asked for "
1.116 djm 1480: "%zu > %zu", len, req->len);
1.134 djm 1481: lmodified = 1;
1.21 djm 1482: if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1.44 deraadt 1483: atomicio(vwrite, local_fd, data, len) != len) &&
1.21 djm 1484: !write_error) {
1485: write_errno = errno;
1486: write_error = 1;
1487: max_req = 0;
1488: }
1.101 djm 1489: else if (!reordered && req->offset <= highwater)
1490: highwater = req->offset + len;
1491: else if (!reordered && req->offset > highwater)
1492: reordered = 1;
1.39 fgsch 1493: progress_counter += len;
1.98 djm 1494: free(data);
1.1 djm 1495:
1.21 djm 1496: if (len == req->len) {
1497: TAILQ_REMOVE(&requests, req, tq);
1.98 djm 1498: free(req);
1.21 djm 1499: num_req--;
1500: } else {
1501: /* Resend the request for the missing data */
1502: debug3("Short data block, re-requesting "
1.26 itojun 1503: "%llu -> %llu (%2d)",
1.28 markus 1504: (unsigned long long)req->offset + len,
1.27 itojun 1505: (unsigned long long)req->offset +
1506: req->len - 1, num_req);
1.23 djm 1507: req->id = conn->msg_id++;
1.21 djm 1508: req->len -= len;
1509: req->offset += len;
1.93 djm 1510: send_read_request(conn, req->id,
1.23 djm 1511: req->offset, req->len, handle, handle_len);
1.21 djm 1512: /* Reduce the request size */
1513: if (len < buflen)
1.125 deraadt 1514: buflen = MAXIMUM(MIN_READ_SIZE, len);
1.21 djm 1515: }
1516: if (max_req > 0) { /* max_req = 0 iff EOF received */
1517: if (size > 0 && offset > size) {
1518: /* Only one request at a time
1519: * after the expected EOF */
1520: debug3("Finish at %llu (%2d)",
1.26 itojun 1521: (unsigned long long)offset,
1522: num_req);
1.21 djm 1523: max_req = 1;
1.136 djm 1524: } else if (max_req < conn->num_requests) {
1.21 djm 1525: ++max_req;
1526: }
1.1 djm 1527: }
1.21 djm 1528: break;
1529: default:
1.33 deraadt 1530: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1.1 djm 1531: SSH2_FXP_DATA, type);
1532: }
1.21 djm 1533: }
1.1 djm 1534:
1.39 fgsch 1535: if (showprogress && size)
1536: stop_progress_meter();
1537:
1.21 djm 1538: /* Sanity check */
1539: if (TAILQ_FIRST(&requests) != NULL)
1540: fatal("Transfer complete, but requests still in queue");
1.101 djm 1541: /* Truncate at highest contiguous point to avoid holes on interrupt */
1542: if (read_error || write_error || interrupted) {
1.105 djm 1543: if (reordered && resume_flag) {
1.101 djm 1544: error("Unable to resume download of \"%s\": "
1545: "server reordered requests", local_path);
1546: }
1547: debug("truncating at %llu", (unsigned long long)highwater);
1.120 djm 1548: if (ftruncate(local_fd, highwater) == -1)
1549: error("ftruncate \"%s\": %s", local_path,
1550: strerror(errno));
1.101 djm 1551: }
1.21 djm 1552: if (read_error) {
1.28 markus 1553: error("Couldn't read from remote file \"%s\" : %s",
1.21 djm 1554: remote_path, fx2txt(status));
1.103 djm 1555: status = -1;
1.23 djm 1556: do_close(conn, handle, handle_len);
1.21 djm 1557: } else if (write_error) {
1558: error("Couldn't write to \"%s\": %s", local_path,
1559: strerror(write_errno));
1.116 djm 1560: status = SSH2_FX_FAILURE;
1.23 djm 1561: do_close(conn, handle, handle_len);
1.21 djm 1562: } else {
1.116 djm 1563: if (do_close(conn, handle, handle_len) != 0 || interrupted)
1564: status = SSH2_FX_FAILURE;
1565: else
1566: status = SSH2_FX_OK;
1.21 djm 1567: /* Override umask and utimes if asked */
1.105 djm 1568: if (preserve_flag && fchmod(local_fd, mode) == -1)
1.21 djm 1569: error("Couldn't set mode on \"%s\": %s", local_path,
1.37 deraadt 1570: strerror(errno));
1.105 djm 1571: if (preserve_flag &&
1572: (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1.21 djm 1573: struct timeval tv[2];
1574: tv[0].tv_sec = a->atime;
1575: tv[1].tv_sec = a->mtime;
1576: tv[0].tv_usec = tv[1].tv_usec = 0;
1577: if (utimes(local_path, tv) == -1)
1578: error("Can't set times on \"%s\": %s",
1.37 deraadt 1579: local_path, strerror(errno));
1.1 djm 1580: }
1.134 djm 1581: if (resume_flag && !lmodified)
1582: logit("File \"%s\" was not modified", local_path);
1583: else if (fsync_flag) {
1.107 djm 1584: debug("syncing \"%s\"", local_path);
1585: if (fsync(local_fd) == -1)
1586: error("Couldn't sync file \"%s\": %s",
1587: local_path, strerror(errno));
1588: }
1.10 djm 1589: }
1.5 djm 1590: close(local_fd);
1.116 djm 1591: sshbuf_free(msg);
1.98 djm 1592: free(handle);
1.23 djm 1593:
1.129 djm 1594: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 1595: }
1596:
1.89 djm 1597: static int
1.116 djm 1598: download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1599: int depth, Attrib *dirattrib, int preserve_flag, int print_flag,
1600: int resume_flag, int fsync_flag)
1.89 djm 1601: {
1602: int i, ret = 0;
1603: SFTP_DIRENT **dir_entries;
1.130 djm 1604: char *filename, *new_src = NULL, *new_dst = NULL;
1.138 dtucker 1605: mode_t mode = 0777, tmpmode = mode;
1.89 djm 1606:
1607: if (depth >= MAX_DIR_DEPTH) {
1608: error("Maximum directory depth exceeded: %d levels", depth);
1609: return -1;
1610: }
1611:
1612: if (dirattrib == NULL &&
1613: (dirattrib = do_stat(conn, src, 1)) == NULL) {
1614: error("Unable to stat remote directory \"%s\"", src);
1615: return -1;
1616: }
1617: if (!S_ISDIR(dirattrib->perm)) {
1618: error("\"%s\" is not a directory", src);
1619: return -1;
1620: }
1.147 djm 1621: if (print_flag && print_flag != SFTP_PROGRESS_ONLY)
1.124 schwarze 1622: mprintf("Retrieving %s\n", src);
1.89 djm 1623:
1.138 dtucker 1624: if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1.89 djm 1625: mode = dirattrib->perm & 01777;
1.138 dtucker 1626: tmpmode = mode | (S_IWUSR|S_IXUSR);
1627: } else {
1.89 djm 1628: debug("Server did not send permissions for "
1629: "directory \"%s\"", dst);
1630: }
1631:
1.138 dtucker 1632: if (mkdir(dst, tmpmode) == -1 && errno != EEXIST) {
1.89 djm 1633: error("mkdir %s: %s", dst, strerror(errno));
1634: return -1;
1635: }
1636:
1637: if (do_readdir(conn, src, &dir_entries) == -1) {
1638: error("%s: Failed to get directory contents", src);
1639: return -1;
1640: }
1641:
1642: for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1.130 djm 1643: free(new_dst);
1644: free(new_src);
1645:
1.89 djm 1646: filename = dir_entries[i]->filename;
1647: new_dst = path_append(dst, filename);
1648: new_src = path_append(src, filename);
1649:
1650: if (S_ISDIR(dir_entries[i]->a.perm)) {
1651: if (strcmp(filename, ".") == 0 ||
1652: strcmp(filename, "..") == 0)
1653: continue;
1654: if (download_dir_internal(conn, new_src, new_dst,
1.105 djm 1655: depth + 1, &(dir_entries[i]->a), preserve_flag,
1.107 djm 1656: print_flag, resume_flag, fsync_flag) == -1)
1.89 djm 1657: ret = -1;
1658: } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1659: if (do_download(conn, new_src, new_dst,
1.107 djm 1660: &(dir_entries[i]->a), preserve_flag,
1661: resume_flag, fsync_flag) == -1) {
1.89 djm 1662: error("Download of file %s to %s failed",
1663: new_src, new_dst);
1664: ret = -1;
1665: }
1666: } else
1667: logit("%s: not a regular file\n", new_src);
1668:
1669: }
1.130 djm 1670: free(new_dst);
1671: free(new_src);
1.89 djm 1672:
1.105 djm 1673: if (preserve_flag) {
1.89 djm 1674: if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1675: struct timeval tv[2];
1676: tv[0].tv_sec = dirattrib->atime;
1677: tv[1].tv_sec = dirattrib->mtime;
1678: tv[0].tv_usec = tv[1].tv_usec = 0;
1679: if (utimes(dst, tv) == -1)
1680: error("Can't set times on \"%s\": %s",
1681: dst, strerror(errno));
1682: } else
1683: debug("Server did not send times for directory "
1684: "\"%s\"", dst);
1685: }
1686:
1.138 dtucker 1687: if (mode != tmpmode && chmod(dst, mode) == -1)
1688: error("Can't set final mode on \"%s\": %s", dst,
1689: strerror(errno));
1690:
1.89 djm 1691: free_sftp_dirents(dir_entries);
1692:
1693: return ret;
1694: }
1695:
1696: int
1.116 djm 1697: download_dir(struct sftp_conn *conn, const char *src, const char *dst,
1698: Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag,
1699: int fsync_flag)
1.89 djm 1700: {
1701: char *src_canon;
1702: int ret;
1703:
1704: if ((src_canon = do_realpath(conn, src)) == NULL) {
1.107 djm 1705: error("Unable to canonicalize path \"%s\"", src);
1.89 djm 1706: return -1;
1707: }
1708:
1.105 djm 1709: ret = download_dir_internal(conn, src_canon, dst, 0,
1.107 djm 1710: dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag);
1.98 djm 1711: free(src_canon);
1.89 djm 1712: return ret;
1713: }
1714:
1.1 djm 1715: int
1.116 djm 1716: do_upload(struct sftp_conn *conn, const char *local_path,
1717: const char *remote_path, int preserve_flag, int resume, int fsync_flag)
1.1 djm 1718: {
1.116 djm 1719: int r, local_fd;
1720: u_int status = SSH2_FX_OK;
1721: u_int id;
1722: u_char type;
1.100 dtucker 1723: off_t offset, progress_counter;
1.116 djm 1724: u_char *handle, *data;
1725: struct sshbuf *msg;
1.1 djm 1726: struct stat sb;
1.115 logan 1727: Attrib a, *c = NULL;
1.21 djm 1728: u_int32_t startid;
1729: u_int32_t ackid;
1.22 djm 1730: struct outstanding_ack {
1731: u_int id;
1732: u_int len;
1.77 djm 1733: off_t offset;
1.28 markus 1734: TAILQ_ENTRY(outstanding_ack) tq;
1.22 djm 1735: };
1736: TAILQ_HEAD(ackhead, outstanding_ack) acks;
1.50 pedro 1737: struct outstanding_ack *ack = NULL;
1.116 djm 1738: size_t handle_len;
1.22 djm 1739:
1740: TAILQ_INIT(&acks);
1.1 djm 1741:
1742: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1743: error("Couldn't open local file \"%s\" for reading: %s",
1744: local_path, strerror(errno));
1745: return(-1);
1746: }
1747: if (fstat(local_fd, &sb) == -1) {
1748: error("Couldn't fstat local file \"%s\": %s",
1749: local_path, strerror(errno));
1.41 djm 1750: close(local_fd);
1751: return(-1);
1752: }
1753: if (!S_ISREG(sb.st_mode)) {
1754: error("%s is not a regular file", local_path);
1.1 djm 1755: close(local_fd);
1756: return(-1);
1757: }
1758: stat_to_attrib(&sb, &a);
1759:
1760: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1761: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1762: a.perm &= 0777;
1.105 djm 1763: if (!preserve_flag)
1.1 djm 1764: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1765:
1.115 logan 1766: if (resume) {
1767: /* Get remote file size if it exists */
1768: if ((c = do_stat(conn, remote_path, 0)) == NULL) {
1.122 djm 1769: close(local_fd);
1.115 logan 1770: return -1;
1771: }
1772:
1773: if ((off_t)c->size >= sb.st_size) {
1774: error("destination file bigger or same size as "
1.142 djm 1775: "source file");
1.115 logan 1776: close(local_fd);
1777: return -1;
1778: }
1779:
1780: if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) {
1781: close(local_fd);
1782: return -1;
1783: }
1784: }
1785:
1.1 djm 1786: /* Send open request */
1.144 djm 1787: if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT|
1788: (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC),
1789: &a, &handle, &handle_len) != 0) {
1.1 djm 1790: close(local_fd);
1.80 djm 1791: return -1;
1.1 djm 1792: }
1793:
1.144 djm 1794: id = conn->msg_id;
1.21 djm 1795: startid = ackid = id + 1;
1.141 djm 1796: data = xmalloc(conn->upload_buflen);
1.20 djm 1797:
1.1 djm 1798: /* Read from local and write to remote */
1.115 logan 1799: offset = progress_counter = (resume ? c->size : 0);
1.39 fgsch 1800: if (showprogress)
1.100 dtucker 1801: start_progress_meter(local_path, sb.st_size,
1802: &progress_counter);
1.39 fgsch 1803:
1.144 djm 1804: if ((msg = sshbuf_new()) == NULL)
1805: fatal_f("sshbuf_new failed");
1.19 deraadt 1806: for (;;) {
1.1 djm 1807: int len;
1808:
1809: /*
1.51 deraadt 1810: * Can't use atomicio here because it returns 0 on EOF,
1.49 djm 1811: * thus losing the last block of the file.
1.51 deraadt 1812: * Simulate an EOF on interrupt, allowing ACKs from the
1.49 djm 1813: * server to drain.
1.1 djm 1814: */
1.80 djm 1815: if (interrupted || status != SSH2_FX_OK)
1.49 djm 1816: len = 0;
1817: else do
1.141 djm 1818: len = read(local_fd, data, conn->upload_buflen);
1.1 djm 1819: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1820:
1821: if (len == -1)
1822: fatal("Couldn't read from \"%s\": %s", local_path,
1823: strerror(errno));
1.21 djm 1824:
1825: if (len != 0) {
1.108 djm 1826: ack = xcalloc(1, sizeof(*ack));
1.22 djm 1827: ack->id = ++id;
1828: ack->offset = offset;
1829: ack->len = len;
1830: TAILQ_INSERT_TAIL(&acks, ack, tq);
1831:
1.116 djm 1832: sshbuf_reset(msg);
1833: if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 ||
1834: (r = sshbuf_put_u32(msg, ack->id)) != 0 ||
1835: (r = sshbuf_put_string(msg, handle,
1836: handle_len)) != 0 ||
1837: (r = sshbuf_put_u64(msg, offset)) != 0 ||
1838: (r = sshbuf_put_string(msg, data, len)) != 0)
1.137 djm 1839: fatal_fr(r, "compose");
1.116 djm 1840: send_msg(conn, msg);
1.33 deraadt 1841: debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1.37 deraadt 1842: id, (unsigned long long)offset, len);
1.22 djm 1843: } else if (TAILQ_FIRST(&acks) == NULL)
1.1 djm 1844: break;
1845:
1.22 djm 1846: if (ack == NULL)
1847: fatal("Unexpected ACK %u", id);
1848:
1.28 markus 1849: if (id == startid || len == 0 ||
1.23 djm 1850: id - ackid >= conn->num_requests) {
1.116 djm 1851: u_int rid;
1.31 djm 1852:
1.116 djm 1853: sshbuf_reset(msg);
1854: get_msg(conn, msg);
1855: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
1856: (r = sshbuf_get_u32(msg, &rid)) != 0)
1.137 djm 1857: fatal_fr(r, "parse");
1.22 djm 1858:
1859: if (type != SSH2_FXP_STATUS)
1860: fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1861: "got %d", SSH2_FXP_STATUS, type);
1862:
1.116 djm 1863: if ((r = sshbuf_get_u32(msg, &status)) != 0)
1.137 djm 1864: fatal_fr(r, "parse status");
1.116 djm 1865: debug3("SSH2_FXP_STATUS %u", status);
1.22 djm 1866:
1867: /* Find the request in our queue */
1.53 deraadt 1868: for (ack = TAILQ_FIRST(&acks);
1.116 djm 1869: ack != NULL && ack->id != rid;
1.22 djm 1870: ack = TAILQ_NEXT(ack, tq))
1871: ;
1872: if (ack == NULL)
1.116 djm 1873: fatal("Can't find request for ID %u", rid);
1.22 djm 1874: TAILQ_REMOVE(&acks, ack, tq);
1.77 djm 1875: debug3("In write loop, ack for %u %u bytes at %lld",
1876: ack->id, ack->len, (long long)ack->offset);
1.21 djm 1877: ++ackid;
1.100 dtucker 1878: progress_counter += ack->len;
1.98 djm 1879: free(ack);
1.1 djm 1880: }
1881: offset += len;
1.77 djm 1882: if (offset < 0)
1.137 djm 1883: fatal_f("offset < 0");
1.1 djm 1884: }
1.116 djm 1885: sshbuf_free(msg);
1.80 djm 1886:
1.39 fgsch 1887: if (showprogress)
1888: stop_progress_meter();
1.98 djm 1889: free(data);
1.1 djm 1890:
1.80 djm 1891: if (status != SSH2_FX_OK) {
1892: error("Couldn't write to remote file \"%s\": %s",
1893: remote_path, fx2txt(status));
1.116 djm 1894: status = SSH2_FX_FAILURE;
1.80 djm 1895: }
1896:
1.1 djm 1897: if (close(local_fd) == -1) {
1898: error("Couldn't close local file \"%s\": %s", local_path,
1899: strerror(errno));
1.116 djm 1900: status = SSH2_FX_FAILURE;
1.1 djm 1901: }
1902:
1.10 djm 1903: /* Override umask and utimes if asked */
1.105 djm 1904: if (preserve_flag)
1.23 djm 1905: do_fsetstat(conn, handle, handle_len, &a);
1.10 djm 1906:
1.107 djm 1907: if (fsync_flag)
1908: (void)do_fsync(conn, handle, handle_len);
1909:
1.121 djm 1910: if (do_close(conn, handle, handle_len) != 0)
1.116 djm 1911: status = SSH2_FX_FAILURE;
1912:
1.98 djm 1913: free(handle);
1.5 djm 1914:
1.116 djm 1915: return status == SSH2_FX_OK ? 0 : -1;
1.1 djm 1916: }
1.89 djm 1917:
1918: static int
1.116 djm 1919: upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst,
1920: int depth, int preserve_flag, int print_flag, int resume, int fsync_flag)
1.89 djm 1921: {
1.116 djm 1922: int ret = 0;
1.89 djm 1923: DIR *dirp;
1924: struct dirent *dp;
1.130 djm 1925: char *filename, *new_src = NULL, *new_dst = NULL;
1.89 djm 1926: struct stat sb;
1.121 djm 1927: Attrib a, *dirattrib;
1.138 dtucker 1928: u_int32_t saved_perm;
1.89 djm 1929:
1930: if (depth >= MAX_DIR_DEPTH) {
1931: error("Maximum directory depth exceeded: %d levels", depth);
1932: return -1;
1933: }
1934:
1935: if (stat(src, &sb) == -1) {
1936: error("Couldn't stat directory \"%s\": %s",
1937: src, strerror(errno));
1938: return -1;
1939: }
1940: if (!S_ISDIR(sb.st_mode)) {
1941: error("\"%s\" is not a directory", src);
1942: return -1;
1943: }
1.147 djm 1944: if (print_flag && print_flag != SFTP_PROGRESS_ONLY)
1.124 schwarze 1945: mprintf("Entering %s\n", src);
1.89 djm 1946:
1947: attrib_clear(&a);
1948: stat_to_attrib(&sb, &a);
1949: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1950: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1951: a.perm &= 01777;
1.105 djm 1952: if (!preserve_flag)
1.89 djm 1953: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1.101 djm 1954:
1.89 djm 1955: /*
1.121 djm 1956: * sftp lacks a portable status value to match errno EEXIST,
1957: * so if we get a failure back then we must check whether
1.138 dtucker 1958: * the path already existed and is a directory. Ensure we can
1959: * write to the directory we create for the duration of the transfer.
1.89 djm 1960: */
1.138 dtucker 1961: saved_perm = a.perm;
1962: a.perm |= (S_IWUSR|S_IXUSR);
1.121 djm 1963: if (do_mkdir(conn, dst, &a, 0) != 0) {
1964: if ((dirattrib = do_stat(conn, dst, 0)) == NULL)
1.89 djm 1965: return -1;
1.121 djm 1966: if (!S_ISDIR(dirattrib->perm)) {
1967: error("\"%s\" exists but is not a directory", dst);
1.89 djm 1968: return -1;
1.121 djm 1969: }
1.89 djm 1970: }
1.138 dtucker 1971: a.perm = saved_perm;
1.89 djm 1972:
1973: if ((dirp = opendir(src)) == NULL) {
1974: error("Failed to open dir \"%s\": %s", src, strerror(errno));
1975: return -1;
1976: }
1.101 djm 1977:
1.89 djm 1978: while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1979: if (dp->d_ino == 0)
1980: continue;
1.130 djm 1981: free(new_dst);
1982: free(new_src);
1.89 djm 1983: filename = dp->d_name;
1984: new_dst = path_append(dst, filename);
1985: new_src = path_append(src, filename);
1986:
1.90 dtucker 1987: if (lstat(new_src, &sb) == -1) {
1988: logit("%s: lstat failed: %s", filename,
1989: strerror(errno));
1990: ret = -1;
1991: } else if (S_ISDIR(sb.st_mode)) {
1.89 djm 1992: if (strcmp(filename, ".") == 0 ||
1993: strcmp(filename, "..") == 0)
1994: continue;
1995:
1996: if (upload_dir_internal(conn, new_src, new_dst,
1.115 logan 1997: depth + 1, preserve_flag, print_flag, resume,
1.107 djm 1998: fsync_flag) == -1)
1.89 djm 1999: ret = -1;
1.90 dtucker 2000: } else if (S_ISREG(sb.st_mode)) {
1.105 djm 2001: if (do_upload(conn, new_src, new_dst,
1.115 logan 2002: preserve_flag, resume, fsync_flag) == -1) {
1.89 djm 2003: error("Uploading of file %s to %s failed!",
2004: new_src, new_dst);
2005: ret = -1;
2006: }
2007: } else
2008: logit("%s: not a regular file\n", filename);
2009: }
1.130 djm 2010: free(new_dst);
2011: free(new_src);
1.89 djm 2012:
2013: do_setstat(conn, dst, &a);
2014:
2015: (void) closedir(dirp);
2016: return ret;
2017: }
2018:
2019: int
1.116 djm 2020: upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
2021: int preserve_flag, int print_flag, int resume, int fsync_flag)
1.89 djm 2022: {
2023: char *dst_canon;
2024: int ret;
2025:
2026: if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1.107 djm 2027: error("Unable to canonicalize path \"%s\"", dst);
1.89 djm 2028: return -1;
2029: }
2030:
1.106 djm 2031: ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag,
1.115 logan 2032: print_flag, resume, fsync_flag);
1.107 djm 2033:
1.98 djm 2034: free(dst_canon);
1.145 djm 2035: return ret;
2036: }
2037:
2038: static void
2039: handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous,
2040: u_int *nreqsp, u_int *write_errorp)
2041: {
2042: struct sshbuf *msg;
2043: u_char type;
2044: u_int id, status;
2045: int r;
2046: struct pollfd pfd;
2047:
2048: if ((msg = sshbuf_new()) == NULL)
2049: fatal_f("sshbuf_new failed");
2050:
2051: /* Try to eat replies from the upload side */
2052: while (*nreqsp > 0) {
2053: debug3_f("%u outstanding replies", *nreqsp);
2054: if (!synchronous) {
2055: /* Bail out if no data is ready to be read */
2056: pfd.fd = to->fd_in;
2057: pfd.events = POLLIN;
2058: if ((r = poll(&pfd, 1, 0)) == -1) {
2059: if (errno == EINTR)
2060: break;
2061: fatal_f("poll: %s", strerror(errno));
2062: } else if (r == 0)
2063: break; /* fd not ready */
2064: }
2065: sshbuf_reset(msg);
2066: get_msg(to, msg);
2067:
2068: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
2069: (r = sshbuf_get_u32(msg, &id)) != 0)
2070: fatal_fr(r, "dest parse");
2071: debug3("Received dest reply T:%u I:%u R:%u", type, id, *nreqsp);
2072: if (type != SSH2_FXP_STATUS) {
2073: fatal_f("Expected SSH2_FXP_STATUS(%d) packet, got %d",
2074: SSH2_FXP_STATUS, type);
2075: }
2076: if ((r = sshbuf_get_u32(msg, &status)) != 0)
2077: fatal_fr(r, "parse dest status");
2078: debug3("dest SSH2_FXP_STATUS %u", status);
2079: if (status != SSH2_FX_OK) {
2080: /* record first error */
2081: if (*write_errorp == 0)
2082: *write_errorp = status;
2083: }
2084: /*
2085: * XXX this doesn't do full reply matching like do_upload and
2086: * so cannot gracefully truncate terminated uploads at a
2087: * high-water mark. ATM the only caller of this function (scp)
2088: * doesn't support transfer resumption, so this doesn't matter
2089: * a whole lot.
2090: *
2091: * To be safe, do_crossload truncates the destination file to
2092: * zero length on upload failure, since we can't trust the
2093: * server not to have reordered replies that could have
2094: * inserted holes where none existed in the source file.
2095: *
2096: * XXX we could get a more accutate progress bar if we updated
2097: * the counter based on the reply from the destination...
2098: */
2099: (*nreqsp)--;
2100: }
2101: debug3_f("done: %u outstanding replies", *nreqsp);
2102: }
2103:
2104: int
2105: do_crossload(struct sftp_conn *from, struct sftp_conn *to,
2106: const char *from_path, const char *to_path,
2107: Attrib *a, int preserve_flag)
2108: {
2109: struct sshbuf *msg;
2110: int write_error, read_error, lmodified = 0, r;
2111: u_int64_t offset = 0, size;
2112: u_int id, buflen, num_req, max_req, status = SSH2_FX_OK;
2113: u_int num_upload_req;
2114: off_t progress_counter;
2115: u_char *from_handle, *to_handle;
2116: size_t from_handle_len, to_handle_len;
1.146 djm 2117: struct requests requests;
1.145 djm 2118: struct request *req;
2119: u_char type;
2120:
2121: TAILQ_INIT(&requests);
2122:
2123: if (a == NULL && (a = do_stat(from, from_path, 0)) == NULL)
2124: return -1;
2125:
2126: if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
2127: (!S_ISREG(a->perm))) {
2128: error("Cannot download non-regular file: %s", from_path);
2129: return(-1);
2130: }
2131: if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
2132: size = a->size;
2133: else
2134: size = 0;
2135:
2136: buflen = from->download_buflen;
2137: if (buflen > to->upload_buflen)
2138: buflen = to->upload_buflen;
2139:
2140: /* Send open request to read side */
2141: if (send_open(from, from_path, "origin", SSH2_FXF_READ, NULL,
2142: &from_handle, &from_handle_len) != 0)
2143: return -1;
2144:
2145: /* Send open request to write side */
2146: a->flags &= ~SSH2_FILEXFER_ATTR_SIZE;
2147: a->flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
2148: a->perm &= 0777;
2149: if (!preserve_flag)
2150: a->flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
2151: if (send_open(to, to_path, "dest",
2152: SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a,
2153: &to_handle, &to_handle_len) != 0) {
2154: do_close(from, from_handle, from_handle_len);
2155: return -1;
2156: }
2157:
2158: /* Read from remote "from" and write to remote "to" */
2159: offset = 0;
2160: write_error = read_error = num_req = num_upload_req = 0;
2161: max_req = 1;
2162: progress_counter = 0;
2163:
2164: if (showprogress && size != 0)
2165: start_progress_meter(from_path, size, &progress_counter);
2166: if ((msg = sshbuf_new()) == NULL)
2167: fatal_f("sshbuf_new failed");
2168: while (num_req > 0 || max_req > 0) {
2169: u_char *data;
2170: size_t len;
2171:
2172: /*
2173: * Simulate EOF on interrupt: stop sending new requests and
2174: * allow outstanding requests to drain gracefully
2175: */
2176: if (interrupted) {
2177: if (num_req == 0) /* If we haven't started yet... */
2178: break;
2179: max_req = 0;
2180: }
2181:
2182: /* Send some more requests */
2183: while (num_req < max_req) {
2184: debug3("Request range %llu -> %llu (%d/%d)",
2185: (unsigned long long)offset,
2186: (unsigned long long)offset + buflen - 1,
2187: num_req, max_req);
2188: req = xcalloc(1, sizeof(*req));
2189: req->id = from->msg_id++;
2190: req->len = buflen;
2191: req->offset = offset;
2192: offset += buflen;
2193: num_req++;
2194: TAILQ_INSERT_TAIL(&requests, req, tq);
2195: send_read_request(from, req->id, req->offset,
2196: req->len, from_handle, from_handle_len);
2197: }
2198:
2199: /* Try to eat replies from the upload side (nonblocking) */
2200: handle_dest_replies(to, to_path, 0,
2201: &num_upload_req, &write_error);
2202:
2203: sshbuf_reset(msg);
2204: get_msg(from, msg);
2205: if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
2206: (r = sshbuf_get_u32(msg, &id)) != 0)
2207: fatal_fr(r, "parse");
2208: debug3("Received origin reply T:%u I:%u R:%d",
2209: type, id, max_req);
2210:
2211: /* Find the request in our queue */
2212: for (req = TAILQ_FIRST(&requests);
2213: req != NULL && req->id != id;
2214: req = TAILQ_NEXT(req, tq))
2215: ;
2216: if (req == NULL)
2217: fatal("Unexpected reply %u", id);
2218:
2219: switch (type) {
2220: case SSH2_FXP_STATUS:
2221: if ((r = sshbuf_get_u32(msg, &status)) != 0)
2222: fatal_fr(r, "parse status");
2223: if (status != SSH2_FX_EOF)
2224: read_error = 1;
2225: max_req = 0;
2226: TAILQ_REMOVE(&requests, req, tq);
2227: free(req);
2228: num_req--;
2229: break;
2230: case SSH2_FXP_DATA:
2231: if ((r = sshbuf_get_string(msg, &data, &len)) != 0)
2232: fatal_fr(r, "parse data");
2233: debug3("Received data %llu -> %llu",
2234: (unsigned long long)req->offset,
2235: (unsigned long long)req->offset + len - 1);
2236: if (len > req->len)
2237: fatal("Received more data than asked for "
2238: "%zu > %zu", len, req->len);
2239: lmodified = 1;
2240:
2241: /* Write this chunk out to the destination */
2242: sshbuf_reset(msg);
2243: if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 ||
2244: (r = sshbuf_put_u32(msg, to->msg_id++)) != 0 ||
2245: (r = sshbuf_put_string(msg, to_handle,
2246: to_handle_len)) != 0 ||
2247: (r = sshbuf_put_u64(msg, req->offset)) != 0 ||
2248: (r = sshbuf_put_string(msg, data, len)) != 0)
2249: fatal_fr(r, "compose write");
2250: send_msg(to, msg);
2251: debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%zu",
2252: id, (unsigned long long)offset, len);
2253: num_upload_req++;
2254: progress_counter += len;
2255: free(data);
2256:
2257: if (len == req->len) {
2258: TAILQ_REMOVE(&requests, req, tq);
2259: free(req);
2260: num_req--;
2261: } else {
2262: /* Resend the request for the missing data */
2263: debug3("Short data block, re-requesting "
2264: "%llu -> %llu (%2d)",
2265: (unsigned long long)req->offset + len,
2266: (unsigned long long)req->offset +
2267: req->len - 1, num_req);
2268: req->id = from->msg_id++;
2269: req->len -= len;
2270: req->offset += len;
2271: send_read_request(from, req->id,
2272: req->offset, req->len,
2273: from_handle, from_handle_len);
2274: /* Reduce the request size */
2275: if (len < buflen)
2276: buflen = MAXIMUM(MIN_READ_SIZE, len);
2277: }
2278: if (max_req > 0) { /* max_req = 0 iff EOF received */
2279: if (size > 0 && offset > size) {
2280: /* Only one request at a time
2281: * after the expected EOF */
2282: debug3("Finish at %llu (%2d)",
2283: (unsigned long long)offset,
2284: num_req);
2285: max_req = 1;
2286: } else if (max_req < from->num_requests) {
2287: ++max_req;
2288: }
2289: }
2290: break;
2291: default:
2292: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
2293: SSH2_FXP_DATA, type);
2294: }
2295: }
2296:
2297: if (showprogress && size)
2298: stop_progress_meter();
2299:
2300: /* Drain replies from the server (blocking) */
2301: debug3_f("waiting for %u replies from destination", num_upload_req);
2302: handle_dest_replies(to, to_path, 1, &num_upload_req, &write_error);
2303:
2304: /* Sanity check */
2305: if (TAILQ_FIRST(&requests) != NULL)
2306: fatal("Transfer complete, but requests still in queue");
2307: /* Truncate at 0 length on interrupt or error to avoid holes at dest */
2308: if (read_error || write_error || interrupted) {
2309: debug("truncating \"%s\" at 0", to_path);
2310: do_close(to, to_handle, to_handle_len);
2311: free(to_handle);
2312: if (send_open(to, to_path, "dest",
2313: SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a,
2314: &to_handle, &to_handle_len) != 0) {
2315: error("truncation failed for \"%s\"", to_path);
2316: to_handle = NULL;
2317: }
2318: }
2319: if (read_error) {
2320: error("Couldn't read from origin file \"%s\" : %s",
2321: from_path, fx2txt(status));
2322: status = -1;
2323: do_close(from, from_handle, from_handle_len);
2324: if (to_handle != NULL)
2325: do_close(to, to_handle, to_handle_len);
2326: } else if (write_error) {
2327: error("Couldn't write to \"%s\": %s",
2328: to_path, fx2txt(write_error));
2329: status = SSH2_FX_FAILURE;
2330: do_close(from, from_handle, from_handle_len);
2331: if (to_handle != NULL)
2332: do_close(to, to_handle, to_handle_len);
2333: } else {
2334: if (do_close(from, from_handle, from_handle_len) != 0 ||
2335: interrupted)
2336: status = -1;
2337: else
2338: status = SSH2_FX_OK;
2339: if (to_handle != NULL) {
2340: /* Need to resend utimes after write */
2341: if (preserve_flag)
2342: do_fsetstat(to, to_handle, to_handle_len, a);
2343: do_close(to, to_handle, to_handle_len);
2344: }
2345: }
2346: sshbuf_free(msg);
2347: free(from_handle);
2348: free(to_handle);
2349:
2350: return status == SSH2_FX_OK ? 0 : -1;
2351: }
2352:
2353: static int
2354: crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to,
2355: const char *from_path, const char *to_path,
2356: int depth, Attrib *dirattrib, int preserve_flag, int print_flag)
2357: {
2358: int i, ret = 0;
2359: SFTP_DIRENT **dir_entries;
2360: char *filename, *new_from_path = NULL, *new_to_path = NULL;
2361: mode_t mode = 0777;
1.149 ! djm 2362: Attrib curdir;
1.145 djm 2363:
2364: if (depth >= MAX_DIR_DEPTH) {
2365: error("Maximum directory depth exceeded: %d levels", depth);
2366: return -1;
2367: }
2368:
2369: if (dirattrib == NULL &&
2370: (dirattrib = do_stat(from, from_path, 1)) == NULL) {
2371: error("Unable to stat remote directory \"%s\"", from_path);
2372: return -1;
2373: }
2374: if (!S_ISDIR(dirattrib->perm)) {
2375: error("\"%s\" is not a directory", from_path);
2376: return -1;
2377: }
2378: if (print_flag)
2379: mprintf("Retrieving %s\n", from_path);
2380:
1.149 ! djm 2381: curdir = *dirattrib; /* dirattrib will be clobbered */
! 2382: curdir.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
! 2383: curdir.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
! 2384: if ((curdir.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) == 0) {
! 2385: debug("Origin did not send permissions for "
1.145 djm 2386: "directory \"%s\"", to_path);
1.149 ! djm 2387: curdir.perm = S_IWUSR|S_IXUSR;
! 2388: curdir.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1.145 djm 2389: }
1.149 ! djm 2390: /* We need to be able to write to the directory while we transfer it */
! 2391: mode = curdir.perm & 01777;
! 2392: curdir.perm = mode | (S_IWUSR|S_IXUSR);
! 2393:
! 2394: /*
! 2395: * sftp lacks a portable status value to match errno EEXIST,
! 2396: * so if we get a failure back then we must check whether
! 2397: * the path already existed and is a directory. Ensure we can
! 2398: * write to the directory we create for the duration of the transfer.
! 2399: */
! 2400: if (do_mkdir(to, to_path, &curdir, 0) != 0) {
! 2401: if ((dirattrib = do_stat(to, to_path, 0)) == NULL)
! 2402: return -1;
! 2403: if (!S_ISDIR(dirattrib->perm)) {
! 2404: error("\"%s\" exists but is not a directory", to_path);
! 2405: return -1;
! 2406: }
! 2407: }
! 2408: curdir.perm = mode;
1.145 djm 2409:
2410: if (do_readdir(from, from_path, &dir_entries) == -1) {
2411: error("%s: Failed to get directory contents", from_path);
2412: return -1;
2413: }
2414:
2415: for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
2416: free(new_from_path);
2417: free(new_to_path);
2418:
2419: filename = dir_entries[i]->filename;
2420: new_from_path = path_append(from_path, filename);
2421: new_to_path = path_append(to_path, filename);
2422:
2423: if (S_ISDIR(dir_entries[i]->a.perm)) {
2424: if (strcmp(filename, ".") == 0 ||
2425: strcmp(filename, "..") == 0)
2426: continue;
2427: if (crossload_dir_internal(from, to,
2428: new_from_path, new_to_path,
2429: depth + 1, &(dir_entries[i]->a), preserve_flag,
2430: print_flag) == -1)
2431: ret = -1;
2432: } else if (S_ISREG(dir_entries[i]->a.perm) ) {
2433: if (do_crossload(from, to, new_from_path, new_to_path,
2434: &(dir_entries[i]->a), preserve_flag) == -1) {
2435: error("Transfer of file %s to %s failed",
2436: new_from_path, new_to_path);
2437: ret = -1;
2438: }
2439: } else
2440: logit("%s: not a regular file\n", new_from_path);
2441:
2442: }
2443: free(new_to_path);
2444: free(new_from_path);
2445:
1.149 ! djm 2446: do_setstat(to, to_path, &curdir);
1.145 djm 2447:
2448: free_sftp_dirents(dir_entries);
2449:
2450: return ret;
2451: }
2452:
2453: int
2454: crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
2455: const char *from_path, const char *to_path,
2456: Attrib *dirattrib, int preserve_flag, int print_flag)
2457: {
2458: char *from_path_canon;
2459: int ret;
2460:
2461: if ((from_path_canon = do_realpath(from, from_path)) == NULL) {
2462: error("Unable to canonicalize path \"%s\"", from_path);
2463: return -1;
2464: }
2465:
2466: ret = crossload_dir_internal(from, to, from_path_canon, to_path, 0,
2467: dirattrib, preserve_flag, print_flag);
2468: free(from_path_canon);
1.89 djm 2469: return ret;
2470: }
2471:
2472: char *
1.116 djm 2473: path_append(const char *p1, const char *p2)
1.89 djm 2474: {
2475: char *ret;
2476: size_t len = strlen(p1) + strlen(p2) + 2;
2477:
2478: ret = xmalloc(len);
2479: strlcpy(ret, p1, len);
2480: if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
2481: strlcat(ret, "/", len);
2482: strlcat(ret, p2, len);
2483:
2484: return(ret);
1.139 djm 2485: }
2486:
2487: char *
2488: make_absolute(char *p, const char *pwd)
2489: {
2490: char *abs_str;
2491:
2492: /* Derelativise */
2493: if (p && !path_absolute(p)) {
2494: abs_str = path_append(pwd, p);
2495: free(p);
2496: return(abs_str);
2497: } else
2498: return(p);
2499: }
2500:
2501: int
2502: remote_is_dir(struct sftp_conn *conn, const char *path)
2503: {
2504: Attrib *a;
2505:
2506: /* XXX: report errors? */
2507: if ((a = do_stat(conn, path, 1)) == NULL)
2508: return(0);
2509: if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
2510: return(0);
2511: return(S_ISDIR(a->perm));
2512: }
2513:
2514:
2515: int
2516: local_is_dir(const char *path)
2517: {
2518: struct stat sb;
2519:
2520: /* XXX: report errors? */
2521: if (stat(path, &sb) == -1)
2522: return(0);
2523:
2524: return(S_ISDIR(sb.st_mode));
2525: }
2526:
2527: /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
2528: int
2529: globpath_is_dir(const char *pathname)
2530: {
2531: size_t l = strlen(pathname);
2532:
2533: return l > 0 && pathname[l - 1] == '/';
1.89 djm 2534: }
2535: