blob: f89d414da8cfe6b0ab8a0cbab8e570abfae65071 [file] [log] [blame]
Matthias Andreas Benkardb5d657a2022-02-03 21:14:30 +01001// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +01005package eu.mulk.jgvariant.ostree;
6
7import eu.mulk.jgvariant.core.Decoder;
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +01008import java.io.ByteArrayInputStream;
9import java.io.ByteArrayOutputStream;
10import java.io.IOException;
11import java.io.UncheckedIOException;
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010012import java.nio.ByteBuffer;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010013import java.nio.ByteOrder;
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010014import java.util.ArrayList;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010015import java.util.List;
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +010016import org.tukaani.xz.XZInputStream;
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010017
18/**
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010019 * A payload file from a static delta.
20 *
21 * <p>The first byte is a compression byte: {@code 0} for none, {@code 'x'} for LZMA. The actual
22 * GVariant data begins right after.
23 *
24 * <p>Reference: {@code
25 * ostree-repo-static-delta-private.h#OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0}
26 *
27 * @param fileModes the {@link FileMode}s of the files generated by this delta payload.
28 * @param xattrs the {@link Xattr}s of the files generated by this delta payload.
29 * @param rawDataSource the data bytes used in the delta operations.
30 * @param operations the operations to apply during delta patching.
31 * @see DeltaSuperblock
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010032 */
33public record DeltaPartPayload(
34 List<FileMode> fileModes,
35 List<List<Xattr>> xattrs,
36 ByteString rawDataSource,
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010037 List<DeltaOperation> operations) {
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010038
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010039 private static ByteBuffer preparse(ByteBuffer byteBuffer) {
40 byte compressionByte = byteBuffer.get(0);
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +010041 var dataSlice = byteBuffer.slice(1, byteBuffer.limit() - 1);
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010042 return switch (compressionByte) {
Matthias Andreas Benkard50a626d2021-12-30 19:13:49 +010043 case 0 -> dataSlice;
44 case (byte) 'x' -> {
45 try {
46 var dataBytes = new byte[dataSlice.limit()];
47 dataSlice.get(dataBytes);
48 var decompressingInputStream = new XZInputStream(new ByteArrayInputStream(dataBytes));
49
50 var decompressedOutputStream = new ByteArrayOutputStream();
51 decompressingInputStream.transferTo(decompressedOutputStream);
52
53 yield ByteBuffer.wrap(decompressedOutputStream.toByteArray());
54 } catch (IOException e) {
55 // impossible
56 throw new UncheckedIOException(e);
57 }
58 }
Matthias Andreas Benkarde74b8002021-12-30 18:52:10 +010059 default -> throw new IllegalArgumentException(
60 "unrecognized compression byte '%d'".formatted(compressionByte));
61 };
62 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010063
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010064 private static List<DeltaOperation> parseDeltaOperationList(
65 ByteString byteString, List<ObjectType> objectTypes) {
66 List<DeltaOperation> deltaOperations = new ArrayList<>();
67 var byteBuffer = ByteBuffer.wrap(byteString.bytes());
68 int objectIndex = 0;
69
70 while (byteBuffer.hasRemaining()) {
71 var currentOperation = DeltaOperation.readFrom(byteBuffer, objectTypes.get(objectIndex));
72 deltaOperations.add(currentOperation);
73 if (currentOperation instanceof DeltaOperation.Close
74 || currentOperation instanceof DeltaOperation.OpenSpliceAndCloseMeta
75 || currentOperation instanceof DeltaOperation.OpenSpliceAndCloseReal) {
76 ++objectIndex;
77 }
78 }
79
80 return deltaOperations;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010081 }
82
83 /**
84 * A file mode triple (UID, GID, and permission bits).
85 *
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010086 * @param uid the user ID that owns the file.
87 * @param gid the group ID that owns the file.
88 * @param mode the POSIX permission bits.
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010089 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010090 public record FileMode(int uid, int gid, int mode) {
91
92 private static final Decoder<FileMode> DECODER =
93 Decoder.ofStructure(
94 FileMode.class,
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010095 Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
96 Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN),
97 Decoder.ofInt().withByteOrder(ByteOrder.BIG_ENDIAN));
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +010098
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010099 /**
100 * Acquires a {@link Decoder} for the enclosing type.
101 *
102 * @return a possibly shared {@link Decoder}.
103 */
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100104 public static Decoder<FileMode> decoder() {
105 return DECODER;
106 }
107 }
108
Matthias Andreas Benkard05114642021-12-29 21:51:29 +0100109 /**
110 * Acquires a {@link Decoder} for the enclosing type.
111 *
Matthias Andreas Benkard05114642021-12-29 21:51:29 +0100112 * @return a possibly shared {@link Decoder}.
113 */
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +0100114 public static Decoder<DeltaPartPayload> decoder(DeltaMetaEntry deltaMetaEntry) {
115 var objectTypes =
116 deltaMetaEntry.objects().stream().map(DeltaMetaEntry.DeltaObject::objectType).toList();
117 return Decoder.ofStructure(
118 DeltaPartPayload.class,
119 Decoder.ofArray(FileMode.decoder()),
120 Decoder.ofArray(Decoder.ofArray(Xattr.decoder())),
121 ByteString.decoder(),
122 ByteString.decoder()
123 .map(byteString -> parseDeltaOperationList(byteString, objectTypes)))
124 .contramap(DeltaPartPayload::preparse);
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100125 }
126}