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