@@ -127,29 +127,33 @@ public static async Task DeleteOrRecycleAsync(
127127
128128 if ( ! specifics . Options . IsRecycleBinEnabled ( ) || deleteImmediately )
129129 {
130- await ciphertextSourceFolder . DeleteAsync ( ciphertextItem , cancellationToken ) ;
130+ await DeleteImmediatelyAsync ( ciphertextSourceFolder , ciphertextItem , cancellationToken ) ;
131131 return ;
132132 }
133133
134+ // Allocate Directory ID for later use
135+ var directoryId = AbstractPathHelpers . AllocateDirectoryId ( specifics . Security , ciphertextSourceFolder . Id ) ;
136+
137+ // Decrypt the plaintext name
138+ var plaintextName = await AbstractPathHelpers . DecryptNameAsync ( ciphertextItem . Name , ciphertextSourceFolder , specifics , cancellationToken ) ?? string . Empty ;
139+ if ( plaintextName is null )
140+ throw new FormatException ( "Could not decrypt name for recycle bin configuration file." ) ;
141+
142+ // Check for wildcard file names
134143 if ( OperatingSystem . IsMacOS ( ) || OperatingSystem . IsMacCatalyst ( ) )
135144 {
136- var parentFolder = await ciphertextItem . GetParentAsync ( cancellationToken ) ;
137- if ( parentFolder is not null )
145+ if ( plaintextName == ".DS_Store" || plaintextName . StartsWith ( "._" , StringComparison . Ordinal ) )
138146 {
139- var plaintextName = await AbstractPathHelpers . DecryptNameAsync ( ciphertextItem . Name , parentFolder , specifics , cancellationToken ) ?? string . Empty ;
140- if ( plaintextName == ".DS_Store" || plaintextName . StartsWith ( "._" , StringComparison . Ordinal ) )
141- {
142- // .DS_Store and Apple Double files are not supported by the recycle bin, delete immediately
143- await ciphertextSourceFolder . DeleteAsync ( ciphertextItem , cancellationToken ) ;
144- return ;
145- }
147+ // .DS_Store and Apple Double files are unsupported by the recycle bin, delete immediately
148+ await DeleteImmediatelyAsync ( ciphertextSourceFolder , ciphertextItem , cancellationToken ) ;
149+ return ;
146150 }
147151
148152 // Check if the file was recently created (likely part of a copy operation)
149153 // On macOS, Finder creates files and immediately deletes them during copy operations
150154 if ( await IsRecentlyCreatedAsync ( ciphertextItem , cancellationToken ) )
151155 {
152- await ciphertextSourceFolder . DeleteAsync ( ciphertextItem , cancellationToken ) ;
156+ await DeleteImmediatelyAsync ( ciphertextSourceFolder , ciphertextItem , cancellationToken ) ;
153157 return ;
154158 }
155159 }
@@ -172,15 +176,11 @@ public static async Task DeleteOrRecycleAsync(
172176 var availableSize = specifics . Options . RecycleBinSize - occupiedSize ;
173177 if ( availableSize < sizeHint )
174178 {
175- await ciphertextSourceFolder . DeleteAsync ( ciphertextItem , cancellationToken ) ;
179+ await DeleteImmediatelyAsync ( ciphertextSourceFolder , ciphertextItem , cancellationToken ) ;
176180 return ;
177181 }
178182 }
179183
180- // Get source Directory ID
181- var directoryId = AbstractPathHelpers . AllocateDirectoryId ( specifics . Security , ciphertextSourceFolder . Id ) ;
182- var directoryIdResult = await AbstractPathHelpers . GetDirectoryIdAsync ( ciphertextSourceFolder , specifics , directoryId , cancellationToken ) ;
183-
184184 // Rename and move item
185185 var guid = Guid . NewGuid ( ) . ToString ( ) ;
186186 _ = await modifiableRecycleBin . MoveStorableFromAsync ( ciphertextItem , ciphertextSourceFolder , false , guid , null , cancellationToken ) ;
@@ -189,21 +189,25 @@ public static async Task DeleteOrRecycleAsync(
189189 var configurationFile = await modifiableRecycleBin . CreateFileAsync ( $ "{ guid } .json", false , cancellationToken ) ;
190190 await using ( var configurationStream = await configurationFile . OpenWriteAsync ( cancellationToken ) )
191191 {
192- // Decrypt the plaintext name and parent ID
193- var plaintextName = await AbstractPathHelpers . DecryptNameAsync ( ciphertextItem . Name , ciphertextSourceFolder , specifics , cancellationToken ) ?? string . Empty ;
194- var plaintextParentId = await AbstractPathHelpers . GetPlaintextPathAsync ( ( IStorableChild ) ciphertextSourceFolder , specifics , cancellationToken ) ?? string . Empty ;
192+ // Decrypt the plaintext parent ID
193+ var plaintextParentId = await AbstractPathHelpers . GetPlaintextPathAsync ( ( IStorableChild ) ciphertextSourceFolder , specifics , cancellationToken ) ;
194+ if ( plaintextParentId is null )
195+ throw new FormatException ( "Could not decrypt parent path for the recycle bin configuration file." ) ;
196+
197+ // Determine if Directory ID is present
198+ var isDirectoryIdPresent = directoryId . IsEmpty ( ) || directoryId . IsAllZeros ( ) ;
195199
196200 // Encrypt the new plaintext name and parent ID
197- var newCiphertextName = RecycleBinItemDataModel . Encrypt ( plaintextName , specifics . Security , directoryIdResult ? directoryId : ReadOnlySpan < byte > . Empty ) ;
198- var newCiphertextParentId = RecycleBinItemDataModel . Encrypt ( plaintextParentId , specifics . Security , directoryIdResult ? directoryId : ReadOnlySpan < byte > . Empty ) ;
201+ var newCiphertextName = RecycleBinItemDataModel . Encrypt ( plaintextName , specifics . Security , isDirectoryIdPresent ? directoryId : ReadOnlySpan < byte > . Empty ) ;
202+ var newCiphertextParentId = RecycleBinItemDataModel . Encrypt ( plaintextParentId , specifics . Security , isDirectoryIdPresent ? directoryId : ReadOnlySpan < byte > . Empty ) ;
199203
200204 // Serialize configuration data model
201205 await using var serializedStream = await streamSerializer . SerializeAsync (
202206 new RecycleBinItemDataModel ( )
203207 {
204208 Name = newCiphertextName ,
205209 ParentId = newCiphertextParentId ,
206- DirectoryId = directoryIdResult ? directoryId : [ ] ,
210+ DirectoryId = isDirectoryIdPresent ? directoryId : [ ] ,
207211 DeletionTimestamp = DateTime . Now ,
208212 Size = sizeHint
209213 } , cancellationToken ) ;
@@ -220,6 +224,11 @@ public static async Task DeleteOrRecycleAsync(
220224 var newSize = occupiedSize + sizeHint ;
221225 await SetOccupiedSizeAsync ( modifiableRecycleBin , newSize , cancellationToken ) ;
222226 }
227+
228+ static async Task DeleteImmediatelyAsync ( IModifiableFolder ciphertextSourceFolder , IStorableChild ciphertextItem , CancellationToken cancellationToken )
229+ {
230+ await ciphertextSourceFolder . DeleteAsync ( ciphertextItem , deleteImmediately : true , cancellationToken : cancellationToken ) ;
231+ }
223232 }
224233
225234 private static async Task < string > GetAvailableDestinationNameAsync ( IFolder ciphertextDestinationFolder , string ciphertextName , string plaintextOriginalName , FileSystemSpecifics specifics , CancellationToken cancellationToken )
0 commit comments