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