]> andersk Git - openssh.git/blame - sftp-client.c
- (dtucker) d_type is not mandated by POSIX, so add fallback code using
[openssh.git] / sftp-client.c
CommitLineData
d141f964 1/* $OpenBSD: sftp-client.c,v 1.89 2009/08/18 18:36:20 djm Exp $ */
61e96248 2/*
ab3932ab 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
61e96248 4 *
ab3932ab 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.
61e96248 8 *
ab3932ab 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.
61e96248 16 */
17
18/* XXX: memleaks */
19/* XXX: signed vs unsigned */
22e6c827 20/* XXX: remove all logging, only return status codes */
61e96248 21/* XXX: copy between two remote sites */
22
23#include "includes.h"
4095f623 24
25#include <sys/types.h>
536c14e8 26#include <sys/param.h>
4538e135 27#ifdef HAVE_SYS_STATVFS_H
360b43ab 28#include <sys/statvfs.h>
4538e135 29#endif
31652869 30#include "openbsd-compat/sys-queue.h"
4095f623 31#ifdef HAVE_SYS_STAT_H
32# include <sys/stat.h>
33#endif
e264ac72 34#ifdef HAVE_SYS_TIME_H
35# include <sys/time.h>
36#endif
31652869 37#include <sys/uio.h>
d3221cca 38
d141f964 39#include <dirent.h>
4b48f754 40#ifdef DTTOIF_IN_FS_FFS_DIR_H
41# include <fs/ffs/dir.h>
42#endif
028094f4 43#include <errno.h>
d3221cca 44#include <fcntl.h>
45#include <signal.h>
31652869 46#include <stdarg.h>
cf851879 47#include <stdio.h>
00146caa 48#include <string.h>
5188ba17 49#include <unistd.h>
c25d3df7 50
61e96248 51#include "xmalloc.h"
31652869 52#include "buffer.h"
61e96248 53#include "log.h"
54#include "atomicio.h"
b65c3807 55#include "progressmeter.h"
51e7a012 56#include "misc.h"
61e96248 57
58#include "sftp.h"
59#include "sftp-common.h"
60#include "sftp-client.h"
61
0e5de6f8 62extern volatile sig_atomic_t interrupted;
b65c3807 63extern int showprogress;
64
2db34ac9 65/* Minimum amount of data to read at a time */
c25d3df7 66#define MIN_READ_SIZE 512
67
d141f964 68/* Maximum depth to descend in directory trees */
69#define MAX_DIR_DEPTH 64
70
22e6c827 71struct sftp_conn {
72 int fd_in;
73 int fd_out;
74 u_int transfer_buflen;
75 u_int num_requests;
76 u_int version;
77 u_int msg_id;
360b43ab 78#define SFTP_EXT_POSIX_RENAME 0x00000001
79#define SFTP_EXT_STATVFS 0x00000002
80#define SFTP_EXT_FSTATVFS 0x00000004
530f04a8 81 u_int exts;
22e6c827 82};
9c5a8165 83
e746280c 84static char *
85get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
86 __attribute__((format(printf, 4, 5)));
87
396c147e 88static void
61e96248 89send_msg(int fd, Buffer *m)
90{
bf0bf24b 91 u_char mlen[4];
2c4369de 92 struct iovec iov[2];
bf0bf24b 93
3eee3b86 94 if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
bf0bf24b 95 fatal("Outbound message too long %u", buffer_len(m));
61e96248 96
bf0bf24b 97 /* Send length first */
51e7a012 98 put_u32(mlen, buffer_len(m));
2c4369de 99 iov[0].iov_base = mlen;
100 iov[0].iov_len = sizeof(mlen);
101 iov[1].iov_base = buffer_ptr(m);
102 iov[1].iov_len = buffer_len(m);
31652869 103
2c4369de 104 if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
61e96248 105 fatal("Couldn't send packet: %s", strerror(errno));
106
bf0bf24b 107 buffer_clear(m);
61e96248 108}
109
396c147e 110static void
61e96248 111get_msg(int fd, Buffer *m)
112{
bf0bf24b 113 u_int msg_len;
61e96248 114
bf0bf24b 115 buffer_append_space(m, 4);
05624c18 116 if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
117 if (errno == EPIPE)
118 fatal("Connection closed");
119 else
120 fatal("Couldn't read packet: %s", strerror(errno));
121 }
61e96248 122
bf0bf24b 123 msg_len = buffer_get_int(m);
3eee3b86 124 if (msg_len > SFTP_MAX_MSG_LENGTH)
9906a836 125 fatal("Received message too long %u", msg_len);
61e96248 126
bf0bf24b 127 buffer_append_space(m, msg_len);
05624c18 128 if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
129 if (errno == EPIPE)
130 fatal("Connection closed");
131 else
132 fatal("Read packet: %s", strerror(errno));
133 }
61e96248 134}
135
396c147e 136static void
61e96248 137send_string_request(int fd, u_int id, u_int code, char *s,
138 u_int len)
139{
140 Buffer msg;
141
142 buffer_init(&msg);
143 buffer_put_char(&msg, code);
144 buffer_put_int(&msg, id);
145 buffer_put_string(&msg, s, len);
146 send_msg(fd, &msg);
9906a836 147 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
61e96248 148 buffer_free(&msg);
149}
150
396c147e 151static void
61e96248 152send_string_attrs_request(int fd, u_int id, u_int code, char *s,
153 u_int len, Attrib *a)
154{
155 Buffer msg;
156
157 buffer_init(&msg);
158 buffer_put_char(&msg, code);
159 buffer_put_int(&msg, id);
160 buffer_put_string(&msg, s, len);
161 encode_attrib(&msg, a);
162 send_msg(fd, &msg);
9906a836 163 debug3("Sent message fd %d T:%u I:%u", fd, code, id);
61e96248 164 buffer_free(&msg);
165}
166
396c147e 167static u_int
9906a836 168get_status(int fd, u_int expected_id)
61e96248 169{
170 Buffer msg;
171 u_int type, id, status;
172
173 buffer_init(&msg);
174 get_msg(fd, &msg);
175 type = buffer_get_char(&msg);
176 id = buffer_get_int(&msg);
177
178 if (id != expected_id)
9906a836 179 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 180 if (type != SSH2_FXP_STATUS)
9906a836 181 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
61e96248 182 SSH2_FXP_STATUS, type);
183
184 status = buffer_get_int(&msg);
185 buffer_free(&msg);
186
9906a836 187 debug3("SSH2_FXP_STATUS %u", status);
61e96248 188
189 return(status);
190}
191
396c147e 192static char *
e746280c 193get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
61e96248 194{
195 Buffer msg;
196 u_int type, id;
e746280c 197 char *handle, errmsg[256];
198 va_list args;
199 int status;
200
201 va_start(args, errfmt);
202 if (errfmt != NULL)
203 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
204 va_end(args);
61e96248 205
206 buffer_init(&msg);
207 get_msg(fd, &msg);
208 type = buffer_get_char(&msg);
209 id = buffer_get_int(&msg);
210
211 if (id != expected_id)
e746280c 212 fatal("%s: ID mismatch (%u != %u)",
213 errfmt == NULL ? __func__ : errmsg, id, expected_id);
61e96248 214 if (type == SSH2_FXP_STATUS) {
e746280c 215 status = buffer_get_int(&msg);
216 if (errfmt != NULL)
217 error("%s: %s", errmsg, fx2txt(status));
aa41be57 218 buffer_free(&msg);
61e96248 219 return(NULL);
220 } else if (type != SSH2_FXP_HANDLE)
e746280c 221 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
222 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
61e96248 223
224 handle = buffer_get_string(&msg, len);
225 buffer_free(&msg);
226
227 return(handle);
228}
229
396c147e 230static Attrib *
d50d9b63 231get_decode_stat(int fd, u_int expected_id, int quiet)
61e96248 232{
233 Buffer msg;
234 u_int type, id;
235 Attrib *a;
236
237 buffer_init(&msg);
238 get_msg(fd, &msg);
239
240 type = buffer_get_char(&msg);
241 id = buffer_get_int(&msg);
242
9906a836 243 debug3("Received stat reply T:%u I:%u", type, id);
61e96248 244 if (id != expected_id)
9906a836 245 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 246 if (type == SSH2_FXP_STATUS) {
247 int status = buffer_get_int(&msg);
248
d50d9b63 249 if (quiet)
250 debug("Couldn't stat remote file: %s", fx2txt(status));
251 else
252 error("Couldn't stat remote file: %s", fx2txt(status));
aa41be57 253 buffer_free(&msg);
61e96248 254 return(NULL);
255 } else if (type != SSH2_FXP_ATTRS) {
9906a836 256 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
61e96248 257 SSH2_FXP_ATTRS, type);
258 }
259 a = decode_attrib(&msg);
260 buffer_free(&msg);
261
262 return(a);
263}
264
360b43ab 265static int
5a3cde15 266get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
267 int quiet)
360b43ab 268{
269 Buffer msg;
270 u_int type, id, flag;
271
272 buffer_init(&msg);
273 get_msg(fd, &msg);
274
275 type = buffer_get_char(&msg);
276 id = buffer_get_int(&msg);
277
278 debug3("Received statvfs reply T:%u I:%u", type, id);
279 if (id != expected_id)
280 fatal("ID mismatch (%u != %u)", id, expected_id);
281 if (type == SSH2_FXP_STATUS) {
282 int status = buffer_get_int(&msg);
283
284 if (quiet)
285 debug("Couldn't statvfs: %s", fx2txt(status));
286 else
287 error("Couldn't statvfs: %s", fx2txt(status));
288 buffer_free(&msg);
289 return -1;
290 } else if (type != SSH2_FXP_EXTENDED_REPLY) {
291 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
292 SSH2_FXP_EXTENDED_REPLY, type);
293 }
294
295 bzero(st, sizeof(*st));
5a3cde15 296 st->f_bsize = buffer_get_int64(&msg);
297 st->f_frsize = buffer_get_int64(&msg);
360b43ab 298 st->f_blocks = buffer_get_int64(&msg);
299 st->f_bfree = buffer_get_int64(&msg);
300 st->f_bavail = buffer_get_int64(&msg);
301 st->f_files = buffer_get_int64(&msg);
302 st->f_ffree = buffer_get_int64(&msg);
303 st->f_favail = buffer_get_int64(&msg);
4f36159a 304 st->f_fsid = buffer_get_int64(&msg);
5a3cde15 305 flag = buffer_get_int64(&msg);
306 st->f_namemax = buffer_get_int64(&msg);
360b43ab 307
308 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
309 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
310
311 buffer_free(&msg);
312
313 return 0;
314}
315
22e6c827 316struct sftp_conn *
317do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
61e96248 318{
530f04a8 319 u_int type, exts = 0;
9906a836 320 int version;
61e96248 321 Buffer msg;
22e6c827 322 struct sftp_conn *ret;
61e96248 323
324 buffer_init(&msg);
325 buffer_put_char(&msg, SSH2_FXP_INIT);
326 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
327 send_msg(fd_out, &msg);
328
329 buffer_clear(&msg);
330
331 get_msg(fd_in, &msg);
332
2b87da3b 333 /* Expecting a VERSION reply */
61e96248 334 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
9906a836 335 error("Invalid packet back from SSH2_FXP_INIT (type %u)",
61e96248 336 type);
337 buffer_free(&msg);
22e6c827 338 return(NULL);
61e96248 339 }
340 version = buffer_get_int(&msg);
341
342 debug2("Remote version: %d", version);
343
344 /* Check for extensions */
345 while (buffer_len(&msg) > 0) {
346 char *name = buffer_get_string(&msg, NULL);
347 char *value = buffer_get_string(&msg, NULL);
ad39a852 348 int known = 0;
61e96248 349
360b43ab 350 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
ad39a852 351 strcmp(value, "1") == 0) {
530f04a8 352 exts |= SFTP_EXT_POSIX_RENAME;
ad39a852 353 known = 1;
354 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
355 strcmp(value, "2") == 0) {
360b43ab 356 exts |= SFTP_EXT_STATVFS;
ad39a852 357 known = 1;
358 } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
359 strcmp(value, "2") == 0) {
360b43ab 360 exts |= SFTP_EXT_FSTATVFS;
ad39a852 361 known = 1;
362 }
363 if (known) {
364 debug2("Server supports extension \"%s\" revision %s",
365 name, value);
366 } else {
367 debug2("Unrecognised server extension \"%s\"", name);
368 }
61e96248 369 xfree(name);
370 xfree(value);
371 }
372
373 buffer_free(&msg);
3a7fe5ba 374
22e6c827 375 ret = xmalloc(sizeof(*ret));
376 ret->fd_in = fd_in;
377 ret->fd_out = fd_out;
378 ret->transfer_buflen = transfer_buflen;
379 ret->num_requests = num_requests;
380 ret->version = version;
381 ret->msg_id = 1;
530f04a8 382 ret->exts = exts;
22e6c827 383
384 /* Some filexfer v.0 servers don't support large packets */
385 if (version == 0)
92053302 386 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
22e6c827 387
388 return(ret);
389}
390
391u_int
392sftp_proto_version(struct sftp_conn *conn)
393{
394 return(conn->version);
61e96248 395}
396
397int
22e6c827 398do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
61e96248 399{
400 u_int id, status;
401 Buffer msg;
402
403 buffer_init(&msg);
404
22e6c827 405 id = conn->msg_id++;
61e96248 406 buffer_put_char(&msg, SSH2_FXP_CLOSE);
407 buffer_put_int(&msg, id);
408 buffer_put_string(&msg, handle, handle_len);
22e6c827 409 send_msg(conn->fd_out, &msg);
9906a836 410 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
61e96248 411
22e6c827 412 status = get_status(conn->fd_in, id);
61e96248 413 if (status != SSH2_FX_OK)
414 error("Couldn't close file: %s", fx2txt(status));
415
416 buffer_free(&msg);
417
418 return(status);
419}
420
2e4fb373 421
396c147e 422static int
22e6c827 423do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
2e4fb373 424 SFTP_DIRENT ***dir)
61e96248 425{
426 Buffer msg;
2ceb8101 427 u_int count, type, id, handle_len, i, expected_id, ents = 0;
61e96248 428 char *handle;
429
22e6c827 430 id = conn->msg_id++;
61e96248 431
432 buffer_init(&msg);
433 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
434 buffer_put_int(&msg, id);
435 buffer_put_cstring(&msg, path);
22e6c827 436 send_msg(conn->fd_out, &msg);
61e96248 437
438 buffer_clear(&msg);
439
e746280c 440 handle = get_handle(conn->fd_in, id, &handle_len,
441 "remote readdir(\"%s\")", path);
61e96248 442 if (handle == NULL)
443 return(-1);
444
2e4fb373 445 if (dir) {
446 ents = 0;
447 *dir = xmalloc(sizeof(**dir));
448 (*dir)[0] = NULL;
449 }
2e4fb373 450
0e5de6f8 451 for (; !interrupted;) {
22e6c827 452 id = expected_id = conn->msg_id++;
61e96248 453
9906a836 454 debug3("Sending SSH2_FXP_READDIR I:%u", id);
61e96248 455
456 buffer_clear(&msg);
457 buffer_put_char(&msg, SSH2_FXP_READDIR);
458 buffer_put_int(&msg, id);
459 buffer_put_string(&msg, handle, handle_len);
22e6c827 460 send_msg(conn->fd_out, &msg);
61e96248 461
462 buffer_clear(&msg);
463
22e6c827 464 get_msg(conn->fd_in, &msg);
61e96248 465
466 type = buffer_get_char(&msg);
467 id = buffer_get_int(&msg);
468
9906a836 469 debug3("Received reply T:%u I:%u", type, id);
61e96248 470
471 if (id != expected_id)
9906a836 472 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 473
474 if (type == SSH2_FXP_STATUS) {
475 int status = buffer_get_int(&msg);
476
477 debug3("Received SSH2_FXP_STATUS %d", status);
478
479 if (status == SSH2_FX_EOF) {
480 break;
481 } else {
482 error("Couldn't read directory: %s",
483 fx2txt(status));
22e6c827 484 do_close(conn, handle, handle_len);
3e2f2431 485 xfree(handle);
b655a207 486 return(status);
61e96248 487 }
488 } else if (type != SSH2_FXP_NAME)
9906a836 489 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
61e96248 490 SSH2_FXP_NAME, type);
491
492 count = buffer_get_int(&msg);
0426a3b4 493 if (count == 0)
494 break;
495 debug3("Received %d SSH2_FXP_NAME responses", count);
184eed6a 496 for (i = 0; i < count; i++) {
61e96248 497 char *filename, *longname;
498 Attrib *a;
499
500 filename = buffer_get_string(&msg, NULL);
501 longname = buffer_get_string(&msg, NULL);
502 a = decode_attrib(&msg);
503
2e4fb373 504 if (printflag)
505 printf("%s\n", longname);
506
d141f964 507 /*
508 * Directory entries should never contain '/'
509 * These can be used to attack recursive ops
510 * (e.g. send '../../../../etc/passwd')
511 */
512 if (strchr(filename, '/') != NULL) {
513 error("Server sent suspect path \"%s\" "
514 "during readdir of \"%s\"", filename, path);
515 goto next;
516 }
517
2e4fb373 518 if (dir) {
c5d10563 519 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
2e4fb373 520 (*dir)[ents] = xmalloc(sizeof(***dir));
521 (*dir)[ents]->filename = xstrdup(filename);
522 (*dir)[ents]->longname = xstrdup(longname);
523 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
524 (*dir)[++ents] = NULL;
525 }
d141f964 526 next:
61e96248 527 xfree(filename);
528 xfree(longname);
529 }
530 }
531
532 buffer_free(&msg);
22e6c827 533 do_close(conn, handle, handle_len);
61e96248 534 xfree(handle);
535
0e5de6f8 536 /* Don't return partial matches on interrupt */
537 if (interrupted && dir != NULL && *dir != NULL) {
538 free_sftp_dirents(*dir);
539 *dir = xmalloc(sizeof(**dir));
540 **dir = NULL;
541 }
542
61e96248 543 return(0);
544}
545
2e4fb373 546int
22e6c827 547do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
2e4fb373 548{
22e6c827 549 return(do_lsreaddir(conn, path, 0, dir));
2e4fb373 550}
551
552void free_sftp_dirents(SFTP_DIRENT **s)
553{
554 int i;
184eed6a 555
556 for (i = 0; s[i]; i++) {
2e4fb373 557 xfree(s[i]->filename);
558 xfree(s[i]->longname);
559 xfree(s[i]);
560 }
561 xfree(s);
562}
563
61e96248 564int
22e6c827 565do_rm(struct sftp_conn *conn, char *path)
61e96248 566{
567 u_int status, id;
568
569 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
570
22e6c827 571 id = conn->msg_id++;
762715ce 572 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path,
22e6c827 573 strlen(path));
574 status = get_status(conn->fd_in, id);
61e96248 575 if (status != SSH2_FX_OK)
576 error("Couldn't delete file: %s", fx2txt(status));
577 return(status);
578}
579
580int
d141f964 581do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
61e96248 582{
583 u_int status, id;
584
22e6c827 585 id = conn->msg_id++;
586 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path,
61e96248 587 strlen(path), a);
588
22e6c827 589 status = get_status(conn->fd_in, id);
d141f964 590 if (status != SSH2_FX_OK && printflag)
61e96248 591 error("Couldn't create directory: %s", fx2txt(status));
592
593 return(status);
594}
595
596int
22e6c827 597do_rmdir(struct sftp_conn *conn, char *path)
61e96248 598{
599 u_int status, id;
600
22e6c827 601 id = conn->msg_id++;
602 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path,
603 strlen(path));
61e96248 604
22e6c827 605 status = get_status(conn->fd_in, id);
61e96248 606 if (status != SSH2_FX_OK)
607 error("Couldn't remove directory: %s", fx2txt(status));
608
609 return(status);
610}
611
612Attrib *
22e6c827 613do_stat(struct sftp_conn *conn, char *path, int quiet)
61e96248 614{
615 u_int id;
616
22e6c827 617 id = conn->msg_id++;
618
762715ce 619 send_string_request(conn->fd_out, id,
620 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
22e6c827 621 path, strlen(path));
622
623 return(get_decode_stat(conn->fd_in, id, quiet));
61e96248 624}
625
626Attrib *
22e6c827 627do_lstat(struct sftp_conn *conn, char *path, int quiet)
61e96248 628{
629 u_int id;
630
22e6c827 631 if (conn->version == 0) {
632 if (quiet)
633 debug("Server version does not support lstat operation");
634 else
bbe88b6d 635 logit("Server version does not support lstat operation");
9c74a24d 636 return(do_stat(conn, path, quiet));
22e6c827 637 }
638
639 id = conn->msg_id++;
640 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path,
641 strlen(path));
642
643 return(get_decode_stat(conn->fd_in, id, quiet));
61e96248 644}
645
737cce6f 646#ifdef notyet
61e96248 647Attrib *
22e6c827 648do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
61e96248 649{
650 u_int id;
651
22e6c827 652 id = conn->msg_id++;
653 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle,
654 handle_len);
655
656 return(get_decode_stat(conn->fd_in, id, quiet));
61e96248 657}
737cce6f 658#endif
61e96248 659
660int
22e6c827 661do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
61e96248 662{
663 u_int status, id;
664
22e6c827 665 id = conn->msg_id++;
666 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path,
61e96248 667 strlen(path), a);
668
22e6c827 669 status = get_status(conn->fd_in, id);
61e96248 670 if (status != SSH2_FX_OK)
671 error("Couldn't setstat on \"%s\": %s", path,
672 fx2txt(status));
673
674 return(status);
675}
676
677int
22e6c827 678do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
61e96248 679 Attrib *a)
680{
681 u_int status, id;
682
22e6c827 683 id = conn->msg_id++;
684 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle,
61e96248 685 handle_len, a);
686
22e6c827 687 status = get_status(conn->fd_in, id);
61e96248 688 if (status != SSH2_FX_OK)
689 error("Couldn't fsetstat: %s", fx2txt(status));
690
691 return(status);
692}
693
694char *
22e6c827 695do_realpath(struct sftp_conn *conn, char *path)
61e96248 696{
697 Buffer msg;
698 u_int type, expected_id, count, id;
699 char *filename, *longname;
700 Attrib *a;
701
22e6c827 702 expected_id = id = conn->msg_id++;
703 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path,
704 strlen(path));
61e96248 705
706 buffer_init(&msg);
707
22e6c827 708 get_msg(conn->fd_in, &msg);
61e96248 709 type = buffer_get_char(&msg);
710 id = buffer_get_int(&msg);
711
712 if (id != expected_id)
9906a836 713 fatal("ID mismatch (%u != %u)", id, expected_id);
61e96248 714
715 if (type == SSH2_FXP_STATUS) {
716 u_int status = buffer_get_int(&msg);
717
718 error("Couldn't canonicalise: %s", fx2txt(status));
719 return(NULL);
720 } else if (type != SSH2_FXP_NAME)
9906a836 721 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
61e96248 722 SSH2_FXP_NAME, type);
723
724 count = buffer_get_int(&msg);
725 if (count != 1)
726 fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
727
728 filename = buffer_get_string(&msg, NULL);
729 longname = buffer_get_string(&msg, NULL);
730 a = decode_attrib(&msg);
731
732 debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
733
734 xfree(longname);
735
736 buffer_free(&msg);
737
738 return(filename);
739}
740
741int
22e6c827 742do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
61e96248 743{
744 Buffer msg;
745 u_int status, id;
746
747 buffer_init(&msg);
748
749 /* Send rename request */
22e6c827 750 id = conn->msg_id++;
530f04a8 751 if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
752 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
753 buffer_put_int(&msg, id);
754 buffer_put_cstring(&msg, "posix-rename@openssh.com");
755 } else {
756 buffer_put_char(&msg, SSH2_FXP_RENAME);
757 buffer_put_int(&msg, id);
758 }
61e96248 759 buffer_put_cstring(&msg, oldpath);
760 buffer_put_cstring(&msg, newpath);
22e6c827 761 send_msg(conn->fd_out, &msg);
530f04a8 762 debug3("Sent message %s \"%s\" -> \"%s\"",
763 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
764 "SSH2_FXP_RENAME", oldpath, newpath);
61e96248 765 buffer_free(&msg);
766
22e6c827 767 status = get_status(conn->fd_in, id);
61e96248 768 if (status != SSH2_FX_OK)
22e6c827 769 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
770 newpath, fx2txt(status));
61e96248 771
772 return(status);
773}
774
3a7fe5ba 775int
22e6c827 776do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
3a7fe5ba 777{
778 Buffer msg;
779 u_int status, id;
780
22e6c827 781 if (conn->version < 3) {
782 error("This server does not support the symlink operation");
783 return(SSH2_FX_OP_UNSUPPORTED);
784 }
785
3a7fe5ba 786 buffer_init(&msg);
787
b093b499 788 /* Send symlink request */
22e6c827 789 id = conn->msg_id++;
3a7fe5ba 790 buffer_put_char(&msg, SSH2_FXP_SYMLINK);
791 buffer_put_int(&msg, id);
792 buffer_put_cstring(&msg, oldpath);
793 buffer_put_cstring(&msg, newpath);
22e6c827 794 send_msg(conn->fd_out, &msg);
3a7fe5ba 795 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
796 newpath);
797 buffer_free(&msg);
798
22e6c827 799 status = get_status(conn->fd_in, id);
3a7fe5ba 800 if (status != SSH2_FX_OK)
9db1a8e9 801 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
22e6c827 802 newpath, fx2txt(status));
3a7fe5ba 803
804 return(status);
805}
806
737cce6f 807#ifdef notyet
3a7fe5ba 808char *
22e6c827 809do_readlink(struct sftp_conn *conn, char *path)
3a7fe5ba 810{
811 Buffer msg;
812 u_int type, expected_id, count, id;
813 char *filename, *longname;
814 Attrib *a;
815
22e6c827 816 expected_id = id = conn->msg_id++;
817 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path,
818 strlen(path));
3a7fe5ba 819
820 buffer_init(&msg);
821
22e6c827 822 get_msg(conn->fd_in, &msg);
3a7fe5ba 823 type = buffer_get_char(&msg);
824 id = buffer_get_int(&msg);
825
826 if (id != expected_id)
9906a836 827 fatal("ID mismatch (%u != %u)", id, expected_id);
3a7fe5ba 828
829 if (type == SSH2_FXP_STATUS) {
830 u_int status = buffer_get_int(&msg);
831
832 error("Couldn't readlink: %s", fx2txt(status));
833 return(NULL);
834 } else if (type != SSH2_FXP_NAME)
9906a836 835 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
3a7fe5ba 836 SSH2_FXP_NAME, type);
837
838 count = buffer_get_int(&msg);
839 if (count != 1)
840 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
841
842 filename = buffer_get_string(&msg, NULL);
843 longname = buffer_get_string(&msg, NULL);
844 a = decode_attrib(&msg);
845
846 debug3("SSH_FXP_READLINK %s -> %s", path, filename);
847
848 xfree(longname);
849
850 buffer_free(&msg);
851
852 return(filename);
853}
737cce6f 854#endif
3a7fe5ba 855
360b43ab 856int
5a3cde15 857do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
360b43ab 858 int quiet)
859{
860 Buffer msg;
861 u_int id;
862
863 if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
864 error("Server does not support statvfs@openssh.com extension");
865 return -1;
866 }
867
868 id = conn->msg_id++;
869
870 buffer_init(&msg);
871 buffer_clear(&msg);
872 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
873 buffer_put_int(&msg, id);
874 buffer_put_cstring(&msg, "statvfs@openssh.com");
875 buffer_put_cstring(&msg, path);
876 send_msg(conn->fd_out, &msg);
877 buffer_free(&msg);
878
879 return get_decode_statvfs(conn->fd_in, st, id, quiet);
880}
881
882#ifdef notyet
883int
884do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
5a3cde15 885 struct sftp_statvfs *st, int quiet)
360b43ab 886{
887 Buffer msg;
888 u_int id;
889
890 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
891 error("Server does not support fstatvfs@openssh.com extension");
892 return -1;
893 }
894
895 id = conn->msg_id++;
896
897 buffer_init(&msg);
898 buffer_clear(&msg);
899 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
900 buffer_put_int(&msg, id);
901 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
902 buffer_put_string(&msg, handle, handle_len);
903 send_msg(conn->fd_out, &msg);
904 buffer_free(&msg);
905
906 return get_decode_statvfs(conn->fd_in, st, id, quiet);
907}
908#endif
909
c25d3df7 910static void
911send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
912 char *handle, u_int handle_len)
913{
914 Buffer msg;
762715ce 915
c25d3df7 916 buffer_init(&msg);
917 buffer_clear(&msg);
918 buffer_put_char(&msg, SSH2_FXP_READ);
919 buffer_put_int(&msg, id);
920 buffer_put_string(&msg, handle, handle_len);
921 buffer_put_int64(&msg, offset);
922 buffer_put_int(&msg, len);
923 send_msg(fd_out, &msg);
924 buffer_free(&msg);
762715ce 925}
c25d3df7 926
61e96248 927int
22e6c827 928do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
d141f964 929 Attrib *a, int pflag)
61e96248 930{
d141f964 931 Attrib junk;
c25d3df7 932 Buffer msg;
933 char *handle;
a056dfa2 934 int local_fd, status = 0, write_error;
c25d3df7 935 int read_error, write_errno;
936 u_int64_t offset, size;
2ceb8101 937 u_int handle_len, mode, type, id, buflen, num_req, max_req;
b65c3807 938 off_t progress_counter;
c25d3df7 939 struct request {
940 u_int id;
941 u_int len;
942 u_int64_t offset;
762715ce 943 TAILQ_ENTRY(request) tq;
c25d3df7 944 };
945 TAILQ_HEAD(reqhead, request) requests;
946 struct request *req;
947
948 TAILQ_INIT(&requests);
61e96248 949
d141f964 950 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
951 return -1;
61e96248 952
681efe9f 953 /* Do not preserve set[ug]id here, as we do not preserve ownership */
61e96248 954 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
bfd934b9 955 mode = a->perm & 0777;
61e96248 956 else
957 mode = 0666;
958
d50d9b63 959 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
538840a2 960 (!S_ISREG(a->perm))) {
961 error("Cannot download non-regular file: %s", remote_path);
d50d9b63 962 return(-1);
963 }
964
c25d3df7 965 if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
966 size = a->size;
967 else
968 size = 0;
969
22e6c827 970 buflen = conn->transfer_buflen;
61e96248 971 buffer_init(&msg);
972
973 /* Send open request */
22e6c827 974 id = conn->msg_id++;
61e96248 975 buffer_put_char(&msg, SSH2_FXP_OPEN);
976 buffer_put_int(&msg, id);
977 buffer_put_cstring(&msg, remote_path);
978 buffer_put_int(&msg, SSH2_FXF_READ);
979 attrib_clear(&junk); /* Send empty attributes */
980 encode_attrib(&msg, &junk);
22e6c827 981 send_msg(conn->fd_out, &msg);
9906a836 982 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
61e96248 983
e746280c 984 handle = get_handle(conn->fd_in, id, &handle_len,
985 "remote open(\"%s\")", remote_path);
61e96248 986 if (handle == NULL) {
987 buffer_free(&msg);
61e96248 988 return(-1);
989 }
990
aff51935 991 local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC,
bfd934b9 992 mode | S_IWRITE);
22e6c827 993 if (local_fd == -1) {
994 error("Couldn't open local file \"%s\" for writing: %s",
995 local_path, strerror(errno));
1c861236 996 do_close(conn, handle, handle_len);
f60ace9f 997 buffer_free(&msg);
998 xfree(handle);
22e6c827 999 return(-1);
1000 }
1001
61e96248 1002 /* Read from remote and write to local */
c25d3df7 1003 write_error = read_error = write_errno = num_req = offset = 0;
1004 max_req = 1;
b65c3807 1005 progress_counter = 0;
1006
213bab61 1007 if (showprogress && size != 0)
1008 start_progress_meter(remote_path, size, &progress_counter);
b65c3807 1009
c25d3df7 1010 while (num_req > 0 || max_req > 0) {
61e96248 1011 char *data;
c25d3df7 1012 u_int len;
61e96248 1013
0e5de6f8 1014 /*
f2107e97 1015 * Simulate EOF on interrupt: stop sending new requests and
0e5de6f8 1016 * allow outstanding requests to drain gracefully
1017 */
1018 if (interrupted) {
1019 if (num_req == 0) /* If we haven't started yet... */
1020 break;
1021 max_req = 0;
1022 }
1023
c25d3df7 1024 /* Send some more requests */
1025 while (num_req < max_req) {
762715ce 1026 debug3("Request range %llu -> %llu (%d/%d)",
8627f3e0 1027 (unsigned long long)offset,
1028 (unsigned long long)offset + buflen - 1,
1029 num_req, max_req);
c25d3df7 1030 req = xmalloc(sizeof(*req));
22e6c827 1031 req->id = conn->msg_id++;
c25d3df7 1032 req->len = buflen;
1033 req->offset = offset;
1034 offset += buflen;
1035 num_req++;
1036 TAILQ_INSERT_TAIL(&requests, req, tq);
762715ce 1037 send_read_request(conn->fd_out, req->id, req->offset,
c25d3df7 1038 req->len, handle, handle_len);
1039 }
61e96248 1040
1041 buffer_clear(&msg);
22e6c827 1042 get_msg(conn->fd_in, &msg);
61e96248 1043 type = buffer_get_char(&msg);
1044 id = buffer_get_int(&msg);
9906a836 1045 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
c25d3df7 1046
1047 /* Find the request in our queue */
f8cc7664 1048 for (req = TAILQ_FIRST(&requests);
c25d3df7 1049 req != NULL && req->id != id;
1050 req = TAILQ_NEXT(req, tq))
1051 ;
1052 if (req == NULL)
1053 fatal("Unexpected reply %u", id);
1054
1055 switch (type) {
1056 case SSH2_FXP_STATUS:
0426a3b4 1057 status = buffer_get_int(&msg);
c25d3df7 1058 if (status != SSH2_FX_EOF)
1059 read_error = 1;
1060 max_req = 0;
1061 TAILQ_REMOVE(&requests, req, tq);
1062 xfree(req);
1063 num_req--;
1064 break;
1065 case SSH2_FXP_DATA:
1066 data = buffer_get_string(&msg, &len);
bfa7f960 1067 debug3("Received data %llu -> %llu",
762715ce 1068 (unsigned long long)req->offset,
bfa7f960 1069 (unsigned long long)req->offset + len - 1);
c25d3df7 1070 if (len > req->len)
1071 fatal("Received more data than asked for "
b77a87e5 1072 "%u > %u", len, req->len);
c25d3df7 1073 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
dc54438a 1074 atomicio(vwrite, local_fd, data, len) != len) &&
c25d3df7 1075 !write_error) {
1076 write_errno = errno;
1077 write_error = 1;
1078 max_req = 0;
1079 }
b65c3807 1080 progress_counter += len;
c25d3df7 1081 xfree(data);
61e96248 1082
c25d3df7 1083 if (len == req->len) {
1084 TAILQ_REMOVE(&requests, req, tq);
1085 xfree(req);
1086 num_req--;
1087 } else {
1088 /* Resend the request for the missing data */
1089 debug3("Short data block, re-requesting "
bfa7f960 1090 "%llu -> %llu (%2d)",
762715ce 1091 (unsigned long long)req->offset + len,
5fc7dbc9 1092 (unsigned long long)req->offset +
1093 req->len - 1, num_req);
22e6c827 1094 req->id = conn->msg_id++;
c25d3df7 1095 req->len -= len;
1096 req->offset += len;
762715ce 1097 send_read_request(conn->fd_out, req->id,
22e6c827 1098 req->offset, req->len, handle, handle_len);
c25d3df7 1099 /* Reduce the request size */
1100 if (len < buflen)
1101 buflen = MAX(MIN_READ_SIZE, len);
1102 }
1103 if (max_req > 0) { /* max_req = 0 iff EOF received */
1104 if (size > 0 && offset > size) {
1105 /* Only one request at a time
1106 * after the expected EOF */
1107 debug3("Finish at %llu (%2d)",
bfa7f960 1108 (unsigned long long)offset,
1109 num_req);
c25d3df7 1110 max_req = 1;
0e5de6f8 1111 } else if (max_req <= conn->num_requests) {
c25d3df7 1112 ++max_req;
1113 }
61e96248 1114 }
c25d3df7 1115 break;
1116 default:
9906a836 1117 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
61e96248 1118 SSH2_FXP_DATA, type);
1119 }
61e96248 1120 }
61e96248 1121
b65c3807 1122 if (showprogress && size)
1123 stop_progress_meter();
1124
c25d3df7 1125 /* Sanity check */
1126 if (TAILQ_FIRST(&requests) != NULL)
1127 fatal("Transfer complete, but requests still in queue");
1128
1129 if (read_error) {
762715ce 1130 error("Couldn't read from remote file \"%s\" : %s",
22e6c827 1131 remote_path, fx2txt(status));
1132 do_close(conn, handle, handle_len);
c25d3df7 1133 } else if (write_error) {
22e6c827 1134 error("Couldn't write to \"%s\": %s", local_path,
1135 strerror(write_errno));
1136 status = -1;
1137 do_close(conn, handle, handle_len);
c25d3df7 1138 } else {
22e6c827 1139 status = do_close(conn, handle, handle_len);
c25d3df7 1140
1141 /* Override umask and utimes if asked */
663fd560 1142#ifdef HAVE_FCHMOD
c25d3df7 1143 if (pflag && fchmod(local_fd, mode) == -1)
aff51935 1144#else
c25d3df7 1145 if (pflag && chmod(local_path, mode) == -1)
663fd560 1146#endif /* HAVE_FCHMOD */
c25d3df7 1147 error("Couldn't set mode on \"%s\": %s", local_path,
b77a87e5 1148 strerror(errno));
c25d3df7 1149 if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
1150 struct timeval tv[2];
1151 tv[0].tv_sec = a->atime;
1152 tv[1].tv_sec = a->mtime;
1153 tv[0].tv_usec = tv[1].tv_usec = 0;
1154 if (utimes(local_path, tv) == -1)
1155 error("Can't set times on \"%s\": %s",
b77a87e5 1156 local_path, strerror(errno));
c25d3df7 1157 }
58c54a79 1158 }
0426a3b4 1159 close(local_fd);
1160 buffer_free(&msg);
1161 xfree(handle);
22e6c827 1162
1163 return(status);
61e96248 1164}
1165
d141f964 1166static int
1167download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1168 Attrib *dirattrib, int pflag, int printflag, int depth)
1169{
1170 int i, ret = 0;
1171 SFTP_DIRENT **dir_entries;
1172 char *filename, *new_src, *new_dst;
1173 mode_t mode = 0777;
1174
1175 if (depth >= MAX_DIR_DEPTH) {
1176 error("Maximum directory depth exceeded: %d levels", depth);
1177 return -1;
1178 }
1179
1180 if (dirattrib == NULL &&
1181 (dirattrib = do_stat(conn, src, 1)) == NULL) {
1182 error("Unable to stat remote directory \"%s\"", src);
1183 return -1;
1184 }
1185 if (!S_ISDIR(dirattrib->perm)) {
1186 error("\"%s\" is not a directory", src);
1187 return -1;
1188 }
1189 if (printflag)
1190 printf("Retrieving %s\n", src);
1191
1192 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1193 mode = dirattrib->perm & 01777;
1194 else {
1195 debug("Server did not send permissions for "
1196 "directory \"%s\"", dst);
1197 }
1198
1199 if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1200 error("mkdir %s: %s", dst, strerror(errno));
1201 return -1;
1202 }
1203
1204 if (do_readdir(conn, src, &dir_entries) == -1) {
1205 error("%s: Failed to get directory contents", src);
1206 return -1;
1207 }
1208
1209 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1210 filename = dir_entries[i]->filename;
1211
1212 new_dst = path_append(dst, filename);
1213 new_src = path_append(src, filename);
1214
1215 if (S_ISDIR(dir_entries[i]->a.perm)) {
1216 if (strcmp(filename, ".") == 0 ||
1217 strcmp(filename, "..") == 0)
1218 continue;
1219 if (download_dir_internal(conn, new_src, new_dst,
1220 &(dir_entries[i]->a), pflag, printflag,
1221 depth + 1) == -1)
1222 ret = -1;
1223 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1224 if (do_download(conn, new_src, new_dst,
1225 &(dir_entries[i]->a), pflag) == -1) {
1226 error("Download of file %s to %s failed",
1227 new_src, new_dst);
1228 ret = -1;
1229 }
1230 } else
1231 logit("%s: not a regular file\n", new_src);
1232
1233 xfree(new_dst);
1234 xfree(new_src);
1235 }
1236
1237 if (pflag) {
1238 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1239 struct timeval tv[2];
1240 tv[0].tv_sec = dirattrib->atime;
1241 tv[1].tv_sec = dirattrib->mtime;
1242 tv[0].tv_usec = tv[1].tv_usec = 0;
1243 if (utimes(dst, tv) == -1)
1244 error("Can't set times on \"%s\": %s",
1245 dst, strerror(errno));
1246 } else
1247 debug("Server did not send times for directory "
1248 "\"%s\"", dst);
1249 }
1250
1251 free_sftp_dirents(dir_entries);
1252
1253 return ret;
1254}
1255
1256int
1257download_dir(struct sftp_conn *conn, char *src, char *dst,
1258 Attrib *dirattrib, int pflag, int printflag)
1259{
1260 char *src_canon;
1261 int ret;
1262
1263 if ((src_canon = do_realpath(conn, src)) == NULL) {
1264 error("Unable to canonicalise path \"%s\"", src);
1265 return -1;
1266 }
1267
1268 ret = download_dir_internal(conn, src_canon, dst,
1269 dirattrib, pflag, printflag, 0);
1270 xfree(src_canon);
1271 return ret;
1272}
1273
61e96248 1274int
22e6c827 1275do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1276 int pflag)
61e96248 1277{
514a858e 1278 int local_fd;
1279 int status = SSH2_FX_OK;
b2bab059 1280 u_int handle_len, id, type;
9e7f4c4f 1281 off_t offset;
375f867e 1282 char *handle, *data;
61e96248 1283 Buffer msg;
1284 struct stat sb;
1285 Attrib a;
c25d3df7 1286 u_int32_t startid;
1287 u_int32_t ackid;
b2bab059 1288 struct outstanding_ack {
1289 u_int id;
1290 u_int len;
9e7f4c4f 1291 off_t offset;
762715ce 1292 TAILQ_ENTRY(outstanding_ack) tq;
b2bab059 1293 };
1294 TAILQ_HEAD(ackhead, outstanding_ack) acks;
6e007f08 1295 struct outstanding_ack *ack = NULL;
b2bab059 1296
1297 TAILQ_INIT(&acks);
61e96248 1298
1299 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
1300 error("Couldn't open local file \"%s\" for reading: %s",
1301 local_path, strerror(errno));
1302 return(-1);
1303 }
1304 if (fstat(local_fd, &sb) == -1) {
1305 error("Couldn't fstat local file \"%s\": %s",
1306 local_path, strerror(errno));
1307 close(local_fd);
1308 return(-1);
1309 }
538840a2 1310 if (!S_ISREG(sb.st_mode)) {
1311 error("%s is not a regular file", local_path);
1312 close(local_fd);
1313 return(-1);
1314 }
61e96248 1315 stat_to_attrib(&sb, &a);
1316
1317 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1318 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1319 a.perm &= 0777;
1320 if (!pflag)
1321 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1322
1323 buffer_init(&msg);
1324
1325 /* Send open request */
22e6c827 1326 id = conn->msg_id++;
61e96248 1327 buffer_put_char(&msg, SSH2_FXP_OPEN);
1328 buffer_put_int(&msg, id);
1329 buffer_put_cstring(&msg, remote_path);
1330 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1331 encode_attrib(&msg, &a);
22e6c827 1332 send_msg(conn->fd_out, &msg);
9906a836 1333 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
61e96248 1334
1335 buffer_clear(&msg);
1336
e746280c 1337 handle = get_handle(conn->fd_in, id, &handle_len,
1338 "remote open(\"%s\")", remote_path);
61e96248 1339 if (handle == NULL) {
1340 close(local_fd);
1341 buffer_free(&msg);
514a858e 1342 return -1;
61e96248 1343 }
1344
c25d3df7 1345 startid = ackid = id + 1;
22e6c827 1346 data = xmalloc(conn->transfer_buflen);
375f867e 1347
61e96248 1348 /* Read from local and write to remote */
1349 offset = 0;
b65c3807 1350 if (showprogress)
1351 start_progress_meter(local_path, sb.st_size, &offset);
b65c3807 1352
184eed6a 1353 for (;;) {
61e96248 1354 int len;
61e96248 1355
1356 /*
f2107e97 1357 * Can't use atomicio here because it returns 0 on EOF,
0e5de6f8 1358 * thus losing the last block of the file.
f2107e97 1359 * Simulate an EOF on interrupt, allowing ACKs from the
0e5de6f8 1360 * server to drain.
61e96248 1361 */
514a858e 1362 if (interrupted || status != SSH2_FX_OK)
0e5de6f8 1363 len = 0;
1364 else do
22e6c827 1365 len = read(local_fd, data, conn->transfer_buflen);
5a0c8771 1366 while ((len == -1) &&
1367 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
61e96248 1368
1369 if (len == -1)
1370 fatal("Couldn't read from \"%s\": %s", local_path,
1371 strerror(errno));
c25d3df7 1372
1373 if (len != 0) {
b2bab059 1374 ack = xmalloc(sizeof(*ack));
1375 ack->id = ++id;
1376 ack->offset = offset;
1377 ack->len = len;
1378 TAILQ_INSERT_TAIL(&acks, ack, tq);
1379
c25d3df7 1380 buffer_clear(&msg);
1381 buffer_put_char(&msg, SSH2_FXP_WRITE);
b2bab059 1382 buffer_put_int(&msg, ack->id);
c25d3df7 1383 buffer_put_string(&msg, handle, handle_len);
1384 buffer_put_int64(&msg, offset);
1385 buffer_put_string(&msg, data, len);
22e6c827 1386 send_msg(conn->fd_out, &msg);
9906a836 1387 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
b77a87e5 1388 id, (unsigned long long)offset, len);
b2bab059 1389 } else if (TAILQ_FIRST(&acks) == NULL)
61e96248 1390 break;
1391
b2bab059 1392 if (ack == NULL)
1393 fatal("Unexpected ACK %u", id);
1394
762715ce 1395 if (id == startid || len == 0 ||
22e6c827 1396 id - ackid >= conn->num_requests) {
875ec275 1397 u_int r_id;
af2b3cd9 1398
b2bab059 1399 buffer_clear(&msg);
22e6c827 1400 get_msg(conn->fd_in, &msg);
b2bab059 1401 type = buffer_get_char(&msg);
af2b3cd9 1402 r_id = buffer_get_int(&msg);
b2bab059 1403
1404 if (type != SSH2_FXP_STATUS)
1405 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
1406 "got %d", SSH2_FXP_STATUS, type);
1407
1408 status = buffer_get_int(&msg);
1409 debug3("SSH2_FXP_STATUS %d", status);
1410
1411 /* Find the request in our queue */
f8cc7664 1412 for (ack = TAILQ_FIRST(&acks);
af2b3cd9 1413 ack != NULL && ack->id != r_id;
b2bab059 1414 ack = TAILQ_NEXT(ack, tq))
1415 ;
1416 if (ack == NULL)
9906a836 1417 fatal("Can't find request for ID %u", r_id);
b2bab059 1418 TAILQ_REMOVE(&acks, ack, tq);
9e7f4c4f 1419 debug3("In write loop, ack for %u %u bytes at %lld",
1420 ack->id, ack->len, (long long)ack->offset);
c25d3df7 1421 ++ackid;
30e37ee6 1422 xfree(ack);
61e96248 1423 }
61e96248 1424 offset += len;
9e7f4c4f 1425 if (offset < 0)
1426 fatal("%s: offset < 0", __func__);
61e96248 1427 }
514a858e 1428 buffer_free(&msg);
1429
b65c3807 1430 if (showprogress)
1431 stop_progress_meter();
375f867e 1432 xfree(data);
61e96248 1433
514a858e 1434 if (status != SSH2_FX_OK) {
1435 error("Couldn't write to remote file \"%s\": %s",
1436 remote_path, fx2txt(status));
1437 status = -1;
1438 }
1439
61e96248 1440 if (close(local_fd) == -1) {
1441 error("Couldn't close local file \"%s\": %s", local_path,
1442 strerror(errno));
0426a3b4 1443 status = -1;
61e96248 1444 }
1445
58c54a79 1446 /* Override umask and utimes if asked */
1447 if (pflag)
22e6c827 1448 do_fsetstat(conn, handle, handle_len, &a);
58c54a79 1449
514a858e 1450 if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1451 status = -1;
0426a3b4 1452 xfree(handle);
514a858e 1453
1454 return status;
61e96248 1455}
d141f964 1456
c7e0fa79 1457static mode_t
1458dirent_to_mode(struct dirent *dp)
1459{
1460#if defined(HAVE_STRUCT_DIRENT_D_TYPE) && defined(DTTOIF)
1461 return DTTOIF(dp->d_type);
1462#else
1463 struct stat sb;
1464
1465 if (stat(dp->d_name, &sb) == -1)
1466 return 0;
1467 return sb.st_mode;
1468#endif
1469}
1470
d141f964 1471static int
1472upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1473 int pflag, int printflag, int depth)
1474{
1475 int ret = 0, status;
1476 DIR *dirp;
1477 struct dirent *dp;
1478 char *filename, *new_src, *new_dst;
1479 struct stat sb;
1480 Attrib a;
1481
1482 if (depth >= MAX_DIR_DEPTH) {
1483 error("Maximum directory depth exceeded: %d levels", depth);
1484 return -1;
1485 }
1486
1487 if (stat(src, &sb) == -1) {
1488 error("Couldn't stat directory \"%s\": %s",
1489 src, strerror(errno));
1490 return -1;
1491 }
1492 if (!S_ISDIR(sb.st_mode)) {
1493 error("\"%s\" is not a directory", src);
1494 return -1;
1495 }
1496 if (printflag)
1497 printf("Entering %s\n", src);
1498
1499 attrib_clear(&a);
1500 stat_to_attrib(&sb, &a);
1501 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1502 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1503 a.perm &= 01777;
1504 if (!pflag)
1505 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1506
1507 status = do_mkdir(conn, dst, &a, 0);
1508 /*
1509 * we lack a portable status for errno EEXIST,
1510 * so if we get a SSH2_FX_FAILURE back we must check
1511 * if it was created successfully.
1512 */
1513 if (status != SSH2_FX_OK) {
1514 if (status != SSH2_FX_FAILURE)
1515 return -1;
1516 if (do_stat(conn, dst, 0) == NULL)
1517 return -1;
1518 }
1519
1520 if ((dirp = opendir(src)) == NULL) {
1521 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1522 return -1;
1523 }
1524
1525 while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1526 if (dp->d_ino == 0)
1527 continue;
1528 filename = dp->d_name;
1529 new_dst = path_append(dst, filename);
1530 new_src = path_append(src, filename);
1531
c7e0fa79 1532 if (S_ISDIR(dirent_to_mode(dp))) {
d141f964 1533 if (strcmp(filename, ".") == 0 ||
1534 strcmp(filename, "..") == 0)
1535 continue;
1536
1537 if (upload_dir_internal(conn, new_src, new_dst,
1538 pflag, depth + 1, printflag) == -1)
1539 ret = -1;
c7e0fa79 1540 } else if (S_ISREG(dirent_to_mode(dp))) {
d141f964 1541 if (do_upload(conn, new_src, new_dst, pflag) == -1) {
1542 error("Uploading of file %s to %s failed!",
1543 new_src, new_dst);
1544 ret = -1;
1545 }
1546 } else
1547 logit("%s: not a regular file\n", filename);
1548 xfree(new_dst);
1549 xfree(new_src);
1550 }
1551
1552 do_setstat(conn, dst, &a);
1553
1554 (void) closedir(dirp);
1555 return ret;
1556}
1557
1558int
1559upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
1560 int pflag)
1561{
1562 char *dst_canon;
1563 int ret;
1564
1565 if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1566 error("Unable to canonicalise path \"%s\"", dst);
1567 return -1;
1568 }
1569
1570 ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
1571 xfree(dst_canon);
1572 return ret;
1573}
1574
1575char *
1576path_append(char *p1, char *p2)
1577{
1578 char *ret;
1579 size_t len = strlen(p1) + strlen(p2) + 2;
1580
1581 ret = xmalloc(len);
1582 strlcpy(ret, p1, len);
1583 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1584 strlcat(ret, "/", len);
1585 strlcat(ret, p2, len);
1586
1587 return(ret);
1588}
1589
This page took 0.513302 seconds and 5 git commands to generate.