📄 tftpdriver.c
字号:
/* * Reject it if it's not read-only or write-only. */ flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE; if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) ) rtems_set_errno_and_return_minus_one( EINVAL ); return 0;}/* * 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, unsigned32 flags, unsigned32 mode){ struct tftpStream *tp; int retryCount; struct in_addr farAddress; int s; int len; char *cp1; char *cp2; char *remoteFilename; rtems_interval now; rtems_status_code sc; char *hostname; /* * Extract the host name component */ cp2 = full_path_name; while (*cp2 == '/') cp2++; hostname = cp2; while (*cp2 != '/') { if (*cp2 == '\0') return ENOENT; cp2++; } *cp2++ = '\0'; /* * Convert hostname to Internet address */ if (strcmp (hostname, "BOOTP_HOST") == 0) farAddress = rtems_bsdnet_bootp_server_address; else farAddress.s_addr = inet_addr (hostname); if ((farAddress.s_addr == 0) || (farAddress.s_addr == ~0)) return ENOENT; /* * Extract file pathname component */ while (*cp2 == '/') cp2++; if (strcmp (cp2, "BOOTP_FILE") == 0) { cp2 = rtems_bsdnet_bootp_boot_file_name; while (*cp2 == '/') cp2++; } if (*cp2 == '\0') return ENOENT; remoteFilename = cp2; if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) return ENOENT; /* * Find a free stream */ sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) return EBUSY; for (s = 0 ; s < nStreams ; s++) { if (tftpStreams[s] == NULL) break; } if (s == nStreams) { /* * Reallocate stream pointers * Guard against the case where realloc() returns NULL. */ struct tftpStream **np; np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams); if (np == NULL) { rtems_semaphore_release (tftp_mutex); return ENOMEM; } tftpStreams = np; } tp = tftpStreams[s] = malloc (sizeof (struct tftpStream)); rtems_semaphore_release (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 (s); return ENOMEM; } /* * Bind the socket to a local address */ retryCount = 0; rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); for (;;) { int try = (now + retryCount) % 10; tp->myAddress.sin_family = AF_INET; tp->myAddress.sin_port = htons (UDP_PORT_BASE + 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) { close (tp->socket); releaseStream (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 ((flags & 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) { close (tp->socket); releaseStream (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 (rtems_unsigned16); tp->eof = (tp->nleft < TFTP_BUFSIZE); if (sendAck (tp) != 0) { close (tp->socket); releaseStream (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); close (tp->socket); releaseStream (s); return e; } } /* * Keep trying */ if (++retryCount >= OPEN_RETRY_LIMIT) { close (tp->socket); releaseStream (s); return EIO; } } return 0;}/* * The IMFS open handler */static int rtems_tftp_open( rtems_libio_t *iop, const char *new_name, unsigned32 flags, unsigned32 mode){ char *full_path_name; char *s1; int err; /* * Tack the `current directory' on to relative paths. * We know that the current directory ends in a / character. */ if (*new_name == '/') { /* * Skip the TFTP filesystem prefix. */ int len = strlen (TFTP_PATHNAME_PREFIX); if (strncmp (new_name, TFTP_PATHNAME_PREFIX, len)) return ENOENT; new_name += len; s1 = ""; } else { s1 = rtems_filesystem_current.node_access; } full_path_name = malloc (strlen (s1) + strlen (new_name) + 1); if (full_path_name == NULL) return ENOMEM; strcpy (full_path_name, s1); strcat (full_path_name, new_name); fixPath (full_path_name); err = rtems_tftp_open_worker (iop, full_path_name, flags, mode); free (full_path_name); return err;}/* * Read from a TFTP stream */static ssize_t rtems_tftp_read( rtems_libio_t *iop, void *buffer, unsigned32 count){ char *bp; struct tftpStream *tp = iop->data1; int retryCount; int nwant; /* * 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); rtems_unsigned16 nextBlock = tp->blocknum + 1; if ((opcode == TFTP_OPCODE_DATA) && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { tp->nused = 0; tp->nleft = len - 2 * sizeof (rtems_unsigned16); 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 (rtems_unsigned16); 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){ struct tftpStream *tp = iop->data1;; if (tp->writing) rtems_tftp_flush (tp); if (!tp->eof && !tp->firstReply) { /* * Tell the other end to stop */ rtems_interval ticksPerSecond; sendStifle (tp, &tp->farAddress); rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); rtems_task_wake_after (1 + ticksPerSecond / 10); } close (tp->socket); releaseStream (iop->data0); return RTEMS_SUCCESSFUL;}static ssize_t rtems_tftp_write( rtems_libio_t *iop, const void *buffer, unsigned32 count){ const char *bp; struct tftpStream *tp = iop->data1; int nleft, nfree, ncopy; /* * Bail out if an error has occurred */ if (!tp->writing) return EIO; /* * Write till user request is satisfied * Notice that the buffer is flushed as soon as it is filled rather * than waiting for the next write or a close. This ensures that * the flush in close writes a less than full buffer so the far * end can detect the end-of-file condition. */ bp = buffer; nleft = count; while (nleft) { nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused; if (nleft < nfree) ncopy = nleft; else ncopy = nfree; memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy); tp->nused += ncopy; nleft -= ncopy; bp += ncopy; if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) { int e = rtems_tftp_flush (tp); if (e) { tp->writing = 0; 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, off_t count){ return 0;}static rtems_filesystem_node_types_t rtems_tftp_node_type( rtems_filesystem_location_info_t *pathloc /* IN */){ if ((pathloc->node_access == NULL) || (pathloc->node_access == ROOT_NODE_ACCESS)) return RTEMS_FILESYSTEM_MEMORY_FILE; return RTEMS_FILESYSTEM_DIRECTORY;}static int rtems_tftp_free_node_info( rtems_filesystem_location_info_t *pathloc /* IN */){ if (pathloc->node_access && (pathloc->node_access != ROOT_NODE_ACCESS)) { free (pathloc->node_access); pathloc->node_access = NULL; } return 0;}rtems_filesystem_operations_table rtems_tftp_ops = { rtems_tftp_eval_path, /* eval_path */ rtems_tftp_evaluate_for_make, /* evaluate_for_make */ NULL, /* link */ NULL, /* unlink */ rtems_tftp_node_type, /* node_type */ NULL, /* mknod */ NULL, /* chown */ rtems_tftp_free_node_info, /* freenodinfo */ NULL, /* mount */ rtems_tftp_mount_me, /* initialize */ NULL, /* unmount */ NULL, /* fsunmount */ NULL, /* utime, */ NULL, /* evaluate_link */ NULL, /* symlink */ NULL, /* readlin */}; rtems_filesystem_file_handlers_r rtems_tftp_handlers = { rtems_tftp_open, /* open */ rtems_tftp_close, /* close */ rtems_tftp_read, /* read */ rtems_tftp_write, /* write */ NULL, /* ioctl */ NULL, /* lseek */ NULL, /* fstat */ NULL, /* fchmod */ rtems_tftp_ftruncate, /* ftruncate */ NULL, /* fpathconf */ NULL, /* fsync */ NULL, /* fdatasync */ NULL, /* fcntl */ NULL /* rmnod */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -