Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/fs/tftp/fsys_tftp.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /* TFTP "filesystem" driver.  TFTP is specified by RFC 1350. */
0019 
0020 #include "lwip/ip.h"
0021 #include "lwip/netif.h"
0022 #include "lwip/udp.h"
0023 #include "fs/filesys.h"
0024 #include "mem/virtual.h"
0025 #include "mem/physical.h"
0026 #include "kernel.h"
0027 #include "util/debug.h"
0028 #include "util/circular.h"
0029 
0030 #define DEBUG_TFTP
0031 
0032 #define TFTP_PORT 69
0033 #define TFTP_BLOCK_SIZE 512
0034 #define BLOCKS_PER_NODE 7       /* 7 * 512 + 8 < 4096 */
0035 #define TFTP_RING_LEN 4
0036 
0037 /*
0038  *         opcode  operation
0039  *           1     Read request (RRQ)
0040  *           2     Write request (WRQ)
0041  *           3     Data (DATA)
0042  *           4     Acknowledgment (ACK)
0043  *           5     Error (ERROR)
0044  */
0045 
0046 enum {
0047   TFTP_OP_RRQ=1,
0048   TFTP_OP_WRQ,
0049   TFTP_OP_DATA,
0050   TFTP_OP_ACK,
0051   TFTP_OP_ERR
0052 };
0053 
0054 /*
0055  *           2 bytes     string    1 byte     string   1 byte
0056  *           ------------------------------------------------
0057  *          | Opcode |  Filename  |   0  |    Mode    |   0  |
0058  *           ------------------------------------------------
0059  *
0060  *                      Figure 5-1: RRQ/WRQ packet
0061  */
0062 
0063 /*
0064  *                  2 bytes     2 bytes      n bytes
0065  *                  ----------------------------------
0066  *                 | Opcode |   Block #  |   Data     |
0067  *                  ----------------------------------
0068  *
0069  *                       Figure 5-2: DATA packet
0070  *
0071  */
0072 
0073 /*
0074  *                        2 bytes     2 bytes
0075  *                        ---------------------
0076  *                       | Opcode |   Block #  |
0077  *                        ---------------------
0078  *
0079  *                        Figure 5-3: ACK packet
0080  */
0081 
0082 #ifdef DEBUG_TFTP
0083 #define DLOG(fmt,...) DLOG_PREFIX("tftp",fmt,##__VA_ARGS__)
0084 #else
0085 #define DLOG(fmt,...) ;
0086 #endif
0087 
0088 static struct netif *server_if;
0089 static struct ip_addr server_ip;
0090 static uint16 server_port = TFTP_PORT;
0091 static struct udp_pcb *pcb;
0092 static struct pbuf *incoming_buf[TFTP_RING_LEN];
0093 static circular incoming;
0094 
0095 struct _blocklist {
0096   struct _blocklist *next;
0097   uint32 len, start;
0098   uint8 blocks[TFTP_BLOCK_SIZE * BLOCKS_PER_NODE];
0099 };
0100 typedef struct _blocklist blocklist_t;
0101 
0102 blocklist_t *curbuf, *curend;
0103 
0104 /* format a read request */
0105 static int
0106 format_rrq (uint8 *buf, int len, const char *filename)
0107 {
0108   int filename_len = strlen (filename);
0109   if (2+filename_len+1+6 > len)
0110     return 0;
0111   memset (buf, 0, len);
0112   buf[1] = TFTP_OP_RRQ;
0113   memcpy (buf+2, filename, filename_len);
0114   buf[2+filename_len] = 0;
0115   memcpy (buf+2+filename_len+1, "octet\0", 6); /* mode=octet */
0116   return 2+filename_len+1+6;
0117 }
0118 
0119 static int
0120 send (uint8 *buf, uint32 len)
0121 {
0122   struct pbuf *p;
0123 
0124   /* assume gateway has TFTP server */
0125   server_ip.addr = server_if->gw.addr;
0126   if (server_ip.addr == 0) {
0127     DLOG ("no server_ip");
0128     return -1;
0129   }
0130 
0131   p = pbuf_alloc (PBUF_TRANSPORT, len, PBUF_RAM);
0132 
0133   if (!p) {
0134     DLOG ("pbuf_alloc");
0135     return -1;
0136   }
0137 
0138   if (pbuf_take (p, buf, len) != ERR_OK) {
0139     pbuf_free (p);
0140     DLOG ("pbuf_take");
0141     return -1;
0142   }
0143 
0144   if (udp_sendto (pcb, p, &server_ip, server_port) != ERR_OK) {
0145     pbuf_free (p);
0146     DLOG ("udp_sendto");
0147     return -1;
0148   }
0149 
0150   pbuf_free (p);
0151 
0152   return len;
0153 }
0154 
0155 static blocklist_t *
0156 alloc_node (void)
0157 {
0158   uint32 frame = alloc_phys_frame ();
0159   if (frame == -1) return NULL;
0160   return map_virtual_page (frame | 3);
0161 }
0162 
0163 static void
0164 free_node (blocklist_t *bl)
0165 {
0166   uint32 frame = (uint32) get_phys_addr (bl);
0167   unmap_virtual_page (bl);
0168   free_phys_frame (frame);
0169 }
0170 
0171 #define NODE_CAPACITY (BLOCKS_PER_NODE * TFTP_BLOCK_SIZE)
0172 static void
0173 cache (uint8 *buf, uint32 len)
0174 {
0175   while (len > 0) {
0176     if (curend && curend->len < NODE_CAPACITY) {
0177       /* can use existing node */
0178       uint32 rem = NODE_CAPACITY - curend->len;
0179       uint32 amount = len < rem ? len : rem;
0180 
0181       memcpy (&curend->blocks[curend->len], buf, amount);
0182       curend->len += amount;
0183       len -= amount;
0184       buf += amount;
0185     } else {
0186       /* need new node */
0187       blocklist_t *n = alloc_node ();
0188       memset (n, 0, sizeof (blocklist_t));
0189       n->next = NULL;
0190       n->len = len < NODE_CAPACITY ? len : NODE_CAPACITY;
0191       memcpy (n->blocks, buf, n->len);
0192       if (curend) {
0193         /* append to current end of list */
0194         curend->next = n;
0195         curend = curend->next;
0196       } else
0197         /* starting new list */
0198         curbuf = curend = n;
0199       len -= n->len;
0200       buf += n->len;
0201     }
0202   }
0203 }
0204 
0205 static void
0206 free_cache (void)
0207 {
0208   blocklist_t *bl;
0209   if (curbuf) {
0210     for (bl = curbuf; curbuf;) {
0211       bl = bl->next;
0212       free_node (curbuf);
0213       curbuf = bl;
0214     }
0215   }
0216   curend = curbuf = NULL;
0217 }
0218 
0219 int
0220 eztftp_dir (char *pathname)
0221 {
0222   struct pbuf *p, *q;
0223   uint8 buf[TFTP_BLOCK_SIZE+4], *ins;
0224   uint32 len, rem, filesize=0;
0225 
0226   circular_init (&incoming, incoming_buf,
0227                  TFTP_RING_LEN, sizeof (struct pbuf *));
0228 
0229   DLOG ("dir (%s)", pathname);
0230   if (pathname[0] == '/')
0231     /* some servers don't like leading slash */
0232     pathname++;
0233 
0234   if (curbuf) free_cache ();
0235 
0236   /* format and send a read request */
0237   len = format_rrq (buf, TFTP_BLOCK_SIZE+4, pathname);
0238   server_port = TFTP_PORT;
0239   if (send (buf, len) < 0) {
0240     DLOG ("failed to send request: %d %s", *((u16 *) buf), buf+2);
0241     return -1;
0242   }
0243 
0244   /* fetch file loop */
0245   rem = TFTP_BLOCK_SIZE+4; len=0; ins=buf;
0246   for (;;) {
0247     /* wait for incoming data */
0248     circular_remove (&incoming, &p);
0249     if (!p) continue;
0250 
0251     while (rem > 0) {
0252       /* copy data from current pbuf */
0253       memcpy (ins, p->payload, p->len);
0254 
0255       /* adjust counters */
0256       rem -= p->len;
0257       ins += p->len;
0258       len += p->len;
0259 
0260       /* chop pbuf off */
0261       q = p->next;
0262       if (q) pbuf_ref (q);
0263       pbuf_free (p);
0264       p = q;
0265 
0266       /* if last pbuf in chain */
0267       if (!p) break;
0268     }
0269 
0270     /* check for valid DATA packet */
0271     if (buf[1] == TFTP_OP_DATA) {
0272       DLOG ("received DATA packet num=0x%.04X len=%d bytes",
0273             (buf[2] << 8) | buf[3], len-4);
0274       /* send ACK the easy way */
0275       buf[1] = TFTP_OP_ACK;
0276       send (buf, 4);
0277       /* now put the data on our cached pbuf chain */
0278       cache (buf+4, len-4);
0279     } else if (buf[1] == TFTP_OP_ERR) {
0280       /* got error, probably file not found */
0281       DLOG ("error code=%d str=%s", (buf[2] << 8) | buf[3], &buf[4]);
0282       return -1;
0283     } else {
0284       /* discard buffer */
0285       DLOG ("received unexpected packet opcode=%d", buf[1]);
0286     }
0287 
0288     filesize += len-4;
0289 
0290     if (len - 4 < TFTP_BLOCK_SIZE)
0291       /* that was the last packet */
0292       break;
0293 
0294     /* reset buffer */
0295     rem = TFTP_BLOCK_SIZE+4; len=0; ins=buf;
0296   }
0297 
0298   DLOG ("opened file size=%d bytes", filesize);
0299   return filesize;
0300 }
0301 
0302 int
0303 eztftp_read (char *buf, int len)
0304 {
0305   char *ptr = buf;
0306   int actual = 0;
0307   DLOG ("read (%p, %d)", buf, len);
0308   while (len > 0 && curbuf) {
0309     int amount = len < curbuf->len ? len : curbuf->len;
0310 
0311     /* copy data from current node */
0312     memcpy (ptr, &curbuf->blocks[curbuf->start], amount);
0313     ptr += amount;
0314     actual += amount;
0315     curbuf->start += amount;
0316     len -= amount;
0317     curbuf->len -= amount;
0318 
0319     /* check if current node is empty */
0320     if (curbuf->len <= 0) {
0321       blocklist_t *n = curbuf->next;
0322       free_node (curbuf);
0323       curbuf = n;
0324       if (curbuf == NULL)
0325         curend = NULL;
0326     }
0327   }
0328 
0329   return actual;
0330 }
0331 
0332 static void
0333 recv_callback (void *arg, struct udp_pcb *pcb, struct pbuf *p,
0334                struct ip_addr *addr, uint16 port)
0335 {
0336   /* we get the pbuf to keep */
0337   DLOG ("recv_callback: addr=%p port=0x%.04X p->tot_len=%d bytes",
0338         addr->addr, port, p->tot_len);
0339   server_port = port;
0340   circular_insert_nowait (&incoming, &p);
0341 }
0342 
0343 bool
0344 eztftp_mount (char *ifname)
0345 {
0346   DLOG ("mount %s", ifname);
0347   circular_init (&incoming, incoming_buf,
0348                  TFTP_RING_LEN, sizeof (struct pbuf *));
0349   server_if = netif_find (ifname);
0350   if (server_if == NULL) return FALSE;
0351   server_ip.addr = 0;
0352   pcb = udp_new ();
0353   if (!pcb) return FALSE;
0354   if (udp_bind (pcb, IP_ADDR_ANY, 0) != ERR_OK)
0355     return FALSE;
0356   udp_recv (pcb, recv_callback, NULL);
0357   return TRUE;
0358 }
0359 
0360 /*
0361  * Local Variables:
0362  * indent-tabs-mode: nil
0363  * mode: C
0364  * c-file-style: "gnu"
0365  * c-basic-offset: 2
0366  * End:
0367  */
0368 
0369 /* vi: set et sw=2 sts=2: */