Repository: brendan-duncan/archive

Issue:
Symlinks not created correctly when creating tarfiles
When creating a tarfile with symlinks, they are not being created. Instead, empty files with a size of 0 are created.
This can simply be fixed by adding the symlink location to the tar header after the type flag, I will open a PR later.



Fix patch:
diff --git a/lib/src/tar/tar_file.dart b/lib/src/tar/tar_file.dart
index 88bbeee2..efb01284 100644
--- a/lib/src/tar/tar_file.dart
+++ b/lib/src/tar/tar_file.dart
@@ -51,7 +51,7 @@ class TarFile {
   int lastModTime = 0; // 12 bytes
   int checksum = 0; // 8 bytes
   String typeFlag = '0'; // 1 byte
-  late String nameOfLinkedFile; // 100 bytes
+  String? nameOfLinkedFile; // 100 bytes
   // UStar Format
   String ustarIndicator = ''; // 6 bytes (ustar)
   String ustarVersion = ''; // 2 bytes (00)
@@ -146,6 +146,11 @@ class TarFile {
     _writeInt(header, lastModTime, 12);
     _writeString(header, '        ', 8); // checksum placeholder
     _writeString(header, typeFlag, 1);
+    if (nameOfLinkedFile != null) {
+      _writeString(header, nameOfLinkedFile!, 100);
+    } else {
+      _writeString(header, '', 100);
+    }
 
     final remainder = 512 - header.length;
     var nulls = Uint8List(remainder); // typed arrays default to 0.
diff --git a/lib/src/tar_decoder.dart b/lib/src/tar_decoder.dart
index b318ff59..12dc21bd 100644
--- a/lib/src/tar_decoder.dart
+++ b/lib/src/tar_decoder.dart
@@ -91,7 +91,10 @@ class TarDecoder {
       file.lastModTime = tf.lastModTime;
       file.isFile = tf.isFile;
       file.isSymbolicLink = tf.typeFlag == TarFile.TYPE_SYMBOLIC_LINK;
-      file.nameOfLinkedFile = tf.nameOfLinkedFile;
+
+      if (tf.nameOfLinkedFile != null) {
+        file.nameOfLinkedFile = tf.nameOfLinkedFile!;
+      }
 
       archive.addFile(file);
     }
diff --git a/lib/src/tar_encoder.dart b/lib/src/tar_encoder.dart
index 6e086e81..06c34f57 100644
--- a/lib/src/tar_encoder.dart
+++ b/lib/src/tar_encoder.dart
@@ -52,6 +52,10 @@ class TarEncoder {
     ts.groupId = file.groupId;
     ts.lastModTime = file.lastModTime;
     ts.content = file.content;
+    if (file.isSymbolicLink) {
+      ts.typeFlag = TarFile.TYPE_SYMBOLIC_LINK;
+      ts.nameOfLinkedFile = file.nameOfLinkedFile;
+    }
     ts.write(_outputStream);
   }
 


Test patch:
diff --git a/test/tests/tar_test.dart b/test/tests/tar_test.dart
index c10b5689..d550a13e 100644
--- a/test/tests/tar_test.dart
+++ b/test/tests/tar_test.dart
@@ -168,6 +168,18 @@ void main() {
       ..writeAsBytesSync(tar);
   });
 
+  test('tar file with symlink', () {
+    ArchiveFile symlink = ArchiveFile('file.txt', 1, List<int>.empty())
+      ..isSymbolicLink = true
+      ..nameOfLinkedFile = 'file2.txt';
+    final tar = TarEncoder().encode(Archive()..addFile(symlink));
+    File(p.join(testDirPath, 'out/tar_encoded.tar'))
+      ..createSync(recursive: true)
+      ..writeAsBytesSync(tar);
+    final archive = TarDecoder().decodeBytes(tar);
+    expect(archive.files[0].isSymbolicLink, true);
+  });
+
   test('long file name', () {
     final file = File(p.join(testDirPath, 'res/tar/x.tar'));
     final bytes = file.readAsBytesSync();