Revision 796a7c29 trunk/Pithos.Core/Agents/BlockUpdater.cs

b/trunk/Pithos.Core/Agents/BlockUpdater.cs
1
#region
2
/* -----------------------------------------------------------------------
3
 * <copyright file="BlockUpdater.cs" company="GRNet">
4
 * 
5
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or
8
 * without modification, are permitted provided that the following
9
 * conditions are met:
10
 *
11
 *   1. Redistributions of source code must retain the above
12
 *      copyright notice, this list of conditions and the following
13
 *      disclaimer.
14
 *
15
 *   2. Redistributions in binary form must reproduce the above
16
 *      copyright notice, this list of conditions and the following
17
 *      disclaimer in the documentation and/or other materials
18
 *      provided with the distribution.
19
 *
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
 * POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 * The views and conclusions contained in the software and
35
 * documentation are those of the authors and should not be
36
 * interpreted as representing official policies, either expressed
37
 * or implied, of GRNET S.A.
38
 * </copyright>
39
 * -----------------------------------------------------------------------
40
 */
41
#endregion
42
using System;
43
using System.Collections.Concurrent;
44
using System.Collections.Generic;
45
using System.Diagnostics.Contracts;
46
using System.IO;
47
using System.Linq;
48
using System.Reflection;
49
using System.Security.Cryptography;
50
using System.Text;
51
using System.Threading.Tasks;
52
using Pithos.Network;
53

  
54
namespace Pithos.Core.Agents
55
{
56
    class BlockUpdater
57
    {
58
        //TODO: Must clean orphaned blocks from the Cache folder.
59
        //
60
        //The Cache folder may have orphaned blocks. Blocks may be left in the Cache folder because:
61
        //1. A download was in progress when the application terminated. These blocks are needed to proceed 
62
        //  with partial download
63
        //2. The application terminated abnormally before the blocks were cleared after a download
64
        //3. The server file was deleted before the download completed.
65
        //
66
        //In #1, we need to keep the blocks. We need to detect the other cases and delete orphans
67
        //
68
        //Mitigations:
69
        // - Delete blocks with no corresponding state
70
        // - Check and delete possible orphans when a Deletion is detected
71
        // - Add Advanced command "Clear Cache"
72
        //
73
        //Need a better way to differentiate between cases #2, #3 and #1
74

  
75
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
76

  
77
        public string FilePath { get; private set; }
78
        public string RelativePath { get; private set; }
79

  
80
        public string CachePath { get; private set; }
81

  
82
        public TreeHash ServerHash { get; private set; }
83

  
84
        public string TempPath { get; private set; }
85

  
86
        public bool HasBlocks
87
        {
88
            get { return _blocks.Count>0; }            
89
        }
90

  
91
        readonly ConcurrentDictionary<long, string> _blocks = new ConcurrentDictionary<long, string>();
92
        readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
93

  
94
        [ContractInvariantMethod]
95
        private void Invariants()
96
        {
97
            Contract.Invariant(Path.IsPathRooted(CachePath));
98
            Contract.Invariant(Path.IsPathRooted(FilePath));
99
            Contract.Invariant(Path.IsPathRooted(TempPath));
100
            Contract.Invariant(!Path.IsPathRooted(RelativePath));
101
            Contract.Invariant(_blocks!=null);
102
            Contract.Invariant(_orphanBlocks!=null);
103
            Contract.Invariant(ServerHash!=null);
104
        }
105

  
106
        public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash)
107
        {   
108
            if (String.IsNullOrWhiteSpace(cachePath))
109
                throw new ArgumentNullException("cachePath");
110
            if (!Path.IsPathRooted(cachePath))
111
                throw new ArgumentException("The cachePath must be rooted", "cachePath");
112
            
113
            if (string.IsNullOrWhiteSpace(filePath))
114
                throw new ArgumentNullException("filePath");
115
            if (!Path.IsPathRooted(filePath))
116
                throw new ArgumentException("The filePath must be rooted", "filePath");
117
            
118
            if (string.IsNullOrWhiteSpace(relativePath))
119
                throw new ArgumentNullException("relativePath");
120
            if (Path.IsPathRooted(relativePath))
121
                throw new ArgumentException("The relativePath must NOT be rooted", "relativePath");
122

  
123
            if (serverHash == null)
124
                throw new ArgumentNullException("serverHash");
125
            Contract.EndContractBlock();
126

  
127
            CachePath=cachePath;
128
            FilePath = filePath;
129
            RelativePath=relativePath;
130
            ServerHash = serverHash;
131
            //The file will be stored in a temporary location while downloading with an extension .download
132
            TempPath = Path.Combine(CachePath, RelativePath + ".download");
133
            
134
            //Need to calculate the directory path because RelativePath may include folders
135
            var directoryPath = Path.GetDirectoryName(TempPath);            
136
            //directoryPath CAN be null if TempPath is a root path
137
            if (String.IsNullOrWhiteSpace(directoryPath))
138
                throw new ArgumentException("TempPath");
139
            //CachePath was absolute so directoryPath is absolute too
140
            Contract.Assume(Path.IsPathRooted(directoryPath));
141
            
142
            if (!Directory.Exists(directoryPath))
143
                Directory.CreateDirectory(directoryPath);
144

  
145
            LoadOrphans(directoryPath);
146
        }
147

  
148
        private void LoadOrphans(string directoryPath)
149
        {
150
            if (string.IsNullOrWhiteSpace(directoryPath))
151
                throw new ArgumentNullException("directoryPath");
152
            if (!Path.IsPathRooted(directoryPath))
153
                throw new ArgumentException("The directoryPath must be rooted", "directoryPath");
154
            if (ServerHash==null)
155
                throw new InvalidOperationException("ServerHash wasn't initialized");
156
            Contract.EndContractBlock();
157

  
158
            var fileNamename = Path.GetFileName(FilePath);
159
            var orphans = Directory.GetFiles(directoryPath, fileNamename + ".*");
160
            foreach (var orphan in orphans)
161
            {
162
                using (HashAlgorithm hasher = HashAlgorithm.Create(ServerHash.BlockHash))
163
                {
164
                    var buffer=File.ReadAllBytes(orphan);
165
                    //The server truncates nulls before calculating hashes, have to do the same
166
                    //Find the last non-null byte, starting from the end
167
                    var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0);
168
                    //lastByteIndex may be -1 if the file was empty. We don't want to use that block file
169
                    if (lastByteIndex >= 0)
170
                    {
171
                        var binHash = hasher.ComputeHash(buffer, 0, lastByteIndex);
172
                        var hash = binHash.ToHashString();
173
                        _orphanBlocks[hash] = orphan;
174
                    }
175
                }
176
            }
177
        }
178

  
179

  
180
        public void Commit()
181
        {
182
            if (String.IsNullOrWhiteSpace(FilePath))
183
                throw new InvalidOperationException("FilePath is empty");
184
            if (String.IsNullOrWhiteSpace(TempPath))
185
                throw new InvalidOperationException("TempPath is empty");
186
            Contract.EndContractBlock();
187

  
188
            //Copy the file to a temporary location. Changes will be made to the
189
            //temporary file, then it will replace the original file
190
            if (File.Exists(FilePath))
191
                File.Copy(FilePath, TempPath, true);
192

  
193
            //Set the size of the file to the size specified in the treehash
194
            //This will also create an empty file if the file doesn't exist                        
195
            
196
            
197
            SetFileSize(TempPath, ServerHash.Bytes);
198

  
199
            //Update the temporary file with the data from the blocks
200
            using (var stream = File.OpenWrite(TempPath))
201
            {
202
                foreach (var block in _blocks)
203
                {
204
                    var blockPath = block.Value;
205
                    var blockIndex = block.Key;
206
                    using (var blockStream = File.OpenRead(blockPath))
207
                    {                        
208
                        long offset = blockIndex*ServerHash.BlockSize;
209
                        stream.Seek(offset, SeekOrigin.Begin);
210
                        blockStream.CopyTo(stream);
211
                    }
212
                }
213
            }
214
            SwapFiles();
215

  
216
            ClearBlocks();
217
        }
218

  
219
        private void SwapFiles()
220
        {
221
            if (String.IsNullOrWhiteSpace(FilePath))
222
                throw new InvalidOperationException("FilePath is empty");
223
            if (String.IsNullOrWhiteSpace(TempPath))
224
                throw new InvalidOperationException("TempPath is empty");            
225
            Contract.EndContractBlock();
226

  
227
            if (File.Exists(FilePath))
228
                File.Replace(TempPath, FilePath, null, true);
229
            else
230
            {
231
                var targetDirectory = Path.GetDirectoryName(FilePath);
232
                if (!Directory.Exists(targetDirectory))
233
                    Directory.CreateDirectory(targetDirectory);
234
                File.Move(TempPath, FilePath);
235
            }
236
        }
237

  
238
        private void ClearBlocks()
239
        {
240
            if (Log.IsDebugEnabled)
241
                Log.DebugFormat("Clearing blocks for {0}",this.FilePath);
242
            //Get all the the block paths, orphan or not
243
            var paths= _blocks.Select(pair => pair.Value)
244
                          .Union(_orphanBlocks.Select(pair => pair.Value));
245
            foreach (var filePath in paths)
246
            {
247
                File.Delete(filePath);
248
            }
249

  
250
            File.Delete(TempPath);
251
            _blocks.Clear();
252
            _orphanBlocks.Clear();
253
        }
254

  
255
        //Change the file's size, possibly truncating or adding to it
256
        private  void SetFileSize(string filePath, long fileSize)
257
        {
258
            if (String.IsNullOrWhiteSpace(filePath))
259
                throw new ArgumentNullException("filePath");
260
            if (!Path.IsPathRooted(filePath))
261
                throw new ArgumentException("The filePath must be rooted", "filePath");
262
            if (fileSize < 0)
263
                throw new ArgumentOutOfRangeException("fileSize");
264
            Contract.EndContractBlock();
265

  
266
            using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
267
            {
268
                stream.SetLength(fileSize);
269
            }
270
        }
271

  
272
       /* //Check whether we should copy the local file to a temp path        
273
        private  bool ShouldCopy(string localPath, string tempPath)
274
        {
275
            //No need to copy if there is no file
276
            if (!File.Exists(localPath))
277
                return false;
278

  
279
            //If there is no temp file, go ahead and copy
280
            if (!File.Exists(tempPath))
281
                return true;
282

  
283
            //If there is a temp file and is newer than the actual file, don't copy
284
            var localLastWrite = File.GetLastWriteTime(localPath);
285
            var tempLastWrite = File.GetLastWriteTime(tempPath);
286

  
287
            //This could mean there is an interrupted download in progress
288
            return (tempLastWrite < localLastWrite);
289
        }*/
290

  
291

  
292
        public bool UseOrphan(long blockIndex, string blockHash)
293
        {
294
            string blockPath=null;
295
            if (_orphanBlocks.TryGetValue(blockHash,out blockPath))
296
            {
297
                _blocks[blockIndex] = blockPath;
298
                return true;
299
            }
300
            return false;
301
        }
302

  
303
        public Task StoreBlock(long blockIndex,byte[] buffer)
304
        {
305
            var blockPath = String.Format("{0}.{1:000000}", TempPath, blockIndex);
306
            _blocks[blockIndex] = blockPath;
307
            //Remove any orphan files
308
            if (File.Exists(blockPath))
309
                File.Delete(blockPath);
310

  
311
            return FileAsync.WriteAllBytes(blockPath, buffer);
312
        }
313

  
314
       
315

  
316
    }
317
}
1
#region
2
/* -----------------------------------------------------------------------
3
 * <copyright file="BlockUpdater.cs" company="GRNet">
4
 * 
5
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or
8
 * without modification, are permitted provided that the following
9
 * conditions are met:
10
 *
11
 *   1. Redistributions of source code must retain the above
12
 *      copyright notice, this list of conditions and the following
13
 *      disclaimer.
14
 *
15
 *   2. Redistributions in binary form must reproduce the above
16
 *      copyright notice, this list of conditions and the following
17
 *      disclaimer in the documentation and/or other materials
18
 *      provided with the distribution.
19
 *
20
 *
21
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
 * POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 * The views and conclusions contained in the software and
35
 * documentation are those of the authors and should not be
36
 * interpreted as representing official policies, either expressed
37
 * or implied, of GRNET S.A.
38
 * </copyright>
39
 * -----------------------------------------------------------------------
40
 */
41
#endregion
42
using System;
43
using System.Collections.Concurrent;
44
using System.Diagnostics.Contracts;
45
using System.IO;
46
using System.Linq;
47
using System.Reflection;
48
using System.Threading.Tasks;
49
using OpenSSL.Crypto;
50
using Pithos.Network;
51

  
52
namespace Pithos.Core.Agents
53
{
54
    class BlockUpdater
55
    {
56
        //TODO: Must clean orphaned blocks from the Cache folder.
57
        //
58
        //The Cache folder may have orphaned blocks. Blocks may be left in the Cache folder because:
59
        //1. A download was in progress when the application terminated. These blocks are needed to proceed 
60
        //  with partial download
61
        //2. The application terminated abnormally before the blocks were cleared after a download
62
        //3. The server file was deleted before the download completed.
63
        //
64
        //In #1, we need to keep the blocks. We need to detect the other cases and delete orphans
65
        //
66
        //Mitigations:
67
        // - Delete blocks with no corresponding state
68
        // - Check and delete possible orphans when a Deletion is detected
69
        // - Add Advanced command "Clear Cache"
70
        //
71
        //Need a better way to differentiate between cases #2, #3 and #1
72

  
73
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74

  
75
        public string FilePath { get; private set; }
76
        public string RelativePath { get; private set; }
77

  
78
        public string CachePath { get; private set; }
79

  
80
        public TreeHash ServerHash { get; private set; }
81

  
82
        public string TempPath { get; private set; }
83

  
84
        public bool HasBlocks
85
        {
86
            get { return _blocks.Count>0; }            
87
        }
88

  
89
        readonly ConcurrentDictionary<long, string> _blocks = new ConcurrentDictionary<long, string>();
90
        readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
91

  
92
        [ContractInvariantMethod]
93
        private void Invariants()
94
        {
95
            Contract.Invariant(Path.IsPathRooted(CachePath));
96
            Contract.Invariant(Path.IsPathRooted(FilePath));
97
            Contract.Invariant(Path.IsPathRooted(TempPath));
98
            Contract.Invariant(!Path.IsPathRooted(RelativePath));
99
            Contract.Invariant(_blocks!=null);
100
            Contract.Invariant(_orphanBlocks!=null);
101
            Contract.Invariant(ServerHash!=null);
102
        }
103

  
104
        public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash)
105
        {   
106
            if (String.IsNullOrWhiteSpace(cachePath))
107
                throw new ArgumentNullException("cachePath");
108
            if (!Path.IsPathRooted(cachePath))
109
                throw new ArgumentException("The cachePath must be rooted", "cachePath");
110
            
111
            if (string.IsNullOrWhiteSpace(filePath))
112
                throw new ArgumentNullException("filePath");
113
            if (!Path.IsPathRooted(filePath))
114
                throw new ArgumentException("The filePath must be rooted", "filePath");
115
            
116
            if (string.IsNullOrWhiteSpace(relativePath))
117
                throw new ArgumentNullException("relativePath");
118
            if (Path.IsPathRooted(relativePath))
119
                throw new ArgumentException("The relativePath must NOT be rooted", "relativePath");
120

  
121
            if (serverHash == null)
122
                throw new ArgumentNullException("serverHash");
123
            Contract.EndContractBlock();
124

  
125
            CachePath=cachePath;
126
            FilePath = filePath;
127
            RelativePath=relativePath;
128
            ServerHash = serverHash;
129
            //The file will be stored in a temporary location while downloading with an extension .download
130
            TempPath = Path.Combine(CachePath, RelativePath + ".download");
131
            
132
            //Need to calculate the directory path because RelativePath may include folders
133
            var directoryPath = Path.GetDirectoryName(TempPath);            
134
            //directoryPath CAN be null if TempPath is a root path
135
            if (String.IsNullOrWhiteSpace(directoryPath))
136
                throw new ArgumentException("TempPath");
137
            //CachePath was absolute so directoryPath is absolute too
138
            Contract.Assume(Path.IsPathRooted(directoryPath));
139
            
140
            if (!Directory.Exists(directoryPath))
141
                Directory.CreateDirectory(directoryPath);
142

  
143
            LoadOrphans(directoryPath);
144
        }
145

  
146
        private void LoadOrphans(string directoryPath)
147
        {
148
            if (string.IsNullOrWhiteSpace(directoryPath))
149
                throw new ArgumentNullException("directoryPath");
150
            if (!Path.IsPathRooted(directoryPath))
151
                throw new ArgumentException("The directoryPath must be rooted", "directoryPath");
152
            if (ServerHash==null)
153
                throw new InvalidOperationException("ServerHash wasn't initialized");
154
            Contract.EndContractBlock();
155

  
156
            var fileNamename = Path.GetFileName(FilePath);
157
            var orphans = Directory.GetFiles(directoryPath, fileNamename + ".*");
158
            foreach (var orphan in orphans)
159
            {
160
                using (var hasher = new MessageDigestContext(MessageDigest.CreateByName(ServerHash.BlockHash)))                
161
                {
162
                    hasher.Init();
163
                    var buffer=File.ReadAllBytes(orphan);
164
                    //The server truncates nulls before calculating hashes, have to do the same
165
                    //Find the last non-null byte, starting from the end
166
                    var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0);
167
                    //lastByteIndex may be -1 if the file was empty. We don't want to use that block file
168
                    if (lastByteIndex >= 0)
169
                    {
170
                        byte[] block;
171
                        if (lastByteIndex == buffer.Length - 1)
172
                            block = buffer;
173
                        else
174
                        {
175
                            block=new byte[lastByteIndex];
176
                            Buffer.BlockCopy(buffer,0,block,0,lastByteIndex);
177
                        }
178
                        var binHash = hasher.Digest(block);
179
                        var hash = binHash.ToHashString();
180
                        _orphanBlocks[hash] = orphan;
181
                    }
182
                }
183
            }
184
        }
185

  
186

  
187
        public void Commit()
188
        {
189
            if (String.IsNullOrWhiteSpace(FilePath))
190
                throw new InvalidOperationException("FilePath is empty");
191
            if (String.IsNullOrWhiteSpace(TempPath))
192
                throw new InvalidOperationException("TempPath is empty");
193
            Contract.EndContractBlock();
194

  
195
            //Copy the file to a temporary location. Changes will be made to the
196
            //temporary file, then it will replace the original file
197
            if (File.Exists(FilePath))
198
                File.Copy(FilePath, TempPath, true);
199

  
200
            //Set the size of the file to the size specified in the treehash
201
            //This will also create an empty file if the file doesn't exist                        
202
            
203
            
204
            SetFileSize(TempPath, ServerHash.Bytes);
205

  
206
            //Update the temporary file with the data from the blocks
207
            using (var stream = File.OpenWrite(TempPath))
208
            {
209
                foreach (var block in _blocks)
210
                {
211
                    var blockPath = block.Value;
212
                    var blockIndex = block.Key;
213
                    using (var blockStream = File.OpenRead(blockPath))
214
                    {                        
215
                        long offset = blockIndex*ServerHash.BlockSize;
216
                        stream.Seek(offset, SeekOrigin.Begin);
217
                        blockStream.CopyTo(stream);
218
                    }
219
                }
220
            }
221
            SwapFiles();
222

  
223
            ClearBlocks();
224
        }
225

  
226
        private void SwapFiles()
227
        {
228
            if (String.IsNullOrWhiteSpace(FilePath))
229
                throw new InvalidOperationException("FilePath is empty");
230
            if (String.IsNullOrWhiteSpace(TempPath))
231
                throw new InvalidOperationException("TempPath is empty");            
232
            Contract.EndContractBlock();
233

  
234
            if (File.Exists(FilePath))
235
                File.Replace(TempPath, FilePath, null, true);
236
            else
237
            {
238
                var targetDirectory = Path.GetDirectoryName(FilePath);
239
                if (!Directory.Exists(targetDirectory))
240
                    Directory.CreateDirectory(targetDirectory);
241
                File.Move(TempPath, FilePath);
242
            }
243
        }
244

  
245
        private void ClearBlocks()
246
        {
247
            if (Log.IsDebugEnabled)
248
                Log.DebugFormat("Clearing blocks for {0}",this.FilePath);
249
            //Get all the the block paths, orphan or not
250
            var paths= _blocks.Select(pair => pair.Value)
251
                          .Union(_orphanBlocks.Select(pair => pair.Value));
252
            foreach (var filePath in paths)
253
            {
254
                File.Delete(filePath);
255
            }
256

  
257
            File.Delete(TempPath);
258
            _blocks.Clear();
259
            _orphanBlocks.Clear();
260
        }
261

  
262
        //Change the file's size, possibly truncating or adding to it
263
        private  void SetFileSize(string filePath, long fileSize)
264
        {
265
            if (String.IsNullOrWhiteSpace(filePath))
266
                throw new ArgumentNullException("filePath");
267
            if (!Path.IsPathRooted(filePath))
268
                throw new ArgumentException("The filePath must be rooted", "filePath");
269
            if (fileSize < 0)
270
                throw new ArgumentOutOfRangeException("fileSize");
271
            Contract.EndContractBlock();
272

  
273
            using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
274
            {
275
                stream.SetLength(fileSize);
276
            }
277
        }
278

  
279
       /* //Check whether we should copy the local file to a temp path        
280
        private  bool ShouldCopy(string localPath, string tempPath)
281
        {
282
            //No need to copy if there is no file
283
            if (!File.Exists(localPath))
284
                return false;
285

  
286
            //If there is no temp file, go ahead and copy
287
            if (!File.Exists(tempPath))
288
                return true;
289

  
290
            //If there is a temp file and is newer than the actual file, don't copy
291
            var localLastWrite = File.GetLastWriteTime(localPath);
292
            var tempLastWrite = File.GetLastWriteTime(tempPath);
293

  
294
            //This could mean there is an interrupted download in progress
295
            return (tempLastWrite < localLastWrite);
296
        }*/
297

  
298

  
299
        public bool UseOrphan(long blockIndex, string blockHash)
300
        {
301
            string blockPath=null;
302
            if (_orphanBlocks.TryGetValue(blockHash,out blockPath))
303
            {
304
                _blocks[blockIndex] = blockPath;
305
                return true;
306
            }
307
            return false;
308
        }
309

  
310
        public Task StoreBlock(long blockIndex,byte[] buffer)
311
        {
312
            var blockPath = String.Format("{0}.{1:000000}", TempPath, blockIndex);
313
            _blocks[blockIndex] = blockPath;
314
            //Remove any orphan files
315
            if (File.Exists(blockPath))
316
                File.Delete(blockPath);
317

  
318
            return FileAsync.WriteAllBytes(blockPath, buffer);
319
        }
320

  
321
       
322

  
323
    }
324
}

Also available in: Unified diff