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