/*
 * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Kungliga Tekniska
 *      Högskolan and its contributors.
 * 
 * 4. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "milko_locl.h"

RCSID("$Id: fs.c,v 1.27 1998/11/09 19:28:27 lha Exp $");


int
RXAFS_printhost(struct rx_call *call, char *fmt, ...)
{
    va_list args;
    struct in_addr addr;
    
    u_int32_t host = 
	call ? 
	(call->conn ? 
	 (call->conn->peer ? rx_HostOf(call->conn->peer) 
	  : 0) 
	 : 0)
	: 0;

    addr.s_addr = host;

    va_start (args, fmt);
    vprintf(fmt, args);
    va_end (args);
    printf(" from host %s\n", inet_ntoa(addr));
    return 0;
}

#define RESET_STRUCTPTR(name) {if((name)){memset((name),0,sizeof((*name)));}}


int
RXAFS_FetchData(struct rx_call *call,
		const AFSFid * a_fidToFetchP, 
		const int32_t a_offset, 
		const int32_t a_lenInBytes, 
		AFSFetchStatus * a_fidStatP,
		AFSCallBack * a_callBackP, 
		AFSVolSync * a_volSyncP )
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_FetchData: fid: %d %d %d offset: %d len: %d", 
	   a_fidToFetchP->Volume,
	   a_fidToFetchP->Vnode,
	   a_fidToFetchP->Unique,
	   a_offset,
	   a_lenInBytes) ;

    memset(a_fidStatP, 0, sizeof(struct AFSFetchStatus)) ;
    memset(a_callBackP, 0, sizeof(struct AFSCallBack)) ;
    memset(a_volSyncP, 0, sizeof(struct AFSVolSync)) ;

    a_callBackP->CallBackType = CBSHARED;
    a_callBackP->CallBackVersion = 1;
    a_callBackP->ExpirationTime = 4000;

    mvol = volser_getById(a_fidToFetchP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_getdata == NULL)
	return EOPNOTSUPP;

    ret = (*mvol->fs->fs_getdata)(cred, mvol->data, call,
				  a_fidToFetchP, a_offset, a_lenInBytes,
				  a_fidStatP);
    
    volser_release(mvol);
    
    return ret;
}

int
RXAFS_FetchACL (struct rx_call *call,
		const AFSFid *a_dirFidP,
		AFSOpaque *a_ACLP,
		AFSFetchStatus *a_dirNewStatP,
		AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    MilkoCred *cred = NULL;
    int ret;

    RXAFS_printhost(call,"RXAFS_FetchACL") ;

    RESET_STRUCTPTR(a_ACLP);
    RESET_STRUCTPTR(a_dirNewStatP);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(a_dirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_fetchacl == NULL)
	return EOPNOTSUPP;

    ret = (*mvol->fs->fs_fetchacl)(cred, mvol->data, 
				   a_dirFidP, a_ACLP, a_dirNewStatP);
    
    volser_release(mvol);
    return ret;
}

int
GetStatus(const MilkoCred *cred, 
	  const AFSFid *a_fidToStatP,
	  AFSFetchStatus *a_currStatP,
	  AFSCallBack *a_callBackP,
	  AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;

    RESET_STRUCTPTR(a_currStatP);
    RESET_STRUCTPTR(a_volSyncP);
    RESET_STRUCTPTR(a_callBackP);
    /* XXX CB module anyone ? */
    a_callBackP->CallBackType = CBSHARED;
    a_callBackP->CallBackVersion = 1;
    a_callBackP->ExpirationTime = 4000;

    mvol = volser_getById(a_fidToStatP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL)
	return EOPNOTSUPP;

    ret = (*mvol->fs->fs_getstatus)(cred, mvol->data,
				    a_fidToStatP, a_currStatP);
    
    volser_release(mvol);
    return ret;
}    


int
RXAFS_FetchStatus (struct rx_call *call,
		   const AFSFid *a_fidToStatP,
		   AFSFetchStatus *a_currStatP,
		   AFSCallBack *a_callBackP,
		   AFSVolSync *a_volSyncP)
{
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_FetchStatus: fid: %d %d %d",
	   a_fidToStatP->Volume,
	   a_fidToStatP->Vnode,
	   a_fidToStatP->Unique) ;

    return GetStatus(cred, a_fidToStatP, a_currStatP, 
		     a_callBackP, a_volSyncP);

}

int
RXAFS_StoreData (struct rx_call *call,
		 const AFSFid *a_fidToStoreP,
		 const AFSStoreStatus *a_fidStatusP,
		 const int32_t a_offset,
		 const int32_t a_lenInBytes,
		 const int32_t a_fileLenInBytes,
		 struct AFSFetchStatus *a_fidStatP,
		 struct AFSCallBack *a_callBackP,
		 struct AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,
		    "RXAFS_StoreData: fid: %d %d %d offset: %d "
		    "len: %d filelen: %d", 
		    a_fidToStoreP->Volume,
		    a_fidToStoreP->Vnode,
		    a_fidToStoreP->Unique,
		    a_offset,
		    a_lenInBytes,
		    a_fileLenInBytes) ;

    memset(a_fidStatP, 0, sizeof(struct AFSFetchStatus)) ;
    memset(a_callBackP, 0, sizeof(struct AFSCallBack)) ;
    memset(a_volSyncP, 0, sizeof(struct AFSVolSync)) ;

    /* XXX cb module */
    a_callBackP->CallBackType = CBSHARED;
    a_callBackP->CallBackVersion = 1;
    a_callBackP->ExpirationTime = 4000;

    mvol = volser_getById(a_fidToStoreP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_getdata == NULL)
	return EROFS;

    ret = (*mvol->fs->fs_putdata)(cred, mvol->data, call,
				  a_fidToStoreP, a_fidStatusP, a_offset, 
				  a_lenInBytes, a_fidStatP, a_fileLenInBytes);
    
    volser_release(mvol);
    
    return ret;
}

int RXAFS_StoreACL(struct rx_call *call,
		   const struct AFSFid *a_dirFidP,
		   const AFSOpaque *a_ACLToStoreP,
		   struct AFSFetchStatus *a_dirNewStatP,
		   struct AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_StoreACL") ;

    RESET_STRUCTPTR(a_dirNewStatP);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(a_dirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_storeacl == NULL)
	return EPERM;

    ret = (*mvol->fs->fs_storeacl)(cred, mvol->data,
				   a_dirFidP, a_ACLToStoreP, 
				   a_dirNewStatP, a_volSyncP);
    
    volser_release(mvol);
    
    return ret;
}

int
RXAFS_StoreStatus (struct rx_call *call,
		   const AFSFid *a_fidP,
		   const AFSStoreStatus *a_currStatusP,
		   AFSFetchStatus *a_srStatusP,
		   AFSVolSync *a_volSyncP)
{
    MilkoCred *cred = NULL;
    struct mvolume *mvol;
    int ret;

    RXAFS_printhost(call,"RXAFS_StoreStatus: fid: %d %d %d",
	   a_fidP->Volume,
	   a_fidP->Vnode,
	   a_fidP->Unique) ;


    RESET_STRUCTPTR(a_srStatusP);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(a_fidP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL)
	return EROFS;

    ret = (*mvol->fs->fs_setstatus)(cred, mvol->data, 
				    a_fidP, a_currStatusP, a_srStatusP);

    /* if (ret == 0)
       tell_cb_module() */
    
    volser_release(mvol);
    return ret;
}

int
RXAFS_RemoveFile (struct rx_call *call,
		  const AFSFid *a_dirFidP,
		  const char *a_name,
		  AFSFetchStatus *a_srvStatusP,
		  AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred cred;
    
    RXAFS_printhost(call,"RXAFS_RemoveFile") ;

    RESET_STRUCTPTR(a_srvStatusP);
    RESET_STRUCTPTR(a_volSyncP);

    if (a_name[0] == '.' &&
	(a_name[1] == '\0' || (a_name[1] == '.' && 
				   a_name[2] == '\0')))
	return EINVAL;

    mvol = volser_getById(a_dirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;
    
    if (mvol->fs == NULL || mvol->fs->fs_removefile == NULL)
	return EOPNOTSUPP;
    
    ret = (*mvol->fs->fs_removefile)(&cred, mvol->data, call,
				     a_dirFidP, a_name,
				     a_srvStatusP, a_volSyncP);
    
    /* if (ret == 0)
       tell_cb_module() */
    
    volser_release(mvol);
    return ret;
}

int
RXAFS_CreateFile (struct rx_call *call,
		  const AFSFid *DirFid,
		  const char *Name,
		  const AFSStoreStatus *InStatus,
		  AFSFid *OutFid,
		  AFSFetchStatus *OutFidStatus,
		  AFSFetchStatus *OutDirStatus,
		  AFSCallBack *CallBack,
		  AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_CreateFile") ;

    RESET_STRUCTPTR(OutFid);
    RESET_STRUCTPTR(OutFidStatus);
    RESET_STRUCTPTR(OutDirStatus);
    RESET_STRUCTPTR(CallBack);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(DirFid->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_createfile == NULL)
	return EROFS;

    ret = (*mvol->fs->fs_createfile)(cred, mvol->data, call,
				     DirFid, Name, InStatus, OutFid,
				     OutFidStatus, OutDirStatus,
				     CallBack, a_volSyncP);
    volser_release(mvol);

    if (ret)
	printf("RXAFS_CreateFile: error %d\n", ret);
    else
	printf("RXAFS_CreateFile: fid %d.%d.%d\n", OutFid->Volume, 
	       OutFid->Vnode, OutFid->Unique);

    return ret;
}

int
RXAFS_Rename (struct rx_call *call,
	      const AFSFid *a_origDirFidP,
	      const char *a_origNameP,
	      const AFSFid *a_newDirFidP,
	      const char *a_newNameP,
	      AFSFetchStatus *a_origDirStatusP,
	      AFSFetchStatus *a_newDirStatusP,
	      AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_Rename") ;
    RESET_STRUCTPTR(a_origDirStatusP);
    RESET_STRUCTPTR(a_newDirStatusP);
    RESET_STRUCTPTR(a_volSyncP);

    if (a_origDirFidP->Volume != a_newDirFidP->Volume)
	return EINVAL;

    /* XXX check names */

    mvol = volser_getById(a_origDirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_rename == NULL)
	return EROFS;

    ret = (*mvol->fs->fs_rename)(cred, mvol->data, call,
				 a_origDirFidP, a_origNameP,
				 a_newDirFidP, a_newNameP,
				 a_origDirStatusP, a_newDirStatusP,
				 a_volSyncP);

    volser_release(mvol);
    
    return ret;
}

int
RXAFS_Symlink (struct rx_call *call,
	       const AFSFid *a_dirFidP,
	       const char *a_nameP,
	       const char *a_linkContentsP,
	       const AFSStoreStatus *a_origDirStatP,
	       AFSFid *a_newFidP,
	       AFSFetchStatus *a_newFidStatP,
	       AFSFetchStatus *a_newDirStatP,
	       AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_Symlink") ;
    RESET_STRUCTPTR(a_newFidP);
    RESET_STRUCTPTR(a_newFidStatP);
    RESET_STRUCTPTR(a_newDirStatP);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(a_dirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_symlink == NULL)
	return EROFS;

    ret = (*mvol->fs->fs_symlink)(cred, mvol->data, call,
				  a_dirFidP, a_nameP, 
				  a_linkContentsP, a_origDirStatP,
				  a_newFidP, a_newFidStatP,
				  a_newDirStatP, a_volSyncP);
				 
    volser_release(mvol);
    
    return ret;
}

int
RXAFS_Link (struct rx_call *call,
	    const AFSFid *a_dirFidP,
	    const char *a_nameP,
	    const AFSFid *a_existingFidP,
	    AFSFetchStatus *a_newFidStatP,
	    AFSFetchStatus *a_newDirStatP,
	    AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred *cred = NULL;

    RXAFS_printhost(call,"RXAFS_Link") ;
    RESET_STRUCTPTR(a_newFidStatP);
    RESET_STRUCTPTR(a_newDirStatP);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(a_dirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;

    if (mvol->fs == NULL || mvol->fs->fs_link == NULL)
	return EROFS;

    ret = (*mvol->fs->fs_link)(cred, mvol->data, call,
			       a_dirFidP, a_nameP, 
			       a_existingFidP,
			       a_newFidStatP,
			       a_newDirStatP,
			       a_volSyncP);
				 
    volser_release(mvol);
    
    return ret;
}

int
RXAFS_MakeDir (struct rx_call *call,
	       const AFSFid *a_parentDirFidP,
	       const char *a_newDirNameP,
	       const AFSStoreStatus *a_currStatP,
	       AFSFid *a_newDirFidP,
	       AFSFetchStatus *a_dirFidStatP,
	       AFSFetchStatus *a_parentDirStatP,
	       AFSCallBack *a_newDirCallBackP,
	       AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred cred;
    
    RXAFS_printhost(call,"RXAFS_MakeDir") ;
    
    RESET_STRUCTPTR(a_newDirFidP);
    RESET_STRUCTPTR(a_dirFidStatP);
    RESET_STRUCTPTR(a_parentDirStatP);
    RESET_STRUCTPTR(a_newDirCallBackP);
    RESET_STRUCTPTR(a_volSyncP);

    mvol = volser_getById(a_parentDirFidP->Volume);
    if (mvol == NULL)
	return ENODEV;
    
    if (mvol->fs == NULL || mvol->fs->fs_makedir == NULL)
	return EOPNOTSUPP;
    
    ret = (*mvol->fs->fs_makedir)(&cred, mvol->data, call,
				  a_parentDirFidP, a_newDirNameP,
				  a_currStatP, 
				  a_newDirFidP, a_dirFidStatP, 
				  a_parentDirStatP, a_newDirCallBackP,
				  a_volSyncP);
    
    /* if (ret == 0)
       tell_cb_module() */
    
    volser_release(mvol);
    return ret;
}

int
RXAFS_RemoveDir (struct rx_call *call,
		 const AFSFid *a_parentDirStatP,
		 const char *a_dirNameP,
		 AFSFetchStatus *a_newParentDirStatP,
		 AFSVolSync *a_volSyncP)
{
    struct mvolume *mvol;
    int ret;
    MilkoCred cred;
    
    RXAFS_printhost(call,"RXAFS_RemoveDir") ;

    RESET_STRUCTPTR(a_newParentDirStatP);
    RESET_STRUCTPTR(a_volSyncP);

    if (a_dirNameP[0] == '.' &&
	(a_dirNameP[1] == '\0' || (a_dirNameP[1] == '.' && 
				   a_dirNameP[2] == '\0')))
	return EINVAL;

    mvol = volser_getById(a_parentDirStatP->Volume);
    if (mvol == NULL)
	return ENODEV;
    
    if (mvol->fs == NULL || mvol->fs->fs_removedir == NULL)
	return EOPNOTSUPP;
    
    ret = (*mvol->fs->fs_removedir)(&cred, mvol->data, call,
				    a_parentDirStatP, a_dirNameP,
				    a_newParentDirStatP, a_volSyncP);
    
    /* if (ret == 0)
       tell_cb_module() */
    
    volser_release(mvol);
    return ret;
}

int
RXAFS_GiveUpCallBacks (struct rx_call *call,
		       const AFSCBFids *a_fidArrayP,
		       const AFSCBs *a_callBackArrayP)
{
    RXAFS_printhost(call,"RXAFS_GiveUpCallBacks") ;
    return 0 ;
}

int
RXAFS_GetRootVolume (struct rx_call *call,
		     char *a_rootVolNameP)
{
    RXAFS_printhost(call,"RXAFS_GetRootVolume") ;
    strncpy(a_rootVolNameP, "root.afs", AFSNAMEMAX);
    return 0 ;
}

int 
RXAFS_GetVolumeStatus(struct rx_call *call,
			  const int32_t a_volIDP,
			  struct AFSFetchVolumeStatus *a_volFetchStatP,
			  char *a_volNameP,
			  char *a_offLineMsgP,
			  char *a_motdP)
{
    RXAFS_printhost(call,"RXAFS_GetVolumeStatus");
    return EPERM;
}

int 
RXAFS_SetVolumeStatus(struct rx_call *call,
		      const int32_t a_volIDP,
		      const struct AFSStoreVolumeStatus *a_volStoreStatP,
		      const char *a_volNameP,
		      const char *a_offLineMsgP,
		      const char *a_motdP)
{
    RXAFS_printhost(call,"RXAFS_SetVolumeStatus");
    return EPERM;
}

int
RXAFS_GetTime (struct rx_call *call,
	       u_int32_t *a_secondsP, 
	       u_int32_t *a_uSecondsP)
{
    struct timeval tv;

    RXAFS_printhost(call,"RXAFS_GetTime") ;

    gettimeofday(&tv, NULL);

    *a_secondsP = tv.tv_sec ;
    *a_uSecondsP = tv.tv_usec ;

    return 0 ;
}

int 
RXAFS_NGetVolumeInfo(struct rx_call *call,
		     const char *VolumeName,
		     struct AFSVolumeInfo *foo)
{
    return EPERM;
}

int
RXAFS_BulkStatus(struct rx_call *call,
		 const AFSCBFids *FidsArray,
		 AFSBulkStats *StatArray,
		 AFSCBs *CBArray,
		 AFSVolSync *Sync)
{
    int i;
    int error = 0;

    RXAFS_printhost(call,"RXAFS_BulkStatus") ;

    StatArray->val = NULL;
    CBArray->val = NULL;

    if ((StatArray->val = 
	 malloc(sizeof(*StatArray->val) * FidsArray->len)) == NULL) {
	error = ENOMEM;
	goto err;
    }

    if ((CBArray->val = 
	 malloc(sizeof(*CBArray->val) * FidsArray->len)) == NULL) {
	error = ENOMEM;
	goto err;
    }
    
    CBArray->len = StatArray->len = FidsArray->len;

    for (i = 0 ; i < FidsArray->len; i++) {
	error = GetStatus(NULL, 
			  &FidsArray->val[i],
			  &StatArray->val[i], 
			  &CBArray->val[i],
			  Sync);
	if (error)
	    goto err;

    }


    printf("RXAFS_BulkStatus: end ok\n");
    return error;

 err:
    if (StatArray->val)
	free(StatArray->val);

    if (CBArray->val)
	free  (CBArray->val);

    printf("RXAFS_BulkStatus: failed with %d\n", error);
    return error;
}

int
RXAFS_SetLock(struct rx_call *call,
	      const AFSFid *Fid, 
	      const ViceLockType Type, 
	      AFSVolSync *Sync)
{
    return EPERM;
}

int
RXAFS_ExtendLock(struct rx_call *call,
		 const AFSFid *Fid, 
		 AFSVolSync *Sync)
{
    return EPERM;
}

int
RXAFS_ReleaseLock(struct rx_call *call,
		  const AFSFid *Fid, 
		  AFSVolSync *Sync) 
{
    return EPERM;
}



