// Функция резервирует базу данных по указанному пути. Таблицы должны распологаться в таком порядке, что бы первыми шли таблицы которые не ссылаются на другие. public bool Write(string path, Log log) { var flag = true; FileStream stream_meta = null; FileStream stream_data = null; // Стартовая позиция метаданных таблиц var meta_start = 0l; // Размер метаданных var meta_size = 0; try { var dt = DateTime.Now; log.Append("Backup begin at "); log.Append(dt.ToString("F")); log.Append("\r\n"); stream_meta = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); stream_data = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); var sm = new BinaryWriterCRC32(stream_meta); var sd = new BinaryWriter(stream_data); var counts = new int[DB.Tables.Count]; var version = Assembly.GetCallingAssembly().GetName().Version; // Запись типа бекапа sm.Write(1); // Запись времени бекапа sm.Write(dt.Ticks); // Запись версии базы sm.Write(DB.Version); // Запись версии кода sm.Write(version.Major); sm.Write(version.Minor); sm.Write(version.Build); sm.Write(version.Revision); // Запись количества таблиц sm.Write(DB.Tables.Count); // Оставляем шапку 1024 байта stream_meta.Position = 1024; // crc шапки sm.WriteAndResetCRC(); // Записываем структуру таблиц for (int i = 0; i < DB.Tables.Count; ++i) { var itm = DB.Tables[i]; // Количество sm.Write(itm.File.Count); // Позиция в файле sm.Write(meta_size); // Название таблицы sm.Write(itm.Key); // Версия таблицы sm.Write(itm.CurrentVersion.Version); // Размер таблицы sm.Write(itm.CurrentVersion.RecordSize); // Режим записи sm.Write((int)itm.CurrentVersion.Mode); // Массивы произвольной длинны sm.Write(itm.CurrentVersion.HasStorage); // Количество полей sm.Write(itm.CurrentVersion.Structure.Length); // Поля for (var j = 0; j < itm.CurrentVersion.Structure.Length; ++j) { var fld = itm.CurrentVersion.Structure[j]; sm.Write(fld.IsStorage); sm.Write(fld.Name); sm.Write(fld.Size); sm.Write(fld.Offset); } // Размер мета meta_size += itm.File.Count * 8 + 4; // CRC записи sm.WriteAndResetCRC(); counts[i] = itm.File.Count; } // Инициализируем место для метаданых meta_start = stream_meta.Position; stream_data.Position = meta_start + meta_size; // Записываем даные for (int i = 0; i < DB.Tables.Count; ++i) { var itm = DB.Tables[i]; flag &= WriteTable(itm, sm, sd, counts[i], log); // CRC метаданных таблицы sm.WriteAndResetCRC(); } } catch (Exception e) { flag = false; log.Append(e); } finally { //var wtf = stream_data.Position; //stream_data.Position = Backup.TestPosition; //var br = new BinaryReader(stream_data); //var ppc = br.ReadInt32(); //stream_data.Position = wtf; //Debug.WriteLine("Final Control: " + ppc); if (stream_meta != null) { if (stream_meta.Position != meta_start + meta_size) { flag = false; log.Append("Stream metada is oversized."); } stream_meta.Close(); } if (stream_data != null) { stream_data.Close(); } } log.Append("\r\n"); if (flag) { log.Append("Backup DONE"); } else { log.Append("Backup FAIL"); } return(flag); }
// Функция копирует данные таблицы в бекап bool WriteTable(ITable itm, BinaryWriterCRC32 sm, BinaryWriter sd, int count, Log log) { log.Append("Begin read table: "); log.Append(itm.Key); log.Append(", count: "); log.Append(count); log.Append("\r\n"); var crc = new CRC32(); var crcerr = 0; var crcused = (itm.CurrentVersion.Mode & PageFileIOMode.CRC32) == PageFileIOMode.CRC32; var fs = itm.File.CreateStream(); var size = itm.CurrentVersion.RecordSize; var buf = new byte[size]; var map = itm.File.GetDeletedMap(); var ver = itm.CurrentVersion; var delta = size_time; var flag = true; if (crcused) { delta = size_time_crc; } fs.Position = PageFile.HeaderSize + size; for (int i = 0; i < count; ++i) { // Записываем позицию метаданных sm.Write(sd.BaseStream.Position); //Debug.WriteLine("Backup " + itm.Key + ", code: " + (i + 1) + ", offset:" + sd.BaseStream.Position); fs.Read(buf, 0, size); if (i + 1 == 1350685) { var bp = 0; } var time = 0l; var fact = 0l; // Читаем время и crc fixed(byte *ptr = &buf[size - delta]) { time = *(long *)ptr; if (crcused) { fact = *(int *)(ptr + size_time); } } // Пишем время if (map.Contains(i + 1)) { if (time == 0) { time = DateTime.Now.Ticks; } // если запись удалена sd.Write(-time); crc.Reset(); continue; } else { // если запись нормальная sd.Write(time); } // считаем crc данных и времени crc.Update(buf, 0, size - delta); crc.Update(time); // Быстрая проверка без данных переменной длинны if (fact != crc.Value) { flag = false; crcerr++; if (crcerr < 10) { log.Append("Warning reading: The object with code: ", i + 1, " crc32 check failed.\r\n"); } } crc.Reset(); crc.Update(time); // Массивы произвольной длинны if (ver.HasStorage) { for (var j = 0; j < ver.Structure.Length; ++j) { var fld = ver.Structure[j]; if (fld.IsStorage) { //Debug.WriteLine("Backup " + itm.Key + ", code: " + (i + 1) + ", field: " + fld.Name + " pos: " + sd.BaseStream.Position); fixed(byte *ptr = &buf[fld.Offset]) { var tmp = fld.Storage.ReadBytes(*((long *)ptr)); sd.Write(tmp.Length); sd.BaseStream.Write(tmp, 0, tmp.Length); crc.Update(tmp.Length); crc.Update(tmp, 0, tmp.Length); } } else { switch (fld.Size) { case 1: { sd.Write(buf[fld.Offset]); crc.Update(buf[fld.Offset]); break; } case 2: { fixed(byte *ptr = &buf[fld.Offset]) { var tmp = (char *)ptr; sd.Write(*tmp); crc.Update(*tmp); } break; } case 4: { fixed(byte *ptr = &buf[fld.Offset]) { var tmp = (uint *)ptr; sd.Write(*tmp); crc.Update(*tmp); } break; } case 8: { fixed(byte *ptr = &buf[fld.Offset]) { var tmp = (ulong *)ptr; sd.Write(*tmp); crc.Update(*tmp); } break; } case 16: { fixed(byte *ptr = &buf[fld.Offset]) { var tmp = (decimal *)ptr; sd.Write(*tmp); crc.Update(*tmp); } break; } } } } // Пишем CRC sd.Write(crc.Value); } else { // crc данных crc.Update(buf, 0, size - delta); // Пишем данные sd.BaseStream.Write(buf, 0, size - delta); // Пишем CRC sd.Write(crc.Value); } crc.Reset(); if (i > 0 && i % 100000 == 0) { log.Append("Progresss : ", i, "\r\n"); } } fs.Close(); return(flag); }