Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.89
1.89 ! djm 1: /* $OpenBSD: sftp-client.c,v 1.88 2009/08/14 18:17:49 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.21 djm 24: #include <sys/queue.h>
1.60 stevesk 25: #include <sys/stat.h>
1.71 stevesk 26: #include <sys/time.h>
1.72 stevesk 27: #include <sys/param.h>
1.82 djm 28: #include <sys/statvfs.h>
1.74 deraadt 29: #include <sys/uio.h>
1.66 stevesk 30:
1.89 ! djm 31: #include <dirent.h>
1.67 stevesk 32: #include <errno.h>
1.66 stevesk 33: #include <fcntl.h>
1.70 stevesk 34: #include <signal.h>
1.87 dtucker 35: #include <stdarg.h>
1.73 stevesk 36: #include <stdio.h>
1.69 stevesk 37: #include <string.h>
1.68 stevesk 38: #include <unistd.h>
1.1 djm 39:
1.74 deraadt 40: #include "xmalloc.h"
1.1 djm 41: #include "buffer.h"
42: #include "log.h"
43: #include "atomicio.h"
1.39 fgsch 44: #include "progressmeter.h"
1.64 djm 45: #include "misc.h"
1.1 djm 46:
47: #include "sftp.h"
48: #include "sftp-common.h"
49: #include "sftp-client.h"
50:
1.49 djm 51: extern volatile sig_atomic_t interrupted;
1.39 fgsch 52: extern int showprogress;
53:
1.59 david 54: /* Minimum amount of data to read at a time */
1.21 djm 55: #define MIN_READ_SIZE 512
56:
1.89 ! djm 57: /* Maximum depth to descend in directory trees */
! 58: #define MAX_DIR_DEPTH 64
! 59:
1.23 djm 60: struct sftp_conn {
61: int fd_in;
62: int fd_out;
63: u_int transfer_buflen;
64: u_int num_requests;
65: u_int version;
66: u_int msg_id;
1.82 djm 67: #define SFTP_EXT_POSIX_RENAME 0x00000001
68: #define SFTP_EXT_STATVFS 0x00000002
69: #define SFTP_EXT_FSTATVFS 0x00000004
1.81 djm 70: u_int exts;
1.23 djm 71: };
1.4 djm 72:
1.88 djm 73: static char *
74: get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
75: __attribute__((format(printf, 4, 5)));
76:
1.17 itojun 77: static void
1.1 djm 78: send_msg(int fd, Buffer *m)
79: {
1.40 djm 80: u_char mlen[4];
1.65 djm 81: struct iovec iov[2];
1.40 djm 82:
1.58 djm 83: if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
1.40 djm 84: fatal("Outbound message too long %u", buffer_len(m));
85:
86: /* Send length first */
1.64 djm 87: put_u32(mlen, buffer_len(m));
1.65 djm 88: iov[0].iov_base = mlen;
89: iov[0].iov_len = sizeof(mlen);
90: iov[1].iov_base = buffer_ptr(m);
91: iov[1].iov_len = buffer_len(m);
1.74 deraadt 92:
1.65 djm 93: if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
1.1 djm 94: fatal("Couldn't send packet: %s", strerror(errno));
95:
1.40 djm 96: buffer_clear(m);
1.1 djm 97: }
98:
1.17 itojun 99: static void
1.1 djm 100: get_msg(int fd, Buffer *m)
101: {
1.40 djm 102: u_int msg_len;
1.1 djm 103:
1.40 djm 104: buffer_append_space(m, 4);
1.54 avsm 105: if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
106: if (errno == EPIPE)
107: fatal("Connection closed");
108: else
109: fatal("Couldn't read packet: %s", strerror(errno));
110: }
1.1 djm 111:
1.40 djm 112: msg_len = buffer_get_int(m);
1.58 djm 113: if (msg_len > SFTP_MAX_MSG_LENGTH)
1.33 deraadt 114: fatal("Received message too long %u", msg_len);
1.1 djm 115:
1.40 djm 116: buffer_append_space(m, msg_len);
1.54 avsm 117: if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
118: if (errno == EPIPE)
119: fatal("Connection closed");
120: else
121: fatal("Read packet: %s", strerror(errno));
122: }
1.1 djm 123: }
124:
1.17 itojun 125: static void
1.1 djm 126: send_string_request(int fd, u_int id, u_int code, char *s,
127: u_int len)
128: {
129: Buffer msg;
130:
131: buffer_init(&msg);
132: buffer_put_char(&msg, code);
133: buffer_put_int(&msg, id);
134: buffer_put_string(&msg, s, len);
135: send_msg(fd, &msg);
1.33 deraadt 136: debug3("Sent message fd %d T:%u I:%u", fd, code, id);
1.1 djm 137: buffer_free(&msg);
138: }
139:
1.17 itojun 140: static void
1.1 djm 141: send_string_attrs_request(int fd, u_int id, u_int code, char *s,
142: u_int len, Attrib *a)
143: {
144: Buffer msg;
145:
146: buffer_init(&msg);
147: buffer_put_char(&msg, code);
148: buffer_put_int(&msg, id);
149: buffer_put_string(&msg, s, len);
150: encode_attrib(&msg, a);
151: send_msg(fd, &msg);
1.33 deraadt 152: debug3("Sent message fd %d T:%u I:%u", fd, code, id);
1.1 djm 153: buffer_free(&msg);
154: }
155:
1.17 itojun 156: static u_int
1.33 deraadt 157: get_status(int fd, u_int expected_id)
1.1 djm 158: {
159: Buffer msg;
160: u_int type, id, status;
161:
162: buffer_init(&msg);
163: get_msg(fd, &msg);
164: type = buffer_get_char(&msg);
165: id = buffer_get_int(&msg);
166:
167: if (id != expected_id)
1.33 deraadt 168: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 169: if (type != SSH2_FXP_STATUS)
1.33 deraadt 170: fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
1.1 djm 171: SSH2_FXP_STATUS, type);
172:
173: status = buffer_get_int(&msg);
174: buffer_free(&msg);
175:
1.33 deraadt 176: debug3("SSH2_FXP_STATUS %u", status);
1.1 djm 177:
178: return(status);
179: }
180:
1.17 itojun 181: static char *
1.88 djm 182: get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
1.1 djm 183: {
184: Buffer msg;
185: u_int type, id;
1.88 djm 186: char *handle, errmsg[256];
187: va_list args;
188: int status;
189:
190: va_start(args, errfmt);
191: if (errfmt != NULL)
192: vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
193: va_end(args);
1.1 djm 194:
195: buffer_init(&msg);
196: get_msg(fd, &msg);
197: type = buffer_get_char(&msg);
198: id = buffer_get_int(&msg);
199:
200: if (id != expected_id)
1.88 djm 201: fatal("%s: ID mismatch (%u != %u)",
202: errfmt == NULL ? __func__ : errmsg, id, expected_id);
1.1 djm 203: if (type == SSH2_FXP_STATUS) {
1.88 djm 204: status = buffer_get_int(&msg);
205: if (errfmt != NULL)
206: error("%s: %s", errmsg, fx2txt(status));
1.52 markus 207: buffer_free(&msg);
1.1 djm 208: return(NULL);
209: } else if (type != SSH2_FXP_HANDLE)
1.88 djm 210: fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
211: errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
1.1 djm 212:
213: handle = buffer_get_string(&msg, len);
214: buffer_free(&msg);
215:
216: return(handle);
217: }
218:
1.17 itojun 219: static Attrib *
1.14 djm 220: get_decode_stat(int fd, u_int expected_id, int quiet)
1.1 djm 221: {
222: Buffer msg;
223: u_int type, id;
224: Attrib *a;
225:
226: buffer_init(&msg);
227: get_msg(fd, &msg);
228:
229: type = buffer_get_char(&msg);
230: id = buffer_get_int(&msg);
231:
1.33 deraadt 232: debug3("Received stat reply T:%u I:%u", type, id);
1.1 djm 233: if (id != expected_id)
1.33 deraadt 234: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 235: if (type == SSH2_FXP_STATUS) {
236: int status = buffer_get_int(&msg);
237:
1.14 djm 238: if (quiet)
239: debug("Couldn't stat remote file: %s", fx2txt(status));
240: else
241: error("Couldn't stat remote file: %s", fx2txt(status));
1.52 markus 242: buffer_free(&msg);
1.1 djm 243: return(NULL);
244: } else if (type != SSH2_FXP_ATTRS) {
1.33 deraadt 245: fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
1.1 djm 246: SSH2_FXP_ATTRS, type);
247: }
248: a = decode_attrib(&msg);
249: buffer_free(&msg);
250:
251: return(a);
252: }
253:
1.82 djm 254: static int
1.84 dtucker 255: get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
256: int quiet)
1.82 djm 257: {
258: Buffer msg;
259: u_int type, id, flag;
260:
261: buffer_init(&msg);
262: get_msg(fd, &msg);
263:
264: type = buffer_get_char(&msg);
265: id = buffer_get_int(&msg);
266:
267: debug3("Received statvfs reply T:%u I:%u", type, id);
268: if (id != expected_id)
269: fatal("ID mismatch (%u != %u)", id, expected_id);
270: if (type == SSH2_FXP_STATUS) {
271: int status = buffer_get_int(&msg);
272:
273: if (quiet)
274: debug("Couldn't statvfs: %s", fx2txt(status));
275: else
276: error("Couldn't statvfs: %s", fx2txt(status));
277: buffer_free(&msg);
278: return -1;
279: } else if (type != SSH2_FXP_EXTENDED_REPLY) {
280: fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
281: SSH2_FXP_EXTENDED_REPLY, type);
282: }
283:
284: bzero(st, sizeof(*st));
1.84 dtucker 285: st->f_bsize = buffer_get_int64(&msg);
286: st->f_frsize = buffer_get_int64(&msg);
1.82 djm 287: st->f_blocks = buffer_get_int64(&msg);
288: st->f_bfree = buffer_get_int64(&msg);
289: st->f_bavail = buffer_get_int64(&msg);
290: st->f_files = buffer_get_int64(&msg);
291: st->f_ffree = buffer_get_int64(&msg);
292: st->f_favail = buffer_get_int64(&msg);
1.83 djm 293: st->f_fsid = buffer_get_int64(&msg);
1.84 dtucker 294: flag = buffer_get_int64(&msg);
295: st->f_namemax = buffer_get_int64(&msg);
1.82 djm 296:
297: st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
298: st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
299:
300: buffer_free(&msg);
301:
302: return 0;
303: }
304:
1.23 djm 305: struct sftp_conn *
306: do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
1.1 djm 307: {
1.81 djm 308: u_int type, exts = 0;
1.33 deraadt 309: int version;
1.1 djm 310: Buffer msg;
1.23 djm 311: struct sftp_conn *ret;
1.1 djm 312:
313: buffer_init(&msg);
314: buffer_put_char(&msg, SSH2_FXP_INIT);
315: buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
316: send_msg(fd_out, &msg);
317:
318: buffer_clear(&msg);
319:
320: get_msg(fd_in, &msg);
321:
1.3 stevesk 322: /* Expecting a VERSION reply */
1.1 djm 323: if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
1.33 deraadt 324: error("Invalid packet back from SSH2_FXP_INIT (type %u)",
1.1 djm 325: type);
326: buffer_free(&msg);
1.23 djm 327: return(NULL);
1.1 djm 328: }
329: version = buffer_get_int(&msg);
330:
331: debug2("Remote version: %d", version);
332:
333: /* Check for extensions */
334: while (buffer_len(&msg) > 0) {
335: char *name = buffer_get_string(&msg, NULL);
336: char *value = buffer_get_string(&msg, NULL);
1.85 djm 337: int known = 0;
1.1 djm 338:
1.82 djm 339: if (strcmp(name, "posix-rename@openssh.com") == 0 &&
1.85 djm 340: strcmp(value, "1") == 0) {
1.81 djm 341: exts |= SFTP_EXT_POSIX_RENAME;
1.85 djm 342: known = 1;
343: } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
344: strcmp(value, "2") == 0) {
1.82 djm 345: exts |= SFTP_EXT_STATVFS;
1.85 djm 346: known = 1;
347: } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
348: strcmp(value, "2") == 0) {
1.82 djm 349: exts |= SFTP_EXT_FSTATVFS;
1.85 djm 350: known = 1;
351: }
352: if (known) {
353: debug2("Server supports extension \"%s\" revision %s",
354: name, value);
355: } else {
356: debug2("Unrecognised server extension \"%s\"", name);
357: }
1.1 djm 358: xfree(name);
359: xfree(value);
360: }
361:
362: buffer_free(&msg);
1.11 djm 363:
1.23 djm 364: ret = xmalloc(sizeof(*ret));
365: ret->fd_in = fd_in;
366: ret->fd_out = fd_out;
367: ret->transfer_buflen = transfer_buflen;
368: ret->num_requests = num_requests;
369: ret->version = version;
370: ret->msg_id = 1;
1.81 djm 371: ret->exts = exts;
1.23 djm 372:
373: /* Some filexfer v.0 servers don't support large packets */
374: if (version == 0)
1.29 markus 375: ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
1.23 djm 376:
377: return(ret);
378: }
379:
380: u_int
381: sftp_proto_version(struct sftp_conn *conn)
382: {
383: return(conn->version);
1.1 djm 384: }
385:
386: int
1.23 djm 387: do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
1.1 djm 388: {
389: u_int id, status;
390: Buffer msg;
391:
392: buffer_init(&msg);
393:
1.23 djm 394: id = conn->msg_id++;
1.1 djm 395: buffer_put_char(&msg, SSH2_FXP_CLOSE);
396: buffer_put_int(&msg, id);
397: buffer_put_string(&msg, handle, handle_len);
1.23 djm 398: send_msg(conn->fd_out, &msg);
1.33 deraadt 399: debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
1.1 djm 400:
1.23 djm 401: status = get_status(conn->fd_in, id);
1.1 djm 402: if (status != SSH2_FX_OK)
403: error("Couldn't close file: %s", fx2txt(status));
404:
405: buffer_free(&msg);
406:
407: return(status);
408: }
409:
1.12 djm 410:
1.17 itojun 411: static int
1.23 djm 412: do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
1.12 djm 413: SFTP_DIRENT ***dir)
1.1 djm 414: {
415: Buffer msg;
1.55 djm 416: u_int count, type, id, handle_len, i, expected_id, ents = 0;
1.1 djm 417: char *handle;
418:
1.23 djm 419: id = conn->msg_id++;
1.1 djm 420:
421: buffer_init(&msg);
422: buffer_put_char(&msg, SSH2_FXP_OPENDIR);
423: buffer_put_int(&msg, id);
424: buffer_put_cstring(&msg, path);
1.23 djm 425: send_msg(conn->fd_out, &msg);
1.1 djm 426:
427: buffer_clear(&msg);
428:
1.88 djm 429: handle = get_handle(conn->fd_in, id, &handle_len,
430: "remote readdir(\"%s\")", path);
1.1 djm 431: if (handle == NULL)
432: return(-1);
433:
1.12 djm 434: if (dir) {
435: ents = 0;
436: *dir = xmalloc(sizeof(**dir));
437: (*dir)[0] = NULL;
438: }
439:
1.49 djm 440: for (; !interrupted;) {
1.23 djm 441: id = expected_id = conn->msg_id++;
1.1 djm 442:
1.33 deraadt 443: debug3("Sending SSH2_FXP_READDIR I:%u", id);
1.1 djm 444:
445: buffer_clear(&msg);
446: buffer_put_char(&msg, SSH2_FXP_READDIR);
447: buffer_put_int(&msg, id);
448: buffer_put_string(&msg, handle, handle_len);
1.23 djm 449: send_msg(conn->fd_out, &msg);
1.1 djm 450:
451: buffer_clear(&msg);
452:
1.23 djm 453: get_msg(conn->fd_in, &msg);
1.1 djm 454:
455: type = buffer_get_char(&msg);
456: id = buffer_get_int(&msg);
457:
1.33 deraadt 458: debug3("Received reply T:%u I:%u", type, id);
1.1 djm 459:
460: if (id != expected_id)
1.33 deraadt 461: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 462:
463: if (type == SSH2_FXP_STATUS) {
464: int status = buffer_get_int(&msg);
465:
466: debug3("Received SSH2_FXP_STATUS %d", status);
467:
468: if (status == SSH2_FX_EOF) {
469: break;
470: } else {
471: error("Couldn't read directory: %s",
472: fx2txt(status));
1.23 djm 473: do_close(conn, handle, handle_len);
1.42 markus 474: xfree(handle);
1.9 djm 475: return(status);
1.1 djm 476: }
477: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 478: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 479: SSH2_FXP_NAME, type);
480:
481: count = buffer_get_int(&msg);
1.7 markus 482: if (count == 0)
483: break;
1.8 stevesk 484: debug3("Received %d SSH2_FXP_NAME responses", count);
1.19 deraadt 485: for (i = 0; i < count; i++) {
1.1 djm 486: char *filename, *longname;
487: Attrib *a;
488:
489: filename = buffer_get_string(&msg, NULL);
490: longname = buffer_get_string(&msg, NULL);
491: a = decode_attrib(&msg);
492:
1.12 djm 493: if (printflag)
494: printf("%s\n", longname);
495:
1.89 ! djm 496: /*
! 497: * Directory entries should never contain '/'
! 498: * These can be used to attack recursive ops
! 499: * (e.g. send '../../../../etc/passwd')
! 500: */
! 501: if (strchr(filename, '/') != NULL) {
! 502: error("Server sent suspect path \"%s\" "
! 503: "during readdir of \"%s\"", filename, path);
! 504: goto next;
! 505: }
! 506:
1.12 djm 507: if (dir) {
1.62 djm 508: *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
1.12 djm 509: (*dir)[ents] = xmalloc(sizeof(***dir));
510: (*dir)[ents]->filename = xstrdup(filename);
511: (*dir)[ents]->longname = xstrdup(longname);
512: memcpy(&(*dir)[ents]->a, a, sizeof(*a));
513: (*dir)[++ents] = NULL;
514: }
1.89 ! djm 515: next:
1.1 djm 516: xfree(filename);
517: xfree(longname);
518: }
519: }
520:
521: buffer_free(&msg);
1.23 djm 522: do_close(conn, handle, handle_len);
1.1 djm 523: xfree(handle);
524:
1.49 djm 525: /* Don't return partial matches on interrupt */
526: if (interrupted && dir != NULL && *dir != NULL) {
527: free_sftp_dirents(*dir);
528: *dir = xmalloc(sizeof(**dir));
529: **dir = NULL;
530: }
531:
1.1 djm 532: return(0);
1.12 djm 533: }
534:
535: int
1.23 djm 536: do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
1.12 djm 537: {
1.23 djm 538: return(do_lsreaddir(conn, path, 0, dir));
1.12 djm 539: }
540:
541: void free_sftp_dirents(SFTP_DIRENT **s)
542: {
543: int i;
1.19 deraadt 544:
545: for (i = 0; s[i]; i++) {
1.12 djm 546: xfree(s[i]->filename);
547: xfree(s[i]->longname);
548: xfree(s[i]);
549: }
550: xfree(s);
551: }
552:
553: int
1.23 djm 554: do_rm(struct sftp_conn *conn, char *path)
1.1 djm 555: {
556: u_int status, id;
557:
558: debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
559:
1.23 djm 560: id = conn->msg_id++;
1.28 markus 561: send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
1.23 djm 562: strlen(path));
563: status = get_status(conn->fd_in, id);
1.1 djm 564: if (status != SSH2_FX_OK)
565: error("Couldn't delete file: %s", fx2txt(status));
566: return(status);
567: }
568:
569: int
1.89 ! djm 570: do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
1.1 djm 571: {
572: u_int status, id;
573:
1.23 djm 574: id = conn->msg_id++;
575: send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
1.1 djm 576: strlen(path), a);
577:
1.23 djm 578: status = get_status(conn->fd_in, id);
1.89 ! djm 579: if (status != SSH2_FX_OK && printflag)
1.1 djm 580: error("Couldn't create directory: %s", fx2txt(status));
581:
582: return(status);
583: }
584:
585: int
1.23 djm 586: do_rmdir(struct sftp_conn *conn, char *path)
1.1 djm 587: {
588: u_int status, id;
589:
1.23 djm 590: id = conn->msg_id++;
591: send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
592: strlen(path));
1.1 djm 593:
1.23 djm 594: status = get_status(conn->fd_in, id);
1.1 djm 595: if (status != SSH2_FX_OK)
596: error("Couldn't remove directory: %s", fx2txt(status));
597:
598: return(status);
599: }
600:
601: Attrib *
1.23 djm 602: do_stat(struct sftp_conn *conn, char *path, int quiet)
1.1 djm 603: {
604: u_int id;
605:
1.23 djm 606: id = conn->msg_id++;
607:
1.28 markus 608: send_string_request(conn->fd_out, id,
609: conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
1.23 djm 610: path, strlen(path));
611:
612: return(get_decode_stat(conn->fd_in, id, quiet));
1.1 djm 613: }
614:
615: Attrib *
1.23 djm 616: do_lstat(struct sftp_conn *conn, char *path, int quiet)
1.1 djm 617: {
618: u_int id;
619:
1.23 djm 620: if (conn->version == 0) {
621: if (quiet)
622: debug("Server version does not support lstat operation");
623: else
1.43 itojun 624: logit("Server version does not support lstat operation");
1.30 markus 625: return(do_stat(conn, path, quiet));
1.23 djm 626: }
627:
628: id = conn->msg_id++;
629: send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
630: strlen(path));
631:
632: return(get_decode_stat(conn->fd_in, id, quiet));
1.1 djm 633: }
634:
1.78 chl 635: #ifdef notyet
1.1 djm 636: Attrib *
1.23 djm 637: do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
1.1 djm 638: {
639: u_int id;
640:
1.23 djm 641: id = conn->msg_id++;
642: send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
643: handle_len);
644:
645: return(get_decode_stat(conn->fd_in, id, quiet));
1.1 djm 646: }
1.78 chl 647: #endif
1.1 djm 648:
649: int
1.23 djm 650: do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
1.1 djm 651: {
652: u_int status, id;
653:
1.23 djm 654: id = conn->msg_id++;
655: send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
1.1 djm 656: strlen(path), a);
657:
1.23 djm 658: status = get_status(conn->fd_in, id);
1.1 djm 659: if (status != SSH2_FX_OK)
660: error("Couldn't setstat on \"%s\": %s", path,
661: fx2txt(status));
662:
663: return(status);
664: }
665:
666: int
1.23 djm 667: do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
1.1 djm 668: Attrib *a)
669: {
670: u_int status, id;
671:
1.23 djm 672: id = conn->msg_id++;
673: send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
1.1 djm 674: handle_len, a);
675:
1.23 djm 676: status = get_status(conn->fd_in, id);
1.1 djm 677: if (status != SSH2_FX_OK)
678: error("Couldn't fsetstat: %s", fx2txt(status));
679:
680: return(status);
681: }
682:
683: char *
1.23 djm 684: do_realpath(struct sftp_conn *conn, char *path)
1.1 djm 685: {
686: Buffer msg;
687: u_int type, expected_id, count, id;
688: char *filename, *longname;
689: Attrib *a;
690:
1.23 djm 691: expected_id = id = conn->msg_id++;
692: send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
693: strlen(path));
1.1 djm 694:
695: buffer_init(&msg);
696:
1.23 djm 697: get_msg(conn->fd_in, &msg);
1.1 djm 698: type = buffer_get_char(&msg);
699: id = buffer_get_int(&msg);
700:
701: if (id != expected_id)
1.33 deraadt 702: fatal("ID mismatch (%u != %u)", id, expected_id);
1.1 djm 703:
704: if (type == SSH2_FXP_STATUS) {
705: u_int status = buffer_get_int(&msg);
706:
707: error("Couldn't canonicalise: %s", fx2txt(status));
708: return(NULL);
709: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 710: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 711: SSH2_FXP_NAME, type);
712:
713: count = buffer_get_int(&msg);
714: if (count != 1)
715: fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
716:
717: filename = buffer_get_string(&msg, NULL);
718: longname = buffer_get_string(&msg, NULL);
719: a = decode_attrib(&msg);
720:
721: debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
722:
723: xfree(longname);
724:
725: buffer_free(&msg);
726:
727: return(filename);
728: }
729:
730: int
1.23 djm 731: do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
1.1 djm 732: {
733: Buffer msg;
734: u_int status, id;
735:
736: buffer_init(&msg);
737:
738: /* Send rename request */
1.23 djm 739: id = conn->msg_id++;
1.81 djm 740: if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
741: buffer_put_char(&msg, SSH2_FXP_EXTENDED);
742: buffer_put_int(&msg, id);
743: buffer_put_cstring(&msg, "posix-rename@openssh.com");
744: } else {
745: buffer_put_char(&msg, SSH2_FXP_RENAME);
746: buffer_put_int(&msg, id);
747: }
1.1 djm 748: buffer_put_cstring(&msg, oldpath);
749: buffer_put_cstring(&msg, newpath);
1.23 djm 750: send_msg(conn->fd_out, &msg);
1.81 djm 751: debug3("Sent message %s \"%s\" -> \"%s\"",
752: (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
753: "SSH2_FXP_RENAME", oldpath, newpath);
1.1 djm 754: buffer_free(&msg);
755:
1.23 djm 756: status = get_status(conn->fd_in, id);
1.1 djm 757: if (status != SSH2_FX_OK)
1.23 djm 758: error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
759: newpath, fx2txt(status));
1.1 djm 760:
761: return(status);
1.11 djm 762: }
763:
764: int
1.23 djm 765: do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
1.11 djm 766: {
767: Buffer msg;
768: u_int status, id;
769:
1.23 djm 770: if (conn->version < 3) {
771: error("This server does not support the symlink operation");
772: return(SSH2_FX_OP_UNSUPPORTED);
773: }
774:
1.11 djm 775: buffer_init(&msg);
776:
1.48 djm 777: /* Send symlink request */
1.23 djm 778: id = conn->msg_id++;
1.11 djm 779: buffer_put_char(&msg, SSH2_FXP_SYMLINK);
780: buffer_put_int(&msg, id);
781: buffer_put_cstring(&msg, oldpath);
782: buffer_put_cstring(&msg, newpath);
1.23 djm 783: send_msg(conn->fd_out, &msg);
1.11 djm 784: debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
785: newpath);
786: buffer_free(&msg);
787:
1.23 djm 788: status = get_status(conn->fd_in, id);
1.11 djm 789: if (status != SSH2_FX_OK)
1.36 markus 790: error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
1.23 djm 791: newpath, fx2txt(status));
1.11 djm 792:
793: return(status);
794: }
795:
1.78 chl 796: #ifdef notyet
1.11 djm 797: char *
1.23 djm 798: do_readlink(struct sftp_conn *conn, char *path)
1.11 djm 799: {
800: Buffer msg;
801: u_int type, expected_id, count, id;
802: char *filename, *longname;
803: Attrib *a;
804:
1.23 djm 805: expected_id = id = conn->msg_id++;
806: send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
807: strlen(path));
1.11 djm 808:
809: buffer_init(&msg);
810:
1.23 djm 811: get_msg(conn->fd_in, &msg);
1.11 djm 812: type = buffer_get_char(&msg);
813: id = buffer_get_int(&msg);
814:
815: if (id != expected_id)
1.33 deraadt 816: fatal("ID mismatch (%u != %u)", id, expected_id);
1.11 djm 817:
818: if (type == SSH2_FXP_STATUS) {
819: u_int status = buffer_get_int(&msg);
820:
821: error("Couldn't readlink: %s", fx2txt(status));
822: return(NULL);
823: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 824: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.11 djm 825: SSH2_FXP_NAME, type);
826:
827: count = buffer_get_int(&msg);
828: if (count != 1)
829: fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
830:
831: filename = buffer_get_string(&msg, NULL);
832: longname = buffer_get_string(&msg, NULL);
833: a = decode_attrib(&msg);
834:
835: debug3("SSH_FXP_READLINK %s -> %s", path, filename);
836:
837: xfree(longname);
838:
839: buffer_free(&msg);
840:
841: return(filename);
1.82 djm 842: }
843: #endif
844:
845: int
1.84 dtucker 846: do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
1.82 djm 847: int quiet)
848: {
849: Buffer msg;
850: u_int id;
851:
852: if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
853: error("Server does not support statvfs@openssh.com extension");
854: return -1;
855: }
856:
857: id = conn->msg_id++;
858:
859: buffer_init(&msg);
860: buffer_clear(&msg);
861: buffer_put_char(&msg, SSH2_FXP_EXTENDED);
862: buffer_put_int(&msg, id);
863: buffer_put_cstring(&msg, "statvfs@openssh.com");
864: buffer_put_cstring(&msg, path);
865: send_msg(conn->fd_out, &msg);
866: buffer_free(&msg);
867:
868: return get_decode_statvfs(conn->fd_in, st, id, quiet);
869: }
870:
871: #ifdef notyet
872: int
873: do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
1.84 dtucker 874: struct sftp_statvfs *st, int quiet)
1.82 djm 875: {
876: Buffer msg;
877: u_int id;
878:
879: if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
880: error("Server does not support fstatvfs@openssh.com extension");
881: return -1;
882: }
883:
884: id = conn->msg_id++;
885:
886: buffer_init(&msg);
887: buffer_clear(&msg);
888: buffer_put_char(&msg, SSH2_FXP_EXTENDED);
889: buffer_put_int(&msg, id);
890: buffer_put_cstring(&msg, "fstatvfs@openssh.com");
891: buffer_put_string(&msg, handle, handle_len);
892: send_msg(conn->fd_out, &msg);
893: buffer_free(&msg);
894:
895: return get_decode_statvfs(conn->fd_in, st, id, quiet);
1.1 djm 896: }
1.78 chl 897: #endif
1.1 djm 898:
1.21 djm 899: static void
900: send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
901: char *handle, u_int handle_len)
902: {
903: Buffer msg;
1.28 markus 904:
1.21 djm 905: buffer_init(&msg);
906: buffer_clear(&msg);
907: buffer_put_char(&msg, SSH2_FXP_READ);
908: buffer_put_int(&msg, id);
909: buffer_put_string(&msg, handle, handle_len);
910: buffer_put_int64(&msg, offset);
911: buffer_put_int(&msg, len);
912: send_msg(fd_out, &msg);
913: buffer_free(&msg);
1.28 markus 914: }
1.21 djm 915:
1.1 djm 916: int
1.23 djm 917: do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1.89 ! djm 918: Attrib *a, int pflag)
1.1 djm 919: {
1.89 ! djm 920: Attrib junk;
1.21 djm 921: Buffer msg;
1.1 djm 922: char *handle;
1.57 dtucker 923: int local_fd, status = 0, write_error;
1.21 djm 924: int read_error, write_errno;
925: u_int64_t offset, size;
1.55 djm 926: u_int handle_len, mode, type, id, buflen, num_req, max_req;
1.39 fgsch 927: off_t progress_counter;
1.21 djm 928: struct request {
929: u_int id;
930: u_int len;
931: u_int64_t offset;
1.28 markus 932: TAILQ_ENTRY(request) tq;
1.21 djm 933: };
934: TAILQ_HEAD(reqhead, request) requests;
935: struct request *req;
936:
937: TAILQ_INIT(&requests);
1.1 djm 938:
1.89 ! djm 939: if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
! 940: return -1;
1.1 djm 941:
1.86 djm 942: /* Do not preserve set[ug]id here, as we do not preserve ownership */
1.1 djm 943: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1.38 djm 944: mode = a->perm & 0777;
1.1 djm 945: else
946: mode = 0666;
947:
1.14 djm 948: if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1.41 djm 949: (!S_ISREG(a->perm))) {
950: error("Cannot download non-regular file: %s", remote_path);
1.14 djm 951: return(-1);
952: }
953:
1.21 djm 954: if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
955: size = a->size;
956: else
957: size = 0;
958:
1.23 djm 959: buflen = conn->transfer_buflen;
1.1 djm 960: buffer_init(&msg);
961:
962: /* Send open request */
1.23 djm 963: id = conn->msg_id++;
1.1 djm 964: buffer_put_char(&msg, SSH2_FXP_OPEN);
965: buffer_put_int(&msg, id);
966: buffer_put_cstring(&msg, remote_path);
967: buffer_put_int(&msg, SSH2_FXF_READ);
968: attrib_clear(&junk); /* Send empty attributes */
969: encode_attrib(&msg, &junk);
1.23 djm 970: send_msg(conn->fd_out, &msg);
1.33 deraadt 971: debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1.1 djm 972:
1.88 djm 973: handle = get_handle(conn->fd_in, id, &handle_len,
974: "remote open(\"%s\")", remote_path);
1.1 djm 975: if (handle == NULL) {
976: buffer_free(&msg);
977: return(-1);
978: }
979:
1.45 djm 980: local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
1.38 djm 981: mode | S_IWRITE);
1.23 djm 982: if (local_fd == -1) {
983: error("Couldn't open local file \"%s\" for writing: %s",
984: local_path, strerror(errno));
1.79 djm 985: do_close(conn, handle, handle_len);
1.24 markus 986: buffer_free(&msg);
987: xfree(handle);
1.23 djm 988: return(-1);
989: }
990:
1.1 djm 991: /* Read from remote and write to local */
1.21 djm 992: write_error = read_error = write_errno = num_req = offset = 0;
993: max_req = 1;
1.39 fgsch 994: progress_counter = 0;
995:
1.47 djm 996: if (showprogress && size != 0)
997: start_progress_meter(remote_path, size, &progress_counter);
1.39 fgsch 998:
1.21 djm 999: while (num_req > 0 || max_req > 0) {
1000: char *data;
1.1 djm 1001: u_int len;
1002:
1.49 djm 1003: /*
1.51 deraadt 1004: * Simulate EOF on interrupt: stop sending new requests and
1.49 djm 1005: * allow outstanding requests to drain gracefully
1006: */
1007: if (interrupted) {
1008: if (num_req == 0) /* If we haven't started yet... */
1009: break;
1010: max_req = 0;
1011: }
1012:
1.21 djm 1013: /* Send some more requests */
1014: while (num_req < max_req) {
1.28 markus 1015: debug3("Request range %llu -> %llu (%d/%d)",
1.25 itojun 1016: (unsigned long long)offset,
1017: (unsigned long long)offset + buflen - 1,
1018: num_req, max_req);
1.21 djm 1019: req = xmalloc(sizeof(*req));
1.23 djm 1020: req->id = conn->msg_id++;
1.21 djm 1021: req->len = buflen;
1022: req->offset = offset;
1023: offset += buflen;
1024: num_req++;
1025: TAILQ_INSERT_TAIL(&requests, req, tq);
1.28 markus 1026: send_read_request(conn->fd_out, req->id, req->offset,
1.21 djm 1027: req->len, handle, handle_len);
1028: }
1.1 djm 1029:
1030: buffer_clear(&msg);
1.23 djm 1031: get_msg(conn->fd_in, &msg);
1.1 djm 1032: type = buffer_get_char(&msg);
1033: id = buffer_get_int(&msg);
1.33 deraadt 1034: debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1.21 djm 1035:
1036: /* Find the request in our queue */
1.53 deraadt 1037: for (req = TAILQ_FIRST(&requests);
1.21 djm 1038: req != NULL && req->id != id;
1039: req = TAILQ_NEXT(req, tq))
1040: ;
1041: if (req == NULL)
1042: fatal("Unexpected reply %u", id);
1043:
1044: switch (type) {
1045: case SSH2_FXP_STATUS:
1.5 djm 1046: status = buffer_get_int(&msg);
1.21 djm 1047: if (status != SSH2_FX_EOF)
1048: read_error = 1;
1049: max_req = 0;
1050: TAILQ_REMOVE(&requests, req, tq);
1051: xfree(req);
1052: num_req--;
1053: break;
1054: case SSH2_FXP_DATA:
1055: data = buffer_get_string(&msg, &len);
1.26 itojun 1056: debug3("Received data %llu -> %llu",
1.28 markus 1057: (unsigned long long)req->offset,
1.26 itojun 1058: (unsigned long long)req->offset + len - 1);
1.21 djm 1059: if (len > req->len)
1060: fatal("Received more data than asked for "
1.37 deraadt 1061: "%u > %u", len, req->len);
1.21 djm 1062: if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1.44 deraadt 1063: atomicio(vwrite, local_fd, data, len) != len) &&
1.21 djm 1064: !write_error) {
1065: write_errno = errno;
1066: write_error = 1;
1067: max_req = 0;
1068: }
1.39 fgsch 1069: progress_counter += len;
1.21 djm 1070: xfree(data);
1.1 djm 1071:
1.21 djm 1072: if (len == req->len) {
1073: TAILQ_REMOVE(&requests, req, tq);
1074: xfree(req);
1075: num_req--;
1076: } else {
1077: /* Resend the request for the missing data */
1078: debug3("Short data block, re-requesting "
1.26 itojun 1079: "%llu -> %llu (%2d)",
1.28 markus 1080: (unsigned long long)req->offset + len,
1.27 itojun 1081: (unsigned long long)req->offset +
1082: req->len - 1, num_req);
1.23 djm 1083: req->id = conn->msg_id++;
1.21 djm 1084: req->len -= len;
1085: req->offset += len;
1.28 markus 1086: send_read_request(conn->fd_out, req->id,
1.23 djm 1087: req->offset, req->len, handle, handle_len);
1.21 djm 1088: /* Reduce the request size */
1089: if (len < buflen)
1090: buflen = MAX(MIN_READ_SIZE, len);
1091: }
1092: if (max_req > 0) { /* max_req = 0 iff EOF received */
1093: if (size > 0 && offset > size) {
1094: /* Only one request at a time
1095: * after the expected EOF */
1096: debug3("Finish at %llu (%2d)",
1.26 itojun 1097: (unsigned long long)offset,
1098: num_req);
1.21 djm 1099: max_req = 1;
1.49 djm 1100: } else if (max_req <= conn->num_requests) {
1.21 djm 1101: ++max_req;
1102: }
1.1 djm 1103: }
1.21 djm 1104: break;
1105: default:
1.33 deraadt 1106: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1.1 djm 1107: SSH2_FXP_DATA, type);
1108: }
1.21 djm 1109: }
1.1 djm 1110:
1.39 fgsch 1111: if (showprogress && size)
1112: stop_progress_meter();
1113:
1.21 djm 1114: /* Sanity check */
1115: if (TAILQ_FIRST(&requests) != NULL)
1116: fatal("Transfer complete, but requests still in queue");
1117:
1118: if (read_error) {
1.28 markus 1119: error("Couldn't read from remote file \"%s\" : %s",
1.21 djm 1120: remote_path, fx2txt(status));
1.23 djm 1121: do_close(conn, handle, handle_len);
1.21 djm 1122: } else if (write_error) {
1123: error("Couldn't write to \"%s\": %s", local_path,
1124: strerror(write_errno));
1125: status = -1;
1.23 djm 1126: do_close(conn, handle, handle_len);
1.21 djm 1127: } else {
1.23 djm 1128: status = do_close(conn, handle, handle_len);
1.21 djm 1129:
1130: /* Override umask and utimes if asked */
1131: if (pflag && fchmod(local_fd, mode) == -1)
1132: error("Couldn't set mode on \"%s\": %s", local_path,
1.37 deraadt 1133: strerror(errno));
1.21 djm 1134: if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1135: struct timeval tv[2];
1136: tv[0].tv_sec = a->atime;
1137: tv[1].tv_sec = a->mtime;
1138: tv[0].tv_usec = tv[1].tv_usec = 0;
1139: if (utimes(local_path, tv) == -1)
1140: error("Can't set times on \"%s\": %s",
1.37 deraadt 1141: local_path, strerror(errno));
1.1 djm 1142: }
1.10 djm 1143: }
1.5 djm 1144: close(local_fd);
1145: buffer_free(&msg);
1.1 djm 1146: xfree(handle);
1.23 djm 1147:
1148: return(status);
1.1 djm 1149: }
1150:
1.89 ! djm 1151: static int
! 1152: download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
! 1153: Attrib *dirattrib, int pflag, int printflag, int depth)
! 1154: {
! 1155: int i, ret = 0;
! 1156: SFTP_DIRENT **dir_entries;
! 1157: char *filename, *new_src, *new_dst;
! 1158: mode_t mode = 0777;
! 1159:
! 1160: if (depth >= MAX_DIR_DEPTH) {
! 1161: error("Maximum directory depth exceeded: %d levels", depth);
! 1162: return -1;
! 1163: }
! 1164:
! 1165: if (dirattrib == NULL &&
! 1166: (dirattrib = do_stat(conn, src, 1)) == NULL) {
! 1167: error("Unable to stat remote directory \"%s\"", src);
! 1168: return -1;
! 1169: }
! 1170: if (!S_ISDIR(dirattrib->perm)) {
! 1171: error("\"%s\" is not a directory", src);
! 1172: return -1;
! 1173: }
! 1174: if (printflag)
! 1175: printf("Retrieving %s\n", src);
! 1176:
! 1177: if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
! 1178: mode = dirattrib->perm & 01777;
! 1179: else {
! 1180: debug("Server did not send permissions for "
! 1181: "directory \"%s\"", dst);
! 1182: }
! 1183:
! 1184: if (mkdir(dst, mode) == -1 && errno != EEXIST) {
! 1185: error("mkdir %s: %s", dst, strerror(errno));
! 1186: return -1;
! 1187: }
! 1188:
! 1189: if (do_readdir(conn, src, &dir_entries) == -1) {
! 1190: error("%s: Failed to get directory contents", src);
! 1191: return -1;
! 1192: }
! 1193:
! 1194: for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
! 1195: filename = dir_entries[i]->filename;
! 1196:
! 1197: new_dst = path_append(dst, filename);
! 1198: new_src = path_append(src, filename);
! 1199:
! 1200: if (S_ISDIR(dir_entries[i]->a.perm)) {
! 1201: if (strcmp(filename, ".") == 0 ||
! 1202: strcmp(filename, "..") == 0)
! 1203: continue;
! 1204: if (download_dir_internal(conn, new_src, new_dst,
! 1205: &(dir_entries[i]->a), pflag, printflag,
! 1206: depth + 1) == -1)
! 1207: ret = -1;
! 1208: } else if (S_ISREG(dir_entries[i]->a.perm) ) {
! 1209: if (do_download(conn, new_src, new_dst,
! 1210: &(dir_entries[i]->a), pflag) == -1) {
! 1211: error("Download of file %s to %s failed",
! 1212: new_src, new_dst);
! 1213: ret = -1;
! 1214: }
! 1215: } else
! 1216: logit("%s: not a regular file\n", new_src);
! 1217:
! 1218: xfree(new_dst);
! 1219: xfree(new_src);
! 1220: }
! 1221:
! 1222: if (pflag) {
! 1223: if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
! 1224: struct timeval tv[2];
! 1225: tv[0].tv_sec = dirattrib->atime;
! 1226: tv[1].tv_sec = dirattrib->mtime;
! 1227: tv[0].tv_usec = tv[1].tv_usec = 0;
! 1228: if (utimes(dst, tv) == -1)
! 1229: error("Can't set times on \"%s\": %s",
! 1230: dst, strerror(errno));
! 1231: } else
! 1232: debug("Server did not send times for directory "
! 1233: "\"%s\"", dst);
! 1234: }
! 1235:
! 1236: free_sftp_dirents(dir_entries);
! 1237:
! 1238: return ret;
! 1239: }
! 1240:
! 1241: int
! 1242: download_dir(struct sftp_conn *conn, char *src, char *dst,
! 1243: Attrib *dirattrib, int pflag, int printflag)
! 1244: {
! 1245: char *src_canon;
! 1246: int ret;
! 1247:
! 1248: if ((src_canon = do_realpath(conn, src)) == NULL) {
! 1249: error("Unable to canonicalise path \"%s\"", src);
! 1250: return -1;
! 1251: }
! 1252:
! 1253: ret = download_dir_internal(conn, src_canon, dst,
! 1254: dirattrib, pflag, printflag, 0);
! 1255: xfree(src_canon);
! 1256: return ret;
! 1257: }
! 1258:
1.1 djm 1259: int
1.23 djm 1260: do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1261: int pflag)
1.1 djm 1262: {
1.80 djm 1263: int local_fd;
1264: int status = SSH2_FX_OK;
1.22 djm 1265: u_int handle_len, id, type;
1.77 djm 1266: off_t offset;
1.20 djm 1267: char *handle, *data;
1.1 djm 1268: Buffer msg;
1269: struct stat sb;
1270: Attrib a;
1.21 djm 1271: u_int32_t startid;
1272: u_int32_t ackid;
1.22 djm 1273: struct outstanding_ack {
1274: u_int id;
1275: u_int len;
1.77 djm 1276: off_t offset;
1.28 markus 1277: TAILQ_ENTRY(outstanding_ack) tq;
1.22 djm 1278: };
1279: TAILQ_HEAD(ackhead, outstanding_ack) acks;
1.50 pedro 1280: struct outstanding_ack *ack = NULL;
1.22 djm 1281:
1282: TAILQ_INIT(&acks);
1.1 djm 1283:
1284: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1285: error("Couldn't open local file \"%s\" for reading: %s",
1286: local_path, strerror(errno));
1287: return(-1);
1288: }
1289: if (fstat(local_fd, &sb) == -1) {
1290: error("Couldn't fstat local file \"%s\": %s",
1291: local_path, strerror(errno));
1.41 djm 1292: close(local_fd);
1293: return(-1);
1294: }
1295: if (!S_ISREG(sb.st_mode)) {
1296: error("%s is not a regular file", local_path);
1.1 djm 1297: close(local_fd);
1298: return(-1);
1299: }
1300: stat_to_attrib(&sb, &a);
1301:
1302: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1303: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1304: a.perm &= 0777;
1305: if (!pflag)
1306: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1307:
1308: buffer_init(&msg);
1309:
1310: /* Send open request */
1.23 djm 1311: id = conn->msg_id++;
1.1 djm 1312: buffer_put_char(&msg, SSH2_FXP_OPEN);
1313: buffer_put_int(&msg, id);
1314: buffer_put_cstring(&msg, remote_path);
1315: buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1316: encode_attrib(&msg, &a);
1.23 djm 1317: send_msg(conn->fd_out, &msg);
1.33 deraadt 1318: debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1.1 djm 1319:
1320: buffer_clear(&msg);
1321:
1.88 djm 1322: handle = get_handle(conn->fd_in, id, &handle_len,
1323: "remote open(\"%s\")", remote_path);
1.1 djm 1324: if (handle == NULL) {
1325: close(local_fd);
1326: buffer_free(&msg);
1.80 djm 1327: return -1;
1.1 djm 1328: }
1329:
1.21 djm 1330: startid = ackid = id + 1;
1.23 djm 1331: data = xmalloc(conn->transfer_buflen);
1.20 djm 1332:
1.1 djm 1333: /* Read from local and write to remote */
1334: offset = 0;
1.39 fgsch 1335: if (showprogress)
1336: start_progress_meter(local_path, sb.st_size, &offset);
1337:
1.19 deraadt 1338: for (;;) {
1.1 djm 1339: int len;
1340:
1341: /*
1.51 deraadt 1342: * Can't use atomicio here because it returns 0 on EOF,
1.49 djm 1343: * thus losing the last block of the file.
1.51 deraadt 1344: * Simulate an EOF on interrupt, allowing ACKs from the
1.49 djm 1345: * server to drain.
1.1 djm 1346: */
1.80 djm 1347: if (interrupted || status != SSH2_FX_OK)
1.49 djm 1348: len = 0;
1349: else do
1.23 djm 1350: len = read(local_fd, data, conn->transfer_buflen);
1.1 djm 1351: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1352:
1353: if (len == -1)
1354: fatal("Couldn't read from \"%s\": %s", local_path,
1355: strerror(errno));
1.21 djm 1356:
1357: if (len != 0) {
1.22 djm 1358: ack = xmalloc(sizeof(*ack));
1359: ack->id = ++id;
1360: ack->offset = offset;
1361: ack->len = len;
1362: TAILQ_INSERT_TAIL(&acks, ack, tq);
1363:
1.21 djm 1364: buffer_clear(&msg);
1365: buffer_put_char(&msg, SSH2_FXP_WRITE);
1.22 djm 1366: buffer_put_int(&msg, ack->id);
1.21 djm 1367: buffer_put_string(&msg, handle, handle_len);
1368: buffer_put_int64(&msg, offset);
1369: buffer_put_string(&msg, data, len);
1.23 djm 1370: send_msg(conn->fd_out, &msg);
1.33 deraadt 1371: debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1.37 deraadt 1372: id, (unsigned long long)offset, len);
1.22 djm 1373: } else if (TAILQ_FIRST(&acks) == NULL)
1.1 djm 1374: break;
1375:
1.22 djm 1376: if (ack == NULL)
1377: fatal("Unexpected ACK %u", id);
1378:
1.28 markus 1379: if (id == startid || len == 0 ||
1.23 djm 1380: id - ackid >= conn->num_requests) {
1.32 markus 1381: u_int r_id;
1.31 djm 1382:
1.22 djm 1383: buffer_clear(&msg);
1.23 djm 1384: get_msg(conn->fd_in, &msg);
1.22 djm 1385: type = buffer_get_char(&msg);
1.31 djm 1386: r_id = buffer_get_int(&msg);
1.22 djm 1387:
1388: if (type != SSH2_FXP_STATUS)
1389: fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1390: "got %d", SSH2_FXP_STATUS, type);
1391:
1392: status = buffer_get_int(&msg);
1393: debug3("SSH2_FXP_STATUS %d", status);
1394:
1395: /* Find the request in our queue */
1.53 deraadt 1396: for (ack = TAILQ_FIRST(&acks);
1.31 djm 1397: ack != NULL && ack->id != r_id;
1.22 djm 1398: ack = TAILQ_NEXT(ack, tq))
1399: ;
1400: if (ack == NULL)
1.33 deraadt 1401: fatal("Can't find request for ID %u", r_id);
1.22 djm 1402: TAILQ_REMOVE(&acks, ack, tq);
1.77 djm 1403: debug3("In write loop, ack for %u %u bytes at %lld",
1404: ack->id, ack->len, (long long)ack->offset);
1.21 djm 1405: ++ackid;
1.34 deraadt 1406: xfree(ack);
1.1 djm 1407: }
1408: offset += len;
1.77 djm 1409: if (offset < 0)
1410: fatal("%s: offset < 0", __func__);
1.1 djm 1411: }
1.80 djm 1412: buffer_free(&msg);
1413:
1.39 fgsch 1414: if (showprogress)
1415: stop_progress_meter();
1.20 djm 1416: xfree(data);
1.1 djm 1417:
1.80 djm 1418: if (status != SSH2_FX_OK) {
1419: error("Couldn't write to remote file \"%s\": %s",
1420: remote_path, fx2txt(status));
1421: status = -1;
1422: }
1423:
1.1 djm 1424: if (close(local_fd) == -1) {
1425: error("Couldn't close local file \"%s\": %s", local_path,
1426: strerror(errno));
1.5 djm 1427: status = -1;
1.1 djm 1428: }
1429:
1.10 djm 1430: /* Override umask and utimes if asked */
1431: if (pflag)
1.23 djm 1432: do_fsetstat(conn, handle, handle_len, &a);
1.10 djm 1433:
1.80 djm 1434: if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1435: status = -1;
1436: xfree(handle);
1.5 djm 1437:
1.80 djm 1438: return status;
1.1 djm 1439: }
1.89 ! djm 1440:
! 1441: static int
! 1442: upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
! 1443: int pflag, int printflag, int depth)
! 1444: {
! 1445: int ret = 0, status;
! 1446: DIR *dirp;
! 1447: struct dirent *dp;
! 1448: char *filename, *new_src, *new_dst;
! 1449: struct stat sb;
! 1450: Attrib a;
! 1451:
! 1452: if (depth >= MAX_DIR_DEPTH) {
! 1453: error("Maximum directory depth exceeded: %d levels", depth);
! 1454: return -1;
! 1455: }
! 1456:
! 1457: if (stat(src, &sb) == -1) {
! 1458: error("Couldn't stat directory \"%s\": %s",
! 1459: src, strerror(errno));
! 1460: return -1;
! 1461: }
! 1462: if (!S_ISDIR(sb.st_mode)) {
! 1463: error("\"%s\" is not a directory", src);
! 1464: return -1;
! 1465: }
! 1466: if (printflag)
! 1467: printf("Entering %s\n", src);
! 1468:
! 1469: attrib_clear(&a);
! 1470: stat_to_attrib(&sb, &a);
! 1471: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
! 1472: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
! 1473: a.perm &= 01777;
! 1474: if (!pflag)
! 1475: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
! 1476:
! 1477: status = do_mkdir(conn, dst, &a, 0);
! 1478: /*
! 1479: * we lack a portable status for errno EEXIST,
! 1480: * so if we get a SSH2_FX_FAILURE back we must check
! 1481: * if it was created successfully.
! 1482: */
! 1483: if (status != SSH2_FX_OK) {
! 1484: if (status != SSH2_FX_FAILURE)
! 1485: return -1;
! 1486: if (do_stat(conn, dst, 0) == NULL)
! 1487: return -1;
! 1488: }
! 1489:
! 1490: if ((dirp = opendir(src)) == NULL) {
! 1491: error("Failed to open dir \"%s\": %s", src, strerror(errno));
! 1492: return -1;
! 1493: }
! 1494:
! 1495: while (((dp = readdir(dirp)) != NULL) && !interrupted) {
! 1496: if (dp->d_ino == 0)
! 1497: continue;
! 1498: filename = dp->d_name;
! 1499: new_dst = path_append(dst, filename);
! 1500: new_src = path_append(src, filename);
! 1501:
! 1502: if (S_ISDIR(DTTOIF(dp->d_type))) {
! 1503: if (strcmp(filename, ".") == 0 ||
! 1504: strcmp(filename, "..") == 0)
! 1505: continue;
! 1506:
! 1507: if (upload_dir_internal(conn, new_src, new_dst,
! 1508: pflag, depth + 1, printflag) == -1)
! 1509: ret = -1;
! 1510: } else if (S_ISREG(DTTOIF(dp->d_type)) ) {
! 1511: if (do_upload(conn, new_src, new_dst, pflag) == -1) {
! 1512: error("Uploading of file %s to %s failed!",
! 1513: new_src, new_dst);
! 1514: ret = -1;
! 1515: }
! 1516: } else
! 1517: logit("%s: not a regular file\n", filename);
! 1518: xfree(new_dst);
! 1519: xfree(new_src);
! 1520: }
! 1521:
! 1522: do_setstat(conn, dst, &a);
! 1523:
! 1524: (void) closedir(dirp);
! 1525: return ret;
! 1526: }
! 1527:
! 1528: int
! 1529: upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
! 1530: int pflag)
! 1531: {
! 1532: char *dst_canon;
! 1533: int ret;
! 1534:
! 1535: if ((dst_canon = do_realpath(conn, dst)) == NULL) {
! 1536: error("Unable to canonicalise path \"%s\"", dst);
! 1537: return -1;
! 1538: }
! 1539:
! 1540: ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
! 1541: xfree(dst_canon);
! 1542: return ret;
! 1543: }
! 1544:
! 1545: char *
! 1546: path_append(char *p1, char *p2)
! 1547: {
! 1548: char *ret;
! 1549: size_t len = strlen(p1) + strlen(p2) + 2;
! 1550:
! 1551: ret = xmalloc(len);
! 1552: strlcpy(ret, p1, len);
! 1553: if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
! 1554: strlcat(ret, "/", len);
! 1555: strlcat(ret, p2, len);
! 1556:
! 1557: return(ret);
! 1558: }
! 1559: