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

4.10
Last change on this file since a4e052b was a4e052b, checked in by Michael Davidsaver <mdavidsaver@…>, on 07/26/15 at 13:27:10

tftpDriver: close() false error

closes #2376.

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