diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java index e04ac4b3e46..250df05dad9 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/BTreeTests.java @@ -52,7 +52,7 @@ public class BTreeTests extends BaseTestCase { // and invoke it multiple times per Junit test protected void init(int degree) throws Exception { dbFile = File.createTempFile("pdomtest", "db"); - db = new Database(dbFile, new ChunkCache(), 0); + db = new Database(dbFile, new ChunkCache(), 0, false); db.setWritable(); rootRecord = Database.DATA_AREA; comparator = new BTMockRecordComparator(); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBPropertiesTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBPropertiesTests.java index c5318015536..08c43c5b344 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBPropertiesTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBPropertiesTests.java @@ -37,7 +37,7 @@ public class DBPropertiesTests extends BaseTestCase { protected void setUp() throws Exception { dbLoc = File.createTempFile("test", "db"); dbLoc.deleteOnExit(); - db = new Database(dbLoc, new ChunkCache(), 0); + db = new Database(dbLoc, new ChunkCache(), 0, false); db.setWritable(); } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java index a2b3b75ea67..525680ea3a4 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java @@ -13,6 +13,7 @@ package org.eclipse.cdt.internal.pdom.tests; import java.io.File; +import java.io.IOException; import java.util.Random; import junit.framework.Test; @@ -35,7 +36,7 @@ public class DBTest extends BaseTestCase { protected void setUp() throws Exception { super.setUp(); db = new Database(getTestDir().append(getName()+System.currentTimeMillis()+".dat").toFile(), - new ChunkCache(), 0); + new ChunkCache(), 0, false); } public static Test suite() { @@ -73,6 +74,30 @@ public class DBTest extends BaseTestCase { assertEquals(mem - Database.INT_SIZE + blocksize, db.getInt(((Database.CHUNK_SIZE - blocksize) / Database.MIN_SIZE) * Database.INT_SIZE)); } + public void testBug192437() throws IOException { + File tmp= File.createTempFile("readOnlyEmpty", ".db"); + try { + tmp.setReadOnly(); + + /* check opening a readonly file for rw access fails */ + try { + new Database(tmp, ChunkCache.getSharedInstance(), 0, false); + fail("A readonly file should not be openable with write-access"); + } catch(CoreException ioe) { + // we expect to get a failure here + } + + /* check opening a readonly file for read access does not fail */ + try { + new Database(tmp, ChunkCache.getSharedInstance(), 0, true); + } catch(CoreException ce) { + fail("A readonly file should be readable by a permanently readonly database "+ce); + } + } finally { + tmp.delete(); // this may be pointless on some platforms + } + } + public void testFreeBlockLinking() throws Exception { final int realsize = 42; final int blocksize = (realsize / Database.MIN_SIZE + 1) * Database.MIN_SIZE; @@ -126,7 +151,7 @@ public class DBTest extends BaseTestCase { // Tests inserting and retrieving strings File f = getTestDir().append("testStrings.dat").toFile(); f.delete(); - final Database db = new Database(f, new ChunkCache(), 0); + final Database db = new Database(f, new ChunkCache(), 0, false); db.setWritable(); String[] names = { diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java index a377f57be80..1014a694189 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMCPPBugsTest.java @@ -165,7 +165,7 @@ public class PDOMCPPBugsTest extends BaseTestCase { } public void testInterruptingAcquireWriteLock() throws Exception { - final PDOM pdom= (PDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); + final WritablePDOM pdom= (WritablePDOM) CCoreInternals.getPDOMManager().getPDOM(cproject); final boolean[] ok= {false}; pdom.acquireReadLock(); try { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java index c94dc922957..8875071eef3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java @@ -134,10 +134,17 @@ public class PDOM extends PlatformObject implements IIndexFragment, IPDOM { loadDatabase(dbPath, cache); this.locationConverter = locationConverter; } + + /** + * Returns whether this PDOM can never be written to. Writable subclasses should return false. + */ + protected boolean isPermanentlyReadOnly() { + return true; + } private void loadDatabase(File dbPath, ChunkCache cache) throws CoreException { fPath= dbPath; - db = new Database(fPath, cache, VERSION); + db = new Database(fPath, cache, VERSION, isPermanentlyReadOnly()); fileIndex= null; // holds on to the database, so clear it. int version= db.getVersion(); @@ -258,7 +265,9 @@ public class PDOM extends PlatformObject implements IIndexFragment, IPDOM { CCorePlugin.log(e); } loadDatabase(file, db.getChunkCache()); - db.setWritable(); + if(!isPermanentlyReadOnly()) { + db.setWritable(); + } oldFile.delete(); } @@ -549,10 +558,21 @@ public class PDOM extends PlatformObject implements IIndexFragment, IPDOM { } } + /** + * Acquire a write lock on this PDOM. Blocks until any existing read/write locks are released. + * @throws InterruptedException + * @throws IllegalStateException if this PDOM is not writable + */ public void acquireWriteLock() throws InterruptedException { acquireWriteLock(0); } + /** + * Acquire a write lock on this PDOM, giving up the specified number of read locks first. Blocks + * until any existing read/write locks are released. + * @throws InterruptedException + * @throws IllegalStateException if this PDOM is not writable + */ public void acquireWriteLock(int giveupReadLocks) throws InterruptedException { synchronized (mutex) { if (giveupReadLocks > 0) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/WritablePDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/WritablePDOM.java index 0f95cc08e81..158c70591d2 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/WritablePDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/WritablePDOM.java @@ -154,4 +154,8 @@ public class WritablePDOM extends PDOM implements IWritableIndexFragment { void setCreatedFromScratch(boolean createdFromScratch) { fCreatedFromScratch = createdFromScratch; } + + protected final boolean isPermanentlyReadOnly() { + return false; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java index e3a3ea27d09..859332df2e3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java @@ -62,6 +62,7 @@ public class Database { private final File location; private final RandomAccessFile file; private boolean fWritable= false; + private boolean fPermanentlyReadOnly; private Chunk[] chunks; private long malloced; @@ -81,18 +82,29 @@ public class Database { public static final int DATA_AREA = CHUNK_SIZE / MIN_SIZE * INT_SIZE + INT_SIZE; public static final int MAX_SIZE = CHUNK_SIZE - 4; // Room for overhead - - public Database(File location, ChunkCache cache, int version) throws CoreException { + + /** + * Construct a new Database object, creating a backing file if necessary. + * @param location the local file path for the database + * @param cache the cache to be used optimisation + * @param version the version number to store in the database (only applicable for new databases) + * @param permanentReadOnly whether this Database object will ever need writing to + * @throws CoreException + */ + public Database(File location, ChunkCache cache, int version, boolean permanentlyReadOnly) throws CoreException { try { this.location = location; - this.file = new RandomAccessFile(location, "rw"); //$NON-NLS-1$ - fCache= cache; + this.fPermanentlyReadOnly= permanentlyReadOnly; + this.file = new RandomAccessFile(location, permanentlyReadOnly ? "r" : "rw"); //$NON-NLS-1$ //$NON-NLS-2$ + this.fCache= cache; // Allocate chunk table, make sure we have at least one long nChunks = file.length() / CHUNK_SIZE; chunks = new Chunk[(int)nChunks]; if (nChunks == 0) { - setWritable(); + if(!permanentlyReadOnly) { + setWritable(); + } createNewChunk(); setVersion(version); setReadOnly(true); @@ -428,7 +440,15 @@ public class Database { return fCache; } + /** + * Marks this Database as writable. This is used for avoiding some synchronization on chunk fetching. An + * exception is thrown if this Database was constructed as a permanently read only Database. + * @see Database#Database(File, ChunkCache, int, boolean) + * @throw IllegalStateException if called on a permanently read-only database + */ public void setWritable() { + if(fPermanentlyReadOnly) + throw new IllegalStateException("A Database created as permanent-read-only may not be changed to writable state"); //$NON-NLS-1$ fWritable= true; }