source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 3535439f

5
Last change on this file since 3535439f was 3535439f, checked in by Sebastian Huber <sebastian.huber@…>, on 01/03/18 at 15:17:21

tftpfs: Use self-contained mutex

Update #2843.

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