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 | |
| 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
| -rw-r--r-- | coreutils/Kbuild | 1 | ||||
| -rw-r--r-- | coreutils/ls.c | 1 | ||||
| -rw-r--r-- | include/libbb.h | 2 | ||||
| -rw-r--r-- | networking/ftpd.c | 400 |
4 files changed, 157 insertions, 247 deletions
diff --git a/coreutils/Kbuild b/coreutils/Kbuild index 67c56588a..57100a9cf 100644 --- a/coreutils/Kbuild +++ b/coreutils/Kbuild | |||
| @@ -44,6 +44,7 @@ lib-$(CONFIG_LENGTH) += length.o | |||
| 44 | lib-$(CONFIG_LN) += ln.o | 44 | lib-$(CONFIG_LN) += ln.o |
| 45 | lib-$(CONFIG_LOGNAME) += logname.o | 45 | lib-$(CONFIG_LOGNAME) += logname.o |
| 46 | lib-$(CONFIG_LS) += ls.o | 46 | lib-$(CONFIG_LS) += ls.o |
| 47 | lib-$(CONFIG_FTPD) += ls.o | ||
| 47 | lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o | 48 | lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o |
| 48 | lib-$(CONFIG_MKDIR) += mkdir.o | 49 | lib-$(CONFIG_MKDIR) += mkdir.o |
| 49 | lib-$(CONFIG_MKFIFO) += mkfifo.o | 50 | lib-$(CONFIG_MKFIFO) += mkfifo.o |
diff --git a/coreutils/ls.c b/coreutils/ls.c index 7b65d049e..f4f2724c5 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
| @@ -864,7 +864,6 @@ static const char ls_color_opt[] ALIGN1 = | |||
| 864 | #endif | 864 | #endif |
| 865 | 865 | ||
| 866 | 866 | ||
| 867 | int ls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 868 | int ls_main(int argc UNUSED_PARAM, char **argv) | 867 | int ls_main(int argc UNUSED_PARAM, char **argv) |
| 869 | { | 868 | { |
| 870 | struct dnode **dnd; | 869 | struct dnode **dnd; |
diff --git a/include/libbb.h b/include/libbb.h index 7d6ea9029..80a1c912c 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
| @@ -917,6 +917,8 @@ int test_main(int argc, char **argv) USE_TEST(MAIN_EXTERNALLY_VISIBLE); | |||
| 917 | int kill_main(int argc, char **argv) USE_KILL(MAIN_EXTERNALLY_VISIBLE); | 917 | int kill_main(int argc, char **argv) USE_KILL(MAIN_EXTERNALLY_VISIBLE); |
| 918 | /* Similar, but used by chgrp, not shell */ | 918 | /* Similar, but used by chgrp, not shell */ |
| 919 | int chown_main(int argc, char **argv) USE_CHOWN(MAIN_EXTERNALLY_VISIBLE); | 919 | int chown_main(int argc, char **argv) USE_CHOWN(MAIN_EXTERNALLY_VISIBLE); |
| 920 | /* Used by ftpd */ | ||
| 921 | int ls_main(int argc, char **argv) USE_LS(MAIN_EXTERNALLY_VISIBLE); | ||
| 920 | /* Don't need USE_xxx() guard for these */ | 922 | /* Don't need USE_xxx() guard for these */ |
| 921 | int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 923 | int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 922 | int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 924 | int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
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) |
