/*
 * fsTopMigrate.c --
 *
 * Procedures to handle migrating open files between machines.  The basic
 * strategy is to first do some local book-keeping on the client we are
 * leaving, then ship state to the new client, then finally tell the
 * I/O server about it, and finish up with local book-keeping on the
 * new client.  There are three stream-type procedures used: 'migStart'
 * does the initial book-keeping on the original client, 'migEnd' does
 * the final book-keeping on the new client, and 'migrate' is called
 * on the I/O server to shift around state associated with the client.
 *
 * Copyright (C) 1985, 1988, 1989 Regents of the University of California
 * 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: /r3/kupfer/spriteserver/src/sprited/fs/RCS/fsTopMigrate.c,v 1.3 92/01/21 15:03:22 kupfer Exp $ SPRITE (Berkeley)";
#endif not lint


#include <sprite.h>
#include <byte.h>
#include <ckalloc.h>
#include <fs.h>
#include <fsutil.h>
#include <fsconsist.h>
#include <fspdev.h>
#include <fsio.h>
#include <fsprefix.h>
#include <fsNameOps.h>
#include <fsStat.h>
#include <procMigrate.h>
#include <rpc.h>
#include <string.h>

extern int fsio_MigDebug;
#define DEBUG( format ) \
	if (fsio_MigDebug) { printf format ; }


#ifdef SPRITED_MIGRATION
/*
 * ----------------------------------------------------------------------------
 *
 * Fs_InitiateMigration --
 *
 *	Return the size of the encapsulated file system state.
 *	(Note: for now, we'll let the encapsulation procedure do the same
 *	work (in part); later things can be simplified to use a structure
 *	and to keep around some info off the ClientData hook.)
 *
 * Results:
 *	SUCCESS is returned directly; the size of the encapsulated state
 *	is returned in infoPtr->size.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 *
 */

/* ARGSUSED */
ReturnStatus
Fs_InitiateMigration(procPtr, hostID, infoPtr)
    Proc_ControlBlock *procPtr;			/* process being migrated */
    int hostID;					/* host to which it migrates */
    Proc_EncapInfo *infoPtr;			/* area w/ information about
						 * encapsulated state */
{
    Fs_ProcessState *fsPtr;
    int numStreams;
    int streamFlagsLen;
    Fsprefix *prefixPtr;
    int cwdLength;


    fsPtr = procPtr->fsPtr;
    numStreams = fsPtr->numStreams;
    /*
     * Get the prefix for the current working directory, and its size.
     * We pass the name over so it can be opened to make sure the prefix
     * is available.
     */
    if (fsPtr->cwdPtr->nameInfoPtr == (Fs_NameInfo *)NIL) {
	panic("Fs_GetEncapSize: no name information for cwd.\n");
	return(FAILURE);
    }
    prefixPtr = fsPtr->cwdPtr->nameInfoPtr->prefixPtr;
    if (prefixPtr == (Fsprefix *)NIL) {
	panic("Fs_GetEncapSize: no prefix for cwd.\n");
	return(FAILURE);
    }
    cwdLength = Byte_AlignAddr(prefixPtr->prefixLength + 1);
    
    /*
     * When sending an array of characters, it has to be even-aligned.
     */
    streamFlagsLen = Byte_AlignAddr(numStreams * sizeof(char));
    
    /*
     * Send the groups, file permissions, number of streams, and encapsulated
     * current working directory.  For each open file, send the
     * streamID and encapsulated stream contents.
     *
     *	        data			size
     *	 	----			----
     * 		# groups		int
     *	        groups			(# groups) * int
     *		permissions		int
     *		# files			int
     *		per-file flags		(# files) * char
     *		encapsulated files	(# files) * (Fsio_MigInfo + int)
     *		cwd			Fsio_MigInfo + int + strlen(cwdPrefix) + 1
     */
    infoPtr->size = (4 + fsPtr->numGroupIDs) * sizeof(int) +
	    streamFlagsLen + numStreams * (sizeof(Fsio_MigInfo) + sizeof(int)) +
	    sizeof(Fsio_MigInfo) + cwdLength;
    return(SUCCESS);	
}
#endif /* SPRITED_MIGRATION */


/*
 * ----------------------------------------------------------------------------
 *
 * Fs_GetEncapSize --
 *
 *	Return the size of the encapsulated stream.
 *
 * Results:
 *	The size of the migration information structure.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 *
 */

int
Fs_GetEncapSize()
{
    return(sizeof(Fsio_MigInfo));
}


#ifdef SPRITED_MIGRATION
/*
 *----------------------------------------------------------------------
 *
 * Fs_EncapFileState --
 *
 *	Encapsulate the file system state of a process for migration.  
 *
 * Results:
 *	Any error during stream encapsulation
 *	is returned; otherwise, SUCCESS.  The encapsulated state is placed
 *	in the area referenced by ptr.
 *
 * Side effects:
 *	None.  
 *
 *----------------------------------------------------------------------
 */

/* ARGSUSED */
ReturnStatus
Fs_EncapFileState(procPtr, hostID, infoPtr, ptr)
    register Proc_ControlBlock 	*procPtr;  /* The process being migrated */
    int hostID;				   /* host to which it migrates */
    Proc_EncapInfo *infoPtr;		   /* area w/ information about
					    * encapsulated state */
    Address ptr;			   /* Pointer to allocated buffer */
{
    Fs_ProcessState *fsPtr;
    int numStreams;
    int numGroups;
    int streamFlagsLen;
    Fs_Stream *streamPtr;
    int i;
    ReturnStatus status;
    Fsprefix *prefixPtr;
    int cwdLength;
    int size;
    int encaps;


    fsPtr = procPtr->fsPtr;
    numStreams = fsPtr->numStreams;
    /*
     * Get the prefix for the current working directory, and its size.
     * We pass the name over so it can be opened to make sure the prefix
     * is available.
     */
    if (fsPtr->cwdPtr->nameInfoPtr == (Fs_NameInfo *)NIL) {
	panic("Fs_EncapFileState: no name information for cwd.\n");
	return(FAILURE);
    }
    prefixPtr = fsPtr->cwdPtr->nameInfoPtr->prefixPtr;
    if (prefixPtr == (Fsprefix *)NIL) {
	panic("Fs_EncapFileState: no prefix for cwd.\n");
	return(FAILURE);
    }
    cwdLength = Byte_AlignAddr(prefixPtr->prefixLength + 1);
    
    /*
     * When sending an array of characters, it has to be even-aligned.
     */
    streamFlagsLen = Byte_AlignAddr(numStreams * sizeof(char));
    
    /*
     * Send the groups, file permissions, number of streams, and encapsulated
     * current working directory.  For each open file, send the
     * streamID and encapsulated stream contents.
     *
     *	        data			size
     *	 	----			----
     * 		# groups		int
     *	        groups			(# groups) * int
     *		permissions		int
     *		# files			int
     *		per-file flags		(# files) * char
     *		encapsulated files	(# files) * (Fsio_MigInfo + int)
     *		cwd			Fsio_MigInfo + int + strlen(cwdPrefix) + 1
     */
    size = (4 + fsPtr->numGroupIDs) * sizeof(int) +
	    streamFlagsLen + numStreams * (sizeof(Fsio_MigInfo) + sizeof(int)) +
	    sizeof(Fsio_MigInfo) + cwdLength;
    if (size != infoPtr->size) {
	panic("Fs_EncapState: size of encapsulated state changed.\n");
	return(FAILURE);
    }

    /*
     * Send groups, filePermissions, numStreams, the cwd, and each file.
     */
    
    numGroups = fsPtr->numGroupIDs;
    Byte_FillBuffer(ptr, unsigned int, numGroups);
    if (numGroups > 0) {
	bcopy((Address) fsPtr->groupIDs, ptr, numGroups * sizeof(int));
	ptr += numGroups * sizeof(int);
    }
    Byte_FillBuffer(ptr, unsigned int, fsPtr->filePermissions);
    Byte_FillBuffer(ptr, int, numStreams);
    if (numStreams > 0) {
	bcopy((Address) fsPtr->streamFlags, ptr, numStreams * sizeof(char));
	ptr += streamFlagsLen;
    }
    
    Byte_FillBuffer(ptr, int, prefixPtr->prefixLength);
    strncpy(ptr, prefixPtr->prefix, prefixPtr->prefixLength);
    ptr[prefixPtr->prefixLength] = '\0';
    ptr += cwdLength;

    status = Fsio_EncapStream(fsPtr->cwdPtr, ptr);
    if (status != SUCCESS) {
	printf(
		  "Fs_EncapFileState: Error %x from Fsio_EncapStream on cwd.\n",
		  status);
	return(status);
    }
    fsPtr->cwdPtr = (Fs_Stream *) NIL;
    ptr += sizeof(Fsio_MigInfo);
    encaps = 1;

    for (i = 0; i < fsPtr->numStreams; i++) {
	streamPtr = fsPtr->streamList[i];
	if (streamPtr != (Fs_Stream *) NIL) {
	    Byte_FillBuffer(ptr, int, i);
	    status = Fsio_EncapStream(streamPtr, ptr);
	    if (status != SUCCESS) {
		printf(
			  "Fs_EncapFileState: Error %x from Fsio_EncapStream.\n",
			  status);
		return(status);
	    }
	    fsPtr->streamList[i] = (Fs_Stream *) NIL;
	    encaps++;
	} else {
	    Byte_FillBuffer(ptr, int, NIL);
	    bzero(ptr, sizeof(Fsio_MigInfo));
	}	
	ptr += sizeof(Fsio_MigInfo);
    }

#ifndef CLEAN   
    Proc_MigAddToCounter(encaps, &fs_Stats.mig.filesEncapsulated,
			 &fs_Stats.mig.encapSquared);
#endif /* CLEAN */    

    return(SUCCESS);
}
#endif /* SPRITED_MIGRATION */


#ifdef SPRITED_MIGRATION
/*
 *----------------------------------------------------------------------
 *
 * Fs_DeencapFileState --
 *
 *	Get the file system state of a process from another node.  The
 *	buffer contains group information, permissions, the encapsulated
 *	current working directory, and encapsulated streams.
 *
 * Results:
 *	If Fsio_DeencapStream returns an error, that error is returned.
 *	Otherwise, SUCCESS is returned.  
 *
 * Side effects:
 *	"Local" Fs_Streams are created and allocated to the foreign process.
 *
 *----------------------------------------------------------------------
 */
/*ARGSUSED*/
ReturnStatus
Fs_DeencapFileState(procPtr, infoPtr, buffer)
    register Proc_ControlBlock 	*procPtr; /* The process being migrated */
    Proc_EncapInfo *infoPtr;		  /* information about the buffer */
    Address buffer;			  /* buffer containing data */
{
    register Fs_ProcessState *fsPtr;
    int i;
    int index;
    int numGroups;
    int numStreams;
    ReturnStatus status;
    char *cwdName;
    int cwdLength;
    Fs_Stream *prefixStreamPtr;

    /*
     * Set up an fsPtr for the process.  Initialize some fields so that
     * at any point we can bail out on error by calling Fs_CloseState.  Some
     * fields are initialized from the information from the other host.
     */
    procPtr->fsPtr = fsPtr = mnew(Fs_ProcessState);
    fsPtr->cwdPtr = (Fs_Stream *) NIL;

    /*
     * Get group and permissions information.
     */
    Byte_EmptyBuffer(buffer, unsigned int, numGroups);
    fsPtr->numGroupIDs = numGroups;
    if (numGroups > 0) {
	fsPtr->groupIDs = (int *)ckalloc(numGroups * sizeof(int));
	bcopy(buffer, (Address) fsPtr->groupIDs, numGroups * sizeof(int));
	buffer += numGroups * sizeof(int);
    } else {
	fsPtr->groupIDs = (int *)NIL;
    }
    Byte_EmptyBuffer(buffer, unsigned int, fsPtr->filePermissions);

    /*
     * Get numStreams, flags, and the encapsulated cwd.  Allocate memory
     * for the streams and flags arrays if non-empty.  The array of
     * streamFlags may be an odd number of bytes, so we skip past the
     * byte of padding if it exists (using the Byte_AlignAddr macro).
     */

    Byte_EmptyBuffer(buffer, int, numStreams);
    fsPtr->numStreams = numStreams;
    if (numStreams > 0) {
	fsPtr->streamList = (Fs_Stream **)
		ckalloc(numStreams * sizeof(Fs_Stream *));
	fsPtr->streamFlags = (char *)ckalloc(numStreams * sizeof(char));
	bcopy(buffer, (Address) fsPtr->streamFlags, numStreams * sizeof(char));
	buffer += Byte_AlignAddr(numStreams * sizeof(char));
	for (i = 0; i < fsPtr->numStreams; i++) {
	    fsPtr->streamList[i] = (Fs_Stream *) NIL;
	}
    } else {
	fsPtr->streamList = (Fs_Stream **)NIL;
	fsPtr->streamFlags = (char *)NIL;
    }
    /*
     * Get the name of the current working directory and make sure it's
     * an installed prefix.
     */
    Byte_EmptyBuffer(buffer, int, cwdLength);
    cwdName = buffer;
    buffer += Byte_AlignAddr(cwdLength + 1);

    /*
     * Unlock the process while doing remote operations using it, or else
     * we can deadlock.
     */
    Proc_Unlock(procPtr);
    
    status = Fs_Open(cwdName, FS_READ | FS_FOLLOW, FS_FILE, 0,
		     &prefixStreamPtr);
    if (status != SUCCESS) {
	if (fsio_MigDebug) {
	    panic("Unable to open prefix '%s' for migrated process.\n",
		   cwdName);
	} else if (proc_MigDebugLevel > 1) {
	    printf("%s unable to open prefix '%s' for migrated process.\n",
		   "Warning: Fs_DeencapFileState:", cwdName);
	}
	goto failure;
    } else {
	(void) Fs_Close(prefixStreamPtr);
    }

    status = Fsio_DeencapStream(buffer, &fsPtr->cwdPtr);
    if (status != SUCCESS) {
	if (fsio_MigDebug) {
	    panic("GetFileState: Fsio_DeencapStream returned %x for cwd.\n",
		  status);
	} else if (proc_MigDebugLevel > 1) {
	    printf("%s Fsio_DeencapStream returned %x for cwd.\n",
		  "Warning: Fs_DeencapFileState:", status);
	}
	fsPtr->cwdPtr = (Fs_Stream *) NIL;
        goto failure;
    }
    buffer += sizeof(Fsio_MigInfo);

    

    /*
     * Get the other streams.
     */
    for (i = 0; i < fsPtr->numStreams; i++) {
	Byte_EmptyBuffer(buffer, int, index);
	if ((status == SUCCESS) && (index != NIL)) {
	    status = Fsio_DeencapStream(buffer, &fsPtr->streamList[index]);
	    if (status != SUCCESS) {
		    printf(
      "Fs_DeencapFileState: Fsio_DeencapStream for file id %d returned %x.\n",
			      index, status);
		    fsPtr->streamList[index] = (Fs_Stream *) NIL;
		    goto failure;
	    }
	}
	buffer += sizeof(Fsio_MigInfo);
    }

#ifndef CLEAN   
    Proc_MigAddToCounter(numStreams + 1, &fs_Stats.mig.filesDeencapsulated,
			 &fs_Stats.mig.deencapSquared);
#endif /* CLEAN */    
    
    Proc_Lock(procPtr);
    return(SUCCESS);
    
failure:
    Proc_Lock(procPtr);
    Fs_CloseState(procPtr,1);
    return(status);
    
}
#endif /* SPRITED_MIGRATION */
