📄 tftpdriver.c
字号:
/* * vim: set expandtab tabstop=4 shiftwidth=4 ai : * * 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 * * $Id: tftpDriver.c,v 1.21 2002/10/28 13:55:58 joel Exp $ * */#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <malloc.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <rtems.h>#include <rtems/libio.h>#include <rtems/libio_.h>#include <rtems/seterr.h>#include <rtems/rtems_bsdnet.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#ifdef RTEMS_TFTP_DRIVER_DEBUGint rtems_tftp_driver_debug = 1;#endif/* * Range of UDP ports to try */#define UDP_PORT_BASE 3180/* * Pathname prefix */#define TFTP_PATHNAME_PREFIX "/TFTP/"/* * Root node_access value * By using the address of a local static variable * we ensure a unique value for this identifier. */#define ROOT_NODE_ACCESS (&tftp_mutex)/* * Default limits */#define PACKET_FIRST_TIMEOUT_MILLISECONDS 400#define PACKET_TIMEOUT_MILLISECONDS 6000#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 { rtems_unsigned16 opcode; char filename_mode[TFTP_BUFSIZE]; } tftpRWRQ; /* * DATA packet */ struct tftpDATA { rtems_unsigned16 opcode; rtems_unsigned16 blocknum; rtems_unsigned8 data[TFTP_BUFSIZE]; } tftpDATA; /* * ACK packet */ struct tftpACK { rtems_unsigned16 opcode; rtems_unsigned16 blocknum; } tftpACK; /* * ERROR packet */ struct tftpERROR { rtems_unsigned16 opcode; rtems_unsigned16 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 */ rtems_unsigned16 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;};/* * Number of streams open at the same time */static rtems_id tftp_mutex;static int nStreams;static struct tftpStream ** volatile tftpStreams;typedef const char *tftp_node;extern rtems_filesystem_operations_table rtems_tftp_ops;extern rtems_filesystem_file_handlers_r rtems_tftp_handlers;/* * Direct copy from the IMFS. Look at this. */rtems_filesystem_limits_and_options_t rtems_tftp_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 */};static int rtems_tftp_mount_me( rtems_filesystem_mount_table_entry_t *temp_mt_entry){ rtems_status_code sc; temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers; temp_mt_entry->mt_fs_root.ops = &rtems_tftp_ops; /* * We have no tftp 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 = ROOT_NODE_ACCESS; /* * These need to be looked at for full POSIX semantics. */ temp_mt_entry->pathconf_limits_and_options = rtems_tftp_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('T', 'F', 'T', 'P'), 1, RTEMS_FIFO | RTEMS_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, 0, &tftp_mutex ); if (sc != RTEMS_SUCCESSFUL) rtems_set_errno_and_return_minus_one( ENOMEM ); return 0;}/* * Initialize the TFTP driver */int rtems_bsdnet_initialize_tftp_filesystem () { int status; rtems_filesystem_mount_table_entry_t *entry; status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO ); if ( status == -1 ) return status; status = mount( &entry, &rtems_tftp_ops, RTEMS_FILESYSTEM_READ_WRITE, NULL, TFTP_PATHNAME_PREFIX ); if ( status ) perror( "TFTP mount failed" ); return status;}/* * Map error message */static inttftpErrno (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 voidsendStifle (struct tftpStream *tp, struct sockaddr_in *to){ int len; struct { rtems_unsigned16 opcode; rtems_unsigned16 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 intgetPacket (struct tftpStream *tp, int retryCount){ int len; struct timeval tv; if (retryCount == 0) { tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000; tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000) * 1000; } else { tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000; tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000) * 1000; } setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); for (;;) { union { struct sockaddr s; struct sockaddr_in i; } from; int fromlen = sizeof from; len = recvfrom (tp->socket, (char *)&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 intsendAck (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;}/* * Release a stream and clear the pointer to it */static voidreleaseStream (int s){ rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); free (tftpStreams[s]); tftpStreams[s] = NULL; rtems_semaphore_release (tftp_mutex);}static int rtems_tftp_evaluate_for_make( const char *path, /* IN */ rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ const char **name /* OUT */){ pathloc->node_access = NULL; rtems_set_errno_and_return_minus_one( EIO ); }/* * Convert a path to canonical form */static voidfixPath (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 int rtems_tftp_eval_path( const char *pathname, /* IN */ int flags, /* IN */ rtems_filesystem_location_info_t *pathloc /* IN/OUT */){ pathloc->handlers = &rtems_tftp_handlers; /* * Hack to provide the illusion of directories inside the TFTP file system. * Paths ending in a / are assumed to be directories. */ if (pathname[strlen(pathname)-1] == '/') { int isRelative = (pathloc->node_access != ROOT_NODE_ACCESS); char *cp; /* * Reject attempts to open() directories */ if (flags & RTEMS_LIBIO_PERMS_RDWR) rtems_set_errno_and_return_minus_one( EISDIR ); if (isRelative) { cp = malloc (strlen(pathloc->node_access)+strlen(pathname)+1); if (cp == NULL) rtems_set_errno_and_return_minus_one( ENOMEM ); strcpy (cp, pathloc->node_access); strcat (cp, pathname); } else { cp = strdup (pathname); if (cp == NULL) rtems_set_errno_and_return_minus_one( ENOMEM ); } fixPath (cp); pathloc->node_access = cp; return 0; } if (pathloc->node_access != ROOT_NODE_ACCESS) pathloc->node_access = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -