0001: /*
0002: * ====================================================================
0003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
0004: *
0005: * This software is licensed as described in the file COPYING, which
0006: * you should have received as part of this distribution. The terms
0007: * are also available at http://svnkit.com/license.html
0008: * If newer versions of this license are posted there, you may use a
0009: * newer version instead, at your option.
0010: * ====================================================================
0011: */
0012: package org.tmatesoft.svn.core.internal.io.fs;
0013:
0014: import java.io.File;
0015: import java.io.FileNotFoundException;
0016: import java.io.IOException;
0017: import java.io.OutputStream;
0018: import java.security.MessageDigest;
0019: import java.security.NoSuchAlgorithmException;
0020: import java.util.ArrayList;
0021: import java.util.Collection;
0022: import java.util.Collections;
0023: import java.util.Date;
0024: import java.util.HashMap;
0025: import java.util.Iterator;
0026: import java.util.LinkedList;
0027: import java.util.Map;
0028:
0029: import org.tmatesoft.svn.core.SVNErrorCode;
0030: import org.tmatesoft.svn.core.SVNErrorMessage;
0031: import org.tmatesoft.svn.core.SVNException;
0032: import org.tmatesoft.svn.core.SVNLock;
0033: import org.tmatesoft.svn.core.SVNNodeKind;
0034: import org.tmatesoft.svn.core.SVNProperty;
0035: import org.tmatesoft.svn.core.SVNRevisionProperty;
0036: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0037: import org.tmatesoft.svn.core.internal.util.SVNTimeUtil;
0038: import org.tmatesoft.svn.core.internal.util.SVNUUIDGenerator;
0039: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0040: import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
0041: import org.tmatesoft.svn.core.internal.wc.SVNFileType;
0042: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0043: import org.tmatesoft.svn.core.internal.wc.SVNProperties;
0044: import org.tmatesoft.svn.core.io.ISVNLockHandler;
0045:
0046: /**
0047: * @version 1.1.1
0048: * @author TMate Software Ltd.
0049: */
0050: public class FSFS {
0051: public static final String SVN_REPOS_DB_DIR = "db";
0052: public static final String TXN_PATH_EXT = ".txn";
0053: public static final String TXN_PATH_EXT_CHILDREN = ".children";
0054: public static final String PATH_PREFIX_NODE = "node.";
0055: public static final String TXN_PATH_EXT_PROPS = ".props";
0056: public static final int DIGEST_SUBDIR_LEN = 3;
0057: public static final String SVN_OPAQUE_LOCK_TOKEN = "opaquelocktoken:";
0058: public static final String TXN_PATH_REV = "rev";
0059: public static final String PATH_LOCK_KEY = "path";
0060: public static final String CHILDREN_LOCK_KEY = "children";
0061: public static final String TOKEN_LOCK_KEY = "token";
0062: public static final String OWNER_LOCK_KEY = "owner";
0063: public static final String IS_DAV_COMMENT_LOCK_KEY = "is_dav_comment";
0064: public static final String CREATION_DATE_LOCK_KEY = "creation_date";
0065: public static final String EXPIRATION_DATE_LOCK_KEY = "expiration_date";
0066: public static final String COMMENT_LOCK_KEY = "comment";
0067:
0068: private static final int REPOSITORY_FORMAT = 5;
0069: private static final int DB_FORMAT = 2;
0070: private static final String DB_TYPE = "fsfs";
0071:
0072: private int myDBFormat;
0073: private int myReposFormat;
0074:
0075: private String myUUID;
0076:
0077: private File myRepositoryRoot;
0078: private File myRevisionsRoot;
0079: private File myRevisionPropertiesRoot;
0080: private File myTransactionsRoot;
0081: private File myLocksRoot;
0082: private File myDBRoot;
0083: private File myWriteLockFile;
0084: private File myCurrentFile;
0085:
0086: public FSFS(File repositoryRoot) {
0087: myRepositoryRoot = repositoryRoot;
0088: myDBRoot = new File(myRepositoryRoot, "db");
0089: myRevisionsRoot = new File(myDBRoot, "revs");
0090: myRevisionPropertiesRoot = new File(myDBRoot, "revprops");
0091: myTransactionsRoot = new File(myDBRoot, "transactions");
0092: myWriteLockFile = new File(myDBRoot, "write-lock");
0093: myLocksRoot = new File(myDBRoot, "locks");
0094: }
0095:
0096: public int getDBFormat() {
0097: return myDBFormat;
0098: }
0099:
0100: public int getReposFormat() {
0101: return myReposFormat;
0102: }
0103:
0104: public void open() throws SVNException {
0105: // repo format /root/format
0106: FSFile formatFile = new FSFile(new File(myRepositoryRoot,
0107: "format"));
0108: int format = -1;
0109: try {
0110: format = formatFile.readInt();
0111: } finally {
0112: formatFile.close();
0113: }
0114: if (format > REPOSITORY_FORMAT) {
0115: SVNErrorMessage err = SVNErrorMessage
0116: .create(
0117: SVNErrorCode.REPOS_UNSUPPORTED_VERSION,
0118: "Expected format ''{0,number,integer}'' of repository; found format ''{1,number,integer}''",
0119: new Object[] {
0120: new Integer(REPOSITORY_FORMAT),
0121: new Integer(format) });
0122: SVNErrorManager.error(err);
0123: }
0124: myReposFormat = format;
0125: // fs format /root/db/format
0126: formatFile = new FSFile(new File(myDBRoot, "format"));
0127: try {
0128: format = formatFile.readInt();
0129: } catch (SVNException svne) {
0130: if (svne.getCause() instanceof FileNotFoundException) {
0131: format = DB_FORMAT;
0132: }
0133: } finally {
0134: formatFile.close();
0135: }
0136: if (format > DB_FORMAT) {
0137: SVNErrorMessage err = SVNErrorMessage
0138: .create(
0139: SVNErrorCode.FS_UNSUPPORTED_FORMAT,
0140: "Expected FS format ''{0,number,integer}''; found format ''{1,number,integer}''",
0141: new Object[] { new Integer(DB_FORMAT),
0142: new Integer(format) });
0143: SVNErrorManager.error(err);
0144: }
0145: myDBFormat = format;
0146:
0147: // fs type /root/db/fs-type
0148: formatFile = new FSFile(new File(myDBRoot, "fs-type"));
0149: String fsType = null;
0150: try {
0151: fsType = formatFile.readLine(128);
0152: } finally {
0153: formatFile.close();
0154: }
0155: if (!DB_TYPE.equals(fsType)) {
0156: SVNErrorMessage err = SVNErrorMessage.create(
0157: SVNErrorCode.FS_UNKNOWN_FS_TYPE,
0158: "Unsupported fs type ''{0}''", fsType);
0159: SVNErrorManager.error(err);
0160: }
0161:
0162: File dbCurrentFile = getCurrentFile();
0163: if (!(dbCurrentFile.exists() && dbCurrentFile.canRead())) {
0164: SVNErrorMessage err = SVNErrorMessage.create(
0165: SVNErrorCode.IO_ERROR, "Can''t open file ''{0}''",
0166: dbCurrentFile);
0167: SVNErrorManager.error(err);
0168: }
0169:
0170: }
0171:
0172: public String getUUID() throws SVNException {
0173: if (myUUID == null) {
0174: // uuid
0175: FSFile formatFile = new FSFile(new File(myDBRoot, "uuid"));
0176: try {
0177: myUUID = formatFile.readLine(38);
0178: } finally {
0179: formatFile.close();
0180: }
0181: }
0182:
0183: return myUUID;
0184: }
0185:
0186: public File getWriteLockFile() {
0187: return myWriteLockFile;
0188: }
0189:
0190: public long getDatedRevision(Date date) throws SVNException {
0191: long latest = getYoungestRevision();
0192: long top = latest;
0193: long bottom = 0;
0194: long middle;
0195: Date currentTime = null;
0196:
0197: while (bottom <= top) {
0198: middle = (top + bottom) / 2;
0199: currentTime = getRevisionTime(middle);
0200: if (currentTime.compareTo(date) > 0) {
0201: if ((middle - 1) < 0) {
0202: return 0;
0203: }
0204: Date prevTime = getRevisionTime(middle - 1);
0205: if (prevTime.compareTo(date) < 0) {
0206: return middle - 1;
0207: }
0208: top = middle - 1;
0209: } else if (currentTime.compareTo(date) < 0) {
0210: if ((middle + 1) > latest) {
0211: return latest;
0212: }
0213: Date nextTime = getRevisionTime(middle + 1);
0214: if (nextTime.compareTo(date) > 0) {
0215: return middle;
0216: }
0217: bottom = middle + 1;
0218: } else {
0219: return middle;
0220: }
0221: }
0222: return 0;
0223:
0224: }
0225:
0226: public long getYoungestRevision() throws SVNException {
0227: FSFile file = new FSFile(getCurrentFile());
0228: try {
0229: String line = file.readLine(180);
0230: int spaceIndex = line.indexOf(' ');
0231: if (spaceIndex > 0) {
0232: return Long.parseLong(line.substring(0, spaceIndex));
0233: }
0234: } catch (NumberFormatException nfe) {
0235: //
0236: } finally {
0237: file.close();
0238: }
0239: SVNErrorMessage err = SVNErrorMessage.create(
0240: SVNErrorCode.FS_CORRUPT,
0241: "Can''t parse revision number in file ''{0}''", file);
0242: SVNErrorManager.error(err);
0243: return -1;
0244: }
0245:
0246: public File getDBRoot() {
0247: return myDBRoot;
0248: }
0249:
0250: public Map getRevisionProperties(long revision) throws SVNException {
0251: FSFile file = new FSFile(getRevisionPropertiesFile(revision));
0252: try {
0253: return file.readProperties(false);
0254: } finally {
0255: file.close();
0256: }
0257: }
0258:
0259: public FSRevisionRoot createRevisionRoot(long revision) {
0260: return new FSRevisionRoot(this , revision);
0261: }
0262:
0263: public FSTransactionRoot createTransactionRoot(String txnId)
0264: throws SVNException {
0265: Map txnProps = getTransactionProperties(txnId);
0266: int flags = 0;
0267: if (txnProps.get(SVNProperty.TXN_CHECK_OUT_OF_DATENESS) != null) {
0268: flags |= FSTransactionRoot.SVN_FS_TXN_CHECK_OUT_OF_DATENESS;
0269: }
0270: if (txnProps.get(SVNProperty.TXN_CHECK_LOCKS) != null) {
0271: flags |= FSTransactionRoot.SVN_FS_TXN_CHECK_LOCKS;
0272: }
0273:
0274: return new FSTransactionRoot(this , txnId, flags);
0275: }
0276:
0277: public FSTransactionInfo openTxn(String txnName)
0278: throws SVNException {
0279: SVNFileType kind = SVNFileType
0280: .getType(getTransactionDir(txnName));
0281: if (kind != SVNFileType.DIRECTORY) {
0282: SVNErrorMessage err = SVNErrorMessage.create(
0283: SVNErrorCode.FS_NO_SUCH_TRANSACTION,
0284: "No such transaction");
0285: SVNErrorManager.error(err);
0286: }
0287:
0288: FSTransactionRoot txnRoot = new FSTransactionRoot(this ,
0289: txnName, 0);
0290: FSTransactionInfo localTxn = txnRoot.getTxn();
0291: return new FSTransactionInfo(localTxn.getBaseRevision(),
0292: txnName);
0293: }
0294:
0295: public FSRevisionNode getRevisionNode(FSID id) throws SVNException {
0296: FSFile revisionFile = null;
0297:
0298: if (id.isTxn()) {
0299: File file = new File(getTransactionDir(id.getTxnID()),
0300: PATH_PREFIX_NODE + id.getNodeID() + "."
0301: + id.getCopyID());
0302: revisionFile = new FSFile(file);
0303: } else {
0304: revisionFile = getRevisionFile(id.getRevision());
0305: revisionFile.seek(id.getOffset());
0306: }
0307:
0308: Map headers = null;
0309: try {
0310: headers = revisionFile.readHeader();
0311: } finally {
0312: revisionFile.close();
0313: }
0314:
0315: FSRevisionNode node = FSRevisionNode.fromMap(headers);
0316: if (node.isFreshTxnRoot()) {
0317: node.setFreshRootPredecessorId(node.getPredecessorId());
0318: }
0319: return node;
0320: }
0321:
0322: public Map getDirContents(FSRevisionNode revNode)
0323: throws SVNException {
0324: if (revNode.getTextRepresentation() != null
0325: && revNode.getTextRepresentation().isTxn()) {
0326: FSFile childrenFile = getTransactionRevisionNodeChildrenFile(revNode
0327: .getId());
0328: Map entries = null;
0329: try {
0330: Map rawEntries = childrenFile.readProperties(false);
0331: rawEntries.putAll(childrenFile.readProperties(true));
0332:
0333: Object[] keys = rawEntries.keySet().toArray();
0334: for (int i = 0; i < keys.length; i++) {
0335: if (rawEntries.get(keys[i]) == null) {
0336: rawEntries.remove(keys[i]);
0337: }
0338: }
0339:
0340: entries = parsePlainRepresentation(rawEntries, true);
0341: } finally {
0342: childrenFile.close();
0343: }
0344: return entries;
0345: } else if (revNode.getTextRepresentation() != null) {
0346: FSRepresentation textRep = revNode.getTextRepresentation();
0347: FSFile revisionFile = null;
0348:
0349: try {
0350: revisionFile = openAndSeekRepresentation(textRep);
0351: String repHeader = revisionFile.readLine(160);
0352:
0353: if (!"PLAIN".equals(repHeader)) {
0354: SVNErrorMessage err = SVNErrorMessage.create(
0355: SVNErrorCode.FS_CORRUPT,
0356: "Malformed representation header");
0357: SVNErrorManager.error(err);
0358: }
0359:
0360: revisionFile.resetDigest();
0361: Map rawEntries = revisionFile.readProperties(false);
0362: String checksum = revisionFile.digest();
0363:
0364: if (!checksum.equals(textRep.getHexDigest())) {
0365: SVNErrorMessage err = SVNErrorMessage
0366: .create(
0367: SVNErrorCode.FS_CORRUPT,
0368: "Checksum mismatch while reading representation:\n expected: {0}\n actual: {1}",
0369: new Object[] { checksum,
0370: textRep.getHexDigest() });
0371: SVNErrorManager.error(err);
0372: }
0373:
0374: return parsePlainRepresentation(rawEntries, false);
0375: } finally {
0376: if (revisionFile != null) {
0377: revisionFile.close();
0378: }
0379: }
0380: }
0381: return new HashMap();// returns an empty map, must not be null!!
0382: }
0383:
0384: public Map getProperties(FSRevisionNode revNode)
0385: throws SVNException {
0386: if (revNode.getPropsRepresentation() != null
0387: && revNode.getPropsRepresentation().isTxn()) {
0388: FSFile propsFile = null;
0389: try {
0390: propsFile = getTransactionRevisionNodePropertiesFile(revNode
0391: .getId());
0392: return propsFile.readProperties(false);
0393: } finally {
0394: if (propsFile != null) {
0395: propsFile.close();
0396: }
0397: }
0398: } else if (revNode.getPropsRepresentation() != null) {
0399: FSRepresentation propsRep = revNode
0400: .getPropsRepresentation();
0401: FSFile revisionFile = null;
0402:
0403: try {
0404: revisionFile = openAndSeekRepresentation(propsRep);
0405: String repHeader = revisionFile.readLine(160);
0406:
0407: if (!"PLAIN".equals(repHeader)) {
0408: SVNErrorMessage err = SVNErrorMessage.create(
0409: SVNErrorCode.FS_CORRUPT,
0410: "Malformed representation header");
0411: SVNErrorManager.error(err);
0412: }
0413:
0414: revisionFile.resetDigest();
0415: Map props = revisionFile.readProperties(false);
0416: String checksum = revisionFile.digest();
0417:
0418: if (!checksum.equals(propsRep.getHexDigest())) {
0419: SVNErrorMessage err = SVNErrorMessage
0420: .create(
0421: SVNErrorCode.FS_CORRUPT,
0422: "Checksum mismatch while reading representation:\n expected: {0}\n actual: {1}",
0423: new Object[] { checksum,
0424: propsRep.getHexDigest() });
0425: SVNErrorManager.error(err);
0426: }
0427: return props;
0428: } finally {
0429: if (revisionFile != null) {
0430: revisionFile.close();
0431: }
0432: }
0433: }
0434: return new HashMap();// no properties? return an empty map
0435: }
0436:
0437: public String[] getNextRevisionIDs() throws SVNException {
0438: String[] ids = new String[2];
0439: FSFile currentFile = new FSFile(getCurrentFile());
0440: String idsLine = null;
0441:
0442: try {
0443: idsLine = currentFile.readLine(80);
0444: } finally {
0445: currentFile.close();
0446: }
0447:
0448: if (idsLine == null || idsLine.length() == 0) {
0449: SVNErrorMessage err = SVNErrorMessage.create(
0450: SVNErrorCode.FS_CORRUPT, "Corrupt current file");
0451: SVNErrorManager.error(err);
0452: }
0453:
0454: int spaceInd = idsLine.indexOf(' ');
0455: if (spaceInd == -1) {
0456: SVNErrorMessage err = SVNErrorMessage.create(
0457: SVNErrorCode.FS_CORRUPT, "Corrupt current file");
0458: SVNErrorManager.error(err);
0459: }
0460:
0461: idsLine = idsLine.substring(spaceInd + 1);
0462: spaceInd = idsLine.indexOf(' ');
0463: if (spaceInd == -1) {
0464: SVNErrorMessage err = SVNErrorMessage.create(
0465: SVNErrorCode.FS_CORRUPT, "Corrupt current file");
0466: SVNErrorManager.error(err);
0467: }
0468: String nodeID = idsLine.substring(0, spaceInd);
0469: String copyID = idsLine.substring(spaceInd + 1);
0470:
0471: ids[0] = nodeID;
0472: ids[1] = copyID;
0473: return ids;
0474: }
0475:
0476: public Map listTransactions() {
0477: Map result = new HashMap();
0478: File txnsDir = getTransactionsParentDir();
0479:
0480: File[] entries = SVNFileListUtil.listFiles(txnsDir);
0481: for (int i = 0; i < entries.length; i++) {
0482: File entry = entries[i];
0483: if (entry.getName().length() <= TXN_PATH_EXT.length()
0484: || !entry.getName().endsWith(TXN_PATH_EXT)) {
0485: continue;
0486: }
0487: String txnName = entry.getName().substring(0,
0488: entry.getName().lastIndexOf(TXN_PATH_EXT));
0489: result.put(txnName, entry);
0490: }
0491: return result;
0492: }
0493:
0494: public File getNewRevisionFile(long revision) throws SVNException {
0495: File revFile = new File(myRevisionsRoot, String
0496: .valueOf(revision));
0497: if (revFile.exists()) {
0498: SVNErrorMessage err = SVNErrorMessage
0499: .create(SVNErrorCode.FS_CONFLICT,
0500: "Revision already exists");
0501: SVNErrorManager.error(err);
0502: }
0503: return revFile;
0504: }
0505:
0506: public File getNewRevisionPropertiesFile(long revision)
0507: throws SVNException {
0508: File revPropsFile = new File(myRevisionPropertiesRoot, String
0509: .valueOf(revision));
0510: if (revPropsFile.exists()) {
0511: SVNErrorMessage err = SVNErrorMessage
0512: .create(SVNErrorCode.FS_CONFLICT,
0513: "Revision already exists");
0514: SVNErrorManager.error(err);
0515: }
0516: return revPropsFile;
0517: }
0518:
0519: public File getTransactionDir(String txnID) {
0520: return new File(getTransactionsParentDir(), txnID
0521: + TXN_PATH_EXT);
0522: }
0523:
0524: public File getTransactionsParentDir() {
0525: return myTransactionsRoot;
0526: }
0527:
0528: public void setUUID(String uuid) throws SVNException {
0529: File uuidFile = new File(myDBRoot, "uuid");
0530: File uniqueFile = SVNFileUtil.createUniqueFile(myDBRoot,
0531: "uuid", ".tmp");
0532: uuid += '\n';
0533:
0534: OutputStream uuidOS = null;
0535: try {
0536: uuidOS = SVNFileUtil.openFileForWriting(uniqueFile);
0537: uuidOS.write(uuid.getBytes("US-ASCII"));
0538: } catch (IOException e) {
0539: SVNErrorMessage err = SVNErrorMessage.create(
0540: SVNErrorCode.IO_ERROR,
0541: "Error writing repository UUID to ''{0}''",
0542: uuidFile);
0543: err.setChildErrorMessage(SVNErrorMessage.create(
0544: SVNErrorCode.IO_ERROR, e.getLocalizedMessage()));
0545: SVNErrorManager.error(err);
0546: } finally {
0547: SVNFileUtil.closeFile(uuidOS);
0548: }
0549: SVNFileUtil.rename(uniqueFile, uuidFile);
0550: }
0551:
0552: public File getRevisionPropertiesFile(long revision)
0553: throws SVNException {
0554: File file = new File(myRevisionPropertiesRoot, String
0555: .valueOf(revision));
0556: if (!file.exists()) {
0557: SVNErrorMessage err = SVNErrorMessage.create(
0558: SVNErrorCode.FS_NO_SUCH_REVISION,
0559: "No such revision {0,number,integer}", new Long(
0560: revision));
0561: SVNErrorManager.error(err);
0562: }
0563: return file;
0564: }
0565:
0566: public File getRepositoryRoot() {
0567: return myRepositoryRoot;
0568: }
0569:
0570: public FSFile openAndSeekRepresentation(FSRepresentation rep)
0571: throws SVNException {
0572: if (!rep.isTxn()) {
0573: return openAndSeekRevision(rep.getRevision(), rep
0574: .getOffset());
0575: }
0576: return openAndSeekTransaction(rep);
0577: }
0578:
0579: public File getNextIDsFile(String txnID) {
0580: return new File(getTransactionDir(txnID), "next-ids");
0581: }
0582:
0583: public void writeNextIDs(String txnID, String nodeID, String copyID)
0584: throws SVNException {
0585: OutputStream nextIdsFile = null;
0586: try {
0587: nextIdsFile = SVNFileUtil
0588: .openFileForWriting(getNextIDsFile(txnID));
0589: String ids = nodeID + " " + copyID + "\n";
0590: nextIdsFile.write(ids.getBytes("UTF-8"));
0591: } catch (IOException ioe) {
0592: SVNErrorMessage err = SVNErrorMessage.create(
0593: SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage());
0594: SVNErrorManager.error(err, ioe);
0595: } finally {
0596: SVNFileUtil.closeFile(nextIdsFile);
0597: }
0598: }
0599:
0600: public void setTransactionProperty(String txnID,
0601: String propertyName, String propertyValue)
0602: throws SVNException {
0603: SVNProperties revProps = new SVNProperties(
0604: getTransactionPropertiesFile(txnID), null);
0605: revProps.setPropertyValue(propertyName, propertyValue);
0606: }
0607:
0608: public void setRevisionProperty(long revision, String propertyName,
0609: String propertyValue) throws SVNException {
0610: SVNProperties revProps = new SVNProperties(
0611: getRevisionPropertiesFile(revision), null);
0612: revProps.setPropertyValue(propertyName, propertyValue);
0613: }
0614:
0615: public Map getTransactionProperties(String txnID)
0616: throws SVNException {
0617: FSFile txnPropsFile = new FSFile(
0618: getTransactionPropertiesFile(txnID));
0619: try {
0620: return txnPropsFile.readProperties(false);
0621: } finally {
0622: txnPropsFile.close();
0623: }
0624: }
0625:
0626: public File getTransactionPropertiesFile(String txnID) {
0627: return new File(getTransactionDir(txnID), "props");
0628: }
0629:
0630: public void createNewTxnNodeRevisionFromRevision(String txnID,
0631: FSRevisionNode sourceNode) throws SVNException {
0632: if (sourceNode.getId().isTxn()) {
0633: SVNErrorMessage err = SVNErrorMessage.create(
0634: SVNErrorCode.FS_CORRUPT,
0635: "Copying from transactions not allowed");
0636: SVNErrorManager.error(err);
0637: }
0638: FSRevisionNode revNode = FSRevisionNode
0639: .dumpRevisionNode(sourceNode);
0640: revNode.setPredecessorId(sourceNode.getId());
0641: revNode.setCount(revNode.getCount() + 1);
0642: revNode.setCopyFromPath(null);
0643: revNode.setIsFreshTxnRoot(true);
0644: revNode.setCopyFromRevision(FSRepository.SVN_INVALID_REVNUM);
0645: revNode.setId(FSID.createTxnId(sourceNode.getId().getNodeID(),
0646: sourceNode.getId().getCopyID(), txnID));
0647: putTxnRevisionNode(revNode.getId(), revNode);
0648: }
0649:
0650: public void putTxnRevisionNode(FSID id, FSRevisionNode revNode)
0651: throws SVNException {
0652: if (!id.isTxn()) {
0653: SVNErrorMessage err = SVNErrorMessage.create(
0654: SVNErrorCode.FS_CORRUPT,
0655: "Attempted to write to non-transaction");
0656: SVNErrorManager.error(err);
0657: }
0658: OutputStream revNodeFile = null;
0659: try {
0660: revNodeFile = SVNFileUtil
0661: .openFileForWriting(getTransactionRevNodeFile(id));
0662: writeTxnNodeRevision(revNodeFile, revNode);
0663: } catch (IOException ioe) {
0664: SVNErrorMessage err = SVNErrorMessage.create(
0665: SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage());
0666: SVNErrorManager.error(err, ioe);
0667: } finally {
0668: SVNFileUtil.closeFile(revNodeFile);
0669: }
0670: }
0671:
0672: public File getTransactionRevNodeFile(FSID id) {
0673: return new File(getTransactionDir(id.getTxnID()),
0674: PATH_PREFIX_NODE + id.getNodeID() + "."
0675: + id.getCopyID());
0676: }
0677:
0678: public void writeTxnNodeRevision(OutputStream revNodeFile,
0679: FSRevisionNode revNode) throws IOException {
0680: String id = FSRevisionNode.HEADER_ID + ": " + revNode.getId()
0681: + "\n";
0682: revNodeFile.write(id.getBytes("UTF-8"));
0683: String type = FSRevisionNode.HEADER_TYPE + ": "
0684: + revNode.getType() + "\n";
0685: revNodeFile.write(type.getBytes("UTF-8"));
0686:
0687: if (revNode.getPredecessorId() != null) {
0688: String predId = FSRevisionNode.HEADER_PRED + ": "
0689: + revNode.getPredecessorId() + "\n";
0690: revNodeFile.write(predId.getBytes("UTF-8"));
0691: }
0692:
0693: String count = FSRevisionNode.HEADER_COUNT + ": "
0694: + revNode.getCount() + "\n";
0695: revNodeFile.write(count.getBytes("UTF-8"));
0696:
0697: if (revNode.getTextRepresentation() != null) {
0698: String textRepresentation = FSRevisionNode.HEADER_TEXT
0699: + ": "
0700: + (revNode.getTextRepresentation().getTxnId() != null
0701: && revNode.getType() == SVNNodeKind.DIR ? "-1"
0702: : revNode.getTextRepresentation()
0703: .toString()) + "\n";
0704: revNodeFile.write(textRepresentation.getBytes("UTF-8"));
0705: }
0706:
0707: if (revNode.getPropsRepresentation() != null) {
0708: String propsRepresentation = FSRevisionNode.HEADER_PROPS
0709: + ": "
0710: + (revNode.getPropsRepresentation().getTxnId() != null ? "-1"
0711: : revNode.getPropsRepresentation()
0712: .toString()) + "\n";
0713: revNodeFile.write(propsRepresentation.getBytes("UTF-8"));
0714: }
0715:
0716: String cpath = FSRevisionNode.HEADER_CPATH + ": "
0717: + revNode.getCreatedPath() + "\n";
0718: revNodeFile.write(cpath.getBytes("UTF-8"));
0719:
0720: if (revNode.getCopyFromPath() != null) {
0721: String copyFromPath = FSRevisionNode.HEADER_COPYFROM + ": "
0722: + revNode.getCopyFromRevision() + " "
0723: + revNode.getCopyFromPath() + "\n";
0724: revNodeFile.write(copyFromPath.getBytes("UTF-8"));
0725: }
0726:
0727: if (revNode.getCopyRootRevision() != revNode.getId()
0728: .getRevision()
0729: || !revNode.getCopyRootPath().equals(
0730: revNode.getCreatedPath())) {
0731: String copyroot = FSRevisionNode.HEADER_COPYROOT + ": "
0732: + revNode.getCopyRootRevision() + " "
0733: + revNode.getCopyRootPath() + "\n";
0734: revNodeFile.write(copyroot.getBytes("UTF-8"));
0735: }
0736:
0737: if (revNode.isFreshTxnRoot()) {
0738: String isFreshRootStr = FSRevisionNode.HEADER_IS_FRESH_TXN_ROOT
0739: + ": y\n";
0740: revNodeFile.write(isFreshRootStr.getBytes("UTF-8"));
0741: }
0742:
0743: revNodeFile.write("\n".getBytes("UTF-8"));
0744: }
0745:
0746: public SVNLock getLock(String repositoryPath, boolean haveWriteLock)
0747: throws SVNException {
0748: SVNLock lock = fetchLockFromDigestFile(null, repositoryPath,
0749: null);
0750:
0751: if (lock == null) {
0752: SVNErrorManager.error(FSErrors.errorNoSuchLock(
0753: repositoryPath, this ));
0754: }
0755:
0756: Date current = new Date(System.currentTimeMillis());
0757:
0758: if (lock.getExpirationDate() != null
0759: && current.compareTo(lock.getExpirationDate()) > 0) {
0760: if (haveWriteLock) {
0761: deleteLock(lock);
0762: }
0763: SVNErrorManager.error(FSErrors.errorLockExpired(lock
0764: .getID(), this ));
0765: }
0766: return lock;
0767: }
0768:
0769: public void deleteLock(SVNLock lock) throws SVNException {
0770: String reposPath = lock.getPath();
0771: String childToKill = null;
0772: Collection children = new ArrayList();
0773: while (true) {
0774: fetchLockFromDigestFile(null, reposPath, children);
0775: if (childToKill != null) {
0776: children.remove(childToKill);
0777: }
0778:
0779: if (children.size() == 0) {
0780: childToKill = getDigestFromRepositoryPath(reposPath);
0781: File digestFile = getDigestFileFromRepositoryPath(reposPath);
0782: SVNFileUtil.deleteFile(digestFile);
0783: } else {
0784: writeDigestLockFile(null, children, reposPath);
0785: childToKill = null;
0786: }
0787:
0788: if ("/".equals(reposPath)) {
0789: break;
0790: }
0791:
0792: reposPath = SVNPathUtil.removeTail(reposPath);
0793:
0794: if ("".equals(reposPath)) {
0795: reposPath = "/";
0796: }
0797: children.clear();
0798: }
0799: }
0800:
0801: public void walkDigestFiles(File digestFile,
0802: ISVNLockHandler getLocksHandler, boolean haveWriteLock)
0803: throws SVNException {
0804: Collection children = new LinkedList();
0805: SVNLock lock = fetchLockFromDigestFile(digestFile, null,
0806: children);
0807:
0808: if (lock != null) {
0809: Date current = new Date(System.currentTimeMillis());
0810: if (lock.getExpirationDate() == null
0811: || current.compareTo(lock.getExpirationDate()) < 0) {
0812: getLocksHandler.handleLock(null, lock, null);
0813: } else if (haveWriteLock) {
0814: deleteLock(lock);
0815: }
0816: }
0817:
0818: if (children.isEmpty()) {
0819: return;
0820: }
0821:
0822: for (Iterator entries = children.iterator(); entries.hasNext();) {
0823: String digestName = (String) entries.next();
0824: File parent = new File(myLocksRoot, digestName.substring(0,
0825: FSFS.DIGEST_SUBDIR_LEN));
0826: File childDigestFile = new File(parent, digestName);
0827: walkDigestFiles(childDigestFile, getLocksHandler,
0828: haveWriteLock);
0829: }
0830: }
0831:
0832: public SVNLock getLockHelper(String repositoryPath,
0833: boolean haveWriteLock) throws SVNException {
0834: SVNLock lock = null;
0835: try {
0836: lock = getLock(repositoryPath, haveWriteLock);
0837: } catch (SVNException svne) {
0838: if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.FS_NO_SUCH_LOCK
0839: || svne.getErrorMessage().getErrorCode() == SVNErrorCode.FS_LOCK_EXPIRED) {
0840: return null;
0841: }
0842: throw svne;
0843: }
0844: return lock;
0845: }
0846:
0847: public SVNLock fetchLockFromDigestFile(File digestFile,
0848: String repositoryPath, Collection children)
0849: throws SVNException {
0850: File digestLockFile = digestFile == null ? getDigestFileFromRepositoryPath(repositoryPath)
0851: : digestFile;
0852: Map lockProps = null;
0853:
0854: if (digestLockFile.exists()) {
0855: FSFile reader = new FSFile(digestLockFile);
0856: try {
0857: lockProps = reader.readProperties(false);
0858: } catch (SVNException svne) {
0859: SVNErrorMessage err = svne.getErrorMessage().wrap(
0860: "Can't parse lock/entries hashfile ''{0}''",
0861: digestLockFile);
0862: SVNErrorManager.error(err);
0863: } finally {
0864: reader.close();
0865: }
0866: } else {
0867: lockProps = Collections.EMPTY_MAP;
0868: }
0869:
0870: SVNLock lock = null;
0871: String lockPath = (String) lockProps.get(FSFS.PATH_LOCK_KEY);
0872: if (lockPath != null) {
0873: String lockToken = (String) lockProps
0874: .get(FSFS.TOKEN_LOCK_KEY);
0875: if (lockToken == null) {
0876: SVNErrorManager.error(FSErrors.errorCorruptLockFile(
0877: lockPath, this ));
0878: }
0879: String lockOwner = (String) lockProps
0880: .get(FSFS.OWNER_LOCK_KEY);
0881: if (lockOwner == null) {
0882: SVNErrorManager.error(FSErrors.errorCorruptLockFile(
0883: lockPath, this ));
0884: }
0885: String davComment = (String) lockProps
0886: .get(FSFS.IS_DAV_COMMENT_LOCK_KEY);
0887: if (davComment == null) {
0888: SVNErrorManager.error(FSErrors.errorCorruptLockFile(
0889: lockPath, this ));
0890: }
0891: String creationTime = (String) lockProps
0892: .get(FSFS.CREATION_DATE_LOCK_KEY);
0893: if (creationTime == null) {
0894: SVNErrorManager.error(FSErrors.errorCorruptLockFile(
0895: lockPath, this ));
0896: }
0897: Date creationDate = SVNTimeUtil
0898: .parseDateString(creationTime);
0899: String expirationTime = (String) lockProps
0900: .get(FSFS.EXPIRATION_DATE_LOCK_KEY);
0901: Date expirationDate = null;
0902: if (expirationTime != null) {
0903: expirationDate = SVNTimeUtil
0904: .parseDateString(expirationTime);
0905: }
0906: String comment = (String) lockProps
0907: .get(FSFS.COMMENT_LOCK_KEY);
0908: lock = new SVNLock(lockPath, lockToken, lockOwner, comment,
0909: creationDate, expirationDate);
0910: }
0911:
0912: String childEntries = (String) lockProps
0913: .get(FSFS.CHILDREN_LOCK_KEY);
0914: if (children != null && childEntries != null) {
0915: String[] digests = childEntries.split("\n");
0916: for (int i = 0; i < digests.length; i++) {
0917: children.add(digests[i]);
0918: }
0919: }
0920: return lock;
0921: }
0922:
0923: public File getDigestFileFromRepositoryPath(String repositoryPath)
0924: throws SVNException {
0925: String digest = getDigestFromRepositoryPath(repositoryPath);
0926: File parent = new File(myLocksRoot, digest.substring(0,
0927: FSFS.DIGEST_SUBDIR_LEN));
0928: return new File(parent, digest);
0929: }
0930:
0931: public String getDigestFromRepositoryPath(String repositoryPath)
0932: throws SVNException {
0933: MessageDigest digestFromPath = null;
0934: try {
0935: digestFromPath = MessageDigest.getInstance("MD5");
0936: digestFromPath.update(repositoryPath.getBytes("UTF-8"));
0937: } catch (NoSuchAlgorithmException nsae) {
0938: SVNErrorMessage err = SVNErrorMessage.create(
0939: SVNErrorCode.IO_ERROR,
0940: "MD5 implementation not found: {0}", nsae
0941: .getLocalizedMessage());
0942: SVNErrorManager.error(err, nsae);
0943: } catch (IOException ioe) {
0944: SVNErrorMessage err = SVNErrorMessage.create(
0945: SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage());
0946: SVNErrorManager.error(err, ioe);
0947: }
0948: return SVNFileUtil.toHexDigest(digestFromPath);
0949: }
0950:
0951: public void unlockPath(String path, String token, String username,
0952: boolean breakLock, boolean enableHooks) throws SVNException {
0953: String[] paths = { path };
0954:
0955: if (!breakLock && username == null) {
0956: SVNErrorMessage err = SVNErrorMessage
0957: .create(
0958: SVNErrorCode.FS_NO_USER,
0959: "Cannot unlock path ''{0}'', no authenticated username available",
0960: path);
0961: SVNErrorManager.error(err);
0962: }
0963:
0964: if (enableHooks) {
0965: FSHooks.runPreUnlockHook(myRepositoryRoot, path, username);
0966: }
0967:
0968: FSWriteLock writeLock = FSWriteLock.getWriteLock(this );
0969: synchronized (writeLock) {
0970: try {
0971: writeLock.lock();
0972: unlock(path, token, username, breakLock);
0973: } finally {
0974: writeLock.unlock();
0975: FSWriteLock.realease(writeLock);
0976: }
0977: }
0978:
0979: if (enableHooks) {
0980: try {
0981: FSHooks.runPostUnlockHook(myRepositoryRoot, paths,
0982: username);
0983: } catch (SVNException svne) {
0984: SVNErrorMessage err = SVNErrorMessage
0985: .create(
0986: SVNErrorCode.REPOS_POST_UNLOCK_HOOK_FAILED,
0987: "Unlock succeeded, but post-unlock hook failed");
0988: err.setChildErrorMessage(svne.getErrorMessage());
0989: SVNErrorManager.error(err, svne);
0990: }
0991: }
0992: }
0993:
0994: public SVNLock lockPath(String path, String token, String username,
0995: String comment, Date expirationDate, long currentRevision,
0996: boolean stealLock) throws SVNException {
0997: String[] paths = { path };
0998:
0999: if (username == null) {
1000: SVNErrorMessage err = SVNErrorMessage
1001: .create(
1002: SVNErrorCode.FS_NO_USER,
1003: "Cannot lock path ''{0}'', no authenticated username available.",
1004: path);
1005: SVNErrorManager.error(err);
1006: }
1007:
1008: FSHooks.runPreLockHook(myRepositoryRoot, path, username);
1009: SVNLock lock = null;
1010:
1011: FSWriteLock writeLock = FSWriteLock.getWriteLock(this );
1012:
1013: synchronized (writeLock) {
1014: try {
1015: writeLock.lock();
1016: lock = lock(path, token, username, comment,
1017: expirationDate, currentRevision, stealLock);
1018: } finally {
1019: writeLock.unlock();
1020: FSWriteLock.realease(writeLock);
1021: }
1022: }
1023:
1024: try {
1025: FSHooks.runPostLockHook(myRepositoryRoot, paths, username);
1026: } catch (SVNException svne) {
1027: SVNErrorMessage err = SVNErrorMessage.create(
1028: SVNErrorCode.REPOS_POST_LOCK_HOOK_FAILED,
1029: "Lock succeeded, but post-lock hook failed");
1030: err.setChildErrorMessage(svne.getErrorMessage());
1031: SVNErrorManager.error(err, svne);
1032: }
1033: return lock;
1034: }
1035:
1036: public Map compoundMetaProperties(long revision)
1037: throws SVNException {
1038: Map metaProps = new HashMap();
1039: Map revProps = getRevisionProperties(revision);
1040: String author = (String) revProps
1041: .get(SVNRevisionProperty.AUTHOR);
1042: String date = (String) revProps.get(SVNRevisionProperty.DATE);
1043: String uuid = getUUID();
1044: String rev = String.valueOf(revision);
1045:
1046: metaProps.put(SVNProperty.LAST_AUTHOR, author);
1047: metaProps.put(SVNProperty.COMMITTED_DATE, date);
1048: metaProps.put(SVNProperty.COMMITTED_REVISION, rev);
1049: metaProps.put(SVNProperty.UUID, uuid);
1050: return metaProps;
1051: }
1052:
1053: public static File findRepositoryRoot(File path) {
1054: if (path == null) {
1055: path = new File("");
1056: }
1057: File rootPath = path;
1058: while (!isRepositoryRoot(rootPath)) {
1059: rootPath = rootPath.getParentFile();
1060: if (rootPath == null) {
1061: return null;
1062: }
1063: }
1064: return rootPath;
1065: }
1066:
1067: public static String findRepositoryRoot(String host, String path) {
1068: if (path == null) {
1069: path = "";
1070: }
1071:
1072: String testPath = host != null ? SVNPathUtil.append("\\\\"
1073: + host, path) : path;
1074: File rootPath = new File(testPath).getAbsoluteFile();
1075: while (!isRepositoryRoot(rootPath)) {
1076: if (rootPath.getParentFile() == null) {
1077: return null;
1078: }
1079: path = SVNPathUtil.removeTail(path);
1080: rootPath = rootPath.getParentFile();
1081: }
1082: while (path.endsWith("/")) {
1083: path = path.substring(0, path.length() - 1);
1084: }
1085: while (path.endsWith("\\")) {
1086: path = path.substring(0, path.length() - 1);
1087: }
1088: return path;
1089: }
1090:
1091: protected FSFile getTransactionRevisionPrototypeFile(String txnID) {
1092: File revFile = new File(getTransactionDir(txnID), TXN_PATH_REV);
1093: return new FSFile(revFile);
1094: }
1095:
1096: protected FSFile getTransactionChangesFile(String txnID) {
1097: File file = new File(getTransactionDir(txnID), "changes");
1098: return new FSFile(file);
1099: }
1100:
1101: protected FSFile getTransactionRevisionNodeChildrenFile(FSID txnID) {
1102: File childrenFile = new File(
1103: getTransactionDir(txnID.getTxnID()), PATH_PREFIX_NODE
1104: + txnID.getNodeID() + "." + txnID.getCopyID()
1105: + TXN_PATH_EXT_CHILDREN);
1106: return new FSFile(childrenFile);
1107: }
1108:
1109: protected FSFile getRevisionFile(long revision) throws SVNException {
1110: File revisionFile = new File(myRevisionsRoot, String
1111: .valueOf(revision));
1112: if (!revisionFile.exists()) {
1113: SVNErrorMessage err = SVNErrorMessage.create(
1114: SVNErrorCode.FS_NO_SUCH_REVISION,
1115: "No such revision {0,number,integer}", new Long(
1116: revision));
1117: SVNErrorManager.error(err);
1118: }
1119: return new FSFile(revisionFile);
1120: }
1121:
1122: protected FSFile getTransactionRevisionNodePropertiesFile(FSID id) {
1123: File revNodePropsFile = new File(getTransactionDir(id
1124: .getTxnID()), PATH_PREFIX_NODE + id.getNodeID() + "."
1125: + id.getCopyID() + TXN_PATH_EXT_PROPS);
1126: return new FSFile(revNodePropsFile);
1127: }
1128:
1129: protected File getCurrentFile() {
1130: if (myCurrentFile == null) {
1131: myCurrentFile = new File(myDBRoot, "current");
1132: }
1133: return myCurrentFile;
1134: }
1135:
1136: private void unlock(String path, String token, String username,
1137: boolean breakLock) throws SVNException {
1138: SVNLock lock = getLock(path, true);
1139: if (!breakLock) {
1140: if (token == null || !token.equals(lock.getID())) {
1141: SVNErrorManager.error(FSErrors.errorNoSuchLock(lock
1142: .getPath(), this ));
1143: }
1144: if (username == null || "".equals(username)) {
1145: SVNErrorManager.error(FSErrors.errorNoUser(this ));
1146: }
1147: if (!username.equals(lock.getOwner())) {
1148: SVNErrorManager.error(FSErrors.errorLockOwnerMismatch(
1149: username, lock.getOwner(), this ));
1150: }
1151: }
1152: deleteLock(lock);
1153: }
1154:
1155: private SVNLock lock(String path, String token, String username,
1156: String comment, Date expirationDate, long currentRevision,
1157: boolean stealLock) throws SVNException {
1158: long youngestRev = getYoungestRevision();
1159: FSRevisionRoot root = createRevisionRoot(youngestRev);
1160: SVNNodeKind kind = root.checkNodeKind(path);
1161:
1162: if (kind == SVNNodeKind.DIR) {
1163: SVNErrorManager.error(FSErrors.errorNotFile(path, this ));
1164: } else if (kind == SVNNodeKind.NONE) {
1165: SVNErrorMessage err = SVNErrorMessage
1166: .create(
1167: SVNErrorCode.FS_NOT_FOUND,
1168: "Path ''{0}'' doesn't exist in HEAD revision",
1169: path);
1170: SVNErrorManager.error(err);
1171: }
1172:
1173: if (username == null || "".equals(username)) {
1174: SVNErrorManager.error(FSErrors.errorNoUser(this ));
1175: }
1176:
1177: if (FSRepository.isValidRevision(currentRevision)) {
1178: FSRevisionNode node = root.getRevisionNode(path);
1179: long createdRev = node.getCreatedRevision();
1180: if (FSRepository.isInvalidRevision(createdRev)) {
1181: SVNErrorMessage err = SVNErrorMessage.create(
1182: SVNErrorCode.FS_OUT_OF_DATE,
1183: "Path ''{0}'' doesn't exist in HEAD revision",
1184: path);
1185: SVNErrorManager.error(err);
1186: }
1187: if (currentRevision < createdRev) {
1188: SVNErrorMessage err = SVNErrorMessage.create(
1189: SVNErrorCode.FS_OUT_OF_DATE,
1190: "Lock failed: newer version of ''{0}'' exists",
1191: path);
1192: SVNErrorManager.error(err);
1193: }
1194: }
1195:
1196: SVNLock existingLock = getLockHelper(path, true);
1197:
1198: if (existingLock != null) {
1199: if (!stealLock) {
1200: SVNErrorManager.error(FSErrors.errorPathAlreadyLocked(
1201: existingLock.getPath(),
1202: existingLock.getOwner(), this ));
1203: } else {
1204: deleteLock(existingLock);
1205: }
1206: }
1207:
1208: SVNLock lock = null;
1209: if (token == null) {
1210: String uuid = SVNUUIDGenerator.formatUUID(SVNUUIDGenerator
1211: .generateUUID());
1212: token = FSFS.SVN_OPAQUE_LOCK_TOKEN + uuid;
1213: lock = new SVNLock(path, token, username, comment,
1214: new Date(System.currentTimeMillis()),
1215: expirationDate);
1216: } else {
1217: lock = new SVNLock(path, token, username, comment,
1218: new Date(System.currentTimeMillis()),
1219: expirationDate);
1220: }
1221:
1222: setLock(lock);
1223: return lock;
1224: }
1225:
1226: private void setLock(SVNLock lock) throws SVNException {
1227: if (lock == null) {
1228: SVNErrorMessage err = SVNErrorMessage.create(
1229: SVNErrorCode.UNKNOWN,
1230: "FATAL error: attempted to set a null lock");
1231: SVNErrorManager.error(err);
1232: }
1233: String lastChild = "";
1234: String path = lock.getPath();
1235: Collection children = new ArrayList();
1236: while (true) {
1237: String digestFileName = getDigestFromRepositoryPath(path);
1238: SVNLock fetchedLock = fetchLockFromDigestFile(null, path,
1239: children);
1240:
1241: if (lock != null) {
1242: fetchedLock = lock;
1243: lock = null;
1244: lastChild = digestFileName;
1245: } else {
1246: if (!children.isEmpty() && children.contains(lastChild)) {
1247: break;
1248: }
1249: children.add(lastChild);
1250: }
1251:
1252: writeDigestLockFile(fetchedLock, children, path);
1253:
1254: if ("/".equals(path)) {
1255: break;
1256: }
1257: path = SVNPathUtil.removeTail(path);
1258:
1259: if ("".equals(path)) {
1260: path = "/";
1261: }
1262: children.clear();
1263: }
1264: }
1265:
1266: private boolean ensureDirExists(File dir, boolean create) {
1267: if (!dir.exists() && create == true) {
1268: return dir.mkdirs();
1269: } else if (!dir.exists()) {
1270: return false;
1271: }
1272: return true;
1273: }
1274:
1275: private void writeDigestLockFile(SVNLock lock, Collection children,
1276: String repositoryPath) throws SVNException {
1277: if (!ensureDirExists(myLocksRoot, true)) {
1278: SVNErrorMessage err = SVNErrorMessage.create(
1279: SVNErrorCode.UNKNOWN,
1280: "Can't create a directory at ''{0}''", myLocksRoot);
1281: SVNErrorManager.error(err);
1282: }
1283:
1284: File digestLockFile = getDigestFileFromRepositoryPath(repositoryPath);
1285: String digest = getDigestFromRepositoryPath(repositoryPath);
1286: File lockDigestSubdir = new File(myLocksRoot, digest.substring(
1287: 0, FSFS.DIGEST_SUBDIR_LEN));
1288:
1289: if (!ensureDirExists(lockDigestSubdir, true)) {
1290: SVNErrorMessage err = SVNErrorMessage.create(
1291: SVNErrorCode.UNKNOWN,
1292: "Can't create a directory at ''{0}''",
1293: lockDigestSubdir);
1294: SVNErrorManager.error(err);
1295: }
1296:
1297: Map props = new HashMap();
1298:
1299: if (lock != null) {
1300: props.put(FSFS.PATH_LOCK_KEY, lock.getPath());
1301: props.put(FSFS.OWNER_LOCK_KEY, lock.getOwner());
1302: props.put(FSFS.TOKEN_LOCK_KEY, lock.getID());
1303: props.put(FSFS.IS_DAV_COMMENT_LOCK_KEY, "0");
1304: if (lock.getComment() != null) {
1305: props.put(FSFS.COMMENT_LOCK_KEY, lock.getComment());
1306: }
1307: if (lock.getCreationDate() != null) {
1308: props.put(FSFS.CREATION_DATE_LOCK_KEY, SVNTimeUtil
1309: .formatDate(lock.getCreationDate()));
1310: }
1311: if (lock.getExpirationDate() != null) {
1312: props.put(FSFS.EXPIRATION_DATE_LOCK_KEY, SVNTimeUtil
1313: .formatDate(lock.getExpirationDate()));
1314: }
1315: }
1316: if (children != null && children.size() > 0) {
1317: Object[] digests = children.toArray();
1318: StringBuffer value = new StringBuffer();
1319: for (int i = 0; i < digests.length; i++) {
1320: value.append(digests[i]);
1321: value.append('\n');
1322: }
1323: props.put(FSFS.CHILDREN_LOCK_KEY, value.toString());
1324: }
1325: try {
1326: SVNProperties.setProperties(props, digestLockFile,
1327: SVNFileUtil.createUniqueFile(digestLockFile
1328: .getParentFile(), digestLockFile.getName(),
1329: ".tmp"), SVNProperties.SVN_HASH_TERMINATOR);
1330: } catch (SVNException svne) {
1331: SVNErrorMessage err = svne.getErrorMessage().wrap(
1332: "Cannot write lock/entries hashfile ''{0}''",
1333: digestLockFile);
1334: SVNErrorManager.error(err, svne);
1335: }
1336: }
1337:
1338: private FSFile openAndSeekTransaction(FSRepresentation rep) {
1339: FSFile file = getTransactionRevisionPrototypeFile(rep
1340: .getTxnId());
1341: file.seek(rep.getOffset());
1342: return file;
1343: }
1344:
1345: private FSFile openAndSeekRevision(long revision, long offset)
1346: throws SVNException {
1347: FSFile file = getRevisionFile(revision);
1348: file.seek(offset);
1349: return file;
1350: }
1351:
1352: private Map parsePlainRepresentation(Map entries,
1353: boolean mayContainNulls) throws SVNException {
1354: Map representationMap = new HashMap();
1355: Object[] names = entries.keySet().toArray();
1356: for (int i = 0; i < names.length; i++) {
1357: String name = (String) names[i];
1358: String unparsedEntry = (String) entries.get(names[i]);
1359:
1360: if (unparsedEntry == null && mayContainNulls) {
1361: continue;
1362: }
1363:
1364: FSEntry nextRepEntry = parseRepEntryValue(name,
1365: unparsedEntry);
1366: if (nextRepEntry == null) {
1367: SVNErrorMessage err = SVNErrorMessage.create(
1368: SVNErrorCode.FS_CORRUPT,
1369: "Directory entry corrupt");
1370: SVNErrorManager.error(err);
1371: }
1372: representationMap.put(name, nextRepEntry);
1373: }
1374: return representationMap;
1375: }
1376:
1377: private FSEntry parseRepEntryValue(String name, String value) {
1378: if (value == null) {
1379: return null;
1380: }
1381: int spaceInd = value.indexOf(' ');
1382: if (spaceInd == -1) {
1383: return null;
1384: }
1385: String kind = value.substring(0, spaceInd);
1386: String rawID = value.substring(spaceInd + 1);
1387:
1388: SVNNodeKind type = SVNNodeKind.parseKind(kind);
1389: FSID id = FSID.fromString(rawID);
1390: if ((type != SVNNodeKind.DIR && type != SVNNodeKind.FILE)
1391: || id == null) {
1392: return null;
1393: }
1394: return new FSEntry(id, type, name);
1395: }
1396:
1397: private Date getRevisionTime(long revision) throws SVNException {
1398: Map revisionProperties = getRevisionProperties(revision);
1399: String timeString = (String) revisionProperties
1400: .get(SVNRevisionProperty.DATE);
1401: if (timeString == null) {
1402: SVNErrorMessage err = SVNErrorMessage
1403: .create(
1404: SVNErrorCode.FS_GENERAL,
1405: "Failed to find time on revision {0,number,integer}",
1406: new Long(revision));
1407: SVNErrorManager.error(err);
1408: }
1409: return SVNTimeUtil.parseDateString(timeString);
1410: }
1411:
1412: private static boolean isRepositoryRoot(File candidatePath) {
1413: File formatFile = new File(candidatePath, "format");
1414: SVNFileType fileType = SVNFileType.getType(formatFile);
1415: if (fileType != SVNFileType.FILE) {
1416: return false;
1417: }
1418: File dbFile = new File(candidatePath, SVN_REPOS_DB_DIR);
1419: fileType = SVNFileType.getType(dbFile);
1420: if (fileType != SVNFileType.DIRECTORY
1421: && fileType != SVNFileType.SYMLINK) {
1422: return false;
1423: }
1424: return true;
1425: }
1426:
1427: }
|