예제 #1
0
        public override void Dispose()
        {
            m_fileHashLookup  = null;
            m_metadataLookup  = null;
            m_blockHashLookup = null;
            m_pathLookup      = null;

            base.Dispose();
        }
예제 #2
0
 public void Dispose()
 {
     m_lookup = null;
     if (m_command != null)
     {
         try { m_command.Dispose(); }
         finally { m_command = null; }
     }
 }
예제 #3
0
            public BlockQuery(System.Data.IDbConnection con, Options options, System.Data.IDbTransaction transaction)
            {
                m_command             = con.CreateCommand();
                m_command.Transaction = transaction;

                if (options.BlockHashLookupMemory > 0)
                {
                    m_lookup = new HashLookupHelper <long>((ulong)options.BlockHashLookupMemory);
                    using (var reader = m_command.ExecuteReader(@"SELECT ""Hash"", ""Size"" FROM ""Block"" "))
                        while (reader.Read())
                        {
                            var hash = reader.GetString(0);
                            var size = reader.GetInt64(1);
                            m_lookup.Add(hash, size, size);
                        }
                }

                m_command.Parameters.Clear();
                m_command.CommandText = @"SELECT ""VolumeID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ? ";
                m_command.AddParameters(2);
            }
예제 #4
0
        public override void Dispose()
        {
            if (m_scantimelookupTablename != null)
            {
                try
                {
                    using (var cmd = m_connection.CreateCommand())
                        cmd.ExecuteNonQuery(string.Format(@"DROP TABLE IF EXISTS ""{0}"" ", m_scantimelookupTablename));
                }
                catch { }
                finally
                {
                    m_scantimelookupTablename = null;
                }
            }


            m_fileHashLookup  = null;
            m_metadataLookup  = null;
            m_blockHashLookup = null;
            m_pathLookup      = null;

            base.Dispose();
        }
예제 #5
0
			public void Dispose()
			{
        		m_lookup = null;
				if (m_command != null)
					try { m_command.Dispose(); }
					finally { m_command = null; }
			}
예제 #6
0
			public BlockQuery(System.Data.IDbConnection con, Options options, System.Data.IDbTransaction transaction)
			{
				m_command = con.CreateCommand();
				m_command.Transaction = transaction;
				
				if (options.BlockHashLookupMemory > 0)
				{
					m_lookup = new HashLookupHelper<long>((ulong)options.BlockHashLookupMemory);
					using(var reader = m_command.ExecuteReader(@"SELECT ""Hash"", ""Size"" FROM ""Block"" "))
					while (reader.Read())
					{
						var hash = reader.GetValue(0).ToString();
						var size = Convert.ToInt64(reader.GetValue(1));
						m_lookup.Add(hash, size, size);
					}
				}
				
				m_command.Parameters.Clear();
				m_command.CommandText = @"SELECT ""VolumeID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ? ";
				m_command.AddParameters(2);
			}
예제 #7
0
        public static int Run(List <string> args, Dictionary <string, string> options, Library.Utility.IFilter filter)
        {
            if (args.Count != 2 && args.Count != 3)
            {
                Console.WriteLine("Invalid argument count ({0} expected 2 or 3): {1}{2}", args.Count, Environment.NewLine, string.Join(Environment.NewLine, args));
                return(100);
            }

            var folder = Path.GetFullPath(args[1]);

            if (!Directory.Exists(folder))
            {
                Console.WriteLine("Folder not found: {0}", folder);
                return(100);
            }

            Directory.SetCurrentDirectory(folder);

            string targetpath;

            options.TryGetValue("targetpath", out targetpath);

            string ixfile;

            options.TryGetValue("indexfile", out ixfile);
            if (string.IsNullOrWhiteSpace(ixfile))
            {
                ixfile = "index.txt";
            }

            ixfile = Path.GetFullPath(ixfile);
            if (!File.Exists(ixfile))
            {
                Console.WriteLine("Index file not found, perhaps you need to run the index command?");
                return(100);
            }

            Console.Write("Sorting index file ...");
            Index.SortFile(ixfile, ixfile);
            Console.WriteLine(" done!");

            string filelist;

            if (args.Count == 2)
            {
                var time = List.ParseListFiles(folder).First();
                filelist = time.Value;

                Console.WriteLine("Using set 0 with timestamp {0}", time.Key.ToLocalTime());
            }
            else
            {
                filelist = List.SelectListFile(args[2], folder);
            }

            Library.Main.Volumes.VolumeReaderBase.UpdateOptionsFromManifest(Path.GetExtension(filelist).Trim('.'), filelist, new Duplicati.Library.Main.Options(options));

            string blocksize_str;

            options.TryGetValue("blocksize", out blocksize_str);
            string blockhash_str;

            options.TryGetValue("block-hash-algorithm", out blockhash_str);
            string filehash_str;

            options.TryGetValue("block-hash-algorithm", out filehash_str);

            long blocksize = string.IsNullOrWhiteSpace(blocksize_str) ? 0 : Library.Utility.Sizeparser.ParseSize(blocksize_str);

            if (blocksize <= 0)
            {
                Console.WriteLine("Invalid blocksize: {0}, try setting --blocksize manually");
                return(100);
            }

            var blockhasher = string.IsNullOrWhiteSpace(blockhash_str) ? null : System.Security.Cryptography.HashAlgorithm.Create(blockhash_str);
            var filehasher  = string.IsNullOrWhiteSpace(filehash_str) ? null : System.Security.Cryptography.HashAlgorithm.Create(filehash_str);

            if (blockhasher == null)
            {
                throw new Duplicati.Library.Interface.UserInformationException(string.Format("Block hash algorithm not valid: {0}", blockhash_str));
            }
            if (filehasher == null)
            {
                throw new Duplicati.Library.Interface.UserInformationException(string.Format("File hash algorithm not valid: {0}", filehash_str));
            }

            var hashesprblock = blocksize / (blockhasher.HashSize / 8);

            using (var mru = new CompressedFileMRUCache(options))
            {
                Console.WriteLine("Building lookup table for file hashes");
                var lookup = new HashLookupHelper(ixfile, mru, (int)blocksize, blockhasher.HashSize / 8);

                var      filecount          = 0L;
                string   largestprefix      = null;
                string[] largestprefixparts = null;

                if (!string.IsNullOrWhiteSpace(targetpath))
                {
                    Console.WriteLine("Computing restore path");
                }

                foreach (var f in List.EnumerateFilesInDList(filelist, filter, options))
                {
                    if (largestprefix == null)
                    {
                        largestprefix      = f.Path;
                        largestprefixparts = largestprefix.Split(new char[] { Path.DirectorySeparatorChar });
                    }
                    else if (largestprefix.Length > 1)
                    {
                        var parts = f.Path.Split(new char[] { Path.DirectorySeparatorChar });

                        var ni = 0;
                        for (; ni < Math.Min(parts.Length, largestprefixparts.Length); ni++)
                        {
                            if (!Library.Utility.Utility.ClientFilenameStringComparer.Equals(parts[ni], largestprefixparts[ni]))
                            {
                                break;
                            }
                        }

                        if (ni != largestprefixparts.Length)
                        {
                            if (ni == 0)
                            {
                                largestprefixparts = new string[0];
                                largestprefix      = string.Empty;
                            }
                            else
                            {
                                Array.Resize(ref largestprefixparts, ni - 1);
                                largestprefix = string.Join(Path.DirectorySeparatorChar.ToString(), largestprefixparts);
                            }
                        }
                    }
                    filecount++;
                }

                Console.WriteLine("Restoring {0} files to {1}", filecount, string.IsNullOrWhiteSpace(targetpath) ? "original position" : targetpath);

                if (Library.Utility.Utility.IsClientLinux || largestprefix.Length > 0)
                {
                    largestprefix = Library.Utility.Utility.AppendDirSeparator(largestprefix);
                }

                if (!string.IsNullOrEmpty(largestprefix))
                {
                    Console.WriteLine("Removing common prefix {0} from files", largestprefix);
                }

                var i      = 0L;
                var errors = 0L;
                foreach (var f in List.EnumerateFilesInDList(filelist, filter, options))
                {
                    try
                    {
                        var targetfile = MapToRestorePath(f.Path, largestprefix, targetpath);
                        if (!Directory.Exists(Path.GetDirectoryName(targetfile)))
                        {
                            Directory.CreateDirectory(Path.GetDirectoryName(targetfile));
                        }

                        Console.Write("{0}: {1} ({2})", i, targetfile, Library.Utility.Utility.FormatSizeString(f.Size));

                        using (var tf = new Library.Utility.TempFile())
                        {
                            using (var sw = File.OpenWrite(tf))
                            {
                                if (f.BlocklistHashes == null)
                                {
                                    lookup.WriteHash(sw, f.Hash);
                                }
                                else
                                {
                                    var blhi = 0L;
                                    foreach (var blh in f.BlocklistHashes)
                                    {
                                        Console.Write(" {0}", blhi);
                                        var blockhashoffset = blhi * hashesprblock * blocksize;

                                        try
                                        {
                                            var bi = 0;
                                            foreach (var h in lookup.ReadBlocklistHashes(blh))
                                            {
                                                try
                                                {
                                                    sw.Position = blockhashoffset + (bi * blocksize);
                                                    lookup.WriteHash(sw, h);
                                                }
                                                catch (Exception ex)
                                                {
                                                    Console.WriteLine("Failed to read hash: {0}{1}{2}", h, Environment.NewLine, ex.ToString());
                                                }

                                                bi++;
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            Console.WriteLine("Failed to read Blocklist hash: {0}{1}{2}", blh, Environment.NewLine, ex.ToString());
                                        }

                                        blhi++;
                                    }
                                }
                            }

                            string fh;
                            using (var fs = File.OpenRead(tf))
                                fh = Convert.ToBase64String(filehasher.ComputeHash(fs));

                            if (fh == f.Hash)
                            {
                                Console.WriteLine(" done!");
                                File.Copy(tf, targetfile, true);
                            }
                            else
                            {
                                Console.Write(" - Restored file hash mismatch");
                                if (File.Exists(targetfile))
                                {
                                    Console.WriteLine(" - not overwriting existing file: {0}", targetfile);
                                }
                                else
                                {
                                    Console.WriteLine(" - restoring file in damaged condition");
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(" error: {0}", ex.ToString());
                        errors++;
                    }
                    i++;
                }
            }

            return(0);
        }
예제 #8
0
        public LocalRecreateDatabase(LocalDatabase parentdb, Options options)
            : base(parentdb, options.Blocksize)
        {
            m_tempblocklist = "TempBlocklist-" + Library.Utility.Utility.ByteArrayAsHexString(Guid.NewGuid().ToByteArray());

            using (var cmd = m_connection.CreateCommand())
                cmd.ExecuteNonQuery(string.Format(@"CREATE TEMPORARY TABLE ""{0}"" (""BlockListHash"" TEXT NOT NULL, ""BlockHash"" TEXT NOT NULL, ""Index"" INTEGER NOT NULL)", m_tempblocklist));

            m_insertFileCommand           = m_connection.CreateCommand();
            m_insertFilesetEntryCommand   = m_connection.CreateCommand();
            m_insertMetadatasetCommand    = m_connection.CreateCommand();
            m_insertBlocksetCommand       = m_connection.CreateCommand();
            m_insertBlocklistHashCommand  = m_connection.CreateCommand();
            m_updateBlockVolumeCommand    = m_connection.CreateCommand();
            m_insertBlockset              = m_connection.CreateCommand();
            m_findBlocksetCommand         = m_connection.CreateCommand();
            m_findMetadatasetCommand      = m_connection.CreateCommand();
            m_findFilesetCommand          = m_connection.CreateCommand();
            m_findblocklisthashCommand    = m_connection.CreateCommand();
            m_findHashBlockCommand        = m_connection.CreateCommand();
            m_insertBlockCommand          = m_connection.CreateCommand();
            m_insertDuplicateBlockCommand = m_connection.CreateCommand();

            m_insertFileCommand.CommandText = @"INSERT INTO ""File"" (""Path"", ""BlocksetID"", ""MetadataID"") VALUES (?,?,?); SELECT last_insert_rowid();";
            m_insertFileCommand.AddParameters(3);

            m_insertFilesetEntryCommand.CommandText = @"INSERT INTO ""FilesetEntry"" (""FilesetID"", ""FileID"", ""Scantime"") VALUES (?,?,?)";
            m_insertFilesetEntryCommand.AddParameters(3);

            m_insertMetadatasetCommand.CommandText = @"INSERT INTO ""Metadataset"" (""BlocksetID"") VALUES (?); SELECT last_insert_rowid();";
            m_insertMetadatasetCommand.AddParameters(1);

            m_insertBlocksetCommand.CommandText = @"INSERT INTO ""Blockset"" (""Length"", ""FullHash"") VALUES (?,?); SELECT last_insert_rowid();";
            m_insertBlocksetCommand.AddParameters(2);

            m_insertBlocklistHashCommand.CommandText = @"INSERT INTO ""BlocklistHash"" (""BlocksetID"", ""Index"", ""Hash"") VALUES (?,?,?)";
            m_insertBlocklistHashCommand.AddParameters(3);

            m_updateBlockVolumeCommand.CommandText = @"UPDATE ""Block"" SET ""VolumeID"" = ? WHERE ""Hash"" = ? AND ""Size"" = ?";
            m_updateBlockVolumeCommand.AddParameters(3);

            m_insertBlockset.CommandText = string.Format(@"INSERT INTO ""{0}"" (""BlocklistHash"", ""BlockHash"", ""Index"") VALUES (?,?,?) ", m_tempblocklist);
            m_insertBlockset.AddParameters(3);

            m_findBlocksetCommand.CommandText = @"SELECT ""ID"" FROM ""Blockset"" WHERE ""Size"" = ? AND ""FullHash"" = ? ";
            m_findBlocksetCommand.AddParameters(2);

            m_findMetadatasetCommand.CommandText = @"SELECT ""Metadataset"".""ID"" FROM ""Metadataset"",""BlocksetEntry"",""Block"" WHERE ""Metadataset"".""BlocksetID"" = ""BlocksetEntry"".""BlocksetID"" AND ""Block"".""ID"" = ""BlocksetEntry"".""BlockID"" AND ""Block"".""Hash"" = ? AND ""Block"".""Size"" = ? ";
            m_findMetadatasetCommand.AddParameters(2);

            m_findFilesetCommand.CommandText = @"SELECT ""ID"" FROM ""File"" WHERE ""Path"" = ? AND ""BlocksetID"" = ? AND ""MetadataID"" = ? ";
            m_findFilesetCommand.AddParameters(3);

            m_findblocklisthashCommand.CommandText = string.Format(@"SELECT DISTINCT ""BlockListHash"" FROM ""{0}"" WHERE ""BlockListHash"" = ? ", m_tempblocklist);
            m_findblocklisthashCommand.AddParameters(1);

            m_findHashBlockCommand.CommandText = @"SELECT ""VolumeID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ? ";
            m_findHashBlockCommand.AddParameters(2);

            m_insertBlockCommand.CommandText = @"INSERT INTO ""Block"" (""Hash"", ""Size"", ""VolumeID"") VALUES (?,?,?)";
            m_insertBlockCommand.AddParameters(3);

            m_insertDuplicateBlockCommand.CommandText = @"INSERT INTO ""DuplicateBlock"" (""Hash"", ""Size"", ""VolumeID"") VALUE (?,?,?)";
            m_insertDuplicateBlockCommand.AddParameters(3);

            if (options.BlockHashLookupMemory > 0)
            {
                m_blockHashLookup     = new HashLookupHelper <long>((ulong)options.BlockHashLookupMemory / 2);
                m_blockListHashLookup = new HashLookupHelper <bool>((ulong)options.BlockHashLookupMemory / 2);
            }
            if (options.FileHashLookupMemory > 0)
            {
                m_fileHashLookup = new HashLookupHelper <long>((ulong)options.FileHashLookupMemory);
            }
            if (options.MetadataHashMemory > 0)
            {
                m_metadataLookup = new HashLookupHelper <long>((ulong)options.MetadataHashMemory);
            }
            if (options.UseFilepathCache)
            {
                m_filesetLookup = new PathLookupHelper <PathEntryKeeper>();
            }
        }
예제 #9
0
        /// <summary>
        /// Builds the lookup tables. Call this method after deleting items, and before processing items
        /// </summary>
        /// <param name="options">The option settings</param>
        public void BuildLookupTable(Options options)
        {
            if (options.BlockHashLookupMemory > 0)
            {
                m_blockHashLookup = new HashLookupHelper <KeyValuePair <long, long> >((ulong)options.BlockHashLookupMemory);
            }
            if (options.FileHashLookupMemory > 0)
            {
                m_fileHashLookup = new HashLookupHelper <long>((ulong)options.FileHashLookupMemory);
            }
            if (options.MetadataHashMemory > 0)
            {
                m_metadataLookup = new HashLookupHelper <long>((ulong)options.MetadataHashMemory);
            }
            if (options.UseFilepathCache)
            {
                m_pathLookup = new PathLookupHelper <PathEntryKeeper>(true);
            }


            //Populate the lookup tables
            using (var cmd = m_connection.CreateCommand())
            {
                if (m_blockHashLookup != null)
                {
                    using (new Logging.Timer("Build blockhash lookup table"))
                        using (var rd = cmd.ExecuteReader(@"SELECT DISTINCT ""Block"".""Hash"", ""Block"".""ID"", ""Block"".""Size"" FROM ""Block"" "))
                            while (rd.Read())
                            {
                                var str  = rd.GetValue(0).ToString();
                                var id   = Convert.ToInt64(rd.GetValue(1));
                                var size = Convert.ToInt64(rd.GetValue(2));
                                m_blockHashLookup.Add(str, size, new KeyValuePair <long, long>(id, size));
                            }
                }

                if (m_fileHashLookup != null)
                {
                    using (new Logging.Timer("Build filehash lookup table"))
                        using (var rd = cmd.ExecuteReader(@"SELECT DISTINCT ""FullHash"", ""Length"", ""ID"" FROM ""BlockSet"""))
                            while (rd.Read())
                            {
                                var str  = rd.GetValue(0).ToString();
                                var size = Convert.ToInt64(rd.GetValue(1));
                                var id   = Convert.ToInt64(rd.GetValue(2));
                                m_fileHashLookup.Add(str, size, id);
                            }
                }

                if (m_metadataLookup != null)
                {
                    using (new Logging.Timer("Build metahash lookup table"))
                        using (var rd = cmd.ExecuteReader(@"SELECT ""Metadataset"".""ID"", ""Blockset"".""FullHash"", ""Blockset"".""Length"" FROM ""Metadataset"", ""Blockset"" WHERE ""Metadataset"".""BlocksetID"" = ""Blockset"".""ID"" "))
                            while (rd.Read())
                            {
                                var metadataid = Convert.ToInt64(rd.GetValue(0));
                                var hash       = rd.GetValue(1).ToString();
                                var size       = Convert.ToInt64(rd.GetValue(2));
                                m_metadataLookup.Add(hash, size, metadataid);
                            }
                }

                if (m_pathLookup != null)
                {
                    using (new Logging.Timer("Build path scantime lookup table"))
                        using (var rd = cmd.ExecuteReader(string.Format(@" SELECT ""FileID"", ""Scantime"", ""Path"", ""Scantime"" FROM ""{0}"" WHERE ""BlocksetID"" >= 0 ", m_scantimelookupTablename)))
                            while (rd.Read())
                            {
                                var id       = Convert.ToInt64(rd.GetValue(0));
                                var scantime = ParseFromEpochSeconds(Convert.ToInt64(rd.GetValue(1)));
                                var path     = rd.GetValue(2).ToString();
                                m_pathLookup.Insert(path, new PathEntryKeeper(id, scantime));
                            }
                }

                if (m_pathLookup != null)
                {
                    using (new Logging.Timer("Build path lookup table"))
                        using (var rd = cmd.ExecuteReader(string.Format(@" SELECT ""Path"", ""BlocksetID"", ""MetadataID"", ""ID"" FROM ""File"" ")))
                            while (rd.Read())
                            {
                                var             path       = rd.GetValue(0).ToString();
                                var             blocksetid = Convert.ToInt64(rd.GetValue(1));
                                var             metadataid = Convert.ToInt64(rd.GetValue(2));
                                var             filesetid  = Convert.ToInt64(rd.GetValue(3));
                                PathEntryKeeper r;
                                if (!m_pathLookup.TryFind(path, out r))
                                {
                                    r = new PathEntryKeeper(-1, DateTime.UtcNow);
                                    r.AddFilesetID(blocksetid, metadataid, filesetid);
                                    m_pathLookup.Insert(path, r);
                                }
                                else
                                {
                                    r.AddFilesetID(blocksetid, metadataid, filesetid);
                                }
                            }
                }

                m_missingBlockHashes = Convert.ToInt64(cmd.ExecuteScalar(@"SELECT COUNT (*) FROM (SELECT DISTINCT ""Block"".""Hash"", ""Block"".""Size"" FROM ""Block"", ""RemoteVolume"" WHERE ""RemoteVolume"".""ID"" = ""Block"".""VolumeID"" AND ""RemoteVolume"".""State"" NOT IN (?,?,?,?))", RemoteVolumeState.Temporary.ToString(), RemoteVolumeState.Uploading.ToString(), RemoteVolumeState.Uploaded.ToString(), RemoteVolumeState.Verified.ToString()));
            }
        }
예제 #10
0
        public static int Run(List<string> args, Dictionary<string, string> options, Library.Utility.IFilter filter)
        {
            if (args.Count != 2 && args.Count != 3)
            {
                Console.WriteLine("Invalid argument count ({0} expected 2 or 3): {1}{2}", args.Count, Environment.NewLine, string.Join(Environment.NewLine, args));
                return 100;
            }

            var folder = Path.GetFullPath(args[1]);

            if (!Directory.Exists(folder))
            {
                Console.WriteLine("Folder not found: {0}", folder);
                return 100;
            }

            Directory.SetCurrentDirectory(folder);

            string targetpath;
            options.TryGetValue("targetpath", out targetpath);

            string ixfile;
            options.TryGetValue("indexfile", out ixfile);
            if (string.IsNullOrWhiteSpace(ixfile))
                ixfile = "index.txt";

            ixfile = Path.GetFullPath(ixfile);
            if (!File.Exists(ixfile))
            {
                Console.WriteLine("Index file not found, perhaps you need to run the index command?");
                return 100;
            }

            Console.Write("Sorting index file ...");
            Index.SortFile(ixfile, ixfile);
            Console.WriteLine(" done!");

            string filelist;
            if (args.Count == 2)
            {
                var time = List.ParseListFiles(folder).First();
                filelist = time.Value;

                Console.WriteLine("Using set 0 with timestamp {0}", time.Key.ToLocalTime());
            }
            else
            {
                filelist = List.SelectListFile(args[2], folder);
            }

            Library.Main.Volumes.VolumeReaderBase.UpdateOptionsFromManifest(Path.GetExtension(filelist).Trim('.'), filelist, new Duplicati.Library.Main.Options(options));

            string blocksize_str;
            options.TryGetValue("blocksize", out blocksize_str);
            string blockhash_str;
            options.TryGetValue("block-hash-algorithm", out blockhash_str);
            string filehash_str;
            options.TryGetValue("block-hash-algorithm", out filehash_str);

            long blocksize = string.IsNullOrWhiteSpace(blocksize_str) ? 0 : Library.Utility.Sizeparser.ParseSize(blocksize_str);

            if (blocksize <= 0)
            {
                Console.WriteLine("Invalid blocksize: {0}, try setting --blocksize manually");
                return 100;
            }

            var blockhasher = string.IsNullOrWhiteSpace(blockhash_str) ? null : System.Security.Cryptography.HashAlgorithm.Create(blockhash_str);
            var filehasher = string.IsNullOrWhiteSpace(filehash_str) ? null : System.Security.Cryptography.HashAlgorithm.Create(filehash_str);

            if (blockhasher == null)
                throw new Exception(string.Format("Block hash algorithm not valid: {0}", blockhash_str));
            if (filehasher == null)
                throw new Exception(string.Format("File hash algorithm not valid: {0}", filehash_str));

            var hashesprblock = blocksize / (blockhasher.HashSize / 8);

            using(var mru = new CompressedFileMRUCache(options))
            {
                Console.WriteLine("Building lookup table for file hashes");
                var lookup = new HashLookupHelper(ixfile, mru, (int)blocksize, blockhasher.HashSize / 8);

                var filecount = 0L;
                string largestprefix = null;
                string[] largestprefixparts = null;

                if (!string.IsNullOrWhiteSpace(targetpath))
                    Console.WriteLine("Computing restore path");
                
                foreach(var f in List.EnumerateFilesInDList(filelist, filter, options))
                {
                    if (largestprefix == null)
                    {
                        largestprefix = f.Path;
                        largestprefixparts = largestprefix.Split(new char[] { Path.DirectorySeparatorChar });
                    }
                    else if (largestprefix.Length > 1)
                    {
                        var parts = f.Path.Split(new char[] { Path.DirectorySeparatorChar });

                        var ni = 0;
                        for(; ni < Math.Min(parts.Length, largestprefixparts.Length); ni++)
                            if (!Library.Utility.Utility.ClientFilenameStringComparer.Equals(parts[ni], largestprefixparts[ni]))
                                break;

                        if (ni != largestprefixparts.Length)
                        {
                            if (ni == 0)
                            {
                                largestprefixparts = new string[0];
                                largestprefix = string.Empty;
                            }
                            else
                            {
                                Array.Resize(ref largestprefixparts, ni - 1);
                                largestprefix = string.Join(Path.DirectorySeparatorChar.ToString(), largestprefixparts);
                            }
                        }
                    }
                    filecount++;
                }

                Console.WriteLine("Restoring {0} files to {1}", filecount, string.IsNullOrWhiteSpace(targetpath) ? "original position" : targetpath);

                if (Library.Utility.Utility.IsClientLinux || largestprefix.Length > 0)
                    largestprefix = Library.Utility.Utility.AppendDirSeparator(largestprefix);

                if (!string.IsNullOrEmpty(largestprefix))
                    Console.WriteLine("Removing common prefix {0} from files", largestprefix);

                var i = 0L;
                var errors = 0L;
                foreach(var f in List.EnumerateFilesInDList(filelist, filter, options))
                {
                    try
                    {
                        var targetfile = MapToRestorePath(f.Path, largestprefix, targetpath);
                        if (!Directory.Exists(Path.GetDirectoryName(targetfile)))
                            Directory.CreateDirectory(Path.GetDirectoryName(targetfile));
                            
                        Console.Write("{0}: {1} ({2})", i, targetfile, Library.Utility.Utility.FormatSizeString(f.Size));

                        using(var tf = new Library.Utility.TempFile())
                        {
                            using(var sw = File.OpenWrite(tf))
                            {
                                if (f.BlocklistHashes == null)
                                {
                                    lookup.WriteHash(sw, f.Hash);
                                }
                                else
                                {
                                    var blhi = 0L;
                                    foreach(var blh in f.BlocklistHashes)
                                    {
                                        Console.Write(" {0}", blhi);
                                        var blockhashoffset = blhi * hashesprblock * blocksize;

                                        try
                                        {
                                            var bi = 0;
                                            foreach(var h in lookup.ReadBlocklistHashes(blh))
                                            {
                                                try
                                                {
                                                    sw.Position = blockhashoffset + (bi * blocksize);
                                                    lookup.WriteHash(sw, h);
                                                }
                                                catch(Exception ex)
                                                {
                                                    Console.WriteLine("Failed to read hash: {0}{1}{2}", h, Environment.NewLine, ex.ToString());
                                                }

                                                bi++;
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            Console.WriteLine("Failed to read Blocklist hash: {0}{1}{2}", blh, Environment.NewLine, ex.ToString());
                                        }

                                        blhi++;
                                    }
                                }
                            }
                                
                            string fh;
                            using(var fs = File.OpenRead(tf))
                                fh = Convert.ToBase64String(filehasher.ComputeHash(fs));

                            if (fh == f.Hash)
                            {
                                Console.WriteLine(" done!");
                                File.Copy(tf, targetfile, true);
                            }
                            else
                            {
                                Console.Write(" - Restored file hash mismatch");
                                if (File.Exists(targetfile))
                                    Console.WriteLine(" - not overwriting existing file: {0}", targetfile);
                                else
                                    Console.WriteLine(" - restoring file in damaged condition");


                            }
                        }

                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(" error: {0}", ex.ToString());
                        errors++;
                    }
                    i++;
                }
            }

            return 0;
        }
예제 #11
0
        /// <summary>
        /// Builds the lookup tables. Call this method after deleting items, and before processing items
        /// </summary>
        /// <param name="options">The option settings</param>
        public void BuildLookupTable(Options options)
        {
            if (options.BlockHashLookupMemory > 0)
            {
                m_blockHashLookup = new HashLookupHelper <KeyValuePair <long, long> >((ulong)options.BlockHashLookupMemory);
            }
            if (options.FileHashLookupMemory > 0)
            {
                m_fileHashLookup = new HashLookupHelper <long>((ulong)options.FileHashLookupMemory);
            }
            if (options.MetadataHashMemory > 0)
            {
                m_metadataLookup = new HashLookupHelper <long>((ulong)options.MetadataHashMemory);
            }
            if (options.UseFilepathCache)
            {
                m_pathLookup = new PathLookupHelper <PathEntryKeeper>(true);
            }


            //Populate the lookup tables
            using (var cmd = m_connection.CreateCommand())
            {
                //Need a temporary table with path/lastmodified lookups
                var scantableDefinition =
                    @"SELECT ""A1"".""ID"" AS ""FileID"", ""A1"".""Lastmodified"" AS ""Lastmodified"", ""A1"".""Path"" AS ""Path"", ""C"".""Length"" AS ""Length"", ""F"".""Fullhash"" AS ""Metahash"", ""F"".""Length"" AS ""Metasize"", ""A1"".""BlocksetID"" " +
                    @"  FROM (SELECT ""File"".""ID"", ""File"".""BlocksetID"", ""File"".""MetadataID"", ""FilesetEntry"".""Lastmodified"", ""File"".""Path"", ""Fileset"".""Timestamp"" " +
                    @"          FROM ""FilesetEntry"", ""Fileset"", ""File"" WHERE ""Fileset"".""ID"" = ""FilesetEntry"".""FilesetID"" AND ""File"".""ID"" = ""FilesetEntry"".""FileID"" " +
                    @"       ) ""A1"" LEFT JOIN " +
                    @"       (SELECT ""File"".""Path"", ""Fileset"".""Timestamp"" " +
                    @"           FROM ""FilesetEntry"", ""Fileset"", ""File"" WHERE ""Fileset"".""ID"" = ""FilesetEntry"".""FilesetID"" AND ""File"".""ID"" = ""FilesetEntry"".""FileID"" " +
                    @"       ) ""A2"" ON ""A1"".""Path"" = ""A2"".""Path"" AND ""A1"".""Timestamp"" < ""A2"".""Timestamp"" " +
                    @"       , ""Blockset"" ""C"", ""Metadataset"" ""E"", ""Blockset"" ""F"" " +
                    @" WHERE ""A2"".""Path"" IS NULL " +
                    @"   AND ""C"".""ID"" = ""A1"".""BlocksetID"" " +
                    @"   AND ""A1"".""MetadataID"" = ""E"".""ID"" " +
                    @"   AND ""F"".""ID"" = ""E"".""BlocksetID"" ";

                if (m_blockHashLookup != null)
                {
                    try
                    {
                        using (new Logging.Timer("Build blockhash lookup table"))
                            using (var rd = cmd.ExecuteReader(@"SELECT DISTINCT ""Block"".""Hash"", ""Block"".""ID"", ""Block"".""Size"" FROM ""Block"" "))
                                while (rd.Read())
                                {
                                    var str  = rd.GetValue(0).ToString();
                                    var id   = rd.GetInt64(1);
                                    var size = rd.GetInt64(2);
                                    m_blockHashLookup.Add(str, size, new KeyValuePair <long, long>(id, size));
                                }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidDataException("Duplicate blockhashes detected, either repair the database or rebuild it", ex);
                    }
                }

                if (m_fileHashLookup != null)
                {
                    try
                    {
                        using (new Logging.Timer("Build filehash lookup table"))
                            using (var rd = cmd.ExecuteReader(@"SELECT DISTINCT ""FullHash"", ""Length"", ""ID"" FROM ""BlockSet"""))
                                while (rd.Read())
                                {
                                    var str  = rd.GetValue(0).ToString();
                                    var size = rd.GetInt64(1);
                                    var id   = rd.GetInt64(2);
                                    m_fileHashLookup.Add(str, size, id);
                                }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidDataException("Duplicate filehashes detected, either repair the database or rebuild it", ex);
                    }
                }


                if (m_metadataLookup != null)
                {
                    try
                    {
                        using (new Logging.Timer("Build metahash lookup table"))
                            using (var rd = cmd.ExecuteReader(@"SELECT ""Metadataset"".""ID"", ""Blockset"".""FullHash"", ""Blockset"".""Length"" FROM ""Metadataset"", ""Blockset"" WHERE ""Metadataset"".""BlocksetID"" = ""Blockset"".""ID"" "))
                                while (rd.Read())
                                {
                                    var metadataid = rd.GetInt64(0);
                                    var hash       = rd.GetValue(1).ToString();
                                    var size       = rd.GetInt64(2);;
                                    m_metadataLookup.Add(hash, size, metadataid);
                                }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidDataException("Duplicate metadatahash detected, run repair to fix it", ex);
                    }
                }

                if (m_pathLookup != null)
                {
                    using (new Logging.Timer("Build path lastmodified lookup table"))
                        using (var rd = cmd.ExecuteReader(string.Format(@" SELECT ""FileID"", ""Lastmodified"", ""Length"", ""Path"", ""Metahash"", ""Metasize"" FROM ({0}) WHERE ""BlocksetID"" >= 0 ", scantableDefinition)))
                            while (rd.Read())
                            {
                                var id           = rd.GetInt64(0);
                                var lastmodified = new DateTime(rd.GetInt64(1), DateTimeKind.Utc);
                                var filesize     = rd.GetInt64(2);
                                var path         = rd.GetString(3);
                                var metahash     = rd.GetString(4);
                                var metasize     = rd.GetInt64(5);
                                m_pathLookup.Insert(path, new PathEntryKeeper(id, lastmodified, filesize, metahash, metasize));
                            }
                }

                if (m_pathLookup != null)
                {
                    try
                    {
                        using (new Logging.Timer("Build path lookup table"))
                            using (var rd = cmd.ExecuteReader(string.Format(@" SELECT ""Path"", ""BlocksetID"", ""MetadataID"", ""ID"" FROM ""File"" ")))
                                while (rd.Read())
                                {
                                    var path       = rd.GetValue(0).ToString();
                                    var blocksetid = rd.GetInt64(1);
                                    var metadataid = rd.GetInt64(2);
                                    var filesetid  = rd.GetInt64(3);

                                    PathEntryKeeper r;
                                    if (!m_pathLookup.TryFind(path, out r))
                                    {
                                        r = new PathEntryKeeper(-1, new DateTime(0, DateTimeKind.Utc), -1, null, -1);
                                        r.AddFilesetID(blocksetid, metadataid, filesetid);
                                        m_pathLookup.Insert(path, r);
                                    }
                                    else
                                    {
                                        r.AddFilesetID(blocksetid, metadataid, filesetid);
                                    }
                                }
                    }
                    catch (Exception ex)
                    {
                        throw new InvalidDataException("Duplicate file entries detected, run repair to fix it", ex);
                    }
                }

                m_missingBlockHashes = cmd.ExecuteScalarInt64(@"SELECT COUNT (*) FROM (SELECT DISTINCT ""Block"".""Hash"", ""Block"".""Size"" FROM ""Block"", ""RemoteVolume"" WHERE ""RemoteVolume"".""ID"" = ""Block"".""VolumeID"" AND ""RemoteVolume"".""State"" NOT IN (?,?,?,?))", 0, RemoteVolumeState.Temporary.ToString(), RemoteVolumeState.Uploading.ToString(), RemoteVolumeState.Uploaded.ToString(), RemoteVolumeState.Verified.ToString());

                var tc = cmd.ExecuteScalarInt64(@"SELECT COUNT(*) FROM ""Remotevolume"" WHERE ""ID"" IN (SELECT DISTINCT ""VolumeID"" FROM ""Block"") AND ""State"" NOT IN (?, ?, ?, ?);", 0, RemoteVolumeState.Temporary.ToString(), RemoteVolumeState.Uploading.ToString(), RemoteVolumeState.Uploaded.ToString(), RemoteVolumeState.Verified.ToString());
                if (tc > 0)
                {
                    throw new InvalidDataException("Detected blocks that are not reachable in the block table");
                }
            }
        }