Logo Search packages:      
Sourcecode: yaird version File versions  Download package

mount.c

#include <sys/mount.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <linux/nfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "nfsmount.h"
#include "sunrpc.h"

static __u32 mount_port;

struct mount_call
{
      struct rpc_call rpc;
      __u32 path_len;
      char path[0];
};


/*
 * The following structure is the NFS v3 on-the-wire file handle,
 * as defined in rfc1813.
 * This differs from the structure used by the kernel,
 * defined in <linux/nfh3.h>: rfc has a long in network order,
 * kernel has a short in native order.
 * Both kernel and rfc use the name nfs_fh; kernel name is
 * visible to user apps in some versions of libc.
 * Use different name to avoid clashes.
 */
#define NFS_MAXFHSIZE_WIRE 64
struct nfs_fh_wire
{
      __u32 size;
      char data[NFS_MAXFHSIZE_WIRE];
} __attribute__((packed));


struct mount_reply
{
      struct rpc_reply reply;
      __u32 status;
      struct nfs_fh_wire fh;
} __attribute__((packed));

#define MNT_REPLY_MINSIZE (sizeof(struct rpc_reply) + sizeof(__u32))

static int get_ports(__u32 server, const struct nfs_mount_data *data)
{
      __u32 nfs_ver, mount_ver;
      __u32 proto;

      if (data->flags & NFS_MOUNT_VER3) {
            nfs_ver = NFS3_VERSION;
            mount_ver = NFS_MNT3_VERSION;
      } else {
            nfs_ver = NFS2_VERSION;
            mount_ver = NFS_MNT_VERSION;
      }

      proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;

      if (nfs_port == 0) {
            nfs_port = portmap(server, NFS_PROGRAM, nfs_ver,
                           proto);
            if (nfs_port == 0) {
                  if (proto == IPPROTO_TCP) {
                        struct in_addr addr = { server };
                        fprintf(stderr, "NFS over TCP not "
                              "available from %s\n",
                              inet_ntoa(addr));
                        return -1;
                  }
                  nfs_port = NFS_PORT;
            }
      }

      if (mount_port == 0) {
            mount_port = portmap(server, NFS_MNT_PROGRAM, mount_ver,
                             proto);
            if (mount_port == 0)
                  mount_port = MOUNT_PORT;
      }
      return 0;
}

static inline int pad_len(int len)
{
      return (len + 3) & ~3;
}


#ifdef NFS_DEBUG
void hex_dump (const char *msg, const char *buf, int len) {
      int i, done = 0;
      printf ("%s: %p, length %d\n", msg, buf, len);
      while (done < len) {
            printf ("%04x: ", done);
            for (i = done; i < done + 16; i++) {
                  if (i < len) 
                        printf ("%02x ", buf[i] & 0xff);
                  else
                        printf ("   ");
                  if (i % 4 == 3) printf (" ");
            }
            for (i = done; i < done + 16; i++) {
                  if (i < len) 
                        printf ("%c", isprint(buf[i]) ? buf[i] : '.');
                  if (i % 4 == 3) printf (" ");
            }
            printf ("\n");
            done += 16;
      }
}
#endif NFS_DEBUG


static inline void dump_params(__u32 server,
                         const char *path,
                         const struct nfs_mount_data *data)
{
#ifdef NFS_DEBUG
      struct in_addr addr = { server };

      printf("NFS params:\n");
      printf("  server = %s, path = \"%s\", ", inet_ntoa(addr), path);
      printf("version = %d, proto = %s\n",
             data->flags & NFS_MOUNT_VER3 ? 3 : 2,
             (data->flags & NFS_MOUNT_TCP) ? "tcp" : "udp");
      printf("  mount_port = %d, nfs_port = %d, flags = %08x\n",
             mount_port, nfs_port, data->flags);
      printf("  rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
             data->rsize, data->wsize, data->timeo, data->retrans);
      printf("  acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
             data->acregmin, data->acregmax, data->acdirmin, data->acdirmax);
      printf("  soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
             (data->flags & NFS_MOUNT_SOFT) != 0,
             (data->flags & NFS_MOUNT_INTR) != 0,
             (data->flags & NFS_MOUNT_POSIX) != 0,
             (data->flags & NFS_MOUNT_NOCTO) != 0,
             (data->flags & NFS_MOUNT_NOAC) != 0);
#endif
}

static inline void dump_fh(const char *data, int len)
{
#ifdef NFS_DEBUG
      hex_dump ("Root file handle", data, len);
#endif
}

static struct mount_reply mnt_reply;

static int mount_call(__u32 proc, __u32 version,
                  const char *path,
                  struct client *clnt)
{
      struct mount_call *mnt_call = NULL;
      size_t path_len, call_len;
      struct rpc rpc;
      int ret = 0;

      path_len = strlen(path);
      call_len = sizeof(*mnt_call) + pad_len(path_len);

      if ((mnt_call = malloc(call_len)) == NULL) {
            perror("malloc");
            goto bail;
      }

      memset(mnt_call, 0, sizeof(*mnt_call));

      mnt_call->rpc.program = htonl(NFS_MNT_PROGRAM);
      mnt_call->rpc.prog_vers = htonl(version);
      mnt_call->rpc.proc = htonl(proc);
      mnt_call->path_len = htonl(path_len);
      memcpy(mnt_call->path, path, path_len);

      rpc.call = (struct rpc_call *) mnt_call;
      rpc.call_len = call_len;
      rpc.reply = (struct rpc_reply *) &mnt_reply;
      rpc.reply_len = sizeof(mnt_reply);

      if (rpc_call(clnt, &rpc) < 0)
            goto bail;

      if (proc != MNTPROC_MNT) {
            goto done;
      }

      if (rpc.reply_len < MNT_REPLY_MINSIZE) {
            fprintf(stderr, "incomplete reply: %zu < %zu\n",
                  rpc.reply_len, MNT_REPLY_MINSIZE);
            goto bail;
      }

      if (mnt_reply.status != 0) {
            fprintf(stderr, "mount call failed: %d\n",
                  ntohl(mnt_reply.status));
            goto bail;
      }

      goto done;

 bail:
      ret = -1;

 done:
      if (mnt_call) {
            free(mnt_call);
      }

      return ret;
}

static int mount_v2(const char *path,
                struct nfs_mount_data *data,
                struct client *clnt)
{
      int ret = mount_call(MNTPROC_MNT, NFS_MNT_VERSION, path, clnt);

      if (ret == 0) {
            dump_fh((const char *) &mnt_reply.fh, NFS2_FHSIZE);

            data->root.size = NFS_FHSIZE;
            memcpy(data->root.data, &mnt_reply.fh, NFS_FHSIZE);
            memcpy(data->old_root.data, &mnt_reply.fh, NFS_FHSIZE);
      }

      return ret;
}

static inline int umount_v2(const char *path, struct client *clnt)
{
      return mount_call(MNTPROC_UMNT, NFS_MNT_VERSION, path, clnt);
}

static int mount_v3(const char *path,
                struct nfs_mount_data *data,
                struct client *clnt)
{
      int ret = mount_call(MNTPROC_MNT, NFS_MNT3_VERSION, path, clnt);

      if (ret == 0) {
            size_t fhsize = ntohl(mnt_reply.fh.size);

            dump_fh((const char *) &mnt_reply.fh.data, fhsize);

            memset(data->old_root.data, 0, NFS_FHSIZE);
            memset(&data->root, 0, sizeof(data->root));
            data->root.size = fhsize;
            memcpy(&data->root.data, mnt_reply.fh.data, fhsize);
            data->flags |= NFS_MOUNT_VER3;
      }

      return ret;
}

static inline int umount_v3(const char *path, struct client *clnt)
{
      return mount_call(MNTPROC_UMNT, NFS_MNT3_VERSION, path, clnt);
}

int nfs_mount(const char *pathname, const char *hostname,
            __u32 server, const char *rem_path, const char *path,
            struct nfs_mount_data *data)
{
      struct client *clnt = NULL;
      struct sockaddr_in addr;
      char mounted = 0;
      int sock = -1;
      int ret = 0;
      int mountflags;

      if (get_ports(server, data) != 0) {
            goto bail;
      }

      dump_params(server, path, data);

      if (data->flags & NFS_MOUNT_TCP) {
            clnt = tcp_client(server, mount_port, CLI_RESVPORT);
      } else {
            clnt = udp_client(server, mount_port, CLI_RESVPORT);
      }

      if (clnt == NULL) {
            goto bail;
      }

      if (data->flags & NFS_MOUNT_VER3) {
            ret = mount_v3(rem_path, data, clnt);
      } else {
            ret = mount_v2(rem_path, data, clnt);
      }

      if (ret == -1) {
            goto bail;
      }
      mounted = 1;

      if (data->flags & NFS_MOUNT_TCP) {
            sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
      } else {
            sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
      }

      if (sock == -1) {
            perror("socket");
            goto bail;
      }

      if (bindresvport(sock, 0) == -1) {
            perror("bindresvport");
            goto bail;
      }

      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = server;
      addr.sin_port = htons(nfs_port);
      memcpy(&data->addr, &addr, sizeof(data->addr));

      strncpy(data->hostname, hostname, sizeof(data->hostname));

      data->fd = sock;

      mountflags = (data->flags & NFS_MOUNT_KLIBC_RONLY) ? MS_RDONLY : 0;
      data->flags = data->flags & NFS_MOUNT_FLAGMASK;
      ret = mount(pathname, path, "nfs", mountflags, data);

      if (ret == -1) {
            perror("mount");
            goto bail;
      }

      DEBUG(("Mounted %s on %s\n", pathname, path));

      goto done;

 bail:
      if (mounted) {
            if (data->flags & NFS_MOUNT_VER3) {
                  umount_v3(path, clnt);
            } else {
                  umount_v2(path, clnt);
            }
      }

      ret = -1;

 done:
      if (clnt) {
            client_free(clnt);
      }

      if (sock != -1) {
            close(sock);
      }

      return ret;
}


Generated by  Doxygen 1.6.0   Back to index