Annotation of src/usr.bin/ssh/sftp-client.c, Revision 1.92
1.92 ! djm 1: /* $OpenBSD: sftp-client.c,v 1.91 2010/06/18 04:43:08 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));
1.91 djm 708: buffer_free(&msg);
709: return NULL;
1.1 djm 710: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 711: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.1 djm 712: SSH2_FXP_NAME, type);
713:
714: count = buffer_get_int(&msg);
715: if (count != 1)
716: fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
717:
718: filename = buffer_get_string(&msg, NULL);
719: longname = buffer_get_string(&msg, NULL);
720: a = decode_attrib(&msg);
721:
722: debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
723:
724: xfree(longname);
725:
726: buffer_free(&msg);
727:
728: return(filename);
729: }
730:
731: int
1.23 djm 732: do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
1.1 djm 733: {
734: Buffer msg;
735: u_int status, id;
736:
737: buffer_init(&msg);
738:
739: /* Send rename request */
1.23 djm 740: id = conn->msg_id++;
1.81 djm 741: if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
742: buffer_put_char(&msg, SSH2_FXP_EXTENDED);
743: buffer_put_int(&msg, id);
744: buffer_put_cstring(&msg, "posix-rename@openssh.com");
745: } else {
746: buffer_put_char(&msg, SSH2_FXP_RENAME);
747: buffer_put_int(&msg, id);
748: }
1.1 djm 749: buffer_put_cstring(&msg, oldpath);
750: buffer_put_cstring(&msg, newpath);
1.23 djm 751: send_msg(conn->fd_out, &msg);
1.81 djm 752: debug3("Sent message %s \"%s\" -> \"%s\"",
753: (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
754: "SSH2_FXP_RENAME", oldpath, newpath);
1.1 djm 755: buffer_free(&msg);
756:
1.23 djm 757: status = get_status(conn->fd_in, id);
1.1 djm 758: if (status != SSH2_FX_OK)
1.23 djm 759: error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
760: newpath, fx2txt(status));
1.1 djm 761:
762: return(status);
1.11 djm 763: }
764:
765: int
1.23 djm 766: do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
1.11 djm 767: {
768: Buffer msg;
769: u_int status, id;
770:
1.23 djm 771: if (conn->version < 3) {
772: error("This server does not support the symlink operation");
773: return(SSH2_FX_OP_UNSUPPORTED);
774: }
775:
1.11 djm 776: buffer_init(&msg);
777:
1.48 djm 778: /* Send symlink request */
1.23 djm 779: id = conn->msg_id++;
1.11 djm 780: buffer_put_char(&msg, SSH2_FXP_SYMLINK);
781: buffer_put_int(&msg, id);
782: buffer_put_cstring(&msg, oldpath);
783: buffer_put_cstring(&msg, newpath);
1.23 djm 784: send_msg(conn->fd_out, &msg);
1.11 djm 785: debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
786: newpath);
787: buffer_free(&msg);
788:
1.23 djm 789: status = get_status(conn->fd_in, id);
1.11 djm 790: if (status != SSH2_FX_OK)
1.36 markus 791: error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
1.23 djm 792: newpath, fx2txt(status));
1.11 djm 793:
794: return(status);
795: }
796:
1.78 chl 797: #ifdef notyet
1.11 djm 798: char *
1.23 djm 799: do_readlink(struct sftp_conn *conn, char *path)
1.11 djm 800: {
801: Buffer msg;
802: u_int type, expected_id, count, id;
803: char *filename, *longname;
804: Attrib *a;
805:
1.23 djm 806: expected_id = id = conn->msg_id++;
807: send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
808: strlen(path));
1.11 djm 809:
810: buffer_init(&msg);
811:
1.23 djm 812: get_msg(conn->fd_in, &msg);
1.11 djm 813: type = buffer_get_char(&msg);
814: id = buffer_get_int(&msg);
815:
816: if (id != expected_id)
1.33 deraadt 817: fatal("ID mismatch (%u != %u)", id, expected_id);
1.11 djm 818:
819: if (type == SSH2_FXP_STATUS) {
820: u_int status = buffer_get_int(&msg);
821:
822: error("Couldn't readlink: %s", fx2txt(status));
823: return(NULL);
824: } else if (type != SSH2_FXP_NAME)
1.33 deraadt 825: fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
1.11 djm 826: SSH2_FXP_NAME, type);
827:
828: count = buffer_get_int(&msg);
829: if (count != 1)
830: fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
831:
832: filename = buffer_get_string(&msg, NULL);
833: longname = buffer_get_string(&msg, NULL);
834: a = decode_attrib(&msg);
835:
836: debug3("SSH_FXP_READLINK %s -> %s", path, filename);
837:
838: xfree(longname);
839:
840: buffer_free(&msg);
841:
842: return(filename);
1.82 djm 843: }
844: #endif
845:
846: int
1.84 dtucker 847: do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
1.82 djm 848: int quiet)
849: {
850: Buffer msg;
851: u_int id;
852:
853: if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
854: error("Server does not support statvfs@openssh.com extension");
855: return -1;
856: }
857:
858: id = conn->msg_id++;
859:
860: buffer_init(&msg);
861: buffer_clear(&msg);
862: buffer_put_char(&msg, SSH2_FXP_EXTENDED);
863: buffer_put_int(&msg, id);
864: buffer_put_cstring(&msg, "statvfs@openssh.com");
865: buffer_put_cstring(&msg, path);
866: send_msg(conn->fd_out, &msg);
867: buffer_free(&msg);
868:
869: return get_decode_statvfs(conn->fd_in, st, id, quiet);
870: }
871:
872: #ifdef notyet
873: int
874: do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
1.84 dtucker 875: struct sftp_statvfs *st, int quiet)
1.82 djm 876: {
877: Buffer msg;
878: u_int id;
879:
880: if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
881: error("Server does not support fstatvfs@openssh.com extension");
882: return -1;
883: }
884:
885: id = conn->msg_id++;
886:
887: buffer_init(&msg);
888: buffer_clear(&msg);
889: buffer_put_char(&msg, SSH2_FXP_EXTENDED);
890: buffer_put_int(&msg, id);
891: buffer_put_cstring(&msg, "fstatvfs@openssh.com");
892: buffer_put_string(&msg, handle, handle_len);
893: send_msg(conn->fd_out, &msg);
894: buffer_free(&msg);
895:
896: return get_decode_statvfs(conn->fd_in, st, id, quiet);
1.1 djm 897: }
1.78 chl 898: #endif
1.1 djm 899:
1.21 djm 900: static void
901: send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
902: char *handle, u_int handle_len)
903: {
904: Buffer msg;
1.28 markus 905:
1.21 djm 906: buffer_init(&msg);
907: buffer_clear(&msg);
908: buffer_put_char(&msg, SSH2_FXP_READ);
909: buffer_put_int(&msg, id);
910: buffer_put_string(&msg, handle, handle_len);
911: buffer_put_int64(&msg, offset);
912: buffer_put_int(&msg, len);
913: send_msg(fd_out, &msg);
914: buffer_free(&msg);
1.28 markus 915: }
1.21 djm 916:
1.1 djm 917: int
1.23 djm 918: do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1.89 djm 919: Attrib *a, int pflag)
1.1 djm 920: {
1.89 djm 921: Attrib junk;
1.21 djm 922: Buffer msg;
1.1 djm 923: char *handle;
1.57 dtucker 924: int local_fd, status = 0, write_error;
1.21 djm 925: int read_error, write_errno;
926: u_int64_t offset, size;
1.55 djm 927: u_int handle_len, mode, type, id, buflen, num_req, max_req;
1.39 fgsch 928: off_t progress_counter;
1.21 djm 929: struct request {
930: u_int id;
931: u_int len;
932: u_int64_t offset;
1.28 markus 933: TAILQ_ENTRY(request) tq;
1.21 djm 934: };
935: TAILQ_HEAD(reqhead, request) requests;
936: struct request *req;
937:
938: TAILQ_INIT(&requests);
1.1 djm 939:
1.89 djm 940: if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
941: return -1;
1.1 djm 942:
1.86 djm 943: /* Do not preserve set[ug]id here, as we do not preserve ownership */
1.1 djm 944: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1.38 djm 945: mode = a->perm & 0777;
1.1 djm 946: else
947: mode = 0666;
948:
1.14 djm 949: if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
1.41 djm 950: (!S_ISREG(a->perm))) {
951: error("Cannot download non-regular file: %s", remote_path);
1.14 djm 952: return(-1);
953: }
954:
1.21 djm 955: if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
956: size = a->size;
957: else
958: size = 0;
959:
1.23 djm 960: buflen = conn->transfer_buflen;
1.1 djm 961: buffer_init(&msg);
962:
963: /* Send open request */
1.23 djm 964: id = conn->msg_id++;
1.1 djm 965: buffer_put_char(&msg, SSH2_FXP_OPEN);
966: buffer_put_int(&msg, id);
967: buffer_put_cstring(&msg, remote_path);
968: buffer_put_int(&msg, SSH2_FXF_READ);
969: attrib_clear(&junk); /* Send empty attributes */
970: encode_attrib(&msg, &junk);
1.23 djm 971: send_msg(conn->fd_out, &msg);
1.33 deraadt 972: debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1.1 djm 973:
1.88 djm 974: handle = get_handle(conn->fd_in, id, &handle_len,
975: "remote open(\"%s\")", remote_path);
1.1 djm 976: if (handle == NULL) {
977: buffer_free(&msg);
978: return(-1);
979: }
980:
1.45 djm 981: local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
1.38 djm 982: mode | S_IWRITE);
1.23 djm 983: if (local_fd == -1) {
984: error("Couldn't open local file \"%s\" for writing: %s",
985: local_path, strerror(errno));
1.79 djm 986: do_close(conn, handle, handle_len);
1.24 markus 987: buffer_free(&msg);
988: xfree(handle);
1.23 djm 989: return(-1);
990: }
991:
1.1 djm 992: /* Read from remote and write to local */
1.21 djm 993: write_error = read_error = write_errno = num_req = offset = 0;
994: max_req = 1;
1.39 fgsch 995: progress_counter = 0;
996:
1.47 djm 997: if (showprogress && size != 0)
998: start_progress_meter(remote_path, size, &progress_counter);
1.39 fgsch 999:
1.21 djm 1000: while (num_req > 0 || max_req > 0) {
1001: char *data;
1.1 djm 1002: u_int len;
1003:
1.49 djm 1004: /*
1.51 deraadt 1005: * Simulate EOF on interrupt: stop sending new requests and
1.49 djm 1006: * allow outstanding requests to drain gracefully
1007: */
1008: if (interrupted) {
1009: if (num_req == 0) /* If we haven't started yet... */
1010: break;
1011: max_req = 0;
1012: }
1013:
1.21 djm 1014: /* Send some more requests */
1015: while (num_req < max_req) {
1.28 markus 1016: debug3("Request range %llu -> %llu (%d/%d)",
1.25 itojun 1017: (unsigned long long)offset,
1018: (unsigned long long)offset + buflen - 1,
1019: num_req, max_req);
1.21 djm 1020: req = xmalloc(sizeof(*req));
1.23 djm 1021: req->id = conn->msg_id++;
1.21 djm 1022: req->len = buflen;
1023: req->offset = offset;
1024: offset += buflen;
1025: num_req++;
1026: TAILQ_INSERT_TAIL(&requests, req, tq);
1.28 markus 1027: send_read_request(conn->fd_out, req->id, req->offset,
1.21 djm 1028: req->len, handle, handle_len);
1029: }
1.1 djm 1030:
1031: buffer_clear(&msg);
1.23 djm 1032: get_msg(conn->fd_in, &msg);
1.1 djm 1033: type = buffer_get_char(&msg);
1034: id = buffer_get_int(&msg);
1.33 deraadt 1035: debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
1.21 djm 1036:
1037: /* Find the request in our queue */
1.53 deraadt 1038: for (req = TAILQ_FIRST(&requests);
1.21 djm 1039: req != NULL && req->id != id;
1040: req = TAILQ_NEXT(req, tq))
1041: ;
1042: if (req == NULL)
1043: fatal("Unexpected reply %u", id);
1044:
1045: switch (type) {
1046: case SSH2_FXP_STATUS:
1.5 djm 1047: status = buffer_get_int(&msg);
1.21 djm 1048: if (status != SSH2_FX_EOF)
1049: read_error = 1;
1050: max_req = 0;
1051: TAILQ_REMOVE(&requests, req, tq);
1052: xfree(req);
1053: num_req--;
1054: break;
1055: case SSH2_FXP_DATA:
1056: data = buffer_get_string(&msg, &len);
1.26 itojun 1057: debug3("Received data %llu -> %llu",
1.28 markus 1058: (unsigned long long)req->offset,
1.26 itojun 1059: (unsigned long long)req->offset + len - 1);
1.21 djm 1060: if (len > req->len)
1061: fatal("Received more data than asked for "
1.37 deraadt 1062: "%u > %u", len, req->len);
1.21 djm 1063: if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
1.44 deraadt 1064: atomicio(vwrite, local_fd, data, len) != len) &&
1.21 djm 1065: !write_error) {
1066: write_errno = errno;
1067: write_error = 1;
1068: max_req = 0;
1069: }
1.39 fgsch 1070: progress_counter += len;
1.21 djm 1071: xfree(data);
1.1 djm 1072:
1.21 djm 1073: if (len == req->len) {
1074: TAILQ_REMOVE(&requests, req, tq);
1075: xfree(req);
1076: num_req--;
1077: } else {
1078: /* Resend the request for the missing data */
1079: debug3("Short data block, re-requesting "
1.26 itojun 1080: "%llu -> %llu (%2d)",
1.28 markus 1081: (unsigned long long)req->offset + len,
1.27 itojun 1082: (unsigned long long)req->offset +
1083: req->len - 1, num_req);
1.23 djm 1084: req->id = conn->msg_id++;
1.21 djm 1085: req->len -= len;
1086: req->offset += len;
1.28 markus 1087: send_read_request(conn->fd_out, req->id,
1.23 djm 1088: req->offset, req->len, handle, handle_len);
1.21 djm 1089: /* Reduce the request size */
1090: if (len < buflen)
1091: buflen = MAX(MIN_READ_SIZE, len);
1092: }
1093: if (max_req > 0) { /* max_req = 0 iff EOF received */
1094: if (size > 0 && offset > size) {
1095: /* Only one request at a time
1096: * after the expected EOF */
1097: debug3("Finish at %llu (%2d)",
1.26 itojun 1098: (unsigned long long)offset,
1099: num_req);
1.21 djm 1100: max_req = 1;
1.49 djm 1101: } else if (max_req <= conn->num_requests) {
1.21 djm 1102: ++max_req;
1103: }
1.1 djm 1104: }
1.21 djm 1105: break;
1106: default:
1.33 deraadt 1107: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
1.1 djm 1108: SSH2_FXP_DATA, type);
1109: }
1.21 djm 1110: }
1.1 djm 1111:
1.39 fgsch 1112: if (showprogress && size)
1113: stop_progress_meter();
1114:
1.21 djm 1115: /* Sanity check */
1116: if (TAILQ_FIRST(&requests) != NULL)
1117: fatal("Transfer complete, but requests still in queue");
1118:
1119: if (read_error) {
1.28 markus 1120: error("Couldn't read from remote file \"%s\" : %s",
1.21 djm 1121: remote_path, fx2txt(status));
1.23 djm 1122: do_close(conn, handle, handle_len);
1.21 djm 1123: } else if (write_error) {
1124: error("Couldn't write to \"%s\": %s", local_path,
1125: strerror(write_errno));
1126: status = -1;
1.23 djm 1127: do_close(conn, handle, handle_len);
1.21 djm 1128: } else {
1.23 djm 1129: status = do_close(conn, handle, handle_len);
1.21 djm 1130:
1131: /* Override umask and utimes if asked */
1132: if (pflag && fchmod(local_fd, mode) == -1)
1133: error("Couldn't set mode on \"%s\": %s", local_path,
1.37 deraadt 1134: strerror(errno));
1.21 djm 1135: if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1136: struct timeval tv[2];
1137: tv[0].tv_sec = a->atime;
1138: tv[1].tv_sec = a->mtime;
1139: tv[0].tv_usec = tv[1].tv_usec = 0;
1140: if (utimes(local_path, tv) == -1)
1141: error("Can't set times on \"%s\": %s",
1.37 deraadt 1142: local_path, strerror(errno));
1.1 djm 1143: }
1.10 djm 1144: }
1.5 djm 1145: close(local_fd);
1146: buffer_free(&msg);
1.1 djm 1147: xfree(handle);
1.23 djm 1148:
1149: return(status);
1.1 djm 1150: }
1151:
1.89 djm 1152: static int
1153: download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1154: Attrib *dirattrib, int pflag, int printflag, int depth)
1155: {
1156: int i, ret = 0;
1157: SFTP_DIRENT **dir_entries;
1158: char *filename, *new_src, *new_dst;
1159: mode_t mode = 0777;
1160:
1161: if (depth >= MAX_DIR_DEPTH) {
1162: error("Maximum directory depth exceeded: %d levels", depth);
1163: return -1;
1164: }
1165:
1166: if (dirattrib == NULL &&
1167: (dirattrib = do_stat(conn, src, 1)) == NULL) {
1168: error("Unable to stat remote directory \"%s\"", src);
1169: return -1;
1170: }
1171: if (!S_ISDIR(dirattrib->perm)) {
1172: error("\"%s\" is not a directory", src);
1173: return -1;
1174: }
1175: if (printflag)
1176: printf("Retrieving %s\n", src);
1177:
1178: if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1179: mode = dirattrib->perm & 01777;
1180: else {
1181: debug("Server did not send permissions for "
1182: "directory \"%s\"", dst);
1183: }
1184:
1185: if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1186: error("mkdir %s: %s", dst, strerror(errno));
1187: return -1;
1188: }
1189:
1190: if (do_readdir(conn, src, &dir_entries) == -1) {
1191: error("%s: Failed to get directory contents", src);
1192: return -1;
1193: }
1194:
1195: for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1196: filename = dir_entries[i]->filename;
1197:
1198: new_dst = path_append(dst, filename);
1199: new_src = path_append(src, filename);
1200:
1201: if (S_ISDIR(dir_entries[i]->a.perm)) {
1202: if (strcmp(filename, ".") == 0 ||
1203: strcmp(filename, "..") == 0)
1204: continue;
1205: if (download_dir_internal(conn, new_src, new_dst,
1206: &(dir_entries[i]->a), pflag, printflag,
1207: depth + 1) == -1)
1208: ret = -1;
1209: } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1210: if (do_download(conn, new_src, new_dst,
1211: &(dir_entries[i]->a), pflag) == -1) {
1212: error("Download of file %s to %s failed",
1213: new_src, new_dst);
1214: ret = -1;
1215: }
1216: } else
1217: logit("%s: not a regular file\n", new_src);
1218:
1219: xfree(new_dst);
1220: xfree(new_src);
1221: }
1222:
1223: if (pflag) {
1224: if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1225: struct timeval tv[2];
1226: tv[0].tv_sec = dirattrib->atime;
1227: tv[1].tv_sec = dirattrib->mtime;
1228: tv[0].tv_usec = tv[1].tv_usec = 0;
1229: if (utimes(dst, tv) == -1)
1230: error("Can't set times on \"%s\": %s",
1231: dst, strerror(errno));
1232: } else
1233: debug("Server did not send times for directory "
1234: "\"%s\"", dst);
1235: }
1236:
1237: free_sftp_dirents(dir_entries);
1238:
1239: return ret;
1240: }
1241:
1242: int
1243: download_dir(struct sftp_conn *conn, char *src, char *dst,
1244: Attrib *dirattrib, int pflag, int printflag)
1245: {
1246: char *src_canon;
1247: int ret;
1248:
1249: if ((src_canon = do_realpath(conn, src)) == NULL) {
1250: error("Unable to canonicalise path \"%s\"", src);
1251: return -1;
1252: }
1253:
1254: ret = download_dir_internal(conn, src_canon, dst,
1255: dirattrib, pflag, printflag, 0);
1256: xfree(src_canon);
1257: return ret;
1258: }
1259:
1.1 djm 1260: int
1.23 djm 1261: do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1262: int pflag)
1.1 djm 1263: {
1.80 djm 1264: int local_fd;
1265: int status = SSH2_FX_OK;
1.22 djm 1266: u_int handle_len, id, type;
1.77 djm 1267: off_t offset;
1.20 djm 1268: char *handle, *data;
1.1 djm 1269: Buffer msg;
1270: struct stat sb;
1271: Attrib a;
1.21 djm 1272: u_int32_t startid;
1273: u_int32_t ackid;
1.22 djm 1274: struct outstanding_ack {
1275: u_int id;
1276: u_int len;
1.77 djm 1277: off_t offset;
1.28 markus 1278: TAILQ_ENTRY(outstanding_ack) tq;
1.22 djm 1279: };
1280: TAILQ_HEAD(ackhead, outstanding_ack) acks;
1.50 pedro 1281: struct outstanding_ack *ack = NULL;
1.22 djm 1282:
1283: TAILQ_INIT(&acks);
1.1 djm 1284:
1285: if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1286: error("Couldn't open local file \"%s\" for reading: %s",
1287: local_path, strerror(errno));
1288: return(-1);
1289: }
1290: if (fstat(local_fd, &sb) == -1) {
1291: error("Couldn't fstat local file \"%s\": %s",
1292: local_path, strerror(errno));
1.41 djm 1293: close(local_fd);
1294: return(-1);
1295: }
1296: if (!S_ISREG(sb.st_mode)) {
1297: error("%s is not a regular file", local_path);
1.1 djm 1298: close(local_fd);
1299: return(-1);
1300: }
1301: stat_to_attrib(&sb, &a);
1302:
1303: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1304: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1305: a.perm &= 0777;
1306: if (!pflag)
1307: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1308:
1309: buffer_init(&msg);
1310:
1311: /* Send open request */
1.23 djm 1312: id = conn->msg_id++;
1.1 djm 1313: buffer_put_char(&msg, SSH2_FXP_OPEN);
1314: buffer_put_int(&msg, id);
1315: buffer_put_cstring(&msg, remote_path);
1316: buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1317: encode_attrib(&msg, &a);
1.23 djm 1318: send_msg(conn->fd_out, &msg);
1.33 deraadt 1319: debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1.1 djm 1320:
1321: buffer_clear(&msg);
1322:
1.88 djm 1323: handle = get_handle(conn->fd_in, id, &handle_len,
1324: "remote open(\"%s\")", remote_path);
1.1 djm 1325: if (handle == NULL) {
1326: close(local_fd);
1327: buffer_free(&msg);
1.80 djm 1328: return -1;
1.1 djm 1329: }
1330:
1.21 djm 1331: startid = ackid = id + 1;
1.23 djm 1332: data = xmalloc(conn->transfer_buflen);
1.20 djm 1333:
1.1 djm 1334: /* Read from local and write to remote */
1335: offset = 0;
1.39 fgsch 1336: if (showprogress)
1337: start_progress_meter(local_path, sb.st_size, &offset);
1338:
1.19 deraadt 1339: for (;;) {
1.1 djm 1340: int len;
1341:
1342: /*
1.51 deraadt 1343: * Can't use atomicio here because it returns 0 on EOF,
1.49 djm 1344: * thus losing the last block of the file.
1.51 deraadt 1345: * Simulate an EOF on interrupt, allowing ACKs from the
1.49 djm 1346: * server to drain.
1.1 djm 1347: */
1.80 djm 1348: if (interrupted || status != SSH2_FX_OK)
1.49 djm 1349: len = 0;
1350: else do
1.23 djm 1351: len = read(local_fd, data, conn->transfer_buflen);
1.1 djm 1352: while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1353:
1354: if (len == -1)
1355: fatal("Couldn't read from \"%s\": %s", local_path,
1356: strerror(errno));
1.21 djm 1357:
1358: if (len != 0) {
1.22 djm 1359: ack = xmalloc(sizeof(*ack));
1360: ack->id = ++id;
1361: ack->offset = offset;
1362: ack->len = len;
1363: TAILQ_INSERT_TAIL(&acks, ack, tq);
1364:
1.21 djm 1365: buffer_clear(&msg);
1366: buffer_put_char(&msg, SSH2_FXP_WRITE);
1.22 djm 1367: buffer_put_int(&msg, ack->id);
1.21 djm 1368: buffer_put_string(&msg, handle, handle_len);
1369: buffer_put_int64(&msg, offset);
1370: buffer_put_string(&msg, data, len);
1.23 djm 1371: send_msg(conn->fd_out, &msg);
1.33 deraadt 1372: debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1.37 deraadt 1373: id, (unsigned long long)offset, len);
1.22 djm 1374: } else if (TAILQ_FIRST(&acks) == NULL)
1.1 djm 1375: break;
1376:
1.22 djm 1377: if (ack == NULL)
1378: fatal("Unexpected ACK %u", id);
1379:
1.28 markus 1380: if (id == startid || len == 0 ||
1.23 djm 1381: id - ackid >= conn->num_requests) {
1.32 markus 1382: u_int r_id;
1.31 djm 1383:
1.22 djm 1384: buffer_clear(&msg);
1.23 djm 1385: get_msg(conn->fd_in, &msg);
1.22 djm 1386: type = buffer_get_char(&msg);
1.31 djm 1387: r_id = buffer_get_int(&msg);
1.22 djm 1388:
1389: if (type != SSH2_FXP_STATUS)
1390: fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1391: "got %d", SSH2_FXP_STATUS, type);
1392:
1393: status = buffer_get_int(&msg);
1394: debug3("SSH2_FXP_STATUS %d", status);
1395:
1396: /* Find the request in our queue */
1.53 deraadt 1397: for (ack = TAILQ_FIRST(&acks);
1.31 djm 1398: ack != NULL && ack->id != r_id;
1.22 djm 1399: ack = TAILQ_NEXT(ack, tq))
1400: ;
1401: if (ack == NULL)
1.33 deraadt 1402: fatal("Can't find request for ID %u", r_id);
1.22 djm 1403: TAILQ_REMOVE(&acks, ack, tq);
1.77 djm 1404: debug3("In write loop, ack for %u %u bytes at %lld",
1405: ack->id, ack->len, (long long)ack->offset);
1.21 djm 1406: ++ackid;
1.34 deraadt 1407: xfree(ack);
1.1 djm 1408: }
1409: offset += len;
1.77 djm 1410: if (offset < 0)
1411: fatal("%s: offset < 0", __func__);
1.1 djm 1412: }
1.80 djm 1413: buffer_free(&msg);
1414:
1.39 fgsch 1415: if (showprogress)
1416: stop_progress_meter();
1.20 djm 1417: xfree(data);
1.1 djm 1418:
1.80 djm 1419: if (status != SSH2_FX_OK) {
1420: error("Couldn't write to remote file \"%s\": %s",
1421: remote_path, fx2txt(status));
1422: status = -1;
1423: }
1424:
1.1 djm 1425: if (close(local_fd) == -1) {
1426: error("Couldn't close local file \"%s\": %s", local_path,
1427: strerror(errno));
1.5 djm 1428: status = -1;
1.1 djm 1429: }
1430:
1.10 djm 1431: /* Override umask and utimes if asked */
1432: if (pflag)
1.23 djm 1433: do_fsetstat(conn, handle, handle_len, &a);
1.10 djm 1434:
1.80 djm 1435: if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1436: status = -1;
1437: xfree(handle);
1.5 djm 1438:
1.80 djm 1439: return status;
1.1 djm 1440: }
1.89 djm 1441:
1442: static int
1443: upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1444: int pflag, int printflag, int depth)
1445: {
1446: int ret = 0, status;
1447: DIR *dirp;
1448: struct dirent *dp;
1449: char *filename, *new_src, *new_dst;
1450: struct stat sb;
1451: Attrib a;
1452:
1453: if (depth >= MAX_DIR_DEPTH) {
1454: error("Maximum directory depth exceeded: %d levels", depth);
1455: return -1;
1456: }
1457:
1458: if (stat(src, &sb) == -1) {
1459: error("Couldn't stat directory \"%s\": %s",
1460: src, strerror(errno));
1461: return -1;
1462: }
1463: if (!S_ISDIR(sb.st_mode)) {
1464: error("\"%s\" is not a directory", src);
1465: return -1;
1466: }
1467: if (printflag)
1468: printf("Entering %s\n", src);
1469:
1470: attrib_clear(&a);
1471: stat_to_attrib(&sb, &a);
1472: a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1473: a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1474: a.perm &= 01777;
1475: if (!pflag)
1476: a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1477:
1478: status = do_mkdir(conn, dst, &a, 0);
1479: /*
1480: * we lack a portable status for errno EEXIST,
1481: * so if we get a SSH2_FX_FAILURE back we must check
1482: * if it was created successfully.
1483: */
1484: if (status != SSH2_FX_OK) {
1485: if (status != SSH2_FX_FAILURE)
1486: return -1;
1487: if (do_stat(conn, dst, 0) == NULL)
1488: return -1;
1489: }
1490:
1491: if ((dirp = opendir(src)) == NULL) {
1492: error("Failed to open dir \"%s\": %s", src, strerror(errno));
1493: return -1;
1494: }
1495:
1496: while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1497: if (dp->d_ino == 0)
1498: continue;
1499: filename = dp->d_name;
1500: new_dst = path_append(dst, filename);
1501: new_src = path_append(src, filename);
1502:
1.90 dtucker 1503: if (lstat(new_src, &sb) == -1) {
1504: logit("%s: lstat failed: %s", filename,
1505: strerror(errno));
1506: ret = -1;
1507: } else if (S_ISDIR(sb.st_mode)) {
1.89 djm 1508: if (strcmp(filename, ".") == 0 ||
1509: strcmp(filename, "..") == 0)
1510: continue;
1511:
1512: if (upload_dir_internal(conn, new_src, new_dst,
1.92 ! djm 1513: pflag, printflag, depth + 1) == -1)
1.89 djm 1514: ret = -1;
1.90 dtucker 1515: } else if (S_ISREG(sb.st_mode)) {
1.89 djm 1516: if (do_upload(conn, new_src, new_dst, pflag) == -1) {
1517: error("Uploading of file %s to %s failed!",
1518: new_src, new_dst);
1519: ret = -1;
1520: }
1521: } else
1522: logit("%s: not a regular file\n", filename);
1523: xfree(new_dst);
1524: xfree(new_src);
1525: }
1526:
1527: do_setstat(conn, dst, &a);
1528:
1529: (void) closedir(dirp);
1530: return ret;
1531: }
1532:
1533: int
1534: upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
1535: int pflag)
1536: {
1537: char *dst_canon;
1538: int ret;
1539:
1540: if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1541: error("Unable to canonicalise path \"%s\"", dst);
1542: return -1;
1543: }
1544:
1545: ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
1546: xfree(dst_canon);
1547: return ret;
1548: }
1549:
1550: char *
1551: path_append(char *p1, char *p2)
1552: {
1553: char *ret;
1554: size_t len = strlen(p1) + strlen(p2) + 2;
1555:
1556: ret = xmalloc(len);
1557: strlcpy(ret, p1, len);
1558: if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1559: strlcat(ret, "/", len);
1560: strlcat(ret, p2, len);
1561:
1562: return(ret);
1563: }
1564: