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
This commit is contained in:
parent
9b2fbda538
commit
c41cba5a57
@ -44,6 +44,7 @@ lib-$(CONFIG_LENGTH) += length.o
|
|||||||
lib-$(CONFIG_LN) += ln.o
|
lib-$(CONFIG_LN) += ln.o
|
||||||
lib-$(CONFIG_LOGNAME) += logname.o
|
lib-$(CONFIG_LOGNAME) += logname.o
|
||||||
lib-$(CONFIG_LS) += ls.o
|
lib-$(CONFIG_LS) += ls.o
|
||||||
|
lib-$(CONFIG_FTPD) += ls.o
|
||||||
lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
|
lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
|
||||||
lib-$(CONFIG_MKDIR) += mkdir.o
|
lib-$(CONFIG_MKDIR) += mkdir.o
|
||||||
lib-$(CONFIG_MKFIFO) += mkfifo.o
|
lib-$(CONFIG_MKFIFO) += mkfifo.o
|
||||||
|
@ -864,7 +864,6 @@ static const char ls_color_opt[] ALIGN1 =
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
int ls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
|
||||||
int ls_main(int argc UNUSED_PARAM, char **argv)
|
int ls_main(int argc UNUSED_PARAM, char **argv)
|
||||||
{
|
{
|
||||||
struct dnode **dnd;
|
struct dnode **dnd;
|
||||||
|
@ -917,6 +917,8 @@ int test_main(int argc, char **argv) USE_TEST(MAIN_EXTERNALLY_VISIBLE);
|
|||||||
int kill_main(int argc, char **argv) USE_KILL(MAIN_EXTERNALLY_VISIBLE);
|
int kill_main(int argc, char **argv) USE_KILL(MAIN_EXTERNALLY_VISIBLE);
|
||||||
/* Similar, but used by chgrp, not shell */
|
/* Similar, but used by chgrp, not shell */
|
||||||
int chown_main(int argc, char **argv) USE_CHOWN(MAIN_EXTERNALLY_VISIBLE);
|
int chown_main(int argc, char **argv) USE_CHOWN(MAIN_EXTERNALLY_VISIBLE);
|
||||||
|
/* Used by ftpd */
|
||||||
|
int ls_main(int argc, char **argv) USE_LS(MAIN_EXTERNALLY_VISIBLE);
|
||||||
/* Don't need USE_xxx() guard for these */
|
/* Don't need USE_xxx() guard for these */
|
||||||
int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
||||||
int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
||||||
|
@ -63,9 +63,24 @@
|
|||||||
#define STR1(s) #s
|
#define STR1(s) #s
|
||||||
#define STR(s) STR1(s)
|
#define STR(s) STR1(s)
|
||||||
|
|
||||||
|
/* Convert a constant to 3-digit string, packed into uint32_t */
|
||||||
enum {
|
enum {
|
||||||
OPT_v = (1 << 0),
|
/* Shift for Nth decimal digit */
|
||||||
OPT_w = (1 << 1),
|
SHIFT0 = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
|
||||||
|
SHIFT1 = 8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
|
||||||
|
SHIFT2 = 0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
|
||||||
|
};
|
||||||
|
#define STRNUM32(s) (uint32_t)(0 \
|
||||||
|
| (('0' + ((s) / 1 % 10)) << SHIFT0) \
|
||||||
|
| (('0' + ((s) / 10 % 10)) << SHIFT1) \
|
||||||
|
| (('0' + ((s) / 100 % 10)) << SHIFT2) \
|
||||||
|
)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OPT_l = (1 << 0),
|
||||||
|
OPT_1 = (1 << 1),
|
||||||
|
OPT_v = (1 << 2),
|
||||||
|
OPT_w = (1 << 3),
|
||||||
|
|
||||||
#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
|
#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
|
||||||
#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
|
#define mk_const3(a,b,c) ((a * 0x100 + b) * 0x100 + c)
|
||||||
@ -106,78 +121,84 @@ struct globals {
|
|||||||
len_and_sockaddr *local_addr;
|
len_and_sockaddr *local_addr;
|
||||||
len_and_sockaddr *port_addr;
|
len_and_sockaddr *port_addr;
|
||||||
int pasv_listen_fd;
|
int pasv_listen_fd;
|
||||||
int data_fd;
|
int proc_self_fd;
|
||||||
off_t restart_pos;
|
off_t restart_pos;
|
||||||
char *ftp_cmd;
|
char *ftp_cmd;
|
||||||
char *ftp_arg;
|
char *ftp_arg;
|
||||||
#if ENABLE_FEATURE_FTP_WRITE
|
#if ENABLE_FEATURE_FTP_WRITE
|
||||||
char *rnfr_filename;
|
char *rnfr_filename;
|
||||||
#endif
|
#endif
|
||||||
smallint opts;
|
|
||||||
};
|
};
|
||||||
#define G (*(struct globals*)&bb_common_bufsiz1)
|
#define G (*(struct globals*)&bb_common_bufsiz1)
|
||||||
#define INIT_G() do { } while (0)
|
#define INIT_G() do { } while (0)
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
escape_text(const char *prepend, const char *str, char from, const char *to, char append)
|
escape_text(const char *prepend, const char *str, unsigned escapee)
|
||||||
{
|
{
|
||||||
size_t retlen, remainlen, chunklen, tolen;
|
unsigned retlen, remainlen, chunklen;
|
||||||
const char *remain;
|
|
||||||
char *ret, *found;
|
char *ret, *found;
|
||||||
|
char append;
|
||||||
|
|
||||||
|
append = (char)escapee;
|
||||||
|
escapee >>= 8;
|
||||||
|
|
||||||
remain = str;
|
|
||||||
remainlen = strlen(str);
|
remainlen = strlen(str);
|
||||||
tolen = strlen(to);
|
|
||||||
|
|
||||||
/* Simply alloc strlen(str)*strlen(to). "to" is max 2 so it's ok */
|
|
||||||
retlen = strlen(prepend);
|
retlen = strlen(prepend);
|
||||||
ret = xmalloc(retlen + remainlen * tolen + 1 + 1);
|
ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
|
||||||
strcpy(ret, prepend);
|
strcpy(ret, prepend);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
found = strchrnul(remain, from);
|
found = strchrnul(str, escapee);
|
||||||
chunklen = found - remain;
|
chunklen = found - str + 1;
|
||||||
|
|
||||||
/* Copy chunk which doesn't contain 'from' to ret */
|
/* Copy chunk up to and including escapee (or NUL) to ret */
|
||||||
memcpy(&ret[retlen], remain, chunklen);
|
memcpy(ret + retlen, str, chunklen);
|
||||||
retlen += chunklen;
|
retlen += chunklen;
|
||||||
|
|
||||||
if (*found != '\0') {
|
if (*found == '\0') {
|
||||||
/* Now copy 'to' instead of 'from' */
|
/* It wasn't escapee, it was NUL! */
|
||||||
memcpy(&ret[retlen], to, tolen);
|
ret[retlen - 1] = append; /* replace NUL */
|
||||||
retlen += tolen;
|
ret[retlen] = '\0'; /* add NUL */
|
||||||
|
|
||||||
remain = found + 1;
|
|
||||||
} else {
|
|
||||||
ret[retlen] = append;
|
|
||||||
ret[retlen+1] = '\0';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ret[retlen++] = escapee; /* duplicate escapee */
|
||||||
|
str = found + 1;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
/* Returns strlen as a bonus */
|
||||||
|
static unsigned
|
||||||
replace_char(char *str, char from, char to)
|
replace_char(char *str, char from, char to)
|
||||||
{
|
{
|
||||||
while ((str = strchr(str, from)) != NULL)
|
char *p = str;
|
||||||
*str++ = to;
|
while (*p) {
|
||||||
|
if (*p == from)
|
||||||
|
*p = to;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return p - str;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cmdio_write(unsigned status, const char *str)
|
cmdio_write(uint32_t status_str, const char *str)
|
||||||
{
|
{
|
||||||
|
union {
|
||||||
|
char buf[4];
|
||||||
|
uint32_t v32;
|
||||||
|
} u;
|
||||||
char *response;
|
char *response;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
u.v32 = status_str;
|
||||||
|
|
||||||
/* FTP allegedly uses telnet protocol for command link.
|
/* FTP allegedly uses telnet protocol for command link.
|
||||||
* In telnet, 0xff is an escape char, and needs to be escaped: */
|
* In telnet, 0xff is an escape char, and needs to be escaped: */
|
||||||
response = escape_text(utoa(status), str, '\xff', "\xff\xff", '\r');
|
response = escape_text(u.buf, str, (0xff << 8) + '\r');
|
||||||
|
|
||||||
/* ?! does FTP send embedded LFs as NULs? wow */
|
/* ?! does FTP send embedded LFs as NULs? wow */
|
||||||
len = strlen(response);
|
len = replace_char(response, '\n', '\0');
|
||||||
replace_char(response, '\n', '\0');
|
|
||||||
|
|
||||||
response[len++] = '\n'; /* tack on trailing '\n' */
|
response[len++] = '\n'; /* tack on trailing '\n' */
|
||||||
xwrite(STDOUT_FILENO, response, len);
|
xwrite(STDOUT_FILENO, response, len);
|
||||||
@ -215,9 +236,9 @@ handle_pwd(void)
|
|||||||
cwd = xstrdup("");
|
cwd = xstrdup("");
|
||||||
|
|
||||||
/* We have to promote each " to "" */
|
/* We have to promote each " to "" */
|
||||||
response = escape_text(" \"", cwd, '\"', "\"\"", '\"');
|
response = escape_text(" \"", cwd, ('"' << 8) + '"');
|
||||||
free(cwd);
|
free(cwd);
|
||||||
cmdio_write(FTP_PWDOK, response);
|
cmdio_write(STRNUM32(FTP_PWDOK), response);
|
||||||
free(response);
|
free(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,21 +282,6 @@ handle_help(void)
|
|||||||
|
|
||||||
/* Download commands */
|
/* Download commands */
|
||||||
|
|
||||||
static void
|
|
||||||
init_data_sock_params(int sock_fd)
|
|
||||||
{
|
|
||||||
struct linger linger;
|
|
||||||
|
|
||||||
G.data_fd = sock_fd;
|
|
||||||
|
|
||||||
memset(&linger, 0, sizeof(linger));
|
|
||||||
linger.l_onoff = 1;
|
|
||||||
linger.l_linger = 32767;
|
|
||||||
|
|
||||||
setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
|
|
||||||
setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ftpdataio_get_pasv_fd(void)
|
ftpdataio_get_pasv_fd(void)
|
||||||
{
|
{
|
||||||
@ -288,39 +294,10 @@ ftpdataio_get_pasv_fd(void)
|
|||||||
return remote_fd;
|
return remote_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_data_sock_params(remote_fd);
|
setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
|
||||||
return remote_fd;
|
return remote_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
ftpdataio_get_port_fd(void)
|
|
||||||
{
|
|
||||||
int remote_fd;
|
|
||||||
|
|
||||||
/* Do we want to die or print error to client? */
|
|
||||||
remote_fd = xconnect_stream(G.port_addr);
|
|
||||||
|
|
||||||
init_data_sock_params(remote_fd);
|
|
||||||
return remote_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
ftpdataio_dispose_transfer_fd(void)
|
|
||||||
{
|
|
||||||
/* This close() blocks because we set SO_LINGER */
|
|
||||||
if (G.data_fd > STDOUT_FILENO) {
|
|
||||||
if (close(G.data_fd) < 0) {
|
|
||||||
/* Do it again without blocking. */
|
|
||||||
struct linger linger;
|
|
||||||
|
|
||||||
memset(&linger, 0, sizeof(linger));
|
|
||||||
setsockopt(G.data_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
|
|
||||||
close(G.data_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
G.data_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
port_active(void)
|
port_active(void)
|
||||||
{
|
{
|
||||||
@ -341,12 +318,12 @@ get_remote_transfer_fd(const char *p_status_msg)
|
|||||||
if (pasv_active())
|
if (pasv_active())
|
||||||
remote_fd = ftpdataio_get_pasv_fd();
|
remote_fd = ftpdataio_get_pasv_fd();
|
||||||
else
|
else
|
||||||
remote_fd = ftpdataio_get_port_fd();
|
remote_fd = xconnect_stream(G.port_addr);
|
||||||
|
|
||||||
if (remote_fd < 0)
|
if (remote_fd < 0)
|
||||||
return remote_fd;
|
return remote_fd;
|
||||||
|
|
||||||
cmdio_write(FTP_DATACONN, p_status_msg);
|
cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
|
||||||
return remote_fd;
|
return remote_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,21 +351,21 @@ port_pasv_cleanup(void)
|
|||||||
static unsigned
|
static unsigned
|
||||||
bind_for_passive_mode(void)
|
bind_for_passive_mode(void)
|
||||||
{
|
{
|
||||||
|
int fd;
|
||||||
unsigned port;
|
unsigned port;
|
||||||
|
|
||||||
port_pasv_cleanup();
|
port_pasv_cleanup();
|
||||||
|
|
||||||
G.pasv_listen_fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
|
G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
|
||||||
setsockopt_reuseaddr(G.pasv_listen_fd);
|
setsockopt_reuseaddr(fd);
|
||||||
|
|
||||||
set_nport(G.local_addr, 0);
|
set_nport(G.local_addr, 0);
|
||||||
xbind(G.pasv_listen_fd, &G.local_addr->u.sa, G.local_addr->len);
|
xbind(fd, &G.local_addr->u.sa, G.local_addr->len);
|
||||||
xlisten(G.pasv_listen_fd, 1);
|
xlisten(fd, 1);
|
||||||
getsockname(G.pasv_listen_fd, &G.local_addr->u.sa, &G.local_addr->len);
|
getsockname(fd, &G.local_addr->u.sa, &G.local_addr->len);
|
||||||
|
|
||||||
port = get_nport(&G.local_addr->u.sa);
|
port = get_nport(&G.local_addr->u.sa);
|
||||||
port = ntohs(port);
|
port = ntohs(port);
|
||||||
|
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,7 +464,7 @@ handle_retr(void)
|
|||||||
G.restart_pos = 0;
|
G.restart_pos = 0;
|
||||||
|
|
||||||
if (!data_transfer_checks_ok())
|
if (!data_transfer_checks_ok())
|
||||||
return;
|
return; /* data_transfer_checks_ok emitted error response */
|
||||||
|
|
||||||
/* O_NONBLOCK is useful if file happens to be a device node */
|
/* O_NONBLOCK is useful if file happens to be a device node */
|
||||||
opened_file = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
|
opened_file = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
|
||||||
@ -520,7 +497,7 @@ handle_retr(void)
|
|||||||
goto port_pasv_cleanup_out;
|
goto port_pasv_cleanup_out;
|
||||||
|
|
||||||
trans_ret = bb_copyfd_eof(opened_file, remote_fd);
|
trans_ret = bb_copyfd_eof(opened_file, remote_fd);
|
||||||
ftpdataio_dispose_transfer_fd();
|
close(remote_fd);
|
||||||
if (trans_ret < 0)
|
if (trans_ret < 0)
|
||||||
cmdio_write_error(FTP_BADSENDFILE);
|
cmdio_write_error(FTP_BADSENDFILE);
|
||||||
else
|
else
|
||||||
@ -535,185 +512,105 @@ handle_retr(void)
|
|||||||
|
|
||||||
/* List commands */
|
/* List commands */
|
||||||
|
|
||||||
static char *
|
static int
|
||||||
statbuf_getperms(const struct stat *statbuf)
|
popen_ls(const char *opt)
|
||||||
{
|
{
|
||||||
char *perms;
|
char *cwd;
|
||||||
enum { r = 'r', w = 'w', x = 'x', s = 's', S = 'S' };
|
const char *argv[5] = { "ftpd", opt, NULL, G.ftp_arg, NULL };
|
||||||
|
struct fd_pair outfd;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
perms = xmalloc(11);
|
cwd = xrealloc_getcwd_or_warn(NULL);
|
||||||
memset(perms, '-', 10);
|
|
||||||
|
|
||||||
perms[0] = '?';
|
xpiped_pair(outfd);
|
||||||
switch (statbuf->st_mode & S_IFMT) {
|
|
||||||
case S_IFREG: perms[0] = '-'; break;
|
|
||||||
case S_IFDIR: perms[0] = 'd'; break;
|
|
||||||
case S_IFLNK: perms[0] = 'l'; break;
|
|
||||||
case S_IFIFO: perms[0] = 'p'; break;
|
|
||||||
case S_IFSOCK: perms[0] = s; break;
|
|
||||||
case S_IFCHR: perms[0] = 'c'; break;
|
|
||||||
case S_IFBLK: perms[0] = 'b'; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statbuf->st_mode & S_IRUSR) perms[1] = r;
|
fflush(NULL);
|
||||||
if (statbuf->st_mode & S_IWUSR) perms[2] = w;
|
pid = vfork();
|
||||||
if (statbuf->st_mode & S_IXUSR) perms[3] = x;
|
|
||||||
if (statbuf->st_mode & S_IRGRP) perms[4] = r;
|
|
||||||
if (statbuf->st_mode & S_IWGRP) perms[5] = w;
|
|
||||||
if (statbuf->st_mode & S_IXGRP) perms[6] = x;
|
|
||||||
if (statbuf->st_mode & S_IROTH) perms[7] = r;
|
|
||||||
if (statbuf->st_mode & S_IWOTH) perms[8] = w;
|
|
||||||
if (statbuf->st_mode & S_IXOTH) perms[9] = x;
|
|
||||||
if (statbuf->st_mode & S_ISUID) perms[3] = (perms[3] == x) ? s : S;
|
|
||||||
if (statbuf->st_mode & S_ISGID) perms[6] = (perms[6] == x) ? s : S;
|
|
||||||
if (statbuf->st_mode & S_ISVTX) perms[9] = (perms[9] == x) ? 't' : 'T';
|
|
||||||
|
|
||||||
perms[10] = '\0';
|
switch (pid) {
|
||||||
|
case -1: /* failure */
|
||||||
return perms;
|
bb_perror_msg_and_die("vfork");
|
||||||
}
|
case 0: /* child */
|
||||||
|
/* NB: close _first_, then move fds! */
|
||||||
static void
|
close(outfd.rd);
|
||||||
write_filestats(int fd, const char *filename,
|
xmove_fd(outfd.wr, STDOUT_FILENO);
|
||||||
const struct stat *statbuf)
|
close(STDIN_FILENO);
|
||||||
{
|
/* xopen("/dev/null", O_RDONLY); - chroot may lack it! */
|
||||||
off_t size;
|
if (fchdir(G.proc_self_fd) == 0) {
|
||||||
char *stats, *lnkname = NULL, *perms;
|
close(G.proc_self_fd);
|
||||||
const char *name;
|
argv[2] = cwd;
|
||||||
char timestr[32];
|
/* ftpd ls helper chdirs to argv[2],
|
||||||
struct tm *tm;
|
* preventing peer from seeing /proc/self
|
||||||
|
*/
|
||||||
name = bb_get_last_path_component_nostrip(filename);
|
execv("exe", (char**) argv);
|
||||||
|
|
||||||
if (statbuf != NULL) {
|
|
||||||
size = statbuf->st_size;
|
|
||||||
|
|
||||||
if (S_ISLNK(statbuf->st_mode))
|
|
||||||
/* Damn symlink... */
|
|
||||||
lnkname = xmalloc_readlink(filename);
|
|
||||||
|
|
||||||
tm = gmtime(&statbuf->st_mtime);
|
|
||||||
if (strftime(timestr, sizeof(timestr), "%b %d %H:%M", tm) == 0)
|
|
||||||
bb_error_msg_and_die("strftime");
|
|
||||||
|
|
||||||
timestr[sizeof(timestr) - 1] = '\0';
|
|
||||||
|
|
||||||
perms = statbuf_getperms(statbuf);
|
|
||||||
|
|
||||||
stats = xasprintf("%s %u\tftp ftp %"OFF_FMT"u\t%s %s",
|
|
||||||
perms, (int) statbuf->st_nlink,
|
|
||||||
size, timestr, name);
|
|
||||||
|
|
||||||
free(perms);
|
|
||||||
} else
|
|
||||||
stats = xstrdup(name);
|
|
||||||
|
|
||||||
xwrite_str(fd, stats);
|
|
||||||
free(stats);
|
|
||||||
if (lnkname != NULL) {
|
|
||||||
xwrite_str(fd, " -> ");
|
|
||||||
xwrite_str(fd, lnkname);
|
|
||||||
free(lnkname);
|
|
||||||
}
|
|
||||||
xwrite_str(fd, "\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_dirstats(int fd, const char *dname, int details)
|
|
||||||
{
|
|
||||||
DIR *dir;
|
|
||||||
struct dirent *dirent;
|
|
||||||
struct stat statbuf;
|
|
||||||
char *filename;
|
|
||||||
|
|
||||||
dir = xopendir(dname);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
dirent = readdir(dir);
|
|
||||||
if (dirent == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Ignore . and .. */
|
|
||||||
if (dirent->d_name[0] == '.') {
|
|
||||||
if (dirent->d_name[1] == '\0'
|
|
||||||
|| (dirent->d_name[1] == '.' && dirent->d_name[2] == '\0')
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_exit(127);
|
||||||
if (details) {
|
|
||||||
filename = xasprintf("%s/%s", dname, dirent->d_name);
|
|
||||||
if (lstat(filename, &statbuf) != 0) {
|
|
||||||
free(filename);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
filename = xstrdup(dirent->d_name);
|
|
||||||
|
|
||||||
write_filestats(fd, filename, details ? &statbuf : NULL);
|
|
||||||
free(filename);
|
|
||||||
}
|
}
|
||||||
|
/* parent */
|
||||||
closedir(dir);
|
close(outfd.wr);
|
||||||
|
free(cwd);
|
||||||
|
return outfd.rd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_dir_common(int full_details, int stat_cmd)
|
handle_dir_common(int opts)
|
||||||
{
|
{
|
||||||
int fd;
|
FILE *ls_fp;
|
||||||
struct stat statbuf;
|
char *line;
|
||||||
|
int ls_fd;
|
||||||
|
|
||||||
if (!stat_cmd && !data_transfer_checks_ok())
|
if (!(opts & 1) && !data_transfer_checks_ok())
|
||||||
return;
|
return; /* data_transfer_checks_ok emitted error response */
|
||||||
|
|
||||||
if (stat_cmd) {
|
ls_fd = popen_ls((opts & 2) ? "-l" : "-1");
|
||||||
fd = STDIN_FILENO;
|
ls_fp = fdopen(ls_fd, "r");
|
||||||
|
if (!ls_fp) /* never happens. paranoia */
|
||||||
|
bb_perror_msg_and_die("fdopen");
|
||||||
|
|
||||||
|
if (opts & 1) {
|
||||||
|
/* STAT <filename> */
|
||||||
cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
|
cmdio_write_raw(STR(FTP_STATFILE_OK)"-Status follows:\r\n");
|
||||||
} else {
|
while (1) {
|
||||||
fd = get_remote_transfer_fd(" Here comes the directory listing");
|
line = xmalloc_fgetline(ls_fp);
|
||||||
if (fd < 0)
|
if (!line)
|
||||||
goto bail;
|
break;
|
||||||
}
|
cmdio_write(0, line); /* hack: 0 results in no status at all */
|
||||||
|
free(line);
|
||||||
if (G.ftp_arg) {
|
|
||||||
if (lstat(G.ftp_arg, &statbuf) != 0) {
|
|
||||||
/* Dir doesn't exist => return ok to client */
|
|
||||||
goto bail;
|
|
||||||
}
|
}
|
||||||
if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))
|
cmdio_write_ok(FTP_STATFILE_OK);
|
||||||
write_filestats(fd, G.ftp_arg, &statbuf);
|
} else {
|
||||||
else if (S_ISDIR(statbuf.st_mode))
|
/* LIST/NLST [<filename>] */
|
||||||
write_dirstats(fd, G.ftp_arg, full_details);
|
int remote_fd = get_remote_transfer_fd(" Here comes the directory listing");
|
||||||
} else
|
if (remote_fd >= 0) {
|
||||||
write_dirstats(fd, ".", full_details);
|
while (1) {
|
||||||
|
line = xmalloc_fgetline(ls_fp);
|
||||||
bail:
|
if (!line)
|
||||||
/* Well, if we can't open directory/file it doesn't matter */
|
break;
|
||||||
if (!stat_cmd) {
|
xwrite_str(remote_fd, line);
|
||||||
ftpdataio_dispose_transfer_fd();
|
xwrite(remote_fd, "\r\n", 2);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(remote_fd);
|
||||||
port_pasv_cleanup();
|
port_pasv_cleanup();
|
||||||
cmdio_write_ok(FTP_TRANSFEROK);
|
cmdio_write_ok(FTP_TRANSFEROK);
|
||||||
} else
|
}
|
||||||
cmdio_write_ok(FTP_STATFILE_OK);
|
fclose(ls_fp); /* closes ls_fd too */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_list(void)
|
handle_list(void)
|
||||||
{
|
{
|
||||||
handle_dir_common(1, 0);
|
handle_dir_common(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_nlst(void)
|
handle_nlst(void)
|
||||||
{
|
{
|
||||||
handle_dir_common(0, 0);
|
handle_dir_common(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_stat_file(void)
|
handle_stat_file(void)
|
||||||
{
|
{
|
||||||
handle_dir_common(1, 1);
|
handle_dir_common(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -840,7 +737,7 @@ handle_upload_common(int is_append, int is_unique)
|
|||||||
goto bail;
|
goto bail;
|
||||||
|
|
||||||
trans_ret = bb_copyfd_eof(remote_fd, local_file_fd);
|
trans_ret = bb_copyfd_eof(remote_fd, local_file_fd);
|
||||||
ftpdataio_dispose_transfer_fd();
|
close(remote_fd);
|
||||||
|
|
||||||
if (trans_ret < 0)
|
if (trans_ret < 0)
|
||||||
cmdio_write_error(FTP_BADSENDFILE);
|
cmdio_write_error(FTP_BADSENDFILE);
|
||||||
@ -905,8 +802,19 @@ cmdio_get_cmd_and_arg(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
||||||
int ftpd_main(int argc UNUSED_PARAM, char **argv)
|
int ftpd_main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
smallint opts;
|
||||||
|
|
||||||
|
opts = getopt32(argv, "l1v" USE_FEATURE_FTP_WRITE("w"));
|
||||||
|
|
||||||
|
if (opts & (OPT_l|OPT_1)) {
|
||||||
|
/* Our secret backdoor to ls */
|
||||||
|
xchdir(argv[2]);
|
||||||
|
argv[2] = (char*)"--";
|
||||||
|
return ls_main(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
INIT_G();
|
INIT_G();
|
||||||
|
|
||||||
G.local_addr = get_sock_lsa(STDIN_FILENO);
|
G.local_addr = get_sock_lsa(STDIN_FILENO);
|
||||||
@ -920,13 +828,13 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
* failure */
|
* failure */
|
||||||
}
|
}
|
||||||
|
|
||||||
G.opts = getopt32(argv, "v" USE_FEATURE_FTP_WRITE("w"));
|
|
||||||
|
|
||||||
openlog(applet_name, LOG_PID, LOG_DAEMON);
|
openlog(applet_name, LOG_PID, LOG_DAEMON);
|
||||||
logmode |= LOGMODE_SYSLOG;
|
logmode |= LOGMODE_SYSLOG;
|
||||||
if (!(G.opts & OPT_v))
|
if (!(opts & OPT_v))
|
||||||
logmode = LOGMODE_SYSLOG;
|
logmode = LOGMODE_SYSLOG;
|
||||||
|
|
||||||
|
G.proc_self_fd = xopen("/proc/self", O_RDONLY | O_DIRECTORY);
|
||||||
|
|
||||||
if (argv[optind]) {
|
if (argv[optind]) {
|
||||||
xchdir(argv[optind]);
|
xchdir(argv[optind]);
|
||||||
chroot(".");
|
chroot(".");
|
||||||
@ -1064,7 +972,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
|
|||||||
else if (cmdval == const_REST)
|
else if (cmdval == const_REST)
|
||||||
handle_rest();
|
handle_rest();
|
||||||
#if ENABLE_FEATURE_FTP_WRITE
|
#if ENABLE_FEATURE_FTP_WRITE
|
||||||
else if (G.opts & OPT_w) {
|
else if (opts & OPT_w) {
|
||||||
if (cmdval == const_STOR)
|
if (cmdval == const_STOR)
|
||||||
handle_stor();
|
handle_stor();
|
||||||
else if (cmdval == const_MKD)
|
else if (cmdval == const_MKD)
|
||||||
|
Loading…
Reference in New Issue
Block a user