void session_file_storage::gc()
{
	std::auto_ptr<_WIN32_FIND_DATAW> entry(new _WIN32_FIND_DATAW);
	HANDLE d=INVALID_HANDLE_VALUE;
	std::string search_path = path_ + "/*";
	try{
		if((d=::FindFirstFileW(booster::nowide::convert(search_path).c_str(),entry.get()))==INVALID_HANDLE_VALUE) {
			if(GetLastError() == ERROR_FILE_NOT_FOUND)
				return;
			throw cppcms_error("Failed to open directory :"+path_);
		}
		do {
			if(entry->cFileName[32]!=0)
				continue;
			int i;
			std::string filename=booster::nowide::convert(entry->cFileName);
			for(i=0;i<32;i++) {
				if(!isxdigit(filename[i]))
					break;
			}
			if(i!=32) 
				continue;
			{
				locked_file file(this,filename);
				if(!read_timestamp(file.handle()))
					::DeleteFileW(booster::nowide::convert(file.name()).c_str());
			}
		} while(::FindNextFileW(d,entry.get()));
		::FindClose(d);
	}
	catch(...) {
		if(d!=INVALID_HANDLE_VALUE) ::FindClose(d);
		throw;
	}
}
/**
 * generic_handle_irq - Invoke the handler for a particular irq
 * @irq:	The irq number to handle
 *
 */
int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	/* merge qcom DEBUG_CODE for RPC crashes */
#ifdef CONFIG_HUAWEI_RPC_CRASH_DEBUG
	uint32_t  timetick=0; 
	timetick = read_timestamp();  
	irq_ts[irq_idx].irq=irq;
	irq_ts[irq_idx].ts=timetick; 
	irq_ts[irq_idx].state=1; 
	/*end of HUAWEI*/
#endif

    if (!desc)
		return -EINVAL;
    generic_handle_irq_desc(irq, desc);

#ifdef CONFIG_HUAWEI_RPC_CRASH_DEBUG
    /*HUAWEI debug */
    irq_ts[irq_idx].state=3;
    irq_idx = (irq_idx + 1)%128; 
#endif

	return 0;
}
Beispiel #3
0
static inline bool perform_read(uint8_t id, uint8_t addr, uint64_t *data)
{
    bool success;

    switch (id) {
        case NIOS_PKT_8x64_TARGET_TIMESTAMP:
            success = read_timestamp(addr, data);
            break;

        /* Add user customizations here

        case NIOS_PKT_8x64_TARGET_USR1:
        ...
        case NIOS_PKT_8x64_TARGET_USR128:

        */

        default:
            DBG("Invalid ID: 0x%x\n", id);
            success = false;

    }

    return success;
}
Beispiel #4
0
static int file_exists (const char *name, const char *timestamp)
{
	struct tm t;
	long zone = -1;

	if (!strcmp (name, "/dev/null"))
		return 0;

	if (read_timestamp (timestamp, &t, &zone))
		return 1;

	/* If the time is less that fifteen hours either side of the
	 * start of 1970, and it's an exact multiple of 15 minutes, it's
	 * very likely to be the result of ctime(&zero). */
	if (t.tm_sec == 0 &&
	    ((t.tm_year == 69 && t.tm_mon == 11 && t.tm_mday == 31 &&
	      t.tm_hour >= 9) ||
	     (t.tm_year == 70 && t.tm_mon == 0 && t.tm_mday == 1 &&
	      t.tm_hour <= 15)) &&
	    (t.tm_min % 15) == 0) {
		if (zone != -1) {
			/* Extra checking, since we know the timezone. */
			long offset = 0;
			if (t.tm_year == 69) {
				offset = 100 * (t.tm_hour - 24);
				if (t.tm_min)
					offset += 100 + t.tm_min - 60;
			} else {
				offset = 100 * t.tm_hour;
				offset += t.tm_min;
			}

			if (offset != zone)
				return 1;
		}

		return 0;
	}

	/* Otherwise, it's a real file timestamp. */
	return 1;
}
/**
 * generic_handle_irq - Invoke the handler for a particular irq
 * @irq:	The irq number to handle
 *
 */
int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

#ifdef CONFIG_HUAWEI_RPC_CRASH_DEBUG
	uint32_t  timetick=0; 
	timetick = read_timestamp();  
	irq_ts[irq_idx].irq=irq;
	irq_ts[irq_idx].ts=timetick; 
	irq_ts[irq_idx].state=1; 
#endif

    if (!desc)
		return -EINVAL;
    generic_handle_irq_desc(irq, desc);

#ifdef CONFIG_HUAWEI_RPC_CRASH_DEBUG
    irq_ts[irq_idx].state=3;
    irq_idx = (irq_idx + 1)%128; 
#endif

	return 0;
}
int main(int argc, char **argv)
{
    // time the program
    struct timeval sysTimeStart, sysTimeEnd;
    gettimeofday(&sysTimeStart, NULL);
    
    mkdir("bplus", 0777);
    
    int i;
    // open file count information
    file_count_t *fc = read_file_count();
    
    // keep track of roots
    bplus_roots_t bpr;
    
    // users
    {
        printf("\n");
        printf("Making user B+ tree with %d users\n", fc->users);
        
        // time this section
        struct timeval startSysTimeSub, endSysTimeSub;
        gettimeofday(&startSysTimeSub, NULL);
        
        // create root
        int_node_t root;
        root.next = -1;
        root.previous = -1;
        root.tableType = TABLE_TYPE_USER;
        root.nodeType = NODE_TYPE_LEAF;
        root.count = 0;
        root.firstFile = -1;
        write_node(0, &root);
        
        int rootFileNum = 0;
        
        for (i = 0; i < fc->users; i++)
        {
            user_t *user = read_user(i);
            rootFileNum = insert_node(rootFileNum, TABLE_TYPE_USER, user->stateId, i);
            free_user(user);
        }
        
        bpr.user = rootFileNum;
        
        // end timing this section
        gettimeofday(&endSysTimeSub, NULL);
        float totalTime = (endSysTimeSub.tv_sec - startSysTimeSub.tv_sec)
        + (endSysTimeSub.tv_usec - startSysTimeSub.tv_usec) / 1000000.0f;
        printf("B+ tree creation time:  %f seconds\n", totalTime);
    }
    
    // cities
    {
        printf("\n");
        printf("Making city B+ tree with %d cities\n", fc->cities);
        
        // time this section
        struct timeval startSysTimeSub, endSysTimeSub;
        gettimeofday(&startSysTimeSub, NULL);
        
        // create root
        int_node_t root;
        root.next = -1;
        root.previous = -1;
        root.tableType = TABLE_TYPE_CITY;
        root.nodeType = NODE_TYPE_LEAF;
        root.count = 0;
        root.firstFile = -1;
        write_node(0, &root);
        
        int rootFileNum = 0;
        
        for (i = 0; i < fc->cities; i++)
        {
            city_t *city = read_city(i);
            rootFileNum = insert_node(rootFileNum, TABLE_TYPE_CITY, city->stateId, i);
            free_city(city);
        }
        
        bpr.city = rootFileNum;
        
        // end timing this section
        gettimeofday(&endSysTimeSub, NULL);
        float totalTime = (endSysTimeSub.tv_sec - startSysTimeSub.tv_sec)
        + (endSysTimeSub.tv_usec - startSysTimeSub.tv_usec) / 1000000.0f;
        printf("B+ tree creation time:  %f seconds\n", totalTime);
    }
    
    //states
    {
        printf("\n");
        printf("Making state B+ tree with %d states\n", fc->states);
        
        // time this section
        struct timeval startSysTimeSub, endSysTimeSub;
        gettimeofday(&startSysTimeSub, NULL);
        
        // create root
        int_node_t root;
        root.next = -1;
        root.previous = -1;
        root.tableType = TABLE_TYPE_STATE;
        root.nodeType = NODE_TYPE_LEAF;
        root.count = 0;
        root.firstFile = -1;
        write_node(0, &root);
        
        int rootFileNum = 0;
        
        for (i = 0; i < fc->states; i++)
        {
            state_t *state = read_state(i);
            rootFileNum = insert_node(rootFileNum, TABLE_TYPE_STATE, (int) hash_state(state), i);
            free_state(state);
        }
        
        bpr.state = rootFileNum;
        
        // end timing this section
        gettimeofday(&endSysTimeSub, NULL);
        float totalTime = (endSysTimeSub.tv_sec - startSysTimeSub.tv_sec)
        + (endSysTimeSub.tv_usec - startSysTimeSub.tv_usec) / 1000000.0f;
        printf("B+ tree creation time:  %f seconds\n", totalTime);
    }
    
    // messages
    {
        printf("\n");
        printf("Making message B+ tree with %d messages\n", fc->messages);
        
        // time this section
        struct timeval startSysTimeSub, endSysTimeSub;
        gettimeofday(&startSysTimeSub, NULL);
        
        // create root
        int_node_t root;
        root.next = -1;
        root.previous = -1;
        root.tableType = TABLE_TYPE_MESSAGE;
        root.nodeType = NODE_TYPE_LEAF;
        root.count = 0;
        root.firstFile = -1;
        write_node(0, &root);
        
        int rootFileNum = 0;
        
        for (i = 0; i < fc->messages; i++)
        {
            message_t *message = read_message(i);
            rootFileNum = insert_node(rootFileNum, TABLE_TYPE_MESSAGE, message->timestampId, i);
            free_message(message);
        }
        
        bpr.message = rootFileNum;
        
        // end timing this section
        gettimeofday(&endSysTimeSub, NULL);
        float totalTime = (endSysTimeSub.tv_sec - startSysTimeSub.tv_sec)
        + (endSysTimeSub.tv_usec - startSysTimeSub.tv_usec) / 1000000.0f;
        printf("B+ tree creation time:  %f seconds\n", totalTime);
    }
    
    // timestamps
    {
        printf("\n");
        printf("Making timestamp B+ tree with %d timestamps\n", fc->timestamps);
        
        // time this section
        struct timeval startSysTimeSub, endSysTimeSub;
        gettimeofday(&startSysTimeSub, NULL);
        
        // create root
        int_node_t root;
        root.next = -1;
        root.previous = -1;
        root.tableType = TABLE_TYPE_TIMESTAMP;
        root.nodeType = NODE_TYPE_LEAF;
        root.count = 0;
        root.firstFile = -1;
        write_node(0, &root);
        
        int rootFileNum = 0;
        
        for (i = 0; i < fc->timestamps; i++)
        {
            timestamp_t *timestamp = read_timestamp(i);
            rootFileNum = insert_node(rootFileNum, TABLE_TYPE_TIMESTAMP, (int) hash_timestamp(timestamp), i);
            free_timestamp(timestamp);
        }
        
        bpr.timestamp = rootFileNum;
        
        // end timing this section
        gettimeofday(&endSysTimeSub, NULL);
        float totalTime = (endSysTimeSub.tv_sec - startSysTimeSub.tv_sec)
        + (endSysTimeSub.tv_usec - startSysTimeSub.tv_usec) / 1000000.0f;
        printf("B+ tree creation time:  %f seconds\n", totalTime);
    }
    
    // datestamps
    {
        printf("\n");
        printf("Making datestamp B+ tree with %d datestamps\n", fc->datestamps);
        
        // time this section
        struct timeval startSysTimeSub, endSysTimeSub;
        gettimeofday(&startSysTimeSub, NULL);
        
        
        // create root
        int_node_t root;
        root.next = -1;
        root.previous = -1;
        root.tableType = TABLE_TYPE_DATESTAMP;
        root.nodeType = NODE_TYPE_LEAF;
        root.count = 0;
        root.firstFile = -1;
        write_node(0, &root);
        
        int rootFileNum = 0;
        
        for (i = 0; i < fc->datestamps; i++)
        {
            datestamp_t *datestamp = read_datestamp(i);
            rootFileNum = insert_node(rootFileNum, TABLE_TYPE_DATESTAMP, (int) hash_datestamp(datestamp), i);
            free_datestamp(datestamp);
        }
        
        bpr.datestamp = rootFileNum;
        
        // end timing this section
        gettimeofday(&endSysTimeSub, NULL);
        float totalTime = (endSysTimeSub.tv_sec - startSysTimeSub.tv_sec)
        + (endSysTimeSub.tv_usec - startSysTimeSub.tv_usec) / 1000000.0f;
        printf("B+ tree creation time:  %f seconds\n", totalTime);
    }
    
    free_file_count(fc);
    
    printf("\n");
    
    print_bplus_roots(&bpr);
    write_bplus_roots(&bpr);
    
    // end timing the program
    gettimeofday(&sysTimeEnd, NULL);
    float totalTime = (sysTimeEnd.tv_sec - sysTimeStart.tv_sec)
    + (sysTimeEnd.tv_usec - sysTimeStart.tv_usec) / 1000000.0f;
    printf("Process time %f seconds\n", totalTime);
    
    return 0;
}
Beispiel #7
0
int get_current_backups(const char *basedir, struct bu **arr, int *a, int log)
{
	int i=0;
	int j=0;
	int tr=0;
	int ret=0;
	DIR *d=NULL;
	char buf[32]="";
	char realwork[32]="";
	char realfinishing[32]="";
	struct dirent *dp=NULL;

	// Find out what certain directories really are, if they exist,
	// so they can be excluded.
	if(get_link(basedir, "working", realwork, sizeof(realwork))
	  || get_link(basedir, "finishing", realfinishing, sizeof(realfinishing)))
			return -1;
	if(!(d=opendir(basedir)))
	{
		if(log) log_and_send("could not open backup directory");
		return -1;
	}
	while((dp=readdir(d)))
	{
		int hardlinked=0;
		struct stat statp;
		char *fullpath=NULL;
		char *timestamp=NULL;
		char *timestampstr=NULL;
		char *hlinkedpath=NULL;

		if(dp->d_ino==0
		  || !strcmp(dp->d_name, ".")
		  || !strcmp(dp->d_name, "..")
		  || !strcmp(dp->d_name, realwork)
		  || !strcmp(dp->d_name, realfinishing))
			continue;
		if(!(fullpath=prepend_s(basedir,
			dp->d_name, strlen(dp->d_name)))
		 || !(timestamp=prepend_s(fullpath,
			"timestamp", strlen("timestamp")))
		 || !(hlinkedpath=prepend_s(fullpath,
			"hardlinked", strlen("hardlinked"))))
		{
			ret=-1;
			if(timestamp) free(timestamp);
			if(fullpath) free(fullpath);
			break;
		}
		if((!lstat(fullpath, &statp) && !S_ISDIR(statp.st_mode))
		  || lstat(timestamp, &statp) || !S_ISREG(statp.st_mode)
		  || read_timestamp(timestamp, buf, sizeof(buf))
		  || !(timestampstr=strdup(buf)))
		{
			free(fullpath);
			free(timestamp);
			free(hlinkedpath);
			continue;
		}
		free(timestamp);

		if(!lstat(hlinkedpath, &statp)) hardlinked++;

		if(!(*arr=(struct bu *)realloc(*arr,(i+1)*sizeof(struct bu)))
		  || !((*arr)[i].data=prepend_s(fullpath, "data", strlen("data")))
		  || !((*arr)[i].delta=prepend_s(fullpath, "deltas.reverse", strlen("deltas.reverse"))))
		{
			if(log) log_and_send_oom(__FUNCTION__);
			free(timestampstr);
			free(fullpath);
			free(hlinkedpath);
			break;
		}
		(*arr)[i].path=fullpath;
		(*arr)[i].timestamp=timestampstr;
		(*arr)[i].hardlinked=hardlinked;
		(*arr)[i].deletable=0;
		(*arr)[i].index=strtoul(timestampstr, NULL, 10);
		(*arr)[i].trindex=0;
		i++;
	}
	closedir(d);

	if(*arr) qsort(*arr, i, sizeof(struct bu), bu_cmp);

	if(i>1)
	{
		tr=(*arr)[i-1].index;
		// The oldest backup is deletable.
		(*arr)[0].deletable=1;
	}

	for(j=0; j<i-1; j++)
	{
		// Backups that come after hardlinked backups are deletable.
		if((*arr)[j].hardlinked) (*arr)[j+1].deletable=1;
	}
	if(!ret)
	{
		if(tr) for(j=0; j<i; j++)
		{
			// Transpose indexes so that the oldest index is set
			// to 1.
			(*arr)[j].trindex=tr-(*arr)[j].index+1;
			//printf("%lu: %lu\n",
			//	(*arr)[j].index, (*arr)[j].trindex);
		}
		*a=i;
	}
		  
	return ret;
}
Beispiel #8
0
int backup_phase4_server(struct sdirs *sdirs, struct conf *cconf)
{
	int ret=-1;
	struct stat statp;
	char *manifest=NULL;
	char *deletionsfile=NULL;
	char *datadir=NULL;
	char *datadirtmp=NULL;
	char *currentdup=NULL;
	char *currentduptmp=NULL;
	char *currentdupdata=NULL;
	char *timestamp=NULL;
	char *fullrealcurrent=NULL;
	char *logpath=NULL;
	char *hlinkedpath=NULL;
	ssize_t len=0;
	char realcurrent[256]="";

	unsigned long bno=0;
	int hardlinked=0;
	char tstmp[64]="";
	int newdup=0;
	int previous_backup=0;

	if((len=readlink(sdirs->current, realcurrent, sizeof(realcurrent)-1))<0)
		len=0;
	realcurrent[len]='\0';

	if(!(datadir=prepend_s(sdirs->finishing, "data"))
	  || !(datadirtmp=prepend_s(sdirs->finishing, "data.tmp"))
	  || !(manifest=prepend_s(sdirs->finishing, "manifest.gz"))
	  || !(deletionsfile=prepend_s(sdirs->finishing, "deletions"))
	  || !(currentdup=prepend_s(sdirs->finishing, "currentdup"))
	  || !(currentduptmp=prepend_s(sdirs->finishing, "currentdup.tmp"))
	  || !(currentdupdata=prepend_s(currentdup, "data"))
	  || !(timestamp=prepend_s(sdirs->finishing, "timestamp"))
	  || !(fullrealcurrent=prepend_s(sdirs->client, realcurrent))
	  || !(logpath=prepend_s(sdirs->finishing, "log"))
	  || !(hlinkedpath=prepend_s(currentdup, "hardlinked")))
		goto end;

	if(set_logfp(logpath, cconf))
		goto end;

	logp("Begin phase4 (shuffle files)\n");

	if(write_status(STATUS_SHUFFLING, NULL, cconf))
		goto end;

	if(!lstat(sdirs->current, &statp)) // Had a previous backup
	{
		previous_backup++;

		if(lstat(currentdup, &statp))
		{
			// Have not duplicated the current backup yet.
			if(!lstat(currentduptmp, &statp))
			{
				logp("Removing previous currentduptmp directory: %s\n", currentduptmp);
				if(recursive_delete(currentduptmp,
					NULL, 1 /* del files */))
				{
					logp("Could not delete %s\n",
						currentduptmp);
					goto end;
				}
			}
			logp("Duplicating current backup.\n");
			if(recursive_hardlink(sdirs->current, currentduptmp, cconf)
			  || do_rename(currentduptmp, currentdup))
				goto end;
			newdup++;
		}

		if(read_timestamp(timestamp, tstmp, sizeof(tstmp)))
		{
			logp("could not read timestamp file: %s\n", timestamp);
			goto end;
		}
		// Get the backup number.
		bno=strtoul(tstmp, NULL, 10);

		if(newdup)
		{
			// When we have just created currentdup, determine
			// hardlinked archive from the conf and the backup
			// number...
			hardlinked=do_hardlinked_archive(cconf, bno);
		}
		else
		{
			// ...if recovering, find out what currentdup started
			// out as.
			// Otherwise it is possible that things can be messed
			// up by somebody swapping between hardlinked and
			// not hardlinked at the same time as a resume happens.
			if(lstat(hlinkedpath, &statp))
			{
				logp("previous attempt started not hardlinked\n");
				hardlinked=0;
			}
			else
			{
				logp("previous attempt started hardlinked\n");
				hardlinked=1;
			}
		}

		if(hardlinked)
		{
			// Create a file to indicate that the previous backup
			// does not have others depending on it.
			FILE *hfp=NULL;
			if(!(hfp=open_file(hlinkedpath, "wb")))
				goto end;
			// Stick the next backup timestamp in it. It might
			// be useful one day when wondering when the next
			// backup, now deleted, was made.
			fprintf(hfp, "%s\n", tstmp);
			if(close_fp(&hfp))
			{
				logp("error closing hardlinked indication\n");
				goto end;
			}
			logp(" doing hardlinked archive\n");
			logp(" will not generate reverse deltas\n");
		}
		else
		{
			logp(" not doing hardlinked archive\n");
			logp(" will generate reverse deltas\n");
			unlink(hlinkedpath);
		}
	}

	if(atomic_data_jiggle(sdirs, cconf, manifest, currentdup,
		currentdupdata, datadir, datadirtmp, deletionsfile,
		hardlinked, bno))
	{
		logp("could not finish up backup.\n");
		goto end;
	}

	if(write_status(STATUS_SHUFFLING, "deleting temporary files", cconf))
		goto end;

	// Remove the temporary data directory, we have now removed
	// everything useful from it.
	recursive_delete(datadirtmp, NULL, 1 /* del files */);

	// Clean up the currentdata directory - this is now the 'old'
	// currentdata directory. Any files that were deleted from
	// the client will be left in there, so call recursive_delete
	// with the option that makes it not delete files.
	// This will have the effect of getting rid of unnecessary
	// directories.
	sync(); // try to help CIFS
	recursive_delete(currentdupdata, NULL, 0 /* do not del files */);

	// Rename the old current to something that we know to delete.
	if(previous_backup)
	{
		if(deleteme_move(sdirs->client,
			fullrealcurrent, realcurrent, cconf)
		  || do_rename(currentdup, fullrealcurrent))
			goto end;
	}

	if(deleteme_maybe_delete(cconf, sdirs->client))
		goto end;

	cntr_stats_to_file(cconf->cntr, sdirs->finishing, ACTION_BACKUP);

	// Rename the finishing symlink so that it becomes the current symlink
	do_rename(sdirs->finishing, sdirs->current);

	cntr_print(cconf->cntr, ACTION_BACKUP);
	logp("Backup completed.\n");
	logp("End phase4 (shuffle files)\n");
	set_logfp(NULL, cconf); // will close logfp.

	compress_filename(sdirs->current, "log", "log.gz", cconf);

	ret=0;
end:
	if(datadir) free(datadir);
	if(datadirtmp) free(datadirtmp);
	if(manifest) free(manifest);
	if(deletionsfile) free(deletionsfile);
	if(currentdup) free(currentdup);
	if(currentduptmp) free(currentduptmp);
	if(currentdupdata) free(currentdupdata);
	if(timestamp) free(timestamp);
	if(fullrealcurrent) free(fullrealcurrent);
	if(logpath) free(logpath);
	if(hlinkedpath) free(hlinkedpath);

	return ret;
}
Beispiel #9
0
int backup_phase4_server(const char *basedir, const char *working, const char *current, const char *currentdata, const char *finishing, struct config *cconf, const char *client, struct cntr *p1cntr, struct cntr *cntr)
{
	int ret=0;
	struct stat statp;
	char *manifest=NULL;
	char *datadir=NULL;
	char *datadirtmp=NULL;
	char *currentdup=NULL;
	char *currentduptmp=NULL;
	char *currentdupdata=NULL;
	char *forward=NULL;
	char *timestamp=NULL;
	char *fullrealcurrent=NULL;
	char *deleteme=NULL;
	char *logpath=NULL;
	char *hlinkedpath=NULL;
	int len=0;
	char realcurrent[256]="";
	FILE *logfp=NULL;

	if((len=readlink(current, realcurrent, sizeof(realcurrent)-1))<0)
		len=0;
	realcurrent[len]='\0';

	if(!(datadir=prepend_s(finishing, "data", strlen("data")))
	  || !(datadirtmp=prepend_s(finishing, "data.tmp", strlen("data.tmp")))
	  || !(manifest=prepend_s(finishing, "manifest.gz", strlen("manifest.gz")))
	  || !(currentdup=prepend_s(finishing, "currentdup", strlen("currentdup")))
	  || !(currentduptmp=prepend_s(finishing, "currentdup.tmp", strlen("currentdup.tmp")))
	  || !(currentdupdata=prepend_s(currentdup, "data", strlen("data")))
	  || !(forward=prepend_s(currentdup, "forward", strlen("forward")))
	  || !(timestamp=prepend_s(finishing, "timestamp", strlen("timestamp")))
	  || !(fullrealcurrent=prepend_s(basedir, realcurrent, strlen(realcurrent)))
	  || !(deleteme=prepend_s(basedir, "deleteme", strlen("deleteme")))
	  || !(logpath=prepend_s(finishing, "log", strlen("log")))
	  || !(hlinkedpath=prepend_s(currentdup, "hardlinked", strlen("hardlinked"))))
	{
		ret=-1;
		goto endfunc;
	}

	if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp, cconf))
	{
		ret=-1;
		goto endfunc;
	}

	logp("Begin phase4 (shuffle files)\n");

	write_status(client, STATUS_SHUFFLING, NULL, p1cntr, cntr);

	if(!lstat(current, &statp)) // Had a previous backup
	{
		unsigned long bno=0;
		FILE *fwd=NULL;
		int hardlinked=0;
		char tstmp[64]="";
		int newdup=0;

		if(lstat(currentdup, &statp))
		{
			// Have not duplicated the current backup yet.
			if(!lstat(currentduptmp, &statp))
			{
				logp("Removing previous currentduptmp directory: %s\n", currentduptmp);
				if(recursive_delete(currentduptmp,
					NULL, TRUE /* del files */))
				{
					logp("Could not delete %s\n",
						currentduptmp);
					ret=-1;
					goto endfunc;
				}
			}
			logp("Duplicating current backup.\n");
			if(recursive_hardlink(current, currentduptmp, client,
				p1cntr, cntr, cconf)
			  || do_rename(currentduptmp, currentdup))
			{
				ret=-1;
				goto endfunc;
			}
			newdup++;
		}

		if(read_timestamp(timestamp, tstmp, sizeof(tstmp)))
		{
			logp("could not read timestamp file: %s\n", timestamp);
			ret=-1;
			goto endfunc;
		}
		// Get the backup number.
		bno=strtoul(tstmp, NULL, 10);

		// Put forward reference in, indicating the timestamp of
		// the working directory (which will soon become the current
		// directory).
		if(!(fwd=open_file(forward, "wb")))
		{
			log_and_send("could not create forward file");
			ret=-1;
			goto endfunc;
		}
		fprintf(fwd, "%s\n", tstmp);
		close_fp(&fwd);

		if(newdup)
		{
			// When we have just created currentdup, determine
			// hardlinked archive from the conf and the backup
			// number...
			hardlinked=do_hardlinked_archive(cconf, bno);
		}
		else
		{
			// ...if recovering, find out what currentdup started
			// out as.
			// Otherwise it is possible that things can be messed
			// up by somebody swapping between hardlinked and
			// not hardlinked at the same time as a resume happens.
			if(lstat(hlinkedpath, &statp))
			{
				logp("previous attempt started not hardlinked\n");
				hardlinked=0;
			}
			else
			{
				logp("previous attempt started hardlinked\n");
				hardlinked=1;
			}
		}

		if(hardlinked)
		{
			// Create a file to indicate that the previous backup
			// does not have others depending on it.
			FILE *hfp=NULL;
			if(!(hfp=open_file(hlinkedpath, "wb")))
			{
				ret=-1;
				goto endfunc;
			}
			// Stick the next backup timestamp in it. It might
			// be useful one day when wondering when the next
			// backup, now deleted, was made.
			fprintf(hfp, "%s\n", tstmp);
			close_fp(&hfp);
			logp(" doing hardlinked archive\n");
			logp(" will not generate reverse deltas\n");
		}
		else
		{
			logp(" not doing hardlinked archive\n");
			logp(" will generate reverse deltas\n");
			unlink(hlinkedpath);
		}

		if(atomic_data_jiggle(finishing,
			working, manifest, currentdup,
			currentdupdata,
			datadir, datadirtmp, cconf, client,
			hardlinked, bno, p1cntr, cntr))
		{
			logp("could not finish up backup.\n");
			ret=-1;
			goto endfunc;
		}

		write_status(client, STATUS_SHUFFLING,
			"deleting temporary files", p1cntr, cntr);

		// Remove the temporary data directory, we have now removed
		// everything useful from it.
		recursive_delete(datadirtmp, NULL, TRUE /* del files */);

		// Clean up the currentdata directory - this is now the 'old'
		// currentdata directory. Any files that were deleted from
		// the client will be left in there, so call recursive_delete
		// with the option that makes it not delete files.
		// This will have the effect of getting rid of unnecessary
		// directories.
		sync(); // try to help CIFS
		recursive_delete(currentdupdata, NULL, FALSE /* do not del files */);

		// Rename the old current to something that we know to
		// delete.
		if(do_rename(fullrealcurrent, deleteme))
		{
			ret=-1;
			goto endfunc;
		}
	}
	else
	{
		// No previous backup, just put datadirtmp in the right place.
		if(do_rename(datadirtmp, datadir))
		{
			ret=-1;
			goto endfunc;
		}
	}

	if(!lstat(deleteme, &statp))
	{
		// Rename the currentdup directory...
		// IMPORTANT TODO: read the path to fullrealcurrent
		// from the deleteme timestamp.
		do_rename(currentdup, fullrealcurrent);

		recursive_delete(deleteme, NULL, TRUE /* delete all */);
	}

	// Rename the finishing symlink so that it becomes the current symlink
	do_rename(finishing, current);

	print_filecounters(p1cntr, cntr, ACTION_BACKUP, 0);
	logp("Backup completed.\n");
	logp("End phase4 (shuffle files)\n");
	set_logfp(NULL, cconf); // will close logfp.

	compress_filename(current, "log", "log.gz", cconf);

endfunc:
	if(datadir) free(datadir);
	if(datadirtmp) free(datadirtmp);
	if(manifest) free(manifest);
	if(currentdup) free(currentdup);
	if(currentduptmp) free(currentduptmp);
	if(currentdupdata) free(currentdupdata);
	if(forward) free(forward);
	if(timestamp) free(timestamp);
	if(fullrealcurrent) free(fullrealcurrent);
	if(deleteme) free(deleteme);
	if(logpath) free(logpath);
	if(hlinkedpath) free(hlinkedpath);

	return ret;
}
int main() {
    /*  First we need to create XML descriptions of the Extents that we mean to use.  For this
        example we will use two ExtentTypes--one for reads and one for writes.  DataSeries doesn't
        like to mix different types of records, so we need separate types for reads and writes if
        we want each field to be non-null. */

    const char* read_xml =
            "<ExtentType name=\"ExtentType1\">"
            "  <field name=\"timestamp\" type=\"int64\" />"
            "  <field name=\"requested_bytes\" type=\"int64\" />"
            "  <field name=\"actual_bytes\" type=\"int64\" />"
            "</ExtentType>\n";

    const char* write_xml =
            "<ExtentType name=\"ExtentType2\">"
            "  <field name=\"timestamp\" type=\"int64\" />"
            "  <field name=\"bytes\" type=\"int64\" />"
            "</ExtentType>\n";

    /*  Next we need to put both of these in an ExtentType library which
        will be the first thing written to the file. */

    ExtentTypeLibrary types_for_file;
    const ExtentType::Ptr read_type = types_for_file.registerTypePtr(read_xml);
    const ExtentType::Ptr write_type = types_for_file.registerTypePtr(write_xml);

    /*  Then we open a file to write the records to. This will overwrite an existing file.
        DataSeries files can not be updated once closed. */

    DataSeriesSink output_file("writing_a_file.ds");
    output_file.writeExtentLibrary(types_for_file);

    /*  Now, we create structures for writing the individual
        types.  Note that each type of extent is stored separately.
        The ExtentSeries will allow us to set the fields in each
        record that we create.  We'll split the records into
        approximately 1024 byte chunks uncompressed. */

    ExtentSeries read_series;
    OutputModule read_output(output_file, read_series, read_type, 1024);
    ExtentSeries write_series;
    OutputModule write_output(output_file, write_series, write_type, 1024);

    /*  These are handles to the fields in the "current record" of
        each ExtentSeries. */

    Int64Field read_timestamp(read_series, "timestamp");
    Int64Field read_requested_bytes(read_series, "requested_bytes");
    Int64Field read_actual_bytes(read_series, "actual_bytes");

    Int64Field write_timestamp(write_series, "timestamp");
    Int64Field write_bytes(write_series, "bytes");

    BOOST_FOREACH(const char* record, original_records) {
        boost::cmatch match;
        boost::regex_match(record, record_regex);
        if (match[submatches::read].matched) {
            /*  Create a new record and set its fields. */
            read_output.newRecord();
            read_timestamp.set(get_field(match, submatches::read_timestamp));
            read_requested_bytes.set(get_field(match, submatches::read_requested));
            read_actual_bytes.set(get_field(match, submatches::read_actual));
        } else if (match[submatches::write].matched) {
            /*  Create a new record and set its fields. */
            write_output.newRecord();
            write_timestamp.set(get_field(match, submatches::write_timestamp));
            write_bytes.set(get_field(match, submatches::write_bytes));
        }
    }