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

4.10
Last change on this file since aecb5086 was aecb5086, checked in by Michael Davidsaver <mdavidsaver@…>, on 07/26/15 at 13:28:07

tftpDriver: don't free directory node's path string

Update #2375.

  • Property mode set to 100644
File size: 27.2 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.node_access = root_path;
207  mt_entry->mt_fs_root.node_access_2 = root_path;
208  mt_entry->mt_fs_root.handlers = &rtems_tftp_handlers;
209  mt_entry->mt_fs_root.ops = &rtems_tftp_ops;
210 
211  /*
212   *  Now allocate a semaphore for mutual exclusion.
213   *
214   *  NOTE:  This could be in an fsinfo for this filesystem type.
215   */
216
217  sc = rtems_semaphore_create (
218    rtems_build_name('T', 'F', 'T', 'P'),
219    1,
220    RTEMS_FIFO |
221    RTEMS_BINARY_SEMAPHORE |
222    RTEMS_NO_INHERIT_PRIORITY |
223    RTEMS_NO_PRIORITY_CEILING |
224    RTEMS_LOCAL,
225    0,
226    &fs->tftp_mutex
227  );
228
229  if (sc != RTEMS_SUCCESSFUL)
230      goto error;
231
232  if (data) {
233      char* config = (char*) data;
234      char* token;
235      char* saveptr;
236      token = strtok_r (config, " ", &saveptr);
237      while (token) {
238          if (strcmp (token, "verbose") == 0)
239              fs->flags |= TFTPFS_VERBOSE;
240          token = strtok_r (NULL, " ", &saveptr);
241      }
242  }
243 
244  return 0;
245
246error:
247
248  free (fs);
249  free (root_path);
250
251  rtems_set_errno_and_return_minus_one (ENOMEM);
252}
253
254/*
255 * Release a stream and clear the pointer to it
256 */
257static void
258releaseStream (tftpfs_info_t *fs, int s)
259{
260    if (fs->tftpStreams[s] && (fs->tftpStreams[s]->socket >= 0))
261        close (fs->tftpStreams[s]->socket);
262    rtems_semaphore_obtain (fs->tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
263    free (fs->tftpStreams[s]);
264    fs->tftpStreams[s] = NULL;
265    rtems_semaphore_release (fs->tftp_mutex);
266}
267
268static int
269rtems_tftpfs_shutdown (rtems_filesystem_mount_table_entry_t* mt_entry)
270{
271  tftpfs_info_t *fs = tftpfs_info_mount_table (mt_entry);
272  int            s;
273  for (s = 0; s < fs->nStreams; s++)
274      releaseStream (fs, s);
275  rtems_semaphore_delete (fs->tftp_mutex);
276  free (fs);
277  free (mt_entry->mt_fs_root.node_access);
278  return 0;
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
433static int rtems_tftp_evaluate_for_make(
434   const char                         *path __attribute__((unused)),       /* IN     */
435   rtems_filesystem_location_info_t   *pathloc,    /* IN/OUT */
436   const char                        **name __attribute__((unused))        /* OUT    */
437)
438{
439  pathloc->node_access = NULL;
440  pathloc->node_access_2 = NULL;
441  rtems_set_errno_and_return_minus_one (EIO);
442}
443
444/*
445 * Convert a path to canonical form
446 */
447static void
448fixPath (char *path)
449{
450    char *inp, *outp, *base;
451
452    outp = inp = path;
453    base = NULL;
454    for (;;) {
455        if (inp[0] == '.') {
456            if (inp[1] == '\0')
457                break;
458            if (inp[1] == '/') {
459                inp += 2;
460                continue;
461            }
462            if (inp[1] == '.') {
463                if (inp[2] == '\0') {
464                    if ((base != NULL) && (outp > base)) {
465                        outp--;
466                        while ((outp > base) && (outp[-1] != '/'))
467                            outp--;
468                    }
469                    break;
470                }
471                if (inp[2] == '/') {
472                    inp += 3;
473                    if (base == NULL)
474                        continue;
475                    if (outp > base) {
476                        outp--;
477                        while ((outp > base) && (outp[-1] != '/'))
478                            outp--;
479                    }
480                    continue;
481                }
482            }
483        }
484        if (base == NULL)
485            base = inp;
486        while (inp[0] != '/') {
487            if ((*outp++ = *inp++) == '\0')
488                return;
489        }
490        *outp++ = '/';
491        while (inp[0] == '/')
492            inp++;
493    }
494    *outp = '\0';
495    return;
496}
497
498static int rtems_tftp_eval_path(
499  const char                        *pathname,     /* IN     */
500  size_t                             pathnamelen,  /* IN     */
501  int                                flags,        /* IN     */
502  rtems_filesystem_location_info_t  *pathloc       /* IN/OUT */
503)
504{
505    const int rw = RTEMS_LIBIO_PERMS_READ | RTEMS_LIBIO_PERMS_WRITE;
506
507    if ((flags & rw) != rw) {
508        rtems_filesystem_location_info_t *currentloc = pathloc;
509        char *current = currentloc->node_access;
510        size_t currentlen = strlen (current);
511        const char *path = pathname;
512        size_t pathlen = pathnamelen;
513        size_t len = currentlen + pathlen;
514
515        current = malloc (len + 1);
516        if (current != NULL) {
517            memcpy (current, currentloc->node_access, currentlen);
518            memcpy (current + currentlen, path, pathlen);
519            current [len] = '\0';
520            if (!rtems_tftp_is_directory (current, len)) {
521                fixPath (current);
522                currentloc->node_access = current;
523                currentloc->node_access_2 = current;
524            } else {
525                /* this directory might be the FS root,
526                 * and unmount() compares the pointer
527                 */
528                if (strcmp(current, currentloc->mt_entry->mt_fs_root.node_access)==0) {
529                    free(current);
530                    currentloc->node_access = currentloc->mt_entry->mt_fs_root.node_access;
531                    currentloc->node_access_2 = NULL;
532                } else {
533                    currentloc->node_access = current;
534                    currentloc->node_access_2 = current;
535                }
536            }
537            return 0;
538        } else {
539            rtems_set_errno_and_return_minus_one (ENOMEM);
540        }
541    } else {
542        rtems_set_errno_and_return_minus_one (EINVAL);
543    }
544}
545
546/*
547 * The routine which does most of the work for the IMFS open handler
548 */
549static int rtems_tftp_open_worker(
550    rtems_libio_t *iop,
551    char          *full_path_name,
552    uint32_t       flags,
553    uint32_t       mode __attribute__((unused))
554)
555{
556    tftpfs_info_t        *fs;
557    struct tftpStream    *tp;
558    int                  retryCount;
559    struct in_addr       farAddress;
560    int                  s;
561    int                  len;
562    char                 *cp1;
563    char                 *cp2;
564    char                 *remoteFilename;
565    rtems_interval       now;
566    rtems_status_code    sc;
567    char                 *hostname;
568
569    /*
570     * Get the file system info.
571     */
572    fs = tftpfs_info_iop (iop);
573   
574    /*
575     * Extract the host name component
576     */
577    hostname = full_path_name;
578    cp1 = strchr (full_path_name, ':');
579    if (!cp1) /* if can't use : as delimiter, try / */
580        cp1 = strchr (full_path_name, '/');
581    if (!cp1) {
582        hostname = "BOOTP_HOST";
583        cp1 = full_path_name;
584    }
585    else {
586        *cp1 = '\0';
587        ++cp1;
588    }
589
590    /*
591     * Convert hostname to Internet address
592     */
593    if (strcmp (hostname, "BOOTP_HOST") == 0)
594        farAddress = rtems_bsdnet_bootp_server_address;
595    else if (inet_aton (hostname, &farAddress) == 0) {
596        struct hostent *he = gethostbyname(hostname);
597        if (he == NULL)
598            return ENOENT;
599        memcpy (&farAddress, he->h_addr, sizeof (farAddress));
600    } else {
601        return ENOENT; /* TODO, can remove? */
602    }
603   
604    /*
605     * Extract file pathname component
606     */
607    if (strcmp (cp1, "BOOTP_FILE") == 0) {
608        cp1 = rtems_bsdnet_bootp_boot_file_name;
609    }
610    if (*cp1 == '\0')
611        return ENOENT;
612    remoteFilename = cp1;
613    if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10))
614        return ENOENT;
615
616    /*
617     * Find a free stream
618     */
619    sc = rtems_semaphore_obtain (fs->tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
620    if (sc != RTEMS_SUCCESSFUL)
621        return EBUSY;
622    for (s = 0 ; s < fs->nStreams ; s++) {
623        if (fs->tftpStreams[s] == NULL)
624        break;
625    }
626    if (s == fs->nStreams) {
627        /*
628         * Reallocate stream pointers
629         * Guard against the case where realloc() returns NULL.
630         */
631        struct tftpStream **np;
632
633        np = realloc (fs->tftpStreams, ++fs->nStreams * sizeof *fs->tftpStreams);
634        if (np == NULL) {
635            rtems_semaphore_release (fs->tftp_mutex);
636            return ENOMEM;
637        }
638        fs->tftpStreams = np;
639    }
640    tp = fs->tftpStreams[s] = malloc (sizeof (struct tftpStream));
641    rtems_semaphore_release (fs->tftp_mutex);
642    if (tp == NULL)
643        return ENOMEM;
644    iop->data0 = s;
645    iop->data1 = tp;
646
647    /*
648     * Create the socket
649     */
650    if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
651        releaseStream (fs, s);
652        return ENOMEM;
653    }
654
655    /*
656     * Bind the socket to a local address
657     */
658    retryCount = 0;
659    now = rtems_clock_get_ticks_since_boot();
660    for (;;) {
661        int try = (now + retryCount) % 10;
662
663        tp->myAddress.sin_family = AF_INET;
664        tp->myAddress.sin_port = htons (UDP_PORT_BASE + fs->nStreams * try + s);
665        tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY);
666        if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0)
667            break;
668        if (++retryCount == 10) {
669            releaseStream (fs, s);
670            return EBUSY;
671        }
672    }
673
674    /*
675     * Set the UDP destination to the TFTP server
676     * port on the remote machine.
677     */
678    tp->farAddress.sin_family = AF_INET;
679    tp->farAddress.sin_addr = farAddress;
680    tp->farAddress.sin_port = htons (69);
681
682    if (fs->flags & TFTPFS_VERBOSE) {
683        printf("TFTPFS: %d %s %s from %08lx\n", (unsigned)flags,
684               ((flags & O_ACCMODE) == O_RDONLY)?"read":"write",
685               remoteFilename, ntohl(farAddress.s_addr));
686    }
687    /*
688     * Start the transfer
689     */
690    tp->firstReply = 1;
691    retryCount = 0;
692    for (;;) {
693        /*
694         * Create the request
695         */
696        if ((flags & O_ACCMODE) == O_RDONLY) {
697            tp->writing = 0;
698            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ);
699        }
700        else {
701            tp->writing = 1;
702            tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_WRQ);
703        }
704        cp1 = (char *) tp->pkbuf.tftpRWRQ.filename_mode;
705        cp2 = (char *) remoteFilename;
706        while ((*cp1++ = *cp2++) != '\0')
707            continue;
708        cp2 = "octet";
709        while ((*cp1++ = *cp2++) != '\0')
710            continue;
711        len = cp1 - (char *)&tp->pkbuf.tftpRWRQ;
712
713        /*
714         * Send the request
715         */
716        if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0,
717                    (struct sockaddr *)&tp->farAddress,
718                    sizeof tp->farAddress) < 0) {
719            releaseStream (fs, s);
720            return EIO;
721        }
722
723        /*
724         * Get reply
725         */
726        len = getPacket (tp, retryCount);
727        if (len >= (int) sizeof tp->pkbuf.tftpACK) {
728            int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
729            if (!tp->writing
730             && (opcode == TFTP_OPCODE_DATA)
731             && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) {
732                tp->nused = 0;
733                tp->blocknum = 1;
734                tp->nleft = len - 2 * sizeof (uint16_t  );
735                tp->eof = (tp->nleft < TFTP_BUFSIZE);
736                if (sendAck (tp) != 0) {
737                    releaseStream (fs, s);
738                    return EIO;
739                }
740                break;
741            }
742            if (tp->writing
743             && (opcode == TFTP_OPCODE_ACK)
744             && (ntohs (tp->pkbuf.tftpACK.blocknum) == 0)) {
745                tp->nused = 0;
746                tp->blocknum = 1;
747                break;
748            }
749            if (opcode == TFTP_OPCODE_ERROR) {
750                int e = tftpErrno (tp);
751                releaseStream (fs, s);
752                return e;
753            }
754        }
755
756        /*
757         * Keep trying
758         */
759        if (++retryCount >= OPEN_RETRY_LIMIT) {
760            releaseStream (fs, s);
761            return EIO;
762        }
763    }
764    return 0;
765}
766
767static int rtems_tftp_open(
768    rtems_libio_t *iop,
769    const char    *new_name,
770    uint32_t      oflag,
771    uint32_t      mode
772)
773{
774    tftpfs_info_t *fs;
775    char          *full_path_name;
776    int           err;
777
778    full_path_name = iop->pathinfo.node_access;
779
780    if (rtems_tftp_is_directory (full_path_name, strlen (full_path_name))) {
781        rtems_set_errno_and_return_minus_one (ENOTSUP);
782    }
783
784    /*
785     * Get the file system info.
786     */
787    fs = tftpfs_info_iop (iop);
788
789    if (fs->flags & TFTPFS_VERBOSE)
790      printf ("TFTPFS: %s\n", full_path_name);
791
792    err = rtems_tftp_open_worker (iop, full_path_name, oflag, mode);
793    if (err != 0) {
794       rtems_set_errno_and_return_minus_one (err);
795    }
796
797    return 0;
798}
799
800/*
801 * Read from a TFTP stream
802 */
803static ssize_t rtems_tftp_read(
804    rtems_libio_t *iop,
805    void          *buffer,
806    size_t         count
807)
808{
809    char              *bp;
810    struct tftpStream *tp = iop->data1;
811    int               retryCount;
812    int               nwant;
813
814    if (!tp)
815        rtems_set_errno_and_return_minus_one( EIO );
816   
817    /*
818     * Read till user request is satisfied or EOF is reached
819     */
820    bp = buffer;
821    nwant = count;
822    while (nwant) {
823        if (tp->nleft) {
824            int ncopy;
825            if (nwant < tp->nleft)
826                ncopy = nwant;
827            else
828                ncopy = tp->nleft;
829            memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], ncopy);
830            tp->nused += ncopy;
831            tp->nleft -= ncopy;
832            bp += ncopy;
833            nwant -= ncopy;
834            if (nwant == 0)
835                break;
836        }
837        if (tp->eof)
838            break;
839
840        /*
841         * Wait for the next packet
842         */
843        retryCount = 0;
844        for (;;) {
845            int len = getPacket (tp, retryCount);
846            if (len >= (int)sizeof tp->pkbuf.tftpACK) {
847                int opcode = ntohs (tp->pkbuf.tftpDATA.opcode);
848                uint16_t   nextBlock = tp->blocknum + 1;
849                if ((opcode == TFTP_OPCODE_DATA)
850                 && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) {
851                    tp->nused = 0;
852                    tp->nleft = len - 2 * sizeof (uint16_t);
853                    tp->eof = (tp->nleft < TFTP_BUFSIZE);
854                    tp->blocknum++;
855                    if (sendAck (tp) != 0)
856                        rtems_set_errno_and_return_minus_one (EIO);
857                    break;
858                }
859                if (opcode == TFTP_OPCODE_ERROR)
860                    rtems_set_errno_and_return_minus_one (tftpErrno (tp));
861            }
862
863            /*
864             * Keep trying?
865             */
866            if (++retryCount == IO_RETRY_LIMIT)
867                rtems_set_errno_and_return_minus_one (EIO);
868            if (sendAck (tp) != 0)
869                rtems_set_errno_and_return_minus_one (EIO);
870        }
871    }
872    return count - nwant;
873}
874
875/*
876 * Flush a write buffer and wait for acknowledgement
877 */
878static int rtems_tftp_flush (struct tftpStream *tp)
879{
880    int wlen, rlen;
881    int retryCount = 0;
882
883    wlen = tp->nused + 2 * sizeof (uint16_t  );
884    for (;;) {
885        tp->pkbuf.tftpDATA.opcode = htons (TFTP_OPCODE_DATA);
886        tp->pkbuf.tftpDATA.blocknum = htons (tp->blocknum);
887#ifdef RTEMS_TFTP_DRIVER_DEBUG
888        if (rtems_tftp_driver_debug)
889            printf ("TFTP: SEND %d (%d)\n", tp->blocknum, tp->nused);
890#endif
891        if (sendto (tp->socket, (char *)&tp->pkbuf, wlen, 0,
892                                        (struct sockaddr *)&tp->farAddress,
893                                        sizeof tp->farAddress) < 0)
894            return EIO;
895        rlen = getPacket (tp, retryCount);
896        /*
897         * Our last packet won't necessarily be acknowledged!
898         */
899        if ((rlen < 0) && (tp->nused < sizeof tp->pkbuf.tftpDATA.data))
900                return 0;
901        if (rlen >= (int)sizeof tp->pkbuf.tftpACK) {
902            int opcode = ntohs (tp->pkbuf.tftpACK.opcode);
903            if ((opcode == TFTP_OPCODE_ACK)
904             && (ntohs (tp->pkbuf.tftpACK.blocknum) == tp->blocknum)) {
905                tp->nused = 0;
906                tp->blocknum++;
907                return 0;
908            }
909            if (opcode == TFTP_OPCODE_ERROR)
910                return tftpErrno (tp);
911        }
912
913        /*
914         * Keep trying?
915         */
916        if (++retryCount == IO_RETRY_LIMIT)
917            return EIO;
918    }
919}
920
921/*
922 * Close a TFTP stream
923 */
924static int rtems_tftp_close(
925    rtems_libio_t *iop
926)
927{
928    tftpfs_info_t     *fs;
929    struct tftpStream *tp = iop->data1;
930    int                e = 0;
931   
932    /*
933     * Get the file system info.
934     */
935    fs = tftpfs_info_iop (iop);
936   
937    if (!tp)
938        rtems_set_errno_and_return_minus_one (EIO);
939   
940    if (tp->writing)
941        e = rtems_tftp_flush (tp);
942    if (!tp->eof && !tp->firstReply) {
943        /*
944         * Tell the other end to stop
945         */
946        rtems_interval ticksPerSecond;
947        sendStifle (tp, &tp->farAddress);
948        ticksPerSecond = rtems_clock_get_ticks_per_second();
949        rtems_task_wake_after (1 + ticksPerSecond / 10);
950    }
951    releaseStream (fs, iop->data0);
952    if (e)
953        rtems_set_errno_and_return_minus_one (e);
954    return 0;
955}
956
957static ssize_t rtems_tftp_write(
958    rtems_libio_t   *iop,
959    const void      *buffer,
960    size_t           count
961)
962{
963    const char        *bp;
964    struct tftpStream *tp = iop->data1;
965    int               nleft, nfree, ncopy;
966
967    /*
968     * Bail out if an error has occurred
969     */
970    if (!tp->writing)
971        rtems_set_errno_and_return_minus_one (EIO);
972
973    /*
974     * Write till user request is satisfied
975     * Notice that the buffer is flushed as soon as it is filled rather
976     * than waiting for the next write or a close.  This ensures that
977     * the flush in close writes a less than full buffer so the far
978     * end can detect the end-of-file condition.
979     */
980    bp = buffer;
981    nleft = count;
982    while (nleft) {
983        nfree = sizeof tp->pkbuf.tftpDATA.data - tp->nused;
984        if (nleft < nfree)
985            ncopy = nleft;
986        else
987            ncopy = nfree;
988        memcpy (&tp->pkbuf.tftpDATA.data[tp->nused], bp, ncopy);
989        tp->nused += ncopy;
990        nleft -= ncopy;
991        bp += ncopy;
992        if (tp->nused == sizeof tp->pkbuf.tftpDATA.data) {
993            int e = rtems_tftp_flush (tp);
994            if (e) {
995                tp->writing = 0;
996                rtems_set_errno_and_return_minus_one (e);
997            }
998        }
999    }
1000    return count;
1001}
1002
1003/*
1004 * Dummy version to let fopen(xxxx,"w") work properly.
1005 */
1006static int rtems_tftp_ftruncate(
1007    rtems_libio_t   *iop __attribute__((unused)),
1008    off_t            count __attribute__((unused))
1009)
1010{
1011    return 0;
1012}
1013
1014static rtems_filesystem_node_types_t rtems_tftp_node_type(
1015     rtems_filesystem_location_info_t        *pathloc                 /* IN */
1016)
1017{
1018    const char * path = pathloc->node_access;
1019    if (!rtems_tftp_is_directory(path, strlen(path)))
1020        return RTEMS_FILESYSTEM_MEMORY_FILE;
1021    return RTEMS_FILESYSTEM_DIRECTORY;
1022}
1023
1024static int rtems_tftp_fstat(
1025    rtems_filesystem_location_info_t *loc,
1026    struct stat                            *buf
1027)
1028{
1029    const char *path = loc->node_access;
1030    size_t pathlen = strlen (path);
1031
1032    buf->st_mode = S_IRWXU | S_IRWXG | S_IRWXO
1033        | (rtems_tftp_is_directory (path, pathlen) ? S_IFDIR : S_IFREG);
1034
1035    return 0;
1036}
1037
1038static int rtems_tftp_free_node_info(
1039    rtems_filesystem_location_info_t *loc
1040)
1041{
1042    free (loc->node_access_2);
1043    return 0;
1044}
1045
1046static const rtems_filesystem_operations_table  rtems_tftp_ops = {
1047    .evalpath_h = rtems_tftp_eval_path,
1048    .evalformake_h = rtems_tftp_evaluate_for_make,
1049    .node_type_h = rtems_tftp_node_type,
1050    .freenod_h = rtems_tftp_free_node_info,
1051    .fsmount_me_h = rtems_tftpfs_initialize,
1052    .fsunmount_me_h = rtems_tftpfs_shutdown,
1053};
1054
1055static const rtems_filesystem_file_handlers_r rtems_tftp_handlers = {
1056   .open_h = rtems_tftp_open,
1057   .close_h = rtems_tftp_close,
1058   .read_h = rtems_tftp_read,
1059   .write_h = rtems_tftp_write,
1060   .fstat_h = rtems_tftp_fstat,
1061   .ftruncate_h = rtems_tftp_ftruncate,
1062};
Note: See TracBrowser for help on using the repository browser.