Bug Summary

File:src/usr.bin/ssh/sftp/../sftp.c
Warning:line 2115, column 2
Value stored to 'argv' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name sftp.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/ssh/sftp/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.bin/ssh/sftp/.. -D WITH_OPENSSL -D WITH_ZLIB -D WITH_DSA -D ENABLE_PKCS11 -D HAVE_DLOPEN -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/ssh/sftp/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/ssh/sftp/../sftp.c
1/* $OpenBSD: sftp.c,v 1.236 2023/09/10 23:12:32 djm Exp $ */
2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/types.h>
19#include <sys/ioctl.h>
20#include <sys/wait.h>
21#include <sys/stat.h>
22#include <sys/socket.h>
23#include <sys/statvfs.h>
24
25#include <ctype.h>
26#include <errno(*__errno()).h>
27#include <glob.h>
28#include <histedit.h>
29#include <paths.h>
30#include <libgen.h>
31#include <locale.h>
32#include <signal.h>
33#include <stdarg.h>
34#include <stdlib.h>
35#include <stdio.h>
36#include <string.h>
37#include <unistd.h>
38#include <limits.h>
39#include <util.h>
40
41#include "xmalloc.h"
42#include "log.h"
43#include "pathnames.h"
44#include "misc.h"
45#include "utf8.h"
46
47#include "sftp.h"
48#include "ssherr.h"
49#include "sshbuf.h"
50#include "sftp-common.h"
51#include "sftp-client.h"
52#include "sftp-usergroup.h"
53
54/* File to read commands from */
55FILE* infile;
56
57/* Are we in batchfile mode? */
58int batchmode = 0;
59
60/* PID of ssh transport process */
61static volatile pid_t sshpid = -1;
62
63/* Suppress diagnostic messages */
64int quiet = 0;
65
66/* This is set to 0 if the progressmeter is not desired. */
67int showprogress = 1;
68
69/* When this option is set, we always recursively download/upload directories */
70int global_rflag = 0;
71
72/* When this option is set, we resume download or upload if possible */
73int global_aflag = 0;
74
75/* When this option is set, the file transfers will always preserve times */
76int global_pflag = 0;
77
78/* When this option is set, transfers will have fsync() called on each file */
79int global_fflag = 0;
80
81/* SIGINT received during command processing */
82volatile sig_atomic_t interrupted = 0;
83
84/* I wish qsort() took a separate ctx for the comparison function...*/
85int sort_flag;
86glob_t *sort_glob;
87
88/* Context used for commandline completion */
89struct complete_ctx {
90 struct sftp_conn *conn;
91 char **remote_pathp;
92};
93
94int sftp_glob(struct sftp_conn *, const char *, int,
95 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
96
97/* Separators for interactive commands */
98#define WHITESPACE" \t\r\n" " \t\r\n"
99
100/* ls flags */
101#define LS_LONG_VIEW0x0001 0x0001 /* Full view ala ls -l */
102#define LS_SHORT_VIEW0x0002 0x0002 /* Single row view ala ls -1 */
103#define LS_NUMERIC_VIEW0x0004 0x0004 /* Long view with numeric uid/gid */
104#define LS_NAME_SORT0x0008 0x0008 /* Sort by name (default) */
105#define LS_TIME_SORT0x0010 0x0010 /* Sort by mtime */
106#define LS_SIZE_SORT0x0020 0x0020 /* Sort by file size */
107#define LS_REVERSE_SORT0x0040 0x0040 /* Reverse sort order */
108#define LS_SHOW_ALL0x0080 0x0080 /* Don't skip filenames starting with '.' */
109#define LS_SI_UNITS0x0100 0x0100 /* Display sizes as K, M, G, etc. */
110
111#define VIEW_FLAGS(0x0001|0x0002|0x0004|0x0100) (LS_LONG_VIEW0x0001|LS_SHORT_VIEW0x0002|LS_NUMERIC_VIEW0x0004|LS_SI_UNITS0x0100)
112#define SORT_FLAGS(0x0008|0x0010|0x0020) (LS_NAME_SORT0x0008|LS_TIME_SORT0x0010|LS_SIZE_SORT0x0020)
113
114/* Commands for interactive mode */
115enum sftp_command {
116 I_CHDIR = 1,
117 I_CHGRP,
118 I_CHMOD,
119 I_CHOWN,
120 I_COPY,
121 I_DF,
122 I_GET,
123 I_HELP,
124 I_LCHDIR,
125 I_LINK,
126 I_LLS,
127 I_LMKDIR,
128 I_LPWD,
129 I_LS,
130 I_LUMASK,
131 I_MKDIR,
132 I_PUT,
133 I_PWD,
134 I_QUIT,
135 I_REGET,
136 I_RENAME,
137 I_REPUT,
138 I_RM,
139 I_RMDIR,
140 I_SHELL,
141 I_SYMLINK,
142 I_VERSION,
143 I_PROGRESS,
144};
145
146struct CMD {
147 const char *c;
148 const int n;
149 const int t; /* Completion type for the first argument */
150 const int t2; /* completion type for the optional second argument */
151};
152
153/* Type of completion */
154#define NOARGS0 0
155#define REMOTE1 1
156#define LOCAL2 2
157
158static const struct CMD cmds[] = {
159 { "bye", I_QUIT, NOARGS0, NOARGS0 },
160 { "cd", I_CHDIR, REMOTE1, NOARGS0 },
161 { "chdir", I_CHDIR, REMOTE1, NOARGS0 },
162 { "chgrp", I_CHGRP, REMOTE1, NOARGS0 },
163 { "chmod", I_CHMOD, REMOTE1, NOARGS0 },
164 { "chown", I_CHOWN, REMOTE1, NOARGS0 },
165 { "copy", I_COPY, REMOTE1, LOCAL2 },
166 { "cp", I_COPY, REMOTE1, LOCAL2 },
167 { "df", I_DF, REMOTE1, NOARGS0 },
168 { "dir", I_LS, REMOTE1, NOARGS0 },
169 { "exit", I_QUIT, NOARGS0, NOARGS0 },
170 { "get", I_GET, REMOTE1, LOCAL2 },
171 { "help", I_HELP, NOARGS0, NOARGS0 },
172 { "lcd", I_LCHDIR, LOCAL2, NOARGS0 },
173 { "lchdir", I_LCHDIR, LOCAL2, NOARGS0 },
174 { "lls", I_LLS, LOCAL2, NOARGS0 },
175 { "lmkdir", I_LMKDIR, LOCAL2, NOARGS0 },
176 { "ln", I_LINK, REMOTE1, REMOTE1 },
177 { "lpwd", I_LPWD, LOCAL2, NOARGS0 },
178 { "ls", I_LS, REMOTE1, NOARGS0 },
179 { "lumask", I_LUMASK, NOARGS0, NOARGS0 },
180 { "mkdir", I_MKDIR, REMOTE1, NOARGS0 },
181 { "mget", I_GET, REMOTE1, LOCAL2 },
182 { "mput", I_PUT, LOCAL2, REMOTE1 },
183 { "progress", I_PROGRESS, NOARGS0, NOARGS0 },
184 { "put", I_PUT, LOCAL2, REMOTE1 },
185 { "pwd", I_PWD, REMOTE1, NOARGS0 },
186 { "quit", I_QUIT, NOARGS0, NOARGS0 },
187 { "reget", I_REGET, REMOTE1, LOCAL2 },
188 { "rename", I_RENAME, REMOTE1, REMOTE1 },
189 { "reput", I_REPUT, LOCAL2, REMOTE1 },
190 { "rm", I_RM, REMOTE1, NOARGS0 },
191 { "rmdir", I_RMDIR, REMOTE1, NOARGS0 },
192 { "symlink", I_SYMLINK, REMOTE1, REMOTE1 },
193 { "version", I_VERSION, NOARGS0, NOARGS0 },
194 { "!", I_SHELL, NOARGS0, NOARGS0 },
195 { "?", I_HELP, NOARGS0, NOARGS0 },
196 { NULL((void *)0), -1, -1, -1 }
197};
198
199static void
200killchild(int signo)
201{
202 pid_t pid;
203
204 pid = sshpid;
205 if (pid > 1) {
206 kill(pid, SIGTERM15);
207 (void)waitpid(pid, NULL((void *)0), 0);
208 }
209
210 _exit(1);
211}
212
213static void
214suspchild(int signo)
215{
216 if (sshpid > 1) {
217 kill(sshpid, signo);
218 while (waitpid(sshpid, NULL((void *)0), WUNTRACED0x02) == -1 && errno(*__errno()) == EINTR4)
219 continue;
220 }
221 kill(getpid(), SIGSTOP17);
222}
223
224static void
225cmd_interrupt(int signo)
226{
227 const char msg[] = "\rInterrupt \n";
228 int olderrno = errno(*__errno());
229
230 (void)write(STDERR_FILENO2, msg, sizeof(msg) - 1);
231 interrupted = 1;
232 errno(*__errno()) = olderrno;
233}
234
235static void
236read_interrupt(int signo)
237{
238 interrupted = 1;
239}
240
241static void
242sigchld_handler(int sig)
243{
244 int save_errno = errno(*__errno());
245 pid_t pid;
246 const char msg[] = "\rConnection closed. \n";
247
248 /* Report if ssh transport process dies. */
249 while ((pid = waitpid(sshpid, NULL((void *)0), WNOHANG0x01)) == -1 && errno(*__errno()) == EINTR4)
250 continue;
251 if (pid == sshpid) {
252 if (!quiet)
253 (void)write(STDERR_FILENO2, msg, sizeof(msg) - 1);
254 sshpid = -1;
255 }
256
257 errno(*__errno()) = save_errno;
258}
259
260static void
261help(void)
262{
263 printf("Available commands:\n"
264 "bye Quit sftp\n"
265 "cd path Change remote directory to 'path'\n"
266 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
267 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
268 "chown [-h] own path Change owner of file 'path' to 'own'\n"
269 "copy oldpath newpath Copy remote file\n"
270 "cp oldpath newpath Copy remote file\n"
271 "df [-hi] [path] Display statistics for current directory or\n"
272 " filesystem containing 'path'\n"
273 "exit Quit sftp\n"
274 "get [-afpR] remote [local] Download file\n"
275 "help Display this help text\n"
276 "lcd path Change local directory to 'path'\n"
277 "lls [ls-options [path]] Display local directory listing\n"
278 "lmkdir path Create local directory\n"
279 "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
280 "lpwd Print local working directory\n"
281 "ls [-1afhlnrSt] [path] Display remote directory listing\n"
282 "lumask umask Set local umask to 'umask'\n"
283 "mkdir path Create remote directory\n"
284 "progress Toggle display of progress meter\n"
285 "put [-afpR] local [remote] Upload file\n"
286 "pwd Display remote working directory\n"
287 "quit Quit sftp\n"
288 "reget [-fpR] remote [local] Resume download file\n"
289 "rename oldpath newpath Rename remote file\n"
290 "reput [-fpR] local [remote] Resume upload file\n"
291 "rm path Delete remote file\n"
292 "rmdir path Remove remote directory\n"
293 "symlink oldpath newpath Symlink remote file\n"
294 "version Show SFTP version\n"
295 "!command Execute 'command' in local shell\n"
296 "! Escape to local shell\n"
297 "? Synonym for help\n");
298}
299
300static void
301local_do_shell(const char *args)
302{
303 int status;
304 char *shell;
305 pid_t pid;
306
307 if (!*args)
308 args = NULL((void *)0);
309
310 if ((shell = getenv("SHELL")) == NULL((void *)0) || *shell == '\0')
311 shell = _PATH_BSHELL"/bin/sh";
312
313 if ((pid = fork()) == -1)
314 fatal("Couldn't fork: %s", strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 314
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't fork: %s", strerror
((*__errno())))
;
315
316 if (pid == 0) {
317 /* XXX: child has pipe fds to ssh subproc open - issue? */
318 if (args) {
319 debug3("Executing %s -c \"%s\"", shell, args)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 319, 0
, SYSLOG_LEVEL_DEBUG3, ((void *)0), "Executing %s -c \"%s\"",
shell, args)
;
320 execl(shell, shell, "-c", args, (char *)NULL((void *)0));
321 } else {
322 debug3("Executing %s", shell)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 322, 0
, SYSLOG_LEVEL_DEBUG3, ((void *)0), "Executing %s", shell)
;
323 execl(shell, shell, (char *)NULL((void *)0));
324 }
325 fprintf(stderr(&__sF[2]), "Couldn't execute \"%s\": %s\n", shell,
326 strerror(errno(*__errno())));
327 _exit(1);
328 }
329 while (waitpid(pid, &status, 0) == -1)
330 if (errno(*__errno()) != EINTR4)
331 fatal("Couldn't wait for child: %s", strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 331
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't wait for child: %s"
, strerror((*__errno())))
;
332 if (!WIFEXITED(status)(((status) & 0177) == 0))
333 error("Shell exited abnormally")sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 333, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Shell exited abnormally")
;
334 else if (WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff))
335 error("Shell exited with status %d", WEXITSTATUS(status))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 335, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Shell exited with status %d"
, (int)(((unsigned)(status) >> 8) & 0xff))
;
336}
337
338static void
339local_do_ls(const char *args)
340{
341 if (!args || !*args)
342 local_do_shell(_PATH_LS"ls");
343 else {
344 int len = strlen(_PATH_LS"ls" " ") + strlen(args) + 1;
345 char *buf = xmalloc(len);
346
347 /* XXX: quoting - rip quoting code from ftp? */
348 snprintf(buf, len, _PATH_LS"ls" " %s", args);
349 local_do_shell(buf);
350 free(buf);
351 }
352}
353
354/* Strip one path (usually the pwd) from the start of another */
355static char *
356path_strip(const char *path, const char *strip)
357{
358 size_t len;
359
360 if (strip == NULL((void *)0))
361 return (xstrdup(path));
362
363 len = strlen(strip);
364 if (strncmp(path, strip, len) == 0) {
365 if (strip[len - 1] != '/' && path[len] == '/')
366 len++;
367 return (xstrdup(path + len));
368 }
369
370 return (xstrdup(path));
371}
372
373static int
374parse_getput_flags(const char *cmd, char **argv, int argc,
375 int *aflag, int *fflag, int *pflag, int *rflag)
376{
377 extern int opterr, optind, optopt, optreset;
378 int ch;
379
380 optind = optreset = 1;
381 opterr = 0;
382
383 *aflag = *fflag = *rflag = *pflag = 0;
384 while ((ch = getopt(argc, argv, "afPpRr")) != -1) {
385 switch (ch) {
386 case 'a':
387 *aflag = 1;
388 break;
389 case 'f':
390 *fflag = 1;
391 break;
392 case 'p':
393 case 'P':
394 *pflag = 1;
395 break;
396 case 'r':
397 case 'R':
398 *rflag = 1;
399 break;
400 default:
401 error("%s: Invalid flag -%c", cmd, optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 401, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: Invalid flag -%c", cmd
, optopt)
;
402 return -1;
403 }
404 }
405
406 return optind;
407}
408
409static int
410parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
411{
412 extern int opterr, optind, optopt, optreset;
413 int ch;
414
415 optind = optreset = 1;
416 opterr = 0;
417
418 *sflag = 0;
419 while ((ch = getopt(argc, argv, "s")) != -1) {
420 switch (ch) {
421 case 's':
422 *sflag = 1;
423 break;
424 default:
425 error("%s: Invalid flag -%c", cmd, optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 425, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: Invalid flag -%c", cmd
, optopt)
;
426 return -1;
427 }
428 }
429
430 return optind;
431}
432
433static int
434parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag)
435{
436 extern int opterr, optind, optopt, optreset;
437 int ch;
438
439 optind = optreset = 1;
440 opterr = 0;
441
442 *lflag = 0;
443 while ((ch = getopt(argc, argv, "l")) != -1) {
444 switch (ch) {
445 case 'l':
446 *lflag = 1;
447 break;
448 default:
449 error("%s: Invalid flag -%c", cmd, optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 449, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: Invalid flag -%c", cmd
, optopt)
;
450 return -1;
451 }
452 }
453
454 return optind;
455}
456
457static int
458parse_ls_flags(char **argv, int argc, int *lflag)
459{
460 extern int opterr, optind, optopt, optreset;
461 int ch;
462
463 optind = optreset = 1;
464 opterr = 0;
465
466 *lflag = LS_NAME_SORT0x0008;
467 while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
468 switch (ch) {
469 case '1':
470 *lflag &= ~VIEW_FLAGS(0x0001|0x0002|0x0004|0x0100);
471 *lflag |= LS_SHORT_VIEW0x0002;
472 break;
473 case 'S':
474 *lflag &= ~SORT_FLAGS(0x0008|0x0010|0x0020);
475 *lflag |= LS_SIZE_SORT0x0020;
476 break;
477 case 'a':
478 *lflag |= LS_SHOW_ALL0x0080;
479 break;
480 case 'f':
481 *lflag &= ~SORT_FLAGS(0x0008|0x0010|0x0020);
482 break;
483 case 'h':
484 *lflag |= LS_SI_UNITS0x0100;
485 break;
486 case 'l':
487 *lflag &= ~LS_SHORT_VIEW0x0002;
488 *lflag |= LS_LONG_VIEW0x0001;
489 break;
490 case 'n':
491 *lflag &= ~LS_SHORT_VIEW0x0002;
492 *lflag |= LS_NUMERIC_VIEW0x0004|LS_LONG_VIEW0x0001;
493 break;
494 case 'r':
495 *lflag |= LS_REVERSE_SORT0x0040;
496 break;
497 case 't':
498 *lflag &= ~SORT_FLAGS(0x0008|0x0010|0x0020);
499 *lflag |= LS_TIME_SORT0x0010;
500 break;
501 default:
502 error("ls: Invalid flag -%c", optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 502, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "ls: Invalid flag -%c", optopt
)
;
503 return -1;
504 }
505 }
506
507 return optind;
508}
509
510static int
511parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
512{
513 extern int opterr, optind, optopt, optreset;
514 int ch;
515
516 optind = optreset = 1;
517 opterr = 0;
518
519 *hflag = *iflag = 0;
520 while ((ch = getopt(argc, argv, "hi")) != -1) {
521 switch (ch) {
522 case 'h':
523 *hflag = 1;
524 break;
525 case 'i':
526 *iflag = 1;
527 break;
528 default:
529 error("%s: Invalid flag -%c", cmd, optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 529, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: Invalid flag -%c", cmd
, optopt)
;
530 return -1;
531 }
532 }
533
534 return optind;
535}
536
537static int
538parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
539{
540 extern int opterr, optind, optopt, optreset;
541 int ch;
542
543 optind = optreset = 1;
544 opterr = 0;
545
546 *hflag = 0;
547 while ((ch = getopt(argc, argv, "h")) != -1) {
548 switch (ch) {
549 case 'h':
550 *hflag = 1;
551 break;
552 default:
553 error("%s: Invalid flag -%c", cmd, optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 553, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: Invalid flag -%c", cmd
, optopt)
;
554 return -1;
555 }
556 }
557
558 return optind;
559}
560
561static int
562parse_no_flags(const char *cmd, char **argv, int argc)
563{
564 extern int opterr, optind, optopt, optreset;
565 int ch;
566
567 optind = optreset = 1;
568 opterr = 0;
569
570 while ((ch = getopt(argc, argv, "")) != -1) {
571 switch (ch) {
572 default:
573 error("%s: Invalid flag -%c", cmd, optopt)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 573, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "%s: Invalid flag -%c", cmd
, optopt)
;
574 return -1;
575 }
576 }
577
578 return optind;
579}
580
581static char *
582escape_glob(const char *s)
583{
584 size_t i, o, len;
585 char *ret;
586
587 len = strlen(s);
588 ret = xcalloc(2, len + 1);
589 for (i = o = 0; i < len; i++) {
590 if (strchr("[]?*\\", s[i]) != NULL((void *)0))
591 ret[o++] = '\\';
592 ret[o++] = s[i];
593 }
594 ret[o++] = '\0';
595 return ret;
596}
597
598/*
599 * Arg p must be dynamically allocated. make_absolute will either return it
600 * or free it and allocate a new one. Caller must free returned string.
601 */
602static char *
603make_absolute_pwd_glob(char *p, const char *pwd)
604{
605 char *ret, *escpwd;
606
607 escpwd = escape_glob(pwd);
608 if (p == NULL((void *)0))
609 return escpwd;
610 ret = sftp_make_absolute(p, escpwd);
611 free(escpwd);
612 return ret;
613}
614
615static int
616local_is_dir(const char *path)
617{
618 struct stat sb;
619
620 if (stat(path, &sb) == -1)
621 return 0;
622 return S_ISDIR(sb.st_mode)((sb.st_mode & 0170000) == 0040000);
623}
624
625static int
626process_get(struct sftp_conn *conn, const char *src, const char *dst,
627 const char *pwd, int pflag, int rflag, int resume, int fflag)
628{
629 char *filename, *abs_src = NULL((void *)0), *abs_dst = NULL((void *)0), *tmp = NULL((void *)0);
630 glob_t g;
631 int i, r, err = 0;
632
633 abs_src = make_absolute_pwd_glob(xstrdup(src), pwd);
634 memset(&g, 0, sizeof(g));
635
636 debug3("Looking up %s", abs_src)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 636, 0
, SYSLOG_LEVEL_DEBUG3, ((void *)0), "Looking up %s", abs_src)
;
637 if ((r = sftp_glob(conn, abs_src, GLOB_MARK0x0008, NULL((void *)0), &g)) != 0) {
638 if (r == GLOB_NOSPACE(-1)) {
639 error("Too many matches for \"%s\".", abs_src)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 639, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Too many matches for \"%s\"."
, abs_src)
;
640 } else {
641 error("File \"%s\" not found.", abs_src)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 641, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "File \"%s\" not found.", abs_src
)
;
642 }
643 err = -1;
644 goto out;
645 }
646
647 /*
648 * If multiple matches then dst must be a directory or
649 * unspecified.
650 */
651 if (g.gl_matchc > 1 && dst != NULL((void *)0) && !local_is_dir(dst)) {
652 error("Multiple source paths, but destination "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 653, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Multiple source paths, but destination "
"\"%s\" is not a directory", dst)
653 "\"%s\" is not a directory", dst)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 653, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Multiple source paths, but destination "
"\"%s\" is not a directory", dst)
;
654 err = -1;
655 goto out;
656 }
657
658 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
659 tmp = xstrdup(g.gl_pathv[i]);
660 if ((filename = basename(tmp)) == NULL((void *)0)) {
661 error("basename %s: %s", tmp, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 661, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "basename %s: %s", tmp, strerror
((*__errno())))
;
662 free(tmp);
663 err = -1;
664 goto out;
665 }
666
667 if (g.gl_matchc == 1 && dst) {
668 if (local_is_dir(dst)) {
669 abs_dst = sftp_path_append(dst, filename);
670 } else {
671 abs_dst = xstrdup(dst);
672 }
673 } else if (dst) {
674 abs_dst = sftp_path_append(dst, filename);
675 } else {
676 abs_dst = xstrdup(filename);
677 }
678 free(tmp);
679
680 resume |= global_aflag;
681 if (!quiet && resume)
682 mprintf("Resuming %s to %s\n",
683 g.gl_pathv[i], abs_dst);
684 else if (!quiet && !resume)
685 mprintf("Fetching %s to %s\n",
686 g.gl_pathv[i], abs_dst);
687 /* XXX follow link flag */
688 if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
689 (rflag || global_rflag)) {
690 if (sftp_download_dir(conn, g.gl_pathv[i], abs_dst,
691 NULL((void *)0), pflag || global_pflag, 1, resume,
692 fflag || global_fflag, 0, 0) == -1)
693 err = -1;
694 } else {
695 if (sftp_download(conn, g.gl_pathv[i], abs_dst, NULL((void *)0),
696 pflag || global_pflag, resume,
697 fflag || global_fflag, 0) == -1)
698 err = -1;
699 }
700 free(abs_dst);
701 abs_dst = NULL((void *)0);
702 }
703
704out:
705 free(abs_src);
706 globfree(&g);
707 return(err);
708}
709
710static int
711process_put(struct sftp_conn *conn, const char *src, const char *dst,
712 const char *pwd, int pflag, int rflag, int resume, int fflag)
713{
714 char *tmp_dst = NULL((void *)0);
715 char *abs_dst = NULL((void *)0);
716 char *tmp = NULL((void *)0), *filename = NULL((void *)0);
717 glob_t g;
718 int err = 0;
719 int i, dst_is_dir = 1;
720 struct stat sb;
721
722 if (dst) {
723 tmp_dst = xstrdup(dst);
724 tmp_dst = sftp_make_absolute(tmp_dst, pwd);
725 }
726
727 memset(&g, 0, sizeof(g));
728 debug3("Looking up %s", src)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 728, 0
, SYSLOG_LEVEL_DEBUG3, ((void *)0), "Looking up %s", src)
;
729 if (glob(src, GLOB_NOCHECK0x0010 | GLOB_MARK0x0008, NULL((void *)0), &g)) {
730 error("File \"%s\" not found.", src)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 730, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "File \"%s\" not found.", src
)
;
731 err = -1;
732 goto out;
733 }
734
735 /* If we aren't fetching to pwd then stash this status for later */
736 if (tmp_dst != NULL((void *)0))
737 dst_is_dir = sftp_remote_is_dir(conn, tmp_dst);
738
739 /* If multiple matches, dst may be directory or unspecified */
740 if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
741 error("Multiple paths match, but destination "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 742, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Multiple paths match, but destination "
"\"%s\" is not a directory", tmp_dst)
742 "\"%s\" is not a directory", tmp_dst)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 742, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Multiple paths match, but destination "
"\"%s\" is not a directory", tmp_dst)
;
743 err = -1;
744 goto out;
745 }
746
747 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
748 if (stat(g.gl_pathv[i], &sb) == -1) {
749 err = -1;
750 error("stat %s: %s", g.gl_pathv[i], strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 750, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "stat %s: %s", g.gl_pathv[
i], strerror((*__errno())))
;
751 continue;
752 }
753
754 tmp = xstrdup(g.gl_pathv[i]);
755 if ((filename = basename(tmp)) == NULL((void *)0)) {
756 error("basename %s: %s", tmp, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 756, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "basename %s: %s", tmp, strerror
((*__errno())))
;
757 free(tmp);
758 err = -1;
759 goto out;
760 }
761
762 free(abs_dst);
763 abs_dst = NULL((void *)0);
764 if (g.gl_matchc == 1 && tmp_dst) {
765 /* If directory specified, append filename */
766 if (dst_is_dir)
767 abs_dst = sftp_path_append(tmp_dst, filename);
768 else
769 abs_dst = xstrdup(tmp_dst);
770 } else if (tmp_dst) {
771 abs_dst = sftp_path_append(tmp_dst, filename);
772 } else {
773 abs_dst = sftp_make_absolute(xstrdup(filename), pwd);
774 }
775 free(tmp);
776
777 resume |= global_aflag;
778 if (!quiet && resume)
779 mprintf("Resuming upload of %s to %s\n",
780 g.gl_pathv[i], abs_dst);
781 else if (!quiet && !resume)
782 mprintf("Uploading %s to %s\n",
783 g.gl_pathv[i], abs_dst);
784 /* XXX follow_link_flag */
785 if (sftp_globpath_is_dir(g.gl_pathv[i]) &&
786 (rflag || global_rflag)) {
787 if (sftp_upload_dir(conn, g.gl_pathv[i], abs_dst,
788 pflag || global_pflag, 1, resume,
789 fflag || global_fflag, 0, 0) == -1)
790 err = -1;
791 } else {
792 if (sftp_upload(conn, g.gl_pathv[i], abs_dst,
793 pflag || global_pflag, resume,
794 fflag || global_fflag, 0) == -1)
795 err = -1;
796 }
797 }
798
799out:
800 free(abs_dst);
801 free(tmp_dst);
802 globfree(&g);
803 return(err);
804}
805
806static int
807sdirent_comp(const void *aa, const void *bb)
808{
809 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
810 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
811 int rmul = sort_flag & LS_REVERSE_SORT0x0040 ? -1 : 1;
812
813#define NCMP(a,b)(a == b ? 0 : (a < b ? 1 : -1)) (a == b ? 0 : (a < b ? 1 : -1))
814 if (sort_flag & LS_NAME_SORT0x0008)
815 return (rmul * strcmp(a->filename, b->filename));
816 else if (sort_flag & LS_TIME_SORT0x0010)
817 return (rmul * NCMP(a->a.mtime, b->a.mtime)(a->a.mtime == b->a.mtime ? 0 : (a->a.mtime < b->
a.mtime ? 1 : -1))
);
818 else if (sort_flag & LS_SIZE_SORT0x0020)
819 return (rmul * NCMP(a->a.size, b->a.size)(a->a.size == b->a.size ? 0 : (a->a.size < b->
a.size ? 1 : -1))
);
820
821 fatal("Unknown ls sort type")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 821
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Unknown ls sort type")
;
822}
823
824/* sftp ls.1 replacement for directories */
825static int
826do_ls_dir(struct sftp_conn *conn, const char *path,
827 const char *strip_path, int lflag)
828{
829 int n;
830 u_int c = 1, colspace = 0, columns = 1;
831 SFTP_DIRENT **d;
832
833 if ((n = sftp_readdir(conn, path, &d)) != 0)
834 return (n);
835
836 if (!(lflag & LS_SHORT_VIEW0x0002)) {
837 u_int m = 0, width = 80;
838 struct winsize ws;
839 char *tmp;
840
841 /* Count entries for sort and find longest filename */
842 for (n = 0; d[n] != NULL((void *)0); n++) {
843 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL0x0080))
844 m = MAXIMUM(m, strlen(d[n]->filename))(((m) > (strlen(d[n]->filename))) ? (m) : (strlen(d[n]->
filename)))
;
845 }
846
847 /* Add any subpath that also needs to be counted */
848 tmp = path_strip(path, strip_path);
849 m += strlen(tmp);
850 free(tmp);
851
852 if (ioctl(fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((&
__sF[0])))
, TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff
) << 16) | ((('t')) << 8) | ((104)))
, &ws) != -1)
853 width = ws.ws_col;
854
855 columns = width / (m + 2);
856 columns = MAXIMUM(columns, 1)(((columns) > (1)) ? (columns) : (1));
857 colspace = width / columns;
858 colspace = MINIMUM(colspace, width)(((colspace) < (width)) ? (colspace) : (width));
859 }
860
861 if (lflag & SORT_FLAGS(0x0008|0x0010|0x0020)) {
862 for (n = 0; d[n] != NULL((void *)0); n++)
863 ; /* count entries */
864 sort_flag = lflag & (SORT_FLAGS(0x0008|0x0010|0x0020)|LS_REVERSE_SORT0x0040);
865 qsort(d, n, sizeof(*d), sdirent_comp);
866 }
867
868 get_remote_user_groups_from_dirents(conn, d);
869 for (n = 0; d[n] != NULL((void *)0) && !interrupted; n++) {
870 char *tmp, *fname;
871
872 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL0x0080))
873 continue;
874
875 tmp = sftp_path_append(path, d[n]->filename);
876 fname = path_strip(tmp, strip_path);
877 free(tmp);
878
879 if (lflag & LS_LONG_VIEW0x0001) {
880 if ((lflag & (LS_NUMERIC_VIEW0x0004|LS_SI_UNITS0x0100)) != 0 ||
881 sftp_can_get_users_groups_by_id(conn)) {
882 char *lname;
883 struct stat sb;
884
885 memset(&sb, 0, sizeof(sb));
886 attrib_to_stat(&d[n]->a, &sb);
887 lname = ls_file(fname, &sb, 1,
888 (lflag & LS_SI_UNITS0x0100),
889 ruser_name(sb.st_uid),
890 rgroup_name(sb.st_gid));
891 mprintf("%s\n", lname);
892 free(lname);
893 } else
894 mprintf("%s\n", d[n]->longname);
895 } else {
896 mprintf("%-*s", colspace, fname);
897 if (c >= columns) {
898 printf("\n");
899 c = 1;
900 } else
901 c++;
902 }
903
904 free(fname);
905 }
906
907 if (!(lflag & LS_LONG_VIEW0x0001) && (c != 1))
908 printf("\n");
909
910 sftp_free_dirents(d);
911 return (0);
912}
913
914static int
915sglob_comp(const void *aa, const void *bb)
916{
917 u_int a = *(const u_int *)aa;
918 u_int b = *(const u_int *)bb;
919 const char *ap = sort_glob->gl_pathv[a];
920 const char *bp = sort_glob->gl_pathv[b];
921 const struct stat *as = sort_glob->gl_statv[a];
922 const struct stat *bs = sort_glob->gl_statv[b];
923 int rmul = sort_flag & LS_REVERSE_SORT0x0040 ? -1 : 1;
924
925#define NCMP(a,b)(a == b ? 0 : (a < b ? 1 : -1)) (a == b ? 0 : (a < b ? 1 : -1))
926 if (sort_flag & LS_NAME_SORT0x0008)
927 return (rmul * strcmp(ap, bp));
928 else if (sort_flag & LS_TIME_SORT0x0010) {
929 if (timespeccmp(&as->st_mtim, &bs->st_mtim, ==)(((&as->st_mtim)->tv_sec == (&bs->st_mtim)->
tv_sec) ? ((&as->st_mtim)->tv_nsec == (&bs->
st_mtim)->tv_nsec) : ((&as->st_mtim)->tv_sec == (
&bs->st_mtim)->tv_sec))
)
930 return 0;
931 return timespeccmp(&as->st_mtim, &bs->st_mtim, <)(((&as->st_mtim)->tv_sec == (&bs->st_mtim)->
tv_sec) ? ((&as->st_mtim)->tv_nsec < (&bs->
st_mtim)->tv_nsec) : ((&as->st_mtim)->tv_sec <
(&bs->st_mtim)->tv_sec))
?
932 rmul : -rmul;
933 } else if (sort_flag & LS_SIZE_SORT0x0020)
934 return (rmul * NCMP(as->st_size, bs->st_size)(as->st_size == bs->st_size ? 0 : (as->st_size < bs
->st_size ? 1 : -1))
);
935
936 fatal("Unknown ls sort type")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 936
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Unknown ls sort type")
;
937}
938
939/* sftp ls.1 replacement which handles path globs */
940static int
941do_globbed_ls(struct sftp_conn *conn, const char *path,
942 const char *strip_path, int lflag)
943{
944 char *fname, *lname;
945 glob_t g;
946 int err, r;
947 struct winsize ws;
948 u_int i, j, nentries, *indices = NULL((void *)0), c = 1;
949 u_int colspace = 0, columns = 1, m = 0, width = 80;
950
951 memset(&g, 0, sizeof(g));
952
953 if ((r = sftp_glob(conn, path,
954 GLOB_MARK0x0008|GLOB_NOCHECK0x0010|GLOB_BRACE0x0080|GLOB_KEEPSTAT0x4000|GLOB_NOSORT0x0020,
955 NULL((void *)0), &g)) != 0 ||
956 (g.gl_pathc && !g.gl_matchc)) {
957 if (g.gl_pathc)
958 globfree(&g);
959 if (r == GLOB_NOSPACE(-1)) {
960 error("Can't ls: Too many matches for \"%s\"", path)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 960, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't ls: Too many matches for \"%s\""
, path)
;
961 } else {
962 error("Can't ls: \"%s\" not found", path)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 962, 0
, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't ls: \"%s\" not found"
, path)
;
963 }
964 return -1;
965 }
966
967 if (interrupted)
968 goto out;
969
970 /*
971 * If the glob returns a single match and it is a directory,
972 * then just list its contents.
973 */
974 if (g.gl_matchc == 1 && g.gl_statv[0] != NULL((void *)0) &&
975 S_ISDIR(g.gl_statv[0]->st_mode)((g.gl_statv[0]->st_mode & 0170000) == 0040000)) {
976 err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
977 globfree(&g);
978 return err;
979 }
980
981 if (ioctl(fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((&
__sF[0])))
, TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff
) << 16) | ((('t')) << 8) | ((104)))
, &ws) != -1)
982 width = ws.ws_col;
983
984 if (!(lflag & LS_SHORT_VIEW0x0002)) {
985 /* Count entries for sort and find longest filename */
986 for (i = 0; g.gl_pathv[i]; i++)
987 m = MAXIMUM(m, strlen(g.gl_pathv[i]))(((m) > (strlen(g.gl_pathv[i]))) ? (m) : (strlen(g.gl_pathv
[i])))
;
988
989 columns = width / (m + 2);
990 columns = MAXIMUM(columns, 1)(((columns) > (1)) ? (columns) : (1));
991 colspace = width / columns;
992 }
993
994 /*
995 * Sorting: rather than mess with the contents of glob_t, prepare
996 * an array of indices into it and sort that. For the usual
997 * unsorted case, the indices are just the identity 1=1, 2=2, etc.
998 */
999 for (nentries = 0; g.gl_pathv[nentries] != NULL((void *)0); nentries++)
1000 ; /* count entries */
1001 indices = xcalloc(nentries, sizeof(*indices));
1002 for (i = 0; i < nentries; i++)
1003 indices[i] = i;
1004
1005 if (lflag & SORT_FLAGS(0x0008|0x0010|0x0020)) {
1006 sort_glob = &g;
1007 sort_flag = lflag & (SORT_FLAGS(0x0008|0x0010|0x0020)|LS_REVERSE_SORT0x0040);
1008 qsort(indices, nentries, sizeof(*indices), sglob_comp);
1009 sort_glob = NULL((void *)0);
1010 }
1011
1012 get_remote_user_groups_from_glob(conn, &g);
1013 for (j = 0; j < nentries && !interrupted; j++) {
1014 i = indices[j];
1015 fname = path_strip(g.gl_pathv[i], strip_path);
1016 if (lflag & LS_LONG_VIEW0x0001) {
1017 if (g.gl_statv[i] == NULL((void *)0)) {
1018 error("no stat information for %s", fname)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1018,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "no stat information for %s"
, fname)
;
1019 free(fname);
1020 continue;
1021 }
1022 lname = ls_file(fname, g.gl_statv[i], 1,
1023 (lflag & LS_SI_UNITS0x0100),
1024 ruser_name(g.gl_statv[i]->st_uid),
1025 rgroup_name(g.gl_statv[i]->st_gid));
1026 mprintf("%s\n", lname);
1027 free(lname);
1028 } else {
1029 mprintf("%-*s", colspace, fname);
1030 if (c >= columns) {
1031 printf("\n");
1032 c = 1;
1033 } else
1034 c++;
1035 }
1036 free(fname);
1037 }
1038
1039 if (!(lflag & LS_LONG_VIEW0x0001) && (c != 1))
1040 printf("\n");
1041
1042 out:
1043 if (g.gl_pathc)
1044 globfree(&g);
1045 free(indices);
1046
1047 return 0;
1048}
1049
1050static int
1051do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag)
1052{
1053 struct sftp_statvfs st;
1054 char s_used[FMT_SCALED_STRSIZE7], s_avail[FMT_SCALED_STRSIZE7];
1055 char s_root[FMT_SCALED_STRSIZE7], s_total[FMT_SCALED_STRSIZE7];
1056 char s_icapacity[16], s_dcapacity[16];
1057
1058 if (sftp_statvfs(conn, path, &st, 1) == -1)
1059 return -1;
1060 if (st.f_files == 0)
1061 strlcpy(s_icapacity, "ERR", sizeof(s_icapacity));
1062 else {
1063 snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%",
1064 (unsigned long long)(100 * (st.f_files - st.f_ffree) /
1065 st.f_files));
1066 }
1067 if (st.f_blocks == 0)
1068 strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity));
1069 else {
1070 snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%",
1071 (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
1072 st.f_blocks));
1073 }
1074 if (iflag) {
1075 printf(" Inodes Used Avail "
1076 "(root) %%Capacity\n");
1077 printf("%11llu %11llu %11llu %11llu %s\n",
1078 (unsigned long long)st.f_files,
1079 (unsigned long long)(st.f_files - st.f_ffree),
1080 (unsigned long long)st.f_favail,
1081 (unsigned long long)st.f_ffree, s_icapacity);
1082 } else if (hflag) {
1083 strlcpy(s_used, "error", sizeof(s_used));
1084 strlcpy(s_avail, "error", sizeof(s_avail));
1085 strlcpy(s_root, "error", sizeof(s_root));
1086 strlcpy(s_total, "error", sizeof(s_total));
1087 fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
1088 fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
1089 fmt_scaled(st.f_bfree * st.f_frsize, s_root);
1090 fmt_scaled(st.f_blocks * st.f_frsize, s_total);
1091 printf(" Size Used Avail (root) %%Capacity\n");
1092 printf("%7sB %7sB %7sB %7sB %s\n",
1093 s_total, s_used, s_avail, s_root, s_dcapacity);
1094 } else {
1095 printf(" Size Used Avail "
1096 "(root) %%Capacity\n");
1097 printf("%12llu %12llu %12llu %12llu %s\n",
1098 (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
1099 (unsigned long long)(st.f_frsize *
1100 (st.f_blocks - st.f_bfree) / 1024),
1101 (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
1102 (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
1103 s_dcapacity);
1104 }
1105 return 0;
1106}
1107
1108/*
1109 * Undo escaping of glob sequences in place. Used to undo extra escaping
1110 * applied in makeargv() when the string is destined for a function that
1111 * does not glob it.
1112 */
1113static void
1114undo_glob_escape(char *s)
1115{
1116 size_t i, j;
1117
1118 for (i = j = 0;;) {
1119 if (s[i] == '\0') {
1120 s[j] = '\0';
1121 return;
1122 }
1123 if (s[i] != '\\') {
1124 s[j++] = s[i++];
1125 continue;
1126 }
1127 /* s[i] == '\\' */
1128 ++i;
1129 switch (s[i]) {
1130 case '?':
1131 case '[':
1132 case '*':
1133 case '\\':
1134 s[j++] = s[i++];
1135 break;
1136 case '\0':
1137 s[j++] = '\\';
1138 s[j] = '\0';
1139 return;
1140 default:
1141 s[j++] = '\\';
1142 s[j++] = s[i++];
1143 break;
1144 }
1145 }
1146}
1147
1148/*
1149 * Split a string into an argument vector using sh(1)-style quoting,
1150 * comment and escaping rules, but with some tweaks to handle glob(3)
1151 * wildcards.
1152 * The "sloppy" flag allows for recovery from missing terminating quote, for
1153 * use in parsing incomplete commandlines during tab autocompletion.
1154 *
1155 * Returns NULL on error or a NULL-terminated array of arguments.
1156 *
1157 * If "lastquote" is not NULL, the quoting character used for the last
1158 * argument is placed in *lastquote ("\0", "'" or "\"").
1159 *
1160 * If "terminated" is not NULL, *terminated will be set to 1 when the
1161 * last argument's quote has been properly terminated or 0 otherwise.
1162 * This parameter is only of use if "sloppy" is set.
1163 */
1164#define MAXARGS128 128
1165#define MAXARGLEN8192 8192
1166static char **
1167makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
1168 u_int *terminated)
1169{
1170 int argc, quot;
1171 size_t i, j;
1172 static char argvs[MAXARGLEN8192];
1173 static char *argv[MAXARGS128 + 1];
1174 enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
1175
1176 *argcp = argc = 0;
1177 if (strlen(arg) > sizeof(argvs) - 1) {
1178 args_too_longs:
1179 error("string too long")sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1179,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "string too long")
;
1180 return NULL((void *)0);
1181 }
1182 if (terminated != NULL((void *)0))
1183 *terminated = 1;
1184 if (lastquote != NULL((void *)0))
1185 *lastquote = '\0';
1186 state = MA_START;
1187 i = j = 0;
1188 for (;;) {
1189 if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){
1190 error("Too many arguments.")sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1190,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Too many arguments.")
;
1191 return NULL((void *)0);
1192 }
1193 if (isspace((unsigned char)arg[i])) {
1194 if (state == MA_UNQUOTED) {
1195 /* Terminate current argument */
1196 argvs[j++] = '\0';
1197 argc++;
1198 state = MA_START;
1199 } else if (state != MA_START)
1200 argvs[j++] = arg[i];
1201 } else if (arg[i] == '"' || arg[i] == '\'') {
1202 q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1203 if (state == MA_START) {
1204 argv[argc] = argvs + j;
1205 state = q;
1206 if (lastquote != NULL((void *)0))
1207 *lastquote = arg[i];
1208 } else if (state == MA_UNQUOTED)
1209 state = q;
1210 else if (state == q)
1211 state = MA_UNQUOTED;
1212 else
1213 argvs[j++] = arg[i];
1214 } else if (arg[i] == '\\') {
1215 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1216 quot = state == MA_SQUOTE ? '\'' : '"';
1217 /* Unescape quote we are in */
1218 /* XXX support \n and friends? */
1219 if (arg[i + 1] == quot) {
1220 i++;
1221 argvs[j++] = arg[i];
1222 } else if (arg[i + 1] == '?' ||
1223 arg[i + 1] == '[' || arg[i + 1] == '*') {
1224 /*
1225 * Special case for sftp: append
1226 * double-escaped glob sequence -
1227 * glob will undo one level of
1228 * escaping. NB. string can grow here.
1229 */
1230 if (j >= sizeof(argvs) - 5)
1231 goto args_too_longs;
1232 argvs[j++] = '\\';
1233 argvs[j++] = arg[i++];
1234 argvs[j++] = '\\';
1235 argvs[j++] = arg[i];
1236 } else {
1237 argvs[j++] = arg[i++];
1238 argvs[j++] = arg[i];
1239 }
1240 } else {
1241 if (state == MA_START) {
1242 argv[argc] = argvs + j;
1243 state = MA_UNQUOTED;
1244 if (lastquote != NULL((void *)0))
1245 *lastquote = '\0';
1246 }
1247 if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1248 arg[i + 1] == '*' || arg[i + 1] == '\\') {
1249 /*
1250 * Special case for sftp: append
1251 * escaped glob sequence -
1252 * glob will undo one level of
1253 * escaping.
1254 */
1255 argvs[j++] = arg[i++];
1256 argvs[j++] = arg[i];
1257 } else {
1258 /* Unescape everything */
1259 /* XXX support \n and friends? */
1260 i++;
1261 argvs[j++] = arg[i];
1262 }
1263 }
1264 } else if (arg[i] == '#') {
1265 if (state == MA_SQUOTE || state == MA_DQUOTE)
1266 argvs[j++] = arg[i];
1267 else
1268 goto string_done;
1269 } else if (arg[i] == '\0') {
1270 if (state == MA_SQUOTE || state == MA_DQUOTE) {
1271 if (sloppy) {
1272 state = MA_UNQUOTED;
1273 if (terminated != NULL((void *)0))
1274 *terminated = 0;
1275 goto string_done;
1276 }
1277 error("Unterminated quoted argument")sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1277,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Unterminated quoted argument"
)
;
1278 return NULL((void *)0);
1279 }
1280 string_done:
1281 if (state == MA_UNQUOTED) {
1282 argvs[j++] = '\0';
1283 argc++;
1284 }
1285 break;
1286 } else {
1287 if (state == MA_START) {
1288 argv[argc] = argvs + j;
1289 state = MA_UNQUOTED;
1290 if (lastquote != NULL((void *)0))
1291 *lastquote = '\0';
1292 }
1293 if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1294 (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1295 /*
1296 * Special case for sftp: escape quoted
1297 * glob(3) wildcards. NB. string can grow
1298 * here.
1299 */
1300 if (j >= sizeof(argvs) - 3)
1301 goto args_too_longs;
1302 argvs[j++] = '\\';
1303 argvs[j++] = arg[i];
1304 } else
1305 argvs[j++] = arg[i];
1306 }
1307 i++;
1308 }
1309 *argcp = argc;
1310 return argv;
1311}
1312
1313static int
1314parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1315 int *fflag, int *hflag, int *iflag, int *lflag, int *pflag,
1316 int *rflag, int *sflag,
1317 unsigned long *n_arg, char **path1, char **path2)
1318{
1319 const char *cmd, *cp = *cpp;
1320 char *cp2, **argv;
1321 int base = 0;
1322 long long ll;
1323 int path1_mandatory = 0, i, cmdnum, optidx, argc;
1324
1325 /* Skip leading whitespace */
1326 cp = cp + strspn(cp, WHITESPACE" \t\r\n");
1327
1328 /*
1329 * Check for leading '-' (disable error processing) and '@' (suppress
1330 * command echo)
1331 */
1332 *ignore_errors = 0;
1333 *disable_echo = 0;
1334 for (;*cp != '\0'; cp++) {
1335 if (*cp == '-') {
1336 *ignore_errors = 1;
1337 } else if (*cp == '@') {
1338 *disable_echo = 1;
1339 } else {
1340 /* all other characters terminate prefix processing */
1341 break;
1342 }
1343 }
1344 cp = cp + strspn(cp, WHITESPACE" \t\r\n");
1345
1346 /* Ignore blank lines and lines which begin with comment '#' char */
1347 if (*cp == '\0' || *cp == '#')
1348 return (0);
1349
1350 if ((argv = makeargv(cp, &argc, 0, NULL((void *)0), NULL((void *)0))) == NULL((void *)0))
1351 return -1;
1352
1353 /* Figure out which command we have */
1354 for (i = 0; cmds[i].c != NULL((void *)0); i++) {
1355 if (argv[0] != NULL((void *)0) && strcasecmp(cmds[i].c, argv[0]) == 0)
1356 break;
1357 }
1358 cmdnum = cmds[i].n;
1359 cmd = cmds[i].c;
1360
1361 /* Special case */
1362 if (*cp == '!') {
1363 cp++;
1364 cmdnum = I_SHELL;
1365 } else if (cmdnum == -1) {
1366 error("Invalid command.")sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1366,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Invalid command.")
;
1367 return -1;
1368 }
1369
1370 /* Get arguments and parse flags */
1371 *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0;
1372 *rflag = *sflag = 0;
1373 *path1 = *path2 = NULL((void *)0);
1374 optidx = 1;
1375 switch (cmdnum) {
1376 case I_GET:
1377 case I_REGET:
1378 case I_REPUT:
1379 case I_PUT:
1380 if ((optidx = parse_getput_flags(cmd, argv, argc,
1381 aflag, fflag, pflag, rflag)) == -1)
1382 return -1;
1383 /* Get first pathname (mandatory) */
1384 if (argc - optidx < 1) {
1385 error("You must specify at least one path after a "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1386,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify at least one path after a "
"%s command.", cmd)
1386 "%s command.", cmd)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1386,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify at least one path after a "
"%s command.", cmd)
;
1387 return -1;
1388 }
1389 *path1 = xstrdup(argv[optidx]);
1390 /* Get second pathname (optional) */
1391 if (argc - optidx > 1) {
1392 *path2 = xstrdup(argv[optidx + 1]);
1393 /* Destination is not globbed */
1394 undo_glob_escape(*path2);
1395 }
1396 break;
1397 case I_LINK:
1398 if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1399 return -1;
1400 goto parse_two_paths;
1401 case I_COPY:
1402 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1403 return -1;
1404 goto parse_two_paths;
1405 case I_RENAME:
1406 if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1)
1407 return -1;
1408 goto parse_two_paths;
1409 case I_SYMLINK:
1410 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1411 return -1;
1412 parse_two_paths:
1413 if (argc - optidx < 2) {
1414 error("You must specify two paths after a %s "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1415,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify two paths after a %s "
"command.", cmd)
1415 "command.", cmd)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1415,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify two paths after a %s "
"command.", cmd)
;
1416 return -1;
1417 }
1418 *path1 = xstrdup(argv[optidx]);
1419 *path2 = xstrdup(argv[optidx + 1]);
1420 /* Paths are not globbed */
1421 undo_glob_escape(*path1);
1422 undo_glob_escape(*path2);
1423 break;
1424 case I_RM:
1425 case I_MKDIR:
1426 case I_RMDIR:
1427 case I_LMKDIR:
1428 path1_mandatory = 1;
1429 /* FALLTHROUGH */
1430 case I_CHDIR:
1431 case I_LCHDIR:
1432 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1433 return -1;
1434 /* Get pathname (mandatory) */
1435 if (argc - optidx < 1) {
1436 if (!path1_mandatory)
1437 break; /* return a NULL path1 */
1438 error("You must specify a path after a %s command.",sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1439,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify a path after a %s command."
, cmd)
1439 cmd)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1439,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify a path after a %s command."
, cmd)
;
1440 return -1;
1441 }
1442 *path1 = xstrdup(argv[optidx]);
1443 /* Only "rm" globs */
1444 if (cmdnum != I_RM)
1445 undo_glob_escape(*path1);
1446 break;
1447 case I_DF:
1448 if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1449 iflag)) == -1)
1450 return -1;
1451 /* Default to current directory if no path specified */
1452 if (argc - optidx < 1)
1453 *path1 = NULL((void *)0);
1454 else {
1455 *path1 = xstrdup(argv[optidx]);
1456 undo_glob_escape(*path1);
1457 }
1458 break;
1459 case I_LS:
1460 if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1461 return(-1);
1462 /* Path is optional */
1463 if (argc - optidx > 0)
1464 *path1 = xstrdup(argv[optidx]);
1465 break;
1466 case I_LLS:
1467 /* Skip ls command and following whitespace */
1468 cp = cp + strlen(cmd) + strspn(cp, WHITESPACE" \t\r\n");
1469 case I_SHELL:
1470 /* Uses the rest of the line */
1471 break;
1472 case I_LUMASK:
1473 case I_CHMOD:
1474 base = 8;
1475 /* FALLTHROUGH */
1476 case I_CHOWN:
1477 case I_CHGRP:
1478 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1479 return -1;
1480 /* Get numeric arg (mandatory) */
1481 if (argc - optidx < 1)
1482 goto need_num_arg;
1483 errno(*__errno()) = 0;
1484 ll = strtoll(argv[optidx], &cp2, base);
1485 if (cp2 == argv[optidx] || *cp2 != '\0' ||
1486 ((ll == LLONG_MIN(-0x7fffffffffffffffLL-1) || ll == LLONG_MAX0x7fffffffffffffffLL) && errno(*__errno()) == ERANGE34) ||
1487 ll < 0 || ll > UINT32_MAX0xffffffffU) {
1488 need_num_arg:
1489 error("You must supply a numeric argument "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1490,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must supply a numeric argument "
"to the %s command.", cmd)
1490 "to the %s command.", cmd)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1490,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must supply a numeric argument "
"to the %s command.", cmd)
;
1491 return -1;
1492 }
1493 *n_arg = ll;
1494 if (cmdnum == I_LUMASK)
1495 break;
1496 /* Get pathname (mandatory) */
1497 if (argc - optidx < 2) {
1498 error("You must specify a path after a %s command.",sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1499,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify a path after a %s command."
, cmd)
1499 cmd)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1499,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "You must specify a path after a %s command."
, cmd)
;
1500 return -1;
1501 }
1502 *path1 = xstrdup(argv[optidx + 1]);
1503 break;
1504 case I_QUIT:
1505 case I_PWD:
1506 case I_LPWD:
1507 case I_HELP:
1508 case I_VERSION:
1509 case I_PROGRESS:
1510 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1)
1511 return -1;
1512 break;
1513 default:
1514 fatal("Command not implemented")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1514
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Command not implemented"
)
;
1515 }
1516
1517 *cpp = cp;
1518 return(cmdnum);
1519}
1520
1521static int
1522parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1523 const char *startdir, int err_abort, int echo_command)
1524{
1525 const char *ocmd = cmd;
1526 char *path1, *path2, *tmp;
1527 int ignore_errors = 0, disable_echo = 1;
1528 int aflag = 0, fflag = 0, hflag = 0, iflag = 0;
1529 int lflag = 0, pflag = 0, rflag = 0, sflag = 0;
1530 int cmdnum, i;
1531 unsigned long n_arg = 0;
1532 Attrib a, aa;
1533 char path_buf[PATH_MAX1024];
1534 int err = 0;
1535 glob_t g;
1536
1537 path1 = path2 = NULL((void *)0);
1538 cmdnum = parse_args(&cmd, &ignore_errors, &disable_echo, &aflag, &fflag,
1539 &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg,
1540 &path1, &path2);
1541 if (ignore_errors != 0)
1542 err_abort = 0;
1543
1544 if (echo_command && !disable_echo)
1545 mprintf("sftp> %s\n", ocmd);
1546
1547 memset(&g, 0, sizeof(g));
1548
1549 /* Perform command */
1550 switch (cmdnum) {
1551 case 0:
1552 /* Blank line */
1553 break;
1554 case -1:
1555 /* Unrecognized command */
1556 err = -1;
1557 break;
1558 case I_REGET:
1559 aflag = 1;
1560 /* FALLTHROUGH */
1561 case I_GET:
1562 err = process_get(conn, path1, path2, *pwd, pflag,
1563 rflag, aflag, fflag);
1564 break;
1565 case I_REPUT:
1566 aflag = 1;
1567 /* FALLTHROUGH */
1568 case I_PUT:
1569 err = process_put(conn, path1, path2, *pwd, pflag,
1570 rflag, aflag, fflag);
1571 break;
1572 case I_COPY:
1573 path1 = sftp_make_absolute(path1, *pwd);
1574 path2 = sftp_make_absolute(path2, *pwd);
1575 err = sftp_copy(conn, path1, path2);
1576 break;
1577 case I_RENAME:
1578 path1 = sftp_make_absolute(path1, *pwd);
1579 path2 = sftp_make_absolute(path2, *pwd);
1580 err = sftp_rename(conn, path1, path2, lflag);
1581 break;
1582 case I_SYMLINK:
1583 sflag = 1;
1584 /* FALLTHROUGH */
1585 case I_LINK:
1586 if (!sflag)
1587 path1 = sftp_make_absolute(path1, *pwd);
1588 path2 = sftp_make_absolute(path2, *pwd);
1589 err = (sflag ? sftp_symlink : sftp_hardlink)(conn,
1590 path1, path2);
1591 break;
1592 case I_RM:
1593 path1 = make_absolute_pwd_glob(path1, *pwd);
1594 sftp_glob(conn, path1, GLOB_NOCHECK0x0010, NULL((void *)0), &g);
1595 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1596 if (!quiet)
1597 mprintf("Removing %s\n", g.gl_pathv[i]);
1598 err = sftp_rm(conn, g.gl_pathv[i]);
1599 if (err != 0 && err_abort)
1600 break;
1601 }
1602 break;
1603 case I_MKDIR:
1604 path1 = sftp_make_absolute(path1, *pwd);
1605 attrib_clear(&a);
1606 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS0x00000004;
1607 a.perm = 0777;
1608 err = sftp_mkdir(conn, path1, &a, 1);
1609 break;
1610 case I_RMDIR:
1611 path1 = sftp_make_absolute(path1, *pwd);
1612 err = sftp_rmdir(conn, path1);
1613 break;
1614 case I_CHDIR:
1615 if (path1 == NULL((void *)0) || *path1 == '\0')
1616 path1 = xstrdup(startdir);
1617 path1 = sftp_make_absolute(path1, *pwd);
1618 if ((tmp = sftp_realpath(conn, path1)) == NULL((void *)0)) {
1619 err = 1;
1620 break;
1621 }
1622 if (sftp_stat(conn, tmp, 0, &aa) != 0) {
1623 free(tmp);
1624 err = 1;
1625 break;
1626 }
1627 if (!(aa.flags & SSH2_FILEXFER_ATTR_PERMISSIONS0x00000004)) {
1628 error("Can't change directory: Can't check target")sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1628,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't change directory: Can't check target"
)
;
1629 free(tmp);
1630 err = 1;
1631 break;
1632 }
1633 if (!S_ISDIR(aa.perm)((aa.perm & 0170000) == 0040000)) {
1634 error("Can't change directory: \"%s\" is not "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1635,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't change directory: \"%s\" is not "
"a directory", tmp)
1635 "a directory", tmp)sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1635,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't change directory: \"%s\" is not "
"a directory", tmp)
;
1636 free(tmp);
1637 err = 1;
1638 break;
1639 }
1640 free(*pwd);
1641 *pwd = tmp;
1642 break;
1643 case I_LS:
1644 if (!path1) {
1645 do_ls_dir(conn, *pwd, *pwd, lflag);
1646 break;
1647 }
1648
1649 /* Strip pwd off beginning of non-absolute paths */
1650 tmp = NULL((void *)0);
1651 if (!path_absolute(path1))
1652 tmp = *pwd;
1653
1654 path1 = make_absolute_pwd_glob(path1, *pwd);
1655 err = do_globbed_ls(conn, path1, tmp, lflag);
1656 break;
1657 case I_DF:
1658 /* Default to current directory if no path specified */
1659 if (path1 == NULL((void *)0))
1660 path1 = xstrdup(*pwd);
1661 path1 = sftp_make_absolute(path1, *pwd);
1662 err = do_df(conn, path1, hflag, iflag);
1663 break;
1664 case I_LCHDIR:
1665 if (path1 == NULL((void *)0) || *path1 == '\0')
1666 path1 = xstrdup("~");
1667 tmp = tilde_expand_filename(path1, getuid());
1668 free(path1);
1669 path1 = tmp;
1670 if (chdir(path1) == -1) {
1671 error("Couldn't change local directory to "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1672,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Couldn't change local directory to "
"\"%s\": %s", path1, strerror((*__errno())))
1672 "\"%s\": %s", path1, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1672,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Couldn't change local directory to "
"\"%s\": %s", path1, strerror((*__errno())))
;
1673 err = 1;
1674 }
1675 break;
1676 case I_LMKDIR:
1677 if (mkdir(path1, 0777) == -1) {
1678 error("Couldn't create local directory "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1679,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Couldn't create local directory "
"\"%s\": %s", path1, strerror((*__errno())))
1679 "\"%s\": %s", path1, strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1679,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Couldn't create local directory "
"\"%s\": %s", path1, strerror((*__errno())))
;
1680 err = 1;
1681 }
1682 break;
1683 case I_LLS:
1684 local_do_ls(cmd);
1685 break;
1686 case I_SHELL:
1687 local_do_shell(cmd);
1688 break;
1689 case I_LUMASK:
1690 umask(n_arg);
1691 printf("Local umask: %03lo\n", n_arg);
1692 break;
1693 case I_CHMOD:
1694 path1 = make_absolute_pwd_glob(path1, *pwd);
1695 attrib_clear(&a);
1696 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS0x00000004;
1697 a.perm = n_arg;
1698 sftp_glob(conn, path1, GLOB_NOCHECK0x0010, NULL((void *)0), &g);
1699 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1700 if (!quiet)
1701 mprintf("Changing mode on %s\n",
1702 g.gl_pathv[i]);
1703 err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
1704 g.gl_pathv[i], &a);
1705 if (err != 0 && err_abort)
1706 break;
1707 }
1708 break;
1709 case I_CHOWN:
1710 case I_CHGRP:
1711 path1 = make_absolute_pwd_glob(path1, *pwd);
1712 sftp_glob(conn, path1, GLOB_NOCHECK0x0010, NULL((void *)0), &g);
1713 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1714 if ((hflag ? sftp_lstat : sftp_stat)(conn,
1715 g.gl_pathv[i], 0, &aa) != 0) {
1716 if (err_abort) {
1717 err = -1;
1718 break;
1719 } else
1720 continue;
1721 }
1722 if (!(aa.flags & SSH2_FILEXFER_ATTR_UIDGID0x00000002)) {
1723 error("Can't get current ownership of "sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1724,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't get current ownership of "
"remote file \"%s\"", g.gl_pathv[i])
1724 "remote file \"%s\"", g.gl_pathv[i])sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1724,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Can't get current ownership of "
"remote file \"%s\"", g.gl_pathv[i])
;
1725 if (err_abort) {
1726 err = -1;
1727 break;
1728 } else
1729 continue;
1730 }
1731 aa.flags &= SSH2_FILEXFER_ATTR_UIDGID0x00000002;
1732 if (cmdnum == I_CHOWN) {
1733 if (!quiet)
1734 mprintf("Changing owner on %s\n",
1735 g.gl_pathv[i]);
1736 aa.uid = n_arg;
1737 } else {
1738 if (!quiet)
1739 mprintf("Changing group on %s\n",
1740 g.gl_pathv[i]);
1741 aa.gid = n_arg;
1742 }
1743 err = (hflag ? sftp_lsetstat : sftp_setstat)(conn,
1744 g.gl_pathv[i], &aa);
1745 if (err != 0 && err_abort)
1746 break;
1747 }
1748 break;
1749 case I_PWD:
1750 mprintf("Remote working directory: %s\n", *pwd);
1751 break;
1752 case I_LPWD:
1753 if (!getcwd(path_buf, sizeof(path_buf))) {
1754 error("Couldn't get local cwd: %s", strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1754,
0, SYSLOG_LEVEL_ERROR, ((void *)0), "Couldn't get local cwd: %s"
, strerror((*__errno())))
;
1755 err = -1;
1756 break;
1757 }
1758 mprintf("Local working directory: %s\n", path_buf);
1759 break;
1760 case I_QUIT:
1761 /* Processed below */
1762 break;
1763 case I_HELP:
1764 help();
1765 break;
1766 case I_VERSION:
1767 printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1768 break;
1769 case I_PROGRESS:
1770 showprogress = !showprogress;
1771 if (showprogress)
1772 printf("Progress meter enabled\n");
1773 else
1774 printf("Progress meter disabled\n");
1775 break;
1776 default:
1777 fatal("%d is not implemented", cmdnum)sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1777
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "%d is not implemented"
, cmdnum)
;
1778 }
1779
1780 if (g.gl_pathc)
1781 globfree(&g);
1782 free(path1);
1783 free(path2);
1784
1785 /* If an unignored error occurs in batch mode we should abort. */
1786 if (err_abort && err != 0)
1787 return (-1);
1788 else if (cmdnum == I_QUIT)
1789 return (1);
1790
1791 return (0);
1792}
1793
1794static char *
1795prompt(EditLine *el)
1796{
1797 return ("sftp> ");
1798}
1799
1800/* Display entries in 'list' after skipping the first 'len' chars */
1801static void
1802complete_display(char **list, u_int len)
1803{
1804 u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1805 struct winsize ws;
1806 char *tmp;
1807
1808 /* Count entries for sort and find longest */
1809 for (y = 0; list[y]; y++)
1810 m = MAXIMUM(m, strlen(list[y]))(((m) > (strlen(list[y]))) ? (m) : (strlen(list[y])));
1811
1812 if (ioctl(fileno(stdin)(!__isthreaded ? (((&__sF[0]))->_file) : (fileno)((&
__sF[0])))
, TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff
) << 16) | ((('t')) << 8) | ((104)))
, &ws) != -1)
1813 width = ws.ws_col;
1814
1815 m = m > len ? m - len : 0;
1816 columns = width / (m + 2);
1817 columns = MAXIMUM(columns, 1)(((columns) > (1)) ? (columns) : (1));
1818 colspace = width / columns;
1819 colspace = MINIMUM(colspace, width)(((colspace) < (width)) ? (colspace) : (width));
1820
1821 printf("\n");
1822 m = 1;
1823 for (y = 0; list[y]; y++) {
1824 llen = strlen(list[y]);
1825 tmp = llen > len ? list[y] + len : "";
1826 mprintf("%-*s", colspace, tmp);
1827 if (m >= columns) {
1828 printf("\n");
1829 m = 1;
1830 } else
1831 m++;
1832 }
1833 printf("\n");
1834}
1835
1836/*
1837 * Given a "list" of words that begin with a common prefix of "word",
1838 * attempt to find an autocompletion to extends "word" by the next
1839 * characters common to all entries in "list".
1840 */
1841static char *
1842complete_ambiguous(const char *word, char **list, size_t count)
1843{
1844 if (word == NULL((void *)0))
1845 return NULL((void *)0);
1846
1847 if (count > 0) {
1848 u_int y, matchlen = strlen(list[0]);
1849
1850 /* Find length of common stem */
1851 for (y = 1; list[y]; y++) {
1852 u_int x;
1853
1854 for (x = 0; x < matchlen; x++)
1855 if (list[0][x] != list[y][x])
1856 break;
1857
1858 matchlen = x;
1859 }
1860
1861 if (matchlen > strlen(word)) {
1862 char *tmp = xstrdup(list[0]);
1863
1864 tmp[matchlen] = '\0';
1865 return tmp;
1866 }
1867 }
1868
1869 return xstrdup(word);
1870}
1871
1872/* Autocomplete a sftp command */
1873static int
1874complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1875 int terminated)
1876{
1877 u_int y, count = 0, cmdlen, tmplen;
1878 char *tmp, **list, argterm[3];
1879 const LineInfo *lf;
1880
1881 list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1882
1883 /* No command specified: display all available commands */
1884 if (cmd == NULL((void *)0)) {
1885 for (y = 0; cmds[y].c; y++)
1886 list[count++] = xstrdup(cmds[y].c);
1887
1888 list[count] = NULL((void *)0);
1889 complete_display(list, 0);
1890
1891 for (y = 0; list[y] != NULL((void *)0); y++)
1892 free(list[y]);
1893 free(list);
1894 return count;
1895 }
1896
1897 /* Prepare subset of commands that start with "cmd" */
1898 cmdlen = strlen(cmd);
1899 for (y = 0; cmds[y].c; y++) {
1900 if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1901 list[count++] = xstrdup(cmds[y].c);
1902 }
1903 list[count] = NULL((void *)0);
1904
1905 if (count == 0) {
1906 free(list);
1907 return 0;
1908 }
1909
1910 /* Complete ambiguous command */
1911 tmp = complete_ambiguous(cmd, list, count);
1912 if (count > 1)
1913 complete_display(list, 0);
1914
1915 for (y = 0; list[y]; y++)
1916 free(list[y]);
1917 free(list);
1918
1919 if (tmp != NULL((void *)0)) {
1920 tmplen = strlen(tmp);
1921 cmdlen = strlen(cmd);
1922 /* If cmd may be extended then do so */
1923 if (tmplen > cmdlen)
1924 if (el_insertstr(el, tmp + cmdlen) == -1)
1925 fatal("el_insertstr failed.")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1925
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "el_insertstr failed.")
;
1926 lf = el_line(el);
1927 /* Terminate argument cleanly */
1928 if (count == 1) {
1929 y = 0;
1930 if (!terminated)
1931 argterm[y++] = quote;
1932 if (lastarg || *(lf->cursor) != ' ')
1933 argterm[y++] = ' ';
1934 argterm[y] = '\0';
1935 if (y > 0 && el_insertstr(el, argterm) == -1)
1936 fatal("el_insertstr failed.")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 1936
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "el_insertstr failed.")
;
1937 }
1938 free(tmp);
1939 }
1940
1941 return count;
1942}
1943
1944/*
1945 * Determine whether a particular sftp command's arguments (if any) represent
1946 * local or remote files. The "cmdarg" argument specifies the actual argument
1947 * and accepts values 1 or 2.
1948 */
1949static int
1950complete_is_remote(char *cmd, int cmdarg) {
1951 int i;
1952
1953 if (cmd == NULL((void *)0))
1954 return -1;
1955
1956 for (i = 0; cmds[i].c; i++) {
1957 if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) {
1958 if (cmdarg == 1)
1959 return cmds[i].t;
1960 else if (cmdarg == 2)
1961 return cmds[i].t2;
1962 break;
1963 }
1964 }
1965
1966 return -1;
1967}
1968
1969/* Autocomplete a filename "file" */
1970static int
1971complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1972 char *file, int remote, int lastarg, char quote, int terminated)
1973{
1974 glob_t g;
1975 char *tmp, *tmp2, ins[8];
1976 u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs;
1977 int clen;
1978 const LineInfo *lf;
1979
1980 /* Glob from "file" location */
1981 if (file == NULL((void *)0))
1982 tmp = xstrdup("*");
1983 else
1984 xasprintf(&tmp, "%s*", file);
1985
1986 /* Check if the path is absolute. */
1987 isabs = path_absolute(tmp);
1988
1989 memset(&g, 0, sizeof(g));
1990 if (remote != LOCAL2) {
1991 tmp = make_absolute_pwd_glob(tmp, remote_path);
1992 sftp_glob(conn, tmp, GLOB_DOOFFS0x0002|GLOB_MARK0x0008, NULL((void *)0), &g);
1993 } else
1994 (void)glob(tmp, GLOB_DOOFFS0x0002|GLOB_MARK0x0008, NULL((void *)0), &g);
1995
1996 /* Determine length of pwd so we can trim completion display */
1997 for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1998 /* Terminate counting on first unescaped glob metacharacter */
1999 if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
2000 if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
2001 hadglob = 1;
2002 break;
2003 }
2004 if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
2005 tmplen++;
2006 if (tmp[tmplen] == '/')
2007 pwdlen = tmplen + 1; /* track last seen '/' */
2008 }
2009 free(tmp);
2010 tmp = NULL((void *)0);
2011
2012 if (g.gl_matchc == 0)
2013 goto out;
2014
2015 if (g.gl_matchc > 1)
2016 complete_display(g.gl_pathv, pwdlen);
2017
2018 /* Don't try to extend globs */
2019 if (file == NULL((void *)0) || hadglob)
2020 goto out;
2021
2022 tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
2023 tmp = path_strip(tmp2, isabs ? NULL((void *)0) : remote_path);
2024 free(tmp2);
2025
2026 if (tmp == NULL((void *)0))
2027 goto out;
2028
2029 tmplen = strlen(tmp);
2030 filelen = strlen(file);
2031
2032 /* Count the number of escaped characters in the input string. */
2033 cesc = isesc = 0;
2034 for (i = 0; i < filelen; i++) {
2035 if (!isesc && file[i] == '\\' && i + 1 < filelen){
2036 isesc = 1;
2037 cesc++;
2038 } else
2039 isesc = 0;
2040 }
2041
2042 if (tmplen > (filelen - cesc)) {
2043 tmp2 = tmp + filelen - cesc;
2044 len = strlen(tmp2);
2045 /* quote argument on way out */
2046 for (i = 0; i < len; i += clen) {
2047 if ((clen = mblen(tmp2 + i, len - i)) < 0 ||
2048 (size_t)clen > sizeof(ins) - 2)
2049 fatal("invalid multibyte character")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2049
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "invalid multibyte character"
)
;
2050 ins[0] = '\\';
2051 memcpy(ins + 1, tmp2 + i, clen);
2052 ins[clen + 1] = '\0';
2053 switch (tmp2[i]) {
2054 case '\'':
2055 case '"':
2056 case '\\':
2057 case '\t':
2058 case '[':
2059 case ' ':
2060 case '#':
2061 case '*':
2062 if (quote == '\0' || tmp2[i] == quote) {
2063 if (el_insertstr(el, ins) == -1)
2064 fatal("el_insertstr "sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2065
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "el_insertstr " "failed."
)
2065 "failed.")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2065
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "el_insertstr " "failed."
)
;
2066 break;
2067 }
2068 /* FALLTHROUGH */
2069 default:
2070 if (el_insertstr(el, ins + 1) == -1)
2071 fatal("el_insertstr failed.")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2071
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "el_insertstr failed.")
;
2072 break;
2073 }
2074 }
2075 }
2076
2077 lf = el_line(el);
2078 if (g.gl_matchc == 1) {
2079 i = 0;
2080 if (!terminated && quote != '\0')
2081 ins[i++] = quote;
2082 if (*(lf->cursor - 1) != '/' &&
2083 (lastarg || *(lf->cursor) != ' '))
2084 ins[i++] = ' ';
2085 ins[i] = '\0';
2086 if (i > 0 && el_insertstr(el, ins) == -1)
2087 fatal("el_insertstr failed.")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2087
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "el_insertstr failed.")
;
2088 }
2089 free(tmp);
2090
2091 out:
2092 globfree(&g);
2093 return g.gl_matchc;
2094}
2095
2096/* tab-completion hook function, called via libedit */
2097static unsigned char
2098complete(EditLine *el, int ch)
2099{
2100 char **argv, *line, quote;
2101 int argc, carg;
2102 u_int cursor, len, terminated, ret = CC_ERROR6;
2103 const LineInfo *lf;
2104 struct complete_ctx *complete_ctx;
2105
2106 lf = el_line(el);
2107 if (el_get(el, EL_CLIENTDATA14, (void**)&complete_ctx) != 0)
2108 fatal_f("el_get failed")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2108
, 1, SYSLOG_LEVEL_FATAL, ((void *)0), "el_get failed")
;
2109
2110 /* Figure out which argument the cursor points to */
2111 cursor = lf->cursor - lf->buffer;
2112 line = xmalloc(cursor + 1);
2113 memcpy(line, lf->buffer, cursor);
2114 line[cursor] = '\0';
2115 argv = makeargv(line, &carg, 1, &quote, &terminated);
Value stored to 'argv' is never read
2116 free(line);
2117
2118 /* Get all the arguments on the line */
2119 len = lf->lastchar - lf->buffer;
2120 line = xmalloc(len + 1);
2121 memcpy(line, lf->buffer, len);
2122 line[len] = '\0';
2123 argv = makeargv(line, &argc, 1, NULL((void *)0), NULL((void *)0));
2124
2125 /* Ensure cursor is at EOL or a argument boundary */
2126 if (line[cursor] != ' ' && line[cursor] != '\0' &&
2127 line[cursor] != '\n') {
2128 free(line);
2129 return ret;
2130 }
2131
2132 if (carg == 0) {
2133 /* Show all available commands */
2134 complete_cmd_parse(el, NULL((void *)0), argc == carg, '\0', 1);
2135 ret = CC_REDISPLAY8;
2136 } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
2137 /* Handle the command parsing */
2138 if (complete_cmd_parse(el, argv[0], argc == carg,
2139 quote, terminated) != 0)
2140 ret = CC_REDISPLAY8;
2141 } else if (carg >= 1) {
2142 /* Handle file parsing */
2143 int remote = 0;
2144 int i = 0, cmdarg = 0;
2145 char *filematch = NULL((void *)0);
2146
2147 if (carg > 1 && line[cursor-1] != ' ')
2148 filematch = argv[carg - 1];
2149
2150 for (i = 1; i < carg; i++) {
2151 /* Skip flags */
2152 if (argv[i][0] != '-')
2153 cmdarg++;
2154 }
2155
2156 /*
2157 * If previous argument is complete, then offer completion
2158 * on the next one.
2159 */
2160 if (line[cursor - 1] == ' ')
2161 cmdarg++;
2162
2163 remote = complete_is_remote(argv[0], cmdarg);
2164
2165 if ((remote == REMOTE1 || remote == LOCAL2) &&
2166 complete_match(el, complete_ctx->conn,
2167 *complete_ctx->remote_pathp, filematch,
2168 remote, carg == argc, quote, terminated) != 0)
2169 ret = CC_REDISPLAY8;
2170 }
2171
2172 free(line);
2173 return ret;
2174}
2175
2176static int
2177interactive_loop(struct sftp_conn *conn, char *file1, char *file2)
2178{
2179 char *remote_path;
2180 char *dir = NULL((void *)0), *startdir = NULL((void *)0);
2181 char cmd[2048];
2182 int err, interactive;
2183 EditLine *el = NULL((void *)0);
2184 History *hl = NULL((void *)0);
2185 HistEvent hev;
2186 extern char *__progname;
2187 struct complete_ctx complete_ctx;
2188
2189 if (!batchmode && isatty(STDIN_FILENO0)) {
2190 if ((el = el_init(__progname, stdin(&__sF[0]), stdout(&__sF[1]), stderr(&__sF[2]))) == NULL((void *)0))
2191 fatal("Couldn't initialise editline")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2191
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't initialise editline"
)
;
2192 if ((hl = history_init()) == NULL((void *)0))
2193 fatal("Couldn't initialise editline history")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2193
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't initialise editline history"
)
;
2194 history(hl, &hev, H_SETSIZE1, 100);
2195 el_set(el, EL_HIST10, history, hl);
2196
2197 el_set(el, EL_PROMPT0, prompt);
2198 el_set(el, EL_EDITOR2, "emacs");
2199 el_set(el, EL_TERMINAL1, NULL((void *)0));
2200 el_set(el, EL_SIGNAL3, 1);
2201 el_source(el, NULL((void *)0));
2202
2203 /* Tab Completion */
2204 el_set(el, EL_ADDFN9, "ftp-complete",
2205 "Context sensitive argument completion", complete);
2206 complete_ctx.conn = conn;
2207 complete_ctx.remote_pathp = &remote_path;
2208 el_set(el, EL_CLIENTDATA14, (void*)&complete_ctx);
2209 el_set(el, EL_BIND4, "^I", "ftp-complete", NULL((void *)0));
2210 /* enable ctrl-left-arrow and ctrl-right-arrow */
2211 el_set(el, EL_BIND4, "\\e[1;5C", "em-next-word", NULL((void *)0));
2212 el_set(el, EL_BIND4, "\\e\\e[C", "em-next-word", NULL((void *)0));
2213 el_set(el, EL_BIND4, "\\e[1;5D", "ed-prev-word", NULL((void *)0));
2214 el_set(el, EL_BIND4, "\\e\\e[D", "ed-prev-word", NULL((void *)0));
2215 /* make ^w match ksh behaviour */
2216 el_set(el, EL_BIND4, "^w", "ed-delete-prev-word", NULL((void *)0));
2217 }
2218
2219 if ((remote_path = sftp_realpath(conn, ".")) == NULL((void *)0))
2220 fatal("Need cwd")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2220
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Need cwd")
;
2221 startdir = xstrdup(remote_path);
2222
2223 if (file1 != NULL((void *)0)) {
2224 dir = xstrdup(file1);
2225 dir = sftp_make_absolute(dir, remote_path);
2226
2227 if (sftp_remote_is_dir(conn, dir) && file2 == NULL((void *)0)) {
2228 if (!quiet)
2229 mprintf("Changing to: %s\n", dir);
2230 snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
2231 if (parse_dispatch_command(conn, cmd,
2232 &remote_path, startdir, 1, 0) != 0) {
2233 free(dir);
2234 free(startdir);
2235 free(remote_path);
2236 free(conn);
2237 return (-1);
2238 }
2239 } else {
2240 /* XXX this is wrong wrt quoting */
2241 snprintf(cmd, sizeof cmd, "get%s %s%s%s",
2242 global_aflag ? " -a" : "", dir,
2243 file2 == NULL((void *)0) ? "" : " ",
2244 file2 == NULL((void *)0) ? "" : file2);
2245 err = parse_dispatch_command(conn, cmd,
2246 &remote_path, startdir, 1, 0);
2247 free(dir);
2248 free(startdir);
2249 free(remote_path);
2250 free(conn);
2251 return (err);
2252 }
2253 free(dir);
2254 }
2255
2256 setvbuf(stdout(&__sF[1]), NULL((void *)0), _IOLBF1, 0);
2257 setvbuf(infile, NULL((void *)0), _IOLBF1, 0);
2258
2259 interactive = !batchmode && isatty(STDIN_FILENO0);
2260 err = 0;
2261 for (;;) {
2262 struct sigaction sa;
2263 const char *line;
2264 int count = 0;
2265
2266 interrupted = 0;
2267 memset(&sa, 0, sizeof(sa));
2268 sa.sa_handler__sigaction_u.__sa_handler = interactive ? read_interrupt : killchild;
2269 if (sigaction(SIGINT2, &sa, NULL((void *)0)) == -1) {
2270 debug3("sigaction(%s): %s", strsignal(SIGINT),sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2271,
0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "sigaction(%s): %s", strsignal
(2), strerror((*__errno())))
2271 strerror(errno))sshlog("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2271,
0, SYSLOG_LEVEL_DEBUG3, ((void *)0), "sigaction(%s): %s", strsignal
(2), strerror((*__errno())))
;
2272 break;
2273 }
2274 if (el == NULL((void *)0)) {
2275 if (interactive)
2276 printf("sftp> ");
2277 if (fgets(cmd, sizeof(cmd), infile) == NULL((void *)0)) {
2278 if (interactive)
2279 printf("\n");
2280 if (interrupted)
2281 continue;
2282 break;
2283 }
2284 } else {
2285 if ((line = el_gets(el, &count)) == NULL((void *)0) ||
2286 count <= 0) {
2287 printf("\n");
2288 if (interrupted)
2289 continue;
2290 break;
2291 }
2292 history(hl, &hev, H_ENTER10, line);
2293 if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
2294 fprintf(stderr(&__sF[2]), "Error: input line too long\n");
2295 continue;
2296 }
2297 }
2298
2299 cmd[strcspn(cmd, "\n")] = '\0';
2300
2301 /* Handle user interrupts gracefully during commands */
2302 interrupted = 0;
2303 ssh_signal(SIGINT2, cmd_interrupt);
2304
2305 err = parse_dispatch_command(conn, cmd, &remote_path,
2306 startdir, batchmode, !interactive && el == NULL((void *)0));
2307 if (err != 0)
2308 break;
2309 }
2310 ssh_signal(SIGCHLD20, SIG_DFL(void (*)(int))0);
2311 free(remote_path);
2312 free(startdir);
2313 free(conn);
2314
2315 if (el != NULL((void *)0))
2316 el_end(el);
2317
2318 /* err == 1 signifies normal "quit" exit */
2319 return (err >= 0 ? 0 : -1);
2320}
2321
2322static void
2323connect_to_server(char *path, char **args, int *in, int *out)
2324{
2325 int c_in, c_out, inout[2];
2326
2327 if (socketpair(AF_UNIX1, SOCK_STREAM1, 0, inout) == -1)
2328 fatal("socketpair: %s", strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2328
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "socketpair: %s", strerror
((*__errno())))
;
2329 *in = *out = inout[0];
2330 c_in = c_out = inout[1];
2331
2332 if ((sshpid = fork()) == -1)
2333 fatal("fork: %s", strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2333
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "fork: %s", strerror((*
__errno())))
;
2334 else if (sshpid == 0) {
2335 if ((dup2(c_in, STDIN_FILENO0) == -1) ||
2336 (dup2(c_out, STDOUT_FILENO1) == -1)) {
2337 fprintf(stderr(&__sF[2]), "dup2: %s\n", strerror(errno(*__errno())));
2338 _exit(1);
2339 }
2340 close(*in);
2341 close(*out);
2342 close(c_in);
2343 close(c_out);
2344
2345 /*
2346 * The underlying ssh is in the same process group, so we must
2347 * ignore SIGINT if we want to gracefully abort commands,
2348 * otherwise the signal will make it to the ssh process and
2349 * kill it too. Contrawise, since sftp sends SIGTERMs to the
2350 * underlying ssh, it must *not* ignore that signal.
2351 */
2352 ssh_signal(SIGINT2, SIG_IGN(void (*)(int))1);
2353 ssh_signal(SIGTERM15, SIG_DFL(void (*)(int))0);
2354 execvp(path, args);
2355 fprintf(stderr(&__sF[2]), "exec: %s: %s\n", path, strerror(errno(*__errno())));
2356 _exit(1);
2357 }
2358
2359 ssh_signal(SIGTERM15, killchild);
2360 ssh_signal(SIGINT2, killchild);
2361 ssh_signal(SIGHUP1, killchild);
2362 ssh_signal(SIGTSTP18, suspchild);
2363 ssh_signal(SIGTTIN21, suspchild);
2364 ssh_signal(SIGTTOU22, suspchild);
2365 ssh_signal(SIGCHLD20, sigchld_handler);
2366 close(c_in);
2367 close(c_out);
2368}
2369
2370static void
2371usage(void)
2372{
2373 extern char *__progname;
2374
2375 fprintf(stderr(&__sF[2]),
2376 "usage: %s [-46AaCfNpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2377 " [-D sftp_server_command] [-F ssh_config] [-i identity_file]\n"
2378 " [-J destination] [-l limit] [-o ssh_option] [-P port]\n"
2379 " [-R num_requests] [-S program] [-s subsystem | sftp_server]\n"
2380 " [-X sftp_option] destination\n",
2381 __progname);
2382 exit(1);
2383}
2384
2385int
2386main(int argc, char **argv)
2387{
2388 int r, in, out, ch, err, tmp, port = -1, noisy = 0;
2389 char *host = NULL((void *)0), *user, *cp, **cpp, *file2 = NULL((void *)0);
2390 int debug_level = 0;
2391 char *file1 = NULL((void *)0), *sftp_server = NULL((void *)0);
2392 char *ssh_program = _PATH_SSH_PROGRAM"/usr/bin/ssh", *sftp_direct = NULL((void *)0);
2393 const char *errstr;
2394 LogLevel ll = SYSLOG_LEVEL_INFO;
2395 arglist args;
2396 extern int optind;
2397 extern char *optarg;
2398 struct sftp_conn *conn;
2399 size_t copy_buffer_len = 0;
2400 size_t num_requests = 0;
2401 long long llv, limit_kbps = 0;
2402
2403 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2404 sanitise_stdfd();
2405 setlocale(LC_CTYPE2, "");
2406
2407 memset(&args, '\0', sizeof(args));
2408 args.list = NULL((void *)0);
2409 addargs(&args, "%s", ssh_program);
2410 addargs(&args, "-oForwardX11 no");
2411 addargs(&args, "-oPermitLocalCommand no");
2412 addargs(&args, "-oClearAllForwardings yes");
2413
2414 ll = SYSLOG_LEVEL_INFO;
2415 infile = stdin(&__sF[0]);
2416
2417 while ((ch = getopt(argc, argv,
2418 "1246AafhNpqrvCc:D:i:l:o:s:S:b:B:F:J:P:R:X:")) != -1) {
2419 switch (ch) {
2420 /* Passed through to ssh(1) */
2421 case 'A':
2422 case '4':
2423 case '6':
2424 case 'C':
2425 addargs(&args, "-%c", ch);
2426 break;
2427 /* Passed through to ssh(1) with argument */
2428 case 'F':
2429 case 'J':
2430 case 'c':
2431 case 'i':
2432 case 'o':
2433 addargs(&args, "-%c", ch);
2434 addargs(&args, "%s", optarg);
2435 break;
2436 case 'q':
2437 ll = SYSLOG_LEVEL_ERROR;
2438 quiet = 1;
2439 showprogress = 0;
2440 addargs(&args, "-%c", ch);
2441 break;
2442 case 'P':
2443 port = a2port(optarg);
2444 if (port <= 0)
2445 fatal("Bad port \"%s\"\n", optarg)sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2445
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Bad port \"%s\"\n", optarg
)
;
2446 break;
2447 case 'v':
2448 if (debug_level < 3) {
2449 addargs(&args, "-v");
2450 ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2451 }
2452 debug_level++;
2453 break;
2454 case '1':
2455 fatal("SSH protocol v.1 is no longer supported")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2455
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "SSH protocol v.1 is no longer supported"
)
;
2456 break;
2457 case '2':
2458 /* accept silently */
2459 break;
2460 case 'a':
2461 global_aflag = 1;
2462 break;
2463 case 'B':
2464 copy_buffer_len = strtol(optarg, &cp, 10);
2465 if (copy_buffer_len == 0 || *cp != '\0')
2466 fatal("Invalid buffer size \"%s\"", optarg)sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2466
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid buffer size \"%s\""
, optarg)
;
2467 break;
2468 case 'b':
2469 if (batchmode)
2470 fatal("Batch file already specified.")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2470
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Batch file already specified."
)
;
2471
2472 /* Allow "-" as stdin */
2473 if (strcmp(optarg, "-") != 0 &&
2474 (infile = fopen(optarg, "r")) == NULL((void *)0))
2475 fatal("%s (%s).", strerror(errno), optarg)sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2475
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "%s (%s).", strerror((*
__errno())), optarg)
;
2476 showprogress = 0;
2477 quiet = batchmode = 1;
2478 addargs(&args, "-obatchmode yes");
2479 break;
2480 case 'f':
2481 global_fflag = 1;
2482 break;
2483 case 'N':
2484 noisy = 1; /* Used to clear quiet mode after getopt */
2485 break;
2486 case 'p':
2487 global_pflag = 1;
2488 break;
2489 case 'D':
2490 sftp_direct = optarg;
2491 break;
2492 case 'l':
2493 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2494 &errstr);
2495 if (errstr != NULL((void *)0))
2496 usage();
2497 limit_kbps *= 1024; /* kbps */
2498 break;
2499 case 'r':
2500 global_rflag = 1;
2501 break;
2502 case 'R':
2503 num_requests = strtol(optarg, &cp, 10);
2504 if (num_requests == 0 || *cp != '\0')
2505 fatal("Invalid number of requests \"%s\"",sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2506
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid number of requests \"%s\""
, optarg)
2506 optarg)sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2506
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid number of requests \"%s\""
, optarg)
;
2507 break;
2508 case 's':
2509 sftp_server = optarg;
2510 break;
2511 case 'S':
2512 ssh_program = optarg;
2513 replacearg(&args, 0, "%s", ssh_program);
2514 break;
2515 case 'X':
2516 /* Please keep in sync with ssh.c -X */
2517 if (strncmp(optarg, "buffer=", 7) == 0) {
2518 r = scan_scaled(optarg + 7, &llv);
2519 if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
2520 r = -1;
2521 errno(*__errno()) = EINVAL22;
2522 }
2523 if (r == -1) {
2524 fatal("Invalid buffer size \"%s\": %s",sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2525
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid buffer size \"%s\": %s"
, optarg + 7, strerror((*__errno())))
2525 optarg + 7, strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2525
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid buffer size \"%s\": %s"
, optarg + 7, strerror((*__errno())))
;
2526 }
2527 copy_buffer_len = (size_t)llv;
2528 } else if (strncmp(optarg, "nrequests=", 10) == 0) {
2529 llv = strtonum(optarg + 10, 1, 256 * 1024,
2530 &errstr);
2531 if (errstr != NULL((void *)0)) {
2532 fatal("Invalid number of requests "sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2533
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid number of requests "
"\"%s\": %s", optarg + 10, errstr)
2533 "\"%s\": %s", optarg + 10, errstr)sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2533
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid number of requests "
"\"%s\": %s", optarg + 10, errstr)
;
2534 }
2535 num_requests = (size_t)llv;
2536 } else {
2537 fatal("Invalid -X option")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2537
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Invalid -X option")
;
2538 }
2539 break;
2540 case 'h':
2541 default:
2542 usage();
2543 }
2544 }
2545
2546 /* Do this last because we want the user to be able to override it */
2547 addargs(&args, "-oForwardAgent no");
2548
2549 if (!isatty(STDERR_FILENO2))
2550 showprogress = 0;
2551
2552 if (noisy)
2553 quiet = 0;
2554
2555 log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2556
2557 if (sftp_direct == NULL((void *)0)) {
2558 if (optind == argc || argc > (optind + 2))
2559 usage();
2560 argv += optind;
2561
2562 switch (parse_uri("sftp", *argv, &user, &host, &tmp, &file1)) {
2563 case -1:
2564 usage();
2565 break;
2566 case 0:
2567 if (tmp != -1)
2568 port = tmp;
2569 break;
2570 default:
2571 /* Try with user, host and path. */
2572 if (parse_user_host_path(*argv, &user, &host,
2573 &file1) == 0)
2574 break;
2575 /* Try with user and host. */
2576 if (parse_user_host_port(*argv, &user, &host, NULL((void *)0))
2577 == 0)
2578 break;
2579 /* Treat as a plain hostname. */
2580 host = xstrdup(*argv);
2581 host = cleanhostname(host);
2582 break;
2583 }
2584 file2 = *(argv + 1);
2585
2586 if (!*host) {
2587 fprintf(stderr(&__sF[2]), "Missing hostname\n");
2588 usage();
2589 }
2590
2591 if (port != -1)
2592 addargs(&args, "-oPort %d", port);
2593 if (user != NULL((void *)0)) {
2594 addargs(&args, "-l");
2595 addargs(&args, "%s", user);
2596 }
2597
2598 /* no subsystem if the server-spec contains a '/' */
2599 if (sftp_server == NULL((void *)0) || strchr(sftp_server, '/') == NULL((void *)0))
2600 addargs(&args, "-s");
2601
2602 addargs(&args, "--");
2603 addargs(&args, "%s", host);
2604 addargs(&args, "%s", (sftp_server != NULL((void *)0) ?
2605 sftp_server : "sftp"));
2606
2607 connect_to_server(ssh_program, args.list, &in, &out);
2608 } else {
2609 if ((r = argv_split(sftp_direct, &tmp, &cpp, 1)) != 0)
2610 fatal_r(r, "Parse -D arguments")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2610
, 0, SYSLOG_LEVEL_FATAL, ssh_err(r), "Parse -D arguments")
;
2611 if (cpp[0] == 0)
2612 fatal("No sftp server specified via -D")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2612
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "No sftp server specified via -D"
)
;
2613 connect_to_server(cpp[0], cpp, &in, &out);
2614 argv_free(cpp, tmp);
2615 }
2616 freeargs(&args);
2617
2618 conn = sftp_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2619 if (conn == NULL((void *)0))
2620 fatal("Couldn't initialise connection to server")sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2620
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't initialise connection to server"
)
;
2621
2622 if (!quiet) {
2623 if (sftp_direct == NULL((void *)0))
2624 fprintf(stderr(&__sF[2]), "Connected to %s.\n", host);
2625 else
2626 fprintf(stderr(&__sF[2]), "Attached to %s.\n", sftp_direct);
2627 }
2628
2629 err = interactive_loop(conn, file1, file2);
2630
2631 close(in);
2632 close(out);
2633 if (batchmode)
2634 fclose(infile);
2635
2636 while (waitpid(sshpid, NULL((void *)0), 0) == -1 && sshpid > 1)
2637 if (errno(*__errno()) != EINTR4)
2638 fatal("Couldn't wait for ssh process: %s",sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2639
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't wait for ssh process: %s"
, strerror((*__errno())))
2639 strerror(errno))sshfatal("/usr/src/usr.bin/ssh/sftp/../sftp.c", __func__, 2639
, 0, SYSLOG_LEVEL_FATAL, ((void *)0), "Couldn't wait for ssh process: %s"
, strerror((*__errno())))
;
2640
2641 exit(err == 0 ? 0 : 1);
2642}