OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems-20020807/] [cpukit/] [libnetworking/] [lib/] [tftpDriver.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.