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);
}
}