* 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>tags/v3.5.3.201412180710-r
@@ -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); | |||
} | |||
@@ -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'); |
@@ -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()); | |||
} | |||
} | |||
} |
@@ -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]); |
@@ -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]); |
@@ -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); |
@@ -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 |
@@ -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); | |||
} | |||
} |
@@ -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]); |
@@ -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()); | |||
} | |||
@@ -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]); |
@@ -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 |
@@ -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,19 +1308,255 @@ 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(); |
@@ -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 { |
@@ -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]; |
@@ -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); |
@@ -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; | |||
} | |||
} | |||
@@ -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; | |||
} |
@@ -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) { |
@@ -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(); |
@@ -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(); | |||
} | |||
} | |||
} | |||
@@ -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(); | |||
} | |||
/** |
@@ -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(); |
@@ -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(); |
@@ -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); | |||
} |