sftp-client.c revision 1.29
1/* $NetBSD: sftp-client.c,v 1.29 2021/09/27 17:03:13 christos Exp $ */ 2/* $OpenBSD: sftp-client.c,v 1.155 2021/09/03 05:12:25 dtucker Exp $ */ 3 4/* 5 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20/* XXX: memleaks */ 21/* XXX: signed vs unsigned */ 22/* XXX: remove all logging, only return status codes */ 23/* XXX: copy between two remote sites */ 24 25#include "includes.h" 26__RCSID("$NetBSD: sftp-client.c,v 1.29 2021/09/27 17:03:13 christos Exp $"); 27 28#include <sys/param.h> /* MIN MAX */ 29#include <sys/types.h> 30#include <sys/poll.h> 31#include <sys/queue.h> 32#include <sys/stat.h> 33#include <sys/time.h> 34#include <sys/statvfs.h> 35#include <sys/uio.h> 36 37#include <dirent.h> 38#include <errno.h> 39#include <fcntl.h> 40#include <poll.h> 41#include <signal.h> 42#include <stdarg.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <unistd.h> 47 48#include "xmalloc.h" 49#include "ssherr.h" 50#include "sshbuf.h" 51#include "log.h" 52#include "atomicio.h" 53#include "progressmeter.h" 54#include "misc.h" 55#include "utf8.h" 56 57#include "sftp.h" 58#include "sftp-common.h" 59#include "sftp-client.h" 60 61extern volatile sig_atomic_t interrupted; 62extern int showprogress; 63 64/* Default size of buffer for up/download */ 65#define DEFAULT_COPY_BUFLEN 32768 66 67/* Default number of concurrent outstanding requests */ 68#define DEFAULT_NUM_REQUESTS 64 69 70/* Minimum amount of data to read at a time */ 71#define MIN_READ_SIZE 512 72 73/* Maximum depth to descend in directory trees */ 74#define MAX_DIR_DEPTH 64 75 76struct sftp_conn { 77 int fd_in; 78 int fd_out; 79 u_int download_buflen; 80 u_int upload_buflen; 81 u_int num_requests; 82 u_int version; 83 u_int msg_id; 84#define SFTP_EXT_POSIX_RENAME 0x00000001 85#define SFTP_EXT_STATVFS 0x00000002 86#define SFTP_EXT_FSTATVFS 0x00000004 87#define SFTP_EXT_HARDLINK 0x00000008 88#define SFTP_EXT_FSYNC 0x00000010 89#define SFTP_EXT_LSETSTAT 0x00000020 90#define SFTP_EXT_LIMITS 0x00000040 91#define SFTP_EXT_PATH_EXPAND 0x00000080 92 u_int exts; 93 u_int64_t limit_kbps; 94 struct bwlimit bwlimit_in, bwlimit_out; 95}; 96 97/* Tracks in-progress requests during file transfers */ 98struct request { 99 u_int id; 100 size_t len; 101 u_int64_t offset; 102 TAILQ_ENTRY(request) tq; 103}; 104TAILQ_HEAD(requests, request); 105 106static u_char * 107get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, 108 const char *errfmt, ...) __attribute__((format(printf, 4, 5))); 109 110static struct request * 111request_enqueue(struct requests *requests, u_int id, size_t len, 112 uint64_t offset) 113{ 114 struct request *req; 115 116 req = xcalloc(1, sizeof(*req)); 117 req->id = id; 118 req->len = len; 119 req->offset = offset; 120 TAILQ_INSERT_TAIL(requests, req, tq); 121 return req; 122} 123 124static struct request * 125request_find(struct requests *requests, u_int id) 126{ 127 struct request *req; 128 129 for (req = TAILQ_FIRST(requests); 130 req != NULL && req->id != id; 131 req = TAILQ_NEXT(req, tq)) 132 ; 133 return req; 134} 135 136/* ARGSUSED */ 137static int 138sftpio(void *_bwlimit, size_t amount) 139{ 140 struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; 141 142 refresh_progress_meter(0); 143 if (bwlimit != NULL) 144 bandwidth_limit(bwlimit, amount); 145 return 0; 146} 147 148static void 149send_msg(struct sftp_conn *conn, struct sshbuf *m) 150{ 151 u_char mlen[4]; 152 struct iovec iov[2]; 153 154 if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH) 155 fatal("Outbound message too long %zu", sshbuf_len(m)); 156 157 /* Send length first */ 158 put_u32(mlen, sshbuf_len(m)); 159 iov[0].iov_base = mlen; 160 iov[0].iov_len = sizeof(mlen); 161 iov[1].iov_base = __UNCONST(sshbuf_ptr(m)); 162 iov[1].iov_len = sshbuf_len(m); 163 164 if (atomiciov6(writev, conn->fd_out, iov, 2, sftpio, 165 conn->limit_kbps > 0 ? &conn->bwlimit_out : NULL) != 166 sshbuf_len(m) + sizeof(mlen)) 167 fatal("Couldn't send packet: %s", strerror(errno)); 168 169 sshbuf_reset(m); 170} 171 172static void 173get_msg_extended(struct sftp_conn *conn, struct sshbuf *m, int initial) 174{ 175 u_int msg_len; 176 u_char *p; 177 int r; 178 179 sshbuf_reset(m); 180 if ((r = sshbuf_reserve(m, 4, &p)) != 0) 181 fatal_fr(r, "reserve"); 182 if (atomicio6(read, conn->fd_in, p, 4, sftpio, 183 conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) != 4) { 184 if (errno == EPIPE || errno == ECONNRESET) 185 fatal("Connection closed"); 186 else 187 fatal("Couldn't read packet: %s", strerror(errno)); 188 } 189 190 if ((r = sshbuf_get_u32(m, &msg_len)) != 0) 191 fatal_fr(r, "sshbuf_get_u32"); 192 if (msg_len > SFTP_MAX_MSG_LENGTH) { 193 do_log2(initial ? SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_FATAL, 194 "Received message too long %u", msg_len); 195 fatal("Ensure the remote shell produces no output " 196 "for non-interactive sessions."); 197 } 198 199 if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) 200 fatal_fr(r, "reserve"); 201 if (atomicio6(read, conn->fd_in, p, msg_len, sftpio, 202 conn->limit_kbps > 0 ? &conn->bwlimit_in : NULL) 203 != msg_len) { 204 if (errno == EPIPE) 205 fatal("Connection closed"); 206 else 207 fatal("Read packet: %s", strerror(errno)); 208 } 209} 210 211static void 212get_msg(struct sftp_conn *conn, struct sshbuf *m) 213{ 214 get_msg_extended(conn, m, 0); 215} 216 217static void 218send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s, 219 u_int len) 220{ 221 struct sshbuf *msg; 222 int r; 223 224 if ((msg = sshbuf_new()) == NULL) 225 fatal_f("sshbuf_new failed"); 226 if ((r = sshbuf_put_u8(msg, code)) != 0 || 227 (r = sshbuf_put_u32(msg, id)) != 0 || 228 (r = sshbuf_put_string(msg, s, len)) != 0) 229 fatal_fr(r, "compose"); 230 send_msg(conn, msg); 231 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); 232 sshbuf_free(msg); 233} 234 235static void 236send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code, 237 const void *s, u_int len, Attrib *a) 238{ 239 struct sshbuf *msg; 240 int r; 241 242 if ((msg = sshbuf_new()) == NULL) 243 fatal_f("sshbuf_new failed"); 244 if ((r = sshbuf_put_u8(msg, code)) != 0 || 245 (r = sshbuf_put_u32(msg, id)) != 0 || 246 (r = sshbuf_put_string(msg, s, len)) != 0 || 247 (r = encode_attrib(msg, a)) != 0) 248 fatal_fr(r, "compose"); 249 send_msg(conn, msg); 250 debug3("Sent message fd %d T:%u I:%u F:0x%04x M:%05o", 251 conn->fd_out, code, id, a->flags, a->perm); 252 sshbuf_free(msg); 253} 254 255static u_int 256get_status(struct sftp_conn *conn, u_int expected_id) 257{ 258 struct sshbuf *msg; 259 u_char type; 260 u_int id, status; 261 int r; 262 263 if ((msg = sshbuf_new()) == NULL) 264 fatal_f("sshbuf_new failed"); 265 get_msg(conn, msg); 266 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 267 (r = sshbuf_get_u32(msg, &id)) != 0) 268 fatal_fr(r, "compose"); 269 270 if (id != expected_id) 271 fatal("ID mismatch (%u != %u)", id, expected_id); 272 if (type != SSH2_FXP_STATUS) 273 fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", 274 SSH2_FXP_STATUS, type); 275 276 if ((r = sshbuf_get_u32(msg, &status)) != 0) 277 fatal_fr(r, "parse"); 278 sshbuf_free(msg); 279 280 debug3("SSH2_FXP_STATUS %u", status); 281 282 return status; 283} 284 285static u_char * 286get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, 287 const char *errfmt, ...) 288{ 289 struct sshbuf *msg; 290 u_int id, status; 291 u_char type; 292 u_char *handle; 293 char errmsg[256]; 294 va_list args; 295 int r; 296 297 va_start(args, errfmt); 298 if (errfmt != NULL) 299 vsnprintf(errmsg, sizeof(errmsg), errfmt, args); 300 va_end(args); 301 302 if ((msg = sshbuf_new()) == NULL) 303 fatal_f("sshbuf_new failed"); 304 get_msg(conn, msg); 305 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 306 (r = sshbuf_get_u32(msg, &id)) != 0) 307 fatal_fr(r, "parse"); 308 309 if (id != expected_id) 310 fatal("%s: ID mismatch (%u != %u)", 311 errfmt == NULL ? __func__ : errmsg, id, expected_id); 312 if (type == SSH2_FXP_STATUS) { 313 if ((r = sshbuf_get_u32(msg, &status)) != 0) 314 fatal_fr(r, "parse status"); 315 if (errfmt != NULL) 316 error("%s: %s", errmsg, fx2txt(status)); 317 sshbuf_free(msg); 318 return(NULL); 319 } else if (type != SSH2_FXP_HANDLE) 320 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", 321 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); 322 323 if ((r = sshbuf_get_string(msg, &handle, len)) != 0) 324 fatal_fr(r, "parse handle"); 325 sshbuf_free(msg); 326 327 return handle; 328} 329 330/* XXX returing &static is error-prone. Refactor to fill *Attrib argument */ 331static Attrib * 332get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet) 333{ 334 struct sshbuf *msg; 335 u_int id; 336 u_char type; 337 int r; 338 static Attrib a; 339 340 if ((msg = sshbuf_new()) == NULL) 341 fatal_f("sshbuf_new failed"); 342 get_msg(conn, msg); 343 344 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 345 (r = sshbuf_get_u32(msg, &id)) != 0) 346 fatal_fr(r, "parse"); 347 348 if (id != expected_id) 349 fatal("ID mismatch (%u != %u)", id, expected_id); 350 if (type == SSH2_FXP_STATUS) { 351 u_int status; 352 353 if ((r = sshbuf_get_u32(msg, &status)) != 0) 354 fatal_fr(r, "parse status"); 355 if (quiet) 356 debug("Couldn't stat remote file: %s", fx2txt(status)); 357 else 358 error("Couldn't stat remote file: %s", fx2txt(status)); 359 sshbuf_free(msg); 360 return(NULL); 361 } else if (type != SSH2_FXP_ATTRS) { 362 fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", 363 SSH2_FXP_ATTRS, type); 364 } 365 if ((r = decode_attrib(msg, &a)) != 0) { 366 error_fr(r, "decode_attrib"); 367 sshbuf_free(msg); 368 return NULL; 369 } 370 debug3("Recevied stat reply T:%u I:%u F:0x%04x M:%05o", 371 type, id, a.flags, a.perm); 372 sshbuf_free(msg); 373 374 return &a; 375} 376 377static int 378get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st, 379 u_int expected_id, int quiet) 380{ 381 struct sshbuf *msg; 382 u_char type; 383 u_int id; 384 u_int64_t flag; 385 int r; 386 387 if ((msg = sshbuf_new()) == NULL) 388 fatal_f("sshbuf_new failed"); 389 get_msg(conn, msg); 390 391 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 392 (r = sshbuf_get_u32(msg, &id)) != 0) 393 fatal_fr(r, "parse"); 394 395 debug3("Received statvfs reply T:%u I:%u", type, id); 396 if (id != expected_id) 397 fatal("ID mismatch (%u != %u)", id, expected_id); 398 if (type == SSH2_FXP_STATUS) { 399 u_int status; 400 401 if ((r = sshbuf_get_u32(msg, &status)) != 0) 402 fatal_fr(r, "parse status"); 403 if (quiet) 404 debug("Couldn't statvfs: %s", fx2txt(status)); 405 else 406 error("Couldn't statvfs: %s", fx2txt(status)); 407 sshbuf_free(msg); 408 return -1; 409 } else if (type != SSH2_FXP_EXTENDED_REPLY) { 410 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", 411 SSH2_FXP_EXTENDED_REPLY, type); 412 } 413 414 memset(st, 0, sizeof(*st)); 415 if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 || 416 (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 || 417 (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 || 418 (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 || 419 (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 || 420 (r = sshbuf_get_u64(msg, &st->f_files)) != 0 || 421 (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 || 422 (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 || 423 (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 || 424 (r = sshbuf_get_u64(msg, &flag)) != 0 || 425 (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0) 426 fatal_fr(r, "parse statvfs"); 427 428 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; 429 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; 430 431 sshbuf_free(msg); 432 433 return 0; 434} 435 436struct sftp_conn * 437do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, 438 u_int64_t limit_kbps) 439{ 440 u_char type; 441 struct sshbuf *msg; 442 struct sftp_conn *ret; 443 int r; 444 445 ret = xcalloc(1, sizeof(*ret)); 446 ret->msg_id = 1; 447 ret->fd_in = fd_in; 448 ret->fd_out = fd_out; 449 ret->download_buflen = ret->upload_buflen = 450 transfer_buflen ? transfer_buflen : DEFAULT_COPY_BUFLEN; 451 ret->num_requests = 452 num_requests ? num_requests : DEFAULT_NUM_REQUESTS; 453 ret->exts = 0; 454 ret->limit_kbps = 0; 455 456 if ((msg = sshbuf_new()) == NULL) 457 fatal_f("sshbuf_new failed"); 458 if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 || 459 (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) 460 fatal_fr(r, "parse"); 461 462 send_msg(ret, msg); 463 464 get_msg_extended(ret, msg, 1); 465 466 /* Expecting a VERSION reply */ 467 if ((r = sshbuf_get_u8(msg, &type)) != 0) 468 fatal_fr(r, "parse type"); 469 if (type != SSH2_FXP_VERSION) { 470 error("Invalid packet back from SSH2_FXP_INIT (type %u)", 471 type); 472 sshbuf_free(msg); 473 free(ret); 474 return(NULL); 475 } 476 if ((r = sshbuf_get_u32(msg, &ret->version)) != 0) 477 fatal_fr(r, "parse version"); 478 479 debug2("Remote version: %u", ret->version); 480 481 /* Check for extensions */ 482 while (sshbuf_len(msg) > 0) { 483 char *name; 484 u_char *value; 485 size_t vlen; 486 int known = 0; 487 488 if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 || 489 (r = sshbuf_get_string(msg, &value, &vlen)) != 0) 490 fatal_fr(r, "parse extension"); 491 if (strcmp(name, "posix-rename@openssh.com") == 0 && 492 strcmp((char *)value, "1") == 0) { 493 ret->exts |= SFTP_EXT_POSIX_RENAME; 494 known = 1; 495 } else if (strcmp(name, "statvfs@openssh.com") == 0 && 496 strcmp((char *)value, "2") == 0) { 497 ret->exts |= SFTP_EXT_STATVFS; 498 known = 1; 499 } else if (strcmp(name, "fstatvfs@openssh.com") == 0 && 500 strcmp((char *)value, "2") == 0) { 501 ret->exts |= SFTP_EXT_FSTATVFS; 502 known = 1; 503 } else if (strcmp(name, "hardlink@openssh.com") == 0 && 504 strcmp((char *)value, "1") == 0) { 505 ret->exts |= SFTP_EXT_HARDLINK; 506 known = 1; 507 } else if (strcmp(name, "fsync@openssh.com") == 0 && 508 strcmp((char *)value, "1") == 0) { 509 ret->exts |= SFTP_EXT_FSYNC; 510 known = 1; 511 } else if (strcmp(name, "lsetstat@openssh.com") == 0 && 512 strcmp((char *)value, "1") == 0) { 513 ret->exts |= SFTP_EXT_LSETSTAT; 514 known = 1; 515 } else if (strcmp(name, "limits@openssh.com") == 0 && 516 strcmp((char *)value, "1") == 0) { 517 ret->exts |= SFTP_EXT_LIMITS; 518 known = 1; 519 } else if (strcmp(name, "expand-path@openssh.com") == 0 && 520 strcmp((char *)value, "1") == 0) { 521 ret->exts |= SFTP_EXT_PATH_EXPAND; 522 known = 1; 523 } 524 if (known) { 525 debug2("Server supports extension \"%s\" revision %s", 526 name, value); 527 } else { 528 debug2("Unrecognised server extension \"%s\"", name); 529 } 530 free(name); 531 free(value); 532 } 533 534 sshbuf_free(msg); 535 536 /* Query the server for its limits */ 537 if (ret->exts & SFTP_EXT_LIMITS) { 538 struct sftp_limits limits; 539 if (do_limits(ret, &limits) != 0) 540 fatal_f("limits failed"); 541 542 /* If the caller did not specify, find a good value */ 543 if (transfer_buflen == 0) { 544 ret->download_buflen = limits.read_length; 545 ret->upload_buflen = limits.write_length; 546 debug("Using server download size %u", ret->download_buflen); 547 debug("Using server upload size %u", ret->upload_buflen); 548 } 549 550 /* Use the server limit to scale down our value only */ 551 if (num_requests == 0 && limits.open_handles) { 552 ret->num_requests = 553 MINIMUM(DEFAULT_NUM_REQUESTS, limits.open_handles); 554 debug("Server handle limit %llu; using %u", 555 (unsigned long long)limits.open_handles, 556 ret->num_requests); 557 } 558 } 559 560 /* Some filexfer v.0 servers don't support large packets */ 561 if (ret->version == 0) { 562 ret->download_buflen = MINIMUM(ret->download_buflen, 20480); 563 ret->upload_buflen = MINIMUM(ret->upload_buflen, 20480); 564 } 565 566 ret->limit_kbps = limit_kbps; 567 if (ret->limit_kbps > 0) { 568 bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps, 569 ret->download_buflen); 570 bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps, 571 ret->upload_buflen); 572 } 573 574 return ret; 575} 576 577u_int 578sftp_proto_version(struct sftp_conn *conn) 579{ 580 return conn->version; 581} 582 583int 584do_limits(struct sftp_conn *conn, struct sftp_limits *limits) 585{ 586 u_int id, msg_id; 587 u_char type; 588 struct sshbuf *msg; 589 int r; 590 591 if ((conn->exts & SFTP_EXT_LIMITS) == 0) { 592 error("Server does not support limits@openssh.com extension"); 593 return -1; 594 } 595 596 if ((msg = sshbuf_new()) == NULL) 597 fatal_f("sshbuf_new failed"); 598 599 id = conn->msg_id++; 600 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 601 (r = sshbuf_put_u32(msg, id)) != 0 || 602 (r = sshbuf_put_cstring(msg, "limits@openssh.com")) != 0) 603 fatal_fr(r, "compose"); 604 send_msg(conn, msg); 605 debug3("Sent message limits@openssh.com I:%u", id); 606 607 get_msg(conn, msg); 608 609 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 610 (r = sshbuf_get_u32(msg, &msg_id)) != 0) 611 fatal_fr(r, "parse"); 612 613 debug3("Received limits reply T:%u I:%u", type, msg_id); 614 if (id != msg_id) 615 fatal("ID mismatch (%u != %u)", msg_id, id); 616 if (type != SSH2_FXP_EXTENDED_REPLY) { 617 debug_f("expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", 618 SSH2_FXP_EXTENDED_REPLY, type); 619 /* Disable the limits extension */ 620 conn->exts &= ~SFTP_EXT_LIMITS; 621 sshbuf_free(msg); 622 return 0; 623 } 624 625 memset(limits, 0, sizeof(*limits)); 626 if ((r = sshbuf_get_u64(msg, &limits->packet_length)) != 0 || 627 (r = sshbuf_get_u64(msg, &limits->read_length)) != 0 || 628 (r = sshbuf_get_u64(msg, &limits->write_length)) != 0 || 629 (r = sshbuf_get_u64(msg, &limits->open_handles)) != 0) 630 fatal_fr(r, "parse limits"); 631 632 sshbuf_free(msg); 633 634 return 0; 635} 636 637int 638do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) 639{ 640 u_int id, status; 641 struct sshbuf *msg; 642 int r; 643 644 if ((msg = sshbuf_new()) == NULL) 645 fatal_f("sshbuf_new failed"); 646 647 id = conn->msg_id++; 648 if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 || 649 (r = sshbuf_put_u32(msg, id)) != 0 || 650 (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 651 fatal_fr(r, "parse"); 652 send_msg(conn, msg); 653 debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 654 655 status = get_status(conn, id); 656 if (status != SSH2_FX_OK) 657 error("Couldn't close file: %s", fx2txt(status)); 658 659 sshbuf_free(msg); 660 661 return status == SSH2_FX_OK ? 0 : -1; 662} 663 664 665static int 666do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, 667 SFTP_DIRENT ***dir) 668{ 669 struct sshbuf *msg; 670 u_int count, id, i, expected_id, ents = 0; 671 size_t handle_len; 672 u_char type, *handle; 673 int status = SSH2_FX_FAILURE; 674 int r; 675 676 if (dir) 677 *dir = NULL; 678 679 id = conn->msg_id++; 680 681 if ((msg = sshbuf_new()) == NULL) 682 fatal_f("sshbuf_new failed"); 683 if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 || 684 (r = sshbuf_put_u32(msg, id)) != 0 || 685 (r = sshbuf_put_cstring(msg, path)) != 0) 686 fatal_fr(r, "compose OPENDIR"); 687 send_msg(conn, msg); 688 689 handle = get_handle(conn, id, &handle_len, 690 "remote readdir(\"%s\")", path); 691 if (handle == NULL) { 692 sshbuf_free(msg); 693 return -1; 694 } 695 696 if (dir) { 697 ents = 0; 698 *dir = xcalloc(1, sizeof(**dir)); 699 (*dir)[0] = NULL; 700 } 701 702 for (; !interrupted;) { 703 id = expected_id = conn->msg_id++; 704 705 debug3("Sending SSH2_FXP_READDIR I:%u", id); 706 707 sshbuf_reset(msg); 708 if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 || 709 (r = sshbuf_put_u32(msg, id)) != 0 || 710 (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 711 fatal_fr(r, "compose READDIR"); 712 send_msg(conn, msg); 713 714 sshbuf_reset(msg); 715 716 get_msg(conn, msg); 717 718 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 719 (r = sshbuf_get_u32(msg, &id)) != 0) 720 fatal_fr(r, "parse"); 721 722 debug3("Received reply T:%u I:%u", type, id); 723 724 if (id != expected_id) 725 fatal("ID mismatch (%u != %u)", id, expected_id); 726 727 if (type == SSH2_FXP_STATUS) { 728 u_int rstatus; 729 730 if ((r = sshbuf_get_u32(msg, &rstatus)) != 0) 731 fatal_fr(r, "parse status"); 732 debug3("Received SSH2_FXP_STATUS %d", rstatus); 733 if (rstatus == SSH2_FX_EOF) 734 break; 735 error("Couldn't read directory: %s", fx2txt(rstatus)); 736 goto out; 737 } else if (type != SSH2_FXP_NAME) 738 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 739 SSH2_FXP_NAME, type); 740 741 if ((r = sshbuf_get_u32(msg, &count)) != 0) 742 fatal_fr(r, "parse count"); 743 if (count > SSHBUF_SIZE_MAX) 744 fatal_f("nonsensical number of entries"); 745 if (count == 0) 746 break; 747 debug3("Received %d SSH2_FXP_NAME responses", count); 748 for (i = 0; i < count; i++) { 749 char *filename, *longname; 750 Attrib a; 751 752 if ((r = sshbuf_get_cstring(msg, &filename, 753 NULL)) != 0 || 754 (r = sshbuf_get_cstring(msg, &longname, 755 NULL)) != 0) 756 fatal_fr(r, "parse filenames"); 757 if ((r = decode_attrib(msg, &a)) != 0) { 758 error_fr(r, "couldn't decode attrib"); 759 free(filename); 760 free(longname); 761 goto out; 762 } 763 764 if (print_flag) 765 mprintf("%s\n", longname); 766 767 /* 768 * Directory entries should never contain '/' 769 * These can be used to attack recursive ops 770 * (e.g. send '../../../../etc/passwd') 771 */ 772 if (strchr(filename, '/') != NULL) { 773 error("Server sent suspect path \"%s\" " 774 "during readdir of \"%s\"", filename, path); 775 } else if (dir) { 776 *dir = xreallocarray(*dir, ents + 2, sizeof(**dir)); 777 (*dir)[ents] = xcalloc(1, sizeof(***dir)); 778 (*dir)[ents]->filename = xstrdup(filename); 779 (*dir)[ents]->longname = xstrdup(longname); 780 memcpy(&(*dir)[ents]->a, &a, sizeof(a)); 781 (*dir)[++ents] = NULL; 782 } 783 free(filename); 784 free(longname); 785 } 786 } 787 status = 0; 788 789 out: 790 sshbuf_free(msg); 791 do_close(conn, handle, handle_len); 792 free(handle); 793 794 if (status != 0 && dir != NULL) { 795 /* Don't return results on error */ 796 free_sftp_dirents(*dir); 797 *dir = NULL; 798 } else if (interrupted && dir != NULL && *dir != NULL) { 799 /* Don't return partial matches on interrupt */ 800 free_sftp_dirents(*dir); 801 *dir = xcalloc(1, sizeof(**dir)); 802 **dir = NULL; 803 } 804 805 return status == SSH2_FX_OK ? 0 : -1; 806} 807 808int 809do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) 810{ 811 return(do_lsreaddir(conn, path, 0, dir)); 812} 813 814void free_sftp_dirents(SFTP_DIRENT **s) 815{ 816 int i; 817 818 if (s == NULL) 819 return; 820 for (i = 0; s[i]; i++) { 821 free(s[i]->filename); 822 free(s[i]->longname); 823 free(s[i]); 824 } 825 free(s); 826} 827 828int 829do_rm(struct sftp_conn *conn, const char *path) 830{ 831 u_int status, id; 832 833 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 834 835 id = conn->msg_id++; 836 send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path)); 837 status = get_status(conn, id); 838 if (status != SSH2_FX_OK) 839 error("Couldn't delete file: %s", fx2txt(status)); 840 return status == SSH2_FX_OK ? 0 : -1; 841} 842 843int 844do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) 845{ 846 u_int status, id; 847 848 id = conn->msg_id++; 849 send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path, 850 strlen(path), a); 851 852 status = get_status(conn, id); 853 if (status != SSH2_FX_OK && print_flag) 854 error("Couldn't create directory: %s", fx2txt(status)); 855 856 return status == SSH2_FX_OK ? 0 : -1; 857} 858 859int 860do_rmdir(struct sftp_conn *conn, const char *path) 861{ 862 u_int status, id; 863 864 id = conn->msg_id++; 865 send_string_request(conn, id, SSH2_FXP_RMDIR, path, 866 strlen(path)); 867 868 status = get_status(conn, id); 869 if (status != SSH2_FX_OK) 870 error("Couldn't remove directory: %s", fx2txt(status)); 871 872 return status == SSH2_FX_OK ? 0 : -1; 873} 874 875Attrib * 876do_stat(struct sftp_conn *conn, const char *path, int quiet) 877{ 878 u_int id; 879 880 id = conn->msg_id++; 881 882 send_string_request(conn, id, 883 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 884 path, strlen(path)); 885 886 return(get_decode_stat(conn, id, quiet)); 887} 888 889Attrib * 890do_lstat(struct sftp_conn *conn, const char *path, int quiet) 891{ 892 u_int id; 893 894 if (conn->version == 0) { 895 if (quiet) 896 debug("Server version does not support lstat operation"); 897 else 898 logit("Server version does not support lstat operation"); 899 return(do_stat(conn, path, quiet)); 900 } 901 902 id = conn->msg_id++; 903 send_string_request(conn, id, SSH2_FXP_LSTAT, path, 904 strlen(path)); 905 906 return(get_decode_stat(conn, id, quiet)); 907} 908 909#ifdef notyet 910Attrib * 911do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, 912 int quiet) 913{ 914 u_int id; 915 916 id = conn->msg_id++; 917 send_string_request(conn, id, SSH2_FXP_FSTAT, handle, 918 handle_len); 919 920 return(get_decode_stat(conn, id, quiet)); 921} 922#endif 923 924int 925do_setstat(struct sftp_conn *conn, const char *path, Attrib *a) 926{ 927 u_int status, id; 928 929 id = conn->msg_id++; 930 send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path, 931 strlen(path), a); 932 933 status = get_status(conn, id); 934 if (status != SSH2_FX_OK) 935 error("Couldn't setstat on \"%s\": %s", path, 936 fx2txt(status)); 937 938 return status == SSH2_FX_OK ? 0 : -1; 939} 940 941int 942do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, 943 Attrib *a) 944{ 945 u_int status, id; 946 947 id = conn->msg_id++; 948 send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle, 949 handle_len, a); 950 951 status = get_status(conn, id); 952 if (status != SSH2_FX_OK) 953 error("Couldn't fsetstat: %s", fx2txt(status)); 954 955 return status == SSH2_FX_OK ? 0 : -1; 956} 957 958/* Implements both the realpath and expand-path operations */ 959static char * 960do_realpath_expand(struct sftp_conn *conn, const char *path, int expand) 961{ 962 struct sshbuf *msg; 963 u_int expected_id, count, id; 964 char *filename, *longname; 965 Attrib a; 966 u_char type; 967 int r; 968 const char *what = "SSH2_FXP_REALPATH"; 969 970 if (expand) 971 what = "expand-path@openssh.com"; 972 if ((msg = sshbuf_new()) == NULL) 973 fatal_f("sshbuf_new failed"); 974 975 expected_id = id = conn->msg_id++; 976 if (expand) { 977 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 978 (r = sshbuf_put_u32(msg, id)) != 0 || 979 (r = sshbuf_put_cstring(msg, 980 "expand-path@openssh.com")) != 0 || 981 (r = sshbuf_put_cstring(msg, path)) != 0) 982 fatal_fr(r, "compose %s", what); 983 send_msg(conn, msg); 984 } else { 985 send_string_request(conn, id, SSH2_FXP_REALPATH, 986 path, strlen(path)); 987 } 988 get_msg(conn, msg); 989 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 990 (r = sshbuf_get_u32(msg, &id)) != 0) 991 fatal_fr(r, "parse"); 992 993 if (id != expected_id) 994 fatal("ID mismatch (%u != %u)", id, expected_id); 995 996 if (type == SSH2_FXP_STATUS) { 997 u_int status; 998 999 if ((r = sshbuf_get_u32(msg, &status)) != 0) 1000 fatal_fr(r, "parse status"); 1001 error("Couldn't canonicalize: %s", fx2txt(status)); 1002 sshbuf_free(msg); 1003 return NULL; 1004 } else if (type != SSH2_FXP_NAME) 1005 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 1006 SSH2_FXP_NAME, type); 1007 1008 if ((r = sshbuf_get_u32(msg, &count)) != 0) 1009 fatal_fr(r, "parse count"); 1010 if (count != 1) 1011 fatal("Got multiple names (%d) from %s", count, what); 1012 1013 if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || 1014 (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || 1015 (r = decode_attrib(msg, &a)) != 0) 1016 fatal_fr(r, "parse filename/attrib"); 1017 1018 debug3("%s %s -> %s", what, path, filename); 1019 1020 free(longname); 1021 1022 sshbuf_free(msg); 1023 1024 return(filename); 1025} 1026 1027char * 1028do_realpath(struct sftp_conn *conn, const char *path) 1029{ 1030 return do_realpath_expand(conn, path, 0); 1031} 1032 1033int 1034can_expand_path(struct sftp_conn *conn) 1035{ 1036 return (conn->exts & SFTP_EXT_PATH_EXPAND) != 0; 1037} 1038 1039char * 1040do_expand_path(struct sftp_conn *conn, const char *path) 1041{ 1042 if (!can_expand_path(conn)) { 1043 debug3_f("no server support, fallback to realpath"); 1044 return do_realpath_expand(conn, path, 0); 1045 } 1046 return do_realpath_expand(conn, path, 1); 1047} 1048 1049int 1050do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, 1051 int force_legacy) 1052{ 1053 struct sshbuf *msg; 1054 u_int status, id; 1055 int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy; 1056 1057 if ((msg = sshbuf_new()) == NULL) 1058 fatal_f("sshbuf_new failed"); 1059 1060 /* Send rename request */ 1061 id = conn->msg_id++; 1062 if (use_ext) { 1063 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1064 (r = sshbuf_put_u32(msg, id)) != 0 || 1065 (r = sshbuf_put_cstring(msg, 1066 "posix-rename@openssh.com")) != 0) 1067 fatal_fr(r, "compose posix-rename"); 1068 } else { 1069 if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 || 1070 (r = sshbuf_put_u32(msg, id)) != 0) 1071 fatal_fr(r, "compose rename"); 1072 } 1073 if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 || 1074 (r = sshbuf_put_cstring(msg, newpath)) != 0) 1075 fatal_fr(r, "compose paths"); 1076 send_msg(conn, msg); 1077 debug3("Sent message %s \"%s\" -> \"%s\"", 1078 use_ext ? "posix-rename@openssh.com" : 1079 "SSH2_FXP_RENAME", oldpath, newpath); 1080 sshbuf_free(msg); 1081 1082 status = get_status(conn, id); 1083 if (status != SSH2_FX_OK) 1084 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, 1085 newpath, fx2txt(status)); 1086 1087 return status == SSH2_FX_OK ? 0 : -1; 1088} 1089 1090int 1091do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) 1092{ 1093 struct sshbuf *msg; 1094 u_int status, id; 1095 int r; 1096 1097 if ((conn->exts & SFTP_EXT_HARDLINK) == 0) { 1098 error("Server does not support hardlink@openssh.com extension"); 1099 return -1; 1100 } 1101 1102 if ((msg = sshbuf_new()) == NULL) 1103 fatal_f("sshbuf_new failed"); 1104 1105 /* Send link request */ 1106 id = conn->msg_id++; 1107 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1108 (r = sshbuf_put_u32(msg, id)) != 0 || 1109 (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || 1110 (r = sshbuf_put_cstring(msg, oldpath)) != 0 || 1111 (r = sshbuf_put_cstring(msg, newpath)) != 0) 1112 fatal_fr(r, "compose"); 1113 send_msg(conn, msg); 1114 debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"", 1115 oldpath, newpath); 1116 sshbuf_free(msg); 1117 1118 status = get_status(conn, id); 1119 if (status != SSH2_FX_OK) 1120 error("Couldn't link file \"%s\" to \"%s\": %s", oldpath, 1121 newpath, fx2txt(status)); 1122 1123 return status == SSH2_FX_OK ? 0 : -1; 1124} 1125 1126int 1127do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) 1128{ 1129 struct sshbuf *msg; 1130 u_int status, id; 1131 int r; 1132 1133 if (conn->version < 3) { 1134 error("This server does not support the symlink operation"); 1135 return(SSH2_FX_OP_UNSUPPORTED); 1136 } 1137 1138 if ((msg = sshbuf_new()) == NULL) 1139 fatal_f("sshbuf_new failed"); 1140 1141 /* Send symlink request */ 1142 id = conn->msg_id++; 1143 if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 || 1144 (r = sshbuf_put_u32(msg, id)) != 0 || 1145 (r = sshbuf_put_cstring(msg, oldpath)) != 0 || 1146 (r = sshbuf_put_cstring(msg, newpath)) != 0) 1147 fatal_fr(r, "compose"); 1148 send_msg(conn, msg); 1149 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 1150 newpath); 1151 sshbuf_free(msg); 1152 1153 status = get_status(conn, id); 1154 if (status != SSH2_FX_OK) 1155 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, 1156 newpath, fx2txt(status)); 1157 1158 return status == SSH2_FX_OK ? 0 : -1; 1159} 1160 1161int 1162do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) 1163{ 1164 struct sshbuf *msg; 1165 u_int status, id; 1166 int r; 1167 1168 /* Silently return if the extension is not supported */ 1169 if ((conn->exts & SFTP_EXT_FSYNC) == 0) 1170 return -1; 1171 1172 /* Send fsync request */ 1173 if ((msg = sshbuf_new()) == NULL) 1174 fatal_f("sshbuf_new failed"); 1175 id = conn->msg_id++; 1176 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1177 (r = sshbuf_put_u32(msg, id)) != 0 || 1178 (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || 1179 (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 1180 fatal_fr(r, "compose"); 1181 send_msg(conn, msg); 1182 debug3("Sent message fsync@openssh.com I:%u", id); 1183 sshbuf_free(msg); 1184 1185 status = get_status(conn, id); 1186 if (status != SSH2_FX_OK) 1187 error("Couldn't sync file: %s", fx2txt(status)); 1188 1189 return status == SSH2_FX_OK ? 0 : -1; 1190} 1191 1192#ifdef notyet 1193char * 1194do_readlink(struct sftp_conn *conn, const char *path) 1195{ 1196 struct sshbuf *msg; 1197 u_int expected_id, count, id; 1198 char *filename, *longname; 1199 Attrib a; 1200 u_char type; 1201 int r; 1202 1203 expected_id = id = conn->msg_id++; 1204 send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path)); 1205 1206 if ((msg = sshbuf_new()) == NULL) 1207 fatal_f("sshbuf_new failed"); 1208 1209 get_msg(conn, msg); 1210 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 1211 (r = sshbuf_get_u32(msg, &id)) != 0) 1212 fatal_fr(r, "parse"); 1213 1214 if (id != expected_id) 1215 fatal("ID mismatch (%u != %u)", id, expected_id); 1216 1217 if (type == SSH2_FXP_STATUS) { 1218 u_int status; 1219 1220 if ((r = sshbuf_get_u32(msg, &status)) != 0) 1221 fatal_fr(r, "parse status"); 1222 error("Couldn't readlink: %s", fx2txt(status)); 1223 sshbuf_free(msg); 1224 return(NULL); 1225 } else if (type != SSH2_FXP_NAME) 1226 fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", 1227 SSH2_FXP_NAME, type); 1228 1229 if ((r = sshbuf_get_u32(msg, &count)) != 0) 1230 fatal_fr(r, "parse count"); 1231 if (count != 1) 1232 fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); 1233 1234 if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || 1235 (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || 1236 (r = decode_attrib(msg, &a)) != 0) 1237 fatal_fr(r, "parse filenames/attrib"); 1238 1239 debug3("SSH_FXP_READLINK %s -> %s", path, filename); 1240 1241 free(longname); 1242 1243 sshbuf_free(msg); 1244 1245 return filename; 1246} 1247#endif 1248 1249int 1250do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, 1251 int quiet) 1252{ 1253 struct sshbuf *msg; 1254 u_int id; 1255 int r; 1256 1257 if ((conn->exts & SFTP_EXT_STATVFS) == 0) { 1258 error("Server does not support statvfs@openssh.com extension"); 1259 return -1; 1260 } 1261 1262 id = conn->msg_id++; 1263 1264 if ((msg = sshbuf_new()) == NULL) 1265 fatal_f("sshbuf_new failed"); 1266 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1267 (r = sshbuf_put_u32(msg, id)) != 0 || 1268 (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || 1269 (r = sshbuf_put_cstring(msg, path)) != 0) 1270 fatal_fr(r, "compose"); 1271 send_msg(conn, msg); 1272 sshbuf_free(msg); 1273 1274 return get_decode_statvfs(conn, st, id, quiet); 1275} 1276 1277#ifdef notyet 1278int 1279do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, 1280 struct sftp_statvfs *st, int quiet) 1281{ 1282 struct sshbuf *msg; 1283 u_int id; 1284 1285 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { 1286 error("Server does not support fstatvfs@openssh.com extension"); 1287 return -1; 1288 } 1289 1290 id = conn->msg_id++; 1291 1292 if ((msg = sshbuf_new()) == NULL) 1293 fatal_f("sshbuf_new failed"); 1294 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1295 (r = sshbuf_put_u32(msg, id)) != 0 || 1296 (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || 1297 (r = sshbuf_put_string(msg, handle, handle_len)) != 0) 1298 fatal_fr(r, "compose"); 1299 send_msg(conn, msg); 1300 sshbuf_free(msg); 1301 1302 return get_decode_statvfs(conn, st, id, quiet); 1303} 1304#endif 1305 1306int 1307do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a) 1308{ 1309 struct sshbuf *msg; 1310 u_int status, id; 1311 int r; 1312 1313 if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) { 1314 error("Server does not support lsetstat@openssh.com extension"); 1315 return -1; 1316 } 1317 1318 id = conn->msg_id++; 1319 if ((msg = sshbuf_new()) == NULL) 1320 fatal_f("sshbuf_new failed"); 1321 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1322 (r = sshbuf_put_u32(msg, id)) != 0 || 1323 (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 || 1324 (r = sshbuf_put_cstring(msg, path)) != 0 || 1325 (r = encode_attrib(msg, a)) != 0) 1326 fatal_fr(r, "compose"); 1327 send_msg(conn, msg); 1328 sshbuf_free(msg); 1329 1330 status = get_status(conn, id); 1331 if (status != SSH2_FX_OK) 1332 error("Couldn't setstat on \"%s\": %s", path, 1333 fx2txt(status)); 1334 1335 return status == SSH2_FX_OK ? 0 : -1; 1336} 1337 1338static void 1339send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, 1340 u_int len, const u_char *handle, u_int handle_len) 1341{ 1342 struct sshbuf *msg; 1343 int r; 1344 1345 if ((msg = sshbuf_new()) == NULL) 1346 fatal_f("sshbuf_new failed"); 1347 if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 || 1348 (r = sshbuf_put_u32(msg, id)) != 0 || 1349 (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || 1350 (r = sshbuf_put_u64(msg, offset)) != 0 || 1351 (r = sshbuf_put_u32(msg, len)) != 0) 1352 fatal_fr(r, "compose"); 1353 send_msg(conn, msg); 1354 sshbuf_free(msg); 1355} 1356 1357static int 1358send_open(struct sftp_conn *conn, const char *path, const char *tag, 1359 u_int openmode, Attrib *a, u_char **handlep, size_t *handle_lenp) 1360{ 1361 Attrib junk; 1362 u_char *handle; 1363 size_t handle_len; 1364 struct sshbuf *msg; 1365 int r; 1366 u_int id; 1367 1368 *handlep = NULL; 1369 *handle_lenp = 0; 1370 1371 if (a == NULL) { 1372 attrib_clear(&junk); /* Send empty attributes */ 1373 a = &junk; 1374 } 1375 /* Send open request */ 1376 if ((msg = sshbuf_new()) == NULL) 1377 fatal_f("sshbuf_new failed"); 1378 id = conn->msg_id++; 1379 if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || 1380 (r = sshbuf_put_u32(msg, id)) != 0 || 1381 (r = sshbuf_put_cstring(msg, path)) != 0 || 1382 (r = sshbuf_put_u32(msg, openmode)) != 0 || 1383 (r = encode_attrib(msg, a)) != 0) 1384 fatal_fr(r, "compose %s open", tag); 1385 send_msg(conn, msg); 1386 sshbuf_free(msg); 1387 debug3("Sent %s message SSH2_FXP_OPEN I:%u P:%s M:0x%04x", 1388 tag, id, path, openmode); 1389 if ((handle = get_handle(conn, id, &handle_len, 1390 "%s open(\"%s\")", tag, path)) == NULL) 1391 return -1; 1392 /* success */ 1393 *handlep = handle; 1394 *handle_lenp = handle_len; 1395 return 0; 1396} 1397 1398static const char * 1399progress_meter_path(const char *path) 1400{ 1401 const char *progresspath; 1402 1403 if ((progresspath = strrchr(path, '/')) == NULL) 1404 return path; 1405 progresspath++; 1406 if (*progresspath == '\0') 1407 return path; 1408 return progresspath; 1409} 1410 1411int 1412do_download(struct sftp_conn *conn, const char *remote_path, 1413 const char *local_path, Attrib *a, int preserve_flag, int resume_flag, 1414 int fsync_flag) 1415{ 1416 struct sshbuf *msg; 1417 u_char *handle; 1418 int local_fd = -1, write_error; 1419 int read_error, write_errno, lmodified = 0, reordered = 0, r; 1420 u_int64_t offset = 0, size, highwater; 1421 u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK; 1422 off_t progress_counter; 1423 size_t handle_len; 1424 struct stat st; 1425 struct requests requests; 1426 struct request *req; 1427 u_char type; 1428 1429 status = -1; 1430 TAILQ_INIT(&requests); 1431 1432 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) 1433 return -1; 1434 1435 /* Do not preserve set[ug]id here, as we do not preserve ownership */ 1436 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 1437 mode = a->perm & 0777; 1438 else 1439 mode = 0666; 1440 1441 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 1442 (!S_ISREG(a->perm))) { 1443 error("Cannot download non-regular file: %s", remote_path); 1444 return(-1); 1445 } 1446 1447 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) 1448 size = a->size; 1449 else 1450 size = 0; 1451 1452 buflen = conn->download_buflen; 1453 1454 /* Send open request */ 1455 if (send_open(conn, remote_path, "remote", SSH2_FXF_READ, NULL, 1456 &handle, &handle_len) != 0) 1457 return -1; 1458 1459 local_fd = open(local_path, 1460 O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR); 1461 if (local_fd == -1) { 1462 error("Couldn't open local file \"%s\" for writing: %s", 1463 local_path, strerror(errno)); 1464 goto fail; 1465 } 1466 offset = highwater = 0; 1467 if (resume_flag) { 1468 if (fstat(local_fd, &st) == -1) { 1469 error("Unable to stat local file \"%s\": %s", 1470 local_path, strerror(errno)); 1471 goto fail; 1472 } 1473 if (st.st_size < 0) { 1474 error("\"%s\" has negative size", local_path); 1475 goto fail; 1476 } 1477 if ((u_int64_t)st.st_size > size) { 1478 error("Unable to resume download of \"%s\": " 1479 "local file is larger than remote", local_path); 1480 fail: 1481 do_close(conn, handle, handle_len); 1482 free(handle); 1483 if (local_fd != -1) 1484 close(local_fd); 1485 return -1; 1486 } 1487 offset = highwater = st.st_size; 1488 } 1489 1490 /* Read from remote and write to local */ 1491 write_error = read_error = write_errno = num_req = 0; 1492 max_req = 1; 1493 progress_counter = offset; 1494 1495 if (showprogress && size != 0) { 1496 start_progress_meter(progress_meter_path(remote_path), 1497 size, &progress_counter); 1498 } 1499 1500 if ((msg = sshbuf_new()) == NULL) 1501 fatal_f("sshbuf_new failed"); 1502 1503 while (num_req > 0 || max_req > 0) { 1504 u_char *data; 1505 size_t len; 1506 1507 /* 1508 * Simulate EOF on interrupt: stop sending new requests and 1509 * allow outstanding requests to drain gracefully 1510 */ 1511 if (interrupted) { 1512 if (num_req == 0) /* If we haven't started yet... */ 1513 break; 1514 max_req = 0; 1515 } 1516 1517 /* Send some more requests */ 1518 while (num_req < max_req) { 1519 debug3("Request range %llu -> %llu (%d/%d)", 1520 (unsigned long long)offset, 1521 (unsigned long long)offset + buflen - 1, 1522 num_req, max_req); 1523 req = request_enqueue(&requests, conn->msg_id++, 1524 buflen, offset); 1525 offset += buflen; 1526 num_req++; 1527 send_read_request(conn, req->id, req->offset, 1528 req->len, handle, handle_len); 1529 } 1530 1531 sshbuf_reset(msg); 1532 get_msg(conn, msg); 1533 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 1534 (r = sshbuf_get_u32(msg, &id)) != 0) 1535 fatal_fr(r, "parse"); 1536 debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 1537 1538 /* Find the request in our queue */ 1539 if ((req = request_find(&requests, id)) == NULL) 1540 fatal("Unexpected reply %u", id); 1541 1542 switch (type) { 1543 case SSH2_FXP_STATUS: 1544 if ((r = sshbuf_get_u32(msg, &status)) != 0) 1545 fatal_fr(r, "parse status"); 1546 if (status != SSH2_FX_EOF) 1547 read_error = 1; 1548 max_req = 0; 1549 TAILQ_REMOVE(&requests, req, tq); 1550 free(req); 1551 num_req--; 1552 break; 1553 case SSH2_FXP_DATA: 1554 if ((r = sshbuf_get_string(msg, &data, &len)) != 0) 1555 fatal_fr(r, "parse data"); 1556 debug3("Received data %llu -> %llu", 1557 (unsigned long long)req->offset, 1558 (unsigned long long)req->offset + len - 1); 1559 if (len > req->len) 1560 fatal("Received more data than asked for " 1561 "%zu > %zu", len, req->len); 1562 lmodified = 1; 1563 if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || 1564 atomicio(vwrite, local_fd, data, len) != len) && 1565 !write_error) { 1566 write_errno = errno; 1567 write_error = 1; 1568 max_req = 0; 1569 } 1570 else if (!reordered && req->offset <= highwater) 1571 highwater = req->offset + len; 1572 else if (!reordered && req->offset > highwater) 1573 reordered = 1; 1574 progress_counter += len; 1575 free(data); 1576 1577 if (len == req->len) { 1578 TAILQ_REMOVE(&requests, req, tq); 1579 free(req); 1580 num_req--; 1581 } else { 1582 /* Resend the request for the missing data */ 1583 debug3("Short data block, re-requesting " 1584 "%llu -> %llu (%2d)", 1585 (unsigned long long)req->offset + len, 1586 (unsigned long long)req->offset + 1587 req->len - 1, num_req); 1588 req->id = conn->msg_id++; 1589 req->len -= len; 1590 req->offset += len; 1591 send_read_request(conn, req->id, 1592 req->offset, req->len, handle, handle_len); 1593 /* Reduce the request size */ 1594 if (len < buflen) 1595 buflen = MAXIMUM(MIN_READ_SIZE, len); 1596 } 1597 if (max_req > 0) { /* max_req = 0 iff EOF received */ 1598 if (size > 0 && offset > size) { 1599 /* Only one request at a time 1600 * after the expected EOF */ 1601 debug3("Finish at %llu (%2d)", 1602 (unsigned long long)offset, 1603 num_req); 1604 max_req = 1; 1605 } else if (max_req < conn->num_requests) { 1606 ++max_req; 1607 } 1608 } 1609 break; 1610 default: 1611 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 1612 SSH2_FXP_DATA, type); 1613 } 1614 } 1615 1616 if (showprogress && size) 1617 stop_progress_meter(); 1618 1619 /* Sanity check */ 1620 if (TAILQ_FIRST(&requests) != NULL) 1621 fatal("Transfer complete, but requests still in queue"); 1622 /* Truncate at highest contiguous point to avoid holes on interrupt */ 1623 if (read_error || write_error || interrupted) { 1624 if (reordered && resume_flag) { 1625 error("Unable to resume download of \"%s\": " 1626 "server reordered requests", local_path); 1627 } 1628 debug("truncating at %llu", (unsigned long long)highwater); 1629 if (ftruncate(local_fd, highwater) == -1) 1630 error("ftruncate \"%s\": %s", local_path, 1631 strerror(errno)); 1632 } 1633 if (read_error) { 1634 error("Couldn't read from remote file \"%s\" : %s", 1635 remote_path, fx2txt(status)); 1636 status = -1; 1637 do_close(conn, handle, handle_len); 1638 } else if (write_error) { 1639 error("Couldn't write to \"%s\": %s", local_path, 1640 strerror(write_errno)); 1641 status = SSH2_FX_FAILURE; 1642 do_close(conn, handle, handle_len); 1643 } else { 1644 if (do_close(conn, handle, handle_len) != 0 || interrupted) 1645 status = SSH2_FX_FAILURE; 1646 else 1647 status = SSH2_FX_OK; 1648 /* Override umask and utimes if asked */ 1649 if (preserve_flag && fchmod(local_fd, mode) == -1) 1650 error("Couldn't set mode on \"%s\": %s", local_path, 1651 strerror(errno)); 1652 if (preserve_flag && 1653 (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { 1654 struct timeval tv[2]; 1655 tv[0].tv_sec = a->atime; 1656 tv[1].tv_sec = a->mtime; 1657 tv[0].tv_usec = tv[1].tv_usec = 0; 1658 if (utimes(local_path, tv) == -1) 1659 error("Can't set times on \"%s\": %s", 1660 local_path, strerror(errno)); 1661 } 1662 if (resume_flag && !lmodified) 1663 logit("File \"%s\" was not modified", local_path); 1664 else if (fsync_flag) { 1665 debug("syncing \"%s\"", local_path); 1666 if (fsync(local_fd) == -1) 1667 error("Couldn't sync file \"%s\": %s", 1668 local_path, strerror(errno)); 1669 } 1670 } 1671 close(local_fd); 1672 sshbuf_free(msg); 1673 free(handle); 1674 1675 return status == SSH2_FX_OK ? 0 : -1; 1676} 1677 1678static int 1679download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, 1680 int depth, Attrib *dirattrib, int preserve_flag, int print_flag, 1681 int resume_flag, int fsync_flag, int follow_link_flag) 1682{ 1683 int i, ret = 0; 1684 SFTP_DIRENT **dir_entries; 1685 char *filename, *new_src = NULL, *new_dst = NULL; 1686 mode_t mode = 0777, tmpmode = mode; 1687 1688 if (depth >= MAX_DIR_DEPTH) { 1689 error("Maximum directory depth exceeded: %d levels", depth); 1690 return -1; 1691 } 1692 1693 if (dirattrib == NULL && 1694 (dirattrib = do_stat(conn, src, 1)) == NULL) { 1695 error("Unable to stat remote directory \"%s\"", src); 1696 return -1; 1697 } 1698 if (!S_ISDIR(dirattrib->perm)) { 1699 error("\"%s\" is not a directory", src); 1700 return -1; 1701 } 1702 if (print_flag && print_flag != SFTP_PROGRESS_ONLY) 1703 mprintf("Retrieving %s\n", src); 1704 1705 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 1706 mode = dirattrib->perm & 01777; 1707 tmpmode = mode | (S_IWUSR|S_IXUSR); 1708 } else { 1709 debug("Server did not send permissions for " 1710 "directory \"%s\"", dst); 1711 } 1712 1713 if (mkdir(dst, tmpmode) == -1 && errno != EEXIST) { 1714 error("mkdir %s: %s", dst, strerror(errno)); 1715 return -1; 1716 } 1717 1718 if (do_readdir(conn, src, &dir_entries) == -1) { 1719 error("%s: Failed to get directory contents", src); 1720 return -1; 1721 } 1722 1723 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { 1724 free(new_dst); 1725 free(new_src); 1726 1727 filename = dir_entries[i]->filename; 1728 new_dst = path_append(dst, filename); 1729 new_src = path_append(src, filename); 1730 1731 if (S_ISDIR(dir_entries[i]->a.perm)) { 1732 if (strcmp(filename, ".") == 0 || 1733 strcmp(filename, "..") == 0) 1734 continue; 1735 if (download_dir_internal(conn, new_src, new_dst, 1736 depth + 1, &(dir_entries[i]->a), preserve_flag, 1737 print_flag, resume_flag, 1738 fsync_flag, follow_link_flag) == -1) 1739 ret = -1; 1740 } else if (S_ISREG(dir_entries[i]->a.perm) || 1741 (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { 1742 /* 1743 * If this is a symlink then don't send the link's 1744 * Attrib. do_download() will do a FXP_STAT operation 1745 * and get the link target's attributes. 1746 */ 1747 if (do_download(conn, new_src, new_dst, 1748 S_ISLNK(dir_entries[i]->a.perm) ? NULL : 1749 &(dir_entries[i]->a), 1750 preserve_flag, resume_flag, fsync_flag) == -1) { 1751 error("Download of file %s to %s failed", 1752 new_src, new_dst); 1753 ret = -1; 1754 } 1755 } else 1756 logit("%s: not a regular file\n", new_src); 1757 1758 } 1759 free(new_dst); 1760 free(new_src); 1761 1762 if (preserve_flag) { 1763 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1764 struct timeval tv[2]; 1765 tv[0].tv_sec = dirattrib->atime; 1766 tv[1].tv_sec = dirattrib->mtime; 1767 tv[0].tv_usec = tv[1].tv_usec = 0; 1768 if (utimes(dst, tv) == -1) 1769 error("Can't set times on \"%s\": %s", 1770 dst, strerror(errno)); 1771 } else 1772 debug("Server did not send times for directory " 1773 "\"%s\"", dst); 1774 } 1775 1776 if (mode != tmpmode && chmod(dst, mode) == -1) 1777 error("Can't set final mode on \"%s\": %s", dst, 1778 strerror(errno)); 1779 1780 free_sftp_dirents(dir_entries); 1781 1782 return ret; 1783} 1784 1785int 1786download_dir(struct sftp_conn *conn, const char *src, const char *dst, 1787 Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, 1788 int fsync_flag, int follow_link_flag) 1789{ 1790 char *src_canon; 1791 int ret; 1792 1793 if ((src_canon = do_realpath(conn, src)) == NULL) { 1794 error("Unable to canonicalize path \"%s\"", src); 1795 return -1; 1796 } 1797 1798 ret = download_dir_internal(conn, src_canon, dst, 0, 1799 dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag, 1800 follow_link_flag); 1801 free(src_canon); 1802 return ret; 1803} 1804 1805int 1806do_upload(struct sftp_conn *conn, const char *local_path, 1807 const char *remote_path, int preserve_flag, int resume, int fsync_flag) 1808{ 1809 int r, local_fd; 1810 u_int status = SSH2_FX_OK; 1811 u_int id; 1812 u_char type; 1813 off_t offset, progress_counter; 1814 u_char *handle, *data; 1815 struct sshbuf *msg; 1816 struct stat sb; 1817 Attrib a, *c = NULL; 1818 u_int32_t startid; 1819 u_int32_t ackid; 1820 struct request *ack = NULL; 1821 struct requests acks; 1822 size_t handle_len; 1823 1824 TAILQ_INIT(&acks); 1825 1826 if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { 1827 error("Couldn't open local file \"%s\" for reading: %s", 1828 local_path, strerror(errno)); 1829 return(-1); 1830 } 1831 if (fstat(local_fd, &sb) == -1) { 1832 error("Couldn't fstat local file \"%s\": %s", 1833 local_path, strerror(errno)); 1834 close(local_fd); 1835 return(-1); 1836 } 1837 if (!S_ISREG(sb.st_mode)) { 1838 error("%s is not a regular file", local_path); 1839 close(local_fd); 1840 return(-1); 1841 } 1842 stat_to_attrib(&sb, &a); 1843 1844 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 1845 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 1846 a.perm &= 0777; 1847 if (!preserve_flag) 1848 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 1849 1850 if (resume) { 1851 /* Get remote file size if it exists */ 1852 if ((c = do_stat(conn, remote_path, 0)) == NULL) { 1853 close(local_fd); 1854 return -1; 1855 } 1856 1857 if ((off_t)c->size >= sb.st_size) { 1858 error("destination file bigger or same size as " 1859 "source file"); 1860 close(local_fd); 1861 return -1; 1862 } 1863 1864 if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { 1865 close(local_fd); 1866 return -1; 1867 } 1868 } 1869 1870 /* Send open request */ 1871 if (send_open(conn, remote_path, "dest", SSH2_FXF_WRITE|SSH2_FXF_CREAT| 1872 (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC), 1873 &a, &handle, &handle_len) != 0) { 1874 close(local_fd); 1875 return -1; 1876 } 1877 1878 id = conn->msg_id; 1879 startid = ackid = id + 1; 1880 data = xmalloc(conn->upload_buflen); 1881 1882 /* Read from local and write to remote */ 1883 offset = progress_counter = (resume ? c->size : 0); 1884 if (showprogress) { 1885 start_progress_meter(progress_meter_path(local_path), 1886 sb.st_size, &progress_counter); 1887 } 1888 1889 if ((msg = sshbuf_new()) == NULL) 1890 fatal_f("sshbuf_new failed"); 1891 for (;;) { 1892 int len; 1893 1894 /* 1895 * Can't use atomicio here because it returns 0 on EOF, 1896 * thus losing the last block of the file. 1897 * Simulate an EOF on interrupt, allowing ACKs from the 1898 * server to drain. 1899 */ 1900 if (interrupted || status != SSH2_FX_OK) 1901 len = 0; 1902 else do 1903 len = read(local_fd, data, conn->upload_buflen); 1904 while ((len == -1) && (errno == EINTR || errno == EAGAIN)); 1905 1906 if (len == -1) 1907 fatal("Couldn't read from \"%s\": %s", local_path, 1908 strerror(errno)); 1909 1910 if (len != 0) { 1911 ack = request_enqueue(&acks, ++id, len, offset); 1912 sshbuf_reset(msg); 1913 if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || 1914 (r = sshbuf_put_u32(msg, ack->id)) != 0 || 1915 (r = sshbuf_put_string(msg, handle, 1916 handle_len)) != 0 || 1917 (r = sshbuf_put_u64(msg, offset)) != 0 || 1918 (r = sshbuf_put_string(msg, data, len)) != 0) 1919 fatal_fr(r, "compose"); 1920 send_msg(conn, msg); 1921 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 1922 id, (unsigned long long)offset, len); 1923 } else if (TAILQ_FIRST(&acks) == NULL) 1924 break; 1925 1926 if (ack == NULL) 1927 fatal("Unexpected ACK %u", id); 1928 1929 if (id == startid || len == 0 || 1930 id - ackid >= conn->num_requests) { 1931 u_int rid; 1932 1933 sshbuf_reset(msg); 1934 get_msg(conn, msg); 1935 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 1936 (r = sshbuf_get_u32(msg, &rid)) != 0) 1937 fatal_fr(r, "parse"); 1938 1939 if (type != SSH2_FXP_STATUS) 1940 fatal("Expected SSH2_FXP_STATUS(%d) packet, " 1941 "got %d", SSH2_FXP_STATUS, type); 1942 1943 if ((r = sshbuf_get_u32(msg, &status)) != 0) 1944 fatal_fr(r, "parse status"); 1945 debug3("SSH2_FXP_STATUS %u", status); 1946 1947 /* Find the request in our queue */ 1948 if ((ack = request_find(&acks, rid)) == NULL) 1949 fatal("Can't find request for ID %u", rid); 1950 TAILQ_REMOVE(&acks, ack, tq); 1951 debug3("In write loop, ack for %u %zu bytes at %lld", 1952 ack->id, ack->len, (unsigned long long)ack->offset); 1953 ++ackid; 1954 progress_counter += ack->len; 1955 free(ack); 1956 } 1957 offset += len; 1958 if (offset < 0) 1959 fatal_f("offset < 0"); 1960 } 1961 sshbuf_free(msg); 1962 1963 if (showprogress) 1964 stop_progress_meter(); 1965 free(data); 1966 1967 if (status != SSH2_FX_OK) { 1968 error("Couldn't write to remote file \"%s\": %s", 1969 remote_path, fx2txt(status)); 1970 status = SSH2_FX_FAILURE; 1971 } 1972 1973 if (close(local_fd) == -1) { 1974 error("Couldn't close local file \"%s\": %s", local_path, 1975 strerror(errno)); 1976 status = SSH2_FX_FAILURE; 1977 } 1978 1979 /* Override umask and utimes if asked */ 1980 if (preserve_flag) 1981 do_fsetstat(conn, handle, handle_len, &a); 1982 1983 if (fsync_flag) 1984 (void)do_fsync(conn, handle, handle_len); 1985 1986 if (do_close(conn, handle, handle_len) != 0) 1987 status = SSH2_FX_FAILURE; 1988 1989 free(handle); 1990 1991 return status == SSH2_FX_OK ? 0 : -1; 1992} 1993 1994static int 1995upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, 1996 int depth, int preserve_flag, int print_flag, int resume, int fsync_flag, 1997 int follow_link_flag) 1998{ 1999 int ret = 0; 2000 DIR *dirp; 2001 struct dirent *dp; 2002 char *filename, *new_src = NULL, *new_dst = NULL; 2003 struct stat sb; 2004 Attrib a, *dirattrib; 2005 u_int32_t saved_perm; 2006 2007 if (depth >= MAX_DIR_DEPTH) { 2008 error("Maximum directory depth exceeded: %d levels", depth); 2009 return -1; 2010 } 2011 2012 if (stat(src, &sb) == -1) { 2013 error("Couldn't stat directory \"%s\": %s", 2014 src, strerror(errno)); 2015 return -1; 2016 } 2017 if (!S_ISDIR(sb.st_mode)) { 2018 error("\"%s\" is not a directory", src); 2019 return -1; 2020 } 2021 if (print_flag && print_flag != SFTP_PROGRESS_ONLY) 2022 mprintf("Entering %s\n", src); 2023 2024 stat_to_attrib(&sb, &a); 2025 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 2026 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 2027 a.perm &= 01777; 2028 if (!preserve_flag) 2029 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 2030 2031 /* 2032 * sftp lacks a portable status value to match errno EEXIST, 2033 * so if we get a failure back then we must check whether 2034 * the path already existed and is a directory. Ensure we can 2035 * write to the directory we create for the duration of the transfer. 2036 */ 2037 saved_perm = a.perm; 2038 a.perm |= (S_IWUSR|S_IXUSR); 2039 if (do_mkdir(conn, dst, &a, 0) != 0) { 2040 if ((dirattrib = do_stat(conn, dst, 0)) == NULL) 2041 return -1; 2042 if (!S_ISDIR(dirattrib->perm)) { 2043 error("\"%s\" exists but is not a directory", dst); 2044 return -1; 2045 } 2046 } 2047 a.perm = saved_perm; 2048 2049 if ((dirp = opendir(src)) == NULL) { 2050 error("Failed to open dir \"%s\": %s", src, strerror(errno)); 2051 return -1; 2052 } 2053 2054 while (((dp = readdir(dirp)) != NULL) && !interrupted) { 2055 if (dp->d_ino == 0) 2056 continue; 2057 free(new_dst); 2058 free(new_src); 2059 filename = dp->d_name; 2060 new_dst = path_append(dst, filename); 2061 new_src = path_append(src, filename); 2062 2063 if (lstat(new_src, &sb) == -1) { 2064 logit("%s: lstat failed: %s", filename, 2065 strerror(errno)); 2066 ret = -1; 2067 } else if (S_ISDIR(sb.st_mode)) { 2068 if (strcmp(filename, ".") == 0 || 2069 strcmp(filename, "..") == 0) 2070 continue; 2071 2072 if (upload_dir_internal(conn, new_src, new_dst, 2073 depth + 1, preserve_flag, print_flag, resume, 2074 fsync_flag, follow_link_flag) == -1) 2075 ret = -1; 2076 } else if (S_ISREG(sb.st_mode) || 2077 (follow_link_flag && S_ISLNK(sb.st_mode))) { 2078 if (do_upload(conn, new_src, new_dst, 2079 preserve_flag, resume, fsync_flag) == -1) { 2080 error("Uploading of file %s to %s failed!", 2081 new_src, new_dst); 2082 ret = -1; 2083 } 2084 } else 2085 logit("%s: not a regular file\n", filename); 2086 } 2087 free(new_dst); 2088 free(new_src); 2089 2090 do_setstat(conn, dst, &a); 2091 2092 (void) closedir(dirp); 2093 return ret; 2094} 2095 2096int 2097upload_dir(struct sftp_conn *conn, const char *src, const char *dst, 2098 int preserve_flag, int print_flag, int resume, int fsync_flag, 2099 int follow_link_flag) 2100{ 2101 char *dst_canon; 2102 int ret; 2103 2104 if ((dst_canon = do_realpath(conn, dst)) == NULL) { 2105 error("Unable to canonicalize path \"%s\"", dst); 2106 return -1; 2107 } 2108 2109 ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, 2110 print_flag, resume, fsync_flag, follow_link_flag); 2111 2112 free(dst_canon); 2113 return ret; 2114} 2115 2116static void 2117handle_dest_replies(struct sftp_conn *to, const char *to_path, int synchronous, 2118 u_int *nreqsp, int *write_errorp) 2119{ 2120 struct sshbuf *msg; 2121 u_char type; 2122 u_int id, status; 2123 int r; 2124 struct pollfd pfd; 2125 2126 if ((msg = sshbuf_new()) == NULL) 2127 fatal_f("sshbuf_new failed"); 2128 2129 /* Try to eat replies from the upload side */ 2130 while (*nreqsp > 0) { 2131 debug3_f("%u outstanding replies", *nreqsp); 2132 if (!synchronous) { 2133 /* Bail out if no data is ready to be read */ 2134 pfd.fd = to->fd_in; 2135 pfd.events = POLLIN; 2136 if ((r = poll(&pfd, 1, 0)) == -1) { 2137 if (errno == EINTR) 2138 break; 2139 fatal_f("poll: %s", strerror(errno)); 2140 } else if (r == 0) 2141 break; /* fd not ready */ 2142 } 2143 sshbuf_reset(msg); 2144 get_msg(to, msg); 2145 2146 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 2147 (r = sshbuf_get_u32(msg, &id)) != 0) 2148 fatal_fr(r, "dest parse"); 2149 debug3("Received dest reply T:%u I:%u R:%u", type, id, *nreqsp); 2150 if (type != SSH2_FXP_STATUS) { 2151 fatal_f("Expected SSH2_FXP_STATUS(%d) packet, got %d", 2152 SSH2_FXP_STATUS, type); 2153 } 2154 if ((r = sshbuf_get_u32(msg, &status)) != 0) 2155 fatal_fr(r, "parse dest status"); 2156 debug3("dest SSH2_FXP_STATUS %u", status); 2157 if (status != SSH2_FX_OK) { 2158 /* record first error */ 2159 if (*write_errorp == 0) 2160 *write_errorp = status; 2161 } 2162 /* 2163 * XXX this doesn't do full reply matching like do_upload and 2164 * so cannot gracefully truncate terminated uploads at a 2165 * high-water mark. ATM the only caller of this function (scp) 2166 * doesn't support transfer resumption, so this doesn't matter 2167 * a whole lot. 2168 * 2169 * To be safe, do_crossload truncates the destination file to 2170 * zero length on upload failure, since we can't trust the 2171 * server not to have reordered replies that could have 2172 * inserted holes where none existed in the source file. 2173 * 2174 * XXX we could get a more accutate progress bar if we updated 2175 * the counter based on the reply from the destination... 2176 */ 2177 (*nreqsp)--; 2178 } 2179 debug3_f("done: %u outstanding replies", *nreqsp); 2180} 2181 2182int 2183do_crossload(struct sftp_conn *from, struct sftp_conn *to, 2184 const char *from_path, const char *to_path, 2185 Attrib *a, int preserve_flag) 2186{ 2187 struct sshbuf *msg; 2188 int write_error, read_error, r; 2189 u_int64_t offset = 0, size; 2190 u_int id, buflen, num_req, max_req, status = SSH2_FX_OK; 2191 u_int num_upload_req; 2192 off_t progress_counter; 2193 u_char *from_handle, *to_handle; 2194 size_t from_handle_len, to_handle_len; 2195 struct requests requests; 2196 struct request *req; 2197 u_char type; 2198 2199 TAILQ_INIT(&requests); 2200 2201 if (a == NULL && (a = do_stat(from, from_path, 0)) == NULL) 2202 return -1; 2203 2204 if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && 2205 (!S_ISREG(a->perm))) { 2206 error("Cannot download non-regular file: %s", from_path); 2207 return(-1); 2208 } 2209 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) 2210 size = a->size; 2211 else 2212 size = 0; 2213 2214 buflen = from->download_buflen; 2215 if (buflen > to->upload_buflen) 2216 buflen = to->upload_buflen; 2217 2218 /* Send open request to read side */ 2219 if (send_open(from, from_path, "origin", SSH2_FXF_READ, NULL, 2220 &from_handle, &from_handle_len) != 0) 2221 return -1; 2222 2223 /* Send open request to write side */ 2224 a->flags &= ~SSH2_FILEXFER_ATTR_SIZE; 2225 a->flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 2226 a->perm &= 0777; 2227 if (!preserve_flag) 2228 a->flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; 2229 if (send_open(to, to_path, "dest", 2230 SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, 2231 &to_handle, &to_handle_len) != 0) { 2232 do_close(from, from_handle, from_handle_len); 2233 return -1; 2234 } 2235 2236 /* Read from remote "from" and write to remote "to" */ 2237 offset = 0; 2238 write_error = read_error = num_req = num_upload_req = 0; 2239 max_req = 1; 2240 progress_counter = 0; 2241 2242 if (showprogress && size != 0) { 2243 start_progress_meter(progress_meter_path(from_path), 2244 size, &progress_counter); 2245 } 2246 if ((msg = sshbuf_new()) == NULL) 2247 fatal_f("sshbuf_new failed"); 2248 while (num_req > 0 || max_req > 0) { 2249 u_char *data; 2250 size_t len; 2251 2252 /* 2253 * Simulate EOF on interrupt: stop sending new requests and 2254 * allow outstanding requests to drain gracefully 2255 */ 2256 if (interrupted) { 2257 if (num_req == 0) /* If we haven't started yet... */ 2258 break; 2259 max_req = 0; 2260 } 2261 2262 /* Send some more requests */ 2263 while (num_req < max_req) { 2264 debug3("Request range %llu -> %llu (%d/%d)", 2265 (unsigned long long)offset, 2266 (unsigned long long)offset + buflen - 1, 2267 num_req, max_req); 2268 req = request_enqueue(&requests, from->msg_id++, 2269 buflen, offset); 2270 offset += buflen; 2271 num_req++; 2272 send_read_request(from, req->id, req->offset, 2273 req->len, from_handle, from_handle_len); 2274 } 2275 2276 /* Try to eat replies from the upload side (nonblocking) */ 2277 handle_dest_replies(to, to_path, 0, 2278 &num_upload_req, &write_error); 2279 2280 sshbuf_reset(msg); 2281 get_msg(from, msg); 2282 if ((r = sshbuf_get_u8(msg, &type)) != 0 || 2283 (r = sshbuf_get_u32(msg, &id)) != 0) 2284 fatal_fr(r, "parse"); 2285 debug3("Received origin reply T:%u I:%u R:%d", 2286 type, id, max_req); 2287 2288 /* Find the request in our queue */ 2289 if ((req = request_find(&requests, id)) == NULL) 2290 fatal("Unexpected reply %u", id); 2291 2292 switch (type) { 2293 case SSH2_FXP_STATUS: 2294 if ((r = sshbuf_get_u32(msg, &status)) != 0) 2295 fatal_fr(r, "parse status"); 2296 if (status != SSH2_FX_EOF) 2297 read_error = 1; 2298 max_req = 0; 2299 TAILQ_REMOVE(&requests, req, tq); 2300 free(req); 2301 num_req--; 2302 break; 2303 case SSH2_FXP_DATA: 2304 if ((r = sshbuf_get_string(msg, &data, &len)) != 0) 2305 fatal_fr(r, "parse data"); 2306 debug3("Received data %llu -> %llu", 2307 (unsigned long long)req->offset, 2308 (unsigned long long)req->offset + len - 1); 2309 if (len > req->len) 2310 fatal("Received more data than asked for " 2311 "%zu > %zu", len, req->len); 2312 2313 /* Write this chunk out to the destination */ 2314 sshbuf_reset(msg); 2315 if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || 2316 (r = sshbuf_put_u32(msg, to->msg_id++)) != 0 || 2317 (r = sshbuf_put_string(msg, to_handle, 2318 to_handle_len)) != 0 || 2319 (r = sshbuf_put_u64(msg, req->offset)) != 0 || 2320 (r = sshbuf_put_string(msg, data, len)) != 0) 2321 fatal_fr(r, "compose write"); 2322 send_msg(to, msg); 2323 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%zu", 2324 id, (unsigned long long)offset, len); 2325 num_upload_req++; 2326 progress_counter += len; 2327 free(data); 2328 2329 if (len == req->len) { 2330 TAILQ_REMOVE(&requests, req, tq); 2331 free(req); 2332 num_req--; 2333 } else { 2334 /* Resend the request for the missing data */ 2335 debug3("Short data block, re-requesting " 2336 "%llu -> %llu (%2d)", 2337 (unsigned long long)req->offset + len, 2338 (unsigned long long)req->offset + 2339 req->len - 1, num_req); 2340 req->id = from->msg_id++; 2341 req->len -= len; 2342 req->offset += len; 2343 send_read_request(from, req->id, 2344 req->offset, req->len, 2345 from_handle, from_handle_len); 2346 /* Reduce the request size */ 2347 if (len < buflen) 2348 buflen = MAXIMUM(MIN_READ_SIZE, len); 2349 } 2350 if (max_req > 0) { /* max_req = 0 iff EOF received */ 2351 if (size > 0 && offset > size) { 2352 /* Only one request at a time 2353 * after the expected EOF */ 2354 debug3("Finish at %llu (%2d)", 2355 (unsigned long long)offset, 2356 num_req); 2357 max_req = 1; 2358 } else if (max_req < from->num_requests) { 2359 ++max_req; 2360 } 2361 } 2362 break; 2363 default: 2364 fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", 2365 SSH2_FXP_DATA, type); 2366 } 2367 } 2368 2369 if (showprogress && size) 2370 stop_progress_meter(); 2371 2372 /* Drain replies from the server (blocking) */ 2373 debug3_f("waiting for %u replies from destination", num_upload_req); 2374 handle_dest_replies(to, to_path, 1, &num_upload_req, &write_error); 2375 2376 /* Sanity check */ 2377 if (TAILQ_FIRST(&requests) != NULL) 2378 fatal("Transfer complete, but requests still in queue"); 2379 /* Truncate at 0 length on interrupt or error to avoid holes at dest */ 2380 if (read_error || write_error || interrupted) { 2381 debug("truncating \"%s\" at 0", to_path); 2382 do_close(to, to_handle, to_handle_len); 2383 free(to_handle); 2384 if (send_open(to, to_path, "dest", 2385 SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC, a, 2386 &to_handle, &to_handle_len) != 0) { 2387 error("truncation failed for \"%s\"", to_path); 2388 to_handle = NULL; 2389 } 2390 } 2391 if (read_error) { 2392 error("Couldn't read from origin file \"%s\" : %s", 2393 from_path, fx2txt(status)); 2394 status = -1; 2395 do_close(from, from_handle, from_handle_len); 2396 if (to_handle != NULL) 2397 do_close(to, to_handle, to_handle_len); 2398 } else if (write_error) { 2399 error("Couldn't write to \"%s\": %s", 2400 to_path, fx2txt(write_error)); 2401 status = SSH2_FX_FAILURE; 2402 do_close(from, from_handle, from_handle_len); 2403 if (to_handle != NULL) 2404 do_close(to, to_handle, to_handle_len); 2405 } else { 2406 if (do_close(from, from_handle, from_handle_len) != 0 || 2407 interrupted) 2408 status = -1; 2409 else 2410 status = SSH2_FX_OK; 2411 if (to_handle != NULL) { 2412 /* Need to resend utimes after write */ 2413 if (preserve_flag) 2414 do_fsetstat(to, to_handle, to_handle_len, a); 2415 do_close(to, to_handle, to_handle_len); 2416 } 2417 } 2418 sshbuf_free(msg); 2419 free(from_handle); 2420 free(to_handle); 2421 2422 return status == SSH2_FX_OK ? 0 : -1; 2423} 2424 2425static int 2426crossload_dir_internal(struct sftp_conn *from, struct sftp_conn *to, 2427 const char *from_path, const char *to_path, 2428 int depth, Attrib *dirattrib, int preserve_flag, int print_flag, 2429 int follow_link_flag) 2430{ 2431 int i, ret = 0; 2432 SFTP_DIRENT **dir_entries; 2433 char *filename, *new_from_path = NULL, *new_to_path = NULL; 2434 mode_t mode = 0777; 2435 Attrib curdir; 2436 2437 if (depth >= MAX_DIR_DEPTH) { 2438 error("Maximum directory depth exceeded: %d levels", depth); 2439 return -1; 2440 } 2441 2442 if (dirattrib == NULL && 2443 (dirattrib = do_stat(from, from_path, 1)) == NULL) { 2444 error("Unable to stat remote directory \"%s\"", from_path); 2445 return -1; 2446 } 2447 if (!S_ISDIR(dirattrib->perm)) { 2448 error("\"%s\" is not a directory", from_path); 2449 return -1; 2450 } 2451 if (print_flag && print_flag != SFTP_PROGRESS_ONLY) 2452 mprintf("Retrieving %s\n", from_path); 2453 2454 curdir = *dirattrib; /* dirattrib will be clobbered */ 2455 curdir.flags &= ~SSH2_FILEXFER_ATTR_SIZE; 2456 curdir.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; 2457 if ((curdir.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) == 0) { 2458 debug("Origin did not send permissions for " 2459 "directory \"%s\"", to_path); 2460 curdir.perm = S_IWUSR|S_IXUSR; 2461 curdir.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 2462 } 2463 /* We need to be able to write to the directory while we transfer it */ 2464 mode = curdir.perm & 01777; 2465 curdir.perm = mode | (S_IWUSR|S_IXUSR); 2466 2467 /* 2468 * sftp lacks a portable status value to match errno EEXIST, 2469 * so if we get a failure back then we must check whether 2470 * the path already existed and is a directory. Ensure we can 2471 * write to the directory we create for the duration of the transfer. 2472 */ 2473 if (do_mkdir(to, to_path, &curdir, 0) != 0) { 2474 if ((dirattrib = do_stat(to, to_path, 0)) == NULL) 2475 return -1; 2476 if (!S_ISDIR(dirattrib->perm)) { 2477 error("\"%s\" exists but is not a directory", to_path); 2478 return -1; 2479 } 2480 } 2481 curdir.perm = mode; 2482 2483 if (do_readdir(from, from_path, &dir_entries) == -1) { 2484 error("%s: Failed to get directory contents", from_path); 2485 return -1; 2486 } 2487 2488 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { 2489 free(new_from_path); 2490 free(new_to_path); 2491 2492 filename = dir_entries[i]->filename; 2493 new_from_path = path_append(from_path, filename); 2494 new_to_path = path_append(to_path, filename); 2495 2496 if (S_ISDIR(dir_entries[i]->a.perm)) { 2497 if (strcmp(filename, ".") == 0 || 2498 strcmp(filename, "..") == 0) 2499 continue; 2500 if (crossload_dir_internal(from, to, 2501 new_from_path, new_to_path, 2502 depth + 1, &(dir_entries[i]->a), preserve_flag, 2503 print_flag, follow_link_flag) == -1) 2504 ret = -1; 2505 } else if (S_ISREG(dir_entries[i]->a.perm) || 2506 (follow_link_flag && S_ISLNK(dir_entries[i]->a.perm))) { 2507 /* 2508 * If this is a symlink then don't send the link's 2509 * Attrib. do_download() will do a FXP_STAT operation 2510 * and get the link target's attributes. 2511 */ 2512 if (do_crossload(from, to, new_from_path, new_to_path, 2513 S_ISLNK(dir_entries[i]->a.perm) ? NULL : 2514 &(dir_entries[i]->a), preserve_flag) == -1) { 2515 error("Transfer of file %s to %s failed", 2516 new_from_path, new_to_path); 2517 ret = -1; 2518 } 2519 } else 2520 logit("%s: not a regular file\n", new_from_path); 2521 2522 } 2523 free(new_to_path); 2524 free(new_from_path); 2525 2526 do_setstat(to, to_path, &curdir); 2527 2528 free_sftp_dirents(dir_entries); 2529 2530 return ret; 2531} 2532 2533int 2534crossload_dir(struct sftp_conn *from, struct sftp_conn *to, 2535 const char *from_path, const char *to_path, 2536 Attrib *dirattrib, int preserve_flag, int print_flag, int follow_link_flag) 2537{ 2538 char *from_path_canon; 2539 int ret; 2540 2541 if ((from_path_canon = do_realpath(from, from_path)) == NULL) { 2542 error("Unable to canonicalize path \"%s\"", from_path); 2543 return -1; 2544 } 2545 2546 ret = crossload_dir_internal(from, to, from_path_canon, to_path, 0, 2547 dirattrib, preserve_flag, print_flag, follow_link_flag); 2548 free(from_path_canon); 2549 return ret; 2550} 2551 2552char * 2553path_append(const char *p1, const char *p2) 2554{ 2555 char *ret; 2556 size_t len = strlen(p1) + strlen(p2) + 2; 2557 2558 ret = xmalloc(len); 2559 strlcpy(ret, p1, len); 2560 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') 2561 strlcat(ret, "/", len); 2562 strlcat(ret, p2, len); 2563 2564 return(ret); 2565} 2566 2567char * 2568make_absolute(char *p, const char *pwd) 2569{ 2570 char *abs_str; 2571 2572 /* Derelativise */ 2573 if (p && !path_absolute(p)) { 2574 abs_str = path_append(pwd, p); 2575 free(p); 2576 return(abs_str); 2577 } else 2578 return(p); 2579} 2580 2581int 2582remote_is_dir(struct sftp_conn *conn, const char *path) 2583{ 2584 Attrib *a; 2585 2586 /* XXX: report errors? */ 2587 if ((a = do_stat(conn, path, 1)) == NULL) 2588 return(0); 2589 if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) 2590 return(0); 2591 return(S_ISDIR(a->perm)); 2592} 2593 2594 2595int 2596local_is_dir(const char *path) 2597{ 2598 struct stat sb; 2599 2600 /* XXX: report errors? */ 2601 if (stat(path, &sb) == -1) 2602 return(0); 2603 2604 return(S_ISDIR(sb.st_mode)); 2605} 2606 2607/* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ 2608int 2609globpath_is_dir(const char *pathname) 2610{ 2611 size_t l = strlen(pathname); 2612 2613 return l > 0 && pathname[l - 1] == '/'; 2614} 2615 2616