source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 74ef2c10

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