diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2014-12-18 15:18:07 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2014-12-18 15:18:07 +0100 |
commit | 4d6f66400eddb5df6097bc2f58fd2538d8ce6b37 (patch) | |
tree | a9ec93d2a0fe7218f51cfa8fef0dc71dc00f1a2b | |
parent | 3521712032aca2dccf6f6ee4a77b3746bf17f276 (diff) | |
parent | dbe8b52c5cac2cb084a0a47b945094db7e6c759e (diff) | |
download | jgit-4d6f66400eddb5df6097bc2f58fd2538d8ce6b37.tar.gz jgit-4d6f66400eddb5df6097bc2f58fd2538d8ce6b37.zip |
Merge branch 'stable-3.4' into stable-3.5
* stable-3.4:
JGit v3.4.2.201412180340-r
ObjectChecker: Disallow names potentially mapping to ".git" on HFS+
ObjectChecker: Disallow Windows shortname "GIT~1"
ObjectChecker: Disallow ".git." and ".git<space>"
Always ignore case when forbidding .git in ObjectChecker
DirCache: Refuse to read files with invalid paths
DirCache: Replace isValidPath with DirCacheCheckout.checkValidPath
Replace "a." with "a-" in unit tests
Apache HttpClientConnection: replace calls to deprecated LocalFile()
Fix two nits about DirCacheEntry constructors
Detect buffering failures while writing rebase todo file
Deprecate TemporaryBuffer.LocalFile without parent directory
Switch FileHeader.extractFileLines to TemporaryBuffer.Heap
AmazonS3: Buffer pushed pack content under $GIT_DIR
DirCache: Buffer TREE extension to $GIT_DIR
Change-Id: I398cf40b006a05a6537788fc6eb1f84df1ed8814
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
25 files changed, 533 insertions, 141 deletions
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java index b776119e23..d42d6f29ee 100644 --- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java +++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java @@ -309,19 +309,19 @@ public class HttpClientConnection implements HttpConnection { public void setFixedLengthStreamingMode(int contentLength) { if (entity != null) throw new IllegalArgumentException(); - entity = new TemporaryBufferEntity(new LocalFile()); + entity = new TemporaryBufferEntity(new LocalFile(null)); entity.setContentLength(contentLength); } public OutputStream getOutputStream() throws IOException { if (entity == null) - entity = new TemporaryBufferEntity(new LocalFile()); + entity = new TemporaryBufferEntity(new LocalFile(null)); return entity.getBuffer(); } public void setChunkedStreamingMode(int chunklen) { if (entity == null) - entity = new TemporaryBufferEntity(new LocalFile()); + entity = new TemporaryBufferEntity(new LocalFile(null)); entity.setChunked(true); } diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java index dc5821b5cc..76930f2b86 100644 --- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java +++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java @@ -216,7 +216,7 @@ public class EGitPatchHistoryTest { buf.destroy(); } commitId = line.substring("commit ".length()); - buf = new TemporaryBuffer.LocalFile(); + buf = new TemporaryBuffer.LocalFile(null); } else if (buf != null) { buf.write(line.getBytes("ISO-8859-1")); buf.write('\n'); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java index 6ac718ec5b..0c1baab2b8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBasicTest.java @@ -47,12 +47,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; +import java.text.MessageFormat; +import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.junit.MockSystemReader; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.util.SystemReader; import org.junit.Test; public class DirCacheBasicTest extends RepositoryTestCase { @@ -190,7 +197,7 @@ public class DirCacheBasicTest extends RepositoryTestCase { public void testBuildThenClear() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final String[] paths = { "a-", "a.b", "a/b", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -234,4 +241,41 @@ public class DirCacheBasicTest extends RepositoryTestCase { final byte[] path = Constants.encode("a"); assertEquals(-1, dc.findEntry(path, path.length)); } + + @Test + public void testRejectInvalidWindowsPaths() throws Exception { + SystemReader.setInstance(new MockSystemReader() { + { + setUnix(); + } + }); + + String path = "src/con.txt"; + DirCache dc = db.lockDirCache(); + DirCacheBuilder b = dc.builder(); + DirCacheEntry e = new DirCacheEntry(path); + e.setFileMode(FileMode.REGULAR_FILE); + e.setObjectId(new ObjectInserter.Formatter().idFor( + Constants.OBJ_BLOB, + Constants.encode(path))); + b.add(e); + b.commit(); + db.readDirCache(); + + SystemReader.setInstance(new MockSystemReader() { + { + setWindows(); + } + }); + + try { + db.readDirCache(); + fail("should have rejected " + path); + } catch (CorruptObjectException err) { + assertEquals(MessageFormat.format(JGitText.get().invalidPath, path), + err.getMessage()); + assertNotNull(err.getCause()); + assertEquals("invalid name 'CON'", err.getCause().getMessage()); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java index 254431f74b..8561fdf35b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderIteratorTest.java @@ -63,7 +63,7 @@ public class DirCacheBuilderIteratorTest extends RepositoryTestCase { final DirCache dc = db.readDirCache(); final FileMode mode = FileMode.REGULAR_FILE; - final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java index a502db3ac1..5408f761dc 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java @@ -290,7 +290,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase { public void testAdd_InGitSortOrder() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final String[] paths = { "a-", "a.b", "a/b", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -315,7 +315,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase { public void testAdd_ReverseGitSortOrder() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final String[] paths = { "a-", "a.b", "a/b", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -340,7 +340,7 @@ public class DirCacheBuilderTest extends RepositoryTestCase { public void testBuilderClear() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a.b", "a/b", "a0b" }; + final String[] paths = { "a-", "a.b", "a/b", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java index 53d672d7f3..7f58a1cbed 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java @@ -213,7 +213,7 @@ public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase { assertV3TreeEntry(9, "newfile.txt", false, true, dc); final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - dc.writeTo(bos); + dc.writeTo(null, bos); final byte[] indexBytes = bos.toByteArray(); final byte[] expectedBytes = IO.readFully(file); assertArrayEquals(expectedBytes, indexBytes); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java index 225ce2a907..e159ed939e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java @@ -49,7 +49,6 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectId; import org.junit.Test; @@ -71,7 +70,12 @@ public class DirCacheEntryTest { } private static boolean isValidPath(final String path) { - return DirCacheEntry.isValidPath(Constants.encode(path)); + try { + DirCacheCheckout.checkValidPath(path); + return true; + } catch (InvalidPathException e) { + return false; + } } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java index 0b43b2311a..3b8c6ee7b4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheFindTest.java @@ -56,7 +56,7 @@ public class DirCacheFindTest extends RepositoryTestCase { public void testEntriesWithin() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -96,13 +96,13 @@ public class DirCacheFindTest extends RepositoryTestCase { assertSame(ents[i], aContents[i]); } - assertNotNull(dc.getEntriesWithin("a.")); - assertEquals(0, dc.getEntriesWithin("a.").length); + assertNotNull(dc.getEntriesWithin("a-")); + assertEquals(0, dc.getEntriesWithin("a-").length); assertNotNull(dc.getEntriesWithin("a0b")); - assertEquals(0, dc.getEntriesWithin("a0b.").length); + assertEquals(0, dc.getEntriesWithin("a0b-").length); assertNotNull(dc.getEntriesWithin("zoo")); - assertEquals(0, dc.getEntriesWithin("zoo.").length); + assertEquals(0, dc.getEntriesWithin("zoo-").length); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java index 8d29a73188..af1c8a3567 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheIteratorTest.java @@ -85,7 +85,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { public void testNoSubtree_NoTreeWalk() throws Exception { final DirCache dc = DirCache.newInCore(); - final String[] paths = { "a.", "a0b" }; + final String[] paths = { "a-", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -111,7 +111,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { public void testNoSubtree_WithTreeWalk() throws Exception { final DirCache dc = DirCache.newInCore(); - final String[] paths = { "a.", "a0b" }; + final String[] paths = { "a-", "a0b" }; final FileMode[] modes = { FileMode.EXECUTABLE_FILE, FileMode.GITLINK }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { @@ -144,7 +144,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { public void testSingleSubtree_NoRecursion() throws Exception { final DirCache dc = DirCache.newInCore(); - final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -156,7 +156,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { b.add(ents[i]); b.finish(); - final String[] expPaths = { "a.", "a", "a0b" }; + final String[] expPaths = { "a-", "a", "a0b" }; final FileMode[] expModes = { FileMode.REGULAR_FILE, FileMode.TREE, FileMode.REGULAR_FILE }; final int expPos[] = { 0, -1, 4 }; @@ -189,7 +189,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { final DirCache dc = DirCache.newInCore(); final FileMode mode = FileMode.REGULAR_FILE; - final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -224,7 +224,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { final DirCache dc = DirCache.newInCore(); final FileMode mode = FileMode.REGULAR_FILE; - final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -258,7 +258,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { final DirCache dc = DirCache.newInCore(); final FileMode mode = FileMode.REGULAR_FILE; - final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -272,7 +272,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { DirCacheIterator dci = new DirCacheIterator(dc); assertFalse(dci.eof()); - assertEquals("a.", dci.getEntryPathString()); + assertEquals("a-", dci.getEntryPathString()); dci.next(1); assertFalse(dci.eof()); assertEquals("a", dci.getEntryPathString()); @@ -285,7 +285,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { // same entries the second time dci.reset(); assertFalse(dci.eof()); - assertEquals("a.", dci.getEntryPathString()); + assertEquals("a-", dci.getEntryPathString()); dci.next(1); assertFalse(dci.eof()); assertEquals("a", dci.getEntryPathString()); @@ -304,12 +304,12 @@ public class DirCacheIteratorTest extends RepositoryTestCase { assertEquals("a", dci.getEntryPathString()); dci.back(1); assertFalse(dci.eof()); - assertEquals("a.", dci.getEntryPathString()); + assertEquals("a-", dci.getEntryPathString()); assertTrue(dci.first()); // forward assertFalse(dci.eof()); - assertEquals("a.", dci.getEntryPathString()); + assertEquals("a-", dci.getEntryPathString()); dci.next(1); assertFalse(dci.eof()); assertEquals("a", dci.getEntryPathString()); @@ -385,7 +385,7 @@ public class DirCacheIteratorTest extends RepositoryTestCase { final DirCache dc = DirCache.newInCore(); final FileMode mode = FileMode.REGULAR_FILE; - final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java index 28140f330e..63ec85861d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java @@ -113,21 +113,23 @@ public class DirCachePathEditTest { DirCache dc = DirCache.newInCore(); DirCacheEditor editor = dc.editor(); editor.add(new AddEdit("a/b")); - editor.add(new AddEdit("a.")); + editor.add(new AddEdit("a-")); editor.add(new AddEdit("ab")); editor.finish(); assertEquals(3, dc.getEntryCount()); + // Validate sort order - assertEquals("a.", dc.getEntry(0).getPathString()); + assertEquals("a-", dc.getEntry(0).getPathString()); assertEquals("a/b", dc.getEntry(1).getPathString()); assertEquals("ab", dc.getEntry(2).getPathString()); editor = dc.editor(); + // Sort order should not confuse DeleteTree editor.add(new DirCacheEditor.DeleteTree("a")); editor.finish(); assertEquals(2, dc.getEntryCount()); - assertEquals("a.", dc.getEntry(0).getPathString()); + assertEquals("a-", dc.getEntry(0).getPathString()); assertEquals("ab", dc.getEntry(1).getPathString()); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java index 89d7bed8b7..f662e2660c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheTreeTest.java @@ -92,7 +92,7 @@ public class DirCacheTreeTest extends RepositoryTestCase { public void testSingleSubtree() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -130,7 +130,7 @@ public class DirCacheTreeTest extends RepositoryTestCase { public void testTwoLevelSubtree() throws Exception { final DirCache dc = db.readDirCache(); - final String[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; + final String[] paths = { "a-", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); @@ -190,7 +190,7 @@ public class DirCacheTreeTest extends RepositoryTestCase { final String A = String.format("a%2000s", "a"); final String B = String.format("b%2000s", "b"); - final String[] paths = { A + ".", A + "." + B, A + "/" + B, A + "0" + B }; + final String[] paths = { A + "-", A + "-" + B, A + "/" + B, A + "0" + B }; final DirCacheEntry[] ents = new DirCacheEntry[paths.length]; for (int i = 0; i < paths.length; i++) { ents[i] = new DirCacheEntry(paths[i]); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java index d29a75ef71..ca3e0666ea 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java @@ -186,10 +186,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase { @Test public void testMaliciousGitPathEndSpaceUnixOk() throws Exception { - if (File.separatorChar == '\\') - return; // cannot emulate Unix on Windows for this test - ((MockSystemReader) SystemReader.getInstance()).setUnix(); - testMaliciousPathGoodFirstCheckout(".git ", "konfig"); + testMaliciousPathBadFirstCheckout(".git ", "konfig"); } @Test @@ -212,10 +209,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase { @Test public void testMaliciousGitPathEndDotUnixOk() throws Exception { - if (File.separatorChar == '\\') - return; // cannot emulate Unix on Windows for this test - ((MockSystemReader) SystemReader.getInstance()).setUnix(); - testMaliciousPathGoodFirstCheckout(".git.", "konfig"); + testMaliciousPathBadFirstCheckout(".git.", "konfig"); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java index 9fc7fca987..c6578ccfae 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java @@ -1295,12 +1295,11 @@ public class ObjectCheckerTest { } @Test - public void testInvalidTreeNameIsMixedCaseGitWindows() { + public void testInvalidTreeNameIsMixedCaseGit() { StringBuilder b = new StringBuilder(); entry(b, "100644 .GiT"); byte[] data = Constants.encodeASCII(b.toString()); try { - checker.setSafeForWindows(true); checker.checkTree(data); fail("incorrectly accepted an invalid tree"); } catch (CorruptObjectException e) { @@ -1309,20 +1308,256 @@ public class ObjectCheckerTest { } @Test - public void testInvalidTreeNameIsMixedCaseGitMacOS() { + public void testInvalidTreeNameIsMacHFSGit() { StringBuilder b = new StringBuilder(); - entry(b, "100644 .GiT"); - byte[] data = Constants.encodeASCII(b.toString()); + entry(b, "100644 .gi\u200Ct"); + byte[] data = Constants.encode(b.toString()); try { checker.setSafeForMacOS(true); checker.checkTree(data); fail("incorrectly accepted an invalid tree"); } catch (CorruptObjectException e) { - assertEquals("invalid name '.GiT'", e.getMessage()); + assertEquals( + "invalid name '.gi\u200Ct' contains ignorable Unicode characters", + e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsMacHFSGit2() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 \u206B.git"); + byte[] data = Constants.encode(b.toString()); + try { + checker.setSafeForMacOS(true); + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals( + "invalid name '\u206B.git' contains ignorable Unicode characters", + e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsMacHFSGit3() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git\uFEFF"); + byte[] data = Constants.encode(b.toString()); + try { + checker.setSafeForMacOS(true); + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals( + "invalid name '.git\uFEFF' contains ignorable Unicode characters", + e.getMessage()); + } + } + + private static byte[] concat(byte[] b1, byte[] b2) { + byte[] data = new byte[b1.length + b2.length]; + System.arraycopy(b1, 0, data, 0, b1.length); + System.arraycopy(b2, 0, data, b1.length, b2.length); + return data; + } + + @Test + public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd() { + byte[] data = concat(Constants.encode("100644 .git"), + new byte[] { (byte) 0xef }); + StringBuilder b = new StringBuilder(); + entry(b, ""); + data = concat(data, Constants.encode(b.toString())); + try { + checker.setSafeForMacOS(true); + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals( + "invalid name contains byte sequence '0xef' which is not a valid UTF-8 character", + e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd2() { + byte[] data = concat(Constants.encode("100644 .git"), new byte[] { + (byte) 0xe2, (byte) 0xab }); + StringBuilder b = new StringBuilder(); + entry(b, ""); + data = concat(data, Constants.encode(b.toString())); + try { + checker.setSafeForMacOS(true); + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals( + "invalid name contains byte sequence '0xe2ab' which is not a valid UTF-8 character", + e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsNotMacHFSGit() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git\u200Cx"); + byte[] data = Constants.encode(b.toString()); + checker.setSafeForMacOS(true); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsNotMacHFSGit2() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .kit\u200C"); + byte[] data = Constants.encode(b.toString()); + checker.setSafeForMacOS(true); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsNotMacHFSGitOtherPlatform() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git\u200C"); + byte[] data = Constants.encode(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitDot() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git."); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git.'", e.getMessage()); + } + } + + @Test + public void testValidTreeNameIsDotGitDotDot() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git.."); + checker.checkTree(Constants.encodeASCII(b.toString())); + } + + @Test + public void testInvalidTreeNameIsDotGitSpace() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git "); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git '", e.getMessage()); } } @Test + public void testInvalidTreeNameIsDotGitSomething() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoobar"); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitSomethingSpaceSomething() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoo bar"); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitSomethingDot() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoobar."); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitSomethingDotDot() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoobar.."); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitDotSpace() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git. "); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git. '", e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsDotGitSpaceDot() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git . "); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git . '", e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsGITTilde1() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 GIT~1"); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name 'GIT~1'", e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsGiTTilde1() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 GiT~1"); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name 'GiT~1'", e.getMessage()); + } + } + + @Test + public void testValidTreeNameIsGitTilde11() throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 GIT~11"); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test public void testInvalidTreeTruncatedInName() { final StringBuilder b = new StringBuilder(); b.append("100644 b"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java index 4c329cb191..d0062e1990 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java @@ -169,7 +169,7 @@ public class PathFilterGroupTest { } // less obvious due to git sorting order - filter.include(fakeWalk("d.")); + filter.include(fakeWalk("d-")); // less obvious due to git sorting order try { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java index 5f5968dbcf..9817cdc0a1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java @@ -59,7 +59,7 @@ import org.junit.Test; public class TemporaryBufferTest { @Test public void testEmpty() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); try { b.close(); assertEquals(0, b.length()); @@ -73,7 +73,7 @@ public class TemporaryBufferTest { @Test public void testOneByte() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte test = (byte) new TestRng(getName()).nextInt(); try { b.write(test); @@ -100,7 +100,7 @@ public class TemporaryBufferTest { @Test public void testOneBlock_BulkWrite() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.Block.SZ); try { @@ -131,7 +131,7 @@ public class TemporaryBufferTest { @Test public void testOneBlockAndHalf_BulkWrite() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.Block.SZ * 3 / 2); try { @@ -162,7 +162,7 @@ public class TemporaryBufferTest { @Test public void testOneBlockAndHalf_SingleWrite() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.Block.SZ * 3 / 2); try { @@ -191,7 +191,7 @@ public class TemporaryBufferTest { @Test public void testOneBlockAndHalf_Copy() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.Block.SZ * 3 / 2); try { @@ -221,7 +221,7 @@ public class TemporaryBufferTest { @Test public void testLarge_SingleWrite() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 3); try { @@ -263,7 +263,7 @@ public class TemporaryBufferTest { @Test public void testInCoreLimit_SwitchOnAppendByte() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT + 1); try { @@ -292,7 +292,7 @@ public class TemporaryBufferTest { @Test public void testInCoreLimit_SwitchBeforeAppendByte() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 3); try { @@ -321,7 +321,7 @@ public class TemporaryBufferTest { @Test public void testInCoreLimit_SwitchOnCopy() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final byte[] test = new TestRng(getName()) .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2); try { @@ -354,7 +354,7 @@ public class TemporaryBufferTest { @Test public void testDestroyWhileOpen() throws IOException { @SuppressWarnings("resource" /* java 7 */) - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); try { b.write(new TestRng(getName()) .nextBytes(TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2)); @@ -365,7 +365,7 @@ public class TemporaryBufferTest { @Test public void testRandomWrites() throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); + final TemporaryBuffer b = new TemporaryBuffer.LocalFile(null); final TestRng rng = new TestRng(getName()); final int max = TemporaryBuffer.DEFAULT_IN_CORE_LIMIT * 2; final byte[] expect = new byte[max]; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 645de2704e..98a1c8ca4b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -607,7 +607,8 @@ public class DirCache { final LockFile tmp = myLock; requireLocked(tmp); try { - writeTo(new SafeBufferedOutputStream(tmp.getOutputStream())); + writeTo(liveFile.getParentFile(), + new SafeBufferedOutputStream(tmp.getOutputStream())); } catch (IOException err) { tmp.unlock(); throw err; @@ -620,7 +621,7 @@ public class DirCache { } } - void writeTo(final OutputStream os) throws IOException { + void writeTo(File dir, final OutputStream os) throws IOException { final MessageDigest foot = Constants.newMessageDigest(); final DigestOutputStream dos = new DigestOutputStream(os, foot); @@ -670,14 +671,18 @@ public class DirCache { } if (writeTree) { - final TemporaryBuffer bb = new TemporaryBuffer.LocalFile(); - tree.write(tmp, bb); - bb.close(); - - NB.encodeInt32(tmp, 0, EXT_TREE); - NB.encodeInt32(tmp, 4, (int) bb.length()); - dos.write(tmp, 0, 8); - bb.writeTo(dos, null); + TemporaryBuffer bb = new TemporaryBuffer.LocalFile(dir, 5 << 20); + try { + tree.write(tmp, bb); + bb.close(); + + NB.encodeInt32(tmp, 0, EXT_TREE); + NB.encodeInt32(tmp, 4, (int) bb.length()); + dos.write(tmp, 0, 8); + bb.writeTo(dos, null); + } finally { + bb.destroy(); + } } writeIndexChecksum = foot.digest(); os.write(writeIndexChecksum); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java index 4b0d58600f..399c1d130a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java @@ -1281,7 +1281,9 @@ public class DirCacheCheckout { } chk.checkPathSegment(bytes, segmentStart, bytes.length); } catch (CorruptObjectException e) { - throw new InvalidPathException(e.getMessage()); + InvalidPathException p = new InvalidPathException(path); + p.initCause(e); + throw p; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index 65188c8f43..eef2e6d3c3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -56,6 +56,7 @@ import java.security.MessageDigest; import java.text.MessageFormat; import java.util.Arrays; +import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; @@ -64,7 +65,6 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.MutableInteger; import org.eclipse.jgit.util.NB; -import org.eclipse.jgit.util.SystemReader; /** * A single file (or stage of a file) in a {@link DirCache}. @@ -190,6 +190,16 @@ public class DirCacheEntry { md.update((byte) 0); } + try { + DirCacheCheckout.checkValidPath(toString(path)); + } catch (InvalidPathException e) { + CorruptObjectException p = + new CorruptObjectException(e.getMessage()); + if (e.getCause() != null) + p.initCause(e.getCause()); + throw p; + } + // Index records are padded out to the next 8 byte alignment // for historical reasons related to how C Git read the files. // @@ -203,7 +213,6 @@ public class DirCacheEntry { if (mightBeRacilyClean(smudge_s, smudge_ns)) smudgeRacilyClean(); - } /** @@ -217,7 +226,7 @@ public class DirCacheEntry { * or DirCache file. */ public DirCacheEntry(final String newPath) { - this(Constants.encode(newPath)); + this(Constants.encode(newPath), STAGE_0); } /** @@ -266,11 +275,11 @@ public class DirCacheEntry { */ @SuppressWarnings("boxing") public DirCacheEntry(final byte[] newPath, final int stage) { - if (!isValidPath(newPath)) - throw new InvalidPathException(toString(newPath)); + DirCacheCheckout.checkValidPath(toString(newPath)); if (stage < 0 || 3 < stage) - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidStageForPath - , stage, toString(newPath))); + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().invalidStageForPath, + stage, toString(newPath))); info = new byte[INFO_LEN]; infoOffset = 0; @@ -725,36 +734,6 @@ public class DirCacheEntry { return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString(); } - static boolean isValidPath(final byte[] path) { - if (path.length == 0) - return false; // empty path is not permitted. - - boolean componentHasChars = false; - for (final byte c : path) { - switch (c) { - case 0: - return false; // NUL is never allowed within the path. - - case '/': - if (componentHasChars) - componentHasChars = false; - else - return false; - break; - case '\\': - case ':': - // Tree's never have a backslash in them, not even on Windows - // but even there we regard it as an invalid path - if (SystemReader.getInstance().isWindows()) - return false; - //$FALL-THROUGH$ - default: - componentHasChars = true; - } - } - return componentHasChars; - } - static int getMaximumInfoLength(boolean extended) { return extended ? INFO_LEN_EXTENDED : INFO_LEN; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java index d8a70c104f..281bccde65 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java @@ -453,13 +453,27 @@ public class ObjectChecker { throw new CorruptObjectException("invalid name '..'"); break; case 4: - if (isDotGit(raw, ptr + 1)) + if (isGit(raw, ptr + 1)) + throw new CorruptObjectException(String.format( + "invalid name '%s'", + RawParseUtils.decode(raw, ptr, end))); + break; + default: + if (end - ptr > 4 && isNormalizedGit(raw, ptr + 1, end)) throw new CorruptObjectException(String.format( "invalid name '%s'", RawParseUtils.decode(raw, ptr, end))); } + } else if (isGitTilde1(raw, ptr, end)) { + throw new CorruptObjectException(String.format("invalid name '%s'", + RawParseUtils.decode(raw, ptr, end))); } + if (macosx && isMacHFSGit(raw, ptr, end)) + throw new CorruptObjectException(String.format( + "invalid name '%s' contains ignorable Unicode characters", + RawParseUtils.decode(raw, ptr, end))); + if (windows) { // Windows ignores space and dot at end of file name. if (raw[end - 1] == ' ' || raw[end - 1] == '.') @@ -470,6 +484,88 @@ public class ObjectChecker { } } + // Mac's HFS+ folds permutations of ".git" and Unicode ignorable characters + // to ".git" therefore we should prevent such names + private static boolean isMacHFSGit(byte[] raw, int ptr, int end) + throws CorruptObjectException { + boolean ignorable = false; + byte[] git = new byte[] { '.', 'g', 'i', 't' }; + int g = 0; + while (ptr < end) { + switch (raw[ptr]) { + case (byte) 0xe2: // http://www.utf8-chartable.de/unicode-utf8-table.pl?start=8192 + checkTruncatedIgnorableUTF8(raw, ptr, end); + switch (raw[ptr + 1]) { + case (byte) 0x80: + switch (raw[ptr + 2]) { + case (byte) 0x8c: // U+200C 0xe2808c ZERO WIDTH NON-JOINER + case (byte) 0x8d: // U+200D 0xe2808d ZERO WIDTH JOINER + case (byte) 0x8e: // U+200E 0xe2808e LEFT-TO-RIGHT MARK + case (byte) 0x8f: // U+200F 0xe2808f RIGHT-TO-LEFT MARK + case (byte) 0xaa: // U+202A 0xe280aa LEFT-TO-RIGHT EMBEDDING + case (byte) 0xab: // U+202B 0xe280ab RIGHT-TO-LEFT EMBEDDING + case (byte) 0xac: // U+202C 0xe280ac POP DIRECTIONAL FORMATTING + case (byte) 0xad: // U+202D 0xe280ad LEFT-TO-RIGHT OVERRIDE + case (byte) 0xae: // U+202E 0xe280ae RIGHT-TO-LEFT OVERRIDE + ignorable = true; + ptr += 3; + continue; + default: + return false; + } + case (byte) 0x81: + switch (raw[ptr + 2]) { + case (byte) 0xaa: // U+206A 0xe281aa INHIBIT SYMMETRIC SWAPPING + case (byte) 0xab: // U+206B 0xe281ab ACTIVATE SYMMETRIC SWAPPING + case (byte) 0xac: // U+206C 0xe281ac INHIBIT ARABIC FORM SHAPING + case (byte) 0xad: // U+206D 0xe281ad ACTIVATE ARABIC FORM SHAPING + case (byte) 0xae: // U+206E 0xe281ae NATIONAL DIGIT SHAPES + case (byte) 0xaf: // U+206F 0xe281af NOMINAL DIGIT SHAPES + ignorable = true; + ptr += 3; + continue; + default: + return false; + } + } + break; + case (byte) 0xef: // http://www.utf8-chartable.de/unicode-utf8-table.pl?start=65024 + checkTruncatedIgnorableUTF8(raw, ptr, end); + // U+FEFF 0xefbbbf ZERO WIDTH NO-BREAK SPACE + if ((raw[ptr + 1] == (byte) 0xbb) + && (raw[ptr + 2] == (byte) 0xbf)) { + ignorable = true; + ptr += 3; + continue; + } + return false; + default: + if (g == 4) + return false; + if (raw[ptr++] != git[g++]) + return false; + } + } + if (g == 4 && ignorable) + return true; + return false; + } + + private static void checkTruncatedIgnorableUTF8(byte[] raw, int ptr, int end) + throws CorruptObjectException { + if ((ptr + 2) >= end) + throw new CorruptObjectException(MessageFormat.format( + "invalid name contains byte sequence ''{0}'' which is not a valid UTF-8 character", + toHexString(raw, ptr, end))); + } + + private static String toHexString(byte[] raw, int ptr, int end) { + StringBuilder b = new StringBuilder("0x"); //$NON-NLS-1$ + for (int i = ptr; i < end; i++) + b.append(String.format("%02x", Byte.valueOf(raw[i]))); //$NON-NLS-1$ + return b.toString(); + } + private static void checkNotWindowsDevice(byte[] raw, int ptr, int end) throws CorruptObjectException { switch (toLower(raw[ptr])) { @@ -540,12 +636,36 @@ public class ObjectChecker { return 1 <= c && c <= 31; } - private boolean isDotGit(byte[] buf, int p) { - if (windows || macosx) - return toLower(buf[p]) == 'g' - && toLower(buf[p + 1]) == 'i' - && toLower(buf[p + 2]) == 't'; - return buf[p] == 'g' && buf[p + 1] == 'i' && buf[p + 2] == 't'; + private static boolean isGit(byte[] buf, int p) { + return toLower(buf[p]) == 'g' + && toLower(buf[p + 1]) == 'i' + && toLower(buf[p + 2]) == 't'; + } + + private static boolean isGitTilde1(byte[] buf, int p, int end) { + if (end - p != 5) + return false; + return toLower(buf[p]) == 'g' && toLower(buf[p + 1]) == 'i' + && toLower(buf[p + 2]) == 't' && buf[p + 3] == '~' + && buf[p + 4] == '1'; + } + + private static boolean isNormalizedGit(byte[] raw, int ptr, int end) { + if (isGit(raw, ptr)) { + int dots = 0; + boolean space = false; + int p = end - 1; + for (; (ptr + 2) < p; p--) { + if (raw[p] == '.') + dots++; + else if (raw[p] == ' ') + space = true; + else + break; + } + return p == ptr + 2 && (dots == 1 || space); + } + return false; } private static char toLower(byte b) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java index ef61e22032..4ebe5fedf3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java @@ -43,17 +43,17 @@ package org.eclipse.jgit.lib; -import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; +import java.io.OutputStream; import java.util.LinkedList; import java.util.List; import org.eclipse.jgit.lib.RebaseTodoLine.Action; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.io.SafeBufferedOutputStream; /** * Offers methods to read and write files formatted like the git-rebase-todo @@ -216,9 +216,8 @@ public class RebaseTodoFile { */ public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps, boolean append) throws IOException { - BufferedWriter fw = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(new File(repo.getDirectory(), path), - append), Constants.CHARACTER_ENCODING)); + OutputStream fw = new SafeBufferedOutputStream(new FileOutputStream( + new File(repo.getDirectory(), path), append)); try { StringBuilder sb = new StringBuilder(); for (RebaseTodoLine step : steps) { @@ -232,8 +231,8 @@ public class RebaseTodoFile { sb.append(" "); //$NON-NLS-1$ sb.append(step.getShortMessage().trim()); } - fw.write(sb.toString()); - fw.newLine(); + sb.append('\n'); + fw.write(Constants.encode(sb.toString())); } } finally { fw.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java index 8171669bc8..534c827314 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java @@ -267,7 +267,7 @@ public class FileHeader extends DiffEntry { final TemporaryBuffer[] tmp = new TemporaryBuffer[getParentCount() + 1]; try { for (int i = 0; i < tmp.length; i++) - tmp[i] = new TemporaryBuffer.LocalFile(); + tmp[i] = new TemporaryBuffer.Heap(Integer.MAX_VALUE); for (final HunkHeader h : getHunks()) h.extractFileLines(tmp); @@ -281,11 +281,6 @@ public class FileHeader extends DiffEntry { return r; } catch (IOException ioe) { throw new RuntimeException(JGitText.get().cannotConvertScriptToText, ioe); - } finally { - for (final TemporaryBuffer b : tmp) { - if (b != null) - b.destroy(); - } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java index 7b48473523..383c1f8fef 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java @@ -139,14 +139,10 @@ public class Patch { } private static byte[] readFully(final InputStream is) throws IOException { - final TemporaryBuffer b = new TemporaryBuffer.LocalFile(); - try { - b.copy(is); - b.close(); - return b.toByteArray(); - } finally { - b.destroy(); - } + TemporaryBuffer b = new TemporaryBuffer.Heap(Integer.MAX_VALUE); + b.copy(is); + b.close(); + return b.toByteArray(); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java index 99d8b09d87..722bfc489d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java @@ -182,6 +182,9 @@ public class AmazonS3 { /** Encryption algorithm, may be a null instance that provides pass-through. */ private final WalkEncryption encryption; + /** Directory for locally buffered content. */ + private final File tmpDir; + /** * Create a new S3 client for the supplied user information. * <p> @@ -251,6 +254,9 @@ public class AmazonS3 { maxAttempts = Integer.parseInt(props.getProperty( "httpclient.retry-max", "3")); //$NON-NLS-1$ //$NON-NLS-2$ proxySelector = ProxySelector.getDefault(); + + String tmp = props.getProperty("tmpdir"); //$NON-NLS-1$ + tmpDir = tmp != null && tmp.length() > 0 ? new File(tmp) : null; } /** @@ -452,7 +458,7 @@ public class AmazonS3 { final ProgressMonitor monitor, final String monitorTask) throws IOException { final MessageDigest md5 = newMD5(); - final TemporaryBuffer buffer = new TemporaryBuffer.LocalFile() { + final TemporaryBuffer buffer = new TemporaryBuffer.LocalFile(tmpDir) { @Override public void close() throws IOException { super.close(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java index b3a55a581b..afaaa69a43 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java @@ -147,7 +147,11 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport { throws NotSupportedException { super(local, uri); - s3 = new AmazonS3(loadProperties()); + Properties props = loadProperties(); + if (!props.contains("tmpdir") && local.getDirectory() != null) //$NON-NLS-1$ + props.put("tmpdir", local.getDirectory().getPath()); //$NON-NLS-1$ + + s3 = new AmazonS3(props); bucket = uri.getHost(); String p = uri.getPath(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java index 88c32d2f53..10aade4e11 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java @@ -361,7 +361,12 @@ public abstract class TemporaryBuffer extends OutputStream { */ private File onDiskFile; - /** Create a new temporary buffer. */ + /** + * Create a new temporary buffer. + * + * @deprecated Use the {@code File} overload to supply a directory. + */ + @Deprecated public LocalFile() { this(null, DEFAULT_IN_CORE_LIMIT); } @@ -372,7 +377,9 @@ public abstract class TemporaryBuffer extends OutputStream { * @param inCoreLimit * maximum number of bytes to store in memory. Storage beyond * this limit will use the local file. + * @deprecated Use the {@code File,int} overload to supply a directory. */ + @Deprecated public LocalFile(final int inCoreLimit) { this(null, inCoreLimit); } |