Exemple #1
0
        /// <summary>
        /// Writes a message file.
        /// </summary>
        /// <param name="path">The file path.</param>
        /// <param name="msg">The message.</param>
        /// <param name="msgInfo">The message metadata.</param>
        internal void WriteMessage(string path, QueuedMsg msg, QueuedMsgInfo msgInfo)
        {
            byte[] md5Hash;

            using (var fsMsg = new EnhancedFileStream(path, FileMode.Create, FileAccess.ReadWrite))
            {
                // Write the file header

                fsMsg.WriteInt32(MsgMagic);         // Magic Number
                fsMsg.WriteInt32(0);                // Format Version
                fsMsg.WriteInt32(0);                // Reserved
                fsMsg.WriteBytesNoLen(md5Zeros);    // MD5 hash placeholder

                // Write the message body.

                fsMsg.WriteBytes32(msg.BodyRaw);

                // Write the metadata.

                fsMsg.WriteString16(msgInfo.ToString());

                // Compute and save the MD5 hash

                fsMsg.Position = MsgHeaderSize;
                md5Hash        = MD5Hasher.Compute(fsMsg, fsMsg.Length - fsMsg.Position);
                fsMsg.Position = MsgMD5Offset;
                fsMsg.WriteBytesNoLen(md5Hash);
            }
        }
Exemple #2
0
        /// <summary>
        /// Writes an <see cref="IOperation" /> to the log.
        /// </summary>
        /// <param name="resource">The parent <see cref="ITransactedResource" /> responsible for serializing the operation.</param>
        /// <param name="operation">The operation to be written.</param>
        /// <remarks>
        /// <note>
        /// This property can only be called if the operation log is in <see cref="OperationLogMode.Undo" />
        /// mode.
        /// </note>
        /// </remarks>
        /// <exception cref="TransactionException">Thrown if the log isn't open or if the mode isn't <see cref="OperationLogMode.Undo" />.</exception>
        public void Write(ITransactedResource resource, IOperation operation)
        {
            long cbPos;
            long cb;

            using (TimedLock.Lock(this))
            {
                if (file == null)
                {
                    throw new TransactionException(ClosedMsg);
                }

                if (mode != OperationLogMode.Undo)
                {
                    throw new TransactionException("Write is available only when the log is in UNDO mode.");
                }

                file.WriteInt32(Magic);                     // Magic number
                cbPos = file.Position;                      // Length place holder
                file.WriteInt32(0);
                file.WriteString32(operation.Description);  // Description
                resource.WriteOperation(file, operation);   // Serialized operation

                cb = file.Position - cbPos - 4;
                if (cb < 0 || cb > int.MaxValue)
                {
                    throw new TransactionException("ITransactedResource.WriteOperation() returned with an unexpected stream position.");
                }

                file.Position = cbPos;
                file.WriteInt32((int)cb);
                file.Position = file.Length;
                file.Flush();
            }
        }
Exemple #3
0
        private OperationLogMode mode;                          // The current mode

        /// <summary>
        /// Opens or creates a file operation log file.
        /// </summary>
        /// <param name="path">The path to the log file.</param>
        /// <param name="transactionID">The log's transaction <see cref="Guid" />.</param>
        /// <remarks>
        /// New logs will be created in <see cref="OperationLogMode.Undo" /> mode.
        /// </remarks>
        public FileOperationLog(string path, Guid transactionID)
        {
            this.path = Path.GetFullPath(path);
            this.file = new EnhancedFileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);

            if (file.Length == 0)
            {
                // Initialize a new file

                file.WriteInt32(Magic);
                file.WriteInt32(0);
                file.WriteInt32(0);
                file.WriteInt32((int)OperationLogMode.Undo);
                file.WriteBytesNoLen(transactionID.ToByteArray());
                file.Flush();
            }
            else
            {
                // Open an existing file.

                try
                {
                    if (file.ReadInt32() != Magic || // Magic number
                        file.ReadInt32() != 0)
                    {                                // Format Versopn
                        throw new Exception();
                    }

                    file.ReadInt32();                   // Reserved

                    switch (file.ReadInt32())           // Mode
                    {
                    case (int)OperationLogMode.Undo:

                        mode = OperationLogMode.Undo;
                        break;

                    case (int)OperationLogMode.Redo:

                        mode = OperationLogMode.Redo;
                        break;

                    default:

                        throw new Exception();
                    }

                    this.transactionID = new Guid(file.ReadBytes(16));
                    if (transactionID != this.transactionID)
                    {
                        throw new Exception();
                    }
                }
                catch
                {
                    throw new TransactionException(CorruptMsg);
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Writes the index file.
        /// </summary>
        /// <param name="path">The target file path.</param>
        /// <param name="phrases">The phrases to be written.</param>
        private void SaveIndexTo(string path, Phrase[] phrases)
        {
            using (var fs = new EnhancedFileStream(path, FileMode.Create))
            {
                fs.WriteInt32(Magic);
                fs.WriteInt32(phrases.Length);

                foreach (var phrase in phrases)
                {
                    fs.WriteString16(phrase.PhraseType.ToString());
                    fs.WriteString32(phrase.Text);
                    fs.WriteString16(phrase.Voice);
                    fs.WriteString16(phrase.ActualVoice);
                    fs.WriteString16(phrase.Encoding.ToString());
                    fs.WriteString16(phrase.SampleRate.ToString());
                    fs.WriteInt64(phrase.LastAccessUtc.Ticks);
                    fs.WriteString16(phrase.Path);
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Loads the message index file.  If the index does not exist or is corrupt then
        /// the index will be rebuilt by performing a full scan of message files under
        /// the root folder.
        /// </summary>
        /// <param name="newIndexFile">Pass as <c>true</c> if a new index file is being created.</param>
        private void LoadIndex(bool newIndexFile)
        {
            string formatErr = string.Format("Invalid or missing index file [{0}].", indexPath);
            int    cMessages;

            string[] files;
            Dictionary <Guid, string> msgFiles;

            TimedLock.AssertLocked(this);
            messages = new Dictionary <Guid, QueuedMsgInfo>();

            // List the message files and build a hash table mapping each message GUID
            // the fully qualified path to the file.

            files    = Helper.GetFilesByPattern(root + "*.msg", SearchOption.AllDirectories);
            msgFiles = new Dictionary <Guid, string>(files.Length);

            foreach (string path in files)
            {
                string file = Path.GetFileName(path).ToLowerInvariant();
                Guid   msgID;

                if (!file.EndsWith(".msg"))
                {
                    continue;
                }

                try
                {
                    msgID = new Guid(file.Substring(0, file.Length - 4));
                }
                catch
                {
                    continue;
                }

                msgFiles[msgID] = path;
            }

            // Load the index file.

            try
            {
                // Parse the index file header

                fsIndex.Position = 0;
                if (fsIndex.ReadInt32() != IndexMagic)      // Magic Number
                {
                    throw new FormatException(formatErr);
                }

                if (fsIndex.ReadInt32() != 0)               // Format Version
                {
                    throw new FormatException(formatErr);
                }

                fsIndex.ReadInt32();                        // Reserved

                if (fsIndex.ReadInt32() != 0)
                {
                    throw new FormatException(string.Format("Index file [{0}] was not closed properly. Full message folder scan will be performed.", indexPath));
                }

                cMessages = fsIndex.ReadInt32();            // Message Count

                // Parse the message metadata

                for (int i = 0; i < cMessages; i++)
                {
                    QueuedMsgInfo msgInfo;

                    msgInfo              = new QueuedMsgInfo(fsIndex.ReadString16());
                    msgInfo.PersistID    = msgInfo.ID;
                    msgInfo.ProviderData = root + fsIndex.ReadString16();   // Make the paths absolute

                    messages[msgInfo.ID] = msgInfo;
                }

                // Perform an extra consistency check by listing all of the message files
                // under the root folder and comparing the message GUIDs encoded into the
                // file names with the GUIDs loaded from the index file and then bringing
                // the index into sync with the actual message files.

                bool updated = false;
                int  cLoaded = 0;

                // Delete any metadata for messages that exist in the index
                // but don't exist on the file system.

                var delList = new List <Guid>();

                foreach (Guid msgID in messages.Keys)
                {
                    if (!msgFiles.ContainsKey(msgID))
                    {
                        delList.Add(msgID);
                    }
                }

                foreach (Guid msgID in delList)
                {
                    messages.Remove(msgID);
                }

                if (delList.Count > 0)
                {
                    updated = true;
                    SysLog.LogWarning(string.Format("Message index [{0}] has message metadata for messages that do not exist. [{1}] messages will be removed from the index.", indexPath, delList.Count));
                }

                // Load metadata for messages that exist in the file system
                // but were not in the index.

                foreach (Guid msgID in msgFiles.Keys)
                {
                    if (!messages.ContainsKey(msgID))
                    {
                        string path = msgFiles[msgID];

                        try
                        {
                            messages[msgID] = ReadMessageMetadata(msgID, path);
                            cLoaded++;
                        }
                        catch (Exception e)
                        {
                            SysLog.LogException(e);
                        }
                    }
                }

                if (cLoaded > 0)
                {
                    updated = true;
                    SysLog.LogWarning(string.Format("Message index [{0}] is missing metadata for [{1}] messages. Missing entries will be added.", indexPath, cLoaded));
                }

                if (updated)
                {
                    SaveIndex(true);
                }

                // Mark the index as "open" for crash detection.

                fsIndex.Position = OpenFlagOffset;
                fsIndex.WriteInt32(1);
                fsIndex.Flush();
            }
            catch
            {
                if (newIndexFile)
                {
                    SysLog.LogWarning("Rebuilding missing message index file [{0}].", indexPath);
                }
                else
                {
                    SysLog.LogWarning("Rebuilding corrupt message index file [{0}].", indexPath);
                }

                // Clear the index file if there was a serious error and then
                // rebuild it from scratch by scanning the message metadata.

                fsIndex.SetLength(0);
                fsIndex.Flush();
                messages.Clear();

                foreach (Guid msgID in msgFiles.Keys)
                {
                    try
                    {
                        messages.Add(msgID, ReadMessageMetadata(msgID, msgFiles[msgID]));
                    }
                    catch (Exception e2)
                    {
                        SysLog.LogException(e2);
                    }
                }

                // Save the index, marking the file as "open" for crash detection

                SaveIndex(true);
            }
        }