From a28eafb2a4d7418ad1efb3f8b72e9cbb014c1c0b Mon Sep 17 00:00:00 2001 From: Vijay Kumar Banerjee Date: Wed, 24 Feb 2021 12:27:29 -0700 Subject: cpukit: Move ftpfs from libnetworking to libfs Update #3850 --- cpukit/libfs/src/ftpfs/ftpfs.c | 1430 +++++++++++++++++++++++++++++++++ cpukit/libfs/src/ftpfs/tftpDriver.c | 1066 ++++++++++++++++++++++++ cpukit/libnetworking/lib/ftpfs.c | 1430 --------------------------------- cpukit/libnetworking/lib/tftpDriver.c | 1066 ------------------------ spec/build/cpukit/libftpfs.yml | 2 +- spec/build/cpukit/libtftpfs.yml | 2 +- 6 files changed, 2498 insertions(+), 2498 deletions(-) create mode 100644 cpukit/libfs/src/ftpfs/ftpfs.c create mode 100644 cpukit/libfs/src/ftpfs/tftpDriver.c delete mode 100644 cpukit/libnetworking/lib/ftpfs.c delete mode 100644 cpukit/libnetworking/lib/tftpDriver.c diff --git a/cpukit/libfs/src/ftpfs/ftpfs.c b/cpukit/libfs/src/ftpfs/ftpfs.c new file mode 100644 index 0000000000..5e0cb95dd3 --- /dev/null +++ b/cpukit/libfs/src/ftpfs/ftpfs.c @@ -0,0 +1,1430 @@ +/** + * @file + * + * File Transfer Protocol file system (FTP client). + */ + +/* + * Copyright (c) 2009-2012 embedded brains GmbH. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * + * + * (c) Copyright 2002 + * Thomas Doerfler + * IMD Ingenieurbuero fuer Microcomputertechnik + * Herbststr. 8 + * 82178 Puchheim, Germany + * + * + * This code has been created after closly inspecting "tftpdriver.c" from Eric + * Norum. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef DEBUG + #define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else + #define DEBUG_PRINTF(...) +#endif + +/** + * Connection entry for each open file stream. + */ +typedef struct { + off_t file_size; + + /** + * Control connection socket. + */ + int ctrl_socket; + + uint32_t client_address; + + /** + * Data transfer socket. + */ + int data_socket; + + /** + * Current index into the reply buffer. + */ + size_t reply_current; + + /** + * End index of the reply buffer. + */ + size_t reply_end; + + /** + * Buffer for relpy data. + */ + char reply_buffer [128]; + + /** + * End of file flag. + */ + bool eof; + + bool write; + + /** + * Indicates if we should do a SIZE command. + * + * The first call to the rtems_ftpfs_fstat() handler is issued by the path + * evaluation to check for access permission. For this case we avoid the + * SIZE command. + */ + bool do_size_command; + + ino_t ino; + + const char *user; + + const char *password; + + const char *hostname; + + const char *filename; + + char buffer []; +} rtems_ftpfs_entry; + +/** + * Mount entry for each file system instance. + */ +typedef struct { + /** + * Verbose mode enabled or disabled. + */ + bool verbose; + + /** + * Timeout value + */ + struct timeval timeout; + + /** + * Inode counter. + */ + ino_t ino; +} rtems_ftpfs_mount_entry; + +static const rtems_filesystem_operations_table rtems_ftpfs_ops; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers; + +static bool rtems_ftpfs_use_timeout(const struct timeval *to) +{ + return to->tv_sec != 0 || to->tv_usec != 0; +} + +static int rtems_ftpfs_set_connection_timeout( + int socket, + const struct timeval *to +) +{ + if (rtems_ftpfs_use_timeout(to)) { + int rv = 0; + + rv = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof(*to)); + if (rv != 0) { + return EIO; + } + + rv = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof(*to)); + if (rv != 0) { + return EIO; + } + } + + return 0; +} + +static rtems_status_code rtems_ftpfs_do_ioctl( + const char *mount_point, + ioctl_command_t req, + ... +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + int rv = 0; + int fd = 0; + va_list ap; + + if (mount_point == NULL) { + mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT; + } + + fd = open(mount_point, O_RDWR); + if (fd < 0) { + return RTEMS_INVALID_NAME; + } + + va_start(ap, req); + rv = ioctl(fd, req, va_arg(ap, void *)); + va_end(ap); + if (rv != 0) { + sc = RTEMS_INVALID_NUMBER; + } + + rv = close(fd); + if (rv != 0 && sc == RTEMS_SUCCESSFUL) { + sc = RTEMS_IO_ERROR; + } + + return sc; +} + +rtems_status_code rtems_ftpfs_get_verbose(const char *mount_point, bool *verbose) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_GET_VERBOSE, + verbose + ); +} + +rtems_status_code rtems_ftpfs_set_verbose(const char *mount_point, bool verbose) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_SET_VERBOSE, + &verbose + ); +} + +rtems_status_code rtems_ftpfs_get_timeout( + const char *mount_point, + struct timeval *timeout +) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_GET_TIMEOUT, + timeout + ); +} + +rtems_status_code rtems_ftpfs_set_timeout( + const char *mount_point, + const struct timeval *timeout +) +{ + return rtems_ftpfs_do_ioctl( + mount_point, + RTEMS_FTPFS_IOCTL_SET_TIMEOUT, + timeout + ); +} + +typedef void (*rtems_ftpfs_reply_parser)( + const char * /* reply fragment */, + size_t /* reply fragment length */, + void * /* parser argument */ +); + +typedef enum { + RTEMS_FTPFS_REPLY_START, + RTEMS_FTPFS_REPLY_SINGLE_LINE, + RTEMS_FTPFS_REPLY_DONE, + RTEMS_FTPFS_REPLY_MULTI_LINE, + RTEMS_FTPFS_REPLY_MULTI_LINE_START +} rtems_ftpfs_reply_state; + +typedef enum { + RTEMS_FTPFS_REPLY_ERROR = 0, + RTEMS_FTPFS_REPLY_1 = '1', + RTEMS_FTPFS_REPLY_2 = '2', + RTEMS_FTPFS_REPLY_3 = '3', + RTEMS_FTPFS_REPLY_4 = '4', + RTEMS_FTPFS_REPLY_5 = '5' +} rtems_ftpfs_reply; + +#define RTEMS_FTPFS_REPLY_SIZE 3 + +static bool rtems_ftpfs_is_reply_code_valid(unsigned char *reply) +{ + return isdigit(reply [0]) && isdigit(reply [1]) && isdigit(reply [2]); +} + +static rtems_ftpfs_reply rtems_ftpfs_get_reply( + rtems_ftpfs_entry *e, + rtems_ftpfs_reply_parser parser, + void *parser_arg, + bool verbose +) +{ + rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START; + unsigned char reply_code [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' }; + size_t reply_code_index = 0; + + while (state != RTEMS_FTPFS_REPLY_DONE) { + char *buf = NULL; + size_t i = 0; + size_t n = 0; + + /* Receive reply fragment from socket */ + if (e->reply_current == e->reply_end) { + ssize_t rv = 0; + + e->reply_current = 0; + e->reply_end = 0; + + rv = recv( + e->ctrl_socket, + &e->reply_buffer [0], + sizeof(e->reply_buffer), + 0 + ); + + if (rv > 0) { + e->reply_end = (size_t) rv; + } else { + return RTEMS_FTPFS_REPLY_ERROR; + } + } + + buf = &e->reply_buffer [e->reply_current]; + n = e->reply_end - e->reply_current; + + /* Invoke parser if necessary */ + if (parser != NULL) { + parser(buf, n, parser_arg); + } + + /* Parse reply fragment */ + for (i = 0; i < n && state != RTEMS_FTPFS_REPLY_DONE; ++i) { + char c = buf [i]; + + switch (state) { + case RTEMS_FTPFS_REPLY_START: + if (reply_code_index < RTEMS_FTPFS_REPLY_SIZE) { + reply_code [reply_code_index] = c; + ++reply_code_index; + } else if (rtems_ftpfs_is_reply_code_valid(reply_code)) { + if (c == '-') { + state = RTEMS_FTPFS_REPLY_MULTI_LINE; + } else { + state = RTEMS_FTPFS_REPLY_SINGLE_LINE; + } + } else { + return RTEMS_FTPFS_REPLY_ERROR; + } + break; + case RTEMS_FTPFS_REPLY_SINGLE_LINE: + if (c == '\n') { + state = RTEMS_FTPFS_REPLY_DONE; + } + break; + case RTEMS_FTPFS_REPLY_MULTI_LINE: + if (c == '\n') { + state = RTEMS_FTPFS_REPLY_MULTI_LINE_START; + reply_code_index = 0; + } + break; + case RTEMS_FTPFS_REPLY_MULTI_LINE_START: + if (reply_code_index < RTEMS_FTPFS_REPLY_SIZE) { + if (reply_code [reply_code_index] == c) { + ++reply_code_index; + } else { + state = RTEMS_FTPFS_REPLY_MULTI_LINE; + } + } else { + if (c == ' ') { + state = RTEMS_FTPFS_REPLY_SINGLE_LINE; + } else { + state = RTEMS_FTPFS_REPLY_MULTI_LINE; + } + } + break; + default: + return RTEMS_FTPFS_REPLY_ERROR; + } + } + + /* Be verbose if necessary */ + if (verbose) { + write(STDERR_FILENO, buf, i); + } + + /* Update reply index */ + e->reply_current += i; + } + + return reply_code [0]; +} + +static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser( + rtems_ftpfs_entry *e, + const char *cmd, + const char *arg, + rtems_ftpfs_reply_parser parser, + void *parser_arg, + bool verbose +) +{ + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + size_t cmd_len = strlen(cmd); + size_t arg_len = arg != NULL ? strlen(arg) : 0; + size_t len = cmd_len + arg_len + 2; + char *buf = malloc(len); + + if (buf != NULL) { + ssize_t n = 0; + char *buf_arg = buf + cmd_len; + char *buf_eol = buf_arg + arg_len; + + memcpy(buf, cmd, cmd_len); + memcpy(buf_arg, arg, arg_len); + buf_eol [0] = '\r'; + buf_eol [1] = '\n'; + + /* Send */ + n = send(e->ctrl_socket, buf, len, 0); + if (n == (ssize_t) len) { + if (verbose) { + write(STDERR_FILENO, buf, len); + } + + /* Reply */ + reply = rtems_ftpfs_get_reply(e, parser, parser_arg, verbose); + } + + free(buf); + } + + return reply; +} + +static rtems_ftpfs_reply rtems_ftpfs_send_command( + rtems_ftpfs_entry *e, + const char *cmd, + const char *arg, + bool verbose +) +{ + return rtems_ftpfs_send_command_with_parser( + e, + cmd, + arg, + NULL, + NULL, + verbose + ); +} + +typedef enum { + STATE_USER_NAME, + STATE_START_PASSWORD, + STATE_START_HOST_NAME, + STATE_START_HOST_NAME_OR_PATH, + STATE_START_PATH, + STATE_PASSWORD, + STATE_HOST_NAME, + STATE_DONE, + STATE_INVALID +} split_state; + +static int rtems_ftpfs_split_names ( + char *s, + const char **user, + const char **password, + const char **hostname, + const char **path +) +{ + split_state state = STATE_USER_NAME; + size_t len = strlen(s); + size_t i = 0; + + *user = s; + + for (i = 0; i < len; ++i) { + char c = s [i]; + + switch (state) { + case STATE_USER_NAME: + if (c == ':') { + state = STATE_START_PASSWORD; + s [i] = '\0'; + } else if (c == '@') { + state = STATE_START_HOST_NAME; + s [i] = '\0'; + } else if (c == '/') { + state = STATE_START_HOST_NAME_OR_PATH; + s [i] = '\0'; + } + break; + case STATE_START_PASSWORD: + state = STATE_PASSWORD; + *password = &s [i]; + --i; + break; + case STATE_START_HOST_NAME: + state = STATE_HOST_NAME; + *hostname = &s [i]; + --i; + break; + case STATE_START_HOST_NAME_OR_PATH: + if (c == '@') { + state = STATE_START_HOST_NAME; + } else { + state = STATE_DONE; + *path = &s [i]; + goto done; + } + break; + case STATE_START_PATH: + state = STATE_DONE; + *path = &s [i]; + goto done; + case STATE_PASSWORD: + if (c == '@') { + state = STATE_START_HOST_NAME; + s [i] = '\0'; + } else if (c == '/') { + state = STATE_START_HOST_NAME_OR_PATH; + s [i] = '\0'; + } + break; + case STATE_HOST_NAME: + if (c == '/') { + state = STATE_START_PATH; + s [i] = '\0'; + } + break; + default: + state = STATE_INVALID; + goto done; + } + } + +done: + + /* This is a special case with no username and password */ + if (*hostname == NULL) { + *hostname = &s [0]; + *user = "anonymous"; + *password = *user; + } + + /* If we have no password use the user name */ + if (*password == NULL) { + *password = *user; + } + + return state == STATE_DONE ? 0 : ENOENT; +} + +static socklen_t rtems_ftpfs_create_address( + struct sockaddr_in *sa, + unsigned long address, + unsigned short port +) +{ + memset(sa, 0, sizeof(*sa)); + + sa->sin_family = AF_INET; + sa->sin_addr.s_addr = address; + sa->sin_port = port; + sa->sin_len = sizeof(*sa); + + return sizeof(*sa); +} + +static int rtems_ftpfs_close_data_connection( + rtems_ftpfs_entry *e, + bool verbose, + bool error +) +{ + int eno = 0; + + /* Close data connection if necessary */ + if (e->data_socket >= 0) { + int rv = close(e->data_socket); + + e->data_socket = -1; + if (rv != 0) { + eno = EIO; + } + + /* For write connections we have to obtain the transfer reply */ + if (e->write && !error) { + rtems_ftpfs_reply reply = + rtems_ftpfs_get_reply(e, NULL, NULL, verbose); + + if (reply != RTEMS_FTPFS_REPLY_2) { + eno = EIO; + } + } + } + + return eno; +} + +static int rtems_ftpfs_open_ctrl_connection( + rtems_ftpfs_entry *e, + bool verbose, + const struct timeval *timeout +) +{ + int rv = 0; + int eno = 0; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + struct in_addr address = { .s_addr = 0 }; + struct sockaddr_in sa; + socklen_t size = 0; + + /* Create the socket for the control connection */ + e->ctrl_socket = socket(AF_INET, SOCK_STREAM, 0); + if (e->ctrl_socket < 0) { + return ENOMEM; + } + + /* Set up the server address from the hostname */ + if (inet_aton(e->hostname, &address) == 0) { + /* Try to get the address by name */ + struct hostent *he = gethostbyname(e->hostname); + + if (he != NULL) { + memcpy(&address, he->h_addr, sizeof(address)); + } else { + return ENOENT; + } + } + rtems_ftpfs_create_address(&sa, address.s_addr, htons(RTEMS_FTPFS_CTRL_PORT)); + DEBUG_PRINTF("server = %s\n", inet_ntoa(sa.sin_addr)); + + /* Open control connection */ + rv = connect( + e->ctrl_socket, + (struct sockaddr *) &sa, + sizeof(sa) + ); + if (rv != 0) { + return ENOENT; + } + + /* Set control connection timeout */ + eno = rtems_ftpfs_set_connection_timeout(e->ctrl_socket, timeout); + if (eno != 0) { + return eno; + } + + /* Get client address */ + size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); + rv = getsockname( + e->ctrl_socket, + (struct sockaddr *) &sa, + &size + ); + if (rv != 0) { + return ENOMEM; + } + e->client_address = ntohl(sa.sin_addr.s_addr); + DEBUG_PRINTF("client = %s\n", inet_ntoa(sa.sin_addr)); + + /* Now we should get a welcome message from the server */ + reply = rtems_ftpfs_get_reply(e, NULL, NULL, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + return ENOENT; + } + + /* Send USER command */ + reply = rtems_ftpfs_send_command(e, "USER ", e->user, verbose); + if (reply == RTEMS_FTPFS_REPLY_3) { + /* Send PASS command */ + reply = rtems_ftpfs_send_command(e, "PASS ", e->password, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + return EACCES; + } + + /* TODO: Some server may require an account */ + } else if (reply != RTEMS_FTPFS_REPLY_2) { + return EACCES; + } + + /* Send TYPE command to set binary mode for all data transfers */ + reply = rtems_ftpfs_send_command(e, "TYPE I", NULL, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + return EIO; + } + + return 0; +} + +static int rtems_ftpfs_open_data_connection_active( + rtems_ftpfs_entry *e, + const char *file_command, + bool verbose, + const struct timeval *timeout +) +{ + int rv = 0; + int eno = 0; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + struct sockaddr_in sa; + socklen_t size = 0; + int port_socket = -1; + char port_command [] = "PORT 000,000,000,000,000,000"; + uint16_t data_port = 0; + + /* Create port socket to establish a data data connection */ + port_socket = socket(AF_INET, SOCK_STREAM, 0); + if (port_socket < 0) { + eno = ENOMEM; + goto cleanup; + } + + /* Bind port socket */ + rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); + rv = bind( + port_socket, + (struct sockaddr *) &sa, + sizeof(sa) + ); + if (rv != 0) { + eno = EBUSY; + goto cleanup; + } + + /* Get port number for data socket */ + size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); + rv = getsockname( + port_socket, + (struct sockaddr *) &sa, + &size + ); + if (rv != 0) { + eno = ENOMEM; + goto cleanup; + } + data_port = ntohs(sa.sin_port); + + /* Send PORT command to set data connection port for server */ + snprintf( + port_command, + sizeof(port_command), + "PORT %lu,%lu,%lu,%lu,%lu,%lu", + (e->client_address >> 24) & 0xffUL, + (e->client_address >> 16) & 0xffUL, + (e->client_address >> 8) & 0xffUL, + (e->client_address >> 0) & 0xffUL, + (data_port >> 8) & 0xffUL, + (data_port >> 0) & 0xffUL + ); + reply = rtems_ftpfs_send_command(e, port_command, NULL, verbose); + if (reply != RTEMS_FTPFS_REPLY_2) { + eno = ENOTSUP; + goto cleanup; + } + + /* Listen on port socket for incoming data connections */ + rv = listen(port_socket, 1); + if (rv != 0) { + eno = EBUSY; + goto cleanup; + } + + /* Send RETR or STOR command with filename */ + reply = rtems_ftpfs_send_command(e, file_command, e->filename, verbose); + if (reply != RTEMS_FTPFS_REPLY_1) { + eno = EIO; + goto cleanup; + } + + /* Wait for connect on data connection if necessary */ + if (rtems_ftpfs_use_timeout(timeout)) { + struct timeval to = *timeout; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(port_socket, &fds); + + rv = select(port_socket + 1, &fds, NULL, NULL, &to); + if (rv <= 0) { + eno = EIO; + goto cleanup; + } + } + + /* Accept data connection */ + size = sizeof(sa); + e->data_socket = accept( + port_socket, + (struct sockaddr *) &sa, + &size + ); + if (e->data_socket < 0) { + eno = EIO; + goto cleanup; + } + +cleanup: + + /* Close port socket if necessary */ + if (port_socket >= 0) { + rv = close(port_socket); + if (rv != 0) { + eno = EIO; + } + } + + return eno; +} + +typedef enum { + RTEMS_FTPFS_PASV_START = 0, + RTEMS_FTPFS_PASV_JUNK, + RTEMS_FTPFS_PASV_DATA, + RTEMS_FTPFS_PASV_DONE +} rtems_ftpfs_pasv_state; + +typedef struct { + rtems_ftpfs_pasv_state state; + size_t index; + uint8_t data [6]; +} rtems_ftpfs_pasv_entry; + +static void rtems_ftpfs_pasv_parser( + const char* buf, + size_t len, + void *arg +) +{ + rtems_ftpfs_pasv_entry *pe = arg; + size_t i = 0; + + for (i = 0; i < len; ++i) { + int c = buf [i]; + + switch (pe->state) { + case RTEMS_FTPFS_PASV_START: + if (!isdigit(c)) { + pe->state = RTEMS_FTPFS_PASV_JUNK; + pe->index = 0; + } + break; + case RTEMS_FTPFS_PASV_JUNK: + if (isdigit(c)) { + pe->state = RTEMS_FTPFS_PASV_DATA; + pe->data [pe->index] = (uint8_t) (c - '0'); + } + break; + case RTEMS_FTPFS_PASV_DATA: + if (isdigit(c)) { + pe->data [pe->index] = + (uint8_t) (pe->data [pe->index] * 10 + c - '0'); + } else if (c == ',') { + ++pe->index; + if (pe->index < sizeof(pe->data)) { + pe->data [pe->index] = 0; + } else { + pe->state = RTEMS_FTPFS_PASV_DONE; + } + } else { + pe->state = RTEMS_FTPFS_PASV_DONE; + } + break; + default: + return; + } + } +} + +static int rtems_ftpfs_open_data_connection_passive( + rtems_ftpfs_entry *e, + const char *file_command, + bool verbose, + const struct timeval *timeout +) +{ + int rv = 0; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + struct sockaddr_in sa; + uint32_t data_address = 0; + uint16_t data_port = 0; + rtems_ftpfs_pasv_entry pe; + + memset(&pe, 0, sizeof(pe)); + + /* Send PASV command */ + reply = rtems_ftpfs_send_command_with_parser( + e, + "PASV", + NULL, + rtems_ftpfs_pasv_parser, + &pe, + verbose + ); + if (reply != RTEMS_FTPFS_REPLY_2) { + return ENOTSUP; + } + data_address = ((uint32_t)(pe.data [0]) << 24) + + ((uint32_t)(pe.data [1]) << 16) + + ((uint32_t)(pe.data [2]) << 8) + + ((uint32_t)(pe.data [3])); + data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]); + rtems_ftpfs_create_address(&sa, htonl(data_address), htons(data_port)); + DEBUG_PRINTF( + "server data = %s:%u\n", + inet_ntoa(sa.sin_addr), + (unsigned) ntohs(sa.sin_port) + ); + + /* Create data socket */ + e->data_socket = socket(AF_INET, SOCK_STREAM, 0); + if (e->data_socket < 0) { + return ENOMEM; + } + + /* Open data connection */ + rv = connect( + e->data_socket, + (struct sockaddr *) &sa, + sizeof(sa) + ); + if (rv != 0) { + return EIO; + } + + /* Send RETR or STOR command with filename */ + reply = rtems_ftpfs_send_command(e, file_command, e->filename, verbose); + if (reply != RTEMS_FTPFS_REPLY_1) { + return EIO; + } + + return 0; +} + +typedef enum { + RTEMS_FTPFS_SIZE_START = 0, + RTEMS_FTPFS_SIZE_SPACE, + RTEMS_FTPFS_SIZE_NUMBER, + RTEMS_FTPFS_SIZE_NL +} rtems_ftpfs_size_state; + +typedef struct { + rtems_ftpfs_size_state state; + size_t index; + off_t size; +} rtems_ftpfs_size_entry; + +static void rtems_ftpfs_size_parser( + const char* buf, + size_t len, + void *arg +) +{ + rtems_ftpfs_size_entry *se = arg; + size_t i = 0; + + for (i = 0; se->size >= 0 && i < len; ++i, ++se->index) { + int c = buf [i]; + + switch (se->state) { + case RTEMS_FTPFS_SIZE_START: + if (se->index == 2) { + se->state = RTEMS_FTPFS_SIZE_SPACE; + } + break; + case RTEMS_FTPFS_SIZE_SPACE: + if (c == ' ') { + se->state = RTEMS_FTPFS_SIZE_NUMBER; + } else { + se->size = -1; + } + break; + case RTEMS_FTPFS_SIZE_NUMBER: + if (isdigit(c)) { + se->size = 10 * se->size + c - '0'; + } else if (c == '\r') { + se->state = RTEMS_FTPFS_SIZE_NL; + } else { + se->size = -1; + } + break; + case RTEMS_FTPFS_SIZE_NL: + if (c != '\n') { + se->size = -1; + } + break; + default: + se->size = -1; + break; + } + } +} + +static void rtems_ftpfs_get_file_size(rtems_ftpfs_entry *e, bool verbose) +{ + if (e->file_size < 0) { + if (e->write) { + e->file_size = 0; + } else { + rtems_ftpfs_size_entry se; + rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; + + memset(&se, 0, sizeof(se)); + + reply = rtems_ftpfs_send_command_with_parser( + e, + "SIZE ", + e->filename, + rtems_ftpfs_size_parser, + &se, + verbose + ); + if (reply == RTEMS_FTPFS_REPLY_2 && se.size >= 0) { + e->file_size = se.size; + } else { + e->file_size = 0; + } + } + } +} + +static int rtems_ftpfs_open( + rtems_libio_t *iop, + const char *path, + int oflag, + mode_t mode +) +{ + int eno = 0; + rtems_ftpfs_entry *e = iop->pathinfo.node_access; + rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool verbose = me->verbose; + const struct timeval *timeout = &me->timeout; + + e->write = rtems_libio_iop_is_writeable(iop); + + /* Check for either read-only or write-only flags */ + if ( + rtems_libio_iop_is_writeable(iop) + && rtems_libio_iop_is_readable(iop) + ) { + eno = ENOTSUP; + } + + if (eno == 0) { + rtems_ftpfs_get_file_size(e, verbose); + } + + if (eno == 0) { + const char *file_command = e->write ? "STOR " : "RETR "; + + /* Open passive data connection */ + eno = rtems_ftpfs_open_data_connection_passive( + e, + file_command, + verbose, + timeout + ); + if (eno == ENOTSUP) { + /* Open active data connection */ + eno = rtems_ftpfs_open_data_connection_active( + e, + file_command, + verbose, + timeout + ); + } + } + + /* Set data connection timeout */ + if (eno == 0) { + eno = rtems_ftpfs_set_connection_timeout(e->data_socket, timeout); + } + + if (eno == 0) { + return 0; + } else { + rtems_ftpfs_close_data_connection(e, verbose, true); + + rtems_set_errno_and_return_minus_one(eno); + } +} + +static ssize_t rtems_ftpfs_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + rtems_ftpfs_entry *e = iop->pathinfo.node_access; + const rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool verbose = me->verbose; + char *in = buffer; + size_t todo = count; + + if (e->eof) { + return 0; + } + + while (todo > 0) { + ssize_t rv = recv(e->data_socket, in, todo, 0); + + if (rv <= 0) { + if (rv == 0) { + rtems_ftpfs_reply reply = + rtems_ftpfs_get_reply(e, NULL, NULL, verbose); + + if (reply == RTEMS_FTPFS_REPLY_2) { + e->eof = true; + break; + } + } + + rtems_set_errno_and_return_minus_one(EIO); + } + + in += rv; + todo -= (size_t) rv; + } + + return (ssize_t) (count - todo); +} + +static ssize_t rtems_ftpfs_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + rtems_ftpfs_entry *e = iop->pathinfo.node_access; + const char *out = buffer; + size_t todo = count; + + while (todo > 0) { + ssize_t rv = send(e->data_socket, out, todo, 0); + + if (rv <= 0) { + if (rv == 0) { + break; + } else { + rtems_set_errno_and_return_minus_one(EIO); + } + } + + out += rv; + todo -= (size_t) rv; + + e->file_size += rv; + } + + return (ssize_t) (count - todo); +} + +static int rtems_ftpfs_close(rtems_libio_t *iop) +{ + rtems_ftpfs_entry *e = iop->pathinfo.node_access; + const rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + int eno = rtems_ftpfs_close_data_connection(e, me->verbose, false); + + if (eno == 0) { + return 0; + } else { + rtems_set_errno_and_return_minus_one(eno); + } +} + +/* Dummy version to let fopen(*,"w") work properly */ +static int rtems_ftpfs_ftruncate(rtems_libio_t *iop, off_t count) +{ + return 0; +} + +static void rtems_ftpfs_eval_path( + rtems_filesystem_eval_path_context_t *self +) +{ + int eno = 0; + + rtems_filesystem_eval_path_eat_delimiter(self); + + if (rtems_filesystem_eval_path_has_path(self)) { + const char *path = rtems_filesystem_eval_path_get_path(self); + size_t pathlen = rtems_filesystem_eval_path_get_pathlen(self); + rtems_ftpfs_entry *e = calloc(1, sizeof(*e) + pathlen + 1); + + rtems_filesystem_eval_path_clear_path(self); + + if (e != NULL) { + memcpy(e->buffer, path, pathlen); + + eno = rtems_ftpfs_split_names( + e->buffer, + &e->user, + &e->password, + &e->hostname, + &e->filename + ); + + DEBUG_PRINTF( + "user = '%s', password = '%s', filename = '%s'\n", + e->user, + e->password, + e->filename + ); + + if (eno == 0) { + rtems_filesystem_location_info_t *currentloc = + rtems_filesystem_eval_path_get_currentloc(self); + rtems_ftpfs_mount_entry *me = currentloc->mt_entry->fs_info; + + rtems_libio_lock(); + ++me->ino; + e->ino = me->ino; + rtems_libio_unlock(); + + e->file_size = -1; + e->ctrl_socket = -1; + + eno = rtems_ftpfs_open_ctrl_connection( + e, + me->verbose, + &me->timeout + ); + if (eno == 0) { + currentloc->node_access = e; + currentloc->handlers = &rtems_ftpfs_handlers; + } + } + + if (eno != 0) { + free(e); + } + } else { + eno = ENOMEM; + } + } + + if (eno != 0) { + rtems_filesystem_eval_path_error(self, eno); + } +} + +static void rtems_ftpfs_free_node(const rtems_filesystem_location_info_t *loc) +{ + rtems_ftpfs_entry *e = loc->node_access; + + /* The root node handler has no entry */ + if (e != NULL) { + const rtems_ftpfs_mount_entry *me = loc->mt_entry->fs_info; + + /* Close control connection if necessary */ + if (e->ctrl_socket >= 0) { + rtems_ftpfs_send_command(e, "QUIT", NULL, me->verbose); + + close(e->ctrl_socket); + } + + free(e); + } +} + +int rtems_ftpfs_initialize( + rtems_filesystem_mount_table_entry_t *e, + const void *d +) +{ + rtems_ftpfs_mount_entry *me = calloc(1, sizeof(*me)); + + /* Mount entry for FTP file system instance */ + e->fs_info = me; + if (e->fs_info == NULL) { + rtems_set_errno_and_return_minus_one(ENOMEM); + } + me->verbose = false; + me->timeout.tv_sec = 0; + me->timeout.tv_usec = 0; + + /* Set handler and oparations table */ + e->mt_fs_root->location.handlers = &rtems_ftpfs_root_handlers; + e->ops = &rtems_ftpfs_ops; + + /* We maintain no real file system nodes, so there is no real root */ + e->mt_fs_root->location.node_access = NULL; + + return 0; +} + +static void rtems_ftpfs_unmount_me( + rtems_filesystem_mount_table_entry_t *e +) +{ + free(e->fs_info); +} + +static int rtems_ftpfs_ioctl( + rtems_libio_t *iop, + ioctl_command_t command, + void *arg +) +{ + rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; + bool *verbose = arg; + struct timeval *timeout = arg; + + if (arg == NULL) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + + switch (command) { + case RTEMS_FTPFS_IOCTL_GET_VERBOSE: + *verbose = me->verbose; + break; + case RTEMS_FTPFS_IOCTL_SET_VERBOSE: + me->verbose = *verbose; + break; + case RTEMS_FTPFS_IOCTL_GET_TIMEOUT: + *timeout = me->timeout; + break; + case RTEMS_FTPFS_IOCTL_SET_TIMEOUT: + me->timeout = *timeout; + break; + default: + rtems_set_errno_and_return_minus_one(EINVAL); + } + + return 0; +} + +/* + * The stat() support is intended only for the cp shell command. Each request + * will return that we have a regular file with read, write and execute + * permissions for every one. The node index uses a global counter to support + * a remote to remote copy. This is not a very sophisticated method. + */ +static int rtems_ftpfs_fstat( + const rtems_filesystem_location_info_t *loc, + struct stat *st +) +{ + int eno = 0; + rtems_ftpfs_entry *e = loc->node_access; + + /* FIXME */ + st->st_ino = e->ino; + st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU); + + st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; + + if (e->do_size_command) { + const rtems_ftpfs_mount_entry *me = loc->mt_entry->fs_info; + + rtems_ftpfs_get_file_size(e, me->verbose); + st->st_size = e->file_size; + } else { + e->do_size_command = true; + } + + if (eno == 0) { + return 0; + } else { + rtems_set_errno_and_return_minus_one(eno); + } +} + +static void rtems_ftpfs_lock_or_unlock( + const rtems_filesystem_mount_table_entry_t *mt_entry +) +{ + /* Do nothing */ +} + +static const rtems_filesystem_operations_table rtems_ftpfs_ops = { + .lock_h = rtems_ftpfs_lock_or_unlock, + .unlock_h = rtems_ftpfs_lock_or_unlock, + .eval_path_h = rtems_ftpfs_eval_path, + .link_h = rtems_filesystem_default_link, + .are_nodes_equal_h = rtems_filesystem_default_are_nodes_equal, + .mknod_h = rtems_filesystem_default_mknod, + .rmnod_h = rtems_filesystem_default_rmnod, + .fchmod_h = rtems_filesystem_default_fchmod, + .chown_h = rtems_filesystem_default_chown, + .clonenod_h = rtems_filesystem_default_clonenode, + .freenod_h = rtems_ftpfs_free_node, + .mount_h = rtems_filesystem_default_mount, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_ftpfs_unmount_me, + .utime_h = rtems_filesystem_default_utime, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink, + .rename_h = rtems_filesystem_default_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = { + .open_h = rtems_ftpfs_open, + .close_h = rtems_ftpfs_close, + .read_h = rtems_ftpfs_read, + .write_h = rtems_ftpfs_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_ftpfs_fstat, + .ftruncate_h = rtems_ftpfs_ftruncate, + .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .kqfilter_h = rtems_filesystem_default_kqfilter, + .mmap_h = rtems_filesystem_default_mmap, + .poll_h = rtems_filesystem_default_poll, + .readv_h = rtems_filesystem_default_readv, + .writev_h = rtems_filesystem_default_writev +}; + +static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = { + .open_h = rtems_filesystem_default_open, + .close_h = rtems_filesystem_default_close, + .read_h = rtems_filesystem_default_read, + .write_h = rtems_filesystem_default_write, + .ioctl_h = rtems_ftpfs_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_filesystem_default_fstat, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .kqfilter_h = rtems_filesystem_default_kqfilter, + .mmap_h = rtems_filesystem_default_mmap, + .poll_h = rtems_filesystem_default_poll, + .readv_h = rtems_filesystem_default_readv, + .writev_h = rtems_filesystem_default_writev +}; diff --git a/cpukit/libfs/src/ftpfs/tftpDriver.c b/cpukit/libfs/src/ftpfs/tftpDriver.c new file mode 100644 index 0000000000..7cbb402b63 --- /dev/null +++ b/cpukit/libfs/src/ftpfs/tftpDriver.c @@ -0,0 +1,1066 @@ +/* + * Trivial File Transfer Protocol (RFC 1350) + * + * Transfer file to/from remote host + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * Modifications to support reference counting in the file system are + * Copyright (c) 2012 embedded brains GmbH. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef RTEMS_NETWORKING +#include +#endif + +#ifdef RTEMS_TFTP_DRIVER_DEBUG +int rtems_tftp_driver_debug = 1; +#endif + +/* + * Range of UDP ports to try + */ +#define UDP_PORT_BASE 3180 + +/* + * Default limits + */ +#define PACKET_FIRST_TIMEOUT_MILLISECONDS 400L +#define PACKET_TIMEOUT_MILLISECONDS 6000L +#define OPEN_RETRY_LIMIT 10 +#define IO_RETRY_LIMIT 10 + +/* + * TFTP opcodes + */ +#define TFTP_OPCODE_RRQ 1 +#define TFTP_OPCODE_WRQ 2 +#define TFTP_OPCODE_DATA 3 +#define TFTP_OPCODE_ACK 4 +#define TFTP_OPCODE_ERROR 5 + +/* + * Largest data transfer + */ +#define TFTP_BUFSIZE 512 + +/* + * Packets transferred between machines + */ +union tftpPacket { + /* + * RRQ/WRQ packet + */ + struct tftpRWRQ { + uint16_t opcode; + char filename_mode[TFTP_BUFSIZE]; + } tftpRWRQ; + + /* + * DATA packet + */ + struct tftpDATA { + uint16_t opcode; + uint16_t blocknum; + uint8_t data[TFTP_BUFSIZE]; + } tftpDATA; + + /* + * ACK packet + */ + struct tftpACK { + uint16_t opcode; + uint16_t blocknum; + } tftpACK; + + /* + * ERROR packet + */ + struct tftpERROR { + uint16_t opcode; + uint16_t errorCode; + char errorMessage[TFTP_BUFSIZE]; + } tftpERROR; +}; + +/* + * State of each TFTP stream + */ +struct tftpStream { + /* + * Buffer for storing most recently-received packet + */ + union tftpPacket pkbuf; + + /* + * Last block number transferred + */ + uint16_t blocknum; + + /* + * Data transfer socket + */ + int socket; + struct sockaddr_in myAddress; + struct sockaddr_in farAddress; + + /* + * Indices into buffer + */ + int nleft; + int nused; + + /* + * Flags + */ + int firstReply; + int eof; + int writing; +}; + +/* + * Flags for filesystem info. + */ +#define TFTPFS_VERBOSE (1 << 0) + +/* + * TFTP File system info. + */ +typedef struct tftpfs_info_s { + uint32_t flags; + rtems_mutex tftp_mutex; + int nStreams; + struct tftpStream ** volatile tftpStreams; +} tftpfs_info_t; + +#define tftpfs_info_mount_table(_mt) ((tftpfs_info_t*) ((_mt)->fs_info)) +#define tftpfs_info_pathloc(_pl) ((tftpfs_info_t*) ((_pl)->mt_entry->fs_info)) +#define tftpfs_info_iop(_iop) (tftpfs_info_pathloc (&((_iop)->pathinfo))) + +/* + * Number of streams open at the same time + */ + +static const rtems_filesystem_operations_table rtems_tftp_ops; +static const rtems_filesystem_file_handlers_r rtems_tftp_handlers; + +static bool rtems_tftp_is_directory( + const char *path, + size_t pathlen +) +{ + return path [pathlen - 1] == '/'; +} + +int rtems_tftpfs_initialize( + rtems_filesystem_mount_table_entry_t *mt_entry, + const void *data +) +{ + const char *device = mt_entry->dev; + size_t devicelen = strlen (device); + tftpfs_info_t *fs = NULL; + char *root_path; + + if (devicelen == 0) { + root_path = malloc (1); + if (root_path == NULL) + goto error; + root_path [0] = '\0'; + } + else { + root_path = malloc (devicelen + 2); + if (root_path == NULL) + goto error; + + root_path = memcpy (root_path, device, devicelen); + root_path [devicelen] = '/'; + root_path [devicelen + 1] = '\0'; + } + + fs = malloc (sizeof (*fs)); + if (fs == NULL) + goto error; + fs->flags = 0; + fs->nStreams = 0; + fs->tftpStreams = 0; + + mt_entry->fs_info = fs; + mt_entry->mt_fs_root->location.node_access = root_path; + mt_entry->mt_fs_root->location.handlers = &rtems_tftp_handlers; + mt_entry->ops = &rtems_tftp_ops; + + /* + * Now allocate a semaphore for mutual exclusion. + * + * NOTE: This could be in an fsinfo for this filesystem type. + */ + + rtems_mutex_init (&fs->tftp_mutex, "TFTPFS"); + + if (data) { + char* config = (char*) data; + char* token; + char* saveptr; + token = strtok_r (config, " ", &saveptr); + while (token) { + if (strcmp (token, "verbose") == 0) + fs->flags |= TFTPFS_VERBOSE; + token = strtok_r (NULL, " ", &saveptr); + } + } + + return 0; + +error: + + free (fs); + free (root_path); + + rtems_set_errno_and_return_minus_one (ENOMEM); +} + +/* + * Release a stream and clear the pointer to it + */ +static void +releaseStream (tftpfs_info_t *fs, int s) +{ + if (fs->tftpStreams[s] && (fs->tftpStreams[s]->socket >= 0)) + close (fs->tftpStreams[s]->socket); + rtems_mutex_lock (&fs->tftp_mutex); + free (fs->tftpStreams[s]); + fs->tftpStreams[s] = NULL; + rtems_mutex_unlock (&fs->tftp_mutex); +} + +static void +rtems_tftpfs_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry) +{ + tftpfs_info_t *fs = tftpfs_info_mount_table (mt_entry); + int s; + for (s = 0; s < fs->nStreams; s++) + releaseStream (fs, s); + rtems_mutex_destroy (&fs->tftp_mutex); + free (fs); + free (mt_entry->mt_fs_root->location.node_access); +} + +/* + * Map error message + */ +static int +tftpErrno (struct tftpStream *tp) +{ + unsigned int tftpError; + static const int errorMap[] = { + EINVAL, + ENOENT, + EPERM, + ENOSPC, + EINVAL, + ENXIO, + EEXIST, + ESRCH, + }; + + tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); + if (tftpError < (sizeof errorMap / sizeof errorMap[0])) + return errorMap[tftpError]; + else + return 1000 + tftpError; +} + +/* + * Send a message to make the other end shut up + */ +static void +sendStifle (struct tftpStream *tp, struct sockaddr_in *to) +{ + int len; + struct { + uint16_t opcode; + uint16_t errorCode; + char errorMessage[12]; + } msg; + + /* + * Create the error packet (Unknown transfer ID). + */ + msg.opcode = htons (TFTP_OPCODE_ERROR); + msg.errorCode = htons (5); + len = sizeof msg.opcode + sizeof msg.errorCode + 1; + len += sprintf (msg.errorMessage, "GO AWAY"); + + /* + * Send it + */ + sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to); +} + +/* + * Wait for a data packet + */ +static int +getPacket (struct tftpStream *tp, int retryCount) +{ + int len; + struct timeval tv; + + if (retryCount == 0) { + tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000L; + tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000L) * 1000L; + } + else { + tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000L; + tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000L) * 1000L; + } + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + for (;;) { + union { + struct sockaddr s; + struct sockaddr_in i; + } from; + socklen_t fromlen = sizeof from; + len = recvfrom (tp->socket, &tp->pkbuf, + sizeof tp->pkbuf, 0, + &from.s, &fromlen); + if (len < 0) + break; + if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { + if (tp->firstReply) { + tp->firstReply = 0; + tp->farAddress.sin_port = from.i.sin_port; + } + if (tp->farAddress.sin_port == from.i.sin_port) + break; + } + + /* + * Packet is from someone with whom we are + * not interested. Tell them to go away. + */ + sendStifle (tp, &from.i); + } + tv.tv_sec = 0; + tv.tv_usec = 0; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); +#ifdef RTEMS_TFTP_DRIVER_DEBUG + if (rtems_tftp_driver_debug) { + if (len >= (int) sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + switch (opcode) { + default: + printf ("TFTP: OPCODE %d\n", opcode); + break; + + case TFTP_OPCODE_DATA: + printf ("TFTP: RECV %d\n", ntohs (tp->pkbuf.tftpDATA.blocknum)); + break; + + case TFTP_OPCODE_ACK: + printf ("TFTP: GOT ACK %d\n", ntohs (tp->pkbuf.tftpACK.blocknum)); + break; + } + } + else { + printf ("TFTP: %d-byte packet\n", len); + } + } +#endif + return len; +} + +/* + * Send an acknowledgement + */ +static int +sendAck (struct tftpStream *tp) +{ +#ifdef RTEMS_TFTP_DRIVER_DEBUG + if (rtems_tftp_driver_debug) + printf ("TFTP: ACK %d\n", tp->blocknum); +#endif + + /* + * Create the acknowledgement + */ + tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); + tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); + + /* + * Send it + */ + if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) + return errno; + return 0; +} + +/* + * Convert a path to canonical form + */ +static void +fixPath (char *path) +{ + char *inp, *outp, *base; + + outp = inp = path; + base = NULL; + for (;;) { + if (inp[0] == '.') { + if (inp[1] == '\0') + break; + if (inp[1] == '/') { + inp += 2; + continue; + } + if (inp[1] == '.') { + if (inp[2] == '\0') { + if ((base != NULL) && (outp > base)) { + outp--; + while ((outp > base) && (outp[-1] != '/')) + outp--; + } + break; + } + if (inp[2] == '/') { + inp += 3; + if (base == NULL) + continue; + if (outp > base) { + outp--; + while ((outp > base) && (outp[-1] != '/')) + outp--; + } + continue; + } + } + } + if (base == NULL) + base = inp; + while (inp[0] != '/') { + if ((*outp++ = *inp++) == '\0') + return; + } + *outp++ = '/'; + while (inp[0] == '/') + inp++; + } + *outp = '\0'; + return; +} + +static void rtems_tftp_eval_path(rtems_filesystem_eval_path_context_t *self) +{ + int eval_flags = rtems_filesystem_eval_path_get_flags (self); + + if ((eval_flags & RTEMS_FS_MAKE) == 0) { + int rw = RTEMS_FS_PERMS_READ | RTEMS_FS_PERMS_WRITE; + + if ((eval_flags & rw) != rw) { + rtems_filesystem_location_info_t *currentloc = + rtems_filesystem_eval_path_get_currentloc (self); + char *current = currentloc->node_access; + size_t currentlen = strlen (current); + const char *path = rtems_filesystem_eval_path_get_path (self); + size_t pathlen = rtems_filesystem_eval_path_get_pathlen (self); + size_t len = currentlen + pathlen; + + rtems_filesystem_eval_path_clear_path (self); + + current = realloc (current, len + 1); + if (current != NULL) { + memcpy (current + currentlen, path, pathlen); + current [len] = '\0'; + if (!rtems_tftp_is_directory (current, len)) { + fixPath (current); + } + currentloc->node_access = current; + } else { + rtems_filesystem_eval_path_error (self, ENOMEM); + } + } else { + rtems_filesystem_eval_path_error (self, EINVAL); + } + } else { + rtems_filesystem_eval_path_error (self, EIO); + } +} + +/* + * The routine which does most of the work for the IMFS open handler + */ +static int rtems_tftp_open_worker( + rtems_libio_t *iop, + char *full_path_name, + int oflag +) +{ + tftpfs_info_t *fs; + struct tftpStream *tp; + int retryCount; + struct in_addr farAddress; + int s; + int len; + char *cp1; + char *cp2; + char *remoteFilename; + rtems_interval now; + char *hostname; + + /* + * Get the file system info. + */ + fs = tftpfs_info_iop (iop); + + /* + * Extract the host name component + */ + if (*full_path_name == '/') + full_path_name++; + + hostname = full_path_name; + cp1 = strchr (full_path_name, ':'); + if (!cp1) { +#ifdef RTEMS_NETWORKING + hostname = "BOOTP_HOST"; +#endif + } else { + *cp1 = '\0'; + ++cp1; + } + + /* + * Convert hostname to Internet address + */ +#ifdef RTEMS_NETWORKING + if (strcmp (hostname, "BOOTP_HOST") == 0) + farAddress = rtems_bsdnet_bootp_server_address; + else +#endif + if (inet_aton (hostname, &farAddress) == 0) { + struct hostent *he = gethostbyname(hostname); + if (he == NULL) + return ENOENT; + memcpy (&farAddress, he->h_addr, sizeof (farAddress)); + } + + /* + * Extract file pathname component + */ +#ifdef RTEMS_NETWORKING + if (strcmp (cp1, "BOOTP_FILE") == 0) { + cp1 = rtems_bsdnet_bootp_boot_file_name; + } +#endif + if (*cp1 == '\0') + return ENOENT; + remoteFilename = cp1; + if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) + return ENOENT; + + /* + * Find a free stream + */ + rtems_mutex_lock (&fs->tftp_mutex); + for (s = 0 ; s < fs->nStreams ; s++) { + if (fs->tftpStreams[s] == NULL) + break; + } + if (s == fs->nStreams) { + /* + * Reallocate stream pointers + * Guard against the case where realloc() returns NULL. + */ + struct tftpStream **np; + + np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams); + if (np == NULL) { + rtems_mutex_unlock (&fs->tftp_mutex); + return ENOMEM; + } + fs->tftpStreams = np; + } + tp = fs->tftpStreams[s] = malloc (sizeof (struct tftpStream)); + rtems_mutex_unlock (&fs->tftp_mutex); + if (tp == NULL) + return ENOMEM; + iop->data0 = s; + iop->data1 = tp; + + /* + * Create the socket + */ + if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { + releaseStream (fs, s); + return ENOMEM; + } + + /* + * Bind the socket to a local address + */ + retryCount = 0; + now = rtems_clock_get_ticks_since_boot(); + for (;;) { + int try = (now + retryCount) % 10; + + tp->myAddress.sin_family = AF_INET; + tp->myAddress.sin_port = htons (UDP_PORT_BASE + fs->nStreams * try + s); + tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0) + break; + if (++retryCount == 10) { + releaseStream (fs, s); + return EBUSY; + } + } + + /* + * Set the UDP destination to the TFTP server + * port on the remote machine. + */ + tp->farAddress.sin_family = AF_INET; + tp->farAddress.sin_addr = farAddress; + tp->farAddress.sin_port = htons (69); + + /* + * Start the transfer + */ + tp->firstReply = 1; + retryCount = 0; + for (;;) { + /* + * Create the request + */ + if ((oflag & O_ACCMODE) == O_RDONLY) { + tp->writing = 0; + 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; + cp2 = (char *) remoteFilename; + while ((*cp1++ = *cp2++) != '\0') + continue; + cp2 = "octet"; + while ((*cp1++ = *cp2++) != '\0') + continue; + len = cp1 - (char *)&tp->pkbuf.tftpRWRQ; + + /* + * Send the request + */ + if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) { + releaseStream (fs, s); + return EIO; + } + + /* + * Get reply + */ + len = getPacket (tp, retryCount); + if (len >= (int) sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + if (!tp->writing + && (opcode == TFTP_OPCODE_DATA) + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { + tp->nused = 0; + tp->blocknum = 1; + tp->nleft = len - 2 * sizeof (uint16_t ); + tp->eof = (tp->nleft < TFTP_BUFSIZE); + if (sendAck (tp) != 0) { + releaseStream (fs, s); + return EIO; + } + 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) { + int e = tftpErrno (tp); + releaseStream (fs, s); + return e; + } + } + + /* + * Keep trying + */ + if (++retryCount >= OPEN_RETRY_LIMIT) { + releaseStream (fs, s); + return EIO; + } + } + return 0; +} + +static int rtems_tftp_open( + rtems_libio_t *iop, + const char *new_name, + int oflag, + mode_t mode +) +{ + tftpfs_info_t *fs; + char *full_path_name; + int err; + + full_path_name = iop->pathinfo.node_access; + + if (rtems_tftp_is_directory (full_path_name, strlen (full_path_name))) { + rtems_set_errno_and_return_minus_one (ENOTSUP); + } + + /* + * Get the file system info. + */ + fs = tftpfs_info_iop (iop); + + if (fs->flags & TFTPFS_VERBOSE) + printf ("TFTPFS: %s\n", full_path_name); + + err = rtems_tftp_open_worker (iop, full_path_name, oflag); + if (err != 0) { + rtems_set_errno_and_return_minus_one (err); + } + + return 0; +} + +/* + * Read from a TFTP stream + */ +static ssize_t rtems_tftp_read( + rtems_libio_t *iop, + void *buffer, + size_t count +) +{ + char *bp; + struct tftpStream *tp = iop->data1; + int retryCount; + int nwant; + + if (!tp) + rtems_set_errno_and_return_minus_one( EIO ); + + /* + * Read till user request is satisfied or EOF is reached + */ + bp = buffer; + nwant = count; + while (nwant) { + if (tp->nleft) { + int ncopy; + if (nwant < tp->nleft) + ncopy = nwant; + else + ncopy = tp->nleft; + memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy); + tp->nused += ncopy; + tp->nleft -= ncopy; + bp += ncopy; + nwant -= ncopy; + if (nwant == 0) + break; + } + if (tp->eof) + break; + + /* + * Wait for the next packet + */ + retryCount = 0; + for (;;) { + int len = getPacket (tp, retryCount); + if (len >= (int)sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + uint16_t nextBlock = tp->blocknum + 1; + if ((opcode == TFTP_OPCODE_DATA) + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { + tp->nused = 0; + tp->nleft = len - 2 * sizeof (uint16_t); + tp->eof = (tp->nleft < TFTP_BUFSIZE); + tp->blocknum++; + if (sendAck (tp) != 0) + rtems_set_errno_and_return_minus_one (EIO); + break; + } + if (opcode == TFTP_OPCODE_ERROR) + rtems_set_errno_and_return_minus_one (tftpErrno (tp)); + } + + /* + * Keep trying? + */ + if (++retryCount == IO_RETRY_LIMIT) + rtems_set_errno_and_return_minus_one (EIO); + if (sendAck (tp) != 0) + rtems_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 (uint16_t ); + for (;;) { + tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA); + tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum); +#ifdef RTEMS_TFTP_DRIVER_DEBUG + if (rtems_tftp_driver_debug) + printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused); +#endif + if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) + return EIO; + rlen = getPacket (tp, retryCount); + /* + * Our last packet won't necessarily be acknowledged! + */ + if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data)) + return 0; + 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); + } + + /* + * Keep trying? + */ + if (++retryCount == IO_RETRY_LIMIT) + return EIO; + } +} + +/* + * Close a TFTP stream + */ +static int rtems_tftp_close( + rtems_libio_t *iop +) +{ + tftpfs_info_t *fs; + struct tftpStream *tp = iop->data1; + int e = 0; + + /* + * Get the file system info. + */ + fs = tftpfs_info_iop (iop); + + if (!tp) + rtems_set_errno_and_return_minus_one (EIO); + + if (tp->writing) + e = rtems_tftp_flush (tp); + if (!tp->eof && !tp->firstReply) { + /* + * Tell the other end to stop + */ + rtems_interval ticksPerSecond; + sendStifle (tp, &tp->farAddress); + ticksPerSecond = rtems_clock_get_ticks_per_second(); + rtems_task_wake_after (1 + ticksPerSecond / 10); + } + releaseStream (fs, iop->data0); + if (e) + rtems_set_errno_and_return_minus_one (e); + return 0; +} + +static ssize_t rtems_tftp_write( + rtems_libio_t *iop, + const void *buffer, + size_t count +) +{ + const char *bp; + struct tftpStream *tp = iop->data1; + int nleft, nfree, ncopy; + + /* + * Bail out if an error has occurred + */ + if (!tp->writing) + rtems_set_errno_and_return_minus_one (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; + rtems_set_errno_and_return_minus_one (e); + } + } + } + return count; +} + +/* + * Dummy version to let fopen(xxxx,"w") work properly. + */ +static int rtems_tftp_ftruncate( + rtems_libio_t *iop RTEMS_UNUSED, + off_t count RTEMS_UNUSED +) +{ + return 0; +} + +static int rtems_tftp_fstat( + const rtems_filesystem_location_info_t *loc, + struct stat *buf +) +{ + const char *path = loc->node_access; + size_t pathlen = strlen (path); + + buf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO + | (rtems_tftp_is_directory (path, pathlen) ? S_IFDIR : S_IFREG); + + return 0; +} + +static int rtems_tftp_clone( + rtems_filesystem_location_info_t *loc +) +{ + int rv = 0; + + loc->node_access = strdup (loc->node_access); + + if (loc->node_access == NULL) { + errno = ENOMEM; + rv = -1; + } + + return rv; +} + +static void rtems_tftp_free_node_info( + const rtems_filesystem_location_info_t *loc +) +{ + free (loc->node_access); +} + +static bool rtems_tftp_are_nodes_equal( + const rtems_filesystem_location_info_t *a, + const rtems_filesystem_location_info_t *b +) +{ + return strcmp (a->node_access, b->node_access) == 0; +} + +static const rtems_filesystem_operations_table rtems_tftp_ops = { + .lock_h = rtems_filesystem_default_lock, + .unlock_h = rtems_filesystem_default_unlock, + .eval_path_h = rtems_tftp_eval_path, + .link_h = rtems_filesystem_default_link, + .are_nodes_equal_h = rtems_tftp_are_nodes_equal, + .mknod_h = rtems_filesystem_default_mknod, + .rmnod_h = rtems_filesystem_default_rmnod, + .fchmod_h = rtems_filesystem_default_fchmod, + .chown_h = rtems_filesystem_default_chown, + .clonenod_h = rtems_tftp_clone, + .freenod_h = rtems_tftp_free_node_info, + .mount_h = rtems_filesystem_default_mount, + .unmount_h = rtems_filesystem_default_unmount, + .fsunmount_me_h = rtems_tftpfs_shutdown, + .utime_h = rtems_filesystem_default_utime, + .symlink_h = rtems_filesystem_default_symlink, + .readlink_h = rtems_filesystem_default_readlink, + .rename_h = rtems_filesystem_default_rename, + .statvfs_h = rtems_filesystem_default_statvfs +}; + +static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = { + .open_h = rtems_tftp_open, + .close_h = rtems_tftp_close, + .read_h = rtems_tftp_read, + .write_h = rtems_tftp_write, + .ioctl_h = rtems_filesystem_default_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_tftp_fstat, + .ftruncate_h = rtems_tftp_ftruncate, + .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .kqfilter_h = rtems_filesystem_default_kqfilter, + .mmap_h = rtems_filesystem_default_mmap, + .poll_h = rtems_filesystem_default_poll, + .readv_h = rtems_filesystem_default_readv, + .writev_h = rtems_filesystem_default_writev +}; diff --git a/cpukit/libnetworking/lib/ftpfs.c b/cpukit/libnetworking/lib/ftpfs.c deleted file mode 100644 index 5e0cb95dd3..0000000000 --- a/cpukit/libnetworking/lib/ftpfs.c +++ /dev/null @@ -1,1430 +0,0 @@ -/** - * @file - * - * File Transfer Protocol file system (FTP client). - */ - -/* - * Copyright (c) 2009-2012 embedded brains GmbH. - * - * embedded brains GmbH - * Obere Lagerstr. 30 - * 82178 Puchheim - * Germany - * - * - * (c) Copyright 2002 - * Thomas Doerfler - * IMD Ingenieurbuero fuer Microcomputertechnik - * Herbststr. 8 - * 82178 Puchheim, Germany - * - * - * This code has been created after closly inspecting "tftpdriver.c" from Eric - * Norum. - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rtems.org/license/LICENSE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef DEBUG - #define DEBUG_PRINTF(...) printf(__VA_ARGS__) -#else - #define DEBUG_PRINTF(...) -#endif - -/** - * Connection entry for each open file stream. - */ -typedef struct { - off_t file_size; - - /** - * Control connection socket. - */ - int ctrl_socket; - - uint32_t client_address; - - /** - * Data transfer socket. - */ - int data_socket; - - /** - * Current index into the reply buffer. - */ - size_t reply_current; - - /** - * End index of the reply buffer. - */ - size_t reply_end; - - /** - * Buffer for relpy data. - */ - char reply_buffer [128]; - - /** - * End of file flag. - */ - bool eof; - - bool write; - - /** - * Indicates if we should do a SIZE command. - * - * The first call to the rtems_ftpfs_fstat() handler is issued by the path - * evaluation to check for access permission. For this case we avoid the - * SIZE command. - */ - bool do_size_command; - - ino_t ino; - - const char *user; - - const char *password; - - const char *hostname; - - const char *filename; - - char buffer []; -} rtems_ftpfs_entry; - -/** - * Mount entry for each file system instance. - */ -typedef struct { - /** - * Verbose mode enabled or disabled. - */ - bool verbose; - - /** - * Timeout value - */ - struct timeval timeout; - - /** - * Inode counter. - */ - ino_t ino; -} rtems_ftpfs_mount_entry; - -static const rtems_filesystem_operations_table rtems_ftpfs_ops; - -static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers; - -static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers; - -static bool rtems_ftpfs_use_timeout(const struct timeval *to) -{ - return to->tv_sec != 0 || to->tv_usec != 0; -} - -static int rtems_ftpfs_set_connection_timeout( - int socket, - const struct timeval *to -) -{ - if (rtems_ftpfs_use_timeout(to)) { - int rv = 0; - - rv = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, to, sizeof(*to)); - if (rv != 0) { - return EIO; - } - - rv = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, to, sizeof(*to)); - if (rv != 0) { - return EIO; - } - } - - return 0; -} - -static rtems_status_code rtems_ftpfs_do_ioctl( - const char *mount_point, - ioctl_command_t req, - ... -) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - int rv = 0; - int fd = 0; - va_list ap; - - if (mount_point == NULL) { - mount_point = RTEMS_FTPFS_MOUNT_POINT_DEFAULT; - } - - fd = open(mount_point, O_RDWR); - if (fd < 0) { - return RTEMS_INVALID_NAME; - } - - va_start(ap, req); - rv = ioctl(fd, req, va_arg(ap, void *)); - va_end(ap); - if (rv != 0) { - sc = RTEMS_INVALID_NUMBER; - } - - rv = close(fd); - if (rv != 0 && sc == RTEMS_SUCCESSFUL) { - sc = RTEMS_IO_ERROR; - } - - return sc; -} - -rtems_status_code rtems_ftpfs_get_verbose(const char *mount_point, bool *verbose) -{ - return rtems_ftpfs_do_ioctl( - mount_point, - RTEMS_FTPFS_IOCTL_GET_VERBOSE, - verbose - ); -} - -rtems_status_code rtems_ftpfs_set_verbose(const char *mount_point, bool verbose) -{ - return rtems_ftpfs_do_ioctl( - mount_point, - RTEMS_FTPFS_IOCTL_SET_VERBOSE, - &verbose - ); -} - -rtems_status_code rtems_ftpfs_get_timeout( - const char *mount_point, - struct timeval *timeout -) -{ - return rtems_ftpfs_do_ioctl( - mount_point, - RTEMS_FTPFS_IOCTL_GET_TIMEOUT, - timeout - ); -} - -rtems_status_code rtems_ftpfs_set_timeout( - const char *mount_point, - const struct timeval *timeout -) -{ - return rtems_ftpfs_do_ioctl( - mount_point, - RTEMS_FTPFS_IOCTL_SET_TIMEOUT, - timeout - ); -} - -typedef void (*rtems_ftpfs_reply_parser)( - const char * /* reply fragment */, - size_t /* reply fragment length */, - void * /* parser argument */ -); - -typedef enum { - RTEMS_FTPFS_REPLY_START, - RTEMS_FTPFS_REPLY_SINGLE_LINE, - RTEMS_FTPFS_REPLY_DONE, - RTEMS_FTPFS_REPLY_MULTI_LINE, - RTEMS_FTPFS_REPLY_MULTI_LINE_START -} rtems_ftpfs_reply_state; - -typedef enum { - RTEMS_FTPFS_REPLY_ERROR = 0, - RTEMS_FTPFS_REPLY_1 = '1', - RTEMS_FTPFS_REPLY_2 = '2', - RTEMS_FTPFS_REPLY_3 = '3', - RTEMS_FTPFS_REPLY_4 = '4', - RTEMS_FTPFS_REPLY_5 = '5' -} rtems_ftpfs_reply; - -#define RTEMS_FTPFS_REPLY_SIZE 3 - -static bool rtems_ftpfs_is_reply_code_valid(unsigned char *reply) -{ - return isdigit(reply [0]) && isdigit(reply [1]) && isdigit(reply [2]); -} - -static rtems_ftpfs_reply rtems_ftpfs_get_reply( - rtems_ftpfs_entry *e, - rtems_ftpfs_reply_parser parser, - void *parser_arg, - bool verbose -) -{ - rtems_ftpfs_reply_state state = RTEMS_FTPFS_REPLY_START; - unsigned char reply_code [RTEMS_FTPFS_REPLY_SIZE] = { 'a', 'a', 'a' }; - size_t reply_code_index = 0; - - while (state != RTEMS_FTPFS_REPLY_DONE) { - char *buf = NULL; - size_t i = 0; - size_t n = 0; - - /* Receive reply fragment from socket */ - if (e->reply_current == e->reply_end) { - ssize_t rv = 0; - - e->reply_current = 0; - e->reply_end = 0; - - rv = recv( - e->ctrl_socket, - &e->reply_buffer [0], - sizeof(e->reply_buffer), - 0 - ); - - if (rv > 0) { - e->reply_end = (size_t) rv; - } else { - return RTEMS_FTPFS_REPLY_ERROR; - } - } - - buf = &e->reply_buffer [e->reply_current]; - n = e->reply_end - e->reply_current; - - /* Invoke parser if necessary */ - if (parser != NULL) { - parser(buf, n, parser_arg); - } - - /* Parse reply fragment */ - for (i = 0; i < n && state != RTEMS_FTPFS_REPLY_DONE; ++i) { - char c = buf [i]; - - switch (state) { - case RTEMS_FTPFS_REPLY_START: - if (reply_code_index < RTEMS_FTPFS_REPLY_SIZE) { - reply_code [reply_code_index] = c; - ++reply_code_index; - } else if (rtems_ftpfs_is_reply_code_valid(reply_code)) { - if (c == '-') { - state = RTEMS_FTPFS_REPLY_MULTI_LINE; - } else { - state = RTEMS_FTPFS_REPLY_SINGLE_LINE; - } - } else { - return RTEMS_FTPFS_REPLY_ERROR; - } - break; - case RTEMS_FTPFS_REPLY_SINGLE_LINE: - if (c == '\n') { - state = RTEMS_FTPFS_REPLY_DONE; - } - break; - case RTEMS_FTPFS_REPLY_MULTI_LINE: - if (c == '\n') { - state = RTEMS_FTPFS_REPLY_MULTI_LINE_START; - reply_code_index = 0; - } - break; - case RTEMS_FTPFS_REPLY_MULTI_LINE_START: - if (reply_code_index < RTEMS_FTPFS_REPLY_SIZE) { - if (reply_code [reply_code_index] == c) { - ++reply_code_index; - } else { - state = RTEMS_FTPFS_REPLY_MULTI_LINE; - } - } else { - if (c == ' ') { - state = RTEMS_FTPFS_REPLY_SINGLE_LINE; - } else { - state = RTEMS_FTPFS_REPLY_MULTI_LINE; - } - } - break; - default: - return RTEMS_FTPFS_REPLY_ERROR; - } - } - - /* Be verbose if necessary */ - if (verbose) { - write(STDERR_FILENO, buf, i); - } - - /* Update reply index */ - e->reply_current += i; - } - - return reply_code [0]; -} - -static rtems_ftpfs_reply rtems_ftpfs_send_command_with_parser( - rtems_ftpfs_entry *e, - const char *cmd, - const char *arg, - rtems_ftpfs_reply_parser parser, - void *parser_arg, - bool verbose -) -{ - rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; - size_t cmd_len = strlen(cmd); - size_t arg_len = arg != NULL ? strlen(arg) : 0; - size_t len = cmd_len + arg_len + 2; - char *buf = malloc(len); - - if (buf != NULL) { - ssize_t n = 0; - char *buf_arg = buf + cmd_len; - char *buf_eol = buf_arg + arg_len; - - memcpy(buf, cmd, cmd_len); - memcpy(buf_arg, arg, arg_len); - buf_eol [0] = '\r'; - buf_eol [1] = '\n'; - - /* Send */ - n = send(e->ctrl_socket, buf, len, 0); - if (n == (ssize_t) len) { - if (verbose) { - write(STDERR_FILENO, buf, len); - } - - /* Reply */ - reply = rtems_ftpfs_get_reply(e, parser, parser_arg, verbose); - } - - free(buf); - } - - return reply; -} - -static rtems_ftpfs_reply rtems_ftpfs_send_command( - rtems_ftpfs_entry *e, - const char *cmd, - const char *arg, - bool verbose -) -{ - return rtems_ftpfs_send_command_with_parser( - e, - cmd, - arg, - NULL, - NULL, - verbose - ); -} - -typedef enum { - STATE_USER_NAME, - STATE_START_PASSWORD, - STATE_START_HOST_NAME, - STATE_START_HOST_NAME_OR_PATH, - STATE_START_PATH, - STATE_PASSWORD, - STATE_HOST_NAME, - STATE_DONE, - STATE_INVALID -} split_state; - -static int rtems_ftpfs_split_names ( - char *s, - const char **user, - const char **password, - const char **hostname, - const char **path -) -{ - split_state state = STATE_USER_NAME; - size_t len = strlen(s); - size_t i = 0; - - *user = s; - - for (i = 0; i < len; ++i) { - char c = s [i]; - - switch (state) { - case STATE_USER_NAME: - if (c == ':') { - state = STATE_START_PASSWORD; - s [i] = '\0'; - } else if (c == '@') { - state = STATE_START_HOST_NAME; - s [i] = '\0'; - } else if (c == '/') { - state = STATE_START_HOST_NAME_OR_PATH; - s [i] = '\0'; - } - break; - case STATE_START_PASSWORD: - state = STATE_PASSWORD; - *password = &s [i]; - --i; - break; - case STATE_START_HOST_NAME: - state = STATE_HOST_NAME; - *hostname = &s [i]; - --i; - break; - case STATE_START_HOST_NAME_OR_PATH: - if (c == '@') { - state = STATE_START_HOST_NAME; - } else { - state = STATE_DONE; - *path = &s [i]; - goto done; - } - break; - case STATE_START_PATH: - state = STATE_DONE; - *path = &s [i]; - goto done; - case STATE_PASSWORD: - if (c == '@') { - state = STATE_START_HOST_NAME; - s [i] = '\0'; - } else if (c == '/') { - state = STATE_START_HOST_NAME_OR_PATH; - s [i] = '\0'; - } - break; - case STATE_HOST_NAME: - if (c == '/') { - state = STATE_START_PATH; - s [i] = '\0'; - } - break; - default: - state = STATE_INVALID; - goto done; - } - } - -done: - - /* This is a special case with no username and password */ - if (*hostname == NULL) { - *hostname = &s [0]; - *user = "anonymous"; - *password = *user; - } - - /* If we have no password use the user name */ - if (*password == NULL) { - *password = *user; - } - - return state == STATE_DONE ? 0 : ENOENT; -} - -static socklen_t rtems_ftpfs_create_address( - struct sockaddr_in *sa, - unsigned long address, - unsigned short port -) -{ - memset(sa, 0, sizeof(*sa)); - - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = address; - sa->sin_port = port; - sa->sin_len = sizeof(*sa); - - return sizeof(*sa); -} - -static int rtems_ftpfs_close_data_connection( - rtems_ftpfs_entry *e, - bool verbose, - bool error -) -{ - int eno = 0; - - /* Close data connection if necessary */ - if (e->data_socket >= 0) { - int rv = close(e->data_socket); - - e->data_socket = -1; - if (rv != 0) { - eno = EIO; - } - - /* For write connections we have to obtain the transfer reply */ - if (e->write && !error) { - rtems_ftpfs_reply reply = - rtems_ftpfs_get_reply(e, NULL, NULL, verbose); - - if (reply != RTEMS_FTPFS_REPLY_2) { - eno = EIO; - } - } - } - - return eno; -} - -static int rtems_ftpfs_open_ctrl_connection( - rtems_ftpfs_entry *e, - bool verbose, - const struct timeval *timeout -) -{ - int rv = 0; - int eno = 0; - rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; - struct in_addr address = { .s_addr = 0 }; - struct sockaddr_in sa; - socklen_t size = 0; - - /* Create the socket for the control connection */ - e->ctrl_socket = socket(AF_INET, SOCK_STREAM, 0); - if (e->ctrl_socket < 0) { - return ENOMEM; - } - - /* Set up the server address from the hostname */ - if (inet_aton(e->hostname, &address) == 0) { - /* Try to get the address by name */ - struct hostent *he = gethostbyname(e->hostname); - - if (he != NULL) { - memcpy(&address, he->h_addr, sizeof(address)); - } else { - return ENOENT; - } - } - rtems_ftpfs_create_address(&sa, address.s_addr, htons(RTEMS_FTPFS_CTRL_PORT)); - DEBUG_PRINTF("server = %s\n", inet_ntoa(sa.sin_addr)); - - /* Open control connection */ - rv = connect( - e->ctrl_socket, - (struct sockaddr *) &sa, - sizeof(sa) - ); - if (rv != 0) { - return ENOENT; - } - - /* Set control connection timeout */ - eno = rtems_ftpfs_set_connection_timeout(e->ctrl_socket, timeout); - if (eno != 0) { - return eno; - } - - /* Get client address */ - size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); - rv = getsockname( - e->ctrl_socket, - (struct sockaddr *) &sa, - &size - ); - if (rv != 0) { - return ENOMEM; - } - e->client_address = ntohl(sa.sin_addr.s_addr); - DEBUG_PRINTF("client = %s\n", inet_ntoa(sa.sin_addr)); - - /* Now we should get a welcome message from the server */ - reply = rtems_ftpfs_get_reply(e, NULL, NULL, verbose); - if (reply != RTEMS_FTPFS_REPLY_2) { - return ENOENT; - } - - /* Send USER command */ - reply = rtems_ftpfs_send_command(e, "USER ", e->user, verbose); - if (reply == RTEMS_FTPFS_REPLY_3) { - /* Send PASS command */ - reply = rtems_ftpfs_send_command(e, "PASS ", e->password, verbose); - if (reply != RTEMS_FTPFS_REPLY_2) { - return EACCES; - } - - /* TODO: Some server may require an account */ - } else if (reply != RTEMS_FTPFS_REPLY_2) { - return EACCES; - } - - /* Send TYPE command to set binary mode for all data transfers */ - reply = rtems_ftpfs_send_command(e, "TYPE I", NULL, verbose); - if (reply != RTEMS_FTPFS_REPLY_2) { - return EIO; - } - - return 0; -} - -static int rtems_ftpfs_open_data_connection_active( - rtems_ftpfs_entry *e, - const char *file_command, - bool verbose, - const struct timeval *timeout -) -{ - int rv = 0; - int eno = 0; - rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; - struct sockaddr_in sa; - socklen_t size = 0; - int port_socket = -1; - char port_command [] = "PORT 000,000,000,000,000,000"; - uint16_t data_port = 0; - - /* Create port socket to establish a data data connection */ - port_socket = socket(AF_INET, SOCK_STREAM, 0); - if (port_socket < 0) { - eno = ENOMEM; - goto cleanup; - } - - /* Bind port socket */ - rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); - rv = bind( - port_socket, - (struct sockaddr *) &sa, - sizeof(sa) - ); - if (rv != 0) { - eno = EBUSY; - goto cleanup; - } - - /* Get port number for data socket */ - size = rtems_ftpfs_create_address(&sa, INADDR_ANY, 0); - rv = getsockname( - port_socket, - (struct sockaddr *) &sa, - &size - ); - if (rv != 0) { - eno = ENOMEM; - goto cleanup; - } - data_port = ntohs(sa.sin_port); - - /* Send PORT command to set data connection port for server */ - snprintf( - port_command, - sizeof(port_command), - "PORT %lu,%lu,%lu,%lu,%lu,%lu", - (e->client_address >> 24) & 0xffUL, - (e->client_address >> 16) & 0xffUL, - (e->client_address >> 8) & 0xffUL, - (e->client_address >> 0) & 0xffUL, - (data_port >> 8) & 0xffUL, - (data_port >> 0) & 0xffUL - ); - reply = rtems_ftpfs_send_command(e, port_command, NULL, verbose); - if (reply != RTEMS_FTPFS_REPLY_2) { - eno = ENOTSUP; - goto cleanup; - } - - /* Listen on port socket for incoming data connections */ - rv = listen(port_socket, 1); - if (rv != 0) { - eno = EBUSY; - goto cleanup; - } - - /* Send RETR or STOR command with filename */ - reply = rtems_ftpfs_send_command(e, file_command, e->filename, verbose); - if (reply != RTEMS_FTPFS_REPLY_1) { - eno = EIO; - goto cleanup; - } - - /* Wait for connect on data connection if necessary */ - if (rtems_ftpfs_use_timeout(timeout)) { - struct timeval to = *timeout; - fd_set fds; - - FD_ZERO(&fds); - FD_SET(port_socket, &fds); - - rv = select(port_socket + 1, &fds, NULL, NULL, &to); - if (rv <= 0) { - eno = EIO; - goto cleanup; - } - } - - /* Accept data connection */ - size = sizeof(sa); - e->data_socket = accept( - port_socket, - (struct sockaddr *) &sa, - &size - ); - if (e->data_socket < 0) { - eno = EIO; - goto cleanup; - } - -cleanup: - - /* Close port socket if necessary */ - if (port_socket >= 0) { - rv = close(port_socket); - if (rv != 0) { - eno = EIO; - } - } - - return eno; -} - -typedef enum { - RTEMS_FTPFS_PASV_START = 0, - RTEMS_FTPFS_PASV_JUNK, - RTEMS_FTPFS_PASV_DATA, - RTEMS_FTPFS_PASV_DONE -} rtems_ftpfs_pasv_state; - -typedef struct { - rtems_ftpfs_pasv_state state; - size_t index; - uint8_t data [6]; -} rtems_ftpfs_pasv_entry; - -static void rtems_ftpfs_pasv_parser( - const char* buf, - size_t len, - void *arg -) -{ - rtems_ftpfs_pasv_entry *pe = arg; - size_t i = 0; - - for (i = 0; i < len; ++i) { - int c = buf [i]; - - switch (pe->state) { - case RTEMS_FTPFS_PASV_START: - if (!isdigit(c)) { - pe->state = RTEMS_FTPFS_PASV_JUNK; - pe->index = 0; - } - break; - case RTEMS_FTPFS_PASV_JUNK: - if (isdigit(c)) { - pe->state = RTEMS_FTPFS_PASV_DATA; - pe->data [pe->index] = (uint8_t) (c - '0'); - } - break; - case RTEMS_FTPFS_PASV_DATA: - if (isdigit(c)) { - pe->data [pe->index] = - (uint8_t) (pe->data [pe->index] * 10 + c - '0'); - } else if (c == ',') { - ++pe->index; - if (pe->index < sizeof(pe->data)) { - pe->data [pe->index] = 0; - } else { - pe->state = RTEMS_FTPFS_PASV_DONE; - } - } else { - pe->state = RTEMS_FTPFS_PASV_DONE; - } - break; - default: - return; - } - } -} - -static int rtems_ftpfs_open_data_connection_passive( - rtems_ftpfs_entry *e, - const char *file_command, - bool verbose, - const struct timeval *timeout -) -{ - int rv = 0; - rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; - struct sockaddr_in sa; - uint32_t data_address = 0; - uint16_t data_port = 0; - rtems_ftpfs_pasv_entry pe; - - memset(&pe, 0, sizeof(pe)); - - /* Send PASV command */ - reply = rtems_ftpfs_send_command_with_parser( - e, - "PASV", - NULL, - rtems_ftpfs_pasv_parser, - &pe, - verbose - ); - if (reply != RTEMS_FTPFS_REPLY_2) { - return ENOTSUP; - } - data_address = ((uint32_t)(pe.data [0]) << 24) - + ((uint32_t)(pe.data [1]) << 16) - + ((uint32_t)(pe.data [2]) << 8) - + ((uint32_t)(pe.data [3])); - data_port = (uint16_t) ((pe.data [4] << 8) + pe.data [5]); - rtems_ftpfs_create_address(&sa, htonl(data_address), htons(data_port)); - DEBUG_PRINTF( - "server data = %s:%u\n", - inet_ntoa(sa.sin_addr), - (unsigned) ntohs(sa.sin_port) - ); - - /* Create data socket */ - e->data_socket = socket(AF_INET, SOCK_STREAM, 0); - if (e->data_socket < 0) { - return ENOMEM; - } - - /* Open data connection */ - rv = connect( - e->data_socket, - (struct sockaddr *) &sa, - sizeof(sa) - ); - if (rv != 0) { - return EIO; - } - - /* Send RETR or STOR command with filename */ - reply = rtems_ftpfs_send_command(e, file_command, e->filename, verbose); - if (reply != RTEMS_FTPFS_REPLY_1) { - return EIO; - } - - return 0; -} - -typedef enum { - RTEMS_FTPFS_SIZE_START = 0, - RTEMS_FTPFS_SIZE_SPACE, - RTEMS_FTPFS_SIZE_NUMBER, - RTEMS_FTPFS_SIZE_NL -} rtems_ftpfs_size_state; - -typedef struct { - rtems_ftpfs_size_state state; - size_t index; - off_t size; -} rtems_ftpfs_size_entry; - -static void rtems_ftpfs_size_parser( - const char* buf, - size_t len, - void *arg -) -{ - rtems_ftpfs_size_entry *se = arg; - size_t i = 0; - - for (i = 0; se->size >= 0 && i < len; ++i, ++se->index) { - int c = buf [i]; - - switch (se->state) { - case RTEMS_FTPFS_SIZE_START: - if (se->index == 2) { - se->state = RTEMS_FTPFS_SIZE_SPACE; - } - break; - case RTEMS_FTPFS_SIZE_SPACE: - if (c == ' ') { - se->state = RTEMS_FTPFS_SIZE_NUMBER; - } else { - se->size = -1; - } - break; - case RTEMS_FTPFS_SIZE_NUMBER: - if (isdigit(c)) { - se->size = 10 * se->size + c - '0'; - } else if (c == '\r') { - se->state = RTEMS_FTPFS_SIZE_NL; - } else { - se->size = -1; - } - break; - case RTEMS_FTPFS_SIZE_NL: - if (c != '\n') { - se->size = -1; - } - break; - default: - se->size = -1; - break; - } - } -} - -static void rtems_ftpfs_get_file_size(rtems_ftpfs_entry *e, bool verbose) -{ - if (e->file_size < 0) { - if (e->write) { - e->file_size = 0; - } else { - rtems_ftpfs_size_entry se; - rtems_ftpfs_reply reply = RTEMS_FTPFS_REPLY_ERROR; - - memset(&se, 0, sizeof(se)); - - reply = rtems_ftpfs_send_command_with_parser( - e, - "SIZE ", - e->filename, - rtems_ftpfs_size_parser, - &se, - verbose - ); - if (reply == RTEMS_FTPFS_REPLY_2 && se.size >= 0) { - e->file_size = se.size; - } else { - e->file_size = 0; - } - } - } -} - -static int rtems_ftpfs_open( - rtems_libio_t *iop, - const char *path, - int oflag, - mode_t mode -) -{ - int eno = 0; - rtems_ftpfs_entry *e = iop->pathinfo.node_access; - rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; - bool verbose = me->verbose; - const struct timeval *timeout = &me->timeout; - - e->write = rtems_libio_iop_is_writeable(iop); - - /* Check for either read-only or write-only flags */ - if ( - rtems_libio_iop_is_writeable(iop) - && rtems_libio_iop_is_readable(iop) - ) { - eno = ENOTSUP; - } - - if (eno == 0) { - rtems_ftpfs_get_file_size(e, verbose); - } - - if (eno == 0) { - const char *file_command = e->write ? "STOR " : "RETR "; - - /* Open passive data connection */ - eno = rtems_ftpfs_open_data_connection_passive( - e, - file_command, - verbose, - timeout - ); - if (eno == ENOTSUP) { - /* Open active data connection */ - eno = rtems_ftpfs_open_data_connection_active( - e, - file_command, - verbose, - timeout - ); - } - } - - /* Set data connection timeout */ - if (eno == 0) { - eno = rtems_ftpfs_set_connection_timeout(e->data_socket, timeout); - } - - if (eno == 0) { - return 0; - } else { - rtems_ftpfs_close_data_connection(e, verbose, true); - - rtems_set_errno_and_return_minus_one(eno); - } -} - -static ssize_t rtems_ftpfs_read( - rtems_libio_t *iop, - void *buffer, - size_t count -) -{ - rtems_ftpfs_entry *e = iop->pathinfo.node_access; - const rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; - bool verbose = me->verbose; - char *in = buffer; - size_t todo = count; - - if (e->eof) { - return 0; - } - - while (todo > 0) { - ssize_t rv = recv(e->data_socket, in, todo, 0); - - if (rv <= 0) { - if (rv == 0) { - rtems_ftpfs_reply reply = - rtems_ftpfs_get_reply(e, NULL, NULL, verbose); - - if (reply == RTEMS_FTPFS_REPLY_2) { - e->eof = true; - break; - } - } - - rtems_set_errno_and_return_minus_one(EIO); - } - - in += rv; - todo -= (size_t) rv; - } - - return (ssize_t) (count - todo); -} - -static ssize_t rtems_ftpfs_write( - rtems_libio_t *iop, - const void *buffer, - size_t count -) -{ - rtems_ftpfs_entry *e = iop->pathinfo.node_access; - const char *out = buffer; - size_t todo = count; - - while (todo > 0) { - ssize_t rv = send(e->data_socket, out, todo, 0); - - if (rv <= 0) { - if (rv == 0) { - break; - } else { - rtems_set_errno_and_return_minus_one(EIO); - } - } - - out += rv; - todo -= (size_t) rv; - - e->file_size += rv; - } - - return (ssize_t) (count - todo); -} - -static int rtems_ftpfs_close(rtems_libio_t *iop) -{ - rtems_ftpfs_entry *e = iop->pathinfo.node_access; - const rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; - int eno = rtems_ftpfs_close_data_connection(e, me->verbose, false); - - if (eno == 0) { - return 0; - } else { - rtems_set_errno_and_return_minus_one(eno); - } -} - -/* Dummy version to let fopen(*,"w") work properly */ -static int rtems_ftpfs_ftruncate(rtems_libio_t *iop, off_t count) -{ - return 0; -} - -static void rtems_ftpfs_eval_path( - rtems_filesystem_eval_path_context_t *self -) -{ - int eno = 0; - - rtems_filesystem_eval_path_eat_delimiter(self); - - if (rtems_filesystem_eval_path_has_path(self)) { - const char *path = rtems_filesystem_eval_path_get_path(self); - size_t pathlen = rtems_filesystem_eval_path_get_pathlen(self); - rtems_ftpfs_entry *e = calloc(1, sizeof(*e) + pathlen + 1); - - rtems_filesystem_eval_path_clear_path(self); - - if (e != NULL) { - memcpy(e->buffer, path, pathlen); - - eno = rtems_ftpfs_split_names( - e->buffer, - &e->user, - &e->password, - &e->hostname, - &e->filename - ); - - DEBUG_PRINTF( - "user = '%s', password = '%s', filename = '%s'\n", - e->user, - e->password, - e->filename - ); - - if (eno == 0) { - rtems_filesystem_location_info_t *currentloc = - rtems_filesystem_eval_path_get_currentloc(self); - rtems_ftpfs_mount_entry *me = currentloc->mt_entry->fs_info; - - rtems_libio_lock(); - ++me->ino; - e->ino = me->ino; - rtems_libio_unlock(); - - e->file_size = -1; - e->ctrl_socket = -1; - - eno = rtems_ftpfs_open_ctrl_connection( - e, - me->verbose, - &me->timeout - ); - if (eno == 0) { - currentloc->node_access = e; - currentloc->handlers = &rtems_ftpfs_handlers; - } - } - - if (eno != 0) { - free(e); - } - } else { - eno = ENOMEM; - } - } - - if (eno != 0) { - rtems_filesystem_eval_path_error(self, eno); - } -} - -static void rtems_ftpfs_free_node(const rtems_filesystem_location_info_t *loc) -{ - rtems_ftpfs_entry *e = loc->node_access; - - /* The root node handler has no entry */ - if (e != NULL) { - const rtems_ftpfs_mount_entry *me = loc->mt_entry->fs_info; - - /* Close control connection if necessary */ - if (e->ctrl_socket >= 0) { - rtems_ftpfs_send_command(e, "QUIT", NULL, me->verbose); - - close(e->ctrl_socket); - } - - free(e); - } -} - -int rtems_ftpfs_initialize( - rtems_filesystem_mount_table_entry_t *e, - const void *d -) -{ - rtems_ftpfs_mount_entry *me = calloc(1, sizeof(*me)); - - /* Mount entry for FTP file system instance */ - e->fs_info = me; - if (e->fs_info == NULL) { - rtems_set_errno_and_return_minus_one(ENOMEM); - } - me->verbose = false; - me->timeout.tv_sec = 0; - me->timeout.tv_usec = 0; - - /* Set handler and oparations table */ - e->mt_fs_root->location.handlers = &rtems_ftpfs_root_handlers; - e->ops = &rtems_ftpfs_ops; - - /* We maintain no real file system nodes, so there is no real root */ - e->mt_fs_root->location.node_access = NULL; - - return 0; -} - -static void rtems_ftpfs_unmount_me( - rtems_filesystem_mount_table_entry_t *e -) -{ - free(e->fs_info); -} - -static int rtems_ftpfs_ioctl( - rtems_libio_t *iop, - ioctl_command_t command, - void *arg -) -{ - rtems_ftpfs_mount_entry *me = iop->pathinfo.mt_entry->fs_info; - bool *verbose = arg; - struct timeval *timeout = arg; - - if (arg == NULL) { - rtems_set_errno_and_return_minus_one(EINVAL); - } - - switch (command) { - case RTEMS_FTPFS_IOCTL_GET_VERBOSE: - *verbose = me->verbose; - break; - case RTEMS_FTPFS_IOCTL_SET_VERBOSE: - me->verbose = *verbose; - break; - case RTEMS_FTPFS_IOCTL_GET_TIMEOUT: - *timeout = me->timeout; - break; - case RTEMS_FTPFS_IOCTL_SET_TIMEOUT: - me->timeout = *timeout; - break; - default: - rtems_set_errno_and_return_minus_one(EINVAL); - } - - return 0; -} - -/* - * The stat() support is intended only for the cp shell command. Each request - * will return that we have a regular file with read, write and execute - * permissions for every one. The node index uses a global counter to support - * a remote to remote copy. This is not a very sophisticated method. - */ -static int rtems_ftpfs_fstat( - const rtems_filesystem_location_info_t *loc, - struct stat *st -) -{ - int eno = 0; - rtems_ftpfs_entry *e = loc->node_access; - - /* FIXME */ - st->st_ino = e->ino; - st->st_dev = rtems_filesystem_make_dev_t(0xcc494cd6U, 0x1d970b4dU); - - st->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; - - if (e->do_size_command) { - const rtems_ftpfs_mount_entry *me = loc->mt_entry->fs_info; - - rtems_ftpfs_get_file_size(e, me->verbose); - st->st_size = e->file_size; - } else { - e->do_size_command = true; - } - - if (eno == 0) { - return 0; - } else { - rtems_set_errno_and_return_minus_one(eno); - } -} - -static void rtems_ftpfs_lock_or_unlock( - const rtems_filesystem_mount_table_entry_t *mt_entry -) -{ - /* Do nothing */ -} - -static const rtems_filesystem_operations_table rtems_ftpfs_ops = { - .lock_h = rtems_ftpfs_lock_or_unlock, - .unlock_h = rtems_ftpfs_lock_or_unlock, - .eval_path_h = rtems_ftpfs_eval_path, - .link_h = rtems_filesystem_default_link, - .are_nodes_equal_h = rtems_filesystem_default_are_nodes_equal, - .mknod_h = rtems_filesystem_default_mknod, - .rmnod_h = rtems_filesystem_default_rmnod, - .fchmod_h = rtems_filesystem_default_fchmod, - .chown_h = rtems_filesystem_default_chown, - .clonenod_h = rtems_filesystem_default_clonenode, - .freenod_h = rtems_ftpfs_free_node, - .mount_h = rtems_filesystem_default_mount, - .unmount_h = rtems_filesystem_default_unmount, - .fsunmount_me_h = rtems_ftpfs_unmount_me, - .utime_h = rtems_filesystem_default_utime, - .symlink_h = rtems_filesystem_default_symlink, - .readlink_h = rtems_filesystem_default_readlink, - .rename_h = rtems_filesystem_default_rename, - .statvfs_h = rtems_filesystem_default_statvfs -}; - -static const rtems_filesystem_file_handlers_r rtems_ftpfs_handlers = { - .open_h = rtems_ftpfs_open, - .close_h = rtems_ftpfs_close, - .read_h = rtems_ftpfs_read, - .write_h = rtems_ftpfs_write, - .ioctl_h = rtems_filesystem_default_ioctl, - .lseek_h = rtems_filesystem_default_lseek, - .fstat_h = rtems_ftpfs_fstat, - .ftruncate_h = rtems_ftpfs_ftruncate, - .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, - .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, - .fcntl_h = rtems_filesystem_default_fcntl, - .kqfilter_h = rtems_filesystem_default_kqfilter, - .mmap_h = rtems_filesystem_default_mmap, - .poll_h = rtems_filesystem_default_poll, - .readv_h = rtems_filesystem_default_readv, - .writev_h = rtems_filesystem_default_writev -}; - -static const rtems_filesystem_file_handlers_r rtems_ftpfs_root_handlers = { - .open_h = rtems_filesystem_default_open, - .close_h = rtems_filesystem_default_close, - .read_h = rtems_filesystem_default_read, - .write_h = rtems_filesystem_default_write, - .ioctl_h = rtems_ftpfs_ioctl, - .lseek_h = rtems_filesystem_default_lseek, - .fstat_h = rtems_filesystem_default_fstat, - .ftruncate_h = rtems_filesystem_default_ftruncate, - .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, - .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, - .fcntl_h = rtems_filesystem_default_fcntl, - .kqfilter_h = rtems_filesystem_default_kqfilter, - .mmap_h = rtems_filesystem_default_mmap, - .poll_h = rtems_filesystem_default_poll, - .readv_h = rtems_filesystem_default_readv, - .writev_h = rtems_filesystem_default_writev -}; diff --git a/cpukit/libnetworking/lib/tftpDriver.c b/cpukit/libnetworking/lib/tftpDriver.c deleted file mode 100644 index 7cbb402b63..0000000000 --- a/cpukit/libnetworking/lib/tftpDriver.c +++ /dev/null @@ -1,1066 +0,0 @@ -/* - * Trivial File Transfer Protocol (RFC 1350) - * - * Transfer file to/from remote host - * - * W. Eric Norum - * Saskatchewan Accelerator Laboratory - * University of Saskatchewan - * Saskatoon, Saskatchewan, CANADA - * eric@skatter.usask.ca - * - * Modifications to support reference counting in the file system are - * Copyright (c) 2012 embedded brains GmbH. - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef RTEMS_NETWORKING -#include -#endif - -#ifdef RTEMS_TFTP_DRIVER_DEBUG -int rtems_tftp_driver_debug = 1; -#endif - -/* - * Range of UDP ports to try - */ -#define UDP_PORT_BASE 3180 - -/* - * Default limits - */ -#define PACKET_FIRST_TIMEOUT_MILLISECONDS 400L -#define PACKET_TIMEOUT_MILLISECONDS 6000L -#define OPEN_RETRY_LIMIT 10 -#define IO_RETRY_LIMIT 10 - -/* - * TFTP opcodes - */ -#define TFTP_OPCODE_RRQ 1 -#define TFTP_OPCODE_WRQ 2 -#define TFTP_OPCODE_DATA 3 -#define TFTP_OPCODE_ACK 4 -#define TFTP_OPCODE_ERROR 5 - -/* - * Largest data transfer - */ -#define TFTP_BUFSIZE 512 - -/* - * Packets transferred between machines - */ -union tftpPacket { - /* - * RRQ/WRQ packet - */ - struct tftpRWRQ { - uint16_t opcode; - char filename_mode[TFTP_BUFSIZE]; - } tftpRWRQ; - - /* - * DATA packet - */ - struct tftpDATA { - uint16_t opcode; - uint16_t blocknum; - uint8_t data[TFTP_BUFSIZE]; - } tftpDATA; - - /* - * ACK packet - */ - struct tftpACK { - uint16_t opcode; - uint16_t blocknum; - } tftpACK; - - /* - * ERROR packet - */ - struct tftpERROR { - uint16_t opcode; - uint16_t errorCode; - char errorMessage[TFTP_BUFSIZE]; - } tftpERROR; -}; - -/* - * State of each TFTP stream - */ -struct tftpStream { - /* - * Buffer for storing most recently-received packet - */ - union tftpPacket pkbuf; - - /* - * Last block number transferred - */ - uint16_t blocknum; - - /* - * Data transfer socket - */ - int socket; - struct sockaddr_in myAddress; - struct sockaddr_in farAddress; - - /* - * Indices into buffer - */ - int nleft; - int nused; - - /* - * Flags - */ - int firstReply; - int eof; - int writing; -}; - -/* - * Flags for filesystem info. - */ -#define TFTPFS_VERBOSE (1 << 0) - -/* - * TFTP File system info. - */ -typedef struct tftpfs_info_s { - uint32_t flags; - rtems_mutex tftp_mutex; - int nStreams; - struct tftpStream ** volatile tftpStreams; -} tftpfs_info_t; - -#define tftpfs_info_mount_table(_mt) ((tftpfs_info_t*) ((_mt)->fs_info)) -#define tftpfs_info_pathloc(_pl) ((tftpfs_info_t*) ((_pl)->mt_entry->fs_info)) -#define tftpfs_info_iop(_iop) (tftpfs_info_pathloc (&((_iop)->pathinfo))) - -/* - * Number of streams open at the same time - */ - -static const rtems_filesystem_operations_table rtems_tftp_ops; -static const rtems_filesystem_file_handlers_r rtems_tftp_handlers; - -static bool rtems_tftp_is_directory( - const char *path, - size_t pathlen -) -{ - return path [pathlen - 1] == '/'; -} - -int rtems_tftpfs_initialize( - rtems_filesystem_mount_table_entry_t *mt_entry, - const void *data -) -{ - const char *device = mt_entry->dev; - size_t devicelen = strlen (device); - tftpfs_info_t *fs = NULL; - char *root_path; - - if (devicelen == 0) { - root_path = malloc (1); - if (root_path == NULL) - goto error; - root_path [0] = '\0'; - } - else { - root_path = malloc (devicelen + 2); - if (root_path == NULL) - goto error; - - root_path = memcpy (root_path, device, devicelen); - root_path [devicelen] = '/'; - root_path [devicelen + 1] = '\0'; - } - - fs = malloc (sizeof (*fs)); - if (fs == NULL) - goto error; - fs->flags = 0; - fs->nStreams = 0; - fs->tftpStreams = 0; - - mt_entry->fs_info = fs; - mt_entry->mt_fs_root->location.node_access = root_path; - mt_entry->mt_fs_root->location.handlers = &rtems_tftp_handlers; - mt_entry->ops = &rtems_tftp_ops; - - /* - * Now allocate a semaphore for mutual exclusion. - * - * NOTE: This could be in an fsinfo for this filesystem type. - */ - - rtems_mutex_init (&fs->tftp_mutex, "TFTPFS"); - - if (data) { - char* config = (char*) data; - char* token; - char* saveptr; - token = strtok_r (config, " ", &saveptr); - while (token) { - if (strcmp (token, "verbose") == 0) - fs->flags |= TFTPFS_VERBOSE; - token = strtok_r (NULL, " ", &saveptr); - } - } - - return 0; - -error: - - free (fs); - free (root_path); - - rtems_set_errno_and_return_minus_one (ENOMEM); -} - -/* - * Release a stream and clear the pointer to it - */ -static void -releaseStream (tftpfs_info_t *fs, int s) -{ - if (fs->tftpStreams[s] && (fs->tftpStreams[s]->socket >= 0)) - close (fs->tftpStreams[s]->socket); - rtems_mutex_lock (&fs->tftp_mutex); - free (fs->tftpStreams[s]); - fs->tftpStreams[s] = NULL; - rtems_mutex_unlock (&fs->tftp_mutex); -} - -static void -rtems_tftpfs_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry) -{ - tftpfs_info_t *fs = tftpfs_info_mount_table (mt_entry); - int s; - for (s = 0; s < fs->nStreams; s++) - releaseStream (fs, s); - rtems_mutex_destroy (&fs->tftp_mutex); - free (fs); - free (mt_entry->mt_fs_root->location.node_access); -} - -/* - * Map error message - */ -static int -tftpErrno (struct tftpStream *tp) -{ - unsigned int tftpError; - static const int errorMap[] = { - EINVAL, - ENOENT, - EPERM, - ENOSPC, - EINVAL, - ENXIO, - EEXIST, - ESRCH, - }; - - tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); - if (tftpError < (sizeof errorMap / sizeof errorMap[0])) - return errorMap[tftpError]; - else - return 1000 + tftpError; -} - -/* - * Send a message to make the other end shut up - */ -static void -sendStifle (struct tftpStream *tp, struct sockaddr_in *to) -{ - int len; - struct { - uint16_t opcode; - uint16_t errorCode; - char errorMessage[12]; - } msg; - - /* - * Create the error packet (Unknown transfer ID). - */ - msg.opcode = htons (TFTP_OPCODE_ERROR); - msg.errorCode = htons (5); - len = sizeof msg.opcode + sizeof msg.errorCode + 1; - len += sprintf (msg.errorMessage, "GO AWAY"); - - /* - * Send it - */ - sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to); -} - -/* - * Wait for a data packet - */ -static int -getPacket (struct tftpStream *tp, int retryCount) -{ - int len; - struct timeval tv; - - if (retryCount == 0) { - tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000L; - tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000L) * 1000L; - } - else { - tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000L; - tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000L) * 1000L; - } - setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); - for (;;) { - union { - struct sockaddr s; - struct sockaddr_in i; - } from; - socklen_t fromlen = sizeof from; - len = recvfrom (tp->socket, &tp->pkbuf, - sizeof tp->pkbuf, 0, - &from.s, &fromlen); - if (len < 0) - break; - if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { - if (tp->firstReply) { - tp->firstReply = 0; - tp->farAddress.sin_port = from.i.sin_port; - } - if (tp->farAddress.sin_port == from.i.sin_port) - break; - } - - /* - * Packet is from someone with whom we are - * not interested. Tell them to go away. - */ - sendStifle (tp, &from.i); - } - tv.tv_sec = 0; - tv.tv_usec = 0; - setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); -#ifdef RTEMS_TFTP_DRIVER_DEBUG - if (rtems_tftp_driver_debug) { - if (len >= (int) sizeof tp->pkbuf.tftpACK) { - int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); - switch (opcode) { - default: - printf ("TFTP: OPCODE %d\n", opcode); - break; - - case TFTP_OPCODE_DATA: - printf ("TFTP: RECV %d\n", ntohs (tp->pkbuf.tftpDATA.blocknum)); - break; - - case TFTP_OPCODE_ACK: - printf ("TFTP: GOT ACK %d\n", ntohs (tp->pkbuf.tftpACK.blocknum)); - break; - } - } - else { - printf ("TFTP: %d-byte packet\n", len); - } - } -#endif - return len; -} - -/* - * Send an acknowledgement - */ -static int -sendAck (struct tftpStream *tp) -{ -#ifdef RTEMS_TFTP_DRIVER_DEBUG - if (rtems_tftp_driver_debug) - printf ("TFTP: ACK %d\n", tp->blocknum); -#endif - - /* - * Create the acknowledgement - */ - tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); - tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); - - /* - * Send it - */ - if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0, - (struct sockaddr *)&tp->farAddress, - sizeof tp->farAddress) < 0) - return errno; - return 0; -} - -/* - * Convert a path to canonical form - */ -static void -fixPath (char *path) -{ - char *inp, *outp, *base; - - outp = inp = path; - base = NULL; - for (;;) { - if (inp[0] == '.') { - if (inp[1] == '\0') - break; - if (inp[1] == '/') { - inp += 2; - continue; - } - if (inp[1] == '.') { - if (inp[2] == '\0') { - if ((base != NULL) && (outp > base)) { - outp--; - while ((outp > base) && (outp[-1] != '/')) - outp--; - } - break; - } - if (inp[2] == '/') { - inp += 3; - if (base == NULL) - continue; - if (outp > base) { - outp--; - while ((outp > base) && (outp[-1] != '/')) - outp--; - } - continue; - } - } - } - if (base == NULL) - base = inp; - while (inp[0] != '/') { - if ((*outp++ = *inp++) == '\0') - return; - } - *outp++ = '/'; - while (inp[0] == '/') - inp++; - } - *outp = '\0'; - return; -} - -static void rtems_tftp_eval_path(rtems_filesystem_eval_path_context_t *self) -{ - int eval_flags = rtems_filesystem_eval_path_get_flags (self); - - if ((eval_flags & RTEMS_FS_MAKE) == 0) { - int rw = RTEMS_FS_PERMS_READ | RTEMS_FS_PERMS_WRITE; - - if ((eval_flags & rw) != rw) { - rtems_filesystem_location_info_t *currentloc = - rtems_filesystem_eval_path_get_currentloc (self); - char *current = currentloc->node_access; - size_t currentlen = strlen (current); - const char *path = rtems_filesystem_eval_path_get_path (self); - size_t pathlen = rtems_filesystem_eval_path_get_pathlen (self); - size_t len = currentlen + pathlen; - - rtems_filesystem_eval_path_clear_path (self); - - current = realloc (current, len + 1); - if (current != NULL) { - memcpy (current + currentlen, path, pathlen); - current [len] = '\0'; - if (!rtems_tftp_is_directory (current, len)) { - fixPath (current); - } - currentloc->node_access = current; - } else { - rtems_filesystem_eval_path_error (self, ENOMEM); - } - } else { - rtems_filesystem_eval_path_error (self, EINVAL); - } - } else { - rtems_filesystem_eval_path_error (self, EIO); - } -} - -/* - * The routine which does most of the work for the IMFS open handler - */ -static int rtems_tftp_open_worker( - rtems_libio_t *iop, - char *full_path_name, - int oflag -) -{ - tftpfs_info_t *fs; - struct tftpStream *tp; - int retryCount; - struct in_addr farAddress; - int s; - int len; - char *cp1; - char *cp2; - char *remoteFilename; - rtems_interval now; - char *hostname; - - /* - * Get the file system info. - */ - fs = tftpfs_info_iop (iop); - - /* - * Extract the host name component - */ - if (*full_path_name == '/') - full_path_name++; - - hostname = full_path_name; - cp1 = strchr (full_path_name, ':'); - if (!cp1) { -#ifdef RTEMS_NETWORKING - hostname = "BOOTP_HOST"; -#endif - } else { - *cp1 = '\0'; - ++cp1; - } - - /* - * Convert hostname to Internet address - */ -#ifdef RTEMS_NETWORKING - if (strcmp (hostname, "BOOTP_HOST") == 0) - farAddress = rtems_bsdnet_bootp_server_address; - else -#endif - if (inet_aton (hostname, &farAddress) == 0) { - struct hostent *he = gethostbyname(hostname); - if (he == NULL) - return ENOENT; - memcpy (&farAddress, he->h_addr, sizeof (farAddress)); - } - - /* - * Extract file pathname component - */ -#ifdef RTEMS_NETWORKING - if (strcmp (cp1, "BOOTP_FILE") == 0) { - cp1 = rtems_bsdnet_bootp_boot_file_name; - } -#endif - if (*cp1 == '\0') - return ENOENT; - remoteFilename = cp1; - if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) - return ENOENT; - - /* - * Find a free stream - */ - rtems_mutex_lock (&fs->tftp_mutex); - for (s = 0 ; s < fs->nStreams ; s++) { - if (fs->tftpStreams[s] == NULL) - break; - } - if (s == fs->nStreams) { - /* - * Reallocate stream pointers - * Guard against the case where realloc() returns NULL. - */ - struct tftpStream **np; - - np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams); - if (np == NULL) { - rtems_mutex_unlock (&fs->tftp_mutex); - return ENOMEM; - } - fs->tftpStreams = np; - } - tp = fs->tftpStreams[s] = malloc (sizeof (struct tftpStream)); - rtems_mutex_unlock (&fs->tftp_mutex); - if (tp == NULL) - return ENOMEM; - iop->data0 = s; - iop->data1 = tp; - - /* - * Create the socket - */ - if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { - releaseStream (fs, s); - return ENOMEM; - } - - /* - * Bind the socket to a local address - */ - retryCount = 0; - now = rtems_clock_get_ticks_since_boot(); - for (;;) { - int try = (now + retryCount) % 10; - - tp->myAddress.sin_family = AF_INET; - tp->myAddress.sin_port = htons (UDP_PORT_BASE + fs->nStreams * try + s); - tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY); - if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0) - break; - if (++retryCount == 10) { - releaseStream (fs, s); - return EBUSY; - } - } - - /* - * Set the UDP destination to the TFTP server - * port on the remote machine. - */ - tp->farAddress.sin_family = AF_INET; - tp->farAddress.sin_addr = farAddress; - tp->farAddress.sin_port = htons (69); - - /* - * Start the transfer - */ - tp->firstReply = 1; - retryCount = 0; - for (;;) { - /* - * Create the request - */ - if ((oflag & O_ACCMODE) == O_RDONLY) { - tp->writing = 0; - 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; - cp2 = (char *) remoteFilename; - while ((*cp1++ = *cp2++) != '\0') - continue; - cp2 = "octet"; - while ((*cp1++ = *cp2++) != '\0') - continue; - len = cp1 - (char *)&tp->pkbuf.tftpRWRQ; - - /* - * Send the request - */ - if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, - (struct sockaddr *)&tp->farAddress, - sizeof tp->farAddress) < 0) { - releaseStream (fs, s); - return EIO; - } - - /* - * Get reply - */ - len = getPacket (tp, retryCount); - if (len >= (int) sizeof tp->pkbuf.tftpACK) { - int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); - if (!tp->writing - && (opcode == TFTP_OPCODE_DATA) - && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { - tp->nused = 0; - tp->blocknum = 1; - tp->nleft = len - 2 * sizeof (uint16_t ); - tp->eof = (tp->nleft < TFTP_BUFSIZE); - if (sendAck (tp) != 0) { - releaseStream (fs, s); - return EIO; - } - 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) { - int e = tftpErrno (tp); - releaseStream (fs, s); - return e; - } - } - - /* - * Keep trying - */ - if (++retryCount >= OPEN_RETRY_LIMIT) { - releaseStream (fs, s); - return EIO; - } - } - return 0; -} - -static int rtems_tftp_open( - rtems_libio_t *iop, - const char *new_name, - int oflag, - mode_t mode -) -{ - tftpfs_info_t *fs; - char *full_path_name; - int err; - - full_path_name = iop->pathinfo.node_access; - - if (rtems_tftp_is_directory (full_path_name, strlen (full_path_name))) { - rtems_set_errno_and_return_minus_one (ENOTSUP); - } - - /* - * Get the file system info. - */ - fs = tftpfs_info_iop (iop); - - if (fs->flags & TFTPFS_VERBOSE) - printf ("TFTPFS: %s\n", full_path_name); - - err = rtems_tftp_open_worker (iop, full_path_name, oflag); - if (err != 0) { - rtems_set_errno_and_return_minus_one (err); - } - - return 0; -} - -/* - * Read from a TFTP stream - */ -static ssize_t rtems_tftp_read( - rtems_libio_t *iop, - void *buffer, - size_t count -) -{ - char *bp; - struct tftpStream *tp = iop->data1; - int retryCount; - int nwant; - - if (!tp) - rtems_set_errno_and_return_minus_one( EIO ); - - /* - * Read till user request is satisfied or EOF is reached - */ - bp = buffer; - nwant = count; - while (nwant) { - if (tp->nleft) { - int ncopy; - if (nwant < tp->nleft) - ncopy = nwant; - else - ncopy = tp->nleft; - memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy); - tp->nused += ncopy; - tp->nleft -= ncopy; - bp += ncopy; - nwant -= ncopy; - if (nwant == 0) - break; - } - if (tp->eof) - break; - - /* - * Wait for the next packet - */ - retryCount = 0; - for (;;) { - int len = getPacket (tp, retryCount); - if (len >= (int)sizeof tp->pkbuf.tftpACK) { - int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); - uint16_t nextBlock = tp->blocknum + 1; - if ((opcode == TFTP_OPCODE_DATA) - && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { - tp->nused = 0; - tp->nleft = len - 2 * sizeof (uint16_t); - tp->eof = (tp->nleft < TFTP_BUFSIZE); - tp->blocknum++; - if (sendAck (tp) != 0) - rtems_set_errno_and_return_minus_one (EIO); - break; - } - if (opcode == TFTP_OPCODE_ERROR) - rtems_set_errno_and_return_minus_one (tftpErrno (tp)); - } - - /* - * Keep trying? - */ - if (++retryCount == IO_RETRY_LIMIT) - rtems_set_errno_and_return_minus_one (EIO); - if (sendAck (tp) != 0) - rtems_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 (uint16_t ); - for (;;) { - tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA); - tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum); -#ifdef RTEMS_TFTP_DRIVER_DEBUG - if (rtems_tftp_driver_debug) - printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused); -#endif - if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0, - (struct sockaddr *)&tp->farAddress, - sizeof tp->farAddress) < 0) - return EIO; - rlen = getPacket (tp, retryCount); - /* - * Our last packet won't necessarily be acknowledged! - */ - if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data)) - return 0; - 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); - } - - /* - * Keep trying? - */ - if (++retryCount == IO_RETRY_LIMIT) - return EIO; - } -} - -/* - * Close a TFTP stream - */ -static int rtems_tftp_close( - rtems_libio_t *iop -) -{ - tftpfs_info_t *fs; - struct tftpStream *tp = iop->data1; - int e = 0; - - /* - * Get the file system info. - */ - fs = tftpfs_info_iop (iop); - - if (!tp) - rtems_set_errno_and_return_minus_one (EIO); - - if (tp->writing) - e = rtems_tftp_flush (tp); - if (!tp->eof && !tp->firstReply) { - /* - * Tell the other end to stop - */ - rtems_interval ticksPerSecond; - sendStifle (tp, &tp->farAddress); - ticksPerSecond = rtems_clock_get_ticks_per_second(); - rtems_task_wake_after (1 + ticksPerSecond / 10); - } - releaseStream (fs, iop->data0); - if (e) - rtems_set_errno_and_return_minus_one (e); - return 0; -} - -static ssize_t rtems_tftp_write( - rtems_libio_t *iop, - const void *buffer, - size_t count -) -{ - const char *bp; - struct tftpStream *tp = iop->data1; - int nleft, nfree, ncopy; - - /* - * Bail out if an error has occurred - */ - if (!tp->writing) - rtems_set_errno_and_return_minus_one (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; - rtems_set_errno_and_return_minus_one (e); - } - } - } - return count; -} - -/* - * Dummy version to let fopen(xxxx,"w") work properly. - */ -static int rtems_tftp_ftruncate( - rtems_libio_t *iop RTEMS_UNUSED, - off_t count RTEMS_UNUSED -) -{ - return 0; -} - -static int rtems_tftp_fstat( - const rtems_filesystem_location_info_t *loc, - struct stat *buf -) -{ - const char *path = loc->node_access; - size_t pathlen = strlen (path); - - buf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO - | (rtems_tftp_is_directory (path, pathlen) ? S_IFDIR : S_IFREG); - - return 0; -} - -static int rtems_tftp_clone( - rtems_filesystem_location_info_t *loc -) -{ - int rv = 0; - - loc->node_access = strdup (loc->node_access); - - if (loc->node_access == NULL) { - errno = ENOMEM; - rv = -1; - } - - return rv; -} - -static void rtems_tftp_free_node_info( - const rtems_filesystem_location_info_t *loc -) -{ - free (loc->node_access); -} - -static bool rtems_tftp_are_nodes_equal( - const rtems_filesystem_location_info_t *a, - const rtems_filesystem_location_info_t *b -) -{ - return strcmp (a->node_access, b->node_access) == 0; -} - -static const rtems_filesystem_operations_table rtems_tftp_ops = { - .lock_h = rtems_filesystem_default_lock, - .unlock_h = rtems_filesystem_default_unlock, - .eval_path_h = rtems_tftp_eval_path, - .link_h = rtems_filesystem_default_link, - .are_nodes_equal_h = rtems_tftp_are_nodes_equal, - .mknod_h = rtems_filesystem_default_mknod, - .rmnod_h = rtems_filesystem_default_rmnod, - .fchmod_h = rtems_filesystem_default_fchmod, - .chown_h = rtems_filesystem_default_chown, - .clonenod_h = rtems_tftp_clone, - .freenod_h = rtems_tftp_free_node_info, - .mount_h = rtems_filesystem_default_mount, - .unmount_h = rtems_filesystem_default_unmount, - .fsunmount_me_h = rtems_tftpfs_shutdown, - .utime_h = rtems_filesystem_default_utime, - .symlink_h = rtems_filesystem_default_symlink, - .readlink_h = rtems_filesystem_default_readlink, - .rename_h = rtems_filesystem_default_rename, - .statvfs_h = rtems_filesystem_default_statvfs -}; - -static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = { - .open_h = rtems_tftp_open, - .close_h = rtems_tftp_close, - .read_h = rtems_tftp_read, - .write_h = rtems_tftp_write, - .ioctl_h = rtems_filesystem_default_ioctl, - .lseek_h = rtems_filesystem_default_lseek, - .fstat_h = rtems_tftp_fstat, - .ftruncate_h = rtems_tftp_ftruncate, - .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, - .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, - .fcntl_h = rtems_filesystem_default_fcntl, - .kqfilter_h = rtems_filesystem_default_kqfilter, - .mmap_h = rtems_filesystem_default_mmap, - .poll_h = rtems_filesystem_default_poll, - .readv_h = rtems_filesystem_default_readv, - .writev_h = rtems_filesystem_default_writev -}; diff --git a/spec/build/cpukit/libftpfs.yml b/spec/build/cpukit/libftpfs.yml index d32304fcf3..bde2f2e988 100644 --- a/spec/build/cpukit/libftpfs.yml +++ b/spec/build/cpukit/libftpfs.yml @@ -14,6 +14,6 @@ install: install-path: ${BSP_LIBDIR} links: [] source: -- cpukit/libnetworking/lib/ftpfs.c +- cpukit/libfs/src/ftpfs/ftpfs.c target: ftpfs type: build diff --git a/spec/build/cpukit/libtftpfs.yml b/spec/build/cpukit/libtftpfs.yml index 57c75c6639..90a0eabc80 100644 --- a/spec/build/cpukit/libtftpfs.yml +++ b/spec/build/cpukit/libtftpfs.yml @@ -15,6 +15,6 @@ install: install-path: ${BSP_LIBDIR} links: [] source: -- cpukit/libnetworking/lib/tftpDriver.c +- cpukit/libfs/src/ftpfs/tftpDriver.c target: tftpfs type: build -- cgit v1.2.3