/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:afs_gfs_subr.c 12.1$ */
/* $ACIS:afs_gfs_subr.c 12.1$ */
/* $Source: /ibm/acis/usr/sys/afs/RCS/afs_gfs_subr.c,v $ */

#ifndef lint
static char *rcsid = "$Header:afs_gfs_subr.c 12.1$";
#endif

#ifdef	vax
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/user.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../h/stat.h"
#include "../h/fs_types.h"
#include "../h/mount.h"
#include "../h/kernel.h"
#include "../h/socket.h"
#include "../h/ioctl.h"
#include "../h/vfs.h"
#include "../h/vnode.h"
#include "../net/if.h"
#include "../netinet/in.h"
#include "../h/mbuf.h"
#include "../rpc/types.h"
#include "../rpc/xdr.h"

#include "../afs/osi.h"
#define RFTP_INTERNALS 1
#include "../afs/r.h"
#include "../afs/rftp.h"
#include "../afs/lock.h"
#include "../afs/volerrors.h"
#include "../afsint/rvice.h"
#include "../afsint/rvaux.h"
#include "../afs/afs.h"

/* Emulates the lookupname() SunOS routine */

int gop_lookupname(fnamep, seg, followlink, parent, gpp)
char *fnamep;		    /* user pathname */
int seg;		    /* addr space that name is in */
int followlink;		    /* follow sym links */
struct gnode **parent;	    /* Return Ptr for parent dir -- ignored */
struct gnode **gpp;	    /* Return Ptr to component gnode */
{
    register struct nameidata *ndp = &u.u_nd;

    if (seg == AFS_UIOUSER) {
	if ((ndp->ni_dirp = km_alloc(MAXPATHLEN, KM_SLEEP)) == NULL)
	    return(EIO);
	if (u.u_error = copyinstr(fnamep, ndp->ni_dirp, MAXPATHLEN, (u_int *)0)){
	    km_free(ndp->ni_dirp, MAXPATHLEN);
	    return(u.u_error);
	}
    }
    else
	ndp->ni_dirp = fnamep;
    ndp->ni_nameiop = LOOKUP;
    if (followlink)
	ndp->ni_nameiop |= FOLLOW;
    *gpp = gfs_namei(ndp);
    if (seg == AFS_UIOUSER)
	km_free(ndp->ni_dirp, MAXPATHLEN);
    if (*gpp == NULL) {
	return(u.u_error);
    }
    gfs_unlock(*gpp);
   return(0);
}
	
int
gop_rdwr(rw, gp, base, len, offset, segflg, unit, aresid)
enum uio_rw rw;
register struct gnode *gp;
register caddr_t base;
int len, offset, segflg;
register int *aresid;
int unit;	/* Ignored by GFS; in Sun allowes for "IO as atomic unit" */
{
    int error;

    gfs_lock(gp);
    error = rdwri(rw, gp, base, len, offset, segflg, aresid);
    gfs_unlock(gp);
    return(error);
}

/* The two routines below emulate Sun's VOP_RDRW() getattr() and setattr() routines; it's used so that minimal changes will be required for a common in-kernel venus */

int
vop_rdwr(gp, uiop, rw, syncflg, cred)
	struct gnode *gp;
	struct uio *uiop;
	int rw;
	int syncflg;
	struct ucred *cred;
{
	int error;

	if ((gp->g_mode & GFMT) == GFREG) {
	    gfs_lock(gp);
	    error = GRWGP(gp, uiop, rw, syncflg, cred);
	    gfs_unlock(gp);
	}
	else {
	    error = u.u_error = EINVAL;
	}
	return (error);
}


int
vop_getattr(gp, vap, cred)
	struct gnode *gp;
	register struct vattr *vap;
	struct ucred *cred;
{
	int ret;

	ret = GGETVAL(gp);
	gfs_lock(gp);
	vap->va_type = IFTOVT(gp->g_mode);
	vap->va_mode = gp->g_mode;
	vap->va_uid = gp->g_uid;
	vap->va_gid = gp->g_gid;
	vap->va_fsid = gp->g_dev;
	vap->va_nodeid = gp->g_number;
	vap->va_nlink = gp->g_nlink;
	vap->va_size = gp->g_size;
	vap->va_atime = gp->g_atime;
	vap->va_mtime = gp->g_mtime;
	vap->va_ctime = gp->g_ctime;
	vap->va_rdev = gp->g_rdev;
	vap->va_blocks = gp->g_blocks;
	switch(gp->g_mode & GFMT) {

	case GFBLK:
		vap->va_blocksize = BLKDEV_IOSIZE;		
		break;

	case GFCHR:
		vap->va_blocksize = MAXBSIZE;
		break;

	default:
		vap->va_blocksize = gp->g_mp->m_fs_data->fd_bsize;
		break;
	}
	gfs_unlock(gp);
	return (0);
}

int
vop_setattr(gp, vap, cred)
	register struct gnode *gp;
	register struct vattr *vap;
	struct ucred *cred;
{
	int chtime = 0;
	int error = 0;
	struct timeval atime;
	struct timeval mtime;
	
	atime = time;
	mtime = time;
	
	/*
	 * cannot set these attributes
	 */
	if ((vap->va_nlink != -1) || (vap->va_blocksize != -1) ||
	    (vap->va_rdev != -1) || (vap->va_blocks != -1) ||
	    (vap->va_fsid != -1) || (vap->va_nodeid != -1)) {
		return (EINVAL);
	}

	gfs_lock(gp);

	/*
	 * Change file access modes. Must be owner or su.
	 */
	if (vap->va_mode != -1) {
		error = OWNER(gp, cred);
		if (error) {
			goto out;
		}
		gp->g_mode &= GFMT;
		gp->g_mode |= vap->va_mode & ~GFMT;
		if (cred->cr_uid != 0) {
			gp->g_mode &= ~GSVTX;	/* XXX Why? */
			if (!groupmember(gp->g_gid))
				gp->g_mode &= ~GSGID;
		}
		gp->g_flag |= GCHG;
		/* Remember to xrele text if su ever allowed over net. */
	}

	/*
	 * Change file ownership (must be su).
	 */
	if ( ((vap->va_uid != -1) && (vap->va_uid != gp->g_uid)) ||
	     ((vap->va_gid != -1) && (vap->va_gid != gp->g_gid)) )
	{
		if (!suser()) {
			error = u.u_error;
			goto out;
		}
		error = chown2(gp, vap->va_uid, vap->va_gid);
		if (error)
			goto out;
	}

	/*
	 * Truncate file. Must have write permission and not be a directory.
	 */
	if (vap->va_size != -1) {
		if ((gp->g_mode & GFMT) == GFDIR) {
			error = EISDIR;
			goto out;
		}
		if (access(gp, GWRITE)) {
			error = u.u_error;
			goto out;
		}
		(void)GTRUNC(gp, vap->va_size, cred);

	}
	/*
	 * Change file access or modified times.
	 */

	if (vap->va_atime.tv_sec != -1) {
		if (access(gp, GREAD) && access(gp, GWRITE)) {
			error = u.u_error;
			goto out;
		}
		atime.tv_sec = vap->va_atime.tv_sec;
		atime.tv_usec = vap->va_atime.tv_usec;
		chtime++;
	}
	if (vap->va_mtime.tv_sec != -1) {
		if (access(gp, GWRITE)) {
			error = u.u_error;
			goto out;
		}
		mtime.tv_sec = vap->va_mtime.tv_sec;
		mtime.tv_usec = vap->va_mtime.tv_usec;
		chtime++;
	}
	if (chtime) {
		gp->g_flag |= GACC|GUPD|GCHG;
		gp->g_ctime = time;
	}

out:
	(void) GUPDATE(gp, &atime, &mtime, 1, cred);
	gfs_unlock(gp);
	return (error);
}


/*
 * Perform chown operation on gnode gp.  This is similar to chown1()
 * in gfs_syscalls.c, but we leave some preliminary error checking and
 * the GUPDATE operation for the caller to do.  This routine should not
 * exist; please merge these someday.
 */

struct dquot *inoquota();

chown2(gp, uid, gid)
	register struct gnode *gp;
	register int uid, gid;
{
#ifdef QUOTA
	register long change;
#endif

	if (uid == -1)
		uid = gp->g_uid;
	if (gid == -1)
		gid = gp->g_gid;

#ifdef QUOTA
	if (gp->g_uid == uid)
		change = 0;
	else
		change = gp->g_blocks;
	(void) chkdq(gp, -change, 1);
	(void) chkiq(gp->g_dev, gp, gp->g_uid, 1);
	dqrele(gp->g_dquot);
#endif
	gp->g_uid = uid;
	gp->g_gid = gid;
	gp->g_flag |= (GCHG | GCID);
		
	if (u.u_ruid != 0)
		gp->g_mode &= ~(GSUID|GSGID);

#ifdef QUOTA
	gp->g_dquot = inoquota(gp);
	(void) chkdq(gp, change, 1);
	(void) chkiq(gp->g_dev, (struct gnode *)NULL, uid, 1);
	return (u.u_error);	
#else
	return(0);
#endif
}


uniqtime(tv)
	register struct timeval *tv;
{
	static struct timeval last;
	static int uniq;

	while (last.tv_usec != time.tv_usec || last.tv_sec != time.tv_sec) {
		last = time;
		uniq = 0;
	}
	*tv = last;
	tv->tv_usec += uniq++;
}


/*
 * Copy an mbuf to the contiguous area pointed to by cp.
 * Skip <off> bytes and copy <len> bytes.
 * Returns the number of bytes not transferred.
 * The mbuf is NOT changed.
 */
int
m_cpytoc(m, off, len, cp)
	register struct mbuf *m;
	register int off, len;
	register caddr_t cp;
{
	register int ml;

	if (m == NULL || off < 0 || len < 0 || cp == NULL)
		panic("m_cpytoc");
	while (off && m)
		if (m->m_len <= off) {
			off -= m->m_len;
			m = m->m_next;
			continue;
		} else
			break;
	if (m == NULL)
		return (len);

	ml = imin(len, m->m_len - off);
	bcopy(mtod(m, caddr_t)+off, cp, (u_int)ml);
	cp += ml;
	len -= ml;
	m = m->m_next;

	while (len && m) {
		ml = m->m_len;
		bcopy(mtod(m, caddr_t), cp, (u_int)ml);
		cp += ml;
		len -= ml;
		m = m->m_next;
	}

	return (len);
}


/*
 * Set vattr structure to a null value.
 */
void
vattr_null(vap)
	struct vattr *vap;
{
	register int n;
	register char *cp;

	n = sizeof(struct vattr);
	cp = (char *)vap;
	while (n--) {
		*cp++ = -1;
	}
}

/* XXXXXXXXXXX THESE are defined somewhere on the darn kernel XXXXXXXXX */
void
bzero(sp, len)
	register char *sp;
	int len;
{
	register int n;

	if ((n = len) <= 0)
		return;
	do
		*sp++ = 0;
	while (--n);
}

char *
strncpy(s1, s2, n)
register char *s1, *s2;
register int n;
{
	register char *os1 = s1;

	while (--n >= 0)
		if ((*s1++ = *s2++) == '\0')
			while (--n >= 0)
				*s1++ = '\0';
	return (os1);
}


char *
strcpy(s1, s2)
	register char *s1, *s2;
{
	register char *os1;

	os1 = s1;
	while (*s1++ = *s2++)
		;
	return (os1);
}
#endif	vax

