1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.IO; 10 using Antmicro.Renode.Utilities; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Exceptions; 13 14 namespace Antmicro.Renode.Storage 15 { 16 public class LBABackend : IDisposable 17 { LBABackend(int numberOfBlocks, int blockSize = 512)18 public LBABackend(int numberOfBlocks, int blockSize = 512) : this(TemporaryFilesManager.Instance.GetTemporaryFile(), numberOfBlocks, blockSize) 19 { 20 } 21 LBABackend(string underlyingFile, int? numberOfBlocks = null, int blockSize = 512, bool persistent = true)22 public LBABackend(string underlyingFile, int? numberOfBlocks = null, int blockSize = 512, bool persistent = true) 23 { 24 if(!File.Exists(underlyingFile)) 25 { 26 throw new RecoverableException(new FileNotFoundException("File not found: {0}.".FormatWith(underlyingFile), underlyingFile)); 27 } 28 this.blockSize = blockSize; 29 this.persistent = persistent; 30 this.underlyingFile = underlyingFile; 31 this.numberOfBlocks = Touch(numberOfBlocks); 32 } 33 34 public int BlockSize 35 { 36 get 37 { 38 return blockSize; 39 } 40 } 41 42 public int NumberOfBlocks 43 { 44 get 45 { 46 return numberOfBlocks; 47 } 48 } 49 50 public string UnderlyingFile 51 { 52 get 53 { 54 return underlyingFile; 55 } 56 } 57 Read(int startingBlock, int numberOfBlocksToRead = 1)58 public byte[] Read(int startingBlock, int numberOfBlocksToRead = 1) 59 { 60 Touch(numberOfBlocks); 61 var bytesToRead = blockSize * numberOfBlocksToRead; 62 Logger.LogAs(this, LogLevel.Noisy, "Reading {0} blocks ({1}B), starting at block no {2}.", 63 numberOfBlocksToRead, Misc.NormalizeBinary(numberOfBlocksToRead * bytesToRead), startingBlock); 64 file.Seek((long)blockSize * startingBlock, SeekOrigin.Begin); 65 return file.ReadBytes(bytesToRead); 66 } 67 Write(int startingBlock, byte[] data, int numberOfBlocksToWrite = 1)68 public void Write(int startingBlock, byte[] data, int numberOfBlocksToWrite = 1) 69 { 70 Touch(numberOfBlocks); 71 Logger.LogAs(this, LogLevel.Noisy, "Writing {0} blocks ({1}B), starting at block no {2}.", 72 numberOfBlocksToWrite, Misc.NormalizeBinary(data.Length), startingBlock); 73 if(data.Length > (long)blockSize * numberOfBlocksToWrite) 74 { 75 throw new InvalidOperationException("Cannot write more data than the LBA block size multiplied by number of blocks to read."); 76 } 77 file.Seek((long)blockSize * startingBlock, SeekOrigin.Begin); 78 file.Write(data, 0, data.Length); 79 } 80 Dispose()81 public void Dispose() 82 { 83 if(file != null) 84 { 85 file.Dispose(); 86 } 87 } 88 ToNumberOfBlocks(long value)89 private int ToNumberOfBlocks(long value) 90 { 91 return checked((int)(value / blockSize) + ((value % blockSize > 0) ? 1 : 0)); 92 } 93 Touch(int? numberOfBlocks)94 private int Touch(int? numberOfBlocks) 95 { 96 if(file != null) 97 { 98 return 0; 99 } 100 if(!persistent) 101 { 102 var tempFileName = TemporaryFilesManager.Instance.GetTemporaryFile(); 103 FileCopier.Copy(underlyingFile, tempFileName, true); 104 underlyingFile = tempFileName; 105 } 106 if(numberOfBlocks == null) 107 { 108 numberOfBlocks = ToNumberOfBlocks(new FileInfo(underlyingFile).Length); 109 } 110 var size = blockSize * (long)numberOfBlocks; 111 file = new SerializableStreamView(new FileStream(underlyingFile, FileMode.OpenOrCreate), size); 112 113 return (int)numberOfBlocks; 114 } 115 116 private readonly int blockSize; 117 private readonly int numberOfBlocks; 118 private readonly bool persistent; 119 private string underlyingFile; 120 private SerializableStreamView file; 121 } 122 } 123 124