2021-10-11 18:20:23 -03:00

1135 lines
27 KiB
C

#ident "@(#)rfs_vnodeops.c 1.1 92/07/30 SMI"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/dirent.h>
#include <sys/pathname.h>
#include <rpc/types.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/xdr.h>
#include <nfs/nfs.h>
#include <sys/stream.h>
#include <sys/fcntl.h>
#include <sys/mount.h>
#include <rfs/rfs_misc.h>
#include <rfs/nserve.h>
#include <rfs/rfs_node.h>
#include <rfs/rfs_mnt.h>
#include <rfs/sema.h>
#include <rfs/comm.h>
#include <sys/debug.h>
#include <rfs/rdebug.h>
#include <rfs/cirmgr.h>
#include <rfs/hetero.h>
#include <rfs/message.h>
static int rf_open();
static int rf_close();
static int rf_rdwr();
static int rf_ioctl();
static int rf_select();
static int rf_getattr();
static int rf_setattr();
static int rf_access();
static int rf_lookup();
static int rf_create();
static int rf_remove();
static int rf_link();
static int rf_rename();
static int rf_mkdir();
static int rf_rmdir();
static int rf_readdir();
static int rf_symlink();
static int rf_readlink();
static int rf_fsync();
static int rf_inactive();
static int rf_lockctl();
static int rf_fid();
static int rf_getpage();
static int rf_putpage();
static int rf_map();
static int rf_cmp();
static int rf_realvp();
static int rf_badop();
static int rf_cntl();
struct vnodeops rfs_vnodeops = {
rf_open,
rf_close,
rf_rdwr,
rf_ioctl,
rf_select,
rf_getattr,
rf_setattr,
rf_access,
rf_lookup,
rf_create,
rf_remove,
rf_link,
rf_rename,
rf_mkdir,
rf_rmdir,
rf_readdir,
rf_symlink,
rf_readlink,
rf_fsync,
rf_inactive,
rf_lockctl,
rf_fid,
rf_getpage,
rf_putpage,
rf_map,
rf_badop,
rf_cmp,
rf_realvp,
rf_cntl,
};
static int
rf_open(vpp, flag, cred)
register struct vnode **vpp;
int flag;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(*vpp);
sndd_t sdp = (sndd_t) NULL;
struct nodeinfo ninfo;
int error = 0;
DUPRINT3(DB_VNODE, "rfopen: vp %x, flag %x\n", *vpp, flag);
/*
* KLUDGE: don't do the open call if it was already opened by a
* VOP_CREAT().
*/
if (rfp->rfs_flags & RFSOPEN) {
rfp->rfs_flags &= ~RFSOPEN;
goto out;
}
/* Do the open */
if (error = du_open(rfp->rfs_name, rfp->rfs_psdp, cred,
(u_short) OMODETORFS(flag), 0, &sdp, &ninfo))
goto out;
/* Get client cache status on file */
rfp->rfs_sdp->sd_stat &= ~SDCACHE_MASK;
rfp->rfs_sdp->sd_stat |= sdp->sd_stat & SDCACHE_MASK;
rfp->rfs_vcode = sdp->sd_vcode;
/* Check the remote reference returned */
(void) transform_rfsnode(vpp, sdp, &ninfo);
/*
* Invalidate the cache if mandatory locking or write was done
* since close (version # check) or cache disabled
*/
/* rfcache_check(sd, vp) */
out:
DUPRINT2(DB_VNODE, "rfopen: result %d\n", error);
return (error);
}
static int
rf_close(vp, flag, count, cred)
struct vnode *vp;
int flag;
int count;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
int error;
DUPRINT3(DB_VNODE, "rf_close: vp %x, flag %x\n", vp, flag);
ASSERT(rfp->rfs_sdp);
error = du_close(rfp->rfs_sdp, FFLAGTORFS(flag), count, cred);
/* Get client cache status on file */
rfp->rfs_vcode = rfp->rfs_sdp->sd_vcode;
DUPRINT2(DB_VNODE, "rf_close: result %d\n", error);
return (error);
}
/*
* Read/write peculiarities:
* ioflag options
* a) IO_UNIT -- nothing special is done for this, i.e., it's not
* really supported.
* b) IO_SYNC -- S5R3 has a mode, FSYNC, which supports this. This
* can be passed with RFS read/write system call in the rq_mode field.
*/
static int
rf_rdwr(vp, uiop, rw, ioflag, cred)
struct vnode *vp;
struct uio *uiop;
enum uio_rw rw;
int ioflag;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
register sndd_t sdp = rfp->rfs_sdp;
int fflag = 0;
int error = 0;
int rwerror = 0;
DUPRINT6(DB_VNODE, "rfrdwr: vp %x, %s, base %x, len %d, ioflag %x\n",
vp, rw == UIO_READ ? "READ" : "WRITE", uiop->uio_iov->iov_base,
uiop->uio_iov->iov_len, ioflag);
ASSERT(rfp->rfs_sdp);
/* Translate the io options to RFSese */
fflag = FFLAGTORFS(uiop->uio_fmode);
if (ioflag & IO_SYNC)
fflag |= RFS_FSYNC;
if (ioflag & IO_APPEND)
fflag |= RFS_FAPPEND;
/* If the server has turned caching off, make sure cache is off */
if ((sdp->sd_stat & SDMNDLCK) || !(sdp->sd_stat & SDCACHE))
/* rfinval(sd, -1, 0) */;
/* Do the read/write */
if (rw == UIO_READ)
rwerror = du_read(sdp, uiop, fflag, cred);
else
rwerror = du_write(sdp, uiop, fflag, cred);
DUPRINT2(DB_VNODE, "rfrdwr: results %d\n", error);
return (rwerror ? rwerror : error);
}
static int
rf_ioctl(vp, com, data, flag, cred)
struct vnode *vp;
int com;
caddr_t data;
int flag;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
int error;
DUPRINT5(DB_VNODE, "rfioctl: vp %x, com %x, data %x, flag %x\n",
vp, com, data, flag);
if (!rfp->rfs_sdp) {
error = EBADF;
goto out;
}
/*
* Can only do ioctl between homogeneous machines, since you
* don't know what data elements are in the ioctl args
*/
if (GDP(rfp->rfs_sdp->sd_queue)->hetero != NO_CONV) {
error = EOPNOTSUPP;
goto out;
}
error = du_ioctl(rfp->rfs_sdp, com, data, flag, cred);
out:
DUPRINT2(DB_VNODE, "rfioctl: result %d\n", error);
return (error);
}
/*ARGSUSED*/
static int
rf_select(vp, which, cred)
struct vnode *vp;
int which;
struct ucred *cred;
{
return (EOPNOTSUPP);
}
static int
rf_getattr(vp, vap, cred)
struct vnode *vp;
struct vattr *vap;
struct ucred *cred;
{
register struct rfsnode *rfp = vtorfs(vp);
struct rfsmnt *rm = (struct rfsmnt *) vp->v_vfsp->vfs_data;
struct rfs_stat sb;
int error;
DUPRINT2(DB_VNODE, "rf_getattr: vp %x\n", vp);
/* Do FSTAT if true ref, else do STAT with name and parent ref */
if (rfp->rfs_sdp)
error = du_fstat(rfp->rfs_sdp, cred, &sb);
else
error = du_stat(rfp->rfs_name, rfp->rfs_psdp, cred, &sb);
/* Put the results in the attribute structure */
if (!error) {
vap->va_type = RFSTOVT(sb.st_mode);
vap->va_mode = sb.st_mode; /* Same bits for S5 and sun */
vap->va_uid = sb.st_uid;
vap->va_gid = sb.st_gid;
vap->va_fsid =
0x0ffff & (long) makedev(rm->rm_mntno, 0x00ff & sb.st_dev);
vap->va_nodeid = sb.st_ino;
vap->va_nlink = sb.st_nlink;
vap->va_size = (unsigned) sb.st_size;
vap->va_rdev = sb.st_rdev;
vap->va_atime.tv_sec = sb.st_atime;
vap->va_atime.tv_usec = 0;
vap->va_mtime.tv_sec = sb.st_mtime;
vap->va_mtime.tv_usec = 0;
vap->va_ctime.tv_sec = sb.st_ctime;
vap->va_ctime.tv_usec = 0;
/* These values derive from statfs done at mount time */
vap->va_blocksize = rm->rm_bsize;
vap->va_blocks = sb.st_size / rm->rm_bsize +
(sb.st_size % rm->rm_bsize != 0);
}
DUPRINT6(DB_VNODE, "rf_getattr: rslt %d, type %d, mode %o, nodeid %d, size %d\n", error, vap->va_type, vap->va_mode, vap->va_nodeid, vap->va_size);
return (error);
}
/*
* rf_setattr --
* 1)Depending on how many attributes you want to set, this routine
* may require several RFS system calls. This means that in case
* of an error return some of the file attributes may be modified
* to the requested values and some may not be. This is only a problem
* if you attempt to change multiple attrbutes at once, something not
* currently done by any usages of VOP_SETATTR in Sun's kernel.
* 2)Note the restrictions on what can be set:
* a) va_size can only be set to 0.
* b) If setting va_atime or va_mtime both must be set.
*/
static int
rf_setattr(vp, vap, cred)
register struct vnode *vp;
register struct vattr *vap;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
sndd_t gift = (sndd_t) NULL;
struct nodeinfo ninfo;
long atime, mtime;
uid_t uid;
gid_t gid;
int size;
int error;
struct rfs_stat sb;
DUPRINT6(DB_VNODE,
"rfsetattr: vp %x, sz %d, mode %x, uid %d, atim %d\n",
vp, vap->va_size, vap->va_mode, vap->va_uid, vap->va_atime.tv_sec);
/* Set size (only support 0 size) */
size = vap->va_size;
if (size > 0) {
error = EOPNOTSUPP;
goto out;
} else if (size == 0) {
error = du_open(rfp->rfs_name, rfp->rfs_psdp, cred,
(u_short) (RFS_O_WRONLY|RFS_O_TRUNC), 0,
&gift, &ninfo);
if (error) goto out;
error = du_close(gift, 0, 1, cred);
/* Get client cache status on file */
rfp->rfs_sdp->sd_stat &= ~SDCACHE_MASK;
rfp->rfs_sdp->sd_stat |= gift->sd_stat & SDCACHE_MASK;
rfp->rfs_vcode = gift->sd_vcode;
error = del_sndd(gift); /* Free remote reference from open() */
if (error) goto out;
/* Invalidate the cache rfs_cache_check(vp, sdp); */
}
/* Set mode */
if (vap->va_mode != (u_short) -1) {
error = du_chmod(rfp->rfs_name, rfp->rfs_psdp, cred,
vap->va_mode);
if (error)
goto out;
}
/* Set uid, gid */
uid = vap->va_uid; gid = vap->va_gid;
if (uid != (uid_t) -1 || gid != (gid_t) -1) {
/* Use current values if requestor doesn't want to set both */
if (uid == (uid_t) -1 || gid == (gid_t) -1) {
error = du_fstat(rfp->rfs_sdp, cred, &sb);
if (error) goto out;
if (uid == (uid_t) -1)
uid = sb.st_uid;
if (gid == (gid_t) -1)
gid = sb.st_gid;
}
error = du_chown(rfp->rfs_name, rfp->rfs_psdp, cred,
uid, gid);
if (error) goto out;
}
/* Set atime, mtime (only support setting both) */
atime = vap->va_atime.tv_sec; mtime = vap->va_mtime.tv_sec;
if ((atime != -1 && mtime == -1) || (atime == -1 && mtime != -1)) {
if (!vap->va_mtime.tv_sec && vap->va_mtime.tv_usec == -1) {
vap->va_atime.tv_sec = -1;
vap->va_mtime.tv_sec = -1;
error = du_utime(rfp->rfs_name, rfp->rfs_psdp, cred,
&vap->va_atime, &vap->va_mtime);
if (error) goto out;
} else {
error = EOPNOTSUPP;
goto out;
}
} else if (atime != -1 && mtime != -1) {
error = du_utime(rfp->rfs_name, rfp->rfs_psdp, cred,
&vap->va_atime, &vap->va_mtime);
if (error) goto out;
}
out:
DUPRINT2(DB_VNODE, "rfsetattr: result %d\n", error);
return (error);
}
static int
rf_access(vp, mode, cred)
struct vnode *vp;
int mode;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
int error;
DUPRINT3(DB_VNODE, "rf_access: vp %x, mode %x\n", vp, mode);
error = du_access(rfp->rfs_name, rfp->rfs_psdp, cred,
(u_short)VACCTORFS(mode));
DUPRINT2(DB_VNODE, "rf_access: result %d\n", error);
return (error);
}
/*
* rf_lookup - lookup an RFS file and return a vnode for the file.
* If multi-component lookup is specified on this filesystem, the
* operation tries to eat as many components of name/pnp as it can.
* In case of 1) backing over a server mount point, 2) the parent
* directory vnode is desired, or 3) real error, it eats less and
* returns the unused path.
*/
/*ARGSUSED*/
static int
rf_lookup(dvp, name, vpp, cred, pnp, flags)
struct vnode *dvp;
char *name;
struct vnode **vpp;
struct ucred *cred;
struct pathname *pnp;
int flags;
{
register struct rfsnode *drfp;
sndd_t psdp;
sndd_t sdp = (sndd_t) NULL; /* Returned sd for name */
struct nodeinfo ninfo;
extern int rfs_magic_cred;
struct ucred lcred;
int error = 0;
char *lastcomp = NULL;
struct pathname pn;
static caddr_t kmp;
int multi = 0;
DUPRINT3(DB_VNODE, "rf_lookup: dvp %x, name %s\n", dvp, name);
drfp = vtorfs(dvp);
psdp = drfp->rfs_sdp;
*vpp = (struct vnode *) NULL;
(void) pn_alloc(&pn);
(void) pn_set(&pn, name);
/*
* If specified for this filesystem, do multi-component lookup
* XXX u_rdir test is a hack to prevent backing over a chroot()
* in this filesystem
*/
multi = ((dvp->v_vfsp->vfs_flag & VFS_MULTI) &&
pnp && pn_pathleft(pnp) &&
(!u.u_rdir || (u.u_rdir->v_vfsp != dvp->v_vfsp)));
if (multi) {
pnp->pn_path[pnp->pn_pathlen] = '\0';
error = pn_append(&pn, pn_getpath(pnp)); /* Remake path */
ASSERT(!error);
/* If parent wanted, remove last component */
if (flags & LOOKUP_DIR) {
lastcomp = new_kmem_fast_alloc(
&kmp, MAXNAMLEN, 1, KMEM_SLEEP);
error = pn_getlast(&pn, lastcomp);
ASSERT(!error);
}
}
rfslock(drfp);
/* Parent dir vnode must have remote reference, or can't look in it. */
if (!psdp) {
error = EACCES;
goto out;
}
/*
* If the magic id is set, use it. It provides super-user access on
* the server and guarantees that LINK will acquire a remote reference
* if the file exists. Normally this will not be set and we assume we're
* dealing with a server who does only lookup-type permissions checking.
* and not link() related checking. The magic id is left in to deal
* with servers that aren't that nice.
*/
if (rfs_magic_cred != -1) {
lcred.cr_uid = rfs_magic_cred;
lcred.cr_gid = rfs_magic_cred;
} else {
lcred.cr_uid = cred->cr_uid;
lcred.cr_gid = cred->cr_gid;
}
/* Get the remote reference using the link call */
error = du_link(&pn, psdp, &lcred, &sdp, &ninfo);
if (error == EDOTDOT) { /* ..'ed server mnt point, return root vnode */
ASSERT(multi);
(void) VFS_ROOT(dvp->v_vfsp, vpp);
error = 0;
} else if (error) { /* Real error, just return */
goto out;
} else { /* Normal completion, make new vnode */
error = get_rfsnode(dvp->v_vfsp, psdp,
pn_getpath(&pn), sdp, &ninfo, vpp);
(void) pn_set(&pn, ""); /* No path left */
}
/* If did multi-component, return remaining path */
if (multi) {
error = pn_copy(&pn, pnp);
ASSERT(!error);
if (lastcomp) {
error = pn_append(pnp, lastcomp);
ASSERT(!error);
}
}
out:
DUPRINT6(DB_VNODE,
"rf_lookup: rslt %d, vp %x, type %d sd %x, pn %s\n",
error, *vpp, *vpp ? (unsigned) (*vpp)->v_type : (unsigned) 0,
sdp, pnp ? pn_getpath(pnp) : "");
rfsunlock(drfp);
pn_free(&pn);
if (lastcomp)
kmem_fast_free(&kmp, lastcomp);
return (error);
}
static int
rf_create(dvp, nm, vap, exclusive, mode, vpp, cred)
struct vnode *dvp;
char *nm;
struct vattr *vap;
enum vcexcl exclusive;
int mode;
struct vnode **vpp;
struct ucred *cred;
{
struct rfsnode *drfp = vtorfs(dvp);
sndd_t sdp = (sndd_t) NULL;
struct nodeinfo ninfo;
int error;
u_short nmode;
u_short openflag;
int vcode, stat;
struct rfsnode *rfp;
DUPRINT6(DB_VNODE, "rf_create: dvp %x, nm %s, excl %x, va_mode %o, mode %x\n",
dvp, nm, exclusive, vap->va_mode, mode);
if (!drfp->rfs_sdp) {
error = EACCES;
goto out;
}
switch (vap->va_type) {
case VREG:
/*
* Create file by opening it O_CREAT. KLUDGE: Flag it open
* so that the caller won't try to open it again later in
* VOP_OPEN. This is not per-process so it only works if there's
* no context switch between the VOP_CREATE() and the VOP_OPEN()
*/
openflag = RFS_O_CREAT |
(vap->va_size == 0 ? RFS_O_TRUNC : 0) |
((mode & (VREAD|VWRITE)) == VREAD ? RFS_O_RDONLY : 0) |
((mode & (VREAD|VWRITE)) == VWRITE ? RFS_O_WRONLY : 0) |
((mode & (VREAD|VWRITE)) == (VREAD|VWRITE) ? RFS_O_RDWR : 0)|
(exclusive == EXCL ? RFS_O_EXCL : 0);
if (error = du_open(nm, drfp->rfs_sdp, cred, openflag,
vap->va_mode, &sdp, &ninfo))
break;
stat = sdp->sd_stat;
vcode = sdp->sd_vcode;
error = get_rfsnode(
dvp->v_vfsp, drfp->rfs_sdp, nm, sdp, &ninfo, vpp);
if (error)
break;
rfp = vtorfs(*vpp);
rfp->rfs_flags |= RFSOPEN; /* KLUDGE: flag file open */
/* Get client cache status on file */
rfp->rfs_sdp->sd_stat &= ~SDCACHE_MASK;
rfp->rfs_sdp->sd_stat |= stat & SDCACHE_MASK;
rfp->rfs_vcode = vcode;
/* Check the cache and invalidate if out of date */
/* XXX sd has already been freed by get_rfsnode() */
/* rfs_cache_check(vp, sd) */
break;
case VDIR:
/* can't create directories in rf_create. use rf_mkdir */
if (vap->va_type == VDIR)
error = EISDIR;
break;
case VBLK:
case VCHR:
case VFIFO:
nmode = vap->va_mode | VTTORFS(vap->va_type);
/* Device file: mknod */
if (error=du_mknod(nm, drfp->rfs_sdp, cred, nmode, vap->va_rdev))
break;
/* Lookup result to get remote reference and make the vnode */
error = rf_lookup(dvp, nm, vpp, cred, (struct pathname *)NULL, 0);
break;
default:
error = EOPNOTSUPP;
break;
}
out:
DUPRINT2(DB_VNODE, "rf_create: result %d\n", error);
return (error);
}
static int
rf_remove(dvp, nm, cred)
struct vnode *dvp;
char *nm;
struct ucred *cred;
{
register struct rfsnode *drfp = vtorfs(dvp);
int error;
DUPRINT3(DB_VNODE, "rfremove: dvp %x, name %s\n", dvp, nm);
if (!drfp->rfs_sdp) {
error = EACCES;
goto out;
}
error = du_unlink(nm, drfp->rfs_sdp, cred);
out:
DUPRINT2(DB_VNODE, "rfremove: result %d\n", error);
return (error);
}
static int
rf_link(vp, tdvp, tnm, cred)
struct vnode *vp;
struct vnode *tdvp;
char *tnm;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
struct rfsnode *drfp = vtorfs(tdvp);
sndd_t sdp = (sndd_t) NULL;
struct nodeinfo ninfo;
struct pathname pn;
int error;
DUPRINT4(DB_VNODE, "rflink: vp %x, tdvp %x, name %s\n", vp, tdvp, tnm);
pn_alloc(&pn);
(void) pn_set(&pn, rfp->rfs_name);
if (!drfp->rfs_sdp) {
error = EACCES;
goto out;
}
/* First half of call: get remote ref to source */
if (error = du_link(&pn, rfp->rfs_psdp, cred, &sdp, &ninfo))
goto out;
/* Check the remote reference returned */
error = transform_rfsnode(&vp, sdp, &ninfo);
/* Second half of call: link it */
error = du_link1(sdp, tnm, drfp->rfs_sdp, cred);
out:
pn_free(&pn);
DUPRINT2(DB_VNODE, "rflink: result %d\n", error);
return (error);
}
/* Rename
* First link the new name then remove the old one.
* This insures that you can't lose the node in a system
* crash. But you can end up with a node with both the old
* and new names linked to it. RFS doesn't really provide
* you with the capability to get around this.
*/
static int
rf_rename(odvp, onm, ndvp, nnm, cred)
struct vnode *odvp;
char *onm;
struct vnode *ndvp;
char *nnm;
struct ucred *cred;
{
struct rfsnode *odrfp = vtorfs(odvp);
struct rfsnode *ndrfp = vtorfs(ndvp);
sndd_t sdp = (sndd_t) NULL;
struct nodeinfo ninfo;
struct pathname pn;
int error;
DUPRINT5(DB_VNODE, "rfrename: odvp %x, onm %s, ndvp %x, nnm %s\n",
odvp, onm, ndvp, nnm);
pn_alloc(&pn);
(void) pn_set(&pn, onm);
if (!odrfp->rfs_sdp || !ndrfp->rfs_sdp)
return (EACCES);
/* Link the new name */
/* First half of call: get remote ref to source */
if (error = du_link(&pn, odrfp->rfs_sdp, cred, &sdp, &ninfo))
goto out;
/* Second half of call: link it */
if (error = du_link1(sdp, nnm, ndrfp->rfs_sdp, cred)) {
if (error != EEXIST)
goto out;
/* Destination exists try to unlink it, then try linking again */
if (error = du_unlink(nnm, ndrfp->rfs_sdp, cred))
goto out;
if (error = du_link1(sdp, nnm, ndrfp->rfs_sdp, cred))
goto out;
}
/* Unlink the old name */
if (error = du_unlink(onm, odrfp->rfs_sdp, cred)) {
/* Failed: try to unlink the new name */
error = du_unlink(nnm, ndrfp->rfs_sdp, cred);
}
out:
pn_free(&pn);
if (sdp)
error = del_sndd(sdp);
DUPRINT2(DB_VNODE, "rfrename: result %d\n", error);
return (error);
}
/*
* Make a remote directory. Note that the vnode mkdir op is expected to
* return a vnode for the directory, but the RFS call, DUMKDIR, does not
* hold open the inode for the directory on completion. Thus you have
* to follow the DUMKDIR with an RFS lookup to hold open the remote inode.
* This is potentially racy (someone could remove the directory between
* the time you create it and the time you look it up), but it can't be helped.
*/
static int
rf_mkdir(dvp, nm, va, vpp, cred)
struct vnode *dvp;
char *nm;
register struct vattr *va;
struct vnode **vpp;
struct ucred *cred;
{
struct rfsnode *drfp = vtorfs(dvp);
int error;
DUPRINT3(DB_VNODE, "rfmkdir: dvp %x, nm %s\n", dvp, nm);
if (!drfp->rfs_sdp) {
error = EACCES;
goto out;
}
/* Make the directory */
if (error = du_mkdir(nm, drfp->rfs_sdp, cred, va->va_mode))
goto out;
/* Lookup result to get remote reference and make the vnode */
error = rf_lookup(dvp, nm, vpp, cred, (struct pathname *) NULL, 0);
out:
DUPRINT2(DB_VNODE, "rfmkdir: result %d\n", error);
return (error);
}
static int
rf_rmdir(dvp, nm, cred)
struct vnode *dvp;
char *nm;
struct ucred *cred;
{
struct rfsnode *drfp = vtorfs(dvp);
int error;
struct vnode *vp;
DUPRINT3(DB_VNODE, "rfrmdir: dvp %x, nm %s\n", dvp, nm);
if (!drfp->rfs_sdp) {
error = EACCES;
goto out;
}
/* Lookup result to get remote reference */
error = rf_lookup(dvp, nm, &vp, cred, (struct pathname *) NULL, 0);
if (error) goto out;
if (VN_CMP(vp, u.u_cdir)) {
error = EINVAL;
VN_RELE(vp);
goto out;
}
VN_RELE(vp);
error = du_rmdir(nm, drfp->rfs_sdp, cred);
out:
DUPRINT2(DB_VNODE, "rfrmdir: result %d\n", error);
return (error);
}
/*
* rf_readdir
* Have to copy the S5 directory entries into an in-kernel buffer,
* convert them to Sun format, and copy them out to user land.
*/
#define DCHUNK 512
static int
rf_readdir(vp, uiop, cred)
struct vnode *vp;
register struct uio *uiop;
struct ucred *cred;
{
struct rfsnode *rfp = vtorfs(vp);
int error;
caddr_t ibuf = (caddr_t) NULL;
caddr_t obuf = (caddr_t) NULL;
int inbufcnt, outbufcnt;
struct uio tuio;
struct iovec tiov;
struct dirent *dp;
struct rfs_dirent *rdp;
int in_reclen = 0;
register char *inp, *onp;
int bufsize;
int reqsize;
long last_offset;
DUPRINT4(DB_VNODE, "rfreaddir: vp %x, uio offset %x, count %d\n",
vp, uiop->uio_offset, uiop->uio_resid);
/* Must have already opened directory and gotten remote reference */
if (!rfp->rfs_sdp) {
error = EACCES;
goto out;
}
/*
* Regardless of how much was asked for request no more than DCHUNK
* data. This hack is because of a bug which makes 3b server barf
* on large chunks if it has to XDRize. Making a small request works,
* because semantic of VOP_READDIR() says that you may get less than
* you asked for.
*/
reqsize = (uiop->uio_resid < DCHUNK ? uiop->uio_resid : DCHUNK);
/* Setup to copy results to temp buffer */
bcopy((caddr_t) uiop, (caddr_t) &tuio, sizeof (struct uio));
tuio.uio_iov = &tiov;
bufsize = 2 * reqsize; /* Leave server room to return more than req'd */
ibuf = new_kmem_alloc((u_int)bufsize, KMEM_SLEEP);
tuio.uio_iov->iov_base = ibuf;
tuio.uio_iov->iov_len = bufsize;
tuio.uio_resid = bufsize;
tuio.uio_seg = UIOSEG_KERNEL;
obuf = new_kmem_alloc((u_int)bufsize, KMEM_SLEEP);
/*
* Make the getdents call -- on return dirents are in buf and
* uio_offset contains remote directory offset.
*/
if (error = du_getdents(rfp->rfs_sdp, &tuio, reqsize, cred))
goto out;
/* Transform the entries (in place) to local format */
for (inbufcnt = bufsize - tuio.uio_resid, outbufcnt = 0,
rdp=(struct rfs_dirent *)ibuf, dp = (struct dirent *)obuf;
(caddr_t) rdp < (ibuf + inbufcnt);
outbufcnt += dp->d_reclen,
rdp = (struct rfs_dirent *) ((char *) rdp + in_reclen),
dp = (struct dirent *) ((char *) dp + dp->d_reclen)) {
in_reclen = rdp->d_reclen;
dp->d_off = rdp->d_off;
dp->d_fileno = rdp->d_ino;
inp = rdp->d_name;
onp = dp->d_name;
while (*onp++ = *inp++);
dp->d_namlen = inp - rdp->d_name;
dp->d_reclen = DIRSIZ(dp);
/* Got more than requested, back up one */
if (outbufcnt + dp->d_reclen > reqsize)
break;
last_offset = rdp->d_off;
DUPRINT5(DB_VNODE,
"rfreaddir: f# %d, reclen %d, namelen %d, name %s\n",
dp->d_fileno, dp->d_reclen, dp->d_namlen, dp->d_name);
}
/* Copy out the result to user space */
error = uiomove(obuf, outbufcnt, UIO_READ, uiop);
/* Return remote directory offset as side effect */
uiop->uio_offset = last_offset;
out:
if (ibuf)
kmem_free(ibuf, (u_int) bufsize);
if (obuf)
kmem_free(obuf, (u_int) bufsize);
DUPRINT4(DB_VNODE, "rfreaddir: result %d, ocnt %d, resid %d\n",
error, outbufcnt, uiop->uio_resid);
return (error);
}
/*ARGSUSED*/
static int
rf_symlink(dvp, lnm, tva, tnm, cred)
struct vnode *dvp;
char *lnm;
struct vattr *tva;
char *tnm;
struct ucred *cred;
{
DUPRINT1(DB_VNODE, "rf_symlink: not supported\n");
return (EOPNOTSUPP);
}
/*ARGSUSED*/
static int
rf_readlink(vp, uiop, cred)
struct vnode *vp;
struct uio *uiop;
struct ucred *cred;
{
DUPRINT1(DB_VNODE, "rf_readlink: not supported\n");
return (EOPNOTSUPP);
}
/*
* fsync op -- There is currently no equivalent of this in RFS, so instead
* do a sync()
*/
/*ARGSUSED*/
static int
rf_fsync(vp, cred)
struct vnode *vp;
struct ucred *cred;
{
struct vnode *rf_rootvp;
struct rfsnode *rfp;
int error = 0;
DUPRINT2(DB_VNODE, "rf_fsync: vp %x\n", vp);
/* Get the root node for the file system */
if (error = VFS_ROOT(vp->v_vfsp, &rf_rootvp))
goto out;
rfp = vtorfs(rf_rootvp);
/* Do the update system call */
error = du_update(rfp->rfs_sdp);
VN_RELE(rf_rootvp);
out:
DUPRINT2(DB_VNODE, "rf_fsync: result %d\n", error);
return (error);
}
/*
* Release an rf_vnode.
* Uncache the node.
* Free send descriptor, parent send descriptor and memory.
*/
static int
rf_inactive(vp)
struct vnode *vp;
{
register struct rfsnode *rfp = vtorfs(vp);
int error = 0;
ASSERT(rfp->rfs_sdp && rfp->rfs_psdp);
DUPRINT5(DB_VNODE,
"rfinactive: freeing vp %x, sd %x, psd %x, name %s\n",
vp, rfp->rfs_sdp, rfp->rfs_psdp, rfp->rfs_name);
rfs_cache_lock();
rf_unsave(rfp, vp->v_vfsp);
rfs_cache_unlock();
error = del_sndd(rfp->rfs_psdp); /* XXX -- what if it fails? */
error = del_sndd(rfp->rfs_sdp);
/* Invalidate the cache pages associated with the node */
/* rfinval(); */
((struct rfsmnt *)(vp->v_vfsp->vfs_data))->rm_refcnt--;
kmem_free((caddr_t)rfp, (u_int) rfp->rfs_alloc);
return (error);
}
/*ARGSUSED*/
static int
rf_lockctl(vp, ld, com, cred, clid)
struct vnode *vp;
struct flock *ld;
int com;
struct ucred *cred;
int clid;
{
struct rfsnode *rfp = vtorfs(vp);
int error;
DUPRINT4(DB_VNODE, "rflockctl: vp %x, ld %x, com %x\n",
vp, ld, com);
if (!rfp->rfs_sdp) {
error = EBADF;
goto out;
}
error = du_fcntl(rfp->rfs_sdp, com, ld, cred);
out:
DUPRINT2(DB_VNODE, "rflockctl: result %d\n", error);
return (error);
}
/*ARGSUSED*/
static int
rf_fid(vp, fidpp)
struct vnode *vp;
struct fid **fidpp;
{
DUPRINT1(DB_VNODE, "rf_fid: not supported\n");
return (EOPNOTSUPP);
}
/*ARGSUSED*/
static int
rf_getpage()
{
DUPRINT1(DB_VNODE, "rf_putpage: not supported\n");
return (EOPNOTSUPP);
}
/*ARGSUSED*/
static int
rf_putpage()
{
DUPRINT1(DB_VNODE, "rf_putpage: not supported\n");
return (EOPNOTSUPP);
}
/*ARGSUSED*/
static int
rf_map(vp, off, as, addr, len, prot, maxprot, flags, cred)
struct vnode *vp;
u_int off;
struct as *as;
addr_t addr;
u_int len;
u_int prot, maxprot;
u_int flags;
struct ucred *cred;
{
DUPRINT1(DB_VNODE, "rf_map: not supported\n");
return (EOPNOTSUPP);
}
static int
rf_cmp(vp1, vp2)
struct vnode *vp1, *vp2;
{
register struct sndd *sdp1, *sdp2;
/* Both are the same */
if (vp1 == vp2)
return (1);
/* One is NULL (unfortunately some people pass in NULL vnodes) */
if (vp1 == (struct vnode *) NULL || vp2 == (struct vnode *) NULL)
return (0);
/* One is not an rfs vnode */
if (vp1->v_op != &rfs_vnodeops || vp2->v_op != &rfs_vnodeops)
return (0);
/* Two distinct rfs vnodes -- do they reference same remote inode? */
sdp1 = vtorfs(vp1)->rfs_sdp;
sdp2 = vtorfs(vp2)->rfs_sdp;
return (sdp1->sd_queue == sdp2->sd_queue &&
sdp1->sd_sindex == sdp2->sd_sindex);
}
static int
rf_realvp(vp, vpp)
struct vnode *vp;
struct vnode **vpp;
{
*vpp = vp;
return (0);
}
static int
rf_badop()
{
panic("rf_badop");
}
/*ARGSUSED*/
static int
rf_cntl(vp, cmd, idata, odata, iflg, oflg)
struct vnode *vp;
caddr_t idata, odata;
{
return (ENOSYS);
}