diff options
Diffstat (limited to 'sftp.c')
-rw-r--r-- | sftp.c | 141 |
1 files changed, 82 insertions, 59 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */ | 1 | /* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 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 | * |
@@ -132,6 +132,7 @@ extern char *__progname; | |||
132 | #define I_GET 5 | 132 | #define I_GET 5 |
133 | #define I_HELP 6 | 133 | #define I_HELP 6 |
134 | #define I_LCHDIR 7 | 134 | #define I_LCHDIR 7 |
135 | #define I_LINK 25 | ||
135 | #define I_LLS 8 | 136 | #define I_LLS 8 |
136 | #define I_LMKDIR 9 | 137 | #define I_LMKDIR 9 |
137 | #define I_LPWD 10 | 138 | #define I_LPWD 10 |
@@ -176,7 +177,7 @@ static const struct CMD cmds[] = { | |||
176 | { "lchdir", I_LCHDIR, LOCAL }, | 177 | { "lchdir", I_LCHDIR, LOCAL }, |
177 | { "lls", I_LLS, LOCAL }, | 178 | { "lls", I_LLS, LOCAL }, |
178 | { "lmkdir", I_LMKDIR, LOCAL }, | 179 | { "lmkdir", I_LMKDIR, LOCAL }, |
179 | { "ln", I_SYMLINK, REMOTE }, | 180 | { "ln", I_LINK, REMOTE }, |
180 | { "lpwd", I_LPWD, LOCAL }, | 181 | { "lpwd", I_LPWD, LOCAL }, |
181 | { "ls", I_LS, REMOTE }, | 182 | { "ls", I_LS, REMOTE }, |
182 | { "lumask", I_LUMASK, NOARGS }, | 183 | { "lumask", I_LUMASK, NOARGS }, |
@@ -240,7 +241,7 @@ help(void) | |||
240 | "lcd path Change local directory to 'path'\n" | 241 | "lcd path Change local directory to 'path'\n" |
241 | "lls [ls-options [path]] Display local directory listing\n" | 242 | "lls [ls-options [path]] Display local directory listing\n" |
242 | "lmkdir path Create local directory\n" | 243 | "lmkdir path Create local directory\n" |
243 | "ln oldpath newpath Symlink remote file\n" | 244 | "ln [-s] oldpath newpath Link remote file (-s for symlink)\n" |
244 | "lpwd Print local working directory\n" | 245 | "lpwd Print local working directory\n" |
245 | "ls [-1afhlnrSt] [path] Display remote directory listing\n" | 246 | "ls [-1afhlnrSt] [path] Display remote directory listing\n" |
246 | "lumask umask Set local umask to 'umask'\n" | 247 | "lumask umask Set local umask to 'umask'\n" |
@@ -269,7 +270,7 @@ local_do_shell(const char *args) | |||
269 | if (!*args) | 270 | if (!*args) |
270 | args = NULL; | 271 | args = NULL; |
271 | 272 | ||
272 | if ((shell = getenv("SHELL")) == NULL) | 273 | if ((shell = getenv("SHELL")) == NULL || *shell == '\0') |
273 | shell = _PATH_BSHELL; | 274 | shell = _PATH_BSHELL; |
274 | 275 | ||
275 | if ((pid = fork()) == -1) | 276 | if ((pid = fork()) == -1) |
@@ -377,6 +378,30 @@ parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag, | |||
377 | } | 378 | } |
378 | 379 | ||
379 | static int | 380 | static int |
381 | parse_link_flags(const char *cmd, char **argv, int argc, int *sflag) | ||
382 | { | ||
383 | extern int opterr, optind, optopt, optreset; | ||
384 | int ch; | ||
385 | |||
386 | optind = optreset = 1; | ||
387 | opterr = 0; | ||
388 | |||
389 | *sflag = 0; | ||
390 | while ((ch = getopt(argc, argv, "s")) != -1) { | ||
391 | switch (ch) { | ||
392 | case 's': | ||
393 | *sflag = 1; | ||
394 | break; | ||
395 | default: | ||
396 | error("%s: Invalid flag -%c", cmd, optopt); | ||
397 | return -1; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | return optind; | ||
402 | } | ||
403 | |||
404 | static int | ||
380 | parse_ls_flags(char **argv, int argc, int *lflag) | 405 | parse_ls_flags(char **argv, int argc, int *lflag) |
381 | { | 406 | { |
382 | extern int opterr, optind, optopt, optreset; | 407 | extern int opterr, optind, optopt, optreset; |
@@ -758,18 +783,22 @@ static int | |||
758 | do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | 783 | do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, |
759 | int lflag) | 784 | int lflag) |
760 | { | 785 | { |
761 | glob_t g; | ||
762 | u_int i, c = 1, colspace = 0, columns = 1; | ||
763 | Attrib *a = NULL; | 786 | Attrib *a = NULL; |
787 | char *fname, *lname; | ||
788 | glob_t g; | ||
789 | int err; | ||
790 | struct winsize ws; | ||
791 | u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80; | ||
764 | 792 | ||
765 | memset(&g, 0, sizeof(g)); | 793 | memset(&g, 0, sizeof(g)); |
766 | 794 | ||
767 | if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE, | 795 | if (remote_glob(conn, path, |
768 | NULL, &g) || (g.gl_pathc && !g.gl_matchc)) { | 796 | GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT, NULL, &g) || |
797 | (g.gl_pathc && !g.gl_matchc)) { | ||
769 | if (g.gl_pathc) | 798 | if (g.gl_pathc) |
770 | globfree(&g); | 799 | globfree(&g); |
771 | error("Can't ls: \"%s\" not found", path); | 800 | error("Can't ls: \"%s\" not found", path); |
772 | return (-1); | 801 | return -1; |
773 | } | 802 | } |
774 | 803 | ||
775 | if (interrupted) | 804 | if (interrupted) |
@@ -779,59 +808,35 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
779 | * If the glob returns a single match and it is a directory, | 808 | * If the glob returns a single match and it is a directory, |
780 | * then just list its contents. | 809 | * then just list its contents. |
781 | */ | 810 | */ |
782 | if (g.gl_matchc == 1) { | 811 | if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && |
783 | if ((a = do_lstat(conn, g.gl_pathv[0], 1)) == NULL) { | 812 | S_ISDIR(g.gl_statv[0]->st_mode)) { |
784 | globfree(&g); | 813 | err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); |
785 | return (-1); | 814 | globfree(&g); |
786 | } | 815 | return err; |
787 | if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && | ||
788 | S_ISDIR(a->perm)) { | ||
789 | int err; | ||
790 | |||
791 | err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); | ||
792 | globfree(&g); | ||
793 | return (err); | ||
794 | } | ||
795 | } | 816 | } |
796 | 817 | ||
797 | if (!(lflag & LS_SHORT_VIEW)) { | 818 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) |
798 | u_int m = 0, width = 80; | 819 | width = ws.ws_col; |
799 | struct winsize ws; | ||
800 | 820 | ||
821 | if (!(lflag & LS_SHORT_VIEW)) { | ||
801 | /* Count entries for sort and find longest filename */ | 822 | /* Count entries for sort and find longest filename */ |
802 | for (i = 0; g.gl_pathv[i]; i++) | 823 | for (i = 0; g.gl_pathv[i]; i++) |
803 | m = MAX(m, strlen(g.gl_pathv[i])); | 824 | m = MAX(m, strlen(g.gl_pathv[i])); |
804 | 825 | ||
805 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) | ||
806 | width = ws.ws_col; | ||
807 | |||
808 | columns = width / (m + 2); | 826 | columns = width / (m + 2); |
809 | columns = MAX(columns, 1); | 827 | columns = MAX(columns, 1); |
810 | colspace = width / columns; | 828 | colspace = width / columns; |
811 | } | 829 | } |
812 | 830 | ||
813 | for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { | 831 | for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) { |
814 | char *fname; | ||
815 | |||
816 | fname = path_strip(g.gl_pathv[i], strip_path); | 832 | fname = path_strip(g.gl_pathv[i], strip_path); |
817 | |||
818 | if (lflag & LS_LONG_VIEW) { | 833 | if (lflag & LS_LONG_VIEW) { |
819 | char *lname; | 834 | if (g.gl_statv[i] == NULL) { |
820 | struct stat sb; | 835 | error("no stat information for %s", fname); |
821 | 836 | continue; | |
822 | /* | 837 | } |
823 | * XXX: this is slow - 1 roundtrip per path | 838 | lname = ls_file(fname, g.gl_statv[i], 1, |
824 | * A solution to this is to fork glob() and | 839 | (lflag & LS_SI_UNITS)); |
825 | * build a sftp specific version which keeps the | ||
826 | * attribs (which currently get thrown away) | ||
827 | * that the server returns as well as the filenames. | ||
828 | */ | ||
829 | memset(&sb, 0, sizeof(sb)); | ||
830 | if (a == NULL) | ||
831 | a = do_lstat(conn, g.gl_pathv[i], 1); | ||
832 | if (a != NULL) | ||
833 | attrib_to_stat(a, &sb); | ||
834 | lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); | ||
835 | printf("%s\n", lname); | 840 | printf("%s\n", lname); |
836 | xfree(lname); | 841 | xfree(lname); |
837 | } else { | 842 | } else { |
@@ -852,7 +857,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
852 | if (g.gl_pathc) | 857 | if (g.gl_pathc) |
853 | globfree(&g); | 858 | globfree(&g); |
854 | 859 | ||
855 | return (0); | 860 | return 0; |
856 | } | 861 | } |
857 | 862 | ||
858 | static int | 863 | static int |
@@ -1108,7 +1113,7 @@ makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, | |||
1108 | 1113 | ||
1109 | static int | 1114 | static int |
1110 | parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, | 1115 | parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, |
1111 | int *hflag, unsigned long *n_arg, char **path1, char **path2) | 1116 | int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) |
1112 | { | 1117 | { |
1113 | const char *cmd, *cp = *cpp; | 1118 | const char *cmd, *cp = *cpp; |
1114 | char *cp2, **argv; | 1119 | char *cp2, **argv; |
@@ -1158,7 +1163,8 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, | |||
1158 | switch (cmdnum) { | 1163 | switch (cmdnum) { |
1159 | case I_GET: | 1164 | case I_GET: |
1160 | case I_PUT: | 1165 | case I_PUT: |
1161 | if ((optidx = parse_getput_flags(cmd, argv, argc, pflag, rflag)) == -1) | 1166 | if ((optidx = parse_getput_flags(cmd, argv, argc, |
1167 | pflag, rflag)) == -1) | ||
1162 | return -1; | 1168 | return -1; |
1163 | /* Get first pathname (mandatory) */ | 1169 | /* Get first pathname (mandatory) */ |
1164 | if (argc - optidx < 1) { | 1170 | if (argc - optidx < 1) { |
@@ -1174,8 +1180,11 @@ parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag, | |||
1174 | undo_glob_escape(*path2); | 1180 | undo_glob_escape(*path2); |
1175 | } | 1181 | } |
1176 | break; | 1182 | break; |
1177 | case I_RENAME: | 1183 | case I_LINK: |
1184 | if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) | ||
1185 | return -1; | ||
1178 | case I_SYMLINK: | 1186 | case I_SYMLINK: |
1187 | case I_RENAME: | ||
1179 | if (argc - optidx < 2) { | 1188 | if (argc - optidx < 2) { |
1180 | error("You must specify two paths after a %s " | 1189 | error("You must specify two paths after a %s " |
1181 | "command.", cmd); | 1190 | "command.", cmd); |
@@ -1278,7 +1287,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1278 | int err_abort) | 1287 | int err_abort) |
1279 | { | 1288 | { |
1280 | char *path1, *path2, *tmp; | 1289 | char *path1, *path2, *tmp; |
1281 | int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, cmdnum, i; | 1290 | int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0; |
1291 | int cmdnum, i; | ||
1282 | unsigned long n_arg = 0; | 1292 | unsigned long n_arg = 0; |
1283 | Attrib a, *aa; | 1293 | Attrib a, *aa; |
1284 | char path_buf[MAXPATHLEN]; | 1294 | char path_buf[MAXPATHLEN]; |
@@ -1286,8 +1296,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1286 | glob_t g; | 1296 | glob_t g; |
1287 | 1297 | ||
1288 | path1 = path2 = NULL; | 1298 | path1 = path2 = NULL; |
1289 | cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, &n_arg, | 1299 | cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag, |
1290 | &path1, &path2); | 1300 | &sflag, &n_arg, &path1, &path2); |
1291 | 1301 | ||
1292 | if (iflag != 0) | 1302 | if (iflag != 0) |
1293 | err_abort = 0; | 1303 | err_abort = 0; |
@@ -1315,8 +1325,11 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1315 | err = do_rename(conn, path1, path2); | 1325 | err = do_rename(conn, path1, path2); |
1316 | break; | 1326 | break; |
1317 | case I_SYMLINK: | 1327 | case I_SYMLINK: |
1328 | sflag = 1; | ||
1329 | case I_LINK: | ||
1330 | path1 = make_absolute(path1, *pwd); | ||
1318 | path2 = make_absolute(path2, *pwd); | 1331 | path2 = make_absolute(path2, *pwd); |
1319 | err = do_symlink(conn, path1, path2); | 1332 | err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); |
1320 | break; | 1333 | break; |
1321 | case I_RM: | 1334 | case I_RM: |
1322 | path1 = make_absolute(path1, *pwd); | 1335 | path1 = make_absolute(path1, *pwd); |
@@ -1745,6 +1758,7 @@ complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, | |||
1745 | case '"': | 1758 | case '"': |
1746 | case '\\': | 1759 | case '\\': |
1747 | case '\t': | 1760 | case '\t': |
1761 | case '[': | ||
1748 | case ' ': | 1762 | case ' ': |
1749 | if (quote == '\0' || tmp2[i] == quote) { | 1763 | if (quote == '\0' || tmp2[i] == quote) { |
1750 | if (el_insertstr(el, ins) == -1) | 1764 | if (el_insertstr(el, ins) == -1) |
@@ -1874,7 +1888,7 @@ interactive_loop(struct sftp_conn *conn, char *file1, char *file2) | |||
1874 | 1888 | ||
1875 | /* Tab Completion */ | 1889 | /* Tab Completion */ |
1876 | el_set(el, EL_ADDFN, "ftp-complete", | 1890 | el_set(el, EL_ADDFN, "ftp-complete", |
1877 | "Context senstive argument completion", complete); | 1891 | "Context sensitive argument completion", complete); |
1878 | complete_ctx.conn = conn; | 1892 | complete_ctx.conn = conn; |
1879 | complete_ctx.remote_pathp = &remote_path; | 1893 | complete_ctx.remote_pathp = &remote_path; |
1880 | el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); | 1894 | el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); |
@@ -2054,7 +2068,7 @@ usage(void) | |||
2054 | fprintf(stderr, | 2068 | fprintf(stderr, |
2055 | "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" | 2069 | "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" |
2056 | " [-D sftp_server_path] [-F ssh_config] " | 2070 | " [-D sftp_server_path] [-F ssh_config] " |
2057 | "[-i identity_file]\n" | 2071 | "[-i identity_file] [-l limit]\n" |
2058 | " [-o ssh_option] [-P port] [-R num_requests] " | 2072 | " [-o ssh_option] [-P port] [-R num_requests] " |
2059 | "[-S program]\n" | 2073 | "[-S program]\n" |
2060 | " [-s subsystem | sftp_server] host\n" | 2074 | " [-s subsystem | sftp_server] host\n" |
@@ -2073,6 +2087,7 @@ main(int argc, char **argv) | |||
2073 | int debug_level = 0, sshver = 2; | 2087 | int debug_level = 0, sshver = 2; |
2074 | char *file1 = NULL, *sftp_server = NULL; | 2088 | char *file1 = NULL, *sftp_server = NULL; |
2075 | char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; | 2089 | char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; |
2090 | const char *errstr; | ||
2076 | LogLevel ll = SYSLOG_LEVEL_INFO; | 2091 | LogLevel ll = SYSLOG_LEVEL_INFO; |
2077 | arglist args; | 2092 | arglist args; |
2078 | extern int optind; | 2093 | extern int optind; |
@@ -2080,6 +2095,7 @@ main(int argc, char **argv) | |||
2080 | struct sftp_conn *conn; | 2095 | struct sftp_conn *conn; |
2081 | size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; | 2096 | size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; |
2082 | size_t num_requests = DEFAULT_NUM_REQUESTS; | 2097 | size_t num_requests = DEFAULT_NUM_REQUESTS; |
2098 | long long limit_kbps = 0; | ||
2083 | 2099 | ||
2084 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 2100 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
2085 | sanitise_stdfd(); | 2101 | sanitise_stdfd(); |
@@ -2097,7 +2113,7 @@ main(int argc, char **argv) | |||
2097 | infile = stdin; | 2113 | infile = stdin; |
2098 | 2114 | ||
2099 | while ((ch = getopt(argc, argv, | 2115 | while ((ch = getopt(argc, argv, |
2100 | "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { | 2116 | "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { |
2101 | switch (ch) { | 2117 | switch (ch) { |
2102 | /* Passed through to ssh(1) */ | 2118 | /* Passed through to ssh(1) */ |
2103 | case '4': | 2119 | case '4': |
@@ -2158,6 +2174,13 @@ main(int argc, char **argv) | |||
2158 | case 'D': | 2174 | case 'D': |
2159 | sftp_direct = optarg; | 2175 | sftp_direct = optarg; |
2160 | break; | 2176 | break; |
2177 | case 'l': | ||
2178 | limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, | ||
2179 | &errstr); | ||
2180 | if (errstr != NULL) | ||
2181 | usage(); | ||
2182 | limit_kbps *= 1024; /* kbps */ | ||
2183 | break; | ||
2161 | case 'r': | 2184 | case 'r': |
2162 | global_rflag = 1; | 2185 | global_rflag = 1; |
2163 | break; | 2186 | break; |
@@ -2235,7 +2258,7 @@ main(int argc, char **argv) | |||
2235 | } | 2258 | } |
2236 | freeargs(&args); | 2259 | freeargs(&args); |
2237 | 2260 | ||
2238 | conn = do_init(in, out, copy_buffer_len, num_requests); | 2261 | conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); |
2239 | if (conn == NULL) | 2262 | if (conn == NULL) |
2240 | fatal("Couldn't initialise connection to server"); | 2263 | fatal("Couldn't initialise connection to server"); |
2241 | 2264 | ||