diff options
4 files changed, 100 insertions, 61 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index 3838316..1e21b74 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs | |||
@@ -109,7 +109,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
109 | /// <summary> | 109 | /// <summary> |
110 | /// Inventory nodes loaded from the iar. | 110 | /// Inventory nodes loaded from the iar. |
111 | /// </summary> | 111 | /// </summary> |
112 | protected HashSet<InventoryNodeBase> m_loadedNodes = new HashSet<InventoryNodeBase>(); | 112 | protected Dictionary<UUID, InventoryNodeBase> m_loadedNodes = new Dictionary<UUID, InventoryNodeBase>(); |
113 | 113 | ||
114 | /// <summary> | 114 | /// <summary> |
115 | /// In order to load identically named folders, we need to keep track of the folders that we have already | 115 | /// In order to load identically named folders, we need to keep track of the folders that we have already |
@@ -122,6 +122,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
122 | /// after OSP resolution (since OSP creators are only stored in the item | 122 | /// after OSP resolution (since OSP creators are only stored in the item |
123 | /// </summary> | 123 | /// </summary> |
124 | protected Dictionary<UUID, UUID> m_creatorIdForAssetId = new Dictionary<UUID, UUID>(); | 124 | protected Dictionary<UUID, UUID> m_creatorIdForAssetId = new Dictionary<UUID, UUID>(); |
125 | protected Dictionary<UUID, UUID> m_itemIDs = new Dictionary<UUID, UUID>(); | ||
126 | protected List<InventoryItemBase> m_invLinks = new List<InventoryItemBase>(); | ||
127 | protected Dictionary<UUID, InventoryNodeBase> m_invLinksFolders = new Dictionary<UUID, InventoryNodeBase>(); | ||
125 | 128 | ||
126 | public InventoryArchiveReadRequest( | 129 | public InventoryArchiveReadRequest( |
127 | IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, string loadPath, bool merge) | 130 | IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, string loadPath, bool merge) |
@@ -181,7 +184,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
181 | /// returned | 184 | /// returned |
182 | /// </returns> | 185 | /// </returns> |
183 | /// <exception cref="System.Exception">Thrown if load fails.</exception> | 186 | /// <exception cref="System.Exception">Thrown if load fails.</exception> |
184 | public HashSet<InventoryNodeBase> Execute() | 187 | public Dictionary<UUID,InventoryNodeBase> Execute() |
185 | { | 188 | { |
186 | try | 189 | try |
187 | { | 190 | { |
@@ -223,6 +226,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
223 | } | 226 | } |
224 | 227 | ||
225 | archive.Close(); | 228 | archive.Close(); |
229 | LoadInventoryLinks(); | ||
226 | 230 | ||
227 | m_log.DebugFormat( | 231 | m_log.DebugFormat( |
228 | "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures", | 232 | "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures", |
@@ -271,7 +275,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
271 | string iarPath, | 275 | string iarPath, |
272 | InventoryFolderBase rootDestFolder, | 276 | InventoryFolderBase rootDestFolder, |
273 | Dictionary <string, InventoryFolderBase> resolvedFolders, | 277 | Dictionary <string, InventoryFolderBase> resolvedFolders, |
274 | HashSet<InventoryNodeBase> loadedNodes) | 278 | Dictionary<UUID, InventoryNodeBase> loadedNodes) |
275 | { | 279 | { |
276 | string iarPathExisting = iarPath; | 280 | string iarPathExisting = iarPath; |
277 | 281 | ||
@@ -392,7 +396,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
392 | string iarPathExisting, | 396 | string iarPathExisting, |
393 | string iarPathToReplicate, | 397 | string iarPathToReplicate, |
394 | Dictionary <string, InventoryFolderBase> resolvedFolders, | 398 | Dictionary <string, InventoryFolderBase> resolvedFolders, |
395 | HashSet<InventoryNodeBase> loadedNodes) | 399 | Dictionary<UUID, InventoryNodeBase> loadedNodes) |
396 | { | 400 | { |
397 | string[] rawDirsToCreate = iarPathToReplicate.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); | 401 | string[] rawDirsToCreate = iarPathToReplicate.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); |
398 | 402 | ||
@@ -424,7 +428,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
424 | resolvedFolders[iarPathExisting] = destFolder; | 428 | resolvedFolders[iarPathExisting] = destFolder; |
425 | 429 | ||
426 | if (0 == i) | 430 | if (0 == i) |
427 | loadedNodes.Add(destFolder); | 431 | loadedNodes[destFolder.ID] = destFolder; |
428 | } | 432 | } |
429 | } | 433 | } |
430 | 434 | ||
@@ -439,8 +443,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
439 | { | 443 | { |
440 | InventoryItemBase item = UserInventoryItemSerializer.Deserialize(data); | 444 | InventoryItemBase item = UserInventoryItemSerializer.Deserialize(data); |
441 | 445 | ||
446 | UUID oldID = item.ID; | ||
442 | // Don't use the item ID that's in the file | 447 | // Don't use the item ID that's in the file |
443 | item.ID = UUID.Random(); | 448 | item.ID = UUID.Random(); |
449 | m_itemIDs[oldID] = item.ID; | ||
444 | 450 | ||
445 | UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_UserAccountService); | 451 | UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_UserAccountService); |
446 | if (UUID.Zero != ospResolvedId) // The user exists in this grid | 452 | if (UUID.Zero != ospResolvedId) // The user exists in this grid |
@@ -457,7 +463,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
457 | else if (string.IsNullOrEmpty(item.CreatorData)) | 463 | else if (string.IsNullOrEmpty(item.CreatorData)) |
458 | { | 464 | { |
459 | item.CreatorId = m_userInfo.PrincipalID.ToString(); | 465 | item.CreatorId = m_userInfo.PrincipalID.ToString(); |
460 | // item.CreatorIdAsUuid = new UUID(item.CreatorId); | ||
461 | } | 466 | } |
462 | 467 | ||
463 | item.Owner = m_userInfo.PrincipalID; | 468 | item.Owner = m_userInfo.PrincipalID; |
@@ -470,10 +475,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
470 | // FIXME: This relies on the items coming before the assets in the TAR file. Need to create stronger | 475 | // FIXME: This relies on the items coming before the assets in the TAR file. Need to create stronger |
471 | // checks for this, and maybe even an external tool for creating OARs which enforces this, rather than | 476 | // checks for this, and maybe even an external tool for creating OARs which enforces this, rather than |
472 | // relying on native tar tools. | 477 | // relying on native tar tools. |
473 | m_creatorIdForAssetId[item.AssetID] = item.CreatorIdAsUuid; | 478 | if(item.AssetType == (int)AssetType.Link) |
474 | 479 | { | |
475 | if (!m_InventoryService.AddItem(item)) | 480 | m_invLinks.Add(item); |
476 | m_log.WarnFormat("[INVENTORY ARCHIVER]: Unable to save item {0} in folder {1}", item.Name, item.Folder); | 481 | if(!m_loadedNodes.ContainsKey(item.Folder) && !m_invLinksFolders.ContainsKey(item.Folder)) |
482 | m_invLinksFolders[item.Folder] = loadFolder; | ||
483 | return null; | ||
484 | } | ||
485 | else | ||
486 | { | ||
487 | m_creatorIdForAssetId[item.AssetID] = item.CreatorIdAsUuid; | ||
488 | if (!m_InventoryService.AddItem(item)) | ||
489 | m_log.WarnFormat("[INVENTORY ARCHIVER]: Unable to save item {0} in folder {1}", item.Name, item.Folder); | ||
490 | } | ||
477 | 491 | ||
478 | return item; | 492 | return item; |
479 | } | 493 | } |
@@ -504,56 +518,57 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
504 | string rawUuid = filename.Remove(filename.Length - extension.Length); | 518 | string rawUuid = filename.Remove(filename.Length - extension.Length); |
505 | UUID assetId = new UUID(rawUuid); | 519 | UUID assetId = new UUID(rawUuid); |
506 | 520 | ||
507 | if (ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension)) | 521 | if (!ArchiveConstants.EXTENSION_TO_ASSET_TYPE.ContainsKey(extension)) |
522 | { | ||
523 | m_log.ErrorFormat( | ||
524 | "[INVENTORY ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}", | ||
525 | assetPath, extension); | ||
526 | return false; | ||
527 | } | ||
528 | |||
529 | sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension]; | ||
530 | if (assetType == (sbyte)AssetType.Unknown) | ||
508 | { | 531 | { |
509 | sbyte assetType = ArchiveConstants.EXTENSION_TO_ASSET_TYPE[extension]; | 532 | m_log.WarnFormat("[INVENTORY ARCHIVER]: Importing {0} byte asset {1} with unknown type", data.Length, assetId); |
533 | return false; | ||
534 | } | ||
510 | 535 | ||
511 | if (assetType == (sbyte)AssetType.Unknown) | 536 | if(assetType == (sbyte)AssetType.Object) |
512 | { | 537 | { |
513 | m_log.WarnFormat("[INVENTORY ARCHIVER]: Importing {0} byte asset {1} with unknown type", data.Length, assetId); | 538 | UUID owner = m_userInfo.PrincipalID; |
514 | } | 539 | bool doCreatorID = m_creatorIdForAssetId.ContainsKey(assetId); |
515 | else if (assetType == (sbyte)AssetType.Object) | 540 | |
516 | { | 541 | data = SceneObjectSerializer.ModifySerializedObject(assetId, data, |
517 | if (m_creatorIdForAssetId.ContainsKey(assetId)) | 542 | sog => |
518 | { | 543 | { |
519 | data = SceneObjectSerializer.ModifySerializedObject(assetId, data, | 544 | foreach(SceneObjectPart sop in sog.Parts) |
520 | sog => { | 545 | { |
521 | bool modified = false; | 546 | sop.OwnerID = owner; |
522 | 547 | if(doCreatorID && string.IsNullOrEmpty(sop.CreatorData)) | |
523 | foreach (SceneObjectPart sop in sog.Parts) | 548 | sop.CreatorID = m_creatorIdForAssetId[assetId]; |
524 | { | 549 | |
525 | if (string.IsNullOrEmpty(sop.CreatorData)) | 550 | foreach(TaskInventoryItem it in sop.Inventory.GetInventoryItems()) |
526 | { | 551 | { |
527 | sop.CreatorID = m_creatorIdForAssetId[assetId]; | 552 | it.OwnerID = owner; |
528 | modified = true; | 553 | if(string.IsNullOrEmpty(it.CreatorData) && m_creatorIdForAssetId.ContainsKey(it.AssetID)) |
529 | } | 554 | it.CreatorID = m_creatorIdForAssetId[it.AssetID]; |
530 | } | 555 | } |
531 | 556 | } | |
532 | return modified; | 557 | return true; |
533 | }); | 558 | }); |
534 | |||
535 | if (data == null) | ||
536 | return false; | ||
537 | } | ||
538 | } | ||
539 | 559 | ||
540 | //m_log.DebugFormat("[INVENTORY ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType); | 560 | if(data == null) |
561 | return false; | ||
562 | } | ||
541 | 563 | ||
542 | AssetBase asset = new AssetBase(assetId, "From IAR", assetType, UUID.Zero.ToString()); | 564 | //m_log.DebugFormat("[INVENTORY ARCHIVER]: Importing asset {0}, type {1}", uuid, assetType); |
543 | asset.Data = data; | ||
544 | 565 | ||
545 | m_AssetService.Store(asset); | 566 | AssetBase asset = new AssetBase(assetId, "From IAR", assetType, UUID.Zero.ToString()); |
567 | asset.Data = data; | ||
546 | 568 | ||
547 | return true; | 569 | m_AssetService.Store(asset); |
548 | } | ||
549 | else | ||
550 | { | ||
551 | m_log.ErrorFormat( | ||
552 | "[INVENTORY ARCHIVER]: Tried to dearchive data with path {0} with an unknown type extension {1}", | ||
553 | assetPath, extension); | ||
554 | 570 | ||
555 | return false; | 571 | return true; |
556 | } | ||
557 | } | 572 | } |
558 | 573 | ||
559 | /// <summary> | 574 | /// <summary> |
@@ -621,14 +636,38 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
621 | 636 | ||
622 | // If we aren't loading the folder containing the item then well need to update the | 637 | // If we aren't loading the folder containing the item then well need to update the |
623 | // viewer separately for that item. | 638 | // viewer separately for that item. |
624 | if (!m_loadedNodes.Contains(foundFolder)) | 639 | if (!m_loadedNodes.ContainsKey(foundFolder.ID)) |
625 | m_loadedNodes.Add(item); | 640 | m_loadedNodes[foundFolder.ID] = item; |
626 | } | 641 | } |
627 | } | 642 | } |
628 | 643 | ||
629 | m_inventoryNodesLoaded = true; | 644 | m_inventoryNodesLoaded = true; |
630 | } | 645 | } |
631 | 646 | ||
647 | private void LoadInventoryLinks() | ||
648 | { | ||
649 | foreach(InventoryItemBase it in m_invLinks) | ||
650 | { | ||
651 | UUID target = it.AssetID; | ||
652 | if(m_itemIDs.ContainsKey(target)) | ||
653 | { | ||
654 | it.AssetID = m_itemIDs[target]; | ||
655 | if(!m_InventoryService.AddItem(it)) | ||
656 | m_log.WarnFormat("[INVENTORY ARCHIVER]: Unable to save item {0} in folder {1}",it.Name,it.Folder); | ||
657 | else | ||
658 | { | ||
659 | m_successfulItemRestores++; | ||
660 | UUID fid = it.Folder; | ||
661 | if (!m_loadedNodes.ContainsKey(fid) && m_invLinksFolders.ContainsKey(fid)) | ||
662 | m_loadedNodes[fid] = m_invLinksFolders[fid]; | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | |||
667 | m_itemIDs.Clear(); | ||
668 | m_invLinks.Clear(); | ||
669 | m_invLinksFolders.Clear(); | ||
670 | } | ||
632 | /// <summary> | 671 | /// <summary> |
633 | /// Load asset file | 672 | /// Load asset file |
634 | /// </summary> | 673 | /// </summary> |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs index 06aec7b..d50ebf5 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs | |||
@@ -593,7 +593,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
593 | /// Notify the client of loaded nodes if they are logged in | 593 | /// Notify the client of loaded nodes if they are logged in |
594 | /// </summary> | 594 | /// </summary> |
595 | /// <param name="loadedNodes">Can be empty. In which case, nothing happens</param> | 595 | /// <param name="loadedNodes">Can be empty. In which case, nothing happens</param> |
596 | private void UpdateClientWithLoadedNodes(UserAccount userInfo, HashSet<InventoryNodeBase> loadedNodes) | 596 | private void UpdateClientWithLoadedNodes(UserAccount userInfo, Dictionary<UUID, InventoryNodeBase> loadedNodes) |
597 | { | 597 | { |
598 | if (loadedNodes.Count == 0) | 598 | if (loadedNodes.Count == 0) |
599 | return; | 599 | return; |
@@ -604,7 +604,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
604 | 604 | ||
605 | if (user != null && !user.IsChildAgent) | 605 | if (user != null && !user.IsChildAgent) |
606 | { | 606 | { |
607 | foreach (InventoryNodeBase node in loadedNodes) | 607 | foreach (InventoryNodeBase node in loadedNodes.Values) |
608 | { | 608 | { |
609 | // m_log.DebugFormat( | 609 | // m_log.DebugFormat( |
610 | // "[INVENTORY ARCHIVER]: Notifying {0} of loaded inventory node {1}", | 610 | // "[INVENTORY ARCHIVER]: Notifying {0} of loaded inventory node {1}", |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadPathTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadPathTests.cs index f559c2e..86eca17 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadPathTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadPathTests.cs | |||
@@ -212,7 +212,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
212 | UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene); | 212 | UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene); |
213 | 213 | ||
214 | Dictionary <string, InventoryFolderBase> foldersCreated = new Dictionary<string, InventoryFolderBase>(); | 214 | Dictionary <string, InventoryFolderBase> foldersCreated = new Dictionary<string, InventoryFolderBase>(); |
215 | HashSet<InventoryNodeBase> nodesLoaded = new HashSet<InventoryNodeBase>(); | 215 | Dictionary<UUID, InventoryNodeBase> nodesLoaded = new Dictionary<UUID, InventoryNodeBase>(); |
216 | 216 | ||
217 | string folder1Name = "1"; | 217 | string folder1Name = "1"; |
218 | string folder2aName = "2a"; | 218 | string folder2aName = "2a"; |
@@ -293,7 +293,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
293 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, null, (Stream)null, false) | 293 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, null, (Stream)null, false) |
294 | .ReplicateArchivePathToUserInventory( | 294 | .ReplicateArchivePathToUserInventory( |
295 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), | 295 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), |
296 | new Dictionary<string, InventoryFolderBase>(), new HashSet<InventoryNodeBase>()); | 296 | new Dictionary<string, InventoryFolderBase>(), new Dictionary<UUID, InventoryNodeBase>()); |
297 | 297 | ||
298 | List<InventoryFolderBase> folder1PostCandidates | 298 | List<InventoryFolderBase> folder1PostCandidates |
299 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); | 299 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); |
@@ -344,7 +344,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
344 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, folder1ExistingName, (Stream)null, true) | 344 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, folder1ExistingName, (Stream)null, true) |
345 | .ReplicateArchivePathToUserInventory( | 345 | .ReplicateArchivePathToUserInventory( |
346 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), | 346 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), |
347 | new Dictionary<string, InventoryFolderBase>(), new HashSet<InventoryNodeBase>()); | 347 | new Dictionary<string, InventoryFolderBase>(), new Dictionary<UUID, InventoryNodeBase>()); |
348 | 348 | ||
349 | List<InventoryFolderBase> folder1PostCandidates | 349 | List<InventoryFolderBase> folder1PostCandidates |
350 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); | 350 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); |
diff --git a/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs b/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs index df9d4f9..5d77201 100644 --- a/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Library/LibraryModule.cs | |||
@@ -178,7 +178,7 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
178 | InventoryArchiveReadRequest archread = new InventoryArchiveReadRequest(m_MockScene.InventoryService, m_MockScene.AssetService, m_MockScene.UserAccountService, uinfo, simpleName, iarFileName, false); | 178 | InventoryArchiveReadRequest archread = new InventoryArchiveReadRequest(m_MockScene.InventoryService, m_MockScene.AssetService, m_MockScene.UserAccountService, uinfo, simpleName, iarFileName, false); |
179 | try | 179 | try |
180 | { | 180 | { |
181 | HashSet<InventoryNodeBase> nodes = archread.Execute(); | 181 | Dictionary<UUID, InventoryNodeBase> nodes = archread.Execute(); |
182 | if (nodes != null && nodes.Count == 0) | 182 | if (nodes != null && nodes.Count == 0) |
183 | { | 183 | { |
184 | // didn't find the subfolder with the given name; place it on the top | 184 | // didn't find the subfolder with the given name; place it on the top |
@@ -188,7 +188,7 @@ namespace OpenSim.Region.CoreModules.Framework.Library | |||
188 | archread.Execute(); | 188 | archread.Execute(); |
189 | } | 189 | } |
190 | 190 | ||
191 | foreach (InventoryNodeBase node in nodes) | 191 | foreach (InventoryNodeBase node in nodes.Values) |
192 | FixPerms(node); | 192 | FixPerms(node); |
193 | } | 193 | } |
194 | catch (Exception e) | 194 | catch (Exception e) |