Exemple #1
0
        /// <summary>
        /// Searches through the files in the archive for the control file, and reads it.
        /// We must read the control file first, in order to know which regions are available.
        /// </summary>
        /// <remarks>
        /// In most cases the control file *is* first, since that's how we create archives. However,
        /// it's possible that someone rewrote the archive externally so we can't rely on this fact.
        /// </remarks>
        /// <param name="archive"></param>
        /// <param name="dearchivedScenes"></param>
        private void FindAndLoadControlFile(out TarArchiveReader archive, out DearchiveScenesInfo dearchivedScenes)
        {
            archive = new TarArchiveReader(m_loadStream);
            dearchivedScenes = new DearchiveScenesInfo();

            string filePath;
            byte[] data;
            TarArchiveReader.TarEntryType entryType;
            bool firstFile = true;

            while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
            {
                if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
                    continue;
                    
                if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
                {
                    LoadControlFile(filePath, data, dearchivedScenes);

                    // Find which scenes are available in the simulator
                    ArchiveScenesGroup simulatorScenes = new ArchiveScenesGroup();
                    SceneManager.Instance.ForEachScene(delegate(Scene scene2)
                    {
                        simulatorScenes.AddScene(scene2);
                    });
                    simulatorScenes.CalcSceneLocations();
                    dearchivedScenes.SetSimulatorScenes(m_rootScene, simulatorScenes);

                    // If the control file wasn't the first file then reset the read pointer
                    if (!firstFile)
                    {
                        m_log.Warn("Control file wasn't the first file in the archive");
                        if (m_loadStream.CanSeek)
                        {
                            m_loadStream.Seek(0, SeekOrigin.Begin);
                        }
                        else if (m_loadPath != null)
                        {
                            archive.Close();
                            archive = null;
                            m_loadStream.Close();
                            m_loadStream = null;
                            m_loadStream = new GZipStream(ArchiveHelpers.GetStream(m_loadPath), CompressionMode.Decompress);
                            archive = new TarArchiveReader(m_loadStream);
                        }
                        else
                        {
                            // There isn't currently a scenario where this happens, but it's best to add a check just in case
                            throw new Exception("Error reading archive: control file wasn't the first file, and the input stream doesn't allow seeking");
                        }
                    }

                    return;
                }

                firstFile = false;
            }

            throw new Exception("Control file not found");
        }
        /// <summary>
        /// Writes the list of regions included in a multi-region OAR.
        /// </summary>
        private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw)
        {
            xtw.WriteStartElement("regions");

            // Write the regions in order: rows from South to North, then regions from West to East.
            // The list of regions can have "holes"; we write empty elements in their position.

            for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y)
            {
                SortedDictionary <uint, Scene> row;
                if (scenesGroup.Regions.TryGetValue(y, out row))
                {
                    xtw.WriteStartElement("row");

                    for (uint x = (uint)scenesGroup.Rect.Left; x < scenesGroup.Rect.Right; ++x)
                    {
                        Scene scene;
                        if (row.TryGetValue(x, out scene))
                        {
                            xtw.WriteStartElement("region");
                            xtw.WriteElementString("id", scene.RegionInfo.RegionID.ToString());
                            xtw.WriteElementString("dir", scenesGroup.GetRegionDir(scene.RegionInfo.RegionID));
                            WriteRegionInfo(scene, xtw);
                            xtw.WriteEndElement();
                        }
                        else
                        {
                            // Write a placeholder for a missing region
                            xtw.WriteElementString("region", "");
                        }
                    }

                    xtw.WriteEndElement();
                }
                else
                {
                    // Write a placeholder for a missing row
                    xtw.WriteElementString("row", "");
                }
            }

            xtw.WriteEndElement();  // "regions"
        }
Exemple #3
0
        /// <summary>
        /// Sets all the scenes present in the simulator.
        /// </summary>
        /// <remarks>
        /// This method matches regions in the archive to scenes in the simulator according to
        /// their relative position. We only load regions if there's an existing Scene in the
        /// grid location where the region should be loaded.
        /// </remarks>
        /// <param name="rootScene">The scene where the Load OAR operation was run</param>
        /// <param name="simulatorScenes">All the scenes in the simulator</param>
        public void SetSimulatorScenes(Scene rootScene, ArchiveScenesGroup simulatorScenes)
        {
            foreach (RegionInfo archivedRegion in m_directory2region.Values)
            {
                Point location = new Point((int)rootScene.RegionInfo.RegionLocX, (int)rootScene.RegionInfo.RegionLocY);
                location.Offset(archivedRegion.Location);

                Scene scene;
                if (simulatorScenes.TryGetScene(location, out scene))
                {
                    archivedRegion.Scene = scene;
                    m_newId2region[scene.RegionInfo.RegionID] = archivedRegion;
                }
                else
                {
                    m_log.WarnFormat("[ARCHIVER]: Not loading archived region {0} because there's no existing region at location {1},{2}",
                                     archivedRegion.Directory, location.X, location.Y);
                }
            }
        }
        /// <summary>
        /// Create the control file.
        /// </summary>
        /// <returns></returns>
        public string CreateControlFile(ArchiveScenesGroup scenesGroup)
        {
            int majorVersion;
            int minorVersion;

            if (MultiRegionFormat)
            {
                majorVersion = MAX_MAJOR_VERSION;
                minorVersion = 0;
            }
            else
            {
                // To support older versions of OpenSim, we continue to create single-region OARs
                // using the old file format. In the future this format will be discontinued.
                majorVersion = 0;
                minorVersion = 8;
            }
//
//            if (m_options.ContainsKey("version"))
//            {
//                string[] parts = m_options["version"].ToString().Split('.');
//                if (parts.Length >= 1)
//                {
//                    majorVersion = Int32.Parse(parts[0]);
//
//                    if (parts.Length >= 2)
//                        minorVersion = Int32.Parse(parts[1]);
//                }
//            }
//
//            if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
//            {
//                throw new Exception(
//                    string.Format(
//                        "OAR version number for save must be between {0} and {1}",
//                        MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
//            }
//            else if (majorVersion == MAX_MAJOR_VERSION)
//            {
//                // Force 1.0
//                minorVersion = 0;
//            }
//            else if (majorVersion == MIN_MAJOR_VERSION)
//            {
//                // Force 0.4
//                minorVersion = 4;
//            }

            m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
            if (majorVersion == 1)
            {
                m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim versions prior to 0.7.4. Do not use the --all option if you want to produce a compatible OAR");
            }

            String s;

            using (StringWriter sw = new StringWriter())
            {
                using (XmlTextWriter xtw = new XmlTextWriter(sw))
                {
                    xtw.Formatting = Formatting.Indented;
                    xtw.WriteStartDocument();
                    xtw.WriteStartElement("archive");
                    xtw.WriteAttributeString("major_version", majorVersion.ToString());
                    xtw.WriteAttributeString("minor_version", minorVersion.ToString());

                    xtw.WriteStartElement("creation_info");
                    DateTime now = DateTime.UtcNow;
                    TimeSpan t   = now - new DateTime(1970, 1, 1);
                    xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
                    if (!MultiRegionFormat)
                    {
                        xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString());
                    }
                    xtw.WriteEndElement();

                    xtw.WriteElementString("assets_included", SaveAssets.ToString());

                    if (MultiRegionFormat)
                    {
                        WriteRegionsManifest(scenesGroup, xtw);
                    }
                    else
                    {
                        xtw.WriteStartElement("region_info");
                        WriteRegionInfo(m_rootScene, xtw);
                        xtw.WriteEndElement();
                    }

                    xtw.WriteEndElement();

                    xtw.Flush();
                }

                s = sw.ToString();
            }

            return(s);
        }
        /// <summary>
        /// Archive the region requested.
        /// </summary>
        /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
        public void ArchiveRegion(Dictionary <string, object> options)
        {
            m_options = options;

            if (options.ContainsKey("all") && (bool)options["all"])
            {
                MultiRegionFormat = true;
            }

            if (options.ContainsKey("noassets") && (bool)options["noassets"])
            {
                SaveAssets = false;
            }

            if (options.TryGetValue("checkPermissions", out Object temp))
            {
                FilterContent = (string)temp;
            }


            // Find the regions to archive
            ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();

            if (MultiRegionFormat)
            {
                m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
                SceneManager.Instance.ForEachScene(delegate(Scene scene)
                {
                    scenesGroup.AddScene(scene);
                });
            }
            else
            {
                scenesGroup.AddScene(m_rootScene);
            }
            scenesGroup.CalcSceneLocations();

            m_archiveWriter = new TarArchiveWriter(m_saveStream);

            try
            {
                // Write out control file. It should be first so that it will be found ASAP when loading the file.
                m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
                m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");

                // Archive the regions

                Dictionary <UUID, sbyte> assetUuids           = new Dictionary <UUID, sbyte>();
                HashSet <UUID>           failedIDs            = new HashSet <UUID>();
                HashSet <UUID>           uncertainAssetsUUIDs = new HashSet <UUID>();

                scenesGroup.ForEachScene(delegate(Scene scene)
                {
                    string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
                    ArchiveOneRegion(scene, regionDir, assetUuids, failedIDs, uncertainAssetsUUIDs);
                });

                // Archive the assets

                if (SaveAssets)
                {
                    m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);

                    AssetsRequest ar = new AssetsRequest(
                        new AssetsArchiver(m_archiveWriter), assetUuids,
                        failedIDs.Count,
                        m_rootScene.AssetService, m_rootScene.UserAccountService,
                        m_rootScene.RegionInfo.ScopeID, options, null);
                    ar.Execute();
                    assetUuids = null;
                }
                else
                {
                    m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
//                    CloseArchive(string.Empty);
                }
                CloseArchive(string.Empty);
            }
            catch (Exception e)
            {
                CloseArchive(e.Message);
                throw;
            }

            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.Default;
        }
        /// <summary>
        /// Writes the list of regions included in a multi-region OAR.
        /// </summary>
        private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw)
        {
            xtw.WriteStartElement("regions");

            // Write the regions in order: rows from South to North, then regions from West to East.
            // The list of regions can have "holes"; we write empty elements in their position.

            for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y)
            {
                SortedDictionary<uint, Scene> row;
                if (scenesGroup.Regions.TryGetValue(y, out row))
                {
                    xtw.WriteStartElement("row");

                    for (uint x = (uint)scenesGroup.Rect.Left; x < scenesGroup.Rect.Right; ++x)
                    {
                        Scene scene;
                        if (row.TryGetValue(x, out scene))
                        {
                            xtw.WriteStartElement("region");
                            xtw.WriteElementString("id", scene.RegionInfo.RegionID.ToString());
                            xtw.WriteElementString("dir", scenesGroup.GetRegionDir(scene.RegionInfo.RegionID));
                            WriteRegionInfo(scene, xtw);
                            xtw.WriteEndElement();
                        }
                        else
                        {
                            // Write a placeholder for a missing region
                            xtw.WriteElementString("region", "");
                        }
                    }

                    xtw.WriteEndElement();
                }
                else
                {
                    // Write a placeholder for a missing row
                    xtw.WriteElementString("row", "");
                }
            }

            xtw.WriteEndElement();  // "regions"
        }
        /// <summary>
        /// Create the control file.
        /// </summary>
        /// <returns></returns>
        public string CreateControlFile(ArchiveScenesGroup scenesGroup)
        {
            int majorVersion;
            int minorVersion;

            if (MultiRegionFormat)
            {
                majorVersion = MAX_MAJOR_VERSION;
                minorVersion = 0;
            }
            else
            {
                // To support older versions of OpenSim, we continue to create single-region OARs
                // using the old file format. In the future this format will be discontinued.
                majorVersion = 0;
                minorVersion = 8;
            }
//
//            if (m_options.ContainsKey("version"))
//            {
//                string[] parts = m_options["version"].ToString().Split('.');
//                if (parts.Length >= 1)
//                {
//                    majorVersion = Int32.Parse(parts[0]);                    
//                                        
//                    if (parts.Length >= 2)
//                        minorVersion = Int32.Parse(parts[1]);
//                }
//            }
//            
//            if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
//            {
//                throw new Exception(
//                    string.Format(
//                        "OAR version number for save must be between {0} and {1}", 
//                        MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
//            }
//            else if (majorVersion == MAX_MAJOR_VERSION)
//            {
//                // Force 1.0
//                minorVersion = 0;
//            }
//            else if (majorVersion == MIN_MAJOR_VERSION)
//            {
//                // Force 0.4
//                minorVersion = 4;                                        
//            }
            
            m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
            if (majorVersion == 1)
            {
                m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim versions prior to 0.7.4. Do not use the --all option if you want to produce a compatible OAR");
            }

            String s;
            
            using (StringWriter sw = new StringWriter())
            {
                using (XmlTextWriter xtw = new XmlTextWriter(sw))
                {
                    xtw.Formatting = Formatting.Indented;
                    xtw.WriteStartDocument();
                    xtw.WriteStartElement("archive");
                    xtw.WriteAttributeString("major_version", majorVersion.ToString());
                    xtw.WriteAttributeString("minor_version", minorVersion.ToString());
        
                    xtw.WriteStartElement("creation_info");
                    DateTime now = DateTime.UtcNow;
                    TimeSpan t = now - new DateTime(1970, 1, 1);
                    xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
                    if (!MultiRegionFormat)
                        xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString());
                    xtw.WriteEndElement();
        
                    xtw.WriteElementString("assets_included", SaveAssets.ToString());

                    if (MultiRegionFormat)
                    {
                        WriteRegionsManifest(scenesGroup, xtw);
                    }
                    else
                    {
                        xtw.WriteStartElement("region_info");
                        WriteRegionInfo(m_rootScene, xtw);
                        xtw.WriteEndElement();
                    }

                    xtw.WriteEndElement();
        
                    xtw.Flush();
                }

                s = sw.ToString();
            }

            return s;
        }
        /// <summary>
        /// Archive the region requested.
        /// </summary>
        /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
        public void ArchiveRegion(Dictionary<string, object> options)
        {
            m_options = options;

            if (options.ContainsKey("all") && (bool)options["all"])
                MultiRegionFormat = true;

            if (options.ContainsKey("noassets") && (bool)options["noassets"])
                SaveAssets = false;

            Object temp;
            if (options.TryGetValue("checkPermissions", out temp))
                FilterContent = (string)temp;


            // Find the regions to archive
            ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
            if (MultiRegionFormat)
            {
                m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
                SceneManager.Instance.ForEachScene(delegate(Scene scene)
                {
                    scenesGroup.AddScene(scene);
                });
            }
            else
            {
                scenesGroup.AddScene(m_rootScene);
            }
            scenesGroup.CalcSceneLocations();

            m_archiveWriter = new TarArchiveWriter(m_saveStream);

            try
            {
                // Write out control file. It should be first so that it will be found ASAP when loading the file.
                m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
                m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");

                // Archive the regions

                Dictionary<UUID, sbyte> assetUuids = new Dictionary<UUID, sbyte>();

                scenesGroup.ForEachScene(delegate(Scene scene)
                {
                    string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
                    ArchiveOneRegion(scene, regionDir, assetUuids);
                });

                // Archive the assets

                if (SaveAssets)
                {
                    m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);

                    // Asynchronously request all the assets required to perform this archive operation
                    AssetsRequest ar
                        = new AssetsRequest(
                            new AssetsArchiver(m_archiveWriter), assetUuids,
                            m_rootScene.AssetService, m_rootScene.UserAccountService,
                            m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets);

                    WorkManager.RunInThread(o => ar.Execute(), null, "Archive Assets Request");

                    // CloseArchive() will be called from ReceivedAllAssets()
                }
                else
                {
                    m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
                    CloseArchive(string.Empty);
                }
            }
            catch (Exception e)
            {
                CloseArchive(e.Message);
                throw;
            }
        }
        /// <summary>
        /// Archive the region requested.
        /// </summary>
        /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
        public void ArchiveRegion(Dictionary <string, object> options)
        {
            m_options = options;

            if (options.ContainsKey("all") && (bool)options["all"])
            {
                MultiRegionFormat = true;
            }

            if (options.ContainsKey("noassets") && (bool)options["noassets"])
            {
                SaveAssets = false;
            }

            Object temp;

            if (options.TryGetValue("checkPermissions", out temp))
            {
                CheckPermissions = (string)temp;
            }


            // Find the regions to archive
            ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();

            if (MultiRegionFormat)
            {
                m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
                SceneManager.Instance.ForEachScene(delegate(Scene scene)
                {
                    scenesGroup.AddScene(scene);
                });
            }
            else
            {
                scenesGroup.AddScene(m_rootScene);
            }
            scenesGroup.CalcSceneLocations();

            m_archiveWriter = new TarArchiveWriter(m_saveStream);

            try
            {
                // Write out control file. It should be first so that it will be found ASAP when loading the file.
                m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
                m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");

                // Archive the regions

                Dictionary <UUID, AssetType> assetUuids = new Dictionary <UUID, AssetType>();

                scenesGroup.ForEachScene(delegate(Scene scene)
                {
                    string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
                    ArchiveOneRegion(scene, regionDir, assetUuids);
                });

                // Archive the assets

                if (SaveAssets)
                {
                    m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);

                    // Asynchronously request all the assets required to perform this archive operation
                    AssetsRequest ar
                        = new AssetsRequest(
                              new AssetsArchiver(m_archiveWriter), assetUuids,
                              m_rootScene.AssetService, m_rootScene.UserAccountService,
                              m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets);

                    Util.FireAndForget(o => ar.Execute());

                    // CloseArchive() will be called from ReceivedAllAssets()
                }
                else
                {
                    m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
                    CloseArchive(string.Empty);
                }
            }
            catch (Exception e)
            {
                CloseArchive(e.Message);
                throw;
            }
        }
        /// <summary>
        /// Sets all the scenes present in the simulator.
        /// </summary>
        /// <remarks>
        /// This method matches regions in the archive to scenes in the simulator according to
        /// their relative position. We only load regions if there's an existing Scene in the
        /// grid location where the region should be loaded.
        /// </remarks>
        /// <param name="rootScene">The scene where the Load OAR operation was run</param>
        /// <param name="simulatorScenes">All the scenes in the simulator</param>
        public void SetSimulatorScenes(Scene rootScene, ArchiveScenesGroup simulatorScenes)
        {
            foreach (RegionInfo archivedRegion in m_directory2region.Values)
            {
                Point location = new Point((int)rootScene.RegionInfo.RegionLocX, (int)rootScene.RegionInfo.RegionLocY);
                location.Offset(archivedRegion.Location);

                Scene scene;
                if (simulatorScenes.TryGetScene(location, out scene))
                {
                    archivedRegion.Scene = scene;
                    m_newId2region[scene.RegionInfo.RegionID] = archivedRegion;
                }
                else
                {
                    m_log.WarnFormat("[ARCHIVER]: Not loading archived region {0} because there's no existing region at location {1},{2}",
                        archivedRegion.Directory, location.X, location.Y);
                }
            }
        }
Exemple #11
0
        /// <summary>
        /// Searches through the files in the archive for the control file, and reads it.
        /// We must read the control file first, in order to know which regions are available.
        /// </summary>
        /// <remarks>
        /// In most cases the control file *is* first, since that's how we create archives. However,
        /// it's possible that someone rewrote the archive externally so we can't rely on this fact.
        /// </remarks>
        /// <param name="archive"></param>
        /// <param name="dearchivedScenes"></param>
        private void FindAndLoadControlFile(out TarArchiveReader archive, out DearchiveScenesInfo dearchivedScenes)
        {
            archive = new TarArchiveReader(m_loadStream);
            dearchivedScenes = new DearchiveScenesInfo();

            string filePath;
            byte[] data;
            TarArchiveReader.TarEntryType entryType;
            bool firstFile = true;

            while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
            {
                if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
                    continue;
                    
                if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
                {
                    LoadControlFile(filePath, data, dearchivedScenes);

                    // Find which scenes are available in the simulator
                    ArchiveScenesGroup simulatorScenes = new ArchiveScenesGroup();
                    SceneManager.Instance.ForEachScene(delegate(Scene scene2)
                    {
                        simulatorScenes.AddScene(scene2);
                    });
                    simulatorScenes.CalcSceneLocations();
                    dearchivedScenes.SetSimulatorScenes(m_rootScene, simulatorScenes);

                    // If the control file wasn't the first file then reset the read pointer
                    if (!firstFile)
                    {
                        m_log.Warn("Control file wasn't the first file in the archive");
                        if (m_loadStream.CanSeek)
                        {
                            m_loadStream.Seek(0, SeekOrigin.Begin);
                        }
                        else if (m_loadPath != null)
                        {
                            archive.Close();
                            archive = null;
                            m_loadStream.Close();
                            m_loadStream = null;
                            m_loadStream = new GZipStream(ArchiveHelpers.GetStream(m_loadPath), CompressionMode.Decompress);
                            archive = new TarArchiveReader(m_loadStream);
                        }
                        else
                        {
                            // There isn't currently a scenario where this happens, but it's best to add a check just in case
                            throw new Exception("Error reading archive: control file wasn't the first file, and the input stream doesn't allow seeking");
                        }
                    }

                    return;
                }

                firstFile = false;
            }

            throw new Exception("Control file not found");
        }