#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously -- deleting local file is synchronous public async Task Delete(string objectName, CancellationToken cancellationToken) #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { var path = FilesystemUtils.PreparePath(objectName, _context); var recyclePath = _recycleContext is object?FilesystemUtils.PreparePath(objectName, _recycleContext) : null; cancellationToken.ThrowIfCancellationRequested(); if (recyclePath is object) { if (Path.GetDirectoryName(path) is { } directory) { Directory.CreateDirectory(directory); } try { File.Move(path, recyclePath, true); } catch (FileNotFoundException) { return; } catch (DirectoryNotFoundException) { return; } } else { try { File.Delete(path); } catch (DirectoryNotFoundException) { return; } } var root = Path.GetDirectoryName(FilesystemUtils.PreparePath("", _context)); if (root is null) { return; } var rootLength = root.Length; while (true) { if (Path.GetDirectoryName(path) is not { } directory) { break; } cancellationToken.ThrowIfCancellationRequested(); if (Directory.EnumerateFileSystemEntries(directory).Any()) { break; } var dirLength = directory.Length; if (dirLength == rootLength) { break; } if (dirLength < rootLength) { throw new InvalidProgramException(FormattableString.Invariant($"Recursive deletion of directory \"{directory}\" attempted to escape root \"{root}\".")); } Directory.Delete(directory); path = directory; } }