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