blob: ed0f4ffdf2a33012cd2e7769cecd117dc7e87168 [file] [log] [blame]
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01001package eu.mulk.jgvariant.ostree;
2
3import eu.mulk.jgvariant.core.Decoder;
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +01004import java.io.ByteArrayInputStream;
5import java.io.ByteArrayOutputStream;
6import java.io.IOException;
7import java.io.UncheckedIOException;
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +01008import java.nio.ByteBuffer;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01009import java.nio.ByteOrder;
10import java.util.List;
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +010011import org.tukaani.xz.XZInputStream;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010012
13/**
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010014 * A payload file from a static delta.
15 *
16 * <p>The first byte is a compression byte: {@code 0} for none, {@code 'x'} for LZMA. The actual
17 * GVariant data begins right after.
18 *
19 * <p>Reference: {@code
20 * ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0}
21 *
22 * @param fileModes the {@link FileMode}s of the files generated by this delta payload.
23 * @param xattrs the {@link Xattr}s of the files generated by this delta payload.
24 * @param rawDataSource the data bytes used in the delta operations.
25 * @param operations the operations to apply during delta patching.
26 * @see DeltaSuperblock
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010027 */
28public record DeltaPartPayload(
29 List<FileMode> fileModes,
30 List<List<Xattr>> xattrs,
31 ByteString rawDataSource,
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010032 List<DeltaOperation> operations) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010033
34 private static final Decoder<DeltaPartPayload> DECODER =
35 Decoder.ofStructure(
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010036 DeltaPartPayload.class,
37 Decoder.ofArray(FileMode.decoder()),
38 Decoder.ofArray(Decoder.ofArray(Xattr.decoder())),
39 ByteString.decoder(),
40 ByteString.decoder().map(DeltaPartPayload::parseDeltaOperationList))
41 .contramap(DeltaPartPayload::preparse);
42
43 private static ByteBuffer preparse(ByteBuffer byteBuffer) {
44 byte compressionByte = byteBuffer.get(0);
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +010045 var dataSlice = byteBuffer.slice(1, byteBuffer.limit() - 1);
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010046 return switch (compressionByte) {
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +010047 case 0 -> dataSlice;
48 case (byte) 'x' -> {
49 try {
50 var dataBytes = new byte[dataSlice.limit()];
51 dataSlice.get(dataBytes);
52 var decompressingInputStream = new XZInputStream(new ByteArrayInputStream(dataBytes));
53
54 var decompressedOutputStream = new ByteArrayOutputStream();
55 decompressingInputStream.transferTo(decompressedOutputStream);
56
57 yield ByteBuffer.wrap(decompressedOutputStream.toByteArray());
58 } catch (IOException e) {
59 // impossible
60 throw new UncheckedIOException(e);
61 }
62 }
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010063 default -> throw new IllegalArgumentException(
64 "unrecognized compression byte '%d'".formatted(compressionByte));
65 };
66 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010067
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010068 private static List<DeltaOperation> parseDeltaOperationList(ByteString byteString) {
69 return byteString.stream().map(DeltaOperation::valueOf).toList();
70 }
71
72 /**
73 * A file mode triple (UID, GID, and permission bits).
74 *
75 * @param uid
76 * @param gid
77 * @param mode
78 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010079 public record FileMode(int uid, int gid, int mode) {
80
81 private static final Decoder<FileMode> DECODER =
82 Decoder.ofStructure(
83 FileMode.class,
84 Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN),
85 Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN),
86 Decoder.ofInt().withByteOrder(ByteOrder.LITTLE_ENDIAN));
87
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010088 /**
89 * Acquires a {@link Decoder} for the enclosing type.
90 *
91 * @return a possibly shared {@link Decoder}.
92 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010093 public static Decoder<FileMode> decoder() {
94 return DECODER;
95 }
96 }
97
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010098 /**
99 * Acquires a {@link Decoder} for the enclosing type.
100 *
101 * <p>FIXME: The first byte is actually a compression byte: {@code 0} for none, {@code 'x'} for
102 * LZMA.
103 *
104 * @return a possibly shared {@link Decoder}.
105 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100106 public static Decoder<DeltaPartPayload> decoder() {
107 return DECODER;
108 }
109}