blob: 9670a76608671c4bf953c3775357c2a20e8897db [file] [log] [blame]
Damien Miller33804262001-02-04 23:20:18 +11001/*
2 * Copyright (c) 2001 Damien Miller. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25/* XXX: memleaks */
26/* XXX: signed vs unsigned */
27/* XXX: redesign to allow concurrent overlapped operations */
28/* XXX: we use fatal too much, error may be more appropriate in places */
29/* XXX: copy between two remote sites */
30
31#include "includes.h"
Ben Lindstrombba81212001-06-25 05:01:22 +000032RCSID("$OpenBSD: sftp-client.c,v 1.17 2001/06/23 15:12:20 itojun Exp $");
Damien Miller33804262001-02-04 23:20:18 +110033
34#include "ssh.h"
35#include "buffer.h"
36#include "bufaux.h"
37#include "getput.h"
38#include "xmalloc.h"
39#include "log.h"
40#include "atomicio.h"
41#include "pathnames.h"
42
43#include "sftp.h"
44#include "sftp-common.h"
45#include "sftp-client.h"
46
47/* How much data to read/write at at time during copies */
48/* XXX: what should this be? */
49#define COPY_SIZE 8192
50
Ben Lindstrom288cc392001-02-09 02:58:04 +000051/* Message ID */
52static u_int msg_id = 1;
53
Ben Lindstrombba81212001-06-25 05:01:22 +000054static void
Damien Miller33804262001-02-04 23:20:18 +110055send_msg(int fd, Buffer *m)
56{
57 int mlen = buffer_len(m);
58 int len;
59 Buffer oqueue;
60
61 buffer_init(&oqueue);
62 buffer_put_int(&oqueue, mlen);
63 buffer_append(&oqueue, buffer_ptr(m), mlen);
64 buffer_consume(m, mlen);
65
66 len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
67 if (len <= 0)
68 fatal("Couldn't send packet: %s", strerror(errno));
69
70 buffer_free(&oqueue);
71}
72
Ben Lindstrombba81212001-06-25 05:01:22 +000073static void
Damien Miller33804262001-02-04 23:20:18 +110074get_msg(int fd, Buffer *m)
75{
76 u_int len, msg_len;
77 unsigned char buf[4096];
78
79 len = atomicio(read, fd, buf, 4);
Damien Millercafff192001-03-19 22:29:46 +110080 if (len == 0)
81 fatal("Connection closed");
82 else if (len == -1)
Damien Miller33804262001-02-04 23:20:18 +110083 fatal("Couldn't read packet: %s", strerror(errno));
84
85 msg_len = GET_32BIT(buf);
86 if (msg_len > 256 * 1024)
87 fatal("Received message too long %d", msg_len);
88
89 while (msg_len) {
90 len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
Damien Millercafff192001-03-19 22:29:46 +110091 if (len == 0)
92 fatal("Connection closed");
93 else if (len == -1)
Damien Miller33804262001-02-04 23:20:18 +110094 fatal("Couldn't read packet: %s", strerror(errno));
95
96 msg_len -= len;
97 buffer_append(m, buf, len);
98 }
99}
100
Ben Lindstrombba81212001-06-25 05:01:22 +0000101static void
Damien Miller33804262001-02-04 23:20:18 +1100102send_string_request(int fd, u_int id, u_int code, char *s,
103 u_int len)
104{
105 Buffer msg;
106
107 buffer_init(&msg);
108 buffer_put_char(&msg, code);
109 buffer_put_int(&msg, id);
110 buffer_put_string(&msg, s, len);
111 send_msg(fd, &msg);
112 debug3("Sent message fd %d T:%d I:%d", fd, code, id);
113 buffer_free(&msg);
114}
115
Ben Lindstrombba81212001-06-25 05:01:22 +0000116static void
Damien Miller33804262001-02-04 23:20:18 +1100117send_string_attrs_request(int fd, u_int id, u_int code, char *s,
118 u_int len, Attrib *a)
119{
120 Buffer msg;
121
122 buffer_init(&msg);
123 buffer_put_char(&msg, code);
124 buffer_put_int(&msg, id);
125 buffer_put_string(&msg, s, len);
126 encode_attrib(&msg, a);
127 send_msg(fd, &msg);
128 debug3("Sent message fd %d T:%d I:%d", fd, code, id);
129 buffer_free(&msg);
130}
131
Ben Lindstrombba81212001-06-25 05:01:22 +0000132static u_int
Damien Miller33804262001-02-04 23:20:18 +1100133get_status(int fd, int expected_id)
134{
135 Buffer msg;
136 u_int type, id, status;
137
138 buffer_init(&msg);
139 get_msg(fd, &msg);
140 type = buffer_get_char(&msg);
141 id = buffer_get_int(&msg);
142
143 if (id != expected_id)
144 fatal("ID mismatch (%d != %d)", id, expected_id);
145 if (type != SSH2_FXP_STATUS)
146 fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d",
147 SSH2_FXP_STATUS, type);
148
149 status = buffer_get_int(&msg);
150 buffer_free(&msg);
151
152 debug3("SSH2_FXP_STATUS %d", status);
153
154 return(status);
155}
156
Ben Lindstrombba81212001-06-25 05:01:22 +0000157static char *
Damien Miller33804262001-02-04 23:20:18 +1100158get_handle(int fd, u_int expected_id, u_int *len)
159{
160 Buffer msg;
161 u_int type, id;
162 char *handle;
163
164 buffer_init(&msg);
165 get_msg(fd, &msg);
166 type = buffer_get_char(&msg);
167 id = buffer_get_int(&msg);
168
169 if (id != expected_id)
170 fatal("ID mismatch (%d != %d)", id, expected_id);
171 if (type == SSH2_FXP_STATUS) {
172 int status = buffer_get_int(&msg);
173
174 error("Couldn't get handle: %s", fx2txt(status));
175 return(NULL);
176 } else if (type != SSH2_FXP_HANDLE)
177 fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d",
178 SSH2_FXP_HANDLE, type);
179
180 handle = buffer_get_string(&msg, len);
181 buffer_free(&msg);
182
183 return(handle);
184}
185
Ben Lindstrombba81212001-06-25 05:01:22 +0000186static Attrib *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000187get_decode_stat(int fd, u_int expected_id, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100188{
189 Buffer msg;
190 u_int type, id;
191 Attrib *a;
192
193 buffer_init(&msg);
194 get_msg(fd, &msg);
195
196 type = buffer_get_char(&msg);
197 id = buffer_get_int(&msg);
198
199 debug3("Received stat reply T:%d I:%d", type, id);
200 if (id != expected_id)
201 fatal("ID mismatch (%d != %d)", id, expected_id);
202 if (type == SSH2_FXP_STATUS) {
203 int status = buffer_get_int(&msg);
204
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000205 if (quiet)
206 debug("Couldn't stat remote file: %s", fx2txt(status));
207 else
208 error("Couldn't stat remote file: %s", fx2txt(status));
Damien Miller33804262001-02-04 23:20:18 +1100209 return(NULL);
210 } else if (type != SSH2_FXP_ATTRS) {
211 fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d",
212 SSH2_FXP_ATTRS, type);
213 }
214 a = decode_attrib(&msg);
215 buffer_free(&msg);
216
217 return(a);
218}
219
220int
221do_init(int fd_in, int fd_out)
222{
223 int type, version;
224 Buffer msg;
225
226 buffer_init(&msg);
227 buffer_put_char(&msg, SSH2_FXP_INIT);
228 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
229 send_msg(fd_out, &msg);
230
231 buffer_clear(&msg);
232
233 get_msg(fd_in, &msg);
234
Kevin Stevesef4eea92001-02-05 12:42:17 +0000235 /* Expecting a VERSION reply */
Damien Miller33804262001-02-04 23:20:18 +1100236 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
237 error("Invalid packet back from SSH2_FXP_INIT (type %d)",
238 type);
239 buffer_free(&msg);
240 return(-1);
241 }
242 version = buffer_get_int(&msg);
243
244 debug2("Remote version: %d", version);
245
246 /* Check for extensions */
247 while (buffer_len(&msg) > 0) {
248 char *name = buffer_get_string(&msg, NULL);
249 char *value = buffer_get_string(&msg, NULL);
250
251 debug2("Init extension: \"%s\"", name);
252 xfree(name);
253 xfree(value);
254 }
255
256 buffer_free(&msg);
Damien Miller058316f2001-03-08 10:08:49 +1100257
258 return(version);
Damien Miller33804262001-02-04 23:20:18 +1100259}
260
261int
262do_close(int fd_in, int fd_out, char *handle, u_int handle_len)
263{
264 u_int id, status;
265 Buffer msg;
266
267 buffer_init(&msg);
268
Ben Lindstrom288cc392001-02-09 02:58:04 +0000269 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100270 buffer_put_char(&msg, SSH2_FXP_CLOSE);
271 buffer_put_int(&msg, id);
272 buffer_put_string(&msg, handle, handle_len);
273 send_msg(fd_out, &msg);
274 debug3("Sent message SSH2_FXP_CLOSE I:%d", id);
275
276 status = get_status(fd_in, id);
277 if (status != SSH2_FX_OK)
278 error("Couldn't close file: %s", fx2txt(status));
279
280 buffer_free(&msg);
281
282 return(status);
283}
284
Damien Miller4870afd2001-03-14 10:27:09 +1100285
Ben Lindstrombba81212001-06-25 05:01:22 +0000286static int
Ben Lindstroma3700052001-04-05 23:26:32 +0000287do_lsreaddir(int fd_in, int fd_out, char *path, int printflag,
Damien Miller4870afd2001-03-14 10:27:09 +1100288 SFTP_DIRENT ***dir)
Damien Miller33804262001-02-04 23:20:18 +1100289{
290 Buffer msg;
Ben Lindstrom025df4a2001-03-14 15:16:34 +0000291 u_int type, id, handle_len, i, expected_id, ents = 0;
Damien Miller33804262001-02-04 23:20:18 +1100292 char *handle;
293
Ben Lindstrom288cc392001-02-09 02:58:04 +0000294 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100295
296 buffer_init(&msg);
297 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
298 buffer_put_int(&msg, id);
299 buffer_put_cstring(&msg, path);
300 send_msg(fd_out, &msg);
301
302 buffer_clear(&msg);
303
304 handle = get_handle(fd_in, id, &handle_len);
305 if (handle == NULL)
306 return(-1);
307
Damien Miller4870afd2001-03-14 10:27:09 +1100308 if (dir) {
309 ents = 0;
310 *dir = xmalloc(sizeof(**dir));
311 (*dir)[0] = NULL;
312 }
313
314
Damien Miller33804262001-02-04 23:20:18 +1100315 for(;;) {
316 int count;
317
Ben Lindstrom288cc392001-02-09 02:58:04 +0000318 id = expected_id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100319
320 debug3("Sending SSH2_FXP_READDIR I:%d", id);
321
322 buffer_clear(&msg);
323 buffer_put_char(&msg, SSH2_FXP_READDIR);
324 buffer_put_int(&msg, id);
325 buffer_put_string(&msg, handle, handle_len);
326 send_msg(fd_out, &msg);
327
328 buffer_clear(&msg);
329
330 get_msg(fd_in, &msg);
331
332 type = buffer_get_char(&msg);
333 id = buffer_get_int(&msg);
334
335 debug3("Received reply T:%d I:%d", type, id);
336
337 if (id != expected_id)
338 fatal("ID mismatch (%d != %d)", id, expected_id);
339
340 if (type == SSH2_FXP_STATUS) {
341 int status = buffer_get_int(&msg);
342
343 debug3("Received SSH2_FXP_STATUS %d", status);
344
345 if (status == SSH2_FX_EOF) {
346 break;
347 } else {
348 error("Couldn't read directory: %s",
349 fx2txt(status));
350 do_close(fd_in, fd_out, handle, handle_len);
Ben Lindstrom10ac33f2001-02-10 21:53:40 +0000351 return(status);
Damien Miller33804262001-02-04 23:20:18 +1100352 }
353 } else if (type != SSH2_FXP_NAME)
354 fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
355 SSH2_FXP_NAME, type);
356
357 count = buffer_get_int(&msg);
Damien Millerd7686fd2001-02-10 00:40:03 +1100358 if (count == 0)
359 break;
360 debug3("Received %d SSH2_FXP_NAME responses", count);
Damien Miller33804262001-02-04 23:20:18 +1100361 for(i = 0; i < count; i++) {
362 char *filename, *longname;
363 Attrib *a;
364
365 filename = buffer_get_string(&msg, NULL);
366 longname = buffer_get_string(&msg, NULL);
367 a = decode_attrib(&msg);
368
Damien Miller4870afd2001-03-14 10:27:09 +1100369 if (printflag)
370 printf("%s\n", longname);
371
372 if (dir) {
Ben Lindstroma3700052001-04-05 23:26:32 +0000373 *dir = xrealloc(*dir, sizeof(**dir) *
Damien Miller4870afd2001-03-14 10:27:09 +1100374 (ents + 2));
375 (*dir)[ents] = xmalloc(sizeof(***dir));
376 (*dir)[ents]->filename = xstrdup(filename);
377 (*dir)[ents]->longname = xstrdup(longname);
378 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
379 (*dir)[++ents] = NULL;
380 }
Damien Miller33804262001-02-04 23:20:18 +1100381
382 xfree(filename);
383 xfree(longname);
384 }
385 }
386
387 buffer_free(&msg);
388 do_close(fd_in, fd_out, handle, handle_len);
389 xfree(handle);
390
391 return(0);
392}
393
394int
Damien Miller4870afd2001-03-14 10:27:09 +1100395do_ls(int fd_in, int fd_out, char *path)
396{
397 return(do_lsreaddir(fd_in, fd_out, path, 1, NULL));
398}
399
400int
401do_readdir(int fd_in, int fd_out, char *path, SFTP_DIRENT ***dir)
402{
403 return(do_lsreaddir(fd_in, fd_out, path, 0, dir));
404}
405
406void free_sftp_dirents(SFTP_DIRENT **s)
407{
408 int i;
409
410 for(i = 0; s[i]; i++) {
411 xfree(s[i]->filename);
412 xfree(s[i]->longname);
413 xfree(s[i]);
414 }
415 xfree(s);
416}
417
418int
Damien Miller33804262001-02-04 23:20:18 +1100419do_rm(int fd_in, int fd_out, char *path)
420{
421 u_int status, id;
422
423 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
424
Ben Lindstrom288cc392001-02-09 02:58:04 +0000425 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100426 send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path));
427 status = get_status(fd_in, id);
428 if (status != SSH2_FX_OK)
429 error("Couldn't delete file: %s", fx2txt(status));
430 return(status);
431}
432
433int
434do_mkdir(int fd_in, int fd_out, char *path, Attrib *a)
435{
436 u_int status, id;
437
Ben Lindstrom288cc392001-02-09 02:58:04 +0000438 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100439 send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path,
440 strlen(path), a);
441
442 status = get_status(fd_in, id);
443 if (status != SSH2_FX_OK)
444 error("Couldn't create directory: %s", fx2txt(status));
445
446 return(status);
447}
448
449int
450do_rmdir(int fd_in, int fd_out, char *path)
451{
452 u_int status, id;
453
Ben Lindstrom288cc392001-02-09 02:58:04 +0000454 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100455 send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path));
456
457 status = get_status(fd_in, id);
458 if (status != SSH2_FX_OK)
459 error("Couldn't remove directory: %s", fx2txt(status));
460
461 return(status);
462}
463
464Attrib *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000465do_stat(int fd_in, int fd_out, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100466{
467 u_int id;
468
Ben Lindstrom288cc392001-02-09 02:58:04 +0000469 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100470 send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path));
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000471 return(get_decode_stat(fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100472}
473
474Attrib *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000475do_lstat(int fd_in, int fd_out, char *path, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100476{
477 u_int id;
478
Ben Lindstrom288cc392001-02-09 02:58:04 +0000479 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100480 send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path));
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000481 return(get_decode_stat(fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100482}
483
484Attrib *
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000485do_fstat(int fd_in, int fd_out, char *handle, u_int handle_len, int quiet)
Damien Miller33804262001-02-04 23:20:18 +1100486{
487 u_int id;
488
Ben Lindstrom288cc392001-02-09 02:58:04 +0000489 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100490 send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len);
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000491 return(get_decode_stat(fd_in, id, quiet));
Damien Miller33804262001-02-04 23:20:18 +1100492}
493
494int
495do_setstat(int fd_in, int fd_out, char *path, Attrib *a)
496{
497 u_int status, id;
498
Ben Lindstrom288cc392001-02-09 02:58:04 +0000499 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100500 send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path,
501 strlen(path), a);
502
503 status = get_status(fd_in, id);
504 if (status != SSH2_FX_OK)
505 error("Couldn't setstat on \"%s\": %s", path,
506 fx2txt(status));
507
508 return(status);
509}
510
511int
512do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len,
513 Attrib *a)
514{
515 u_int status, id;
516
Ben Lindstrom288cc392001-02-09 02:58:04 +0000517 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100518 send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle,
519 handle_len, a);
520
521 status = get_status(fd_in, id);
522 if (status != SSH2_FX_OK)
523 error("Couldn't fsetstat: %s", fx2txt(status));
524
525 return(status);
526}
527
528char *
529do_realpath(int fd_in, int fd_out, char *path)
530{
531 Buffer msg;
532 u_int type, expected_id, count, id;
533 char *filename, *longname;
534 Attrib *a;
535
Ben Lindstrom288cc392001-02-09 02:58:04 +0000536 expected_id = id = msg_id++;
Damien Miller058316f2001-03-08 10:08:49 +1100537 send_string_request(fd_out, id, SSH2_FXP_REALPATH, path, strlen(path));
Damien Miller33804262001-02-04 23:20:18 +1100538
539 buffer_init(&msg);
540
541 get_msg(fd_in, &msg);
542 type = buffer_get_char(&msg);
543 id = buffer_get_int(&msg);
544
545 if (id != expected_id)
546 fatal("ID mismatch (%d != %d)", id, expected_id);
547
548 if (type == SSH2_FXP_STATUS) {
549 u_int status = buffer_get_int(&msg);
550
551 error("Couldn't canonicalise: %s", fx2txt(status));
552 return(NULL);
553 } else if (type != SSH2_FXP_NAME)
554 fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
555 SSH2_FXP_NAME, type);
556
557 count = buffer_get_int(&msg);
558 if (count != 1)
559 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
560
561 filename = buffer_get_string(&msg, NULL);
562 longname = buffer_get_string(&msg, NULL);
563 a = decode_attrib(&msg);
564
565 debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
566
567 xfree(longname);
568
569 buffer_free(&msg);
570
571 return(filename);
572}
573
574int
575do_rename(int fd_in, int fd_out, char *oldpath, char *newpath)
576{
577 Buffer msg;
578 u_int status, id;
579
580 buffer_init(&msg);
581
582 /* Send rename request */
Ben Lindstrom288cc392001-02-09 02:58:04 +0000583 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100584 buffer_put_char(&msg, SSH2_FXP_RENAME);
585 buffer_put_int(&msg, id);
586 buffer_put_cstring(&msg, oldpath);
587 buffer_put_cstring(&msg, newpath);
588 send_msg(fd_out, &msg);
589 debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
590 newpath);
591 buffer_free(&msg);
592
593 status = get_status(fd_in, id);
594 if (status != SSH2_FX_OK)
595 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
596 fx2txt(status));
597
598 return(status);
599}
600
601int
Damien Miller058316f2001-03-08 10:08:49 +1100602do_symlink(int fd_in, int fd_out, char *oldpath, char *newpath)
603{
604 Buffer msg;
605 u_int status, id;
606
607 buffer_init(&msg);
608
609 /* Send rename request */
610 id = msg_id++;
611 buffer_put_char(&msg, SSH2_FXP_SYMLINK);
612 buffer_put_int(&msg, id);
613 buffer_put_cstring(&msg, oldpath);
614 buffer_put_cstring(&msg, newpath);
615 send_msg(fd_out, &msg);
616 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
617 newpath);
618 buffer_free(&msg);
619
620 status = get_status(fd_in, id);
621 if (status != SSH2_FX_OK)
622 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
623 fx2txt(status));
624
625 return(status);
626}
627
628char *
629do_readlink(int fd_in, int fd_out, char *path)
630{
631 Buffer msg;
632 u_int type, expected_id, count, id;
633 char *filename, *longname;
634 Attrib *a;
635
636 expected_id = id = msg_id++;
637 send_string_request(fd_out, id, SSH2_FXP_READLINK, path, strlen(path));
638
639 buffer_init(&msg);
640
641 get_msg(fd_in, &msg);
642 type = buffer_get_char(&msg);
643 id = buffer_get_int(&msg);
644
645 if (id != expected_id)
646 fatal("ID mismatch (%d != %d)", id, expected_id);
647
648 if (type == SSH2_FXP_STATUS) {
649 u_int status = buffer_get_int(&msg);
650
651 error("Couldn't readlink: %s", fx2txt(status));
652 return(NULL);
653 } else if (type != SSH2_FXP_NAME)
654 fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
655 SSH2_FXP_NAME, type);
656
657 count = buffer_get_int(&msg);
658 if (count != 1)
659 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
660
661 filename = buffer_get_string(&msg, NULL);
662 longname = buffer_get_string(&msg, NULL);
663 a = decode_attrib(&msg);
664
665 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
666
667 xfree(longname);
668
669 buffer_free(&msg);
670
671 return(filename);
672}
673
674int
Damien Miller33804262001-02-04 23:20:18 +1100675do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
676 int pflag)
677{
678 int local_fd;
679 u_int expected_id, handle_len, mode, type, id;
680 u_int64_t offset;
681 char *handle;
682 Buffer msg;
683 Attrib junk, *a;
Damien Millerd7686fd2001-02-10 00:40:03 +1100684 int status;
Damien Miller33804262001-02-04 23:20:18 +1100685
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000686 a = do_stat(fd_in, fd_out, remote_path, 0);
Damien Miller33804262001-02-04 23:20:18 +1100687 if (a == NULL)
688 return(-1);
689
690 /* XXX: should we preserve set[ug]id? */
691 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
692 mode = S_IWRITE | (a->perm & 0777);
693 else
694 mode = 0666;
695
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000696 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
697 (a->perm & S_IFDIR)) {
698 error("Cannot download a directory: %s", remote_path);
699 return(-1);
700 }
701
Damien Miller33804262001-02-04 23:20:18 +1100702 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
703 if (local_fd == -1) {
704 error("Couldn't open local file \"%s\" for writing: %s",
705 local_path, strerror(errno));
Ben Lindstromc8d1c302001-03-17 00:34:46 +0000706 return(-1);
Damien Miller33804262001-02-04 23:20:18 +1100707 }
708
Damien Miller33804262001-02-04 23:20:18 +1100709 buffer_init(&msg);
710
711 /* Send open request */
Ben Lindstrom288cc392001-02-09 02:58:04 +0000712 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100713 buffer_put_char(&msg, SSH2_FXP_OPEN);
714 buffer_put_int(&msg, id);
715 buffer_put_cstring(&msg, remote_path);
716 buffer_put_int(&msg, SSH2_FXF_READ);
717 attrib_clear(&junk); /* Send empty attributes */
718 encode_attrib(&msg, &junk);
719 send_msg(fd_out, &msg);
720 debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
721
722 handle = get_handle(fd_in, id, &handle_len);
723 if (handle == NULL) {
724 buffer_free(&msg);
725 close(local_fd);
726 return(-1);
727 }
728
729 /* Read from remote and write to local */
730 offset = 0;
731 for(;;) {
732 u_int len;
733 char *data;
734
Ben Lindstrom288cc392001-02-09 02:58:04 +0000735 id = expected_id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100736
737 buffer_clear(&msg);
738 buffer_put_char(&msg, SSH2_FXP_READ);
739 buffer_put_int(&msg, id);
740 buffer_put_string(&msg, handle, handle_len);
741 buffer_put_int64(&msg, offset);
742 buffer_put_int(&msg, COPY_SIZE);
743 send_msg(fd_out, &msg);
744 debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u",
Ben Lindstrom416d8742001-02-25 02:02:43 +0000745 id, (u_int64_t)offset, COPY_SIZE);
Damien Miller33804262001-02-04 23:20:18 +1100746
747 buffer_clear(&msg);
748
749 get_msg(fd_in, &msg);
750 type = buffer_get_char(&msg);
751 id = buffer_get_int(&msg);
752 debug3("Received reply T:%d I:%d", type, id);
753 if (id != expected_id)
754 fatal("ID mismatch (%d != %d)", id, expected_id);
755 if (type == SSH2_FXP_STATUS) {
Damien Millerd7686fd2001-02-10 00:40:03 +1100756 status = buffer_get_int(&msg);
Damien Miller33804262001-02-04 23:20:18 +1100757
758 if (status == SSH2_FX_EOF)
759 break;
760 else {
761 error("Couldn't read from remote "
762 "file \"%s\" : %s", remote_path,
763 fx2txt(status));
764 do_close(fd_in, fd_out, handle, handle_len);
Damien Millerd7686fd2001-02-10 00:40:03 +1100765 goto done;
Damien Miller33804262001-02-04 23:20:18 +1100766 }
767 } else if (type != SSH2_FXP_DATA) {
768 fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
769 SSH2_FXP_DATA, type);
770 }
771
772 data = buffer_get_string(&msg, &len);
773 if (len > COPY_SIZE)
774 fatal("Received more data than asked for %d > %d",
775 len, COPY_SIZE);
776
Damien Millerd7686fd2001-02-10 00:40:03 +1100777 debug3("In read loop, got %d offset %llu", len,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000778 (u_int64_t)offset);
Damien Miller33804262001-02-04 23:20:18 +1100779 if (atomicio(write, local_fd, data, len) != len) {
780 error("Couldn't write to \"%s\": %s", local_path,
781 strerror(errno));
782 do_close(fd_in, fd_out, handle, handle_len);
Damien Millerd7686fd2001-02-10 00:40:03 +1100783 status = -1;
Damien Miller33804262001-02-04 23:20:18 +1100784 xfree(data);
Damien Millerd7686fd2001-02-10 00:40:03 +1100785 goto done;
Damien Miller33804262001-02-04 23:20:18 +1100786 }
787
788 offset += len;
789 xfree(data);
790 }
Damien Millerd7686fd2001-02-10 00:40:03 +1100791 status = do_close(fd_in, fd_out, handle, handle_len);
Damien Miller33804262001-02-04 23:20:18 +1100792
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000793 /* Override umask and utimes if asked */
Ben Lindstrom6dc75f52001-02-17 16:47:47 +0000794#ifdef HAVE_FCHMOD
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000795 if (pflag && fchmod(local_fd, mode) == -1)
Ben Lindstrom6dc75f52001-02-17 16:47:47 +0000796#else
797 if (pflag && chmod(local_path, mode) == -1)
798#endif /* HAVE_FCHMOD */
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000799 error("Couldn't set mode on \"%s\": %s", local_path,
800 strerror(errno));
801 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
802 struct timeval tv[2];
803 tv[0].tv_sec = a->atime;
804 tv[1].tv_sec = a->mtime;
805 tv[0].tv_usec = tv[1].tv_usec = 0;
806 if (utimes(local_path, tv) == -1)
807 error("Can't set times on \"%s\": %s", local_path,
808 strerror(errno));
809 }
810
Damien Millerd7686fd2001-02-10 00:40:03 +1100811done:
812 close(local_fd);
813 buffer_free(&msg);
814 xfree(handle);
815 return status;
Damien Miller33804262001-02-04 23:20:18 +1100816}
817
818int
819do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
820 int pflag)
821{
822 int local_fd;
823 u_int handle_len, id;
824 u_int64_t offset;
825 char *handle;
826 Buffer msg;
827 struct stat sb;
828 Attrib a;
Damien Millerd7686fd2001-02-10 00:40:03 +1100829 int status;
Damien Miller33804262001-02-04 23:20:18 +1100830
831 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
832 error("Couldn't open local file \"%s\" for reading: %s",
833 local_path, strerror(errno));
834 return(-1);
835 }
836 if (fstat(local_fd, &sb) == -1) {
837 error("Couldn't fstat local file \"%s\": %s",
838 local_path, strerror(errno));
839 close(local_fd);
840 return(-1);
841 }
842 stat_to_attrib(&sb, &a);
843
844 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
845 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
846 a.perm &= 0777;
847 if (!pflag)
848 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
849
850 buffer_init(&msg);
851
852 /* Send open request */
Ben Lindstrom288cc392001-02-09 02:58:04 +0000853 id = msg_id++;
Damien Miller33804262001-02-04 23:20:18 +1100854 buffer_put_char(&msg, SSH2_FXP_OPEN);
855 buffer_put_int(&msg, id);
856 buffer_put_cstring(&msg, remote_path);
857 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
858 encode_attrib(&msg, &a);
859 send_msg(fd_out, &msg);
860 debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
861
862 buffer_clear(&msg);
863
864 handle = get_handle(fd_in, id, &handle_len);
865 if (handle == NULL) {
866 close(local_fd);
867 buffer_free(&msg);
868 return(-1);
869 }
870
Damien Miller33804262001-02-04 23:20:18 +1100871 /* Read from local and write to remote */
872 offset = 0;
873 for(;;) {
874 int len;
875 char data[COPY_SIZE];
Damien Miller33804262001-02-04 23:20:18 +1100876
877 /*
878 * Can't use atomicio here because it returns 0 on EOF, thus losing
879 * the last block of the file
880 */
881 do
882 len = read(local_fd, data, COPY_SIZE);
883 while ((len == -1) && (errno == EINTR || errno == EAGAIN));
884
885 if (len == -1)
886 fatal("Couldn't read from \"%s\": %s", local_path,
887 strerror(errno));
888 if (len == 0)
889 break;
890
891 buffer_clear(&msg);
892 buffer_put_char(&msg, SSH2_FXP_WRITE);
893 buffer_put_int(&msg, ++id);
894 buffer_put_string(&msg, handle, handle_len);
895 buffer_put_int64(&msg, offset);
896 buffer_put_string(&msg, data, len);
897 send_msg(fd_out, &msg);
898 debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
Ben Lindstrom416d8742001-02-25 02:02:43 +0000899 id, (u_int64_t)offset, len);
Damien Miller33804262001-02-04 23:20:18 +1100900
901 status = get_status(fd_in, id);
902 if (status != SSH2_FX_OK) {
903 error("Couldn't write to remote file \"%s\": %s",
904 remote_path, fx2txt(status));
905 do_close(fd_in, fd_out, handle, handle_len);
Damien Miller33804262001-02-04 23:20:18 +1100906 close(local_fd);
Damien Millerd7686fd2001-02-10 00:40:03 +1100907 goto done;
Damien Miller33804262001-02-04 23:20:18 +1100908 }
Kevin Stevesec1c1402001-02-05 15:39:22 +0000909 debug3("In write loop, got %d offset %llu", len,
Ben Lindstrom416d8742001-02-25 02:02:43 +0000910 (u_int64_t)offset);
Damien Miller33804262001-02-04 23:20:18 +1100911
912 offset += len;
913 }
Damien Miller33804262001-02-04 23:20:18 +1100914
915 if (close(local_fd) == -1) {
916 error("Couldn't close local file \"%s\": %s", local_path,
917 strerror(errno));
918 do_close(fd_in, fd_out, handle, handle_len);
Damien Millerd7686fd2001-02-10 00:40:03 +1100919 status = -1;
920 goto done;
Damien Miller33804262001-02-04 23:20:18 +1100921 }
922
Ben Lindstrom9d4f2c82001-02-15 03:22:45 +0000923 /* Override umask and utimes if asked */
924 if (pflag)
925 do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
926
Damien Millerd7686fd2001-02-10 00:40:03 +1100927 status = do_close(fd_in, fd_out, handle, handle_len);
928
929done:
930 xfree(handle);
931 buffer_free(&msg);
932 return status;
Damien Miller33804262001-02-04 23:20:18 +1100933}
Damien Miller4870afd2001-03-14 10:27:09 +1100934