/// <inheritdoc /> protected override IFileSystemWatcher WatchImpl(UPath path) { // TODO: create/delete events when mounts are added/removed var watcher = new AggregateFileSystemWatcher(this, path); lock (_mounts) lock (_aggregateWatchers) { foreach (var kvp in _mounts) { if (!IsMountIncludedInWatch(kvp.Key, path, out var remainingPath)) { continue; } if (kvp.Value.CanWatch(remainingPath)) { var internalWatcher = kvp.Value.Watch(remainingPath); watcher.Add(new Watcher(kvp.Value, kvp.Key, remainingPath, internalWatcher)); } } if (NextFileSystem != null && NextFileSystem.CanWatch(path)) { var internalWatcher = NextFileSystem.Watch(path); watcher.Add(new Watcher(NextFileSystem, null, path, internalWatcher)); } _aggregateWatchers.Add(watcher); } return(watcher); }
public void Dispose() { if (Owned) { NextFileSystem?.Dispose(); } }
protected override void Dispose(bool disposing) { if (disposing && Owned) { NextFileSystem?.Dispose(); } }
/// <inheritdoc /> protected override IFileSystemWatcher WatchImpl(UPath path) { lock (_fileSystems) { var watcher = new Watcher(this, path); if (NextFileSystem != null && NextFileSystem.CanWatch(path)) { watcher.Add(NextFileSystem.Watch(path)); } foreach (var fs in _fileSystems) { if (fs.CanWatch(path)) { watcher.Add(fs.Watch(path)); } } _watchers.Add(watcher); return(watcher); } }
/// <inheritdoc /> protected override IEnumerable <UPath> EnumeratePathsImpl(UPath path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { // Use the search pattern to normalize the path/search pattern var search = SearchPattern.Parse(ref path, ref searchPattern); // Query all mounts just once List <KeyValuePair <UPath, IFileSystem> > mounts; lock (_mounts) { mounts = _mounts.ToList(); } // Internal method used to retrieve the list of search locations List <SearchLocation> GetSearchLocations(UPath basePath) { var locations = new List <SearchLocation>(); var matchedMount = false; foreach (var kvp in mounts) { // Check if path partially matches a mount name var remainingPath = GetRemaining(basePath, kvp.Key); if (!remainingPath.IsNull && remainingPath != UPath.Root) { locations.Add(new SearchLocation(this, basePath, remainingPath)); continue; } if (!matchedMount) { // Check if path fully matches a mount name remainingPath = GetRemaining(kvp.Key, basePath); if (!remainingPath.IsNull) { matchedMount = true; // don't check other mounts, we don't want to merge them together if (kvp.Value.DirectoryExists(remainingPath)) { locations.Add(new SearchLocation(kvp.Value, kvp.Key, remainingPath)); } } } } if (!matchedMount && NextFileSystem != null && NextFileSystem.DirectoryExists(basePath)) { locations.Add(new SearchLocation(NextFileSystem, null, basePath)); } return(locations); } var directoryToVisit = new List <UPath>(); directoryToVisit.Add(path); var entries = new SortedSet <UPath>(UPath.DefaultComparerIgnoreCase); var sortedDirectories = new SortedSet <UPath>(UPath.DefaultComparerIgnoreCase); var first = true; while (directoryToVisit.Count > 0) { var pathToVisit = directoryToVisit[0]; directoryToVisit.RemoveAt(0); var dirIndex = 0; entries.Clear(); sortedDirectories.Clear(); var locations = GetSearchLocations(pathToVisit); // Only need to search within one filesystem, no need to sort or do other work if (locations.Count == 1 && locations[0].FileSystem != this && (!first || searchOption == SearchOption.AllDirectories)) { var last = locations[0]; foreach (var item in last.FileSystem.EnumeratePaths(last.Path, searchPattern, searchOption, searchTarget)) { yield return(CombinePrefix(last.Prefix, item)); } } else { for (var i = locations.Count - 1; i >= 0; i--) { var location = locations[i]; var fileSystem = location.FileSystem; var searchPath = location.Path; if (fileSystem == this) { // List a single part of a mount name, queue it to be visited if needed var mountPart = new UPath(searchPath.GetFirstDirectory(out _)).ToRelative(); var mountPath = location.Prefix / mountPart; var isMatching = search.Match(mountPath); if (isMatching && searchTarget != SearchTarget.File) { entries.Add(mountPath); } if (searchOption == SearchOption.AllDirectories) { sortedDirectories.Add(mountPath); } } else { // List files in the mounted filesystems, merged and sorted into one list foreach (var item in fileSystem.EnumeratePaths(searchPath, "*", SearchOption.TopDirectoryOnly, SearchTarget.Both)) { var publicName = CombinePrefix(location.Prefix, item); if (entries.Contains(publicName)) { continue; } var isFile = fileSystem.FileExists(item); var isDirectory = fileSystem.DirectoryExists(item); var isMatching = search.Match(publicName); if (isMatching && ((isFile && searchTarget != SearchTarget.Directory) || (isDirectory && searchTarget != SearchTarget.File))) { entries.Add(publicName); } if (searchOption == SearchOption.AllDirectories && isDirectory) { sortedDirectories.Add(publicName); } } } } } if (first) { if (locations.Count == 0 && path != UPath.Root) { throw NewDirectoryNotFoundException(path); } first = false; } // Enqueue directories and respect order foreach (var nextDir in sortedDirectories) { directoryToVisit.Insert(dirIndex++, nextDir); } // Return entries foreach (var entry in entries) { yield return(entry); } } }
/// <inheritdoc /> protected override IEnumerable <UPath> EnumeratePathsImpl(UPath path, string searchPattern, SearchOption searchOption, SearchTarget searchTarget) { // Use the search pattern to normalize the path/search pattern var search = SearchPattern.Parse(ref path, ref searchPattern); var originalSrcPath = path; // Internal method used to retrieve the list of root directories SortedSet <UPath> GetRootDirectories() { var directories = new SortedSet <UPath>(UPath.DefaultComparerIgnoreCase); lock (_mounts) { foreach (var mountName in _mounts.Keys) { directories.Add(mountName); } } if (NextFileSystem != null) { foreach (var dir in NextFileSystem.EnumeratePaths(path, "*", SearchOption.TopDirectoryOnly, SearchTarget.Directory)) { if (!directories.Contains(dir)) { directories.Add(dir); } } } return(directories); } IEnumerable <UPath> EnumeratePathFromFileSystem(UPath subPath, bool failOnInvalidPath) { var fs = TryGetMountOrNext(ref subPath, out var mountPath); if (fs == null) { if (failOnInvalidPath) { throw NewDirectoryNotFoundException(originalSrcPath); } yield break; } if (fs != NextFileSystem) { // In the case of a mount, we need to return the full path Debug.Assert(!mountPath.IsNull); foreach (var entry in fs.EnumeratePaths(subPath, searchPattern, searchOption, searchTarget)) { yield return(mountPath / entry.ToRelative()); } } else { foreach (var entry in fs.EnumeratePaths(subPath, searchPattern, searchOption, searchTarget)) { yield return(entry); } } } // Special case for the root as we have to return the list of mount directories // and merge them with the underlying FileSystem if (path == UPath.Root) { var entries = new SortedSet <UPath>(UPath.DefaultComparerIgnoreCase); // Return the list of dircetories var directories = GetRootDirectories(); // Process the files first if (NextFileSystem != null && (searchTarget == SearchTarget.File || searchTarget == SearchTarget.Both)) { foreach (var file in NextFileSystem.EnumeratePaths(path, searchPattern, SearchOption.TopDirectoryOnly, SearchTarget.File)) { entries.Add(file); } } if (searchTarget != SearchTarget.File) { foreach (var dir in directories) { if (search.Match(dir)) { entries.Add(dir); } } } // Return all entries sorted foreach (var entry in entries) { yield return(entry); } if (searchOption == SearchOption.AllDirectories) { foreach (var dir in directories) { foreach (var entry in EnumeratePathFromFileSystem(dir, false)) { yield return(entry); } } } } else { foreach (var entry in EnumeratePathFromFileSystem(path, true)) { yield return(entry); } } }