📄 ftpfs.c
字号:
/* * File Transfer Protocol client * * Transfer file to/from remote host * * This driver can be added to the RTEMS file system with a call to * "rtems_bsdnet_initialize_ftp_filesystem () ". * From then on, you can open, read and close files on a remote FTP server * using the following syntax: * To open a file "myfile.txt" in the directory "mydir" (relative to home * directory) on a server named "myserver" using the user id * "myuserid" and the password "my_very_secret_password" you must * specify the following path: * * /FTP/myuserid:my_very_secret_password/@myserver/mydirectory/myfile.txt * * If the server is the default server specified in BOOTP, it can be ommitted: * * /FTP/myuserid:my_very_secret_password/mydirectory/myfile.txt * * WARNING: write accesses have not yet been tested. * * * (c) Copyright 2002 * Thomas Doerfler * IMD Ingenieurbuero fuer Microcomputertechnik * Herbststr. 8 * 82178 Puchheim, Germany * <Thomas.Doerfler@imd-systems.de> * * This code has been created after closly inspecting * "tftpdriver.c" from Eric Norum. * * $Id: ftpfs.c,v 1.3 2003/02/05 21:25:55 joel Exp $ */#include <stdio.h>#include <errno.h>#include <malloc.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <ctype.h>#include <rtems.h>#include <rtems/libio.h>#include <rtems/rtems_bsdnet.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#include <rtems/ftpfs.h>#ifndef set_errno_and_return_minus_one#define set_errno_and_return_minus_one( _error ) \ do { errno = (_error); return -1; } while(0)#endif/* #define DEBUG_OUT *//* * Well-known port for FTP */#define FTP_PORT_NUM 21/* * Pathname prefix */#define FTP_PATHNAME_PREFIX "/FTP/"/* * reply codes */#define FTP_REPLY_CONNECT 220 /* Connection established */#define FTP_REPLY_PASSREQ 331 /* user ok, password required */#define FTP_REPLY_LOGIN 230 /* login finished */#define FTP_REPLY_SUCCESS 200 /* xxx successful */#define FTP_REPLY_OPENCONN 150 /* opening connection for tfer */#define FTP_REPLY_TFERCMPL 226 /* transfer complete */extern rtems_filesystem_operations_table rtems_ftp_ops; extern rtems_filesystem_file_handlers_r rtems_ftp_handlers;/* * FTP command strings */#define FTP_USER_CMD "USER "#define FTP_PASS_CMD "PASS "#define FTP_BINARY_CMD "TYPE I"#define FTP_PORT_CMD "PORT "#define FTP_STOR_CMD "STOR "#define FTP_RETR_CMD "RETR "#define FTP_QUIT_CMD "QUIT"/* * State of each FTP stream */struct ftpStream { /* * Control connection socket */ int ctrl_socket; struct sockaddr_in myCtrlAddress; struct sockaddr_in farCtrlAddress; /* * Data transfer socket */ int port_socket; int data_socket; struct sockaddr_in myDataAddress; struct sockaddr_in farDataAddress; /* * other stuff to remember */ boolean eof_reached;};/* * Number of streams open at the same time */static rtems_id ftp_mutex;static int nStreams;static struct ftpStream ** volatile ftpStreams;extern rtems_filesystem_operations_table rtems_tftp_ops;extern rtems_filesystem_file_handlers_r rtems_tftp_handlers;/* * Direct copy from the IMFS/TFTP. Look at this. */rtems_filesystem_limits_and_options_t rtems_ftp_limits_and_options = { 5, /* link_max */ 6, /* max_canon */ 7, /* max_input */ 255, /* name_max */ 255, /* path_max */ 2, /* pipe_buf */ 1, /* posix_async_io */ 2, /* posix_chown_restrictions */ 3, /* posix_no_trunc */ 4, /* posix_prio_io */ 5, /* posix_sync_io */ 6 /* posix_vdisable */};int rtems_ftp_mount_me( rtems_filesystem_mount_table_entry_t *temp_mt_entry){ rtems_status_code sc; temp_mt_entry->mt_fs_root.handlers = &rtems_ftp_handlers; temp_mt_entry->mt_fs_root.ops = &rtems_ftp_ops; /* * We have no ftp filesystem specific data to maintain. This * filesystem may only be mounted ONCE. * * And we maintain no real filesystem nodes, so there is no real root. */ temp_mt_entry->fs_info = NULL; temp_mt_entry->mt_fs_root.node_access = NULL; /* * These need to be looked at for full POSIX semantics. */ temp_mt_entry->pathconf_limits_and_options = rtems_ftp_limits_and_options; /* * Now allocate a semaphore for mutual exclusion. * * NOTE: This could be in an fsinfo for this filesystem type. */ sc = rtems_semaphore_create (rtems_build_name('F','T','P',' '), 1, RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &ftp_mutex); if (sc != RTEMS_SUCCESSFUL) set_errno_and_return_minus_one( ENOMEM ); return 0;}/* * Initialize the FTP driver */int rtems_bsdnet_initialize_ftp_filesystem () { int status; rtems_filesystem_mount_table_entry_t *entry; status = mkdir( FTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO ); if ( status == -1 ) return status; status = mount( &entry, &rtems_ftp_ops, RTEMS_FILESYSTEM_READ_ONLY, NULL, FTP_PATHNAME_PREFIX ); if ( status ) perror( "FTP mount failed" ); return status;}/* * read and return message code from ftp control connection */int rtems_ftp_get_message( const struct ftpStream *fsp, /* ptr to ftp control structure */ int *msg_code /* ptr to return message code */){ char rd_buffer[4]; size_t rd_size; size_t tmp_size; int eno = 0; rtems_boolean finished = FALSE; do { /* * fetch (at least) 4 characters from control connection * FIXME: how about a timeout? */ rd_size = 0; while ((eno == 0) && (rd_size < sizeof(rd_buffer))) { tmp_size = read(fsp->ctrl_socket, (&rd_buffer)+rd_size, sizeof(rd_buffer)-rd_size); if (tmp_size < 0) { eno = EIO; } else {#ifdef DEBUG_OUT write(1,(&rd_buffer)+rd_size,tmp_size);#endif rd_size += tmp_size; } } /* * check for 3 digits and space, otherwise not finished */ if ((eno == 0) && (isdigit((unsigned int)rd_buffer[0])) && (isdigit((unsigned int)rd_buffer[1])) && (isdigit((unsigned int)rd_buffer[2])) && (rd_buffer[3] == ' ')) { finished = TRUE; rd_buffer[3] = '\0'; *msg_code = atol(rd_buffer); } /* * skip rest until end-of-line */ do { tmp_size = read(fsp->ctrl_socket, &rd_buffer, 1); if (tmp_size < 0) { eno = EIO; }#ifdef DEBUG_OUT else { write(1,(&rd_buffer),tmp_size); }#endif } while ((eno == 0) && (rd_buffer[0] != '\n')); } while ((eno == 0) && !finished); return eno;}/* * split a pseudo file name into host, user, password, filename * NOTE: this function will allocate space for these strings, * the calling function should free the space, when no longer needed * exception: when we return any error, we will also cleanup * the strings * valid forms: * /FTP/user:pass/filepath * /FTP/user:pass@hostname/filepath * /FTP/user:pass/filepath * /FTP/user:pass/@hostname/filepath * NOTE: /FTP is already stripped from the name */int rtems_ftp_split_names( const char *pathname, /* total path name (including prefix) */ char **usernamep, /* ptr to ptr to user name */ char **passwordp, /* ptr to ptr to password */ char **hostnamep, /* ptr to ptr to host name */ char **filenamep) /* ptr to ptr to hostremaining file name */{ const char *chunk_start; const char *chunk_end; size_t chunk_len; int rc = 0; /* * ensure, that result pointers are NULL... */ *usernamep = NULL; *passwordp = NULL; *hostnamep = NULL; *filenamep = NULL;#if 1 chunk_start = pathname;#else /* no longer needed with IMFS */ /* * check, that total path is long enough, skip prefix */ if (rc == 0) { if (strlen (pathname) <= strlen (FTP_PATHNAME_PREFIX)) { rc = ENOENT; } else { chunk_start = pathname + strlen (FTP_PATHNAME_PREFIX); } }#endif /* * fetch user name: terminated with ":" */ if (rc == 0) { chunk_end = strchr(chunk_start,':'); if ((chunk_end == NULL) || /* No ':' found or */ (chunk_end == chunk_start)) { /* ':' is first character-> no name */ rc = ENOENT; } else { chunk_len = chunk_end-chunk_start; *usernamep = malloc(chunk_len+1); if (*usernamep == NULL) { rc = ENOMEM; } else { memcpy(*usernamep,chunk_start,chunk_len); (*usernamep)[chunk_len] = '\0'; } } } /* * fetch password: terminated with "/" or "@" */ if (rc == 0) { chunk_start = chunk_end + 1; /* skip ":" after user name */ chunk_end = strchr(chunk_start,'/'); if ((chunk_end == NULL) || /* No '/' found or */ (chunk_end == chunk_start)) { /* '/' is first character-> no pwd */ rc = ENOENT; } else { /* * we have found a proper '/' * this is the end of the password */ chunk_len = chunk_end-chunk_start; *passwordp = malloc(chunk_len+1); if (*passwordp == NULL) { rc = ENOMEM; } else { memcpy(*passwordp,chunk_start,chunk_len); (*passwordp)[chunk_len] = '\0'; } } } /* * if first char after '/' is '@', then this is the hostname * fetch hostname terminated with "/" * if exists at all. otherwise take default server from bootp */ if (rc == 0) { chunk_start = chunk_end+1; if (*chunk_start == '@') { /* * hostname follows */ chunk_start = chunk_start + 1; /* skip "@" after password */ chunk_end = strchr(chunk_start,'/'); if ((chunk_end == NULL) || /* No '/' found or */ (chunk_end == chunk_start)) { /* '/' is first character-> no host */ rc = ENOENT; } else { /* * we have found a proper '/' */ chunk_len = chunk_end-chunk_start; *hostnamep = malloc(chunk_len+1); if (*hostnamep == NULL) { rc = ENOMEM; } else { memcpy(*hostnamep,chunk_start,chunk_len); (*hostnamep)[chunk_len] = '\0'; } } } else { /* chunk_start != '@' */ /* * no host name given, keep string empty */ *hostnamep = malloc(1); if (*hostnamep == NULL) { rc = ENOMEM; } else { (*hostnamep)[0] = '\0'; } } } /* * fetch filename. This is all the rest... */ if (rc == 0) { chunk_start = chunk_end+1; if (*chunk_start == '\0') { /* nothing left for filename */ rc = ENOENT; } else { chunk_len = strlen(chunk_start); *filenamep = malloc(chunk_len+1); if (*filenamep == NULL) { rc = ENOMEM; } else { memcpy(*filenamep,chunk_start,chunk_len); (*filenamep)[chunk_len] = '\0'; } } } /* * cleanup anything, if error occured */ if (rc != 0) { if (*hostnamep != NULL) { free(*hostnamep); *hostnamep = NULL; } if (*usernamep != NULL) { free(*usernamep); *usernamep = NULL; } if (*passwordp != NULL) { free(*passwordp); *passwordp = NULL; } if (*filenamep != NULL) { free(*filenamep); *filenamep = NULL; } } return rc;} int rtems_ftp_evaluate_for_make( const char *path, /* IN */ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ const char **name /* OUT */){ set_errno_and_return_minus_one( EIO ); }/* * XXX - Fix return values. */int rtems_ftp_eval_path( const char *pathname, /* IN */ int flags, /* IN */ rtems_filesystem_location_info_t *pathloc /* IN/OUT */){ /* * Read-only for now */ if ( ((flags & O_RDONLY) != O_RDONLY ) && ((flags & O_WRONLY) != O_WRONLY )) { set_errno_and_return_minus_one( ENOENT ); } /* * The File system is mounted at FTP_PATHNAME_PREFIX * the caller of this routine has striped off this part of the * name. Save the remainder of the name for use by the open routine. */ pathloc->node_access = (void * ) pathname; pathloc->handlers = &rtems_ftp_handlers; return 0;}/* * Open a FTP stream */int rtems_ftp_open( rtems_libio_t *iop, const char *new_name, unsigned32 flag, unsigned32 mode){ int s = 0; char *filename = NULL; char *uname = NULL; char *upass = NULL; char *hostname = NULL; char port_buffer[sizeof(FTP_PORT_CMD)+6*4+1+1]; rtems_unsigned32 my_ip; rtems_unsigned16 my_port; int eno = 0; rtems_status_code rc; rtems_boolean is_write = FALSE; rtems_boolean sema_obtained = FALSE; struct ftpStream *fsp = NULL; int msg_tmp; int sockaddr_size; /* * check for R/O or W/O flags */ if (eno == 0) { if ((0 != (iop->flags & LIBIO_FLAGS_WRITE)) && (0 != (iop->flags & LIBIO_FLAGS_READ))) { eno = ENOTSUP; } else { is_write = (0 != (iop->flags & LIBIO_FLAGS_WRITE)); } } /* * split pathname into parts */ if (eno == 0) { eno = rtems_ftp_split_names(iop->file_info, &uname, &upass, &hostname, &filename); } /* * Find a free stream */ if (eno == 0) { rc = rtems_semaphore_obtain (ftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (rc == RTEMS_SUCCESSFUL) { sema_obtained = TRUE; } else { eno = EBUSY; } } if (eno == 0) { for (s = 0 ; s < nStreams ; s++) { if (ftpStreams[s] == NULL) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -