Changeset 3f777d0e in rtems


Ignore:
Timestamp:
01/12/01 13:51:56 (22 years ago)
Author:
Joel Sherrill <joel.sherrill@…>
Branches:
4.10, 4.11, 4.8, 4.9, 5, master
Children:
c2a4084f
Parents:
d1941587
Message:

2001-01-12 Sergei Organov <osv@…>

  • rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements as listed below:
    • use pool of pre-created threads to handle sessions instead of

creating/deleting threads on the fly

  • LIST output is now similar to what "/bin/ls -al" would output, thus FTP clients such Netscape are happy with it.
  • LIST NAME now works (both for files and directories)
  • added support for NLST, CDUP, and MDTM FTP commands to make more FTP clients happy
  • keep track of CWD for every session separately
  • ability to specify root directory name for FTPD in configuration table. FTPD will then create illusion for FTP clients that this is actually root directory.
  • ignore options sent in commands, thus LIST -al FILE works and doesn't try to list "-al" directory.
  • buffers are allocated on stack instead of heap where possible to eliminate malloc/free calls (avoid possible heap fragmentation troubles).
  • drop using of task notepad to pass parameters - use function arguments instead
  • use snprintf() instead of sprintf() as the latter is unsafe
  • use of PF_INET in socket() instead of AF_INET

Here are ftp clients I've tried new FTPD with (all of them
running on Debian GNU/Linux 2.2):

Lftp 2.1.10
NcFTP 2.4.3
Netscape 4.75
ftp
mc 4.5.49

Files:
7 edited

Legend:

Unmodified
Added
Removed
  • c/src/exec/libnetworking/ChangeLog

    rd1941587 r3f777d0e  
     12001-01-12      Sergei Organov <osv@javad.ru>
     2
     3        * rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements
     4        as listed below:
     5            - use pool of pre-created threads to handle sessions instead of
     6              creating/deleting threads on the fly
     7            - LIST output is now similar to what "/bin/ls -al" would output,
     8              thus FTP clients such Netscape are happy with it.
     9            - LIST NAME now works (both for files and directories)
     10            - added support for NLST, CDUP, and MDTM FTP commands to make
     11              more FTP clients happy
     12            - keep track of CWD for every session separately
     13            - ability to specify root directory name for FTPD in configuration
     14              table. FTPD will then create illusion for FTP clients that this
     15              is actually root directory.
     16            - ignore options sent in commands, thus LIST -al FILE works and
     17              doesn't try to list "-al" directory.
     18            - buffers are allocated on stack instead of heap where possible to
     19              eliminate malloc/free calls (avoid possible heap fragmentation
     20              troubles).
     21            - drop using of task notepad to pass parameters - use function
     22              arguments instead
     23            - use snprintf() instead of sprintf() as the latter is unsafe
     24            - use of PF_INET in socket() instead of AF_INET
     25
     26            Here are ftp clients I've tried new FTPD with (all of them
     27            running on Debian GNU/Linux 2.2):
     28
     29                 Lftp 2.1.10
     30                 NcFTP 2.4.3
     31                 Netscape 4.75
     32                 ftp
     33                 mc 4.5.49
     34
    1352001-01-02      Joel Sherrill <joel@OARcorp.com>
    236
  • c/src/libnetworking/ChangeLog

    rd1941587 r3f777d0e  
     12001-01-12      Sergei Organov <osv@javad.ru>
     2
     3        * rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements
     4        as listed below:
     5            - use pool of pre-created threads to handle sessions instead of
     6              creating/deleting threads on the fly
     7            - LIST output is now similar to what "/bin/ls -al" would output,
     8              thus FTP clients such Netscape are happy with it.
     9            - LIST NAME now works (both for files and directories)
     10            - added support for NLST, CDUP, and MDTM FTP commands to make
     11              more FTP clients happy
     12            - keep track of CWD for every session separately
     13            - ability to specify root directory name for FTPD in configuration
     14              table. FTPD will then create illusion for FTP clients that this
     15              is actually root directory.
     16            - ignore options sent in commands, thus LIST -al FILE works and
     17              doesn't try to list "-al" directory.
     18            - buffers are allocated on stack instead of heap where possible to
     19              eliminate malloc/free calls (avoid possible heap fragmentation
     20              troubles).
     21            - drop using of task notepad to pass parameters - use function
     22              arguments instead
     23            - use snprintf() instead of sprintf() as the latter is unsafe
     24            - use of PF_INET in socket() instead of AF_INET
     25
     26            Here are ftp clients I've tried new FTPD with (all of them
     27            running on Debian GNU/Linux 2.2):
     28
     29                 Lftp 2.1.10
     30                 NcFTP 2.4.3
     31                 Netscape 4.75
     32                 ftp
     33                 mc 4.5.49
     34
    1352001-01-02      Joel Sherrill <joel@OARcorp.com>
    236
  • c/src/libnetworking/rtems_servers/ftpd.c

    rd1941587 r3f777d0e  
    11/* FIXME: 1. Parse command is a hack.  We can do better.
    2  *        2. chdir is a hack.  We can do better.
    3  *        3. PWD doesn't work.
    4  *        4. Some sort of access control?
    5  *
    6  *  FTP Server Daemon
     2 *        2. Some sort of access control?
     3 *        3. OSV: Timeouts on sockets.
     4 *        4. OSV: hooks support seems to be a joke, as it requires storing of
     5 *           entire input file in memory. Seem to be better to change it to
     6 *           something more reasonable, like having
     7 *           'hook_write(void const *buf, int count)' routine that will be
     8 *           called multiple times while file is being received.
     9 *        5. OSV: Remove hack with "/dev/null"?
     10 *        6. OSV: data_addr field of SessionInfo structure is not initialized.
     11 *           This will lead to undefined behavior if FTP client doesn't send
     12 *           PORT command. Seems we need to implement usage of default port
     13 *           (ftp port - 1). I didn't find any FTP client that doesn't send
     14 *           PORT command though.
     15 *        7. OSV: while server claims TYPE ASCII command succeeds (to make
     16 *           happy some clients), ASCII mode isn't actually implemented.
     17 *        8. Passive Mode?
     18 *
     19 *  FTP Server Daemon
    720 *
    821 *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
     22 *
     23 *  Changed by:   Sergei Organov <osv@javad.ru> (OSV)
     24 *    Changes:
     25 *      - use pool of pre-created threads to handle sessions
     26 *      - LIST output now similar to what "/bin/ls -al" would output, thus
     27 *        FTP clients could parse it.
     28 *      - LIST NAME now works (both for files and directories)
     29 *      - keep track of CWD for every session separately
     30 *      - ability to specify root directory name in configuration table
     31 *      - options sent in commands are ignored, thus LIST -al FILE works
     32 *      - added support for NLST, CDUP and MDTM commands
     33 *      - buffers are allocated on stack instead of heap where possible
     34 *      - drop using of task notepad to pass parameters - use function
     35 *        arguments instead
     36 *      - various bug-fixes, e.g., use of PF_INET in socket() instead of
     37 *        AF_INET, use snprintf() instead of sprintf() everywhere for safety,
     38 *        etc.
    939 *
    1040 *  $Id$
     
    2555 *    Organization:                                                       *
    2656 *                                                                        *
    27  *       The FTP daemon is started upon boot.  It runs all the time       *
    28  *       and waits for connections on the known FTP port (21).  When      *
    29  *       a connection is made, it starts a 'session' task.  That          *
     57 *       The FTP daemon is started upon boot along with a (configurable)  *
     58 *       number of tasks to handle sessions. It runs all the time and     *
     59 *       waits for connections on the known FTP port (21).  When          *
     60 *       a connection is made, it wakeups a 'session' task.  That         *
    3061 *       session then interacts with the remote host.  When the session   *
    31  *       is complete, the session task deletes itself.  The daemon still  *
     62 *       is complete, the session task goes to sleep.  The daemon still   *
    3263 *       runs, however.                                                   *
    3364 *                                                                        *
     
    3869 * STOR xxx     - Receives a file from the client.  xxx = filename.       *
    3970 * LIST xxx     - Sends a file list to the client.                        *
    40  *                (LIST xxx isn't working yet...)                         *
     71 * NLST xxx     - Sends a file list to the client.                        *
    4172 * USER         - Does nothing.                                           *
    4273 * PASS         - Does nothing.                                           *
     
    4778 * PWD          - Print working directory.                                *
    4879 * CWD xxx      - Change working directory.                               *
     80 * CDUP         - Change to upper directory.                              *
    4981 * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
    5082 * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
    5183 *                      and port (x*256 + y).                             *
     84 * MDTM xxx     - Send date/time to the client. xxx = filename.           *
    5285 *                                                                        *
    5386 *                                                                        *
    5487 * The public routines contained in this file are:                        *
    5588 *                                                                        *
    56  *    rtems_initialize_ftpd_start - Starts the server daemon, then        *
    57  *                                  returns to its caller.                *
    58  *                                                                        *
    59  *                                                                        *
    60  * The private routines contained in this file are:                       *
    61  *                                                                        *
    62  *    rtems_ftpd_send_reply    - Sends a reply code and text through the  *
    63  *                               control port.                            *
    64  *    rtems_ftpd_command_retrieve - Performs to "RETR" command.           *
    65  *    rtems_ftpd_command_store - Performs the "STOR" command.             *
    66  *    rtems_ftpd_command_list  - Performs the "LIST" command.             *
    67  *    rtems_ftpd_command_port  - Opens a data port (the "PORT" command).  *
    68  *    rtems_ftpd_parse_command - Parses an incoming command.              *
    69  *    rtmes_ftpd_session       - Begins a service session.                *
    70  *    rtems_ftpd_daemon        - Listens on the FTP port for service      *
    71  *                               requests.                                *
     89 *    rtems_initialize_ftpd - Initializes and starts the server daemon,   *
     90 *                            then returns to its caller.                 *
     91 *                                                                        *
    7292 *------------------------------------------------------------------------*
    7393 * Jake Janovetz                                                          *
     
    7797 **************************************************************************
    7898 * Change History:                                                        *
    79  *  12/01/97 - Creation (JWJ)                                             *
     99 *  12/01/97   - Creation (JWJ)                                           *
     100 *  2001-01-08 - Changes by OSV                                           *
    80101 *************************************************************************/
    81 
    82 #include <stdio.h>
    83 #include <stdlib.h>
    84 #include <string.h>
    85 #include <unistd.h>
    86 #include <fcntl.h>
    87 #include <dirent.h>
    88 
    89 #include <rtems.h>
    90 #include <rtems/rtems_bsdnet.h>
    91 #include <rtems/error.h>
    92 #include <syslog.h>
    93 
    94 #include <sys/types.h>
    95 #include <sys/socket.h>
    96 #include <arpa/ftp.h>
    97 #include <netinet/in.h>
    98 
    99 #include "ftpd.h"
    100 
    101 
    102 extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
    103102
    104103/**************************************************************************
     
    115114 *         not take place, but the error condition is temporary so the
    116115 *         command can be reissued later.
    117  *  5yz    Permanent negative completion reply.  The command was not 
     116 *  5yz    Permanent negative completion reply.  The command was not
    118117 *         accepted and should not be retried.
    119118 *-------------------------------------------------------------------------
     
    128127 *************************************************************************/
    129128
    130 
    131 /**************************************************************************
    132  * SessionInfo structure.
    133  *
    134  * The following structure is allocated for each session.  The pointer
    135  * to this structure is contained in the tasks notepad entry.
    136  *************************************************************************/
    137 typedef struct
    138 {
    139    struct sockaddr_in  data_addr;   /* Data address for PORT commands */
    140    FILE                *ctrl_fp;    /* File pointer for control connection */
    141    char                cwd[255];    /* Current working directory */
    142                                     /* Login -- future use -- */
    143    int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
    144 } FTPD_SessionInfo_t;
    145 
    146 
    147 #define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.0-JWJ) ready."
     129#include <stdio.h>
     130#include <stdlib.h>
     131#include <string.h>
     132#include <unistd.h>
     133#include <fcntl.h>
     134#include <dirent.h>
     135#include <errno.h>
     136
     137#include <rtems.h>
     138#include <rtems/rtems_bsdnet.h>
     139#include <rtems/error.h>
     140#include <syslog.h>
     141
     142#include <sys/types.h>
     143#include <sys/socket.h>
     144#include <arpa/ftp.h>
     145#include <netinet/in.h>
     146
     147#include "ftpd.h"
     148
     149
     150#ifdef __GNUC__
     151/* change to #if 1 to disable syslog entirely */
     152#if 0
     153#undef  syslog
     154#define syslog(a, b, ...) while(0){}
     155#endif
     156#endif
     157
     158#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.1-JWJ) ready."
     159
     160#define FTPD_SYSTYPE "RTEMS"
     161
     162/* Seem to be unused */
     163#if 0
    148164#define FTPD_WELCOME_MESSAGE \
    149165   "Welcome to the RTEMS FTP server.\n" \
    150166   "\n" \
    151167   "Login accepted.\n"
     168#endif
     169
     170/* Various buffer sizes */
     171enum
     172{
     173  FTPD_BUFSIZE  = 256,       /* Size for temporary buffers */
     174  FTPD_DATASIZE = 1024,      /* Size for file transfer buffers */
     175  FTPD_STACKSIZE = 8 * 1024, /* Tasks stack size */
     176};
     177
     178/* Event to be used by session tasks for waiting */
     179enum
     180{
     181  FTPD_RTEMS_EVENT = RTEMS_EVENT_1
     182};
     183
     184/* Configuration table */
     185extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
     186
     187/* this is not prototyped in strict ansi mode */
     188FILE *fdopen (int fildes, const char *mode);
     189
     190/*
     191 * SessionInfo structure.
     192 *
     193 * The following structure is allocated for each session.
     194 */
     195typedef struct
     196{
     197  struct sockaddr_in  data_addr;   /* Data address for PORT commands */
     198  FILE                *ctrl_fp;    /* File pointer for control connection */
     199  int                 socket;      /* Socket descriptor for ctrl connection */
     200  char                cwd[FTPD_BUFSIZE];  /* Current working directory */
     201  /* Login -- future use -- */
     202  int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
     203  rtems_id            tid;         /* Task id */
     204} FTPD_SessionInfo_t;
     205
     206
     207/*
     208 * TaskPool structure.
     209 */
     210typedef struct
     211{
     212  FTPD_SessionInfo_t    *info;
     213  FTPD_SessionInfo_t    **queue;
     214  int                   count;
     215  int                   head;
     216  int                   tail;
     217  rtems_id              mutex;
     218  rtems_id              sem;
     219} FTPD_TaskPool_t;
     220
     221/*
     222 * Task pool instance.
     223 */
     224static FTPD_TaskPool_t task_pool;
     225
     226/*
     227 * Root node for FTPD without trailing slash. Even '/' node is denoted as
     228 * empty string here.
     229 */
     230static char root[FTPD_BUFSIZE];
     231
     232
     233/*PAGE
     234 *
     235 * serr
     236 *
     237 * Return errror string corresponding to current 'errno'.
     238 *
     239 */
     240static char const*
     241serr(void)
     242{
     243  return strerror(errno);
     244}
     245
     246
     247/*
     248 *  Utility routines to manage root directory and session local
     249 *  current working directory.
     250 */
     251
     252
     253/*PAGE
     254 *
     255 * squeeze_path
     256 *
     257 * Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
     258 * from the path. Does nothing if the path is relative, i.e. doesn't begin
     259 * with '/'. The trailing slash is always removed, even when alone, i.e. "/"
     260 * will be "" after squeeze.
     261 *
     262 * Input parameters:
     263 *   path - the path to be squeezed
     264 *
     265 * Output parameters:
     266 *   path - squeezed path
     267 *
     268 */
     269static void
     270squeeze_path(char* path)
     271{
     272  if(path[0] == '/')
     273  {
     274    char* e = path + 1;
     275    int rest = strlen(e);
     276    while(rest >= 0)
     277    {
     278      int len;
     279      char* s = e;
     280      e = strchr(s, '/');
     281      if(e)
     282        ++e;
     283      else
     284        e = s + rest + 1;
     285      len = e - s;
     286      rest -= len;
     287      if(len == 1 || (len == 2 && s[0] == '.'))
     288      {
     289        if(rest >= 0)
     290          memmove(s, e, rest + 1);
     291        else
     292          *s++ = '\0';
     293        e = s;
     294      }
     295      else if(len == 3 && s[0] == '.' && s[1] == '.')
     296      {
     297        char* ps = s;
     298        if(ps - 1 > path) {
     299          do
     300            --ps;
     301          while(ps[-1] != '/');
     302        }
     303        if(rest >= 0)
     304          memmove(ps, e, rest + 1);
     305        else
     306          *ps++ = '\0';
     307        e = ps;
     308      }
     309    }
     310    if(e[-2] == '/')
     311      e[-2] = '\0';
     312  }
     313}
     314
     315
     316/*PAGE
     317 *
     318 * make_path
     319 *
     320 * Makes full path given file name, current working directory and root
     321 * directory (file scope variable 'root').
     322 *
     323 * Input parameters:
     324 *   cwd  - current working directory
     325 *   name - file name
     326 *   root (file scope variable) - FTPD root directory
     327 *
     328 * Output parameters:
     329 *   buf - full path
     330 *   returns pointer to non-root part of the 'buf', i.e. to first character
     331 *           different from '/' after root part.
     332 *
     333 */
     334static char const*
     335make_path(char* buf, char const* cwd, char const* name)
     336{
     337  char* res = NULL;
     338
     339  int rlen = strlen(root);
     340  int clen = strlen(cwd);
     341  int nlen = strlen(name);
     342  int len = rlen + nlen;
     343
     344  if (name[0] != '/')
     345  {
     346    ++len;
     347    if (clen > 0)
     348      len += clen + 1;
     349  }
     350
     351  if (FTPD_BUFSIZE > len)
     352  {
     353    char* b = buf;
     354    memcpy(b, root, rlen); b += rlen;
     355    if (name[0] != '/')
     356    {
     357      *b++ = '/';
     358      if (clen > 0)
     359      {
     360        memcpy(b, cwd, clen); b += clen;
     361        *b++ = '/';
     362      }
     363    }
     364    memcpy(b, name, nlen); b += nlen;
     365    *b = '\0';
     366
     367    res = buf + rlen;
     368    while(rlen-- > 0 && res[-1] == '/')
     369      --res;
     370    squeeze_path(res);
     371  }
     372
     373  return res;
     374}
     375
     376/*
     377 * Task pool management routines
     378 */
     379
     380
     381/*PAGE
     382 *
     383 * task_pool_done
     384 *
     385 * Cleanup task pool.
     386 *
     387 * Input parameters:
     388 *   count - number of entries in task pool to cleanup
     389 *
     390 * Output parameters:
     391 *   NONE
     392 *
     393 */
     394static void
     395task_pool_done(int count)
     396{
     397  int i;
     398  for(i = 0; i < count; ++i)
     399    rtems_task_delete(task_pool.info[i].tid);
     400  if(task_pool.info)
     401    free(task_pool.info);
     402  if(task_pool.queue)
     403    free(task_pool.queue);
     404  if(task_pool.mutex != (rtems_id)-1)
     405    rtems_semaphore_delete(task_pool.mutex);
     406  if(task_pool.sem != (rtems_id)-1)
     407    rtems_semaphore_delete(task_pool.sem);
     408  task_pool.info = 0;
     409  task_pool.queue = 0;
     410  task_pool.count = 0;
     411  task_pool.sem = -1;
     412  task_pool.mutex = -1;
     413}
     414
     415/* Forward declare */
     416static void session(rtems_task_argument arg);
     417
     418/*PAGE
     419 *
     420 * task_pool_init
     421 *
     422 * Initialize task pool.
     423 *
     424 * Input parameters:
     425 *   count    - number of entries in task pool to create
     426 *   priority - priority tasks are started with
     427 *
     428 * Output parameters:
     429 *   returns 1 on success, 0 on failure.
     430 *
     431 */
     432static int
     433task_pool_init(int count, rtems_task_priority priority)
     434{
     435  int i;
     436  rtems_status_code sc;
     437  char id = 'a';
     438
     439  task_pool.count = 0;
     440  task_pool.head = task_pool.tail = 0;
     441  task_pool.mutex = (rtems_id)-1;
     442  task_pool.sem   = (rtems_id)-1;
     443
     444  sc = rtems_semaphore_create(
     445    rtems_build_name('F', 'T', 'P', 'M'),
     446    1,
     447    RTEMS_DEFAULT_ATTRIBUTES
     448    | RTEMS_BINARY_SEMAPHORE
     449    | RTEMS_INHERIT_PRIORITY
     450    | RTEMS_PRIORITY,
     451    RTEMS_NO_PRIORITY,
     452    &task_pool.mutex);
     453
     454  if(sc == RTEMS_SUCCESSFUL)
     455    sc = rtems_semaphore_create(
     456      rtems_build_name('F', 'T', 'P', 'S'),
     457      count,
     458      RTEMS_DEFAULT_ATTRIBUTES,
     459      RTEMS_NO_PRIORITY,
     460      &task_pool.sem);
     461
     462  if(sc != RTEMS_SUCCESSFUL) {
     463    task_pool_done(0);
     464    syslog(LOG_ERR, "ftpd: Can not create semaphores");
     465    return 0;
     466  }
     467
     468  task_pool.info = (FTPD_SessionInfo_t*)
     469    malloc(sizeof(FTPD_SessionInfo_t) * count);
     470  task_pool.queue = (FTPD_SessionInfo_t**)
     471    malloc(sizeof(FTPD_SessionInfo_t*) * count);
     472  if (NULL == task_pool.info || NULL == task_pool.queue)
     473  {
     474    task_pool_done(0);
     475    syslog(LOG_ERR, "ftpd: Not enough memory");
     476    return 0;
     477  }
     478
     479  for(i = 0; i < count; ++i)
     480  {
     481    FTPD_SessionInfo_t *info = &task_pool.info[i];
     482    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', id),
     483      priority, FTPD_STACKSIZE,
     484      RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
     485      RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
     486      RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
     487      &info->tid);
     488    if (sc == RTEMS_SUCCESSFUL)
     489    {
     490      sc = rtems_task_start(
     491        info->tid, session, (rtems_task_argument)info);
     492      if (sc != RTEMS_SUCCESSFUL)
     493        task_pool_done(i);
     494    }
     495    else
     496      task_pool_done(i + 1);
     497    if (sc != RTEMS_SUCCESSFUL)
     498    {
     499      syslog(LOG_ERR, "ftpd: Could not create/start FTPD session: %s",
     500        rtems_status_text(sc));
     501      return 0;
     502    }
     503    task_pool.queue[i] = task_pool.info + i;
     504    info->ctrl_fp = NULL;
     505    info->socket  = -1;
     506    if (++id > 'z')
     507      id = 'a';
     508  }
     509  task_pool.count = count;
     510  return 1;
     511}
     512
     513/*PAGE
     514 *
     515 * task_pool_obtain
     516 *
     517 * Obtain free task from task pool.
     518 *
     519 * Input parameters:
     520 *   NONE
     521 *
     522 * Output parameters:
     523 *   returns pointer to the corresponding SessionInfo structure on success,
     524 *           NULL if there are no free tasks in the pool.
     525 *
     526 */
     527static FTPD_SessionInfo_t*
     528task_pool_obtain()
     529{
     530  FTPD_SessionInfo_t* info = 0;
     531  rtems_status_code sc;
     532  sc = rtems_semaphore_obtain(task_pool.sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT);
     533  if (sc == RTEMS_SUCCESSFUL)
     534  {
     535    rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
     536    info = task_pool.queue[task_pool.head];
     537    if(++task_pool.head >= task_pool.count)
     538      task_pool.head = 0;
     539    info->socket = -1;
     540    info->ctrl_fp = NULL;
     541    rtems_semaphore_release(task_pool.mutex);
     542  }
     543  return info;
     544}
     545
     546/*PAGE
     547 *
     548 * task_pool_release
     549 *
     550 * Return task obtained by 'obtain()' back to the task pool.
     551 *
     552 * Input parameters:
     553 *   info  - pointer to corresponding SessionInfo structure.
     554 *
     555 * Output parameters:
     556 *   NONE
     557 *
     558 */
     559static void
     560task_pool_release(FTPD_SessionInfo_t* info)
     561{
     562  rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
     563  task_pool.queue[task_pool.tail] = info;
     564  if(++task_pool.tail >= task_pool.count)
     565    task_pool.tail = 0;
     566  rtems_semaphore_release(task_pool.mutex);
     567  rtems_semaphore_release(task_pool.sem);
     568}
     569
     570/*
     571 * End of task pool routines
     572 */
     573
     574/*PAGE
     575 *
     576 * close_socket
     577 *
     578 * Close socket.
     579 *
     580 * Input parameters:
     581 *   s - socket descriptor to be closed.
     582 *
     583 * Output parameters:
     584 *   returns 1 on success, 0 on failure
     585 *
     586 */
     587static int
     588close_socket(int s)
     589{
     590  if (0 <= s)
     591  {
     592    if (0 != close(s))
     593    {
     594      shutdown(s, 2);
     595      if (0 != close(s))
     596        return 0;
     597    }
     598  }
     599  return 1;
     600}
     601
     602/*PAGE
     603 *
     604 * close_stream
     605 *
     606 * Close data stream of session.
     607 *
     608 * Input parameters:
     609 *   info - corresponding SessionInfo structure
     610 *
     611 * Output parameters:
     612 *   NONE
     613 *
     614 */
     615static void
     616close_stream(FTPD_SessionInfo_t* info)
     617{
     618  if (NULL != info->ctrl_fp)
     619  {
     620    if (0 != fclose(info->ctrl_fp))
     621    {
     622      syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
     623    }
     624    else
     625      info->socket = -1;
     626  }
     627
     628  if (!close_socket(info->socket))
     629    syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
     630
     631  info->ctrl_fp = NULL;
     632  info->socket = -1;
     633}
    152634
    153635
    154636/**************************************************************************
    155  * Function: rtems_ftpd_send_reply                                        *
     637 * Function: send_reply                                                   *
    156638 **************************************************************************
    157639 * Description:                                                           *
     
    170652 *    none                                                                *
    171653 *                                                                        *
    172  **************************************************************************
    173  * Change History:                                                        *
    174  *  12/01/97 - Creation (JWJ)                                             *
    175654 *************************************************************************/
    176655static void
    177 rtems_ftpd_send_reply(int code, char *text)
    178 {
    179    rtems_status_code   sc;
    180    FTPD_SessionInfo_t  *info = NULL;
    181    char                str[80];
    182 
    183 
    184    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    185                             (rtems_unsigned32 *)&info);
    186 
    187    /***********************************************************************
    188     * code must be a 3-digit number.
    189     **********************************************************************/
    190    if ((code < 100) || (code > 999))
    191    {
    192       syslog(LOG_ERR, "ftpd: Code not 3-digits.");
    193       return;
    194    }
    195 
    196    /***********************************************************************
    197     * If a text reply exists, add it to the reply data.
    198     **********************************************************************/
    199    if (text != NULL)
    200    {
    201       sprintf(str, "%d %.70s\r\n", code, text);
    202       fprintf(info->ctrl_fp, "%d %.70s\r\n", code, text);
    203    }
    204    else
    205    {
    206       sprintf(str, "%d\r\n", code);
    207       fprintf(info->ctrl_fp, "%d\r\n", code);
    208    }
    209    fflush(info->ctrl_fp);
    210 }
    211 
     656send_reply(FTPD_SessionInfo_t  *info, int code, char *text)
     657{
     658  char const* s;
     659
     660
     661  /***********************************************************************
     662   * code must be a 3-digit number.
     663   **********************************************************************/
     664  if ((code < 100) || (code > 999))
     665  {
     666    syslog(LOG_ERR, "ftpd: Code not 3-digits.");
     667    return;
     668  }
     669
     670  /***********************************************************************
     671   * If a text reply exists, add it to the reply data.
     672   **********************************************************************/
     673  s  = (info->xfer_mode == TYPE_A) ? "\r" : "";
     674  if (text != NULL)
     675    fprintf(info->ctrl_fp, "%d %.70s%s\n", code, text, s);
     676  else
     677    fprintf(info->ctrl_fp, "%d%s\n", code, s);
     678  fflush(info->ctrl_fp);
     679}
     680
     681
     682/*PAGE
     683 *
     684 * send_mode_reply
     685 *
     686 * Sends BINARY/ASCII reply string depending on current transfer mode.
     687 *
     688 * Input parameters:
     689 *   info - corresponding SessionInfo structure
     690 *
     691 * Output parameters:
     692 *   NONE
     693 *
     694 */
     695static void
     696send_mode_reply(FTPD_SessionInfo_t *info)
     697{
     698  if(info->xfer_mode == TYPE_I)
     699    send_reply(info, 150, "Opening BINARY mode data connection.");
     700  else
     701    send_reply(info, 150, "Opening ASCII mode data connection.");
     702}
     703
     704/*PAGE
     705 *
     706 * data_socket
     707 *
     708 * Create data socket for session.
     709 *
     710 * Input parameters:
     711 *   info - corresponding SessionInfo structure
     712 *
     713 * Output parameters:
     714 *   returns socket descriptor, or -1 if failure
     715 *
     716 */
     717static int
     718data_socket(FTPD_SessionInfo_t *info)
     719{
     720  int s = socket(PF_INET, SOCK_STREAM, 0);
     721  if(0 > s)
     722    send_reply(info, 420, "Server error - could not create socket.");
     723  else if(0 > connect(s, (struct sockaddr *)&info->data_addr,
     724    sizeof(struct sockaddr)))
     725  {
     726    send_reply(info, 420, "Server error - could not connect socket.");
     727    close_socket(s);
     728    s = -1;
     729  }
     730  return s;
     731}
    212732
    213733/**************************************************************************
    214  * Function: rtems_ftpd_command_retrieve                                  *
     734 * Function: command_retrieve                                             *
    215735 **************************************************************************
    216736 * Description:                                                           *
     
    230750 *           1 for no reply sent.                                         *
    231751 *                                                                        *
    232  **************************************************************************
    233  * Change History:                                                        *
    234  *  04/29/98 - Creation (JWJ)                                             *
    235752 *************************************************************************/
    236753static int
    237 rtems_ftpd_command_retrieve(char *filename)
    238 {
    239    int                 s;
    240    int                 n;
    241    int                 fd;
    242    unsigned char       *bufr;
    243    rtems_status_code   sc;
    244    FTPD_SessionInfo_t  *info = NULL;
    245 
    246 
    247    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    248                             (rtems_unsigned32 *)&info);
    249 
    250    if ((fd = open(filename, O_RDONLY)) == -1)
    251    {
    252       rtems_ftpd_send_reply(450, "Error opening file.");
    253       return(0);
    254    }
    255 
    256    bufr = (unsigned char *)malloc(BUFSIZ);
    257    if (bufr == NULL)
    258    {
    259       rtems_ftpd_send_reply(440, "Server error - malloc fail.");
    260       close(fd);
    261       return(0);
    262    }
    263 
    264    /***********************************************************************
    265     * Connect to the data connection (PORT made in an earlier PORT call).
    266     **********************************************************************/
    267    rtems_ftpd_send_reply(150, "BINARY data connection.");
    268    s = socket(AF_INET, SOCK_STREAM, 0);
    269    if (connect(s, (struct sockaddr *)&info->data_addr,
    270                sizeof(struct sockaddr)) < 0)
    271    {
    272       rtems_ftpd_send_reply(420, "Server error - could not connect socket.");
    273       free(bufr);
    274       close(fd);
    275       close(s);
    276       return(1);
    277    }
    278 
    279    /***********************************************************************
    280     * Send the data over the ether.
    281     **********************************************************************/
    282    while ((n = read(fd, bufr, BUFSIZ)) > 0)
    283    {
    284       send(s, bufr, n, 0);
    285       bufr[n-1] = '\0';
    286    }
    287 
    288    if (n == 0)
    289    {
    290       rtems_ftpd_send_reply(210, "File sent successfully.");
    291    }
    292    else
    293    {
    294       rtems_ftpd_send_reply(450, "Retrieve failed.");
    295    }
    296 
    297    if (close(s) != 0)
    298    {
    299       syslog(LOG_ERR, "ftpd: Error closing data socket");
    300    }
    301 
    302    free(bufr);
    303    close(fd);
    304    return(0);
     754command_retrieve(FTPD_SessionInfo_t  *info, char *filename)
     755{
     756  int                 s = -1;
     757  int                 n;
     758  int                 fd = -1;
     759  char                buf[FTPD_DATASIZE];
     760  int                 res = 0;
     761
     762  char const* r = make_path(buf, info->cwd, filename);
     763
     764  if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
     765  {
     766    send_reply(info, 450, "Error opening file.");
     767    return(res);
     768  }
     769
     770  send_mode_reply(info);
     771
     772  /***********************************************************************
     773   * Connect to the data connection (PORT made in an earlier PORT call).
     774   **********************************************************************/
     775  s = data_socket(info);
     776  if (0 <= s)
     777  {
     778    /***********************************************************************
     779     * Send the data over the ether.
     780     **********************************************************************/
     781    while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
     782    {
     783      send(s, buf, n, 0);
     784    }
     785
     786    if (0 == n)
     787    {
     788      if (0 == close(fd))
     789      {
     790        fd = -1;
     791        res = 1;
     792      }
     793    }
     794  }
     795
     796  if (0 == res)
     797    send_reply(info, 450, "Retrieve failed.");
     798  else
     799    send_reply(info, 210, "File sent successfully.");
     800
     801  if (-1 != fd)
     802    close(fd);
     803
     804  if (!close_socket(s))
     805    syslog(LOG_ERR, "ftpd: Error closing data socket");
     806
     807  return(res);
    305808}
    306809
    307810
    308811/**************************************************************************
    309  * Function: rtems_ftpd_command_store                                     *
     812 * Function: command_store                                                *
    310813 **************************************************************************
    311814 * Description:                                                           *
     
    325828 *           1 for failure.                                               *
    326829 *                                                                        *
    327  **************************************************************************
    328  * Change History:                                                        *
    329  *  12/01/97 - Creation (JWJ)                                             *
    330830 *************************************************************************/
    331831static int
    332 rtems_ftpd_command_store(char *filename)
    333 {
    334    char                   *bufr;
    335    int                    s;
    336    int                    n;
    337    unsigned long          size = 0;
    338    rtems_status_code      sc;
    339    FTPD_SessionInfo_t     *info = NULL;
    340    struct rtems_ftpd_hook *usehook = NULL;
    341 
    342 
    343    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    344                             (rtems_unsigned32 *)&info);
    345 
    346    bufr = (char *)malloc(BUFSIZ * sizeof(char));
    347    if (bufr == NULL)
    348    {
    349       rtems_ftpd_send_reply(440, "Server error - malloc fail.");
     832command_store(FTPD_SessionInfo_t *info, char *filename)
     833{
     834  int                    s;
     835  int                    n;
     836  unsigned long          size = 0;
     837  struct rtems_ftpd_hook *usehook = NULL;
     838  char                   buf[FTPD_DATASIZE];
     839
     840  int null = !strcmp("/dev/null", filename);
     841
     842  if(!null)
     843  {
     844    if (NULL == make_path(buf, info->cwd, filename))
     845    {
     846      send_reply(info, 450, "Error creating file.");
     847      return(0);
     848    }
     849  }
     850
     851  send_mode_reply(info);
     852
     853  s = data_socket(info);
     854  if(0 > s)
     855  {
     856    return(1);
     857  }
     858
     859
     860  /***********************************************************************
     861   * File: "/dev/null" just throws the data away.
     862   * Otherwise, search our list of hooks to see if we need to do something
     863   *   special.
     864   * OSV: FIXME: this is hack. Using /dev/null filesystem entry would be
     865   *             better. However, it's not clear how to handle root directory
     866   *             other than '/' then.
     867   **********************************************************************/
     868  if (null)
     869  {
     870    while ((n = read(s, buf, FTPD_DATASIZE)) > 0);
     871  }
     872  else if (rtems_ftpd_configuration.hooks != NULL)
     873  {
     874    struct rtems_ftpd_hook *hook;
     875    int i;
     876
     877    i = 0;
     878    hook = &rtems_ftpd_configuration.hooks[i++];
     879    while (hook->filename != NULL)
     880    {
     881      if (!strcmp(hook->filename, buf))
     882      {
     883        usehook = hook;
     884        break;
     885      }
     886      hook = &rtems_ftpd_configuration.hooks[i++];
     887    }
     888  }
     889
     890  if (usehook != NULL)
     891  {
     892    /*
     893     * OSV: FIXME: Small buffer could be used and hook routine
     894     * called multiple times instead. Alternatively, the support could be
     895     * removed entirely in favor of configuring RTEMS pseudo-device with
     896     * given name.
     897     */
     898
     899    char                *bigBufr;
     900    size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1;
     901
     902    /***********************************************************************
     903     * Allocate space for our "file".
     904     **********************************************************************/
     905    bigBufr = (char *)malloc(filesize);
     906    if (bigBufr == NULL)
     907    {
     908      send_reply(info, 440, "Server error - malloc fail.");
    350909      return(1);
    351    }
    352 
    353    rtems_ftpd_send_reply(150, "BINARY data connection.");
    354 
    355    s = socket(AF_INET, SOCK_STREAM, 0);
    356    if (connect(s, (struct sockaddr *)&info->data_addr,
    357                sizeof(struct sockaddr)) < 0)
    358    {
    359       free(bufr);
    360       close(s);
     910    }
     911
     912    /***********************************************************************
     913     * Retrieve the file into our buffer space.
     914     **********************************************************************/
     915    size = 0;
     916    while ((n = read(s, bigBufr + size, filesize - size)) > 0)
     917    {
     918      size += n;
     919    }
     920    if (size >= filesize)
     921    {
     922      send_reply(info, 440, "Server error - Buffer size exceeded.");
     923      free(bigBufr);
     924      close_socket(s);
    361925      return(1);
    362    }
    363 
    364 
    365    /***********************************************************************
    366     * File: "/dev/null" just throws the data away.
    367     * Otherwise, search our list of hooks to see if we need to do something
    368     *   special.
    369     **********************************************************************/
    370    if (!strncmp("/dev/null", filename, 9))
    371    {
    372       while ((n = read(s, bufr, BUFSIZ)) > 0);
    373    }
    374    else if (rtems_ftpd_configuration.hooks != NULL)
    375    {
    376       struct rtems_ftpd_hook *hook;
    377       int i;
    378 
    379       i = 0;
    380       hook = &rtems_ftpd_configuration.hooks[i++];
    381       while (hook->filename != NULL)
     926    }
     927    close_socket(s);
     928
     929    /***********************************************************************
     930     * Call our hook.
     931     **********************************************************************/
     932    if ((usehook->hook_function)(bigBufr, size) == 0)
     933    {
     934      send_reply(info, 210, "File transferred successfully.");
     935    }
     936    else
     937    {
     938      send_reply(info, 440, "File transfer failed.");
     939    }
     940    free(bigBufr);
     941  }
     942  else
     943  {
     944    int fd =
     945      creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
     946
     947    if (0 > fd)
     948    {
     949      send_reply(info, 450, "Error creating file.");
     950      close_socket(s);
     951      return(0);
     952    }
     953
     954    while ((n = read(s, buf, FTPD_DATASIZE)) > 0)
     955    {
     956      if (0 > write(fd, buf, n))
    382957      {
    383          if (!strcmp(hook->filename, filename))
    384          {
    385             usehook = hook;
    386             break;
    387          }
    388          hook = &rtems_ftpd_configuration.hooks[i++];
     958        send_reply(info, 450, "Error during write.");
     959        close(fd);
     960        close_socket(s);
     961        return(1);
    389962      }
    390    }
    391 
    392    if (usehook != NULL)
    393    {
    394       char                *bigBufr;
    395 
    396       /***********************************************************************
    397        * Allocate space for our "file".
    398        **********************************************************************/
    399       bigBufr = (char *)malloc(
    400                   rtems_ftpd_configuration.max_hook_filesize * sizeof(char));
    401       if (bigBufr == NULL)
     963    }
     964    if (0 > close(fd))
     965    {
     966      send_reply(info, 450, "Error during write.");
     967      close_socket(s);
     968      return(1);
     969    }
     970    close_socket(s);
     971    send_reply(info, 226, "Transfer complete.");
     972  }
     973
     974  return(0);
     975}
     976
     977
     978/*PAGE
     979 *
     980 * send_dirline
     981 *
     982 * Sends one line of LIST command reply corresponding to single file.
     983 *
     984 * Input parameters:
     985 *   s - socket descriptor to send data to
     986 *   wide - if 0, send only file name. If not 0, send 'stat' info as well in
     987 *          "ls -l" format.
     988 *   curTime - current time
     989 *   path - path to be prepended to what is given by 'add'
     990 *   add  - path to be appended to what is given by 'path', the resulting path
     991 *          is then passed to 'stat()' routine
     992 *   name - file name to be reported in output
     993 *   buf  - buffer for temporary data
     994 *
     995 * Output parameters:
     996 *   NONE
     997 *
     998 */
     999static void
     1000send_dirline(int s, int wide, time_t curTime, char const* path,
     1001  char const* add, char const* fname, char* buf)
     1002{
     1003  if(wide)
     1004  {
     1005    struct stat stat_buf;
     1006
     1007    int plen = strlen(path);
     1008    int alen = strlen(add);
     1009    if(plen == 0)
     1010    {
     1011      buf[plen++] = '/';
     1012      buf[plen] = '\0';
     1013    }
     1014    else
     1015    {
     1016      strcpy(buf, path);
     1017      if(alen > 0 && buf[plen - 1] != '/')
    4021018      {
    403          rtems_ftpd_send_reply(440, "Server error - malloc fail.");
    404          free(bufr);
    405          return(1);
     1019        buf[plen++] = '/';
     1020        if(plen >= FTPD_BUFSIZE)
     1021          return;
     1022        buf[plen] = '\0';
    4061023      }
    407 
    408       /***********************************************************************
    409        * Retrieve the file into our buffer space.
    410        **********************************************************************/
    411       size = 0;
    412       while ((n = read(s, bufr, BUFSIZ)) > 0)
    413       {
    414          if (size + n >
    415                rtems_ftpd_configuration.max_hook_filesize * sizeof(char))
    416          {
    417             rtems_ftpd_send_reply(440, "Server error - Buffer size exceeded.");
    418             free(bufr);
    419             free(bigBufr);
    420             close(s);
    421             return(1);
    422          }
    423          memcpy(&bigBufr[size], bufr, n);
    424          size += n;
    425       }
    426       close(s);
    427 
    428       /***********************************************************************
    429        * Call our hook.
    430        **********************************************************************/
    431       if ((usehook->hook_function)(bigBufr, size) == 0)
    432       {
    433          rtems_ftpd_send_reply(210, "File transferred successfully.");
    434       }
     1024    }
     1025    if(plen + alen >= FTPD_BUFSIZE)
     1026      return;
     1027    strcpy(buf + plen, add);
     1028
     1029    if (stat(buf, &stat_buf) == 0)
     1030    {
     1031      int len;
     1032      struct tm bt;
     1033      time_t tf = stat_buf.st_mtime;
     1034      enum { SIZE = 80 };
     1035      enum { SIX_MONTHS = 365*24*60*60/2 };
     1036      char timeBuf[SIZE];
     1037      gmtime_r(&tf, &bt);
     1038      if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS)
     1039        strftime (timeBuf, SIZE, "%b %d  %Y", &bt);
    4351040      else
    436       {
    437          rtems_ftpd_send_reply(440, "File transfer failed.");
    438       }
    439       free(bigBufr);
    440    }
    441    else
    442    {
    443       int    fd;
    444       size_t written;
    445 
    446       fd = creat(filename, S_IRUSR | S_IWUSR |
    447                            S_IRGRP | S_IWGRP |
    448                            S_IROTH | S_IWOTH);
    449       if (fd == -1)
    450       {
    451          rtems_ftpd_send_reply(450, "Could not open file.");
    452          close(s);
    453          free(bufr);
    454          return(1);
    455       }
    456       while ((n = read(s, bufr, BUFSIZ)) > 0)
    457       {
    458          written = write(fd, bufr, n);
    459          if (written == -1)
    460          {
    461             rtems_ftpd_send_reply(450, "Error during write.");
    462             close(fd);
    463             close(s);
    464             free(bufr);
    465             return(1);
    466          }
    467       }
    468       close(fd);
    469       close(s);
    470       rtems_ftpd_send_reply(226, "Transfer complete.");
    471    }
    472 
    473    free(bufr);
    474    return(0);
    475 }
    476 
     1041        strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);
     1042
     1043      len = snprintf(buf, FTPD_BUFSIZE,
     1044        "%c%c%c%c%c%c%c%c%c%c  1 %5d %5d %11u %s %s\r\n",
     1045        (S_ISLNK(stat_buf.st_mode)?('l'):
     1046          (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
     1047        (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
     1048        (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
     1049        (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
     1050        (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
     1051        (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
     1052        (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
     1053        (stat_buf.st_mode & S_IROTH)?('r'):('-'),
     1054        (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
     1055        (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
     1056        (int)stat_buf.st_uid,
     1057        (int)stat_buf.st_gid,
     1058        (int)stat_buf.st_size,
     1059        timeBuf,
     1060        fname
     1061      );
     1062
     1063      send(s, buf, len, 0);
     1064    }
     1065  }
     1066  else
     1067  {
     1068    int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
     1069    send(s, buf, len, 0);
     1070  }
     1071}
    4771072
    4781073/**************************************************************************
    479  * Function: rtems_ftpd_command_list                                      *
     1074 * Function: command_list                                      *
    4801075 **************************************************************************
    4811076 * Description:                                                           *
     
    4931088 *    none                                                                *
    4941089 *                                                                        *
    495  **************************************************************************
    496  * Change History:                                                        *
    497  *  12/01/97 - Creation (JWJ)                                             *
    4981090 *************************************************************************/
    4991091static void
    500 rtems_ftpd_command_list(char *fname)
    501 {
    502    int                 s;
    503    rtems_status_code   sc;
    504    FTPD_SessionInfo_t  *info = NULL;
    505    DIR                 *dirp;
    506    struct dirent       *dp;
    507    char                dirline[255];
    508    struct stat         stat_buf;
    509 
    510 
    511    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    512                             (rtems_unsigned32 *)&info);
    513 
    514    rtems_ftpd_send_reply(150, "ASCII data connection for LIST.");
    515 
    516    s = socket(AF_INET, SOCK_STREAM, 0);
    517    if (connect(s, (struct sockaddr *)&info->data_addr,
    518                sizeof(struct sockaddr)) < 0)
    519    {
    520       syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
    521       return;
    522    }
    523 
    524    if ((dirp = opendir(fname)) == NULL)
    525    {
    526       sprintf(dirline, "%s: No such file or directory.%s\n",
    527               fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
    528       send(s, dirline, strlen(dirline), 0);
    529       close(s);
    530       rtems_ftpd_send_reply(226, "Transfer complete.");
    531       return;
    532    }
    533    while ((dp = readdir(dirp)) != NULL)
    534    {
    535       if (stat(dp->d_name, &stat_buf) == 0)
    536       {
    537          sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c  %5d %5d %11d  %s%s\n",
    538                  (S_ISLNK(stat_buf.st_mode)?('l'):
    539                     (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
    540                  (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
    541                  (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
    542                  (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
    543                  (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
    544                  (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
    545                  (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
    546                  (stat_buf.st_mode & S_IROTH)?('r'):('-'),
    547                  (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
    548                  (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
    549                  (int)stat_buf.st_uid,
    550                  (int)stat_buf.st_gid,
    551                  (int)stat_buf.st_size,
    552                  dp->d_name,
    553                  (info->xfer_mode==TYPE_A)?("\r"):(""));
    554          send(s, dirline, strlen(dirline), 0);
    555       }
    556    }
    557    closedir(dirp);
    558 
    559    close(s);
    560    rtems_ftpd_send_reply(226, "Transfer complete.");
    561 }
    562 
    563 
    564 /*
    565  * Cheesy way to change directories
     1092command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
     1093{
     1094  int                 s;
     1095  DIR                 *dirp = 0;
     1096  struct dirent       *dp = 0;
     1097  struct stat         stat_buf;
     1098  char                buf[FTPD_BUFSIZE];
     1099  char                path[FTPD_BUFSIZE];
     1100  time_t curTime;
     1101  char const* res;
     1102  char const* cwd = info->cwd;
     1103
     1104  send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
     1105
     1106  s = data_socket(info);
     1107  if(0 > s)
     1108  {
     1109    syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
     1110    return;
     1111  }
     1112
     1113  if(fname[0] == '\0')
     1114    fname = ".";
     1115
     1116  res = make_path(path, cwd, fname);
     1117
     1118  if (NULL == res || 0 > stat(path, &stat_buf))
     1119  {
     1120    snprintf(buf, FTPD_BUFSIZE,
     1121      "%s: No such file or directory.\r\n", fname);
     1122    send(s, buf, strlen(buf), 0);
     1123  }
     1124  else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(path))))
     1125  {
     1126    snprintf(buf, FTPD_BUFSIZE,
     1127      "%s: Can not open directory.\r\n", fname);
     1128    send(s, buf, strlen(buf), 0);
     1129  }
     1130  else
     1131  {
     1132    time(&curTime);
     1133    if(!dirp)
     1134      send_dirline(s, wide, curTime, path, "", fname, buf);
     1135    else {
     1136      /* FIXME: need "." and ".." only when '-a' option is given */
     1137      send_dirline(s, wide, curTime, path, "", ".", buf);
     1138      if(!strcmp(path, root))
     1139        send_dirline(s, wide, curTime, path, "", "..", buf);
     1140      else
     1141        send_dirline(s, wide, curTime, path, "..", "..", buf);
     1142      while ((dp = readdir(dirp)) != NULL)
     1143        send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf);
     1144    }
     1145  }
     1146
     1147  if(dirp)
     1148    closedir(dirp);
     1149  close_socket(s);
     1150  send_reply(info, 226, "Transfer complete.");
     1151}
     1152
     1153
     1154/*PAGE
     1155 *
     1156 * rtems_ftpd_cwd
     1157 *
     1158 * Change current working directory. We use 'chdir' here only to validate the
     1159 * new directory. We keep track of current working directory ourselves because
     1160 * current working directory in RTEMS isn't thread local, but we need it to be
     1161 * session local.
     1162 *
     1163 * Input parameters:
     1164 *   info - corresponding SessionInfo structure
     1165 *   dir  - directory name passed in CWD command
     1166 *
     1167 * Output parameters:
     1168 *   info->cwd is set to new CWD value.
     1169 *
    5661170 */
    5671171static void
    568 rtems_ftpd_CWD(char *dir)
    569 {
    570    rtems_status_code   sc;
    571    FTPD_SessionInfo_t  *info = NULL;
    572 
    573 
    574    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    575                             (rtems_unsigned32 *)&info);
    576 
    577    if (chdir(dir) == 0)
    578    {
    579       rtems_ftpd_send_reply(250, "CWD command successful.");
    580    }
    581    else
    582    {
    583       rtems_ftpd_send_reply(550, "CWD command failed.");
    584    }
    585 }
    586 
     1172rtems_ftpd_cwd(FTPD_SessionInfo_t  *info, char *dir)
     1173{
     1174  char buf[FTPD_BUFSIZE];
     1175  char const* cwd = make_path(buf, info->cwd, dir);
     1176  if(cwd && chdir(buf) == 0)
     1177  {
     1178    send_reply(info, 250, "CWD command successful.");
     1179    strcpy(info->cwd, cwd);
     1180  }
     1181  else
     1182  {
     1183    send_reply(info, 550, "CWD command failed.");
     1184  }
     1185}
     1186
     1187
     1188
     1189/*PAGE
     1190 *
     1191 * command_mdtm
     1192 *
     1193 * Handle FTP MDTM command
     1194 *
     1195 * Input parameters:
     1196 *   info - corresponding SessionInfo structure
     1197 *   fname - file name passed in MDTM command
     1198 *
     1199 * Output parameters:
     1200 *   info->cwd is set to new CWD value.
     1201 *
     1202 */
     1203static void
     1204command_mdtm(FTPD_SessionInfo_t  *info, char const* fname)
     1205{
     1206  struct stat stbuf;
     1207  char buf[FTPD_BUFSIZE];
     1208
     1209  if(*fname == '\0')
     1210    fname = ".";
     1211
     1212  if (stat(fname, &stbuf) < 0)
     1213  {
     1214    snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
     1215    send_reply(info, 550, buf);
     1216  }
     1217  else
     1218  {
     1219    struct tm *t = gmtime(&stbuf.st_mtime);
     1220    snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d",
     1221      1900 + t->tm_year,
     1222      t->tm_mon+1, t->tm_mday,
     1223      t->tm_hour, t->tm_min, t->tm_sec);
     1224    send_reply(info, 213, buf);
     1225  }
     1226}
    5871227
    5881228/**************************************************************************
    589  * Function: rtems_ftpd_command_port                                      *
     1229 * Function: command_port                                                 *
    5901230 **************************************************************************
    5911231 * Description:                                                           *
     
    6051245 *    none                                                                *
    6061246 *                                                                        *
    607  **************************************************************************
    608  * Change History:                                                        *
    609  *  12/01/97 - Creation (JWJ)                                             *
    6101247 *************************************************************************/
    6111248static void
    612 rtems_ftpd_command_port(char *bufr)
    613 {
    614    char                *ip;
    615    char                *port;
    616    int                 ip0, ip1, ip2, ip3, port0, port1;
    617    rtems_status_code   sc;
    618    FTPD_SessionInfo_t  *info = NULL;
    619 
    620 
    621    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    622                             (rtems_unsigned32 *)&info);
    623 
    624    sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
    625    ip = (char *)&(info->data_addr.sin_addr);
    626    ip[0] = ip0 & 0xff;
    627    ip[1] = ip1 & 0xff;
    628    ip[2] = ip2 & 0xff;
    629    ip[3] = ip3 & 0xff;
    630    port = (char *)&(info->data_addr.sin_port);
    631    port[0] = port0 & 0xff;
    632    port[1] = port1 & 0xff;
    633    info->data_addr.sin_family = AF_INET;
    634 }
    635 
     1249command_port(FTPD_SessionInfo_t *info, char *bufr)
     1250{
     1251  char                *ip;
     1252  char                *port;
     1253  int                 ip0, ip1, ip2, ip3, port0, port1;
     1254
     1255  sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
     1256  ip = (char *)&(info->data_addr.sin_addr);
     1257  ip[0] = ip0 & 0xff;
     1258  ip[1] = ip1 & 0xff;
     1259  ip[2] = ip2 & 0xff;
     1260  ip[3] = ip3 & 0xff;
     1261  port = (char *)&(info->data_addr.sin_port);
     1262  port[0] = port0 & 0xff;
     1263  port[1] = port1 & 0xff;
     1264  info->data_addr.sin_family = AF_INET;
     1265}
     1266
     1267
     1268/*PAGE
     1269 *
     1270 * skip_options
     1271 *
     1272 * Utility routine to skip options (if any) from input command.
     1273 *
     1274 * Input parameters:
     1275 *   p  - pointer to pointer to command
     1276 *
     1277 * Output parameters:
     1278 *   p  - is changed to point to first non-option argument
     1279 *
     1280 */
     1281static void
     1282skip_options(char **p)
     1283{
     1284  char* buf = *p;
     1285  while(1) {
     1286    while(*buf == ' ')
     1287      ++buf;
     1288    if(*buf == '-') {
     1289      if(*++buf == '-') { /* `--' should terminate options */
     1290        ++buf;
     1291        while(*buf == ' ')
     1292          ++buf;
     1293        break;
     1294      }
     1295      while(*buf != ' ' && *buf != '\0')
     1296        ++buf;
     1297    }
     1298    else
     1299      break;
     1300  }
     1301  *p = buf;
     1302}
    6361303
    6371304/**************************************************************************
    638  * Function: rtems_ftpd_parse_command                                     *
     1305 * Function: parse_command                                     *
    6391306 **************************************************************************
    6401307 * Description:                                                           *
     
    6551322 *    none                                                                *
    6561323 *                                                                        *
    657  **************************************************************************
    658  * Change History:                                                        *
    659  *  12/01/97 - Creation (JWJ)                                             *
    6601324 *************************************************************************/
    6611325static void
    662 rtems_ftpd_parse_command(char *bufr)
    663 {
    664    char fname[255];
    665    rtems_status_code   sc;
    666    FTPD_SessionInfo_t  *info = NULL;
    667 
    668 
    669    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    670                             (rtems_unsigned32 *)&info);
    671 
    672    if (!strncmp("PORT", bufr, 4))
    673    {
    674       rtems_ftpd_send_reply(200, "PORT command successful.");
    675       rtems_ftpd_command_port(&bufr[5]);
    676    }
    677    else if (!strncmp("RETR", bufr, 4))
    678    {
    679       sscanf(&bufr[5], "%254s", fname);
    680       rtems_ftpd_command_retrieve(fname);
    681    }
    682    else if (!strncmp("STOR", bufr, 4))
    683    {
    684       sscanf(&bufr[5], "%254s", fname);
    685       rtems_ftpd_command_store(fname);
    686    }
    687    else if (!strncmp("LIST", bufr, 4))
    688    {
    689       if (bufr[5] == '\n')
    690       {
    691          rtems_ftpd_command_list(".");
    692       }
    693       else
    694       {
    695          sscanf(&bufr[5], "%254s", fname);
    696          rtems_ftpd_command_list(fname);
    697       }
    698    }
    699    else if (!strncmp("USER", bufr, 4))
    700    {
    701       rtems_ftpd_send_reply(230, "User logged in.");
    702    }
    703    else if (!strncmp("SYST", bufr, 4))
    704    {
    705       rtems_ftpd_send_reply(240, "RTEMS");
    706    }
    707    else if (!strncmp("TYPE", bufr, 4))
    708    {
    709       if (bufr[5] == 'I')
    710       {
    711          info->xfer_mode = TYPE_I;
    712          rtems_ftpd_send_reply(200, "Type set to I.");
    713       }
    714       else if (bufr[5] == 'A')
    715       {
    716          info->xfer_mode = TYPE_A;
    717          rtems_ftpd_send_reply(200, "Type set to A.");
    718       }
    719       else
    720       {
    721          info->xfer_mode = TYPE_I;
    722          rtems_ftpd_send_reply(504, "Type not implemented.  Set to I.");
    723       }
    724    }
    725    else if (!strncmp("PASS", bufr, 4))
    726    {
    727       rtems_ftpd_send_reply(230, "User logged in.");
    728    }
    729    else if (!strncmp("DELE", bufr, 4))
    730    {
    731       sscanf(&bufr[4], "%254s", fname);
    732       if (unlink(fname) == 0)
    733       {
    734          rtems_ftpd_send_reply(257, "DELE successful.");
    735       }
    736       else
    737       {
    738          rtems_ftpd_send_reply(550, "DELE failed.");
    739       }
    740    }
    741    else if (!strncmp("SITE CHMOD", bufr, 10))
    742    {
    743       int mask;
    744 
    745       sscanf(&bufr[11], "%o %254s", &mask, fname);
    746       if (chmod(fname, (mode_t)mask) == 0)
    747       {
    748          rtems_ftpd_send_reply(257, "CHMOD successful.");
    749       }
    750       else
    751       {
    752          rtems_ftpd_send_reply(550, "CHMOD failed.");
    753       }
    754    }
    755    else if (!strncmp("RMD", bufr, 3))
    756    {
    757       sscanf(&bufr[4], "%254s", fname);
    758       if (rmdir(fname) == 0)
    759       {
    760          rtems_ftpd_send_reply(257, "RMD successful.");
    761       }
    762       else
    763       {
    764          rtems_ftpd_send_reply(550, "RMD failed.");
    765       }
    766    }
    767    else if (!strncmp("MKD", bufr, 3))
    768    {
    769       sscanf(&bufr[4], "%254s", fname);
    770       if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
    771       {
    772          rtems_ftpd_send_reply(257, "MKD successful.");
    773       }
    774       else
    775       {
    776          rtems_ftpd_send_reply(550, "MKD failed.");
    777       }
    778    }
    779    else if (!strncmp("CWD", bufr, 3))
    780    {
    781       sscanf(&bufr[4], "%254s", fname);
    782       rtems_ftpd_CWD(fname);
    783    }
    784    else if (!strncmp("PWD", bufr, 3))
    785    {
    786       char *cwd = getcwd(0, 0);
    787       sprintf(bufr, "\"%s\" is the current directory.", cwd);
    788       rtems_ftpd_send_reply(250, bufr);
    789       free(cwd);
    790    }
    791    else
    792    {
    793       rtems_ftpd_send_reply(500, "Unrecognized/unsupported command.");
    794    }
     1326parse_command(FTPD_SessionInfo_t *info, char *bufr)
     1327{
     1328  char fname[FTPD_BUFSIZE];
     1329
     1330  if (!strncmp("PORT", bufr, 4))
     1331  {
     1332    send_reply(info, 200, "PORT command successful.");
     1333    command_port(info, &bufr[5]);
     1334  }
     1335  else if (!strncmp("RETR", bufr, 4))
     1336  {
     1337    sscanf(&bufr[5], "%254s", fname);
     1338    command_retrieve(info, fname);
     1339  }
     1340  else if (!strncmp("STOR", bufr, 4))
     1341  {
     1342    sscanf(&bufr[5], "%254s", fname);
     1343    command_store(info, fname);
     1344  }
     1345  else if (!strncmp("LIST", bufr, 4))
     1346  {
     1347    bufr += 4;
     1348    skip_options(&bufr);
     1349    sscanf(bufr, "%254s", fname);
     1350    command_list(info, fname, 1);
     1351  }
     1352  else if (!strncmp("NLST", bufr, 4))
     1353  {
     1354    bufr += 4;
     1355    skip_options(&bufr);
     1356    sscanf(bufr, "%254s", fname);
     1357    command_list(info, fname, 0);
     1358  }
     1359  else if (!strncmp("MDTM", bufr, 4))
     1360  {
     1361    bufr += 4;
     1362    skip_options(&bufr);
     1363    sscanf(bufr, "%254s", fname);
     1364    command_mdtm(info, fname);
     1365  }
     1366  else if (!strncmp("USER", bufr, 4))
     1367  {
     1368    send_reply(info, 230, "User logged in.");
     1369  }
     1370  else if (!strncmp("SYST", bufr, 4))
     1371  {
     1372    send_reply(info, 240, FTPD_SYSTYPE);
     1373  }
     1374  else if (!strncmp("TYPE", bufr, 4))
     1375  {
     1376    if (bufr[5] == 'I')
     1377    {
     1378      info->xfer_mode = TYPE_I;
     1379      send_reply(info, 200, "Type set to I.");
     1380    }
     1381    else if (bufr[5] == 'A')
     1382    {
     1383      /* FIXME: ASCII mode isn't actually supported yet. */
     1384      info->xfer_mode = TYPE_A;
     1385      send_reply(info, 200, "Type set to A.");
     1386    }
     1387    else
     1388    {
     1389      info->xfer_mode = TYPE_I;
     1390      send_reply(info, 504, "Type not implemented.  Set to I.");
     1391    }
     1392  }
     1393  else if (!strncmp("PASS", bufr, 4))
     1394  {
     1395    send_reply(info, 230, "User logged in.");
     1396  }
     1397  else if (!strncmp("DELE", bufr, 4))
     1398  {
     1399    sscanf(&bufr[4], "%254s", fname);
     1400    if (unlink(fname) == 0)
     1401    {
     1402      send_reply(info, 257, "DELE successful.");
     1403    }
     1404    else
     1405    {
     1406      send_reply(info, 550, "DELE failed.");
     1407    }
     1408  }
     1409  else if (!strncmp("SITE CHMOD", bufr, 10))
     1410  {
     1411    int mask;
     1412
     1413    sscanf(&bufr[11], "%o %254s", &mask, fname);
     1414    if (chmod(fname, (mode_t)mask) == 0)
     1415    {
     1416      send_reply(info, 257, "CHMOD successful.");
     1417    }
     1418    else
     1419    {
     1420      send_reply(info, 550, "CHMOD failed.");
     1421    }
     1422  }
     1423  else if (!strncmp("RMD", bufr, 3))
     1424  {
     1425    sscanf(&bufr[4], "%254s", fname);
     1426    if (rmdir(fname) == 0)
     1427    {
     1428      send_reply(info, 257, "RMD successful.");
     1429    }
     1430    else
     1431    {
     1432      send_reply(info, 550, "RMD failed.");
     1433    }
     1434  }
     1435  else if (!strncmp("MKD", bufr, 3))
     1436  {
     1437    sscanf(&bufr[4], "%254s", fname);
     1438    if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
     1439    {
     1440      send_reply(info, 257, "MKD successful.");
     1441    }
     1442    else
     1443    {
     1444      send_reply(info, 550, "MKD failed.");
     1445    }
     1446  }
     1447  else if (!strncmp("CWD", bufr, 3))
     1448  {
     1449    sscanf(&bufr[4], "%254s", fname);
     1450    rtems_ftpd_cwd(info, fname);
     1451  }
     1452  else if (!strncmp("CDUP", bufr, 4))
     1453  {
     1454    rtems_ftpd_cwd(info, "..");
     1455  }
     1456  else if (!strncmp("PWD", bufr, 3))
     1457  {
     1458    char const* cwd = "/";
     1459    if(info->cwd[0])
     1460      cwd = info->cwd;
     1461    snprintf(bufr, FTPD_BUFSIZE,
     1462      "\"%s\" is the current directory.", cwd);
     1463    send_reply(info, 250, bufr);
     1464  }
     1465  else
     1466  {
     1467    send_reply(info, 500, "Unrecognized/unsupported command.");
     1468  }
    7951469}
    7961470
    7971471
    7981472/**************************************************************************
    799  * Function: rtems_ftpd_session                                           *
     1473 * Function: session                                           *
    8001474 **************************************************************************
    8011475 * Description:                                                           *
     
    8171491 *    none                                                                *
    8181492 *                                                                        *
    819  **************************************************************************
    820  * Change History:                                                        *
    821  *  12/01/97 - Creation (JWJ)                                             *
    8221493 *************************************************************************/
    8231494static void
    824 rtems_ftpd_session(rtems_task_argument arg)
    825 {
    826    char                cmd[256];
    827    rtems_status_code   sc;
    828    FTPD_SessionInfo_t  *info = NULL;
    829 
    830 
    831    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    832                             (rtems_unsigned32 *)&info);
    833 
    834    rtems_ftpd_send_reply(220, FTPD_SERVER_MESSAGE);
    835 
    836    /***********************************************************************
    837     * Set initial directory to "/".
    838     **********************************************************************/
    839    strcpy(info->cwd, "/");
    840    info->xfer_mode = TYPE_A;
    841    while (1)
    842    {
    843       if (fgets(cmd, 256, info->ctrl_fp) == NULL)
     1495session(rtems_task_argument arg)
     1496{
     1497  char cmd[FTPD_BUFSIZE];
     1498  FTPD_SessionInfo_t  *info = (FTPD_SessionInfo_t  *)arg;
     1499  rtems_event_set set;
     1500
     1501  while(1) {
     1502    rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
     1503      &set);
     1504
     1505    send_reply(info, 220, FTPD_SERVER_MESSAGE);
     1506
     1507    info->cwd[0] = 0;
     1508    info->xfer_mode = TYPE_I;
     1509
     1510    while (1)
     1511    {
     1512      if (fgets(cmd, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
    8441513      {
    845          syslog(LOG_INFO, "ftpd: Connection aborted.");
    846          break;
     1514        syslog(LOG_INFO, "ftpd: Connection aborted.");
     1515        break;
    8471516      }
    8481517
    8491518      if (!strncmp("QUIT", cmd, 4))
    8501519      {
    851          rtems_ftpd_send_reply(221, "Goodbye.");
    852          break;
     1520        send_reply(info, 221, "Goodbye.");
     1521        break;
    8531522      }
    8541523      else
    8551524      {
    856          rtems_ftpd_parse_command(cmd);
     1525        parse_command(info, cmd);
    8571526      }
    858    }
    859 
    860    if (fclose(info->ctrl_fp) != 0)
    861    {
    862       syslog(LOG_ERR, "ftpd: Could not close session.");
    863    }
    864 
    865 
    866    /* Least we can do is put the CWD back to /. */
    867    chdir("/");
    868 
    869    /***********************************************************************
    870     * Free up the allocated SessionInfo struct and exit.
    871     **********************************************************************/
    872    free(info);
    873    sc = rtems_task_delete(RTEMS_SELF);
    874    syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
    875           rtems_status_text(sc));
     1527    }
     1528
     1529    /* Close connection and put ourselves back into the task pool. */
     1530    close_stream(info);
     1531    task_pool_release(info);
     1532  }
    8761533}
    8771534
    8781535
    8791536/**************************************************************************
    880  * Function: rtems_ftpd_daemon                                            *
     1537 * Function: daemon                                            *
    8811538 **************************************************************************
    8821539 * Description:                                                           *
     
    8961553 *    none                                                                *
    8971554 *                                                                        *
    898  **************************************************************************
    899  * Change History:                                                        *
    900  *  12/01/97 - Creation (JWJ)                                             *
    9011555 *************************************************************************/
    902 
    903 /* this is not prototyped in strict ansi mode */
    904 
    905 FILE *fdopen (int fildes, const char *mode);
    906 
    9071556static void
    908 rtems_ftpd_daemon()
    909 {
    910    int                 s;
    911    int                 s1;
    912    int                 addrLen;
    913    struct sockaddr_in  remoteAddr;
    914    struct sockaddr_in  localAddr;
    915    char                sessionID;
    916    rtems_task_priority priority;
    917    rtems_status_code   sc;
    918    rtems_id            tid;
    919    FTPD_SessionInfo_t  *info = NULL;
    920 
    921 
    922    sessionID = 'a';
    923 
    924    s = socket(AF_INET, SOCK_STREAM, 0);
    925    if (s < 0)
    926    {
    927       perror("Creating socket");
    928    }
    929 
    930    localAddr.sin_family      = AF_INET;
    931    localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
    932    localAddr.sin_addr.s_addr = INADDR_ANY;
    933    memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
    934    if (bind(s, (struct sockaddr *)&localAddr,
    935             sizeof(localAddr)) < 0)
    936    {
    937       perror("Binding control socket");
    938    }
    939 
    940    if (listen(s, 2) < 0)
    941    {
    942       perror("Listening on control socket");
    943    }
    944 
    945    while (1)
    946    {
    947       /********************************************************************
    948        * Allocate a SessionInfo structure for the session task.
    949        *******************************************************************/
    950       info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
    951       if (info == NULL)
     1557daemon()
     1558{
     1559  int                 s;
     1560  int                 addrLen;
     1561  struct sockaddr_in  remoteAddr;
     1562  struct sockaddr_in  localAddr;
     1563  char                sessionID;
     1564  FTPD_SessionInfo_t  *info = NULL;
     1565
     1566
     1567  sessionID = 'a';
     1568
     1569  s = socket(PF_INET, SOCK_STREAM, 0);
     1570  if (s < 0)
     1571    syslog(LOG_ERR, "ftpd: Error creating socket: %s", serr());
     1572
     1573  localAddr.sin_family      = AF_INET;
     1574  localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
     1575  localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
     1576  memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
     1577
     1578  if (0 > bind(s, (struct sockaddr *)&localAddr, sizeof(localAddr)))
     1579    syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());
     1580
     1581  if (0 > listen(s, 1))
     1582    syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());
     1583
     1584  while (1)
     1585  {
     1586    int ss;
     1587    addrLen = sizeof(remoteAddr);
     1588    ss = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
     1589    if (0 > ss)
     1590    {
     1591      syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());
     1592    }
     1593    else
     1594    {
     1595      info = task_pool_obtain();
     1596      if (NULL == info)
    9521597      {
    953          syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
    954          rtems_panic("Malloc fail.");
    955       }
    956 
    957       /********************************************************************
    958        * Accept on the socket and start the session task.
    959        *******************************************************************/
    960       addrLen = sizeof(remoteAddr);
    961       s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
    962       if (s1 < 0)
    963       {
    964          perror("Accepting control connection");
    965       }
    966 
    967       rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
    968       sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
    969                              priority, 8*1024,
    970                              RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
    971                              RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
    972                              RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
    973                              &tid);
    974       if (sc != RTEMS_SUCCESSFUL)
    975       {
    976          syslog(LOG_ERR, "ftpd: Could not create FTPD session: %s",
    977                 rtems_status_text(sc));
    978       }
    979 
    980       if (sessionID == 'z')
    981       {
    982          sessionID = 'a';
     1598        close_socket(ss);
    9831599      }
    9841600      else
    9851601      {
    986          sessionID++;
     1602        info->socket = ss;
     1603        if ((info->ctrl_fp = fdopen(info->socket, "r+")) == NULL)
     1604        {
     1605          syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());
     1606          close_stream(info);
     1607          task_pool_release(info);
     1608        }
     1609        else
     1610        {
     1611          /* Wakeup the session task. The task will call task_pool_release
     1612             after it closes connection. */
     1613          rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
     1614        }
    9871615      }
    988 
    989       /********************************************************************
    990        * Send the socket on to the new session.
    991        *******************************************************************/
    992       if ((info->ctrl_fp = fdopen(s1, "r+")) == NULL)
    993       {
    994          syslog(LOG_ERR, "ftpd: fdopen() on socket failed.");
    995          close(s1);
    996       }
    997       else
    998       {
    999          sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
    1000                                   (rtems_unsigned32)info);
    1001          sc = rtems_task_start(tid, rtems_ftpd_session, 0);
    1002          if (sc != RTEMS_SUCCESSFUL)
    1003          {
    1004             syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
    1005                    rtems_status_text(sc));
    1006          }
    1007       }
    1008    }
     1616    }
     1617  }
    10091618}
    10101619
     
    10221631 * Inputs:                                                                *
    10231632 *                                                                        *
    1024  *    rtems_task_priority priority - Priority to assign to this task.     *
    1025  *                                                                        *
    10261633 * Output:                                                                *
    10271634 *                                                                        *
    10281635 *    int - RTEMS_SUCCESSFUL on successful start of the daemon.           *
    10291636 *                                                                        *
    1030  **************************************************************************
    1031  * Change History:                                                        *
    1032  *  12/01/97 - Creation (JWJ)                                             *
    10331637 *************************************************************************/
    10341638int
    10351639rtems_initialize_ftpd()
    10361640{
    1037    rtems_status_code   sc;
    1038    rtems_id            tid;
    1039 
    1040 
    1041    if (rtems_ftpd_configuration.port == 0)
    1042    {
    1043       rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
    1044    }
    1045 
    1046    /***********************************************************************
    1047     * Default FTPD priority.
    1048     **********************************************************************/
    1049    if (rtems_ftpd_configuration.priority == 0)
    1050    {
    1051       rtems_ftpd_configuration.priority = 40;
    1052    }
    1053    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
    1054                           rtems_ftpd_configuration.priority, 8*1024,
    1055                           RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
    1056                           RTEMS_INTERRUPT_LEVEL(0),
    1057                           RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
    1058                           &tid);
    1059    if (sc != RTEMS_SUCCESSFUL)
    1060    {
    1061       syslog(LOG_ERR, "ftpd: Could not create FTP daemon: %s",
    1062              rtems_status_text(sc));
    1063       return(RTEMS_UNSATISFIED);
    1064    }
    1065 
    1066    sc = rtems_task_start(tid, rtems_ftpd_daemon, 0);
    1067    if (sc != RTEMS_SUCCESSFUL)
    1068    {
    1069       syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
    1070              rtems_status_text(sc));
    1071       return(RTEMS_UNSATISFIED);
    1072    }   
    1073 
    1074    syslog(LOG_INFO, "ftpd: FTP daemon started.");
    1075    return(RTEMS_SUCCESSFUL);
    1076 }
    1077 
     1641  rtems_status_code   sc;
     1642  rtems_id            tid;
     1643  rtems_task_priority priority;
     1644  int count;
     1645
     1646  if (rtems_ftpd_configuration.port == 0)
     1647  {
     1648    rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
     1649  }
     1650
     1651  if (rtems_ftpd_configuration.priority == 0)
     1652  {
     1653    rtems_ftpd_configuration.priority = 40;
     1654  }
     1655  priority = rtems_ftpd_configuration.priority;
     1656
     1657  if (rtems_ftpd_configuration.tasks_count <= 0)
     1658    rtems_ftpd_configuration.tasks_count = 1;
     1659  count = rtems_ftpd_configuration.tasks_count;
     1660
     1661  if (!task_pool_init(count, priority))
     1662  {
     1663    syslog(LOG_ERR, "ftpd: Could not initialize task pool.");
     1664    return RTEMS_UNSATISFIED;
     1665  }
     1666
     1667  sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
     1668    priority, FTPD_STACKSIZE,
     1669    RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
     1670    RTEMS_INTERRUPT_LEVEL(0),
     1671    RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
     1672    &tid);
     1673
     1674  if (sc == RTEMS_SUCCESSFUL)
     1675  {
     1676    sc = rtems_task_start(tid, daemon, 0);
     1677    if (sc != RTEMS_SUCCESSFUL)
     1678      rtems_task_delete(tid);
     1679  }
     1680
     1681  if (sc != RTEMS_SUCCESSFUL)
     1682  {
     1683    task_pool_done(count);
     1684    syslog(LOG_ERR, "ftpd: Could not create/start FTP daemon: %s",
     1685      rtems_status_text(sc));
     1686    return RTEMS_UNSATISFIED;
     1687  }
     1688
     1689  root[0] = '\0';
     1690  if (
     1691    rtems_ftpd_configuration.root &&
     1692    strlen(rtems_ftpd_configuration.root) < FTPD_BUFSIZE &&
     1693    rtems_ftpd_configuration.root[0] == '/')
     1694  {
     1695    strcpy(root, rtems_ftpd_configuration.root);
     1696    squeeze_path(root);
     1697    rtems_ftpd_configuration.root = root;
     1698  }
     1699
     1700  syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
     1701    count, ((count > 1) ? "s" : ""));
     1702  return RTEMS_SUCCESSFUL;
     1703}
  • c/src/libnetworking/rtems_servers/ftpd.h

    rd1941587 r3f777d0e  
    2626   int                     port;               /* Well-known port     */
    2727   struct rtems_ftpd_hook  *hooks;             /* List of hooks       */
     28   char const              *root;              /* Root for FTPD or 0 for / */
     29   int                     tasks_count;        /* Max. connections    */
    2830};
    2931
  • cpukit/ftpd/ftpd.c

    rd1941587 r3f777d0e  
    11/* FIXME: 1. Parse command is a hack.  We can do better.
    2  *        2. chdir is a hack.  We can do better.
    3  *        3. PWD doesn't work.
    4  *        4. Some sort of access control?
    5  *
    6  *  FTP Server Daemon
     2 *        2. Some sort of access control?
     3 *        3. OSV: Timeouts on sockets.
     4 *        4. OSV: hooks support seems to be a joke, as it requires storing of
     5 *           entire input file in memory. Seem to be better to change it to
     6 *           something more reasonable, like having
     7 *           'hook_write(void const *buf, int count)' routine that will be
     8 *           called multiple times while file is being received.
     9 *        5. OSV: Remove hack with "/dev/null"?
     10 *        6. OSV: data_addr field of SessionInfo structure is not initialized.
     11 *           This will lead to undefined behavior if FTP client doesn't send
     12 *           PORT command. Seems we need to implement usage of default port
     13 *           (ftp port - 1). I didn't find any FTP client that doesn't send
     14 *           PORT command though.
     15 *        7. OSV: while server claims TYPE ASCII command succeeds (to make
     16 *           happy some clients), ASCII mode isn't actually implemented.
     17 *        8. Passive Mode?
     18 *
     19 *  FTP Server Daemon
    720 *
    821 *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
     22 *
     23 *  Changed by:   Sergei Organov <osv@javad.ru> (OSV)
     24 *    Changes:
     25 *      - use pool of pre-created threads to handle sessions
     26 *      - LIST output now similar to what "/bin/ls -al" would output, thus
     27 *        FTP clients could parse it.
     28 *      - LIST NAME now works (both for files and directories)
     29 *      - keep track of CWD for every session separately
     30 *      - ability to specify root directory name in configuration table
     31 *      - options sent in commands are ignored, thus LIST -al FILE works
     32 *      - added support for NLST, CDUP and MDTM commands
     33 *      - buffers are allocated on stack instead of heap where possible
     34 *      - drop using of task notepad to pass parameters - use function
     35 *        arguments instead
     36 *      - various bug-fixes, e.g., use of PF_INET in socket() instead of
     37 *        AF_INET, use snprintf() instead of sprintf() everywhere for safety,
     38 *        etc.
    939 *
    1040 *  $Id$
     
    2555 *    Organization:                                                       *
    2656 *                                                                        *
    27  *       The FTP daemon is started upon boot.  It runs all the time       *
    28  *       and waits for connections on the known FTP port (21).  When      *
    29  *       a connection is made, it starts a 'session' task.  That          *
     57 *       The FTP daemon is started upon boot along with a (configurable)  *
     58 *       number of tasks to handle sessions. It runs all the time and     *
     59 *       waits for connections on the known FTP port (21).  When          *
     60 *       a connection is made, it wakeups a 'session' task.  That         *
    3061 *       session then interacts with the remote host.  When the session   *
    31  *       is complete, the session task deletes itself.  The daemon still  *
     62 *       is complete, the session task goes to sleep.  The daemon still   *
    3263 *       runs, however.                                                   *
    3364 *                                                                        *
     
    3869 * STOR xxx     - Receives a file from the client.  xxx = filename.       *
    3970 * LIST xxx     - Sends a file list to the client.                        *
    40  *                (LIST xxx isn't working yet...)                         *
     71 * NLST xxx     - Sends a file list to the client.                        *
    4172 * USER         - Does nothing.                                           *
    4273 * PASS         - Does nothing.                                           *
     
    4778 * PWD          - Print working directory.                                *
    4879 * CWD xxx      - Change working directory.                               *
     80 * CDUP         - Change to upper directory.                              *
    4981 * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
    5082 * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
    5183 *                      and port (x*256 + y).                             *
     84 * MDTM xxx     - Send date/time to the client. xxx = filename.           *
    5285 *                                                                        *
    5386 *                                                                        *
    5487 * The public routines contained in this file are:                        *
    5588 *                                                                        *
    56  *    rtems_initialize_ftpd_start - Starts the server daemon, then        *
    57  *                                  returns to its caller.                *
    58  *                                                                        *
    59  *                                                                        *
    60  * The private routines contained in this file are:                       *
    61  *                                                                        *
    62  *    rtems_ftpd_send_reply    - Sends a reply code and text through the  *
    63  *                               control port.                            *
    64  *    rtems_ftpd_command_retrieve - Performs to "RETR" command.           *
    65  *    rtems_ftpd_command_store - Performs the "STOR" command.             *
    66  *    rtems_ftpd_command_list  - Performs the "LIST" command.             *
    67  *    rtems_ftpd_command_port  - Opens a data port (the "PORT" command).  *
    68  *    rtems_ftpd_parse_command - Parses an incoming command.              *
    69  *    rtmes_ftpd_session       - Begins a service session.                *
    70  *    rtems_ftpd_daemon        - Listens on the FTP port for service      *
    71  *                               requests.                                *
     89 *    rtems_initialize_ftpd - Initializes and starts the server daemon,   *
     90 *                            then returns to its caller.                 *
     91 *                                                                        *
    7292 *------------------------------------------------------------------------*
    7393 * Jake Janovetz                                                          *
     
    7797 **************************************************************************
    7898 * Change History:                                                        *
    79  *  12/01/97 - Creation (JWJ)                                             *
     99 *  12/01/97   - Creation (JWJ)                                           *
     100 *  2001-01-08 - Changes by OSV                                           *
    80101 *************************************************************************/
    81 
    82 #include <stdio.h>
    83 #include <stdlib.h>
    84 #include <string.h>
    85 #include <unistd.h>
    86 #include <fcntl.h>
    87 #include <dirent.h>
    88 
    89 #include <rtems.h>
    90 #include <rtems/rtems_bsdnet.h>
    91 #include <rtems/error.h>
    92 #include <syslog.h>
    93 
    94 #include <sys/types.h>
    95 #include <sys/socket.h>
    96 #include <arpa/ftp.h>
    97 #include <netinet/in.h>
    98 
    99 #include "ftpd.h"
    100 
    101 
    102 extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
    103102
    104103/**************************************************************************
     
    115114 *         not take place, but the error condition is temporary so the
    116115 *         command can be reissued later.
    117  *  5yz    Permanent negative completion reply.  The command was not 
     116 *  5yz    Permanent negative completion reply.  The command was not
    118117 *         accepted and should not be retried.
    119118 *-------------------------------------------------------------------------
     
    128127 *************************************************************************/
    129128
    130 
    131 /**************************************************************************
    132  * SessionInfo structure.
    133  *
    134  * The following structure is allocated for each session.  The pointer
    135  * to this structure is contained in the tasks notepad entry.
    136  *************************************************************************/
    137 typedef struct
    138 {
    139    struct sockaddr_in  data_addr;   /* Data address for PORT commands */
    140    FILE                *ctrl_fp;    /* File pointer for control connection */
    141    char                cwd[255];    /* Current working directory */
    142                                     /* Login -- future use -- */
    143    int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
    144 } FTPD_SessionInfo_t;
    145 
    146 
    147 #define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.0-JWJ) ready."
     129#include <stdio.h>
     130#include <stdlib.h>
     131#include <string.h>
     132#include <unistd.h>
     133#include <fcntl.h>
     134#include <dirent.h>
     135#include <errno.h>
     136
     137#include <rtems.h>
     138#include <rtems/rtems_bsdnet.h>
     139#include <rtems/error.h>
     140#include <syslog.h>
     141
     142#include <sys/types.h>
     143#include <sys/socket.h>
     144#include <arpa/ftp.h>
     145#include <netinet/in.h>
     146
     147#include "ftpd.h"
     148
     149
     150#ifdef __GNUC__
     151/* change to #if 1 to disable syslog entirely */
     152#if 0
     153#undef  syslog
     154#define syslog(a, b, ...) while(0){}
     155#endif
     156#endif
     157
     158#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.1-JWJ) ready."
     159
     160#define FTPD_SYSTYPE "RTEMS"
     161
     162/* Seem to be unused */
     163#if 0
    148164#define FTPD_WELCOME_MESSAGE \
    149165   "Welcome to the RTEMS FTP server.\n" \
    150166   "\n" \
    151167   "Login accepted.\n"
     168#endif
     169
     170/* Various buffer sizes */
     171enum
     172{
     173  FTPD_BUFSIZE  = 256,       /* Size for temporary buffers */
     174  FTPD_DATASIZE = 1024,      /* Size for file transfer buffers */
     175  FTPD_STACKSIZE = 8 * 1024, /* Tasks stack size */
     176};
     177
     178/* Event to be used by session tasks for waiting */
     179enum
     180{
     181  FTPD_RTEMS_EVENT = RTEMS_EVENT_1
     182};
     183
     184/* Configuration table */
     185extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
     186
     187/* this is not prototyped in strict ansi mode */
     188FILE *fdopen (int fildes, const char *mode);
     189
     190/*
     191 * SessionInfo structure.
     192 *
     193 * The following structure is allocated for each session.
     194 */
     195typedef struct
     196{
     197  struct sockaddr_in  data_addr;   /* Data address for PORT commands */
     198  FILE                *ctrl_fp;    /* File pointer for control connection */
     199  int                 socket;      /* Socket descriptor for ctrl connection */
     200  char                cwd[FTPD_BUFSIZE];  /* Current working directory */
     201  /* Login -- future use -- */
     202  int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
     203  rtems_id            tid;         /* Task id */
     204} FTPD_SessionInfo_t;
     205
     206
     207/*
     208 * TaskPool structure.
     209 */
     210typedef struct
     211{
     212  FTPD_SessionInfo_t    *info;
     213  FTPD_SessionInfo_t    **queue;
     214  int                   count;
     215  int                   head;
     216  int                   tail;
     217  rtems_id              mutex;
     218  rtems_id              sem;
     219} FTPD_TaskPool_t;
     220
     221/*
     222 * Task pool instance.
     223 */
     224static FTPD_TaskPool_t task_pool;
     225
     226/*
     227 * Root node for FTPD without trailing slash. Even '/' node is denoted as
     228 * empty string here.
     229 */
     230static char root[FTPD_BUFSIZE];
     231
     232
     233/*PAGE
     234 *
     235 * serr
     236 *
     237 * Return errror string corresponding to current 'errno'.
     238 *
     239 */
     240static char const*
     241serr(void)
     242{
     243  return strerror(errno);
     244}
     245
     246
     247/*
     248 *  Utility routines to manage root directory and session local
     249 *  current working directory.
     250 */
     251
     252
     253/*PAGE
     254 *
     255 * squeeze_path
     256 *
     257 * Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
     258 * from the path. Does nothing if the path is relative, i.e. doesn't begin
     259 * with '/'. The trailing slash is always removed, even when alone, i.e. "/"
     260 * will be "" after squeeze.
     261 *
     262 * Input parameters:
     263 *   path - the path to be squeezed
     264 *
     265 * Output parameters:
     266 *   path - squeezed path
     267 *
     268 */
     269static void
     270squeeze_path(char* path)
     271{
     272  if(path[0] == '/')
     273  {
     274    char* e = path + 1;
     275    int rest = strlen(e);
     276    while(rest >= 0)
     277    {
     278      int len;
     279      char* s = e;
     280      e = strchr(s, '/');
     281      if(e)
     282        ++e;
     283      else
     284        e = s + rest + 1;
     285      len = e - s;
     286      rest -= len;
     287      if(len == 1 || (len == 2 && s[0] == '.'))
     288      {
     289        if(rest >= 0)
     290          memmove(s, e, rest + 1);
     291        else
     292          *s++ = '\0';
     293        e = s;
     294      }
     295      else if(len == 3 && s[0] == '.' && s[1] == '.')
     296      {
     297        char* ps = s;
     298        if(ps - 1 > path) {
     299          do
     300            --ps;
     301          while(ps[-1] != '/');
     302        }
     303        if(rest >= 0)
     304          memmove(ps, e, rest + 1);
     305        else
     306          *ps++ = '\0';
     307        e = ps;
     308      }
     309    }
     310    if(e[-2] == '/')
     311      e[-2] = '\0';
     312  }
     313}
     314
     315
     316/*PAGE
     317 *
     318 * make_path
     319 *
     320 * Makes full path given file name, current working directory and root
     321 * directory (file scope variable 'root').
     322 *
     323 * Input parameters:
     324 *   cwd  - current working directory
     325 *   name - file name
     326 *   root (file scope variable) - FTPD root directory
     327 *
     328 * Output parameters:
     329 *   buf - full path
     330 *   returns pointer to non-root part of the 'buf', i.e. to first character
     331 *           different from '/' after root part.
     332 *
     333 */
     334static char const*
     335make_path(char* buf, char const* cwd, char const* name)
     336{
     337  char* res = NULL;
     338
     339  int rlen = strlen(root);
     340  int clen = strlen(cwd);
     341  int nlen = strlen(name);
     342  int len = rlen + nlen;
     343
     344  if (name[0] != '/')
     345  {
     346    ++len;
     347    if (clen > 0)
     348      len += clen + 1;
     349  }
     350
     351  if (FTPD_BUFSIZE > len)
     352  {
     353    char* b = buf;
     354    memcpy(b, root, rlen); b += rlen;
     355    if (name[0] != '/')
     356    {
     357      *b++ = '/';
     358      if (clen > 0)
     359      {
     360        memcpy(b, cwd, clen); b += clen;
     361        *b++ = '/';
     362      }
     363    }
     364    memcpy(b, name, nlen); b += nlen;
     365    *b = '\0';
     366
     367    res = buf + rlen;
     368    while(rlen-- > 0 && res[-1] == '/')
     369      --res;
     370    squeeze_path(res);
     371  }
     372
     373  return res;
     374}
     375
     376/*
     377 * Task pool management routines
     378 */
     379
     380
     381/*PAGE
     382 *
     383 * task_pool_done
     384 *
     385 * Cleanup task pool.
     386 *
     387 * Input parameters:
     388 *   count - number of entries in task pool to cleanup
     389 *
     390 * Output parameters:
     391 *   NONE
     392 *
     393 */
     394static void
     395task_pool_done(int count)
     396{
     397  int i;
     398  for(i = 0; i < count; ++i)
     399    rtems_task_delete(task_pool.info[i].tid);
     400  if(task_pool.info)
     401    free(task_pool.info);
     402  if(task_pool.queue)
     403    free(task_pool.queue);
     404  if(task_pool.mutex != (rtems_id)-1)
     405    rtems_semaphore_delete(task_pool.mutex);
     406  if(task_pool.sem != (rtems_id)-1)
     407    rtems_semaphore_delete(task_pool.sem);
     408  task_pool.info = 0;
     409  task_pool.queue = 0;
     410  task_pool.count = 0;
     411  task_pool.sem = -1;
     412  task_pool.mutex = -1;
     413}
     414
     415/* Forward declare */
     416static void session(rtems_task_argument arg);
     417
     418/*PAGE
     419 *
     420 * task_pool_init
     421 *
     422 * Initialize task pool.
     423 *
     424 * Input parameters:
     425 *   count    - number of entries in task pool to create
     426 *   priority - priority tasks are started with
     427 *
     428 * Output parameters:
     429 *   returns 1 on success, 0 on failure.
     430 *
     431 */
     432static int
     433task_pool_init(int count, rtems_task_priority priority)
     434{
     435  int i;
     436  rtems_status_code sc;
     437  char id = 'a';
     438
     439  task_pool.count = 0;
     440  task_pool.head = task_pool.tail = 0;
     441  task_pool.mutex = (rtems_id)-1;
     442  task_pool.sem   = (rtems_id)-1;
     443
     444  sc = rtems_semaphore_create(
     445    rtems_build_name('F', 'T', 'P', 'M'),
     446    1,
     447    RTEMS_DEFAULT_ATTRIBUTES
     448    | RTEMS_BINARY_SEMAPHORE
     449    | RTEMS_INHERIT_PRIORITY
     450    | RTEMS_PRIORITY,
     451    RTEMS_NO_PRIORITY,
     452    &task_pool.mutex);
     453
     454  if(sc == RTEMS_SUCCESSFUL)
     455    sc = rtems_semaphore_create(
     456      rtems_build_name('F', 'T', 'P', 'S'),
     457      count,
     458      RTEMS_DEFAULT_ATTRIBUTES,
     459      RTEMS_NO_PRIORITY,
     460      &task_pool.sem);
     461
     462  if(sc != RTEMS_SUCCESSFUL) {
     463    task_pool_done(0);
     464    syslog(LOG_ERR, "ftpd: Can not create semaphores");
     465    return 0;
     466  }
     467
     468  task_pool.info = (FTPD_SessionInfo_t*)
     469    malloc(sizeof(FTPD_SessionInfo_t) * count);
     470  task_pool.queue = (FTPD_SessionInfo_t**)
     471    malloc(sizeof(FTPD_SessionInfo_t*) * count);
     472  if (NULL == task_pool.info || NULL == task_pool.queue)
     473  {
     474    task_pool_done(0);
     475    syslog(LOG_ERR, "ftpd: Not enough memory");
     476    return 0;
     477  }
     478
     479  for(i = 0; i < count; ++i)
     480  {
     481    FTPD_SessionInfo_t *info = &task_pool.info[i];
     482    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', id),
     483      priority, FTPD_STACKSIZE,
     484      RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
     485      RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
     486      RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
     487      &info->tid);
     488    if (sc == RTEMS_SUCCESSFUL)
     489    {
     490      sc = rtems_task_start(
     491        info->tid, session, (rtems_task_argument)info);
     492      if (sc != RTEMS_SUCCESSFUL)
     493        task_pool_done(i);
     494    }
     495    else
     496      task_pool_done(i + 1);
     497    if (sc != RTEMS_SUCCESSFUL)
     498    {
     499      syslog(LOG_ERR, "ftpd: Could not create/start FTPD session: %s",
     500        rtems_status_text(sc));
     501      return 0;
     502    }
     503    task_pool.queue[i] = task_pool.info + i;
     504    info->ctrl_fp = NULL;
     505    info->socket  = -1;
     506    if (++id > 'z')
     507      id = 'a';
     508  }
     509  task_pool.count = count;
     510  return 1;
     511}
     512
     513/*PAGE
     514 *
     515 * task_pool_obtain
     516 *
     517 * Obtain free task from task pool.
     518 *
     519 * Input parameters:
     520 *   NONE
     521 *
     522 * Output parameters:
     523 *   returns pointer to the corresponding SessionInfo structure on success,
     524 *           NULL if there are no free tasks in the pool.
     525 *
     526 */
     527static FTPD_SessionInfo_t*
     528task_pool_obtain()
     529{
     530  FTPD_SessionInfo_t* info = 0;
     531  rtems_status_code sc;
     532  sc = rtems_semaphore_obtain(task_pool.sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT);
     533  if (sc == RTEMS_SUCCESSFUL)
     534  {
     535    rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
     536    info = task_pool.queue[task_pool.head];
     537    if(++task_pool.head >= task_pool.count)
     538      task_pool.head = 0;
     539    info->socket = -1;
     540    info->ctrl_fp = NULL;
     541    rtems_semaphore_release(task_pool.mutex);
     542  }
     543  return info;
     544}
     545
     546/*PAGE
     547 *
     548 * task_pool_release
     549 *
     550 * Return task obtained by 'obtain()' back to the task pool.
     551 *
     552 * Input parameters:
     553 *   info  - pointer to corresponding SessionInfo structure.
     554 *
     555 * Output parameters:
     556 *   NONE
     557 *
     558 */
     559static void
     560task_pool_release(FTPD_SessionInfo_t* info)
     561{
     562  rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
     563  task_pool.queue[task_pool.tail] = info;
     564  if(++task_pool.tail >= task_pool.count)
     565    task_pool.tail = 0;
     566  rtems_semaphore_release(task_pool.mutex);
     567  rtems_semaphore_release(task_pool.sem);
     568}
     569
     570/*
     571 * End of task pool routines
     572 */
     573
     574/*PAGE
     575 *
     576 * close_socket
     577 *
     578 * Close socket.
     579 *
     580 * Input parameters:
     581 *   s - socket descriptor to be closed.
     582 *
     583 * Output parameters:
     584 *   returns 1 on success, 0 on failure
     585 *
     586 */
     587static int
     588close_socket(int s)
     589{
     590  if (0 <= s)
     591  {
     592    if (0 != close(s))
     593    {
     594      shutdown(s, 2);
     595      if (0 != close(s))
     596        return 0;
     597    }
     598  }
     599  return 1;
     600}
     601
     602/*PAGE
     603 *
     604 * close_stream
     605 *
     606 * Close data stream of session.
     607 *
     608 * Input parameters:
     609 *   info - corresponding SessionInfo structure
     610 *
     611 * Output parameters:
     612 *   NONE
     613 *
     614 */
     615static void
     616close_stream(FTPD_SessionInfo_t* info)
     617{
     618  if (NULL != info->ctrl_fp)
     619  {
     620    if (0 != fclose(info->ctrl_fp))
     621    {
     622      syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
     623    }
     624    else
     625      info->socket = -1;
     626  }
     627
     628  if (!close_socket(info->socket))
     629    syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
     630
     631  info->ctrl_fp = NULL;
     632  info->socket = -1;
     633}
    152634
    153635
    154636/**************************************************************************
    155  * Function: rtems_ftpd_send_reply                                        *
     637 * Function: send_reply                                                   *
    156638 **************************************************************************
    157639 * Description:                                                           *
     
    170652 *    none                                                                *
    171653 *                                                                        *
    172  **************************************************************************
    173  * Change History:                                                        *
    174  *  12/01/97 - Creation (JWJ)                                             *
    175654 *************************************************************************/
    176655static void
    177 rtems_ftpd_send_reply(int code, char *text)
    178 {
    179    rtems_status_code   sc;
    180    FTPD_SessionInfo_t  *info = NULL;
    181    char                str[80];
    182 
    183 
    184    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    185                             (rtems_unsigned32 *)&info);
    186 
    187    /***********************************************************************
    188     * code must be a 3-digit number.
    189     **********************************************************************/
    190    if ((code < 100) || (code > 999))
    191    {
    192       syslog(LOG_ERR, "ftpd: Code not 3-digits.");
    193       return;
    194    }
    195 
    196    /***********************************************************************
    197     * If a text reply exists, add it to the reply data.
    198     **********************************************************************/
    199    if (text != NULL)
    200    {
    201       sprintf(str, "%d %.70s\r\n", code, text);
    202       fprintf(info->ctrl_fp, "%d %.70s\r\n", code, text);
    203    }
    204    else
    205    {
    206       sprintf(str, "%d\r\n", code);
    207       fprintf(info->ctrl_fp, "%d\r\n", code);
    208    }
    209    fflush(info->ctrl_fp);
    210 }
    211 
     656send_reply(FTPD_SessionInfo_t  *info, int code, char *text)
     657{
     658  char const* s;
     659
     660
     661  /***********************************************************************
     662   * code must be a 3-digit number.
     663   **********************************************************************/
     664  if ((code < 100) || (code > 999))
     665  {
     666    syslog(LOG_ERR, "ftpd: Code not 3-digits.");
     667    return;
     668  }
     669
     670  /***********************************************************************
     671   * If a text reply exists, add it to the reply data.
     672   **********************************************************************/
     673  s  = (info->xfer_mode == TYPE_A) ? "\r" : "";
     674  if (text != NULL)
     675    fprintf(info->ctrl_fp, "%d %.70s%s\n", code, text, s);
     676  else
     677    fprintf(info->ctrl_fp, "%d%s\n", code, s);
     678  fflush(info->ctrl_fp);
     679}
     680
     681
     682/*PAGE
     683 *
     684 * send_mode_reply
     685 *
     686 * Sends BINARY/ASCII reply string depending on current transfer mode.
     687 *
     688 * Input parameters:
     689 *   info - corresponding SessionInfo structure
     690 *
     691 * Output parameters:
     692 *   NONE
     693 *
     694 */
     695static void
     696send_mode_reply(FTPD_SessionInfo_t *info)
     697{
     698  if(info->xfer_mode == TYPE_I)
     699    send_reply(info, 150, "Opening BINARY mode data connection.");
     700  else
     701    send_reply(info, 150, "Opening ASCII mode data connection.");
     702}
     703
     704/*PAGE
     705 *
     706 * data_socket
     707 *
     708 * Create data socket for session.
     709 *
     710 * Input parameters:
     711 *   info - corresponding SessionInfo structure
     712 *
     713 * Output parameters:
     714 *   returns socket descriptor, or -1 if failure
     715 *
     716 */
     717static int
     718data_socket(FTPD_SessionInfo_t *info)
     719{
     720  int s = socket(PF_INET, SOCK_STREAM, 0);
     721  if(0 > s)
     722    send_reply(info, 420, "Server error - could not create socket.");
     723  else if(0 > connect(s, (struct sockaddr *)&info->data_addr,
     724    sizeof(struct sockaddr)))
     725  {
     726    send_reply(info, 420, "Server error - could not connect socket.");
     727    close_socket(s);
     728    s = -1;
     729  }
     730  return s;
     731}
    212732
    213733/**************************************************************************
    214  * Function: rtems_ftpd_command_retrieve                                  *
     734 * Function: command_retrieve                                             *
    215735 **************************************************************************
    216736 * Description:                                                           *
     
    230750 *           1 for no reply sent.                                         *
    231751 *                                                                        *
    232  **************************************************************************
    233  * Change History:                                                        *
    234  *  04/29/98 - Creation (JWJ)                                             *
    235752 *************************************************************************/
    236753static int
    237 rtems_ftpd_command_retrieve(char *filename)
    238 {
    239    int                 s;
    240    int                 n;
    241    int                 fd;
    242    unsigned char       *bufr;
    243    rtems_status_code   sc;
    244    FTPD_SessionInfo_t  *info = NULL;
    245 
    246 
    247    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    248                             (rtems_unsigned32 *)&info);
    249 
    250    if ((fd = open(filename, O_RDONLY)) == -1)
    251    {
    252       rtems_ftpd_send_reply(450, "Error opening file.");
    253       return(0);
    254    }
    255 
    256    bufr = (unsigned char *)malloc(BUFSIZ);
    257    if (bufr == NULL)
    258    {
    259       rtems_ftpd_send_reply(440, "Server error - malloc fail.");
    260       close(fd);
    261       return(0);
    262    }
    263 
    264    /***********************************************************************
    265     * Connect to the data connection (PORT made in an earlier PORT call).
    266     **********************************************************************/
    267    rtems_ftpd_send_reply(150, "BINARY data connection.");
    268    s = socket(AF_INET, SOCK_STREAM, 0);
    269    if (connect(s, (struct sockaddr *)&info->data_addr,
    270                sizeof(struct sockaddr)) < 0)
    271    {
    272       rtems_ftpd_send_reply(420, "Server error - could not connect socket.");
    273       free(bufr);
    274       close(fd);
    275       close(s);
    276       return(1);
    277    }
    278 
    279    /***********************************************************************
    280     * Send the data over the ether.
    281     **********************************************************************/
    282    while ((n = read(fd, bufr, BUFSIZ)) > 0)
    283    {
    284       send(s, bufr, n, 0);
    285       bufr[n-1] = '\0';
    286    }
    287 
    288    if (n == 0)
    289    {
    290       rtems_ftpd_send_reply(210, "File sent successfully.");
    291    }
    292    else
    293    {
    294       rtems_ftpd_send_reply(450, "Retrieve failed.");
    295    }
    296 
    297    if (close(s) != 0)
    298    {
    299       syslog(LOG_ERR, "ftpd: Error closing data socket");
    300    }
    301 
    302    free(bufr);
    303    close(fd);
    304    return(0);
     754command_retrieve(FTPD_SessionInfo_t  *info, char *filename)
     755{
     756  int                 s = -1;
     757  int                 n;
     758  int                 fd = -1;
     759  char                buf[FTPD_DATASIZE];
     760  int                 res = 0;
     761
     762  char const* r = make_path(buf, info->cwd, filename);
     763
     764  if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
     765  {
     766    send_reply(info, 450, "Error opening file.");
     767    return(res);
     768  }
     769
     770  send_mode_reply(info);
     771
     772  /***********************************************************************
     773   * Connect to the data connection (PORT made in an earlier PORT call).
     774   **********************************************************************/
     775  s = data_socket(info);
     776  if (0 <= s)
     777  {
     778    /***********************************************************************
     779     * Send the data over the ether.
     780     **********************************************************************/
     781    while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
     782    {
     783      send(s, buf, n, 0);
     784    }
     785
     786    if (0 == n)
     787    {
     788      if (0 == close(fd))
     789      {
     790        fd = -1;
     791        res = 1;
     792      }
     793    }
     794  }
     795
     796  if (0 == res)
     797    send_reply(info, 450, "Retrieve failed.");
     798  else
     799    send_reply(info, 210, "File sent successfully.");
     800
     801  if (-1 != fd)
     802    close(fd);
     803
     804  if (!close_socket(s))
     805    syslog(LOG_ERR, "ftpd: Error closing data socket");
     806
     807  return(res);
    305808}
    306809
    307810
    308811/**************************************************************************
    309  * Function: rtems_ftpd_command_store                                     *
     812 * Function: command_store                                                *
    310813 **************************************************************************
    311814 * Description:                                                           *
     
    325828 *           1 for failure.                                               *
    326829 *                                                                        *
    327  **************************************************************************
    328  * Change History:                                                        *
    329  *  12/01/97 - Creation (JWJ)                                             *
    330830 *************************************************************************/
    331831static int
    332 rtems_ftpd_command_store(char *filename)
    333 {
    334    char                   *bufr;
    335    int                    s;
    336    int                    n;
    337    unsigned long          size = 0;
    338    rtems_status_code      sc;
    339    FTPD_SessionInfo_t     *info = NULL;
    340    struct rtems_ftpd_hook *usehook = NULL;
    341 
    342 
    343    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    344                             (rtems_unsigned32 *)&info);
    345 
    346    bufr = (char *)malloc(BUFSIZ * sizeof(char));
    347    if (bufr == NULL)
    348    {
    349       rtems_ftpd_send_reply(440, "Server error - malloc fail.");
     832command_store(FTPD_SessionInfo_t *info, char *filename)
     833{
     834  int                    s;
     835  int                    n;
     836  unsigned long          size = 0;
     837  struct rtems_ftpd_hook *usehook = NULL;
     838  char                   buf[FTPD_DATASIZE];
     839
     840  int null = !strcmp("/dev/null", filename);
     841
     842  if(!null)
     843  {
     844    if (NULL == make_path(buf, info->cwd, filename))
     845    {
     846      send_reply(info, 450, "Error creating file.");
     847      return(0);
     848    }
     849  }
     850
     851  send_mode_reply(info);
     852
     853  s = data_socket(info);
     854  if(0 > s)
     855  {
     856    return(1);
     857  }
     858
     859
     860  /***********************************************************************
     861   * File: "/dev/null" just throws the data away.
     862   * Otherwise, search our list of hooks to see if we need to do something
     863   *   special.
     864   * OSV: FIXME: this is hack. Using /dev/null filesystem entry would be
     865   *             better. However, it's not clear how to handle root directory
     866   *             other than '/' then.
     867   **********************************************************************/
     868  if (null)
     869  {
     870    while ((n = read(s, buf, FTPD_DATASIZE)) > 0);
     871  }
     872  else if (rtems_ftpd_configuration.hooks != NULL)
     873  {
     874    struct rtems_ftpd_hook *hook;
     875    int i;
     876
     877    i = 0;
     878    hook = &rtems_ftpd_configuration.hooks[i++];
     879    while (hook->filename != NULL)
     880    {
     881      if (!strcmp(hook->filename, buf))
     882      {
     883        usehook = hook;
     884        break;
     885      }
     886      hook = &rtems_ftpd_configuration.hooks[i++];
     887    }
     888  }
     889
     890  if (usehook != NULL)
     891  {
     892    /*
     893     * OSV: FIXME: Small buffer could be used and hook routine
     894     * called multiple times instead. Alternatively, the support could be
     895     * removed entirely in favor of configuring RTEMS pseudo-device with
     896     * given name.
     897     */
     898
     899    char                *bigBufr;
     900    size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1;
     901
     902    /***********************************************************************
     903     * Allocate space for our "file".
     904     **********************************************************************/
     905    bigBufr = (char *)malloc(filesize);
     906    if (bigBufr == NULL)
     907    {
     908      send_reply(info, 440, "Server error - malloc fail.");
    350909      return(1);
    351    }
    352 
    353    rtems_ftpd_send_reply(150, "BINARY data connection.");
    354 
    355    s = socket(AF_INET, SOCK_STREAM, 0);
    356    if (connect(s, (struct sockaddr *)&info->data_addr,
    357                sizeof(struct sockaddr)) < 0)
    358    {
    359       free(bufr);
    360       close(s);
     910    }
     911
     912    /***********************************************************************
     913     * Retrieve the file into our buffer space.
     914     **********************************************************************/
     915    size = 0;
     916    while ((n = read(s, bigBufr + size, filesize - size)) > 0)
     917    {
     918      size += n;
     919    }
     920    if (size >= filesize)
     921    {
     922      send_reply(info, 440, "Server error - Buffer size exceeded.");
     923      free(bigBufr);
     924      close_socket(s);
    361925      return(1);
    362    }
    363 
    364 
    365    /***********************************************************************
    366     * File: "/dev/null" just throws the data away.
    367     * Otherwise, search our list of hooks to see if we need to do something
    368     *   special.
    369     **********************************************************************/
    370    if (!strncmp("/dev/null", filename, 9))
    371    {
    372       while ((n = read(s, bufr, BUFSIZ)) > 0);
    373    }
    374    else if (rtems_ftpd_configuration.hooks != NULL)
    375    {
    376       struct rtems_ftpd_hook *hook;
    377       int i;
    378 
    379       i = 0;
    380       hook = &rtems_ftpd_configuration.hooks[i++];
    381       while (hook->filename != NULL)
     926    }
     927    close_socket(s);
     928
     929    /***********************************************************************
     930     * Call our hook.
     931     **********************************************************************/
     932    if ((usehook->hook_function)(bigBufr, size) == 0)
     933    {
     934      send_reply(info, 210, "File transferred successfully.");
     935    }
     936    else
     937    {
     938      send_reply(info, 440, "File transfer failed.");
     939    }
     940    free(bigBufr);
     941  }
     942  else
     943  {
     944    int fd =
     945      creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
     946
     947    if (0 > fd)
     948    {
     949      send_reply(info, 450, "Error creating file.");
     950      close_socket(s);
     951      return(0);
     952    }
     953
     954    while ((n = read(s, buf, FTPD_DATASIZE)) > 0)
     955    {
     956      if (0 > write(fd, buf, n))
    382957      {
    383          if (!strcmp(hook->filename, filename))
    384          {
    385             usehook = hook;
    386             break;
    387          }
    388          hook = &rtems_ftpd_configuration.hooks[i++];
     958        send_reply(info, 450, "Error during write.");
     959        close(fd);
     960        close_socket(s);
     961        return(1);
    389962      }
    390    }
    391 
    392    if (usehook != NULL)
    393    {
    394       char                *bigBufr;
    395 
    396       /***********************************************************************
    397        * Allocate space for our "file".
    398        **********************************************************************/
    399       bigBufr = (char *)malloc(
    400                   rtems_ftpd_configuration.max_hook_filesize * sizeof(char));
    401       if (bigBufr == NULL)
     963    }
     964    if (0 > close(fd))
     965    {
     966      send_reply(info, 450, "Error during write.");
     967      close_socket(s);
     968      return(1);
     969    }
     970    close_socket(s);
     971    send_reply(info, 226, "Transfer complete.");
     972  }
     973
     974  return(0);
     975}
     976
     977
     978/*PAGE
     979 *
     980 * send_dirline
     981 *
     982 * Sends one line of LIST command reply corresponding to single file.
     983 *
     984 * Input parameters:
     985 *   s - socket descriptor to send data to
     986 *   wide - if 0, send only file name. If not 0, send 'stat' info as well in
     987 *          "ls -l" format.
     988 *   curTime - current time
     989 *   path - path to be prepended to what is given by 'add'
     990 *   add  - path to be appended to what is given by 'path', the resulting path
     991 *          is then passed to 'stat()' routine
     992 *   name - file name to be reported in output
     993 *   buf  - buffer for temporary data
     994 *
     995 * Output parameters:
     996 *   NONE
     997 *
     998 */
     999static void
     1000send_dirline(int s, int wide, time_t curTime, char const* path,
     1001  char const* add, char const* fname, char* buf)
     1002{
     1003  if(wide)
     1004  {
     1005    struct stat stat_buf;
     1006
     1007    int plen = strlen(path);
     1008    int alen = strlen(add);
     1009    if(plen == 0)
     1010    {
     1011      buf[plen++] = '/';
     1012      buf[plen] = '\0';
     1013    }
     1014    else
     1015    {
     1016      strcpy(buf, path);
     1017      if(alen > 0 && buf[plen - 1] != '/')
    4021018      {
    403          rtems_ftpd_send_reply(440, "Server error - malloc fail.");
    404          free(bufr);
    405          return(1);
     1019        buf[plen++] = '/';
     1020        if(plen >= FTPD_BUFSIZE)
     1021          return;
     1022        buf[plen] = '\0';
    4061023      }
    407 
    408       /***********************************************************************
    409        * Retrieve the file into our buffer space.
    410        **********************************************************************/
    411       size = 0;
    412       while ((n = read(s, bufr, BUFSIZ)) > 0)
    413       {
    414          if (size + n >
    415                rtems_ftpd_configuration.max_hook_filesize * sizeof(char))
    416          {
    417             rtems_ftpd_send_reply(440, "Server error - Buffer size exceeded.");
    418             free(bufr);
    419             free(bigBufr);
    420             close(s);
    421             return(1);
    422          }
    423          memcpy(&bigBufr[size], bufr, n);
    424          size += n;
    425       }
    426       close(s);
    427 
    428       /***********************************************************************
    429        * Call our hook.
    430        **********************************************************************/
    431       if ((usehook->hook_function)(bigBufr, size) == 0)
    432       {
    433          rtems_ftpd_send_reply(210, "File transferred successfully.");
    434       }
     1024    }
     1025    if(plen + alen >= FTPD_BUFSIZE)
     1026      return;
     1027    strcpy(buf + plen, add);
     1028
     1029    if (stat(buf, &stat_buf) == 0)
     1030    {
     1031      int len;
     1032      struct tm bt;
     1033      time_t tf = stat_buf.st_mtime;
     1034      enum { SIZE = 80 };
     1035      enum { SIX_MONTHS = 365*24*60*60/2 };
     1036      char timeBuf[SIZE];
     1037      gmtime_r(&tf, &bt);
     1038      if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS)
     1039        strftime (timeBuf, SIZE, "%b %d  %Y", &bt);
    4351040      else
    436       {
    437          rtems_ftpd_send_reply(440, "File transfer failed.");
    438       }
    439       free(bigBufr);
    440    }
    441    else
    442    {
    443       int    fd;
    444       size_t written;
    445 
    446       fd = creat(filename, S_IRUSR | S_IWUSR |
    447                            S_IRGRP | S_IWGRP |
    448                            S_IROTH | S_IWOTH);
    449       if (fd == -1)
    450       {
    451          rtems_ftpd_send_reply(450, "Could not open file.");
    452          close(s);
    453          free(bufr);
    454          return(1);
    455       }
    456       while ((n = read(s, bufr, BUFSIZ)) > 0)
    457       {
    458          written = write(fd, bufr, n);
    459          if (written == -1)
    460          {
    461             rtems_ftpd_send_reply(450, "Error during write.");
    462             close(fd);
    463             close(s);
    464             free(bufr);
    465             return(1);
    466          }
    467       }
    468       close(fd);
    469       close(s);
    470       rtems_ftpd_send_reply(226, "Transfer complete.");
    471    }
    472 
    473    free(bufr);
    474    return(0);
    475 }
    476 
     1041        strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);
     1042
     1043      len = snprintf(buf, FTPD_BUFSIZE,
     1044        "%c%c%c%c%c%c%c%c%c%c  1 %5d %5d %11u %s %s\r\n",
     1045        (S_ISLNK(stat_buf.st_mode)?('l'):
     1046          (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
     1047        (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
     1048        (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
     1049        (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
     1050        (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
     1051        (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
     1052        (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
     1053        (stat_buf.st_mode & S_IROTH)?('r'):('-'),
     1054        (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
     1055        (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
     1056        (int)stat_buf.st_uid,
     1057        (int)stat_buf.st_gid,
     1058        (int)stat_buf.st_size,
     1059        timeBuf,
     1060        fname
     1061      );
     1062
     1063      send(s, buf, len, 0);
     1064    }
     1065  }
     1066  else
     1067  {
     1068    int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
     1069    send(s, buf, len, 0);
     1070  }
     1071}
    4771072
    4781073/**************************************************************************
    479  * Function: rtems_ftpd_command_list                                      *
     1074 * Function: command_list                                      *
    4801075 **************************************************************************
    4811076 * Description:                                                           *
     
    4931088 *    none                                                                *
    4941089 *                                                                        *
    495  **************************************************************************
    496  * Change History:                                                        *
    497  *  12/01/97 - Creation (JWJ)                                             *
    4981090 *************************************************************************/
    4991091static void
    500 rtems_ftpd_command_list(char *fname)
    501 {
    502    int                 s;
    503    rtems_status_code   sc;
    504    FTPD_SessionInfo_t  *info = NULL;
    505    DIR                 *dirp;
    506    struct dirent       *dp;
    507    char                dirline[255];
    508    struct stat         stat_buf;
    509 
    510 
    511    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    512                             (rtems_unsigned32 *)&info);
    513 
    514    rtems_ftpd_send_reply(150, "ASCII data connection for LIST.");
    515 
    516    s = socket(AF_INET, SOCK_STREAM, 0);
    517    if (connect(s, (struct sockaddr *)&info->data_addr,
    518                sizeof(struct sockaddr)) < 0)
    519    {
    520       syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
    521       return;
    522    }
    523 
    524    if ((dirp = opendir(fname)) == NULL)
    525    {
    526       sprintf(dirline, "%s: No such file or directory.%s\n",
    527               fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
    528       send(s, dirline, strlen(dirline), 0);
    529       close(s);
    530       rtems_ftpd_send_reply(226, "Transfer complete.");
    531       return;
    532    }
    533    while ((dp = readdir(dirp)) != NULL)
    534    {
    535       if (stat(dp->d_name, &stat_buf) == 0)
    536       {
    537          sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c  %5d %5d %11d  %s%s\n",
    538                  (S_ISLNK(stat_buf.st_mode)?('l'):
    539                     (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
    540                  (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
    541                  (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
    542                  (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
    543                  (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
    544                  (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
    545                  (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
    546                  (stat_buf.st_mode & S_IROTH)?('r'):('-'),
    547                  (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
    548                  (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
    549                  (int)stat_buf.st_uid,
    550                  (int)stat_buf.st_gid,
    551                  (int)stat_buf.st_size,
    552                  dp->d_name,
    553                  (info->xfer_mode==TYPE_A)?("\r"):(""));
    554          send(s, dirline, strlen(dirline), 0);
    555       }
    556    }
    557    closedir(dirp);
    558 
    559    close(s);
    560    rtems_ftpd_send_reply(226, "Transfer complete.");
    561 }
    562 
    563 
    564 /*
    565  * Cheesy way to change directories
     1092command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
     1093{
     1094  int                 s;
     1095  DIR                 *dirp = 0;
     1096  struct dirent       *dp = 0;
     1097  struct stat         stat_buf;
     1098  char                buf[FTPD_BUFSIZE];
     1099  char                path[FTPD_BUFSIZE];
     1100  time_t curTime;
     1101  char const* res;
     1102  char const* cwd = info->cwd;
     1103
     1104  send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
     1105
     1106  s = data_socket(info);
     1107  if(0 > s)
     1108  {
     1109    syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
     1110    return;
     1111  }
     1112
     1113  if(fname[0] == '\0')
     1114    fname = ".";
     1115
     1116  res = make_path(path, cwd, fname);
     1117
     1118  if (NULL == res || 0 > stat(path, &stat_buf))
     1119  {
     1120    snprintf(buf, FTPD_BUFSIZE,
     1121      "%s: No such file or directory.\r\n", fname);
     1122    send(s, buf, strlen(buf), 0);
     1123  }
     1124  else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(path))))
     1125  {
     1126    snprintf(buf, FTPD_BUFSIZE,
     1127      "%s: Can not open directory.\r\n", fname);
     1128    send(s, buf, strlen(buf), 0);
     1129  }
     1130  else
     1131  {
     1132    time(&curTime);
     1133    if(!dirp)
     1134      send_dirline(s, wide, curTime, path, "", fname, buf);
     1135    else {
     1136      /* FIXME: need "." and ".." only when '-a' option is given */
     1137      send_dirline(s, wide, curTime, path, "", ".", buf);
     1138      if(!strcmp(path, root))
     1139        send_dirline(s, wide, curTime, path, "", "..", buf);
     1140      else
     1141        send_dirline(s, wide, curTime, path, "..", "..", buf);
     1142      while ((dp = readdir(dirp)) != NULL)
     1143        send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf);
     1144    }
     1145  }
     1146
     1147  if(dirp)
     1148    closedir(dirp);
     1149  close_socket(s);
     1150  send_reply(info, 226, "Transfer complete.");
     1151}
     1152
     1153
     1154/*PAGE
     1155 *
     1156 * rtems_ftpd_cwd
     1157 *
     1158 * Change current working directory. We use 'chdir' here only to validate the
     1159 * new directory. We keep track of current working directory ourselves because
     1160 * current working directory in RTEMS isn't thread local, but we need it to be
     1161 * session local.
     1162 *
     1163 * Input parameters:
     1164 *   info - corresponding SessionInfo structure
     1165 *   dir  - directory name passed in CWD command
     1166 *
     1167 * Output parameters:
     1168 *   info->cwd is set to new CWD value.
     1169 *
    5661170 */
    5671171static void
    568 rtems_ftpd_CWD(char *dir)
    569 {
    570    rtems_status_code   sc;
    571    FTPD_SessionInfo_t  *info = NULL;
    572 
    573 
    574    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    575                             (rtems_unsigned32 *)&info);
    576 
    577    if (chdir(dir) == 0)
    578    {
    579       rtems_ftpd_send_reply(250, "CWD command successful.");
    580    }
    581    else
    582    {
    583       rtems_ftpd_send_reply(550, "CWD command failed.");
    584    }
    585 }
    586 
     1172rtems_ftpd_cwd(FTPD_SessionInfo_t  *info, char *dir)
     1173{
     1174  char buf[FTPD_BUFSIZE];
     1175  char const* cwd = make_path(buf, info->cwd, dir);
     1176  if(cwd && chdir(buf) == 0)
     1177  {
     1178    send_reply(info, 250, "CWD command successful.");
     1179    strcpy(info->cwd, cwd);
     1180  }
     1181  else
     1182  {
     1183    send_reply(info, 550, "CWD command failed.");
     1184  }
     1185}
     1186
     1187
     1188
     1189/*PAGE
     1190 *
     1191 * command_mdtm
     1192 *
     1193 * Handle FTP MDTM command
     1194 *
     1195 * Input parameters:
     1196 *   info - corresponding SessionInfo structure
     1197 *   fname - file name passed in MDTM command
     1198 *
     1199 * Output parameters:
     1200 *   info->cwd is set to new CWD value.
     1201 *
     1202 */
     1203static void
     1204command_mdtm(FTPD_SessionInfo_t  *info, char const* fname)
     1205{
     1206  struct stat stbuf;
     1207  char buf[FTPD_BUFSIZE];
     1208
     1209  if(*fname == '\0')
     1210    fname = ".";
     1211
     1212  if (stat(fname, &stbuf) < 0)
     1213  {
     1214    snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
     1215    send_reply(info, 550, buf);
     1216  }
     1217  else
     1218  {
     1219    struct tm *t = gmtime(&stbuf.st_mtime);
     1220    snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d",
     1221      1900 + t->tm_year,
     1222      t->tm_mon+1, t->tm_mday,
     1223      t->tm_hour, t->tm_min, t->tm_sec);
     1224    send_reply(info, 213, buf);
     1225  }
     1226}
    5871227
    5881228/**************************************************************************
    589  * Function: rtems_ftpd_command_port                                      *
     1229 * Function: command_port                                                 *
    5901230 **************************************************************************
    5911231 * Description:                                                           *
     
    6051245 *    none                                                                *
    6061246 *                                                                        *
    607  **************************************************************************
    608  * Change History:                                                        *
    609  *  12/01/97 - Creation (JWJ)                                             *
    6101247 *************************************************************************/
    6111248static void
    612 rtems_ftpd_command_port(char *bufr)
    613 {
    614    char                *ip;
    615    char                *port;
    616    int                 ip0, ip1, ip2, ip3, port0, port1;
    617    rtems_status_code   sc;
    618    FTPD_SessionInfo_t  *info = NULL;
    619 
    620 
    621    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    622                             (rtems_unsigned32 *)&info);
    623 
    624    sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
    625    ip = (char *)&(info->data_addr.sin_addr);
    626    ip[0] = ip0 & 0xff;
    627    ip[1] = ip1 & 0xff;
    628    ip[2] = ip2 & 0xff;
    629    ip[3] = ip3 & 0xff;
    630    port = (char *)&(info->data_addr.sin_port);
    631    port[0] = port0 & 0xff;
    632    port[1] = port1 & 0xff;
    633    info->data_addr.sin_family = AF_INET;
    634 }
    635 
     1249command_port(FTPD_SessionInfo_t *info, char *bufr)
     1250{
     1251  char                *ip;
     1252  char                *port;
     1253  int                 ip0, ip1, ip2, ip3, port0, port1;
     1254
     1255  sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
     1256  ip = (char *)&(info->data_addr.sin_addr);
     1257  ip[0] = ip0 & 0xff;
     1258  ip[1] = ip1 & 0xff;
     1259  ip[2] = ip2 & 0xff;
     1260  ip[3] = ip3 & 0xff;
     1261  port = (char *)&(info->data_addr.sin_port);
     1262  port[0] = port0 & 0xff;
     1263  port[1] = port1 & 0xff;
     1264  info->data_addr.sin_family = AF_INET;
     1265}
     1266
     1267
     1268/*PAGE
     1269 *
     1270 * skip_options
     1271 *
     1272 * Utility routine to skip options (if any) from input command.
     1273 *
     1274 * Input parameters:
     1275 *   p  - pointer to pointer to command
     1276 *
     1277 * Output parameters:
     1278 *   p  - is changed to point to first non-option argument
     1279 *
     1280 */
     1281static void
     1282skip_options(char **p)
     1283{
     1284  char* buf = *p;
     1285  while(1) {
     1286    while(*buf == ' ')
     1287      ++buf;
     1288    if(*buf == '-') {
     1289      if(*++buf == '-') { /* `--' should terminate options */
     1290        ++buf;
     1291        while(*buf == ' ')
     1292          ++buf;
     1293        break;
     1294      }
     1295      while(*buf != ' ' && *buf != '\0')
     1296        ++buf;
     1297    }
     1298    else
     1299      break;
     1300  }
     1301  *p = buf;
     1302}
    6361303
    6371304/**************************************************************************
    638  * Function: rtems_ftpd_parse_command                                     *
     1305 * Function: parse_command                                     *
    6391306 **************************************************************************
    6401307 * Description:                                                           *
     
    6551322 *    none                                                                *
    6561323 *                                                                        *
    657  **************************************************************************
    658  * Change History:                                                        *
    659  *  12/01/97 - Creation (JWJ)                                             *
    6601324 *************************************************************************/
    6611325static void
    662 rtems_ftpd_parse_command(char *bufr)
    663 {
    664    char fname[255];
    665    rtems_status_code   sc;
    666    FTPD_SessionInfo_t  *info = NULL;
    667 
    668 
    669    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    670                             (rtems_unsigned32 *)&info);
    671 
    672    if (!strncmp("PORT", bufr, 4))
    673    {
    674       rtems_ftpd_send_reply(200, "PORT command successful.");
    675       rtems_ftpd_command_port(&bufr[5]);
    676    }
    677    else if (!strncmp("RETR", bufr, 4))
    678    {
    679       sscanf(&bufr[5], "%254s", fname);
    680       rtems_ftpd_command_retrieve(fname);
    681    }
    682    else if (!strncmp("STOR", bufr, 4))
    683    {
    684       sscanf(&bufr[5], "%254s", fname);
    685       rtems_ftpd_command_store(fname);
    686    }
    687    else if (!strncmp("LIST", bufr, 4))
    688    {
    689       if (bufr[5] == '\n')
    690       {
    691          rtems_ftpd_command_list(".");
    692       }
    693       else
    694       {
    695          sscanf(&bufr[5], "%254s", fname);
    696          rtems_ftpd_command_list(fname);
    697       }
    698    }
    699    else if (!strncmp("USER", bufr, 4))
    700    {
    701       rtems_ftpd_send_reply(230, "User logged in.");
    702    }
    703    else if (!strncmp("SYST", bufr, 4))
    704    {
    705       rtems_ftpd_send_reply(240, "RTEMS");
    706    }
    707    else if (!strncmp("TYPE", bufr, 4))
    708    {
    709       if (bufr[5] == 'I')
    710       {
    711          info->xfer_mode = TYPE_I;
    712          rtems_ftpd_send_reply(200, "Type set to I.");
    713       }
    714       else if (bufr[5] == 'A')
    715       {
    716          info->xfer_mode = TYPE_A;
    717          rtems_ftpd_send_reply(200, "Type set to A.");
    718       }
    719       else
    720       {
    721          info->xfer_mode = TYPE_I;
    722          rtems_ftpd_send_reply(504, "Type not implemented.  Set to I.");
    723       }
    724    }
    725    else if (!strncmp("PASS", bufr, 4))
    726    {
    727       rtems_ftpd_send_reply(230, "User logged in.");
    728    }
    729    else if (!strncmp("DELE", bufr, 4))
    730    {
    731       sscanf(&bufr[4], "%254s", fname);
    732       if (unlink(fname) == 0)
    733       {
    734          rtems_ftpd_send_reply(257, "DELE successful.");
    735       }
    736       else
    737       {
    738          rtems_ftpd_send_reply(550, "DELE failed.");
    739       }
    740    }
    741    else if (!strncmp("SITE CHMOD", bufr, 10))
    742    {
    743       int mask;
    744 
    745       sscanf(&bufr[11], "%o %254s", &mask, fname);
    746       if (chmod(fname, (mode_t)mask) == 0)
    747       {
    748          rtems_ftpd_send_reply(257, "CHMOD successful.");
    749       }
    750       else
    751       {
    752          rtems_ftpd_send_reply(550, "CHMOD failed.");
    753       }
    754    }
    755    else if (!strncmp("RMD", bufr, 3))
    756    {
    757       sscanf(&bufr[4], "%254s", fname);
    758       if (rmdir(fname) == 0)
    759       {
    760          rtems_ftpd_send_reply(257, "RMD successful.");
    761       }
    762       else
    763       {
    764          rtems_ftpd_send_reply(550, "RMD failed.");
    765       }
    766    }
    767    else if (!strncmp("MKD", bufr, 3))
    768    {
    769       sscanf(&bufr[4], "%254s", fname);
    770       if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
    771       {
    772          rtems_ftpd_send_reply(257, "MKD successful.");
    773       }
    774       else
    775       {
    776          rtems_ftpd_send_reply(550, "MKD failed.");
    777       }
    778    }
    779    else if (!strncmp("CWD", bufr, 3))
    780    {
    781       sscanf(&bufr[4], "%254s", fname);
    782       rtems_ftpd_CWD(fname);
    783    }
    784    else if (!strncmp("PWD", bufr, 3))
    785    {
    786       char *cwd = getcwd(0, 0);
    787       sprintf(bufr, "\"%s\" is the current directory.", cwd);
    788       rtems_ftpd_send_reply(250, bufr);
    789       free(cwd);
    790    }
    791    else
    792    {
    793       rtems_ftpd_send_reply(500, "Unrecognized/unsupported command.");
    794    }
     1326parse_command(FTPD_SessionInfo_t *info, char *bufr)
     1327{
     1328  char fname[FTPD_BUFSIZE];
     1329
     1330  if (!strncmp("PORT", bufr, 4))
     1331  {
     1332    send_reply(info, 200, "PORT command successful.");
     1333    command_port(info, &bufr[5]);
     1334  }
     1335  else if (!strncmp("RETR", bufr, 4))
     1336  {
     1337    sscanf(&bufr[5], "%254s", fname);
     1338    command_retrieve(info, fname);
     1339  }
     1340  else if (!strncmp("STOR", bufr, 4))
     1341  {
     1342    sscanf(&bufr[5], "%254s", fname);
     1343    command_store(info, fname);
     1344  }
     1345  else if (!strncmp("LIST", bufr, 4))
     1346  {
     1347    bufr += 4;
     1348    skip_options(&bufr);
     1349    sscanf(bufr, "%254s", fname);
     1350    command_list(info, fname, 1);
     1351  }
     1352  else if (!strncmp("NLST", bufr, 4))
     1353  {
     1354    bufr += 4;
     1355    skip_options(&bufr);
     1356    sscanf(bufr, "%254s", fname);
     1357    command_list(info, fname, 0);
     1358  }
     1359  else if (!strncmp("MDTM", bufr, 4))
     1360  {
     1361    bufr += 4;
     1362    skip_options(&bufr);
     1363    sscanf(bufr, "%254s", fname);
     1364    command_mdtm(info, fname);
     1365  }
     1366  else if (!strncmp("USER", bufr, 4))
     1367  {
     1368    send_reply(info, 230, "User logged in.");
     1369  }
     1370  else if (!strncmp("SYST", bufr, 4))
     1371  {
     1372    send_reply(info, 240, FTPD_SYSTYPE);
     1373  }
     1374  else if (!strncmp("TYPE", bufr, 4))
     1375  {
     1376    if (bufr[5] == 'I')
     1377    {
     1378      info->xfer_mode = TYPE_I;
     1379      send_reply(info, 200, "Type set to I.");
     1380    }
     1381    else if (bufr[5] == 'A')
     1382    {
     1383      /* FIXME: ASCII mode isn't actually supported yet. */
     1384      info->xfer_mode = TYPE_A;
     1385      send_reply(info, 200, "Type set to A.");
     1386    }
     1387    else
     1388    {
     1389      info->xfer_mode = TYPE_I;
     1390      send_reply(info, 504, "Type not implemented.  Set to I.");
     1391    }
     1392  }
     1393  else if (!strncmp("PASS", bufr, 4))
     1394  {
     1395    send_reply(info, 230, "User logged in.");
     1396  }
     1397  else if (!strncmp("DELE", bufr, 4))
     1398  {
     1399    sscanf(&bufr[4], "%254s", fname);
     1400    if (unlink(fname) == 0)
     1401    {
     1402      send_reply(info, 257, "DELE successful.");
     1403    }
     1404    else
     1405    {
     1406      send_reply(info, 550, "DELE failed.");
     1407    }
     1408  }
     1409  else if (!strncmp("SITE CHMOD", bufr, 10))
     1410  {
     1411    int mask;
     1412
     1413    sscanf(&bufr[11], "%o %254s", &mask, fname);
     1414    if (chmod(fname, (mode_t)mask) == 0)
     1415    {
     1416      send_reply(info, 257, "CHMOD successful.");
     1417    }
     1418    else
     1419    {
     1420      send_reply(info, 550, "CHMOD failed.");
     1421    }
     1422  }
     1423  else if (!strncmp("RMD", bufr, 3))
     1424  {
     1425    sscanf(&bufr[4], "%254s", fname);
     1426    if (rmdir(fname) == 0)
     1427    {
     1428      send_reply(info, 257, "RMD successful.");
     1429    }
     1430    else
     1431    {
     1432      send_reply(info, 550, "RMD failed.");
     1433    }
     1434  }
     1435  else if (!strncmp("MKD", bufr, 3))
     1436  {
     1437    sscanf(&bufr[4], "%254s", fname);
     1438    if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
     1439    {
     1440      send_reply(info, 257, "MKD successful.");
     1441    }
     1442    else
     1443    {
     1444      send_reply(info, 550, "MKD failed.");
     1445    }
     1446  }
     1447  else if (!strncmp("CWD", bufr, 3))
     1448  {
     1449    sscanf(&bufr[4], "%254s", fname);
     1450    rtems_ftpd_cwd(info, fname);
     1451  }
     1452  else if (!strncmp("CDUP", bufr, 4))
     1453  {
     1454    rtems_ftpd_cwd(info, "..");
     1455  }
     1456  else if (!strncmp("PWD", bufr, 3))
     1457  {
     1458    char const* cwd = "/";
     1459    if(info->cwd[0])
     1460      cwd = info->cwd;
     1461    snprintf(bufr, FTPD_BUFSIZE,
     1462      "\"%s\" is the current directory.", cwd);
     1463    send_reply(info, 250, bufr);
     1464  }
     1465  else
     1466  {
     1467    send_reply(info, 500, "Unrecognized/unsupported command.");
     1468  }
    7951469}
    7961470
    7971471
    7981472/**************************************************************************
    799  * Function: rtems_ftpd_session                                           *
     1473 * Function: session                                           *
    8001474 **************************************************************************
    8011475 * Description:                                                           *
     
    8171491 *    none                                                                *
    8181492 *                                                                        *
    819  **************************************************************************
    820  * Change History:                                                        *
    821  *  12/01/97 - Creation (JWJ)                                             *
    8221493 *************************************************************************/
    8231494static void
    824 rtems_ftpd_session(rtems_task_argument arg)
    825 {
    826    char                cmd[256];
    827    rtems_status_code   sc;
    828    FTPD_SessionInfo_t  *info = NULL;
    829 
    830 
    831    sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
    832                             (rtems_unsigned32 *)&info);
    833 
    834    rtems_ftpd_send_reply(220, FTPD_SERVER_MESSAGE);
    835 
    836    /***********************************************************************
    837     * Set initial directory to "/".
    838     **********************************************************************/
    839    strcpy(info->cwd, "/");
    840    info->xfer_mode = TYPE_A;
    841    while (1)
    842    {
    843       if (fgets(cmd, 256, info->ctrl_fp) == NULL)
     1495session(rtems_task_argument arg)
     1496{
     1497  char cmd[FTPD_BUFSIZE];
     1498  FTPD_SessionInfo_t  *info = (FTPD_SessionInfo_t  *)arg;
     1499  rtems_event_set set;
     1500
     1501  while(1) {
     1502    rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
     1503      &set);
     1504
     1505    send_reply(info, 220, FTPD_SERVER_MESSAGE);
     1506
     1507    info->cwd[0] = 0;
     1508    info->xfer_mode = TYPE_I;
     1509
     1510    while (1)
     1511    {
     1512      if (fgets(cmd, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
    8441513      {
    845          syslog(LOG_INFO, "ftpd: Connection aborted.");
    846          break;
     1514        syslog(LOG_INFO, "ftpd: Connection aborted.");
     1515        break;
    8471516      }
    8481517
    8491518      if (!strncmp("QUIT", cmd, 4))
    8501519      {
    851          rtems_ftpd_send_reply(221, "Goodbye.");
    852          break;
     1520        send_reply(info, 221, "Goodbye.");
     1521        break;
    8531522      }
    8541523      else
    8551524      {
    856          rtems_ftpd_parse_command(cmd);
     1525        parse_command(info, cmd);
    8571526      }
    858    }
    859 
    860    if (fclose(info->ctrl_fp) != 0)
    861    {
    862       syslog(LOG_ERR, "ftpd: Could not close session.");
    863    }
    864 
    865 
    866    /* Least we can do is put the CWD back to /. */
    867    chdir("/");
    868 
    869    /***********************************************************************
    870     * Free up the allocated SessionInfo struct and exit.
    871     **********************************************************************/
    872    free(info);
    873    sc = rtems_task_delete(RTEMS_SELF);
    874    syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
    875           rtems_status_text(sc));
     1527    }
     1528
     1529    /* Close connection and put ourselves back into the task pool. */
     1530    close_stream(info);
     1531    task_pool_release(info);
     1532  }
    8761533}
    8771534
    8781535
    8791536/**************************************************************************
    880  * Function: rtems_ftpd_daemon                                            *
     1537 * Function: daemon                                            *
    8811538 **************************************************************************
    8821539 * Description:                                                           *
     
    8961553 *    none                                                                *
    8971554 *                                                                        *
    898  **************************************************************************
    899  * Change History:                                                        *
    900  *  12/01/97 - Creation (JWJ)                                             *
    9011555 *************************************************************************/
    902 
    903 /* this is not prototyped in strict ansi mode */
    904 
    905 FILE *fdopen (int fildes, const char *mode);
    906 
    9071556static void
    908 rtems_ftpd_daemon()
    909 {
    910    int                 s;
    911    int                 s1;
    912    int                 addrLen;
    913    struct sockaddr_in  remoteAddr;
    914    struct sockaddr_in  localAddr;
    915    char                sessionID;
    916    rtems_task_priority priority;
    917    rtems_status_code   sc;
    918    rtems_id            tid;
    919    FTPD_SessionInfo_t  *info = NULL;
    920 
    921 
    922    sessionID = 'a';
    923 
    924    s = socket(AF_INET, SOCK_STREAM, 0);
    925    if (s < 0)
    926    {
    927       perror("Creating socket");
    928    }
    929 
    930    localAddr.sin_family      = AF_INET;
    931    localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
    932    localAddr.sin_addr.s_addr = INADDR_ANY;
    933    memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
    934    if (bind(s, (struct sockaddr *)&localAddr,
    935             sizeof(localAddr)) < 0)
    936    {
    937       perror("Binding control socket");
    938    }
    939 
    940    if (listen(s, 2) < 0)
    941    {
    942       perror("Listening on control socket");
    943    }
    944 
    945    while (1)
    946    {
    947       /********************************************************************
    948        * Allocate a SessionInfo structure for the session task.
    949        *******************************************************************/
    950       info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
    951       if (info == NULL)
     1557daemon()
     1558{
     1559  int                 s;
     1560  int                 addrLen;
     1561  struct sockaddr_in  remoteAddr;
     1562  struct sockaddr_in  localAddr;
     1563  char                sessionID;
     1564  FTPD_SessionInfo_t  *info = NULL;
     1565
     1566
     1567  sessionID = 'a';
     1568
     1569  s = socket(PF_INET, SOCK_STREAM, 0);
     1570  if (s < 0)
     1571    syslog(LOG_ERR, "ftpd: Error creating socket: %s", serr());
     1572
     1573  localAddr.sin_family      = AF_INET;
     1574  localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
     1575  localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
     1576  memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
     1577
     1578  if (0 > bind(s, (struct sockaddr *)&localAddr, sizeof(localAddr)))
     1579    syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());
     1580
     1581  if (0 > listen(s, 1))
     1582    syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());
     1583
     1584  while (1)
     1585  {
     1586    int ss;
     1587    addrLen = sizeof(remoteAddr);
     1588    ss = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
     1589    if (0 > ss)
     1590    {
     1591      syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());
     1592    }
     1593    else
     1594    {
     1595      info = task_pool_obtain();
     1596      if (NULL == info)
    9521597      {
    953          syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
    954          rtems_panic("Malloc fail.");
    955       }
    956 
    957       /********************************************************************
    958        * Accept on the socket and start the session task.
    959        *******************************************************************/
    960       addrLen = sizeof(remoteAddr);
    961       s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
    962       if (s1 < 0)
    963       {
    964          perror("Accepting control connection");
    965       }
    966 
    967       rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
    968       sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
    969                              priority, 8*1024,
    970                              RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
    971                              RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
    972                              RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
    973                              &tid);
    974       if (sc != RTEMS_SUCCESSFUL)
    975       {
    976          syslog(LOG_ERR, "ftpd: Could not create FTPD session: %s",
    977                 rtems_status_text(sc));
    978       }
    979 
    980       if (sessionID == 'z')
    981       {
    982          sessionID = 'a';
     1598        close_socket(ss);
    9831599      }
    9841600      else
    9851601      {
    986          sessionID++;
     1602        info->socket = ss;
     1603        if ((info->ctrl_fp = fdopen(info->socket, "r+")) == NULL)
     1604        {
     1605          syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());
     1606          close_stream(info);
     1607          task_pool_release(info);
     1608        }
     1609        else
     1610        {
     1611          /* Wakeup the session task. The task will call task_pool_release
     1612             after it closes connection. */
     1613          rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
     1614        }
    9871615      }
    988 
    989       /********************************************************************
    990        * Send the socket on to the new session.
    991        *******************************************************************/
    992       if ((info->ctrl_fp = fdopen(s1, "r+")) == NULL)
    993       {
    994          syslog(LOG_ERR, "ftpd: fdopen() on socket failed.");
    995          close(s1);
    996       }
    997       else
    998       {
    999          sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
    1000                                   (rtems_unsigned32)info);
    1001          sc = rtems_task_start(tid, rtems_ftpd_session, 0);
    1002          if (sc != RTEMS_SUCCESSFUL)
    1003          {
    1004             syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
    1005                    rtems_status_text(sc));
    1006          }
    1007       }
    1008    }
     1616    }
     1617  }
    10091618}
    10101619
     
    10221631 * Inputs:                                                                *
    10231632 *                                                                        *
    1024  *    rtems_task_priority priority - Priority to assign to this task.     *
    1025  *                                                                        *
    10261633 * Output:                                                                *
    10271634 *                                                                        *
    10281635 *    int - RTEMS_SUCCESSFUL on successful start of the daemon.           *
    10291636 *                                                                        *
    1030  **************************************************************************
    1031  * Change History:                                                        *
    1032  *  12/01/97 - Creation (JWJ)                                             *
    10331637 *************************************************************************/
    10341638int
    10351639rtems_initialize_ftpd()
    10361640{
    1037    rtems_status_code   sc;
    1038    rtems_id            tid;
    1039 
    1040 
    1041    if (rtems_ftpd_configuration.port == 0)
    1042    {
    1043       rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
    1044    }
    1045 
    1046    /***********************************************************************
    1047     * Default FTPD priority.
    1048     **********************************************************************/
    1049    if (rtems_ftpd_configuration.priority == 0)
    1050    {
    1051       rtems_ftpd_configuration.priority = 40;
    1052    }
    1053    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
    1054                           rtems_ftpd_configuration.priority, 8*1024,
    1055                           RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
    1056                           RTEMS_INTERRUPT_LEVEL(0),
    1057                           RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
    1058                           &tid);
    1059    if (sc != RTEMS_SUCCESSFUL)
    1060    {
    1061       syslog(LOG_ERR, "ftpd: Could not create FTP daemon: %s",
    1062              rtems_status_text(sc));
    1063       return(RTEMS_UNSATISFIED);
    1064    }
    1065 
    1066    sc = rtems_task_start(tid, rtems_ftpd_daemon, 0);
    1067    if (sc != RTEMS_SUCCESSFUL)
    1068    {
    1069       syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
    1070              rtems_status_text(sc));
    1071       return(RTEMS_UNSATISFIED);
    1072    }   
    1073 
    1074    syslog(LOG_INFO, "ftpd: FTP daemon started.");
    1075    return(RTEMS_SUCCESSFUL);
    1076 }
    1077 
     1641  rtems_status_code   sc;
     1642  rtems_id            tid;
     1643  rtems_task_priority priority;
     1644  int count;
     1645
     1646  if (rtems_ftpd_configuration.port == 0)
     1647  {
     1648    rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
     1649  }
     1650
     1651  if (rtems_ftpd_configuration.priority == 0)
     1652  {
     1653    rtems_ftpd_configuration.priority = 40;
     1654  }
     1655  priority = rtems_ftpd_configuration.priority;
     1656
     1657  if (rtems_ftpd_configuration.tasks_count <= 0)
     1658    rtems_ftpd_configuration.tasks_count = 1;
     1659  count = rtems_ftpd_configuration.tasks_count;
     1660
     1661  if (!task_pool_init(count, priority))
     1662  {
     1663    syslog(LOG_ERR, "ftpd: Could not initialize task pool.");
     1664    return RTEMS_UNSATISFIED;
     1665  }
     1666
     1667  sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
     1668    priority, FTPD_STACKSIZE,
     1669    RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
     1670    RTEMS_INTERRUPT_LEVEL(0),
     1671    RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
     1672    &tid);
     1673
     1674  if (sc == RTEMS_SUCCESSFUL)
     1675  {
     1676    sc = rtems_task_start(tid, daemon, 0);
     1677    if (sc != RTEMS_SUCCESSFUL)
     1678      rtems_task_delete(tid);
     1679  }
     1680
     1681  if (sc != RTEMS_SUCCESSFUL)
     1682  {
     1683    task_pool_done(count);
     1684    syslog(LOG_ERR, "ftpd: Could not create/start FTP daemon: %s",
     1685      rtems_status_text(sc));
     1686    return RTEMS_UNSATISFIED;
     1687  }
     1688
     1689  root[0] = '\0';
     1690  if (
     1691    rtems_ftpd_configuration.root &&
     1692    strlen(rtems_ftpd_configuration.root) < FTPD_BUFSIZE &&
     1693    rtems_ftpd_configuration.root[0] == '/')
     1694  {
     1695    strcpy(root, rtems_ftpd_configuration.root);
     1696    squeeze_path(root);
     1697    rtems_ftpd_configuration.root = root;
     1698  }
     1699
     1700  syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
     1701    count, ((count > 1) ? "s" : ""));
     1702  return RTEMS_SUCCESSFUL;
     1703}
  • cpukit/ftpd/ftpd.h

    rd1941587 r3f777d0e  
    2626   int                     port;               /* Well-known port     */
    2727   struct rtems_ftpd_hook  *hooks;             /* List of hooks       */
     28   char const              *root;              /* Root for FTPD or 0 for / */
     29   int                     tasks_count;        /* Max. connections    */
    2830};
    2931
  • cpukit/libnetworking/ChangeLog

    rd1941587 r3f777d0e  
     12001-01-12      Sergei Organov <osv@javad.ru>
     2
     3        * rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements
     4        as listed below:
     5            - use pool of pre-created threads to handle sessions instead of
     6              creating/deleting threads on the fly
     7            - LIST output is now similar to what "/bin/ls -al" would output,
     8              thus FTP clients such Netscape are happy with it.
     9            - LIST NAME now works (both for files and directories)
     10            - added support for NLST, CDUP, and MDTM FTP commands to make
     11              more FTP clients happy
     12            - keep track of CWD for every session separately
     13            - ability to specify root directory name for FTPD in configuration
     14              table. FTPD will then create illusion for FTP clients that this
     15              is actually root directory.
     16            - ignore options sent in commands, thus LIST -al FILE works and
     17              doesn't try to list "-al" directory.
     18            - buffers are allocated on stack instead of heap where possible to
     19              eliminate malloc/free calls (avoid possible heap fragmentation
     20              troubles).
     21            - drop using of task notepad to pass parameters - use function
     22              arguments instead
     23            - use snprintf() instead of sprintf() as the latter is unsafe
     24            - use of PF_INET in socket() instead of AF_INET
     25
     26            Here are ftp clients I've tried new FTPD with (all of them
     27            running on Debian GNU/Linux 2.2):
     28
     29                 Lftp 2.1.10
     30                 NcFTP 2.4.3
     31                 Netscape 4.75
     32                 ftp
     33                 mc 4.5.49
     34
    1352001-01-02      Joel Sherrill <joel@OARcorp.com>
    236
Note: See TracChangeset for help on using the changeset viewer.