aboutsummaryrefslogtreecommitdiff
path: root/networking/ftpd.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-03-09 15:46:07 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-03-09 15:46:07 +0000
commitc41cba5a578b278dc7d57ef97234a2a67e076d20 (patch)
tree8a8c63bd0ac7c1ecf09ff4a9d3326a7596ee85b0 /networking/ftpd.c
parent9b2fbda53853ae651349f97bcf86c891cc137f92 (diff)
downloadbusybox-w32-c41cba5a578b278dc7d57ef97234a2a67e076d20.tar.gz
busybox-w32-c41cba5a578b278dc7d57ef97234a2a67e076d20.tar.bz2
busybox-w32-c41cba5a578b278dc7d57ef97234a2a67e076d20.zip
ftpd: reuse ls applet for LIST/NLST/STAT generation
function old new delta popen_ls - 211 +211 ftpd_main 1760 1826 +66 handle_dir_common 199 228 +29 get_remote_transfer_fd 89 104 +15 replace_char 30 34 +4 handle_upload_common 263 265 +2 bind_for_passive_mode 129 121 -8 cmdio_write 84 62 -22 escape_text 166 136 -30 init_data_sock_params 81 - -81 ftpdataio_dispose_transfer_fd 87 - -87 write_dirstats 149 - -149 write_filestats 603 - -603 ------------------------------------------------------------------------------ (add/remove: 1/4 grow/shrink: 11/5 up/down: 384/-986) Total: -602 bytes text data bss dec hex filename 808804 476 7864 817144 c77f8 busybox_old 808156 476 7864 816496 c7570 busybox_unstripped
Diffstat (limited to 'networking/ftpd.c')
-rw-r--r--networking/ftpd.c400
1 files changed, 154 insertions, 246 deletions
diff --git a/networking/ftpd.c b/networking/ftpd.c
index e83066032..2a7d8006b 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -63,9 +63,24 @@
63#define STR1(s) #s 63#define STR1(s) #s
64#define STR(s) STR1(s) 64#define STR(s) STR1(s)
65 65
66/* Convert a constant to 3-digit string, packed into uint32_t */
66enum { 67enum {
67 OPT_v = (1 << 0), 68 /* Shift for Nth decimal digit */
68 OPT_w = (1 << 1), 69 SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
70 SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
71 SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
72};
73#define STRNUM32(s) (uint32_t)(0 \
74 | (('0' + ((s) / 1 % 10)) << SHIFT0) \
75 | (('0' + ((s) / 10 % 10)) << SHIFT1) \
76 | (('0' + ((s) / 100 % 10)) << SHIFT2) \
77)
78
79enum {
80 OPT_l = (1 << 0),
81 OPT_1 = (1 << 1),
82 OPT_v = (1 << 2),
83 OPT_w = (1 << 3),
69 84
70#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d) 85#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
71#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c) 86#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
@@ -106,78 +121,84 @@ struct globals {
106 len_and_sockaddr *local_addr; 121 len_and_sockaddr *local_addr;
107 len_and_sockaddr *port_addr; 122 len_and_sockaddr *port_addr;
108 int pasv_listen_fd; 123 int pasv_listen_fd;
109 int data_fd; 124 int proc_self_fd;
110 off_t restart_pos; 125 off_t restart_pos;
111 char *ftp_cmd; 126 char *ftp_cmd;
112 char *ftp_arg; 127 char *ftp_arg;
113#if ENABLE_FEATURE_FTP_WRITE 128#if ENABLE_FEATURE_FTP_WRITE
114 char *rnfr_filename; 129 char *rnfr_filename;
115#endif 130#endif
116 smallint opts;
117}; 131};
118#define G (*(struct globals*)&bb_common_bufsiz1) 132#define G (*(struct globals*)&bb_common_bufsiz1)
119#define INIT_G() do { } while (0) 133#define INIT_G() do { } while (0)
120 134
121 135
122static char * 136static char *
123escape_text(const char *prepend, const char *str, char from, const char *to, char append) 137escape_text(const char *prepend, const char *str, unsigned escapee)
124{ 138{
125 size_t retlen, remainlen, chunklen, tolen; 139 unsigned retlen, remainlen, chunklen;
126 const char *remain;
127 char *ret, *found; 140 char *ret, *found;
141 char append;
128 142
129 remain = str; 143 append = (char)escapee;
130 remainlen = strlen(str); 144 escapee >>= 8;
131 tolen = strlen(to);
132 145
133 /* Simply alloc strlen(str)*strlen(to). "to" is max 2 so it's ok */ 146 remainlen = strlen(str);
134 retlen = strlen(prepend); 147 retlen = strlen(prepend);
135 ret = xmalloc(retlen + remainlen * tolen + 1 + 1); 148 ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
136 strcpy(ret, prepend); 149 strcpy(ret, prepend);
137 150
138 for (;;) { 151 for (;;) {
139 found = strchrnul(remain, from); 152 found = strchrnul(str, escapee);
140 chunklen = found - remain; 153 chunklen = found - str + 1;
141 154
142 /* Copy chunk which doesn't contain 'from' to ret */ 155 /* Copy chunk up to and including escapee (or NUL) to ret */
143 memcpy(&ret[retlen], remain, chunklen); 156 memcpy(ret + retlen, str, chunklen);
144 retlen += chunklen; 157 retlen += chunklen;
145 158
146 if (*found != '\0') { 159 if (*found == '\0') {
147 /* Now copy 'to' instead of 'from' */ 160 /* It wasn't escapee, it was NUL! */
148 memcpy(&ret[retlen], to, tolen); 161 ret[retlen - 1] = append; /* replace NUL */
149 retlen += tolen; 162 ret[retlen] = '\0'; /* add NUL */
150
151 remain = found + 1;
152 } else {
153 ret[retlen] = append;
154 ret[retlen+1] = '\0';
155 break; 163 break;
156 } 164 }
165 ret[retlen++] = escapee; /* duplicate escapee */
166 str = found + 1;
157 } 167 }
158 return ret; 168 return ret;
159} 169}
160 170
161static void 171/* Returns strlen as a bonus */
172static unsigned
162replace_char(char *str, char from, char to) 173replace_char(char *str, char from, char to)
163{ 174{
164 while ((str = strchr(str, from)) != NULL) 175 char *p = str;
165 *str++ = to; 176 while (*p) {
177 if (*p == from)
178 *p = to;
179 p++;
180 }
181 return p - str;
166} 182}
167 183
168static void 184static void
169cmdio_write(unsigned status, const char *str) 185cmdio_write(uint32_t status_str, const char *str)
170{ 186{
187 union {
188 char buf[4];
189 uint32_t v32;
190 } u;
171 char *response; 191 char *response;
172 int len; 192 int len;
173 193
194 u.v32 = status_str;
195
174 /* FTP allegedly uses telnet protocol for command link. 196 /* FTP allegedly uses telnet protocol for command link.
175 * In telnet, 0xff is an escape char, and needs to be escaped: */ 197 * In telnet, 0xff is an escape char, and needs to be escaped: */
176 response = escape_text(utoa(status), str, '\xff', "\xff\xff", '\r'); 198 response = escape_text(u.buf, str, (0xff << 8) + '\r');
177 199
178 /* ?! does FTP send embedded LFs as NULs? wow */ 200 /* ?! does FTP send embedded LFs as NULs? wow */
179 len = strlen(response); 201 len = replace_char(response, '\n', '\0');
180 replace_char(response, '\n', '\0');
181 202
182 response[len++] = '\n'; /* tack on trailing '\n' */ 203 response[len++] = '\n'; /* tack on trailing '\n' */
183 xwrite(STDOUT_FILENO, response, len); 204 xwrite(STDOUT_FILENO, response, len);
@@ -215,9 +236,9 @@ handle_pwd(void)
215 cwd = xstrdup(""); 236 cwd = xstrdup("");
216 237
217 /* We have to promote each " to "" */ 238 /* We have to promote each " to "" */
218 response = escape_text(" \"", cwd, '\"', "\"\"", '\"'); 239 response = escape_text(" \"", cwd, ('"' << 8) + '"');
219 free(cwd); 240 free(cwd);
220 cmdio_write(FTP_PWDOK, response); 241 cmdio_write(STRNUM32(FTP_PWDOK), response);
221 free(response); 242 free(response);
222} 243}
223 244
@@ -261,21 +282,6 @@ handle_help(void)
261 282
262/* Download commands */ 283/* Download commands */
263 284
264static void
265init_data_sock_params(int sock_fd)
266{
267 struct linger linger;
268
269 G.data_fd = sock_fd;
270
271 memset(&linger, 0, sizeof(linger));
272 linger.l_onoff = 1;
273 linger.l_linger = 32767;
274
275 setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
276 setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
277}
278
279static int 285static int
280ftpdataio_get_pasv_fd(void) 286ftpdataio_get_pasv_fd(void)
281{ 287{
@@ -288,39 +294,10 @@ ftpdataio_get_pasv_fd(void)
288 return remote_fd; 294 return remote_fd;
289 } 295 }
290 296
291 init_data_sock_params(remote_fd); 297 setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
292 return remote_fd;
293}
294
295static int
296ftpdataio_get_port_fd(void)
297{
298 int remote_fd;
299
300 /* Do we want to die or print error to client? */
301 remote_fd = xconnect_stream(G.port_addr);
302
303 init_data_sock_params(remote_fd);
304 return remote_fd; 298 return remote_fd;
305} 299}
306 300
307static void
308ftpdataio_dispose_transfer_fd(void)
309{
310 /* This close() blocks because we set SO_LINGER */
311 if (G.data_fd > STDOUT_FILENO) {
312 if (close(G.data_fd) < 0) {
313 /* Do it again without blocking. */
314 struct linger linger;
315
316 memset(&linger, 0, sizeof(linger));
317 setsockopt(G.data_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
318 close(G.data_fd);
319 }
320 }
321 G.data_fd = -1;
322}
323
324static inline int 301static inline int
325port_active(void) 302port_active(void)
326{ 303{
@@ -341,12 +318,12 @@ get_remote_transfer_fd(const char *p_status_msg)
341 if (pasv_active()) 318 if (pasv_active())
342 remote_fd = ftpdataio_get_pasv_fd(); 319 remote_fd = ftpdataio_get_pasv_fd();
343 else 320 else
344 remote_fd = ftpdataio_get_port_fd(); 321 remote_fd = xconnect_stream(G.port_addr);
345 322
346 if (remote_fd < 0) 323 if (remote_fd < 0)
347 return remote_fd; 324 return remote_fd;
348 325
349 cmdio_write(FTP_DATACONN, p_status_msg); 326 cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
350 return remote_fd; 327 return remote_fd;
351} 328}
352 329
@@ -374,21 +351,21 @@ port_pasv_cleanup(void)
374static unsigned 351static unsigned
375bind_for_passive_mode(void) 352bind_for_passive_mode(void)
376{ 353{
354 int fd;
377 unsigned port; 355 unsigned port;
378 356
379 port_pasv_cleanup(); 357 port_pasv_cleanup();
380 358
381 G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0); 359 G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
382 setsockopt_reuseaddr(G.pasv_listen_fd); 360 setsockopt_reuseaddr(fd);
383 361
384 set_nport(G.local_addr, 0); 362 set_nport(G.local_addr, 0);
385 xbind(G.pasv_listen_fd, &G.local_addr->u.sa, G.local_addr->len); 363 xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
386 xlisten(G.pasv_listen_fd, 1); 364 xlisten(fd, 1);
387 getsockname(G.pasv_listen_fd, &G.local_addr->u.sa, &G.local_addr->len); 365 getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
388 366
389 port = get_nport(&G.local_addr->u.sa); 367 port = get_nport(&G.local_addr->u.sa);
390 port = ntohs(port); 368 port = ntohs(port);
391
392 return port; 369 return port;
393} 370}
394 371
@@ -487,7 +464,7 @@ handle_retr(void)
487 G.restart_pos = 0; 464 G.restart_pos = 0;
488 465
489 if (!data_transfer_checks_ok()) 466 if (!data_transfer_checks_ok())
490 return; 467 return; /* data_transfer_checks_ok emitted error response */
491 468
492 /* O_NONBLOCK is useful if file happens to be a device node */ 469 /* O_NONBLOCK is useful if file happens to be a device node */
493 opened_file = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1; 470 opened_file = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
@@ -520,7 +497,7 @@ handle_retr(void)
520 goto port_pasv_cleanup_out; 497 goto port_pasv_cleanup_out;
521 498
522 trans_ret = bb_copyfd_eof(opened_file, remote_fd); 499 trans_ret = bb_copyfd_eof(opened_file, remote_fd);
523 ftpdataio_dispose_transfer_fd(); 500 close(remote_fd);
524 if (trans_ret < 0) 501 if (trans_ret < 0)
525 cmdio_write_error(FTP_BADSENDFILE); 502 cmdio_write_error(FTP_BADSENDFILE);
526 else 503 else
@@ -535,185 +512,105 @@ handle_retr(void)
535 512
536/* List commands */ 513/* List commands */
537 514
538static char * 515static int
539statbuf_getperms(const struct stat *statbuf) 516popen_ls(const char *opt)
540{
541 char *perms;
542 enum { r = 'r', w = 'w', x = 'x', s = 's', S = 'S' };
543
544 perms = xmalloc(11);
545 memset(perms, '-', 10);
546
547 perms[0] = '?';
548 switch (statbuf->st_mode & S_IFMT) {
549 case S_IFREG: perms[0] = '-'; break;
550 case S_IFDIR: perms[0] = 'd'; break;
551 case S_IFLNK: perms[0] = 'l'; break;
552 case S_IFIFO: perms[0] = 'p'; break;
553 case S_IFSOCK: perms[0] = s; break;
554 case S_IFCHR: perms[0] = 'c'; break;
555 case S_IFBLK: perms[0] = 'b'; break;
556 }
557
558 if (statbuf->st_mode & S_IRUSR) perms[1] = r;
559 if (statbuf->st_mode & S_IWUSR) perms[2] = w;
560 if (statbuf->st_mode & S_IXUSR) perms[3] = x;
561 if (statbuf->st_mode & S_IRGRP) perms[4] = r;
562 if (statbuf->st_mode & S_IWGRP) perms[5] = w;
563 if (statbuf->st_mode & S_IXGRP) perms[6] = x;
564 if (statbuf->st_mode & S_IROTH) perms[7] = r;
565 if (statbuf->st_mode & S_IWOTH) perms[8] = w;
566 if (statbuf->st_mode & S_IXOTH) perms[9] = x;
567 if (statbuf->st_mode & S_ISUID) perms[3] = (perms[3] == x) ? s : S;
568 if (statbuf->st_mode & S_ISGID) perms[6] = (perms[6] == x) ? s : S;
569 if (statbuf->st_mode & S_ISVTX) perms[9] = (perms[9] == x) ? 't' : 'T';
570
571 perms[10] = '\0';
572
573 return perms;
574}
575
576static void
577write_filestats(int fd, const char *filename,
578 const struct stat *statbuf)
579{
580 off_t size;
581 char *stats, *lnkname = NULL, *perms;
582 const char *name;
583 char timestr[32];
584 struct tm *tm;
585
586 name = bb_get_last_path_component_nostrip(filename);
587
588 if (statbuf != NULL) {
589 size = statbuf->st_size;
590
591 if (S_ISLNK(statbuf->st_mode))
592 /* Damn symlink... */
593 lnkname = xmalloc_readlink(filename);
594
595 tm = gmtime(&statbuf->st_mtime);
596 if (strftime(timestr, sizeof(timestr), "%b %d %H:%M", tm) == 0)
597 bb_error_msg_and_die("strftime");
598
599 timestr[sizeof(timestr) - 1] = '\0';
600
601 perms = statbuf_getperms(statbuf);
602
603 stats = xasprintf("%s %u\tftp ftp %"OFF_FMT"u\t%s %s",
604 perms, (int) statbuf->st_nlink,
605 size, timestr, name);
606
607 free(perms);
608 } else
609 stats = xstrdup(name);
610
611 xwrite_str(fd, stats);
612 free(stats);
613 if (lnkname != NULL) {
614 xwrite_str(fd, " -> ");
615 xwrite_str(fd, lnkname);
616 free(lnkname);
617 }
618 xwrite_str(fd, "\r\n");
619}
620
621static void
622write_dirstats(int fd, const char *dname, int details)
623{ 517{
624 DIR *dir; 518 char *cwd;
625 struct dirent *dirent; 519 const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
626 struct stat statbuf; 520 struct fd_pair outfd;
627 char *filename; 521 pid_t pid;
628
629 dir = xopendir(dname);
630 522
631 for (;;) { 523 cwd = xrealloc_getcwd_or_warn(NULL);
632 dirent = readdir(dir);
633 if (dirent == NULL)
634 break;
635 524
636 /* Ignore . and .. */ 525 xpiped_pair(outfd);
637 if (dirent->d_name[0] == '.') { 526
638 if (dirent->d_name[1] == '\0' 527 fflush(NULL);
639 || (dirent->d_name[1] == '.' && dirent->d_name[2] == '\0') 528 pid = vfork();
640 ) { 529
641 continue; 530 switch (pid) {
642 } 531 case -1: /* failure */
532 bb_perror_msg_and_die("vfork");
533 case 0: /* child */
534 /* NB: close _first_, then move fds! */
535 close(outfd.rd);
536 xmove_fd(outfd.wr, STDOUT_FILENO);
537 close(STDIN_FILENO);
538 /* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
539 if (fchdir(G.proc_self_fd) == 0) {
540 close(G.proc_self_fd);
541 argv[2] = cwd;
542 /* ftpd ls helper chdirs to argv[2],
543 * preventing peer from seeing /proc/self
544 */
545 execv("exe", (char**) argv);
643 } 546 }
644 547 _exit(127);
645 if (details) {
646 filename = xasprintf("%s/%s", dname, dirent->d_name);
647 if (lstat(filename, &statbuf) != 0) {
648 free(filename);
649 break;
650 }
651 } else
652 filename = xstrdup(dirent->d_name);
653
654 write_filestats(fd, filename, details ? &statbuf : NULL);
655 free(filename);
656 } 548 }
657 549 /* parent */
658 closedir(dir); 550 close(outfd.wr);
551 free(cwd);
552 return outfd.rd;
659} 553}
660 554
661static void 555static void
662handle_dir_common(int full_details, int stat_cmd) 556handle_dir_common(int opts)
663{ 557{
664 int fd; 558 FILE *ls_fp;
665 struct stat statbuf; 559 char *line;
560 int ls_fd;
666 561
667 if (!stat_cmd && !data_transfer_checks_ok()) 562 if (!(opts & 1) && !data_transfer_checks_ok())
668 return; 563 return; /* data_transfer_checks_ok emitted error response */
564
565 ls_fd = popen_ls((opts & 2) ? "-l" : "-1");
566 ls_fp = fdopen(ls_fd, "r");
567 if (!ls_fp) /* never happens. paranoia */
568 bb_perror_msg_and_die("fdopen");
669 569
670 if (stat_cmd) { 570 if (opts & 1) {
671 fd = STDIN_FILENO; 571 /* STAT <filename> */
672 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n"); 572 cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
573 while (1) {
574 line = xmalloc_fgetline(ls_fp);
575 if (!line)
576 break;
577 cmdio_write(0, line); /* hack: 0 results in no status at all */
578 free(line);
579 }
580 cmdio_write_ok(FTP_STATFILE_OK);
673 } else { 581 } else {
674 fd = get_remote_transfer_fd(" Here comes the directory listing"); 582 /* LIST/NLST [<filename>] */
675 if (fd < 0) 583 int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
676 goto bail; 584 if (remote_fd >= 0) {
677 } 585 while (1) {
678 586 line = xmalloc_fgetline(ls_fp);
679 if (G.ftp_arg) { 587 if (!line)
680 if (lstat(G.ftp_arg, &statbuf) != 0) { 588 break;
681 /* Dir doesn't exist => return ok to client */ 589 xwrite_str(remote_fd, line);
682 goto bail; 590 xwrite(remote_fd, "\r\n", 2);
591 free(line);
592 }
683 } 593 }
684 if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) 594 close(remote_fd);
685 write_filestats(fd, G.ftp_arg, &statbuf);
686 else if (S_ISDIR(statbuf.st_mode))
687 write_dirstats(fd, G.ftp_arg, full_details);
688 } else
689 write_dirstats(fd, ".", full_details);
690
691 bail:
692 /* Well, if we can't open directory/file it doesn't matter */
693 if (!stat_cmd) {
694 ftpdataio_dispose_transfer_fd();
695 port_pasv_cleanup(); 595 port_pasv_cleanup();
696 cmdio_write_ok(FTP_TRANSFEROK); 596 cmdio_write_ok(FTP_TRANSFEROK);
697 } else 597 }
698 cmdio_write_ok(FTP_STATFILE_OK); 598 fclose(ls_fp); /* closes ls_fd too */
699} 599}
700
701static void 600static void
702handle_list(void) 601handle_list(void)
703{ 602{
704 handle_dir_common(1, 0); 603 handle_dir_common(2);
705} 604}
706
707static void 605static void
708handle_nlst(void) 606handle_nlst(void)
709{ 607{
710 handle_dir_common(0, 0); 608 handle_dir_common(0);
711} 609}
712
713static void 610static void
714handle_stat_file(void) 611handle_stat_file(void)
715{ 612{
716 handle_dir_common(1, 1); 613 handle_dir_common(3);
717} 614}
718 615
719static void 616static void
@@ -840,7 +737,7 @@ handle_upload_common(int is_append, int is_unique)
840 goto bail; 737 goto bail;
841 738
842 trans_ret = bb_copyfd_eof(remote_fd, local_file_fd); 739 trans_ret = bb_copyfd_eof(remote_fd, local_file_fd);
843 ftpdataio_dispose_transfer_fd(); 740 close(remote_fd);
844 741
845 if (trans_ret < 0) 742 if (trans_ret < 0)
846 cmdio_write_error(FTP_BADSENDFILE); 743 cmdio_write_error(FTP_BADSENDFILE);
@@ -905,8 +802,19 @@ cmdio_get_cmd_and_arg(void)
905} 802}
906 803
907int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 804int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
908int ftpd_main(int argc UNUSED_PARAM, char **argv) 805int ftpd_main(int argc, char **argv)
909{ 806{
807 smallint opts;
808
809 opts = getopt32(argv, "l1v" USE_FEATURE_FTP_WRITE("w"));
810
811 if (opts & (OPT_l|OPT_1)) {
812 /* Our secret backdoor to ls */
813 xchdir(argv[2]);
814 argv[2] = (char*)"--";
815 return ls_main(argc, argv);
816 }
817
910 INIT_G(); 818 INIT_G();
911 819
912 G.local_addr = get_sock_lsa(STDIN_FILENO); 820 G.local_addr = get_sock_lsa(STDIN_FILENO);
@@ -920,13 +828,13 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
920 * failure */ 828 * failure */
921 } 829 }
922 830
923 G.opts = getopt32(argv, "v" USE_FEATURE_FTP_WRITE("w"));
924
925 openlog(applet_name, LOG_PID, LOG_DAEMON); 831 openlog(applet_name, LOG_PID, LOG_DAEMON);
926 logmode |= LOGMODE_SYSLOG; 832 logmode |= LOGMODE_SYSLOG;
927 if (!(G.opts & OPT_v)) 833 if (!(opts & OPT_v))
928 logmode = LOGMODE_SYSLOG; 834 logmode = LOGMODE_SYSLOG;
929 835
836 G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
837
930 if (argv[optind]) { 838 if (argv[optind]) {
931 xchdir(argv[optind]); 839 xchdir(argv[optind]);
932 chroot("."); 840 chroot(".");
@@ -1064,7 +972,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
1064 else if (cmdval == const_REST) 972 else if (cmdval == const_REST)
1065 handle_rest(); 973 handle_rest();
1066#if ENABLE_FEATURE_FTP_WRITE 974#if ENABLE_FEATURE_FTP_WRITE
1067 else if (G.opts & OPT_w) { 975 else if (opts & OPT_w) {
1068 if (cmdval == const_STOR) 976 if (cmdval == const_STOR)
1069 handle_stor(); 977 handle_stor();
1070 else if (cmdval == const_MKD) 978 else if (cmdval == const_MKD)