static char rcsid[] = "$Header: dfs_director.c,v 820.1 86/12/04 19:44:06 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * DFS directory routines.
 * 
 *	Bakul shah
 *
 * bvs 840322 -- original version
 * jht 841115,16,26-29 -- complete conversion to use prpc.
 * jht 850213 -- add dfsPktHdr_t containing version/functionality info to pkts.
 * jht 850301 -- conversion to use new errno.h error-code mnemonics.
 * jht 850401 -- fill out the gathering of statistics.
 * jht 850414 -- differentiate between no remote struct and one not locked.
 * jht 850414 -- complete DFS_LOCK_FIX for rmdir() processing: clear i_rmt_lock.
 */

#include "../h/param.h"
#include "../h/ioctl.h"
#include "../h/dir.h"
#include "../h/user.h"

#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../dfs/dfs.h"

#include "../h/inode.h"
#include "../h/socket.h"
#include "../h/kernel.h"
#include "../net/if.h"
#include "../h/mbuf.h"


typedef struct dfsc_mkdir {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t		directoryIp;	/* Parent directory */
	long		offset;		/* Create at this offset */
	long		count;		/* Size of the hole */
	short		mode;		/* File mode	*/
	short		cmask;		/* Create mask	*/
	short		uid;		/* User id	*/
	/* directory entry  follows the header */
} dfsc_mkdir_t;

typedef struct dfsr_mkdir {	/* Pkt from client <-- server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	int		error;		/* Error number or 0 on success */
} dfsr_mkdir_t;

/*
 * Remote mkdir.
 * u.u_pdir points to the directory
 * where a new one is to be made.
 * u.u_pdir is locally iput
 * once the call is complete; successful or not.
 */
dfsClient_mkdir()
{
	register dfsc_mkdir_t * call;
	register dfsr_mkdir_t * result;
	register struct inode * dp = u.u_pdir;
	struct a {
		char *	name;
		int	dmode;
	};

	dfs_incClient(mkdir);
	if (!dfsClient_validContext( dp==DFS_ILLEGAL_INODE
					? (struct inode *)-1 /* Special case */
					: dp,		/* Pass parent inode */
				     "mkdir"))
		goto bad;

	call = DFSC_VPACKET(mkdir, DIRSIZ(&u.u_dent));
	if (!call)
		goto bad;
	call->directoryIp	= dp->i_rmt_ip;
	call->mode		= ((struct a *)u.u_ap)->dmode;
	call->offset		= u.u_offset;
	call->count		= u.u_count;
	call->cmask		= u.u_cmask;
	call->uid		= u.u_uid;
	denttombuf(m_field(call, DFS_DATA(call)), &u.u_dent);
	result = DFS_CALL(dp->i_host,mkdir,call);
	if (!result)
		goto bad;
	u.u_error = result->error;
	DFS_FREE_RESULTS(result);
	IUNLOCK(dp);
	dfs_irele(dp);
	return;
bad:
	iput(dp);
}

/*
 * Transfer all paremeters into the u structure
 * and make a local mkdir1 call.
 * Clear the lock flag from the dfs_remote entry.
 */
dfsr_mkdir_t *
dfsServer_mkdir(clientConn, clientId, operation, call)
	connection_t	*	clientConn;	/* Host we are serving	*/
	u_long			clientId;	/* Specific client@host	*/
	u_short			operation;	/* Redundant: what to do*/
	register dfsc_mkdir_t *	call;		/* Call pkt -- params	*/
{
	register dfsr_mkdir_t * result;
	struct a {
		char *	name;
		int	dmode;
	};
	register dfs_remote_t * rp;

	dfs_incServer(mkdir);
	if (!dfsServer_validContext(operation, call, "mkdir")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(mkdir);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	rp = dfs_rmt_find(DFS_NODE(clientConn), call->directoryIp);
	/*
	 * The parent directory must be locked
	 * before we can create a new dir in it.
	 */
	if (!rp || !(rp->flags & DFS_RMT_LOCKED)) {
		result->error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		DFS_FREE_PARAMS(call);
		DFS_RETURN(result);
	}
	rp->locktime = UPTIME();
	u.u_pdir = rp->ip;

	u.u_offset = call->offset;
	u.u_count = call->count;
	u.u_cmask = call->cmask;
	u.u_uid = DFS_UID(call->uid, clientConn);
	((struct a *)u.u_ap)->dmode = call->mode;
	mbuftodent(&u.u_dent, m_field(call, DFS_DATA(call)));
	mkdir1();
	rp->flags &= ~DFS_RMT_LOCKED;
	dfs_rmt_remove(DFS_NODE(clientConn), u.u_pdir);
#ifdef	DFS_LOCK_FIX
	u.u_pdir->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
	result->error = u.u_error;
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}

typedef struct dfsc_rmdir {	/* Pkt from client --> server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	caddr_t		ip;		/* Inode to be unlinked */
	caddr_t		directoryIp;	/* Parent directory */
	long		offset;		/* Create at this offset */
	long		count;		/* Size of the hole */
	/* directory entry  follows the header */
} dfsc_rmdir_t;

typedef struct dfsr_rmdir {	/* Pkt from client <-- server */
#ifdef	DFS_PACKETHDR
	dfsPktHdr_t	dfsPktHdr;	/* Info about DFS versions, etc	*/
#endif	DFS_PACKETHDR
	int		error;		/* Error number or 0 on success */
} dfsr_rmdir_t;

/*
 * Remove an entry from a directory
 */

dfsClient_rmdir(ip)
	register struct inode * ip;
{
	register dfsc_rmdir_t * call;
	register dfsr_rmdir_t * result;

	dfs_incClient(rmdir);
	if (!dfsClient_validContext(ip, "rmdir"))
		goto ret;

	call = DFSC_VPACKET(rmdir, DIRSIZ(&u.u_dent));
	if (!call)
		goto ret;
	call->ip		= ip->i_rmt_ip;
	call->directoryIp	= u.u_pdir->i_rmt_ip;
	call->offset		= u.u_offset;
	call->count		= u.u_count;
	denttombuf(m_field(call, DFS_DATA(call)), &u.u_dent);
	result = DFS_CALL(ip->i_host,rmdir,call);
	if (!result)
		goto ret;
	u.u_error = result->error;
	DFS_FREE_RESULTS(result);

	IUNLOCK(u.u_pdir);
	dfs_irele(u.u_pdir);

	IUNLOCK(ip);
	dfs_irele(ip);
	return;
ret:
	iput(u.u_pdir);
	iput(ip);
}

dfsr_rmdir_t *
dfsServer_rmdir(clientConn, clientId, operation, call)
	connection_t	*	clientConn;	/* Host we are serving	*/
	u_long			clientId;	/* Specific client@host	*/
	u_short			operation;	/* Redundant: what to do*/
	register dfsc_rmdir_t * call;		/* Call pkt -- params	*/
{
	register dfsr_rmdir_t * result;
	register dfs_remote_t * rp;
	register dfs_remote_t * dir_rp;
	register struct inode * ip;

	dfs_incServer(rmdir);
	if (!dfsServer_validContext(operation, call, "rmdir")) {
		DFS_FREE_PARAMS(call);
		return NULL;
	}
	result = DFSR_PACKET(rmdir);
	if (!result) {
		DFS_FREE_PARAMS(call);
		return NULL;		/* Say "no result pkt" */
	}
	dir_rp = dfs_rmt_find(DFS_NODE(clientConn), call->directoryIp);
	/*
	 * The parent directory must be locked
	 * before we can remove an entry from it.
	 */
	if (!dir_rp || !(dir_rp->flags & DFS_RMT_LOCKED)) {
		result->error = !dir_rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		DFS_FREE_PARAMS(call);
		DFS_RETURN(result);
	}
	dir_rp->locktime = UPTIME();
	u.u_pdir = dir_rp->ip;

	rp = dfs_rmt_find(DFS_NODE(clientConn), call->ip);
	if (!rp || !(rp->flags & DFS_RMT_LOCKED)) {
		result->error = !rp ? DFS_EBADRMTDESC : DFS_ERMTNOTLOCKED;
		DFS_FREE_PARAMS(call);
		DFS_RETURN(result);
	}
	rp->locktime = UPTIME();
	ip = rp->ip;

	u.u_offset = call->offset;
	u.u_count = call->count;
	mbuftodent(&u.u_dent, m_field(call, DFS_DATA(call)));
	rmdir1(ip);
	result->error = u.u_error;
	/*
	 * Drop remote locks and references
	 * to the parent directory.
	 */
	dir_rp->flags &= ~DFS_RMT_LOCKED;
	dfs_rmt_remove(DFS_NODE(clientConn), u.u_pdir);
#ifdef	DFS_LOCK_FIX
	u.u_pdir->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
	/*
	 * Cleanup the target directory
	 */
	rp->flags &= ~DFS_RMT_LOCKED;
	dfs_rmt_remove(DFS_NODE(clientConn), ip);
#ifdef	DFS_LOCK_FIX
	ip->i_rmt_lock = NULL;
#endif	DFS_LOCK_FIX
	DFS_FREE_PARAMS(call);
	DFS_RETURN(result);
}
