/* 
 * fsSysCall.c --
 *
 *	These are the system call stubs that get called from the
 *	system call trap handler.  These routines make sure that
 *	their arguments are addressable and then call the workhorse
 *	routines to do the actual work.
 *
 * Copyright 1987 Regents of the University of California
 * All rights reserved.
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, provided that the above copyright
 * notice appear in all copies.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 */

#ifndef lint
static char rcsid[] = "$Header: /sprite/src/kernel/fs/RCS/fsSysCall.c,v 9.10 91/09/10 18:22:48 rab Exp $ SPRITE (Berkeley)";
#endif not lint


#include <sprite.h>

#include <fs.h>
#include <fsutil.h>
#include <fsNameOps.h>
#include <fsio.h>
#include <fslcl.h>
#include <fsdm.h>
#include <vm.h>
#include <proc.h>


/*
 *----------------------------------------------------------------------
 *
 * Fs_AttachDiskStub --
 *
 *	Stub for the Fs_AttachDisk system call.  This gets ahold of
 *	the filename arguments, opens the disk to be attached, and
 *	calls Fsdm_AttachDisk to do the work.
 *
 * Results:
 *	SUCCESS, or an error code from finding junk on the disk.
 *
 * Side effects:
 *	Call Fs_AttachDisk or Fs_DetachDisk.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_AttachDiskStub(userDeviceName, userLocalName, flags)
    char *userDeviceName;/* Name of raw disk device file to attach */
    char *userLocalName;/* Local directory name where disk is attached */
    int flags;		/* FS_ATTACH_READ_ONLY, FS_DETATCH, FS_ATTACH_LOCAL */
{
    ReturnStatus status;
    int useFlags;		/* Flags for the Fs_Open call */
    Fs_Stream *streamPtr;	/* Stream from the Fs_Open call */
    char *deviceName;		/* Kernel resident pathname storage */
    char *localName;		/* Kernel resident pathname storage */
    int	localNameLength;
    int	deviceNameLength;

    if (userLocalName != (char *)NIL && userLocalName != (char *)0) {
	localName = (char *)malloc(FS_MAX_PATH_NAME_LENGTH);
	if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, userLocalName, localName,
			   &localNameLength) != SUCCESS) {
	    return(SYS_ARG_NOACCESS);
	}
    } else {
	localName = (char *)NIL;
    }
    if (userDeviceName != (char *)NIL && userDeviceName != (char *)0) {
	deviceName = (char *)malloc(FS_MAX_PATH_NAME_LENGTH);
	if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, userDeviceName, deviceName,
			   &deviceNameLength) != SUCCESS) {
	    if (localName != (char *)NIL) {
		free(localName);
	    }
	    return(SYS_ARG_NOACCESS);
	}
    } else {
	deviceName = (char *)NIL;
    }
    if (flags & FS_DETACH) {
	/*
	 * Flush data associated with the domain and remove the domain
	 * from the prefix table.
	 */
	status = Fsdm_DetachDisk(localName);
    } else {
	/*
	 * Attach a local domain into the prefix table.
	 */
	if (deviceName == (char *)NIL ||
	    localName == (char *)NIL) {
	    status = FS_INVALID_ARG;
	} else {
	    if (flags & FS_ATTACH_READ_ONLY) {
		useFlags = FS_READ;
	    } else {
		useFlags = FS_READ|FS_WRITE;
	    }
	    status = Fs_Open(deviceName, useFlags, FS_DEVICE, 0, &streamPtr);
	    if (status == SUCCESS) {
		status = Fsdm_AttachDiskByHandle(streamPtr->ioHandlePtr, 
					     localName, flags);
	    }
	}
    }
    if (deviceName != (char *)NIL) {
	free(deviceName);
    }
    if (localName != (char *)NIL) {
	free(localName);
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_OpenStub --
 *
 *      This is the stub for the Fs_Open system call.  This routine makes
 *	its arguments accessible, then sets up a Stream ID for the Fs_Stream
 *      object returned by (the internal) Fs_Open.  Stream Id's get
 *      passed to read and write calls and identify the stream to the
 *      system.  If FS_CREATE is one of the usage flags then the third
 *      argument, permissions, specifies the base permissions for the
 *      newly created file.
 *
 * Results:
 *	An error code or SUCCESS if successful.
 *
 * Side effects:
 *	The last argument is an out parameter and gets filled in with
 *	the stream id.  
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_OpenStub(pathName, usageFlags, permissions, streamIDPtr)
    char *pathName;		/* The name of the file to open */
    int usageFlags;		/* FS_READ, FS_WRITE, FS_CREATE, FS_TRUNC */
    int permissions;		/* Permission mask to use on creation */
    int *streamIDPtr;		/* This is the user's handle on the open
				 * file used in later filesystem requests */
{
    int		 	streamID;
    Fs_Stream	 	*streamPtr;
    ReturnStatus 	status;
    int			pathNameLength;
    char		newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space to the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    /*
     * Open the file and get a stream descriptor for it.
     */
    usageFlags &= ~FS_KERNEL_FLAGS;
    usageFlags |= (FS_USER | FS_FOLLOW);

    status = Fs_Open(newName, usageFlags, FS_FILE,
		     permissions & 0777, &streamPtr);
    
    if (status != SUCCESS) {
	return(status);
    }

    /*
     * Save a pointer to the descriptor in a list kept in the proc table.
     * Its index in the list is the "Stream ID".
     */

    status = Fs_GetStreamID(streamPtr, &streamID);
    if (status != SUCCESS) {
	(void) Fs_Close(streamPtr);
	return(status);
    }

    if (Vm_CopyOut(sizeof(int), (Address) &streamID, 
		   (Address) streamIDPtr) == SUCCESS) {
	return(SUCCESS);
    } 
    status = SYS_ARG_NOACCESS;
    (void) Fs_UserClose(streamID);	/* This clears the streamID, too */
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_UserClose --
 *
 *	System call to release a stream Id.  Calls then internal Fs_Close
 *	routine.
 *
 * Results:
 *	An return status.
 *
 * Side effects:
 *	The streamID becomes invalid (until it gets reassigned by
 *	another Fs_Open call).
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_UserClose(streamID)
    int streamID;
{
    ReturnStatus	status;
    Fs_Stream 		*streamPtr;	/* Stream pointer from the process's
				 	 * list of open streams */
    Proc_ControlBlock	*procPtr;	/* This process's control block */
    
    /*
     * Map from the streamID to a streamPtr.
     */

    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status != SUCCESS) {
	/*
	 * Fudge the return status.  A close() can only return EBADF or
	 * EINTR, so return something that maps to EBADF even if it
	 * doesn't make sense here.  Sprite system calls are going
	 * away soon anyway.
	 */
	if (status != GEN_ENOENT) {
	    return(FS_NEW_ID_TOO_BIG);
	}
	return(status);
    }

    /*
     * Look after any shared memory associated with the file.
     */
    if (procPtr->vmPtr->sharedSegs != (List_Links *)NIL) {
	Vm_CleanupSharedFile(procPtr,streamPtr);
    }

    /*
     * Clear the user-level streamID and then close the underlying stream.
     */

    Fs_ClearStreamID(streamID, procPtr);
    status = Fs_Close(streamPtr);

    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_ReadStub --
 *
 *      The stub for the Fs_Read system call.  Calls Fs_UserRead to do the
 *	work, then copies out the argument to the user's address space.
 *
 * Results:
 *	An return status.
 *
 * Side effects:
 *	The amountReadPtr is updated to reflect the amount actually read.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_ReadStub(streamID, amountRead, buffer, amountReadPtr)
    int		streamID;	/* The user's index into its open file list */
    int		amountRead;	/* The amount of bytes to read */
    Address	buffer;		/* The storage place for the read */
    int		*amountReadPtr;	/* The amount of bytes actually read */
{
    register ReturnStatus	status;
    Fs_Stream		*streamPtr;	/* The stream to read from */
    Proc_ControlBlock 	*procPtr;	/* This process's control block */

    /*
     * (Begin inlined called to Fs_UserRead.)
     */
    /*
     * Map from stream ID to file pointer and do the read.
     * If the process is not remote, Fs_Read takes care of making
     * the read buffer accessible.
     */

    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status == SUCCESS) {
	status = Fs_Read(streamPtr, buffer, streamPtr->offset, &amountRead);
    } else {
	amountRead = 0;
    }
    /*
     * (End Fs_UserRead.)
     */

    if (Vm_CopyOut(sizeof(int), (Address) &amountRead, 
		   (Address) amountReadPtr) != SUCCESS) {
	status = SYS_ARG_NOACCESS;
    }
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_UserRead --
 *
 *      The Fs_Read system call.  Read from the file indicated by the
 *      stream id into the buffer.  bufSize indicates how much
 *      data to read, and amountReadPtr is an output parameter that
 *      indicates how much data was read.  A length of zero means
 *      end-of-file.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	The amountReadPtr is updated to reflect the amount actually read.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_UserRead(streamID, amountRead, buffer, amountReadPtr)
    int		streamID;	/* The user's index into its open file list */
    int		amountRead;	/* The amount of bytes to read */
    Address	buffer;		/* The storage place for the read */
    int		*amountReadPtr;	/* The amount of bytes actually read */
{
    register ReturnStatus	status;
    Fs_Stream		*streamPtr;	/* The stream to read from */
    Proc_ControlBlock 	*procPtr;	/* This process's control block */

    /*
     * Map from stream ID to file pointer and do the read.
     * If the process is not remote, Fs_Read takes care of making
     * the read buffer accessible.
     */

    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status == SUCCESS) {
	*amountReadPtr = amountRead;
	status = Fs_Read(streamPtr, buffer, streamPtr->offset, amountReadPtr);
    } else {
	*amountReadPtr = 0;
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_ReadVectorStub --
 *
 *      The stub for the Fs_ReadVector system call.  Calls Fs_UserReadVector 
 *	to do the work, then copies out the argument to the user's address 
 *	space.
 *
 * Results:
 *	An return status.
 *
 * Side effects:
 *	The amountReadPtr is updated to reflect the amount actually read.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_ReadVectorStub(streamID, numVectors, userVectorArray, amountReadPtr)
    int		streamID;	/* The user's index into its open file list. */
    int		numVectors;	/* The # of vectors in userVectorArray. */
    Fs_IOVector	userVectorArray[];	
				/* The vectors defining where and how much to
				 * read. */
    int		*amountReadPtr;	/* The amount of bytes actually read */
{
    ReturnStatus status;
    int		amountRead;
    Fs_IOVector *vectorPtr;

    if (numVectors < 0) {
	return(SYS_INVALID_ARG);
    }

    /*
     * Copy the I/O vector into kernel space. The buffer addresses in each
     * vector will be made accessible by the Fs_Read routine.
     */
    vectorPtr = (Fs_IOVector *) malloc(numVectors * sizeof(Fs_IOVector));
    if (Vm_CopyIn(numVectors * sizeof(Fs_IOVector), 
		   (Address) userVectorArray,
		   (Address) vectorPtr) != SUCCESS) {
	free((Address) vectorPtr);
	return(SYS_ARG_NOACCESS);
    }

    status = Fs_UserReadVector(streamID, numVectors, vectorPtr, &amountRead);

    free((Address) vectorPtr);
    if (Vm_CopyOut(sizeof(int), (Address) &amountRead, 
		   (Address) amountReadPtr) != SUCCESS) {
	status = SYS_ARG_NOACCESS;
    }
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_UserReadVector --
 *
 *      The Fs_ReadVector system call.  Read from the file indicated by
 *      the stream ID into the buffer.  Each I/O vector contains a value
 *      to indicate how much data to read. *AmountReadPtr contains the
 *      total number of bytes read into the vector buffers.
 *      *amountReadPtr will be smaller that the sum of the bufSize fields
 *      in the vector array if an error occurs or an end-of-file is
 *      reached.  A length of zero in *amountReadPtr means an end-of-file
 *      occurred on the first buffer in the vector.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	The *amountReadPtr is updated to reflect the amount actually read.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_UserReadVector(streamID, numVectors, vectorPtr, amountReadPtr)
    int		streamID;	/* The user's index into its open file list */
    register int numVectors;	/* The # of vectors in vectorArray */
    register Fs_IOVector *vectorPtr;	
				/* The vectors defining where and how much to 
				 * read. */
    int		*amountReadPtr;	/* Total # of bytes read. */
{
    ReturnStatus	status;
    Fs_Stream		*streamPtr;	/* The stream to read from */
    Proc_ControlBlock 	*procPtr;	/* This process's control block */
    register int	sum = 0;	/* Total # of bytes read. */
    register int	i;

    /*
     * Map from stream ID to file pointer and do the read.
     * If the process is not remote, Fs_Read takes care of making
     * the read buffer accessible.
     */

    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);

    if (status == SUCCESS) {
	/*
	 * Doing a Fs_Read for each vector buffer is probably not the
	 * most efficiennt way to do this, but it is simple.
	 */
	for (i= 0; i < numVectors; i++, vectorPtr++) {

	    status = Fs_Read(streamPtr, vectorPtr->buffer, streamPtr->offset, 
				&(vectorPtr->bufSize));
	    /*
	     * Check to see if the read failed or we got an end-of-file.
	     */
	    if ((status != SUCCESS) || (vectorPtr->bufSize == 0)) {
		break;
	    }
	    sum += vectorPtr->bufSize;
	}
    }
    *amountReadPtr = sum;
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_WriteStub --
 *
 *	The Fs_Write system call stub.  Calls Fs_UserWrite to do the
 *	work, then copies out the amount written to the user's address space.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	The writeLengthPtr is updated to reflect the amount actually written.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_WriteStub(streamID, writeLength, buffer, writeLengthPtr)
    int streamID;
    int writeLength;
    Address buffer;
    int *writeLengthPtr;
{
    register ReturnStatus status;
    Fs_Stream	*streamPtr;	/* The stream to write to */
    Proc_ControlBlock *procPtr;	/* This process's control block */

    /*
     * (Begin inlined Fs_UserWrite.)
     */
    /*
     * Map from stream ID to streamPtr.  If the process is not remote,
     * Fs_Write takes care of making the user's buffer accessible.
     */
    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status == SUCCESS) {
	status = Fs_Write(streamPtr, buffer, streamPtr->offset, &writeLength);
    } else {
	writeLength = 0;
    }
    /*
     * (End inlined Fs_UserWrite.)
     */
    if (Vm_CopyOut(sizeof(int), (Address) &writeLength, 
		   (Address) writeLengthPtr) != SUCCESS) {
	status = SYS_ARG_NOACCESS;
    }
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_UserWrite --
 *
 *	The user interface to Fs_Write.  Write writeLength characters from
 *	buffer to the file identified by streamID.  *writeLengthPtr
 *	is updated to reflect how much was actually written.
 *
 * Results:
 *	A return status. 
 *
 * Side effects:
 *	The writeLengthPtr is updated to reflect the amount actually written.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_UserWrite(streamID, writeLength, buffer, writeLengthPtr)
    int streamID;
    int writeLength;
    Address buffer;
    int *writeLengthPtr;
{
    ReturnStatus status;
    Fs_Stream	*streamPtr;	/* The stream to write to */
    Proc_ControlBlock *procPtr;	/* This process's control block */

    /*
     * Map from stream ID to streamPtr.  If the process is not remote,
     * Fs_Write takes care of making the user's buffer accessible.
     */
    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status == SUCCESS) {
	*writeLengthPtr = writeLength;
	status = Fs_Write(streamPtr, buffer, streamPtr->offset, writeLengthPtr);
    } else {
	*writeLengthPtr = 0;
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_WriteVectorStub --
 *
 *      The stub for the Fs_WriteVector system call.  Calls Fs_UserWriteVector 
 *	to do the work, then copies out the argument to the user's address 
 *	space.
 *
 * Results:
 *	An return status.
 *
 * Side effects:
 *	The *amountWrittenPtr is updated to reflect the amount actually read.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_WriteVectorStub(streamID, numVectors, userVectorArray, amountWrittenPtr)
    int		streamID;	/* The user's index into its open file list. */
    int		numVectors;	/* The # of vectors in userVectorArray. */
    Fs_IOVector	userVectorArray[];	
				/* The vectors defining where and how much to
				 * write. */
    int	*amountWrittenPtr;	/* The amount of bytes actually written. */
{
    ReturnStatus status;
    int		amountWritten;
    Fs_IOVector *vectorPtr;

    if (numVectors < 0) {
	return(SYS_INVALID_ARG);
    }

    /*
     * Copy the I/O vector into kernel space. The buffer addresses in each
     * vector will be made accessible by the Fs_Write routine.
     */
    vectorPtr = (Fs_IOVector *) malloc(numVectors * sizeof(Fs_IOVector));
    if (Vm_CopyIn(numVectors * sizeof(Fs_IOVector), 
		   (Address) userVectorArray,
		   (Address) vectorPtr) != SUCCESS) {
	free((Address) vectorPtr);
	return(SYS_ARG_NOACCESS);
    }

    status = Fs_UserWriteVector(streamID, numVectors, vectorPtr, 
			&amountWritten);

    free((Address) vectorPtr);
    if (Vm_CopyOut(sizeof(int), (Address) &amountWritten, 
		   (Address) amountWrittenPtr) != SUCCESS) {
	status = SYS_ARG_NOACCESS;
    }
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_UserWriteVector --
 *
 *      The Fs_WriteVector system call.  Write from the file indicated by
 *      the stream ID into the buffer.  Each I/O vector contains a value
 *      to indicate how much data to write. *AmountWrittenPtr contains the
 *      total number of bytes written from the vector buffers.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	The *amountWrittenPtr is updated to reflect the amount 
 *	actually written.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_UserWriteVector(streamID, numVectors, vectorPtr, amountWrittenPtr)
    int		streamID;	/* The user's index into its open file list */
    register int numVectors;	/* The # of vectors in vectorArray */
    register Fs_IOVector *vectorPtr;	
				/* The vectors defining where and how much to 
				 * write. */
    int		*amountWrittenPtr;	/* Total # of bytes written. */
{
    ReturnStatus	status;
    Fs_Stream		*streamPtr;	/* The stream to write to. */
    Proc_ControlBlock 	*procPtr;	/* This process's control block. */
    register int	sum = 0;	/* Total # of bytes written. */
    register int	i;

    /*
     * Map from stream ID to file pointer and do the write.
     * If the process is not remote, Fs_Write takes care of making
     * the write buffer accessible.
     */

    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);

    if (status == SUCCESS) {
	/*
	 * Doing a Fs_Write for each vector buffer is probably not the
	 * most efficiennt way to do this, but it is simple.
	 */
	for (i= 0; i < numVectors; i++, vectorPtr++) {

	    status = Fs_Write(streamPtr, vectorPtr->buffer, streamPtr->offset, 
				&(vectorPtr->bufSize));
	    /*
	     * Check to see if the write failed or we got an end-of-file.
	     */
	    if ((status != SUCCESS) || (vectorPtr->bufSize == 0)) {
		break;
	    }
	    sum += vectorPtr->bufSize;
	}
    }
    *amountWrittenPtr = sum;
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_CommandStub --
 *
 *	Stub for the Fs_Command system call.  This copies in a buffer
 *	containing arguments for the particular command, and copies
 *	out whatever Fs_Command leaves in the buffer after its done.
 *
 * Results:
 *	A return code.
 *
 * Side effects:
 *	Call Fs_Command
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_CommandStub(command, bufSize, buffer)
    int command;	/* Defined in user include "test.h" */
    int bufSize;	/* Size in bytes of buffer */
    Address buffer;	/* In/Out - Command specific data */
{
    ReturnStatus status;
    Address localBuffer;

    if (bufSize > 0) {
	localBuffer = (Address)malloc(bufSize);
	status = Vm_CopyIn(bufSize, buffer, localBuffer);
	if (status != SUCCESS) {
	    return(status);
	}
    } else {
	localBuffer = (Address)NIL;
    }

    status = Fs_Command(command, bufSize, localBuffer);

    if (localBuffer != (Address)NIL) {
	if (status == SUCCESS) {
	    status = Vm_CopyOut(bufSize, localBuffer, buffer);
	}
	free(localBuffer);
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_MakeDeviceStub --
 *
 *	The Fs_MakeDevice system call stub.
 *
 * Results:
 *	Those of Fs_MakeDevice.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_MakeDeviceStub(pathName, devicePtr, permissions)
    char *pathName;		/* Device file to create */
    Fs_Device *devicePtr;	/* Specifies the device file attributes */
    int permissions;		/* Permissions on the special file */
{
    Fs_Device 			device;
    int				pathNameLength;
    char			newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the device name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }
    if (Vm_CopyIn(sizeof(Fs_Device), (Address)devicePtr, 
		  (Address)&device) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }

    return(Fs_MakeDevice(newName, &device, permissions));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_MakeDirStub --
 *
 *	The Fs_MakeDir system call stub.
 *
 * Results:
 *	Those of Fs_MakeDir.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_MakeDirStub(pathName, permissions)
    char *pathName;
    int permissions;
{
    int			pathNameLength;
    char		newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space to the kernel stack.
     */

    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    return(Fs_MakeDir(newName, permissions));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_RemoveStub --
 *
 *	The stub for the Fs_Remove system call.
 *
 * Results:
 *	Those of Fs_Remove.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_RemoveStub(pathName)
    char *pathName;
{
    int			pathNameLength;
    char		newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space to the kernel stack.
     */

    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    return(Fs_Remove(newName));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_RemoveDirStub --
 *
 *	The Fs_RemoveDir system call stub.
 *
 * Results:
 *	The results of Fs_RemoveDir.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_RemoveDirStub(pathName)
    char *pathName;
{
    int		pathNameLength;
    char	newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    return(Fs_RemoveDir(newName));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_ChangeDirStub --
 *
 *	The stub for the system call that changes the current directory.
 *	The argument specifies a new current directory.  The argument
 *	can be a relative path name.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_ChangeDirStub(pathName)
    char *pathName;
{
    int		pathNameLength;
    char	newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    return(Fs_ChangeDir(newName));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_GetNewIDStub --
 *
 *	The stub for the system call that duplicates a stream ID.
 *	See Fs_GetNewID.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_GetNewIDStub(streamID, newStreamIDPtr)
    int streamID;
    int *newStreamIDPtr;
{
    ReturnStatus 	status;
    int 		newStreamID;

    if (Vm_CopyIn(sizeof(int), (Address) newStreamIDPtr, 
		  (Address) &newStreamID) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    status = Fs_GetNewID(streamID, &newStreamID);
    if (status != SUCCESS) {
	return(status);
    }

    if (Vm_CopyOut(sizeof(int), (Address) &newStreamID, 
			(Address) newStreamIDPtr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    } else {
	return(SUCCESS);
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_GetAttributesStub --
 *
 *	Stub for the Fs_GetAttributes system call.
 *
 * Results:
 *	The attributes for the named file.
 *
 * Side effects:
 *	*attrPtr is filled in with the attributes of the file.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_GetAttributesStub(pathName, fileOrLink, attrPtr)
    char *pathName;
    int fileOrLink;
    Fs_Attributes *attrPtr;
{
    ReturnStatus 	status;
    Fs_Attributes	attributes;
    int			pathNameLength;
    char		newName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    status = Fs_GetAttributes(newName, fileOrLink, &attributes);
    if (status != SUCCESS) {
	return(status);
    }

    if (Vm_CopyOut(sizeof(attributes), (Address) &attributes, 
		   (Address) attrPtr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    } else {
	return(SUCCESS);
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_CheckAccess --
 *
 *	Process the Fs_CheckAccess system all.
 *
 * Results:
 *	SYS_ARG_NOACCESS if pathname not accessible.
 *	FS_INVALID_ARG if pathname too long.
 *	Otherwise:
 *	    SUCCESS if accessible.
 *	    FS_NO_ACCESS if not accessible.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_CheckAccess(pathName, perm, useRealID)
    char 	*pathName;
    int		perm;
    Boolean	useRealID;
{
    ReturnStatus	status;
    Fs_Attributes	attributes;
    Proc_ControlBlock	*procPtr;
    int			pathNameLength;
    char		newName[FS_MAX_PATH_NAME_LENGTH];
    register int	bits;

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    procPtr = Proc_GetEffectiveProc();

    status = Fs_GetAttributes(newName, FS_ATTRIB_FILE, &attributes);
    if (status != SUCCESS) {
	return(status);
    } else if (perm == FS_EXISTS) {
	/*
	 * Only checking for existence, not read or write access.
	 */
	return(SUCCESS);
    } else if ((useRealID && (procPtr->userID == 0)) ||
		(!useRealID && (procPtr->effectiveUserID == 0))) {
	/*
	 * Let the super-user do anything.
	 */
	return(SUCCESS);
    }
    /*
     * Translate from FS_READ etc, into permission bits, ie. FS_WORLD_READ.
     * The world permissions are shifted left to the owner or group permissions
     * if the process's userID or one of its group IDs matches the file's.
     */
    bits = 0;
    if (perm & FS_READ) {
	bits |= FS_WORLD_READ;
    }
    if (perm & FS_WRITE) {
	bits |= FS_WORLD_WRITE;
    }
    if (perm & FS_EXECUTE) {
	bits |= FS_WORLD_EXEC;
    }

    if ((useRealID && attributes.uid == procPtr->userID) ||
        (!useRealID && attributes.uid == procPtr->effectiveUserID)) {
	bits <<= 6;
    } else {
	int	i;
	for (i = 0; i < procPtr->fsPtr->numGroupIDs; i++) {
	    if (attributes.gid == procPtr->fsPtr->groupIDs[i]) {
		bits <<= 3;
		break;
	    }
	}
    }
    if ((attributes.permissions & bits) == bits) {
	return (GEN_SUCCESS);
    } else {
	return (FS_NO_ACCESS);
    }
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_GetAttributesIDStub --
 *
 *	Stub for the Fs_GetAttributesID system call.
 *
 * Results:
 *	Get the attributes for the file referred to by the specified
 *	descriptor.
 *
 * Side effects:
 *	 *attrPtr is filled in with the attributes of the file.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_GetAttributesIDStub(streamID, attrPtr)
    int		streamID;	/* The user's index into its open file list */
    Fs_Attributes *attrPtr;	/* point to buffer to hold attributes */
{
    register	ReturnStatus 	status;
    Fs_Stream			*streamPtr;
    Fs_Attributes 		attributes;

    status = Fs_GetStreamPtr(Proc_GetEffectiveProc(), streamID, &streamPtr);
    if (status != SUCCESS) {
	return(status);
    }
    status = Fs_GetAttrStream(streamPtr, &attributes);
    if (status != SUCCESS) {
	return(status);
    }

    if (Vm_CopyOut(sizeof(attributes), (Address) &attributes, 
		   (Address) attrPtr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    } else {
	return(SUCCESS);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_SetAttributesStub --
 *
 *	Stub for Fs_SetAttributes system call.  This is a general
 *	routine that sets ownership, access & modify times, permissions,
 *	and user-defined file type.  The calling process must own the file.	
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None here in this stub.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_SetAttributesStub(pathName, fileOrLink, attrPtr)
    char *pathName;
    int fileOrLink;
    Fs_Attributes *attrPtr;
{
    int				pathNameLength;
    Fs_Attributes		attr;
    char			newName[FS_MAX_PATH_NAME_LENGTH];

    if (Vm_CopyIn(sizeof(attr), (Address) attrPtr, 
		  (Address) &attr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    return(Fs_SetAttributes(newName, fileOrLink, &attr, FS_SET_ALL_ATTRS));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_SetAttrStub --
 *
 *	Stub for Fs_SetAttr system call.  Like the old Fs_SetAttributes
 *	but with an additional flag specifying exactly which attributes
 *	to update.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_SetAttrStub(pathName, fileOrLink, attrPtr, flags)
    char *pathName;		/* Name of file to manipulate */
    int fileOrLink;		/* Whether or not to follow symbolic links */
    Fs_Attributes *attrPtr;	/* New values for attributes */
    int flags;			/* What attributes to set */
{
    int				pathNameLength;
    Fs_Attributes		attr;
    char			newName[FS_MAX_PATH_NAME_LENGTH];

    if (Vm_CopyIn(sizeof(attr), (Address) attrPtr, 
		  (Address) &attr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    return(Fs_SetAttributes(newName, fileOrLink, &attr, flags));
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_SetAttributesIDStub --
 *
 *	Stub for Fs_SetAttributesID system call.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_SetAttributesIDStub(streamID, attrPtr)
    int streamID;
    Fs_Attributes *attrPtr;
{
    register 	ReturnStatus 	status;
    Fs_Stream			*streamPtr;
    Fs_Attributes		attr;
    Fs_UserIDs			ids;
    Proc_ControlBlock		*procPtr;

    if (Vm_CopyIn(sizeof(attr), (Address) attrPtr, 
		  (Address) &attr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }

    /*
     * Map from stream ID to file pointer and get the attributes.
     */
    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status == SUCCESS) {
	Fs_SetIDs(procPtr, &ids);
	status = Fs_SetAttrStream(streamPtr, &attr, &ids, FS_SET_ALL_ATTRS);
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_SetAttrIDStub --
 *
 *	Stub for Fs_SetAttrID system call.  Like Fs_SetAttributesID
 *	but has a flag parameter that specifies what attrs to set.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_SetAttrIDStub(streamID, attrPtr, flags)
    int streamID;		/* References file to manipulate */
    Fs_Attributes *attrPtr;	/* New attributes for the file */
    int flags;			/* What attributes to set */
{
    register 	ReturnStatus 	status;
    Fs_Stream			*streamPtr;
    Fs_Attributes		attr;
    Fs_UserIDs			ids;
    Proc_ControlBlock		*procPtr;

    if (Vm_CopyIn(sizeof(attr), (Address) attrPtr, 
		  (Address) &attr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }

    /*
     * Map from stream ID to file pointer and get the attributes.
     */
    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status == SUCCESS) {
	Fs_SetIDs(procPtr, &ids);
	status = Fs_SetAttrStream(streamPtr, &attr, &ids, flags);
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_SetDefPermStub --
 *
 *	Set the default permissions on files created by this process.
 *	These defaults get inherited by child processes.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	Sets the default permissions mask in the filesystem related
 *	state kept in the proc table.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_SetDefPermStub(permissions, oldPermPtr)
    int permissions;	/*  A mask of 9 permission bits.  Three sets of 3
			 * bits.  The first set is for the owner's permissions,
			 * the second for the group's, and the last is for
			 * everyone else.  The three bits indicate read,
			 * write, and execute permissions, respectively.
			 * A permission bit set in this mask ENABLES the
			 * permission on created files. */
    int *oldPermPtr;	/* Output, the previous value of the permissions mask */
{
    register	Proc_ControlBlock	*procPtr;
    int 				oldPerm;

    procPtr = Proc_GetEffectiveProc();
    oldPerm = procPtr->fsPtr->filePermissions;
    procPtr->fsPtr->filePermissions = (unsigned int)(permissions & 0777);
    if (Proc_ByteCopy(FALSE, sizeof(int), (Address)&oldPerm, 
		(Address)oldPermPtr) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    } else {
	return(SUCCESS);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_HardLinkStub --
 *
 *	Stub for the Fs_HardLink system call.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_HardLinkStub(fileName, linkName)
    char *fileName;	/* Name of the existing file */
    char *linkName;	/* New name to link to the existing file */
{
    ReturnStatus 	status = SUCCESS;
    int			fileNameLength;
    int			linkNameLength;
    char		newFileName[FS_MAX_PATH_NAME_LENGTH];
    char		*newLinkName;

    /*
     * Copy the filename in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, fileName, newFileName,
		       &fileNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (fileNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    /*
     * Maybe I could put the link name onto the stack as well but I am 
     * allocing it here because I'm not sure if the stack is big enough.
     * Should just make the stack bigger I suppose ...
     */
    newLinkName = (char  *) malloc(FS_MAX_PATH_NAME_LENGTH);
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, linkName, newLinkName,
		       &linkNameLength) == SUCCESS) {
	if (linkNameLength == FS_MAX_PATH_NAME_LENGTH) {
	    status = FS_INVALID_ARG;
	} else {
	    status = Fs_HardLink(newFileName, newLinkName);
	}
    } else {
	status = SYS_ARG_NOACCESS;
    }

    free((Address) newLinkName);

    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_RenameStub --
 *
 *	Stub for Fs_Rename system call.
 *
 * Results:
 *	A return status.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_RenameStub(pathName, newName)
    char *pathName;
    char *newName;
{
    ReturnStatus	status;
    int			pathNameLength;
    int			newNameLength;
    char		newPathName[FS_MAX_PATH_NAME_LENGTH];
    char		*newNewName;

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, pathName, newPathName,
		       &pathNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (pathNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    /*
     * Maybe I could put the new name onto the stack as well but I am 
     * allocing it here because I'm not sure if the stack is big enough.
     * Should just make the stack bigger I suppose ...
     */
    newNewName = (char  *) malloc(FS_MAX_PATH_NAME_LENGTH);
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, newName, newNewName,
		       &newNameLength) == SUCCESS) {
	if (newNameLength == FS_MAX_PATH_NAME_LENGTH) {
	    status = FS_INVALID_ARG;
	} else {
	    status = Fs_Rename(newPathName, newNewName);
	}
    } else {
	status = SYS_ARG_NOACCESS;
    }

    free((Address) newNewName);

    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_SymLinkStub --
 *
 *	Stub for Fs_SymLink system call.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_SymLinkStub(targetName, linkName, remoteFlag)
    char *targetName;
    char *linkName;
    Boolean remoteFlag;
{
    ReturnStatus	status;
    int			targetNameLength;
    int			linkNameLength;
    char		newTargetName[FS_MAX_PATH_NAME_LENGTH];
    char		*newLinkName;

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, targetName, newTargetName,
		       &targetNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (targetNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    /*
     * Maybe I could put the new name onto the stack as well but I am 
     * allocing it here because I'm not sure if the stack is big enough.
     * Should just make the stack bigger I suppose ...
     */
    newLinkName = (char  *) malloc(FS_MAX_PATH_NAME_LENGTH);
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, linkName, newLinkName,
		       &linkNameLength) == SUCCESS) {
	if (linkNameLength == FS_MAX_PATH_NAME_LENGTH) {
	    status = FS_INVALID_ARG;
	} else {
	    status = Fs_SymLink(newTargetName, newLinkName, remoteFlag);
	}
    } else {
	status = SYS_ARG_NOACCESS;
    }
    free((Address) newLinkName);

    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_ReadLinkStub --
 *
 *	Stub for Fs_ReadLink system call.  The named file should be a
 *	remote link or a symbolic link file.  It is opened and its
 *	contents (another file's name) are read and stored in the buffer.
 *
 * Results:
 *	A return code from the open and read.
 *
 * Side effects:
 *	Fills in the buffer with contents of the link file, and
 *	returns the amount of data stored there in *linkSizePtr.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_ReadLinkStub(linkName, bufSize, buffer, linkSizePtr)
    char *linkName;		/* Name of the link file */
    int bufSize;		/* Size of the user's buffer */
    char *buffer;		/* The buffer to hold the link's value */
    int *linkSizePtr;		/* Return, the amount of data in buffer */
{
    ReturnStatus	status;
    Fs_Stream		*streamPtr;
    int			linkNameLength;
    char		newLinkName[FS_MAX_PATH_NAME_LENGTH];

    /*
     * Copy the name in from user space onto the kernel stack.
     */
    if (Fsutil_StringNCopy(FS_MAX_PATH_NAME_LENGTH, linkName, newLinkName,
		       &linkNameLength) != SUCCESS) {
	return(SYS_ARG_NOACCESS);
    }
    if (linkNameLength == FS_MAX_PATH_NAME_LENGTH) {
	return(FS_INVALID_ARG);
    }

    status = Fs_Open(newLinkName, FS_READ | FS_USER,
		     FS_SYMBOLIC_LINK|FS_REMOTE_LINK, 0, &streamPtr);
    if (status == SUCCESS) {
	status = Fs_Read(streamPtr, buffer, 0, &bufSize);
	if (status == SUCCESS) {
	    /*
	     * Sprite's link count includes the terminating null character
	     * in the character count return while Unix doesn't.  Make our
	     * count backward-compatible with Unix.
	     */
	     status = Proc_ByteCopy(TRUE, sizeof(int), (Address) &bufSize,
		    (Address) &linkNameLength);
	    if (status == SUCCESS) {
		if (linkNameLength >= FS_MAX_PATH_NAME_LENGTH) {
		    status = FS_INVALID_ARG;
		} else {
		    status = Proc_ByteCopy(TRUE, linkNameLength,
			    (Address) buffer, (Address) newLinkName);
		    if (status == SUCCESS &&
			    newLinkName[linkNameLength-1] == '\0') {
			linkNameLength--;
			status = Proc_ByteCopy(FALSE, sizeof(int),
				(Address) &linkNameLength, (Address) &bufSize);
		    }
		}
	    }
	    status = Proc_ByteCopy(FALSE, sizeof(int), (Address)&linkNameLength,
			       (Address)linkSizePtr);
	}
	(void)Fs_Close(streamPtr);
    }
    return(status);
}

/*
 *----------------------------------------------------------------------
 *
 * Fs_IOControlStub --
 *
 *	Stub for the Fs_IOControl system call.  IOControl commands
 *	that affect Fs_Stream objects are done here.  The rest of
 *	the commands are passed down to the stream type specific
 *	IOControl procedures.
 *
 * Results:
 *	A return status.  The output buffer is modified with results
 *	for the command.  The format of the various results is explained
 *	in user/fs.h for generic operations, and in dev/ *.h for
 *	device specific operations.
 *
 * Side effects:
 *	Depends on the command.
 *
 *----------------------------------------------------------------------
 */

ReturnStatus
Fs_IOControlStub(streamID, command, inBufSize, inBuffer,
			   outBufSize, outBuffer)
    int 	streamID;	/* User's handle on the stream */
    int 	command;	/* IOControl command */
    int 	inBufSize;	/* Size of inBuffer */
    Address 	inBuffer;	/* Command specific input parameters */
    int 	outBufSize;	/* Size of outBuffer */
    Address 	outBuffer;	/* Command specific output parameters */
{
    Proc_ControlBlock *procPtr;
    Fs_ProcessState *fsPtr;
    Fs_Stream 	 *streamPtr;
    register ReturnStatus status = SUCCESS;
    Address	localInBuffer = (Address)NIL;
    Address	localOutBuffer = (Address)NIL;
    Fs_IOCParam ioctl;
    Fs_IOReply reply;

    /*
     * Get a stream pointer.
     */
    procPtr = Proc_GetEffectiveProc();
    status = Fs_GetStreamPtr(procPtr, streamID, &streamPtr);
    if (status != SUCCESS) {
	return(status);
    }

    if (!Fsutil_HandleValid(streamPtr->ioHandlePtr)) {
	return(FS_STALE_HANDLE);
    }

    ioctl.command = command;
    ioctl.format = mach_Format;
    ioctl.procID = procPtr->processID;
    ioctl.familyID = procPtr->familyID;
    ioctl.uid = procPtr->effectiveUserID;

    /*
     * Fast path for non-generic I/O controls to pseudo-devices.
     * We don't copy in/out the user's parameter blocks because the
     * pseudo-device code does direct cross-address-space copy later.
     * We also skip the check against large parameter blocks so arbitrary
     * amounts of data can be fed to and from a pseudo-device.
     */
    if ((streamPtr->ioHandlePtr->fileID.type == FSIO_LCL_PSEUDO_STREAM) &&
	(command > IOC_GENERIC_LIMIT)) {
	ioctl.inBufSize = inBufSize;
	ioctl.inBuffer = inBuffer;
	ioctl.outBufSize = outBufSize;  
	ioctl.outBuffer = outBuffer;
	ioctl.flags = FS_USER_IN|FS_USER_OUT;
	return(Fs_IOControl(streamPtr, &ioctl, &reply));
    }

    if (inBufSize > IOC_MAX_BYTES || outBufSize > IOC_MAX_BYTES) {
	return(SYS_INVALID_ARG);
    }
    ioctl.flags = 0;	/* We'll copy buffer's into/out of the kernel */

    /*
     * The input parameters are copied into kernel
     * space, and the results are copied back out so that the
     * lower level routines don't have to worry about it.
     */
    if ((outBufSize > 0) && (outBuffer != (Address)0) &&
			    (outBuffer != (Address)NIL)){
	ioctl.outBuffer = localOutBuffer = (Address) malloc(outBufSize);
	ioctl.outBufSize = outBufSize;
    } else {
	ioctl.outBuffer = (Address)NIL;
	ioctl.outBufSize = outBufSize = 0;
    }
    if ((inBufSize > 0) && (inBuffer != (Address)0) &&
			   (inBuffer != (Address)NIL)) {
	ioctl.inBuffer  = localInBuffer = (Address) malloc(inBufSize);
	ioctl.inBufSize = inBufSize;
    } else {
	ioctl.inBuffer = (Address)NIL;
	ioctl.inBufSize = 0;
    }

    if (ioctl.inBufSize &&
	Vm_CopyIn(ioctl.inBufSize, inBuffer, ioctl.inBuffer) != SUCCESS) {
	status = SYS_ARG_NOACCESS;
    } else {
	status = Fs_IOControl(streamPtr, &ioctl, &reply);
	if (status == SUCCESS) {
	    /*
	     * Post process the set/get flags stuff because the close-on-exec
	     * flag is not kept down at the stream level, but up along
	     * with the streamID.
	     */
	    fsPtr = procPtr->fsPtr;
	    switch(command) {
		case IOC_GET_FLAGS: {
		    if (fsPtr->streamFlags[streamID] & FS_CLOSE_ON_EXEC) {
			*(int *)ioctl.outBuffer |= IOC_CLOSE_ON_EXEC;
		    }
		    break;
		}
		case IOC_SET_BITS:
		case IOC_SET_FLAGS: {
		    int flags;
		    flags = *(int *)ioctl.inBuffer;

		    if (flags & IOC_CLOSE_ON_EXEC) {
			fsPtr->streamFlags[streamID] |= FS_CLOSE_ON_EXEC;
		    } else if (command == IOC_SET_FLAGS) {
			fsPtr->streamFlags[streamID] &= ~FS_CLOSE_ON_EXEC;
		    }
		    break;
		}
		case IOC_CLEAR_BITS:{
		    int flags;
		    flags = *(int *)ioctl.inBuffer;
		    if (flags & IOC_CLOSE_ON_EXEC) {
			fsPtr->streamFlags[streamID] &= ~FS_CLOSE_ON_EXEC;
		    }
		    break;
		}
	    }
	    if (outBufSize) {
		status = Vm_CopyOut(reply.length, ioctl.outBuffer,
				    outBuffer);
	    }
	}
    }
    if (localInBuffer != (Address)NIL) {
	free(localInBuffer);
    }
    if (localOutBuffer != (Address)NIL) {
	free(localOutBuffer);
    }
    return(status);
}


/*
 *----------------------------------------------------------------------
 *
 * Fs_CreatePipeStub --
 *
 *      This is the stub for the Fsio_CreatePipe system call.  This routine sets
 *      up stream IDs for the two Fs_Stream objects returned by (the internal)
 *      Fsio_CreatePipe: one for reading the pipe and one for writing to it.
 *
 * Results:
 *	A retrun status or SUCCESS if successful.
 *
 * Side effects:
 *	The two arguments are out parameters and get filled in with a
 *	stream id.  Also, opening a pipe sets up state until both ends are 
 *	closed with Fs_Close system calls.
 *
 *----------------------------------------------------------------------
 */
ReturnStatus
Fs_CreatePipeStub(inStreamIDPtr, outStreamIDPtr)
    int *inStreamIDPtr;		/* Handle that the user can use to read the
				 * pipe. */
    int *outStreamIDPtr;	/* Handle that the user can use to write the
				 * pipe. */
{
    register ReturnStatus	status;
    Fs_Stream			*inStreamPtr;
    int		 		inStreamID;
    Fs_Stream			*outStreamPtr;
    int				outStreamID;

    /*
     * Call the internal routine to create the pipe.
     */
    status = Fsio_CreatePipe(&inStreamPtr, &outStreamPtr);
    if (status != SUCCESS) {
	return(status);
    }
    inStreamPtr->flags |= FS_USER;
    outStreamPtr->flags |= FS_USER;

    /*
     * Get stream ids for the two streams.
     */

    status = Fs_GetStreamID(inStreamPtr, &inStreamID);
    if (status != SUCCESS) {
	(void) Fs_Close(inStreamPtr);
	(void) Fs_Close(outStreamPtr);
	return(status);
    }
    status = Fs_GetStreamID(outStreamPtr, &outStreamID);
    if (status != SUCCESS) {
	Fs_ClearStreamID(inStreamID, (Proc_ControlBlock *)NIL);
	(void) Fs_Close(inStreamPtr);
	(void) Fs_Close(outStreamPtr);
	return(status);
    }

    /*
     * Copy out the stream ids to the user.
     */

    if (Vm_CopyOut(sizeof(int), (Address) &inStreamID, 
		   (Address) inStreamIDPtr) == SUCCESS &&
	Vm_CopyOut(sizeof(int), (Address) &outStreamID, 
		   (Address) outStreamIDPtr) == SUCCESS) {
	return(SUCCESS);
    }

    /*
     * If couldn't copy out then clean up state and return an error.
     */

    Fs_ClearStreamID(inStreamID, (Proc_ControlBlock *)NIL);
    Fs_ClearStreamID(outStreamID, (Proc_ControlBlock *)NIL);
    (void) Fs_Close(inStreamPtr);
    (void) Fs_Close(outStreamPtr);

    return(SYS_ARG_NOACCESS);
}

