source: rtems/cpukit/libnetworking/lib/tftpDriver.c @ 984c4c49

4.11
Last change on this file since 984c4c49 was 984c4c49, checked in by Ralf Corsepius <ralf.corsepius@…>, on Nov 6, 2011 at 12:00:04 PM

2011-11-06 Ralf Corsépius <ralf.corsepius@…>

PR1945/cpukit

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