]> andersk Git - openssh.git/blame - sftp-client.c
- (tim) [kex.c myproposal.h md-sha256.c openbsd-compat/sha2.c,h] Disable
[openssh.git] / sftp-client.c
CommitLineData
61e96248 1/*
ab3932ab 2 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
61e96248 3 *
ab3932ab 4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
61e96248 7 *
ab3932ab 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
61e96248 15 */
16
17/* XXX: memleaks */
18/* XXX: signed vs unsigned */
22e6c827 19/* XXX: remove all logging, only return status codes */
61e96248 20/* XXX: copy between two remote sites */
21
22#include "includes.h"
4095f623 23RCSID("$OpenBSD: sftp-client.c,v 1.60 2006/02/20 17:19:54 stevesk Exp $");
24
25#include <sys/types.h>
26#ifdef HAVE_SYS_STAT_H
27# include <sys/stat.h>
28#endif
b5b88c19 29# include <signal.h>
c25d3df7 30
9fd2a215 31#include "openbsd-compat/sys-queue.h"
61e96248 32
61e96248 33#include "buffer.h"
34#include "bufaux.h"
35#include "getput.h"
36#include "xmalloc.h"
37#include "log.h"
38#include "atomicio.h"
b65c3807 39#include "progressmeter.h"
61e96248 40
41#include "sftp.h"
42#include "sftp-common.h"
43#include "sftp-client.h"
44
0e5de6f8 45extern volatile sig_atomic_t interrupted;
b65c3807 46extern int showprogress;
47
2db34ac9 48/* Minimum amount of data to read at a time */
c25d3df7 49#define MIN_READ_SIZE 512
50
22e6c827 51struct sftp_conn {
52 int fd_in;
53 int fd_out;
54 u_int transfer_buflen;
55 u_int num_requests;
56 u_int version;
57 u_int msg_id;
58};
9c5a8165 59
396c147e 60static void
61e96248 61send_msg(int fd, Buffer *m)
62{
bf0bf24b 63 u_char mlen[4];
64
3eee3b86 65 if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
bf0bf24b 66 fatal("Outbound message too long %u", buffer_len(m));
61e96248 67
bf0bf24b 68 /* Send length first */
69 PUT_32BIT(mlen, buffer_len(m));
05624c18 70 if (atomicio(vwrite, fd, mlen, sizeof(mlen)) != sizeof(mlen))
bf0bf24b 71 fatal("Couldn't send packet: %s", strerror(errno));
61e96248 72
05624c18 73 if (atomicio(vwrite, fd, buffer_ptr(m), buffer_len(m)) != buffer_len(m))
61e96248 74 fatal("Couldn't send packet: %s", strerror(errno));
75
bf0bf24b 76 buffer_clear(m);
61e96248 77}
78
396c147e 79static void
61e96248 80get_msg(int fd, Buffer *m)
81{
bf0bf24b 82 u_int msg_len;
61e96248 83
bf0bf24b 84 buffer_append_space(m, 4);
05624c18 85 if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
86 if (errno == EPIPE)
87 fatal("Connection closed");
88 else
89 fatal("Couldn't read packet: %s", strerror(errno));
90 }
61e96248 91
bf0bf24b 92 msg_len = buffer_get_int(m);
3eee3b86 93 if (msg_len > SFTP_MAX_MSG_LENGTH)
9906a836 94 fatal("Received message too long %u", msg_len);
61e96248 95
bf0bf24b 96 buffer_append_space(m, msg_len);
05624c18 97 if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
98 if (errno == EPIPE)
99 fatal("Connection closed");
100 else
101 fatal("Read packet: %s", strerror(errno));
102 }
61e96248 103}
104
396c147e 105static void
61e96248 106send_string_request(int fd, u_int id, u_int code, char *s,
107 u_int len)
108{
109 Buffer msg;
110
111 buffer_init(&msg);
112 buffer_put_char(&msg, code);
113 buffer_put_int(&msg, id);
114 buffer_put_string(&msg, s, len);
115 send_msg(fd, &msg);
9906a836 116 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
61e96248 117 buffer_free(&msg);
118}
119
396c147e 120static void
61e96248 121send_string_attrs_request(int fd, u_int id, u_int code, char *s,
122 u_int len, Attrib *a)
123{
124 Buffer msg;
125
126 buffer_init(&msg);
127 buffer_put_char(&msg, code);
128 buffer_put_int(&msg, id);
129 buffer_put_string(&msg, s, len);
130 encode_attrib(&msg, a);
131 send_msg(fd, &msg);
9906a836 132 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
61e96248 133 buffer_free(&msg);
134}
135
396c147e 136static u_int
9906a836 137get_status(int fd, u_int expected_id)
61e96248 138{
139 Buffer msg;
140 u_int type, id, status;
141
142 buffer_init(&msg);
143 get_msg(fd, &msg);
144 type = buffer_get_char(&msg);
145 id = buffer_get_int(&msg);
146
147 if (id != expected_id)
9906a836 148 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 149 if (type != SSH2_FXP_STATUS)
9906a836 150 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
61e96248 151 SSH2_FXP_STATUS, type);
152
153 status = buffer_get_int(&msg);
154 buffer_free(&msg);
155
9906a836 156 debug3("SSH2_FXP_STATUS %u", status);
61e96248 157
158 return(status);
159}
160
396c147e 161static char *
61e96248 162get_handle(int fd, u_int expected_id, u_int *len)
163{
164 Buffer msg;
165 u_int type, id;
166 char *handle;
167
168 buffer_init(&msg);
169 get_msg(fd, &msg);
170 type = buffer_get_char(&msg);
171 id = buffer_get_int(&msg);
172
173 if (id != expected_id)
9906a836 174 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 175 if (type == SSH2_FXP_STATUS) {
176 int status = buffer_get_int(&msg);
177
178 error("Couldn't get handle: %s", fx2txt(status));
aa41be57 179 buffer_free(&msg);
61e96248 180 return(NULL);
181 } else if (type != SSH2_FXP_HANDLE)
9906a836 182 fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
61e96248 183 SSH2_FXP_HANDLE, type);
184
185 handle = buffer_get_string(&msg, len);
186 buffer_free(&msg);
187
188 return(handle);
189}
190
396c147e 191static Attrib *
d50d9b63 192get_decode_stat(int fd, u_int expected_id, int quiet)
61e96248 193{
194 Buffer msg;
195 u_int type, id;
196 Attrib *a;
197
198 buffer_init(&msg);
199 get_msg(fd, &msg);
200
201 type = buffer_get_char(&msg);
202 id = buffer_get_int(&msg);
203
9906a836 204 debug3("Received stat reply T:%u I:%u", type, id);
61e96248 205 if (id != expected_id)
9906a836 206 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 207 if (type == SSH2_FXP_STATUS) {
208 int status = buffer_get_int(&msg);
209
d50d9b63 210 if (quiet)
211 debug("Couldn't stat remote file: %s", fx2txt(status));
212 else
213 error("Couldn't stat remote file: %s", fx2txt(status));
aa41be57 214 buffer_free(&msg);
61e96248 215 return(NULL);
216 } else if (type != SSH2_FXP_ATTRS) {
9906a836 217 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
61e96248 218 SSH2_FXP_ATTRS, type);
219 }
220 a = decode_attrib(&msg);
221 buffer_free(&msg);
222
223 return(a);
224}
225
22e6c827 226struct sftp_conn *
227do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
61e96248 228{
9906a836 229 u_int type;
230 int version;
61e96248 231 Buffer msg;
22e6c827 232 struct sftp_conn *ret;
61e96248 233
234 buffer_init(&msg);
235 buffer_put_char(&msg, SSH2_FXP_INIT);
236 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
237 send_msg(fd_out, &msg);
238
239 buffer_clear(&msg);
240
241 get_msg(fd_in, &msg);
242
2b87da3b 243 /* Expecting a VERSION reply */
61e96248 244 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
9906a836 245 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
61e96248 246 type);
247 buffer_free(&msg);
22e6c827 248 return(NULL);
61e96248 249 }
250 version = buffer_get_int(&msg);
251
252 debug2("Remote version: %d", version);
253
254 /* Check for extensions */
255 while (buffer_len(&msg) > 0) {
256 char *name = buffer_get_string(&msg, NULL);
257 char *value = buffer_get_string(&msg, NULL);
258
259 debug2("Init extension: \"%s\"", name);
260 xfree(name);
261 xfree(value);
262 }
263
264 buffer_free(&msg);
3a7fe5ba 265
22e6c827 266 ret = xmalloc(sizeof(*ret));
267 ret->fd_in = fd_in;
268 ret->fd_out = fd_out;
269 ret->transfer_buflen = transfer_buflen;
270 ret->num_requests = num_requests;
271 ret->version = version;
272 ret->msg_id = 1;
273
274 /* Some filexfer v.0 servers don't support large packets */
275 if (version == 0)
92053302 276 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
22e6c827 277
278 return(ret);
279}
280
281u_int
282sftp_proto_version(struct sftp_conn *conn)
283{
284 return(conn->version);
61e96248 285}
286
287int
22e6c827 288do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
61e96248 289{
290 u_int id, status;
291 Buffer msg;
292
293 buffer_init(&msg);
294
22e6c827 295 id = conn->msg_id++;
61e96248 296 buffer_put_char(&msg, SSH2_FXP_CLOSE);
297 buffer_put_int(&msg, id);
298 buffer_put_string(&msg, handle, handle_len);
22e6c827 299 send_msg(conn->fd_out, &msg);
9906a836 300 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
61e96248 301
22e6c827 302 status = get_status(conn->fd_in, id);
61e96248 303 if (status != SSH2_FX_OK)
304 error("Couldn't close file: %s", fx2txt(status));
305
306 buffer_free(&msg);
307
308 return(status);
309}
310
2e4fb373 311
396c147e 312static int
22e6c827 313do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
2e4fb373 314 SFTP_DIRENT ***dir)
61e96248 315{
316 Buffer msg;
2ceb8101 317 u_int count, type, id, handle_len, i, expected_id, ents = 0;
61e96248 318 char *handle;
319
22e6c827 320 id = conn->msg_id++;
61e96248 321
322 buffer_init(&msg);
323 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
324 buffer_put_int(&msg, id);
325 buffer_put_cstring(&msg, path);
22e6c827 326 send_msg(conn->fd_out, &msg);
61e96248 327
328 buffer_clear(&msg);
329
22e6c827 330 handle = get_handle(conn->fd_in, id, &handle_len);
61e96248 331 if (handle == NULL)
332 return(-1);
333
2e4fb373 334 if (dir) {
335 ents = 0;
336 *dir = xmalloc(sizeof(**dir));
337 (*dir)[0] = NULL;
338 }
2e4fb373 339
0e5de6f8 340 for (; !interrupted;) {
22e6c827 341 id = expected_id = conn->msg_id++;
61e96248 342
9906a836 343 debug3("Sending SSH2_FXP_READDIR I:%u", id);
61e96248 344
345 buffer_clear(&msg);
346 buffer_put_char(&msg, SSH2_FXP_READDIR);
347 buffer_put_int(&msg, id);
348 buffer_put_string(&msg, handle, handle_len);
22e6c827 349 send_msg(conn->fd_out, &msg);
61e96248 350
351 buffer_clear(&msg);
352
22e6c827 353 get_msg(conn->fd_in, &msg);
61e96248 354
355 type = buffer_get_char(&msg);
356 id = buffer_get_int(&msg);
357
9906a836 358 debug3("Received reply T:%u I:%u", type, id);
61e96248 359
360 if (id != expected_id)
9906a836 361 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 362
363 if (type == SSH2_FXP_STATUS) {
364 int status = buffer_get_int(&msg);
365
366 debug3("Received SSH2_FXP_STATUS %d", status);
367
368 if (status == SSH2_FX_EOF) {
369 break;
370 } else {
371 error("Couldn't read directory: %s",
372 fx2txt(status));
22e6c827 373 do_close(conn, handle, handle_len);
3e2f2431 374 xfree(handle);
b655a207 375 return(status);
61e96248 376 }
377 } else if (type != SSH2_FXP_NAME)
9906a836 378 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
61e96248 379 SSH2_FXP_NAME, type);
380
381 count = buffer_get_int(&msg);
0426a3b4 382 if (count == 0)
383 break;
384 debug3("Received %d SSH2_FXP_NAME responses", count);
184eed6a 385 for (i = 0; i < count; i++) {
61e96248 386 char *filename, *longname;
387 Attrib *a;
388
389 filename = buffer_get_string(&msg, NULL);
390 longname = buffer_get_string(&msg, NULL);
391 a = decode_attrib(&msg);
392
2e4fb373 393 if (printflag)
394 printf("%s\n", longname);
395
396 if (dir) {
cd332296 397 *dir = xrealloc(*dir, sizeof(**dir) *
2e4fb373 398 (ents + 2));
399 (*dir)[ents] = xmalloc(sizeof(***dir));
400 (*dir)[ents]->filename = xstrdup(filename);
401 (*dir)[ents]->longname = xstrdup(longname);
402 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
403 (*dir)[++ents] = NULL;
404 }
61e96248 405
406 xfree(filename);
407 xfree(longname);
408 }
409 }
410
411 buffer_free(&msg);
22e6c827 412 do_close(conn, handle, handle_len);
61e96248 413 xfree(handle);
414
0e5de6f8 415 /* Don't return partial matches on interrupt */
416 if (interrupted && dir != NULL && *dir != NULL) {
417 free_sftp_dirents(*dir);
418 *dir = xmalloc(sizeof(**dir));
419 **dir = NULL;
420 }
421
61e96248 422 return(0);
423}
424
2e4fb373 425int
22e6c827 426do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
2e4fb373 427{
22e6c827 428 return(do_lsreaddir(conn, path, 0, dir));
2e4fb373 429}
430
431void free_sftp_dirents(SFTP_DIRENT **s)
432{
433 int i;
184eed6a 434
435 for (i = 0; s[i]; i++) {
2e4fb373 436 xfree(s[i]->filename);
437 xfree(s[i]->longname);
438 xfree(s[i]);
439 }
440 xfree(s);
441}
442
61e96248 443int
22e6c827 444do_rm(struct sftp_conn *conn, char *path)
61e96248 445{
446 u_int status, id;
447
448 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
449
22e6c827 450 id = conn->msg_id++;
762715ce 451 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
22e6c827 452 strlen(path));
453 status = get_status(conn->fd_in, id);
61e96248 454 if (status != SSH2_FX_OK)
455 error("Couldn't delete file: %s", fx2txt(status));
456 return(status);
457}
458
459int
22e6c827 460do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
61e96248 461{
462 u_int status, id;
463
22e6c827 464 id = conn->msg_id++;
465 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
61e96248 466 strlen(path), a);
467
22e6c827 468 status = get_status(conn->fd_in, id);
61e96248 469 if (status != SSH2_FX_OK)
470 error("Couldn't create directory: %s", fx2txt(status));
471
472 return(status);
473}
474
475int
22e6c827 476do_rmdir(struct sftp_conn *conn, char *path)
61e96248 477{
478 u_int status, id;
479
22e6c827 480 id = conn->msg_id++;
481 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
482 strlen(path));
61e96248 483
22e6c827 484 status = get_status(conn->fd_in, id);
61e96248 485 if (status != SSH2_FX_OK)
486 error("Couldn't remove directory: %s", fx2txt(status));
487
488 return(status);
489}
490
491Attrib *
22e6c827 492do_stat(struct sftp_conn *conn, char *path, int quiet)
61e96248 493{
494 u_int id;
495
22e6c827 496 id = conn->msg_id++;
497
762715ce 498 send_string_request(conn->fd_out, id,
499 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
22e6c827 500 path, strlen(path));
501
502 return(get_decode_stat(conn->fd_in, id, quiet));
61e96248 503}
504
505Attrib *
22e6c827 506do_lstat(struct sftp_conn *conn, char *path, int quiet)
61e96248 507{
508 u_int id;
509
22e6c827 510 if (conn->version == 0) {
511 if (quiet)
512 debug("Server version does not support lstat operation");
513 else
bbe88b6d 514 logit("Server version does not support lstat operation");
9c74a24d 515 return(do_stat(conn, path, quiet));
22e6c827 516 }
517
518 id = conn->msg_id++;
519 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
520 strlen(path));
521
522 return(get_decode_stat(conn->fd_in, id, quiet));
61e96248 523}
524
525Attrib *
22e6c827 526do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
61e96248 527{
528 u_int id;
529
22e6c827 530 id = conn->msg_id++;
531 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
532 handle_len);
533
534 return(get_decode_stat(conn->fd_in, id, quiet));
61e96248 535}
536
537int
22e6c827 538do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
61e96248 539{
540 u_int status, id;
541
22e6c827 542 id = conn->msg_id++;
543 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
61e96248 544 strlen(path), a);
545
22e6c827 546 status = get_status(conn->fd_in, id);
61e96248 547 if (status != SSH2_FX_OK)
548 error("Couldn't setstat on \"%s\": %s", path,
549 fx2txt(status));
550
551 return(status);
552}
553
554int
22e6c827 555do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
61e96248 556 Attrib *a)
557{
558 u_int status, id;
559
22e6c827 560 id = conn->msg_id++;
561 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
61e96248 562 handle_len, a);
563
22e6c827 564 status = get_status(conn->fd_in, id);
61e96248 565 if (status != SSH2_FX_OK)
566 error("Couldn't fsetstat: %s", fx2txt(status));
567
568 return(status);
569}
570
571char *
22e6c827 572do_realpath(struct sftp_conn *conn, char *path)
61e96248 573{
574 Buffer msg;
575 u_int type, expected_id, count, id;
576 char *filename, *longname;
577 Attrib *a;
578
22e6c827 579 expected_id = id = conn->msg_id++;
580 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
581 strlen(path));
61e96248 582
583 buffer_init(&msg);
584
22e6c827 585 get_msg(conn->fd_in, &msg);
61e96248 586 type = buffer_get_char(&msg);
587 id = buffer_get_int(&msg);
588
589 if (id != expected_id)
9906a836 590 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 591
592 if (type == SSH2_FXP_STATUS) {
593 u_int status = buffer_get_int(&msg);
594
595 error("Couldn't canonicalise: %s", fx2txt(status));
596 return(NULL);
597 } else if (type != SSH2_FXP_NAME)
9906a836 598 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
61e96248 599 SSH2_FXP_NAME, type);
600
601 count = buffer_get_int(&msg);
602 if (count != 1)
603 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
604
605 filename = buffer_get_string(&msg, NULL);
606 longname = buffer_get_string(&msg, NULL);
607 a = decode_attrib(&msg);
608
609 debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
610
611 xfree(longname);
612
613 buffer_free(&msg);
614
615 return(filename);
616}
617
618int
22e6c827 619do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
61e96248 620{
621 Buffer msg;
622 u_int status, id;
623
624 buffer_init(&msg);
625
626 /* Send rename request */
22e6c827 627 id = conn->msg_id++;
61e96248 628 buffer_put_char(&msg, SSH2_FXP_RENAME);
629 buffer_put_int(&msg, id);
630 buffer_put_cstring(&msg, oldpath);
631 buffer_put_cstring(&msg, newpath);
22e6c827 632 send_msg(conn->fd_out, &msg);
61e96248 633 debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
634 newpath);
635 buffer_free(&msg);
636
22e6c827 637 status = get_status(conn->fd_in, id);
61e96248 638 if (status != SSH2_FX_OK)
22e6c827 639 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
640 newpath, fx2txt(status));
61e96248 641
642 return(status);
643}
644
3a7fe5ba 645int
22e6c827 646do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
3a7fe5ba 647{
648 Buffer msg;
649 u_int status, id;
650
22e6c827 651 if (conn->version < 3) {
652 error("This server does not support the symlink operation");
653 return(SSH2_FX_OP_UNSUPPORTED);
654 }
655
3a7fe5ba 656 buffer_init(&msg);
657
b093b499 658 /* Send symlink request */
22e6c827 659 id = conn->msg_id++;
3a7fe5ba 660 buffer_put_char(&msg, SSH2_FXP_SYMLINK);
661 buffer_put_int(&msg, id);
662 buffer_put_cstring(&msg, oldpath);
663 buffer_put_cstring(&msg, newpath);
22e6c827 664 send_msg(conn->fd_out, &msg);
3a7fe5ba 665 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
666 newpath);
667 buffer_free(&msg);
668
22e6c827 669 status = get_status(conn->fd_in, id);
3a7fe5ba 670 if (status != SSH2_FX_OK)
9db1a8e9 671 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
22e6c827 672 newpath, fx2txt(status));
3a7fe5ba 673
674 return(status);
675}
676
677char *
22e6c827 678do_readlink(struct sftp_conn *conn, char *path)
3a7fe5ba 679{
680 Buffer msg;
681 u_int type, expected_id, count, id;
682 char *filename, *longname;
683 Attrib *a;
684
22e6c827 685 expected_id = id = conn->msg_id++;
686 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
687 strlen(path));
3a7fe5ba 688
689 buffer_init(&msg);
690
22e6c827 691 get_msg(conn->fd_in, &msg);
3a7fe5ba 692 type = buffer_get_char(&msg);
693 id = buffer_get_int(&msg);
694
695 if (id != expected_id)
9906a836 696 fatal("ID mismatch (%u != %u)", id, expected_id);
3a7fe5ba 697
698 if (type == SSH2_FXP_STATUS) {
699 u_int status = buffer_get_int(&msg);
700
701 error("Couldn't readlink: %s", fx2txt(status));
702 return(NULL);
703 } else if (type != SSH2_FXP_NAME)
9906a836 704 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
3a7fe5ba 705 SSH2_FXP_NAME, type);
706
707 count = buffer_get_int(&msg);
708 if (count != 1)
709 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
710
711 filename = buffer_get_string(&msg, NULL);
712 longname = buffer_get_string(&msg, NULL);
713 a = decode_attrib(&msg);
714
715 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
716
717 xfree(longname);
718
719 buffer_free(&msg);
720
721 return(filename);
722}
723
c25d3df7 724static void
725send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
726 char *handle, u_int handle_len)
727{
728 Buffer msg;
762715ce 729
c25d3df7 730 buffer_init(&msg);
731 buffer_clear(&msg);
732 buffer_put_char(&msg, SSH2_FXP_READ);
733 buffer_put_int(&msg, id);
734 buffer_put_string(&msg, handle, handle_len);
735 buffer_put_int64(&msg, offset);
736 buffer_put_int(&msg, len);
737 send_msg(fd_out, &msg);
738 buffer_free(&msg);
762715ce 739}
c25d3df7 740
61e96248 741int
22e6c827 742do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
743 int pflag)
61e96248 744{
61e96248 745 Attrib junk, *a;
c25d3df7 746 Buffer msg;
747 char *handle;
a056dfa2 748 int local_fd, status = 0, write_error;
c25d3df7 749 int read_error, write_errno;
750 u_int64_t offset, size;
2ceb8101 751 u_int handle_len, mode, type, id, buflen, num_req, max_req;
b65c3807 752 off_t progress_counter;
c25d3df7 753 struct request {
754 u_int id;
755 u_int len;
756 u_int64_t offset;
762715ce 757 TAILQ_ENTRY(request) tq;
c25d3df7 758 };
759 TAILQ_HEAD(reqhead, request) requests;
760 struct request *req;
761
762 TAILQ_INIT(&requests);
61e96248 763
22e6c827 764 a = do_stat(conn, remote_path, 0);
61e96248 765 if (a == NULL)
766 return(-1);
767
768 /* XXX: should we preserve set[ug]id? */
769 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
bfd934b9 770 mode = a->perm & 0777;
61e96248 771 else
772 mode = 0666;
773
d50d9b63 774 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
538840a2 775 (!S_ISREG(a->perm))) {
776 error("Cannot download non-regular file: %s", remote_path);
d50d9b63 777 return(-1);
778 }
779
c25d3df7 780 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
781 size = a->size;
782 else
783 size = 0;
784
22e6c827 785 buflen = conn->transfer_buflen;
61e96248 786 buffer_init(&msg);
787
788 /* Send open request */
22e6c827 789 id = conn->msg_id++;
61e96248 790 buffer_put_char(&msg, SSH2_FXP_OPEN);
791 buffer_put_int(&msg, id);
792 buffer_put_cstring(&msg, remote_path);
793 buffer_put_int(&msg, SSH2_FXF_READ);
794 attrib_clear(&junk); /* Send empty attributes */
795 encode_attrib(&msg, &junk);
22e6c827 796 send_msg(conn->fd_out, &msg);
9906a836 797 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
61e96248 798
22e6c827 799 handle = get_handle(conn->fd_in, id, &handle_len);
61e96248 800 if (handle == NULL) {
801 buffer_free(&msg);
61e96248 802 return(-1);
803 }
804
aff51935 805 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
bfd934b9 806 mode | S_IWRITE);
22e6c827 807 if (local_fd == -1) {
808 error("Couldn't open local file \"%s\" for writing: %s",
809 local_path, strerror(errno));
f60ace9f 810 buffer_free(&msg);
811 xfree(handle);
22e6c827 812 return(-1);
813 }
814
61e96248 815 /* Read from remote and write to local */
c25d3df7 816 write_error = read_error = write_errno = num_req = offset = 0;
817 max_req = 1;
b65c3807 818 progress_counter = 0;
819
213bab61 820 if (showprogress && size != 0)
821 start_progress_meter(remote_path, size, &progress_counter);
b65c3807 822
c25d3df7 823 while (num_req > 0 || max_req > 0) {
61e96248 824 char *data;
c25d3df7 825 u_int len;
61e96248 826
0e5de6f8 827 /*
f2107e97 828 * Simulate EOF on interrupt: stop sending new requests and
0e5de6f8 829 * allow outstanding requests to drain gracefully
830 */
831 if (interrupted) {
832 if (num_req == 0) /* If we haven't started yet... */
833 break;
834 max_req = 0;
835 }
836
c25d3df7 837 /* Send some more requests */
838 while (num_req < max_req) {
762715ce 839 debug3("Request range %llu -> %llu (%d/%d)",
8627f3e0 840 (unsigned long long)offset,
841 (unsigned long long)offset + buflen - 1,
842 num_req, max_req);
c25d3df7 843 req = xmalloc(sizeof(*req));
22e6c827 844 req->id = conn->msg_id++;
c25d3df7 845 req->len = buflen;
846 req->offset = offset;
847 offset += buflen;
848 num_req++;
849 TAILQ_INSERT_TAIL(&requests, req, tq);
762715ce 850 send_read_request(conn->fd_out, req->id, req->offset,
c25d3df7 851 req->len, handle, handle_len);
852 }
61e96248 853
854 buffer_clear(&msg);
22e6c827 855 get_msg(conn->fd_in, &msg);
61e96248 856 type = buffer_get_char(&msg);
857 id = buffer_get_int(&msg);
9906a836 858 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
c25d3df7 859
860 /* Find the request in our queue */
f8cc7664 861 for (req = TAILQ_FIRST(&requests);
c25d3df7 862 req != NULL && req->id != id;
863 req = TAILQ_NEXT(req, tq))
864 ;
865 if (req == NULL)
866 fatal("Unexpected reply %u", id);
867
868 switch (type) {
869 case SSH2_FXP_STATUS:
0426a3b4 870 status = buffer_get_int(&msg);
c25d3df7 871 if (status != SSH2_FX_EOF)
872 read_error = 1;
873 max_req = 0;
874 TAILQ_REMOVE(&requests, req, tq);
875 xfree(req);
876 num_req--;
877 break;
878 case SSH2_FXP_DATA:
879 data = buffer_get_string(&msg, &len);
bfa7f960 880 debug3("Received data %llu -> %llu",
762715ce 881 (unsigned long long)req->offset,
bfa7f960 882 (unsigned long long)req->offset + len - 1);
c25d3df7 883 if (len > req->len)
884 fatal("Received more data than asked for "
b77a87e5 885 "%u > %u", len, req->len);
c25d3df7 886 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
dc54438a 887 atomicio(vwrite, local_fd, data, len) != len) &&
c25d3df7 888 !write_error) {
889 write_errno = errno;
890 write_error = 1;
891 max_req = 0;
892 }
b65c3807 893 progress_counter += len;
c25d3df7 894 xfree(data);
61e96248 895
c25d3df7 896 if (len == req->len) {
897 TAILQ_REMOVE(&requests, req, tq);
898 xfree(req);
899 num_req--;
900 } else {
901 /* Resend the request for the missing data */
902 debug3("Short data block, re-requesting "
bfa7f960 903 "%llu -> %llu (%2d)",
762715ce 904 (unsigned long long)req->offset + len,
5fc7dbc9 905 (unsigned long long)req->offset +
906 req->len - 1, num_req);
22e6c827 907 req->id = conn->msg_id++;
c25d3df7 908 req->len -= len;
909 req->offset += len;
762715ce 910 send_read_request(conn->fd_out, req->id,
22e6c827 911 req->offset, req->len, handle, handle_len);
c25d3df7 912 /* Reduce the request size */
913 if (len < buflen)
914 buflen = MAX(MIN_READ_SIZE, len);
915 }
916 if (max_req > 0) { /* max_req = 0 iff EOF received */
917 if (size > 0 && offset > size) {
918 /* Only one request at a time
919 * after the expected EOF */
920 debug3("Finish at %llu (%2d)",
bfa7f960 921 (unsigned long long)offset,
922 num_req);
c25d3df7 923 max_req = 1;
0e5de6f8 924 } else if (max_req <= conn->num_requests) {
c25d3df7 925 ++max_req;
926 }
61e96248 927 }
c25d3df7 928 break;
929 default:
9906a836 930 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
61e96248 931 SSH2_FXP_DATA, type);
932 }
61e96248 933 }
61e96248 934
b65c3807 935 if (showprogress && size)
936 stop_progress_meter();
937
c25d3df7 938 /* Sanity check */
939 if (TAILQ_FIRST(&requests) != NULL)
940 fatal("Transfer complete, but requests still in queue");
941
942 if (read_error) {
762715ce 943 error("Couldn't read from remote file \"%s\" : %s",
22e6c827 944 remote_path, fx2txt(status));
945 do_close(conn, handle, handle_len);
c25d3df7 946 } else if (write_error) {
22e6c827 947 error("Couldn't write to \"%s\": %s", local_path,
948 strerror(write_errno));
949 status = -1;
950 do_close(conn, handle, handle_len);
c25d3df7 951 } else {
22e6c827 952 status = do_close(conn, handle, handle_len);
c25d3df7 953
954 /* Override umask and utimes if asked */
663fd560 955#ifdef HAVE_FCHMOD
c25d3df7 956 if (pflag && fchmod(local_fd, mode) == -1)
aff51935 957#else
c25d3df7 958 if (pflag && chmod(local_path, mode) == -1)
663fd560 959#endif /* HAVE_FCHMOD */
c25d3df7 960 error("Couldn't set mode on \"%s\": %s", local_path,
b77a87e5 961 strerror(errno));
c25d3df7 962 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
963 struct timeval tv[2];
964 tv[0].tv_sec = a->atime;
965 tv[1].tv_sec = a->mtime;
966 tv[0].tv_usec = tv[1].tv_usec = 0;
967 if (utimes(local_path, tv) == -1)
968 error("Can't set times on \"%s\": %s",
b77a87e5 969 local_path, strerror(errno));
c25d3df7 970 }
58c54a79 971 }
0426a3b4 972 close(local_fd);
973 buffer_free(&msg);
974 xfree(handle);
22e6c827 975
976 return(status);
61e96248 977}
978
979int
22e6c827 980do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
981 int pflag)
61e96248 982{
375f867e 983 int local_fd, status;
b2bab059 984 u_int handle_len, id, type;
61e96248 985 u_int64_t offset;
375f867e 986 char *handle, *data;
61e96248 987 Buffer msg;
988 struct stat sb;
989 Attrib a;
c25d3df7 990 u_int32_t startid;
991 u_int32_t ackid;
b2bab059 992 struct outstanding_ack {
993 u_int id;
994 u_int len;
995 u_int64_t offset;
762715ce 996 TAILQ_ENTRY(outstanding_ack) tq;
b2bab059 997 };
998 TAILQ_HEAD(ackhead, outstanding_ack) acks;
6e007f08 999 struct outstanding_ack *ack = NULL;
b2bab059 1000
1001 TAILQ_INIT(&acks);
61e96248 1002
1003 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1004 error("Couldn't open local file \"%s\" for reading: %s",
1005 local_path, strerror(errno));
1006 return(-1);
1007 }
1008 if (fstat(local_fd, &sb) == -1) {
1009 error("Couldn't fstat local file \"%s\": %s",
1010 local_path, strerror(errno));
1011 close(local_fd);
1012 return(-1);
1013 }
538840a2 1014 if (!S_ISREG(sb.st_mode)) {
1015 error("%s is not a regular file", local_path);
1016 close(local_fd);
1017 return(-1);
1018 }
61e96248 1019 stat_to_attrib(&sb, &a);
1020
1021 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1022 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1023 a.perm &= 0777;
1024 if (!pflag)
1025 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1026
1027 buffer_init(&msg);
1028
1029 /* Send open request */
22e6c827 1030 id = conn->msg_id++;
61e96248 1031 buffer_put_char(&msg, SSH2_FXP_OPEN);
1032 buffer_put_int(&msg, id);
1033 buffer_put_cstring(&msg, remote_path);
1034 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1035 encode_attrib(&msg, &a);
22e6c827 1036 send_msg(conn->fd_out, &msg);
9906a836 1037 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
61e96248 1038
1039 buffer_clear(&msg);
1040
22e6c827 1041 handle = get_handle(conn->fd_in, id, &handle_len);
61e96248 1042 if (handle == NULL) {
1043 close(local_fd);
1044 buffer_free(&msg);
1045 return(-1);
1046 }
1047
c25d3df7 1048 startid = ackid = id + 1;
22e6c827 1049 data = xmalloc(conn->transfer_buflen);
375f867e 1050
61e96248 1051 /* Read from local and write to remote */
1052 offset = 0;
b65c3807 1053 if (showprogress)
1054 start_progress_meter(local_path, sb.st_size, &offset);
b65c3807 1055
184eed6a 1056 for (;;) {
61e96248 1057 int len;
61e96248 1058
1059 /*
f2107e97 1060 * Can't use atomicio here because it returns 0 on EOF,
0e5de6f8 1061 * thus losing the last block of the file.
f2107e97 1062 * Simulate an EOF on interrupt, allowing ACKs from the
0e5de6f8 1063 * server to drain.
61e96248 1064 */
0e5de6f8 1065 if (interrupted)
1066 len = 0;
1067 else do
22e6c827 1068 len = read(local_fd, data, conn->transfer_buflen);
61e96248 1069 while ((len == -1) && (errno == EINTR || errno == EAGAIN));
1070
1071 if (len == -1)
1072 fatal("Couldn't read from \"%s\": %s", local_path,
1073 strerror(errno));
c25d3df7 1074
1075 if (len != 0) {
b2bab059 1076 ack = xmalloc(sizeof(*ack));
1077 ack->id = ++id;
1078 ack->offset = offset;
1079 ack->len = len;
1080 TAILQ_INSERT_TAIL(&acks, ack, tq);
1081
c25d3df7 1082 buffer_clear(&msg);
1083 buffer_put_char(&msg, SSH2_FXP_WRITE);
b2bab059 1084 buffer_put_int(&msg, ack->id);
c25d3df7 1085 buffer_put_string(&msg, handle, handle_len);
1086 buffer_put_int64(&msg, offset);
1087 buffer_put_string(&msg, data, len);
22e6c827 1088 send_msg(conn->fd_out, &msg);
9906a836 1089 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
b77a87e5 1090 id, (unsigned long long)offset, len);
b2bab059 1091 } else if (TAILQ_FIRST(&acks) == NULL)
61e96248 1092 break;
1093
b2bab059 1094 if (ack == NULL)
1095 fatal("Unexpected ACK %u", id);
1096
762715ce 1097 if (id == startid || len == 0 ||
22e6c827 1098 id - ackid >= conn->num_requests) {
875ec275 1099 u_int r_id;
af2b3cd9 1100
b2bab059 1101 buffer_clear(&msg);
22e6c827 1102 get_msg(conn->fd_in, &msg);
b2bab059 1103 type = buffer_get_char(&msg);
af2b3cd9 1104 r_id = buffer_get_int(&msg);
b2bab059 1105
1106 if (type != SSH2_FXP_STATUS)
1107 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1108 "got %d", SSH2_FXP_STATUS, type);
1109
1110 status = buffer_get_int(&msg);
1111 debug3("SSH2_FXP_STATUS %d", status);
1112
1113 /* Find the request in our queue */
f8cc7664 1114 for (ack = TAILQ_FIRST(&acks);
af2b3cd9 1115 ack != NULL && ack->id != r_id;
b2bab059 1116 ack = TAILQ_NEXT(ack, tq))
1117 ;
1118 if (ack == NULL)
9906a836 1119 fatal("Can't find request for ID %u", r_id);
b2bab059 1120 TAILQ_REMOVE(&acks, ack, tq);
1121
c25d3df7 1122 if (status != SSH2_FX_OK) {
1123 error("Couldn't write to remote file \"%s\": %s",
b77a87e5 1124 remote_path, fx2txt(status));
22e6c827 1125 do_close(conn, handle, handle_len);
c25d3df7 1126 close(local_fd);
3e2f2431 1127 xfree(data);
1128 xfree(ack);
c25d3df7 1129 goto done;
1130 }
9906a836 1131 debug3("In write loop, ack for %u %u bytes at %llu",
4e2e5cfd 1132 ack->id, ack->len, (unsigned long long)ack->offset);
c25d3df7 1133 ++ackid;
30e37ee6 1134 xfree(ack);
61e96248 1135 }
61e96248 1136 offset += len;
1137 }
b65c3807 1138 if (showprogress)
1139 stop_progress_meter();
375f867e 1140 xfree(data);
61e96248 1141
1142 if (close(local_fd) == -1) {
1143 error("Couldn't close local file \"%s\": %s", local_path,
1144 strerror(errno));
22e6c827 1145 do_close(conn, handle, handle_len);
0426a3b4 1146 status = -1;
1147 goto done;
61e96248 1148 }
1149
58c54a79 1150 /* Override umask and utimes if asked */
1151 if (pflag)
22e6c827 1152 do_fsetstat(conn, handle, handle_len, &a);
58c54a79 1153
22e6c827 1154 status = do_close(conn, handle, handle_len);
0426a3b4 1155
1156done:
1157 xfree(handle);
1158 buffer_free(&msg);
22e6c827 1159 return(status);
61e96248 1160}
This page took 0.447147 seconds and 5 git commands to generate.