diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index c380d67db1..67854a2a7a 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Security;
using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
@@ -643,7 +644,15 @@ namespace Emby.Server.Implementations.IO
///
public virtual IEnumerable GetFileSystemEntryPaths(string path, bool recursive = false)
{
- return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
+ try
+ {
+ return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
+ }
+ catch (Exception ex) when (ex is UnauthorizedAccessException or DirectoryNotFoundException or SecurityException)
+ {
+ _logger.LogError(ex, "Failed to enumerate path {Path}", path);
+ return Enumerable.Empty();
+ }
}
///
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 1f13c833b6..a2957cdca4 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -331,8 +331,25 @@ namespace MediaBrowser.Controller.Entities
}
}
+ private static bool IsLibraryFolderAccessible(IDirectoryService directoryService, BaseItem item)
+ {
+ // For top parents i.e. Library folders, skip the validation if it's empty or inaccessible
+ if (item.IsTopParent && !directoryService.IsAccessible(item.ContainingFolderPath))
+ {
+ Logger.LogWarning("Library folder {LibraryFolderPath} is inaccessible or empty, skipping", item.ContainingFolderPath);
+ return false;
+ }
+
+ return true;
+ }
+
private async Task ValidateChildrenInternal2(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken)
{
+ if (!IsLibraryFolderAccessible(directoryService, this))
+ {
+ return;
+ }
+
cancellationToken.ThrowIfCancellationRequested();
var validChildren = new List();
@@ -369,6 +386,11 @@ namespace MediaBrowser.Controller.Entities
foreach (var child in nonCachedChildren)
{
+ if (!IsLibraryFolderAccessible(directoryService, child))
+ {
+ continue;
+ }
+
if (currentChildren.TryGetValue(child.Id, out BaseItem currentChild))
{
validChildren.Add(currentChild);
@@ -392,8 +414,8 @@ namespace MediaBrowser.Controller.Entities
validChildren.Add(child);
}
- // If any items were added or removed....
- if (newItems.Count > 0 || currentChildren.Count != validChildren.Count)
+ // If it's an AggregateFolder, don't remove
+ if (!IsRoot && currentChildren.Count != validChildren.Count)
{
// That's all the new and changed ones - now see if there are any that are missing
var itemsRemoved = currentChildren.Values.Except(validChildren).ToList();
@@ -408,7 +430,10 @@ namespace MediaBrowser.Controller.Entities
LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }, this, false);
}
}
+ }
+ if (newItems.Count > 0)
+ {
LibraryManager.CreateItems(newItems, this, cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index d4de97651f..7fe2f64af3 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -78,5 +78,10 @@ namespace MediaBrowser.Controller.Providers
return filePaths;
}
+
+ public bool IsAccessible(string path)
+ {
+ return _fileSystem.GetFileSystemEntryPaths(path).Any();
+ }
}
}
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
index 48d6276918..6d7550ab53 100644
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs
@@ -16,5 +16,7 @@ namespace MediaBrowser.Controller.Providers
IReadOnlyList GetFilePaths(string path);
IReadOnlyList GetFilePaths(string path, bool clearCache, bool sort = false);
+
+ bool IsAccessible(string path);
}
}