Statistics
| Branch: | Revision:

root / trunk / Pithos.Core / Agents / BlockUpdater.cs @ 89472316

History | View | Annotate | Download (11.4 kB)

1 255f5f86 Panagiotis Kanavos
#region
2 255f5f86 Panagiotis Kanavos
/* -----------------------------------------------------------------------
3 255f5f86 Panagiotis Kanavos
 * <copyright file="BlockUpdater.cs" company="GRNet">
4 255f5f86 Panagiotis Kanavos
 * 
5 255f5f86 Panagiotis Kanavos
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6 255f5f86 Panagiotis Kanavos
 *
7 255f5f86 Panagiotis Kanavos
 * Redistribution and use in source and binary forms, with or
8 255f5f86 Panagiotis Kanavos
 * without modification, are permitted provided that the following
9 255f5f86 Panagiotis Kanavos
 * conditions are met:
10 255f5f86 Panagiotis Kanavos
 *
11 255f5f86 Panagiotis Kanavos
 *   1. Redistributions of source code must retain the above
12 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
13 255f5f86 Panagiotis Kanavos
 *      disclaimer.
14 255f5f86 Panagiotis Kanavos
 *
15 255f5f86 Panagiotis Kanavos
 *   2. Redistributions in binary form must reproduce the above
16 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
17 255f5f86 Panagiotis Kanavos
 *      disclaimer in the documentation and/or other materials
18 255f5f86 Panagiotis Kanavos
 *      provided with the distribution.
19 255f5f86 Panagiotis Kanavos
 *
20 255f5f86 Panagiotis Kanavos
 *
21 255f5f86 Panagiotis Kanavos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 255f5f86 Panagiotis Kanavos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 255f5f86 Panagiotis Kanavos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 255f5f86 Panagiotis Kanavos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 255f5f86 Panagiotis Kanavos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 255f5f86 Panagiotis Kanavos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 255f5f86 Panagiotis Kanavos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 255f5f86 Panagiotis Kanavos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 255f5f86 Panagiotis Kanavos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 255f5f86 Panagiotis Kanavos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 255f5f86 Panagiotis Kanavos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 255f5f86 Panagiotis Kanavos
 * POSSIBILITY OF SUCH DAMAGE.
33 255f5f86 Panagiotis Kanavos
 *
34 255f5f86 Panagiotis Kanavos
 * The views and conclusions contained in the software and
35 255f5f86 Panagiotis Kanavos
 * documentation are those of the authors and should not be
36 255f5f86 Panagiotis Kanavos
 * interpreted as representing official policies, either expressed
37 255f5f86 Panagiotis Kanavos
 * or implied, of GRNET S.A.
38 255f5f86 Panagiotis Kanavos
 * </copyright>
39 255f5f86 Panagiotis Kanavos
 * -----------------------------------------------------------------------
40 255f5f86 Panagiotis Kanavos
 */
41 255f5f86 Panagiotis Kanavos
#endregion
42 255f5f86 Panagiotis Kanavos
using System;
43 a64c87c8 Panagiotis Kanavos
using System.Collections.Concurrent;
44 a64c87c8 Panagiotis Kanavos
using System.Collections.Generic;
45 a64c87c8 Panagiotis Kanavos
using System.Diagnostics.Contracts;
46 a64c87c8 Panagiotis Kanavos
using System.IO;
47 a64c87c8 Panagiotis Kanavos
using System.Linq;
48 0af3141d Panagiotis Kanavos
using System.Security.Cryptography;
49 a64c87c8 Panagiotis Kanavos
using System.Text;
50 a64c87c8 Panagiotis Kanavos
using System.Threading.Tasks;
51 a64c87c8 Panagiotis Kanavos
using Pithos.Network;
52 a64c87c8 Panagiotis Kanavos
53 a64c87c8 Panagiotis Kanavos
namespace Pithos.Core.Agents
54 a64c87c8 Panagiotis Kanavos
{
55 a64c87c8 Panagiotis Kanavos
    class BlockUpdater
56 a64c87c8 Panagiotis Kanavos
    {
57 a64c87c8 Panagiotis Kanavos
        public string FilePath { get; private set; }
58 a64c87c8 Panagiotis Kanavos
        public string RelativePath { get; private set; }
59 a64c87c8 Panagiotis Kanavos
60 77e10b4f Panagiotis Kanavos
        public string CachePath { get; private set; }
61 a64c87c8 Panagiotis Kanavos
62 a64c87c8 Panagiotis Kanavos
        public TreeHash ServerHash { get; private set; }
63 a64c87c8 Panagiotis Kanavos
64 a64c87c8 Panagiotis Kanavos
        public string TempPath { get; private set; }
65 0af3141d Panagiotis Kanavos
66 0bd56b7c Panagiotis Kanavos
        public bool HasBlocks
67 0bd56b7c Panagiotis Kanavos
        {
68 0bd56b7c Panagiotis Kanavos
            get { return _blocks.Count>0; }            
69 0bd56b7c Panagiotis Kanavos
        }
70 0bd56b7c Panagiotis Kanavos
71 cfed7823 Panagiotis Kanavos
        readonly ConcurrentDictionary<int, string> _blocks = new ConcurrentDictionary<int, string>();
72 cfed7823 Panagiotis Kanavos
        readonly ConcurrentDictionary<string, string> _orphanBlocks = new ConcurrentDictionary<string, string>();
73 cfed7823 Panagiotis Kanavos
74 cfed7823 Panagiotis Kanavos
        [ContractInvariantMethod]
75 cfed7823 Panagiotis Kanavos
        private void Invariants()
76 cfed7823 Panagiotis Kanavos
        {
77 77e10b4f Panagiotis Kanavos
            Contract.Invariant(Path.IsPathRooted(CachePath));
78 cfed7823 Panagiotis Kanavos
            Contract.Invariant(Path.IsPathRooted(FilePath));
79 cfed7823 Panagiotis Kanavos
            Contract.Invariant(Path.IsPathRooted(TempPath));
80 cfed7823 Panagiotis Kanavos
            Contract.Invariant(!Path.IsPathRooted(RelativePath));
81 cfed7823 Panagiotis Kanavos
            Contract.Invariant(_blocks!=null);
82 cfed7823 Panagiotis Kanavos
            Contract.Invariant(_orphanBlocks!=null);
83 cfed7823 Panagiotis Kanavos
            Contract.Invariant(ServerHash!=null);
84 cfed7823 Panagiotis Kanavos
        }
85 a64c87c8 Panagiotis Kanavos
86 77e10b4f Panagiotis Kanavos
        public BlockUpdater(string cachePath, string filePath, string relativePath,TreeHash serverHash)
87 0af3141d Panagiotis Kanavos
        {   
88 77e10b4f Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(cachePath))
89 77e10b4f Panagiotis Kanavos
                throw new ArgumentNullException("cachePath");
90 77e10b4f Panagiotis Kanavos
            if (!Path.IsPathRooted(cachePath))
91 77e10b4f Panagiotis Kanavos
                throw new ArgumentException("The cachePath must be rooted", "cachePath");
92 0af3141d Panagiotis Kanavos
            
93 0af3141d Panagiotis Kanavos
            if (string.IsNullOrWhiteSpace(filePath))
94 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("filePath");
95 0af3141d Panagiotis Kanavos
            if (!Path.IsPathRooted(filePath))
96 0af3141d Panagiotis Kanavos
                throw new ArgumentException("The filePath must be rooted", "filePath");
97 0af3141d Panagiotis Kanavos
            
98 0af3141d Panagiotis Kanavos
            if (string.IsNullOrWhiteSpace(relativePath))
99 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("relativePath");
100 0af3141d Panagiotis Kanavos
            if (Path.IsPathRooted(relativePath))
101 0af3141d Panagiotis Kanavos
                throw new ArgumentException("The relativePath must NOT be rooted", "relativePath");
102 0af3141d Panagiotis Kanavos
103 0af3141d Panagiotis Kanavos
            if (serverHash == null)
104 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("serverHash");
105 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
106 0af3141d Panagiotis Kanavos
107 77e10b4f Panagiotis Kanavos
            CachePath=cachePath;
108 a64c87c8 Panagiotis Kanavos
            FilePath = filePath;
109 a64c87c8 Panagiotis Kanavos
            RelativePath=relativePath;
110 a64c87c8 Panagiotis Kanavos
            ServerHash = serverHash;
111 a64c87c8 Panagiotis Kanavos
            //The file will be stored in a temporary location while downloading with an extension .download
112 77e10b4f Panagiotis Kanavos
            TempPath = Path.Combine(CachePath, RelativePath + ".download");
113 cfed7823 Panagiotis Kanavos
            
114 cfed7823 Panagiotis Kanavos
            //Need to calculate the directory path because RelativePath may include folders
115 cfed7823 Panagiotis Kanavos
            var directoryPath = Path.GetDirectoryName(TempPath);            
116 cfed7823 Panagiotis Kanavos
            //directoryPath CAN be null if TempPath is a root path
117 cfed7823 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(directoryPath))
118 cfed7823 Panagiotis Kanavos
                throw new ArgumentException("TempPath");
119 77e10b4f Panagiotis Kanavos
            //CachePath was absolute so directoryPath is absolute too
120 cfed7823 Panagiotis Kanavos
            Contract.Assume(Path.IsPathRooted(directoryPath));
121 cfed7823 Panagiotis Kanavos
            
122 a64c87c8 Panagiotis Kanavos
            if (!Directory.Exists(directoryPath))
123 a64c87c8 Panagiotis Kanavos
                Directory.CreateDirectory(directoryPath);
124 0af3141d Panagiotis Kanavos
125 0af3141d Panagiotis Kanavos
            LoadOrphans(directoryPath);
126 0af3141d Panagiotis Kanavos
        }
127 0af3141d Panagiotis Kanavos
128 0af3141d Panagiotis Kanavos
        private void LoadOrphans(string directoryPath)
129 0af3141d Panagiotis Kanavos
        {
130 0af3141d Panagiotis Kanavos
            if (string.IsNullOrWhiteSpace(directoryPath))
131 0af3141d Panagiotis Kanavos
                throw new ArgumentNullException("directoryPath");
132 0af3141d Panagiotis Kanavos
            if (!Path.IsPathRooted(directoryPath))
133 0af3141d Panagiotis Kanavos
                throw new ArgumentException("The directoryPath must be rooted", "directoryPath");
134 cfed7823 Panagiotis Kanavos
            if (ServerHash==null)
135 cfed7823 Panagiotis Kanavos
                throw new InvalidOperationException("ServerHash wasn't initialized");
136 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
137 0af3141d Panagiotis Kanavos
138 0af3141d Panagiotis Kanavos
            var fileNamename = Path.GetFileName(FilePath);
139 0af3141d Panagiotis Kanavos
            var orphans = Directory.GetFiles(directoryPath, fileNamename + ".*");
140 0af3141d Panagiotis Kanavos
            foreach (var orphan in orphans)
141 0af3141d Panagiotis Kanavos
            {
142 0af3141d Panagiotis Kanavos
                using (HashAlgorithm hasher = HashAlgorithm.Create(ServerHash.BlockHash))
143 0af3141d Panagiotis Kanavos
                {
144 0af3141d Panagiotis Kanavos
                    var buffer=File.ReadAllBytes(orphan);
145 0af3141d Panagiotis Kanavos
                    //The server truncates nulls before calculating hashes, have to do the same
146 0af3141d Panagiotis Kanavos
                    //Find the last non-null byte, starting from the end
147 0af3141d Panagiotis Kanavos
                    var lastByteIndex = Array.FindLastIndex(buffer, buffer.Length-1, aByte => aByte != 0);
148 cfed7823 Panagiotis Kanavos
                    //lastByteIndex may be -1 if the file was empty. We don't want to use that block file
149 cfed7823 Panagiotis Kanavos
                    if (lastByteIndex >= 0)
150 cfed7823 Panagiotis Kanavos
                    {
151 cfed7823 Panagiotis Kanavos
                        var binHash = hasher.ComputeHash(buffer, 0, lastByteIndex);
152 cfed7823 Panagiotis Kanavos
                        var hash = binHash.ToHashString();
153 cfed7823 Panagiotis Kanavos
                        _orphanBlocks[hash] = orphan;
154 cfed7823 Panagiotis Kanavos
                    }
155 0af3141d Panagiotis Kanavos
                }
156 0af3141d Panagiotis Kanavos
            }
157 a64c87c8 Panagiotis Kanavos
        }
158 a64c87c8 Panagiotis Kanavos
159 a64c87c8 Panagiotis Kanavos
160 a64c87c8 Panagiotis Kanavos
        public void Commit()
161 0af3141d Panagiotis Kanavos
        {
162 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(FilePath))
163 0af3141d Panagiotis Kanavos
                throw new InvalidOperationException("FilePath is empty");
164 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(TempPath))
165 0af3141d Panagiotis Kanavos
                throw new InvalidOperationException("TempPath is empty");
166 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
167 0af3141d Panagiotis Kanavos
168 a64c87c8 Panagiotis Kanavos
            //Copy the file to a temporary location. Changes will be made to the
169 a64c87c8 Panagiotis Kanavos
            //temporary file, then it will replace the original file
170 0af3141d Panagiotis Kanavos
            if (File.Exists(FilePath))
171 0af3141d Panagiotis Kanavos
                File.Copy(FilePath, TempPath, true);
172 a64c87c8 Panagiotis Kanavos
173 a64c87c8 Panagiotis Kanavos
            //Set the size of the file to the size specified in the treehash
174 cfed7823 Panagiotis Kanavos
            //This will also create an empty file if the file doesn't exist                        
175 cfed7823 Panagiotis Kanavos
            
176 cfed7823 Panagiotis Kanavos
            
177 a64c87c8 Panagiotis Kanavos
            SetFileSize(TempPath, ServerHash.Bytes);
178 a64c87c8 Panagiotis Kanavos
179 a64c87c8 Panagiotis Kanavos
            //Update the temporary file with the data from the blocks
180 a64c87c8 Panagiotis Kanavos
            using (var stream = File.OpenWrite(TempPath))
181 a64c87c8 Panagiotis Kanavos
            {
182 a64c87c8 Panagiotis Kanavos
                foreach (var block in _blocks)
183 a64c87c8 Panagiotis Kanavos
                {
184 a64c87c8 Panagiotis Kanavos
                    var blockPath = block.Value;
185 a64c87c8 Panagiotis Kanavos
                    var blockIndex = block.Key;
186 a64c87c8 Panagiotis Kanavos
                    using (var blockStream = File.OpenRead(blockPath))
187 a64c87c8 Panagiotis Kanavos
                    {                        
188 a64c87c8 Panagiotis Kanavos
                        var offset = blockIndex*ServerHash.BlockSize;
189 a64c87c8 Panagiotis Kanavos
                        stream.Seek(offset, SeekOrigin.Begin);
190 a64c87c8 Panagiotis Kanavos
                        blockStream.CopyTo(stream);
191 a64c87c8 Panagiotis Kanavos
                    }
192 a64c87c8 Panagiotis Kanavos
                }
193 a64c87c8 Panagiotis Kanavos
            }
194 a64c87c8 Panagiotis Kanavos
            SwapFiles();
195 a64c87c8 Panagiotis Kanavos
196 a64c87c8 Panagiotis Kanavos
            ClearBlocks();
197 a64c87c8 Panagiotis Kanavos
        }
198 a64c87c8 Panagiotis Kanavos
199 a64c87c8 Panagiotis Kanavos
        private void SwapFiles()
200 a64c87c8 Panagiotis Kanavos
        {
201 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(FilePath))
202 0af3141d Panagiotis Kanavos
                throw new InvalidOperationException("FilePath is empty");
203 0af3141d Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(TempPath))
204 0af3141d Panagiotis Kanavos
                throw new InvalidOperationException("TempPath is empty");            
205 0af3141d Panagiotis Kanavos
            Contract.EndContractBlock();
206 0af3141d Panagiotis Kanavos
207 a64c87c8 Panagiotis Kanavos
            if (File.Exists(FilePath))
208 a64c87c8 Panagiotis Kanavos
                File.Replace(TempPath, FilePath, null, true);
209 a64c87c8 Panagiotis Kanavos
            else
210 a64c87c8 Panagiotis Kanavos
                File.Move(TempPath, FilePath);
211 a64c87c8 Panagiotis Kanavos
        }
212 a64c87c8 Panagiotis Kanavos
213 a64c87c8 Panagiotis Kanavos
        private void ClearBlocks()
214 a64c87c8 Panagiotis Kanavos
        {
215 0af3141d Panagiotis Kanavos
            //Get all the the block paths, orphan or not
216 0af3141d Panagiotis Kanavos
            var paths= _blocks.Select(pair => pair.Value)
217 0af3141d Panagiotis Kanavos
                          .Union(_orphanBlocks.Select(pair => pair.Value));
218 0af3141d Panagiotis Kanavos
            foreach (var filePath in paths)
219 a64c87c8 Panagiotis Kanavos
            {
220 a64c87c8 Panagiotis Kanavos
                File.Delete(filePath);
221 a64c87c8 Panagiotis Kanavos
            }
222 0af3141d Panagiotis Kanavos
223 a64c87c8 Panagiotis Kanavos
            File.Delete(TempPath);
224 a64c87c8 Panagiotis Kanavos
            _blocks.Clear();
225 0af3141d Panagiotis Kanavos
            _orphanBlocks.Clear();
226 a64c87c8 Panagiotis Kanavos
        }
227 a64c87c8 Panagiotis Kanavos
228 a64c87c8 Panagiotis Kanavos
        //Change the file's size, possibly truncating or adding to it
229 a64c87c8 Panagiotis Kanavos
        private  void SetFileSize(string filePath, long fileSize)
230 a64c87c8 Panagiotis Kanavos
        {
231 a64c87c8 Panagiotis Kanavos
            if (String.IsNullOrWhiteSpace(filePath))
232 a64c87c8 Panagiotis Kanavos
                throw new ArgumentNullException("filePath");
233 a64c87c8 Panagiotis Kanavos
            if (!Path.IsPathRooted(filePath))
234 a64c87c8 Panagiotis Kanavos
                throw new ArgumentException("The filePath must be rooted", "filePath");
235 a64c87c8 Panagiotis Kanavos
            if (fileSize < 0)
236 a64c87c8 Panagiotis Kanavos
                throw new ArgumentOutOfRangeException("fileSize");
237 a64c87c8 Panagiotis Kanavos
            Contract.EndContractBlock();
238 a64c87c8 Panagiotis Kanavos
239 a64c87c8 Panagiotis Kanavos
            using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
240 a64c87c8 Panagiotis Kanavos
            {
241 a64c87c8 Panagiotis Kanavos
                stream.SetLength(fileSize);
242 a64c87c8 Panagiotis Kanavos
            }
243 a64c87c8 Panagiotis Kanavos
        }
244 a64c87c8 Panagiotis Kanavos
245 a64c87c8 Panagiotis Kanavos
       /* //Check whether we should copy the local file to a temp path        
246 a64c87c8 Panagiotis Kanavos
        private  bool ShouldCopy(string localPath, string tempPath)
247 a64c87c8 Panagiotis Kanavos
        {
248 a64c87c8 Panagiotis Kanavos
            //No need to copy if there is no file
249 a64c87c8 Panagiotis Kanavos
            if (!File.Exists(localPath))
250 a64c87c8 Panagiotis Kanavos
                return false;
251 a64c87c8 Panagiotis Kanavos
252 a64c87c8 Panagiotis Kanavos
            //If there is no temp file, go ahead and copy
253 a64c87c8 Panagiotis Kanavos
            if (!File.Exists(tempPath))
254 a64c87c8 Panagiotis Kanavos
                return true;
255 a64c87c8 Panagiotis Kanavos
256 a64c87c8 Panagiotis Kanavos
            //If there is a temp file and is newer than the actual file, don't copy
257 a64c87c8 Panagiotis Kanavos
            var localLastWrite = File.GetLastWriteTime(localPath);
258 a64c87c8 Panagiotis Kanavos
            var tempLastWrite = File.GetLastWriteTime(tempPath);
259 a64c87c8 Panagiotis Kanavos
260 a64c87c8 Panagiotis Kanavos
            //This could mean there is an interrupted download in progress
261 a64c87c8 Panagiotis Kanavos
            return (tempLastWrite < localLastWrite);
262 a64c87c8 Panagiotis Kanavos
        }*/
263 a64c87c8 Panagiotis Kanavos
264 0af3141d Panagiotis Kanavos
265 0af3141d Panagiotis Kanavos
        public bool UseOrphan(int blockIndex, string blockHash)
266 0af3141d Panagiotis Kanavos
        {
267 0af3141d Panagiotis Kanavos
            string blockPath=null;
268 0af3141d Panagiotis Kanavos
            if (_orphanBlocks.TryGetValue(blockHash,out blockPath))
269 0af3141d Panagiotis Kanavos
            {
270 0af3141d Panagiotis Kanavos
                _blocks[blockIndex] = blockPath;
271 0af3141d Panagiotis Kanavos
                return true;
272 0af3141d Panagiotis Kanavos
            }
273 0af3141d Panagiotis Kanavos
            return false;
274 0af3141d Panagiotis Kanavos
        }
275 a64c87c8 Panagiotis Kanavos
276 a64c87c8 Panagiotis Kanavos
        public Task StoreBlock(int blockIndex,byte[] buffer)
277 a64c87c8 Panagiotis Kanavos
        {
278 0af3141d Panagiotis Kanavos
            var blockPath = String.Format("{0}.{1:000000}", TempPath, blockIndex);
279 a64c87c8 Panagiotis Kanavos
            _blocks[blockIndex] = blockPath;
280 0af3141d Panagiotis Kanavos
            //Remove any orphan files
281 0af3141d Panagiotis Kanavos
            if (File.Exists(blockPath))
282 0af3141d Panagiotis Kanavos
                File.Delete(blockPath);
283 0af3141d Panagiotis Kanavos
284 a64c87c8 Panagiotis Kanavos
            return FileAsync.WriteAllBytes(blockPath, buffer);
285 a64c87c8 Panagiotis Kanavos
        }
286 a64c87c8 Panagiotis Kanavos
287 73cdd135 Panagiotis Kanavos
       
288 a64c87c8 Panagiotis Kanavos
289 a64c87c8 Panagiotis Kanavos
    }
290 a64c87c8 Panagiotis Kanavos
}