ArchiveWriteRequestPreparation.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.IO;
  30. using System.IO.Compression;
  31. using System.Reflection;
  32. using System.Text.RegularExpressions;
  33. using System.Threading;
  34. using System.Xml;
  35. using log4net;
  36. using OpenMetaverse;
  37. using OpenSim.Framework;
  38. using OpenSim.Framework.Serialization;
  39. using OpenSim.Region.CoreModules.World.Terrain;
  40. using OpenSim.Region.Framework.Interfaces;
  41. using OpenSim.Region.Framework.Scenes;
  42. using Ionic.Zlib;
  43. using GZipStream = Ionic.Zlib.GZipStream;
  44. using CompressionMode = Ionic.Zlib.CompressionMode;
  45. namespace OpenSim.Region.CoreModules.World.Archiver
  46. {
  47. /// <summary>
  48. /// Prepare to write out an archive.
  49. /// </summary>
  50. public class ArchiveWriteRequestPreparation
  51. {
  52. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  53. /// <summary>
  54. /// The minimum major version of OAR that we can write.
  55. /// </summary>
  56. public static int MIN_MAJOR_VERSION = 0;
  57. /// <summary>
  58. /// The maximum major version of OAR that we can write.
  59. /// </summary>
  60. public static int MAX_MAJOR_VERSION = 0;
  61. /// <summary>
  62. /// Determine whether this archive will save assets. Default is true.
  63. /// </summary>
  64. public bool SaveAssets { get; set; }
  65. protected ArchiverModule m_module;
  66. protected Scene m_scene;
  67. protected Stream m_saveStream;
  68. protected Guid m_requestId;
  69. /// <summary>
  70. /// Constructor
  71. /// </summary>
  72. /// <param name="module">Calling module</param>
  73. /// <param name="savePath">The path to which to save data.</param>
  74. /// <param name="requestId">The id associated with this request</param>
  75. /// <exception cref="System.IO.IOException">
  76. /// If there was a problem opening a stream for the file specified by the savePath
  77. /// </exception>
  78. public ArchiveWriteRequestPreparation(ArchiverModule module, string savePath, Guid requestId) : this(module, requestId)
  79. {
  80. try
  81. {
  82. m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
  83. }
  84. catch (EntryPointNotFoundException e)
  85. {
  86. m_log.ErrorFormat(
  87. "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
  88. + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
  89. m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
  90. }
  91. }
  92. /// <summary>
  93. /// Constructor.
  94. /// </summary>
  95. /// <param name="module">Calling module</param>
  96. /// <param name="saveStream">The stream to which to save data.</param>
  97. /// <param name="requestId">The id associated with this request</param>
  98. public ArchiveWriteRequestPreparation(ArchiverModule module, Stream saveStream, Guid requestId) : this(module, requestId)
  99. {
  100. m_saveStream = saveStream;
  101. }
  102. protected ArchiveWriteRequestPreparation(ArchiverModule module, Guid requestId)
  103. {
  104. m_module = module;
  105. // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
  106. // this.
  107. if (m_module != null)
  108. m_scene = m_module.Scene;
  109. m_requestId = requestId;
  110. SaveAssets = true;
  111. }
  112. /// <summary>
  113. /// Archive the region requested.
  114. /// </summary>
  115. /// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
  116. public void ArchiveRegion(Dictionary<string, object> options)
  117. {
  118. if (options.ContainsKey("noassets") && (bool)options["noassets"])
  119. SaveAssets = false;
  120. try
  121. {
  122. Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
  123. EntityBase[] entities = m_scene.GetEntities();
  124. List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
  125. string checkPermissions = null;
  126. int numObjectsSkippedPermissions = 0;
  127. Object temp;
  128. if (options.TryGetValue("checkPermissions", out temp))
  129. checkPermissions = (string)temp;
  130. // Filter entities so that we only have scene objects.
  131. // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
  132. // end up having to do this
  133. foreach (EntityBase entity in entities)
  134. {
  135. if (entity is SceneObjectGroup)
  136. {
  137. SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
  138. if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
  139. {
  140. if (!CanUserArchiveObject(m_scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, checkPermissions))
  141. {
  142. // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
  143. ++numObjectsSkippedPermissions;
  144. }
  145. else
  146. {
  147. sceneObjects.Add(sceneObject);
  148. }
  149. }
  150. }
  151. }
  152. if (SaveAssets)
  153. {
  154. UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService);
  155. foreach (SceneObjectGroup sceneObject in sceneObjects)
  156. {
  157. assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
  158. }
  159. m_log.DebugFormat(
  160. "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
  161. sceneObjects.Count, assetUuids.Count);
  162. }
  163. else
  164. {
  165. m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
  166. }
  167. if (numObjectsSkippedPermissions > 0)
  168. {
  169. m_log.DebugFormat(
  170. "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
  171. numObjectsSkippedPermissions);
  172. }
  173. // Make sure that we also request terrain texture assets
  174. RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings;
  175. if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
  176. assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
  177. if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
  178. assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
  179. if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
  180. assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
  181. if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
  182. assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
  183. TarArchiveWriter archiveWriter = new TarArchiveWriter(m_saveStream);
  184. // Asynchronously request all the assets required to perform this archive operation
  185. ArchiveWriteRequestExecution awre
  186. = new ArchiveWriteRequestExecution(
  187. sceneObjects,
  188. m_scene.RequestModuleInterface<ITerrainModule>(),
  189. m_scene.RequestModuleInterface<IRegionSerialiserModule>(),
  190. m_scene,
  191. archiveWriter,
  192. m_requestId,
  193. options);
  194. m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time.");
  195. // Write out control file. This has to be done first so that subsequent loaders will see this file first
  196. // XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this
  197. archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options));
  198. m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
  199. if (SaveAssets)
  200. {
  201. AssetsRequest ar
  202. = new AssetsRequest(
  203. new AssetsArchiver(archiveWriter), assetUuids,
  204. m_scene.AssetService, m_scene.UserAccountService,
  205. m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets);
  206. Util.FireAndForget(o => ar.Execute());
  207. }
  208. else
  209. {
  210. awre.ReceivedAllAssets(new List<UUID>(), new List<UUID>());
  211. }
  212. }
  213. catch (Exception)
  214. {
  215. m_saveStream.Close();
  216. throw;
  217. }
  218. }
  219. /// <summary>
  220. /// Checks whether the user has permission to export an object group to an OAR.
  221. /// </summary>
  222. /// <param name="user">The user</param>
  223. /// <param name="objGroup">The object group</param>
  224. /// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
  225. /// <returns>Whether the user is allowed to export the object to an OAR</returns>
  226. private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
  227. {
  228. if (checkPermissions == null)
  229. return true;
  230. IPermissionsModule module = m_scene.RequestModuleInterface<IPermissionsModule>();
  231. if (module == null)
  232. return true; // this shouldn't happen
  233. // Check whether the user is permitted to export all of the parts in the SOG. If any
  234. // part can't be exported then the entire SOG can't be exported.
  235. bool permitted = true;
  236. //int primNumber = 1;
  237. foreach (SceneObjectPart obj in objGroup.Parts)
  238. {
  239. uint perm;
  240. PermissionClass permissionClass = module.GetPermissionClass(user, obj);
  241. switch (permissionClass)
  242. {
  243. case PermissionClass.Owner:
  244. perm = obj.BaseMask;
  245. break;
  246. case PermissionClass.Group:
  247. perm = obj.GroupMask | obj.EveryoneMask;
  248. break;
  249. case PermissionClass.Everyone:
  250. default:
  251. perm = obj.EveryoneMask;
  252. break;
  253. }
  254. bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
  255. bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
  256. // Special case: if Everyone can copy the object then this implies it can also be
  257. // Transferred.
  258. // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
  259. // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
  260. // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
  261. if (permissionClass != PermissionClass.Owner)
  262. canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
  263. bool partPermitted = true;
  264. if (checkPermissions.Contains("C") && !canCopy)
  265. partPermitted = false;
  266. if (checkPermissions.Contains("T") && !canTransfer)
  267. partPermitted = false;
  268. // If the user is the Creator of the object then it can always be included in the OAR
  269. bool creator = (obj.CreatorID.Guid == user.Guid);
  270. if (creator)
  271. partPermitted = true;
  272. //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
  273. //m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
  274. // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
  275. // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
  276. if (!partPermitted)
  277. {
  278. permitted = false;
  279. break;
  280. }
  281. //++primNumber;
  282. }
  283. return permitted;
  284. }
  285. /// <summary>
  286. /// Create the control file for the most up to date archive
  287. /// </summary>
  288. /// <returns></returns>
  289. public string CreateControlFile(Dictionary<string, object> options)
  290. {
  291. int majorVersion = MAX_MAJOR_VERSION, minorVersion = 8;
  292. //
  293. // if (options.ContainsKey("version"))
  294. // {
  295. // string[] parts = options["version"].ToString().Split('.');
  296. // if (parts.Length >= 1)
  297. // {
  298. // majorVersion = Int32.Parse(parts[0]);
  299. //
  300. // if (parts.Length >= 2)
  301. // minorVersion = Int32.Parse(parts[1]);
  302. // }
  303. // }
  304. //
  305. // if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
  306. // {
  307. // throw new Exception(
  308. // string.Format(
  309. // "OAR version number for save must be between {0} and {1}",
  310. // MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
  311. // }
  312. // else if (majorVersion == MAX_MAJOR_VERSION)
  313. // {
  314. // // Force 1.0
  315. // minorVersion = 0;
  316. // }
  317. // else if (majorVersion == MIN_MAJOR_VERSION)
  318. // {
  319. // // Force 0.4
  320. // minorVersion = 4;
  321. // }
  322. m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
  323. //if (majorVersion == 1)
  324. //{
  325. // m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim 0.7.0.2 and earlier. Please use the --version=0 option if you want to produce a compatible OAR");
  326. //}
  327. String s;
  328. using (StringWriter sw = new StringWriter())
  329. {
  330. using (XmlTextWriter xtw = new XmlTextWriter(sw))
  331. {
  332. xtw.Formatting = Formatting.Indented;
  333. xtw.WriteStartDocument();
  334. xtw.WriteStartElement("archive");
  335. xtw.WriteAttributeString("major_version", majorVersion.ToString());
  336. xtw.WriteAttributeString("minor_version", minorVersion.ToString());
  337. xtw.WriteStartElement("creation_info");
  338. DateTime now = DateTime.UtcNow;
  339. TimeSpan t = now - new DateTime(1970, 1, 1);
  340. xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
  341. xtw.WriteElementString("id", UUID.Random().ToString());
  342. xtw.WriteEndElement();
  343. xtw.WriteStartElement("region_info");
  344. bool isMegaregion;
  345. Vector2 size;
  346. IRegionCombinerModule rcMod = null;
  347. // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
  348. // this, possibly by doing control file creation somewhere else.
  349. if (m_module != null)
  350. rcMod = m_module.RegionCombinerModule;
  351. if (rcMod != null)
  352. isMegaregion = rcMod.IsRootForMegaregion(m_scene.RegionInfo.RegionID);
  353. else
  354. isMegaregion = false;
  355. if (isMegaregion)
  356. size = rcMod.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
  357. else
  358. size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
  359. xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
  360. xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
  361. xtw.WriteEndElement();
  362. xtw.WriteElementString("assets_included", SaveAssets.ToString());
  363. xtw.WriteEndElement();
  364. xtw.Flush();
  365. }
  366. s = sw.ToString();
  367. }
  368. // if (m_scene != null)
  369. // Console.WriteLine(
  370. // "[ARCHIVE WRITE REQUEST PREPARATION]: Control file for {0} is: {1}", m_scene.RegionInfo.RegionName, s);
  371. return s;
  372. }
  373. }
  374. }