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

4.11
Last change on this file since da154e14 was da154e14, checked in by Sebastian Huber <sebastian.huber@…>, on May 14, 2012 at 2:55:41 PM

Filesystem: Move operations to mount table entry

The scope of the file system operations is the file system instance.
The scope of the file system node handlers is the file location. The
benefit of moving the operations to the mount table entry is a size
reduction of the file location (rtems_filesystem_location_info_t). The
code size is slightly increased due to additional load instructions.

Restructure rtems_filesystem_mount_table_entry_t to improve cache
efficiency.

  • 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/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    rtems_set_errno_and_return_minus_one (e);
917}
918
919static ssize_t rtems_tftp_write(
920    rtems_libio_t   *iop,
921    const void      *buffer,
922    size_t           count
923)
924{
925    const char        *bp;
926    struct tftpStream *tp = iop->data1;
927    int               nleft, nfree, ncopy;
928
929    /*
930     * Bail out if an error has occurred
931     */
932    if (!tp->writing)
933        rtems_set_errno_and_return_minus_one (EIO);
934
935    /*
936     * Write till user request is satisfied
937     * Notice that the buffer is flushed as soon as it is filled rather
938     * than waiting for the next write or a close.  This ensures that
939     * the flush in close writes a less than full buffer so the far
940     * end can detect the end-of-file condition.
941     */
942    bp = buffer;
943    nleft = count;
944    while (nleft) {
945        nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
946        if (nleft < nfree)
947            ncopy = nleft;
948        else
949            ncopy = nfree;
950        memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
951        tp->nused += ncopy;
952        nleft -= ncopy;
953        bp += ncopy;
954        if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
955            int e = rtems_tftp_flush (tp);
956            if (e) {
957                tp->writing = 0;
958                rtems_set_errno_and_return_minus_one (e);
959            }
960        }
961    }
962    return count;
963}
964
965/*
966 * Dummy version to let fopen(xxxx,"w") work properly.
967 */
968static int rtems_tftp_ftruncate(
969    rtems_libio_t   *iop __attribute__((unused)),
970    off_t            count __attribute__((unused))
971)
972{
973    return 0;
974}
975
976static rtems_filesystem_node_types_t rtems_tftp_node_type(
977    const rtems_filesystem_location_info_t *loc
978)
979{
980    const char *path = loc->node_access;
981    size_t pathlen = strlen (path);
982
983    return rtems_tftp_is_directory (path, pathlen) ?
984        RTEMS_FILESYSTEM_DIRECTORY
985            : RTEMS_FILESYSTEM_MEMORY_FILE;
986}
987
988static int rtems_tftp_clone(
989    rtems_filesystem_location_info_t *loc
990)
991{
992    int rv = 0;
993
994    loc->node_access = strdup (loc->node_access);
995
996    if (loc->node_access == NULL) {
997        errno = ENOMEM;
998        rv = -1;
999    }
1000
1001    return rv;
1002}
1003
1004static void rtems_tftp_free_node_info(
1005    const rtems_filesystem_location_info_t *loc
1006)
1007{
1008    free (loc->node_access);
1009}
1010
1011static bool rtems_tftp_are_nodes_equal(
1012  const rtems_filesystem_location_info_t *a,
1013  const rtems_filesystem_location_info_t *b
1014)
1015{
1016  return strcmp (a->node_access, b->node_access) == 0;
1017}
1018
1019static const rtems_filesystem_operations_table  rtems_tftp_ops = {
1020    .lock_h = rtems_filesystem_default_lock,
1021    .unlock_h = rtems_filesystem_default_unlock,
1022    .eval_path_h = rtems_tftp_eval_path,
1023    .link_h = rtems_filesystem_default_link,
1024    .are_nodes_equal_h = rtems_tftp_are_nodes_equal,
1025    .node_type_h = rtems_tftp_node_type,
1026    .mknod_h = rtems_filesystem_default_mknod,
1027    .rmnod_h = rtems_filesystem_default_rmnod,
1028    .fchmod_h = rtems_filesystem_default_fchmod,
1029    .chown_h = rtems_filesystem_default_chown,
1030    .clonenod_h = rtems_tftp_clone,
1031    .freenod_h = rtems_tftp_free_node_info,
1032    .mount_h = rtems_filesystem_default_mount,
1033    .fsmount_me_h = rtems_tftpfs_initialize,
1034    .unmount_h = rtems_filesystem_default_unmount,
1035    .fsunmount_me_h = rtems_tftpfs_shutdown,
1036    .utime_h = rtems_filesystem_default_utime,
1037    .symlink_h = rtems_filesystem_default_symlink,
1038    .readlink_h = rtems_filesystem_default_readlink,
1039    .rename_h = rtems_filesystem_default_rename,
1040    .statvfs_h = rtems_filesystem_default_statvfs
1041};
1042
1043static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
1044   .open_h = rtems_tftp_open,
1045   .close_h = rtems_tftp_close,
1046   .read_h = rtems_tftp_read,
1047   .write_h = rtems_tftp_write,
1048   .ioctl_h = rtems_filesystem_default_ioctl,
1049   .lseek_h = rtems_filesystem_default_lseek,
1050   .fstat_h = rtems_filesystem_default_fstat,
1051   .ftruncate_h = rtems_tftp_ftruncate,
1052   .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
1053   .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
1054   .fcntl_h = rtems_filesystem_default_fcntl
1055};
Note: See TracBrowser for help on using the repository browser.