[39e6e65a] | 1 | /* |
---|
| 2 | * Trivial File Transfer Protocol (RFC 1350) |
---|
| 3 | * |
---|
| 4 | * Transfer file to/from remote host |
---|
| 5 | * |
---|
| 6 | * W. Eric Norum |
---|
| 7 | * Saskatchewan Accelerator Laboratory |
---|
| 8 | * University of Saskatchewan |
---|
| 9 | * Saskatoon, Saskatchewan, CANADA |
---|
| 10 | * eric@skatter.usask.ca |
---|
| 11 | * |
---|
| 12 | * $Id$ |
---|
| 13 | */ |
---|
| 14 | |
---|
| 15 | #include <stdio.h> |
---|
[94b3ee13] | 16 | #include <stdlib.h> |
---|
[39e6e65a] | 17 | #include <errno.h> |
---|
| 18 | #include <malloc.h> |
---|
| 19 | #include <string.h> |
---|
| 20 | #include <unistd.h> |
---|
[c1a37d3e] | 21 | #include <fcntl.h> |
---|
[39e6e65a] | 22 | #include <rtems.h> |
---|
| 23 | #include <rtems/libio.h> |
---|
| 24 | #include <rtems/rtems_bsdnet.h> |
---|
| 25 | #include <sys/types.h> |
---|
| 26 | #include <sys/socket.h> |
---|
| 27 | #include <netinet/in.h> |
---|
| 28 | #include <arpa/inet.h> |
---|
| 29 | |
---|
[94b3ee13] | 30 | #ifndef set_errno_and_return_minus_one |
---|
| 31 | #define set_errno_and_return_minus_one( _error ) \ |
---|
| 32 | do { errno = (_error); return -1; } while(0) |
---|
| 33 | #endif |
---|
| 34 | |
---|
| 35 | |
---|
[39e6e65a] | 36 | /* |
---|
| 37 | * Range of UDP ports to try |
---|
| 38 | */ |
---|
| 39 | #define UDP_PORT_BASE 3180 |
---|
| 40 | |
---|
| 41 | /* |
---|
| 42 | * Pathname prefix |
---|
| 43 | */ |
---|
| 44 | #define TFTP_PATHNAME_PREFIX "/TFTP/" |
---|
| 45 | |
---|
| 46 | /* |
---|
| 47 | * Default limits |
---|
| 48 | */ |
---|
| 49 | #define PACKET_REPLY_MILLISECONDS 6000 |
---|
| 50 | #define OPEN_RETRY_LIMIT 10 |
---|
| 51 | #define IO_RETRY_LIMIT 10 |
---|
| 52 | |
---|
| 53 | /* |
---|
| 54 | * TFTP opcodes |
---|
| 55 | */ |
---|
| 56 | #define TFTP_OPCODE_RRQ 1 |
---|
| 57 | #define TFTP_OPCODE_WRQ 2 |
---|
| 58 | #define TFTP_OPCODE_DATA 3 |
---|
| 59 | #define TFTP_OPCODE_ACK 4 |
---|
| 60 | #define TFTP_OPCODE_ERROR 5 |
---|
| 61 | |
---|
| 62 | /* |
---|
| 63 | * Largest data transfer |
---|
| 64 | */ |
---|
| 65 | #define TFTP_BUFSIZE 512 |
---|
| 66 | |
---|
| 67 | /* |
---|
| 68 | * Packets transferred between machines |
---|
| 69 | */ |
---|
| 70 | union tftpPacket { |
---|
| 71 | /* |
---|
| 72 | * RRQ/WRQ packet |
---|
| 73 | */ |
---|
| 74 | struct tftpRWRQ { |
---|
| 75 | rtems_unsigned16 opcode; |
---|
| 76 | char filename_mode[TFTP_BUFSIZE]; |
---|
| 77 | } tftpRWRQ; |
---|
| 78 | |
---|
| 79 | /* |
---|
| 80 | * DATA packet |
---|
| 81 | */ |
---|
| 82 | struct tftpDATA { |
---|
| 83 | rtems_unsigned16 opcode; |
---|
| 84 | rtems_unsigned16 blocknum; |
---|
| 85 | rtems_unsigned8 data[TFTP_BUFSIZE]; |
---|
| 86 | } tftpDATA; |
---|
| 87 | |
---|
| 88 | /* |
---|
| 89 | * ACK packet |
---|
| 90 | */ |
---|
| 91 | struct tftpACK { |
---|
| 92 | rtems_unsigned16 opcode; |
---|
| 93 | rtems_unsigned16 blocknum; |
---|
| 94 | } tftpACK; |
---|
| 95 | |
---|
| 96 | /* |
---|
| 97 | * ERROR packet |
---|
| 98 | */ |
---|
| 99 | struct tftpERROR { |
---|
| 100 | rtems_unsigned16 opcode; |
---|
| 101 | rtems_unsigned16 errorCode; |
---|
| 102 | char errorMessage[TFTP_BUFSIZE]; |
---|
| 103 | } tftpERROR; |
---|
| 104 | }; |
---|
| 105 | |
---|
| 106 | /* |
---|
| 107 | * State of each TFTP stream |
---|
| 108 | */ |
---|
| 109 | struct tftpStream { |
---|
| 110 | /* |
---|
| 111 | * Buffer for storing most recently-received packet |
---|
| 112 | */ |
---|
| 113 | union tftpPacket pkbuf; |
---|
| 114 | |
---|
| 115 | /* |
---|
| 116 | * Last block number received |
---|
| 117 | */ |
---|
| 118 | rtems_unsigned16 blocknum; |
---|
| 119 | |
---|
| 120 | /* |
---|
| 121 | * Data transfer socket |
---|
| 122 | */ |
---|
| 123 | int socket; |
---|
| 124 | struct sockaddr_in myAddress; |
---|
| 125 | struct sockaddr_in farAddress; |
---|
| 126 | |
---|
| 127 | /* |
---|
| 128 | * Indices into buffer |
---|
| 129 | */ |
---|
| 130 | int nleft; |
---|
| 131 | int nused; |
---|
| 132 | |
---|
| 133 | /* |
---|
| 134 | * Flags |
---|
| 135 | */ |
---|
| 136 | int firstReply; |
---|
| 137 | int eof; |
---|
| 138 | }; |
---|
| 139 | |
---|
| 140 | /* |
---|
| 141 | * Number of streams open at the same time |
---|
| 142 | */ |
---|
[c1a37d3e] | 143 | |
---|
[39e6e65a] | 144 | static rtems_id tftp_mutex; |
---|
| 145 | static int nStreams; |
---|
| 146 | static struct tftpStream ** volatile tftpStreams; |
---|
| 147 | |
---|
[c1a37d3e] | 148 | typedef const char *tftp_node; |
---|
| 149 | extern rtems_filesystem_operations_table rtems_tftp_ops; |
---|
| 150 | extern rtems_filesystem_file_handlers_r rtems_tftp_handlers; |
---|
| 151 | |
---|
| 152 | /* |
---|
| 153 | * Direct copy from the IMFS. Look at this. |
---|
| 154 | */ |
---|
| 155 | |
---|
| 156 | rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = { |
---|
| 157 | 5, /* link_max */ |
---|
| 158 | 6, /* max_canon */ |
---|
| 159 | 7, /* max_input */ |
---|
| 160 | 255, /* name_max */ |
---|
| 161 | 255, /* path_max */ |
---|
| 162 | 2, /* pipe_buf */ |
---|
| 163 | 1, /* posix_async_io */ |
---|
| 164 | 2, /* posix_chown_restrictions */ |
---|
| 165 | 3, /* posix_no_trunc */ |
---|
| 166 | 4, /* posix_prio_io */ |
---|
| 167 | 5, /* posix_sync_io */ |
---|
| 168 | 6 /* posix_vdisable */ |
---|
| 169 | }; |
---|
[94b3ee13] | 170 | |
---|
| 171 | int rtems_tftp_mount_me( |
---|
| 172 | rtems_filesystem_mount_table_entry_t *temp_mt_entry |
---|
| 173 | ) |
---|
| 174 | { |
---|
| 175 | rtems_status_code sc; |
---|
| 176 | |
---|
[c1a37d3e] | 177 | temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers; |
---|
| 178 | temp_mt_entry->mt_fs_root.ops = &rtems_tftp_ops; |
---|
| 179 | |
---|
[94b3ee13] | 180 | /* |
---|
[c1a37d3e] | 181 | * We have no tftp filesystem specific data to maintain. This |
---|
| 182 | * filesystem may only be mounted ONCE. |
---|
| 183 | * |
---|
| 184 | * And we maintain no real filesystem nodes, so there is no real root. |
---|
[94b3ee13] | 185 | */ |
---|
| 186 | |
---|
[c1a37d3e] | 187 | temp_mt_entry->fs_info = NULL; |
---|
| 188 | temp_mt_entry->mt_fs_root.node_access = NULL; |
---|
| 189 | |
---|
| 190 | /* |
---|
| 191 | * These need to be looked at for full POSIX semantics. |
---|
| 192 | */ |
---|
| 193 | |
---|
| 194 | temp_mt_entry->pathconf_limits_and_options = rtems_tftp_limits_and_options; |
---|
[94b3ee13] | 195 | |
---|
| 196 | |
---|
[c1a37d3e] | 197 | /* |
---|
| 198 | * Now allocate a semaphore for mutual exclusion. |
---|
| 199 | * |
---|
| 200 | * NOTE: This could be in an fsinfo for this filesystem type. |
---|
| 201 | */ |
---|
[94b3ee13] | 202 | |
---|
| 203 | sc = rtems_semaphore_create ( |
---|
| 204 | rtems_build_name('T', 'F', 'T', 'P'), |
---|
| 205 | 1, |
---|
| 206 | RTEMS_FIFO | |
---|
| 207 | RTEMS_BINARY_SEMAPHORE | |
---|
| 208 | RTEMS_NO_INHERIT_PRIORITY | |
---|
| 209 | RTEMS_NO_PRIORITY_CEILING | |
---|
| 210 | RTEMS_LOCAL, |
---|
| 211 | 0, |
---|
[c1a37d3e] | 212 | &tftp_mutex |
---|
[94b3ee13] | 213 | ); |
---|
| 214 | |
---|
| 215 | if (sc != RTEMS_SUCCESSFUL) |
---|
[c1a37d3e] | 216 | set_errno_and_return_minus_one( ENOMEM ); |
---|
[94b3ee13] | 217 | |
---|
| 218 | return 0; |
---|
| 219 | } |
---|
| 220 | |
---|
[39e6e65a] | 221 | /* |
---|
| 222 | * Initialize the TFTP driver |
---|
| 223 | */ |
---|
[c1a37d3e] | 224 | |
---|
| 225 | int rtems_bsdnet_initialize_tftp_filesystem () |
---|
[39e6e65a] | 226 | { |
---|
[c1a37d3e] | 227 | int status; |
---|
| 228 | rtems_filesystem_mount_table_entry_t *entry; |
---|
| 229 | |
---|
| 230 | status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO ); |
---|
| 231 | if ( status == -1 ) |
---|
| 232 | return status; |
---|
| 233 | |
---|
| 234 | status = mount( |
---|
| 235 | &entry, |
---|
| 236 | &rtems_tftp_ops, |
---|
[937ab62c] | 237 | RTEMS_FILESYSTEM_READ_ONLY, |
---|
[c1a37d3e] | 238 | NULL, |
---|
| 239 | TFTP_PATHNAME_PREFIX |
---|
| 240 | ); |
---|
| 241 | |
---|
| 242 | if ( status ) |
---|
| 243 | perror( "TFTP mount failed" ); |
---|
[39e6e65a] | 244 | |
---|
[c1a37d3e] | 245 | return status; |
---|
[39e6e65a] | 246 | } |
---|
| 247 | |
---|
| 248 | /* |
---|
| 249 | * Set error message |
---|
| 250 | * This RTEMS/UNIX error mapping needs to be fixed! |
---|
| 251 | */ |
---|
| 252 | static void |
---|
| 253 | tftpSetErrno (struct tftpStream *tp) |
---|
| 254 | { |
---|
| 255 | unsigned int tftpError; |
---|
| 256 | static const int errorMap[] = { |
---|
| 257 | 0, |
---|
| 258 | ENOENT, |
---|
| 259 | EPERM, |
---|
| 260 | ENOSPC, |
---|
| 261 | EINVAL, |
---|
| 262 | ENXIO, |
---|
| 263 | EEXIST, |
---|
| 264 | ESRCH, |
---|
| 265 | 0, |
---|
| 266 | }; |
---|
| 267 | |
---|
| 268 | tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); |
---|
| 269 | if (tftpError < (sizeof errorMap / sizeof errorMap[0])) |
---|
| 270 | errno = errorMap[tftpError]; |
---|
| 271 | else |
---|
| 272 | errno = 1000 + tftpError; |
---|
| 273 | } |
---|
| 274 | |
---|
| 275 | /* |
---|
| 276 | * Send a message to make the other end shut up |
---|
| 277 | */ |
---|
| 278 | static void |
---|
| 279 | sendStifle (struct tftpStream *tp, struct sockaddr_in *to) |
---|
| 280 | { |
---|
| 281 | int len; |
---|
| 282 | |
---|
| 283 | /* |
---|
| 284 | * Create the error packet (Unknown transfer ID). |
---|
| 285 | */ |
---|
| 286 | tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR); |
---|
| 287 | tp->pkbuf.tftpERROR.errorCode = htons (5); |
---|
| 288 | len = sizeof tp->pkbuf.tftpERROR.opcode + |
---|
| 289 | sizeof tp->pkbuf.tftpERROR.errorCode + 1; |
---|
| 290 | len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY"); |
---|
| 291 | |
---|
| 292 | /* |
---|
| 293 | * Send it |
---|
| 294 | */ |
---|
| 295 | sendto (tp->socket, (char *)&tp->pkbuf, len, 0, |
---|
| 296 | (struct sockaddr *)to, sizeof *to); |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | /* |
---|
| 300 | * Wait for a data packet |
---|
| 301 | */ |
---|
| 302 | static int |
---|
| 303 | getPacket (struct tftpStream *tp) |
---|
| 304 | { |
---|
| 305 | int len; |
---|
| 306 | struct timeval tv; |
---|
| 307 | |
---|
[809f4451] | 308 | tv.tv_sec = PACKET_REPLY_MILLISECONDS / 1000; |
---|
| 309 | tv.tv_usec = (PACKET_REPLY_MILLISECONDS % 1000) * 1000; |
---|
[39e6e65a] | 310 | setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); |
---|
| 311 | for (;;) { |
---|
| 312 | union { |
---|
| 313 | struct sockaddr s; |
---|
| 314 | struct sockaddr_in i; |
---|
| 315 | } from; |
---|
| 316 | int fromlen = sizeof from; |
---|
| 317 | len = recvfrom (tp->socket, (char *)&tp->pkbuf, |
---|
| 318 | sizeof tp->pkbuf, 0, |
---|
| 319 | &from.s, &fromlen); |
---|
| 320 | if (len < 0) |
---|
| 321 | break; |
---|
| 322 | if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { |
---|
| 323 | if (tp->firstReply) { |
---|
| 324 | tp->firstReply = 0; |
---|
| 325 | tp->farAddress.sin_port = from.i.sin_port; |
---|
| 326 | } |
---|
| 327 | if (tp->farAddress.sin_port == from.i.sin_port) |
---|
| 328 | break; |
---|
| 329 | } |
---|
| 330 | |
---|
| 331 | /* |
---|
| 332 | * Packet is from someone with whom we are |
---|
| 333 | * not interested. Tell them to go away. |
---|
| 334 | */ |
---|
| 335 | sendStifle (tp, &from.i); |
---|
| 336 | } |
---|
| 337 | tv.tv_sec = 0; |
---|
[809f4451] | 338 | tv.tv_usec = 0; |
---|
[39e6e65a] | 339 | setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); |
---|
| 340 | return len; |
---|
| 341 | } |
---|
| 342 | |
---|
| 343 | /* |
---|
| 344 | * Send an acknowledgement |
---|
| 345 | */ |
---|
| 346 | static int |
---|
| 347 | sendAck (struct tftpStream *tp) |
---|
| 348 | { |
---|
| 349 | /* |
---|
| 350 | * Create the acknowledgement |
---|
| 351 | */ |
---|
| 352 | tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); |
---|
| 353 | tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); |
---|
| 354 | |
---|
| 355 | /* |
---|
| 356 | * Send it |
---|
| 357 | */ |
---|
| 358 | if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0, |
---|
| 359 | (struct sockaddr *)&tp->farAddress, |
---|
| 360 | sizeof tp->farAddress) < 0) |
---|
| 361 | return errno; |
---|
| 362 | return 0; |
---|
| 363 | } |
---|
| 364 | |
---|
| 365 | /* |
---|
| 366 | * Release a stream and clear the pointer to it |
---|
| 367 | */ |
---|
| 368 | static void |
---|
| 369 | releaseStream (int s) |
---|
| 370 | { |
---|
| 371 | rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); |
---|
| 372 | free (tftpStreams[s]); |
---|
| 373 | tftpStreams[s] = NULL; |
---|
| 374 | rtems_semaphore_release (tftp_mutex); |
---|
| 375 | } |
---|
| 376 | |
---|
[94b3ee13] | 377 | int rtems_tftp_evaluate_for_make( |
---|
| 378 | const char *path, /* IN */ |
---|
| 379 | rtems_filesystem_location_info_t *pathloc, /* IN/OUT */ |
---|
| 380 | const char **name /* OUT */ |
---|
| 381 | ) |
---|
| 382 | { |
---|
[c1a37d3e] | 383 | set_errno_and_return_minus_one( EIO ); |
---|
[94b3ee13] | 384 | } |
---|
[c1a37d3e] | 385 | |
---|
[39e6e65a] | 386 | /* |
---|
[94b3ee13] | 387 | * XXX - Fix return values. |
---|
[39e6e65a] | 388 | */ |
---|
[94b3ee13] | 389 | |
---|
[c1a37d3e] | 390 | int rtems_tftp_eval_path( |
---|
[94b3ee13] | 391 | const char *pathname, /* IN */ |
---|
| 392 | int flags, /* IN */ |
---|
| 393 | rtems_filesystem_location_info_t *pathloc /* IN/OUT */ |
---|
[39e6e65a] | 394 | ) |
---|
| 395 | { |
---|
| 396 | |
---|
[c1a37d3e] | 397 | /* |
---|
| 398 | * Read-only for now |
---|
| 399 | */ |
---|
| 400 | |
---|
| 401 | if ( (flags & O_WRONLY) == O_WRONLY ) |
---|
| 402 | set_errno_and_return_minus_one( ENOENT ); |
---|
[39e6e65a] | 403 | |
---|
[c1a37d3e] | 404 | /* |
---|
| 405 | * The File system is mounted at TFTP_PATHNAME_PREFIX |
---|
| 406 | * the caller of this routine has striped off this part of the |
---|
| 407 | * name. Save the remainder of the name for use by the open routine. |
---|
| 408 | */ |
---|
[94b3ee13] | 409 | |
---|
[c1a37d3e] | 410 | pathloc->node_access = (void * ) pathname; |
---|
| 411 | pathloc->handlers = &rtems_tftp_handlers; |
---|
[39e6e65a] | 412 | |
---|
[94b3ee13] | 413 | return 0; |
---|
| 414 | } |
---|
| 415 | |
---|
[c1a37d3e] | 416 | |
---|
| 417 | int rtems_tftp_open( |
---|
| 418 | rtems_libio_t *iop, |
---|
| 419 | const char *new_name, |
---|
| 420 | unsigned32 flag, |
---|
| 421 | unsigned32 mode |
---|
[94b3ee13] | 422 | ) |
---|
| 423 | { |
---|
[c1a37d3e] | 424 | struct tftpStream *tp; |
---|
| 425 | int retryCount; |
---|
| 426 | rtems_unsigned32 farAddress; |
---|
| 427 | int s; |
---|
| 428 | int len; |
---|
| 429 | char *cp1; |
---|
| 430 | char *cp2; |
---|
| 431 | char *remoteFilename; |
---|
| 432 | rtems_interval now; |
---|
| 433 | rtems_status_code sc; |
---|
[09ea257c] | 434 | char *hostname; |
---|
[94b3ee13] | 435 | |
---|
[c1a37d3e] | 436 | /* |
---|
| 437 | * This came from the evaluate path. |
---|
| 438 | */ |
---|
[94b3ee13] | 439 | |
---|
[c1a37d3e] | 440 | cp2 = iop->file_info; |
---|
[09ea257c] | 441 | |
---|
| 442 | cp1 = cp2; |
---|
| 443 | while (*cp2 != '/') { |
---|
| 444 | if (*cp2 == '\0') |
---|
| 445 | return ENOENT; |
---|
| 446 | cp2++; |
---|
[c1a37d3e] | 447 | } |
---|
| 448 | |
---|
[09ea257c] | 449 | len = cp2 - cp1; |
---|
| 450 | hostname = malloc (len + 1); |
---|
| 451 | if (hostname == NULL) |
---|
| 452 | return ENOMEM; |
---|
[c1a37d3e] | 453 | |
---|
[09ea257c] | 454 | strncpy (hostname, cp1, len); |
---|
| 455 | hostname[len] = '\0'; |
---|
| 456 | farAddress = inet_addr (hostname); |
---|
| 457 | free (hostname); |
---|
[c1a37d3e] | 458 | |
---|
| 459 | if ((farAddress == 0) || (farAddress == ~0)) |
---|
[acb644a6] | 460 | return ENOENT; |
---|
[c1a37d3e] | 461 | |
---|
| 462 | if (*++cp2 == '\0') |
---|
[acb644a6] | 463 | return ENOENT; |
---|
[c1a37d3e] | 464 | |
---|
| 465 | remoteFilename = cp2; |
---|
| 466 | if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) |
---|
[acb644a6] | 467 | return ENOENT; |
---|
[39e6e65a] | 468 | |
---|
[c1a37d3e] | 469 | /* |
---|
| 470 | * Find a free stream |
---|
| 471 | */ |
---|
[39e6e65a] | 472 | |
---|
[c1a37d3e] | 473 | sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); |
---|
| 474 | if (sc != RTEMS_SUCCESSFUL) |
---|
[acb644a6] | 475 | return EBUSY; |
---|
[c1a37d3e] | 476 | |
---|
| 477 | for (s = 0 ; s < nStreams ; s++) { |
---|
| 478 | if (tftpStreams[s] == NULL) |
---|
| 479 | break; |
---|
| 480 | } |
---|
| 481 | |
---|
| 482 | if (s == nStreams) { |
---|
| 483 | /* |
---|
| 484 | * Reallocate stream pointers |
---|
| 485 | * Guard against the case where realloc() returns NULL. |
---|
| 486 | */ |
---|
| 487 | struct tftpStream **np; |
---|
| 488 | |
---|
| 489 | np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams); |
---|
| 490 | if (np == NULL) { |
---|
| 491 | rtems_semaphore_release (tftp_mutex); |
---|
[acb644a6] | 492 | return ENOMEM; |
---|
[c1a37d3e] | 493 | } |
---|
| 494 | tftpStreams = np; |
---|
| 495 | } |
---|
| 496 | |
---|
| 497 | tp = tftpStreams[s] = malloc (sizeof (struct tftpStream)); |
---|
| 498 | rtems_semaphore_release (tftp_mutex); |
---|
| 499 | if (tp == NULL) |
---|
[acb644a6] | 500 | return ENOMEM; |
---|
[c1a37d3e] | 501 | iop->data0 = s; |
---|
| 502 | iop->data1 = tp; |
---|
[39e6e65a] | 503 | |
---|
[c1a37d3e] | 504 | /* |
---|
| 505 | * Create the socket |
---|
| 506 | */ |
---|
[39e6e65a] | 507 | |
---|
[c1a37d3e] | 508 | if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { |
---|
| 509 | releaseStream (s); |
---|
[acb644a6] | 510 | return ENOMEM; |
---|
[c1a37d3e] | 511 | } |
---|
[39e6e65a] | 512 | |
---|
[c1a37d3e] | 513 | /* |
---|
| 514 | * Bind the socket to a local address |
---|
| 515 | */ |
---|
[39e6e65a] | 516 | |
---|
[c1a37d3e] | 517 | retryCount = 0; |
---|
| 518 | rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); |
---|
| 519 | for (;;) { |
---|
| 520 | int try = (now + retryCount) % 10; |
---|
| 521 | |
---|
| 522 | tp->myAddress.sin_family = AF_INET; |
---|
[48abdc31] | 523 | tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s); |
---|
[c1a37d3e] | 524 | tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY); |
---|
| 525 | if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0) |
---|
| 526 | break; |
---|
| 527 | if (++retryCount == 10) { |
---|
| 528 | close (tp->socket); |
---|
[48abdc31] | 529 | releaseStream (s); |
---|
[acb644a6] | 530 | return EBUSY; |
---|
[c1a37d3e] | 531 | } |
---|
| 532 | } |
---|
[39e6e65a] | 533 | |
---|
[c1a37d3e] | 534 | /* |
---|
| 535 | * Set the UDP destination to the TFTP server |
---|
| 536 | * port on the remote machine. |
---|
| 537 | */ |
---|
| 538 | tp->farAddress.sin_family = AF_INET; |
---|
| 539 | tp->farAddress.sin_addr.s_addr = farAddress; |
---|
| 540 | tp->farAddress.sin_port = htons (69); |
---|
[39e6e65a] | 541 | |
---|
[c1a37d3e] | 542 | /* |
---|
| 543 | * Start the transfer |
---|
| 544 | */ |
---|
| 545 | tp->firstReply = 1; |
---|
| 546 | for (;;) { |
---|
| 547 | /* |
---|
| 548 | * Create the request |
---|
| 549 | */ |
---|
| 550 | tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); |
---|
| 551 | cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode; |
---|
| 552 | cp2 = (char *) remoteFilename; |
---|
| 553 | while ((*cp1++ = *cp2++) != '\0') |
---|
| 554 | continue; |
---|
| 555 | cp2 = "octet"; |
---|
| 556 | while ((*cp1++ = *cp2++) != '\0') |
---|
| 557 | continue; |
---|
| 558 | len = cp1 - (char *)&tp->pkbuf.tftpRWRQ; |
---|
| 559 | |
---|
| 560 | /* |
---|
| 561 | * Send the request |
---|
| 562 | */ |
---|
| 563 | if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, |
---|
| 564 | (struct sockaddr *)&tp->farAddress, |
---|
| 565 | sizeof tp->farAddress) < 0) { |
---|
| 566 | close (tp->socket); |
---|
[48abdc31] | 567 | releaseStream (s); |
---|
[acb644a6] | 568 | return EIO; |
---|
[c1a37d3e] | 569 | } |
---|
| 570 | |
---|
| 571 | /* |
---|
| 572 | * Get reply |
---|
| 573 | */ |
---|
| 574 | len = getPacket (tp); |
---|
| 575 | if (len >= (int) sizeof tp->pkbuf.tftpACK) { |
---|
| 576 | int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); |
---|
| 577 | if ((opcode == TFTP_OPCODE_DATA) |
---|
| 578 | && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { |
---|
| 579 | tp->nused = 0; |
---|
| 580 | tp->blocknum = 1; |
---|
| 581 | tp->nleft = len - 2 * sizeof (rtems_unsigned16); |
---|
| 582 | tp->eof = (tp->nleft < TFTP_BUFSIZE); |
---|
| 583 | if (sendAck (tp) != 0) { |
---|
| 584 | close (tp->socket); |
---|
[48abdc31] | 585 | releaseStream (s); |
---|
[acb644a6] | 586 | return EIO; |
---|
[c1a37d3e] | 587 | } |
---|
| 588 | break; |
---|
| 589 | } |
---|
| 590 | if (opcode == TFTP_OPCODE_ERROR) { |
---|
| 591 | tftpSetErrno (tp); |
---|
| 592 | close (tp->socket); |
---|
[48abdc31] | 593 | releaseStream (s); |
---|
[acb644a6] | 594 | return EIO; |
---|
[c1a37d3e] | 595 | } |
---|
| 596 | } |
---|
| 597 | |
---|
| 598 | /* |
---|
| 599 | * Keep trying |
---|
| 600 | */ |
---|
| 601 | if (++retryCount >= OPEN_RETRY_LIMIT) { |
---|
| 602 | close (tp->socket); |
---|
[48abdc31] | 603 | releaseStream (s); |
---|
[acb644a6] | 604 | return EIO; |
---|
[c1a37d3e] | 605 | } |
---|
| 606 | } |
---|
[39e6e65a] | 607 | |
---|
[c1a37d3e] | 608 | return 0; |
---|
[39e6e65a] | 609 | } |
---|
| 610 | |
---|
| 611 | /* |
---|
| 612 | * Read from a TFTP stream |
---|
| 613 | */ |
---|
[c1a37d3e] | 614 | |
---|
| 615 | int rtems_tftp_read( |
---|
| 616 | rtems_libio_t *iop, |
---|
| 617 | void *buffer, |
---|
| 618 | unsigned32 count |
---|
[39e6e65a] | 619 | ) |
---|
| 620 | { |
---|
[c1a37d3e] | 621 | char *bp; |
---|
| 622 | struct tftpStream *tp; |
---|
| 623 | int retryCount; |
---|
| 624 | int nwant; |
---|
[39e6e65a] | 625 | |
---|
[c1a37d3e] | 626 | tp = iop->data1; |
---|
[39e6e65a] | 627 | |
---|
[c1a37d3e] | 628 | /* |
---|
| 629 | * Read till user request is satisfied or EOF is reached |
---|
| 630 | */ |
---|
[39e6e65a] | 631 | |
---|
[c1a37d3e] | 632 | bp = buffer; |
---|
| 633 | nwant = count; |
---|
| 634 | while (nwant) { |
---|
| 635 | if (tp->nleft) { |
---|
| 636 | int count; |
---|
| 637 | if (nwant < tp->nleft) |
---|
| 638 | count = nwant; |
---|
| 639 | else |
---|
| 640 | count = tp->nleft; |
---|
| 641 | memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count); |
---|
| 642 | tp->nused += count; |
---|
| 643 | tp->nleft -= count; |
---|
| 644 | bp += count; |
---|
| 645 | nwant -= count; |
---|
| 646 | if (nwant == 0) |
---|
| 647 | break; |
---|
| 648 | } |
---|
| 649 | if (tp->eof) |
---|
| 650 | break; |
---|
| 651 | |
---|
| 652 | /* |
---|
| 653 | * Wait for the next packet |
---|
| 654 | */ |
---|
| 655 | retryCount = 0; |
---|
| 656 | for (;;) { |
---|
| 657 | int len = getPacket (tp); |
---|
| 658 | if (len >= (int)sizeof tp->pkbuf.tftpACK) { |
---|
| 659 | int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); |
---|
| 660 | rtems_unsigned16 nextBlock = tp->blocknum + 1; |
---|
| 661 | if ((opcode == TFTP_OPCODE_DATA) |
---|
| 662 | && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { |
---|
| 663 | tp->nused = 0; |
---|
| 664 | tp->nleft = len - 2 * sizeof (rtems_unsigned16); |
---|
| 665 | tp->eof = (tp->nleft < TFTP_BUFSIZE); |
---|
| 666 | tp->blocknum++; |
---|
| 667 | if (sendAck (tp) != 0) |
---|
| 668 | set_errno_and_return_minus_one( EIO ); |
---|
| 669 | break; |
---|
| 670 | } |
---|
| 671 | if (opcode == TFTP_OPCODE_ERROR) { |
---|
| 672 | tftpSetErrno (tp); |
---|
| 673 | return RTEMS_INTERNAL_ERROR; |
---|
| 674 | } |
---|
| 675 | } |
---|
| 676 | |
---|
| 677 | /* |
---|
| 678 | * Keep trying? |
---|
| 679 | */ |
---|
| 680 | if (++retryCount == IO_RETRY_LIMIT) |
---|
| 681 | set_errno_and_return_minus_one( EIO ); |
---|
| 682 | if (sendAck (tp) != 0) |
---|
| 683 | set_errno_and_return_minus_one( EIO ); |
---|
| 684 | } |
---|
| 685 | } |
---|
| 686 | |
---|
| 687 | /* |
---|
| 688 | * XXX - Eric is this right? |
---|
| 689 | * |
---|
| 690 | */ |
---|
| 691 | return count - nwant; |
---|
[39e6e65a] | 692 | } |
---|
| 693 | |
---|
| 694 | /* |
---|
| 695 | * Close a TFTP stream |
---|
| 696 | */ |
---|
[c1a37d3e] | 697 | int rtems_tftp_close( |
---|
| 698 | rtems_libio_t *iop |
---|
[39e6e65a] | 699 | ) |
---|
| 700 | { |
---|
[c1a37d3e] | 701 | struct tftpStream *tp = iop->data1;; |
---|
| 702 | |
---|
| 703 | if (!tp->eof && !tp->firstReply) { |
---|
| 704 | /* |
---|
| 705 | * Tell the other end to stop |
---|
| 706 | */ |
---|
| 707 | rtems_interval ticksPerSecond; |
---|
| 708 | sendStifle (tp, &tp->farAddress); |
---|
| 709 | rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); |
---|
| 710 | rtems_task_wake_after (1 + ticksPerSecond / 10); |
---|
| 711 | } |
---|
| 712 | close (tp->socket); |
---|
| 713 | releaseStream (iop->data0); |
---|
| 714 | return RTEMS_SUCCESSFUL; |
---|
[39e6e65a] | 715 | } |
---|
| 716 | |
---|
[c1a37d3e] | 717 | int rtems_tftp_write( |
---|
| 718 | rtems_libio_t *iop, |
---|
| 719 | const void *buffer, |
---|
| 720 | unsigned32 count |
---|
[39e6e65a] | 721 | ) |
---|
| 722 | { |
---|
[c1a37d3e] | 723 | return RTEMS_NOT_CONFIGURED; |
---|
[39e6e65a] | 724 | } |
---|
| 725 | |
---|
| 726 | rtems_device_driver rtems_tftp_control( |
---|
| 727 | rtems_device_major_number major, |
---|
| 728 | rtems_device_minor_number minor, |
---|
| 729 | void *pargp |
---|
| 730 | ) |
---|
| 731 | { |
---|
[c1a37d3e] | 732 | return RTEMS_NOT_CONFIGURED; |
---|
[39e6e65a] | 733 | } |
---|
[c1a37d3e] | 734 | |
---|
| 735 | rtems_filesystem_node_types_t rtems_tftp_node_type( |
---|
| 736 | rtems_filesystem_location_info_t *pathloc /* IN */ |
---|
| 737 | ) |
---|
| 738 | { |
---|
| 739 | return RTEMS_FILESYSTEM_MEMORY_FILE; |
---|
| 740 | } |
---|
| 741 | |
---|
| 742 | |
---|
| 743 | rtems_filesystem_operations_table rtems_tftp_ops = { |
---|
| 744 | rtems_tftp_eval_path, /* eval_path */ |
---|
| 745 | rtems_tftp_evaluate_for_make, /* evaluate_for_make */ |
---|
| 746 | NULL, /* link */ |
---|
| 747 | NULL, /* unlink */ |
---|
| 748 | rtems_tftp_node_type, /* node_type */ |
---|
| 749 | NULL, /* mknod */ |
---|
| 750 | NULL, /* chown */ |
---|
| 751 | NULL, /* freenodinfo */ |
---|
| 752 | NULL, /* mount */ |
---|
| 753 | rtems_tftp_mount_me, /* initialize */ |
---|
| 754 | NULL, /* unmount */ |
---|
| 755 | NULL, /* fsunmount */ |
---|
| 756 | NULL, /* utime, */ |
---|
| 757 | NULL, /* evaluate_link */ |
---|
| 758 | NULL, /* symlink */ |
---|
| 759 | NULL, /* readlin */ |
---|
| 760 | }; |
---|
| 761 | |
---|
| 762 | rtems_filesystem_file_handlers_r rtems_tftp_handlers = { |
---|
[0da0dea] | 763 | rtems_tftp_open, /* open */ |
---|
| 764 | rtems_tftp_close, /* close */ |
---|
| 765 | rtems_tftp_read, /* read */ |
---|
| 766 | rtems_tftp_write, /* write */ |
---|
| 767 | NULL, /* ioctl */ |
---|
| 768 | NULL, /* lseek */ |
---|
| 769 | NULL, /* fstat */ |
---|
| 770 | NULL, /* fchmod */ |
---|
| 771 | NULL, /* ftruncate */ |
---|
| 772 | NULL, /* fpathconf */ |
---|
| 773 | NULL, /* fsync */ |
---|
| 774 | NULL, /* fdatasync */ |
---|
| 775 | NULL, /* fcntl */ |
---|
| 776 | NULL /* rmnod */ |
---|
[c1a37d3e] | 777 | }; |
---|