Blame | Last modification | View Log | RSS feed
/*
* Copyright (c) 1989, 1993
* 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_vnops.c 8.16 (Berkeley) 5/27/95
* $Id: u9fs_vnops.c,v 1.116.2.3 1999/02/13 08:03:47 dillon Exp $
*/
/*
* vnode op calls for 9FS
*/
#include "opt_inet.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <sys/buf.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/namei.h>
#include <sys/socket.h>
#include <sys/vnode.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_zone.h>
#include <vm/vm_prot.h>
#include <vm/vm_page.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>
#include <vm/vnode_pager.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <9fs/bitstring.h>
#include <9fs/9p.h>
#include <9fs/9auth.h>
#include <9fs/9fs.h>
#define u9fs_poll vop_nopoll
static int u9fs_lookup __P((struct vop_lookup_args *));
static int u9fs_create __P((struct vop_create_args *));
static int u9fs_mknod __P((struct vop_mknod_args *));
static int u9fs_open __P((struct vop_open_args *));
static int u9fs_close __P((struct vop_close_args *));
static int u9fs_access __P((struct vop_access_args *));
static int u9fs_getattr __P((struct vop_getattr_args *));
static int u9fs_setattr __P((struct vop_setattr_args *));
static int u9fs_read __P((struct vop_read_args *));
static int u9fs_mmap __P((struct vop_mmap_args *));
static int u9fs_fsync __P((struct vop_fsync_args *));
static int u9fs_remove __P((struct vop_remove_args *));
static int u9fs_link __P((struct vop_link_args *));
static int u9fs_rename __P((struct vop_rename_args *));
static int u9fs_mkdir __P((struct vop_mkdir_args *));
static int u9fs_rmdir __P((struct vop_rmdir_args *));
static int u9fs_symlink __P((struct vop_symlink_args *));
static int u9fs_readdir __P((struct vop_readdir_args *));
static int u9fs_bmap __P((struct vop_bmap_args *));
static int u9fs_strategy __P((struct vop_strategy_args *));
static int u9fs_readlink __P((struct vop_readlink_args *));
static int u9fs_print __P((struct vop_print_args *));
static int u9fs_advlock __P((struct vop_advlock_args *));
static int u9fs_bwrite __P((struct vop_bwrite_args *));
static int u9fs_abortop __P((struct vop_abortop_args *));
static int u9fs_getpages __P((struct vop_getpages_args *));
static int u9fs_putpages __P((struct vop_putpages_args *));
static int u9fs_inactive __P((struct vop_inactive_args *));
static int u9fs_reclaim __P((struct vop_reclaim_args *));
static int u9fs_write __P((struct vop_write_args *));
/*
* Global vfs data structures for u9fs
*/
vop_t **u9fs_vnodeop_p;
static struct vnodeopv_entry_desc u9fs_vnodeop_entries[] = {
{ &vop_default_desc, (vop_t *) vop_defaultop },
{ &vop_abortop_desc, (vop_t *) u9fs_abortop },
{ &vop_access_desc, (vop_t *) u9fs_access },
{ &vop_advlock_desc, (vop_t *) u9fs_advlock },
{ &vop_bmap_desc, (vop_t *) u9fs_bmap },
{ &vop_bwrite_desc, (vop_t *) u9fs_bwrite },
{ &vop_close_desc, (vop_t *) u9fs_close },
{ &vop_create_desc, (vop_t *) u9fs_create },
{ &vop_fsync_desc, (vop_t *) u9fs_fsync },
{ &vop_getattr_desc, (vop_t *) u9fs_getattr },
{ &vop_getpages_desc, (vop_t *) u9fs_getpages },
{ &vop_putpages_desc, (vop_t *) u9fs_putpages },
{ &vop_inactive_desc, (vop_t *) u9fs_inactive },
{ &vop_lease_desc, (vop_t *) vop_null },
{ &vop_link_desc, (vop_t *) u9fs_link },
{ &vop_lock_desc, (vop_t *) vop_sharedlock },
{ &vop_lookup_desc, (vop_t *) u9fs_lookup },
{ &vop_mkdir_desc, (vop_t *) u9fs_mkdir },
{ &vop_mknod_desc, (vop_t *) u9fs_mknod },
{ &vop_mmap_desc, (vop_t *) u9fs_mmap },
{ &vop_open_desc, (vop_t *) u9fs_open },
{ &vop_poll_desc, (vop_t *) vop_nopoll },
{ &vop_print_desc, (vop_t *) u9fs_print },
{ &vop_read_desc, (vop_t *) u9fs_read },
{ &vop_readdir_desc, (vop_t *) u9fs_readdir },
{ &vop_readlink_desc, (vop_t *) u9fs_readlink },
{ &vop_reclaim_desc, (vop_t *) u9fs_reclaim },
{ &vop_remove_desc, (vop_t *) u9fs_remove },
{ &vop_rename_desc, (vop_t *) u9fs_rename },
{ &vop_rmdir_desc, (vop_t *) u9fs_rmdir },
{ &vop_setattr_desc, (vop_t *) u9fs_setattr },
{ &vop_strategy_desc, (vop_t *) u9fs_strategy },
{ &vop_symlink_desc, (vop_t *) u9fs_symlink },
{ &vop_write_desc, (vop_t *) u9fs_write },
{ NULL, NULL }
};
static struct vnodeopv_desc u9fs_vnodeop_opv_desc =
{ &u9fs_vnodeop_p, u9fs_vnodeop_entries };
VNODEOP_SET(u9fs_vnodeop_opv_desc);
extern vm_zone_t u9fsnode_zone;
static int u9fs_trunc(struct vnode * vp, struct ucred * cred, struct proc * p);
static void u9fs_free_fid __P((u_short fid, struct u9fsmount * nmp, struct proc * p));
static void u9fs_updtcache __P((struct u9fsnode *, struct u9fsreq *));
#define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1))
/* open returns a qid for cache consistent check */
static void
u9fs_updtcache(struct u9fsnode * np, struct u9fsreq * rep)
{
if( rep->r_type != Rerror )
np->n_dir.dir_qid = rep->r_qid;
}
static int
u9fs_trunc(vp, cred, p)
register struct vnode * vp;
struct ucred * cred;
struct proc * p;
{
struct u9fsnode *np = VTOU9FS(vp);
struct u9fsmount * nmp = VFSTOU9FS(vp->v_mount);
int error;
u_short newfid;
struct u9fsreq req, rep;
u_char mode;
/*
* Disallow write attempts on filesystems mounted read-only;
* unless the file is a socket, fifo, or a block or character
* device resident on the filesystem.
*/
if ( (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
return (EROFS);
default:
break;
}
}
mode = U9P_MODE_WR | U9P_MODE_TRUNC;
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
newfid = u9fs_id_new(nmp->nm_fids);
req.r_type = Tclone;
req.r_fid = np->n_fid;
req.r_newfid = newfid;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
req.r_type = Topen;
req.r_fid = newfid;
req.r_mode = mode;
error = u9fs_request(&req, &rep, 1);
if( !error )
u9fs_vinvalbuf(vp, 0, cred, p, 0);
if( error || np->n_wrfid ) {
u9fs_free_fid(newfid, nmp, p);
return error;
}
if( !U9P_PERM_EXCL(np->n_dir.dir_mode))
np->n_wrfid = newfid;
else
u9fs_free_fid(newfid, nmp, p);
return (0);
}
/*
* u9fs access vnode op.
*/
static int
u9fs_access(ap)
struct vop_access_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
struct u9fsnode *np = VTOU9FS(vp);
struct u9fsmount * nmp = VFSTOU9FS(vp->v_mount);
struct proc * p = ap->a_p;
int error, a_mode = ap->a_mode;
u_short * fidp = 0, *fidp2 = 0, newfid;
struct u9fsreq req, rep;
u_char mode;
struct ucred * cred = ap->a_cred;
/* XXX: for the moment, only the authenticator has access */
if( cred->cr_uid != nmp->nm_authuid )
return (EPERM);
/*
* Disallow write attempts on filesystems mounted read-only;
* unless the file is a socket, fifo, or a block or character
* device resident on the filesystem.
*/
if ((a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
switch (vp->v_type) {
case VREG:
case VDIR:
case VLNK:
return (EROFS);
default:
break;
}
}
/* we cant open an exclusive open file here */
if( U9P_PERM_EXCL(np->n_dir.dir_mode) )
return 0;
/* check permission by actually opening it */
/* translate mode */
mode = 0;
if( a_mode & VREAD ) {
fidp = &np->n_rdfid;
mode = U9P_MODE_RD;
}
if( a_mode & VWRITE ) {
fidp = &np->n_wrfid;
mode = U9P_MODE_WR;
}
if( (a_mode & (VREAD|VWRITE)) == (VREAD|VWRITE) ) {
fidp2 = &np->n_rdfid;
mode = U9P_MODE_RDWR;
}
if( a_mode & VEXEC ) {
fidp = &np->n_rdfid;
if( vp->v_type == VREG )
mode = U9P_MODE_EX;
}
if( fidp2 == 0 )
fidp2 = fidp;
/* open fid mode */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
newfid = u9fs_id_new(nmp->nm_fids);
req.r_type = Tclone;
req.r_fid = np->n_fid;
req.r_newfid = newfid;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
req.r_type = Topen;
req.r_fid = newfid;
req.r_mode = mode;
error = u9fs_request(&req, &rep, 1);
u9fs_updtcache(np, &rep);
if( error || (*fidp && *fidp2 ) ) {
u9fs_free_fid(newfid, nmp, p);
return error;
}
*fidp = *fidp2 = newfid;
return (0);
}
/*
* u9fs open vnode op
* Check to see if the type is ok
* and that deletion is not in progress.
* For paged in text files, you will need to flush the page cache
* if consistency is lost.
*/
/* ARGSUSED */
static int
u9fs_open(ap)
struct vop_open_args /* {
struct vnode *a_vp;
int a_mode;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
struct u9fsnode *np = VTOU9FS(vp);
int error=0, a_mode = ap->a_mode;
u_short * fidp = 0, *fidp2 = 0, newfid;
struct u9fsmount * nmp = VFSTOU9FS(vp->v_mount);
struct proc * p = ap->a_p;
struct u9fsreq req, rep;
u_char mode;
struct ucred * cred = ap->a_cred;
/* assume access permissions have been checked via VOP_ACCESS */
/* the file is actually opened except the rdwr case */
if( a_mode & (O_EXCL|O_SHLOCK|O_EXLOCK) ) {
#if 0 /* XXX: what can we do here? */
return (EOPNOTSUPP);
#endif
}
/* translate mode */
mode = 0;
if( a_mode & FREAD ) {
fidp = &np->n_rdfid;
mode = U9P_MODE_RD;
}
if( a_mode & FWRITE ) {
fidp = &np->n_wrfid;
mode = U9P_MODE_WR;
}
if( (a_mode & (FREAD|FWRITE)) == (FREAD|FWRITE) ) {
fidp2 = & np->n_rdfid;
mode = U9P_MODE_RDWR;
}
if( fidp2 == 0)
fidp2 = fidp;
if( U9P_PERM_EXCL(np->n_dir.dir_mode) ) {
if( *fidp || *fidp2 )
return ENOLCK;
/* open fid mode */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
newfid = u9fs_id_new(nmp->nm_fids);
req.r_type = Tclone;
req.r_fid = np->n_fid;
req.r_newfid = newfid;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
req.r_type = Topen;
req.r_fid = newfid;
req.r_mode = mode;
error = u9fs_request(&req, &rep, 1);
if( error ) {
u9fs_free_fid(newfid, nmp, p);
return error;
}
u9fs_updtcache(np, &rep);
*fidp = *fidp2 = newfid;
}
if( *fidp == 0 )
panic("open");
if( *fidp2 == 0 ) {
/* open fid mode */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
newfid = u9fs_id_new(nmp->nm_fids);
req.r_type = Tclone;
req.r_fid = np->n_fid;
req.r_newfid = newfid;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
req.r_type = Topen;
req.r_fid = newfid;
req.r_mode = mode;
error = u9fs_request(&req, &rep, 1);
if( error ) {
u9fs_free_fid(newfid, nmp, p);
return error;
}
u9fs_updtcache(np, &rep);
*fidp2 = newfid;
}
if( np->n_qid.vers != np->n_dir.dir_qid.vers ) /* content changed */
u9fs_vinvalbuf(vp, 0, cred, p, 0);
return 0;
}
/*
* u9fs close vnode op
* What an U9FS client should do upon close after writing is a debatable issue.
* Most U9FS clients push delayed writes to the server upon close, basically for
* two reasons:
* 1 - So that any write errors may be reported back to the client process
* doing the close system call. By far the two most likely errors are
* U9FSERR_NOSPC and U9FSERR_DQUOT to indicate space allocation failure.
* 2 - To put a worst case upper bound on cache inconsistency between
* multiple clients for the file.
* There is also a consistency problem for Version 2 of the protocol w.r.t.
* not being able to tell if other clients are writing a file concurrently,
* since there is no way of knowing if the changed modify time in the reply
* is only due to the write for this client.
* (U9FS Version 3 provides weak cache consistency data in the reply that
* should be sufficient to detect and handle this case.)
*
* The current code does the following:
* for U9FS Version 2 - play it safe and flush/invalidate all dirty buffers
* for U9FS Version 3 - flush dirty buffers to the server but don't invalidate
* or commit them (this satisfies 1 and 2 except for the
* case where the server crashes after this close but
* before the commit RPC, which is felt to be "good
* enough". Changing the last argument to u9fs_flush() to
* a 1 would force a commit operation, if it is felt a
* commit is necessary now.
* for NQU9FS - do nothing now, since 2 is dealt with via leases and
* 1 should be dealt with via an fsync() system call for
* cases where write errors are important.
*/
/* ARGSUSED */
static int
u9fs_close(ap)
struct vop_close_args /* {
struct vnodeop_desc *a_desc;
struct vnode *a_vp;
int a_fflag;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
int fflag = ap->a_fflag;
struct vnode * vp = ap->a_vp;
struct u9fsnode * np = VTOU9FS(vp);
struct u9fsmount * nmp = VFSTOU9FS(vp->v_mount);
struct proc * p = ap->a_p;
if( U9P_PERM_EXCL(np->n_dir.dir_mode) ) {
if( (fflag & FREAD) ) {
u9fs_free_fid(np->n_rdfid, nmp, p);
np->n_rdfid = 0;
}
if( (fflag & FWRITE) == FWRITE ) {
u9fs_free_fid(np->n_wrfid, nmp, p);
np->n_wrfid = 0;
}
if( (fflag & (FREAD|FWRITE)) == (FREAD|FWRITE) )
np->n_wrfid = 0;
}
return 0;
}
/*
* u9fs getattr call from vfs.
*/
static int
u9fs_getattr(ap)
struct vop_getattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct u9fsnode *np = VTOU9FS(vp);
int error = 0;
struct u9fsreq req, rep;
struct u9fsmount * nmp = VFSTOU9FS(vp->v_mount);
struct u9fsdir * dir;
struct vattr * vap = ap->a_vap;
/*
* Update local times for special files.
*/
if (np->n_flag & (NACC | NUPD))
np->n_flag |= NCHG;
#if 0
/*
* First look in the cache.
*/
if (u9fs_getattrcache(vp, ap->a_vap) == 0)
return (0);
#endif
if( np->n_fid == 0 )
panic("u9fs_getattr");
/* stat fid */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = ap->a_p;
req.r_type = Tstat;
req.r_fid = np->n_fid;
error = u9fs_request(& req, & rep, 1);
if( error )
return error;
/* fill in vattr */
dir = & np->n_dir;
u9p_m2d(rep.r_stat, dir);
bzero(vap, sizeof(*vap));
/* the plan9 file system has no other types. */
/* XXX: we have not delt with devices yet */
if( U9P_PERM_CHDIR(dir->dir_mode) )
vap->va_type = VDIR;
else
vap->va_type = VREG;
vap->va_mode = U9P_PERM_ALL(dir->dir_mode);
vap->va_nlink = 1;
vap->va_uid = u9fs_name2uid(dir->dir_uid);
vap->va_gid = u9fs_name2uid(dir->dir_gid);
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
vap->va_fileid = dir->dir_qid.path;
vap->va_size = np->n_size = dir->dir_length;
vap->va_blocksize = PAGE_SIZE;
vap->va_atime.tv_sec = dir->dir_atime;
vap->va_atime.tv_nsec = 0;
vap->va_mtime.tv_sec = dir->dir_mtime;
vap->va_mtime.tv_nsec = 0;
vap->va_ctime.tv_sec = dir->dir_mtime;
vap->va_ctime.tv_nsec = dir->dir_mtime;
vap->va_gen = VNOVAL;
vap->va_flags = 0;
vap->va_bytes = vap->va_size;
vap->va_filerev = dir->dir_qid.vers;
vp->v_type = vap->va_type;
vp->v_tag = VT_U9FS;
return (error);
}
/*
* u9fs setattr call.
*/
static int
u9fs_setattr(ap)
struct vop_setattr_args /* {
struct vnodeop_desc *a_desc;
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct u9fsnode *np = VTOU9FS(vp);
register struct vattr *vap = ap->a_vap;
int error = 0;
struct u9fsmount * nmp = VFSTOU9FS(vp->v_mount);
struct u9fsdir dir;
struct u9fsuser * u9p;
struct vattr attr;
struct u9fsreq req, rep;
if( vp->v_mount->mnt_flag & MNT_RDONLY )
return (EROFS);
if( vap->va_nlink != VNOVAL || vap->va_uid != VNOVAL ||
vap->va_fsid != VNOVAL || vap->va_fileid != VNOVAL ||
#if 0
vap->va_size != VNOVAL || vap->va_blocksize != VNOVAL ||
#endif
vap->va_atime.tv_sec != VNOVAL || vap->va_ctime.tv_sec != VNOVAL ||
vap->va_gen != VNOVAL ||
vap->va_flags != VNOVAL || vap->va_bytes != VNOVAL ) {
#if 0
printf("%d %d %d %d %d %d %d %d %d %d %d\n", vap->va_nlink, vap->va_uid, vap->va_fsid,
vap->va_fileid, vap->va_size, vap->va_blocksize,
vap->va_atime.tv_sec, vap->va_ctime.tv_sec, vap->va_gen,
vap->va_flags, vap->va_bytes);
printf("unsupported setattr\n");
/* touch tries to change ctime first.
* if fails, it touches the first byte
*/
#endif
return (EOPNOTSUPP);
}
if( vap->va_size == 0 )
u9fs_trunc(vp, ap->a_cred, ap->a_p);
bcopy(&np->n_dir, &dir, sizeof(dir));
if( vap->va_mode != (mode_t)VNOVAL ) {
dir.dir_mode = U9P_PERM_NONPERM(dir.dir_mode)|U9P_PERM_ALL(vap->va_mode);
}
if( vap->va_gid != VNOVAL ) {
if( (u9p = u9fs_finduser(vap->va_gid)) == 0 )
return (EINVAL);
strncpy(u9p->u_name, dir.dir_gid, U9FS_NAMELEN);
}
if( vap->va_mtime.tv_sec != VNOVAL ) {
dir.dir_mtime = vap->va_mtime.tv_sec;
}
/* stat fid */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = ap->a_p;
req.r_type = Twstat;
req.r_fid = np->n_fid;
u9p_d2m(&dir, req.r_stat);
error = u9fs_request(& req, & rep, 1);
if( error )
return error;
VOP_GETATTR(vp, &attr, ap->a_cred, ap->a_p);
return 0;
}
/*
* u9fs lookup call, one step at a time...
* First look in cache
* If not found, unlock the directory u9fsnode and do the rpc
*/
static int
u9fs_lookup(ap)
struct vop_lookup_args /* {
struct vnodeop_desc *a_desc;
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
} */ *ap;
{
struct componentname *cnp = ap->a_cnp;
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
int flags = cnp->cn_flags;
struct vnode *newvp;
struct u9fsmount *nmp;
long len;
u9fsfh_t fh;
struct u9fsnode *np;
int lockparent, wantparent, error = 0;
struct proc *p = cnp->cn_proc;
struct u9fsreq req, rep;
u_short newfid;
struct vattr attrs;
*vpp = NULLVP;
if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return (EROFS);
if (dvp->v_type != VDIR)
return (ENOTDIR);
lockparent = flags & LOCKPARENT;
wantparent = flags & (LOCKPARENT|WANTPARENT);
nmp = VFSTOU9FS(dvp->v_mount);
np = VTOU9FS(dvp);
#if 0
if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) {
struct vattr vattr;
int vpid;
if (error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, p)) {
*vpp = NULLVP;
return (error);
}
newvp = *vpp;
vpid = newvp->v_id;
/*
* See the comment starting `Step through' in ufs/ufs_lookup.c
* for an explanation of the locking protocol
*/
if (dvp == newvp) {
VREF(newvp);
error = 0;
} else if (flags & ISDOTDOT) {
VOP_UNLOCK(dvp, 0, p);
error = vget(newvp, LK_EXCLUSIVE, p);
if (!error && lockparent && (flags & ISLASTCN))
error = vn_lock(dvp, LK_EXCLUSIVE, p);
} else {
error = vget(newvp, LK_EXCLUSIVE, p);
if (!lockparent || error || !(flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
}
if (!error) {
if (vpid == newvp->v_id) {
if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, p)
&& vattr.va_ctime.tv_sec == VTOU9FS(newvp)->n_ctime) {
u9fsstats.lookupcache_hits++;
if (cnp->cn_nameiop != LOOKUP &&
(flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
return (0);
}
cache_purge(newvp);
}
vput(newvp);
if (lockparent && dvp != newvp && (flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
}
error = vn_lock(dvp, LK_EXCLUSIVE, p);
*vpp = NULLVP;
if (error)
return (error);
}
#endif
error = 0;
newvp = NULLVP;
len = cnp->cn_namelen;
/* Tclwalk tag fid newfid name */
bzero(&req, sizeof(req));
req.r_procp = p;
req.r_nmp = nmp;
req.r_type = Tclwalk;
req.r_fid = np->n_fid;
newfid = req.r_newfid = u9fs_id_new(nmp->nm_fids);
bcopy(cnp->cn_nameptr, req.r_name, len);
if( (error = u9fs_request(&req, &rep, 1)) ) {
u9fs_id_free(nmp->nm_fids, newfid);
return error;
}
fh = rep.r_qid.path;
if( fh == 0 ) {
u9fs_id_free(nmp->nm_fids, newfid);
error = ENOENT;
goto lastcheck;
}
/*
* Handle RENAME case...
*/
if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
#if 0
/* XXX: I dont understand this. rename foo foo? */
if (U9FS_CMPFH(np, fhp, fhsize)) {
m_freem(mrep);
return (EISDIR);
}
#endif
error = u9fs_nget(dvp->v_mount, fh, &np, p);
if (error)
goto fail;
if ( np->n_fid )
u9fs_free_fid(newfid, nmp, p);
else
np->n_fid = newfid;
newvp = U9FSTOV(np);
*vpp = newvp;
cnp->cn_flags |= SAVENAME;
if (!lockparent)
VOP_UNLOCK(dvp, 0, p);
return (0);
}
if (flags & ISDOTDOT) {
VOP_UNLOCK(dvp, 0, p);
error = u9fs_nget(dvp->v_mount, fh, &np, p);
if (error) {
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
goto fail;
}
if( np->n_fid )
u9fs_free_fid(newfid, nmp, p);
else
np->n_fid = req.r_newfid;
newvp = U9FSTOV(np);
if (lockparent && (flags & ISLASTCN) &&
(error = vn_lock(dvp, LK_EXCLUSIVE, p))) {
vput(newvp);
return (error);
}
} else if (np->n_qid.path == fh) {
u9fs_free_fid(newfid, nmp, p);
VREF(dvp);
newvp = dvp;
} else {
error = u9fs_nget(dvp->v_mount, fh, &np, p);
if (error)
goto fail;
if( np->n_fid )
u9fs_free_fid(newfid, nmp, p);
else
np->n_fid = req.r_newfid;
if (!lockparent || !(flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
newvp = U9FSTOV(np);
VOP_GETATTR(newvp, & attrs, p->p_ucred, p);
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
#if 0
if ((cnp->cn_flags & MAKEENTRY) &&
(cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) {
np->n_ctime = np->n_vattr.va_ctime.tv_sec;
cache_enter(dvp, newvp, cnp);
}
#endif
*vpp = newvp;
lastcheck:
if (error) {
if (newvp != NULLVP) {
vrele(newvp);
*vpp = NULLVP;
}
if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) &&
(flags & ISLASTCN) && error == ENOENT) {
if (!lockparent)
VOP_UNLOCK(dvp, 0, p);
if (dvp->v_mount->mnt_flag & MNT_RDONLY)
error = EROFS;
else
error = EJUSTRETURN;
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
}
return (error);
fail:
u9fs_free_fid(newfid, nmp, p);
return (error);
}
/*
* u9fs read call.
* Just call u9fs_bioread() to do the work.
*/
static int
u9fs_read(ap)
struct vop_read_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
if (vp->v_type != VREG)
return (EPERM);
return (u9fs_bioread(vp, ap->a_uio, ap->a_ioflag, ap->a_cred, 0));
}
/*
* u9fs readlink call
*/
static int
u9fs_readlink(ap)
struct vop_readlink_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
} */ *ap;
{
return (EOPNOTSUPP);
}
/*
* u9fs mknod vop
* just call u9fs_mknodrpc() to do the work.
*/
/* ARGSUSED */
static int
u9fs_mknod(ap)
struct vop_mknod_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vattr *a_vap;
} */ *ap;
{
return (EOPNOTSUPP);
}
/*
* u9fs file create call
*/
static int
u9fs_create(ap)
struct vop_create_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vattr *a_vap;
} */ *ap;
{
register struct vnode *dvp = ap->a_dvp;
register struct vattr *vap = ap->a_vap;
register struct componentname *cnp = ap->a_cnp;
struct u9fsnode *np = (struct u9fsnode *)0;
struct vnode *newvp = (struct vnode *)0;
int error = 0, len;
struct vattr vattr;
struct u9fsreq req, rep;
struct u9fsmount *nmp;
u9fsfh_t fh;
struct proc * p;
int pfid;
#if 0
/*
* Oops, not for me..
*/
if (vap->va_type == VSOCK)
return (u9fs_mknodrpc(dvp, ap->a_vpp, cnp, vap));
#endif
if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) {
VOP_ABORTOP(dvp, cnp);
return (error);
}
nmp = VFSTOU9FS(dvp->v_mount);
np = VTOU9FS(dvp);
p = cnp->cn_proc;
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
req.r_type = Tclone;
pfid = req.r_fid = np->n_fid;
req.r_newfid = u9fs_id_new(nmp->nm_fids);
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
req.r_type = Tcreate;
req.r_fid = req.r_newfid;
len = cnp->cn_namelen;
if( len > U9FS_NAMELEN )
len = U9FS_NAMELEN;
strncpy(req.r_name, cnp->cn_nameptr, len);
req.r_name[U9FS_NAMELEN] = 0;
req.r_perm = U9P_PERM_ALL(vap->va_mode);
if( vap->va_type == VDIR ) {
req.r_perm |= 0x80000000;
req.r_mode = U9P_MODE_RD;
} else
req.r_mode = U9P_MODE_WR | U9P_MODE_TRUNC;
if(vap->va_vaflags & VA_EXCLUSIVE)
req.r_mode = U9P_MODE_EX;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
fh = rep.r_qid.path;
u9fs_nget(dvp->v_mount, fh, &np, p);
newvp = U9FSTOV(np);
if( vap->va_type == VDIR )
np->n_rdfid = req.r_fid;
else
np->n_wrfid = req.r_fid;
req.r_type = Tclwalk;
req.r_fid = pfid;
req.r_newfid = u9fs_id_new(nmp->nm_fids);
/* r_name is already filled */
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
np->n_fid = req.r_newfid;
VOP_GETATTR(newvp, & vattr, p->p_ucred, p);
*ap->a_vpp = newvp;
zfree(namei_zone, cnp->cn_pnbuf);
return 0;
}
/*
* u9fs file remove call
* To try and make u9fs semantics closer to ufs semantics, a file that has
* other processes using the vnode is renamed instead of removed and then
* removed later on the last close.
* - If v_usecount > 1
* If a rename is not already in the works
* call u9fs_sillyrename() to set it up
* else
* do the remove rpc
*/
static int
u9fs_remove(ap)
struct vop_remove_args /* {
struct vnodeop_desc *a_desc;
struct vnode * a_dvp;
struct vnode * a_vp;
struct componentname * a_cnp;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct componentname *cnp = ap->a_cnp;
struct u9fsnode *np;
struct u9fsreq req, rep;
struct u9fsmount *nmp;
struct proc * p;
int error;
nmp = VFSTOU9FS(vp->v_mount);
np = VTOU9FS(vp);
p = cnp->cn_proc;
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
req.r_type = Tremove;
req.r_fid = np->n_fid;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
zfree(namei_zone, cnp->cn_pnbuf);
return 0;
}
/*
* u9fs file rename call
*/
static int
u9fs_rename(ap)
struct vop_rename_args /* {
struct vnode *a_fdvp;
struct vnode *a_fvp;
struct componentname *a_fcnp;
struct vnode *a_tdvp;
struct vnode *a_tvp;
struct componentname *a_tcnp;
} */ *ap;
{
register struct vnode *fvp = ap->a_fvp;
register struct vnode *tvp = ap->a_tvp;
register struct vnode *fdvp = ap->a_fdvp;
register struct vnode *tdvp = ap->a_tdvp;
register struct componentname *tcnp = ap->a_tcnp;
register struct componentname *fcnp = ap->a_fcnp;
int error, len;
struct u9fsmount * nmp;
struct u9fsreq req, rep;
struct u9fsdir dir;
struct u9fsnode * np;
/* we cant do cross-directory renaming or move to an existing file */
if( fdvp != tdvp || tvp != 0 || fvp->v_mount->mnt_flag & MNT_RDONLY ){
printf("rename to existing file not supported\n");
error = EOPNOTSUPP;
goto out;
}
nmp = VFSTOU9FS(fvp->v_mount);
np = VTOU9FS(fvp);
bcopy(&np->n_dir, &dir, sizeof(dir));
len = tcnp->cn_namelen;
if( len > U9FS_NAMELEN )
len = U9FS_NAMELEN;
strncpy(dir.dir_name, tcnp->cn_nameptr, len);
dir.dir_name[U9FS_NAMELEN-1] = 0;
/* stat fid */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = fcnp->cn_proc;
req.r_type = Twstat;
req.r_fid = np->n_fid;
u9p_d2m(&dir, req.r_stat);
error = u9fs_request(& req, & rep, 1);
out:
if (tdvp == tvp)
vrele(tdvp);
else
vput(tdvp);
if (tvp)
vput(tvp);
vrele(fdvp);
vrele(fvp);
return error;
}
/*
* u9fs hard link create call
*/
static int
u9fs_link(ap)
struct vop_link_args /* {
struct vnode *a_tdvp;
struct vnode *a_vp;
struct componentname *a_cnp;
} */ *ap;
{
return (EOPNOTSUPP);
}
/*
* u9fs symbolic link create call
*/
static int
u9fs_symlink(ap)
struct vop_symlink_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vattr *a_vap;
char *a_target;
} */ *ap;
{
return (EOPNOTSUPP);
}
/*
* u9fs make dir call
*/
static int
u9fs_mkdir(ap)
struct vop_mkdir_args /* {
struct vnode *a_dvp;
struct vnode **a_vpp;
struct componentname *a_cnp;
struct vattr *a_vap;
} */ *ap;
{
struct vop_create_args cap;
cap.a_dvp = ap->a_dvp;
cap.a_vpp = ap->a_vpp;
cap.a_cnp = ap->a_cnp;
cap.a_vap = ap->a_vap;
return u9fs_create(&cap);
}
/*
* u9fs remove directory call
*/
static int
u9fs_rmdir(ap)
struct vop_rmdir_args /* {
struct vnode *a_dvp;
struct vnode *a_vp;
struct componentname *a_cnp;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct componentname *cnp = ap->a_cnp;
struct u9fsnode *np;
struct u9fsreq req, rep;
struct u9fsmount *nmp;
struct proc * p;
int error;
nmp = VFSTOU9FS(vp->v_mount);
np = VTOU9FS(vp);
p = cnp->cn_proc;
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
req.r_type = Tremove;
req.r_fid = np->n_fid;
error = u9fs_request(&req, &rep, 1);
if( error )
return error;
u9fs_id_free(nmp->nm_fids, np->n_fid);
np->n_fid = 0;
zfree(namei_zone, cnp->cn_pnbuf);
return 0;
}
/*
* u9fs readdir call
*/
static int
u9fs_readdir(ap)
struct vop_readdir_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct uio *uio = ap->a_uio;
int error;
if (vp->v_type != VDIR)
return (EPERM);
/*
* Call u9fs_bioread() to do the real work.
*/
error = u9fs_bioread(vp, uio, 0, ap->a_cred, 0);
return (error);
}
/*
* Kludge City..
* - make u9fs_bmap() essentially a no-op that does no translation
* - do u9fs_strategy() by doing I/O with u9fs_readrpc/u9fs_writerpc
* (Maybe I could use the process's page mapping, but I was concerned that
* Kernel Write might not be enabled and also figured copyout() would do
* a lot more work than bcopy() and also it currently happens in the
* context of the swapper process (2).
*/
static int
u9fs_bmap(ap)
struct vop_bmap_args /* {
struct vnode *a_vp;
daddr_t a_bn;
struct vnode **a_vpp;
daddr_t *a_bnp;
int *a_runp;
int *a_runb;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
if (ap->a_vpp != NULL)
*ap->a_vpp = vp;
if (ap->a_bnp != NULL)
*ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
if (ap->a_runp != NULL)
*ap->a_runp = 0;
if (ap->a_runb != NULL)
*ap->a_runb = 0;
return (0);
return 0;
}
/*
* Strategy routine.
* For async requests when u9fsiod(s) are running, queue the request by
* calling u9fs_asyncio(), otherwise just all u9fs_doio() to do the
* request.
*/
static int
u9fs_strategy(ap)
struct vop_strategy_args *ap;
{
register struct buf *bp = ap->a_bp;
struct ucred *cr;
struct proc *p;
int error = 0;
if (bp->b_flags & B_PHYS)
panic("nfs physio");
if (bp->b_flags & B_ASYNC)
panic("u9fs async");
p = curproc; /* XXX */
if (bp->b_flags & B_READ)
cr = bp->b_rcred;
else
cr = bp->b_wcred;
error = u9fs_doio(bp, cr, p);
return (error);
}
/*
* Mmap a file
*
* NB Currently unsupported.
*/
/* ARGSUSED */
static int
u9fs_mmap(ap)
struct vop_mmap_args /* {
struct vnode *a_vp;
int a_fflags;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
return (EINVAL);
}
/*
* fsync vnode op. Just call u9fs_flush() with commit == 1.
*/
/* ARGSUSED */
static int
u9fs_fsync(ap)
struct vop_fsync_args /* {
struct vnodeop_desc *a_desc;
struct vnode * a_vp;
struct ucred * a_cred;
int a_waitfor;
struct proc * a_p;
} */ *ap;
{
/* we have a blocking writeback cache */
return 0;
}
/*
* U9FS advisory byte-level locks.
* Currently unsupported.
*/
static int
u9fs_advlock(ap)
struct vop_advlock_args /* {
struct vnode *a_vp;
caddr_t a_id;
int a_op;
struct flock *a_fl;
int a_flags;
} */ *ap;
{
register struct u9fsnode *np = VTOU9FS(ap->a_vp);
/*
* The following kludge is to allow diskless support to work
* until a real NFS lockd is implemented. Basically, just pretend
* that this is a local lock.
*/
return (lf_advlock(ap, &(np->n_lockf), np->n_size));
}
/*
* Print out the contents of an u9fsnode.
*/
static int
u9fs_print(ap)
struct vop_print_args /* {
struct vnode *a_vp;
} */ *ap;
{
panic("u9fs_print");
return 0;
}
/*
* Just call u9fs_writebp() with the force argument set to 1.
*/
static int
u9fs_bwrite(ap)
struct vop_bwrite_args /* {
struct vnode *a_bp;
} */ *ap;
{
panic("u9fs_bwrite");
return 0;
}
/*
* Vnode op for VM getpages.
*/
static int
u9fs_getpages(ap)
struct vop_getpages_args /* {
struct vnode *a_vp;
vm_page_t *a_m;
int a_count;
int a_reqpage;
vm_ooffset_t a_offset;
} */ *ap;
{
int i, error, nextoff, size, toff, npages, count;
struct uio uio;
struct iovec iov;
vm_offset_t kva;
struct buf *bp;
struct vnode *vp;
struct proc *p;
struct ucred *cred;
struct u9fsmount *nmp;
vm_page_t *pages;
vp = ap->a_vp;
p = curproc; /* XXX */
cred = curproc->p_ucred; /* XXX */
nmp = VFSTOU9FS(vp->v_mount);
pages = ap->a_m;
count = ap->a_count;
if (vp->v_object == NULL) {
printf("u9fs_getpages: called with non-merged cache vnode??\n");
return VM_PAGER_ERROR;
}
/*
* We use only the kva address for the buffer, but this is extremely
* convienient and fast.
*/
bp = getpbuf();
npages = btoc(count);
kva = (vm_offset_t) bp->b_data;
pmap_qenter(kva, pages, npages);
iov.iov_base = (caddr_t) kva;
iov.iov_len = count;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = IDX_TO_OFF(pages[0]->pindex);
uio.uio_resid = count;
uio.uio_segflg = UIO_SYSSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = p;
error = u9fs_readrpc(vp, &uio, cred);
pmap_qremove(kva, npages);
relpbuf(bp);
if (error && (uio.uio_resid == count))
return VM_PAGER_ERROR;
size = count - uio.uio_resid;
for (i = 0, toff = 0; i < npages; i++, toff = nextoff) {
vm_page_t m;
nextoff = toff + PAGE_SIZE;
m = pages[i];
m->flags &= ~PG_ZERO;
if (nextoff <= size) {
m->valid = VM_PAGE_BITS_ALL;
m->dirty = 0;
} else {
int nvalid = ((size + DEV_BSIZE - 1) - toff) & ~(DEV_BSIZE - 1);
vm_page_set_validclean(m, 0, nvalid);
}
if (i != ap->a_reqpage) {
/*
* Whether or not to leave the page activated is up in
* the air, but we should put the page on a page queue
* somewhere (it already is in the object). Result:
* It appears that emperical results show that
* deactivating pages is best.
*/
/*
* Just in case someone was asking for this page we
* now tell them that it is ok to use.
*/
if (!error) {
if (m->flags & PG_WANTED)
vm_page_activate(m);
else
vm_page_deactivate(m);
vm_page_wakeup(m);
} else {
vnode_pager_freepage(m);
}
}
}
return 0;
}
/*
* Vnode op for VM putpages.
*/
static int
u9fs_putpages(ap)
struct vop_putpages_args /* {
struct vnode *a_vp;
vm_page_t *a_m;
int a_count;
int a_sync;
int *a_rtvals;
vm_ooffset_t a_offset;
} */ *ap;
{
panic("u9fs_putpages");
return 0;
}
static int
u9fs_inactive(ap)
struct vop_inactive_args /* {
struct vnode *a_vp;
struct proc *a_p;
} */ *ap;
{
VOP_UNLOCK(ap->a_vp, 0, ap->a_p);
return 0;
}
/*
* Reclaim an u9fsnode so that it can be used for other purposes.
*/
static int
u9fs_reclaim(ap)
struct vop_reclaim_args /* {
struct vnode *a_vp;
} */ *ap;
{
register struct vnode *vp = ap->a_vp;
register struct u9fsnode *np = VTOU9FS(vp);
register struct u9fsmount *nmp = VFSTOU9FS(vp->v_mount);
struct proc * p = curproc;
/* some vnodes do not have fids due to previous access failure */
if( np->n_fid ) {
/* clunk fids */
u9fs_free_fid(np->n_fid, nmp, p);
if( np->n_rdfid )
u9fs_free_fid(np->n_rdfid, nmp, p);
if( np->n_wrfid )
u9fs_free_fid(np->n_wrfid, nmp, p);
}
LIST_REMOVE(np, n_hash);
cache_purge(vp);
zfree(u9fsnode_zone, vp->v_data);
vp->v_data = (void *)0;
return (0);
}
/*
* Vnode op for write using bio
*/
static int
u9fs_write(ap)
struct vop_write_args /* {
struct vnode *a_vp;
struct uio *a_uio;
int a_ioflag;
struct ucred *a_cred;
} */ *ap;
{
if (ap->a_vp->v_type != VREG)
return (EIO);
return u9fs_biowrite(ap->a_vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
}
/*
* Nfs abort op, called after namei() when a CREATE/DELETE isn't actually
* done. Currently nothing to do.
*/
/* ARGSUSED */
static int
u9fs_abortop(ap)
struct vop_abortop_args /* {
struct vnode *a_dvp;
struct componentname *a_cnp;
} */ *ap;
{
return (0);
}
/*
* u9fs write call
*/
int
u9fs_writerpc(vp, uiop, cred)
register struct vnode *vp;
register struct uio *uiop;
struct ucred *cred;
{
struct u9fsmount *nmp = VFSTOU9FS(vp->v_mount);
int error = 0, len, tsiz, rlen;
struct u9fsreq req, rep;
struct u9fsnode * np = VTOU9FS(vp);
struct proc * p = uiop->uio_procp;
struct mbuf * top;
tsiz = uiop->uio_resid;
if (uiop->uio_offset + tsiz > nmp->nm_maxfilesize)
return (EFBIG);
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
req.r_type = Twrite;
req.r_fid = np->n_wrfid;
while (tsiz > 0) {
len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz;
req.r_offset = uiop->uio_offset;
req.r_count = len;
error = u9fs_uiotombuf(uiop, &top, len);
if( error )
break;
req.r_data = (char *)top;
error = u9fs_request(&req, &rep, 1);
if( error )
break;
rlen = rep.r_count;
if( rlen < len ) {
error = EIO;
break;
}
tsiz -= len;
/* each write message increments version number by one.
to avoid flushing our write cache, update the version */
if( np->n_qid.vers )
np->n_qid.vers++;
else
np->n_qid.vers = np->n_dir.dir_qid.vers + 1;
}
if (error)
uiop->uio_resid = tsiz;
return (error);
}
/*
* Readdir rpc call.
* Called from below the buffer cache by u9fs_doio().
*/
int
u9fs_readdirrpc(vp, uiop, cred)
struct vnode *vp;
register struct uio *uiop;
struct ucred *cred;
{
register int len, left;
register struct dirent *dp;
struct u9fsmount *nmp = VFSTOU9FS(vp->v_mount);
struct u9fsnode *np = VTOU9FS(vp);
int error = 0, tlen, more_dirs = 1, bigenough;
struct u9fsreq req, rep;
int count;
struct u9fsdir u9dir;
bigenough = uiop->uio_resid >= sizeof(struct dirent);
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_type = Tread;
req.r_fid = np->n_rdfid;
req.r_count = nmp->nm_readdirsize;
while ( more_dirs && bigenough ) {
req.r_offset = uiop->uio_offset;
error = u9fs_request(&req, &rep, 0);
if( error )
return error;
count = rep.r_count;
more_dirs = (count == req.r_count);
len = 0;
dp = (struct dirent *)uiop->uio_iov->iov_base;
left = uiop->uio_resid;
while( len < count ) {
/* XXX: too conservative, but OK */
if( left < sizeof(*dp) ) {
bigenough = 0;
break;
}
if( u9p_m_m2d(&req.r_mrep, & u9dir) ) {
printf("u9p_m_m2d failed!\n");
return (EIO);
}
dp->d_fileno = u9dir.dir_qid.path;
if( U9P_PERM_CHDIR(u9dir.dir_mode) )
dp->d_type = DT_DIR;
else
dp->d_type = DT_REG;
u9dir.dir_name[U9FS_NAMELEN-1] = 0; /* just to be sure */
dp->d_namlen = strlen(u9dir.dir_name);
memcpy(dp->d_name, u9dir.dir_name, dp->d_namlen+1);
tlen = DIRHDSIZ + dp->d_namlen + 4;
tlen = tlen - (tlen & 0x3);
dp->d_reclen = tlen;
dp = (struct dirent *)(((char *)dp) + tlen);
left -= tlen;
len += sizeof(u9dir);
}
tlen = uiop->uio_resid - left;
uiop->uio_resid = left;
uiop->uio_iov->iov_base += tlen;
uiop->uio_iov->iov_len -= tlen;
uiop->uio_offset += len;
m_freem(req.r_mrep);
}
return 0;
}
/*
* u9fs read rpc call
* Ditto above
*/
int
u9fs_readrpc(vp, uiop, cred)
register struct vnode *vp;
struct uio *uiop;
struct ucred *cred;
{
struct u9fsmount *nmp;
struct u9fsnode *np = VTOU9FS(vp);
int error = 0, len, retlen, tsiz;
struct u9fsreq req, rep;
nmp = VFSTOU9FS(vp->v_mount);
tsiz = uiop->uio_resid;
if (uiop->uio_offset + tsiz > nmp->nm_maxfilesize)
return (EFBIG);
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_type = Tread;
req.r_fid = np->n_rdfid;
while (tsiz > 0) {
len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz;
req.r_count = len;
req.r_offset = uiop->uio_offset;
error = u9fs_request(&req, &rep, 0);
if( error )
return error;
retlen = rep.r_count;
if( retlen && (error = u9fs_mbuftouio(req.r_mrep, uiop, retlen)) ) {
m_freem(req.r_mrep);
return error;
}
m_freem(req.r_mrep);
req.r_mrep = 0;
tsiz -= retlen;
if (retlen < len)
tsiz = 0;
}
return (0);
}
static void u9fs_free_fid(fid, nmp, p)
u_short fid;
struct u9fsmount * nmp;
struct proc * p;
{
struct u9fsreq req, rep;
/* clunk fid */
bzero(&req, sizeof(req));
req.r_nmp = nmp;
req.r_procp = p;
req.r_type = Tclunk;
req.r_fid = fid;
u9fs_request(&req, &rep, 1);
u9fs_id_free(nmp->nm_fids, fid);
}