xref: /minix3/sys/lib/libsa/tftp.c (revision 58a2b0008e28f606a7f7f5faaeaba4faac57a1ea)
1*58a2b000SEvgeniy Ivanov /*	$NetBSD: tftp.c,v 1.34 2011/12/25 06:09:08 tsutsui Exp $	 */
2*58a2b000SEvgeniy Ivanov 
3*58a2b000SEvgeniy Ivanov /*
4*58a2b000SEvgeniy Ivanov  * Copyright (c) 1996
5*58a2b000SEvgeniy Ivanov  *	Matthias Drochner.  All rights reserved.
6*58a2b000SEvgeniy Ivanov  *
7*58a2b000SEvgeniy Ivanov  * Redistribution and use in source and binary forms, with or without
8*58a2b000SEvgeniy Ivanov  * modification, are permitted provided that the following conditions
9*58a2b000SEvgeniy Ivanov  * are met:
10*58a2b000SEvgeniy Ivanov  * 1. Redistributions of source code must retain the above copyright
11*58a2b000SEvgeniy Ivanov  *    notice, this list of conditions and the following disclaimer.
12*58a2b000SEvgeniy Ivanov  * 2. Redistributions in binary form must reproduce the above copyright
13*58a2b000SEvgeniy Ivanov  *    notice, this list of conditions and the following disclaimer in the
14*58a2b000SEvgeniy Ivanov  *    documentation and/or other materials provided with the distribution.
15*58a2b000SEvgeniy Ivanov  *
16*58a2b000SEvgeniy Ivanov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17*58a2b000SEvgeniy Ivanov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*58a2b000SEvgeniy Ivanov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19*58a2b000SEvgeniy Ivanov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20*58a2b000SEvgeniy Ivanov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21*58a2b000SEvgeniy Ivanov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22*58a2b000SEvgeniy Ivanov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23*58a2b000SEvgeniy Ivanov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24*58a2b000SEvgeniy Ivanov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25*58a2b000SEvgeniy Ivanov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*58a2b000SEvgeniy Ivanov  *
27*58a2b000SEvgeniy Ivanov  */
28*58a2b000SEvgeniy Ivanov 
29*58a2b000SEvgeniy Ivanov /*
30*58a2b000SEvgeniy Ivanov  * Simple TFTP implementation for libsa.
31*58a2b000SEvgeniy Ivanov  * Assumes:
32*58a2b000SEvgeniy Ivanov  *  - socket descriptor (int) at open_file->f_devdata
33*58a2b000SEvgeniy Ivanov  *  - server host IP in global servip
34*58a2b000SEvgeniy Ivanov  * Restrictions:
35*58a2b000SEvgeniy Ivanov  *  - read only
36*58a2b000SEvgeniy Ivanov  *  - lseek only with SEEK_SET or SEEK_CUR
37*58a2b000SEvgeniy Ivanov  *  - no big time differences between transfers (<tftp timeout)
38*58a2b000SEvgeniy Ivanov  */
39*58a2b000SEvgeniy Ivanov 
40*58a2b000SEvgeniy Ivanov /*
41*58a2b000SEvgeniy Ivanov  * XXX Does not currently implement:
42*58a2b000SEvgeniy Ivanov  * XXX
43*58a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_CLOSE
44*58a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_SEEK
45*58a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_WRITE
46*58a2b000SEvgeniy Ivanov  * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
47*58a2b000SEvgeniy Ivanov  * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
48*58a2b000SEvgeniy Ivanov  */
49*58a2b000SEvgeniy Ivanov 
50*58a2b000SEvgeniy Ivanov #include <sys/types.h>
51*58a2b000SEvgeniy Ivanov #include <sys/stat.h>
52*58a2b000SEvgeniy Ivanov #include <netinet/in.h>
53*58a2b000SEvgeniy Ivanov #include <netinet/udp.h>
54*58a2b000SEvgeniy Ivanov #include <netinet/in_systm.h>
55*58a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h>
56*58a2b000SEvgeniy Ivanov 
57*58a2b000SEvgeniy Ivanov #include "stand.h"
58*58a2b000SEvgeniy Ivanov #include "net.h"
59*58a2b000SEvgeniy Ivanov 
60*58a2b000SEvgeniy Ivanov #include "tftp.h"
61*58a2b000SEvgeniy Ivanov 
62*58a2b000SEvgeniy Ivanov extern struct in_addr servip;
63*58a2b000SEvgeniy Ivanov 
64*58a2b000SEvgeniy Ivanov static int      tftpport = 2000;
65*58a2b000SEvgeniy Ivanov 
66*58a2b000SEvgeniy Ivanov #define RSPACE 520		/* max data packet, rounded up */
67*58a2b000SEvgeniy Ivanov 
68*58a2b000SEvgeniy Ivanov struct tftp_handle {
69*58a2b000SEvgeniy Ivanov 	struct iodesc  *iodesc;
70*58a2b000SEvgeniy Ivanov 	int             currblock;	/* contents of lastdata */
71*58a2b000SEvgeniy Ivanov 	int             islastblock;	/* flag */
72*58a2b000SEvgeniy Ivanov 	int             validsize;
73*58a2b000SEvgeniy Ivanov 	int             off;
74*58a2b000SEvgeniy Ivanov 	const char     *path;	/* saved for re-requests */
75*58a2b000SEvgeniy Ivanov 	struct {
76*58a2b000SEvgeniy Ivanov 		u_char header[UDP_TOTAL_HEADER_SIZE];
77*58a2b000SEvgeniy Ivanov 		struct tftphdr t;
78*58a2b000SEvgeniy Ivanov 		u_char space[RSPACE];
79*58a2b000SEvgeniy Ivanov 	} lastdata;
80*58a2b000SEvgeniy Ivanov };
81*58a2b000SEvgeniy Ivanov 
82*58a2b000SEvgeniy Ivanov static const int tftperrors[8] = {
83*58a2b000SEvgeniy Ivanov 	0,			/* ??? */
84*58a2b000SEvgeniy Ivanov 	ENOENT,
85*58a2b000SEvgeniy Ivanov 	EPERM,
86*58a2b000SEvgeniy Ivanov 	ENOSPC,
87*58a2b000SEvgeniy Ivanov 	EINVAL,			/* ??? */
88*58a2b000SEvgeniy Ivanov 	EINVAL,			/* ??? */
89*58a2b000SEvgeniy Ivanov 	EEXIST,
90*58a2b000SEvgeniy Ivanov 	EINVAL,			/* ??? */
91*58a2b000SEvgeniy Ivanov };
92*58a2b000SEvgeniy Ivanov 
93*58a2b000SEvgeniy Ivanov static ssize_t recvtftp(struct iodesc *, void *, size_t, saseconds_t);
94*58a2b000SEvgeniy Ivanov static int tftp_makereq(struct tftp_handle *);
95*58a2b000SEvgeniy Ivanov static int tftp_getnextblock(struct tftp_handle *);
96*58a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
97*58a2b000SEvgeniy Ivanov static void tftp_terminate(struct tftp_handle *);
98*58a2b000SEvgeniy Ivanov #endif
99*58a2b000SEvgeniy Ivanov static ssize_t tftp_size_of_file(struct tftp_handle *tftpfile);
100*58a2b000SEvgeniy Ivanov 
101*58a2b000SEvgeniy Ivanov static ssize_t
102*58a2b000SEvgeniy Ivanov recvtftp(struct iodesc *d, void *pkt, size_t len, saseconds_t tleft)
103*58a2b000SEvgeniy Ivanov {
104*58a2b000SEvgeniy Ivanov 	ssize_t n;
105*58a2b000SEvgeniy Ivanov 	struct tftphdr *t;
106*58a2b000SEvgeniy Ivanov 
107*58a2b000SEvgeniy Ivanov 	errno = 0;
108*58a2b000SEvgeniy Ivanov 
109*58a2b000SEvgeniy Ivanov 	n = readudp(d, pkt, len, tleft);
110*58a2b000SEvgeniy Ivanov 
111*58a2b000SEvgeniy Ivanov 	if (n < 4)
112*58a2b000SEvgeniy Ivanov 		return -1;
113*58a2b000SEvgeniy Ivanov 
114*58a2b000SEvgeniy Ivanov 	t = (struct tftphdr *)pkt;
115*58a2b000SEvgeniy Ivanov 	switch (ntohs(t->th_opcode)) {
116*58a2b000SEvgeniy Ivanov 	case DATA:
117*58a2b000SEvgeniy Ivanov 		if (htons(t->th_block) != d->xid) {
118*58a2b000SEvgeniy Ivanov 			/*
119*58a2b000SEvgeniy Ivanov 			 * Expected block?
120*58a2b000SEvgeniy Ivanov 			 */
121*58a2b000SEvgeniy Ivanov 			return -1;
122*58a2b000SEvgeniy Ivanov 		}
123*58a2b000SEvgeniy Ivanov 		if (d->xid == 1) {
124*58a2b000SEvgeniy Ivanov 			/*
125*58a2b000SEvgeniy Ivanov 			 * First data packet from new port.
126*58a2b000SEvgeniy Ivanov 			 */
127*58a2b000SEvgeniy Ivanov 			struct udphdr *uh;
128*58a2b000SEvgeniy Ivanov 			uh = (struct udphdr *)pkt - 1;
129*58a2b000SEvgeniy Ivanov 			d->destport = uh->uh_sport;
130*58a2b000SEvgeniy Ivanov 		} /* else check uh_sport has not changed??? */
131*58a2b000SEvgeniy Ivanov 		return (n - (t->th_data - (char *)t));
132*58a2b000SEvgeniy Ivanov 	case ERROR:
133*58a2b000SEvgeniy Ivanov 		if ((unsigned int)ntohs(t->th_code) >= 8) {
134*58a2b000SEvgeniy Ivanov 			printf("illegal tftp error %d\n", ntohs(t->th_code));
135*58a2b000SEvgeniy Ivanov 			errno = EIO;
136*58a2b000SEvgeniy Ivanov 		} else {
137*58a2b000SEvgeniy Ivanov #ifdef DEBUG
138*58a2b000SEvgeniy Ivanov 			printf("tftp-error %d\n", ntohs(t->th_code));
139*58a2b000SEvgeniy Ivanov #endif
140*58a2b000SEvgeniy Ivanov 			errno = tftperrors[ntohs(t->th_code)];
141*58a2b000SEvgeniy Ivanov 		}
142*58a2b000SEvgeniy Ivanov 		return -1;
143*58a2b000SEvgeniy Ivanov 	default:
144*58a2b000SEvgeniy Ivanov #ifdef DEBUG
145*58a2b000SEvgeniy Ivanov 		printf("tftp type %d not handled\n", ntohs(t->th_opcode));
146*58a2b000SEvgeniy Ivanov #endif
147*58a2b000SEvgeniy Ivanov 		return -1;
148*58a2b000SEvgeniy Ivanov 	}
149*58a2b000SEvgeniy Ivanov }
150*58a2b000SEvgeniy Ivanov 
151*58a2b000SEvgeniy Ivanov /* send request, expect first block (or error) */
152*58a2b000SEvgeniy Ivanov static int
153*58a2b000SEvgeniy Ivanov tftp_makereq(struct tftp_handle *h)
154*58a2b000SEvgeniy Ivanov {
155*58a2b000SEvgeniy Ivanov 	struct {
156*58a2b000SEvgeniy Ivanov 		u_char header[UDP_TOTAL_HEADER_SIZE];
157*58a2b000SEvgeniy Ivanov 		struct tftphdr t;
158*58a2b000SEvgeniy Ivanov 		u_char space[FNAME_SIZE + 6];
159*58a2b000SEvgeniy Ivanov 	} wbuf;
160*58a2b000SEvgeniy Ivanov 	char           *wtail;
161*58a2b000SEvgeniy Ivanov 	int             l;
162*58a2b000SEvgeniy Ivanov 	ssize_t         res;
163*58a2b000SEvgeniy Ivanov 	struct tftphdr *t;
164*58a2b000SEvgeniy Ivanov 
165*58a2b000SEvgeniy Ivanov 	wbuf.t.th_opcode = htons((u_short)RRQ);
166*58a2b000SEvgeniy Ivanov 	wtail = wbuf.t.th_stuff;
167*58a2b000SEvgeniy Ivanov 	l = strlen(h->path);
168*58a2b000SEvgeniy Ivanov 	(void)memcpy(wtail, h->path, l + 1);
169*58a2b000SEvgeniy Ivanov 	wtail += l + 1;
170*58a2b000SEvgeniy Ivanov 	(void)memcpy(wtail, "octet", 6);
171*58a2b000SEvgeniy Ivanov 	wtail += 6;
172*58a2b000SEvgeniy Ivanov 
173*58a2b000SEvgeniy Ivanov 	t = &h->lastdata.t;
174*58a2b000SEvgeniy Ivanov 
175*58a2b000SEvgeniy Ivanov 	/* h->iodesc->myport = htons(--tftpport); */
176*58a2b000SEvgeniy Ivanov 	h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
177*58a2b000SEvgeniy Ivanov 	h->iodesc->destport = htons(IPPORT_TFTP);
178*58a2b000SEvgeniy Ivanov 	h->iodesc->xid = 1;	/* expected block */
179*58a2b000SEvgeniy Ivanov 
180*58a2b000SEvgeniy Ivanov 	res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
181*58a2b000SEvgeniy Ivanov 		       recvtftp, t, sizeof(*t) + RSPACE);
182*58a2b000SEvgeniy Ivanov 
183*58a2b000SEvgeniy Ivanov 	if (res == -1)
184*58a2b000SEvgeniy Ivanov 		return errno;
185*58a2b000SEvgeniy Ivanov 
186*58a2b000SEvgeniy Ivanov 	h->currblock = 1;
187*58a2b000SEvgeniy Ivanov 	h->validsize = res;
188*58a2b000SEvgeniy Ivanov 	h->islastblock = 0;
189*58a2b000SEvgeniy Ivanov 	if (res < SEGSIZE)
190*58a2b000SEvgeniy Ivanov 		h->islastblock = 1;	/* very short file */
191*58a2b000SEvgeniy Ivanov 	return 0;
192*58a2b000SEvgeniy Ivanov }
193*58a2b000SEvgeniy Ivanov 
194*58a2b000SEvgeniy Ivanov /* ack block, expect next */
195*58a2b000SEvgeniy Ivanov static int
196*58a2b000SEvgeniy Ivanov tftp_getnextblock(struct tftp_handle *h)
197*58a2b000SEvgeniy Ivanov {
198*58a2b000SEvgeniy Ivanov 	struct {
199*58a2b000SEvgeniy Ivanov 		u_char header[UDP_TOTAL_HEADER_SIZE];
200*58a2b000SEvgeniy Ivanov 		struct tftphdr t;
201*58a2b000SEvgeniy Ivanov 	} wbuf;
202*58a2b000SEvgeniy Ivanov 	char           *wtail;
203*58a2b000SEvgeniy Ivanov 	int             res;
204*58a2b000SEvgeniy Ivanov 	struct tftphdr *t;
205*58a2b000SEvgeniy Ivanov 
206*58a2b000SEvgeniy Ivanov 	wbuf.t.th_opcode = htons((u_short)ACK);
207*58a2b000SEvgeniy Ivanov 	wbuf.t.th_block = htons((u_short)h->currblock);
208*58a2b000SEvgeniy Ivanov 	wtail = (char *)&wbuf.t.th_data;
209*58a2b000SEvgeniy Ivanov 
210*58a2b000SEvgeniy Ivanov 	t = &h->lastdata.t;
211*58a2b000SEvgeniy Ivanov 
212*58a2b000SEvgeniy Ivanov 	h->iodesc->xid = h->currblock + 1;	/* expected block */
213*58a2b000SEvgeniy Ivanov 
214*58a2b000SEvgeniy Ivanov 	res = sendrecv(h->iodesc, sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
215*58a2b000SEvgeniy Ivanov 		       recvtftp, t, sizeof(*t) + RSPACE);
216*58a2b000SEvgeniy Ivanov 
217*58a2b000SEvgeniy Ivanov 	if (res == -1)		/* 0 is OK! */
218*58a2b000SEvgeniy Ivanov 		return errno;
219*58a2b000SEvgeniy Ivanov 
220*58a2b000SEvgeniy Ivanov 	h->currblock++;
221*58a2b000SEvgeniy Ivanov 	h->validsize = res;
222*58a2b000SEvgeniy Ivanov 	if (res < SEGSIZE)
223*58a2b000SEvgeniy Ivanov 		h->islastblock = 1;	/* EOF */
224*58a2b000SEvgeniy Ivanov 	return 0;
225*58a2b000SEvgeniy Ivanov }
226*58a2b000SEvgeniy Ivanov 
227*58a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
228*58a2b000SEvgeniy Ivanov static void
229*58a2b000SEvgeniy Ivanov tftp_terminate(struct tftp_handle *h)
230*58a2b000SEvgeniy Ivanov {
231*58a2b000SEvgeniy Ivanov 	struct {
232*58a2b000SEvgeniy Ivanov 		u_char header[UDP_TOTAL_HEADER_SIZE];
233*58a2b000SEvgeniy Ivanov 		struct tftphdr t;
234*58a2b000SEvgeniy Ivanov 	} wbuf;
235*58a2b000SEvgeniy Ivanov 	char           *wtail;
236*58a2b000SEvgeniy Ivanov 
237*58a2b000SEvgeniy Ivanov 	wtail = (char *)&wbuf.t.th_data;
238*58a2b000SEvgeniy Ivanov 	if (h->islastblock) {
239*58a2b000SEvgeniy Ivanov 		wbuf.t.th_opcode = htons((u_short)ACK);
240*58a2b000SEvgeniy Ivanov 		wbuf.t.th_block = htons((u_short)h->currblock);
241*58a2b000SEvgeniy Ivanov 	} else {
242*58a2b000SEvgeniy Ivanov 		wbuf.t.th_opcode = htons((u_short)ERROR);
243*58a2b000SEvgeniy Ivanov 		wbuf.t.th_code = htons((u_short)ENOSPACE); /* ??? */
244*58a2b000SEvgeniy Ivanov 		*wtail++ = '\0'; /* empty error string */
245*58a2b000SEvgeniy Ivanov 	}
246*58a2b000SEvgeniy Ivanov 
247*58a2b000SEvgeniy Ivanov 	(void)sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
248*58a2b000SEvgeniy Ivanov }
249*58a2b000SEvgeniy Ivanov #endif
250*58a2b000SEvgeniy Ivanov 
251*58a2b000SEvgeniy Ivanov __compactcall int
252*58a2b000SEvgeniy Ivanov tftp_open(const char *path, struct open_file *f)
253*58a2b000SEvgeniy Ivanov {
254*58a2b000SEvgeniy Ivanov 	struct tftp_handle *tftpfile;
255*58a2b000SEvgeniy Ivanov 	struct iodesc  *io;
256*58a2b000SEvgeniy Ivanov 	int             res;
257*58a2b000SEvgeniy Ivanov 
258*58a2b000SEvgeniy Ivanov 	tftpfile = (struct tftp_handle *)alloc(sizeof(*tftpfile));
259*58a2b000SEvgeniy Ivanov 	if (!tftpfile)
260*58a2b000SEvgeniy Ivanov 		return ENOMEM;
261*58a2b000SEvgeniy Ivanov 
262*58a2b000SEvgeniy Ivanov 	tftpfile->iodesc = io = socktodesc(*(int *)(f->f_devdata));
263*58a2b000SEvgeniy Ivanov 	io->destip = servip;
264*58a2b000SEvgeniy Ivanov 	tftpfile->off = 0;
265*58a2b000SEvgeniy Ivanov 	tftpfile->path = path;	/* XXXXXXX we hope it's static */
266*58a2b000SEvgeniy Ivanov 
267*58a2b000SEvgeniy Ivanov 	res = tftp_makereq(tftpfile);
268*58a2b000SEvgeniy Ivanov 
269*58a2b000SEvgeniy Ivanov 	if (res) {
270*58a2b000SEvgeniy Ivanov 		dealloc(tftpfile, sizeof(*tftpfile));
271*58a2b000SEvgeniy Ivanov 		return res;
272*58a2b000SEvgeniy Ivanov 	}
273*58a2b000SEvgeniy Ivanov 	f->f_fsdata = (void *)tftpfile;
274*58a2b000SEvgeniy Ivanov 	fsmod = "nfs";
275*58a2b000SEvgeniy Ivanov 	return 0;
276*58a2b000SEvgeniy Ivanov }
277*58a2b000SEvgeniy Ivanov 
278*58a2b000SEvgeniy Ivanov __compactcall int
279*58a2b000SEvgeniy Ivanov tftp_read(struct open_file *f, void *addr, size_t size, size_t *resid)
280*58a2b000SEvgeniy Ivanov {
281*58a2b000SEvgeniy Ivanov 	struct tftp_handle *tftpfile;
282*58a2b000SEvgeniy Ivanov #if !defined(LIBSA_NO_TWIDDLE)
283*58a2b000SEvgeniy Ivanov 	static int      tc = 0;
284*58a2b000SEvgeniy Ivanov #endif
285*58a2b000SEvgeniy Ivanov 	tftpfile = (struct tftp_handle *)f->f_fsdata;
286*58a2b000SEvgeniy Ivanov 
287*58a2b000SEvgeniy Ivanov 	while (size > 0) {
288*58a2b000SEvgeniy Ivanov 		int needblock;
289*58a2b000SEvgeniy Ivanov 		size_t count;
290*58a2b000SEvgeniy Ivanov 
291*58a2b000SEvgeniy Ivanov #if !defined(LIBSA_NO_TWIDDLE)
292*58a2b000SEvgeniy Ivanov 		if (!(tc++ % 16))
293*58a2b000SEvgeniy Ivanov 			twiddle();
294*58a2b000SEvgeniy Ivanov #endif
295*58a2b000SEvgeniy Ivanov 
296*58a2b000SEvgeniy Ivanov 		needblock = tftpfile->off / SEGSIZE + 1;
297*58a2b000SEvgeniy Ivanov 
298*58a2b000SEvgeniy Ivanov 		if (tftpfile->currblock > needblock) {	/* seek backwards */
299*58a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
300*58a2b000SEvgeniy Ivanov 			tftp_terminate(tftpfile);
301*58a2b000SEvgeniy Ivanov #endif
302*58a2b000SEvgeniy Ivanov 			tftp_makereq(tftpfile);	/* no error check, it worked
303*58a2b000SEvgeniy Ivanov 			                         * for open */
304*58a2b000SEvgeniy Ivanov 		}
305*58a2b000SEvgeniy Ivanov 
306*58a2b000SEvgeniy Ivanov 		while (tftpfile->currblock < needblock) {
307*58a2b000SEvgeniy Ivanov 			int res;
308*58a2b000SEvgeniy Ivanov 
309*58a2b000SEvgeniy Ivanov 			res = tftp_getnextblock(tftpfile);
310*58a2b000SEvgeniy Ivanov 			if (res) {	/* no answer */
311*58a2b000SEvgeniy Ivanov #ifdef DEBUG
312*58a2b000SEvgeniy Ivanov 				printf("tftp: read error (block %d->%d)\n",
313*58a2b000SEvgeniy Ivanov 				       tftpfile->currblock, needblock);
314*58a2b000SEvgeniy Ivanov #endif
315*58a2b000SEvgeniy Ivanov 				return res;
316*58a2b000SEvgeniy Ivanov 			}
317*58a2b000SEvgeniy Ivanov 			if (tftpfile->islastblock)
318*58a2b000SEvgeniy Ivanov 				break;
319*58a2b000SEvgeniy Ivanov 		}
320*58a2b000SEvgeniy Ivanov 
321*58a2b000SEvgeniy Ivanov 		if (tftpfile->currblock == needblock) {
322*58a2b000SEvgeniy Ivanov 			size_t offinblock, inbuffer;
323*58a2b000SEvgeniy Ivanov 
324*58a2b000SEvgeniy Ivanov 			offinblock = tftpfile->off % SEGSIZE;
325*58a2b000SEvgeniy Ivanov 
326*58a2b000SEvgeniy Ivanov 			if (offinblock > tftpfile->validsize) {
327*58a2b000SEvgeniy Ivanov #ifdef DEBUG
328*58a2b000SEvgeniy Ivanov 				printf("tftp: invalid offset %d\n",
329*58a2b000SEvgeniy Ivanov 				    tftpfile->off);
330*58a2b000SEvgeniy Ivanov #endif
331*58a2b000SEvgeniy Ivanov 				return EINVAL;
332*58a2b000SEvgeniy Ivanov 			}
333*58a2b000SEvgeniy Ivanov 			inbuffer = tftpfile->validsize - offinblock;
334*58a2b000SEvgeniy Ivanov 			count = (size < inbuffer ? size : inbuffer);
335*58a2b000SEvgeniy Ivanov 			(void)memcpy(addr,
336*58a2b000SEvgeniy Ivanov 			    tftpfile->lastdata.t.th_data + offinblock,
337*58a2b000SEvgeniy Ivanov 			    count);
338*58a2b000SEvgeniy Ivanov 
339*58a2b000SEvgeniy Ivanov 			addr = (char *)addr + count;
340*58a2b000SEvgeniy Ivanov 			tftpfile->off += count;
341*58a2b000SEvgeniy Ivanov 			size -= count;
342*58a2b000SEvgeniy Ivanov 
343*58a2b000SEvgeniy Ivanov 			if ((tftpfile->islastblock) && (count == inbuffer))
344*58a2b000SEvgeniy Ivanov 				break;	/* EOF */
345*58a2b000SEvgeniy Ivanov 		} else {
346*58a2b000SEvgeniy Ivanov #ifdef DEBUG
347*58a2b000SEvgeniy Ivanov 			printf("tftp: block %d not found\n", needblock);
348*58a2b000SEvgeniy Ivanov #endif
349*58a2b000SEvgeniy Ivanov 			return EINVAL;
350*58a2b000SEvgeniy Ivanov 		}
351*58a2b000SEvgeniy Ivanov 
352*58a2b000SEvgeniy Ivanov 	}
353*58a2b000SEvgeniy Ivanov 
354*58a2b000SEvgeniy Ivanov 	if (resid)
355*58a2b000SEvgeniy Ivanov 		*resid = size;
356*58a2b000SEvgeniy Ivanov 	return 0;
357*58a2b000SEvgeniy Ivanov }
358*58a2b000SEvgeniy Ivanov 
359*58a2b000SEvgeniy Ivanov __compactcall int
360*58a2b000SEvgeniy Ivanov tftp_close(struct open_file *f)
361*58a2b000SEvgeniy Ivanov {
362*58a2b000SEvgeniy Ivanov 	struct tftp_handle *tftpfile;
363*58a2b000SEvgeniy Ivanov 	tftpfile = (struct tftp_handle *)f->f_fsdata;
364*58a2b000SEvgeniy Ivanov 
365*58a2b000SEvgeniy Ivanov #ifdef TFTP_NOTERMINATE
366*58a2b000SEvgeniy Ivanov 	/* let it time out ... */
367*58a2b000SEvgeniy Ivanov #else
368*58a2b000SEvgeniy Ivanov 	tftp_terminate(tftpfile);
369*58a2b000SEvgeniy Ivanov #endif
370*58a2b000SEvgeniy Ivanov 
371*58a2b000SEvgeniy Ivanov 	dealloc(tftpfile, sizeof(*tftpfile));
372*58a2b000SEvgeniy Ivanov 	return 0;
373*58a2b000SEvgeniy Ivanov }
374*58a2b000SEvgeniy Ivanov 
375*58a2b000SEvgeniy Ivanov __compactcall int
376*58a2b000SEvgeniy Ivanov tftp_write(struct open_file *f, void *start, size_t size, size_t *resid)
377*58a2b000SEvgeniy Ivanov {
378*58a2b000SEvgeniy Ivanov 
379*58a2b000SEvgeniy Ivanov 	return EROFS;
380*58a2b000SEvgeniy Ivanov }
381*58a2b000SEvgeniy Ivanov 
382*58a2b000SEvgeniy Ivanov static ssize_t
383*58a2b000SEvgeniy Ivanov tftp_size_of_file(struct tftp_handle *tftpfile)
384*58a2b000SEvgeniy Ivanov {
385*58a2b000SEvgeniy Ivanov 	ssize_t filesize;
386*58a2b000SEvgeniy Ivanov 
387*58a2b000SEvgeniy Ivanov 	if (tftpfile->currblock > 1) {	/* move to start of file */
388*58a2b000SEvgeniy Ivanov #ifndef TFTP_NOTERMINATE
389*58a2b000SEvgeniy Ivanov 		tftp_terminate(tftpfile);
390*58a2b000SEvgeniy Ivanov #endif
391*58a2b000SEvgeniy Ivanov 		tftp_makereq(tftpfile);	/* no error check, it worked
392*58a2b000SEvgeniy Ivanov 		      			 * for open */
393*58a2b000SEvgeniy Ivanov 	}
394*58a2b000SEvgeniy Ivanov 
395*58a2b000SEvgeniy Ivanov 	/* start with the size of block 1 */
396*58a2b000SEvgeniy Ivanov 	filesize = tftpfile->validsize;
397*58a2b000SEvgeniy Ivanov 
398*58a2b000SEvgeniy Ivanov 	/* and keep adding the sizes till we hit the last block */
399*58a2b000SEvgeniy Ivanov 	while (!tftpfile->islastblock) {
400*58a2b000SEvgeniy Ivanov 		int res;
401*58a2b000SEvgeniy Ivanov 
402*58a2b000SEvgeniy Ivanov 		res = tftp_getnextblock(tftpfile);
403*58a2b000SEvgeniy Ivanov 		if (res) {	/* no answer */
404*58a2b000SEvgeniy Ivanov #ifdef DEBUG
405*58a2b000SEvgeniy Ivanov 			printf("tftp: read error (block %d)\n",
406*58a2b000SEvgeniy Ivanov 					tftpfile->currblock);
407*58a2b000SEvgeniy Ivanov #endif
408*58a2b000SEvgeniy Ivanov 			return -1;
409*58a2b000SEvgeniy Ivanov 		}
410*58a2b000SEvgeniy Ivanov 		filesize += tftpfile->validsize;
411*58a2b000SEvgeniy Ivanov 	}
412*58a2b000SEvgeniy Ivanov #ifdef DEBUG
413*58a2b000SEvgeniy Ivanov 	printf("tftp_size_of_file: file is %zu bytes\n", filesize);
414*58a2b000SEvgeniy Ivanov #endif
415*58a2b000SEvgeniy Ivanov 	return filesize;
416*58a2b000SEvgeniy Ivanov }
417*58a2b000SEvgeniy Ivanov 
418*58a2b000SEvgeniy Ivanov __compactcall int
419*58a2b000SEvgeniy Ivanov tftp_stat(struct open_file *f, struct stat *sb)
420*58a2b000SEvgeniy Ivanov {
421*58a2b000SEvgeniy Ivanov 	struct tftp_handle *tftpfile;
422*58a2b000SEvgeniy Ivanov 	tftpfile = (struct tftp_handle *)f->f_fsdata;
423*58a2b000SEvgeniy Ivanov 
424*58a2b000SEvgeniy Ivanov 	sb->st_mode = 0444;
425*58a2b000SEvgeniy Ivanov 	sb->st_nlink = 1;
426*58a2b000SEvgeniy Ivanov 	sb->st_uid = 0;
427*58a2b000SEvgeniy Ivanov 	sb->st_gid = 0;
428*58a2b000SEvgeniy Ivanov 	sb->st_size = tftp_size_of_file(tftpfile);
429*58a2b000SEvgeniy Ivanov 	return 0;
430*58a2b000SEvgeniy Ivanov }
431*58a2b000SEvgeniy Ivanov 
432*58a2b000SEvgeniy Ivanov #if defined(LIBSA_ENABLE_LS_OP)
433*58a2b000SEvgeniy Ivanov __compactcall void
434*58a2b000SEvgeniy Ivanov tftp_ls(struct open_file *f, const char *pattern)
435*58a2b000SEvgeniy Ivanov {
436*58a2b000SEvgeniy Ivanov 	printf("Currently ls command is unsupported by tftp\n");
437*58a2b000SEvgeniy Ivanov 	return;
438*58a2b000SEvgeniy Ivanov }
439*58a2b000SEvgeniy Ivanov #endif
440*58a2b000SEvgeniy Ivanov 
441*58a2b000SEvgeniy Ivanov __compactcall off_t
442*58a2b000SEvgeniy Ivanov tftp_seek(struct open_file *f, off_t offset, int where)
443*58a2b000SEvgeniy Ivanov {
444*58a2b000SEvgeniy Ivanov 	struct tftp_handle *tftpfile;
445*58a2b000SEvgeniy Ivanov 	tftpfile = (struct tftp_handle *)f->f_fsdata;
446*58a2b000SEvgeniy Ivanov 
447*58a2b000SEvgeniy Ivanov 	switch (where) {
448*58a2b000SEvgeniy Ivanov 	case SEEK_SET:
449*58a2b000SEvgeniy Ivanov 		tftpfile->off = offset;
450*58a2b000SEvgeniy Ivanov 		break;
451*58a2b000SEvgeniy Ivanov 	case SEEK_CUR:
452*58a2b000SEvgeniy Ivanov 		tftpfile->off += offset;
453*58a2b000SEvgeniy Ivanov 		break;
454*58a2b000SEvgeniy Ivanov 	default:
455*58a2b000SEvgeniy Ivanov 		errno = EOFFSET;
456*58a2b000SEvgeniy Ivanov 		return -1;
457*58a2b000SEvgeniy Ivanov 	}
458*58a2b000SEvgeniy Ivanov 	return tftpfile->off;
459*58a2b000SEvgeniy Ivanov }
460