diff options
author | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
commit | 137d76ba65883aa8143af1fcad83b57e7badef0c (patch) | |
tree | f426e804bb5248ceafedfab7bb78ae6e6752942c /sftp.c | |
parent | dac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff) | |
parent | ef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff) |
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1:
- 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9):
+ Added chroot(2) support for sshd(8), controlled by a new option
"ChrootDirectory" (closes: #139047, LP: #24777).
+ Linked sftp-server(8) into sshd(8). The internal sftp server is used
when the command "internal-sftp" is specified in a Subsystem or
ForceCommand declaration. When used with ChrootDirectory, the
internal sftp server requires no special configuration of files
inside the chroot environment.
+ Added a protocol extension method "posix-rename@openssh.com" for
sftp-server(8) to perform POSIX atomic rename() operations; sftp(1)
prefers this if available (closes: #308561).
+ Removed the fixed limit of 100 file handles in sftp-server(8).
+ ssh(8) will now skip generation of SSH protocol 1 ephemeral server
keys when in inetd mode and protocol 2 connections are negotiated.
This speeds up protocol 2 connections to inetd-mode servers that
also allow Protocol 1.
+ Accept the PermitRootLogin directive in a sshd_config(5) Match
block. Allows for, e.g. permitting root only from the local network.
+ Reworked sftp(1) argument splitting and escaping to be more
internally consistent (i.e. between sftp commands) and more
consistent with sh(1). Please note that this will change the
interpretation of some quoted strings, especially those with
embedded backslash escape sequences.
+ Support "Banner=none" in sshd_config(5) to disable sending of a
pre-login banner (e.g. in a Match block).
+ ssh(1) ProxyCommands are now executed with $SHELL rather than
/bin/sh.
+ ssh(1)'s ConnectTimeout option is now applied to both the TCP
connection and the SSH banner exchange (previously it just covered
the TCP connection). This allows callers of ssh(1) to better detect
and deal with stuck servers that accept a TCP connection but don't
progress the protocol, and also makes ConnectTimeout useful for
connections via a ProxyCommand.
+ scp(1) incorrectly reported "stalled" on slow copies (closes:
#140828).
+ scp(1) date underflow for timestamps before epoch.
+ ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS,
instead of the current standard RRSIG.
+ Correctly drain ACKs when a sftp(1) upload write fails midway,
avoids a fatal() exit from what should be a recoverable condition.
+ Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF
hostname") to not include any IP address in the data to be hashed.
+ Make ssh(1) skip listening on the IPv6 wildcard address when a
binding address of 0.0.0.0 is used against an old SSH server that
does not support the RFC4254 syntax for wildcard bind addresses.
+ Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is
already done for X11/TCP forwarding sockets (closes: #439661).
+ Fix FD leak that could hang a ssh(1) connection multiplexing master.
+ Make ssh(1) -q option documentation consistent with reality.
+ Fixed sshd(8) PAM support not calling pam_session_close(), or
failing to call it with root privileges (closes: #372680).
+ Fix activation of OpenSSL engine support when requested in configure
(LP: #119295).
- 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1):
+ Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1)
and ssh-keygen(1). Visual fingerprint display is controlled by a new
ssh_config(5) option "VisualHostKey". The intent is to render SSH
host keys in a visual form that is amenable to easy recall and
rejection of changed host keys.
+ sshd_config(5) now supports CIDR address/masklen matching in "Match
address" blocks, with a fallback to classic wildcard matching.
+ sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys
from="..." restrictions, also with a fallback to classic wildcard
matching.
+ Added an extended test mode (-T) to sshd(8) to request that it write
its effective configuration to stdout and exit. Extended test mode
also supports the specification of connection parameters (username,
source address and hostname) to test the application of
sshd_config(5) Match rules.
+ ssh(1) now prints the number of bytes transferred and the overall
connection throughput for SSH protocol 2 sessions when in verbose
mode (previously these statistics were displayed for protocol 1
connections only).
+ sftp-server(8) now supports extension methods statvfs@openssh.com
and fstatvfs@openssh.com that implement statvfs(2)-like operations.
+ sftp(1) now has a "df" command to the sftp client that uses the
statvfs@openssh.com to produce a df(1)-like display of filesystem
space and inode utilisation (requires statvfs@openssh.com support on
the server).
+ Added a MaxSessions option to sshd_config(5) to allow control of the
number of multiplexed sessions supported over a single TCP
connection. This allows increasing the number of allowed sessions
above the previous default of 10, disabling connection multiplexing
(MaxSessions=1) or disallowing login/shell/subsystem sessions
entirely (MaxSessions=0).
+ Added a no-more-sessions@openssh.com global request extension that
is sent from ssh(1) to sshd(8) when the client knows that it will
never request another session (i.e. when session multiplexing is
disabled). This allows a server to disallow further session requests
and terminate the session in cases where the client has been
hijacked.
+ ssh-keygen(1) now supports the use of the -l option in combination
with -F to search for a host in ~/.ssh/known_hosts and display its
fingerprint.
+ ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of
"rsa1".
+ Added an AllowAgentForwarding option to sshd_config(8) to control
whether authentication agent forwarding is permitted. Note that this
is a loose control, as a client may install their own unofficial
forwarder.
+ ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when
receiving network data, resulting in a ~10% speedup.
+ ssh(1) and sshd(8) will now try additional addresses when connecting
to a port forward destination whose DNS name resolves to more than
one address. The previous behaviour was to try the only first
address and give up if that failed.
+ ssh(1) and sshd(8) now support signalling that channels are
half-closed for writing, through a channel protocol extension
notification "eow@openssh.com". This allows propagation of closed
file descriptors, so that commands such as "ssh -2 localhost od
/bin/ls | true" do not send unnecessary data over the wire.
+ sshd(8): increased the default size of ssh protocol 1 ephemeral keys
from 768 to 1024 bits.
+ When ssh(1) has been requested to fork after authentication ("ssh
-f") with ExitOnForwardFailure enabled, delay the fork until after
replies for any -R forwards have been seen. Allows for robust
detection of -R forward failure when using -f.
+ "Match group" blocks in sshd_config(5) now support negation of
groups. E.g. "Match group staff,!guests".
+ sftp(1) and sftp-server(8) now allow chmod-like operations to set
set[ug]id/sticky bits.
+ The MaxAuthTries option is now permitted in sshd_config(5) match
blocks.
+ Multiplexed ssh(1) sessions now support a subset of the ~ escapes
that are available to a primary connection.
+ ssh(1) connection multiplexing will now fall back to creating a new
connection in most error cases (closes: #352830).
+ Make ssh(1) deal more gracefully with channel requests that fail.
Previously it would optimistically assume that requests would always
succeed, which could cause hangs if they did not (e.g. when the
server runs out of file descriptors).
+ ssh(1) now reports multiplexing errors via the multiplex slave's
stderr where possible (subject to LogLevel in the mux master).
+ Prevent sshd(8) from erroneously applying public key restrictions
leaned from ~/.ssh/authorized_keys to other authentication methods
when public key authentication subsequently fails (LP: #161047).
+ Fixed an UMAC alignment problem that manifested on Itanium
platforms.
Diffstat (limited to 'sftp.c')
-rw-r--r-- | sftp.c | 576 |
1 files changed, 395 insertions, 181 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp.c,v 1.96 2007/01/03 04:09:15 stevesk Exp $ */ | 1 | /* $OpenBSD: sftp.c,v 1.103 2008/07/13 22:16:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> | 3 | * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> |
4 | * | 4 | * |
@@ -25,7 +25,11 @@ | |||
25 | #include <sys/param.h> | 25 | #include <sys/param.h> |
26 | #include <sys/socket.h> | 26 | #include <sys/socket.h> |
27 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
28 | #ifdef HAVE_SYS_STATVFS_H | ||
29 | #include <sys/statvfs.h> | ||
30 | #endif | ||
28 | 31 | ||
32 | #include <ctype.h> | ||
29 | #include <errno.h> | 33 | #include <errno.h> |
30 | 34 | ||
31 | #ifdef HAVE_PATHS_H | 35 | #ifdef HAVE_PATHS_H |
@@ -43,6 +47,14 @@ typedef void EditLine; | |||
43 | #include <unistd.h> | 47 | #include <unistd.h> |
44 | #include <stdarg.h> | 48 | #include <stdarg.h> |
45 | 49 | ||
50 | #ifdef HAVE_UTIL_H | ||
51 | # include <util.h> | ||
52 | #endif | ||
53 | |||
54 | #ifdef HAVE_LIBUTIL_H | ||
55 | # include <libutil.h> | ||
56 | #endif | ||
57 | |||
46 | #include "xmalloc.h" | 58 | #include "xmalloc.h" |
47 | #include "log.h" | 59 | #include "log.h" |
48 | #include "pathnames.h" | 60 | #include "pathnames.h" |
@@ -63,7 +75,7 @@ int batchmode = 0; | |||
63 | size_t copy_buffer_len = 32768; | 75 | size_t copy_buffer_len = 32768; |
64 | 76 | ||
65 | /* Number of concurrent outstanding requests */ | 77 | /* Number of concurrent outstanding requests */ |
66 | size_t num_requests = 16; | 78 | size_t num_requests = 64; |
67 | 79 | ||
68 | /* PID of ssh transport process */ | 80 | /* PID of ssh transport process */ |
69 | static pid_t sshpid = -1; | 81 | static pid_t sshpid = -1; |
@@ -103,6 +115,7 @@ extern char *__progname; | |||
103 | #define I_CHGRP 2 | 115 | #define I_CHGRP 2 |
104 | #define I_CHMOD 3 | 116 | #define I_CHMOD 3 |
105 | #define I_CHOWN 4 | 117 | #define I_CHOWN 4 |
118 | #define I_DF 24 | ||
106 | #define I_GET 5 | 119 | #define I_GET 5 |
107 | #define I_HELP 6 | 120 | #define I_HELP 6 |
108 | #define I_LCHDIR 7 | 121 | #define I_LCHDIR 7 |
@@ -135,6 +148,7 @@ static const struct CMD cmds[] = { | |||
135 | { "chgrp", I_CHGRP }, | 148 | { "chgrp", I_CHGRP }, |
136 | { "chmod", I_CHMOD }, | 149 | { "chmod", I_CHMOD }, |
137 | { "chown", I_CHOWN }, | 150 | { "chown", I_CHOWN }, |
151 | { "df", I_DF }, | ||
138 | { "dir", I_LS }, | 152 | { "dir", I_LS }, |
139 | { "exit", I_QUIT }, | 153 | { "exit", I_QUIT }, |
140 | { "get", I_GET }, | 154 | { "get", I_GET }, |
@@ -199,6 +213,8 @@ help(void) | |||
199 | printf("chgrp grp path Change group of file 'path' to 'grp'\n"); | 213 | printf("chgrp grp path Change group of file 'path' to 'grp'\n"); |
200 | printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); | 214 | printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); |
201 | printf("chown own path Change owner of file 'path' to 'own'\n"); | 215 | printf("chown own path Change owner of file 'path' to 'own'\n"); |
216 | printf("df [path] Display statistics for current directory or\n"); | ||
217 | printf(" filesystem containing 'path'\n"); | ||
202 | printf("help Display this help text\n"); | 218 | printf("help Display this help text\n"); |
203 | printf("get remote-path [local-path] Download file\n"); | 219 | printf("get remote-path [local-path] Download file\n"); |
204 | printf("lls [ls-options [path]] Display local directory listing\n"); | 220 | printf("lls [ls-options [path]] Display local directory listing\n"); |
@@ -346,144 +362,105 @@ infer_path(const char *p, char **ifp) | |||
346 | } | 362 | } |
347 | 363 | ||
348 | static int | 364 | static int |
349 | parse_getput_flags(const char **cpp, int *pflag) | 365 | parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) |
350 | { | 366 | { |
351 | const char *cp = *cpp; | 367 | extern int opterr, optind, optopt, optreset; |
368 | int ch; | ||
352 | 369 | ||
353 | /* Check for flags */ | 370 | optind = optreset = 1; |
354 | if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { | 371 | opterr = 0; |
355 | switch (cp[1]) { | 372 | |
373 | *pflag = 0; | ||
374 | while ((ch = getopt(argc, argv, "Pp")) != -1) { | ||
375 | switch (ch) { | ||
356 | case 'p': | 376 | case 'p': |
357 | case 'P': | 377 | case 'P': |
358 | *pflag = 1; | 378 | *pflag = 1; |
359 | break; | 379 | break; |
360 | default: | 380 | default: |
361 | error("Invalid flag -%c", cp[1]); | 381 | error("%s: Invalid flag -%c", cmd, optopt); |
362 | return(-1); | 382 | return -1; |
363 | } | 383 | } |
364 | cp += 2; | ||
365 | *cpp = cp + strspn(cp, WHITESPACE); | ||
366 | } | 384 | } |
367 | 385 | ||
368 | return(0); | 386 | return optind; |
369 | } | 387 | } |
370 | 388 | ||
371 | static int | 389 | static int |
372 | parse_ls_flags(const char **cpp, int *lflag) | 390 | parse_ls_flags(char **argv, int argc, int *lflag) |
373 | { | 391 | { |
374 | const char *cp = *cpp; | 392 | extern int opterr, optind, optopt, optreset; |
393 | int ch; | ||
375 | 394 | ||
376 | /* Defaults */ | 395 | optind = optreset = 1; |
377 | *lflag = LS_NAME_SORT; | 396 | opterr = 0; |
378 | 397 | ||
379 | /* Check for flags */ | 398 | *lflag = LS_NAME_SORT; |
380 | if (cp++[0] == '-') { | 399 | while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { |
381 | for (; strchr(WHITESPACE, *cp) == NULL; cp++) { | 400 | switch (ch) { |
382 | switch (*cp) { | 401 | case '1': |
383 | case 'l': | 402 | *lflag &= ~VIEW_FLAGS; |
384 | *lflag &= ~VIEW_FLAGS; | 403 | *lflag |= LS_SHORT_VIEW; |
385 | *lflag |= LS_LONG_VIEW; | 404 | break; |
386 | break; | 405 | case 'S': |
387 | case '1': | 406 | *lflag &= ~SORT_FLAGS; |
388 | *lflag &= ~VIEW_FLAGS; | 407 | *lflag |= LS_SIZE_SORT; |
389 | *lflag |= LS_SHORT_VIEW; | 408 | break; |
390 | break; | 409 | case 'a': |
391 | case 'n': | 410 | *lflag |= LS_SHOW_ALL; |
392 | *lflag &= ~VIEW_FLAGS; | 411 | break; |
393 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; | 412 | case 'f': |
394 | break; | 413 | *lflag &= ~SORT_FLAGS; |
395 | case 'S': | 414 | break; |
396 | *lflag &= ~SORT_FLAGS; | 415 | case 'l': |
397 | *lflag |= LS_SIZE_SORT; | 416 | *lflag &= ~VIEW_FLAGS; |
398 | break; | 417 | *lflag |= LS_LONG_VIEW; |
399 | case 't': | 418 | break; |
400 | *lflag &= ~SORT_FLAGS; | 419 | case 'n': |
401 | *lflag |= LS_TIME_SORT; | 420 | *lflag &= ~VIEW_FLAGS; |
402 | break; | 421 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; |
403 | case 'r': | 422 | break; |
404 | *lflag |= LS_REVERSE_SORT; | 423 | case 'r': |
405 | break; | 424 | *lflag |= LS_REVERSE_SORT; |
406 | case 'f': | 425 | break; |
407 | *lflag &= ~SORT_FLAGS; | 426 | case 't': |
408 | break; | 427 | *lflag &= ~SORT_FLAGS; |
409 | case 'a': | 428 | *lflag |= LS_TIME_SORT; |
410 | *lflag |= LS_SHOW_ALL; | 429 | break; |
411 | break; | 430 | default: |
412 | default: | 431 | error("ls: Invalid flag -%c", optopt); |
413 | error("Invalid flag -%c", *cp); | 432 | return -1; |
414 | return(-1); | ||
415 | } | ||
416 | } | 433 | } |
417 | *cpp = cp + strspn(cp, WHITESPACE); | ||
418 | } | 434 | } |
419 | 435 | ||
420 | return(0); | 436 | return optind; |
421 | } | 437 | } |
422 | 438 | ||
423 | static int | 439 | static int |
424 | get_pathname(const char **cpp, char **path) | 440 | parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) |
425 | { | 441 | { |
426 | const char *cp = *cpp, *end; | 442 | extern int opterr, optind, optopt, optreset; |
427 | char quot; | 443 | int ch; |
428 | u_int i, j; | ||
429 | |||
430 | cp += strspn(cp, WHITESPACE); | ||
431 | if (!*cp) { | ||
432 | *cpp = cp; | ||
433 | *path = NULL; | ||
434 | return (0); | ||
435 | } | ||
436 | |||
437 | *path = xmalloc(strlen(cp) + 1); | ||
438 | |||
439 | /* Check for quoted filenames */ | ||
440 | if (*cp == '\"' || *cp == '\'') { | ||
441 | quot = *cp++; | ||
442 | 444 | ||
443 | /* Search for terminating quote, unescape some chars */ | 445 | optind = optreset = 1; |
444 | for (i = j = 0; i <= strlen(cp); i++) { | 446 | opterr = 0; |
445 | if (cp[i] == quot) { /* Found quote */ | ||
446 | i++; | ||
447 | (*path)[j] = '\0'; | ||
448 | break; | ||
449 | } | ||
450 | if (cp[i] == '\0') { /* End of string */ | ||
451 | error("Unterminated quote"); | ||
452 | goto fail; | ||
453 | } | ||
454 | if (cp[i] == '\\') { /* Escaped characters */ | ||
455 | i++; | ||
456 | if (cp[i] != '\'' && cp[i] != '\"' && | ||
457 | cp[i] != '\\') { | ||
458 | error("Bad escaped character '\\%c'", | ||
459 | cp[i]); | ||
460 | goto fail; | ||
461 | } | ||
462 | } | ||
463 | (*path)[j++] = cp[i]; | ||
464 | } | ||
465 | 447 | ||
466 | if (j == 0) { | 448 | *hflag = *iflag = 0; |
467 | error("Empty quotes"); | 449 | while ((ch = getopt(argc, argv, "hi")) != -1) { |
468 | goto fail; | 450 | switch (ch) { |
451 | case 'h': | ||
452 | *hflag = 1; | ||
453 | break; | ||
454 | case 'i': | ||
455 | *iflag = 1; | ||
456 | break; | ||
457 | default: | ||
458 | error("%s: Invalid flag -%c", cmd, optopt); | ||
459 | return -1; | ||
469 | } | 460 | } |
470 | *cpp = cp + i + strspn(cp + i, WHITESPACE); | ||
471 | } else { | ||
472 | /* Read to end of filename */ | ||
473 | end = strpbrk(cp, WHITESPACE); | ||
474 | if (end == NULL) | ||
475 | end = strchr(cp, '\0'); | ||
476 | *cpp = end + strspn(end, WHITESPACE); | ||
477 | |||
478 | memcpy(*path, cp, end - cp); | ||
479 | (*path)[end - cp] = '\0'; | ||
480 | } | 461 | } |
481 | return (0); | ||
482 | 462 | ||
483 | fail: | 463 | return optind; |
484 | xfree(*path); | ||
485 | *path = NULL; | ||
486 | return (-1); | ||
487 | } | 464 | } |
488 | 465 | ||
489 | static int | 466 | static int |
@@ -499,17 +476,6 @@ is_dir(char *path) | |||
499 | } | 476 | } |
500 | 477 | ||
501 | static int | 478 | static int |
502 | is_reg(char *path) | ||
503 | { | ||
504 | struct stat sb; | ||
505 | |||
506 | if (stat(path, &sb) == -1) | ||
507 | fatal("stat %s: %s", path, strerror(errno)); | ||
508 | |||
509 | return(S_ISREG(sb.st_mode)); | ||
510 | } | ||
511 | |||
512 | static int | ||
513 | remote_is_dir(struct sftp_conn *conn, char *path) | 479 | remote_is_dir(struct sftp_conn *conn, char *path) |
514 | { | 480 | { |
515 | Attrib *a; | 481 | Attrib *a; |
@@ -597,6 +563,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
597 | glob_t g; | 563 | glob_t g; |
598 | int err = 0; | 564 | int err = 0; |
599 | int i; | 565 | int i; |
566 | struct stat sb; | ||
600 | 567 | ||
601 | if (dst) { | 568 | if (dst) { |
602 | tmp_dst = xstrdup(dst); | 569 | tmp_dst = xstrdup(dst); |
@@ -605,7 +572,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
605 | 572 | ||
606 | memset(&g, 0, sizeof(g)); | 573 | memset(&g, 0, sizeof(g)); |
607 | debug3("Looking up %s", src); | 574 | debug3("Looking up %s", src); |
608 | if (glob(src, 0, NULL, &g)) { | 575 | if (glob(src, GLOB_NOCHECK, NULL, &g)) { |
609 | error("File \"%s\" not found.", src); | 576 | error("File \"%s\" not found.", src); |
610 | err = -1; | 577 | err = -1; |
611 | goto out; | 578 | goto out; |
@@ -620,7 +587,13 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
620 | } | 587 | } |
621 | 588 | ||
622 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { | 589 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { |
623 | if (!is_reg(g.gl_pathv[i])) { | 590 | if (stat(g.gl_pathv[i], &sb) == -1) { |
591 | err = -1; | ||
592 | error("stat %s: %s", g.gl_pathv[i], strerror(errno)); | ||
593 | continue; | ||
594 | } | ||
595 | |||
596 | if (!S_ISREG(sb.st_mode)) { | ||
624 | error("skipping non-regular file %s", | 597 | error("skipping non-regular file %s", |
625 | g.gl_pathv[i]); | 598 | g.gl_pathv[i]); |
626 | continue; | 599 | continue; |
@@ -867,14 +840,238 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
867 | } | 840 | } |
868 | 841 | ||
869 | static int | 842 | static int |
870 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | 843 | do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) |
844 | { | ||
845 | struct sftp_statvfs st; | ||
846 | char s_used[FMT_SCALED_STRSIZE]; | ||
847 | char s_avail[FMT_SCALED_STRSIZE]; | ||
848 | char s_root[FMT_SCALED_STRSIZE]; | ||
849 | char s_total[FMT_SCALED_STRSIZE]; | ||
850 | |||
851 | if (do_statvfs(conn, path, &st, 1) == -1) | ||
852 | return -1; | ||
853 | if (iflag) { | ||
854 | printf(" Inodes Used Avail " | ||
855 | "(root) %%Capacity\n"); | ||
856 | printf("%11llu %11llu %11llu %11llu %3llu%%\n", | ||
857 | (unsigned long long)st.f_files, | ||
858 | (unsigned long long)(st.f_files - st.f_ffree), | ||
859 | (unsigned long long)st.f_favail, | ||
860 | (unsigned long long)st.f_ffree, | ||
861 | (unsigned long long)(100 * (st.f_files - st.f_ffree) / | ||
862 | st.f_files)); | ||
863 | } else if (hflag) { | ||
864 | strlcpy(s_used, "error", sizeof(s_used)); | ||
865 | strlcpy(s_avail, "error", sizeof(s_avail)); | ||
866 | strlcpy(s_root, "error", sizeof(s_root)); | ||
867 | strlcpy(s_total, "error", sizeof(s_total)); | ||
868 | fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); | ||
869 | fmt_scaled(st.f_bavail * st.f_frsize, s_avail); | ||
870 | fmt_scaled(st.f_bfree * st.f_frsize, s_root); | ||
871 | fmt_scaled(st.f_blocks * st.f_frsize, s_total); | ||
872 | printf(" Size Used Avail (root) %%Capacity\n"); | ||
873 | printf("%7sB %7sB %7sB %7sB %3llu%%\n", | ||
874 | s_total, s_used, s_avail, s_root, | ||
875 | (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / | ||
876 | st.f_blocks)); | ||
877 | } else { | ||
878 | printf(" Size Used Avail " | ||
879 | "(root) %%Capacity\n"); | ||
880 | printf("%12llu %12llu %12llu %12llu %3llu%%\n", | ||
881 | (unsigned long long)(st.f_frsize * st.f_blocks / 1024), | ||
882 | (unsigned long long)(st.f_frsize * | ||
883 | (st.f_blocks - st.f_bfree) / 1024), | ||
884 | (unsigned long long)(st.f_frsize * st.f_bavail / 1024), | ||
885 | (unsigned long long)(st.f_frsize * st.f_bfree / 1024), | ||
886 | (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / | ||
887 | st.f_blocks)); | ||
888 | } | ||
889 | return 0; | ||
890 | } | ||
891 | |||
892 | /* | ||
893 | * Undo escaping of glob sequences in place. Used to undo extra escaping | ||
894 | * applied in makeargv() when the string is destined for a function that | ||
895 | * does not glob it. | ||
896 | */ | ||
897 | static void | ||
898 | undo_glob_escape(char *s) | ||
899 | { | ||
900 | size_t i, j; | ||
901 | |||
902 | for (i = j = 0;;) { | ||
903 | if (s[i] == '\0') { | ||
904 | s[j] = '\0'; | ||
905 | return; | ||
906 | } | ||
907 | if (s[i] != '\\') { | ||
908 | s[j++] = s[i++]; | ||
909 | continue; | ||
910 | } | ||
911 | /* s[i] == '\\' */ | ||
912 | ++i; | ||
913 | switch (s[i]) { | ||
914 | case '?': | ||
915 | case '[': | ||
916 | case '*': | ||
917 | case '\\': | ||
918 | s[j++] = s[i++]; | ||
919 | break; | ||
920 | case '\0': | ||
921 | s[j++] = '\\'; | ||
922 | s[j] = '\0'; | ||
923 | return; | ||
924 | default: | ||
925 | s[j++] = '\\'; | ||
926 | s[j++] = s[i++]; | ||
927 | break; | ||
928 | } | ||
929 | } | ||
930 | } | ||
931 | |||
932 | /* | ||
933 | * Split a string into an argument vector using sh(1)-style quoting, | ||
934 | * comment and escaping rules, but with some tweaks to handle glob(3) | ||
935 | * wildcards. | ||
936 | * Returns NULL on error or a NULL-terminated array of arguments. | ||
937 | */ | ||
938 | #define MAXARGS 128 | ||
939 | #define MAXARGLEN 8192 | ||
940 | static char ** | ||
941 | makeargv(const char *arg, int *argcp) | ||
942 | { | ||
943 | int argc, quot; | ||
944 | size_t i, j; | ||
945 | static char argvs[MAXARGLEN]; | ||
946 | static char *argv[MAXARGS + 1]; | ||
947 | enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; | ||
948 | |||
949 | *argcp = argc = 0; | ||
950 | if (strlen(arg) > sizeof(argvs) - 1) { | ||
951 | args_too_longs: | ||
952 | error("string too long"); | ||
953 | return NULL; | ||
954 | } | ||
955 | state = MA_START; | ||
956 | i = j = 0; | ||
957 | for (;;) { | ||
958 | if (isspace(arg[i])) { | ||
959 | if (state == MA_UNQUOTED) { | ||
960 | /* Terminate current argument */ | ||
961 | argvs[j++] = '\0'; | ||
962 | argc++; | ||
963 | state = MA_START; | ||
964 | } else if (state != MA_START) | ||
965 | argvs[j++] = arg[i]; | ||
966 | } else if (arg[i] == '"' || arg[i] == '\'') { | ||
967 | q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; | ||
968 | if (state == MA_START) { | ||
969 | argv[argc] = argvs + j; | ||
970 | state = q; | ||
971 | } else if (state == MA_UNQUOTED) | ||
972 | state = q; | ||
973 | else if (state == q) | ||
974 | state = MA_UNQUOTED; | ||
975 | else | ||
976 | argvs[j++] = arg[i]; | ||
977 | } else if (arg[i] == '\\') { | ||
978 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | ||
979 | quot = state == MA_SQUOTE ? '\'' : '"'; | ||
980 | /* Unescape quote we are in */ | ||
981 | /* XXX support \n and friends? */ | ||
982 | if (arg[i + 1] == quot) { | ||
983 | i++; | ||
984 | argvs[j++] = arg[i]; | ||
985 | } else if (arg[i + 1] == '?' || | ||
986 | arg[i + 1] == '[' || arg[i + 1] == '*') { | ||
987 | /* | ||
988 | * Special case for sftp: append | ||
989 | * double-escaped glob sequence - | ||
990 | * glob will undo one level of | ||
991 | * escaping. NB. string can grow here. | ||
992 | */ | ||
993 | if (j >= sizeof(argvs) - 5) | ||
994 | goto args_too_longs; | ||
995 | argvs[j++] = '\\'; | ||
996 | argvs[j++] = arg[i++]; | ||
997 | argvs[j++] = '\\'; | ||
998 | argvs[j++] = arg[i]; | ||
999 | } else { | ||
1000 | argvs[j++] = arg[i++]; | ||
1001 | argvs[j++] = arg[i]; | ||
1002 | } | ||
1003 | } else { | ||
1004 | if (state == MA_START) { | ||
1005 | argv[argc] = argvs + j; | ||
1006 | state = MA_UNQUOTED; | ||
1007 | } | ||
1008 | if (arg[i + 1] == '?' || arg[i + 1] == '[' || | ||
1009 | arg[i + 1] == '*' || arg[i + 1] == '\\') { | ||
1010 | /* | ||
1011 | * Special case for sftp: append | ||
1012 | * escaped glob sequence - | ||
1013 | * glob will undo one level of | ||
1014 | * escaping. | ||
1015 | */ | ||
1016 | argvs[j++] = arg[i++]; | ||
1017 | argvs[j++] = arg[i]; | ||
1018 | } else { | ||
1019 | /* Unescape everything */ | ||
1020 | /* XXX support \n and friends? */ | ||
1021 | i++; | ||
1022 | argvs[j++] = arg[i]; | ||
1023 | } | ||
1024 | } | ||
1025 | } else if (arg[i] == '#') { | ||
1026 | if (state == MA_SQUOTE || state == MA_DQUOTE) | ||
1027 | argvs[j++] = arg[i]; | ||
1028 | else | ||
1029 | goto string_done; | ||
1030 | } else if (arg[i] == '\0') { | ||
1031 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | ||
1032 | error("Unterminated quoted argument"); | ||
1033 | return NULL; | ||
1034 | } | ||
1035 | string_done: | ||
1036 | if (state == MA_UNQUOTED) { | ||
1037 | argvs[j++] = '\0'; | ||
1038 | argc++; | ||
1039 | } | ||
1040 | break; | ||
1041 | } else { | ||
1042 | if (state == MA_START) { | ||
1043 | argv[argc] = argvs + j; | ||
1044 | state = MA_UNQUOTED; | ||
1045 | } | ||
1046 | if ((state == MA_SQUOTE || state == MA_DQUOTE) && | ||
1047 | (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { | ||
1048 | /* | ||
1049 | * Special case for sftp: escape quoted | ||
1050 | * glob(3) wildcards. NB. string can grow | ||
1051 | * here. | ||
1052 | */ | ||
1053 | if (j >= sizeof(argvs) - 3) | ||
1054 | goto args_too_longs; | ||
1055 | argvs[j++] = '\\'; | ||
1056 | argvs[j++] = arg[i]; | ||
1057 | } else | ||
1058 | argvs[j++] = arg[i]; | ||
1059 | } | ||
1060 | i++; | ||
1061 | } | ||
1062 | *argcp = argc; | ||
1063 | return argv; | ||
1064 | } | ||
1065 | |||
1066 | static int | ||
1067 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, | ||
871 | unsigned long *n_arg, char **path1, char **path2) | 1068 | unsigned long *n_arg, char **path1, char **path2) |
872 | { | 1069 | { |
873 | const char *cmd, *cp = *cpp; | 1070 | const char *cmd, *cp = *cpp; |
874 | char *cp2; | 1071 | char *cp2, **argv; |
875 | int base = 0; | 1072 | int base = 0; |
876 | long l; | 1073 | long l; |
877 | int i, cmdnum; | 1074 | int i, cmdnum, optidx, argc; |
878 | 1075 | ||
879 | /* Skip leading whitespace */ | 1076 | /* Skip leading whitespace */ |
880 | cp = cp + strspn(cp, WHITESPACE); | 1077 | cp = cp + strspn(cp, WHITESPACE); |
@@ -890,17 +1087,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
890 | cp++; | 1087 | cp++; |
891 | } | 1088 | } |
892 | 1089 | ||
1090 | if ((argv = makeargv(cp, &argc)) == NULL) | ||
1091 | return -1; | ||
1092 | |||
893 | /* Figure out which command we have */ | 1093 | /* Figure out which command we have */ |
894 | for (i = 0; cmds[i].c; i++) { | 1094 | for (i = 0; cmds[i].c != NULL; i++) { |
895 | int cmdlen = strlen(cmds[i].c); | 1095 | if (strcasecmp(cmds[i].c, argv[0]) == 0) |
896 | |||
897 | /* Check for command followed by whitespace */ | ||
898 | if (!strncasecmp(cp, cmds[i].c, cmdlen) && | ||
899 | strchr(WHITESPACE, cp[cmdlen])) { | ||
900 | cp += cmdlen; | ||
901 | cp = cp + strspn(cp, WHITESPACE); | ||
902 | break; | 1096 | break; |
903 | } | ||
904 | } | 1097 | } |
905 | cmdnum = cmds[i].n; | 1098 | cmdnum = cmds[i].n; |
906 | cmd = cmds[i].c; | 1099 | cmd = cmds[i].c; |
@@ -911,40 +1104,44 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
911 | cmdnum = I_SHELL; | 1104 | cmdnum = I_SHELL; |
912 | } else if (cmdnum == -1) { | 1105 | } else if (cmdnum == -1) { |
913 | error("Invalid command."); | 1106 | error("Invalid command."); |
914 | return (-1); | 1107 | return -1; |
915 | } | 1108 | } |
916 | 1109 | ||
917 | /* Get arguments and parse flags */ | 1110 | /* Get arguments and parse flags */ |
918 | *lflag = *pflag = *n_arg = 0; | 1111 | *lflag = *pflag = *hflag = *n_arg = 0; |
919 | *path1 = *path2 = NULL; | 1112 | *path1 = *path2 = NULL; |
1113 | optidx = 1; | ||
920 | switch (cmdnum) { | 1114 | switch (cmdnum) { |
921 | case I_GET: | 1115 | case I_GET: |
922 | case I_PUT: | 1116 | case I_PUT: |
923 | if (parse_getput_flags(&cp, pflag)) | 1117 | if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) |
924 | return(-1); | 1118 | return -1; |
925 | /* Get first pathname (mandatory) */ | 1119 | /* Get first pathname (mandatory) */ |
926 | if (get_pathname(&cp, path1)) | 1120 | if (argc - optidx < 1) { |
927 | return(-1); | ||
928 | if (*path1 == NULL) { | ||
929 | error("You must specify at least one path after a " | 1121 | error("You must specify at least one path after a " |
930 | "%s command.", cmd); | 1122 | "%s command.", cmd); |
931 | return(-1); | 1123 | return -1; |
1124 | } | ||
1125 | *path1 = xstrdup(argv[optidx]); | ||
1126 | /* Get second pathname (optional) */ | ||
1127 | if (argc - optidx > 1) { | ||
1128 | *path2 = xstrdup(argv[optidx + 1]); | ||
1129 | /* Destination is not globbed */ | ||
1130 | undo_glob_escape(*path2); | ||
932 | } | 1131 | } |
933 | /* Try to get second pathname (optional) */ | ||
934 | if (get_pathname(&cp, path2)) | ||
935 | return(-1); | ||
936 | break; | 1132 | break; |
937 | case I_RENAME: | 1133 | case I_RENAME: |
938 | case I_SYMLINK: | 1134 | case I_SYMLINK: |
939 | if (get_pathname(&cp, path1)) | 1135 | if (argc - optidx < 2) { |
940 | return(-1); | ||
941 | if (get_pathname(&cp, path2)) | ||
942 | return(-1); | ||
943 | if (!*path1 || !*path2) { | ||
944 | error("You must specify two paths after a %s " | 1136 | error("You must specify two paths after a %s " |
945 | "command.", cmd); | 1137 | "command.", cmd); |
946 | return(-1); | 1138 | return -1; |
947 | } | 1139 | } |
1140 | *path1 = xstrdup(argv[optidx]); | ||
1141 | *path2 = xstrdup(argv[optidx + 1]); | ||
1142 | /* Paths are not globbed */ | ||
1143 | undo_glob_escape(*path1); | ||
1144 | undo_glob_escape(*path2); | ||
948 | break; | 1145 | break; |
949 | case I_RM: | 1146 | case I_RM: |
950 | case I_MKDIR: | 1147 | case I_MKDIR: |
@@ -953,59 +1150,69 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
953 | case I_LCHDIR: | 1150 | case I_LCHDIR: |
954 | case I_LMKDIR: | 1151 | case I_LMKDIR: |
955 | /* Get pathname (mandatory) */ | 1152 | /* Get pathname (mandatory) */ |
956 | if (get_pathname(&cp, path1)) | 1153 | if (argc - optidx < 1) { |
957 | return(-1); | ||
958 | if (*path1 == NULL) { | ||
959 | error("You must specify a path after a %s command.", | 1154 | error("You must specify a path after a %s command.", |
960 | cmd); | 1155 | cmd); |
961 | return(-1); | 1156 | return -1; |
1157 | } | ||
1158 | *path1 = xstrdup(argv[optidx]); | ||
1159 | /* Only "rm" globs */ | ||
1160 | if (cmdnum != I_RM) | ||
1161 | undo_glob_escape(*path1); | ||
1162 | break; | ||
1163 | case I_DF: | ||
1164 | if ((optidx = parse_df_flags(cmd, argv, argc, hflag, | ||
1165 | iflag)) == -1) | ||
1166 | return -1; | ||
1167 | /* Default to current directory if no path specified */ | ||
1168 | if (argc - optidx < 1) | ||
1169 | *path1 = NULL; | ||
1170 | else { | ||
1171 | *path1 = xstrdup(argv[optidx]); | ||
1172 | undo_glob_escape(*path1); | ||
962 | } | 1173 | } |
963 | break; | 1174 | break; |
964 | case I_LS: | 1175 | case I_LS: |
965 | if (parse_ls_flags(&cp, lflag)) | 1176 | if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) |
966 | return(-1); | 1177 | return(-1); |
967 | /* Path is optional */ | 1178 | /* Path is optional */ |
968 | if (get_pathname(&cp, path1)) | 1179 | if (argc - optidx > 0) |
969 | return(-1); | 1180 | *path1 = xstrdup(argv[optidx]); |
970 | break; | 1181 | break; |
971 | case I_LLS: | 1182 | case I_LLS: |
1183 | /* Skip ls command and following whitespace */ | ||
1184 | cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); | ||
972 | case I_SHELL: | 1185 | case I_SHELL: |
973 | /* Uses the rest of the line */ | 1186 | /* Uses the rest of the line */ |
974 | break; | 1187 | break; |
975 | case I_LUMASK: | 1188 | case I_LUMASK: |
976 | base = 8; | ||
977 | case I_CHMOD: | 1189 | case I_CHMOD: |
978 | base = 8; | 1190 | base = 8; |
979 | case I_CHOWN: | 1191 | case I_CHOWN: |
980 | case I_CHGRP: | 1192 | case I_CHGRP: |
981 | /* Get numeric arg (mandatory) */ | 1193 | /* Get numeric arg (mandatory) */ |
1194 | if (argc - optidx < 1) | ||
1195 | goto need_num_arg; | ||
982 | errno = 0; | 1196 | errno = 0; |
983 | l = strtol(cp, &cp2, base); | 1197 | l = strtol(argv[optidx], &cp2, base); |
984 | if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && | 1198 | if (cp2 == argv[optidx] || *cp2 != '\0' || |
985 | errno == ERANGE) || l < 0) { | 1199 | ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || |
1200 | l < 0) { | ||
1201 | need_num_arg: | ||
986 | error("You must supply a numeric argument " | 1202 | error("You must supply a numeric argument " |
987 | "to the %s command.", cmd); | 1203 | "to the %s command.", cmd); |
988 | return(-1); | 1204 | return -1; |
989 | } | 1205 | } |
990 | cp = cp2; | ||
991 | *n_arg = l; | 1206 | *n_arg = l; |
992 | if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) | 1207 | if (cmdnum == I_LUMASK) |
993 | break; | 1208 | break; |
994 | if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { | ||
995 | error("You must supply a numeric argument " | ||
996 | "to the %s command.", cmd); | ||
997 | return(-1); | ||
998 | } | ||
999 | cp += strspn(cp, WHITESPACE); | ||
1000 | |||
1001 | /* Get pathname (mandatory) */ | 1209 | /* Get pathname (mandatory) */ |
1002 | if (get_pathname(&cp, path1)) | 1210 | if (argc - optidx < 2) { |
1003 | return(-1); | ||
1004 | if (*path1 == NULL) { | ||
1005 | error("You must specify a path after a %s command.", | 1211 | error("You must specify a path after a %s command.", |
1006 | cmd); | 1212 | cmd); |
1007 | return(-1); | 1213 | return -1; |
1008 | } | 1214 | } |
1215 | *path1 = xstrdup(argv[optidx + 1]); | ||
1009 | break; | 1216 | break; |
1010 | case I_QUIT: | 1217 | case I_QUIT: |
1011 | case I_PWD: | 1218 | case I_PWD: |
@@ -1027,7 +1234,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1027 | int err_abort) | 1234 | int err_abort) |
1028 | { | 1235 | { |
1029 | char *path1, *path2, *tmp; | 1236 | char *path1, *path2, *tmp; |
1030 | int pflag, lflag, iflag, cmdnum, i; | 1237 | int pflag, lflag, iflag, hflag, cmdnum, i; |
1031 | unsigned long n_arg; | 1238 | unsigned long n_arg; |
1032 | Attrib a, *aa; | 1239 | Attrib a, *aa; |
1033 | char path_buf[MAXPATHLEN]; | 1240 | char path_buf[MAXPATHLEN]; |
@@ -1035,7 +1242,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1035 | glob_t g; | 1242 | glob_t g; |
1036 | 1243 | ||
1037 | path1 = path2 = NULL; | 1244 | path1 = path2 = NULL; |
1038 | cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, | 1245 | cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, |
1039 | &path1, &path2); | 1246 | &path1, &path2); |
1040 | 1247 | ||
1041 | if (iflag != 0) | 1248 | if (iflag != 0) |
@@ -1129,6 +1336,13 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1129 | path1 = make_absolute(path1, *pwd); | 1336 | path1 = make_absolute(path1, *pwd); |
1130 | err = do_globbed_ls(conn, path1, tmp, lflag); | 1337 | err = do_globbed_ls(conn, path1, tmp, lflag); |
1131 | break; | 1338 | break; |
1339 | case I_DF: | ||
1340 | /* Default to current directory if no path specified */ | ||
1341 | if (path1 == NULL) | ||
1342 | path1 = xstrdup(*pwd); | ||
1343 | path1 = make_absolute(path1, *pwd); | ||
1344 | err = do_df(conn, path1, hflag, iflag); | ||
1345 | break; | ||
1132 | case I_LCHDIR: | 1346 | case I_LCHDIR: |
1133 | if (chdir(path1) == -1) { | 1347 | if (chdir(path1) == -1) { |
1134 | error("Couldn't change local directory to " | 1348 | error("Couldn't change local directory to " |