@@ -46,6 +46,12 @@ private static void ImportAttributes(SafeFileHandle handle, TarEntry entry)
46
46
47
47
File . SetUnixFileMode ( handle , entry . Mode ) ;
48
48
}
49
+
50
+ var attributes = FileAttributes . NotContentIndexed ;
51
+ if ( entry . EntryType is TarEntryType . SparseFile )
52
+ attributes |= FileAttributes . SparseFile ;
53
+
54
+ File . SetAttributes ( handle , attributes ) ;
49
55
}
50
56
51
57
/// <summary>
@@ -55,7 +61,64 @@ private static void ImportAttributes(SafeFileHandle handle, TarEntry entry)
55
61
/// <param name="token">The token that can be used to cancel the operation.</param>
56
62
/// <returns>A task representing state of asynchronous execution.</returns>
57
63
/// <exception cref="OperationCanceledException">The operation has been canceled.</exception>
58
- public async Task CreateBackupAsync ( Stream output , CancellationToken token = default )
64
+ public Task CreateBackupAsync ( Stream output , CancellationToken token = default )
65
+ => maxLogEntrySize . HasValue ? CreateSparseBackupAsync ( output , token ) : CreateRegularBackupAsync ( output , token ) ;
66
+
67
+ private async Task CreateSparseBackupAsync ( Stream output , CancellationToken token )
68
+ {
69
+ var tarProcess = new Process
70
+ {
71
+ StartInfo = new ( )
72
+ {
73
+ FileName = "tar" ,
74
+ WorkingDirectory = Location . FullName ,
75
+ } ,
76
+ } ;
77
+
78
+ var outputArchive = Path . Combine ( Path . GetTempPath ( ) , Path . GetRandomFileName ( ) ) ;
79
+ tarProcess . StartInfo . ArgumentList . Add ( "cfS" ) ;
80
+ tarProcess . StartInfo . ArgumentList . Add ( outputArchive ) ;
81
+
82
+ FileStream ? archiveStream = null ;
83
+ await syncRoot . AcquireAsync ( LockType . StrongReadLock , token ) . ConfigureAwait ( false ) ;
84
+ try
85
+ {
86
+ foreach ( var file in Location . EnumerateFiles ( ) )
87
+ {
88
+ tarProcess . StartInfo . ArgumentList . Add ( file . Name ) ;
89
+ }
90
+
91
+ tarProcess . StartInfo . ArgumentList . Add ( $ "--format={ GetArchiveFormat ( backupFormat ) } ") ;
92
+ tarProcess . Start ( ) ;
93
+ await tarProcess . WaitForExitAsync ( token ) . ConfigureAwait ( false ) ;
94
+
95
+ if ( tarProcess . ExitCode is not 0 )
96
+ throw new InvalidOperationException ( ) { HResult = tarProcess . ExitCode } ;
97
+
98
+ archiveStream = new ( outputArchive , FileMode . Open , FileAccess . Read , FileShare . Read , 4096 , FileOptions . SequentialScan | FileOptions . Asynchronous | FileOptions . DeleteOnClose ) ;
99
+ await archiveStream . CopyToAsync ( output , token ) . ConfigureAwait ( false ) ;
100
+ await output . FlushAsync ( token ) . ConfigureAwait ( false ) ;
101
+ }
102
+ finally
103
+ {
104
+ syncRoot . Release ( LockType . StrongReadLock ) ;
105
+ tarProcess . Dispose ( ) ;
106
+
107
+ if ( archiveStream is not null )
108
+ await archiveStream . DisposeAsync ( ) . ConfigureAwait ( false ) ;
109
+ }
110
+
111
+ static string GetArchiveFormat ( TarEntryFormat format ) => format switch
112
+ {
113
+ TarEntryFormat . Gnu => "gnu" ,
114
+ TarEntryFormat . Pax => "pax" ,
115
+ TarEntryFormat . Ustar => "ustar" ,
116
+ TarEntryFormat . V7 => "v7" ,
117
+ _ => "gnu" ,
118
+ } ;
119
+ }
120
+
121
+ private async Task CreateRegularBackupAsync ( Stream output , CancellationToken token )
59
122
{
60
123
TarWriter ? archive = null ;
61
124
await syncRoot . AcquireAsync ( LockType . StrongReadLock , token ) . ConfigureAwait ( false ) ;
0 commit comments