Esempio n. 1
0
        /// <summary>
        /// Updates a message file's metadata.
        /// </summary>
        /// <param name="path">The file path.</param>
        /// <param name="msgInfo">The message metadata.</param>
        internal void WriteMessageMetadata(string path, QueuedMsgInfo msgInfo)
        {
            using (var fsMsg = new EnhancedFileStream(path, FileMode.Open, FileAccess.ReadWrite))
            {
                // Seek past the message's header and body.

                int    cbBody;
                byte[] md5Hash;

                fsMsg.Position = MsgHeaderSize;
                cbBody         = fsMsg.ReadInt32();
                fsMsg.Position = fsMsg.Position + cbBody;

                // Write the metadata and truncate the file.

                fsMsg.WriteString16(msgInfo.ToString());
                fsMsg.SetLength(fsMsg.Position);

                // Regenerate the MD5 Hash

                fsMsg.Position = MsgMD5Offset + MD5Hasher.DigestSize;
                md5Hash        = MD5Hasher.Compute(fsMsg, fsMsg.Length - fsMsg.Position);
                fsMsg.Position = MsgMD5Offset;
                fsMsg.WriteBytesNoLen(md5Hash);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Truncates the log to the position passed.
        /// </summary>
        /// <param name="position">The <see cref="ILogPosition" /> defining where the truncation should occur.</param>
        /// <remarks>
        /// <note>
        /// This property can only be called if the operation log is in <see cref="OperationLogMode.Undo" />
        /// mode.
        /// </note>
        /// <para>
        /// This method is used in combination with the <see cref="Position" /> property to roll
        /// back operations within a base transaction.
        /// </para>
        /// </remarks>
        /// <exception cref="TransactionException">Thrown if the log isn't open or if the mode isn't <see cref="OperationLogMode.Undo" />.</exception>
        public void Truncate(ILogPosition position)
        {
            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.SetLength(((FileLogPosition)position).Position);
            }
        }
Esempio n. 3
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);
            }
        }
Esempio n. 4
0
        public void MsgQueueFileStore_Corrupt_Messages()
        {
            MsgQueueFileStore store = null;
            int           count;
            QueuedMsgInfo msgInfo;
            QueuedMsg     msg;
            object        persistID;

            Guid[] ids;
            Dictionary <Guid, bool> corruptIDs;
            List <string>           corruptFiles;

            // Create a store with a bunch of messages then close
            // it and corrupt some of the message files.  Then attempt
            // to open the files and verify that the corrupted files
            // are detected.

            ClearFolders();

            try
            {
                store = new MsgQueueFileStore(root);
                store.Open();

                ids = new Guid[MessageCount];

                for (int i = 0; i < MessageCount; i++)
                {
                    msg          = new QueuedMsg();
                    msg.TargetEP = "logical://test/" + i.ToString();
                    msg.Body     = i;
                    msgInfo      = new QueuedMsgInfo(null, msg);

                    store.Add(msgInfo, msg);
                    ids[i] = msg.ID;
                }

                Assert.AreEqual(MessageCount, store.Count);
                count = 0;
                foreach (QueuedMsgInfo i in store)
                {
                    count++;
                }
                Assert.AreEqual(MessageCount, count);

                for (int i = 0; i < ids.Length; i++)
                {
                    Guid id = ids[i];

                    persistID = store.GetPersistID(id);
                    Assert.IsNotNull(persistID);

                    msgInfo = store.GetInfo(persistID);
                    Assert.IsNotNull(msgInfo);
                    Assert.AreEqual((MsgEP)("logical://test/" + i.ToString()), msgInfo.TargetEP);

                    msg = store.Get(persistID);
                    msg.DeserializedBody();
                    Assert.AreEqual(i, (int)msg.Body);
                }

                // Corrupt 1/2 of the message files in various ways.

                corruptIDs   = new Dictionary <Guid, bool>();
                corruptFiles = new List <string>();

                for (int i = 0; i < MessageCount / 2; i++)
                {
                    corruptIDs.Add(ids[i], true);
                    corruptFiles.Add(store.GetMessagePath(ids[i], false));
                }

                int cbTruncate = 0;

                for (int i = 0; i < corruptFiles.Count; i++)
                {
                    string file = corruptFiles[i];

                    using (EnhancedFileStream fs = new EnhancedFileStream(file, FileMode.Open, FileAccess.ReadWrite))
                    {
                        if ((i & 1) == 0)
                        {
                            // Truncate the file by at least one byte

                            int cb;

                            cb = Math.Min((int)fs.Length - 1, cbTruncate++);
                            fs.SetLength(cb);
                        }
                        else
                        {
                            // Mess with a byte at random position in the file.

                            int  pos = Helper.Rand((int)fs.Length);
                            byte b;

                            fs.Position = pos;
                            b           = (byte)fs.ReadByte();
                            fs.Position = pos;
                            fs.WriteByte((byte)(~b));
                        }
                    }
                }

                // Load all of the message files and verify that the corrupt files
                // are detected.

                for (int i = 0; i < ids.Length; i++)
                {
                    Guid id = ids[i];

                    if (corruptIDs.ContainsKey(id))
                    {
                        try
                        {
                            persistID = store.GetPersistID(id);
                            Assert.IsNotNull(persistID);

                            msgInfo = store.GetInfo(persistID);
                            Assert.IsNotNull(msgInfo);
                            Assert.AreEqual((MsgEP)("logical://test/" + i.ToString()), msgInfo.TargetEP);

                            msg = store.Get(persistID);
                            Assert.Fail("Exception expected");
                        }
                        catch
                        {
                            // Expecting an exception
                        }
                    }
                    else
                    {
                        persistID = store.GetPersistID(id);
                        Assert.IsNotNull(persistID);

                        msgInfo = store.GetInfo(persistID);
                        Assert.IsNotNull(msgInfo);
                        Assert.AreEqual((MsgEP)("logical://test/" + i.ToString()), msgInfo.TargetEP);

                        msg = store.Get(persistID);
                        msg.DeserializedBody();
                        Assert.AreEqual(i, (int)msg.Body);
                    }
                }
            }
            finally
            {
                if (store != null)
                {
                    store.Close();
                }
            }
        }