Net2/usr/src/contrib/isode/others/rfa/sync.c

Compare this file to the similar file:
Show the results in this format:

/*
 * RFA - Remote File Access
 *
 * Access and Management for a partial file system tree that exists
 * at two sites either as master files or slave files
 *
 * sync.c  synchronize local dir with remote site
 *
 * Contributed by Oliver Wenzel, GMD Berlin, 1990
 *
 * $Header: /f/osi/others/rfa/RCS/sync.c,v 7.3 91/02/22 09:28:34 mrose Interim $
 *
 * $Log:	sync.c,v $
 * Revision 7.3  91/02/22  09:28:34  mrose
 * Interim 6.8
 * 
 * Revision 7.2  91/01/14  13:55:09  mrose
 * update
 * 
 * Revision 1.1  91/01/04  16:08:27  ow
 * Initial revision
 * 
 */

#ifndef       lint
static char *rcsid = "$Header: /f/osi/others/rfa/RCS/sync.c,v 7.3 91/02/22 09:28:34 mrose Interim $";
#endif

/*
 *                              NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include <ctype.h>
#include <strings.h>
#include <stdio.h>
#include <pwd.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include "RFA-ops.h"	
#include "RFA-types.h"	
#include "rfa.h"
#include "rfainfo.h"

extern FILE *err, *out;
extern int interactive;

char *incstr = "\t*** INCONSISTENCY : ";

/*--------------------------------------------------------------*/
/*  createEmptyFile						*/
/*--------------------------------------------------------------*/
int createEmptyFile(dir, rfa)
    char *dir;
    struct RfaInfo *rfa;
{
    int tt[2], fd;
    char buf[BUFSIZ];
    char fn[BUFSIZ];

    sprintf(fn, "%s%s%s", dir, *(dir+strlen(dir)-1) == '/' ? "" : "/",
		rfa->ri_filename);
    /*-- create empty file for now --*/
    if ((fd = open(makeFN(fn), O_WRONLY | O_CREAT, rfa->ri_mode & 07555)) == -1)
    {
	fprintf(err,"\t*** can't create local SLAVE for %s (%s) ***\n"
		, fn, sys_errname(errno));
	return NOTOK;
    }
    strcpy(buf,"This file has been created by RFA as a dummy.");
    strcat(buf," Use RFA commands 'get' and 'setauto'\nto retrieve");
    strcat(buf," the actual content of this file\n");

    if (write(fd, buf, strlen(buf)) == 0) {
	fprintf(err,"\t*** can't write local SLAVE for %s (%s) ***\n"
		, fn, sys_errname(errno));
	unlink(makeFN(fn));
	close(fd);
	return NOTOK;
    }
    close(fd);
    tt[0] = rfa->ri_modTime - 60;
    tt[1] = rfa->ri_modTime - 60;
    if (utime(makeFN(fn), tt) == -1) {
	fprintf(err,"\t*** can't create local SLAVE for %s (%s) ***\n"
		, fn, sys_errname(errno));
	unlink(makeFN(fn));
	return NOTOK;
    }
    changeFileOwner(fn, rfa);
    return OK;
}

/*--------------------------------------------------------------*/
/*  removeDir - check if dir is empty and remove it		*/
/*--------------------------------------------------------------*/
int removeDir(dir)
    char *dir;
{
    struct dirent *dp;
    DIR *dirp;


    /*--- dir is a directory name, so open dir ---*/
    if ((dirp = opendir(makeFN(dir))) ==  NULL) {
	fprintf(err, "*** can't open %s - %s ***", dir, sys_errname(errno));
	return NOTOK;
    }

    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
	if (strncmp(dp->d_name, "..", 2) && strncmp(dp->d_name, ".", 1)
		&& strncmp(dp->d_name, ".rfainfo", 8))
	{
	    closedir(dirp);
	    fprintf(err, "%scan't remove %s - not empty ***\n", incstr, dir);
	    return NOTOK;
	}
    }
    closedir(dirp);
    fprintf(err,"\tremoved local SLAVE directory '%s'\n", dir);
    return OK;
}

/*--------------------------------------------------------------*/
/*  checkState - check state of files and perform actions to    */
/*               resolve inconsistencies if possible		*/
/*--------------------------------------------------------------*/
int checkState(rfa, rrfa, dir, wrp)
    struct RfaInfo *rfa, *rrfa;
    char *dir;
    int *wrp;
{
    char fn[BUFSIZ];

    if ((rfa == NULL) && (rrfa == NULL))
	return OK;

    sprintf(fn, "%s%s%s", dir, *(dir+strlen(dir)-1) == '/' ? "" : "/", 
		rfa ? rfa->ri_filename : rrfa->ri_filename);
    /*-- checks if file exists only local --*/
    if (rrfa == NULL)
	switch (RI_STATUS(rfa->ri_status)) {
	    case RI_MASTER:
	    case RI_UNREGISTERED:
		return OK;
	    case RI_SLAVE:
		fprintf(err, "%sno remote MASTER for local SLAVE '%s' ***\n"
			    , incstr, rfa->ri_filename);
		if (doRmWidows) {
		    if (rfa->ri_mode & S_IFDIR)
			removeDir(fn);
		    else {
			unlink(makeFN(fn));
			fprintf(err,"\tremoved local SLAVE file '%s'\n",
				rfa->ri_filename);
		    }
		    return OK;
		} else 
		    return NOTOK;
		
	    default:
		fprintf(err, "%sinvalid state for local '%s' ***\n",
			incstr, rfa->ri_filename);
		return NOTOK;
	}

    /*-- checks if file exists only remote --*/
    if (rfa == NULL)
	switch (RI_STATUS(rrfa->ri_status)) {
	    case RI_MASTER:
	    case RI_UNREGISTERED:
		return OK;
	    case RI_SLAVE:
		fprintf(err, "%sno local MASTER for remote SLAVE '%s' ***\n",
			incstr, rrfa->ri_filename);
		return NOTOK;
	    default:
		fprintf(err, "%sinvalid state for remote '%s' ***\n",
			incstr, rrfa->ri_filename);
		return NOTOK;
	}

		
    /*-- checks if file exists at both sides --*/
    switch (RI_STATUS(rfa->ri_status)) {
	case RI_UNREGISTERED:
	    switch (RI_STATUS(rrfa->ri_status)) {
		case RI_UNREGISTERED:
		    return OK;
		case RI_SLAVE:
		    fprintf(err, "%sUNREG/SLAVE state for '%s' ***\n",
			    incstr, rrfa->ri_filename);
		    return NOTOK;
		case RI_MASTER:
		    fprintf(err, "%sUNREG/MASTER state for '%s' ***\n", 
			    incstr, rrfa->ri_filename);
		    return NOTOK;
		default:
		    fprintf(err, "%sinvalid state for remote '%s' ***\n",
			    incstr, rrfa->ri_filename);
		    return NOTOK;
	    }
	    /* NOTREACHED */
	case RI_MASTER:
	    switch (RI_STATUS(rrfa->ri_status)) {
		case RI_UNREGISTERED:
		    fprintf(err, "%sMASTER/UNREG state for '%s' ***\n",
			    incstr, rrfa->ri_filename);
		    return NOTOK;
		case RI_SLAVE:
		    return OK;
		case RI_MASTER:
		    fprintf(err, "%sMASTER/MASTER state for '%s' ***\n", 
			    incstr, rrfa->ri_filename);
		    return NOTOK;
		default:
		    fprintf(err, "%sinvalid state for remote '%s' ***\n",
			    incstr, rrfa->ri_filename);
		    return NOTOK;
	    }
	    /* NOTREACHED */
	case RI_SLAVE:
	    switch (RI_STATUS(rrfa->ri_status)) {
		case RI_UNREGISTERED:
		    fprintf(err, "%sSLAVE/UNREG state for '%s' ***\n",
			    incstr, rrfa->ri_filename);
		    if (doRmWidows) {
			SET_STATUS(rfa->ri_status, RI_UNREGISTERED);
			SET_TRANSFER(rfa->ri_status, RI_TR_REQ);
			fprintf(err,"\tchanged status of '%s' to UNREGISTERED\n"
				, rfa->ri_filename);
			*wrp = 1;
		    }
		    return NOTOK;
		    
		case RI_SLAVE:
		    fprintf(err, "%sSLAVE/SLAVE state for '%s' ***\n", 
			    incstr, rrfa->ri_filename);
		    return NOTOK;
		case RI_MASTER:
		    return OK;
		default:
		    fprintf(err, "%sinvalid state for remote '%s' ***\n",
			    incstr, rrfa->ri_filename);
		    return NOTOK;
	    }

	    /* NOTREACHED */
	default:
	    fprintf(err, "%sinvalid state for local '%s' ***\n",
		    incstr, rrfa->ri_filename);
	    return NOTOK;
    }
    /* NOTREACHED */
}

/*--------------------------------------------------------------*/
/*  checkMasterSlave - do a consistency check on master and     */
/*		       slave file infos				*/
/*--------------------------------------------------------------*/
int checkMasterSlave(m, s, ms, ss)
    struct RfaInfo *m, *s;
    char *ms, *ss;
{
    if (m->ri_modTime < s->ri_modTime) {
	fprintf(err,"%s %s SLAVE version of '%s' is newer than %s MASTER ***\n"
		, incstr, ss, m->ri_filename, ms);
	return NOTOK;
    }
    if ((m->ri_modTime == s->ri_modTime) && (m->ri_size != s->ri_size)) {
	fprintf(err,"%s %s MASTER of '%s'", incstr, ms, m->ri_filename);
	fprintf(err," has different size than %s SLAVE ***\n", ss);
	return NOTOK;
    }
    return OK;
}

/*--------------------------------------------------------------*/
/*  handleDir							*/
/*--------------------------------------------------------------*/
int handleDir(dir, localRfaListPtr, rrfa, rec, wrp)
    char *dir;
    struct RfaInfo **localRfaListPtr;
    struct RfaInfo *rrfa;
    int rec;
    int *wrp;
{
    struct RfaInfo *rfa;
    char fn[BUFSIZ];
    int rc;

    if (!strcmp(rrfa->ri_filename, ".") || !strcmp(rrfa->ri_filename, ".."))
	return OK;

    sprintf(fn, "%s/%s", strcmp(dir,"/") ? dir : "", rrfa->ri_filename);
    rfa = findRfaInfo(rrfa->ri_filename, *localRfaListPtr);
	
    switch (RI_STATUS(rrfa->ri_status)) {
	case RI_UNREGISTERED:
	    if (rfa)
		checkState(rfa, rrfa, dir, wrp);
	    return OK;

    	case RI_SLAVE:
	    if (rfa == NULL) {
		fprintf(err,"%sno local MASTER for remote SLAVE dir '%s' ***\n",
			  incstr, rrfa->ri_filename);
		return NOTOK_INCONSISTENCY;
	    }
	    if ((rfa->ri_mode & S_IFDIR & S_IFMT) == 0) {
		fprintf(err,
		    "%slocal file '%s' conflicts with remote directory ***\n",
			  incstr, rrfa->ri_filename);
		return NOTOK_INCONSISTENCY;
	    }
	    if (checkState(rfa, rrfa, dir, wrp) == NOTOK)
		return NOTOK_INCONSISTENCY;
	    break;

	case RI_MASTER:
	    if (rfa == NULL) {
		fprintf(err, "found new MASTER sub-directory '%s' at remote\n", 
			    rrfa->ri_filename);
		if ((rfa = mallocRfaInfo(strdup(rrfa->ri_filename))) == NULL) {
		    fprintf(err,"%scant't create local SLAVE for %s ***\n", 
				incstr, rrfa->ri_filename);
		    return NOTOK_LOCAL_ERROR;
		}
		if (mkdir(makeFN(fn), rrfa->ri_mode & 07777) == -1) {
		    fprintf(err,"*** can't create subdir %s ***\n", 
				rrfa->ri_filename);
		    return NOTOK_FILEACCESS;
		}
		SET_STATUS(rfa->ri_status, RI_SLAVE);
		SET_LOCKINFO(rfa->ri_status, RI_UNLOCKED);
		SET_TRANSFER(rfa->ri_status, RI_TRANSFER(rrfa->ri_status));
		time(&(rfa->ri_lastChange));
		rfa->ri_modTime = 0L;
		rfa->ri_mode = rrfa->ri_mode;
		rfa->ri_next = *localRfaListPtr;
		*localRfaListPtr = rfa;
		changeFileOwner(fn, rrfa);
		if (putRfaInfoList(dir, *localRfaListPtr) != OK) {
		    fprintf(err,"%scan't set SLAVE status of %s ***\n", incstr, 
			    rrfa->ri_filename);
		    return NOTOK_FILEACCESS;
		}
	    } else {  /*--- local rfa found ---*/
		if((rfa->ri_mode & S_IFDIR & S_IFMT) == 0) {
		    fprintf(err,"%slocal file '%s' conflicts with",incstr,
			    rrfa->ri_filename);
		    fprintf(err," remote directory ***\n");
		    return NOTOK_INCONSISTENCY;
		}
		if (checkState(rfa, rrfa, dir, wrp) == NOTOK)
		    return NOTOK_INCONSISTENCY;
	    
	    } /* if local rfa exists */
    } /* switch rrfa->ri_status */

    /*--- now we have a local version of the dir ---*/
    if(rec && IS_TR_AUTO(rfa->ri_status))  
	if ((rc = syncDir(fn, rec)) != OK)  {
	    fprintf(err,"\t*** syncdir for %s failed ***\n", rrfa->ri_filename);
	    return rc;
	}
    
    return OK;
} 


/*--------------------------------------------------------------*/
/*  syncDir							*/
/*--------------------------------------------------------------*/
int syncDir (dir, rec)
char *dir;
    int rec;
{
    struct RfaInfo *rfa, *localRfaList, *rrfa, *remoteRfaList;
    char buf[BUFSIZ];
    int writeList, rc;
    char *l, *lp;
    struct stat st;
    int rmode;
    char fn[BUFSIZ];
    char syncfiles[BUFSIZ*10];


    /*--- get file Info ---*/
    if ((rc = getLockedRfaInfoList(dir, &localRfaList, NULL)) != OK)  {
	fprintf(err,"\t*** can't get rfainfo : %s ***\n", errMsg(rc));
	return(rc);
    }

    /*--- get remote rfa list ---*/
    if ((rc = getRemoteRfaInfoList(dir, &remoteRfaList)) != OK) {
	releaseRfaInfoList(dir, localRfaList);
	return rc;
    }
    fprintf(err, "syncing directory %s\n", dir);
    *syncfiles = '\0';

    /*-- check remote list agaist local one --*/
    for(rrfa = remoteRfaList; rrfa; rrfa = rrfa->ri_next) {
	writeList = 0;
	switch(rrfa->ri_mode & S_IFMT) {
	    case S_IFIFO:
	    case S_IFCHR:
	    case S_IFBLK:
	    case S_IFSOCK:
		continue;

	    case S_IFLNK:
		if (rrfa->ri_lnkName == NULL)
		    continue;
		    
		strcpy(buf, makeFN2(dir, rrfa->ri_filename));
		if (*(rrfa->ri_lnkName) == '/') 
		    lp = l = makeFN(rrfa->ri_lnkName);
		else {
		    l = rrfa->ri_lnkName;
		    lp = makeFN2(dir, rrfa->ri_lnkName);
		}

		if (stat(lp, &st) == -1)
		    continue;

		if ((rfa = findRfaInfo(rrfa->ri_filename,localRfaList)) == NULL)
		{
		    fprintf(err, "\tcreating link %s to %s\n", 
			    rrfa->ri_filename, l);
		    if(symlink(l, buf) == -1) {
			fprintf(err,"\t*** can't create link %s ***\n",buf);
			continue;
		    }
		    continue;
		}
		if ((rfa->ri_mode & S_IFMT) == S_IFLNK) {
		    if(strcmp(rfa->ri_lnkName, l) == 0)
			continue;
		    unlink(makeFN2(dir, rfa->ri_filename));
		    fprintf(err, "\tcreating link %s to %s\n", 
			    rrfa->ri_filename, l);
		    if(symlink(l, buf) == -1) {
			fprintf(err,"\t*** can't create link %s ***\n",buf);
			continue;
		    }
		    continue;
		}
		fprintf(err,"%slocal file %s conflicts with remote link ***\n",
			incstr, buf);
		continue;
		break;

				
	    case S_IFDIR:
		if (handleDir(dir, &localRfaList, rrfa,rec,&writeList) != OK) {
		    continue;
		}
		continue;

	    case S_IFREG:
		break;

	    default:
		continue;
	}

	/*--- rrfa->ri_filename is regular file ---*/

	rfa = findRfaInfo(rrfa->ri_filename, localRfaList);

	switch (RI_STATUS(rrfa->ri_status)) {
	    case RI_UNREGISTERED:
		checkState(rfa, rrfa, dir, &writeList);
		break;
	    case RI_SLAVE:
		if (checkState(rfa, rrfa, dir, &writeList) == NOTOK)
		    continue;
		checkMasterSlave(rfa, rrfa, "local", "remote");
		break;

	    case RI_MASTER:
		if (rfa == NULL) {

		    /*--- check if not a .rfaexec file ---*/
		    if (strcmp(rrfa->ri_filename, ".rfaexec") == 0) {
			fprintf(err,
			    "%sremote file '%s' is MASTER, not transfered ***\n"
			    ,rfa->ri_filename);
			continue;
		    }

		    fprintf(err, "\tfound new MASTER file '%s' at remote\n", 
			    rrfa->ri_filename);
		    if ((rfa=mallocRfaInfo(strdup(rrfa->ri_filename)))==NULL) {
			fprintf(err,"%scant't create local SLAVE for %s ***\n",
				incstr, rrfa->ri_filename);
			continue;
		    }
		    SET_STATUS(rfa->ri_status, RI_SLAVE);
		    SET_LOCKINFO(rfa->ri_status, RI_UNLOCKED);
		    SET_TRANSFER(rfa->ri_status, RI_TRANSFER(rrfa->ri_status));
		    time(&(rfa->ri_lastChange));
		    rfa->ri_modTime = 0L;
		    rfa->ri_mode = rrfa->ri_mode;
		    rfa->ri_next = localRfaList;
		    localRfaList = rfa;
		    writeList++;
		} else {
		    if (checkState(rfa, rrfa, dir, &writeList) == NOTOK)
			break;;
		    checkMasterSlave(rrfa, rfa, "remote", "local");
		} /* if local rfa exists */

		/*--- now we are ready to get the remote file ---*/
		if (writeList && IS_TR_REQ(rfa->ri_status)) {
		    if (createEmptyFile(dir, rrfa) == NOTOK) 
			continue;
		} else 
		    if( IS_TR_AUTO(rfa->ri_status) && 
			    (rfa->ri_modTime < rrfa->ri_modTime)) 
		    {
			sprintf(fn, "%s%s%s", dir, 
				*(dir+strlen(dir)-1) == '/' ? "" : "/",
				rfa->ri_filename);
			fprintf(err, "\t%s - ", rrfa->ri_filename);
			if ((rc = getfile_aux(fn, rfa, &rmode)) != OK)  
			    continue;
			strcat(syncfiles, rfa->ri_filename);
			strcat(syncfiles, " ");
		    } 
		break;

	    default:
		fprintf(err, "%sinvalid state for remote '%s' ***\n",
			    incstr, rrfa->ri_filename);
		continue;	
	} /* switch rrfa->ri_status */

	/*--- now we have a local slave version of the file ---*/
	if (writeList)
	    if ((rc = putRfaInfoList(dir, localRfaList)) != OK) {
		fprintf(err,"%scan't set SLAVE status of %s ***\n", incstr, 
			rfa->ri_filename);
		continue;
	    }
	
    } /* for rrfa */


    /*-- now check local list against remote list --*/
    for(rfa = localRfaList; rfa; rfa = rfa->ri_next) 
	if (findRfaInfo(rfa->ri_filename, remoteRfaList) == NULL) {
	    checkState(rfa, NULL, dir, &writeList);
	}
    if (writeList)
	if ((rc = putRfaInfoList(dir, localRfaList)) != OK) {
	    fprintf(err,"%scan't set SLAVE status of %s ***\n", incstr, 
		    rfa->ri_filename);
	}

    releaseRfaInfoList(dir, localRfaList);
    freeRfaInfoList(remoteRfaList);

    /*-- look for .rfaexec script --*/
    return rfaMake(dir, syncfiles);
}

/*--------------------------------------------------------------*/
/*  rfaMake							*/
/*--------------------------------------------------------------*/
int rfaMake(dir, fns)
    char *dir, *fns;
{
    struct RfaInfo *rfa, *rrfa, *localRfaList, *remoteRfaList;
    char parentDir[BUFSIZ];
    char execfn[BUFSIZ];
    char buf[BUFSIZ];
    struct stat st;
    int rc;

    /*-- check if files sync'ed --*/
    if (*fns == '\0' || doRfaExec == 0)
	return OK;

    /*-- check if .rfaexec exists --*/
    sprintf(execfn, "%s/.rfaexec", dir);
    if (lstat(makeFN(execfn),&st) == -1) {
        if (errno == ENOENT) 
	    return OK;
	fprintf(err, "\t*** can't stat %s - %s", execfn,sys_errname(errno));
	return NOTOK_LOCAL_ERROR;
    }
    if (access(makeFN(execfn), X_OK) == -1)  {
	fprintf(err,"\t*** can't exec '%s' (%s) ***\n", execfn, 
		sys_errname(errno));
	return NOTOK_LOCAL_ERROR;
    }

    /*-- check if dir is locked --*/
    if ((rc = getRfaInfoList(dirname(dir), &localRfaList)) != OK) {
	return rc;
    }
    if (rfa = findRfaInfo(basename(dir),localRfaList)) {
	if (IS_MASTER(rfa->ri_status) && IS_LOCKED(rfa->ri_status)) {
	    freeRfaInfoList(localRfaList);
	    return OK;
	}
    }
    if ((rfa == NULL) || (rfa && !IS_MASTER(rfa->ri_status))) {
	freeRfaInfoList(localRfaList);

	/*--- get remote rfa list ---*/
	if ((rc = getRemoteRfaInfoList(dirname(dir), &remoteRfaList)) != OK) {
	    return rc;
	}
	if ((rrfa = findRfaInfo(basename(dir),remoteRfaList)) == NULL) {
	    fprintf(err,"\t*** can't determine status of '%s' ***\n", dir);
	    freeRfaInfoList(remoteRfaList);
	    return NOTOK_REMOTE_ERROR;
	}
	if (!IS_MASTER(rrfa->ri_status)) {
	    fprintf(err,"\t*** can't find MASTER version of '%s' ***\n", dir);
	    freeRfaInfoList(remoteRfaList);
	    return NOTOK_REMOTE_ERROR;
	}
	if (IS_LOCKED(rrfa->ri_status))  {
	    freeRfaInfoList(remoteRfaList);
	    return OK;
	}
	freeRfaInfoList(remoteRfaList);
    }

    /*-- change cwd to sync'ed dir --*/
    if (chdir(makeFN(dir)) == -1) {
	fprintf(err,"\t*** can't change dir to '%s' (%s) ***\n", dir,
		sys_errname(errno));
	return NOTOK_LOCAL_ERROR;
    }

    /*-- so we are able to exec .rfaexec --*/
    fprintf(err,"\texecuting '%s'...\n", execfn);
    sprintf(buf, "%s %s", makeFN(execfn), fns);
    if (system(buf) == -1)
	return NOTOK_LOCAL_ERROR;

    return OK;
}