From c7aa2b6a54064157dc167002c850cd403c3b1656 Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sun, 23 Jan 2022 18:10:03 +0100 Subject: Document repository layout, add modified Base64 ByteString encoding. Change-Id: I564db0e346346b608fa11527590e264c694fedaf --- .../java/eu/mulk/jgvariant/ostree/ByteString.java | 29 ++++ .../java/eu/mulk/jgvariant/ostree/Checksum.java | 28 ++++ .../java/eu/mulk/jgvariant/ostree/ObjectType.java | 44 +++++ .../eu/mulk/jgvariant/ostree/package-info.java | 177 +++++++++++++++++++++ .../eu/mulk/jgvariant/ostree/ByteStringTest.java | 70 ++++++++ .../mulk/jgvariant/ostree/OstreeDecoderTest.java | 6 - 6 files changed, 348 insertions(+), 6 deletions(-) create mode 100644 jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java index cf6e99a..bb8cbb6 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java @@ -2,6 +2,7 @@ package eu.mulk.jgvariant.ostree; import eu.mulk.jgvariant.core.Decoder; import java.util.Arrays; +import java.util.Base64; import java.util.HexFormat; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -61,6 +62,34 @@ public record ByteString(byte[] bytes) { return new ByteString(HexFormat.of().parseHex(hex)); } + /** + * Converts the contained byte array into modified Base64 (with {@code "/"} replaced with {@code + * "-"}). + * + *

Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + *

Useful for printing. + * + * @return a modified Base64 representation of the bytes making up this checksum. + */ + public String modifiedBase64() { + return Base64.getEncoder().withoutPadding().encodeToString(bytes).replace('/', '_'); + } + + /** + * Parses a modified Base64 string into a {@link Checksum}. + * + *

Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + * @param mbase64 a hex string. + * @return a {@link Checksum} corresponding to the given modified Base64 string. + */ + public static ByteString ofModifiedBase64(String mbase64) { + return new ByteString(Base64.getDecoder().decode(mbase64.replace('_', '/'))); + } + /** * Returns the number of bytes in the byte string. * diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java index ce5f9b0..94a728c 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java @@ -75,6 +75,34 @@ public record Checksum(ByteString byteString) { return new Checksum(ByteString.ofHex(hex)); } + /** + * Converts the contained byte array into modified Base64 (with {@code "/"} replaced with {@code + * "-"}). + * + *

Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + *

Useful for printing. + * + * @return a modified Base64 representation of the bytes making up this checksum. + */ + public String modifiedBase64() { + return byteString.modifiedBase64(); + } + + /** + * Parses a modified Base64 string into a {@link Checksum}. + * + *

Modified Base64 is Base64 with {@code "/"} replaced with {@code "_"}. It is used to address + * static deltas in an OSTree repository. + * + * @param mbase64 a hex string. + * @return a {@link Checksum} corresponding to the given modified Base64 string. + */ + public static Checksum ofModifiedBase64(String mbase64) { + return new Checksum(ByteString.ofModifiedBase64(mbase64)); + } + /** * Reads a Checksum for a {@link ByteBuffer}. * diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java index 28371fc..721d857 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ObjectType.java @@ -24,12 +24,56 @@ import org.apiguardian.api.API; */ @API(status = STABLE) public enum ObjectType { + + /** + * A regular file. + * + *

File ending: {@code .file} + */ FILE((byte) 1, "file"), + + /** + * A serialized {@link DirTree} object. + * + *

File ending: {@code .dirtree} + */ DIR_TREE((byte) 2, "dirtree"), + + /** + * A serialized {@link DirMeta} object. + * + *

File ending: {@code .dirmeta} + */ DIR_META((byte) 3, "dirmeta"), + + /** + * A serialized {@link Commit} object. + * + *

File ending: {@code .commit} + */ COMMIT((byte) 4, "commit"), + + /** + * A tombstone file standing in for a commit that was deleted. + * + *

File ending: {@code .commit-tombstone} + */ TOMBSTONE_COMMIT((byte) 5, "commit-tombstone"), + + /** + * Detached metadata for a {@link Commit}. + * + *

Often goes together with a {@link #TOMBSTONE_COMMIT}. + * + *

File ending: {@code .commitmeta} + */ COMMIT_META((byte) 6, "commitmeta"), + + /** + * A symlink to a {@link #FILE} that lives somewhere else. + * + *

File ending: {@code .payload-link} + */ PAYLOAD_LINK((byte) 7, "payload-link"); private final byte byteValue; diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java index c6a61f1..028f4cd 100644 --- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java +++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/package-info.java @@ -2,6 +2,183 @@ * Provides record classes describing the elements of OSTree repositories and factory methods to create * {@link eu.mulk.jgvariant.core.Decoder} instances for them. + * + *

OStree repository structure

+ * + *

An OSTree repository has the following layout: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
OSTree repository layout
{@code config} + *

+ * A plain text file that contains various settings. Among other things, this defines + * the archive + * format of the repository and whether files are compressed ({@code mode=archive-z2}) + * or plain ({@code mode=bare}, {@code mode=bare-user}). + *

+ *
{@code summary} + *

+ * A {@link eu.mulk.jgvariant.ostree.Summary} object containing information on what is + * available under {@code refs/}, {@code deltas/}, and {@code delta-indexes/}. + *

+ * + *

This file may or may not exist and, if it exists, may or may not be up to date.

+ *
{@code refs/heads/{name...}}Plain-text files containing {@link eu.mulk.jgvariant.ostree.Checksum}s in hex format + * (see {@link eu.mulk.jgvariant.ostree.Checksum#ofHex}) referring to + * {@link eu.mulk.jgvariant.ostree.Commit} objects. See below for the layout of the + * {@code objects/} directory that this refers to.
{@code objects/{checksumHead}/{checksumRest}.{fileExtension}} + *

+ * Objects of various types as described by {@link eu.mulk.jgvariant.ostree.ObjectType} + * and keyed by {@link eu.mulk.jgvariant.ostree.Checksum}. + *

+ * + *

+ * Among other things, this is where you find {@link eu.mulk.jgvariant.ostree.Commit} + * ({@code .commit)}, {@link eu.mulk.jgvariant.ostree.DirTree} ({@code .dirtree}), + * and {@link eu.mulk.jgvariant.ostree.DirMeta} ({@code .dirmeta}) objects as well as + * plain ({@code .file}) or compressed files ({@code .filez}). + *

+ * + *

+ * Static delta information is not stored here, but in the {@code deltas/} and + * {@code delta-indexes/} directories (if available). + *

+ * + *

+ * The individual parts of the file path are defined as follows: + *

+ * + *
+ *
{@code {checksumHead}} + *
+ * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#hex()} + *
{@code {checksumRest}} + *
+ * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#hex()} starting from the + * 3rd character + *
{@code {fileExtension}} + *
+ * the {@link eu.mulk.jgvariant.ostree.ObjectType#fileExtension()} of the object type + *
+ *
{@code deltas/{targetChecksumMbase64Head}/{targetChecksumMbase64Rest}/superblock} + *

+ * A {@link eu.mulk.jgvariant.ostree.DeltaSuperblock} to get from nothing (an empty commit) + * to the commit named by the checksum encoded in the path. + *

+ * + *

+ * The individual parts of the file path are defined as follows: + *

+ * + *
+ *
{@code {checksumHead}} + *
+ * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + *
{@code {checksumRest}} + *
+ * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + *
+ *
{@code deltas/{targetChecksumMbase64Head}/{targetChecksumMbase64Rest}/{digit...}} + *

+ * A {@link eu.mulk.jgvariant.ostree.DeltaPartPayload} belonging to a delta that goes from + * nothing (an empty commit) to the commit named by the checksum encoded in the path. + *

+ * + *

+ * The individual parts of the file path are defined as follows: + *

+ * + *
+ *
{@code {checksumHead}} + *
+ * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + *
{@code {checksumRest}} + *
+ * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + *
+ *
{@code deltas/{sourceChecksumMbase64Head}/{sourceChecksumMbase64Rest}-{targetChecksumMbase64}/superblock} + *

+ * A {@link eu.mulk.jgvariant.ostree.DeltaSuperblock} to get from the source commit + * referenced by the first checksum encoded in the path to the target commit referenced + * in the second checksum encoded in the path. + *

+ * + *

+ * The individual parts of the file path are defined as follows: + *

+ * + *
+ *
{@code {checksumHead}} + *
+ * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + *
{@code {checksumRest}} + *
+ * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + *
+ *
{@code deltas/{sourceChecksumMbase64Head}/{sourceChecksumMbase64Rest}-{targetChecksumMbase64}/{digit...}} + *

+ * A {@link eu.mulk.jgvariant.ostree.DeltaPartPayload} belonging to a delta that goes from + * the source commit referenced by the first checksum encoded in the path to the target + * commit referenced in the second checksum encoded in the path. + *

+ * + *

+ * The individual parts of the file path are defined as follows: + *

+ * + *
+ *
{@code {checksumHead}} + *
+ * the first two characters of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} + *
{@code {checksumRest}} + *
+ * the substring of {@link eu.mulk.jgvariant.ostree.Checksum#modifiedBase64()} starting + * from the 3rd character + *
+ *
*/ @API(status = Status.EXPERIMENTAL) package eu.mulk.jgvariant.ostree; diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java new file mode 100644 index 0000000..a5fa2b4 --- /dev/null +++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/ByteStringTest.java @@ -0,0 +1,70 @@ +package eu.mulk.jgvariant.ostree; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ByteStringTest { + + @Test + void testToModifiedBase64() { + assertEquals("MciDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI", testByteString1.modifiedBase64()); + } + + @Test + void testOfModifiedBase64() { + assertEquals( + testByteString1, + ByteString.ofModifiedBase64("MciDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI")); + } + + @Test + void testToHex() { + assertEquals( + "31c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52", testByteString1.hex()); + } + + @Test + void testOfHex() { + assertEquals( + testByteString1, + ByteString.ofHex("31c8835d5c9d2c6687a50091c85142d1b2d853ff416a9fb81b4ee30754510d52")); + } + + private static final ByteString testByteString1 = + new ByteString( + new byte[] { + (byte) 0x31, + (byte) 0xc8, + (byte) 0x83, + (byte) 0x5d, + (byte) 0x5c, + (byte) 0x9d, + (byte) 0x2c, + (byte) 0x66, + (byte) 0x87, + (byte) 0xa5, + (byte) 0x00, + (byte) 0x91, + (byte) 0xc8, + (byte) 0x51, + (byte) 0x42, + (byte) 0xd1, + (byte) 0xb2, + (byte) 0xd8, + (byte) 0x53, + (byte) 0xff, + (byte) 0x41, + (byte) 0x6a, + (byte) 0x9f, + (byte) 0xb8, + (byte) 0x1b, + (byte) 0x4e, + (byte) 0xe3, + (byte) 0x07, + (byte) 0x54, + (byte) 0x51, + (byte) 0x0d, + (byte) 0x52 + }); +} diff --git a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java index db222f9..031b1cd 100644 --- a/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java +++ b/jgvariant-ostree/src/test/java/eu/mulk/jgvariant/ostree/OstreeDecoderTest.java @@ -2,7 +2,6 @@ package eu.mulk.jgvariant.ostree; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import com.adelean.inject.resources.junit.jupiter.GivenBinaryResource; import com.adelean.inject.resources.junit.jupiter.TestWithResources; @@ -37,11 +36,6 @@ class OstreeDecoderTest { @GivenBinaryResource("/ostree/deltas/Mc/iDXVydLGaHpQCRyFFC0bLYU_9Bap+4G07jB1RRDVI/0") byte[] deltaPartPayloadBytes; - @Test - void testTrivial() { - assertTrue(true); - } - @Test void testSummaryDecoder() { var decoder = Summary.decoder(); -- cgit v1.2.3