2001-01-31 Sergei Organov <osv@javad.ru>

* rtems_servers/ftp.d: Following changes:
	  - Hacks with current dir and root dir removed in favor of new libio
	    support for task-local current and root directories.
	  - Bug in `close_data_socket()' introduced by previous change fixed.
	  - `command_pasv()' changed to set timeout on socket we are listening
	    on and code fixed to don't close socket twice on error.
	  - `serr()' changed to clear `errno'.
	  - `data_socket()' changed to clear `errno' before `bind()'.
	  - `session()' changed to clear `errno' before processing session.
	  - `close_data_socket()' fixed to close both active and passive sockets
	  - Initialize info->data_socket to -1 in `daemon()'
	  - Initialize `fname' to empty string  in `exec_command()'
This commit is contained in:
Joel Sherrill
2001-03-05 23:01:43 +00:00
parent 1fc16ff9d1
commit 07fbfcedf0
5 changed files with 367 additions and 530 deletions

View File

@@ -1,3 +1,18 @@
2001-01-31 Sergei Organov <osv@javad.ru>
* rtems_servers/ftp.d: Following changes:
- Hacks with current dir and root dir removed in favor of new libio
support for task-local current and root directories.
- Bug in `close_data_socket()' introduced by previous change fixed.
- `command_pasv()' changed to set timeout on socket we are listening
on and code fixed to don't close socket twice on error.
- `serr()' changed to clear `errno'.
- `data_socket()' changed to clear `errno' before `bind()'.
- `session()' changed to clear `errno' before processing session.
- `close_data_socket()' fixed to close both active and passive sockets
- Initialize info->data_socket to -1 in `daemon()'
- Initialize `fname' to empty string in `exec_command()'
2001-02-03 Ralf Corsepius <corsepiu@faw.uni-ulm.de> 2001-02-03 Ralf Corsepius <corsepiu@faw.uni-ulm.de>
* Makefile.am, arpa/Makefile.am, machine/Makefile.am, net/Makefile.am, * Makefile.am, arpa/Makefile.am, machine/Makefile.am, net/Makefile.am,

View File

@@ -1,3 +1,18 @@
2001-01-31 Sergei Organov <osv@javad.ru>
* rtems_servers/ftp.d: Following changes:
- Hacks with current dir and root dir removed in favor of new libio
support for task-local current and root directories.
- Bug in `close_data_socket()' introduced by previous change fixed.
- `command_pasv()' changed to set timeout on socket we are listening
on and code fixed to don't close socket twice on error.
- `serr()' changed to clear `errno'.
- `data_socket()' changed to clear `errno' before `bind()'.
- `session()' changed to clear `errno' before processing session.
- `close_data_socket()' fixed to close both active and passive sockets
- Initialize info->data_socket to -1 in `daemon()'
- Initialize `fname' to empty string in `exec_command()'
2001-02-03 Ralf Corsepius <corsepiu@faw.uni-ulm.de> 2001-02-03 Ralf Corsepius <corsepiu@faw.uni-ulm.de>
* Makefile.am, arpa/Makefile.am, machine/Makefile.am, net/Makefile.am, * Makefile.am, arpa/Makefile.am, machine/Makefile.am, net/Makefile.am,

View File

@@ -15,6 +15,26 @@
* *
* Changes: * Changes:
* *
* 2001-01-31 Sergei Organov <osv@javad.ru>
*
* * Hacks with current dir and root dir removed in favor of new libio
* support for task-local current and root directories.
*
* 2001-01-30 Sergei Organov <osv@javad.ru>
*
* * Bug in `close_data_socket()' introduced by previous change fixed.
* * `command_pasv()' changed to set timeout on socket we are listening on
* and code fixed to don't close socket twice on error.
* * `serr()' changed to clear `errno'.
* * `data_socket()' changed to clear `errno' before `bind()'.
* * `session()' changed to clear `errno' before processing session.
*
* 2001-01-29 Sergei Organov <osv@javad.ru>
*
* * `close_data_socket()' fixed to close both active and passive sockets
* * Initialize info->data_socket to -1 in `daemon()'
* * Initialize `fname' to empty string in `exec_command()'
*
* 2001-01-22 Sergei Organov <osv@javad.ru> * 2001-01-22 Sergei Organov <osv@javad.ru>
* *
* * Timeouts on sockets implemented. 'idle' field added to * * Timeouts on sockets implemented. 'idle' field added to
@@ -169,6 +189,7 @@
#include <rtems.h> #include <rtems.h>
#include <rtems/rtems_bsdnet.h> #include <rtems/rtems_bsdnet.h>
#include <rtems/error.h> #include <rtems/error.h>
#include <rtems/libio.h>
#include <syslog.h> #include <syslog.h>
#include <sys/types.h> #include <sys/types.h>
@@ -235,7 +256,6 @@ typedef struct
int pasv_socket; /* Socket for PASV connection */ int pasv_socket; /* Socket for PASV connection */
int data_socket; /* Socket for data connection */ int data_socket; /* Socket for data connection */
int idle; /* Timeout in seconds */ int idle; /* Timeout in seconds */
char cwd[FTPD_BUFSIZE]; /* Current working directory */
int xfer_mode; /* Transfer mode (ASCII/binary) */ int xfer_mode; /* Transfer mode (ASCII/binary) */
rtems_id tid; /* Task id */ rtems_id tid; /* Task id */
} FTPD_SessionInfo_t; } FTPD_SessionInfo_t;
@@ -261,10 +281,10 @@ typedef struct
static FTPD_TaskPool_t task_pool; static FTPD_TaskPool_t task_pool;
/* /*
* Root node for FTPD without trailing slash. Even '/' node is denoted as * Root directory
* empty string here.
*/ */
static char ftpd_root[FTPD_BUFSIZE];
static char const* ftpd_root = "/";
/* /*
* Default idle timeout for sockets in seconds. * Default idle timeout for sockets in seconds.
@@ -286,7 +306,9 @@ static int ftpd_access = 0;
static char const* static char const*
serr(void) serr(void)
{ {
return strerror(errno); int err = errno;
errno = 0;
return strerror(err);
} }
/*PAGE /*PAGE
@@ -307,172 +329,6 @@ can_write(void)
return (ftpd_access & FTPD_NO_WRITE) == 0; return (ftpd_access & FTPD_NO_WRITE) == 0;
} }
/*PAGE
*
* Utility routines to manage root directory and session local
* current working directory.
*
*/
/*PAGE
*
* is_dir
*
* Return 1 if file with given 'name' exists and is directory, 0 otherwise.
*
*/
static int
is_dir(char const* name)
{
struct stat s;
int res = stat(name, &s) == 0 && S_ISDIR(s.st_mode);
return res;
}
/*PAGE
*
* squeeze_path
*
* Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
* from the path. Does nothing if the path is relative, i.e. doesn't begin
* with '/'. The trailing slash is always removed, even when alone, i.e. "/"
* will be "" after squeeze.
*
* Input parameters:
* path - the path to be squeezed
* full - full file name or NULL (assumed that it points to the beginning of
* buffer, and 'path' also points somewhere into the same buffer). Is
* used to check if intermediate names are directories.
*
* Output parameters:
* path - squeezed path
* returns 1 on success, 0 on failure (if any name that supposed to denote
* directory is not a directory).
*
*/
static int
squeeze_path(char* path, char* full)
{
if(path[0] == '/')
{
char* e = path + 1;
int rest = strlen(e);
while(rest >= 0)
{
int len;
char* s = e;
e = strchr(s, '/');
if(e)
{
char c = *e;
*e = '\0';
if(full && !is_dir(full))
{
*e = c;
return 0;
}
*e++ = c;
}
else
e = s + rest + 1;
len = e - s;
rest -= len;
if(len == 1 || (len == 2 && s[0] == '.'))
{
if(rest >= 0)
memmove(s, e, rest + 1);
else
*s++ = '\0';
e = s;
}
else if(len == 3 && s[0] == '.' && s[1] == '.')
{
char* ps = s;
if(ps - 1 > path) {
do
--ps;
while(ps[-1] != '/');
}
if(rest >= 0)
memmove(ps, e, rest + 1);
else
*ps++ = '\0';
e = ps;
}
}
if(e[-2] == '/')
{
e[-2] = '\0';
if(full && !is_dir(full))
return 0;
}
}
return 1;
}
/*PAGE
*
* make_path
*
* Makes full path given file name, current working directory and root
* directory (file scope variable 'root').
*
* Input parameters:
* cwd - current working directory
* name - file name
* root (file scope variable) - FTPD root directory
*
* Output parameters:
* buf - full path
* returns pointer to non-root part of the 'buf', i.e. to first character
* different from '/' after root part.
*
*/
static char const*
make_path(char* buf, char const* cwd, char const* name)
{
char* res = NULL;
int rlen = strlen(ftpd_root);
int clen = strlen(cwd);
int nlen = strlen(name);
int len = rlen + nlen;
if (name[0] != '/')
{
++len;
if (clen > 0)
len += clen + 1;
}
if (FTPD_BUFSIZE > len)
{
char* b = buf;
memcpy(b, ftpd_root, rlen); b += rlen;
if (name[0] != '/')
{
*b++ = '/';
if (clen > 0)
{
memcpy(b, cwd, clen); b += clen;
*b++ = '/';
}
}
memcpy(b, name, nlen); b += nlen;
*b = '\0';
res = buf + rlen;
while(rlen-- > 0 && res[-1] == '/')
--res;
if(!squeeze_path(res, buf))
res = NULL;
}
return res;
}
/*PAGE /*PAGE
* *
* Task pool management routines * Task pool management routines
@@ -602,8 +458,6 @@ task_pool_init(int count, rtems_task_priority priority)
return 0; return 0;
} }
task_pool.queue[i] = task_pool.info + i; task_pool.queue[i] = task_pool.info + i;
info->ctrl_fp = NULL;
info->ctrl_socket = -1;
if (++id > 'z') if (++id > 'z')
id = 'a'; id = 'a';
} }
@@ -637,8 +491,6 @@ task_pool_obtain()
info = task_pool.queue[task_pool.head]; info = task_pool.queue[task_pool.head];
if(++task_pool.head >= task_pool.count) if(++task_pool.head >= task_pool.count)
task_pool.head = 0; task_pool.head = 0;
info->ctrl_socket = -1;
info->ctrl_fp = NULL;
rtems_semaphore_release(task_pool.mutex); rtems_semaphore_release(task_pool.mutex);
} }
return info; return info;
@@ -800,6 +652,7 @@ data_socket(FTPD_SessionInfo_t *info)
data_source.sin_port = htons(20); /* ftp-data port */ data_source.sin_port = htons(20); /* ftp-data port */
for(tries = 1; tries < 10; ++tries) for(tries = 1; tries < 10; ++tries)
{ {
errno = 0;
if(bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0) if(bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0)
break; break;
if (errno != EADDRINUSE) if (errno != EADDRINUSE)
@@ -849,9 +702,11 @@ data_socket(FTPD_SessionInfo_t *info)
static void static void
close_data_socket(FTPD_SessionInfo_t *info) close_data_socket(FTPD_SessionInfo_t *info)
{ {
int s = info->pasv_socket; /* As at most one data socket could be open simultaneously and in some cases
data_socket == pasv_socket, we select socket to close, then close it. */
int s = info->data_socket;
if(0 > s) if(0 > s)
s = info->data_socket; s = info->pasv_socket;
if(!close_socket(s)) if(!close_socket(s))
syslog(LOG_ERR, "ftpd: Error closing data socket."); syslog(LOG_ERR, "ftpd: Error closing data socket.");
info->data_socket = -1; info->data_socket = -1;
@@ -936,7 +791,6 @@ command_retrieve(FTPD_SessionInfo_t *info, char const *filename)
int fd = -1; int fd = -1;
char buf[FTPD_DATASIZE]; char buf[FTPD_DATASIZE];
int res = 0; int res = 0;
char const* r = NULL;
if(!can_read()) if(!can_read())
{ {
@@ -944,9 +798,7 @@ command_retrieve(FTPD_SessionInfo_t *info, char const *filename)
return; return;
} }
r = make_path(buf, info->cwd, filename); if (0 > (fd = open(filename, O_RDONLY)))
if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
{ {
send_reply(info, 550, "Error opening file."); send_reply(info, 550, "Error opening file.");
return; return;
@@ -1028,6 +880,29 @@ command_retrieve(FTPD_SessionInfo_t *info, char const *filename)
} }
/*PAGE
*
* discard
*
* Analog of `write' routine that just discards passed data
*
* Input parameters:
* fd - file descriptor (ignored)
* buf - data to write (ignored)
* count - number of bytes in `buf'
*
* Output parameters:
* returns `count'
*
*/
static ssize_t
discard(int fd, void const* buf, size_t count)
{
(void)fd;
(void)buf;
return count;
}
/*PAGE /*PAGE
* *
* command_store * command_store
@@ -1051,8 +926,9 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
char buf[FTPD_DATASIZE]; char buf[FTPD_DATASIZE];
int res = 1; int res = 1;
int bare_lfs = 0; int bare_lfs = 0;
int null = 0;
int null = !strcmp("/dev/null", filename); typedef ssize_t (*WriteProc)(int, void const*, size_t);
WriteProc wrt = &write;
if(!can_write()) if(!can_write())
{ {
@@ -1060,33 +936,23 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
return; return;
} }
if(!null)
{
if (NULL == make_path(buf, info->cwd, filename))
{
send_reply(info, 550, "Error creating file.");
return;
}
}
send_mode_reply(info); send_mode_reply(info);
s = data_socket(info); s = data_socket(info);
if(0 > s) if(0 > s)
return; return;
null = !strcmp("/dev/null", filename);
if (null) if (null)
{ {
/* File "/dev/null" just throws data away. /* File "/dev/null" just throws data away.
* FIXME: this is hack. Using /dev/null filesystem entry would be * FIXME: this is hack. Using `/dev/null' filesystem entry would be
* better. However, it's not clear how to handle root directory other * better.
* than '/' then.
*/ */
while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0) wrt = &discard;
;
} }
else if (rtems_ftpd_configuration.hooks != NULL)
if (!null && rtems_ftpd_configuration.hooks != NULL)
{ {
/* Search our list of hooks to see if we need to do something special. */ /* Search our list of hooks to see if we need to do something special. */
@@ -1159,9 +1025,12 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
} }
else else
{ {
/* Data transfer to regular file. */ /* Data transfer to regular file or /dev/null. */
int fd = int fd = 0;
creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if(!null)
fd = creat(filename,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (0 > fd) if (0 > fd)
{ {
@@ -1174,7 +1043,7 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
{ {
while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0) while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
{ {
if (write(fd, buf, n) != n) if (wrt(fd, buf, n) != n)
{ {
res = 0; res = 0;
break; break;
@@ -1196,7 +1065,7 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
{ {
char const lf = '\r'; char const lf = '\r';
pended_cr = 0; pended_cr = 0;
if(write(fd, &lf, 1) != 1) if(wrt(fd, &lf, 1) != 1)
{ {
res = 0; res = 0;
break; break;
@@ -1231,12 +1100,12 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
if(res == 0) if(res == 0)
break; break;
count = i - sub - pended_cr; count = i - sub - pended_cr;
if(count > 0 && write(fd, b, count) != count) if(count > 0 && wrt(fd, b, count) != count)
{ {
res = 0; res = 0;
break; break;
} }
if(sub == 2 && write(fd, e - 1, 1) != 1) if(sub == 2 && wrt(fd, e - 1, 1) != 1)
res = 0; res = 0;
} }
while((rest -= i) > 0); while((rest -= i) > 0);
@@ -1384,10 +1253,7 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
struct dirent *dp = 0; struct dirent *dp = 0;
struct stat stat_buf; struct stat stat_buf;
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
char path[FTPD_BUFSIZE];
time_t curTime; time_t curTime;
char const* res;
char const* cwd = info->cwd;
int sc = 1; int sc = 1;
send_reply(info, 150, "Opening ASCII mode data connection for LIST."); send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
@@ -1402,15 +1268,13 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
if(fname[0] == '\0') if(fname[0] == '\0')
fname = "."; fname = ".";
res = make_path(path, cwd, fname); if (0 > stat(fname, &stat_buf))
if (NULL == res || 0 > stat(path, &stat_buf))
{ {
snprintf(buf, FTPD_BUFSIZE, snprintf(buf, FTPD_BUFSIZE,
"%s: No such file or directory.\r\n", fname); "%s: No such file or directory.\r\n", fname);
send(s, buf, strlen(buf), 0); send(s, buf, strlen(buf), 0);
} }
else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(path)))) else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(fname))))
{ {
snprintf(buf, FTPD_BUFSIZE, snprintf(buf, FTPD_BUFSIZE,
"%s: Can not open directory.\r\n", fname); "%s: Can not open directory.\r\n", fname);
@@ -1420,15 +1284,15 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
{ {
time(&curTime); time(&curTime);
if(!dirp && *fname) if(!dirp && *fname)
sc = sc && send_dirline(s, wide, curTime, path, "", fname, buf); sc = sc && send_dirline(s, wide, curTime, fname, "", fname, buf);
else { else {
/* FIXME: need "." and ".." only when '-a' option is given */ /* FIXME: need "." and ".." only when '-a' option is given */
sc = sc && send_dirline(s, wide, curTime, path, "", ".", buf); sc = sc && send_dirline(s, wide, curTime, fname, "", ".", buf);
sc = sc && send_dirline(s, wide, curTime, path, sc = sc && send_dirline(s, wide, curTime, fname,
(strcmp(path, ftpd_root) ? ".." : ""), "..", buf); (strcmp(fname, ftpd_root) ? ".." : ""), "..", buf);
while (sc && (dp = readdir(dirp)) != NULL) while (sc && (dp = readdir(dirp)) != NULL)
sc = sc && sc = sc &&
send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf); send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf);
} }
} }
@@ -1445,38 +1309,64 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
/*PAGE /*PAGE
* *
* rtems_ftpd_cwd * command_cwd
* *
* Change current working directory. We use 'chdir' here only to validate the * Change current working directory.
* new directory. We keep track of current working directory ourselves because
* current working directory in RTEMS isn't thread local, but we need it to be
* session local.
* *
* Input parameters: * Input parameters:
* info - corresponding SessionInfo structure * info - corresponding SessionInfo structure
* dir - directory name passed in CWD command * dir - directory name passed in CWD command
* *
* Output parameters: * Output parameters:
* info->cwd is set to new CWD value. * NONE
* *
*/ */
static void static void
rtems_ftpd_cwd(FTPD_SessionInfo_t *info, char *dir) command_cwd(FTPD_SessionInfo_t *info, char *dir)
{ {
char buf[FTPD_BUFSIZE]; if(chdir(dir) == 0)
char const* cwd = make_path(buf, info->cwd, dir);
if(cwd && chdir(buf) == 0)
{
send_reply(info, 250, "CWD command successful."); send_reply(info, 250, "CWD command successful.");
strcpy(info->cwd, cwd);
}
else else
{
send_reply(info, 550, "CWD command failed."); send_reply(info, 550, "CWD command failed.");
}
} }
/*PAGE
*
* command_pwd
*
* Send current working directory to client.
*
* Input parameters:
* info - corresponding SessionInfo structure
*
* Output parameters:
* NONE
*/
static void
command_pwd(FTPD_SessionInfo_t *info)
{
char buf[FTPD_BUFSIZE];
char const* cwd;
errno = 0;
buf[0] = '"';
cwd = getcwd(buf + 1, FTPD_BUFSIZE - 4);
if(cwd)
{
int len = strlen(cwd);
static char const txt[] = "\" is the current directory.";
int size = sizeof(txt);
if(len + size + 1 >= FTPD_BUFSIZE)
size = FTPD_BUFSIZE - len - 2;
memcpy(buf + len + 1, txt, size);
buf[len + size] = '\0';
send_reply(info, 250, buf);
}
else {
snprintf(buf, FTPD_BUFSIZE, "Error: %s.", serr());
send_reply(info, 452, buf);
}
}
/*PAGE /*PAGE
* *
@@ -1497,7 +1387,7 @@ command_mdtm(FTPD_SessionInfo_t *info, char const* fname)
struct stat stbuf; struct stat stbuf;
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
if (NULL == make_path(buf, info->cwd, fname) || 0 > stat(buf, &stbuf)) if (0 > stat(fname, &stbuf))
{ {
snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr()); snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
send_reply(info, 550, buf); send_reply(info, 550, buf);
@@ -1614,7 +1504,7 @@ command_pasv(FTPD_SessionInfo_t *info)
syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr()); syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr());
else if (0 > listen(s, 1)) else if (0 > listen(s, 1))
syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr()); syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr());
else else if(set_socket_timeout(s, info->idle))
{ {
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
unsigned char const *ip, *p; unsigned char const *ip, *p;
@@ -1627,13 +1517,16 @@ command_pasv(FTPD_SessionInfo_t *info)
send_reply(info, 227, buf); send_reply(info, 227, buf);
info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen); info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen);
close_socket(s);
if (0 > info->pasv_socket) if (0 > info->pasv_socket)
syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr()); syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr());
else else
{
close_socket(s);
s = -1;
err = 0; err = 0;
} }
} }
}
if(err) if(err)
{ {
/* (OSV) The note is from FreeBSD FTPD. /* (OSV) The note is from FreeBSD FTPD.
@@ -1746,17 +1639,18 @@ split_command(char *buf, char **cmd, char **opts, char **args)
* info - corresponding SessionInfo structure * info - corresponding SessionInfo structure
* cmd - command to be executed (upper-case) * cmd - command to be executed (upper-case)
* args - arguments of the command * args - arguments of the command
* buf - beginning of buffer where 'cmd' and 'args' reside.
* *
* Output parameters: * Output parameters:
* NONE * NONE
*/ */
static void static void
exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf) exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args)
{ {
char fname[FTPD_BUFSIZE]; char fname[FTPD_BUFSIZE];
int wrong_command = 0; int wrong_command = 0;
fname[0] = '\0';
if (!strcmp("PORT", cmd)) if (!strcmp("PORT", cmd))
{ {
command_port(info, args); command_port(info, args);
@@ -1824,8 +1718,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if ( else if (
1 == sscanf(args, "%254s", fname) && 1 == sscanf(args, "%254s", fname) &&
NULL != make_path(buf, info->cwd, fname) && unlink(fname) == 0)
unlink(buf) == 0)
{ {
send_reply(info, 257, "DELE successful."); send_reply(info, 257, "DELE successful.");
} }
@@ -1848,8 +1741,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if( else if(
2 == sscanf(args, "%o %254s", &mask, fname) && 2 == sscanf(args, "%o %254s", &mask, fname) &&
NULL != make_path(buf, info->cwd, fname) && chmod(fname, (mode_t)mask) == 0)
chmod(buf, (mode_t)mask) == 0)
{ {
send_reply(info, 257, "CHMOD successful."); send_reply(info, 257, "CHMOD successful.");
} }
@@ -1869,8 +1761,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if ( else if (
1 == sscanf(args, "%254s", fname) && 1 == sscanf(args, "%254s", fname) &&
NULL != make_path(buf, info->cwd, fname) && rmdir(fname) == 0)
rmdir(buf) == 0)
{ {
send_reply(info, 257, "RMD successful."); send_reply(info, 257, "RMD successful.");
} }
@@ -1887,8 +1778,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if ( else if (
1 == sscanf(args, "%254s", fname) && 1 == sscanf(args, "%254s", fname) &&
NULL != make_path(buf, info->cwd, fname) && mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
mkdir(buf, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
{ {
send_reply(info, 257, "MKD successful."); send_reply(info, 257, "MKD successful.");
} }
@@ -1900,20 +1790,15 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
else if (!strcmp("CWD", cmd)) else if (!strcmp("CWD", cmd))
{ {
sscanf(args, "%254s", fname); sscanf(args, "%254s", fname);
rtems_ftpd_cwd(info, fname); command_cwd(info, fname);
} }
else if (!strcmp("CDUP", cmd)) else if (!strcmp("CDUP", cmd))
{ {
rtems_ftpd_cwd(info, ".."); command_cwd(info, "..");
} }
else if (!strcmp("PWD", cmd)) else if (!strcmp("PWD", cmd))
{ {
char const* cwd = "/"; command_pwd(info);
if(info->cwd[0])
cwd = info->cwd;
snprintf(buf, FTPD_BUFSIZE,
"\"%s\" is the current directory.", cwd);
send_reply(info, 250, buf);
} }
else else
wrong_command = 1; wrong_command = 1;
@@ -1943,14 +1828,26 @@ static void
session(rtems_task_argument arg) session(rtems_task_argument arg)
{ {
FTPD_SessionInfo_t *const info = (FTPD_SessionInfo_t *)arg; FTPD_SessionInfo_t *const info = (FTPD_SessionInfo_t *)arg;
int chroot_made = 0;
rtems_libio_set_private_env();
/* chroot() can fail here because the directory may not exist yet. */
chroot_made = chroot(ftpd_root) == 0;
while(1) while(1)
{ {
rtems_event_set set; rtems_event_set set;
char buf[128];
rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
&set); &set);
chroot_made = chroot_made || chroot(ftpd_root) == 0;
chdir("/");
errno = 0;
send_reply(info, 220, FTPD_SERVER_MESSAGE); send_reply(info, 220, FTPD_SERVER_MESSAGE);
while (1) while (1)
@@ -1973,7 +1870,7 @@ session(rtems_task_argument arg)
} }
else else
{ {
exec_command(info, cmd, args, buf); exec_command(info, cmd, args);
} }
} }
@@ -2061,8 +1958,8 @@ daemon()
info->use_default = 1; info->use_default = 1;
info->ctrl_addr = addr; info->ctrl_addr = addr;
info->pasv_socket = -1; info->pasv_socket = -1;
info->data_socket = -1;
info->xfer_mode = TYPE_A; info->xfer_mode = TYPE_A;
info->cwd[0] = '\0';
info->data_addr.sin_port = info->data_addr.sin_port =
htons(ntohs(info->ctrl_addr.sin_port) - 1); htons(ntohs(info->ctrl_addr.sin_port) - 1);
info->idle = ftpd_timeout; info->idle = ftpd_timeout;
@@ -2149,18 +2046,17 @@ rtems_initialize_ftpd()
return RTEMS_UNSATISFIED; return RTEMS_UNSATISFIED;
} }
ftpd_root[0] = '\0'; ftpd_root = "/";
if ( if (
rtems_ftpd_configuration.root && rtems_ftpd_configuration.root &&
strlen(rtems_ftpd_configuration.root) < FTPD_BUFSIZE && rtems_ftpd_configuration.root[0] == '/'
rtems_ftpd_configuration.root[0] == '/') )
{ ftpd_root = rtems_ftpd_configuration.root;
strcpy(ftpd_root, rtems_ftpd_configuration.root);
squeeze_path(ftpd_root, NULL);
rtems_ftpd_configuration.root = ftpd_root; rtems_ftpd_configuration.root = ftpd_root;
}
syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)", syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
count, ((count > 1) ? "s" : "")); count, ((count > 1) ? "s" : ""));
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }

View File

@@ -15,6 +15,26 @@
* *
* Changes: * Changes:
* *
* 2001-01-31 Sergei Organov <osv@javad.ru>
*
* * Hacks with current dir and root dir removed in favor of new libio
* support for task-local current and root directories.
*
* 2001-01-30 Sergei Organov <osv@javad.ru>
*
* * Bug in `close_data_socket()' introduced by previous change fixed.
* * `command_pasv()' changed to set timeout on socket we are listening on
* and code fixed to don't close socket twice on error.
* * `serr()' changed to clear `errno'.
* * `data_socket()' changed to clear `errno' before `bind()'.
* * `session()' changed to clear `errno' before processing session.
*
* 2001-01-29 Sergei Organov <osv@javad.ru>
*
* * `close_data_socket()' fixed to close both active and passive sockets
* * Initialize info->data_socket to -1 in `daemon()'
* * Initialize `fname' to empty string in `exec_command()'
*
* 2001-01-22 Sergei Organov <osv@javad.ru> * 2001-01-22 Sergei Organov <osv@javad.ru>
* *
* * Timeouts on sockets implemented. 'idle' field added to * * Timeouts on sockets implemented. 'idle' field added to
@@ -169,6 +189,7 @@
#include <rtems.h> #include <rtems.h>
#include <rtems/rtems_bsdnet.h> #include <rtems/rtems_bsdnet.h>
#include <rtems/error.h> #include <rtems/error.h>
#include <rtems/libio.h>
#include <syslog.h> #include <syslog.h>
#include <sys/types.h> #include <sys/types.h>
@@ -235,7 +256,6 @@ typedef struct
int pasv_socket; /* Socket for PASV connection */ int pasv_socket; /* Socket for PASV connection */
int data_socket; /* Socket for data connection */ int data_socket; /* Socket for data connection */
int idle; /* Timeout in seconds */ int idle; /* Timeout in seconds */
char cwd[FTPD_BUFSIZE]; /* Current working directory */
int xfer_mode; /* Transfer mode (ASCII/binary) */ int xfer_mode; /* Transfer mode (ASCII/binary) */
rtems_id tid; /* Task id */ rtems_id tid; /* Task id */
} FTPD_SessionInfo_t; } FTPD_SessionInfo_t;
@@ -261,10 +281,10 @@ typedef struct
static FTPD_TaskPool_t task_pool; static FTPD_TaskPool_t task_pool;
/* /*
* Root node for FTPD without trailing slash. Even '/' node is denoted as * Root directory
* empty string here.
*/ */
static char ftpd_root[FTPD_BUFSIZE];
static char const* ftpd_root = "/";
/* /*
* Default idle timeout for sockets in seconds. * Default idle timeout for sockets in seconds.
@@ -286,7 +306,9 @@ static int ftpd_access = 0;
static char const* static char const*
serr(void) serr(void)
{ {
return strerror(errno); int err = errno;
errno = 0;
return strerror(err);
} }
/*PAGE /*PAGE
@@ -307,172 +329,6 @@ can_write(void)
return (ftpd_access & FTPD_NO_WRITE) == 0; return (ftpd_access & FTPD_NO_WRITE) == 0;
} }
/*PAGE
*
* Utility routines to manage root directory and session local
* current working directory.
*
*/
/*PAGE
*
* is_dir
*
* Return 1 if file with given 'name' exists and is directory, 0 otherwise.
*
*/
static int
is_dir(char const* name)
{
struct stat s;
int res = stat(name, &s) == 0 && S_ISDIR(s.st_mode);
return res;
}
/*PAGE
*
* squeeze_path
*
* Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
* from the path. Does nothing if the path is relative, i.e. doesn't begin
* with '/'. The trailing slash is always removed, even when alone, i.e. "/"
* will be "" after squeeze.
*
* Input parameters:
* path - the path to be squeezed
* full - full file name or NULL (assumed that it points to the beginning of
* buffer, and 'path' also points somewhere into the same buffer). Is
* used to check if intermediate names are directories.
*
* Output parameters:
* path - squeezed path
* returns 1 on success, 0 on failure (if any name that supposed to denote
* directory is not a directory).
*
*/
static int
squeeze_path(char* path, char* full)
{
if(path[0] == '/')
{
char* e = path + 1;
int rest = strlen(e);
while(rest >= 0)
{
int len;
char* s = e;
e = strchr(s, '/');
if(e)
{
char c = *e;
*e = '\0';
if(full && !is_dir(full))
{
*e = c;
return 0;
}
*e++ = c;
}
else
e = s + rest + 1;
len = e - s;
rest -= len;
if(len == 1 || (len == 2 && s[0] == '.'))
{
if(rest >= 0)
memmove(s, e, rest + 1);
else
*s++ = '\0';
e = s;
}
else if(len == 3 && s[0] == '.' && s[1] == '.')
{
char* ps = s;
if(ps - 1 > path) {
do
--ps;
while(ps[-1] != '/');
}
if(rest >= 0)
memmove(ps, e, rest + 1);
else
*ps++ = '\0';
e = ps;
}
}
if(e[-2] == '/')
{
e[-2] = '\0';
if(full && !is_dir(full))
return 0;
}
}
return 1;
}
/*PAGE
*
* make_path
*
* Makes full path given file name, current working directory and root
* directory (file scope variable 'root').
*
* Input parameters:
* cwd - current working directory
* name - file name
* root (file scope variable) - FTPD root directory
*
* Output parameters:
* buf - full path
* returns pointer to non-root part of the 'buf', i.e. to first character
* different from '/' after root part.
*
*/
static char const*
make_path(char* buf, char const* cwd, char const* name)
{
char* res = NULL;
int rlen = strlen(ftpd_root);
int clen = strlen(cwd);
int nlen = strlen(name);
int len = rlen + nlen;
if (name[0] != '/')
{
++len;
if (clen > 0)
len += clen + 1;
}
if (FTPD_BUFSIZE > len)
{
char* b = buf;
memcpy(b, ftpd_root, rlen); b += rlen;
if (name[0] != '/')
{
*b++ = '/';
if (clen > 0)
{
memcpy(b, cwd, clen); b += clen;
*b++ = '/';
}
}
memcpy(b, name, nlen); b += nlen;
*b = '\0';
res = buf + rlen;
while(rlen-- > 0 && res[-1] == '/')
--res;
if(!squeeze_path(res, buf))
res = NULL;
}
return res;
}
/*PAGE /*PAGE
* *
* Task pool management routines * Task pool management routines
@@ -602,8 +458,6 @@ task_pool_init(int count, rtems_task_priority priority)
return 0; return 0;
} }
task_pool.queue[i] = task_pool.info + i; task_pool.queue[i] = task_pool.info + i;
info->ctrl_fp = NULL;
info->ctrl_socket = -1;
if (++id > 'z') if (++id > 'z')
id = 'a'; id = 'a';
} }
@@ -637,8 +491,6 @@ task_pool_obtain()
info = task_pool.queue[task_pool.head]; info = task_pool.queue[task_pool.head];
if(++task_pool.head >= task_pool.count) if(++task_pool.head >= task_pool.count)
task_pool.head = 0; task_pool.head = 0;
info->ctrl_socket = -1;
info->ctrl_fp = NULL;
rtems_semaphore_release(task_pool.mutex); rtems_semaphore_release(task_pool.mutex);
} }
return info; return info;
@@ -800,6 +652,7 @@ data_socket(FTPD_SessionInfo_t *info)
data_source.sin_port = htons(20); /* ftp-data port */ data_source.sin_port = htons(20); /* ftp-data port */
for(tries = 1; tries < 10; ++tries) for(tries = 1; tries < 10; ++tries)
{ {
errno = 0;
if(bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0) if(bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0)
break; break;
if (errno != EADDRINUSE) if (errno != EADDRINUSE)
@@ -849,9 +702,11 @@ data_socket(FTPD_SessionInfo_t *info)
static void static void
close_data_socket(FTPD_SessionInfo_t *info) close_data_socket(FTPD_SessionInfo_t *info)
{ {
int s = info->pasv_socket; /* As at most one data socket could be open simultaneously and in some cases
data_socket == pasv_socket, we select socket to close, then close it. */
int s = info->data_socket;
if(0 > s) if(0 > s)
s = info->data_socket; s = info->pasv_socket;
if(!close_socket(s)) if(!close_socket(s))
syslog(LOG_ERR, "ftpd: Error closing data socket."); syslog(LOG_ERR, "ftpd: Error closing data socket.");
info->data_socket = -1; info->data_socket = -1;
@@ -936,7 +791,6 @@ command_retrieve(FTPD_SessionInfo_t *info, char const *filename)
int fd = -1; int fd = -1;
char buf[FTPD_DATASIZE]; char buf[FTPD_DATASIZE];
int res = 0; int res = 0;
char const* r = NULL;
if(!can_read()) if(!can_read())
{ {
@@ -944,9 +798,7 @@ command_retrieve(FTPD_SessionInfo_t *info, char const *filename)
return; return;
} }
r = make_path(buf, info->cwd, filename); if (0 > (fd = open(filename, O_RDONLY)))
if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
{ {
send_reply(info, 550, "Error opening file."); send_reply(info, 550, "Error opening file.");
return; return;
@@ -1028,6 +880,29 @@ command_retrieve(FTPD_SessionInfo_t *info, char const *filename)
} }
/*PAGE
*
* discard
*
* Analog of `write' routine that just discards passed data
*
* Input parameters:
* fd - file descriptor (ignored)
* buf - data to write (ignored)
* count - number of bytes in `buf'
*
* Output parameters:
* returns `count'
*
*/
static ssize_t
discard(int fd, void const* buf, size_t count)
{
(void)fd;
(void)buf;
return count;
}
/*PAGE /*PAGE
* *
* command_store * command_store
@@ -1051,8 +926,9 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
char buf[FTPD_DATASIZE]; char buf[FTPD_DATASIZE];
int res = 1; int res = 1;
int bare_lfs = 0; int bare_lfs = 0;
int null = 0;
int null = !strcmp("/dev/null", filename); typedef ssize_t (*WriteProc)(int, void const*, size_t);
WriteProc wrt = &write;
if(!can_write()) if(!can_write())
{ {
@@ -1060,33 +936,23 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
return; return;
} }
if(!null)
{
if (NULL == make_path(buf, info->cwd, filename))
{
send_reply(info, 550, "Error creating file.");
return;
}
}
send_mode_reply(info); send_mode_reply(info);
s = data_socket(info); s = data_socket(info);
if(0 > s) if(0 > s)
return; return;
null = !strcmp("/dev/null", filename);
if (null) if (null)
{ {
/* File "/dev/null" just throws data away. /* File "/dev/null" just throws data away.
* FIXME: this is hack. Using /dev/null filesystem entry would be * FIXME: this is hack. Using `/dev/null' filesystem entry would be
* better. However, it's not clear how to handle root directory other * better.
* than '/' then.
*/ */
while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0) wrt = &discard;
;
} }
else if (rtems_ftpd_configuration.hooks != NULL)
if (!null && rtems_ftpd_configuration.hooks != NULL)
{ {
/* Search our list of hooks to see if we need to do something special. */ /* Search our list of hooks to see if we need to do something special. */
@@ -1159,9 +1025,12 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
} }
else else
{ {
/* Data transfer to regular file. */ /* Data transfer to regular file or /dev/null. */
int fd = int fd = 0;
creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if(!null)
fd = creat(filename,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (0 > fd) if (0 > fd)
{ {
@@ -1174,7 +1043,7 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
{ {
while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0) while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
{ {
if (write(fd, buf, n) != n) if (wrt(fd, buf, n) != n)
{ {
res = 0; res = 0;
break; break;
@@ -1196,7 +1065,7 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
{ {
char const lf = '\r'; char const lf = '\r';
pended_cr = 0; pended_cr = 0;
if(write(fd, &lf, 1) != 1) if(wrt(fd, &lf, 1) != 1)
{ {
res = 0; res = 0;
break; break;
@@ -1231,12 +1100,12 @@ command_store(FTPD_SessionInfo_t *info, char const *filename)
if(res == 0) if(res == 0)
break; break;
count = i - sub - pended_cr; count = i - sub - pended_cr;
if(count > 0 && write(fd, b, count) != count) if(count > 0 && wrt(fd, b, count) != count)
{ {
res = 0; res = 0;
break; break;
} }
if(sub == 2 && write(fd, e - 1, 1) != 1) if(sub == 2 && wrt(fd, e - 1, 1) != 1)
res = 0; res = 0;
} }
while((rest -= i) > 0); while((rest -= i) > 0);
@@ -1384,10 +1253,7 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
struct dirent *dp = 0; struct dirent *dp = 0;
struct stat stat_buf; struct stat stat_buf;
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
char path[FTPD_BUFSIZE];
time_t curTime; time_t curTime;
char const* res;
char const* cwd = info->cwd;
int sc = 1; int sc = 1;
send_reply(info, 150, "Opening ASCII mode data connection for LIST."); send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
@@ -1402,15 +1268,13 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
if(fname[0] == '\0') if(fname[0] == '\0')
fname = "."; fname = ".";
res = make_path(path, cwd, fname); if (0 > stat(fname, &stat_buf))
if (NULL == res || 0 > stat(path, &stat_buf))
{ {
snprintf(buf, FTPD_BUFSIZE, snprintf(buf, FTPD_BUFSIZE,
"%s: No such file or directory.\r\n", fname); "%s: No such file or directory.\r\n", fname);
send(s, buf, strlen(buf), 0); send(s, buf, strlen(buf), 0);
} }
else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(path)))) else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(fname))))
{ {
snprintf(buf, FTPD_BUFSIZE, snprintf(buf, FTPD_BUFSIZE,
"%s: Can not open directory.\r\n", fname); "%s: Can not open directory.\r\n", fname);
@@ -1420,15 +1284,15 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
{ {
time(&curTime); time(&curTime);
if(!dirp && *fname) if(!dirp && *fname)
sc = sc && send_dirline(s, wide, curTime, path, "", fname, buf); sc = sc && send_dirline(s, wide, curTime, fname, "", fname, buf);
else { else {
/* FIXME: need "." and ".." only when '-a' option is given */ /* FIXME: need "." and ".." only when '-a' option is given */
sc = sc && send_dirline(s, wide, curTime, path, "", ".", buf); sc = sc && send_dirline(s, wide, curTime, fname, "", ".", buf);
sc = sc && send_dirline(s, wide, curTime, path, sc = sc && send_dirline(s, wide, curTime, fname,
(strcmp(path, ftpd_root) ? ".." : ""), "..", buf); (strcmp(fname, ftpd_root) ? ".." : ""), "..", buf);
while (sc && (dp = readdir(dirp)) != NULL) while (sc && (dp = readdir(dirp)) != NULL)
sc = sc && sc = sc &&
send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf); send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf);
} }
} }
@@ -1445,38 +1309,64 @@ command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
/*PAGE /*PAGE
* *
* rtems_ftpd_cwd * command_cwd
* *
* Change current working directory. We use 'chdir' here only to validate the * Change current working directory.
* new directory. We keep track of current working directory ourselves because
* current working directory in RTEMS isn't thread local, but we need it to be
* session local.
* *
* Input parameters: * Input parameters:
* info - corresponding SessionInfo structure * info - corresponding SessionInfo structure
* dir - directory name passed in CWD command * dir - directory name passed in CWD command
* *
* Output parameters: * Output parameters:
* info->cwd is set to new CWD value. * NONE
* *
*/ */
static void static void
rtems_ftpd_cwd(FTPD_SessionInfo_t *info, char *dir) command_cwd(FTPD_SessionInfo_t *info, char *dir)
{ {
char buf[FTPD_BUFSIZE]; if(chdir(dir) == 0)
char const* cwd = make_path(buf, info->cwd, dir);
if(cwd && chdir(buf) == 0)
{
send_reply(info, 250, "CWD command successful."); send_reply(info, 250, "CWD command successful.");
strcpy(info->cwd, cwd);
}
else else
{
send_reply(info, 550, "CWD command failed."); send_reply(info, 550, "CWD command failed.");
}
} }
/*PAGE
*
* command_pwd
*
* Send current working directory to client.
*
* Input parameters:
* info - corresponding SessionInfo structure
*
* Output parameters:
* NONE
*/
static void
command_pwd(FTPD_SessionInfo_t *info)
{
char buf[FTPD_BUFSIZE];
char const* cwd;
errno = 0;
buf[0] = '"';
cwd = getcwd(buf + 1, FTPD_BUFSIZE - 4);
if(cwd)
{
int len = strlen(cwd);
static char const txt[] = "\" is the current directory.";
int size = sizeof(txt);
if(len + size + 1 >= FTPD_BUFSIZE)
size = FTPD_BUFSIZE - len - 2;
memcpy(buf + len + 1, txt, size);
buf[len + size] = '\0';
send_reply(info, 250, buf);
}
else {
snprintf(buf, FTPD_BUFSIZE, "Error: %s.", serr());
send_reply(info, 452, buf);
}
}
/*PAGE /*PAGE
* *
@@ -1497,7 +1387,7 @@ command_mdtm(FTPD_SessionInfo_t *info, char const* fname)
struct stat stbuf; struct stat stbuf;
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
if (NULL == make_path(buf, info->cwd, fname) || 0 > stat(buf, &stbuf)) if (0 > stat(fname, &stbuf))
{ {
snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr()); snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
send_reply(info, 550, buf); send_reply(info, 550, buf);
@@ -1614,7 +1504,7 @@ command_pasv(FTPD_SessionInfo_t *info)
syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr()); syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr());
else if (0 > listen(s, 1)) else if (0 > listen(s, 1))
syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr()); syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr());
else else if(set_socket_timeout(s, info->idle))
{ {
char buf[FTPD_BUFSIZE]; char buf[FTPD_BUFSIZE];
unsigned char const *ip, *p; unsigned char const *ip, *p;
@@ -1627,13 +1517,16 @@ command_pasv(FTPD_SessionInfo_t *info)
send_reply(info, 227, buf); send_reply(info, 227, buf);
info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen); info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen);
close_socket(s);
if (0 > info->pasv_socket) if (0 > info->pasv_socket)
syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr()); syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr());
else else
{
close_socket(s);
s = -1;
err = 0; err = 0;
} }
} }
}
if(err) if(err)
{ {
/* (OSV) The note is from FreeBSD FTPD. /* (OSV) The note is from FreeBSD FTPD.
@@ -1746,17 +1639,18 @@ split_command(char *buf, char **cmd, char **opts, char **args)
* info - corresponding SessionInfo structure * info - corresponding SessionInfo structure
* cmd - command to be executed (upper-case) * cmd - command to be executed (upper-case)
* args - arguments of the command * args - arguments of the command
* buf - beginning of buffer where 'cmd' and 'args' reside.
* *
* Output parameters: * Output parameters:
* NONE * NONE
*/ */
static void static void
exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf) exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args)
{ {
char fname[FTPD_BUFSIZE]; char fname[FTPD_BUFSIZE];
int wrong_command = 0; int wrong_command = 0;
fname[0] = '\0';
if (!strcmp("PORT", cmd)) if (!strcmp("PORT", cmd))
{ {
command_port(info, args); command_port(info, args);
@@ -1824,8 +1718,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if ( else if (
1 == sscanf(args, "%254s", fname) && 1 == sscanf(args, "%254s", fname) &&
NULL != make_path(buf, info->cwd, fname) && unlink(fname) == 0)
unlink(buf) == 0)
{ {
send_reply(info, 257, "DELE successful."); send_reply(info, 257, "DELE successful.");
} }
@@ -1848,8 +1741,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if( else if(
2 == sscanf(args, "%o %254s", &mask, fname) && 2 == sscanf(args, "%o %254s", &mask, fname) &&
NULL != make_path(buf, info->cwd, fname) && chmod(fname, (mode_t)mask) == 0)
chmod(buf, (mode_t)mask) == 0)
{ {
send_reply(info, 257, "CHMOD successful."); send_reply(info, 257, "CHMOD successful.");
} }
@@ -1869,8 +1761,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if ( else if (
1 == sscanf(args, "%254s", fname) && 1 == sscanf(args, "%254s", fname) &&
NULL != make_path(buf, info->cwd, fname) && rmdir(fname) == 0)
rmdir(buf) == 0)
{ {
send_reply(info, 257, "RMD successful."); send_reply(info, 257, "RMD successful.");
} }
@@ -1887,8 +1778,7 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
} }
else if ( else if (
1 == sscanf(args, "%254s", fname) && 1 == sscanf(args, "%254s", fname) &&
NULL != make_path(buf, info->cwd, fname) && mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
mkdir(buf, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
{ {
send_reply(info, 257, "MKD successful."); send_reply(info, 257, "MKD successful.");
} }
@@ -1900,20 +1790,15 @@ exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args, char* buf)
else if (!strcmp("CWD", cmd)) else if (!strcmp("CWD", cmd))
{ {
sscanf(args, "%254s", fname); sscanf(args, "%254s", fname);
rtems_ftpd_cwd(info, fname); command_cwd(info, fname);
} }
else if (!strcmp("CDUP", cmd)) else if (!strcmp("CDUP", cmd))
{ {
rtems_ftpd_cwd(info, ".."); command_cwd(info, "..");
} }
else if (!strcmp("PWD", cmd)) else if (!strcmp("PWD", cmd))
{ {
char const* cwd = "/"; command_pwd(info);
if(info->cwd[0])
cwd = info->cwd;
snprintf(buf, FTPD_BUFSIZE,
"\"%s\" is the current directory.", cwd);
send_reply(info, 250, buf);
} }
else else
wrong_command = 1; wrong_command = 1;
@@ -1943,14 +1828,26 @@ static void
session(rtems_task_argument arg) session(rtems_task_argument arg)
{ {
FTPD_SessionInfo_t *const info = (FTPD_SessionInfo_t *)arg; FTPD_SessionInfo_t *const info = (FTPD_SessionInfo_t *)arg;
int chroot_made = 0;
rtems_libio_set_private_env();
/* chroot() can fail here because the directory may not exist yet. */
chroot_made = chroot(ftpd_root) == 0;
while(1) while(1)
{ {
rtems_event_set set; rtems_event_set set;
char buf[128];
rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
&set); &set);
chroot_made = chroot_made || chroot(ftpd_root) == 0;
chdir("/");
errno = 0;
send_reply(info, 220, FTPD_SERVER_MESSAGE); send_reply(info, 220, FTPD_SERVER_MESSAGE);
while (1) while (1)
@@ -1973,7 +1870,7 @@ session(rtems_task_argument arg)
} }
else else
{ {
exec_command(info, cmd, args, buf); exec_command(info, cmd, args);
} }
} }
@@ -2061,8 +1958,8 @@ daemon()
info->use_default = 1; info->use_default = 1;
info->ctrl_addr = addr; info->ctrl_addr = addr;
info->pasv_socket = -1; info->pasv_socket = -1;
info->data_socket = -1;
info->xfer_mode = TYPE_A; info->xfer_mode = TYPE_A;
info->cwd[0] = '\0';
info->data_addr.sin_port = info->data_addr.sin_port =
htons(ntohs(info->ctrl_addr.sin_port) - 1); htons(ntohs(info->ctrl_addr.sin_port) - 1);
info->idle = ftpd_timeout; info->idle = ftpd_timeout;
@@ -2149,18 +2046,17 @@ rtems_initialize_ftpd()
return RTEMS_UNSATISFIED; return RTEMS_UNSATISFIED;
} }
ftpd_root[0] = '\0'; ftpd_root = "/";
if ( if (
rtems_ftpd_configuration.root && rtems_ftpd_configuration.root &&
strlen(rtems_ftpd_configuration.root) < FTPD_BUFSIZE && rtems_ftpd_configuration.root[0] == '/'
rtems_ftpd_configuration.root[0] == '/') )
{ ftpd_root = rtems_ftpd_configuration.root;
strcpy(ftpd_root, rtems_ftpd_configuration.root);
squeeze_path(ftpd_root, NULL);
rtems_ftpd_configuration.root = ftpd_root; rtems_ftpd_configuration.root = ftpd_root;
}
syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)", syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
count, ((count > 1) ? "s" : "")); count, ((count > 1) ? "s" : ""));
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }

View File

@@ -1,3 +1,18 @@
2001-01-31 Sergei Organov <osv@javad.ru>
* rtems_servers/ftp.d: Following changes:
- Hacks with current dir and root dir removed in favor of new libio
support for task-local current and root directories.
- Bug in `close_data_socket()' introduced by previous change fixed.
- `command_pasv()' changed to set timeout on socket we are listening
on and code fixed to don't close socket twice on error.
- `serr()' changed to clear `errno'.
- `data_socket()' changed to clear `errno' before `bind()'.
- `session()' changed to clear `errno' before processing session.
- `close_data_socket()' fixed to close both active and passive sockets
- Initialize info->data_socket to -1 in `daemon()'
- Initialize `fname' to empty string in `exec_command()'
2001-02-03 Ralf Corsepius <corsepiu@faw.uni-ulm.de> 2001-02-03 Ralf Corsepius <corsepiu@faw.uni-ulm.de>
* Makefile.am, arpa/Makefile.am, machine/Makefile.am, net/Makefile.am, * Makefile.am, arpa/Makefile.am, machine/Makefile.am, net/Makefile.am,