diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-09 15:46:07 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-09 15:46:07 +0000 |
commit | c41cba5a578b278dc7d57ef97234a2a67e076d20 (patch) | |
tree | 8a8c63bd0ac7c1ecf09ff4a9d3326a7596ee85b0 /networking/ftpd.c | |
parent | 9b2fbda53853ae651349f97bcf86c891cc137f92 (diff) | |
download | busybox-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.c | 400 |
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 */ | ||
66 | enum { | 67 | enum { |
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 | |||
79 | enum { | ||
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 | ||
122 | static char * | 136 | static char * |
123 | escape_text(const char *prepend, const char *str, char from, const char *to, char append) | 137 | escape_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 | ||
161 | static void | 171 | /* Returns strlen as a bonus */ |
172 | static unsigned | ||
162 | replace_char(char *str, char from, char to) | 173 | replace_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 | ||
168 | static void | 184 | static void |
169 | cmdio_write(unsigned status, const char *str) | 185 | cmdio_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 | ||
264 | static void | ||
265 | init_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 | |||
279 | static int | 285 | static int |
280 | ftpdataio_get_pasv_fd(void) | 286 | ftpdataio_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 | |||
295 | static int | ||
296 | ftpdataio_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 | ||
307 | static void | ||
308 | ftpdataio_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 | |||
324 | static inline int | 301 | static inline int |
325 | port_active(void) | 302 | port_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) | |||
374 | static unsigned | 351 | static unsigned |
375 | bind_for_passive_mode(void) | 352 | bind_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 | ||
538 | static char * | 515 | static int |
539 | statbuf_getperms(const struct stat *statbuf) | 516 | popen_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 | |||
576 | static void | ||
577 | write_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 | |||
621 | static void | ||
622 | write_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 | ||
661 | static void | 555 | static void |
662 | handle_dir_common(int full_details, int stat_cmd) | 556 | handle_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 | |||
701 | static void | 600 | static void |
702 | handle_list(void) | 601 | handle_list(void) |
703 | { | 602 | { |
704 | handle_dir_common(1, 0); | 603 | handle_dir_common(2); |
705 | } | 604 | } |
706 | |||
707 | static void | 605 | static void |
708 | handle_nlst(void) | 606 | handle_nlst(void) |
709 | { | 607 | { |
710 | handle_dir_common(0, 0); | 608 | handle_dir_common(0); |
711 | } | 609 | } |
712 | |||
713 | static void | 610 | static void |
714 | handle_stat_file(void) | 611 | handle_stat_file(void) |
715 | { | 612 | { |
716 | handle_dir_common(1, 1); | 613 | handle_dir_common(3); |
717 | } | 614 | } |
718 | 615 | ||
719 | static void | 616 | static 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 | ||
907 | int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 804 | int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
908 | int ftpd_main(int argc UNUSED_PARAM, char **argv) | 805 | int 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) |