Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * Copyright (c) 1989, 1993, 1995
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)u9fs_vfsops.c       8.12 (Berkeley) 5/20/95
 * $Id: u9fs_vfsops.c,v 1.79 1998/12/04 22:54:54 archie Exp $
 */

#include <sys/param.h>
#include <sys/sockio.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/systm.h>
#include <sys/protosw.h>
#include <sys/syslog.h>

#include <netinet/in.h>
#include <netinet/tcp.h>

#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_zone.h>

#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>

#include <9fs/bitstring.h>
#include <9fs/9p.h>
#include <9fs/9auth.h>
#include <9fs/9fs.h>

vm_zone_t u9fsmount_zone;

static int      u9fs_mount __P(( struct mount *mp, char *path, caddr_t data,
                        struct nameidata *ndp, struct proc *p));
static int      u9fs_start __P(( struct mount *mp, int flags,
                        struct proc *p));
static int      u9fs_unmount __P(( struct mount *mp, int mntflags,
                        struct proc *p));
static int      u9fs_root __P(( struct mount *mp, struct vnode **vpp));
static int      u9fs_quotactl __P(( struct mount *mp, int cmds, uid_t uid,
                        caddr_t arg, struct proc *p));
static int      u9fs_statfs __P(( struct mount *mp, struct statfs *sbp,
                        struct proc *p));
static int      u9fs_sync __P(( struct mount *mp, int waitfor,
                        struct ucred *cred, struct proc *p));
static int      u9fs_vptofh __P(( struct vnode *vp, struct fid *fhp));
static int      u9fs_fhtovp __P((struct mount *mp, struct fid *fhp,
                        struct sockaddr *nam, struct vnode **vpp,
                        int *exflagsp, struct ucred **credanonp));
static int      u9fs_vget __P((struct mount *, ino_t, struct vnode **));
static int      u9fs_init __P((struct vfsconf *vfsp));
int             u9fs_uninit __P((struct vfsconf *vfsp));

/* */
static int      mountu9fs __P((struct u9fs_args *,struct mount *,
                        struct sockaddr *,char *,char *,struct vnode **, struct proc *p));
static int      u9fs_iosize __P((struct u9fsmount *nmp));
static void     u9fs_decode_args __P((struct u9fsmount *nmp, struct u9fs_args *argp, struct proc *p));

/*
 * u9fs vfs operations.
 */
static struct vfsops u9fs_vfsops = {
        u9fs_mount,
        u9fs_start,
        u9fs_unmount,
        u9fs_root,
        u9fs_quotactl,
        u9fs_statfs,
        u9fs_sync,
        u9fs_vget,
        u9fs_fhtovp,
        u9fs_vptofh,
        u9fs_init,
        u9fs_uninit,
        0
};
VFS_SET(u9fs_vfsops, u9fs, VFCF_NETWORK);

/*
 * u9fs statfs call
 */
static int
u9fs_statfs(mp, sbp, p)
        struct mount *mp;
        register struct statfs *sbp;
        struct proc *p;
{
  /* we have a worm with infinite storage,
     stat not supported by 9fs */
  return 0;
}

/*
 * Common code for mount and mountroot
 */
static int
mountu9fs(argp, mp, nam, pth, hst, vpp, p)
        register struct u9fs_args *argp;
        register struct mount *mp;
        struct sockaddr *nam;
        char *pth, *hst;
        struct vnode **vpp;
        struct proc *p;
{
  register struct u9fsmount *nmp;
  struct u9fsnode *np;
  int error;
  struct vattr attrs;
  struct u9fsreq req, rep;
  char * mntpoint;
  struct u9fsuser * u9p;
  struct socket * so;

  if (mp->mnt_flag & MNT_UPDATE) {
#if 0
    nmp = VFSTONFS(mp);
<    /* update paths, file handles, etc, here   XXX */
    FREE(nam, M_SONAME);
#endif
    return (0);
  } else {
    nmp = zalloc(u9fsmount_zone);
    bzero((caddr_t)nmp, sizeof (struct u9fsmount));
#if 0
    TAILQ_INIT(&nmp->nm_uidlruhead);
    TAILQ_INIT(&nmp->nm_bufq);
#endif
    mp->mnt_data = (qaddr_t)nmp;
  }
  vfs_getnewfsid(mp);
  nmp->nm_mountp = mp;

  nmp->nm_maxfilesize = (u_int64_t)0xffffffffffffffffLL;

  nmp->nm_wsize = U9FS_MAXFDATA;
  nmp->nm_rsize = U9FS_MAXFDATA;
  nmp->nm_readdirsize = U9FS_MAXDDATA;
  bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
  bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
  nmp->nm_nam = nam;

  mntpoint = index(hst, '/');
  if( mntpoint )
    mntpoint++;

  /* Set up the sockets and per-host congestion */
  nmp->nm_sotype = argp->sotype;
  nmp->nm_soproto = argp->proto;

  u9fs_decode_args(nmp, argp, p);

  lockinit(& nmp->nm_lock, PVFS, "u9fsmount", 0, 0);
  u9fs_id_init(&nmp->nm_tags);
  u9fs_id_init(&nmp->nm_fids);
  TAILQ_INIT(&nmp->nm_reqq);

  if ((error = u9fs_connect_9fs(nmp)))
    goto bad;
  
  /* "Tnop 1", "Tsession 1 0", "Tattach 1 1 none main 0 0", */
  bzero(&req, sizeof(req));
  req.r_nmp = nmp;
  req.r_procp = p;

  req.r_type = Tnop;
  error = u9fs_request(& req, & rep, 1);
  if( error )
    return error;

  req.r_type = Tsession;
  /* bzero(req.r_chal, sizeof(req.r_chal)); */
  u9auth_genchal(req.r_chal);
  error = u9fs_request(& req, & rep, 1);
  if( error )
    return error;

  if( argp->authaddr ) {
    /* get tickets from the auth server */
    error = u9fs_connect_9auth(nmp, argp, & so);
    if( error )
      goto bad;
    u9p = u9fs_finduser(u9fs_name2uid(argp->user));
    error = u9auth_gettickets(so, & rep, argp->user, u9p->u_ckey, 
                              req.r_ticket, req.r_auth, p);
    u9fs_disconnect(so);
    if( error )
      goto bad;
  }

  req.r_type = Tattach;
  req.r_fid = u9fs_id_new(nmp->nm_fids);
  strcpy(req.r_uname, argp->user);
  strcpy(req.r_aname, mntpoint);
  error = u9fs_request(& req, & rep, 1);
  if( error )
    return error;
  nmp->nm_fh = rep.r_qid.path;
  nmp->nm_fid = req.r_fid;
  /* XXX: we should have checked our challenge to the server! */

  /*
   * This is silly, but it has to be set so that vinifod() works.
   * We do not want to do an u9fs_statfs() here since we can get
   * stuck on a dead server and we are holding a lock on the mount
   * point.
   */
  mp->mnt_stat.f_iosize = u9fs_iosize(nmp);

  /*
   * A reference count is needed on the u9fsnode representing the
   * remote root.  If this object is not persistent, then backward
   * traversals of the mount point (i.e. "..") will not work if
   * the u9fsnode gets flushed out of the cache. Ufs does not have
   * this problem, because one can identify root inodes by their
   * number == ROOTINO (2).
   */
  error = u9fs_nget(mp, nmp->nm_fh, &np, p);
  np->n_fid = nmp->nm_fid;

  nmp->nm_authuid = p->p_ucred->cr_uid;

  if (error)
    goto bad;
  *vpp = U9FSTOV(np);
  
  /*
   * Get file attributes for the mountpoint.  This has the side
   * effect of filling in (*vpp)->v_type with the correct value.
   */
  VOP_GETATTR(*vpp, &attrs, p->p_ucred, p);
  
  /*
   * Lose the lock but keep the ref.
   */
  VOP_UNLOCK(*vpp, 0, p);

  return (0);
bad:
  u9fs_disconnect(nmp->nm_so);
  zfree(u9fsmount_zone, nmp);
  FREE(nam, M_SONAME);
  return (error);
}

/*
 * VFS Operations.
 *
 * mount system call
 * It seems a bit dumb to copyinstr() the host and path here and then
 * bcopy() them in mountu9fs(), but I wanted to detect errors before
 * doing the sockargs() call because sockargs() allocates an mbuf and
 * an error after that means that I have to release the mbuf.
 */
/* ARGSUSED */
static int
u9fs_mount(mp, path, data, ndp, p)
        struct mount *mp;
        char *path;
        caddr_t data;
        struct nameidata *ndp;
        struct proc *p;
{
  int error;
  struct u9fs_args args;
  struct sockaddr *nam;
  struct vnode *vp;
  char pth[MNAMELEN], hst[MNAMELEN];
  size_t len;

  if( path == NULL )
    return (EOPNOTSUPP);

  error = copyin(data, (caddr_t)&args, sizeof (struct u9fs_args));
  if (error)
    return (error);

  if (args.version != U9FS_ARGSVERSION)
    return (EPROGMISMATCH);

  if (mp->mnt_flag & MNT_UPDATE) {
#if 0
    register struct u9fsmount *nmp = VFSTONFS(mp);
    
    if (nmp == NULL)
      return (EIO);
    /*
     * When doing an update, we can't change from or to
     * v3 and/or nqu9fs, or change cookie translation
     */
    args.flags = (args.flags &
                  ~(NFSMNT_NFSV3|NFSMNT_NQNFS /*|NFSMNT_XLATECOOKIE*/)) |
      (nmp->nm_flag &
       (NFSMNT_NFSV3|NFSMNT_NQNFS /*|NFSMNT_XLATECOOKIE*/));
    u9fs_decode_args(nmp, &args, p);
#endif
    return (0);
  }

  error = copyinstr(path, pth, MNAMELEN-1, &len);
  if (error)
    return (error);
  bzero(&pth[len], MNAMELEN - len);
  error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
  if (error)
    return (error);
  bzero(&hst[len], MNAMELEN - len);
  /* sockargs() call must be after above copyin() calls */
  error = getsockaddr(&nam, (caddr_t)args.addr, args.addrlen);
  if (error)
    return (error);
  error = mountu9fs(&args, mp, nam, pth, hst, &vp, p);
  return (error);  
}

/*
 * unmount system call
 */
static int
u9fs_unmount(mp, mntflags, p)
     struct mount *mp;
     int mntflags;
     struct proc *p;
{
  register struct u9fsmount *nmp;
  struct u9fsnode *np;
  struct vnode *vp;
  int error, flags = 0;
  
  if (mntflags & MNT_FORCE)
    flags |= FORCECLOSE;
  nmp = VFSTOU9FS(mp);

  if( p->p_ucred->cr_uid != nmp->nm_authuid )
    return (EPERM);

  /*
   * Goes something like this..
   * - Check for activity on the root vnode (other than ourselves).
   * - Call vflush() to clear out vnodes for this file system,
   *   except for the root vnode.
   * - Decrement reference on the vnode representing remote root.
   * - Close the socket
   * - Free up the data structures
   */
  /*
   * We need to decrement the ref. count on the u9fsnode representing
   * the remote root.  See comment in mountu9fs().  The VFS unmount()
   * has done vput on this vnode, otherwise we would get deadlock!
   */
  error = u9fs_nget(mp, nmp->nm_fh, &np, p);
  if (error)
    return(error);
  vp = U9FSTOV(np);
  if (vp->v_usecount > 2) {
    vput(vp);
    return (EBUSY);
  }

  error = vflush(mp, vp, flags);
  if (error) {
    vput(vp);
    return (error);
  }

  /*
   * We are now committed to the unmount.
   */
  /*
   * There are two reference counts and one lock to get rid of here.
   */
  vput(vp);
  vrele(vp);
  vgone(vp);
  u9fs_disconnect(nmp->nm_so);
  FREE(nmp->nm_nam, M_SONAME);

  zfree(u9fsmount_zone, nmp);
  return (0);
}

/*
 * Return root of a filesystem
 */
static int
u9fs_root(mp, vpp)
        struct mount *mp;
        struct vnode **vpp;
{
        register struct vnode *vp;
        struct u9fsmount *nmp;
        struct u9fsnode *np;
        int error;

        nmp = VFSTOU9FS(mp);
        error = u9fs_nget(mp, nmp->nm_fh, &np, curproc); /* XXX */
        if (error)
                return (error);
        vp = U9FSTOV(np);
        if (vp->v_type == VNON)
            vp->v_type = VDIR;
        vp->v_flag = VROOT;
        *vpp = vp;
        return (0);
}

extern int syncprt;

/*
 * Flush out the buffer cache
 */
/* ARGSUSED */
static int
u9fs_sync(mp, waitfor, cred, p)
        struct mount *mp;
        int waitfor;
        struct ucred *cred;
        struct proc *p;
{
  /* no cache yet */
  return 0;
}

/*
 * U9FS flat namespace lookup.
 * Currently unsupported.
 */
/* ARGSUSED */
static int
u9fs_vget(mp, ino, vpp)
        struct mount *mp;
        ino_t ino;
        struct vnode **vpp;
{

        return (EOPNOTSUPP);
}

/*
 * At this point, this should never happen
 */
/* ARGSUSED */
static int
u9fs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
        register struct mount *mp;
        struct fid *fhp;
        struct sockaddr *nam;
        struct vnode **vpp;
        int *exflagsp;
        struct ucred **credanonp;
{

        return (EINVAL);
}

/*
 * Vnode pointer to File handle, should never happen either
 */
/* ARGSUSED */
static int
u9fs_vptofh(vp, fhp)
        struct vnode *vp;
        struct fid *fhp;
{

        return (EINVAL);
}

/*
 * Vfs start routine, a no-op.
 */
/* ARGSUSED */
static int
u9fs_start(mp, flags, p)
        struct mount *mp;
        int flags;
        struct proc *p;
{

        return (0);
}

/*
 * Do operations associated with quotas, not supported
 */
/* ARGSUSED */
static int
u9fs_quotactl(mp, cmd, uid, arg, p)
        struct mount *mp;
        int cmd;
        uid_t uid;
        caddr_t arg;
        struct proc *p;
{

        return (EOPNOTSUPP);
}

/*
 * Called once to initialize data structures...
 */
int
u9fs_init(vfsp)
        struct vfsconf *vfsp;
{
  u9fsmount_zone = zinit("U9FSMOUNT", sizeof(struct u9fsmount), 0, 0, 1);
  u9fs_nhinit();                        /* Init the u9fsnode table */
  u9fs_uhinit();
  return 0;
}

int
u9fs_uninit(vfsp)
        struct vfsconf *vfsp;
{
  return 0;
}

static int
u9fs_iosize(nmp)
     struct u9fsmount* nmp;
{
  int iosize;
  
  /*
   * Calculate the size used for io buffers.  Use the larger
   * of the two sizes to minimise u9fs requests but make sure
   * that it is at least one VM page to avoid wasting buffer
   * space.
   */
  iosize = max(nmp->nm_rsize, nmp->nm_wsize);
  if (iosize < PAGE_SIZE) iosize = PAGE_SIZE;
  return iosize;
}

static void
u9fs_decode_args(nmp, argp, p)
     struct u9fsmount *nmp;
     struct u9fs_args *argp;
     struct proc * p;
{
  int s, i;
  int maxio;
  struct p9user * p9p, p9u;
  struct u9fsuser * u9p;
  
  s = splnet();
  /* Update flags atomically.  Don't change the lock bits. */
  nmp->nm_flag = argp->flags | nmp->nm_flag;
  splx(s);

  maxio = U9FS_MAXFDATA;

  if (argp->wsize > 0) {
    nmp->nm_wsize = argp->wsize;
    /* Round down to multiple of blocksize */
    nmp->nm_wsize &= ~(U9FS_FABLKSIZE - 1);
    if (nmp->nm_wsize <= 0)
      nmp->nm_wsize = U9FS_FABLKSIZE;
  }
  if (nmp->nm_wsize > maxio)
    nmp->nm_wsize = maxio;
  if (nmp->nm_wsize > MAXBSIZE)
    nmp->nm_wsize = MAXBSIZE;

  if (argp->rsize > 0) {
    nmp->nm_rsize = argp->rsize;
    /* Round down to multiple of blocksize */
    nmp->nm_rsize &= ~(U9FS_FABLKSIZE - 1);
    if (nmp->nm_rsize <= 0)
      nmp->nm_rsize = U9FS_FABLKSIZE;
  }
  if (nmp->nm_rsize > maxio)
    nmp->nm_rsize = maxio;
  if (nmp->nm_rsize > MAXBSIZE)
    nmp->nm_rsize = MAXBSIZE;

  if (argp->readdirsize > 0) {
    nmp->nm_readdirsize = argp->readdirsize;
  }
  if (nmp->nm_readdirsize > maxio)
    nmp->nm_readdirsize = maxio;
  if (nmp->nm_readdirsize > nmp->nm_rsize)
    nmp->nm_readdirsize = nmp->nm_rsize;

  if( argp->nusers ) {
    p9p = argp->users;
    for(i = 0; i < argp->nusers; i++) {
      copyin(p9p, &p9u, sizeof(p9u));
      u9fs_hashuser(p9u.p9_uid, p9u.p9_name);
      p9p ++;
    }
    printf("%d p9users loaded\n", argp->nusers);
  }

  if( (u9p = u9fs_finduser(u9fs_name2uid(argp->user))) ) {
    bcopy(argp->key, u9p->u_ckey, U9AUTH_DESKEYLEN);
  }
}