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