faubackup2/src/copier distributor.cpp,1.10,1.11 distributor.hpp,1.7,1.8 filesender.cpp,1.5,1.6 filesender.hpp,1.3,1.4 informationSender.cpp,1.7,1.8 informationSender.hpp,1.4,1.5

Erik Merkel <[email protected]> Sun, 03 Jul 2005 18:37:17 +0000
Newsgroups gmane.comp.sysutils.backup.faubackup.cvs
Message-ID <[email protected]>
Update of /cvsroot/faubackup/faubackup2/src/copier
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7948/src/copier

Modified Files:
	distributor.cpp distributor.hpp filesender.cpp filesender.hpp 
	informationSender.cpp informationSender.hpp 
Log Message:
fixed hardlink bug: if a new created hardlink is transfered before the 
existing (unchanged) file is transfered, the hardlink is treated as a new
file and the existing (unchanged) file is created as a link to this new
file. These files need space on the disc. They should be both linked to an 
old backupset if the existing file is unchanged.

hardlink checking moved from InformationSender to FileSender

hardlinks are now transfered at the end of the backup process

directorys are now transferd after the sending of the hardlinks because
creating of hardlinks will change the modification time of directorys


Index: informationSender.cpp
===================================================================
RCS file: /cvsroot/faubackup/faubackup2/src/copier/informationSender.cpp,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** informationSender.cpp	26 Jun 2005 18:39:02 -0000	1.7
--- informationSender.cpp	3 Jul 2005 18:37:15 -0000	1.8
***************
*** 18,22 ****
  
  	network = &net;
- 	//register_hardlink_file("bla", 42); // got segfault without this line - why?
  }
  
--- 18,21 ----
***************
*** 27,32 ****
  {
  	output.information("new item: " + data.getFilename(), DEBUG_OUTPUT);
! 	if(isHardlinked(st))
! 		data.setNameForLink(resolv_hardlink_file(data.getFilename()));
  	char * rawdata = (char *) data.getRawData();
  	network->sendData(rawdata, data.getRawDataSize());
--- 26,31 ----
  {
  	output.information("new item: " + data.getFilename(), DEBUG_OUTPUT);
! 	if(!isHardlinked(st))
! 		data.setNoHardlink();
  	char * rawdata = (char *) data.getRawData();
  	network->sendData(rawdata, data.getRawDataSize());
***************
*** 43,71 ****
  }
  
- void InformationSender::register_hardlink_file(string name, ino_t inode_nr)
- {
- 	HardlinkMap.insert(pair<ino_t , string>(inode_nr, name));
- }
- 
- string InformationSender::resolv_hardlink_file(string name)
- {
- 	if(name == "")
- 		return "";
- 	struct stat st = getStat(startdir + name);
- 	multimap<ino_t , string>::iterator i, j;
- 	pair<multimap<ino_t , string>::iterator , multimap<ino_t , string>::iterator>result;
- 	result = HardlinkMap.equal_range(st.st_ino);
- 	i = result.first;
- 	j = result.second;
- 	if(i != HardlinkMap.end())
- 		do {
- 			struct stat st2 = getStat(startdir + i->second);
- 			if(st.st_dev == st2.st_dev) //make sure file is one the same dev
- 				return i->second;
- 			i++;
- 		} while(i != j);
- 	register_hardlink_file(name, st.st_ino);
- 	return "";
- }
  
  }
--- 42,45 ----

Index: informationSender.hpp
===================================================================
RCS file: /cvsroot/faubackup/faubackup2/src/copier/informationSender.hpp,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** informationSender.hpp	22 Jun 2005 22:56:29 -0000	1.4
--- informationSender.hpp	3 Jul 2005 18:37:15 -0000	1.5
***************
*** 22,26 ****
  	string old_dir;
  	string startdir;
- 	multimap<ino_t, string> HardlinkMap;
  
  public:
--- 22,25 ----
***************
*** 29,34 ****
  	void send(Fileinfo &item, struct stat &st);
  	void sendfinish();
- 	void register_hardlink_file(string name, ino_t inode_nr);
- 	string resolv_hardlink_file(string name);
  };
  
--- 28,31 ----

Index: filesender.hpp
===================================================================
RCS file: /cvsroot/faubackup/faubackup2/src/copier/filesender.hpp,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** filesender.hpp	14 Jun 2005 22:10:31 -0000	1.3
--- filesender.hpp	3 Jul 2005 18:37:15 -0000	1.4
***************
*** 3,6 ****
--- 3,9 ----
  
  #include <string>
+ #include <list>
+ 
+ #include "filesystem/r_inodes.hpp"
  
  
***************
*** 9,15 ****
  
  using std::string;
! class NetCommunication;
! 
  
  
  /*!
--- 12,18 ----
  
  using std::string;
! using std::list;
  
+ class NetCommunication;
  
  /*!
***************
*** 32,38 ****
  {
  private:
  	string currentWorkDir;
  	NetCommunication *network;
! 
  
  
--- 35,47 ----
  {
  private:
+ 	struct dir_item
+ 	{
+ 		struct stat st;
+ 		string dirname;
+ 	};
+ 
  	string currentWorkDir;
  	NetCommunication *network;
! 	list<struct dir_item> dir_list;
  
  
***************
*** 43,47 ****
--- 52,58 ----
  	void rdiff_file(string name);
  	void sendInode(string name);
+ 	void sendInode(struct stat &st);
  	void reg_file_hardlink(string name);
+ 	void send_dirs();
  
  

Index: distributor.cpp
===================================================================
RCS file: /cvsroot/faubackup/faubackup2/src/copier/distributor.cpp,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** distributor.cpp	22 Jun 2005 21:42:31 -0000	1.10
--- distributor.cpp	3 Jul 2005 18:37:15 -0000	1.11
***************
*** 19,46 ****
  	if(do_nothing) {
  		output.information(F_("transfer file:\t%s")
! 				% file.getFilename(), DEBUG_OUTPUT);
  		return;
  	}
  	struct stat st = getStat(old_dir + file.getFilename());
! 	if(file.getNameForLink() != "") {
! 		network->sendByte(HardlinkFile);
  		network->sendString(file.getFilename());
! 		network->sendString(file.getNameForLink());
! 	} else
! 		if((st.st_size > MIN_RSYNC_SIZE) &&
! 		    (file.getFileSize() > MIN_RSYNC_SIZE) &&
! 		    (rsync) ) {
! 			output.information(F_("rsync file:\t%s")
! 				% file.getFilename(), DEBUG_OUTPUT);
! 			network->sendByte(RdiffRegFile);
! 			network->sendString(file.getFilename());
! 			checkSum(old_dir + file.getFilename(), st.st_size, *network);
! 		} else {
! 			output.information(F_("copy file:\t%s %d")
! 				% file.getFilename() % rsync, DEBUG_OUTPUT);
  
! 			network->sendByte(CopyRegFile);
! 			network->sendString(file.getFilename());
! 		}
  }
  void Distributor::file_unchanged(Fileinfo &file)
--- 19,41 ----
  	if(do_nothing) {
  		output.information(F_("transfer file:\t%s")
! 				   % file.getFilename(), DEBUG_OUTPUT);
  		return;
  	}
  	struct stat st = getStat(old_dir + file.getFilename());
! 	if((st.st_size > MIN_RSYNC_SIZE) &&
! 	    (file.getFileSize() > MIN_RSYNC_SIZE) &&
! 	    (rsync) ) {
! 		output.information(F_("rsync file:\t%s")
! 				   % file.getFilename(), DEBUG_OUTPUT);
! 		network->sendByte(RdiffRegFile);
  		network->sendString(file.getFilename());
! 		checkSum(old_dir + file.getFilename(), st.st_size, *network);
! 	} else {
! 		output.information(F_("copy file:\t%s %d")
! 				   % file.getFilename() % rsync, DEBUG_OUTPUT);
  
! 		network->sendByte(CopyRegFile);
! 		network->sendString(file.getFilename());
! 	}
  }
  void Distributor::file_unchanged(Fileinfo &file)
***************
*** 48,52 ****
  	if(do_nothing) {
  		output.information(F_("Hardlink file:\t%s")
! 				% file.getFilename(), DEBUG_OUTPUT);
  
  		return;
--- 43,47 ----
  	if(do_nothing) {
  		output.information(F_("Hardlink file:\t%s")
! 				   % file.getFilename(), DEBUG_OUTPUT);
  
  		return;
***************
*** 61,86 ****
  		if(do_nothing) {
  			output.information(F_("Ignore file:\t%s")
! 					% file.getFilename(), DEBUG_OUTPUT);
  		}
  		return;
  	}
! 	if(file.getNameForLink() != "") {
! 		if(do_nothing) {
! 			output.information(F_("Hardlink file:\t%s")
! 					% file.getFilename(), DEBUG_OUTPUT);
! 			return;
! 		}
! 		network->sendByte(HardlinkFile);
! 		network->sendString(file.getFilename());
! 		network->sendString(file.getNameForLink());
! 	} else {
! 		if(do_nothing) {
! 			output.information(F_("Transfer file:\t%s")
! 					% file.getFilename(), DEBUG_OUTPUT);
! 			return;
! 		}
! 		network->sendByte(CopyRegFile);
! 		network->sendString(file.getFilename());
  	}
  }
  void Distributor::no_reg_file(Fileinfo &file)
--- 56,71 ----
  		if(do_nothing) {
  			output.information(F_("Ignore file:\t%s")
! 					   % file.getFilename(), DEBUG_OUTPUT);
  		}
  		return;
  	}
! 	if(do_nothing) {
! 		output.information(F_("Transfer file:\t%s")
! 				   % file.getFilename(), DEBUG_OUTPUT);
! 		return;
  	}
+ 	network->sendByte(CopyRegFile);
+ 	network->sendString(file.getFilename());
+ 
  }
  void Distributor::no_reg_file(Fileinfo &file)
***************
*** 88,92 ****
  	if(do_nothing) {
  		output.information(F_("no reg file:\t%s")
! 				% file.getFilename(), DEBUG_OUTPUT);
  		return;
  	}
--- 73,77 ----
  	if(do_nothing) {
  		output.information(F_("no reg file:\t%s")
! 				   % file.getFilename(), DEBUG_OUTPUT);
  		return;
  	}
***************
*** 108,111 ****
--- 93,181 ----
  }
  
+ 
+ void Distributor::register_old_hardlink_file(Fileinfo info)
+ {
+ 	OldHardlinkMap.push_back(info);
+ }
+ 
+ void Distributor::register_hardlink_file(ino_t inodeNo, Fileinfo info)
+ {
+ 	HardlinkMap.insert(pair<ino_t , Fileinfo>(inodeNo, info));
+ }
+ 
+ void Distributor::preservedHardlink(ino_t inodeNo, dev_t deviceNo, string destination)
+ {
+ 	multimap<ino_t , Fileinfo>::iterator i, j, todelete;
+ 	pair<	multimap<ino_t ,  Fileinfo>::iterator,
+ 	multimap<ino_t , Fileinfo>::iterator
+ 	> result;
+ 	result = HardlinkMap.equal_range(inodeNo);
+ 	i = result.first;
+ 	j = result.second;
+ 	if(i != HardlinkMap.end())
+ 		do {
+ 			if(deviceNo == i->second.getDeviceNo()) //make sure file is one the same dev
+ 			{
+ 				network->sendByte(HardlinkFile);
+ 				network->sendString(i->second.getFilename());
+ 				network->sendString(destination);
+ 				todelete = i;
+ 				i++;
+ 				HardlinkMap.erase(todelete);
+ 			} else
+ 				i++;
+ 		} while(i != j);
+ }
+ 
+ void Distributor::unresolvedHardlinks()
+ {
+ 	multimap<ino_t , Fileinfo>::iterator i, j, begin, todelete;
+ 	pair<	multimap<ino_t ,  Fileinfo>::iterator,
+ 	multimap<ino_t , Fileinfo>::iterator
+ 	> result;
+ 	while(HardlinkMap.size()) {
+ 		begin = HardlinkMap.begin();
+ 		result = HardlinkMap.equal_range(begin->first);
+ 		i = result.first;
+ 		string destination = i->second.getFilename();
+ 		dev_t deviceNo = i->second.getDeviceNo();
+ 		if(i->second.getExisting())
+ 			file_changed(i->second);
+ 		else
+ 			new_file(i->second);
+ 		j = result.second;
+ 		if(i == j) //just one element
+ 			continue;
+ 		todelete = i;
+ 		i++;
+ 		HardlinkMap.erase(todelete);
+ 		if(i != HardlinkMap.end())
+ 			do {
+ 				if(deviceNo == i->second.getDeviceNo()) //make sure file is one the same dev
+ 				{
+ 					network->sendByte(HardlinkFile);
+ 					network->sendString(i->second.getFilename());
+ 					network->sendString(destination);
+ 					todelete = i;
+ 					i++;
+ 					HardlinkMap.erase(todelete);
+ 				} else
+ 					i++;
+ 			} while(i != j);
+ 	}
+ }
+ 
+ void Distributor::sendHardlinks()
+ {
+ 	while(OldHardlinkMap.size()) {
+ 		file_unchanged(OldHardlinkMap.front());
+ 		preservedHardlink(OldHardlinkMap.front().getInodeNo(),
+ 				  OldHardlinkMap.front().getDeviceNo(),
+ 				  OldHardlinkMap.front().getFilename());
+ 		OldHardlinkMap.pop_front();
+ 	}
+ 	unresolvedHardlinks();
+ }
+ 
  Distributor::Distributor(NetCommunication &com, string old_backup_dir, string dest)
  		:network(&com), old_dir(old_backup_dir), destination(dest)
***************
*** 116,119 ****
--- 186,190 ----
  		destination.append("/");
  }
+ 
  Distributor::~Distributor()
  {}
***************
*** 128,134 ****
  		Fileinfo fileinfo(data, size);
  		output.information(F_("received Fileinfo: %s type %d")
! 				% fileinfo.getFilename()
! 				% fileinfo.getFileType(), DEBUG_OUTPUT);
  		if(fileinfo.getFileType() == EXIT) {
  			if(!do_nothing)
  				network->sendByte(EXIT);
--- 199,206 ----
  		Fileinfo fileinfo(data, size);
  		output.information(F_("received Fileinfo: %s type %d")
! 				   % fileinfo.getFilename()
! 				   % fileinfo.getFileType(), DEBUG_OUTPUT);
  		if(fileinfo.getFileType() == EXIT) {
+ 			sendHardlinks();
  			if(!do_nothing)
  				network->sendByte(EXIT);
***************
*** 137,168 ****
  
  		output.information(F_("test for: %s %s")
! 			% old_dir % fileinfo.getFilename(), DEBUG_OUTPUT);
  		if(fileinfo.getFileType() != Reg) {
  			output.information(F_("no reg file: %s")
! 					% fileinfo.getFilename(), DEBUG_OUTPUT);
  			no_reg_file(fileinfo);
  		} else
  			if( existFile(old_dir + fileinfo.getFilename()) ) {
  				output.information(F_("file exist: %s %s")
! 					% old_dir % fileinfo.getFilename(),
! 					DEBUG_OUTPUT);
  
  				Fileinfo oldfile(fileinfo.getFilename(), old_dir +
! 				                 fileinfo.getFilename());
  				if(oldfile == fileinfo) {
  					output.information("file exist unchanged: " +
! 					                   fileinfo.getFilename(),
! 					                   DEBUG_OUTPUT);
! 					file_unchanged(fileinfo);
  				} else {
  					output.information("file exist changed: " +
! 					                   fileinfo.getFilename(),
! 					                   DEBUG_OUTPUT);
! 					file_changed(fileinfo);
  				}
  			} else {
  				output.information("file does not exist: "  +
! 				                   fileinfo.getFilename(), DEBUG_OUTPUT);
! 				new_file(fileinfo);
  			}
  	}
--- 209,252 ----
  
  		output.information(F_("test for: %s %s")
! 				   % old_dir % fileinfo.getFilename(), DEBUG_OUTPUT);
  		if(fileinfo.getFileType() != Reg) {
  			output.information(F_("no reg file: %s")
! 					   % fileinfo.getFilename(), DEBUG_OUTPUT);
  			no_reg_file(fileinfo);
  		} else
  			if( existFile(old_dir + fileinfo.getFilename()) ) {
  				output.information(F_("file exist: %s %s")
! 						   % old_dir % fileinfo.getFilename(),
! 						   DEBUG_OUTPUT);
  
  				Fileinfo oldfile(fileinfo.getFilename(), old_dir +
! 						 fileinfo.getFilename());
  				if(oldfile == fileinfo) {
  					output.information("file exist unchanged: " +
! 							   fileinfo.getFilename(),
! 							   DEBUG_OUTPUT);
! 					if(fileinfo.getInodeNo())
! 						register_old_hardlink_file(fileinfo);
! 					else
! 						file_unchanged(fileinfo);
  				} else {
+ 					fileinfo.setExisting(true);
  					output.information("file exist changed: " +
! 							   fileinfo.getFilename(),
! 							   DEBUG_OUTPUT);
! 					if(fileinfo.getInodeNo())
! 						register_hardlink_file(fileinfo.getInodeNo(), fileinfo);
! 					else
! 						file_changed(fileinfo);
  				}
  			} else {
+ 				fileinfo.setExisting(false);
  				output.information("file does not exist: "  +
! 						   fileinfo.getFilename(), DEBUG_OUTPUT);
! 
! 				if(fileinfo.getInodeNo())
! 					register_hardlink_file(fileinfo.getInodeNo(), fileinfo);
! 				else
! 					new_file(fileinfo);
  			}
  	}

Index: distributor.hpp
===================================================================
RCS file: /cvsroot/faubackup/faubackup2/src/copier/distributor.hpp,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** distributor.hpp	26 Jun 2005 23:02:09 -0000	1.7
--- distributor.hpp	3 Jul 2005 18:37:15 -0000	1.8
***************
*** 3,6 ****
--- 3,8 ----
  
  #include <string>
+ #include <map>
+ #include <list>
  
  #include "common/fileinfo.hpp"
***************
*** 13,16 ****
--- 15,21 ----
  using std::cout;
  using std::endl;
+ using std::list;
+ using std::pair;
+ using std::multimap;
  
  namespace faubackup
***************
*** 23,33 ****
--- 28,50 ----
  {
  private:
+ 
+ 
  	NetCommunication *network;
  	string old_dir, destination;
  
+ 	list <Fileinfo> OldHardlinkMap;
+ 	multimap<ino_t, Fileinfo> HardlinkMap;
+ 
+ 	void register_hardlink_file(ino_t inodeNo, Fileinfo info);
+ 	void register_old_hardlink_file(Fileinfo info);
+ 	void preservedHardlink(ino_t inodeNo, dev_t deviceNo, string destination);
+ 	void unresolvedHardlinks();
+ 
+ 
  	void file_changed(Fileinfo &file);
  	void file_unchanged(Fileinfo &file);
  	void new_file(Fileinfo &file);
  	void no_reg_file(Fileinfo &file);
+ 	void sendHardlinks();
  
  public:

Index: filesender.cpp
===================================================================
RCS file: /cvsroot/faubackup/faubackup2/src/copier/filesender.cpp,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** filesender.cpp	21 Jun 2005 09:16:45 -0000	1.5
--- filesender.cpp	3 Jul 2005 18:37:15 -0000	1.6
***************
*** 13,19 ****
--- 13,40 ----
  namespace faubackup
  {
+ void FileSender::send_dirs()
+ {
+ 	while(dir_list.size()) {
+ 		network->sendByte(CopySpecial);
+ 		network->sendString(dir_list.front().dirname);
+ 		sendInode(dir_list.front().st);
+ 		dir_list.pop_front();
+ 	}
+ }
  
  void FileSender::special_file(string name)
  {
+ 	struct stat st = getStat((currentWorkDir + name).c_str());
+ 	if(whatType(st) == Dir) {
+ 		struct dir_item item;
+ 		item.st = st;
+ 		item.dirname=  name;
+ 		dir_list.push_back(item);
+ 	} else {
+ 		network->sendByte(CopySpecial);
+ 		network->sendString(name);
+ 		sendInode(name);
+ 	}
+ 
  	output.information("special file!", DEBUG_OUTPUT);
  	//nothing to do
***************
*** 33,37 ****
  	if((file = fopen((currentWorkDir + name).c_str(), "rb")) == NULL) {
  		output.non_critical(F_("could not open %s for reading")
! 				% name, errno);
  		return;
  	}
--- 54,58 ----
  	if((file = fopen((currentWorkDir + name).c_str(), "rb")) == NULL) {
  		output.non_critical(F_("could not open %s for reading")
! 				    % name, errno);
  		return;
  	}
***************
*** 66,70 ****
  	char *pointer = getRawInode(st, size);
  	network->sendData(pointer, size);
! 	//delete [] pointer;
  }
  
--- 87,97 ----
  	char *pointer = getRawInode(st, size);
  	network->sendData(pointer, size);
! }
! 
! void FileSender::sendInode(struct stat &st)
! {
! 	int size;
! 	char *pointer = getRawInode(st, size);
! 	network->sendData(pointer, size);
  }
  
***************
*** 83,129 ****
  	unsigned char message = 0;
  	while(message != Exit) {
- 		string name, link_dest = "";
- 		//bool bla;
  		message = network->readByte();
  		output.information("message erhalten: " + int2string(message), DEBUG_OUTPUT);
  		if(message == EXIT) {
  			network->sendByte(message);
  			return;
  		}
! 		name = network->readString();
! 
! 		//network_out->sendByte(whatType(getStat(
! 		//                                          (currentWorkDir
! 		//                                           + name).c_str(), bla)));
  		output.information(name, NORMAL_OUTPUT);
  		if(test_for_illegal_dirs(name))
  			output.critical(
! 			        "############ POSSIBLE SECURITY PROBLEM ############\n"
! 			        "\n\n The remote Host has send an illegal pathname which could not be produced with faubackup itself."
! 			        "\n The remote host maybe modified to gain access to your files. Please inform the admin of the remote host!", 0);
! 		network->sendByte(message);
! 		network->sendString(name);
! 
  
  		switch(message) {
  		case CopyRegFile :
  			sendInode(name);
  			reg_file_copy(name);
  			break;
  		case BackupLinkRegFile :
  			reg_file_link(name);
  			break;
  		case HardlinkFile:
  			reg_file_hardlink(name);
  			break;
  		case RdiffRegFile :
  			sendInode(name);
  			rdiff_file(name);
  			break;
  		case CopySpecial :
- 			sendInode(name);
  			special_file(name);
  			break;
  		case SymlinkFile :
  			sendInode(name);
  			symlink_file(name);
--- 110,157 ----
  	unsigned char message = 0;
  	while(message != Exit) {
  		message = network->readByte();
  		output.information("message erhalten: " + int2string(message), DEBUG_OUTPUT);
  		if(message == EXIT) {
+ 			send_dirs();
  			network->sendByte(message);
  			return;
  		}
! 		string name = network->readString();
  		output.information(name, NORMAL_OUTPUT);
  		if(test_for_illegal_dirs(name))
  			output.critical(
! 				"############ POSSIBLE SECURITY PROBLEM ############\n"
! 				"\n\n The remote Host has send an illegal pathname which could not be produced with faubackup itself."
! 				"\n The remote host maybe modified to gain access to your files. Please inform the admin of the remote host!", 0);
  
  		switch(message) {
  		case CopyRegFile :
+ 			network->sendByte(message);
+ 			network->sendString(name);
  			sendInode(name);
  			reg_file_copy(name);
  			break;
  		case BackupLinkRegFile :
+ 			network->sendByte(message);
+ 			network->sendString(name);
  			reg_file_link(name);
  			break;
  		case HardlinkFile:
+ 			network->sendByte(message);
+ 			network->sendString(name);
  			reg_file_hardlink(name);
  			break;
  		case RdiffRegFile :
+ 			network->sendByte(message);
+ 			network->sendString(name);
  			sendInode(name);
  			rdiff_file(name);
  			break;
  		case CopySpecial :
  			special_file(name);
  			break;
  		case SymlinkFile :
+ 			network->sendByte(message);
+ 			network->sendString(name);
  			sendInode(name);
  			symlink_file(name);



-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click