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