/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System.Collections.Generic; using System.Threading; namespace Opc.Ua.Server.FileSystem { /// /// Enumerates the immediate children (files + directories) of a /// via the underlying /// . /// internal sealed class DirectoryBrowser : NodeBrowser { public DirectoryBrowser( ISystemContext context, ViewDescription? view, NodeId referenceType, bool includeSubtypes, BrowseDirection browseDirection, QualifiedName browseName, IEnumerable? additionalReferences, bool internalOnly, FileSystemNodeManager manager, DirectoryObjectState source) : base(context, view, referenceType, includeSubtypes, browseDirection, browseName, additionalReferences, internalOnly) { m_manager = manager; m_source = source; m_stage = Stage.Begin; } public override IReference? Next() { lock (DataLock) { IReference? reference = base.Next(); if (reference != null) { return reference; } if (InternalOnly || m_manager == null) { return null; } if (!IsRequired(ReferenceTypeIds.HasComponent, false)) { return null; } if (m_stage == Stage.Begin) { m_pending = LoadEntries(); m_stage = Stage.Children; } if (m_stage == Stage.Children) { reference = NextChild(); if (reference != null) { return reference; } m_stage = Stage.Done; } return null; } } private List LoadEntries() { var list = new List(); try { IAsyncEnumerator enumerator = m_manager.Provider .EnumerateAsync(m_source.ProviderPath, CancellationToken.None) .GetAsyncEnumerator(CancellationToken.None); try { while (enumerator.MoveNextAsync().AsTask().GetAwaiter().GetResult()) { list.Add(enumerator.Current); } } finally { enumerator.DisposeAsync().AsTask().GetAwaiter().GetResult(); } } catch { // Browse continues without children when the provider // can't enumerate (e.g. permission denied). } return list; } private NodeStateReference? NextChild() { if (m_pending == null) { return null; } // Named child requested — scan once and stop. if (!BrowseName.IsNull) { if (m_source.BrowseName.NamespaceIndex != BrowseName.NamespaceIndex) { m_pending = null; return null; } foreach (FileSystemEntry entry in m_pending) { if (entry.Name == BrowseName.Name) { m_pending = null; return CreateReference(entry); } } m_pending = null; return null; } if (m_pending.Count == 0) { return null; } FileSystemEntry head = m_pending[0]; m_pending.RemoveAt(0); return CreateReference(head); } private NodeStateReference CreateReference(FileSystemEntry entry) { NodeId targetId = entry.IsDirectory ? FileSystemNodeId.BuildDirectory(entry.Path, m_manager.NamespaceIndex) : FileSystemNodeId.BuildFile(entry.Path, m_manager.NamespaceIndex); return new NodeStateReference(ReferenceTypeIds.HasComponent, false, targetId); } private enum Stage { Begin, Children, Done } private readonly FileSystemNodeManager m_manager; private readonly DirectoryObjectState m_source; private List? m_pending; private Stage m_stage; } }