1*0a6a1f1dSLionel Sambuc /* $NetBSD: tftp.c,v 1.35 2014/03/20 03:13:18 christos Exp $ */
258a2b000SEvgeniy Ivanov
358a2b000SEvgeniy Ivanov /*
458a2b000SEvgeniy Ivanov * Copyright (c) 1996
558a2b000SEvgeniy Ivanov * Matthias Drochner. All rights reserved.
658a2b000SEvgeniy Ivanov *
758a2b000SEvgeniy Ivanov * Redistribution and use in source and binary forms, with or without
858a2b000SEvgeniy Ivanov * modification, are permitted provided that the following conditions
958a2b000SEvgeniy Ivanov * are met:
1058a2b000SEvgeniy Ivanov * 1. Redistributions of source code must retain the above copyright
1158a2b000SEvgeniy Ivanov * notice, this list of conditions and the following disclaimer.
1258a2b000SEvgeniy Ivanov * 2. Redistributions in binary form must reproduce the above copyright
1358a2b000SEvgeniy Ivanov * notice, this list of conditions and the following disclaimer in the
1458a2b000SEvgeniy Ivanov * documentation and/or other materials provided with the distribution.
1558a2b000SEvgeniy Ivanov *
1658a2b000SEvgeniy Ivanov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1758a2b000SEvgeniy Ivanov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1858a2b000SEvgeniy Ivanov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1958a2b000SEvgeniy Ivanov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2058a2b000SEvgeniy Ivanov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2158a2b000SEvgeniy Ivanov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2258a2b000SEvgeniy Ivanov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2358a2b000SEvgeniy Ivanov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2458a2b000SEvgeniy Ivanov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2558a2b000SEvgeniy Ivanov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2658a2b000SEvgeniy Ivanov *
2758a2b000SEvgeniy Ivanov */
2858a2b000SEvgeniy Ivanov
2958a2b000SEvgeniy Ivanov /*
3058a2b000SEvgeniy Ivanov * Simple TFTP implementation for libsa.
3158a2b000SEvgeniy Ivanov * Assumes:
3258a2b000SEvgeniy Ivanov * - socket descriptor (int) at open_file->f_devdata
3358a2b000SEvgeniy Ivanov * - server host IP in global servip
3458a2b000SEvgeniy Ivanov * Restrictions:
3558a2b000SEvgeniy Ivanov * - read only
3658a2b000SEvgeniy Ivanov * - lseek only with SEEK_SET or SEEK_CUR
3758a2b000SEvgeniy Ivanov * - no big time differences between transfers (<tftp timeout)
3858a2b000SEvgeniy Ivanov */
3958a2b000SEvgeniy Ivanov
4058a2b000SEvgeniy Ivanov /*
4158a2b000SEvgeniy Ivanov * XXX Does not currently implement:
4258a2b000SEvgeniy Ivanov * XXX
4358a2b000SEvgeniy Ivanov * XXX LIBSA_NO_FS_CLOSE
4458a2b000SEvgeniy Ivanov * XXX LIBSA_NO_FS_SEEK
4558a2b000SEvgeniy Ivanov * XXX LIBSA_NO_FS_WRITE
4658a2b000SEvgeniy Ivanov * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
4758a2b000SEvgeniy Ivanov * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
4858a2b000SEvgeniy Ivanov */
4958a2b000SEvgeniy Ivanov
5058a2b000SEvgeniy Ivanov #include <sys/types.h>
5158a2b000SEvgeniy Ivanov #include <sys/stat.h>
5258a2b000SEvgeniy Ivanov #include <netinet/in.h>
5358a2b000SEvgeniy Ivanov #include <netinet/udp.h>
5458a2b000SEvgeniy Ivanov #include <netinet/in_systm.h>
5558a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h>
5658a2b000SEvgeniy Ivanov
5758a2b000SEvgeniy Ivanov #include "stand.h"
5858a2b000SEvgeniy Ivanov #include "net.h"
5958a2b000SEvgeniy Ivanov
6058a2b000SEvgeniy Ivanov #include "tftp.h"
6158a2b000SEvgeniy Ivanov
6258a2b000SEvgeniy Ivanov extern struct in_addr servip;
6358a2b000SEvgeniy Ivanov
6458a2b000SEvgeniy Ivanov static int tftpport = 2000;
6558a2b000SEvgeniy Ivanov
6658a2b000SEvgeniy Ivanov #define RSPACE 520 /* max data packet, rounded up */
6758a2b000SEvgeniy Ivanov
6858a2b000SEvgeniy Ivanov struct tftp_handle {
6958a2b000SEvgeniy Ivanov struct iodesc *iodesc;
7058a2b000SEvgeniy Ivanov int currblock; /* contents of lastdata */
7158a2b000SEvgeniy Ivanov int islastblock; /* flag */
7258a2b000SEvgeniy Ivanov int validsize;
7358a2b000SEvgeniy Ivanov int off;
7458a2b000SEvgeniy Ivanov const char *path; /* saved for re-requests */
7558a2b000SEvgeniy Ivanov struct {
7658a2b000SEvgeniy Ivanov u_char header[UDP_TOTAL_HEADER_SIZE];
7758a2b000SEvgeniy Ivanov struct tftphdr t;
7858a2b000SEvgeniy Ivanov u_char space[RSPACE];
7958a2b000SEvgeniy Ivanov } lastdata;
8058a2b000SEvgeniy Ivanov };
8158a2b000SEvgeniy Ivanov
8258a2b000SEvgeniy Ivanov static const int tftperrors[8] = {
8358a2b000SEvgeniy Ivanov 0, /* ??? */
8458a2b000SEvgeniy Ivanov ENOENT,
8558a2b000SEvgeniy Ivanov EPERM,
8658a2b000SEvgeniy Ivanov ENOSPC,
8758a2b000SEvgeniy Ivanov EINVAL, /* ??? */
8858a2b000SEvgeniy Ivanov EINVAL, /* ??? */
8958a2b000SEvgeniy Ivanov EEXIST,
9058a2b000SEvgeniy Ivanov EINVAL, /* ??? */
9158a2b000SEvgeniy Ivanov };
9258a2b000SEvgeniy Ivanov
9358a2b000SEvgeniy Ivanov static ssize_t recvtftp(struct iodesc *, void *, size_t, saseconds_t);
9458a2b000SEvgeniy Ivanov static int tftp_makereq(struct tftp_handle *);
9558a2b000SEvgeniy Ivanov static int tftp_getnextblock(struct tftp_handle *);
9658a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
9758a2b000SEvgeniy Ivanov static void tftp_terminate(struct tftp_handle *);
9858a2b000SEvgeniy Ivanov #endif
9958a2b000SEvgeniy Ivanov static ssize_t tftp_size_of_file(struct tftp_handle *tftpfile);
10058a2b000SEvgeniy Ivanov
10158a2b000SEvgeniy Ivanov static ssize_t
recvtftp(struct iodesc * d,void * pkt,size_t len,saseconds_t tleft)10258a2b000SEvgeniy Ivanov recvtftp(struct iodesc *d, void *pkt, size_t len, saseconds_t tleft)
10358a2b000SEvgeniy Ivanov {
10458a2b000SEvgeniy Ivanov ssize_t n;
10558a2b000SEvgeniy Ivanov struct tftphdr *t;
10658a2b000SEvgeniy Ivanov
10758a2b000SEvgeniy Ivanov errno = 0;
10858a2b000SEvgeniy Ivanov
10958a2b000SEvgeniy Ivanov n = readudp(d, pkt, len, tleft);
11058a2b000SEvgeniy Ivanov
11158a2b000SEvgeniy Ivanov if (n < 4)
11258a2b000SEvgeniy Ivanov return -1;
11358a2b000SEvgeniy Ivanov
11458a2b000SEvgeniy Ivanov t = (struct tftphdr *)pkt;
11558a2b000SEvgeniy Ivanov switch (ntohs(t->th_opcode)) {
11658a2b000SEvgeniy Ivanov case DATA:
11758a2b000SEvgeniy Ivanov if (htons(t->th_block) != d->xid) {
11858a2b000SEvgeniy Ivanov /*
11958a2b000SEvgeniy Ivanov * Expected block?
12058a2b000SEvgeniy Ivanov */
12158a2b000SEvgeniy Ivanov return -1;
12258a2b000SEvgeniy Ivanov }
12358a2b000SEvgeniy Ivanov if (d->xid == 1) {
12458a2b000SEvgeniy Ivanov /*
12558a2b000SEvgeniy Ivanov * First data packet from new port.
12658a2b000SEvgeniy Ivanov */
12758a2b000SEvgeniy Ivanov struct udphdr *uh;
12858a2b000SEvgeniy Ivanov uh = (struct udphdr *)pkt - 1;
12958a2b000SEvgeniy Ivanov d->destport = uh->uh_sport;
13058a2b000SEvgeniy Ivanov } /* else check uh_sport has not changed??? */
13158a2b000SEvgeniy Ivanov return (n - (t->th_data - (char *)t));
13258a2b000SEvgeniy Ivanov case ERROR:
13358a2b000SEvgeniy Ivanov if ((unsigned int)ntohs(t->th_code) >= 8) {
13458a2b000SEvgeniy Ivanov printf("illegal tftp error %d\n", ntohs(t->th_code));
13558a2b000SEvgeniy Ivanov errno = EIO;
13658a2b000SEvgeniy Ivanov } else {
13758a2b000SEvgeniy Ivanov #ifdef DEBUG
13858a2b000SEvgeniy Ivanov printf("tftp-error %d\n", ntohs(t->th_code));
13958a2b000SEvgeniy Ivanov #endif
14058a2b000SEvgeniy Ivanov errno = tftperrors[ntohs(t->th_code)];
14158a2b000SEvgeniy Ivanov }
14258a2b000SEvgeniy Ivanov return -1;
14358a2b000SEvgeniy Ivanov default:
14458a2b000SEvgeniy Ivanov #ifdef DEBUG
14558a2b000SEvgeniy Ivanov printf("tftp type %d not handled\n", ntohs(t->th_opcode));
14658a2b000SEvgeniy Ivanov #endif
14758a2b000SEvgeniy Ivanov return -1;
14858a2b000SEvgeniy Ivanov }
14958a2b000SEvgeniy Ivanov }
15058a2b000SEvgeniy Ivanov
15158a2b000SEvgeniy Ivanov /* send request, expect first block (or error) */
15258a2b000SEvgeniy Ivanov static int
tftp_makereq(struct tftp_handle * h)15358a2b000SEvgeniy Ivanov tftp_makereq(struct tftp_handle *h)
15458a2b000SEvgeniy Ivanov {
15558a2b000SEvgeniy Ivanov struct {
15658a2b000SEvgeniy Ivanov u_char header[UDP_TOTAL_HEADER_SIZE];
15758a2b000SEvgeniy Ivanov struct tftphdr t;
15858a2b000SEvgeniy Ivanov u_char space[FNAME_SIZE + 6];
15958a2b000SEvgeniy Ivanov } wbuf;
16058a2b000SEvgeniy Ivanov char *wtail;
16158a2b000SEvgeniy Ivanov int l;
16258a2b000SEvgeniy Ivanov ssize_t res;
16358a2b000SEvgeniy Ivanov struct tftphdr *t;
16458a2b000SEvgeniy Ivanov
16558a2b000SEvgeniy Ivanov wbuf.t.th_opcode = htons((u_short)RRQ);
16658a2b000SEvgeniy Ivanov wtail = wbuf.t.th_stuff;
16758a2b000SEvgeniy Ivanov l = strlen(h->path);
16858a2b000SEvgeniy Ivanov (void)memcpy(wtail, h->path, l + 1);
16958a2b000SEvgeniy Ivanov wtail += l + 1;
17058a2b000SEvgeniy Ivanov (void)memcpy(wtail, "octet", 6);
17158a2b000SEvgeniy Ivanov wtail += 6;
17258a2b000SEvgeniy Ivanov
17358a2b000SEvgeniy Ivanov t = &h->lastdata.t;
17458a2b000SEvgeniy Ivanov
17558a2b000SEvgeniy Ivanov /* h->iodesc->myport = htons(--tftpport); */
17658a2b000SEvgeniy Ivanov h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
17758a2b000SEvgeniy Ivanov h->iodesc->destport = htons(IPPORT_TFTP);
17858a2b000SEvgeniy Ivanov h->iodesc->xid = 1; /* expected block */
17958a2b000SEvgeniy Ivanov
18058a2b000SEvgeniy Ivanov res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
18158a2b000SEvgeniy Ivanov recvtftp, t, sizeof(*t) + RSPACE);
18258a2b000SEvgeniy Ivanov
18358a2b000SEvgeniy Ivanov if (res == -1)
18458a2b000SEvgeniy Ivanov return errno;
18558a2b000SEvgeniy Ivanov
18658a2b000SEvgeniy Ivanov h->currblock = 1;
18758a2b000SEvgeniy Ivanov h->validsize = res;
18858a2b000SEvgeniy Ivanov h->islastblock = 0;
18958a2b000SEvgeniy Ivanov if (res < SEGSIZE)
19058a2b000SEvgeniy Ivanov h->islastblock = 1; /* very short file */
19158a2b000SEvgeniy Ivanov return 0;
19258a2b000SEvgeniy Ivanov }
19358a2b000SEvgeniy Ivanov
19458a2b000SEvgeniy Ivanov /* ack block, expect next */
19558a2b000SEvgeniy Ivanov static int
tftp_getnextblock(struct tftp_handle * h)19658a2b000SEvgeniy Ivanov tftp_getnextblock(struct tftp_handle *h)
19758a2b000SEvgeniy Ivanov {
19858a2b000SEvgeniy Ivanov struct {
19958a2b000SEvgeniy Ivanov u_char header[UDP_TOTAL_HEADER_SIZE];
20058a2b000SEvgeniy Ivanov struct tftphdr t;
20158a2b000SEvgeniy Ivanov } wbuf;
20258a2b000SEvgeniy Ivanov char *wtail;
20358a2b000SEvgeniy Ivanov int res;
20458a2b000SEvgeniy Ivanov struct tftphdr *t;
20558a2b000SEvgeniy Ivanov
20658a2b000SEvgeniy Ivanov wbuf.t.th_opcode = htons((u_short)ACK);
20758a2b000SEvgeniy Ivanov wbuf.t.th_block = htons((u_short)h->currblock);
20858a2b000SEvgeniy Ivanov wtail = (char *)&wbuf.t.th_data;
20958a2b000SEvgeniy Ivanov
21058a2b000SEvgeniy Ivanov t = &h->lastdata.t;
21158a2b000SEvgeniy Ivanov
21258a2b000SEvgeniy Ivanov h->iodesc->xid = h->currblock + 1; /* expected block */
21358a2b000SEvgeniy Ivanov
21458a2b000SEvgeniy Ivanov res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
21558a2b000SEvgeniy Ivanov recvtftp, t, sizeof(*t) + RSPACE);
21658a2b000SEvgeniy Ivanov
21758a2b000SEvgeniy Ivanov if (res == -1) /* 0 is OK! */
21858a2b000SEvgeniy Ivanov return errno;
21958a2b000SEvgeniy Ivanov
22058a2b000SEvgeniy Ivanov h->currblock++;
22158a2b000SEvgeniy Ivanov h->validsize = res;
22258a2b000SEvgeniy Ivanov if (res < SEGSIZE)
22358a2b000SEvgeniy Ivanov h->islastblock = 1; /* EOF */
22458a2b000SEvgeniy Ivanov return 0;
22558a2b000SEvgeniy Ivanov }
22658a2b000SEvgeniy Ivanov
22758a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
22858a2b000SEvgeniy Ivanov static void
tftp_terminate(struct tftp_handle * h)22958a2b000SEvgeniy Ivanov tftp_terminate(struct tftp_handle *h)
23058a2b000SEvgeniy Ivanov {
23158a2b000SEvgeniy Ivanov struct {
23258a2b000SEvgeniy Ivanov u_char header[UDP_TOTAL_HEADER_SIZE];
23358a2b000SEvgeniy Ivanov struct tftphdr t;
23458a2b000SEvgeniy Ivanov } wbuf;
23558a2b000SEvgeniy Ivanov char *wtail;
23658a2b000SEvgeniy Ivanov
23758a2b000SEvgeniy Ivanov wtail = (char *)&wbuf.t.th_data;
23858a2b000SEvgeniy Ivanov if (h->islastblock) {
23958a2b000SEvgeniy Ivanov wbuf.t.th_opcode = htons((u_short)ACK);
24058a2b000SEvgeniy Ivanov wbuf.t.th_block = htons((u_short)h->currblock);
24158a2b000SEvgeniy Ivanov } else {
24258a2b000SEvgeniy Ivanov wbuf.t.th_opcode = htons((u_short)ERROR);
24358a2b000SEvgeniy Ivanov wbuf.t.th_code = htons((u_short)ENOSPACE); /* ??? */
24458a2b000SEvgeniy Ivanov *wtail++ = '\0'; /* empty error string */
24558a2b000SEvgeniy Ivanov }
24658a2b000SEvgeniy Ivanov
24758a2b000SEvgeniy Ivanov (void)sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
24858a2b000SEvgeniy Ivanov }
24958a2b000SEvgeniy Ivanov #endif
25058a2b000SEvgeniy Ivanov
25158a2b000SEvgeniy Ivanov __compactcall int
tftp_open(const char * path,struct open_file * f)25258a2b000SEvgeniy Ivanov tftp_open(const char *path, struct open_file *f)
25358a2b000SEvgeniy Ivanov {
25458a2b000SEvgeniy Ivanov struct tftp_handle *tftpfile;
25558a2b000SEvgeniy Ivanov struct iodesc *io;
25658a2b000SEvgeniy Ivanov int res;
25758a2b000SEvgeniy Ivanov
25858a2b000SEvgeniy Ivanov tftpfile = (struct tftp_handle *)alloc(sizeof(*tftpfile));
25958a2b000SEvgeniy Ivanov if (!tftpfile)
26058a2b000SEvgeniy Ivanov return ENOMEM;
26158a2b000SEvgeniy Ivanov
26258a2b000SEvgeniy Ivanov tftpfile->iodesc = io = socktodesc(*(int *)(f->f_devdata));
26358a2b000SEvgeniy Ivanov io->destip = servip;
26458a2b000SEvgeniy Ivanov tftpfile->off = 0;
26558a2b000SEvgeniy Ivanov tftpfile->path = path; /* XXXXXXX we hope it's static */
26658a2b000SEvgeniy Ivanov
26758a2b000SEvgeniy Ivanov res = tftp_makereq(tftpfile);
26858a2b000SEvgeniy Ivanov
26958a2b000SEvgeniy Ivanov if (res) {
27058a2b000SEvgeniy Ivanov dealloc(tftpfile, sizeof(*tftpfile));
27158a2b000SEvgeniy Ivanov return res;
27258a2b000SEvgeniy Ivanov }
27358a2b000SEvgeniy Ivanov f->f_fsdata = (void *)tftpfile;
27458a2b000SEvgeniy Ivanov fsmod = "nfs";
27558a2b000SEvgeniy Ivanov return 0;
27658a2b000SEvgeniy Ivanov }
27758a2b000SEvgeniy Ivanov
27858a2b000SEvgeniy Ivanov __compactcall int
tftp_read(struct open_file * f,void * addr,size_t size,size_t * resid)27958a2b000SEvgeniy Ivanov tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
28058a2b000SEvgeniy Ivanov {
28158a2b000SEvgeniy Ivanov struct tftp_handle *tftpfile;
28258a2b000SEvgeniy Ivanov #if !defined(LIBSA_NO_TWIDDLE)
28358a2b000SEvgeniy Ivanov static int tc = 0;
28458a2b000SEvgeniy Ivanov #endif
28558a2b000SEvgeniy Ivanov tftpfile = (struct tftp_handle *)f->f_fsdata;
28658a2b000SEvgeniy Ivanov
28758a2b000SEvgeniy Ivanov while (size > 0) {
28858a2b000SEvgeniy Ivanov int needblock;
28958a2b000SEvgeniy Ivanov size_t count;
29058a2b000SEvgeniy Ivanov
29158a2b000SEvgeniy Ivanov #if !defined(LIBSA_NO_TWIDDLE)
29258a2b000SEvgeniy Ivanov if (!(tc++ % 16))
29358a2b000SEvgeniy Ivanov twiddle();
29458a2b000SEvgeniy Ivanov #endif
29558a2b000SEvgeniy Ivanov
29658a2b000SEvgeniy Ivanov needblock = tftpfile->off / SEGSIZE + 1;
29758a2b000SEvgeniy Ivanov
29858a2b000SEvgeniy Ivanov if (tftpfile->currblock > needblock) { /* seek backwards */
29958a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
30058a2b000SEvgeniy Ivanov tftp_terminate(tftpfile);
30158a2b000SEvgeniy Ivanov #endif
30258a2b000SEvgeniy Ivanov tftp_makereq(tftpfile); /* no error check, it worked
30358a2b000SEvgeniy Ivanov * for open */
30458a2b000SEvgeniy Ivanov }
30558a2b000SEvgeniy Ivanov
30658a2b000SEvgeniy Ivanov while (tftpfile->currblock < needblock) {
30758a2b000SEvgeniy Ivanov int res;
30858a2b000SEvgeniy Ivanov
30958a2b000SEvgeniy Ivanov res = tftp_getnextblock(tftpfile);
31058a2b000SEvgeniy Ivanov if (res) { /* no answer */
31158a2b000SEvgeniy Ivanov #ifdef DEBUG
31258a2b000SEvgeniy Ivanov printf("tftp: read error (block %d->%d)\n",
31358a2b000SEvgeniy Ivanov tftpfile->currblock, needblock);
31458a2b000SEvgeniy Ivanov #endif
31558a2b000SEvgeniy Ivanov return res;
31658a2b000SEvgeniy Ivanov }
31758a2b000SEvgeniy Ivanov if (tftpfile->islastblock)
31858a2b000SEvgeniy Ivanov break;
31958a2b000SEvgeniy Ivanov }
32058a2b000SEvgeniy Ivanov
32158a2b000SEvgeniy Ivanov if (tftpfile->currblock == needblock) {
32258a2b000SEvgeniy Ivanov size_t offinblock, inbuffer;
32358a2b000SEvgeniy Ivanov
32458a2b000SEvgeniy Ivanov offinblock = tftpfile->off % SEGSIZE;
32558a2b000SEvgeniy Ivanov
32658a2b000SEvgeniy Ivanov if (offinblock > tftpfile->validsize) {
32758a2b000SEvgeniy Ivanov #ifdef DEBUG
32858a2b000SEvgeniy Ivanov printf("tftp: invalid offset %d\n",
32958a2b000SEvgeniy Ivanov tftpfile->off);
33058a2b000SEvgeniy Ivanov #endif
33158a2b000SEvgeniy Ivanov return EINVAL;
33258a2b000SEvgeniy Ivanov }
33358a2b000SEvgeniy Ivanov inbuffer = tftpfile->validsize - offinblock;
33458a2b000SEvgeniy Ivanov count = (size < inbuffer ? size : inbuffer);
33558a2b000SEvgeniy Ivanov (void)memcpy(addr,
33658a2b000SEvgeniy Ivanov tftpfile->lastdata.t.th_data + offinblock,
33758a2b000SEvgeniy Ivanov count);
33858a2b000SEvgeniy Ivanov
33958a2b000SEvgeniy Ivanov addr = (char *)addr + count;
34058a2b000SEvgeniy Ivanov tftpfile->off += count;
34158a2b000SEvgeniy Ivanov size -= count;
34258a2b000SEvgeniy Ivanov
34358a2b000SEvgeniy Ivanov if ((tftpfile->islastblock) && (count == inbuffer))
34458a2b000SEvgeniy Ivanov break; /* EOF */
34558a2b000SEvgeniy Ivanov } else {
34658a2b000SEvgeniy Ivanov #ifdef DEBUG
34758a2b000SEvgeniy Ivanov printf("tftp: block %d not found\n", needblock);
34858a2b000SEvgeniy Ivanov #endif
34958a2b000SEvgeniy Ivanov return EINVAL;
35058a2b000SEvgeniy Ivanov }
35158a2b000SEvgeniy Ivanov
35258a2b000SEvgeniy Ivanov }
35358a2b000SEvgeniy Ivanov
35458a2b000SEvgeniy Ivanov if (resid)
35558a2b000SEvgeniy Ivanov *resid = size;
35658a2b000SEvgeniy Ivanov return 0;
35758a2b000SEvgeniy Ivanov }
35858a2b000SEvgeniy Ivanov
35958a2b000SEvgeniy Ivanov __compactcall int
tftp_close(struct open_file * f)36058a2b000SEvgeniy Ivanov tftp_close(struct open_file *f)
36158a2b000SEvgeniy Ivanov {
36258a2b000SEvgeniy Ivanov struct tftp_handle *tftpfile;
36358a2b000SEvgeniy Ivanov tftpfile = (struct tftp_handle *)f->f_fsdata;
36458a2b000SEvgeniy Ivanov
36558a2b000SEvgeniy Ivanov #ifdef TFTP_NOTERMINATE
36658a2b000SEvgeniy Ivanov /* let it time out ... */
36758a2b000SEvgeniy Ivanov #else
36858a2b000SEvgeniy Ivanov tftp_terminate(tftpfile);
36958a2b000SEvgeniy Ivanov #endif
37058a2b000SEvgeniy Ivanov
37158a2b000SEvgeniy Ivanov dealloc(tftpfile, sizeof(*tftpfile));
37258a2b000SEvgeniy Ivanov return 0;
37358a2b000SEvgeniy Ivanov }
37458a2b000SEvgeniy Ivanov
37558a2b000SEvgeniy Ivanov __compactcall int
tftp_write(struct open_file * f,void * start,size_t size,size_t * resid)37658a2b000SEvgeniy Ivanov tftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
37758a2b000SEvgeniy Ivanov {
37858a2b000SEvgeniy Ivanov
37958a2b000SEvgeniy Ivanov return EROFS;
38058a2b000SEvgeniy Ivanov }
38158a2b000SEvgeniy Ivanov
38258a2b000SEvgeniy Ivanov static ssize_t
tftp_size_of_file(struct tftp_handle * tftpfile)38358a2b000SEvgeniy Ivanov tftp_size_of_file(struct tftp_handle *tftpfile)
38458a2b000SEvgeniy Ivanov {
38558a2b000SEvgeniy Ivanov ssize_t filesize;
38658a2b000SEvgeniy Ivanov
38758a2b000SEvgeniy Ivanov if (tftpfile->currblock > 1) { /* move to start of file */
38858a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
38958a2b000SEvgeniy Ivanov tftp_terminate(tftpfile);
39058a2b000SEvgeniy Ivanov #endif
39158a2b000SEvgeniy Ivanov tftp_makereq(tftpfile); /* no error check, it worked
39258a2b000SEvgeniy Ivanov * for open */
39358a2b000SEvgeniy Ivanov }
39458a2b000SEvgeniy Ivanov
39558a2b000SEvgeniy Ivanov /* start with the size of block 1 */
39658a2b000SEvgeniy Ivanov filesize = tftpfile->validsize;
39758a2b000SEvgeniy Ivanov
39858a2b000SEvgeniy Ivanov /* and keep adding the sizes till we hit the last block */
39958a2b000SEvgeniy Ivanov while (!tftpfile->islastblock) {
40058a2b000SEvgeniy Ivanov int res;
40158a2b000SEvgeniy Ivanov
40258a2b000SEvgeniy Ivanov res = tftp_getnextblock(tftpfile);
40358a2b000SEvgeniy Ivanov if (res) { /* no answer */
40458a2b000SEvgeniy Ivanov #ifdef DEBUG
40558a2b000SEvgeniy Ivanov printf("tftp: read error (block %d)\n",
40658a2b000SEvgeniy Ivanov tftpfile->currblock);
40758a2b000SEvgeniy Ivanov #endif
40858a2b000SEvgeniy Ivanov return -1;
40958a2b000SEvgeniy Ivanov }
41058a2b000SEvgeniy Ivanov filesize += tftpfile->validsize;
41158a2b000SEvgeniy Ivanov }
41258a2b000SEvgeniy Ivanov #ifdef DEBUG
41358a2b000SEvgeniy Ivanov printf("tftp_size_of_file: file is %zu bytes\n", filesize);
41458a2b000SEvgeniy Ivanov #endif
41558a2b000SEvgeniy Ivanov return filesize;
41658a2b000SEvgeniy Ivanov }
41758a2b000SEvgeniy Ivanov
41858a2b000SEvgeniy Ivanov __compactcall int
tftp_stat(struct open_file * f,struct stat * sb)41958a2b000SEvgeniy Ivanov tftp_stat(struct open_file *f, struct stat *sb)
42058a2b000SEvgeniy Ivanov {
42158a2b000SEvgeniy Ivanov struct tftp_handle *tftpfile;
42258a2b000SEvgeniy Ivanov tftpfile = (struct tftp_handle *)f->f_fsdata;
42358a2b000SEvgeniy Ivanov
42458a2b000SEvgeniy Ivanov sb->st_mode = 0444;
42558a2b000SEvgeniy Ivanov sb->st_nlink = 1;
42658a2b000SEvgeniy Ivanov sb->st_uid = 0;
42758a2b000SEvgeniy Ivanov sb->st_gid = 0;
42858a2b000SEvgeniy Ivanov sb->st_size = tftp_size_of_file(tftpfile);
42958a2b000SEvgeniy Ivanov return 0;
43058a2b000SEvgeniy Ivanov }
43158a2b000SEvgeniy Ivanov
43258a2b000SEvgeniy Ivanov #if defined(LIBSA_ENABLE_LS_OP)
433*0a6a1f1dSLionel Sambuc #include "ls.h"
43458a2b000SEvgeniy Ivanov __compactcall void
tftp_ls(struct open_file * f,const char * pattern)435*0a6a1f1dSLionel Sambuc tftp_ls(struct open_file *f, const char *pattern)
43658a2b000SEvgeniy Ivanov {
437*0a6a1f1dSLionel Sambuc lsunsup("tftp");
43858a2b000SEvgeniy Ivanov }
43958a2b000SEvgeniy Ivanov #endif
44058a2b000SEvgeniy Ivanov
44158a2b000SEvgeniy Ivanov __compactcall off_t
tftp_seek(struct open_file * f,off_t offset,int where)44258a2b000SEvgeniy Ivanov tftp_seek(struct open_file *f, off_t offset, int where)
44358a2b000SEvgeniy Ivanov {
44458a2b000SEvgeniy Ivanov struct tftp_handle *tftpfile;
44558a2b000SEvgeniy Ivanov tftpfile = (struct tftp_handle *)f->f_fsdata;
44658a2b000SEvgeniy Ivanov
44758a2b000SEvgeniy Ivanov switch (where) {
44858a2b000SEvgeniy Ivanov case SEEK_SET:
44958a2b000SEvgeniy Ivanov tftpfile->off = offset;
45058a2b000SEvgeniy Ivanov break;
45158a2b000SEvgeniy Ivanov case SEEK_CUR:
45258a2b000SEvgeniy Ivanov tftpfile->off += offset;
45358a2b000SEvgeniy Ivanov break;
45458a2b000SEvgeniy Ivanov default:
45558a2b000SEvgeniy Ivanov errno = EOFFSET;
45658a2b000SEvgeniy Ivanov return -1;
45758a2b000SEvgeniy Ivanov }
45858a2b000SEvgeniy Ivanov return tftpfile->off;
45958a2b000SEvgeniy Ivanov }
460