xref: /minix3/sys/lib/libsa/tftp.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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