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