source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ b2b143f4

4.104.114.84.95
Last change on this file since b2b143f4 was b2b143f4, checked in by Joel Sherrill <joel.sherrill@…>, on 03/05/04 at 17:58:51

2004-03-05 Joel Sherrill <joel@…>

  • libblock/src/bdbuf.c, libblock/src/ramdisk.c, libcsupport/src/newlibc.c, libcsupport/src/sync.c, libmisc/cpuuse/cpuuse.c, libmisc/monitor/mon-symbols.c, libmisc/shell/cmds.c, libmisc/shell/shell.c, libnetworking/kern/kern_sysctl.c, libnetworking/lib/ftpfs.c, libnetworking/lib/tftpDriver.c, libnetworking/libc/gethostbydns.c, libnetworking/libc/gethostbyht.c, libnetworking/libc/gethostnamadr.c, libnetworking/libc/getnetbyht.c, libnetworking/libc/getnetnamadr.c, libnetworking/libc/inet_addr.c, libnetworking/libc/linkaddr.c, libnetworking/libc/map_v4v6.c, libnetworking/libc/ns_print.c, libnetworking/libc/ns_ttl.c, libnetworking/libc/nsap_addr.c, libnetworking/libc/rcmd.c, libnetworking/libc/res_debug.c, libnetworking/libc/res_mkupdate.c, libnetworking/libc/res_query.c, libnetworking/libc/res_send.c, libnetworking/libc/res_update.c, libnetworking/net/radix.c, libnetworking/rtems/mkrootfs.c, librpc/src/rpc/clnt_perror.c, librpc/src/rpc/rtems_rpc.c, librpc/src/rpc/svc.c, sapi/include/confdefs.h, score/macros/rtems/score/chain.inl, score/src/objectidtoname.c:
  • Property mode set to 100644
File size: 27.0 KB
Line 
1/*
2 * vim: set expandtab tabstop=4 shiftwidth=4 ai :
3 *
4 * Trivial File Transfer Protocol (RFC 1350)
5 *
6 * Transfer file to/from remote host
7 *
8 * W. Eric Norum
9 * Saskatchewan Accelerator Laboratory
10 * University of Saskatchewan
11 * Saskatoon, Saskatchewan, CANADA
12 * eric@skatter.usask.ca
13 *
14 *  $Id$
15 *
16 */
17
18/* Since we compile with strict ANSI we need to undef it to get
19 * prototypes for extensions
20 */
21#undef __STRICT_ANSI__
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <malloc.h>
27#include <string.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <rtems.h>
31#include <rtems/libio.h>
32#include <rtems/libio_.h>
33#include <rtems/seterr.h>
34#include <rtems/rtems_bsdnet.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <netinet/in.h>
38#include <arpa/inet.h>
39
40#ifdef RTEMS_TFTP_DRIVER_DEBUG
41int rtems_tftp_driver_debug = 1;
42#endif
43
44/*
45 * Range of UDP ports to try
46 */
47#define UDP_PORT_BASE        3180
48
49/*
50 * Pathname prefix
51 */
52#define TFTP_PATHNAME_PREFIX "/TFTP/"
53
54/*
55 * Root node_access value
56 * By using the address of a local static variable
57 * we ensure a unique value for this identifier.
58 */
59#define ROOT_NODE_ACCESS    (&tftp_mutex)
60
61/*
62 * Default limits
63 */
64#define PACKET_FIRST_TIMEOUT_MILLISECONDS  400
65#define PACKET_TIMEOUT_MILLISECONDS        6000
66#define OPEN_RETRY_LIMIT                   10
67#define IO_RETRY_LIMIT                     10
68
69/*
70 * TFTP opcodes
71 */
72#define TFTP_OPCODE_RRQ     1
73#define TFTP_OPCODE_WRQ     2
74#define TFTP_OPCODE_DATA    3
75#define TFTP_OPCODE_ACK     4
76#define TFTP_OPCODE_ERROR   5
77
78/*
79 * Largest data transfer
80 */
81#define TFTP_BUFSIZE        512
82
83/*
84 * Packets transferred between machines
85 */
86union tftpPacket {
87    /*
88     * RRQ/WRQ packet
89     */
90    struct tftpRWRQ {
91        rtems_unsigned16    opcode;
92        char                filename_mode[TFTP_BUFSIZE];
93    } tftpRWRQ;
94
95    /*
96     * DATA packet
97     */
98    struct tftpDATA {
99        rtems_unsigned16    opcode;
100        rtems_unsigned16    blocknum;
101        rtems_unsigned8     data[TFTP_BUFSIZE];
102    } tftpDATA;
103
104    /*
105     * ACK packet
106     */
107    struct tftpACK {
108        rtems_unsigned16    opcode;
109        rtems_unsigned16    blocknum;
110    } tftpACK;
111
112    /*
113     * ERROR packet
114     */
115    struct tftpERROR {
116        rtems_unsigned16    opcode;
117        rtems_unsigned16    errorCode;
118        char                errorMessage[TFTP_BUFSIZE];
119    } tftpERROR;
120};
121
122/*
123 * State of each TFTP stream
124 */
125struct tftpStream {
126    /*
127     * Buffer for storing most recently-received packet
128     */
129    union tftpPacket    pkbuf;
130
131    /*
132     * Last block number transferred
133     */
134    rtems_unsigned16    blocknum;
135
136    /*
137     * Data transfer socket
138     */
139    int                 socket;
140    struct sockaddr_in  myAddress;
141    struct sockaddr_in  farAddress;
142
143    /*
144     * Indices into buffer
145     */
146    int     nleft;
147    int     nused;
148
149    /*
150     * Flags
151     */
152    int     firstReply;
153    int     eof;
154    int     writing;
155};
156
157/*
158 * Number of streams open at the same time
159 */
160static rtems_id tftp_mutex;
161static int nStreams;
162static struct tftpStream ** volatile tftpStreams;
163
164typedef const char *tftp_node;
165extern rtems_filesystem_operations_table  rtems_tftp_ops;
166extern rtems_filesystem_file_handlers_r   rtems_tftp_handlers;
167
168/*
169 *  Direct copy from the IMFS.  Look at this.
170 */
171
172rtems_filesystem_limits_and_options_t rtems_tftp_limits_and_options = {
173   5,   /* link_max */
174   6,   /* max_canon */
175   7,   /* max_input */
176   255, /* name_max */
177   255, /* path_max */
178   2,   /* pipe_buf */
179   1,   /* posix_async_io */
180   2,   /* posix_chown_restrictions */
181   3,   /* posix_no_trunc */
182   4,   /* posix_prio_io */
183   5,   /* posix_sync_io */
184   6    /* posix_vdisable */
185};
186
187static int rtems_tftp_mount_me(
188  rtems_filesystem_mount_table_entry_t *temp_mt_entry
189)
190{
191  rtems_status_code  sc;
192
193  temp_mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers;
194  temp_mt_entry->mt_fs_root.ops      = &rtems_tftp_ops;
195
196  /*
197   *   We have no tftp filesystem specific data to maintain.  This
198   *   filesystem may only be mounted ONCE.
199   *
200   *   And we maintain no real filesystem nodes, so there is no real root.
201   */
202
203  temp_mt_entry->fs_info                = NULL;
204  temp_mt_entry->mt_fs_root.node_access = ROOT_NODE_ACCESS;
205
206  /*
207   *  These need to be looked at for full POSIX semantics.
208   */
209
210  temp_mt_entry->pathconf_limits_and_options = rtems_tftp_limits_and_options;
211
212
213  /*
214   *  Now allocate a semaphore for mutual exclusion.
215   *
216   *  NOTE:  This could be in an fsinfo for this filesystem type.
217   */
218 
219  sc = rtems_semaphore_create (
220    rtems_build_name('T', 'F', 'T', 'P'),
221    1,
222    RTEMS_FIFO |
223    RTEMS_BINARY_SEMAPHORE |
224    RTEMS_NO_INHERIT_PRIORITY |
225    RTEMS_NO_PRIORITY_CEILING |
226    RTEMS_LOCAL,
227    0,
228    &tftp_mutex
229  );
230
231  if (sc != RTEMS_SUCCESSFUL)
232    rtems_set_errno_and_return_minus_one( ENOMEM );
233
234  return 0;
235}
236
237/*
238 * Initialize the TFTP driver
239 */
240
241int rtems_bsdnet_initialize_tftp_filesystem ()
242{
243    int                                   status;
244    rtems_filesystem_mount_table_entry_t *entry;
245
246    status = mkdir( TFTP_PATHNAME_PREFIX, S_IRWXU | S_IRWXG | S_IRWXO );
247    if ( status == -1 )
248        return status;
249
250    status = mount(
251            &entry,
252            &rtems_tftp_ops,
253            RTEMS_FILESYSTEM_READ_WRITE,
254            NULL,
255            TFTP_PATHNAME_PREFIX
256    );
257
258    if ( status )
259        perror( "TFTP mount failed" );
260
261    return status;
262}
263
264/*
265 * Map error message
266 */
267static int
268tftpErrno (struct tftpStream *tp)
269{
270    unsigned int tftpError;
271    static const int errorMap[] = {
272        EINVAL,
273        ENOENT,
274        EPERM,
275        ENOSPC,
276        EINVAL,
277        ENXIO,
278        EEXIST,
279        ESRCH,
280    };
281
282    tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode);
283    if (tftpError < (sizeof errorMap / sizeof errorMap[0]))
284        return errorMap[tftpError];
285    else
286        return 1000 + tftpError;
287}
288
289/*
290 * Send a message to make the other end shut up
291 */
292static void
293sendStifle (struct tftpStream *tp, struct sockaddr_in *to)
294{
295    int len;
296    struct {
297        rtems_unsigned16    opcode;
298        rtems_unsigned16    errorCode;
299        char                errorMessage[12];
300    } msg;
301
302    /*
303     * Create the error packet (Unknown transfer ID).
304     */
305    msg.opcode = htons (TFTP_OPCODE_ERROR);
306    msg.errorCode = htons (5);
307    len = sizeof msg.opcode + sizeof msg.errorCode + 1;
308    len += sprintf (msg.errorMessage, "GO AWAY");
309
310    /*
311     * Send it
312     */
313    sendto (tp->socket, (char *)&msg, len, 0, (struct sockaddr *)to, sizeof *to);
314}
315
316/*
317 * Wait for a data packet
318 */
319static int
320getPacket (struct tftpStream *tp, int retryCount)
321{
322    int len;
323    struct timeval tv;
324
325    if (retryCount == 0) {
326        tv.tv_sec = PACKET_FIRST_TIMEOUT_MILLISECONDS / 1000;
327        tv.tv_usec = (PACKET_FIRST_TIMEOUT_MILLISECONDS % 1000) * 1000;
328    }
329    else {
330        tv.tv_sec = PACKET_TIMEOUT_MILLISECONDS / 1000;
331        tv.tv_usec = (PACKET_TIMEOUT_MILLISECONDS % 1000) * 1000;
332    }
333    setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
334    for (;;) {
335        union {
336            struct sockaddr s;
337            struct sockaddr_in i;
338        } from;
339        int fromlen = sizeof from;
340        len = recvfrom (tp->socket, (char *)&tp->pkbuf,
341                                                    sizeof tp->pkbuf, 0,
342                                                    &from.s, &fromlen);
343        if (len < 0)
344            break;
345        if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) {
346            if (tp->firstReply) {
347                tp->firstReply = 0;
348                tp->farAddress.sin_port = from.i.sin_port;
349            }
350            if (tp->farAddress.sin_port == from.i.sin_port)
351                break;
352        }
353
354        /*
355         * Packet is from someone with whom we are
356         * not interested.  Tell them to go away.
357         */
358        sendStifle (tp, &from.i);
359    }
360    tv.tv_sec = 0;
361    tv.tv_usec = 0;
362    setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
363#ifdef RTEMS_TFTP_DRIVER_DEBUG
364    if (rtems_tftp_driver_debug) {
365        if (len >= (int) sizeof tp->pkbuf.tftpACK) {
366            int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
367            switch (opcode) {
368            default:
369                printf ("TFTP: OPCODE %d\n", opcode);
370                break;
371
372            case TFTP_OPCODE_DATA:
373                printf ("TFTP: RECV %d\n", ntohs (tp->pkbuf.tftpDATA.blocknum));
374                break;
375
376            case TFTP_OPCODE_ACK:
377                printf ("TFTP: GOT ACK %d\n", ntohs (tp->pkbuf.tftpACK.blocknum));
378                break;
379            }
380        }
381        else {
382            printf ("TFTP: %d-byte packet\n", len);
383        }
384    }
385#endif
386    return len;
387}
388
389/*
390 * Send an acknowledgement
391 */
392static int
393sendAck (struct tftpStream *tp)
394{
395#ifdef RTEMS_TFTP_DRIVER_DEBUG
396    if (rtems_tftp_driver_debug)
397        printf ("TFTP: ACK %d\n", tp->blocknum);
398#endif
399
400    /*
401     * Create the acknowledgement
402     */
403    tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK);
404    tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum);
405
406    /*
407     * Send it
408     */
409    if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0,
410                                    (struct sockaddr *)&tp->farAddress,
411                                    sizeof tp->farAddress) < 0)
412        return errno;
413    return 0;
414}
415
416/*
417 * Release a stream and clear the pointer to it
418 */
419static void
420releaseStream (int s)
421{
422    rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
423    free (tftpStreams[s]);
424    tftpStreams[s] = NULL;
425    rtems_semaphore_release (tftp_mutex);
426}
427
428static int rtems_tftp_evaluate_for_make(
429   const char                         *path,       /* IN     */
430   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
431   const char                        **name        /* OUT    */
432)
433
434  pathloc->node_access = NULL;
435  rtems_set_errno_and_return_minus_one( EIO );   
436}
437
438/*
439 * Convert a path to canonical form
440 */
441static void
442fixPath (char *path)
443{
444    char *inp, *outp, *base;
445
446    outp = inp = path;
447    base = NULL;
448    for (;;) {
449        if (inp[0] == '.') {
450            if (inp[1] == '\0')
451                break;
452            if (inp[1] == '/') {
453                inp += 2;
454                continue;
455            }
456            if (inp[1] == '.') {
457                if (inp[2] == '\0') {
458                    if ((base != NULL) && (outp > base)) {
459                        outp--;
460                        while ((outp > base) && (outp[-1] != '/'))
461                            outp--;
462                    }
463                    break;
464                }
465                if (inp[2] == '/') {
466                    inp += 3;
467                    if (base == NULL)
468                        continue;
469                    if (outp > base) {
470                        outp--;
471                        while ((outp > base) && (outp[-1] != '/'))
472                            outp--;
473                    }
474                    continue;
475                }
476            }
477        }
478        if (base == NULL)
479            base = inp;
480        while (inp[0] != '/') {
481            if ((*outp++ = *inp++) == '\0')
482                return;
483        }
484        *outp++ = '/';
485        while (inp[0] == '/')
486            inp++;
487    }
488    *outp = '\0';
489    return;
490}
491
492static int rtems_tftp_eval_path( 
493  const char                        *pathname,     /* IN     */
494  int                                flags,        /* IN     */
495  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
496)
497{
498    pathloc->handlers    = &rtems_tftp_handlers;
499
500    /*
501     * Hack to provide the illusion of directories inside the TFTP file system.
502     * Paths ending in a / are assumed to be directories.
503     */
504    if (pathname[strlen(pathname)-1] == '/') {
505        int isRelative = (pathloc->node_access != ROOT_NODE_ACCESS);
506        char *cp;
507       
508        /*
509         * Reject attempts to open() directories
510         */
511        if (flags & RTEMS_LIBIO_PERMS_RDWR)
512            rtems_set_errno_and_return_minus_one( EISDIR );
513        if (isRelative) {
514            cp = malloc (strlen(pathloc->node_access)+strlen(pathname)+1);
515            if (cp == NULL)
516                rtems_set_errno_and_return_minus_one( ENOMEM );
517            strcpy (cp, pathloc->node_access);
518            strcat (cp, pathname);
519        }
520        else {
521            cp = strdup (pathname);
522            if (cp == NULL)
523                rtems_set_errno_and_return_minus_one( ENOMEM );
524        }
525        fixPath (cp);
526        pathloc->node_access = cp;
527        return 0;
528    }
529    if (pathloc->node_access != ROOT_NODE_ACCESS)
530        pathloc->node_access = 0;
531
532    /*
533     * Reject it if it's not read-only or write-only.
534     */
535    flags &= RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
536    if ((flags != RTEMS_LIBIO_PERMS_READ) && (flags != RTEMS_LIBIO_PERMS_WRITE) )
537        rtems_set_errno_and_return_minus_one( EINVAL );
538    return 0;
539}
540
541/*
542 * The routine which does most of the work for the IMFS open handler
543 */
544static int rtems_tftp_open_worker(
545    rtems_libio_t *iop,
546    char          *full_path_name,
547    unsigned32     flags,
548    unsigned32     mode
549)
550{
551    struct tftpStream    *tp;
552    int                  retryCount;
553    struct in_addr       farAddress;
554    int                  s;
555    int                  len;
556    char                 *cp1;
557    char                 *cp2;
558    char                 *remoteFilename;
559    rtems_interval       now;
560    rtems_status_code    sc;
561    char                 *hostname;
562
563    /*
564     * Extract the host name component
565     */
566    cp2 = full_path_name;
567    while (*cp2 == '/')
568        cp2++;
569    hostname = cp2;
570    while (*cp2 != '/') {
571        if (*cp2 == '\0')
572            return ENOENT;
573        cp2++;
574    }
575    *cp2++ = '\0';
576
577    /*
578     * Convert hostname to Internet address
579     */
580    if (strcmp (hostname, "BOOTP_HOST") == 0)
581        farAddress = rtems_bsdnet_bootp_server_address;
582    else
583        farAddress.s_addr = inet_addr (hostname);
584    if ((farAddress.s_addr == 0) || (farAddress.s_addr == ~0))
585        return ENOENT;
586
587    /*
588     * Extract file pathname component
589     */
590    while (*cp2 == '/')
591        cp2++;
592    if (strcmp (cp2, "BOOTP_FILE") == 0) {
593        cp2 = rtems_bsdnet_bootp_boot_file_name;
594        while (*cp2 == '/')
595            cp2++;
596    }
597    if (*cp2 == '\0')
598        return ENOENT;
599    remoteFilename = cp2;
600    if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
601        return ENOENT;       
602
603    /*
604     * Find a free stream
605     */
606    sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
607    if (sc != RTEMS_SUCCESSFUL)
608        return EBUSY;
609    for (s = 0 ; s < nStreams ; s++) {
610        if (tftpStreams[s] == NULL)
611        break;
612    }
613    if (s == nStreams) {
614        /*
615         * Reallocate stream pointers
616         * Guard against the case where realloc() returns NULL.
617         */
618        struct tftpStream **np;
619
620        np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams);
621        if (np == NULL) {
622            rtems_semaphore_release (tftp_mutex);
623            return ENOMEM;
624        }
625        tftpStreams = np;
626    }
627    tp = tftpStreams[s] = malloc (sizeof (struct tftpStream));
628    rtems_semaphore_release (tftp_mutex);
629    if (tp == NULL)
630        return ENOMEM;
631    iop->data0 = s;
632    iop->data1 = tp;
633
634    /*
635     * Create the socket
636     */
637    if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
638        releaseStream (s);
639        return ENOMEM;
640    }
641
642    /*
643     * Bind the socket to a local address
644     */
645    retryCount = 0;
646    rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now);
647    for (;;) {
648        int try = (now + retryCount) % 10;
649
650        tp->myAddress.sin_family = AF_INET;
651        tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + s);
652        tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
653        if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
654            break;
655        if (++retryCount == 10) {
656            close (tp->socket);
657            releaseStream (s);
658            return EBUSY;
659        }
660    }
661
662    /*
663     * Set the UDP destination to the TFTP server
664     * port on the remote machine.
665     */
666    tp->farAddress.sin_family = AF_INET;
667    tp->farAddress.sin_addr = farAddress;
668    tp->farAddress.sin_port = htons (69);
669
670    /*
671     * Start the transfer
672     */
673    tp->firstReply = 1;
674    retryCount = 0;
675    for (;;) {
676        /*
677         * Create the request
678         */
679        if ((flags & O_ACCMODE) == O_RDONLY) {
680            tp->writing = 0;
681            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
682        }
683        else {
684            tp->writing = 1;
685            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
686        }
687        cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
688        cp2 = (char *) remoteFilename;
689        while ((*cp1++ = *cp2++) != '\0')
690            continue;
691        cp2 = "octet";
692        while ((*cp1++ = *cp2++) != '\0')
693            continue;
694        len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
695
696        /*
697         * Send the request
698         */
699        if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
700                    (struct sockaddr *)&tp->farAddress,
701                    sizeof tp->farAddress) < 0) {
702            close (tp->socket);
703            releaseStream (s);
704            return EIO;
705        }
706
707        /*
708         * Get reply
709         */
710        len = getPacket (tp, retryCount);
711        if (len >= (int) sizeof tp->pkbuf.tftpACK) {
712            int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
713            if (!tp->writing
714             && (opcode == TFTP_OPCODE_DATA)
715             && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
716                tp->nused = 0;
717                tp->blocknum = 1;
718                tp->nleft = len - 2 * sizeof (rtems_unsigned16);
719                tp->eof = (tp->nleft < TFTP_BUFSIZE);
720                if (sendAck (tp) != 0) {
721                    close (tp->socket);
722                    releaseStream (s);
723                    return EIO;
724                }
725                break;
726            }
727            if (tp->writing
728             && (opcode == TFTP_OPCODE_ACK)
729             && (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
730                tp->nused = 0;
731                tp->blocknum = 1;
732                break;
733            }
734            if (opcode == TFTP_OPCODE_ERROR) {
735                int e = tftpErrno (tp);
736                close (tp->socket);
737                releaseStream (s);
738                return e;
739            }
740        }
741
742        /*
743         * Keep trying
744         */
745        if (++retryCount >= OPEN_RETRY_LIMIT) {
746            close (tp->socket);
747            releaseStream (s);
748            return EIO;
749        }
750    }
751    return 0;
752}
753
754/*
755 * The IMFS open handler
756 */
757static int rtems_tftp_open(
758    rtems_libio_t *iop,
759    const char    *new_name,
760    unsigned32     flags,
761    unsigned32     mode
762)
763{
764    char *full_path_name;
765    char *s1;
766    int err;
767
768    /*
769     * Tack the `current directory' on to relative paths.
770     * We know that the current directory ends in a / character.
771     */
772    if (*new_name == '/') {
773        /*
774         * Skip the TFTP filesystem prefix.
775         */
776        int len = strlen (TFTP_PATHNAME_PREFIX);
777        if (strncmp (new_name, TFTP_PATHNAME_PREFIX, len))
778            return ENOENT;
779        new_name += len;
780        s1 = "";
781    }
782    else {
783        s1 = rtems_filesystem_current.node_access;
784    }
785    full_path_name = malloc (strlen (s1) + strlen (new_name) + 1);
786    if (full_path_name == NULL)
787        return ENOMEM;
788    strcpy (full_path_name, s1);
789    strcat (full_path_name, new_name);
790    fixPath (full_path_name);
791    err = rtems_tftp_open_worker (iop, full_path_name, flags, mode);
792    free (full_path_name);
793    return err;
794}
795
796/*
797 * Read from a TFTP stream
798 */
799static ssize_t rtems_tftp_read(
800    rtems_libio_t *iop,
801    void          *buffer,
802    unsigned32    count
803)
804{
805    char              *bp;
806    struct tftpStream *tp = iop->data1;
807    int               retryCount;
808    int               nwant;
809
810
811    /*
812     * Read till user request is satisfied or EOF is reached
813     */
814    bp = buffer;
815    nwant = count;
816    while (nwant) {
817        if (tp->nleft) {
818            int ncopy;
819            if (nwant < tp->nleft)
820                ncopy = nwant;
821            else
822                ncopy = tp->nleft;
823            memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
824            tp->nused += ncopy;
825            tp->nleft -= ncopy;
826            bp += ncopy;
827            nwant -= ncopy;
828            if (nwant == 0)
829                break;
830        }
831        if (tp->eof)
832            break;
833
834        /*
835         * Wait for the next packet
836         */
837        retryCount = 0;
838        for (;;) {
839            int len = getPacket (tp, retryCount);
840            if (len >= (int)sizeof tp->pkbuf.tftpACK) {
841                int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
842                rtems_unsigned16 nextBlock = tp->blocknum + 1;
843                if ((opcode == TFTP_OPCODE_DATA)
844                 && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
845                    tp->nused = 0;
846                    tp->nleft = len - 2 * sizeof (rtems_unsigned16);
847                    tp->eof = (tp->nleft < TFTP_BUFSIZE);
848                    tp->blocknum++;
849                    if (sendAck (tp) != 0)
850                        rtems_set_errno_and_return_minus_one( EIO );
851                    break;
852                }
853                if (opcode == TFTP_OPCODE_ERROR)
854                        rtems_set_errno_and_return_minus_one( tftpErrno (tp) );
855            }
856
857            /*
858             * Keep trying?
859             */
860            if (++retryCount == IO_RETRY_LIMIT)
861                rtems_set_errno_and_return_minus_one( EIO );
862            if (sendAck (tp) != 0)
863                rtems_set_errno_and_return_minus_one( EIO );
864        }
865    }
866    return count - nwant;
867}
868
869/*
870 * Flush a write buffer and wait for acknowledgement
871 */
872static int rtems_tftp_flush ( struct tftpStream *tp )
873{
874    int wlen, rlen;
875    int retryCount = 0;
876
877    wlen = tp->nused + 2 * sizeof (rtems_unsigned16);
878    for (;;) {
879        tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
880        tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
881#ifdef RTEMS_TFTP_DRIVER_DEBUG
882        if (rtems_tftp_driver_debug)
883            printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused);
884#endif
885        if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
886                                        (struct sockaddr *)&tp->farAddress,
887                                        sizeof tp->farAddress) < 0)
888            return EIO;
889        rlen = getPacket (tp, retryCount);
890        /*
891         * Our last packet won't necessarily be acknowledged!
892         */
893        if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data))
894                return 0;
895        if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
896            int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
897            if ((opcode == TFTP_OPCODE_ACK)
898             && (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
899                tp->nused = 0;
900                tp->blocknum++;
901                return 0;
902            }
903            if (opcode == TFTP_OPCODE_ERROR)
904                return tftpErrno (tp);
905        }
906
907        /*
908         * Keep trying?
909         */
910        if (++retryCount == IO_RETRY_LIMIT)
911            return EIO;
912    }
913}
914
915/*
916 * Close a TFTP stream
917 */
918static int rtems_tftp_close(
919    rtems_libio_t *iop
920)
921{
922    struct tftpStream *tp = iop->data1;;
923
924    if (tp->writing)
925        rtems_tftp_flush (tp);
926    if (!tp->eof && !tp->firstReply) {
927        /*
928         * Tell the other end to stop
929         */
930        rtems_interval ticksPerSecond;
931        sendStifle (tp, &tp->farAddress);
932        rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond);
933        rtems_task_wake_after (1 + ticksPerSecond / 10);
934    }
935    close (tp->socket);
936    releaseStream (iop->data0);
937    return RTEMS_SUCCESSFUL;
938}
939
940static ssize_t rtems_tftp_write(
941    rtems_libio_t   *iop,
942    const void      *buffer,
943    unsigned32      count
944)
945{
946    const char        *bp;
947    struct tftpStream *tp = iop->data1;
948    int               nleft, nfree, ncopy;
949
950    /*
951     * Bail out if an error has occurred
952     */
953    if (!tp->writing)
954        return EIO;
955
956
957    /*
958     * Write till user request is satisfied
959     * Notice that the buffer is flushed as soon as it is filled rather
960     * than waiting for the next write or a close.  This ensures that
961     * the flush in close writes a less than full buffer so the far
962     * end can detect the end-of-file condition.
963     */
964    bp = buffer;
965    nleft = count;
966    while (nleft) {
967        nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
968        if (nleft < nfree)
969            ncopy = nleft;
970        else
971            ncopy = nfree;
972        memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
973        tp->nused += ncopy;
974        nleft -= ncopy;
975        bp += ncopy;
976        if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
977            int e = rtems_tftp_flush (tp);
978            if (e) {
979                tp->writing = 0;
980                rtems_set_errno_and_return_minus_one (e);
981            }
982        }
983    }
984    return count;
985}
986
987/*
988 * Dummy version to let fopen(xxxx,"w") work properly.
989 */
990static int rtems_tftp_ftruncate(
991    rtems_libio_t   *iop,
992    off_t           count
993)
994{
995    return 0;
996}
997
998static rtems_filesystem_node_types_t rtems_tftp_node_type(
999     rtems_filesystem_location_info_t        *pathloc                 /* IN */
1000)
1001{
1002    if ((pathloc->node_access == NULL)
1003     || (pathloc->node_access == ROOT_NODE_ACCESS))
1004        return RTEMS_FILESYSTEM_MEMORY_FILE;
1005    return RTEMS_FILESYSTEM_DIRECTORY;
1006}
1007
1008static int rtems_tftp_free_node_info(
1009     rtems_filesystem_location_info_t        *pathloc                 /* IN */
1010)
1011{
1012    if (pathloc->node_access && (pathloc->node_access != ROOT_NODE_ACCESS)) {
1013        free (pathloc->node_access);
1014        pathloc->node_access = NULL;
1015    }
1016    return 0;
1017}
1018
1019
1020rtems_filesystem_operations_table  rtems_tftp_ops = {
1021    rtems_tftp_eval_path,            /* eval_path */
1022    rtems_tftp_evaluate_for_make,    /* evaluate_for_make */
1023    NULL,                            /* link */
1024    NULL,                            /* unlink */
1025    rtems_tftp_node_type,            /* node_type */
1026    NULL,                            /* mknod */
1027    NULL,                            /* chown */
1028    rtems_tftp_free_node_info,       /* freenodinfo */
1029    NULL,                            /* mount */
1030    rtems_tftp_mount_me,             /* initialize */
1031    NULL,                            /* unmount */
1032    NULL,                            /* fsunmount */
1033    NULL,                            /* utime, */
1034    NULL,                            /* evaluate_link */
1035    NULL,                            /* symlink */
1036    NULL,                            /* readlin */
1037};
1038 
1039rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
1040    rtems_tftp_open,   /* open */     
1041    rtems_tftp_close,  /* close */   
1042    rtems_tftp_read,   /* read */     
1043    rtems_tftp_write,  /* write */   
1044    NULL,              /* ioctl */   
1045    NULL,              /* lseek */   
1046    NULL,              /* fstat */   
1047    NULL,              /* fchmod */   
1048    rtems_tftp_ftruncate, /* ftruncate */
1049    NULL,              /* fpathconf */
1050    NULL,              /* fsync */   
1051    NULL,              /* fdatasync */
1052    NULL,              /* fcntl */
1053    NULL               /* rmnod */
1054};
Note: See TracBrowser for help on using the repository browser.