in netutils/ftpd/ftpd.c [1765:2085]
static int ftpd_stream(FAR struct ftpd_session_s *session, int cmdtype)
{
FAR char *abspath;
FAR char *path;
bool isnew;
int oflags;
FAR char *buffer;
size_t buflen;
size_t wantsize;
ssize_t rdbytes;
ssize_t wrbytes;
int errval = 0;
int ret;
ret = ftpd_getpath(session, session->param, &abspath, NULL);
if (ret < 0)
{
ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 550, ' ', "Stream error !");
goto errout;
}
path = abspath;
ret = ftpd_dataopen(session);
if (ret < 0)
{
goto errout_with_path;
}
switch (cmdtype)
{
case 0: /* retr */
oflags = O_RDONLY;
break;
case 1: /* stor */
oflags = O_CREAT | O_WRONLY;
break;
case 2: /* appe */
oflags = O_CREAT | O_WRONLY | O_APPEND;
break;
default:
oflags = O_RDONLY;
break;
}
#if defined(O_LARGEFILE)
oflags |= O_LARGEFILE;
#endif
/* Are we creating the file? */
if ((oflags & O_CREAT) != 0)
{
int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
if (session->restartpos <= 0)
{
oflags |= O_TRUNC;
}
isnew = true;
session->fd = open(path, oflags | O_EXCL, mode);
if (session->fd < 0)
{
isnew = false;
session->fd = open(path, oflags, mode);
}
}
else
{
/* No.. we are opening an existing file */
isnew = false;
session->fd = open(path, oflags);
}
if (session->fd < 0)
{
ret = -errno;
ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 550, ' ', "Can not open file !");
goto errout_with_data;
}
/* Restart position */
if (session->restartpos > 0)
{
off_t seekoffs = (off_t)-1;
off_t seekpos;
/* Get the seek position */
if (session->type == FTPD_SESSIONTYPE_A)
{
seekpos = ftpd_offsatoi(path, session->restartpos);
if (seekpos < 0)
{
nerr("ERROR: ftpd_offsatoi failed: %jd\n", (intmax_t)seekpos);
errval = -seekpos;
}
}
else
{
seekpos = session->restartpos;
if (seekpos < 0)
{
nerr("ERROR: Bad restartpos: %jd\n", (intmax_t)seekpos);
errval = EINVAL;
}
}
/* Seek to the request position */
if (seekpos >= 0)
{
seekoffs = lseek(session->fd, seekpos, SEEK_SET);
if (seekoffs < 0)
{
errval = errno;
nerr("ERROR: lseek failed: %d\n", errval);
}
}
/* Report errors. If an error occurred, seekoffs will be negative and
* errval will hold the (positive) error code.
*/
if (seekoffs < 0)
{
ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 550, ' ', "Can not seek file !");
ret = -errval;
goto errout_with_session;
}
}
/* Send success message */
ret = ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 150, ' ', "Opening data connection");
if (ret < 0)
{
nerr("ERROR: ftpd_response failed: %d\n", ret);
goto errout_with_session;
}
for (; ; )
{
/* Read from the source (file or TCP connection) */
if (session->type == FTPD_SESSIONTYPE_A)
{
buffer = &session->data.buffer[session->data.buflen >> 2];
wantsize = session->data.buflen >> 2;
}
else
{
buffer = session->data.buffer;
wantsize = session->data.buflen;
}
if (cmdtype == 0)
{
/* Read from the file. */
rdbytes = read(session->fd, session->data.buffer, wantsize);
if (rdbytes < 0)
{
errval = errno;
}
}
else
{
/* Read from the TCP connection, ftpd_recve returns the negated
* error condition.
*/
rdbytes = ftpd_recv(session->data.sd, session->data.buffer,
wantsize, session->rxtimeout);
if (rdbytes < 0)
{
errval = -rdbytes;
}
}
/* A negative value of rdbytes indicates a read error. errval has the
* (positive) error code associated with the failure.
*/
if (rdbytes < 0)
{
nerr("ERROR: Read failed: rdbytes=%zu errval=%d\n",
rdbytes, errval);
ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 550, ' ', "Data read error !");
ret = -errval;
break;
}
/* A value of rdbytes == 0 means that we have read the entire source
* stream.
*/
if (rdbytes == 0)
{
/* End-of-file */
ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 226, ' ', "Transfer complete");
/* Return success */
ret = 0;
break;
}
/* Write to the destination (file or TCP connection) */
if (session->type == FTPD_SESSIONTYPE_A)
{
/* Change to ascii */
size_t offset = 0;
buflen = 0;
while (offset < ((size_t)rdbytes))
{
if (session->data.buffer[offset] == '\n')
{
buffer[buflen++] = '\r';
}
buffer[buflen++] = session->data.buffer[offset++];
}
}
else
{
buffer = session->data.buffer;
buflen = (size_t)rdbytes;
}
if (cmdtype == 0)
{
/* Write to the TCP connection */
wrbytes = ftpd_send(session->data.sd, buffer, buflen,
session->txtimeout);
if (wrbytes < 0)
{
errval = -wrbytes;
nerr("ERROR: ftpd_send failed: %d\n", errval);
}
}
else
{
int remaining;
int nwritten;
FAR char *next;
remaining = buflen;
next = buffer;
/* Write to the file */
do
{
nwritten = write(session->fd, next, remaining);
if (nwritten < 0)
{
errval = errno;
nerr("ERROR: write() failed: %d\n", errval);
break;
}
remaining -= nwritten;
next += nwritten;
}
while (remaining > 0);
wrbytes = next - buffer;
}
/* If the number of bytes returned by the write is not equal to the
* number that we wanted to write, then an error (or at least an
* unhandled condition) has occurred. errval should should hold
* the (positive) error code.
*/
if (wrbytes != ((ssize_t)buflen))
{
nerr("ERROR: Write failed: wrbytes=%zu errval=%d\n",
wrbytes, errval);
ftpd_response(session->cmd.sd, session->txtimeout,
g_respfmt1, 550, ' ', "Data send error !");
ret = -errval;
break;
}
}
errout_with_session:;
close(session->fd);
session->fd = -1;
if (isnew && ret < 0)
{
unlink(path);
}
errout_with_data:;
ftpd_dataclose(session);
errout_with_path:
free(abspath);
errout:
return ret;
}