📄 nfs.c
字号:
#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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -