source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 4116fce6

4.115
Last change on this file since 4116fce6 was 4116fce6, checked in by Sebastian Huber <sebastian.huber@…>, on 02/24/12 at 16:39:27

Filesystem: New defaults fsync_h and fdatasync_h

New defaults rtems_filesystem_default_fsync_or_fdatasync() and
rtems_filesystem_default_fsync_or_fdatasync_success() for fsync_h and
fdatasync_h. The rtems_filesystem_default_fsync_or_fdatasync() sets now
errno to EINVAL according to POSIX.

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