/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.io.fs;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import org.tmatesoft.sqljet.core.SqlJetException;
import org.tmatesoft.sqljet.core.table.ISqlJetTransaction;
import org.tmatesoft.sqljet.core.table.SqlJetDb;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetDb;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class FSHotCopier {
public void runHotCopy(FSFS srcOwner, File dstPath) throws SVNException {
FSWriteLock dbLogsLock = FSWriteLock.getDBLogsLock(srcOwner, false);
File srcPath = srcOwner.getRepositoryRoot();
synchronized (dbLogsLock) {
try {
dbLogsLock.lock();
createRepositoryLayout(srcPath, dstPath);
File dstReposLocksDir = new File(dstPath, FSFS.LOCKS_DIR);
try {
createReposDir(dstReposLocksDir);
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage().wrap("Creating lock dir");
SVNErrorManager.error(err, SVNLogType.FSFS);
}
createDBLock(dstReposLocksDir);
createDBLogsLock(dstReposLocksDir);
File dstDBDir = new File(dstPath, FSFS.DB_DIR);
dstDBDir.mkdirs();
SVNFileUtil.setSGID(dstDBDir);
FSFS dstOwner = new FSFS(dstPath);
String fsType = srcOwner.getFSType();
hotCopy(srcOwner, dstOwner);
writeFSType(dstOwner, fsType);
SVNFileUtil.writeVersionFile(new File(dstPath, FSFS.REPOS_FORMAT_FILE),
srcOwner.getReposFormat());
} finally {
dbLogsLock.unlock();
FSWriteLock.release(dbLogsLock);
}
}
}
private void writeFSType(FSFS dstOwner, String fsType) throws SVNException {
OutputStream fsTypeStream = null;
try {
fsTypeStream = SVNFileUtil.openFileForWriting(dstOwner.getFSTypeFile());
fsType += '\n';
fsTypeStream.write(fsType.getBytes("US-ASCII"));
} catch (IOException ioe) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage());
SVNErrorManager.error(err, SVNLogType.FSFS);
} finally {
SVNFileUtil.closeFile(fsTypeStream);
}
}
private void createRepositoryLayout(File srcPath, File dstPath) throws SVNException {
File[] children = srcPath.listFiles();
for (int i = 0; i < children.length; i++) {
File child = children[i];
String childName = child.getName();
if (childName.equals(FSFS.DB_DIR) || childName.equals(FSFS.LOCKS_DIR) ||
childName.equals(FSFS.REPOS_FORMAT_FILE)) {
continue;
}
File dstChildPath = new File(dstPath, childName);
if (child.isDirectory()) {
createReposDir(dstChildPath);
createRepositoryLayout(child, dstChildPath);
} else if (child.isFile()) {
SVNFileUtil.copyFile(child, dstChildPath, true);
}
}
}
private void createReposDir(File dir) throws SVNException {
if (dir.exists()) {
File[] dstChildren = dir.listFiles();
if (dstChildren.length > 0) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.DIR_NOT_EMPTY,
"''{0}'' exists and is non-empty", dir);
SVNErrorManager.error(err, SVNLogType.FSFS);
}
} else {
dir.mkdirs();
}
}
private void createDBLock(File dstPath) throws SVNException {
try {
SVNFileUtil.createFile(new File(dstPath, FSFS.DB_LOCK_FILE), FSFS.PRE_12_COMPAT_UNNEEDED_FILE_CONTENTS,
"US-ASCII");
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage().wrap("Creating db lock file");
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
private void createDBLogsLock(File dstPath) throws SVNException {
try {
SVNFileUtil.createFile(new File(dstPath, FSFS.DB_LOGS_LOCK_FILE), FSFS.PRE_12_COMPAT_UNNEEDED_FILE_CONTENTS,
"US-ASCII");
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage().wrap("Creating db logs lock file");
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
private void hotCopy(FSFS srcOwner, FSFS dstOwner) throws SVNException {
int format = srcOwner.readDBFormat();
FSRepositoryUtil.checkReposDBFormat(format);
SVNFileUtil.copyFile(srcOwner.getCurrentFile(), dstOwner.getCurrentFile(), true);
SVNFileUtil.copyFile(srcOwner.getUUIDFile(), dstOwner.getUUIDFile(), true);
long minUnpackedRevision = 0;
if (format >= FSFS.MIN_PACKED_FORMAT) {
SVNFileUtil.copyFile(srcOwner.getMinUnpackedRevFile(), dstOwner.getMinUnpackedRevFile(), true);
minUnpackedRevision = srcOwner.getMinUnpackedRev();
}
long youngestRev = dstOwner.getYoungestRevision();
File dstRevsDir = dstOwner.getDBRevsDir();
dstRevsDir.mkdirs();
long maxFilesPerDirectory = srcOwner.getMaxFilesPerDirectory();
long rev = 0;
for (; rev < minUnpackedRevision; rev += srcOwner.getMaxFilesPerDirectory()) {
long packedShard = rev / maxFilesPerDirectory;
SVNFileUtil.copyDirectory(srcOwner.getPackDir(packedShard), dstOwner.getPackDir(packedShard), false, null);
}
SVNErrorManager.assertionFailure(rev == minUnpackedRevision, "expected minimal unpacked revision " + String.valueOf(minUnpackedRevision) + ", but real revision is " +
String.valueOf(rev), SVNLogType.FSFS);
for (; rev <= youngestRev; rev++) {
File dstDir = dstRevsDir;
if (maxFilesPerDirectory > 0) {
String shard = String.valueOf(rev / maxFilesPerDirectory);
dstDir = new File(dstRevsDir, shard);
}
SVNFileUtil.copyFile(srcOwner.getRevisionFile(rev), new File(dstDir, String.valueOf(rev)), true);
}
long min_unpacked_revprop = 0;
/* Copy the min unpacked revprop file, and read its value. */
if (format >= FSFS.MIN_PACKED_REVPROP_FORMAT)
{
min_unpacked_revprop = srcOwner.getMinUnpackedRevProp();
SVNFileUtil.copyFile(srcOwner.getMinUnpackedRevPropPath(), dstOwner.getMinUnpackedRevPropPath(),true);
final File srcRevPropDb = srcOwner.getRevisionPropertiesDbPath();
final File dstRevPropDb = dstOwner.getRevisionPropertiesDbPath();
final SVNSqlJetDb revPropDb = SVNSqlJetDb.open(
srcOwner.getRevisionPropertiesDbPath(), SVNSqlJetDb.Mode.ReadWrite);
try{
SVNException e = (SVNException) revPropDb.getDb().runReadTransaction(new ISqlJetTransaction() {
public Object run(SqlJetDb db) throws SqlJetException {
try {
SVNFileUtil.copyFile(srcRevPropDb,
dstRevPropDb,true);
} catch (SVNException e) {
return e;
}
return null;
}
});
if(e!=null){
throw e;
}
} catch (SqlJetException e) {
SVNErrorMessage err = SVNErrorMessage.create( SVNErrorCode.SQLITE_ERROR, e );
SVNErrorManager.error(err, SVNLogType.FSFS);
} finally {
revPropDb.close();
}
}
File dstRevPropsDir = dstOwner.getRevisionPropertiesRoot();
for (rev = min_unpacked_revprop; rev <= youngestRev; rev++) {
File dstDir = dstRevPropsDir;
if (maxFilesPerDirectory > 0) {
String shard = String.valueOf(rev / maxFilesPerDirectory);
dstDir = new File(dstRevPropsDir, shard);
}
SVNFileUtil.copyFile(srcOwner.getRevisionPropertiesFile(rev, false), new File(dstDir, String.valueOf(rev)),
true);
}
dstOwner.getTransactionsParentDir().mkdirs();
if (format >= FSFS.MIN_PROTOREVS_DIR_FORMAT) {
dstOwner.getTransactionProtoRevsDir().mkdirs();
}
File srcLocksDir = srcOwner.getDBLocksDir();
if (srcLocksDir.exists()) {
SVNFileUtil.copyDirectory(srcLocksDir, dstOwner.getDBLocksDir(), false, null);
}
File srcNodeOriginsDir = srcOwner.getNodeOriginsDir();
if (srcNodeOriginsDir.exists()) {
SVNFileUtil.copyDirectory(srcNodeOriginsDir, dstOwner.getNodeOriginsDir(), false, null);
}
if (format >= FSFS.MIN_CURRENT_TXN_FORMAT) {
SVNFileUtil.copyFile(srcOwner.getTransactionCurrentFile(), dstOwner.getTransactionCurrentFile(), true);
}
dstOwner.writeDBFormat(format, maxFilesPerDirectory, false);
}
}