@@ -83,7 +83,10 @@ public static IEnumerable<FileEntry> GetFileEntries(FileEntry fileEntry, Extract
8383 // The name length is included in the total size reported in the header
8484 CopyStreamBytes ( fileEntry . Content , entryStream , size - nameLength ) ;
8585
86- yield return new FileEntry ( Encoding . ASCII . GetString ( nameSpan ) . TrimEnd ( '/' ) , entryStream , fileEntry , true , memoryStreamCutoff : options . MemoryStreamCutoff ) ;
86+ yield return new FileEntry ( Encoding . ASCII . GetString ( nameSpan ) . TrimEnd ( '/' ) , entryStream , fileEntry , true , memoryStreamCutoff : options . MemoryStreamCutoff )
87+ {
88+ Metadata = ParseArMetadata ( headerBuffer )
89+ } ;
8790 }
8891 }
8992 else if ( filename . Equals ( '/' ) )
@@ -149,7 +152,10 @@ public static IEnumerable<FileEntry> GetFileEntries(FileEntry fileEntry, Extract
149152
150153 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , innerSize ) ;
151154 CopyStreamBytes ( fileEntry . Content , entryStream , innerSize ) ;
152- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
155+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
156+ {
157+ Metadata = ParseArMetadata ( headerBuffer )
158+ } ;
153159 }
154160 }
155161 fileEntry . Content . Position = fileEntry . Content . Length - 1 ;
@@ -220,7 +226,10 @@ public static IEnumerable<FileEntry> GetFileEntries(FileEntry fileEntry, Extract
220226
221227 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , innerSize ) ;
222228 CopyStreamBytes ( fileEntry . Content , entryStream , innerSize ) ;
223- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
229+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
230+ {
231+ Metadata = ParseArMetadata ( headerBuffer )
232+ } ;
224233 }
225234 }
226235 fileEntry . Content . Position = fileEntry . Content . Length - 1 ;
@@ -241,14 +250,20 @@ public static IEnumerable<FileEntry> GetFileEntries(FileEntry fileEntry, Extract
241250 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , size ) ;
242251 CopyStreamBytes ( fileEntry . Content , entryStream , size ) ;
243252
244- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ; ;
253+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
254+ {
255+ Metadata = ParseArMetadata ( headerBuffer )
256+ } ;
245257 }
246258 else
247259 {
248260 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , size ) ;
249261 CopyStreamBytes ( fileEntry . Content , entryStream , size ) ;
250262
251- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
263+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
264+ {
265+ Metadata = ParseArMetadata ( headerBuffer )
266+ } ;
252267 }
253268 }
254269 else
@@ -329,7 +344,10 @@ public static async IAsyncEnumerable<FileEntry> GetFileEntriesAsync(FileEntry fi
329344 // The name length is included in the total size reported in the header
330345 await CopyStreamBytesAsync ( fileEntry . Content , entryStream , size - nameLength ) . ConfigureAwait ( false ) ;
331346
332- yield return new FileEntry ( Encoding . ASCII . GetString ( nameSpan ) . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
347+ yield return new FileEntry ( Encoding . ASCII . GetString ( nameSpan ) . TrimEnd ( '/' ) , entryStream , fileEntry , true )
348+ {
349+ Metadata = ParseArMetadata ( headerBuffer )
350+ } ;
333351 }
334352 }
335353 else if ( filename . Equals ( '/' ) )
@@ -394,7 +412,10 @@ public static async IAsyncEnumerable<FileEntry> GetFileEntriesAsync(FileEntry fi
394412 }
395413 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , innerSize ) ;
396414 await CopyStreamBytesAsync ( fileEntry . Content , entryStream , innerSize ) . ConfigureAwait ( false ) ;
397- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
415+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
416+ {
417+ Metadata = ParseArMetadata ( headerBuffer )
418+ } ;
398419 }
399420 }
400421 fileEntry . Content . Position = fileEntry . Content . Length - 1 ;
@@ -465,7 +486,10 @@ public static async IAsyncEnumerable<FileEntry> GetFileEntriesAsync(FileEntry fi
465486
466487 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , innerSize ) ;
467488 await CopyStreamBytesAsync ( fileEntry . Content , entryStream , innerSize ) . ConfigureAwait ( false ) ;
468- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
489+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
490+ {
491+ Metadata = ParseArMetadata ( headerBuffer )
492+ } ;
469493 }
470494 }
471495 fileEntry . Content . Position = fileEntry . Content . Length - 1 ;
@@ -485,13 +509,19 @@ public static async IAsyncEnumerable<FileEntry> GetFileEntriesAsync(FileEntry fi
485509 }
486510 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , size ) ;
487511 CopyStreamBytes ( fileEntry . Content , entryStream , size ) ;
488- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
512+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
513+ {
514+ Metadata = ParseArMetadata ( headerBuffer )
515+ } ;
489516 }
490517 else
491518 {
492519 var entryStream = StreamFactory . GenerateAppropriateBackingStream ( options , size ) ;
493520 await CopyStreamBytesAsync ( fileEntry . Content , entryStream , size ) . ConfigureAwait ( false ) ;
494- yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true ) ;
521+ yield return new FileEntry ( filename . TrimEnd ( '/' ) , entryStream , fileEntry , true )
522+ {
523+ Metadata = ParseArMetadata ( headerBuffer )
524+ } ;
495525 }
496526 }
497527 else
@@ -570,6 +600,46 @@ internal static async Task<long> CopyStreamBytesAsync(Stream input, Stream outpu
570600
571601 private const int bufferSize = 4096 ;
572602
603+ /// <summary>
604+ /// Parse file metadata (UID, GID, mode) from an ar file header buffer.
605+ /// </summary>
606+ /// <param name="headerBuffer">The 60-byte ar header</param>
607+ /// <returns>A <see cref="FileEntryMetadata"/> with parsed values, or null if parsing fails.</returns>
608+ internal static FileEntryMetadata ? ParseArMetadata ( byte [ ] headerBuffer )
609+ {
610+ var metadata = new FileEntryMetadata ( ) ;
611+ var hasData = false ;
612+
613+ // ar_uid: bytes 28-33 (6 bytes), decimal
614+ if ( int . TryParse ( Encoding . ASCII . GetString ( headerBuffer [ 28 ..34 ] ) . Trim ( ) , out var uid ) )
615+ {
616+ metadata . Uid = uid ;
617+ hasData = true ;
618+ }
619+
620+ // ar_gid: bytes 34-39 (6 bytes), decimal
621+ if ( int . TryParse ( Encoding . ASCII . GetString ( headerBuffer [ 34 ..40 ] ) . Trim ( ) , out var gid ) )
622+ {
623+ metadata . Gid = gid ;
624+ hasData = true ;
625+ }
626+
627+ // ar_mode: bytes 40-47 (8 bytes), octal
628+ var modeString = Encoding . ASCII . GetString ( headerBuffer [ 40 ..48 ] ) . Trim ( ) ;
629+ try
630+ {
631+ if ( ! string . IsNullOrEmpty ( modeString ) )
632+ {
633+ metadata . Mode = Convert . ToInt64 ( modeString , 8 ) ;
634+ hasData = true ;
635+ }
636+ }
637+ catch ( FormatException ) { }
638+ catch ( OverflowException ) { }
639+
640+ return hasData ? metadata : null ;
641+ }
642+
573643 private readonly static NLog . Logger Logger = NLog . LogManager . GetCurrentClassLogger ( ) ;
574644 }
575645}
0 commit comments