-/* $OpenBSD: sftp.c,v 1.98 2007/12/12 05:04:03 djm Exp $ */
+/* $OpenBSD: sftp.c,v 1.105 2008/12/09 03:04:39 djm Exp $ */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/wait.h>
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
+#ifdef HAVE_UTIL_H
+# include <util.h>
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+# include <libutil.h>
+#endif
+
#include "xmalloc.h"
#include "log.h"
#include "pathnames.h"
size_t copy_buffer_len = 32768;
/* Number of concurrent outstanding requests */
-size_t num_requests = 16;
+size_t num_requests = 64;
/* PID of ssh transport process */
static pid_t sshpid = -1;
#define I_CHGRP 2
#define I_CHMOD 3
#define I_CHOWN 4
+#define I_DF 24
#define I_GET 5
#define I_HELP 6
#define I_LCHDIR 7
{ "chgrp", I_CHGRP },
{ "chmod", I_CHMOD },
{ "chown", I_CHOWN },
+ { "df", I_DF },
{ "dir", I_LS },
{ "exit", I_QUIT },
{ "get", I_GET },
printf("chgrp grp path Change group of file 'path' to 'grp'\n");
printf("chmod mode path Change permissions of file 'path' to 'mode'\n");
printf("chown own path Change owner of file 'path' to 'own'\n");
+ printf("df [path] Display statistics for current directory or\n");
+ printf(" filesystem containing 'path'\n");
printf("help Display this help text\n");
printf("get remote-path [local-path] Download file\n");
printf("lls [ls-options [path]] Display local directory listing\n");
static int
parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
{
- extern int optind, optreset, opterr;
+ extern int opterr, optind, optopt, optreset;
int ch;
optind = optreset = 1;
*pflag = 1;
break;
default:
- error("%s: Invalid flag -%c", cmd, ch);
+ error("%s: Invalid flag -%c", cmd, optopt);
return -1;
}
}
static int
parse_ls_flags(char **argv, int argc, int *lflag)
{
- extern int optind, optreset, opterr;
+ extern int opterr, optind, optopt, optreset;
int ch;
optind = optreset = 1;
*lflag |= LS_TIME_SORT;
break;
default:
- error("ls: Invalid flag -%c", ch);
+ error("ls: Invalid flag -%c", optopt);
return -1;
}
}
}
static int
-is_dir(char *path)
+parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
{
- struct stat sb;
+ extern int opterr, optind, optopt, optreset;
+ int ch;
- /* XXX: report errors? */
- if (stat(path, &sb) == -1)
- return(0);
+ optind = optreset = 1;
+ opterr = 0;
- return(S_ISDIR(sb.st_mode));
+ *hflag = *iflag = 0;
+ while ((ch = getopt(argc, argv, "hi")) != -1) {
+ switch (ch) {
+ case 'h':
+ *hflag = 1;
+ break;
+ case 'i':
+ *iflag = 1;
+ break;
+ default:
+ error("%s: Invalid flag -%c", cmd, optopt);
+ return -1;
+ }
+ }
+
+ return optind;
}
static int
-is_reg(char *path)
+is_dir(char *path)
{
struct stat sb;
+ /* XXX: report errors? */
if (stat(path, &sb) == -1)
- fatal("stat %s: %s", path, strerror(errno));
+ return(0);
- return(S_ISREG(sb.st_mode));
+ return(S_ISDIR(sb.st_mode));
}
static int
glob_t g;
int err = 0;
int i;
+ struct stat sb;
if (dst) {
tmp_dst = xstrdup(dst);
memset(&g, 0, sizeof(g));
debug3("Looking up %s", src);
- if (glob(src, 0, NULL, &g)) {
+ if (glob(src, GLOB_NOCHECK, NULL, &g)) {
error("File \"%s\" not found.", src);
err = -1;
goto out;
}
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
- if (!is_reg(g.gl_pathv[i])) {
+ if (stat(g.gl_pathv[i], &sb) == -1) {
+ err = -1;
+ error("stat %s: %s", g.gl_pathv[i], strerror(errno));
+ continue;
+ }
+
+ if (!S_ISREG(sb.st_mode)) {
error("skipping non-regular file %s",
g.gl_pathv[i]);
continue;
return (0);
}
+static int
+do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
+{
+ struct sftp_statvfs st;
+ char s_used[FMT_SCALED_STRSIZE];
+ char s_avail[FMT_SCALED_STRSIZE];
+ char s_root[FMT_SCALED_STRSIZE];
+ char s_total[FMT_SCALED_STRSIZE];
+
+ if (do_statvfs(conn, path, &st, 1) == -1)
+ return -1;
+ if (iflag) {
+ printf(" Inodes Used Avail "
+ "(root) %%Capacity\n");
+ printf("%11llu %11llu %11llu %11llu %3llu%%\n",
+ (unsigned long long)st.f_files,
+ (unsigned long long)(st.f_files - st.f_ffree),
+ (unsigned long long)st.f_favail,
+ (unsigned long long)st.f_ffree,
+ (unsigned long long)(100 * (st.f_files - st.f_ffree) /
+ st.f_files));
+ } else if (hflag) {
+ strlcpy(s_used, "error", sizeof(s_used));
+ strlcpy(s_avail, "error", sizeof(s_avail));
+ strlcpy(s_root, "error", sizeof(s_root));
+ strlcpy(s_total, "error", sizeof(s_total));
+ fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
+ fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
+ fmt_scaled(st.f_bfree * st.f_frsize, s_root);
+ fmt_scaled(st.f_blocks * st.f_frsize, s_total);
+ printf(" Size Used Avail (root) %%Capacity\n");
+ printf("%7sB %7sB %7sB %7sB %3llu%%\n",
+ s_total, s_used, s_avail, s_root,
+ (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
+ st.f_blocks));
+ } else {
+ printf(" Size Used Avail "
+ "(root) %%Capacity\n");
+ printf("%12llu %12llu %12llu %12llu %3llu%%\n",
+ (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
+ (unsigned long long)(st.f_frsize *
+ (st.f_blocks - st.f_bfree) / 1024),
+ (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
+ (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
+ (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
+ st.f_blocks));
+ }
+ return 0;
+}
+
/*
* Undo escaping of glob sequences in place. Used to undo extra escaping
* applied in makeargv() when the string is destined for a function that
}
static int
-parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
+parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag,
unsigned long *n_arg, char **path1, char **path2)
{
const char *cmd, *cp = *cpp;
}
/* Get arguments and parse flags */
- *lflag = *pflag = *n_arg = 0;
+ *lflag = *pflag = *hflag = *n_arg = 0;
*path1 = *path2 = NULL;
optidx = 1;
switch (cmdnum) {
if (cmdnum != I_RM)
undo_glob_escape(*path1);
break;
+ case I_DF:
+ if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
+ iflag)) == -1)
+ return -1;
+ /* Default to current directory if no path specified */
+ if (argc - optidx < 1)
+ *path1 = NULL;
+ else {
+ *path1 = xstrdup(argv[optidx]);
+ undo_glob_escape(*path1);
+ }
+ break;
case I_LS:
if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
return(-1);
int err_abort)
{
char *path1, *path2, *tmp;
- int pflag, lflag, iflag, cmdnum, i;
+ int pflag, lflag, iflag, hflag, cmdnum, i;
unsigned long n_arg;
Attrib a, *aa;
char path_buf[MAXPATHLEN];
glob_t g;
path1 = path2 = NULL;
- cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg,
+ cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg,
&path1, &path2);
if (iflag != 0)
path1 = make_absolute(path1, *pwd);
err = do_globbed_ls(conn, path1, tmp, lflag);
break;
+ case I_DF:
+ /* Default to current directory if no path specified */
+ if (path1 == NULL)
+ path1 = xstrdup(*pwd);
+ path1 = make_absolute(path1, *pwd);
+ err = do_df(conn, path1, hflag, iflag);
+ break;
case I_LCHDIR:
if (chdir(path1) == -1) {
error("Couldn't change local directory to "
remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
- if (err != 0 && err_abort)
+ if (err_abort) {
+ err = -1;
break;
- else
+ } else
continue;
}
if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
error("Can't get current ownership of "
"remote file \"%s\"", g.gl_pathv[i]);
- if (err != 0 && err_abort)
+ if (err_abort) {
+ err = -1;
break;
- else
+ } else
continue;
}
aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
"usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
" [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
" [-S program] [-s subsystem | sftp_server] host\n"
- " %s [[user@]host[:file [file]]]\n"
- " %s [[user@]host[:dir[/]]]\n"
+ " %s [user@]host[:file ...]\n"
+ " %s [user@]host[:dir[/]]\n"
" %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
exit(1);
}