Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update tar #3976

Merged
merged 3 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/src/third_party/tar/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 1.0.1

- Fix an incompatibility with Dart 3.1.

## 1.0.0

- __Breaking__ Add class modifiers where applicable.

## 0.5.6

- Allow cancelling a `TarEntry.contents` subscription before reading more files.
Expand Down
6 changes: 2 additions & 4 deletions lib/src/third_party/tar/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
include: package:extra_pedantic/analysis_options.2.0.0.yaml
include: package:extra_pedantic/analysis_options.4.0.0.yaml

analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
language:
strict-inference: true
strict-raw-types: true
Expand All @@ -12,6 +9,7 @@ linter:
rules:
close_sinks: false # This rule has just too many false-positives...
comment_references: true
package_api_docs: true
literal_only_boolean_expressions: false # Nothing wrong with a little while(true)
parameter_assignments: false
unnecessary_await_in_return: false
Expand Down
7 changes: 2 additions & 5 deletions lib/src/third_party/tar/lib/src/entry.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import 'dart:async';

import 'package:meta/meta.dart';

import 'header.dart';

/// An entry in a tar file.
///
/// Usually, tar entries are read from a stream, and they're bound to the stream
/// from which they've been read. This means that they can only be read once,
/// and that only one [TarEntry] is active at a time.
@sealed
class TarEntry {
final class TarEntry {
/// The parsed [TarHeader] of this tar entry.
final TarHeader header;

Expand Down Expand Up @@ -58,7 +55,7 @@ class TarEntry {
}

/// A tar entry stored in memory.
class SynchronousTarEntry extends TarEntry {
final class SynchronousTarEntry extends TarEntry {
/// The contents of this tar entry as a byte array.
final List<int> data;

Expand Down
10 changes: 8 additions & 2 deletions lib/src/third_party/tar/lib/src/exception.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import 'package:meta/meta.dart';

/// An exception indicating that there was an issue parsing a `.tar` file.
/// Intended to be seen by the user.
class TarException extends FormatException {
///
/// The [message] contains reported from this exception contains details on the
/// location of the parsing error.
///
/// This is the only exception that should be thrown by the `tar` package. Other
/// exceptions are either a bug in this package or errors thrown as a response
/// to API misuse.
final class TarException extends FormatException {
@internal
TarException(String message) : super(message);

Expand Down
182 changes: 108 additions & 74 deletions lib/src/third_party/tar/lib/src/format.dart
Original file line number Diff line number Diff line change
@@ -1,87 +1,46 @@
import 'package:meta/meta.dart';

/// Handy map to help us translate [TarFormat] values to their names.
/// Be sure to keep this consistent with the constant initializers in
/// [TarFormat].
const _formatNames = {
1: 'V7',
2: 'USTAR',
4: 'PAX',
8: 'GNU',
16: 'STAR',
};

/// Holds the possible TAR formats that a file could take.
///
/// This library only supports the V7, USTAR, PAX, GNU, and STAR formats.
@sealed
class TarFormat {
/// This library supports the V7, USTAR, PAX, GNU, and STAR formats. The
/// [MaybeTarFormat] class generally describes any combination of those formats
/// and represents that we don't know the exact format yet. As soon as we do
/// know, the [TarFormat] enum represents the exact format of a header.
sealed class MaybeTarFormat {
/// The TAR formats are encoded in powers of two in [_value], such that we
/// can refine our guess via bit operations as we discover more information
/// about the TAR file.
/// A value of 0 means that the format is invalid.
final int _value;

const TarFormat._internal(this._value);

@override
int get hashCode => _value;

@override
bool operator ==(Object other) {
if (other is! TarFormat) return false;
int get _value;

return _value == other._value;
factory MaybeTarFormat._(int value) {
return switch (value) {
1 => TarFormat.v7,
2 => TarFormat.ustar,
4 => TarFormat.pax,
8 => TarFormat.gnu,
16 => TarFormat.star,
final other => _MaybeTarFormat(other),
};
}

@override
String toString() {
if (!isValid()) return 'Invalid';

final possibleNames = _formatNames.entries
.where((e) => _value & e.key != 0)
.map((e) => e.value);

return possibleNames.join(' or ');
}

/// Returns if [other] is a possible resolution of `this`.
///
/// For example, a [TarFormat] with a value of 6 means that we do not have
/// enough information to determine if it is [TarFormat.ustar] or
/// [TarFormat.pax], so either of them could be possible resolutions of
/// `this`.
bool has(TarFormat other) => _value & other._value != 0;

/// Returns a new [TarFormat] that signifies that it can be either
/// Returns a new [MaybeTarFormat] that signifies that it can be either
/// `this` or [other]'s format.
///
/// **Example:**
/// ```dart
/// TarFormat format = TarFormat.USTAR | TarFormat.PAX;
/// TarFormat format = TarFormat.ustar | TarFormat.pax;
/// ```
///
/// The above code would signify that we have limited `format` to either
/// the USTAR or PAX format, but need further information to refine the guess.
TarFormat operator |(TarFormat other) {
return mayBe(other);
}
MaybeTarFormat operator |(TarFormat other);

/// Returns a new [TarFormat] that signifies that it can be either
/// `this` or [other]'s format.
///
/// **Example:**
/// ```dart
/// TarFormat format = TarFormat.PAX;
/// format = format.mayBe(TarFormat.USTAR);
/// ```
/// Returns if [other] is a possible resolution of `this`.
///
/// The above code would signify that we learnt that in addition to being a
/// PAX format, it could also be of the USTAR format.
TarFormat mayBe(TarFormat? other) {
if (other == null) return this;
return TarFormat._internal(_value | other._value);
}
/// For example, a [TarFormat] with a value of 6 means that we do not have
/// enough information to determine if it is [TarFormat.ustar] or
/// [TarFormat.pax], so either of them could be possible resolutions of
/// `this`.
bool has(MaybeTarFormat other);

/// Returns a new [TarFormat] that signifies that it can only be [other]'s
/// format.
Expand All @@ -98,16 +57,20 @@ class TarFormat {
///
/// If `has(other) == false`, [mayOnlyBe] will result in an unknown
/// [TarFormat].
TarFormat mayOnlyBe(TarFormat other) {
return TarFormat._internal(_value & other._value);
}
MaybeTarFormat mayOnlyBe(MaybeTarFormat other);

/// Returns if this format might be valid.
///
/// This returns true as well even if we have yet to fully determine what the
/// format is.
bool isValid() => _value > 0;
bool get valid;
}

/// A fully resolved tar format.
///
/// When we know that a tar entry must use a specific format, this is represented
/// with a value from this [TarFormat] enum.
enum TarFormat implements MaybeTarFormat {
/// Original Unix Version 7 (V7) AT&T tar tool prior to standardization.
///
/// The structure of the V7 Header consists of the following:
Expand All @@ -132,7 +95,7 @@ class TarFormat {
/// https://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5&format=html
/// https://www.gnu.org/software/tar/manual/html_chapter/tar_15.html#SEC188
/// http://cdrtools.sourceforge.net/private/man/star/star.4.html
static const v7 = TarFormat._internal(1);
v7(1, 'V7'),

/// USTAR (Unix Standard TAR) header format defined in POSIX.1-1988.
///
Expand Down Expand Up @@ -178,7 +141,7 @@ class TarFormat {
/// https://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5&format=html
/// https://www.gnu.org/software/tar/manual/html_chapter/tar_15.html#SEC188
/// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06
static const ustar = TarFormat._internal(2);
ustar(2, 'USTAR'),

/// PAX header format defined in POSIX.1-2001.
///
Expand All @@ -196,7 +159,7 @@ class TarFormat {
/// https://www.gnu.org/software/tar/manual/html_chapter/tar_15.html#SEC188
/// http://cdrtools.sourceforge.net/private/man/star/star.4.html
/// http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html
static const pax = TarFormat._internal(4);
pax(4, 'PAX'),

/// GNU header format.
///
Expand Down Expand Up @@ -246,7 +209,7 @@ class TarFormat {
///
/// Reference:
/// https://www.gnu.org/software/tar/manual/html_node/Standard.html
static const gnu = TarFormat._internal(8);
gnu(8, 'GNU'),

/// Schily's TAR format, which is incompatible with USTAR.
/// This does not cover STAR extensions to the PAX format; these fall under
Expand Down Expand Up @@ -284,5 +247,76 @@ class TarFormat {
///
/// Reference:
/// http://cdrtools.sourceforge.net/private/man/star/star.4.html
static const star = TarFormat._internal(16);
star(16, 'STAR'),
;

@override
final int _value;

final String _name;

const TarFormat(this._value, this._name);

@override
bool get valid => true;

@override
MaybeTarFormat operator |(TarFormat other) {
return other == this ? this : _MaybeTarFormat(_value | other._value);
}

@override
bool has(MaybeTarFormat other) {
return other == this;
}

@override
MaybeTarFormat mayOnlyBe(MaybeTarFormat other) {
return MaybeTarFormat._(_value & other._value);
}

@override
String toString() => _name;
}

final class _MaybeTarFormat implements MaybeTarFormat {
// Note: We never represent a single tar format in a _MaybeTarFormat, these
// are represented in the TarFormat enum.
@override
final int _value;

const _MaybeTarFormat(this._value);

@override
int get hashCode => _value;

@override
bool operator ==(Object other) {
if (other is! TarFormat) return false;

return _value == other._value;
}

@override
String toString() {
if (!valid) return 'Invalid';

return TarFormat.values.where(has).map((e) => e._name).join(' or ');
}

@override
bool has(MaybeTarFormat other) => _value & other._value != 0;

@override
bool get valid => _value != 0;

@override
MaybeTarFormat mayOnlyBe(MaybeTarFormat other) {
return MaybeTarFormat._(_value & other._value);
}

@override
MaybeTarFormat operator |(TarFormat other) {
return MaybeTarFormat._(_value | other._value);
}
}
20 changes: 13 additions & 7 deletions lib/src/third_party/tar/lib/src/header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ enum TypeFlag {
///
/// A tar header stores meta-information about the matching tar entry, such as
/// its name.
@sealed
abstract class TarHeader {
sealed class TarHeader {
/// Type of header entry. In the V7 TAR format, this field was known as the
/// link flag.
TypeFlag get typeFlag;
Expand Down Expand Up @@ -125,7 +124,11 @@ abstract class TarHeader {
int get devMinor;

/// The TAR format of the header.
TarFormat get format;
///
/// When this library is sure it knows the format of the tar entry, this will
/// be a [TarFormat] enum value. In other cases, a [MaybeTarFormat] could
/// represent multiple possible formats.
MaybeTarFormat get format;

/// Checks if this header indicates that the file will have content.
bool get hasContent {
Expand Down Expand Up @@ -226,11 +229,14 @@ class HeaderImpl extends TarHeader {
int devMinor;

@override
TarFormat format;
MaybeTarFormat format;

@override
TypeFlag get typeFlag {
return internalTypeFlag == TypeFlag.regA ? TypeFlag.reg : internalTypeFlag;
return switch (internalTypeFlag) {
TypeFlag.regA => TypeFlag.reg, // normalize
final other => other,
};
}

/// This constructor is meant to help us deal with header-only headers (i.e.
Expand Down Expand Up @@ -281,7 +287,7 @@ class HeaderImpl extends TarHeader {
throw TarException.header('Indicates an invalid size of $size');
}

if (format.isValid() && format != TarFormat.v7) {
if (format.valid && format != TarFormat.v7) {
// If it's a valid header that is not of the v7 format, it will have the
// USTAR fields
header
Expand Down Expand Up @@ -370,7 +376,7 @@ class HeaderImpl extends TarHeader {
/// Checks that [rawHeader] represents a valid tar header based on the
/// checksum, and then attempts to guess the specific format based
/// on magic values. If the checksum fails, then an error is thrown.
TarFormat _getFormat(Uint8List rawHeader) {
MaybeTarFormat _getFormat(Uint8List rawHeader) {
final checksum = rawHeader.readOctal(checksumOffset, checksumLength);

// Modern TAR archives use the unsigned checksum, but we check the signed
Expand Down
Loading