|
Hi, I made a patch for this issue. Please find attached the description of the patch in HTML format and the patch itself. Regards. Rachid Koucha a écrit : Hi, --
Tel: 0619838579 Mail: [email protected] WEB site: http://rachid.koucha.free.fr/ |
--- inetutils-1.6/ftpd/ftpd.c 2008-12-27 21:05:07.000000000 +0100
+++ inetutils-1.6-retr_size_0/ftpd/ftpd.c 2009-01-15 12:22:50.411041000 +0100
@@ -856,6 +856,9 @@ pass (const char *passwd)
login_attempts = 0; /* This time successful. */
}
+
+#define FTPD_ST_BLKSIZE(st) ((st).st_blksize ? (st).st_blksize : 4096)
+
void
retrieve (const char *cmd, const char *name)
{
@@ -867,7 +870,25 @@ retrieve (const char *cmd, const char *n
if (cmd == 0)
{
fin = fopen (name, "r"), closefunc = fclose;
- st.st_size = 0;
+ 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
{
@@ -876,28 +897,25 @@ retrieve (const char *cmd, const char *n
snprintf (line, sizeof line, cmd, name);
name = line;
fin = ftpd_popen (line, "r"), closefunc = ftpd_pclose;
- st.st_size = -1;
- buffer_size = BUFSIZ;
- }
- if (fin == NULL)
- {
- if (errno != 0)
- {
- perror_reply (550, name);
- if (cmd == 0)
+ if (fin == NULL)
+ {
+ if (errno != 0)
{
- LOGCMD ("get", name);
+ perror_reply (550, name);
+ if (cmd == 0)
+ {
+ LOGCMD ("get", name);
+ }
}
- }
- return;
+ return;
+ }
+ st.st_size = -1;
+ buffer_size = BUFSIZ;
}
+
byte_count = -1;
- if (cmd == 0 && (fstat (fileno (fin), &st) < 0 || !S_ISREG (st.st_mode)))
- {
- reply (550, "%s: not a plain file.", name);
- goto done;
- }
+
if (restart_point)
{
if (type == TYPE_A)
Title: Patch for FTPD in inetutils-1.6 to fix the problem of file
transfer which ends with 0 bytes received on client sideAuthor: 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:
- ftpcmd.y (yacc grammar) triggers the retrieve() function with the following rule:
{
if ($2 && $4 != NULL)
retrieve((char *) 0, $4);
if ($4 != NULL)
free($4);
- ftpd.c/retrieve(NULL, pathname) begins with the following:
if (cmd == 0)
{
fin = fopen (name, "r"), closefunc = fclose;
st.st_size = 0;
}
- ...the function gets the size of the file to transfer (st.st_size):
{
reply (550, "%s: not a plain file.", name);
goto done;
}
- ...the function calls datacon() to establish the data connection:
- ... then it calls send_data() to send the content of the file over the data connection:
- But as you can see, the variable "buffer_size" passed to send_data() is equal to 0 because it has not been modified since the beginning of the function where it has been initialized to 0
- In ftpd.c/send_data(), the parameter "blksize" is equal to 0
(value of the preceding "buffer_size" variable)
- ftpd.c/send_data() behaves in two possible ways. Either it reads the file to send with mmap() or it reads the file with the file system services. This is controlled by HAVE_MMAP flag. This flag is defined with the "configure" script located at the top level of the inetutils subtree. When "configure" is run, it generates the file "config.h" at the top level of the inetutils subtree. On my laptop running Linux ubuntu, "configure" sets the following in config.h:
#define HAVE_MMAP 1
- When it is defined, everything is fine in send_data() both for ASCII mode (case TYPE_A:) and Binary mode (case TYPE_I:) because we don't use the parameter "blksize". For example, for TYPE I, we run the following code to send the data:
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
- But if HAVE_MMAP is not defined in "config.h" (I commented it
out), the "case TYPE_A" still works because it does not use "blksize",
but the "case TYPE I" does not work because it uses the parameter
blksize which has a size of 0. The following while loop stops
immediately as we call read() with a blksize equal to 0:
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:
- In ftpd.c/retrieve(), we initialize the local_variable "buffer_size" to 4096 bytes:
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 = "">
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
- Untar the inetutils-1.6 package.
- Change directory into it
- Copy the patch file inetutils-1.6-retr_size_0.diff
into this directory
- Apply the patch inetutils-1.6-retr_size_0.diff as follow:
$ 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
