コード例 #1
0
ファイル: journal_c.cs プロジェクト: RainsSoft/CsharpSQLite
/*
** If it does not already exists, create and populate the on-disk file
** for JournalFile p.
*/
static int createFile(JournalFile p){
int rc = SQLITE_OK;
if( null==p.pReal ){
sqlite3_file pReal = (sqlite3_file )&p[1];
rc = sqlite3OsOpen(p.pVfs, p.zJournal, pReal, p.flags, 0);
if( rc==SQLITE_OK ){
p.pReal = pReal;
if( p.iSize>0 ){
Debug.Assert(p.iSize<=p.nBuf);
rc = sqlite3OsWrite(p.pReal, p.zBuf, p.iSize, 0);
}
}
}
return rc;
}
コード例 #2
0
        public void ToStream(StorageEnvironment env, JournalFile journal, long start4Kb,
                             long numberOf4KbsToCopy, Stream output, Action <string> infoNotify = null, CancellationToken cancellationToken = default)
        {
            const int pageSize = 4 * Constants.Size.Kilobyte;
            var       maxNumOf4KbsToCopyAtOnce = _buffer.Length / pageSize;
            var       page        = start4Kb;
            var       toBeCopied  = new Size(numberOf4KbsToCopy * pageSize, SizeUnit.Bytes).ToString();
            var       totalSw     = Stopwatch.StartNew();
            var       sw          = Stopwatch.StartNew();
            long      totalCopied = 0;

            fixed(byte *ptr = _buffer)
            {
                while (numberOf4KbsToCopy > 0)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var pageCount = Math.Min(maxNumOf4KbsToCopyAtOnce, numberOf4KbsToCopy);

                    if (journal.JournalWriter.Read(ptr,
                                                   pageCount * pageSize,
                                                   page * pageSize) == false)
                    {
                        throw new InvalidOperationException("Could not read from journal #" + journal.Number + " " +
                                                            +pageCount + " pages.");
                    }
                    var bytesCount = (int)(pageCount * (4 * Constants.Size.Kilobyte));
                    output.Write(_buffer, 0, bytesCount);
                    page += pageCount;
                    numberOf4KbsToCopy -= pageCount;

                    totalCopied += bytesCount;
                    if (sw.ElapsedMilliseconds > 500)
                    {
                        infoNotify?.Invoke($"Copied: {new Size(totalCopied, SizeUnit.Bytes)} / {toBeCopied}");
                        sw.Restart();
                    }
                }
            }

            var totalSecElapsed = Math.Max((double)totalSw.ElapsedMilliseconds / 1000, 0.0001);

            infoNotify?.Invoke($"Finshed copying {new Size(totalCopied, SizeUnit.Bytes)}, " +
                               $"{new Size((long)(totalCopied / totalSecElapsed), SizeUnit.Bytes)}/sec");

            Debug.Assert(numberOf4KbsToCopy == 0);
        }
コード例 #3
0
/*
** If it does not already exists, create and populate the on-disk file
** for JournalFile p.
*/
        static int createFile(JournalFile p)
        {
            int rc = SQLITE_OK;

            if (null == p.pReal)
            {
                sqlite3_file pReal = (sqlite3_file) & p[1];
                rc = sqlite3OsOpen(p.pVfs, p.zJournal, pReal, p.flags, 0);
                if (rc == SQLITE_OK)
                {
                    p.pReal = pReal;
                    if (p.iSize > 0)
                    {
                        Debug.Assert(p.iSize <= p.nBuf);
                        rc = sqlite3OsWrite(p.pReal, p.zBuf, p.iSize, 0);
                    }
                }
            }
            return(rc);
        }
コード例 #4
0
        public void ToStream(JournalFile journal, long startPage, long pagesToCopy, Stream output)
        {
            var maxNumOfPagesToCopyAtOnce = _buffer.Length / AbstractPager.PageSize;
            var page = startPage;

            fixed(byte *ptr = _buffer)
            {
                while (pagesToCopy > 0)
                {
                    var pageCount  = Math.Min(maxNumOfPagesToCopyAtOnce, pagesToCopy);
                    var bytesCount = (int)(pageCount * AbstractPager.PageSize);

                    journal.JournalWriter.Read(page, ptr, bytesCount);
                    output.Write(_buffer, 0, bytesCount);
                    page        += pageCount;
                    pagesToCopy -= pageCount;
                }
            }

            Debug.Assert(pagesToCopy == 0);
        }
コード例 #5
0
        public long ToFile(StorageEnvironment env, string backupPath, CompressionLevel compression = CompressionLevel.Optimal,
                           Action <string> infoNotify = null)
        {
            infoNotify = infoNotify ?? (s => { });

            if (env.Options.IncrementalBackupEnabled == false)
            {
                throw new InvalidOperationException("Incremental backup is disabled for this storage");
            }

            long numberOfBackedUpPages = 0;

            var copier        = new DataCopier(AbstractPager.PageSize * 16);
            var backupSuccess = true;

            long lastWrittenLogPage = -1;
            long lastWrittenLogFile = -1;

            using (var file = new FileStream(backupPath, FileMode.Create))
                using (var package = new ZipArchive(file, ZipArchiveMode.Create))
                {
                    IncrementalBackupInfo backupInfo;
                    using (var txw = env.NewTransaction(TransactionFlags.ReadWrite))
                    {
                        backupInfo = env.HeaderAccessor.Get(ptr => ptr->IncrementalBackup);

                        if (env.Journal.CurrentFile != null)
                        {
                            lastWrittenLogFile = env.Journal.CurrentFile.Number;
                            lastWrittenLogPage = env.Journal.CurrentFile.WritePagePosition;
                        }

                        // txw.Commit(); intentionally not committing
                    }

                    using (env.NewTransaction(TransactionFlags.Read))
                    {
                        var usedJournals = new List <JournalFile>();

                        try
                        {
                            long lastBackedUpPage = -1;
                            long lastBackedUpFile = -1;

                            var firstJournalToBackup = backupInfo.LastBackedUpJournal;

                            if (firstJournalToBackup == -1)
                            {
                                firstJournalToBackup = 0; // first time that we do incremental backup
                            }
                            for (var journalNum = firstJournalToBackup; journalNum <= backupInfo.LastCreatedJournal; journalNum++)
                            {
                                var num = journalNum;

                                var journalFile = env.Journal.Files.FirstOrDefault(x => x.Number == journalNum); // first check journal files currently being in use
                                if (journalFile == null)
                                {
                                    long journalSize;
                                    using (var pager = env.Options.OpenJournalPager(journalNum))
                                    {
                                        journalSize = Utils.NearestPowerOfTwo(pager.NumberOfAllocatedPages * AbstractPager.PageSize);
                                    }

                                    journalFile = new JournalFile(env.Options.CreateJournalWriter(journalNum, journalSize), journalNum);
                                }

                                journalFile.AddRef();

                                usedJournals.Add(journalFile);

                                var startBackupAt = 0L;
                                var pagesToCopy   = journalFile.JournalWriter.NumberOfAllocatedPages;
                                if (journalFile.Number == backupInfo.LastBackedUpJournal)
                                {
                                    startBackupAt = backupInfo.LastBackedUpJournalPage + 1;
                                    pagesToCopy  -= startBackupAt;
                                }

                                if (startBackupAt >= journalFile.JournalWriter.NumberOfAllocatedPages) // nothing to do here
                                {
                                    continue;
                                }

                                var part = package.CreateEntry(StorageEnvironmentOptions.JournalName(journalNum), compression);
                                Debug.Assert(part != null);

                                if (journalFile.Number == lastWrittenLogFile)
                                {
                                    pagesToCopy -= (journalFile.JournalWriter.NumberOfAllocatedPages - lastWrittenLogPage);
                                }

                                using (var stream = part.Open())
                                {
                                    copier.ToStream(journalFile, startBackupAt, pagesToCopy, stream);
                                    infoNotify(string.Format("Voron Incr copy journal number {0}", num));
                                }

                                lastBackedUpFile = journalFile.Number;
                                if (journalFile.Number == backupInfo.LastCreatedJournal)
                                {
                                    lastBackedUpPage = startBackupAt + pagesToCopy - 1;
                                    // we used all of this file, so the next backup should start in the next file
                                    if (lastBackedUpPage == (journalFile.JournalWriter.NumberOfAllocatedPages - 1))
                                    {
                                        lastBackedUpPage = -1;
                                        lastBackedUpFile++;
                                    }
                                }

                                numberOfBackedUpPages += pagesToCopy;
                            }

                            //Debug.Assert(lastBackedUpPage != -1);

                            env.HeaderAccessor.Modify(header =>
                            {
                                header->IncrementalBackup.LastBackedUpJournal     = lastBackedUpFile;
                                header->IncrementalBackup.LastBackedUpJournalPage = lastBackedUpPage;
                            });
                        }
                        catch (Exception)
                        {
                            backupSuccess = false;
                            throw;
                        }
                        finally
                        {
                            foreach (var jrnl in usedJournals)
                            {
                                if (backupSuccess)                        // if backup succeeded we can remove journals
                                {
                                    if (jrnl.Number < lastWrittenLogFile) // prevent deletion of the current journal and journals with a greater number
                                    {
                                        jrnl.DeleteOnClose = true;
                                    }
                                }

                                jrnl.Release();
                            }
                        }
                        infoNotify(string.Format("Voron Incr Backup total {0} pages", numberOfBackedUpPages));
                        return(numberOfBackedUpPages);
                    }
                }
        }
コード例 #6
0
ファイル: FullBackup.cs プロジェクト: Danielle9897/ravendb
        private static void Backup(
            StorageEnvironment env, CompressionLevel compression, Action <string> infoNotify,
            Action backupStarted, AbstractPager dataPager, ZipArchive package, string basePath, DataCopier copier)
        {
            var  usedJournals       = new List <JournalFile>();
            long lastWrittenLogPage = -1;
            long lastWrittenLogFile = -1;
            LowLevelTransaction txr = null;

            try
            {
                long allocatedPages;
                var  writePesistentContext = new TransactionPersistentContext(true);
                var  readPesistentContext  = new TransactionPersistentContext(true);
                using (var txw = env.NewLowLevelTransaction(writePesistentContext, TransactionFlags.ReadWrite)) // so we can snapshot the headers safely
                {
                    txr            = env.NewLowLevelTransaction(readPesistentContext, TransactionFlags.Read);   // now have snapshot view
                    allocatedPages = dataPager.NumberOfAllocatedPages;

                    Debug.Assert(HeaderAccessor.HeaderFileNames.Length == 2);
                    infoNotify("Voron copy headers for " + basePath);
                    VoronBackupUtil.CopyHeaders(compression, package, copier, env.Options, basePath);

                    // journal files snapshot
                    var files = env.Journal.Files; // thread safety copy

                    JournalInfo journalInfo = env.HeaderAccessor.Get(ptr => ptr->Journal);
                    for (var journalNum = journalInfo.CurrentJournal - journalInfo.JournalFilesCount + 1;
                         journalNum <= journalInfo.CurrentJournal;
                         journalNum++)
                    {
                        var journalFile = files.FirstOrDefault(x => x.Number == journalNum);
                        // first check journal files currently being in use
                        if (journalFile == null)
                        {
                            long journalSize;
                            using (var pager = env.Options.OpenJournalPager(journalNum))
                            {
                                journalSize = Bits.NextPowerOf2(pager.NumberOfAllocatedPages * env.Options.PageSize);
                            }

                            journalFile = new JournalFile(env, env.Options.CreateJournalWriter(journalNum, journalSize), journalNum);
                        }

                        journalFile.AddRef();
                        usedJournals.Add(journalFile);
                    }

                    if (env.Journal.CurrentFile != null)
                    {
                        lastWrittenLogFile = env.Journal.CurrentFile.Number;
                        lastWrittenLogPage = env.Journal.CurrentFile.WritePagePosition - 1;
                    }

                    // txw.Commit(); intentionally not committing
                }

                backupStarted?.Invoke();

                // data file backup
                var dataPart = package.CreateEntry(Path.Combine(basePath, Constants.DatabaseFilename), compression);
                Debug.Assert(dataPart != null);

                if (allocatedPages > 0) //only true if dataPager is still empty at backup start
                {
                    using (var dataStream = dataPart.Open())
                    {
                        // now can copy everything else
                        copier.ToStream(dataPager, 0, allocatedPages, dataStream);
                    }
                }

                try
                {
                    foreach (JournalFile journalFile in usedJournals)
                    {
                        var entryName   = Path.Combine(basePath, StorageEnvironmentOptions.JournalName(journalFile.Number));
                        var journalPart = package.CreateEntry(entryName, compression);

                        Debug.Assert(journalPart != null);

                        long pagesToCopy = journalFile.JournalWriter.NumberOfAllocatedPages;
                        if (journalFile.Number == lastWrittenLogFile)
                        {
                            pagesToCopy = lastWrittenLogPage + 1;
                        }

                        using (var stream = journalPart.Open())
                        {
                            copier.ToStream(env, journalFile, 0, pagesToCopy, stream);
                            infoNotify(string.Format("Voron copy journal file {0}", entryName));
                        }
                    }
                }
                finally
                {
                    foreach (var journalFile in usedJournals)
                    {
                        journalFile.Release();
                    }
                }
            }
            finally
            {
                txr?.Dispose();
            }
        }
コード例 #7
0
 /// <summary>
 /// Constructs the entry.
 /// </summary>
 /// <param name="resource_name"></param>
 /// <param name="journal"></param>
 /// <param name="position"></param>
 /// <param name="page_number"></param>
 public JournalEntry(String resource_name, JournalFile journal, long position, long page_number)
 {
     this.resource_name = resource_name;
     this.journal = journal;
     this.position = position;
     this.page_number = page_number;
 }
コード例 #8
0
        /// <summary>
        /// Recovers any lost operations that are currently input the journal.
        /// </summary>
        /// <remarks>
        /// This retries all logged entries. This would typically be called before 
        /// any other IO operations.
        /// </remarks>
        private void RollForwardRecover()
        {
            //    Console.Out.WriteLine("RollForwardRecover()");

            // The list of all journal files,
            ArrayList journal_files_list = new ArrayList();

            // Scan the journal path for any journal files.
            for (int i = 10; i < 74; ++i) {
                String journal_fn = GetJournalFileName(i);
                string f = Path.Combine(journal_path, journal_fn);
                // If the journal exists, create a summary of the journal
                if (File.Exists(f)) {
                    if (read_only) {
                        throw new IOException(
                            "Journal file " + f + " exists for a Read-only session.  " +
                            "There may not be any pending journals for a Read-only session.");
                    }

                    JournalFile jf = new JournalFile(this, f, read_only);
                    // Open the journal file for recovery.  This will set various
                    // information about the journal such as the last check point and the
                    // id of the journal file.
                    JournalSummary summary = jf.OpenForRecovery();
                    // If the journal can be recovered from.
                    if (summary.can_be_recovered) {
                        if (Debug.IsInterestedIn(LogLevel.Information)) {
                            Debug.Log(LogLevel.Information, this, "Journal " + jf +
                                                               " found - can be recovered.");
                        }
                        journal_files_list.Add(summary);
                    } else {
                        if (Debug.IsInterestedIn(LogLevel.Information)) {
                            Debug.Log(LogLevel.Information, this, "Journal " + jf +
                                                               " deleting - nothing to recover.");
                        }
                        // Otherwise close and delete it
                        jf.CloseAndDelete();
                    }
                }
            }

            //    if (journal_files_list.size() == 0) {
            //      Console.Out.WriteLine("Nothing to recover.");
            //    }

            // Sort the journal file list from oldest to newest.  The oldest journals
            // are recovered first.
            journal_files_list.Sort(journal_list_comparator);

            long last_journal_number = -1;

            // Persist the journals
            for (int i = 0; i < journal_files_list.Count; ++i) {
                JournalSummary summary = (JournalSummary)journal_files_list[i];

                // Check the resources for this summary
                ArrayList res_list = summary.resource_list;
                for (int n = 0; n < res_list.Count; ++n) {
                    String resource_name = (String)res_list[n];
                    // This puts the resource into the hash map.
                    IJournalledResource resource = CreateResource(resource_name);
                }

                // Assert that we are recovering the journals input the correct order
                JournalFile jf = summary.journal_file;
                if (jf.journal_number < last_journal_number) {
                    throw new ApplicationException("Assertion failed, sort failed.");
                }
                last_journal_number = jf.journal_number;

                if (Debug.IsInterestedIn(LogLevel.Information)) {
                    Debug.Log(LogLevel.Information, this, "Recovering: " + jf +
                                                       " (8 .. " + summary.last_checkpoint + ")");
                }

                jf.Persist(8, summary.last_checkpoint);
                // Then close and delete.
                jf.CloseAndDelete();

                // Check the resources for this summary and close them
                for (int n = 0; n < res_list.Count; ++n) {
                    String resource_name = (String)res_list[n];
                    AbstractResource resource =
                        (AbstractResource)CreateResource(resource_name);
                    // When we finished, make sure the resource is closed again
                    // Close the resource
                    resource.PersistClose();
                    // Post recover notification
                    resource.OnPostRecover();
                }
            }
        }
コード例 #9
0
        /// <summary>
        /// Creates a new top journal file.
        /// </summary>
        private void NewTopJournalFile()
        {
            //    // Move the old journal to the archive?
            //    if (top_journal_file != null) {
            //      journal_archives.add(top_journal_file);
            //    }

            String journal_fn = GetJournalFileName((int)((journal_number & 63) + 10));
            ++journal_number;

            string f = Path.Combine(journal_path, journal_fn);
            if (File.Exists(f)) {
                throw new IOException("Journal file already exists.");
            }

            top_journal_file = new JournalFile(this, f, read_only);
            top_journal_file.Open(journal_number - 1);
        }
コード例 #10
0
 public JournalSummary(JournalFile journal_file)
 {
     this.journal_file = journal_file;
 }
コード例 #11
0
        private static void Backup(
            StorageEnvironment env, CompressionLevel compression, Action <string> infoNotify,
            Action backupStarted, AbstractPager dataPager, ZipArchive package, string basePath, DataCopier copier)
        {
            var  usedJournals       = new List <JournalFile>();
            long lastWrittenLogPage = -1;
            long lastWrittenLogFile = -1;
            LowLevelTransaction txr = null;
            var backupSuccess       = false;

            try
            {
                long allocatedPages;
                var  writePesistentContext = new TransactionPersistentContext(true);
                var  readPesistentContext  = new TransactionPersistentContext(true);
                using (var txw = env.NewLowLevelTransaction(writePesistentContext, TransactionFlags.ReadWrite)) // so we can snapshot the headers safely
                {
                    txr            = env.NewLowLevelTransaction(readPesistentContext, TransactionFlags.Read);   // now have snapshot view
                    allocatedPages = dataPager.NumberOfAllocatedPages;

                    Debug.Assert(HeaderAccessor.HeaderFileNames.Length == 2);
                    infoNotify("Voron copy headers for " + basePath);
                    VoronBackupUtil.CopyHeaders(compression, package, copier, env.Options, basePath);

                    // journal files snapshot
                    var files = env.Journal.Files; // thread safety copy

                    JournalInfo journalInfo = env.HeaderAccessor.Get(ptr => ptr->Journal);
                    for (var journalNum = journalInfo.CurrentJournal - journalInfo.JournalFilesCount + 1;
                         journalNum <= journalInfo.CurrentJournal;
                         journalNum++)
                    {
                        var journalFile = files.FirstOrDefault(x => x.Number == journalNum);
                        // first check journal files currently being in use
                        if (journalFile == null)
                        {
                            long journalSize;
                            using (var pager = env.Options.OpenJournalPager(journalNum))
                            {
                                journalSize = Bits.NextPowerOf2(pager.NumberOfAllocatedPages * Constants.Storage.PageSize);
                            }

                            journalFile = new JournalFile(env, env.Options.CreateJournalWriter(journalNum, journalSize), journalNum);
                        }

                        journalFile.AddRef();
                        usedJournals.Add(journalFile);
                    }

                    if (env.Journal.CurrentFile != null)
                    {
                        lastWrittenLogFile = env.Journal.CurrentFile.Number;
                        lastWrittenLogPage = env.Journal.CurrentFile.WritePosIn4KbPosition - 1;
                    }

                    // txw.Commit(); intentionally not committing
                }

                backupStarted?.Invoke();

                // data file backup
                var dataPart = package.CreateEntry(Path.Combine(basePath, Constants.DatabaseFilename), compression);
                Debug.Assert(dataPart != null);

                if (allocatedPages > 0) //only true if dataPager is still empty at backup start
                {
                    using (var dataStream = dataPart.Open())
                    {
                        // now can copy everything else
                        copier.ToStream(dataPager, 0, allocatedPages, dataStream);
                    }
                }

                try
                {
                    long lastBackedupJournal = 0;
                    foreach (var journalFile in usedJournals)
                    {
                        var entryName   = StorageEnvironmentOptions.JournalName(journalFile.Number);
                        var journalPart = package.CreateEntry(Path.Combine(basePath, entryName), compression);

                        Debug.Assert(journalPart != null);

                        long pagesToCopy = journalFile.JournalWriter.NumberOfAllocated4Kb;
                        if (journalFile.Number == lastWrittenLogFile)
                        {
                            pagesToCopy = lastWrittenLogPage + 1;
                        }

                        using (var stream = journalPart.Open())
                        {
                            copier.ToStream(env, journalFile, 0, pagesToCopy, stream);
                            infoNotify(string.Format("Voron copy journal file {0}", entryName));
                        }

                        lastBackedupJournal = journalFile.Number;
                    }

                    if (env.Options.IncrementalBackupEnabled)
                    {
                        env.HeaderAccessor.Modify(header =>
                        {
                            header->IncrementalBackup.LastBackedUpJournal = lastBackedupJournal;

                            //since we backed-up everything, no need to start next incremental backup from the middle
                            header->IncrementalBackup.LastBackedUpJournalPage = -1;
                        });
                    }
                    backupSuccess = true;
                }
                catch (Exception)
                {
                    backupSuccess = false;
                    throw;
                }
                finally
                {
                    var lastSyncedJournal = env.HeaderAccessor.Get(header => header->Journal).LastSyncedJournal;
                    foreach (var journalFile in usedJournals)
                    {
                        if (backupSuccess)                                 // if backup succeeded we can remove journals
                        {
                            if (journalFile.Number < lastWrittenLogFile && // prevent deletion of the current journal and journals with a greater number
                                journalFile.Number < lastSyncedJournal)    // prevent deletion of journals that aren't synced with the data file
                            {
                                journalFile.DeleteOnClose = true;
                            }
                        }

                        journalFile.Release();
                    }
                }
            }
            finally
            {
                txr?.Dispose();
            }
        }
コード例 #12
0
        public static void Main(string[] args)
        {
            bool   showHelp        = false;
            bool?  extractUnknowns = null;
            bool   overwriteFiles  = false;
            bool   verbose         = true;
            string pattern         = null;

            var options = new OptionSet()
            {
                { "o|overwrite", "overwrite existing files", v => overwriteFiles = v != null },
                {
                    "nu|no-unknowns", "don't extract unknown files",
                    v => extractUnknowns = v != null ? false : extractUnknowns
                },
                {
                    "ou|only-unknowns", "only extract unknown files",
                    v => extractUnknowns = v != null ? true : extractUnknowns
                },
                { "v|verbose", "be verbose", v => verbose = v != null },
                { "f|filter=", "match files using pattern", v => pattern = v },
                { "h|help", "show this message and exit", v => showHelp = v != null },
            };

            List <string> extras;

            try
            {
                extras = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("{0}: ", GetExecutableName());
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName());
                return;
            }

            if (extras.Count < 1 ||
                extras.Count > 2 ||
                showHelp == true)
            {
                Console.WriteLine("Usage: {0} [OPTIONS]+ input_file.hogg [output_dir]", GetExecutableName());
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return;
            }

            string inputPath  = extras[0];
            string outputPath = extras.Count > 1 ? extras[1] : Path.ChangeExtension(inputPath, null) + "_unpack";

            Regex regex = null;

            if (string.IsNullOrEmpty(pattern) == false)
            {
                regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
            }

            using (var input = File.OpenRead(inputPath))
            {
                var hog = new HogFile();
                hog.Deserialize(input);

                var dataList      = new Dictionary <int, byte[]>();
                var dataListEntry = hog.Files
                                    .SingleOrDefault(e => e.AttributeId == 0 && e.Size != -1);
                if (dataListEntry != null)
                {
                    using (var data = ReadFileData(hog, dataListEntry, input))
                    {
                        if (data.ReadValueU32(hog.Endian) != 0)
                        {
                            throw new FormatException();
                        }

                        var count = data.ReadValueS32(hog.Endian);
                        if (count < 0)
                        {
                            throw new FormatException();
                        }

                        for (int i = 0; i < count; i++)
                        {
                            var length = data.ReadValueU32(hog.Endian);
                            dataList.Add(i, data.ReadBytes((int)length));
                        }
                    }
                }

                using (var data = new MemoryStream(hog.DataListJournal))
                {
                    var journal = new JournalFile()
                    {
                        Endian = hog.Endian,
                    };
                    journal.Deserialize(data);

                    foreach (var entry in journal.Entries)
                    {
                        if (entry.Action == Journal.Action.Add)
                        {
                            dataList[entry.TargetId] = (byte[])entry.Data.Clone();
                        }
                        else if (entry.Action == Journal.Action.Remove)
                        {
                            if (dataList.Remove(entry.TargetId) == false)
                            {
                                throw new InvalidOperationException();
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException();
                        }
                    }
                }

                var consumedAttributes = new List <int>();
                var files = hog.Files
                            .Where(e => e.Size != -1 && e.AttributeId != 0)
                            .ToArray();

                long current = 0;
                long total   = files.Count();

                var names = new Dictionary <int, string>();

                foreach (var entry in files)
                {
                    current++;

                    if (entry.Unknown5 != -2 || entry.AttributeId == -1)
                    {
                        throw new InvalidOperationException();
                    }

                    if (entry.AttributeId < 0 || entry.AttributeId >= hog.Attributes.Count)
                    {
                        throw new InvalidOperationException();
                    }

                    if ((hog.Attributes[entry.AttributeId].Flags & 1) == 1) // entry is unused
                    {
                        throw new InvalidOperationException();
                    }

                    if (consumedAttributes.Contains(entry.AttributeId) == true)
                    {
                        throw new InvalidOperationException();
                    }

                    consumedAttributes.Add(entry.AttributeId);

                    var attribute = hog.Attributes[entry.AttributeId];

                    string name;

                    if (dataList.ContainsKey(attribute.NameId) == false)
                    {
                        name = Path.Combine("__UNKNOWN", attribute.NameId.ToString(CultureInfo.InvariantCulture));
                    }
                    else
                    {
                        if (names.ContainsKey(attribute.NameId) == true)
                        {
                            name = names[attribute.NameId];
                        }
                        else
                        {
                            using (var temp = new MemoryStream(dataList[attribute.NameId]))
                            {
                                name = temp.ReadStringZ(Encoding.UTF8);
                                names.Add(attribute.NameId, name);
                            }
                        }

                        name = name.Replace('/', Path.DirectorySeparatorChar);
                        if (name.StartsWith(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture)) == true)
                        {
                            name = name.Substring(1);
                        }
                    }

                    if (regex != null && regex.IsMatch(name) == false)
                    {
                        continue;
                    }

                    var entryPath = Path.Combine(outputPath, name);

                    if (overwriteFiles == false &&
                        File.Exists(entryPath) == true)
                    {
                        continue;
                    }

                    var entryDirectory = Path.GetDirectoryName(entryPath);
                    if (entryDirectory != null)
                    {
                        Directory.CreateDirectory(entryDirectory);
                    }

                    if (verbose == true)
                    {
                        Console.WriteLine("[{0}/{1}] {2}", current, total, name);
                    }

                    using (var output = File.Create(entryPath))
                    {
                        ReadFileData(hog, entry, input, output);
                    }
                }
            }
        }
コード例 #13
0
        public void ToFile(StorageEnvironment env, string backupPath, CompressionLevel compression = CompressionLevel.Optimal,
                           Action <string> infoNotify = null)
        {
            infoNotify = infoNotify ?? (s => { });

            var         dataPager = env.Options.DataPager;
            var         copier    = new DataCopier(AbstractPager.PageSize * 16);
            Transaction txr       = null;

            try
            {
                infoNotify("Voron copy headers");

                using (var file = new FileStream(backupPath, FileMode.Create))
                    using (var package = new ZipArchive(file, ZipArchiveMode.Create))
                    {
                        long allocatedPages;

                        ImmutableAppendOnlyList <JournalFile> files;                // thread safety copy
                        var  usedJournals       = new List <JournalFile>();
                        long lastWrittenLogPage = -1;
                        long lastWrittenLogFile = -1;
                        using (var txw = env.NewTransaction(TransactionFlags.ReadWrite))         // so we can snapshot the headers safely
                        {
                            txr            = env.NewTransaction(TransactionFlags.Read);          // now have snapshot view
                            allocatedPages = dataPager.NumberOfAllocatedPages;

                            Debug.Assert(HeaderAccessor.HeaderFileNames.Length == 2);

                            VoronBackupUtil.CopyHeaders(compression, package, copier, env.Options);

                            // journal files snapshot
                            files = env.Journal.Files;

                            JournalInfo journalInfo = env.HeaderAccessor.Get(ptr => ptr->Journal);
                            for (var journalNum = journalInfo.CurrentJournal - journalInfo.JournalFilesCount + 1; journalNum <= journalInfo.CurrentJournal; journalNum++)
                            {
                                var journalFile = files.FirstOrDefault(x => x.Number == journalNum); // first check journal files currently being in use
                                if (journalFile == null)
                                {
                                    long journalSize;
                                    using (var pager = env.Options.OpenJournalPager(journalNum))
                                    {
                                        journalSize = Utils.NearestPowerOfTwo(pager.NumberOfAllocatedPages * AbstractPager.PageSize);
                                    }

                                    journalFile = new JournalFile(env.Options.CreateJournalWriter(journalNum, journalSize), journalNum);
                                }

                                journalFile.AddRef();
                                usedJournals.Add(journalFile);
                            }

                            if (env.Journal.CurrentFile != null)
                            {
                                lastWrittenLogFile = env.Journal.CurrentFile.Number;
                                lastWrittenLogPage = env.Journal.CurrentFile.WritePagePosition - 1;
                            }

                            // txw.Commit(); intentionally not committing
                        }

                        // data file backup
                        var dataPart = package.CreateEntry(Constants.DatabaseFilename, compression);
                        Debug.Assert(dataPart != null);

                        if (allocatedPages > 0)                 //only true if dataPager is still empty at backup start
                        {
                            using (var dataStream = dataPart.Open())
                            {
                                // now can copy everything else
                                var firstDataPage = dataPager.Read(0);

                                copier.ToStream(firstDataPage.Base, AbstractPager.PageSize * allocatedPages, dataStream);
                            }
                        }

                        try
                        {
                            foreach (var journalFile in usedJournals)
                            {
                                var journalPart = package.CreateEntry(StorageEnvironmentOptions.JournalName(journalFile.Number), compression);

                                Debug.Assert(journalPart != null);

                                var pagesToCopy = journalFile.JournalWriter.NumberOfAllocatedPages;
                                if (journalFile.Number == lastWrittenLogFile)
                                {
                                    pagesToCopy = lastWrittenLogPage + 1;
                                }

                                using (var stream = journalPart.Open())
                                {
                                    copier.ToStream(journalFile, 0, pagesToCopy, stream);
                                    infoNotify(string.Format("Voron copy journal file {0} ", journalFile));
                                }
                            }
                        }
                        finally
                        {
                            foreach (var journalFile in usedJournals)
                            {
                                journalFile.Release();
                            }
                        }
                    }
            }
            finally
            {
                if (txr != null)
                {
                    txr.Dispose();
                }
            }
            infoNotify(string.Format("Voron backup db finished"));
        }