Logo Search packages:      
Sourcecode: udev version File versions

nfs_no_rpc.c

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/mount.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/* Default path we try to mount. "%s" gets replaced by our IP address */
#define NFS_ROOT        "/tftpboot/%s"
#define NFS_DEF_FILE_IO_BUFFER_SIZE 4096
#define NFS_MAXPATHLEN  1024
#define NFS_MNT_PROGRAM 100005
#define NFS_MNT_PORT    627
#define NFS_PROGRAM     100003
#define NFS_PORT  2049
#define NFS2_VERSION          2
#define NFS3_VERSION          3
#define NFS_MNT_PROGRAM       100005
#define NFS_MNT_VERSION       1
#define NFS_MNT3_VERSION      3
#define MNTPROC_MNT           1
#define MOUNTPROC3_MNT        1
#define RPC_PMAP_PROGRAM      100000
#define RPC_PMAP_VERSION      2
#define RPC_PMAP_PORT         111

#define NFS2_FHSIZE     32
#define NFS3_FHSIZE     64

#define RPC_VERSION 2

enum rpc_msg_type {
      RPC_CALL = 0,
      RPC_REPLY = 1
};

enum rpc_auth_flavor {
      RPC_AUTH_NULL  = 0,
      RPC_AUTH_UNIX  = 1,
      RPC_AUTH_SHORT = 2,
      RPC_AUTH_DES   = 3,
      RPC_AUTH_KRB   = 4,
};

enum rpc_reply_stat {
      RPC_MSG_ACCEPTED = 0,
      RPC_MSG_DENIED = 1
};

#define NFS_MAXFHSIZE         64
struct nfs_fh {
      unsigned short          size;
      unsigned char           data[NFS_MAXFHSIZE];
};

struct nfs2_fh {
      char              data[NFS2_FHSIZE];
};

#define NFS_MOUNT_VERSION     4

struct nfs_mount_data {
      int         version;
      int         fd;
      struct nfs2_fh    old_root;
      int         flags;
      int         rsize;
      int         wsize;
      int         timeo;
      int         retrans;
      int         acregmin;
      int         acregmax;
      int         acdirmin;
      int         acdirmax;
      struct sockaddr_in addr;
      char        hostname[256];
      int         namlen;
      unsigned int      bsize;
      struct nfs_fh     root;
};

#define NFS_MOUNT_SOFT        0x0001      /* 1 */
#define NFS_MOUNT_INTR        0x0002      /* 1 */
#define NFS_MOUNT_SECURE      0x0004      /* 1 */
#define NFS_MOUNT_POSIX       0x0008      /* 1 */
#define NFS_MOUNT_NOCTO       0x0010      /* 1 */
#define NFS_MOUNT_NOAC        0x0020      /* 1 */
#define NFS_MOUNT_TCP         0x0040      /* 2 */
#define NFS_MOUNT_VER3        0x0080      /* 3 */
#define NFS_MOUNT_KERBEROS    0x0100      /* 3 */
#define NFS_MOUNT_NONLM       0x0200      /* 3 */
#define NFS_MOUNT_BROKEN_SUID 0x0400      /* 4 */
#define NFS_MOUNT_FLAGMASK    0xFFFF

static char nfs_root_name[256];
static u_int32_t root_server_addr;
static char root_server_path[256];

/* Address of NFS server */
static u_int32_t servaddr;

/* Name of directory to mount */
static char nfs_path[NFS_MAXPATHLEN];

/* NFS-related data */
static struct nfs_mount_data nfs_data = {
      .version  = NFS_MOUNT_VERSION,
      .flags    = NFS_MOUNT_NONLM,  /* No lockd in nfs root yet */
      .rsize    = NFS_DEF_FILE_IO_BUFFER_SIZE,
      .wsize    = NFS_DEF_FILE_IO_BUFFER_SIZE,
      .bsize        =   0,
      .timeo    = 7,
      .retrans  = 3,
      .acregmin = 3,
      .acregmax = 60,
      .acdirmin = 30,
      .acdirmax = 60,
};
static int nfs_port = -1;
static int mount_port;

/***************************************************************************

                       Parsing of options

 ***************************************************************************/

/*
 *  The following integer options are recognized
 */
static struct nfs_int_opts {
      const char *name;
      int  *val;
} root_int_opts[] = {
      { "port",   &nfs_port },
      { "rsize",  &nfs_data.rsize },
      { "wsize",  &nfs_data.wsize },
      { "timeo",  &nfs_data.timeo },
      { "retrans",      &nfs_data.retrans },
      { "acregmin",     &nfs_data.acregmin },
      { "acregmax",     &nfs_data.acregmax },
      { "acdirmin",     &nfs_data.acdirmin },
      { "acdirmax",     &nfs_data.acdirmax },
      { NULL,           NULL }
};

/*
 *  And now the flag options
 */
static struct nfs_bool_opts {
      const char *name;
      int  and_mask;
      int  or_mask;
} root_bool_opts[] = {
      { "soft",   ~NFS_MOUNT_SOFT,  NFS_MOUNT_SOFT },
      { "hard",   ~NFS_MOUNT_SOFT,  0 },
      { "intr",   ~NFS_MOUNT_INTR,  NFS_MOUNT_INTR },
      { "nointr", ~NFS_MOUNT_INTR,  0 },
      { "posix",  ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
      { "noposix",      ~NFS_MOUNT_POSIX, 0 },
      { "cto",    ~NFS_MOUNT_NOCTO, 0 },
      { "nocto",  ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
      { "ac",           ~NFS_MOUNT_NOAC,  0 },
      { "noac",   ~NFS_MOUNT_NOAC,  NFS_MOUNT_NOAC },
      { "lock",   ~NFS_MOUNT_NONLM, 0 },
      { "nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM },
#ifdef CONFIG_NFS_V3
      { "v2",           ~NFS_MOUNT_VER3,  0 },
      { "v3",           ~NFS_MOUNT_VER3,  NFS_MOUNT_VER3 },
#endif
      { "udp",    ~NFS_MOUNT_TCP,         0 },
      { "tcp",    ~NFS_MOUNT_TCP,         NFS_MOUNT_TCP },
      { "broken_suid",~NFS_MOUNT_BROKEN_SUID,   NFS_MOUNT_BROKEN_SUID },
      { NULL,           0,                0 }
};
/*
 *  Parse option string.
 */
static void root_nfs_parse(char *name, char *buf)
{
      char *options, *val, *cp;

      if ((options = strchr(name, ','))) {
            *options++ = 0;
            cp = strtok(options, ",");
            while (cp) {
                  if ((val = strchr(cp, '='))) {
                        struct nfs_int_opts *opts = root_int_opts;
                        *val++ = '\0';
                        while (opts->name && strcmp(opts->name, cp))
                              opts++;
                        if (opts->name)
                              *(opts->val) = (int) strtoul(val, NULL, 10);
                  } else {
                        struct nfs_bool_opts *opts = root_bool_opts;
                        while (opts->name && strcmp(opts->name, cp))
                              opts++;
                        if (opts->name) {
                              nfs_data.flags &= opts->and_mask;
                              nfs_data.flags |= opts->or_mask;
                        }
                  }
                  cp = strtok(NULL, ",");
            }
      }
      if (name[0] && strcmp(name, "default")) {
            strncpy(buf, name, NFS_MAXPATHLEN-1);
            buf[NFS_MAXPATHLEN-1] = 0;
      }
}

/*
 *  Prepare the NFS data structure and parse all options.
 */
static int root_nfs_name(char *name)
{
      char buf[NFS_MAXPATHLEN];
      struct utsname uname_buf;

      /* Set some default values */
      strcpy(buf, NFS_ROOT);

      /* Process options received from the remote server */
      root_nfs_parse(root_server_path, buf);

      /* Override them by options set on kernel command-line */
      root_nfs_parse(name, buf);

      uname(&uname_buf);
      if (strlen(buf) + strlen(uname_buf.nodename) > NFS_MAXPATHLEN) {
            printf("nfsroot: Pathname for remote directory too long.\n");
            return -1;
      }
      sprintf(nfs_path, buf, uname_buf.nodename);

      return 1;
}

/***************************************************************************

             Routines to actually mount the root directory

 ***************************************************************************/

/*
 *  Construct sockaddr_in from address and port number.
 */
static inline void
set_sockaddr(struct sockaddr_in *sin, u_int32_t addr, u_int16_t port)
{
      memset(sin, 0, sizeof(*sin));
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = addr;
      sin->sin_port = port;
}

/*
 * Extremely crude RPC-over-UDP call. We get an already encoded request
 * to pass, we do that and put the reply into buffer. That (and callers
 * below - getport, getfh2 and getfh3) should be replaced with proper
 * librpc use. Now, if we only had one that wasn't bloated as a dead
 * gnu that had lied for a while under the sun...
 */

static u_int32_t XID;
static int flag;
static void timeout(int n)
{
      (void)n;
      flag = 1;
}
static int do_call(struct sockaddr_in *sin, u_int32_t msg[], u_int32_t rmsg[],
            u_int32_t len, u_int32_t rlen)
{
      struct sockaddr_in from;
      int slen = sizeof(struct sockaddr_in);
      struct timeval tv = {1, 0};
      int n;
      int fd;

      sysv_signal(SIGALRM, timeout);
      fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (fd < 0)
            goto Esocket;
      setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&tv, sizeof(tv));
      len *= 4;
      if (sendto(fd, msg, len, 0, (struct sockaddr *)sin, slen)!=(int)len)
            goto Esend;
      setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv, sizeof(tv));
      alarm(0);
      flag = 0;
      alarm(5);
      rlen *= 4;
      do  {
            slen = sizeof(from);
            n = recvfrom(fd, rmsg, rlen, 0, (struct sockaddr*)&from, &slen);
            if (flag || n < 0)
                  goto Erecv;
      } while (memcmp(&from, sin, sizeof(from)) || rmsg[0] != msg[0]);

      if (n < 6*4 || n % 4 || ntohl(rmsg[1]) != 1 || rmsg[2] ||
                              rmsg[3] || rmsg[4] || rmsg[5])
            goto Einval;
      alarm(0);
      close(fd);
      return n / 4 - 6;

Esend:      printf("rpc: write failed\n");
      goto out;
Erecv:      printf("rpc: read failed\n");
      goto out;
Einval:     printf("rpc: invalid response\n");
      goto out;
Esocket:printf("rpc: can't create socket\n");
      return -1;
out:
      alarm(0);
      close(fd);
      return -1;
}

enum {
      PMAP_GETPORT = 3
};

static void do_header(u_int32_t msg[], u_int32_t prog, u_int32_t vers, u_int32_t proc)
{
      msg[0] = XID++;
      msg[1] = htonl(RPC_CALL);
      msg[2] = htonl(RPC_VERSION);
      msg[3] = htonl(prog);
      msg[4] = htonl(vers);
      msg[5] = htonl(proc);
      msg[6] = htonl(RPC_AUTH_NULL);
      msg[7] = htonl(0);
      msg[8] = htonl(RPC_AUTH_NULL);
      msg[9] = htonl(0);
}

static int getport(u_int32_t prog, u_int32_t vers, u_int32_t prot)
{
      struct sockaddr_in sin;
      unsigned msg[14];
      unsigned rmsg[7];
      int n;
      set_sockaddr(&sin, servaddr, htons(RPC_PMAP_PORT));
      do_header(msg, RPC_PMAP_PROGRAM, RPC_PMAP_VERSION, PMAP_GETPORT);
      msg[10] = htonl(prog);
      msg[11] = htonl(vers);
      msg[12] = htonl(prot);
      msg[13] = htonl(0);
      n = do_call(&sin, msg, rmsg, 14, 7);
      if (n <= 0)
            return -1;
      else
            return ntohl(rmsg[6]);
}

static int getfh2(void)
{
      struct sockaddr_in sin;
      unsigned msg[10+1+256/4];
      unsigned rmsg[6 + 1 + NFS2_FHSIZE/4];
      int n;
      int len = strlen(nfs_path);
      set_sockaddr(&sin, servaddr, mount_port);

      if (len > 255) {
            printf("nfsroot: pathname is too long");
            return -1;
      }
      memset(msg, 0, sizeof(msg));
      do_header(msg, NFS_MNT_PROGRAM, NFS_MNT_VERSION, MNTPROC_MNT);
      msg[10] = htonl(len);
      strcpy((char*)&msg[11], nfs_path);
      n = do_call(&sin, msg, rmsg, 11 + (len + 3)/4, 7 + NFS2_FHSIZE/4);
      if (n < 0)
            return -1;
      if (n != NFS2_FHSIZE/4 + 1)
            goto Esize;
      if (rmsg[6]) {
            printf("nfsroot: mountd returned an error (%d)",htonl(rmsg[6]));
            return -1;
      }
      nfs_data.root.size = NFS2_FHSIZE;
      memcpy(nfs_data.root.data, &rmsg[7], NFS2_FHSIZE);
      return 0;
Esize:
      printf("nfsroot: bad fhandle size");
      return -1;
}

static int getfh3(void)
{
      struct sockaddr_in sin;
      unsigned msg[10+1+256/4];
      unsigned rmsg[6 + 1 + 1 + NFS3_FHSIZE/4];
      int n;
      int len = strlen(nfs_path);
      int size;
      set_sockaddr(&sin, servaddr, mount_port);

      if (len > 255) {
            printf("nfsroot: pathname is too long");
            return -1;
      }
      memset(msg, 0, sizeof(msg));
      do_header(msg, NFS_MNT_PROGRAM, NFS_MNT3_VERSION, MOUNTPROC3_MNT);
      msg[10] = htonl(len);
      strcpy((char*)&msg[11], nfs_path);
      n = do_call(&sin, msg, rmsg, 11 + (len + 3)/4, 8 + NFS3_FHSIZE/4);
      if (n < 0)
            return -1;
      if (n <= 2)
            goto Esize;
      if (rmsg[6]) {
            printf("nfsroot: mountd returned an error (%d)",htonl(rmsg[6]));
            return -1;
      }
      size = ntohl(rmsg[7]);
      if (size > NFS3_FHSIZE || n != 2 + size/4)
            goto Esize;
      nfs_data.root.size = size;
      memcpy(nfs_data.root.data, &rmsg[8], size);
      return 0;
Esize:
      printf("nfsroot: bad fhandle size");
      return -1;
}

/*
 *  Use portmapper to find mountd and nfsd port numbers if not overriden
 *  by the user. Use defaults if portmapper is not available.
 *  XXX: Is there any nfs server with no portmapper?
 */
static int root_nfs_ports(void)
{
      int port;
      int nfsd_ver, mountd_ver;
      int proto;

      if (nfs_data.flags & NFS_MOUNT_VER3) {
            nfsd_ver = NFS3_VERSION;
            mountd_ver = NFS_MNT3_VERSION;
      } else {
            nfsd_ver = NFS2_VERSION;
            mountd_ver = NFS_MNT_VERSION;
      }

      proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;

      if (nfs_port < 0) {
            if ((port = getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
                  printf("nfsroot: Unable to get nfsd port "
                              "number from server, using default\n");
                  port = NFS_PORT;
            }
            nfs_port = htons(port);
            printf("nfsroot: Portmapper on server returned %d "
                  "as nfsd port\n", port);
      }

      if ((port = getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
            printf("nfsroot: Unable to get mountd port "
                        "number from server, using default\n");
            port = NFS_MNT_PORT;
      }
      mount_port = htons(port);
      printf("nfsroot: mountd port is %d\n", port);

      return 0;
}

int main(void)
{
      unsigned char *p;
      struct timeval tv;
      char *s;

      /* FIX: use getopt() instead of this */

      s = getenv("root_server_addr");
      if (s)
            root_server_addr = strtoul(s, NULL, 10);
      s = getenv("root_server_path");
      if (s)
            strncpy(root_server_path, s, 255);
      s = getenv("nfs_root_name");
      if (s)
            strncpy(nfs_root_name, s, 255);

      /*
       * Decode the root directory path name and NFS options from
       * the kernel command line. This has to go here in order to
       * be able to use the client IP address for the remote root
       * directory (necessary for pure RARP booting).
       */
      if (root_nfs_name(nfs_root_name) < 0)
            return 0;
      if ((servaddr = root_server_addr) == INADDR_NONE) {
            printf("nfsroot: No NFS server available, giving up.\n");
            return 0;
      }

      p = (char *) &servaddr;
      sprintf(nfs_data.hostname, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);

#ifdef NFSROOT_DEBUG
      printf("nfsroot: Mounting %s on server %s as root\n",
            nfs_path, nfs_data.hostname);
      printf("nfsroot:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
            nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
      printf("nfsroot:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
            nfs_data.acregmin, nfs_data.acregmax,
            nfs_data.acdirmin, nfs_data.acdirmax);
      printf("nfsroot:     nfsd port = %d, mountd port = %d, flags = %08x\n",
            nfs_port, mount_port, nfs_data.flags);
#endif

      gettimeofday(&tv, NULL);
      XID = (tv.tv_sec << 15) ^ tv.tv_usec;

      if (root_nfs_ports() < 0)
            return 0;
      if (nfs_data.flags & NFS_MOUNT_VER3) {
            if (getfh3())
                  return 0;
      } else {
            if (getfh2())
                  return 0;
      }
      set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port);
      return mount("/dev/root", "/mnt", "nfs", 0, &nfs_data) == 0;
}

Generated by  Doxygen 1.6.0   Back to index