opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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  
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.IO.Compression;
32 using System.Reflection;
33 using System.Threading;
34 using System.Text;
35 using System.Xml;
36 using System.Xml.Linq;
37 using log4net;
38 using OpenMetaverse;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Serialization;
41 using OpenSim.Framework.Serialization.External;
42 using OpenSim.Region.CoreModules.World.Archiver;
43 using OpenSim.Region.Framework.Scenes;
44 using OpenSim.Region.Framework.Scenes.Serialization;
45 using OpenSim.Region.Framework.Interfaces;
46 using OpenSim.Services.Interfaces;
47  
48 namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
49 {
50 public class InventoryArchiveReadRequest
51 {
52 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53  
54 /// <summary>
55 /// The maximum major version of archive that we can read. Minor versions shouldn't need a max number since version
56 /// bumps here should be compatible.
57 /// </summary>
58 public static int MAX_MAJOR_VERSION = 1;
59  
60 protected TarArchiveReader archive;
61  
62 private UserAccount m_userInfo;
63 private string m_invPath;
64  
65 /// <summary>
66 /// Do we want to merge this load with existing inventory?
67 /// </summary>
68 protected bool m_merge;
69  
70 protected IInventoryService m_InventoryService;
71 protected IAssetService m_AssetService;
72 protected IUserAccountService m_UserAccountService;
73  
74 /// <value>
75 /// The stream from which the inventory archive will be loaded.
76 /// </value>
77 private Stream m_loadStream;
78  
79 /// <summary>
80 /// Has the control file been loaded for this archive?
81 /// </summary>
82 public bool ControlFileLoaded { get; private set; }
83  
84 /// <summary>
85 /// Do we want to enforce the check. IAR versions before 0.2 and 1.1 do not guarantee this order, so we can't
86 /// enforce.
87 /// </summary>
88 public bool EnforceControlFileCheck { get; private set; }
89  
90 protected bool m_assetsLoaded;
91 protected bool m_inventoryNodesLoaded;
92  
93 protected int m_successfulAssetRestores;
94 protected int m_failedAssetRestores;
95 protected int m_successfulItemRestores;
96  
97 /// <summary>
98 /// Root destination folder for the IAR load.
99 /// </summary>
100 protected InventoryFolderBase m_rootDestinationFolder;
101  
102 /// <summary>
103 /// Inventory nodes loaded from the iar.
104 /// </summary>
105 protected HashSet<InventoryNodeBase> m_loadedNodes = new HashSet<InventoryNodeBase>();
106  
107 /// <summary>
108 /// In order to load identically named folders, we need to keep track of the folders that we have already
109 /// resolved.
110 /// </summary>
111 Dictionary <string, InventoryFolderBase> m_resolvedFolders = new Dictionary<string, InventoryFolderBase>();
112  
113 /// <summary>
114 /// Record the creator id that should be associated with an asset. This is used to adjust asset creator ids
115 /// after OSP resolution (since OSP creators are only stored in the item
116 /// </summary>
117 protected Dictionary<UUID, UUID> m_creatorIdForAssetId = new Dictionary<UUID, UUID>();
118  
119 public InventoryArchiveReadRequest(
120 IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, string loadPath, bool merge)
121 : this(
122 inv,
123 assets,
124 uacc,
125 userInfo,
126 invPath,
127 new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress),
128 merge)
129 {
130 }
131  
132 public InventoryArchiveReadRequest(
133 IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, Stream loadStream, bool merge)
134 {
135 m_InventoryService = inv;
136 m_AssetService = assets;
137 m_UserAccountService = uacc;
138 m_merge = merge;
139 m_userInfo = userInfo;
140 m_invPath = invPath;
141 m_loadStream = loadStream;
142  
143 // FIXME: Do not perform this check since older versions of OpenSim do save the control file after other things
144 // (I thought they weren't). We will need to bump the version number and perform this check on all
145 // subsequent IAR versions only
146 ControlFileLoaded = true;
147 }
148  
149 /// <summary>
150 /// Execute the request
151 /// </summary>
152 /// <remarks>
153 /// Only call this once. To load another IAR, construct another request object.
154 /// </remarks>
155 /// <returns>
156 /// A list of the inventory nodes loaded. If folders were loaded then only the root folders are
157 /// returned
158 /// </returns>
159 /// <exception cref="System.Exception">Thrown if load fails.</exception>
160 public HashSet<InventoryNodeBase> Execute()
161 {
162 try
163 {
164 string filePath = "ERROR";
165  
166 List<InventoryFolderBase> folderCandidates
167 = InventoryArchiveUtils.FindFoldersByPath(
168 m_InventoryService, m_userInfo.PrincipalID, m_invPath);
169  
170 if (folderCandidates.Count == 0)
171 {
172 // Possibly provide an option later on to automatically create this folder if it does not exist
173 m_log.ErrorFormat("[INVENTORY ARCHIVER]: Inventory path {0} does not exist", m_invPath);
174  
175 return m_loadedNodes;
176 }
177  
178 m_rootDestinationFolder = folderCandidates[0];
179 archive = new TarArchiveReader(m_loadStream);
180 byte[] data;
181 TarArchiveReader.TarEntryType entryType;
182  
183 while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
184 {
185 if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
186 {
187 LoadControlFile(filePath, data);
188 }
189 else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
190 {
191 LoadAssetFile(filePath, data);
192 }
193 else if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH))
194 {
195 LoadInventoryFile(filePath, entryType, data);
196 }
197 }
198  
199 archive.Close();
200  
201 m_log.DebugFormat(
202 "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures",
203 m_successfulAssetRestores, m_failedAssetRestores);
204 m_log.InfoFormat("[INVENTORY ARCHIVER]: Successfully loaded {0} items", m_successfulItemRestores);
205  
206 return m_loadedNodes;
207 }
208 finally
209 {
210 m_loadStream.Close();
211 }
212 }
213  
214 public void Close()
215 {
216 if (m_loadStream != null)
217 m_loadStream.Close();
218 }
219  
220 /// <summary>
221 /// Replicate the inventory paths in the archive to the user's inventory as necessary.
222 /// </summary>
223 /// <param name="iarPath">The item archive path to replicate</param>
224 /// <param name="rootDestinationFolder">The root folder for the inventory load</param>
225 /// <param name="resolvedFolders">
226 /// The folders that we have resolved so far for a given archive path.
227 /// This method will add more folders if necessary
228 /// </param>
229 /// <param name="loadedNodes">
230 /// Track the inventory nodes created.
231 /// </param>
232 /// <returns>The last user inventory folder created or found for the archive path</returns>
233 public InventoryFolderBase ReplicateArchivePathToUserInventory(
234 string iarPath,
235 InventoryFolderBase rootDestFolder,
236 Dictionary <string, InventoryFolderBase> resolvedFolders,
237 HashSet<InventoryNodeBase> loadedNodes)
238 {
239 string iarPathExisting = iarPath;
240  
241 // m_log.DebugFormat(
242 // "[INVENTORY ARCHIVER]: Loading folder {0} {1}", rootDestFolder.Name, rootDestFolder.ID);
243  
244 InventoryFolderBase destFolder
245 = ResolveDestinationFolder(rootDestFolder, ref iarPathExisting, resolvedFolders);
246  
247 // m_log.DebugFormat(
248 // "[INVENTORY ARCHIVER]: originalArchivePath [{0}], section already loaded [{1}]",
249 // iarPath, iarPathExisting);
250  
251 string iarPathToCreate = iarPath.Substring(iarPathExisting.Length);
252 CreateFoldersForPath(destFolder, iarPathExisting, iarPathToCreate, resolvedFolders, loadedNodes);
253  
254 return destFolder;
255 }
256  
257 /// <summary>
258 /// Resolve a destination folder
259 /// </summary>
260 ///
261 /// We require here a root destination folder (usually the root of the user's inventory) and the archive
262 /// path. We also pass in a list of previously resolved folders in case we've found this one previously.
263 ///
264 /// <param name="archivePath">
265 /// The item archive path to resolve. The portion of the path passed back is that
266 /// which corresponds to the resolved desintation folder.
267 /// <param name="rootDestinationFolder">
268 /// The root folder for the inventory load
269 /// </param>
270 /// <param name="resolvedFolders">
271 /// The folders that we have resolved so far for a given archive path.
272 /// </param>
273 /// <returns>
274 /// The folder in the user's inventory that matches best the archive path given. If no such folder was found
275 /// then the passed in root destination folder is returned.
276 /// </returns>
277 protected InventoryFolderBase ResolveDestinationFolder(
278 InventoryFolderBase rootDestFolder,
279 ref string archivePath,
280 Dictionary <string, InventoryFolderBase> resolvedFolders)
281 {
282 // string originalArchivePath = archivePath;
283  
284 while (archivePath.Length > 0)
285 {
286 // m_log.DebugFormat("[INVENTORY ARCHIVER]: Trying to resolve destination folder {0}", archivePath);
287  
288 if (resolvedFolders.ContainsKey(archivePath))
289 {
290 // m_log.DebugFormat(
291 // "[INVENTORY ARCHIVER]: Found previously created folder from archive path {0}", archivePath);
292 return resolvedFolders[archivePath];
293 }
294 else
295 {
296 if (m_merge)
297 {
298 // TODO: Using m_invPath is totally wrong - what we need to do is strip the uuid from the
299 // iar name and try to find that instead.
300 string plainPath = ArchiveConstants.ExtractPlainPathFromIarPath(archivePath);
301 List<InventoryFolderBase> folderCandidates
302 = InventoryArchiveUtils.FindFoldersByPath(
303 m_InventoryService, m_userInfo.PrincipalID, plainPath);
304  
305 if (folderCandidates.Count != 0)
306 {
307 InventoryFolderBase destFolder = folderCandidates[0];
308 resolvedFolders[archivePath] = destFolder;
309 return destFolder;
310 }
311 }
312  
313 // Don't include the last slash so find the penultimate one
314 int penultimateSlashIndex = archivePath.LastIndexOf("/", archivePath.Length - 2);
315  
316 if (penultimateSlashIndex >= 0)
317 {
318 // Remove the last section of path so that we can see if we've already resolved the parent
319 archivePath = archivePath.Remove(penultimateSlashIndex + 1);
320 }
321 else
322 {
323 // m_log.DebugFormat(
324 // "[INVENTORY ARCHIVER]: Found no previously created folder for archive path {0}",
325 // originalArchivePath);
326 archivePath = string.Empty;
327 return rootDestFolder;
328 }
329 }
330 }
331  
332 return rootDestFolder;
333 }
334  
335 /// <summary>
336 /// Create a set of folders for the given path.
337 /// </summary>
338 /// <param name="destFolder">
339 /// The root folder from which the creation will take place.
340 /// </param>
341 /// <param name="iarPathExisting">
342 /// the part of the iar path that already exists
343 /// </param>
344 /// <param name="iarPathToReplicate">
345 /// The path to replicate in the user's inventory from iar
346 /// </param>
347 /// <param name="resolvedFolders">
348 /// The folders that we have resolved so far for a given archive path.
349 /// </param>
350 /// <param name="loadedNodes">
351 /// Track the inventory nodes created.
352 /// </param>
353 protected void CreateFoldersForPath(
354 InventoryFolderBase destFolder,
355 string iarPathExisting,
356 string iarPathToReplicate,
357 Dictionary <string, InventoryFolderBase> resolvedFolders,
358 HashSet<InventoryNodeBase> loadedNodes)
359 {
360 string[] rawDirsToCreate = iarPathToReplicate.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
361  
362 for (int i = 0; i < rawDirsToCreate.Length; i++)
363 {
364 // m_log.DebugFormat("[INVENTORY ARCHIVER]: Creating folder {0} from IAR", rawDirsToCreate[i]);
365  
366 if (!rawDirsToCreate[i].Contains(ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR))
367 continue;
368  
369 int identicalNameIdentifierIndex
370 = rawDirsToCreate[i].LastIndexOf(
371 ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR);
372  
373 string newFolderName = rawDirsToCreate[i].Remove(identicalNameIdentifierIndex);
374  
375 newFolderName = InventoryArchiveUtils.UnescapeArchivePath(newFolderName);
376 UUID newFolderId = UUID.Random();
377  
378 // Asset type has to be Unknown here rather than Folder, otherwise the created folder can't be
379 // deleted once the client has relogged.
380 // The root folder appears to be labelled AssetType.Folder (shows up as "Category" in the client)
381 // even though there is a AssetType.RootCategory
382 destFolder
383 = new InventoryFolderBase(
384 newFolderId, newFolderName, m_userInfo.PrincipalID,
385 (short)AssetType.Unknown, destFolder.ID, 1);
386 m_InventoryService.AddFolder(destFolder);
387  
388 // Record that we have now created this folder
389 iarPathExisting += rawDirsToCreate[i] + "/";
390 m_log.DebugFormat("[INVENTORY ARCHIVER]: Created folder {0} from IAR", iarPathExisting);
391 resolvedFolders[iarPathExisting] = destFolder;
392  
393 if (0 == i)
394 loadedNodes.Add(destFolder);
395 }
396 }
397  
398 /// <summary>
399 /// Load an item from the archive
400 /// </summary>
401 /// <param name="filePath">The archive path for the item</param>
402 /// <param name="data">The raw item data</param>
403 /// <param name="rootDestinationFolder">The root destination folder for loaded items</param>
404 /// <param name="nodesLoaded">All the inventory nodes (items and folders) loaded so far</param>
405 protected InventoryItemBase LoadItem(byte[] data, InventoryFolderBase loadFolder)
406 {
407 InventoryItemBase item = UserInventoryItemSerializer.Deserialize(data);
408  
409 // Don't use the item ID that's in the file
410 item.ID = UUID.Random();
411  
412 UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_UserAccountService);
413 if (UUID.Zero != ospResolvedId) // The user exists in this grid
414 {
415 // m_log.DebugFormat("[INVENTORY ARCHIVER]: Found creator {0} via OSPA resolution", ospResolvedId);
416  
417 // item.CreatorIdAsUuid = ospResolvedId;
418  
419 // Don't preserve the OSPA in the creator id (which actually gets persisted to the
420 // database). Instead, replace with the UUID that we found.
421 item.CreatorId = ospResolvedId.ToString();
422 item.CreatorData = string.Empty;
423 }
424 else if (string.IsNullOrEmpty(item.CreatorData))
425 {
426 item.CreatorId = m_userInfo.PrincipalID.ToString();
427 // item.CreatorIdAsUuid = new UUID(item.CreatorId);
428 }
429  
430 item.Owner = m_userInfo.PrincipalID;
431  
432 // Reset folder ID to the one in which we want to load it
433 item.Folder = loadFolder.ID;
434  
435 // Record the creator id for the item's asset so that we can use it later, if necessary, when the asset
436 // is loaded.
437 // FIXME: This relies on the items coming before the assets in the TAR file. Need to create stronger
438 // checks for this, and maybe even an external tool for creating OARs which enforces this, rather than
439 // relying on native tar tools.
440 m_creatorIdForAssetId[item.AssetID] = item.CreatorIdAsUuid;
441  
442 if (!m_InventoryService.AddItem(item))
443 m_log.WarnFormat("[INVENTORY ARCHIVER]: Unable to save item {0} in folder {1}", item.Name, item.Folder);
444  
445 return item;
446 }
447  
448 /// <summary>
449 /// Load an asset
450 /// </summary>
451 /// <param name="assetFilename"></param>
452 /// <param name="data"></param>
453 /// <returns>true if asset was successfully loaded, false otherwise</returns>
454 private bool LoadAsset(string assetPath, byte[] data)
455 {
456 //IRegionSerialiser serialiser = scene.RequestModuleInterface<IRegionSerialiser>();
457 // Right now we're nastily obtaining the UUID from the filename
458 string filename = assetPath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
459 int i = filename.LastIndexOf(ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
460  
461 if (i == -1)
462 {
463 m_log.ErrorFormat(
464 "[INVENTORY ARCHIVER]: Could not find extension information in asset path {0} since it's missing the separator {1}. Skipping",
465 assetPath, ArchiveConstants.ASSET_EXTENSION_SEPARATOR);
466  
467 return false;
468 }
469  
470 string extension = filename.Substring(i);
471 string rawUuid = filename.Remove(filename.Length - extension.Length);
472 UUID assetId = new UUID(rawUuid);
473  
474 if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension))
475 {
476 sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension];
477  
478 if (assetType == (sbyte)AssetType.Unknown)
479 {
480 m_log.WarnFormat("[INVENTORY ARCHIVER]: Importing {0} byte asset {1} with unknown type", data.Length, assetId);
481 }
482 else if (assetType == (sbyte)AssetType.Object)
483 {
484 if (m_creatorIdForAssetId.ContainsKey(assetId))
485 {
486 string xmlData = Utils.BytesToString(data);
487 List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
488  
489 CoalescedSceneObjects coa = null;
490 if (CoalescedSceneObjectsSerializer.TryFromXml(xmlData, out coa))
491 {
492 // m_log.DebugFormat(
493 // "[INVENTORY ARCHIVER]: Loaded coalescence {0} has {1} objects", assetId, coa.Count);
494  
495 if (coa.Objects.Count == 0)
496 {
497 m_log.WarnFormat(
498 "[INVENTORY ARCHIVE READ REQUEST]: Aborting load of coalesced object from asset {0} as it has zero loaded components",
499 assetId);
500 return false;
501 }
502  
503 sceneObjects.AddRange(coa.Objects);
504 }
505 else
506 {
507 SceneObjectGroup deserializedObject = SceneObjectSerializer.FromOriginalXmlFormat(xmlData);
508  
509 if (deserializedObject != null)
510 {
511 sceneObjects.Add(deserializedObject);
512 }
513 else
514 {
515 m_log.WarnFormat(
516 "[INVENTORY ARCHIVE READ REQUEST]: Aborting load of object from asset {0} as deserialization failed",
517 assetId);
518  
519 return false;
520 }
521 }
522  
523 foreach (SceneObjectGroup sog in sceneObjects)
524 foreach (SceneObjectPart sop in sog.Parts)
525 if (string.IsNullOrEmpty(sop.CreatorData))
526 sop.CreatorID = m_creatorIdForAssetId[assetId];
527  
528 if (coa != null)
529 data = Utils.StringToBytes(CoalescedSceneObjectsSerializer.ToXml(coa));
530 else
531 data = Utils.StringToBytes(SceneObjectSerializer.ToOriginalXmlFormat(sceneObjects[0]));
532 }
533 }
534  
535 //m_log.DebugFormat("[INVENTORY ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType);
536  
537 AssetBase asset = new AssetBase(assetId, "From IAR", assetType, UUID.Zero.ToString());
538 asset.Data = data;
539  
540 m_AssetService.Store(asset);
541  
542 return true;
543 }
544 else
545 {
546 m_log.ErrorFormat(
547 "[INVENTORY ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}",
548 assetPath, extension);
549  
550 return false;
551 }
552 }
553  
554 /// <summary>
555 /// Load control file
556 /// </summary>
557 /// <param name="path"></param>
558 /// <param name="data"></param>
559 public void LoadControlFile(string path, byte[] data)
560 {
561 XDocument doc = XDocument.Parse(Encoding.ASCII.GetString(data));
562 XElement archiveElement = doc.Element("archive");
563 int majorVersion = int.Parse(archiveElement.Attribute("major_version").Value);
564 int minorVersion = int.Parse(archiveElement.Attribute("minor_version").Value);
565 string version = string.Format("{0}.{1}", majorVersion, minorVersion);
566  
567 if (majorVersion > MAX_MAJOR_VERSION)
568 {
569 throw new Exception(
570 string.Format(
571 "The IAR you are trying to load has major version number of {0} but this version of OpenSim can only load IARs with major version number {1} and below",
572 majorVersion, MAX_MAJOR_VERSION));
573 }
574  
575 ControlFileLoaded = true;
576 m_log.InfoFormat("[INVENTORY ARCHIVER]: Loading IAR with version {0}", version);
577 }
578  
579 /// <summary>
580 /// Load inventory file
581 /// </summary>
582 /// <param name="path"></param>
583 /// <param name="entryType"></param>
584 /// <param name="data"></param>
585 protected void LoadInventoryFile(string path, TarArchiveReader.TarEntryType entryType, byte[] data)
586 {
587 if (!ControlFileLoaded)
588 throw new Exception(
589 string.Format(
590 "The IAR you are trying to load does not list {0} before {1}. Aborting load",
591 ArchiveConstants.CONTROL_FILE_PATH, ArchiveConstants.INVENTORY_PATH));
592  
593 if (m_assetsLoaded)
594 throw new Exception(
595 string.Format(
596 "The IAR you are trying to load does not list all {0} before {1}. Aborting load",
597 ArchiveConstants.INVENTORY_PATH, ArchiveConstants.ASSETS_PATH));
598  
599 path = path.Substring(ArchiveConstants.INVENTORY_PATH.Length);
600  
601 // Trim off the file portion if we aren't already dealing with a directory path
602 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY != entryType)
603 path = path.Remove(path.LastIndexOf("/") + 1);
604  
605 InventoryFolderBase foundFolder
606 = ReplicateArchivePathToUserInventory(
607 path, m_rootDestinationFolder, m_resolvedFolders, m_loadedNodes);
608  
609 if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY != entryType)
610 {
611 InventoryItemBase item = LoadItem(data, foundFolder);
612  
613 if (item != null)
614 {
615 m_successfulItemRestores++;
616  
617 // If we aren't loading the folder containing the item then well need to update the
618 // viewer separately for that item.
619 if (!m_loadedNodes.Contains(foundFolder))
620 m_loadedNodes.Add(item);
621 }
622 }
623  
624 m_inventoryNodesLoaded = true;
625 }
626  
627 /// <summary>
628 /// Load asset file
629 /// </summary>
630 /// <param name="path"></param>
631 /// <param name="data"></param>
632 protected void LoadAssetFile(string path, byte[] data)
633 {
634 if (!ControlFileLoaded)
635 throw new Exception(
636 string.Format(
637 "The IAR you are trying to load does not list {0} before {1}. Aborting load",
638 ArchiveConstants.CONTROL_FILE_PATH, ArchiveConstants.ASSETS_PATH));
639  
640 if (!m_inventoryNodesLoaded)
641 throw new Exception(
642 string.Format(
643 "The IAR you are trying to load does not list all {0} before {1}. Aborting load",
644 ArchiveConstants.INVENTORY_PATH, ArchiveConstants.ASSETS_PATH));
645  
646 if (LoadAsset(path, data))
647 m_successfulAssetRestores++;
648 else
649 m_failedAssetRestores++;
650  
651 if ((m_successfulAssetRestores) % 50 == 0)
652 m_log.DebugFormat(
653 "[INVENTORY ARCHIVER]: Loaded {0} assets...",
654 m_successfulAssetRestores);
655  
656 m_assetsLoaded = true;
657 }
658 }
659 }