/// <summary> /// Retrieves information about a file in the archive. /// </summary> /// <param name="filePath">The virual path to the file.</param> /// <returns>The <see cref="AegisFileInfo"/> about the file, or null if it isn't found.</returns> public AegisFileInfo GetFileInfo(AegisVirtualFilePath filePath) { ArgCheck.NotNull(filePath, nameof(filePath)); this.ThrowIfLocked(); return(this.FileIndex.GetFileInfo(filePath)); }
/// <summary> /// Removes a file from the archive. /// </summary> /// <param name="filePath">The virtual path to the file.</param> /// <remarks>If no file exists at that path, this operation is a silent no-op.</remarks> public void RemoveFile(AegisVirtualFilePath filePath) { ArgCheck.NotNull(filePath, nameof(filePath)); this.ThrowIfLocked(); var fileInfo = this.GetFileInfo(filePath); if (fileInfo != null) { this.RemoveFile(fileInfo.FileId); } }
/// <summary> /// Adds a file to the archive. /// </summary> /// <param name="virtualPath">The virtual path to add the file at.</param> /// <param name="fileStream">The file data stream.</param> /// <param name="overwriteIfExists"> /// Whether or not to overwrite existing files at that path. /// If false, an <see cref="InvalidOperationException"/> is thrown if a file already exists at the path. /// </param> /// <returns>Metadata about the file added to the archive.</returns> public AegisFileInfo PutFile(AegisVirtualFilePath virtualPath, Stream fileStream, bool overwriteIfExists = false) { ArgCheck.NotNull(virtualPath, nameof(virtualPath)); ArgCheck.NotNull(fileStream, nameof(fileStream)); this.ThrowIfLocked(); // Remove any existing files stored at the path, if we're allowed to. var existingFileInfo = this.FileIndex.GetFileInfo(virtualPath); if (existingFileInfo != null) { if (!overwriteIfExists) { throw new InvalidOperationException( $"A file with ID '{existingFileInfo.FileId}' already exists at virtual path '{virtualPath}'!"); } this.RemoveFile(existingFileInfo.FileId); } var currentTime = DateTimeOffset.UtcNow; var fileInfo = existingFileInfo ?? new AegisFileInfo( virtualPath, new FileIndexEntry { FileId = Guid.NewGuid(), FilePath = virtualPath.ToString(), AddedTime = currentTime, LastModifiedTime = currentTime, }); fileInfo.IndexEntry.LastModifiedTime = currentTime; // Encrypt the file. // This mechanism won't work for very large files since it loads the whole thing into memory. // For now, this is an acceptable limit for Aegis file archiving. using var memoryStream = new MemoryStream(); fileStream.CopyTo(memoryStream); var encryptedFile = this.ArchiveKey.Encrypt( this.CryptoStrategy, memoryStream.ToArray()); encryptedFile.WriteToBinaryStream(this.SecureArchive.GetEntryWriteStream(fileInfo.ArchiveEntryName)); try { this.FileIndex.Add(fileInfo); this.PersistMetadata(); } catch { // Creating file metadata failed. // Revert adding file to keep the archive to keep it consistent. this.SecureArchive.DeleteEntryIfExists(fileInfo.ArchiveEntryName); throw; } this.FlushArchive(); return(fileInfo); }