source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 10135fa

5
Last change on this file since 10135fa was 10135fa, checked in by Thomas Dörfler <thomas.doerfler@…>, on 12/20/18 at 13:26:50

tftpfs: Some bug fixes

Fix for:

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