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