📄 sftp.c
字号:
/* * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */#include "includes.h"RCSID("$OpenBSD: sftp.c,v 1.62 2005/02/20 22:59:06 djm Exp $");#ifdef USE_LIBEDIT#include <histedit.h>#elsetypedef void EditLine;#endif#include "buffer.h"#include "xmalloc.h"#include "log.h"#include "pathnames.h"#include "misc.h"#include "sftp.h"#include "sftp-common.h"#include "sftp-client.h"/* File to read commands from */FILE* infile;/* Are we in batchfile mode? */int batchmode = 0;/* Size of buffer used when copying files */size_t copy_buffer_len = 32768;/* Number of concurrent outstanding requests */size_t num_requests = 16;/* PID of ssh transport process */static pid_t sshpid = -1;/* This is set to 0 if the progressmeter is not desired. */int showprogress = 1;/* SIGINT received during command processing */volatile sig_atomic_t interrupted = 0;/* I wish qsort() took a separate ctx for the comparison function...*/int sort_flag;int remote_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */extern char *__progname;/* Separators for interactive commands */#define WHITESPACE " \t\r\n"/* ls flags */#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */#define LS_NAME_SORT 0x08 /* Sort by name (default) */#define LS_TIME_SORT 0x10 /* Sort by mtime */#define LS_SIZE_SORT 0x20 /* Sort by file size */#define LS_REVERSE_SORT 0x40 /* Reverse sort order */#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)/* Commands for interactive mode */#define I_CHDIR 1#define I_CHGRP 2#define I_CHMOD 3#define I_CHOWN 4#define I_GET 5#define I_HELP 6#define I_LCHDIR 7#define I_LLS 8#define I_LMKDIR 9#define I_LPWD 10#define I_LS 11#define I_LUMASK 12#define I_MKDIR 13#define I_PUT 14#define I_PWD 15#define I_QUIT 16#define I_RENAME 17#define I_RM 18#define I_RMDIR 19#define I_SHELL 20#define I_SYMLINK 21#define I_VERSION 22#define I_PROGRESS 23struct CMD { const char *c; const int n;};static const struct CMD cmds[] = { { "bye", I_QUIT }, { "cd", I_CHDIR }, { "chdir", I_CHDIR }, { "chgrp", I_CHGRP }, { "chmod", I_CHMOD }, { "chown", I_CHOWN }, { "dir", I_LS }, { "exit", I_QUIT }, { "get", I_GET }, { "mget", I_GET }, { "help", I_HELP }, { "lcd", I_LCHDIR }, { "lchdir", I_LCHDIR }, { "lls", I_LLS }, { "lmkdir", I_LMKDIR }, { "ln", I_SYMLINK }, { "lpwd", I_LPWD }, { "ls", I_LS }, { "lumask", I_LUMASK }, { "mkdir", I_MKDIR }, { "progress", I_PROGRESS }, { "put", I_PUT }, { "mput", I_PUT }, { "pwd", I_PWD }, { "quit", I_QUIT }, { "rename", I_RENAME }, { "rm", I_RM }, { "rmdir", I_RMDIR }, { "symlink", I_SYMLINK }, { "version", I_VERSION }, { "!", I_SHELL }, { "?", I_HELP }, { NULL, -1}};int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);static voidkillchild(int signo){ if (sshpid > 1) { kill(sshpid, SIGTERM); waitpid(sshpid, NULL, 0); } _exit(1);}static voidcmd_interrupt(int signo){ const char msg[] = "\rInterrupt \n"; int olderrno = errno; write(STDERR_FILENO, msg, sizeof(msg) - 1); interrupted = 1; errno = olderrno;}static voidhelp(void){ printf("Available commands:\n"); printf("cd path Change remote directory to 'path'\n"); printf("lcd path Change local directory to 'path'\n"); 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("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"); printf("ln oldpath newpath Symlink remote file\n"); printf("lmkdir path Create local directory\n"); printf("lpwd Print local working directory\n"); printf("ls [path] Display remote directory listing\n"); printf("lumask umask Set local umask to 'umask'\n"); printf("mkdir path Create remote directory\n"); printf("progress Toggle display of progress meter\n"); printf("put local-path [remote-path] Upload file\n"); printf("pwd Display remote working directory\n"); printf("exit Quit sftp\n"); printf("quit Quit sftp\n"); printf("rename oldpath newpath Rename remote file\n"); printf("rmdir path Remove remote directory\n"); printf("rm path Delete remote file\n"); printf("symlink oldpath newpath Symlink remote file\n"); printf("version Show SFTP version\n"); printf("!command Execute 'command' in local shell\n"); printf("! Escape to local shell\n"); printf("? Synonym for help\n");}static voidlocal_do_shell(const char *args){ int status; char *shell; pid_t pid; if (!*args) args = NULL; if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; if ((pid = fork()) == -1) fatal("Couldn't fork: %s", strerror(errno)); if (pid == 0) { /* XXX: child has pipe fds to ssh subproc open - issue? */ if (args) { debug3("Executing %s -c \"%s\"", shell, args); execl(shell, shell, "-c", args, (char *)NULL); } else { debug3("Executing %s", shell); execl(shell, shell, (char *)NULL); } fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, strerror(errno)); _exit(1); } while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); if (!WIFEXITED(status)) error("Shell exited abormally"); else if (WEXITSTATUS(status)) error("Shell exited with status %d", WEXITSTATUS(status));}static voidlocal_do_ls(const char *args){ if (!args || !*args) local_do_shell(_PATH_LS); else { int len = strlen(_PATH_LS " ") + strlen(args) + 1; char *buf = xmalloc(len); /* XXX: quoting - rip quoting code from ftp? */ snprintf(buf, len, _PATH_LS " %s", args); local_do_shell(buf); xfree(buf); }}/* Strip one path (usually the pwd) from the start of another */static char *path_strip(char *path, char *strip){ size_t len; if (strip == NULL) return (xstrdup(path)); len = strlen(strip); if (strncmp(path, strip, len) == 0) { if (strip[len - 1] != '/' && path[len] == '/') len++; return (xstrdup(path + len)); } return (xstrdup(path));}static char *path_append(char *p1, char *p2){ char *ret; int len = strlen(p1) + strlen(p2) + 2; ret = xmalloc(len); strlcpy(ret, p1, len); if (p1[strlen(p1) - 1] != '/') strlcat(ret, "/", len); strlcat(ret, p2, len); return(ret);}static char *make_absolute(char *p, char *pwd){ char *abs_str; /* Derelativise */ if (p && p[0] != '/') { abs_str = path_append(pwd, p); xfree(p); return(abs_str); } else return(p);}static intinfer_path(const char *p, char **ifp){ char *cp; cp = strrchr(p, '/'); if (cp == NULL) { *ifp = xstrdup(p); return(0); } if (!cp[1]) { error("Invalid path"); return(-1); } *ifp = xstrdup(cp + 1); return(0);}static intparse_getput_flags(const char **cpp, int *pflag){ const char *cp = *cpp; /* Check for flags */ if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { switch (cp[1]) { case 'p': case 'P': *pflag = 1; break; default: error("Invalid flag -%c", cp[1]); return(-1); } cp += 2; *cpp = cp + strspn(cp, WHITESPACE); } return(0);}static intparse_ls_flags(const char **cpp, int *lflag){ const char *cp = *cpp; /* Defaults */ *lflag = LS_NAME_SORT; /* Check for flags */ if (cp++[0] == '-') { for(; strchr(WHITESPACE, *cp) == NULL; cp++) { switch (*cp) { case 'l': *lflag &= ~VIEW_FLAGS; *lflag |= LS_LONG_VIEW; break; case '1': *lflag &= ~VIEW_FLAGS; *lflag |= LS_SHORT_VIEW; break; case 'n': *lflag &= ~VIEW_FLAGS; *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; break; case 'S': *lflag &= ~SORT_FLAGS; *lflag |= LS_SIZE_SORT; break; case 't': *lflag &= ~SORT_FLAGS; *lflag |= LS_TIME_SORT; break; case 'r': *lflag |= LS_REVERSE_SORT; break; case 'f': *lflag &= ~SORT_FLAGS; break; case 'a': *lflag |= LS_SHOW_ALL; break; default: error("Invalid flag -%c", *cp); return(-1); } } *cpp = cp + strspn(cp, WHITESPACE); } return(0);}static intget_pathname(const char **cpp, char **path){ const char *cp = *cpp, *end; char quot; int i, j; cp += strspn(cp, WHITESPACE); if (!*cp) { *cpp = cp; *path = NULL; return (0); } *path = xmalloc(strlen(cp) + 1); /* Check for quoted filenames */ if (*cp == '\"' || *cp == '\'') { quot = *cp++; /* Search for terminating quote, unescape some chars */ for (i = j = 0; i <= strlen(cp); i++) { if (cp[i] == quot) { /* Found quote */ i++; (*path)[j] = '\0'; break; } if (cp[i] == '\0') { /* End of string */ error("Unterminated quote"); goto fail; } if (cp[i] == '\\') { /* Escaped characters */ i++; if (cp[i] != '\'' && cp[i] != '\"' && cp[i] != '\\') { error("Bad escaped character '\\%c'", cp[i]); goto fail; } } (*path)[j++] = cp[i]; } if (j == 0) { error("Empty quotes"); goto fail; } *cpp = cp + i + strspn(cp + i, WHITESPACE); } else { /* Read to end of filename */ end = strpbrk(cp, WHITESPACE); if (end == NULL) end = strchr(cp, '\0'); *cpp = end + strspn(end, WHITESPACE); memcpy(*path, cp, end - cp); (*path)[end - cp] = '\0'; } return (0); fail: xfree(*path); *path = NULL; return (-1);}static intis_dir(char *path){ struct stat sb; /* XXX: report errors? */ if (stat(path, &sb) == -1) return(0); return(sb.st_mode & S_IFDIR);}static intis_reg(char *path){ struct stat sb; if (stat(path, &sb) == -1) fatal("stat %s: %s", path, strerror(errno)); return(S_ISREG(sb.st_mode));}static intremote_is_dir(struct sftp_conn *conn, char *path){ Attrib *a; /* XXX: report errors? */ if ((a = do_stat(conn, path, 1)) == NULL) return(0); if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) return(0); return(a->perm & S_IFDIR);}static intprocess_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag){ char *abs_src = NULL; char *abs_dst = NULL; char *tmp; glob_t g; int err = 0; int i; abs_src = xstrdup(src); abs_src = make_absolute(abs_src, pwd); memset(&g, 0, sizeof(g)); debug3("Looking up %s", abs_src); if (remote_glob(conn, abs_src, 0, NULL, &g)) { error("File \"%s\" not found.", abs_src); err = -1; goto out; } /* If multiple matches, dst must be a directory or unspecified */ if (g.gl_matchc > 1 && dst && !is_dir(dst)) { error("Multiple files match, but \"%s\" is not a directory",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -