1135 lines
27 KiB
C
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);
|
|
}
|