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