2000-12-14 Eric Norum <eric.norum@usask.ca>

* lib/tftpDriver.c: Added write capability.
This commit is contained in:
Joel Sherrill
2000-12-14 14:12:19 +00:00
parent 40323b5b37
commit 407bc8c9ee
6 changed files with 1752 additions and 1479 deletions

View File

@@ -1,3 +1,7 @@
2000-12-14 Eric Norum <eric.norum@usask.ca>
* lib/tftpDriver.c: Added write capability.
2000-12-08 Joel Sherrill <joel@OARcorp.com> 2000-12-08 Joel Sherrill <joel@OARcorp.com>
* libc/linkaddr.c: Initialized variable to remove warning. * libc/linkaddr.c: Initialized variable to remove warning.

View File

@@ -1,4 +1,6 @@
/* /*
* vim: set expandtab tabstop=4 shiftwidth=4 ai :
*
* Trivial File Transfer Protocol (RFC 1350) * Trivial File Transfer Protocol (RFC 1350)
* *
* Transfer file to/from remote host * Transfer file to/from remote host
@@ -10,6 +12,7 @@
* eric@skatter.usask.ca * eric@skatter.usask.ca
* *
* $Id$ * $Id$
*
*/ */
#include <stdio.h> #include <stdio.h>
@@ -113,7 +116,7 @@ struct tftpStream {
union tftpPacket pkbuf; union tftpPacket pkbuf;
/* /*
* Last block number received * Last block number transferred
*/ */
rtems_unsigned16 blocknum; rtems_unsigned16 blocknum;
@@ -135,6 +138,7 @@ struct tftpStream {
*/ */
int firstReply; int firstReply;
int eof; int eof;
int writing;
}; };
/* /*
@@ -168,7 +172,7 @@ rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
6 /* posix_vdisable */ 6 /* posix_vdisable */
}; };
int rtems_tftp_mount_me( static int rtems_tftp_mount_me(
rtems_filesystem_mount_table_entry_t *temp_mt_entry rtems_filesystem_mount_table_entry_t *temp_mt_entry
) )
{ {
@@ -234,7 +238,7 @@ int rtems_bsdnet_initialize_tftp_filesystem ()
status = mount( status = mount(
&entry, &entry,
&rtems_tftp_ops, &rtems_tftp_ops,
RTEMS_FILESYSTEM_READ_ONLY, RTEMS_FILESYSTEM_READ_WRITE,
NULL, NULL,
TFTP_PATHNAME_PREFIX TFTP_PATHNAME_PREFIX
); );
@@ -246,15 +250,14 @@ int rtems_bsdnet_initialize_tftp_filesystem ()
} }
/* /*
* Set error message * Map error message
* This RTEMS/UNIX error mapping needs to be fixed!
*/ */
static void static int
tftpSetErrno (struct tftpStream *tp) tftpErrno (struct tftpStream *tp)
{ {
unsigned int tftpError; unsigned int tftpError;
static const int errorMap[] = { static const int errorMap[] = {
0, EINVAL,
ENOENT, ENOENT,
EPERM, EPERM,
ENOSPC, ENOSPC,
@@ -262,14 +265,13 @@ tftpSetErrno (struct tftpStream *tp)
ENXIO, ENXIO,
EEXIST, EEXIST,
ESRCH, ESRCH,
0,
}; };
tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
if (tftpError < (sizeof errorMap / sizeof errorMap[0])) if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
errno = errorMap[tftpError]; return errorMap[tftpError];
else else
errno = 1000 + tftpError; return 1000 + tftpError;
} }
/* /*
@@ -279,21 +281,24 @@ static void
sendStifle (struct tftpStream *tp, struct sockaddr_in *to) sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
{ {
int len; int len;
struct {
rtems_unsigned16 opcode;
rtems_unsigned16 errorCode;
char errorMessage[12];
} msg;
/* /*
* Create the error packet (Unknown transfer ID). * Create the error packet (Unknown transfer ID).
*/ */
tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR); msg.opcode = htons (TFTP_OPCODE_ERROR);
tp->pkbuf.tftpERROR.errorCode = htons (5); msg.errorCode = htons (5);
len = sizeof tp->pkbuf.tftpERROR.opcode + len = sizeof msg.opcode + sizeof msg.errorCode + 1;
sizeof tp->pkbuf.tftpERROR.errorCode + 1; len += sprintf (msg.errorMessage, "GO AWAY");
len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
/* /*
* Send it * Send it
*/ */
sendto (tp->socket, (char *)&tp->pkbuf, len, 0, sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to);
(struct sockaddr *)to, sizeof *to);
} }
/* /*
@@ -374,7 +379,7 @@ releaseStream (int s)
rtems_semaphore_release (tftp_mutex); rtems_semaphore_release (tftp_mutex);
} }
int rtems_tftp_evaluate_for_make( static int rtems_tftp_evaluate_for_make(
const char *path, /* IN */ const char *path, /* IN */
rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
const char **name /* OUT */ const char **name /* OUT */
@@ -383,11 +388,8 @@ int rtems_tftp_evaluate_for_make(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
} }
/*
* XXX - Fix return values.
*/
int rtems_tftp_eval_path( static int rtems_tftp_eval_path(
const char *pathname, /* IN */ const char *pathname, /* IN */
int flags, /* IN */ int flags, /* IN */
rtems_filesystem_location_info_t *pathloc /* IN/OUT */ rtems_filesystem_location_info_t *pathloc /* IN/OUT */
@@ -395,35 +397,32 @@ int rtems_tftp_eval_path(
{ {
/* /*
* Read-only for now * Read-only or write-only for now
*/ */
flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
if ( (flags & O_WRONLY) == O_WRONLY ) if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) )
set_errno_and_return_minus_one( ENOENT ); set_errno_and_return_minus_one( EINVAL );
/* /*
* The File system is mounted at TFTP_PATHNAME_PREFIX * The File system is mounted at TFTP_PATHNAME_PREFIX
* the caller of this routine has striped off this part of the * the caller of this routine has striped off this part of the
* name. Save the remainder of the name for use by the open routine. * name. Save the remainder of the name for use by the open routine.
*/ */
pathloc->node_access = (void * ) pathname; pathloc->node_access = (void * ) pathname;
pathloc->handlers = &rtems_tftp_handlers; pathloc->handlers = &rtems_tftp_handlers;
return 0; return 0;
} }
static int rtems_tftp_open(
int rtems_tftp_open(
rtems_libio_t *iop, rtems_libio_t *iop,
const char *new_name, const char *new_name,
unsigned32 flag, unsigned32 flags,
unsigned32 mode unsigned32 mode
) )
{ {
struct tftpStream *tp; struct tftpStream *tp;
int retryCount; int retryCount;
rtems_unsigned32 farAddress; struct in_addr farAddress;
int s; int s;
int len; int len;
char *cp1; char *cp1;
@@ -435,33 +434,44 @@ int rtems_tftp_open(
/* /*
* This came from the evaluate path. * This came from the evaluate path.
* Extract host name component
*/ */
cp1 = cp2 = iop->file_info;
cp2 = iop->file_info;
cp1 = cp2;
while (*cp2 != '/') { while (*cp2 != '/') {
if (*cp2 == '\0') if (*cp2 == '\0')
return ENOENT; return ENOENT;
cp2++; cp2++;
} }
len = cp2 - cp1; len = cp2 - cp1;
hostname = malloc (len + 1); hostname = malloc (len + 1);
if (hostname == NULL) if (hostname == NULL)
return ENOMEM; return ENOMEM;
strncpy (hostname, cp1, len); strncpy (hostname, cp1, len);
hostname[len] = '\0'; hostname[len] = '\0';
farAddress = inet_addr (hostname);
/*
* Convert hostname to Internet address
*/
if (strcmp (hostname, "BOOTP_HOST") == 0)
farAddress = rtems_bsdnet_bootp_server_address;
else
farAddress.s_addr = inet_addr (hostname);
free (hostname); free (hostname);
if ((farAddress.s_addr == 0) || (farAddress.s_addr == ~0))
if ((farAddress == 0) || (farAddress == ~0))
return ENOENT; return ENOENT;
if (*++cp2 == '\0') /*
* Extract file pathname component
*/
while (*cp2 == '/')
cp2++;
if (strcmp (cp2, "BOOTP_FILE") == 0) {
cp2 = rtems_bsdnet_bootp_boot_file_name;
while (*cp2 == '/')
cp2++;
}
if (*cp2 == '\0')
return ENOENT; return ENOENT;
remoteFilename = cp2; remoteFilename = cp2;
if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
return ENOENT; return ENOENT;
@@ -469,16 +479,13 @@ int rtems_tftp_open(
/* /*
* Find a free stream * Find a free stream
*/ */
sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
return EBUSY; return EBUSY;
for (s = 0 ; s < nStreams ; s++) { for (s = 0 ; s < nStreams ; s++) {
if (tftpStreams[s] == NULL) if (tftpStreams[s] == NULL)
break; break;
} }
if (s == nStreams) { if (s == nStreams) {
/* /*
* Reallocate stream pointers * Reallocate stream pointers
@@ -493,7 +500,6 @@ int rtems_tftp_open(
} }
tftpStreams = np; tftpStreams = np;
} }
tp = tftpStreams[s] = malloc (sizeof (struct tftpStream)); tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
rtems_semaphore_release (tftp_mutex); rtems_semaphore_release (tftp_mutex);
if (tp == NULL) if (tp == NULL)
@@ -504,7 +510,6 @@ int rtems_tftp_open(
/* /*
* Create the socket * Create the socket
*/ */
if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
releaseStream (s); releaseStream (s);
return ENOMEM; return ENOMEM;
@@ -513,7 +518,6 @@ int rtems_tftp_open(
/* /*
* Bind the socket to a local address * Bind the socket to a local address
*/ */
retryCount = 0; retryCount = 0;
rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
for (;;) { for (;;) {
@@ -536,7 +540,7 @@ int rtems_tftp_open(
* port on the remote machine. * port on the remote machine.
*/ */
tp->farAddress.sin_family = AF_INET; tp->farAddress.sin_family = AF_INET;
tp->farAddress.sin_addr.s_addr = farAddress; tp->farAddress.sin_addr = farAddress;
tp->farAddress.sin_port = htons (69); tp->farAddress.sin_port = htons (69);
/* /*
@@ -547,7 +551,14 @@ int rtems_tftp_open(
/* /*
* Create the request * Create the request
*/ */
if ((flags & O_ACCMODE) == O_RDONLY) {
tp->writing = 0;
tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
}
else {
tp->writing = 1;
tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
}
cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode; cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
cp2 = (char *) remoteFilename; cp2 = (char *) remoteFilename;
while ((*cp1++ = *cp2++) != '\0') while ((*cp1++ = *cp2++) != '\0')
@@ -574,7 +585,8 @@ int rtems_tftp_open(
len = getPacket (tp); len = getPacket (tp);
if (len >= (int) sizeof tp->pkbuf.tftpACK) { if (len >= (int) sizeof tp->pkbuf.tftpACK) {
int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
if ((opcode == TFTP_OPCODE_DATA) if (!tp->writing
&& (opcode == TFTP_OPCODE_DATA)
&& (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
tp->nused = 0; tp->nused = 0;
tp->blocknum = 1; tp->blocknum = 1;
@@ -587,11 +599,18 @@ int rtems_tftp_open(
} }
break; break;
} }
if (tp->writing
&& (opcode == TFTP_OPCODE_ACK)
&& (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
tp->nused = 0;
tp->blocknum = 1;
break;
}
if (opcode == TFTP_OPCODE_ERROR) { if (opcode == TFTP_OPCODE_ERROR) {
tftpSetErrno (tp); int e = tftpErrno (tp);
close (tp->socket); close (tp->socket);
releaseStream (s); releaseStream (s);
return EIO; return e;
} }
} }
@@ -604,45 +623,41 @@ int rtems_tftp_open(
return EIO; return EIO;
} }
} }
return 0; return 0;
} }
/* /*
* Read from a TFTP stream * Read from a TFTP stream
*/ */
static int rtems_tftp_read(
int rtems_tftp_read(
rtems_libio_t *iop, rtems_libio_t *iop,
void *buffer, void *buffer,
unsigned32 count unsigned32 count
) )
{ {
char *bp; char *bp;
struct tftpStream *tp; struct tftpStream *tp = iop->data1;
int retryCount; int retryCount;
int nwant; int nwant;
tp = iop->data1;
/* /*
* Read till user request is satisfied or EOF is reached * Read till user request is satisfied or EOF is reached
*/ */
bp = buffer; bp = buffer;
nwant = count; nwant = count;
while (nwant) { while (nwant) {
if (tp->nleft) { if (tp->nleft) {
int count; int ncopy;
if (nwant < tp->nleft) if (nwant < tp->nleft)
count = nwant; ncopy = nwant;
else else
count = tp->nleft; ncopy = tp->nleft;
memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count); memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
tp->nused += count; tp->nused += ncopy;
tp->nleft -= count; tp->nleft -= ncopy;
bp += count; bp += ncopy;
nwant -= count; nwant -= ncopy;
if (nwant == 0) if (nwant == 0)
break; break;
} }
@@ -668,10 +683,8 @@ int rtems_tftp_read(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
break; break;
} }
if (opcode == TFTP_OPCODE_ERROR) { if (opcode == TFTP_OPCODE_ERROR)
tftpSetErrno (tp); set_errno_and_return_minus_one( tftpErrno (tp) );
return RTEMS_INTERNAL_ERROR;
}
} }
/* /*
@@ -683,23 +696,57 @@ int rtems_tftp_read(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
} }
} }
return count - nwant;
}
/*
* Flush a write buffer and wait for acknowledgement
*/
static int rtems_tftp_flush ( struct tftpStream *tp )
{
int wlen, rlen;
int retryCount = 0;
wlen = tp->nused + 2 * sizeof (rtems_unsigned16);
for (;;) {
tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
(struct sockaddr *)&tp->farAddress,
sizeof tp->farAddress) < 0)
return EIO;
rlen = getPacket (tp);
if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
if ((opcode == TFTP_OPCODE_ACK)
&& (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
tp->nused = 0;
tp->blocknum++;
return 0;
}
if (opcode == TFTP_OPCODE_ERROR)
return tftpErrno (tp);
}
/* /*
* XXX - Eric is this right? * Keep trying?
*
*/ */
return count - nwant; if (++retryCount == IO_RETRY_LIMIT)
return EIO;
}
} }
/* /*
* Close a TFTP stream * Close a TFTP stream
*/ */
int rtems_tftp_close( static int rtems_tftp_close(
rtems_libio_t *iop rtems_libio_t *iop
) )
{ {
struct tftpStream *tp = iop->data1;; struct tftpStream *tp = iop->data1;;
if (tp->writing)
rtems_tftp_flush (tp);
if (!tp->eof && !tp->firstReply) { if (!tp->eof && !tp->firstReply) {
/* /*
* Tell the other end to stop * Tell the other end to stop
@@ -714,22 +761,62 @@ int rtems_tftp_close(
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
int rtems_tftp_write( static int rtems_tftp_write(
rtems_libio_t *iop, rtems_libio_t *iop,
const void *buffer, const void *buffer,
unsigned32 count unsigned32 count
) )
{ {
return RTEMS_NOT_CONFIGURED; const char *bp;
struct tftpStream *tp = iop->data1;
int nleft, nfree, ncopy;
/*
* Bail out if an error has occurred
*/
if (!tp->writing)
return EIO;
/*
* Write till user request is satisfied
* Notice that the buffer is flushed as soon as it is filled rather
* than waiting for the next write or a close. This ensures that
* the flush in close writes a less than full buffer so the far
* end can detect the end-of-file condition.
*/
bp = buffer;
nleft = count;
while (nleft) {
nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
if (nleft < nfree)
ncopy = nleft;
else
ncopy = nfree;
memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
tp->nused += ncopy;
nleft -= ncopy;
bp += ncopy;
if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
int e = rtems_tftp_flush (tp);
if (e) {
tp->writing = 0;
set_errno_and_return_minus_one (e);
}
}
}
return count;
} }
rtems_device_driver rtems_tftp_control( /*
rtems_device_major_number major, * Dummy version to let fopen(xxxx,"w") work properly.
rtems_device_minor_number minor, */
void *pargp static int rtems_tftp_ftruncate(
rtems_libio_t *iop,
off_t count
) )
{ {
return RTEMS_NOT_CONFIGURED; return 0;
} }
rtems_filesystem_node_types_t rtems_tftp_node_type( rtems_filesystem_node_types_t rtems_tftp_node_type(
@@ -768,7 +855,7 @@ rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
NULL, /* lseek */ NULL, /* lseek */
NULL, /* fstat */ NULL, /* fstat */
NULL, /* fchmod */ NULL, /* fchmod */
NULL, /* ftruncate */ rtems_tftp_ftruncate, /* ftruncate */
NULL, /* fpathconf */ NULL, /* fpathconf */
NULL, /* fsync */ NULL, /* fsync */
NULL, /* fdatasync */ NULL, /* fdatasync */

View File

@@ -1,3 +1,7 @@
2000-12-14 Eric Norum <eric.norum@usask.ca>
* lib/tftpDriver.c: Added write capability.
2000-12-08 Joel Sherrill <joel@OARcorp.com> 2000-12-08 Joel Sherrill <joel@OARcorp.com>
* libc/linkaddr.c: Initialized variable to remove warning. * libc/linkaddr.c: Initialized variable to remove warning.

View File

@@ -1,4 +1,6 @@
/* /*
* vim: set expandtab tabstop=4 shiftwidth=4 ai :
*
* Trivial File Transfer Protocol (RFC 1350) * Trivial File Transfer Protocol (RFC 1350)
* *
* Transfer file to/from remote host * Transfer file to/from remote host
@@ -10,6 +12,7 @@
* eric@skatter.usask.ca * eric@skatter.usask.ca
* *
* $Id$ * $Id$
*
*/ */
#include <stdio.h> #include <stdio.h>
@@ -113,7 +116,7 @@ struct tftpStream {
union tftpPacket pkbuf; union tftpPacket pkbuf;
/* /*
* Last block number received * Last block number transferred
*/ */
rtems_unsigned16 blocknum; rtems_unsigned16 blocknum;
@@ -135,6 +138,7 @@ struct tftpStream {
*/ */
int firstReply; int firstReply;
int eof; int eof;
int writing;
}; };
/* /*
@@ -168,7 +172,7 @@ rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
6 /* posix_vdisable */ 6 /* posix_vdisable */
}; };
int rtems_tftp_mount_me( static int rtems_tftp_mount_me(
rtems_filesystem_mount_table_entry_t *temp_mt_entry rtems_filesystem_mount_table_entry_t *temp_mt_entry
) )
{ {
@@ -234,7 +238,7 @@ int rtems_bsdnet_initialize_tftp_filesystem ()
status = mount( status = mount(
&entry, &entry,
&rtems_tftp_ops, &rtems_tftp_ops,
RTEMS_FILESYSTEM_READ_ONLY, RTEMS_FILESYSTEM_READ_WRITE,
NULL, NULL,
TFTP_PATHNAME_PREFIX TFTP_PATHNAME_PREFIX
); );
@@ -246,15 +250,14 @@ int rtems_bsdnet_initialize_tftp_filesystem ()
} }
/* /*
* Set error message * Map error message
* This RTEMS/UNIX error mapping needs to be fixed!
*/ */
static void static int
tftpSetErrno (struct tftpStream *tp) tftpErrno (struct tftpStream *tp)
{ {
unsigned int tftpError; unsigned int tftpError;
static const int errorMap[] = { static const int errorMap[] = {
0, EINVAL,
ENOENT, ENOENT,
EPERM, EPERM,
ENOSPC, ENOSPC,
@@ -262,14 +265,13 @@ tftpSetErrno (struct tftpStream *tp)
ENXIO, ENXIO,
EEXIST, EEXIST,
ESRCH, ESRCH,
0,
}; };
tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
if (tftpError < (sizeof errorMap / sizeof errorMap[0])) if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
errno = errorMap[tftpError]; return errorMap[tftpError];
else else
errno = 1000 + tftpError; return 1000 + tftpError;
} }
/* /*
@@ -279,21 +281,24 @@ static void
sendStifle (struct tftpStream *tp, struct sockaddr_in *to) sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
{ {
int len; int len;
struct {
rtems_unsigned16 opcode;
rtems_unsigned16 errorCode;
char errorMessage[12];
} msg;
/* /*
* Create the error packet (Unknown transfer ID). * Create the error packet (Unknown transfer ID).
*/ */
tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR); msg.opcode = htons (TFTP_OPCODE_ERROR);
tp->pkbuf.tftpERROR.errorCode = htons (5); msg.errorCode = htons (5);
len = sizeof tp->pkbuf.tftpERROR.opcode + len = sizeof msg.opcode + sizeof msg.errorCode + 1;
sizeof tp->pkbuf.tftpERROR.errorCode + 1; len += sprintf (msg.errorMessage, "GO AWAY");
len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
/* /*
* Send it * Send it
*/ */
sendto (tp->socket, (char *)&tp->pkbuf, len, 0, sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to);
(struct sockaddr *)to, sizeof *to);
} }
/* /*
@@ -374,7 +379,7 @@ releaseStream (int s)
rtems_semaphore_release (tftp_mutex); rtems_semaphore_release (tftp_mutex);
} }
int rtems_tftp_evaluate_for_make( static int rtems_tftp_evaluate_for_make(
const char *path, /* IN */ const char *path, /* IN */
rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
const char **name /* OUT */ const char **name /* OUT */
@@ -383,11 +388,8 @@ int rtems_tftp_evaluate_for_make(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
} }
/*
* XXX - Fix return values.
*/
int rtems_tftp_eval_path( static int rtems_tftp_eval_path(
const char *pathname, /* IN */ const char *pathname, /* IN */
int flags, /* IN */ int flags, /* IN */
rtems_filesystem_location_info_t *pathloc /* IN/OUT */ rtems_filesystem_location_info_t *pathloc /* IN/OUT */
@@ -395,35 +397,32 @@ int rtems_tftp_eval_path(
{ {
/* /*
* Read-only for now * Read-only or write-only for now
*/ */
flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
if ( (flags & O_WRONLY) == O_WRONLY ) if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) )
set_errno_and_return_minus_one( ENOENT ); set_errno_and_return_minus_one( EINVAL );
/* /*
* The File system is mounted at TFTP_PATHNAME_PREFIX * The File system is mounted at TFTP_PATHNAME_PREFIX
* the caller of this routine has striped off this part of the * the caller of this routine has striped off this part of the
* name. Save the remainder of the name for use by the open routine. * name. Save the remainder of the name for use by the open routine.
*/ */
pathloc->node_access = (void * ) pathname; pathloc->node_access = (void * ) pathname;
pathloc->handlers = &rtems_tftp_handlers; pathloc->handlers = &rtems_tftp_handlers;
return 0; return 0;
} }
static int rtems_tftp_open(
int rtems_tftp_open(
rtems_libio_t *iop, rtems_libio_t *iop,
const char *new_name, const char *new_name,
unsigned32 flag, unsigned32 flags,
unsigned32 mode unsigned32 mode
) )
{ {
struct tftpStream *tp; struct tftpStream *tp;
int retryCount; int retryCount;
rtems_unsigned32 farAddress; struct in_addr farAddress;
int s; int s;
int len; int len;
char *cp1; char *cp1;
@@ -435,33 +434,44 @@ int rtems_tftp_open(
/* /*
* This came from the evaluate path. * This came from the evaluate path.
* Extract host name component
*/ */
cp1 = cp2 = iop->file_info;
cp2 = iop->file_info;
cp1 = cp2;
while (*cp2 != '/') { while (*cp2 != '/') {
if (*cp2 == '\0') if (*cp2 == '\0')
return ENOENT; return ENOENT;
cp2++; cp2++;
} }
len = cp2 - cp1; len = cp2 - cp1;
hostname = malloc (len + 1); hostname = malloc (len + 1);
if (hostname == NULL) if (hostname == NULL)
return ENOMEM; return ENOMEM;
strncpy (hostname, cp1, len); strncpy (hostname, cp1, len);
hostname[len] = '\0'; hostname[len] = '\0';
farAddress = inet_addr (hostname);
/*
* Convert hostname to Internet address
*/
if (strcmp (hostname, "BOOTP_HOST") == 0)
farAddress = rtems_bsdnet_bootp_server_address;
else
farAddress.s_addr = inet_addr (hostname);
free (hostname); free (hostname);
if ((farAddress.s_addr == 0) || (farAddress.s_addr == ~0))
if ((farAddress == 0) || (farAddress == ~0))
return ENOENT; return ENOENT;
if (*++cp2 == '\0') /*
* Extract file pathname component
*/
while (*cp2 == '/')
cp2++;
if (strcmp (cp2, "BOOTP_FILE") == 0) {
cp2 = rtems_bsdnet_bootp_boot_file_name;
while (*cp2 == '/')
cp2++;
}
if (*cp2 == '\0')
return ENOENT; return ENOENT;
remoteFilename = cp2; remoteFilename = cp2;
if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
return ENOENT; return ENOENT;
@@ -469,16 +479,13 @@ int rtems_tftp_open(
/* /*
* Find a free stream * Find a free stream
*/ */
sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
return EBUSY; return EBUSY;
for (s = 0 ; s < nStreams ; s++) { for (s = 0 ; s < nStreams ; s++) {
if (tftpStreams[s] == NULL) if (tftpStreams[s] == NULL)
break; break;
} }
if (s == nStreams) { if (s == nStreams) {
/* /*
* Reallocate stream pointers * Reallocate stream pointers
@@ -493,7 +500,6 @@ int rtems_tftp_open(
} }
tftpStreams = np; tftpStreams = np;
} }
tp = tftpStreams[s] = malloc (sizeof (struct tftpStream)); tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
rtems_semaphore_release (tftp_mutex); rtems_semaphore_release (tftp_mutex);
if (tp == NULL) if (tp == NULL)
@@ -504,7 +510,6 @@ int rtems_tftp_open(
/* /*
* Create the socket * Create the socket
*/ */
if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
releaseStream (s); releaseStream (s);
return ENOMEM; return ENOMEM;
@@ -513,7 +518,6 @@ int rtems_tftp_open(
/* /*
* Bind the socket to a local address * Bind the socket to a local address
*/ */
retryCount = 0; retryCount = 0;
rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
for (;;) { for (;;) {
@@ -536,7 +540,7 @@ int rtems_tftp_open(
* port on the remote machine. * port on the remote machine.
*/ */
tp->farAddress.sin_family = AF_INET; tp->farAddress.sin_family = AF_INET;
tp->farAddress.sin_addr.s_addr = farAddress; tp->farAddress.sin_addr = farAddress;
tp->farAddress.sin_port = htons (69); tp->farAddress.sin_port = htons (69);
/* /*
@@ -547,7 +551,14 @@ int rtems_tftp_open(
/* /*
* Create the request * Create the request
*/ */
if ((flags & O_ACCMODE) == O_RDONLY) {
tp->writing = 0;
tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
}
else {
tp->writing = 1;
tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
}
cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode; cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
cp2 = (char *) remoteFilename; cp2 = (char *) remoteFilename;
while ((*cp1++ = *cp2++) != '\0') while ((*cp1++ = *cp2++) != '\0')
@@ -574,7 +585,8 @@ int rtems_tftp_open(
len = getPacket (tp); len = getPacket (tp);
if (len >= (int) sizeof tp->pkbuf.tftpACK) { if (len >= (int) sizeof tp->pkbuf.tftpACK) {
int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
if ((opcode == TFTP_OPCODE_DATA) if (!tp->writing
&& (opcode == TFTP_OPCODE_DATA)
&& (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
tp->nused = 0; tp->nused = 0;
tp->blocknum = 1; tp->blocknum = 1;
@@ -587,11 +599,18 @@ int rtems_tftp_open(
} }
break; break;
} }
if (tp->writing
&& (opcode == TFTP_OPCODE_ACK)
&& (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
tp->nused = 0;
tp->blocknum = 1;
break;
}
if (opcode == TFTP_OPCODE_ERROR) { if (opcode == TFTP_OPCODE_ERROR) {
tftpSetErrno (tp); int e = tftpErrno (tp);
close (tp->socket); close (tp->socket);
releaseStream (s); releaseStream (s);
return EIO; return e;
} }
} }
@@ -604,45 +623,41 @@ int rtems_tftp_open(
return EIO; return EIO;
} }
} }
return 0; return 0;
} }
/* /*
* Read from a TFTP stream * Read from a TFTP stream
*/ */
static int rtems_tftp_read(
int rtems_tftp_read(
rtems_libio_t *iop, rtems_libio_t *iop,
void *buffer, void *buffer,
unsigned32 count unsigned32 count
) )
{ {
char *bp; char *bp;
struct tftpStream *tp; struct tftpStream *tp = iop->data1;
int retryCount; int retryCount;
int nwant; int nwant;
tp = iop->data1;
/* /*
* Read till user request is satisfied or EOF is reached * Read till user request is satisfied or EOF is reached
*/ */
bp = buffer; bp = buffer;
nwant = count; nwant = count;
while (nwant) { while (nwant) {
if (tp->nleft) { if (tp->nleft) {
int count; int ncopy;
if (nwant < tp->nleft) if (nwant < tp->nleft)
count = nwant; ncopy = nwant;
else else
count = tp->nleft; ncopy = tp->nleft;
memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count); memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
tp->nused += count; tp->nused += ncopy;
tp->nleft -= count; tp->nleft -= ncopy;
bp += count; bp += ncopy;
nwant -= count; nwant -= ncopy;
if (nwant == 0) if (nwant == 0)
break; break;
} }
@@ -668,10 +683,8 @@ int rtems_tftp_read(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
break; break;
} }
if (opcode == TFTP_OPCODE_ERROR) { if (opcode == TFTP_OPCODE_ERROR)
tftpSetErrno (tp); set_errno_and_return_minus_one( tftpErrno (tp) );
return RTEMS_INTERNAL_ERROR;
}
} }
/* /*
@@ -683,23 +696,57 @@ int rtems_tftp_read(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
} }
} }
return count - nwant;
}
/*
* Flush a write buffer and wait for acknowledgement
*/
static int rtems_tftp_flush ( struct tftpStream *tp )
{
int wlen, rlen;
int retryCount = 0;
wlen = tp->nused + 2 * sizeof (rtems_unsigned16);
for (;;) {
tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
(struct sockaddr *)&tp->farAddress,
sizeof tp->farAddress) < 0)
return EIO;
rlen = getPacket (tp);
if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
if ((opcode == TFTP_OPCODE_ACK)
&& (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
tp->nused = 0;
tp->blocknum++;
return 0;
}
if (opcode == TFTP_OPCODE_ERROR)
return tftpErrno (tp);
}
/* /*
* XXX - Eric is this right? * Keep trying?
*
*/ */
return count - nwant; if (++retryCount == IO_RETRY_LIMIT)
return EIO;
}
} }
/* /*
* Close a TFTP stream * Close a TFTP stream
*/ */
int rtems_tftp_close( static int rtems_tftp_close(
rtems_libio_t *iop rtems_libio_t *iop
) )
{ {
struct tftpStream *tp = iop->data1;; struct tftpStream *tp = iop->data1;;
if (tp->writing)
rtems_tftp_flush (tp);
if (!tp->eof && !tp->firstReply) { if (!tp->eof && !tp->firstReply) {
/* /*
* Tell the other end to stop * Tell the other end to stop
@@ -714,22 +761,62 @@ int rtems_tftp_close(
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
int rtems_tftp_write( static int rtems_tftp_write(
rtems_libio_t *iop, rtems_libio_t *iop,
const void *buffer, const void *buffer,
unsigned32 count unsigned32 count
) )
{ {
return RTEMS_NOT_CONFIGURED; const char *bp;
struct tftpStream *tp = iop->data1;
int nleft, nfree, ncopy;
/*
* Bail out if an error has occurred
*/
if (!tp->writing)
return EIO;
/*
* Write till user request is satisfied
* Notice that the buffer is flushed as soon as it is filled rather
* than waiting for the next write or a close. This ensures that
* the flush in close writes a less than full buffer so the far
* end can detect the end-of-file condition.
*/
bp = buffer;
nleft = count;
while (nleft) {
nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
if (nleft < nfree)
ncopy = nleft;
else
ncopy = nfree;
memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
tp->nused += ncopy;
nleft -= ncopy;
bp += ncopy;
if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
int e = rtems_tftp_flush (tp);
if (e) {
tp->writing = 0;
set_errno_and_return_minus_one (e);
}
}
}
return count;
} }
rtems_device_driver rtems_tftp_control( /*
rtems_device_major_number major, * Dummy version to let fopen(xxxx,"w") work properly.
rtems_device_minor_number minor, */
void *pargp static int rtems_tftp_ftruncate(
rtems_libio_t *iop,
off_t count
) )
{ {
return RTEMS_NOT_CONFIGURED; return 0;
} }
rtems_filesystem_node_types_t rtems_tftp_node_type( rtems_filesystem_node_types_t rtems_tftp_node_type(
@@ -768,7 +855,7 @@ rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
NULL, /* lseek */ NULL, /* lseek */
NULL, /* fstat */ NULL, /* fstat */
NULL, /* fchmod */ NULL, /* fchmod */
NULL, /* ftruncate */ rtems_tftp_ftruncate, /* ftruncate */
NULL, /* fpathconf */ NULL, /* fpathconf */
NULL, /* fsync */ NULL, /* fsync */
NULL, /* fdatasync */ NULL, /* fdatasync */

View File

@@ -1,3 +1,7 @@
2000-12-14 Eric Norum <eric.norum@usask.ca>
* lib/tftpDriver.c: Added write capability.
2000-12-08 Joel Sherrill <joel@OARcorp.com> 2000-12-08 Joel Sherrill <joel@OARcorp.com>
* libc/linkaddr.c: Initialized variable to remove warning. * libc/linkaddr.c: Initialized variable to remove warning.

View File

@@ -1,4 +1,6 @@
/* /*
* vim: set expandtab tabstop=4 shiftwidth=4 ai :
*
* Trivial File Transfer Protocol (RFC 1350) * Trivial File Transfer Protocol (RFC 1350)
* *
* Transfer file to/from remote host * Transfer file to/from remote host
@@ -10,6 +12,7 @@
* eric@skatter.usask.ca * eric@skatter.usask.ca
* *
* $Id$ * $Id$
*
*/ */
#include <stdio.h> #include <stdio.h>
@@ -113,7 +116,7 @@ struct tftpStream {
union tftpPacket pkbuf; union tftpPacket pkbuf;
/* /*
* Last block number received * Last block number transferred
*/ */
rtems_unsigned16 blocknum; rtems_unsigned16 blocknum;
@@ -135,6 +138,7 @@ struct tftpStream {
*/ */
int firstReply; int firstReply;
int eof; int eof;
int writing;
}; };
/* /*
@@ -168,7 +172,7 @@ rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
6 /* posix_vdisable */ 6 /* posix_vdisable */
}; };
int rtems_tftp_mount_me( static int rtems_tftp_mount_me(
rtems_filesystem_mount_table_entry_t *temp_mt_entry rtems_filesystem_mount_table_entry_t *temp_mt_entry
) )
{ {
@@ -234,7 +238,7 @@ int rtems_bsdnet_initialize_tftp_filesystem ()
status = mount( status = mount(
&entry, &entry,
&rtems_tftp_ops, &rtems_tftp_ops,
RTEMS_FILESYSTEM_READ_ONLY, RTEMS_FILESYSTEM_READ_WRITE,
NULL, NULL,
TFTP_PATHNAME_PREFIX TFTP_PATHNAME_PREFIX
); );
@@ -246,15 +250,14 @@ int rtems_bsdnet_initialize_tftp_filesystem ()
} }
/* /*
* Set error message * Map error message
* This RTEMS/UNIX error mapping needs to be fixed!
*/ */
static void static int
tftpSetErrno (struct tftpStream *tp) tftpErrno (struct tftpStream *tp)
{ {
unsigned int tftpError; unsigned int tftpError;
static const int errorMap[] = { static const int errorMap[] = {
0, EINVAL,
ENOENT, ENOENT,
EPERM, EPERM,
ENOSPC, ENOSPC,
@@ -262,14 +265,13 @@ tftpSetErrno (struct tftpStream *tp)
ENXIO, ENXIO,
EEXIST, EEXIST,
ESRCH, ESRCH,
0,
}; };
tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
if (tftpError < (sizeof errorMap / sizeof errorMap[0])) if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
errno = errorMap[tftpError]; return errorMap[tftpError];
else else
errno = 1000 + tftpError; return 1000 + tftpError;
} }
/* /*
@@ -279,21 +281,24 @@ static void
sendStifle (struct tftpStream *tp, struct sockaddr_in *to) sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
{ {
int len; int len;
struct {
rtems_unsigned16 opcode;
rtems_unsigned16 errorCode;
char errorMessage[12];
} msg;
/* /*
* Create the error packet (Unknown transfer ID). * Create the error packet (Unknown transfer ID).
*/ */
tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR); msg.opcode = htons (TFTP_OPCODE_ERROR);
tp->pkbuf.tftpERROR.errorCode = htons (5); msg.errorCode = htons (5);
len = sizeof tp->pkbuf.tftpERROR.opcode + len = sizeof msg.opcode + sizeof msg.errorCode + 1;
sizeof tp->pkbuf.tftpERROR.errorCode + 1; len += sprintf (msg.errorMessage, "GO AWAY");
len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY");
/* /*
* Send it * Send it
*/ */
sendto (tp->socket, (char *)&tp->pkbuf, len, 0, sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to);
(struct sockaddr *)to, sizeof *to);
} }
/* /*
@@ -374,7 +379,7 @@ releaseStream (int s)
rtems_semaphore_release (tftp_mutex); rtems_semaphore_release (tftp_mutex);
} }
int rtems_tftp_evaluate_for_make( static int rtems_tftp_evaluate_for_make(
const char *path, /* IN */ const char *path, /* IN */
rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */
const char **name /* OUT */ const char **name /* OUT */
@@ -383,11 +388,8 @@ int rtems_tftp_evaluate_for_make(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
} }
/*
* XXX - Fix return values.
*/
int rtems_tftp_eval_path( static int rtems_tftp_eval_path(
const char *pathname, /* IN */ const char *pathname, /* IN */
int flags, /* IN */ int flags, /* IN */
rtems_filesystem_location_info_t *pathloc /* IN/OUT */ rtems_filesystem_location_info_t *pathloc /* IN/OUT */
@@ -395,35 +397,32 @@ int rtems_tftp_eval_path(
{ {
/* /*
* Read-only for now * Read-only or write-only for now
*/ */
flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
if ( (flags & O_WRONLY) == O_WRONLY ) if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) )
set_errno_and_return_minus_one( ENOENT ); set_errno_and_return_minus_one( EINVAL );
/* /*
* The File system is mounted at TFTP_PATHNAME_PREFIX * The File system is mounted at TFTP_PATHNAME_PREFIX
* the caller of this routine has striped off this part of the * the caller of this routine has striped off this part of the
* name. Save the remainder of the name for use by the open routine. * name. Save the remainder of the name for use by the open routine.
*/ */
pathloc->node_access = (void * ) pathname; pathloc->node_access = (void * ) pathname;
pathloc->handlers = &rtems_tftp_handlers; pathloc->handlers = &rtems_tftp_handlers;
return 0; return 0;
} }
static int rtems_tftp_open(
int rtems_tftp_open(
rtems_libio_t *iop, rtems_libio_t *iop,
const char *new_name, const char *new_name,
unsigned32 flag, unsigned32 flags,
unsigned32 mode unsigned32 mode
) )
{ {
struct tftpStream *tp; struct tftpStream *tp;
int retryCount; int retryCount;
rtems_unsigned32 farAddress; struct in_addr farAddress;
int s; int s;
int len; int len;
char *cp1; char *cp1;
@@ -435,33 +434,44 @@ int rtems_tftp_open(
/* /*
* This came from the evaluate path. * This came from the evaluate path.
* Extract host name component
*/ */
cp1 = cp2 = iop->file_info;
cp2 = iop->file_info;
cp1 = cp2;
while (*cp2 != '/') { while (*cp2 != '/') {
if (*cp2 == '\0') if (*cp2 == '\0')
return ENOENT; return ENOENT;
cp2++; cp2++;
} }
len = cp2 - cp1; len = cp2 - cp1;
hostname = malloc (len + 1); hostname = malloc (len + 1);
if (hostname == NULL) if (hostname == NULL)
return ENOMEM; return ENOMEM;
strncpy (hostname, cp1, len); strncpy (hostname, cp1, len);
hostname[len] = '\0'; hostname[len] = '\0';
farAddress = inet_addr (hostname);
/*
* Convert hostname to Internet address
*/
if (strcmp (hostname, "BOOTP_HOST") == 0)
farAddress = rtems_bsdnet_bootp_server_address;
else
farAddress.s_addr = inet_addr (hostname);
free (hostname); free (hostname);
if ((farAddress.s_addr == 0) || (farAddress.s_addr == ~0))
if ((farAddress == 0) || (farAddress == ~0))
return ENOENT; return ENOENT;
if (*++cp2 == '\0') /*
* Extract file pathname component
*/
while (*cp2 == '/')
cp2++;
if (strcmp (cp2, "BOOTP_FILE") == 0) {
cp2 = rtems_bsdnet_bootp_boot_file_name;
while (*cp2 == '/')
cp2++;
}
if (*cp2 == '\0')
return ENOENT; return ENOENT;
remoteFilename = cp2; remoteFilename = cp2;
if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
return ENOENT; return ENOENT;
@@ -469,16 +479,13 @@ int rtems_tftp_open(
/* /*
* Find a free stream * Find a free stream
*/ */
sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
if (sc != RTEMS_SUCCESSFUL) if (sc != RTEMS_SUCCESSFUL)
return EBUSY; return EBUSY;
for (s = 0 ; s < nStreams ; s++) { for (s = 0 ; s < nStreams ; s++) {
if (tftpStreams[s] == NULL) if (tftpStreams[s] == NULL)
break; break;
} }
if (s == nStreams) { if (s == nStreams) {
/* /*
* Reallocate stream pointers * Reallocate stream pointers
@@ -493,7 +500,6 @@ int rtems_tftp_open(
} }
tftpStreams = np; tftpStreams = np;
} }
tp = tftpStreams[s] = malloc (sizeof (struct tftpStream)); tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
rtems_semaphore_release (tftp_mutex); rtems_semaphore_release (tftp_mutex);
if (tp == NULL) if (tp == NULL)
@@ -504,7 +510,6 @@ int rtems_tftp_open(
/* /*
* Create the socket * Create the socket
*/ */
if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
releaseStream (s); releaseStream (s);
return ENOMEM; return ENOMEM;
@@ -513,7 +518,6 @@ int rtems_tftp_open(
/* /*
* Bind the socket to a local address * Bind the socket to a local address
*/ */
retryCount = 0; retryCount = 0;
rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
for (;;) { for (;;) {
@@ -536,7 +540,7 @@ int rtems_tftp_open(
* port on the remote machine. * port on the remote machine.
*/ */
tp->farAddress.sin_family = AF_INET; tp->farAddress.sin_family = AF_INET;
tp->farAddress.sin_addr.s_addr = farAddress; tp->farAddress.sin_addr = farAddress;
tp->farAddress.sin_port = htons (69); tp->farAddress.sin_port = htons (69);
/* /*
@@ -547,7 +551,14 @@ int rtems_tftp_open(
/* /*
* Create the request * Create the request
*/ */
if ((flags & O_ACCMODE) == O_RDONLY) {
tp->writing = 0;
tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
}
else {
tp->writing = 1;
tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
}
cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode; cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
cp2 = (char *) remoteFilename; cp2 = (char *) remoteFilename;
while ((*cp1++ = *cp2++) != '\0') while ((*cp1++ = *cp2++) != '\0')
@@ -574,7 +585,8 @@ int rtems_tftp_open(
len = getPacket (tp); len = getPacket (tp);
if (len >= (int) sizeof tp->pkbuf.tftpACK) { if (len >= (int) sizeof tp->pkbuf.tftpACK) {
int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
if ((opcode == TFTP_OPCODE_DATA) if (!tp->writing
&& (opcode == TFTP_OPCODE_DATA)
&& (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
tp->nused = 0; tp->nused = 0;
tp->blocknum = 1; tp->blocknum = 1;
@@ -587,11 +599,18 @@ int rtems_tftp_open(
} }
break; break;
} }
if (tp->writing
&& (opcode == TFTP_OPCODE_ACK)
&& (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
tp->nused = 0;
tp->blocknum = 1;
break;
}
if (opcode == TFTP_OPCODE_ERROR) { if (opcode == TFTP_OPCODE_ERROR) {
tftpSetErrno (tp); int e = tftpErrno (tp);
close (tp->socket); close (tp->socket);
releaseStream (s); releaseStream (s);
return EIO; return e;
} }
} }
@@ -604,45 +623,41 @@ int rtems_tftp_open(
return EIO; return EIO;
} }
} }
return 0; return 0;
} }
/* /*
* Read from a TFTP stream * Read from a TFTP stream
*/ */
static int rtems_tftp_read(
int rtems_tftp_read(
rtems_libio_t *iop, rtems_libio_t *iop,
void *buffer, void *buffer,
unsigned32 count unsigned32 count
) )
{ {
char *bp; char *bp;
struct tftpStream *tp; struct tftpStream *tp = iop->data1;
int retryCount; int retryCount;
int nwant; int nwant;
tp = iop->data1;
/* /*
* Read till user request is satisfied or EOF is reached * Read till user request is satisfied or EOF is reached
*/ */
bp = buffer; bp = buffer;
nwant = count; nwant = count;
while (nwant) { while (nwant) {
if (tp->nleft) { if (tp->nleft) {
int count; int ncopy;
if (nwant < tp->nleft) if (nwant < tp->nleft)
count = nwant; ncopy = nwant;
else else
count = tp->nleft; ncopy = tp->nleft;
memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count); memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
tp->nused += count; tp->nused += ncopy;
tp->nleft -= count; tp->nleft -= ncopy;
bp += count; bp += ncopy;
nwant -= count; nwant -= ncopy;
if (nwant == 0) if (nwant == 0)
break; break;
} }
@@ -668,10 +683,8 @@ int rtems_tftp_read(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
break; break;
} }
if (opcode == TFTP_OPCODE_ERROR) { if (opcode == TFTP_OPCODE_ERROR)
tftpSetErrno (tp); set_errno_and_return_minus_one( tftpErrno (tp) );
return RTEMS_INTERNAL_ERROR;
}
} }
/* /*
@@ -683,23 +696,57 @@ int rtems_tftp_read(
set_errno_and_return_minus_one( EIO ); set_errno_and_return_minus_one( EIO );
} }
} }
return count - nwant;
}
/*
* Flush a write buffer and wait for acknowledgement
*/
static int rtems_tftp_flush ( struct tftpStream *tp )
{
int wlen, rlen;
int retryCount = 0;
wlen = tp->nused + 2 * sizeof (rtems_unsigned16);
for (;;) {
tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
(struct sockaddr *)&tp->farAddress,
sizeof tp->farAddress) < 0)
return EIO;
rlen = getPacket (tp);
if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
if ((opcode == TFTP_OPCODE_ACK)
&& (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
tp->nused = 0;
tp->blocknum++;
return 0;
}
if (opcode == TFTP_OPCODE_ERROR)
return tftpErrno (tp);
}
/* /*
* XXX - Eric is this right? * Keep trying?
*
*/ */
return count - nwant; if (++retryCount == IO_RETRY_LIMIT)
return EIO;
}
} }
/* /*
* Close a TFTP stream * Close a TFTP stream
*/ */
int rtems_tftp_close( static int rtems_tftp_close(
rtems_libio_t *iop rtems_libio_t *iop
) )
{ {
struct tftpStream *tp = iop->data1;; struct tftpStream *tp = iop->data1;;
if (tp->writing)
rtems_tftp_flush (tp);
if (!tp->eof && !tp->firstReply) { if (!tp->eof && !tp->firstReply) {
/* /*
* Tell the other end to stop * Tell the other end to stop
@@ -714,22 +761,62 @@ int rtems_tftp_close(
return RTEMS_SUCCESSFUL; return RTEMS_SUCCESSFUL;
} }
int rtems_tftp_write( static int rtems_tftp_write(
rtems_libio_t *iop, rtems_libio_t *iop,
const void *buffer, const void *buffer,
unsigned32 count unsigned32 count
) )
{ {
return RTEMS_NOT_CONFIGURED; const char *bp;
struct tftpStream *tp = iop->data1;
int nleft, nfree, ncopy;
/*
* Bail out if an error has occurred
*/
if (!tp->writing)
return EIO;
/*
* Write till user request is satisfied
* Notice that the buffer is flushed as soon as it is filled rather
* than waiting for the next write or a close. This ensures that
* the flush in close writes a less than full buffer so the far
* end can detect the end-of-file condition.
*/
bp = buffer;
nleft = count;
while (nleft) {
nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
if (nleft < nfree)
ncopy = nleft;
else
ncopy = nfree;
memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
tp->nused += ncopy;
nleft -= ncopy;
bp += ncopy;
if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
int e = rtems_tftp_flush (tp);
if (e) {
tp->writing = 0;
set_errno_and_return_minus_one (e);
}
}
}
return count;
} }
rtems_device_driver rtems_tftp_control( /*
rtems_device_major_number major, * Dummy version to let fopen(xxxx,"w") work properly.
rtems_device_minor_number minor, */
void *pargp static int rtems_tftp_ftruncate(
rtems_libio_t *iop,
off_t count
) )
{ {
return RTEMS_NOT_CONFIGURED; return 0;
} }
rtems_filesystem_node_types_t rtems_tftp_node_type( rtems_filesystem_node_types_t rtems_tftp_node_type(
@@ -768,7 +855,7 @@ rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
NULL, /* lseek */ NULL, /* lseek */
NULL, /* fstat */ NULL, /* fstat */
NULL, /* fchmod */ NULL, /* fchmod */
NULL, /* ftruncate */ rtems_tftp_ftruncate, /* ftruncate */
NULL, /* fpathconf */ NULL, /* fpathconf */
NULL, /* fsync */ NULL, /* fsync */
NULL, /* fdatasync */ NULL, /* fdatasync */