123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- #ifdef DOWNLOAD_PROTO_NFS
-
- #include "etherboot.h"
- #include "nic.h"
-
- /* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
- * large portions are copied verbatim) as distributed in OSKit 0.97. A few
- * changes were necessary to adapt the code to Etherboot and to fix several
- * inconsistencies. Also the RPC message preparation is done "by hand" to
- * avoid adding netsprintf() which I find hard to understand and use. */
-
- /* NOTE 2: Etherboot does not care about things beyond the kernel image, so
- * it loads the kernel image off the boot server (ARP_SERVER) and does not
- * access the client root disk (root-path in dhcpd.conf), which would use
- * ARP_ROOTSERVER. The root disk is something the operating system we are
- * about to load needs to use. This is different from the OSKit 0.97 logic. */
-
- /* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
- * If a symlink is encountered, it is followed as far as possible (recursion
- * possible, maximum 16 steps). There is no clearing of ".."'s inside the
- * path, so please DON'T DO THAT. thx. */
-
- #define START_OPORT 700 /* mountd usually insists on secure ports */
- #define OPORT_SWEEP 200 /* make sure we don't leave secure range */
-
- static int oport = START_OPORT;
- static int mount_port = -1;
- static int nfs_port = -1;
- static int fs_mounted = 0;
- static unsigned long rpc_id;
-
- /**************************************************************************
- RPC_INIT - set up the ID counter to something fairly random
- **************************************************************************/
- void rpc_init(void)
- {
- unsigned long t;
-
- t = currticks();
- rpc_id = t ^ (t << 8) ^ (t << 16);
- }
-
-
- /**************************************************************************
- RPC_PRINTERROR - Print a low level RPC error message
- **************************************************************************/
- static void rpc_printerror(struct rpc_t *rpc)
- {
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus) {
- /* rpc_printerror() is called for any RPC related error,
- * suppress output if no low level RPC error happened. */
- printf("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
- ntohl(rpc->u.reply.verifier),
- ntohl(rpc->u.reply.astatus));
- }
- }
-
- /**************************************************************************
- AWAIT_RPC - Wait for an rpc packet
- **************************************************************************/
- static int await_rpc(int ival, void *ptr,
- unsigned short ptype, struct iphdr *ip, struct udphdr *udp)
- {
- struct rpc_t *rpc;
- if (!udp)
- return 0;
- if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
- return 0;
- if (ntohs(udp->dest) != ival)
- return 0;
- if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
- return 0;
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
- return 0;
- if (MSG_REPLY != ntohl(rpc->u.reply.type))
- return 0;
- return 1;
- }
-
- /**************************************************************************
- RPC_LOOKUP - Lookup RPC Port numbers
- **************************************************************************/
- static int rpc_lookup(int addr, int prog, int ver, int sport)
- {
- struct rpc_t buf, *rpc;
- unsigned long id;
- int retries;
- long *p;
-
- id = rpc_id++;
- buf.u.call.id = htonl(id);
- buf.u.call.type = htonl(MSG_CALL);
- buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
- buf.u.call.prog = htonl(PROG_PORTMAP);
- buf.u.call.vers = htonl(2); /* portmapper is version 2 */
- buf.u.call.proc = htonl(PORTMAP_GETPORT);
- p = (long *)buf.u.call.data;
- *p++ = 0; *p++ = 0; /* auth credential */
- *p++ = 0; *p++ = 0; /* auth verifier */
- *p++ = htonl(prog);
- *p++ = htonl(ver);
- *p++ = htonl(IP_UDP);
- *p++ = 0;
- for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
- long timeout;
- udp_transmit(arptable[addr].ipaddr.s_addr, sport, SUNRPC_PORT,
- (char *)p - (char *)&buf, &buf);
- timeout = rfc2131_sleep_interval(TIMEOUT, retries);
- if (await_reply(await_rpc, sport, &id, timeout)) {
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus) {
- rpc_printerror(rpc);
- return -1;
- } else {
- return ntohl(rpc->u.reply.data[0]);
- }
- }
- }
- return -1;
- }
-
- /**************************************************************************
- RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
- **************************************************************************/
- static long *rpc_add_credentials(long *p)
- {
- int hl;
-
- /* Here's the executive summary on authentication requirements of the
- * various NFS server implementations: Linux accepts both AUTH_NONE
- * and AUTH_UNIX authentication (also accepts an empty hostname field
- * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
- * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
- * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
- * it (if the BOOTP/DHCP reply didn't give one, just use an empty
- * hostname). */
-
- hl = (hostnamelen + 3) & ~3;
-
- /* Provide an AUTH_UNIX credential. */
- *p++ = htonl(1); /* AUTH_UNIX */
- *p++ = htonl(hl+20); /* auth length */
- *p++ = htonl(0); /* stamp */
- *p++ = htonl(hostnamelen); /* hostname string */
- if (hostnamelen & 3) {
- *(p + hostnamelen / 4) = 0; /* add zero padding */
- }
- memcpy(p, hostname, hostnamelen);
- p += hl / 4;
- *p++ = 0; /* uid */
- *p++ = 0; /* gid */
- *p++ = 0; /* auxiliary gid list */
-
- /* Provide an AUTH_NONE verifier. */
- *p++ = 0; /* AUTH_NONE */
- *p++ = 0; /* auth length */
-
- return p;
- }
-
- /**************************************************************************
- NFS_PRINTERROR - Print a NFS error message
- **************************************************************************/
- static void nfs_printerror(int err)
- {
- switch (-err) {
- case NFSERR_PERM:
- printf("Not owner\n");
- break;
- case NFSERR_NOENT:
- printf("No such file or directory\n");
- break;
- case NFSERR_ACCES:
- printf("Permission denied\n");
- break;
- case NFSERR_ISDIR:
- printf("Directory given where filename expected\n");
- break;
- case NFSERR_INVAL:
- printf("Invalid filehandle\n");
- break; // INVAL is not defined in NFSv2, some NFS-servers
- // seem to use it in answers to v2 nevertheless.
- case 9998:
- printf("low-level RPC failure (parameter decoding problem?)\n");
- break;
- case 9999:
- printf("low-level RPC failure (authentication problem?)\n");
- break;
- default:
- printf("Unknown NFS error %d\n", -err);
- }
- }
-
- /**************************************************************************
- NFS_MOUNT - Mount an NFS Filesystem
- **************************************************************************/
- static int nfs_mount(int server, int port, char *path, char *fh, int sport)
- {
- struct rpc_t buf, *rpc;
- unsigned long id;
- int retries;
- long *p;
- int pathlen = strlen(path);
-
- id = rpc_id++;
- buf.u.call.id = htonl(id);
- buf.u.call.type = htonl(MSG_CALL);
- buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
- buf.u.call.prog = htonl(PROG_MOUNT);
- buf.u.call.vers = htonl(1); /* mountd is version 1 */
- buf.u.call.proc = htonl(MOUNT_ADDENTRY);
- p = rpc_add_credentials((long *)buf.u.call.data);
- *p++ = htonl(pathlen);
- if (pathlen & 3) {
- *(p + pathlen / 4) = 0; /* add zero padding */
- }
- memcpy(p, path, pathlen);
- p += (pathlen + 3) / 4;
- for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
- long timeout;
- udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
- (char *)p - (char *)&buf, &buf);
- timeout = rfc2131_sleep_interval(TIMEOUT, retries);
- if (await_reply(await_rpc, sport, &id, timeout)) {
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus || rpc->u.reply.data[0]) {
- rpc_printerror(rpc);
- if (rpc->u.reply.rstatus) {
- /* RPC failed, no verifier, data[0] */
- return -9999;
- }
- if (rpc->u.reply.astatus) {
- /* RPC couldn't decode parameters */
- return -9998;
- }
- return -ntohl(rpc->u.reply.data[0]);
- } else {
- fs_mounted = 1;
- memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
- return 0;
- }
- }
- }
- return -1;
- }
-
- /**************************************************************************
- NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
- **************************************************************************/
- void nfs_umountall(int server)
- {
- struct rpc_t buf, *rpc;
- unsigned long id;
- int retries;
- long *p;
-
- if (!arptable[server].ipaddr.s_addr) {
- /* Haven't sent a single UDP packet to this server */
- return;
- }
- if ((mount_port == -1) || (!fs_mounted)) {
- /* Nothing mounted, nothing to umount */
- return;
- }
- id = rpc_id++;
- buf.u.call.id = htonl(id);
- buf.u.call.type = htonl(MSG_CALL);
- buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
- buf.u.call.prog = htonl(PROG_MOUNT);
- buf.u.call.vers = htonl(1); /* mountd is version 1 */
- buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
- p = rpc_add_credentials((long *)buf.u.call.data);
- for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
- long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
- udp_transmit(arptable[server].ipaddr.s_addr, oport, mount_port,
- (char *)p - (char *)&buf, &buf);
- if (await_reply(await_rpc, oport, &id, timeout)) {
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus) {
- rpc_printerror(rpc);
- }
- fs_mounted = 0;
- return;
- }
- }
- }
- /***************************************************************************
- * NFS_READLINK (AH 2003-07-14)
- * This procedure is called when read of the first block fails -
- * this probably happens when it's a directory or a symlink
- * In case of successful readlink(), the dirname is manipulated,
- * so that inside the nfs() function a recursion can be done.
- **************************************************************************/
- static int nfs_readlink(int server, int port, char *fh, char *path, char *nfh,
- int sport)
- {
- struct rpc_t buf, *rpc;
- unsigned long id;
- long *p;
- int retries;
- int pathlen = strlen(path);
-
- id = rpc_id++;
- buf.u.call.id = htonl(id);
- buf.u.call.type = htonl(MSG_CALL);
- buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
- buf.u.call.prog = htonl(PROG_NFS);
- buf.u.call.vers = htonl(2); /* nfsd is version 2 */
- buf.u.call.proc = htonl(NFS_READLINK);
- p = rpc_add_credentials((long *)buf.u.call.data);
- memcpy(p, nfh, NFS_FHSIZE);
- p += (NFS_FHSIZE / 4);
- for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
- long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
- udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
- (char *)p - (char *)&buf, &buf);
- if (await_reply(await_rpc, sport, &id, timeout)) {
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus || rpc->u.reply.data[0]) {
- rpc_printerror(rpc);
- if (rpc->u.reply.rstatus) {
- /* RPC failed, no verifier, data[0] */
- return -9999;
- }
- if (rpc->u.reply.astatus) {
- /* RPC couldn't decode parameters */
- return -9998;
- }
- return -ntohl(rpc->u.reply.data[0]);
- } else {
- // It *is* a link.
- // If it's a relative link, append everything to dirname, filename TOO!
- retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
- if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
- path[pathlen++] = '/';
- while ( ( retries + pathlen ) > 298 ) {
- retries--;
- }
- if ( retries > 0 ) {
- memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
- } else { retries = 0; }
- path[pathlen + retries] = 0;
- } else {
- // Else make it the only path.
- if ( retries > 298 ) { retries = 298; }
- memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
- path[retries] = 0;
- }
- return 0;
- }
- }
- }
- return -1;
- }
- /**************************************************************************
- NFS_LOOKUP - Lookup Pathname
- **************************************************************************/
- static int nfs_lookup(int server, int port, char *fh, char *path, char *nfh,
- int sport)
- {
- struct rpc_t buf, *rpc;
- unsigned long id;
- long *p;
- int retries;
- int pathlen = strlen(path);
-
- id = rpc_id++;
- buf.u.call.id = htonl(id);
- buf.u.call.type = htonl(MSG_CALL);
- buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
- buf.u.call.prog = htonl(PROG_NFS);
- buf.u.call.vers = htonl(2); /* nfsd is version 2 */
- buf.u.call.proc = htonl(NFS_LOOKUP);
- p = rpc_add_credentials((long *)buf.u.call.data);
- memcpy(p, fh, NFS_FHSIZE);
- p += (NFS_FHSIZE / 4);
- *p++ = htonl(pathlen);
- if (pathlen & 3) {
- *(p + pathlen / 4) = 0; /* add zero padding */
- }
- memcpy(p, path, pathlen);
- p += (pathlen + 3) / 4;
- for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
- long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
- udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
- (char *)p - (char *)&buf, &buf);
- if (await_reply(await_rpc, sport, &id, timeout)) {
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus || rpc->u.reply.data[0]) {
- rpc_printerror(rpc);
- if (rpc->u.reply.rstatus) {
- /* RPC failed, no verifier, data[0] */
- return -9999;
- }
- if (rpc->u.reply.astatus) {
- /* RPC couldn't decode parameters */
- return -9998;
- }
- return -ntohl(rpc->u.reply.data[0]);
- } else {
- memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
- return 0;
- }
- }
- }
- return -1;
- }
-
- /**************************************************************************
- NFS_READ - Read File on NFS Server
- **************************************************************************/
- static int nfs_read(int server, int port, char *fh, int offset, int len,
- int sport)
- {
- struct rpc_t buf, *rpc;
- unsigned long id;
- int retries;
- long *p;
-
- static int tokens=0;
- /*
- * Try to implement something similar to a window protocol in
- * terms of response to losses. On successful receive, increment
- * the number of tokens by 1 (cap at 256). On failure, halve it.
- * When the number of tokens is >= 2, use a very short timeout.
- */
-
- id = rpc_id++;
- buf.u.call.id = htonl(id);
- buf.u.call.type = htonl(MSG_CALL);
- buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
- buf.u.call.prog = htonl(PROG_NFS);
- buf.u.call.vers = htonl(2); /* nfsd is version 2 */
- buf.u.call.proc = htonl(NFS_READ);
- p = rpc_add_credentials((long *)buf.u.call.data);
- memcpy(p, fh, NFS_FHSIZE);
- p += NFS_FHSIZE / 4;
- *p++ = htonl(offset);
- *p++ = htonl(len);
- *p++ = 0; /* unused parameter */
- for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
- long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
- if (tokens >= 2)
- timeout = TICKS_PER_SEC/2;
-
- udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
- (char *)p - (char *)&buf, &buf);
- if (await_reply(await_rpc, sport, &id, timeout)) {
- if (tokens < 256)
- tokens++;
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
- if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
- rpc->u.reply.astatus || rpc->u.reply.data[0]) {
- rpc_printerror(rpc);
- if (rpc->u.reply.rstatus) {
- /* RPC failed, no verifier, data[0] */
- return -9999;
- }
- if (rpc->u.reply.astatus) {
- /* RPC couldn't decode parameters */
- return -9998;
- }
- return -ntohl(rpc->u.reply.data[0]);
- } else {
- return 0;
- }
- } else
- tokens >>= 1;
- }
- return -1;
- }
-
- /**************************************************************************
- NFS - Download extended BOOTP data, or kernel image from NFS server
- **************************************************************************/
- int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
- {
- static int recursion = 0;
- int sport;
- int err, namelen = strlen(name);
- char dirname[300], *fname;
- char dirfh[NFS_FHSIZE]; /* file handle of directory */
- char filefh[NFS_FHSIZE]; /* file handle of kernel image */
- unsigned int block;
- int rlen, size, offs, len;
- struct rpc_t *rpc;
-
- rx_qdrain();
-
- sport = oport++;
- if (oport > START_OPORT+OPORT_SWEEP) {
- oport = START_OPORT;
- }
- if ( name != dirname ) {
- memcpy(dirname, name, namelen + 1);
- }
- recursion = 0;
- nfssymlink:
- if ( recursion > NFS_MAXLINKDEPTH ) {
- printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
- return 0;
- }
- recursion++;
- fname = dirname + (namelen - 1);
- while (fname >= dirname) {
- if (*fname == '/') {
- *fname = '\0';
- fname++;
- break;
- }
- fname--;
- }
- if (fname < dirname) {
- printf("can't parse file name %s\n", name);
- return 0;
- }
-
- if (mount_port == -1) {
- mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
- }
- if (nfs_port == -1) {
- nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
- }
- if (nfs_port == -1 || mount_port == -1) {
- printf("can't get nfs/mount ports from portmapper\n");
- return 0;
- }
-
-
- err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
- if (err) {
- printf("mounting %s: ", dirname);
- nfs_printerror(err);
- /* just to be sure... */
- nfs_umountall(ARP_SERVER);
- return 0;
- }
-
- err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
- if (err) {
- printf("looking up %s: ", fname);
- nfs_printerror(err);
- nfs_umountall(ARP_SERVER);
- return 0;
- }
-
- offs = 0;
- block = 1; /* blocks are numbered starting from 1 */
- size = -1; /* will be set properly with the first reply */
- len = NFS_READ_SIZE; /* first request is always full size */
- do {
- err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
- if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
- // An error occured. NFS servers tend to sending
- // errors 21 / 22 when symlink instead of real file
- // is requested. So check if it's a symlink!
- block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
- filefh, sport);
- if ( 0 == block ) {
- printf("\nLoading symlink:%s ..",dirname);
- goto nfssymlink;
- }
- nfs_printerror(err);
- nfs_umountall(ARP_SERVER);
- return 0;
- }
- if (err) {
- printf("reading at offset %d: ", offs);
- nfs_printerror(err);
- nfs_umountall(ARP_SERVER);
- return 0;
- }
-
- rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
-
- /* size must be found out early to allow EOF detection */
- if (size == -1) {
- size = ntohl(rpc->u.reply.data[6]);
- }
- rlen = ntohl(rpc->u.reply.data[18]);
- if (rlen > len) {
- rlen = len; /* shouldn't happen... */
- }
-
- err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
- (offs+rlen == size));
- if (err <= 0) {
- nfs_umountall(ARP_SERVER);
- return err;
- }
-
- block++;
- offs += rlen;
- /* last request is done with matching requested read size */
- if (size-offs < NFS_READ_SIZE) {
- len = size-offs;
- }
- } while (len != 0);
- /* len == 0 means that all the file has been read */
- return 1;
- }
-
- #endif /* DOWNLOAD_PROTO_NFS */
|