root / trunk / Pithos.Core / Agents / BlockUpdater.cs @ 2f5fcd2f
History | View | Annotate | Download (15.8 kB)
1 | 796a7c29 | pkanavos | #region |
---|---|---|---|
2 | 796a7c29 | pkanavos | /* ----------------------------------------------------------------------- |
3 | 796a7c29 | pkanavos | * <copyright file="BlockUpdater.cs" company="GRNet"> |
4 | 796a7c29 | pkanavos | * |
5 | 796a7c29 | pkanavos | * Copyright 2011-2012 GRNET S.A. All rights reserved. |
6 | 796a7c29 | pkanavos | * |
7 | 796a7c29 | pkanavos | * Redistribution and use in source and binary forms, with or |
8 | 796a7c29 | pkanavos | * without modification, are permitted provided that the following |
9 | 796a7c29 | pkanavos | * conditions are met: |
10 | 796a7c29 | pkanavos | * |
11 | 796a7c29 | pkanavos | * 1. Redistributions of source code must retain the above |
12 | 796a7c29 | pkanavos | * copyright notice, this list of conditions and the following |
13 | 796a7c29 | pkanavos | * disclaimer. |
14 | 796a7c29 | pkanavos | * |
15 | 796a7c29 | pkanavos | * 2. Redistributions in binary form must reproduce the above |
16 | 796a7c29 | pkanavos | * copyright notice, this list of conditions and the following |
17 | 796a7c29 | pkanavos | * disclaimer in the documentation and/or other materials |
18 | 796a7c29 | pkanavos | * provided with the distribution. |
19 | 796a7c29 | pkanavos | * |
20 | 796a7c29 | pkanavos | * |
21 | 796a7c29 | pkanavos | * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
22 | 796a7c29 | pkanavos | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
23 | 796a7c29 | pkanavos | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | 796a7c29 | pkanavos | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
25 | 796a7c29 | pkanavos | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | 796a7c29 | pkanavos | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | 796a7c29 | pkanavos | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
28 | 796a7c29 | pkanavos | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
29 | 796a7c29 | pkanavos | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | 796a7c29 | pkanavos | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
31 | 796a7c29 | pkanavos | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
32 | 796a7c29 | pkanavos | * POSSIBILITY OF SUCH DAMAGE. |
33 | 796a7c29 | pkanavos | * |
34 | 796a7c29 | pkanavos | * The views and conclusions contained in the software and |
35 | 796a7c29 | pkanavos | * documentation are those of the authors and should not be |
36 | 796a7c29 | pkanavos | * interpreted as representing official policies, either expressed |
37 | 796a7c29 | pkanavos | * or implied, of GRNET S.A. |
38 | 796a7c29 | pkanavos | * </copyright> |
39 | 796a7c29 | pkanavos | * ----------------------------------------------------------------------- |
40 | 796a7c29 | pkanavos | */ |
41 | 796a7c29 | pkanavos | #endregion |
42 | 796a7c29 | pkanavos | using System; |
43 | 796a7c29 | pkanavos | using System.Collections.Concurrent; |
44 | 796a7c29 | pkanavos | using System.Diagnostics.Contracts; |
45 | 796a7c29 | pkanavos | using System.IO; |
46 | 796a7c29 | pkanavos | using System.Linq; |
47 | 796a7c29 | pkanavos | using System.Reflection; |
48 | 1101a72e | pkanavos | using System.Threading; |
49 | 796a7c29 | pkanavos | using System.Threading.Tasks; |
50 | 796a7c29 | pkanavos | using OpenSSL.Crypto; |
51 | 796a7c29 | pkanavos | using Pithos.Network; |
52 | 030b9baf | pkanavos | using Alpha = Alphaleonis.Win32.Filesystem; |
53 | 796a7c29 | pkanavos | |
54 | 796a7c29 | pkanavos | namespace Pithos.Core.Agents |
55 | 796a7c29 | pkanavos | { |
56 | 796a7c29 | pkanavos | class BlockUpdater |
57 | 796a7c29 | pkanavos | { |
58 | 796a7c29 | pkanavos | //TODO: Must clean orphaned blocks from the Cache folder. |
59 | 796a7c29 | pkanavos | // |
60 | 796a7c29 | pkanavos | //The Cache folder may have orphaned blocks. Blocks may be left in the Cache folder because: |
61 | 796a7c29 | pkanavos | //1. A download was in progress when the application terminated. These blocks are needed to proceed |
62 | 796a7c29 | pkanavos | // with partial download |
63 | 796a7c29 | pkanavos | //2. The application terminated abnormally before the blocks were cleared after a download |
64 | 796a7c29 | pkanavos | //3. The server file was deleted before the download completed. |
65 | 796a7c29 | pkanavos | // |
66 | 796a7c29 | pkanavos | //In #1, we need to keep the blocks. We need to detect the other cases and delete orphans |
67 | 796a7c29 | pkanavos | // |
68 | 796a7c29 | pkanavos | //Mitigations: |
69 | 796a7c29 | pkanavos | // - Delete blocks with no corresponding state |
70 | 796a7c29 | pkanavos | // - Check and delete possible orphans when a Deletion is detected |
71 | 796a7c29 | pkanavos | // - Add Advanced command "Clear Cache" |
72 | 796a7c29 | pkanavos | // |
73 | 796a7c29 | pkanavos | //Need a better way to differentiate between cases #2, #3 and #1 |
74 | 796a7c29 | pkanavos | |
75 | 796a7c29 | pkanavos | private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
76 | 796a7c29 | pkanavos | |
77 | 796a7c29 | pkanavos | public string FilePath { get; private set; } |
78 | 796a7c29 | pkanavos | public string RelativePath { get; private set; } |
79 | 796a7c29 | pkanavos | |
80 | 796a7c29 | pkanavos | public string CachePath { get; private set; } |
81 | 796a7c29 | pkanavos | |
82 | 796a7c29 | pkanavos | public TreeHash ServerHash { get; private set; } |
83 | 796a7c29 | pkanavos | |
84 | 796a7c29 | pkanavos | public string TempPath { get; private set; } |
85 | 796a7c29 | pkanavos | |
86 | 796a7c29 | pkanavos | public bool HasBlocks |
87 | 796a7c29 | pkanavos | { |
88 | 796a7c29 | pkanavos | get { return _blocks.Count>0; } |
89 | 796a7c29 | pkanavos | } |
90 | 796a7c29 | pkanavos | |
91 | 796a7c29 | pkanavos | readonly ConcurrentDictionary<long, string> _blocks = new ConcurrentDictionary<long, string>(); |
92 | 796a7c29 | pkanavos | readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>(); |
93 | 796a7c29 | pkanavos | |
94 | 796a7c29 | pkanavos | [ContractInvariantMethod] |
95 | 796a7c29 | pkanavos | private void Invariants() |
96 | 796a7c29 | pkanavos | { |
97 | 796a7c29 | pkanavos | Contract.Invariant(Path.IsPathRooted(CachePath)); |
98 | 796a7c29 | pkanavos | Contract.Invariant(Path.IsPathRooted(FilePath)); |
99 | 796a7c29 | pkanavos | Contract.Invariant(Path.IsPathRooted(TempPath)); |
100 | 796a7c29 | pkanavos | Contract.Invariant(!Path.IsPathRooted(RelativePath)); |
101 | 796a7c29 | pkanavos | Contract.Invariant(_blocks!=null); |
102 | 796a7c29 | pkanavos | Contract.Invariant(_orphanBlocks!=null); |
103 | 796a7c29 | pkanavos | Contract.Invariant(ServerHash!=null); |
104 | 796a7c29 | pkanavos | } |
105 | 796a7c29 | pkanavos | |
106 | 796a7c29 | pkanavos | public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash) |
107 | 796a7c29 | pkanavos | { |
108 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(cachePath)) |
109 | 796a7c29 | pkanavos | throw new ArgumentNullException("cachePath"); |
110 | 796a7c29 | pkanavos | if (!Path.IsPathRooted(cachePath)) |
111 | 796a7c29 | pkanavos | throw new ArgumentException("The cachePath must be rooted", "cachePath"); |
112 | 796a7c29 | pkanavos | |
113 | 796a7c29 | pkanavos | if (string.IsNullOrWhiteSpace(filePath)) |
114 | 796a7c29 | pkanavos | throw new ArgumentNullException("filePath"); |
115 | 796a7c29 | pkanavos | if (!Path.IsPathRooted(filePath)) |
116 | 796a7c29 | pkanavos | throw new ArgumentException("The filePath must be rooted", "filePath"); |
117 | 796a7c29 | pkanavos | |
118 | 796a7c29 | pkanavos | if (string.IsNullOrWhiteSpace(relativePath)) |
119 | 796a7c29 | pkanavos | throw new ArgumentNullException("relativePath"); |
120 | 796a7c29 | pkanavos | if (Path.IsPathRooted(relativePath)) |
121 | 796a7c29 | pkanavos | throw new ArgumentException("The relativePath must NOT be rooted", "relativePath"); |
122 | 796a7c29 | pkanavos | |
123 | 796a7c29 | pkanavos | if (serverHash == null) |
124 | 796a7c29 | pkanavos | throw new ArgumentNullException("serverHash"); |
125 | 796a7c29 | pkanavos | Contract.EndContractBlock(); |
126 | 796a7c29 | pkanavos | |
127 | 796a7c29 | pkanavos | CachePath=cachePath; |
128 | 796a7c29 | pkanavos | FilePath = filePath; |
129 | 796a7c29 | pkanavos | RelativePath=relativePath; |
130 | 796a7c29 | pkanavos | ServerHash = serverHash; |
131 | 796a7c29 | pkanavos | //The file will be stored in a temporary location while downloading with an extension .download |
132 | 796a7c29 | pkanavos | TempPath = Path.Combine(CachePath, RelativePath + ".download"); |
133 | 796a7c29 | pkanavos | |
134 | 796a7c29 | pkanavos | //Need to calculate the directory path because RelativePath may include folders |
135 | 796a7c29 | pkanavos | var directoryPath = Path.GetDirectoryName(TempPath); |
136 | 796a7c29 | pkanavos | //directoryPath CAN be null if TempPath is a root path |
137 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(directoryPath)) |
138 | 796a7c29 | pkanavos | throw new ArgumentException("TempPath"); |
139 | 796a7c29 | pkanavos | //CachePath was absolute so directoryPath is absolute too |
140 | 796a7c29 | pkanavos | Contract.Assume(Path.IsPathRooted(directoryPath)); |
141 | 796a7c29 | pkanavos | |
142 | 796a7c29 | pkanavos | if (!Directory.Exists(directoryPath)) |
143 | 796a7c29 | pkanavos | Directory.CreateDirectory(directoryPath); |
144 | 796a7c29 | pkanavos | |
145 | 796a7c29 | pkanavos | LoadOrphans(directoryPath); |
146 | 796a7c29 | pkanavos | } |
147 | 796a7c29 | pkanavos | |
148 | 796a7c29 | pkanavos | private void LoadOrphans(string directoryPath) |
149 | 796a7c29 | pkanavos | { |
150 | 796a7c29 | pkanavos | if (string.IsNullOrWhiteSpace(directoryPath)) |
151 | 796a7c29 | pkanavos | throw new ArgumentNullException("directoryPath"); |
152 | 796a7c29 | pkanavos | if (!Path.IsPathRooted(directoryPath)) |
153 | 796a7c29 | pkanavos | throw new ArgumentException("The directoryPath must be rooted", "directoryPath"); |
154 | 796a7c29 | pkanavos | if (ServerHash==null) |
155 | 796a7c29 | pkanavos | throw new InvalidOperationException("ServerHash wasn't initialized"); |
156 | 796a7c29 | pkanavos | Contract.EndContractBlock(); |
157 | 796a7c29 | pkanavos | |
158 | 796a7c29 | pkanavos | var fileNamename = Path.GetFileName(FilePath); |
159 | 796a7c29 | pkanavos | var orphans = Directory.GetFiles(directoryPath, fileNamename + ".*"); |
160 | 796a7c29 | pkanavos | foreach (var orphan in orphans) |
161 | 796a7c29 | pkanavos | { |
162 | 796a7c29 | pkanavos | using (var hasher = new MessageDigestContext(MessageDigest.CreateByName(ServerHash.BlockHash))) |
163 | 796a7c29 | pkanavos | { |
164 | 796a7c29 | pkanavos | hasher.Init(); |
165 | 796a7c29 | pkanavos | var buffer=File.ReadAllBytes(orphan); |
166 | 796a7c29 | pkanavos | //The server truncates nulls before calculating hashes, have to do the same |
167 | 796a7c29 | pkanavos | //Find the last non-null byte, starting from the end |
168 | 796a7c29 | pkanavos | var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0); |
169 | 796a7c29 | pkanavos | //lastByteIndex may be -1 if the file was empty. We don't want to use that block file |
170 | 796a7c29 | pkanavos | if (lastByteIndex >= 0) |
171 | 796a7c29 | pkanavos | { |
172 | 796a7c29 | pkanavos | byte[] block; |
173 | 796a7c29 | pkanavos | if (lastByteIndex == buffer.Length - 1) |
174 | 796a7c29 | pkanavos | block = buffer; |
175 | 796a7c29 | pkanavos | else |
176 | 796a7c29 | pkanavos | { |
177 | d5617d4f | pkanavos | block=new byte[lastByteIndex+1]; |
178 | d5617d4f | pkanavos | Buffer.BlockCopy(buffer,0,block,0,lastByteIndex+1); |
179 | 796a7c29 | pkanavos | } |
180 | 796a7c29 | pkanavos | var binHash = hasher.Digest(block); |
181 | 796a7c29 | pkanavos | var hash = binHash.ToHashString(); |
182 | 796a7c29 | pkanavos | _orphanBlocks[hash] = orphan; |
183 | 796a7c29 | pkanavos | } |
184 | 796a7c29 | pkanavos | } |
185 | 796a7c29 | pkanavos | } |
186 | 796a7c29 | pkanavos | } |
187 | 796a7c29 | pkanavos | |
188 | 796a7c29 | pkanavos | |
189 | 796a7c29 | pkanavos | public void Commit() |
190 | 796a7c29 | pkanavos | { |
191 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(FilePath)) |
192 | 796a7c29 | pkanavos | throw new InvalidOperationException("FilePath is empty"); |
193 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(TempPath)) |
194 | 796a7c29 | pkanavos | throw new InvalidOperationException("TempPath is empty"); |
195 | 796a7c29 | pkanavos | Contract.EndContractBlock(); |
196 | 796a7c29 | pkanavos | |
197 | 397b9100 | pkanavos | var info = Alpha.Volume.GetVolumeInformation(Alpha.Path.GetPathRoot(FilePath)); |
198 | 397b9100 | pkanavos | var isNTFS = info.FileSystemName.Equals("NTFS"); |
199 | 9a9c797a | pkanavos | |
200 | 9a9c797a | pkanavos | |
201 | 030b9baf | pkanavos | if (isNTFS && MS.WindowsAPICodePack.Internal.CoreHelpers.RunningOnVista) |
202 | 9a9c797a | pkanavos | { |
203 | 030b9baf | pkanavos | ApplyBlocksWithTransaction(); |
204 | 9a9c797a | pkanavos | } |
205 | 9a9c797a | pkanavos | else |
206 | 9a9c797a | pkanavos | { |
207 | 030b9baf | pkanavos | ApplyBlocksWithCopy(); |
208 | 9a9c797a | pkanavos | } |
209 | 9a9c797a | pkanavos | ClearBlocks(); |
210 | 9a9c797a | pkanavos | } |
211 | 9a9c797a | pkanavos | |
212 | 9a9c797a | pkanavos | private void ApplyBlocksWithTransaction() |
213 | 9a9c797a | pkanavos | { |
214 | 030b9baf | pkanavos | var targetFile = FilePath; |
215 | 030b9baf | pkanavos | using (var tx = new Alpha.KernelTransaction()) |
216 | 030b9baf | pkanavos | { |
217 | 1101a72e | pkanavos | //Retry if the file is still open |
218 | 1101a72e | pkanavos | using (var stream = Alpha.File.Open(tx, targetFile, |
219 | 1101a72e | pkanavos | Alpha.FileMode.OpenOrCreate,Alpha.FileAccess.Write)) |
220 | 030b9baf | pkanavos | { |
221 | 1101a72e | pkanavos | stream.SetLength(ServerHash.Bytes); |
222 | 1101a72e | pkanavos | foreach (var block in _blocks) |
223 | 030b9baf | pkanavos | { |
224 | 1101a72e | pkanavos | var blockPath = block.Value; |
225 | 1101a72e | pkanavos | var blockIndex = block.Key; |
226 | 1101a72e | pkanavos | //Retry if we can't open the block immediatelly |
227 | 1101a72e | pkanavos | for (int i = 0; i < 3; i++) |
228 | 1101a72e | pkanavos | { |
229 | 1101a72e | pkanavos | try |
230 | 1101a72e | pkanavos | { |
231 | 1101a72e | pkanavos | using (var blockStream = File.OpenRead(blockPath)) |
232 | 1101a72e | pkanavos | { |
233 | 1101a72e | pkanavos | long offset = blockIndex*ServerHash.BlockSize; |
234 | 1101a72e | pkanavos | stream.Seek(offset, SeekOrigin.Begin); |
235 | 1101a72e | pkanavos | blockStream.CopyTo(stream); |
236 | 1101a72e | pkanavos | } |
237 | 1101a72e | pkanavos | break; |
238 | 1101a72e | pkanavos | } |
239 | 1101a72e | pkanavos | catch (IOException) |
240 | 1101a72e | pkanavos | { |
241 | 0e1acc13 | pkanavos | if (i>=2) |
242 | 1101a72e | pkanavos | throw; |
243 | 0e1acc13 | pkanavos | Thread.Sleep((i+1)*300); |
244 | 1101a72e | pkanavos | } |
245 | 1101a72e | pkanavos | } |
246 | 030b9baf | pkanavos | } |
247 | 030b9baf | pkanavos | } |
248 | 030b9baf | pkanavos | tx.Commit(); |
249 | 030b9baf | pkanavos | } |
250 | 030b9baf | pkanavos | |
251 | 9a9c797a | pkanavos | } |
252 | 9a9c797a | pkanavos | |
253 | 9a9c797a | pkanavos | private void ApplyBlocksWithCopy() |
254 | 9a9c797a | pkanavos | { |
255 | 796a7c29 | pkanavos | //Copy the file to a temporary location. Changes will be made to the |
256 | 796a7c29 | pkanavos | //temporary file, then it will replace the original file |
257 | 796a7c29 | pkanavos | if (File.Exists(FilePath)) |
258 | 796a7c29 | pkanavos | File.Copy(FilePath, TempPath, true); |
259 | 796a7c29 | pkanavos | |
260 | 796a7c29 | pkanavos | //Set the size of the file to the size specified in the treehash |
261 | 796a7c29 | pkanavos | //This will also create an empty file if the file doesn't exist |
262 | 9a9c797a | pkanavos | |
263 | 9a9c797a | pkanavos | ApplyBlocks(TempPath); |
264 | 9a9c797a | pkanavos | SwapFiles(); |
265 | 9a9c797a | pkanavos | } |
266 | 9a9c797a | pkanavos | |
267 | 9a9c797a | pkanavos | private void ApplyBlocks(string targetFile) |
268 | 9a9c797a | pkanavos | { |
269 | 9a9c797a | pkanavos | SetFileSize(targetFile, ServerHash.Bytes); |
270 | 796a7c29 | pkanavos | |
271 | 796a7c29 | pkanavos | //Update the temporary file with the data from the blocks |
272 | 9a9c797a | pkanavos | using (var stream = File.OpenWrite(targetFile)) |
273 | 796a7c29 | pkanavos | { |
274 | 796a7c29 | pkanavos | foreach (var block in _blocks) |
275 | 796a7c29 | pkanavos | { |
276 | 796a7c29 | pkanavos | var blockPath = block.Value; |
277 | 796a7c29 | pkanavos | var blockIndex = block.Key; |
278 | 796a7c29 | pkanavos | using (var blockStream = File.OpenRead(blockPath)) |
279 | 9a9c797a | pkanavos | { |
280 | 796a7c29 | pkanavos | long offset = blockIndex*ServerHash.BlockSize; |
281 | 796a7c29 | pkanavos | stream.Seek(offset, SeekOrigin.Begin); |
282 | 796a7c29 | pkanavos | blockStream.CopyTo(stream); |
283 | 796a7c29 | pkanavos | } |
284 | 796a7c29 | pkanavos | } |
285 | 796a7c29 | pkanavos | } |
286 | 796a7c29 | pkanavos | } |
287 | 796a7c29 | pkanavos | |
288 | 796a7c29 | pkanavos | private void SwapFiles() |
289 | 796a7c29 | pkanavos | { |
290 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(FilePath)) |
291 | 796a7c29 | pkanavos | throw new InvalidOperationException("FilePath is empty"); |
292 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(TempPath)) |
293 | 796a7c29 | pkanavos | throw new InvalidOperationException("TempPath is empty"); |
294 | 796a7c29 | pkanavos | Contract.EndContractBlock(); |
295 | 796a7c29 | pkanavos | |
296 | 796a7c29 | pkanavos | if (File.Exists(FilePath)) |
297 | 796a7c29 | pkanavos | File.Replace(TempPath, FilePath, null, true); |
298 | 796a7c29 | pkanavos | else |
299 | 796a7c29 | pkanavos | { |
300 | 796a7c29 | pkanavos | var targetDirectory = Path.GetDirectoryName(FilePath); |
301 | 796a7c29 | pkanavos | if (!Directory.Exists(targetDirectory)) |
302 | 796a7c29 | pkanavos | Directory.CreateDirectory(targetDirectory); |
303 | 796a7c29 | pkanavos | File.Move(TempPath, FilePath); |
304 | 796a7c29 | pkanavos | } |
305 | 796a7c29 | pkanavos | } |
306 | 796a7c29 | pkanavos | |
307 | 796a7c29 | pkanavos | private void ClearBlocks() |
308 | 796a7c29 | pkanavos | { |
309 | 796a7c29 | pkanavos | if (Log.IsDebugEnabled) |
310 | 796a7c29 | pkanavos | Log.DebugFormat("Clearing blocks for {0}",this.FilePath); |
311 | 796a7c29 | pkanavos | //Get all the the block paths, orphan or not |
312 | 796a7c29 | pkanavos | var paths= _blocks.Select(pair => pair.Value) |
313 | 796a7c29 | pkanavos | .Union(_orphanBlocks.Select(pair => pair.Value)); |
314 | 796a7c29 | pkanavos | foreach (var filePath in paths) |
315 | 796a7c29 | pkanavos | { |
316 | 796a7c29 | pkanavos | File.Delete(filePath); |
317 | 796a7c29 | pkanavos | } |
318 | 796a7c29 | pkanavos | |
319 | 796a7c29 | pkanavos | File.Delete(TempPath); |
320 | 796a7c29 | pkanavos | _blocks.Clear(); |
321 | 796a7c29 | pkanavos | _orphanBlocks.Clear(); |
322 | 796a7c29 | pkanavos | } |
323 | 796a7c29 | pkanavos | |
324 | 796a7c29 | pkanavos | //Change the file's size, possibly truncating or adding to it |
325 | 796a7c29 | pkanavos | private void SetFileSize(string filePath, long fileSize) |
326 | 796a7c29 | pkanavos | { |
327 | 796a7c29 | pkanavos | if (String.IsNullOrWhiteSpace(filePath)) |
328 | 796a7c29 | pkanavos | throw new ArgumentNullException("filePath"); |
329 | 796a7c29 | pkanavos | if (!Path.IsPathRooted(filePath)) |
330 | 796a7c29 | pkanavos | throw new ArgumentException("The filePath must be rooted", "filePath"); |
331 | 796a7c29 | pkanavos | if (fileSize < 0) |
332 | 796a7c29 | pkanavos | throw new ArgumentOutOfRangeException("fileSize"); |
333 | 796a7c29 | pkanavos | Contract.EndContractBlock(); |
334 | 796a7c29 | pkanavos | |
335 | 796a7c29 | pkanavos | using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write)) |
336 | 796a7c29 | pkanavos | { |
337 | 796a7c29 | pkanavos | stream.SetLength(fileSize); |
338 | 796a7c29 | pkanavos | } |
339 | 796a7c29 | pkanavos | } |
340 | 796a7c29 | pkanavos | |
341 | 796a7c29 | pkanavos | /* //Check whether we should copy the local file to a temp path |
342 | 796a7c29 | pkanavos | private bool ShouldCopy(string localPath, string tempPath) |
343 | 796a7c29 | pkanavos | { |
344 | 796a7c29 | pkanavos | //No need to copy if there is no file |
345 | 796a7c29 | pkanavos | if (!File.Exists(localPath)) |
346 | 796a7c29 | pkanavos | return false; |
347 | 796a7c29 | pkanavos | |
348 | 796a7c29 | pkanavos | //If there is no temp file, go ahead and copy |
349 | 796a7c29 | pkanavos | if (!File.Exists(tempPath)) |
350 | 796a7c29 | pkanavos | return true; |
351 | 796a7c29 | pkanavos | |
352 | 796a7c29 | pkanavos | //If there is a temp file and is newer than the actual file, don't copy |
353 | 796a7c29 | pkanavos | var localLastWrite = File.GetLastWriteTime(localPath); |
354 | 796a7c29 | pkanavos | var tempLastWrite = File.GetLastWriteTime(tempPath); |
355 | 796a7c29 | pkanavos | |
356 | 796a7c29 | pkanavos | //This could mean there is an interrupted download in progress |
357 | 796a7c29 | pkanavos | return (tempLastWrite < localLastWrite); |
358 | 796a7c29 | pkanavos | }*/ |
359 | 796a7c29 | pkanavos | |
360 | 796a7c29 | pkanavos | |
361 | 796a7c29 | pkanavos | public bool UseOrphan(long blockIndex, string blockHash) |
362 | 796a7c29 | pkanavos | { |
363 | 796a7c29 | pkanavos | string blockPath=null; |
364 | 796a7c29 | pkanavos | if (_orphanBlocks.TryGetValue(blockHash,out blockPath)) |
365 | 796a7c29 | pkanavos | { |
366 | 796a7c29 | pkanavos | _blocks[blockIndex] = blockPath; |
367 | 796a7c29 | pkanavos | return true; |
368 | 796a7c29 | pkanavos | } |
369 | 796a7c29 | pkanavos | return false; |
370 | 796a7c29 | pkanavos | } |
371 | 796a7c29 | pkanavos | |
372 | 796a7c29 | pkanavos | public Task StoreBlock(long blockIndex,byte[] buffer) |
373 | 796a7c29 | pkanavos | { |
374 | 796a7c29 | pkanavos | var blockPath = String.Format("{0}.{1:000000}", TempPath, blockIndex); |
375 | 796a7c29 | pkanavos | _blocks[blockIndex] = blockPath; |
376 | 796a7c29 | pkanavos | //Remove any orphan files |
377 | 796a7c29 | pkanavos | if (File.Exists(blockPath)) |
378 | 796a7c29 | pkanavos | File.Delete(blockPath); |
379 | 796a7c29 | pkanavos | |
380 | 796a7c29 | pkanavos | return FileAsync.WriteAllBytes(blockPath, buffer); |
381 | 796a7c29 | pkanavos | } |
382 | 796a7c29 | pkanavos | |
383 | 796a7c29 | pkanavos | |
384 | 796a7c29 | pkanavos | |
385 | 796a7c29 | pkanavos | } |
386 | 796a7c29 | pkanavos | } |