Author: R. Koucha
Last update:15-Jan-2009

Patch to fix the reception of files with a size of 0 bytes

 in

FTPD from inetutils-1.6





Introduction
Detailed description
How to apply the patch
About the author



Introduction

When HAVE_MMAP is not defined in inetutils-1.6, the RETR commands from the FTP client ends with the sending of files with a size of 0 bytes from FTPD server.

Detailed description

The RETR command sent by the client to the server to get a file, triggers the following when HAVE_MMAP flag is not defined:

    | RETR check_login SP pathname CRLF
        {
            if ($2 && $4 != NULL)
                retrieve((char *) 0, $4);
            if ($4 != NULL)
                free($4);

size_t buffer_size = 0;

if (cmd == 0)
  {
    fin = fopen (name, "r"), closefunc = fclose;
    st.st_size = 0;
  }

  if (cmd == 0 && (fstat (fileno (fin), &st) < 0 || !S_ISREG (st.st_mode)))
    {
      reply (550, "%s: not a plain file.", name);
      goto done;
    }

  dout = dataconn (name, st.st_size, "w");
  send_data (fin, dout, buffer_size);
/* Define to 1 if you have a working `mmap' system call. */
#define HAVE_MMAP 1
    case TYPE_I:
    case TYPE_L:
#ifdef HAVE_MMAP
      if (file_size > 0 && curpos >= 0 && buf != MAP_FAILED)
    {
      bp = buf;
      len = filesize;
      do
        {
          cnt = write (netfd, bp, len);
          len -= cnt;
          bp += cnt;
          if (cnt > 0)
        byte_count += cnt;
        }
      while (cnt > 0 && len > 0);
      transflag = 0;
      munmap (buf, (size_t) filesize);
      if (cnt < 0)
        goto data_err;
      reply (226, "Transfer complete with mmap (blksize = %u).", (u_int)blksize);
      return;
    }
#endif
      buf = malloc ((u_int) blksize);
      if (buf == NULL)
    {
      transflag = 0;
      perror_reply (451, "Local resource failure: malloc");
      return;
    }
      while ((cnt = read (filefd, buf, (u_int) blksize)) > 0 &&
         write (netfd, buf, cnt) == cnt)
    byte_count += cnt;
      transflag = 0;
      free (buf);
      if (cnt != 0)
    {
      if (cnt < 0)
        goto file_err;
      goto data_err;
    }
      reply (226, "Transfer complete.");
      return;

So, I would propose the following patch:
#define FTPD_ST_BLKSIZE(st)  ((st).st_blksize ? (st).st_blksize : 4096)

void
retrieve (const char *cmd, const char *name)
{
  FILE *fin, *dout;
  struct stat st;
  int (*closefunc) (FILE *);
  size_t buffer_size = 0;

  if (cmd == 0)
    {
      fin = fopen (name, "r"), closefunc = fclose;
      if (fin == NULL)
        {
          if (errno != 0)
        {
          perror_reply (550, name);
          if (cmd == 0)
            {
              LOGCMD ("get", name);
            }
        }
          return;
        }

      if (fstat(fileno(fin), &st) < 0 || !S_ISREG (st.st_mode))
        {
          reply (550, "%s: not a plain file.", name);
          goto done;
        }
      buffer_size = FTPD_ST_BLKSIZE(st);
    }
  else
    {
      char line[BUFSIZ];

      snprintf (line, sizeof line, cmd, name);
      name = line;
      fin = ftpd_popen (line, "r"), closefunc = ftpd_pclose;

      if (fin == NULL)
        {
          if (errno != 0)
        {
          perror_reply (550, name);
          if (cmd == 0)
            {
              LOGCMD ("get", name);
            }
        }
          return;
        }
      st.st_size = -1;
      buffer_size = BUFSIZ;
    }

  byte_count = -1;

  if (restart_point)
    {
      if (type == TYPE_A)
    {
      off_t i, n;
      int c;

      n = restart_point;
      i = 0;
      while (i++ < n)
        {
          c = getc (fin);
          if (c == EOF)
        {
          perror_reply (550, name);
          goto done;
        }
          if (c == '\n')
        i++;
        }
    }
      else if (lseek (fileno (fin), restart_point, SEEK_SET) < 0)
    {
      perror_reply (550, name);
      goto done;
    }
    }
  dout = dataconn (name, st.st_size, "w");
  if (dout == NULL)
    goto done;
  send_data (fin, dout, buffer_size);
  fclose (dout);
  data = -1;
  pdata = -1;
done:
  if (cmd == 0)
    LOGBYTES ("get", name, byte_count);
  (*closefunc) (fin);
}

I tried it with HAVE_MMAP not defined in "config.h" and it works.

How to apply the patch

$ tar xvfz inetutils-1.6.tar.gz
$ cd inetutils-1.6
$ cp ....../inetutils-1.6-retr_size_0.diff .
$ patch -p1 < inetutils-1.6-retr_size_0.diff

patching file ftpd/ftpd.c

About the author

The author is an engineer in computer sciences located in France. He is glad to graciously offer this utility under the GPL open source license. He can be contacted through this page or you can have a look at his WEB home page.