Skip to content

Commit

Permalink
improve registry check performance
Browse files Browse the repository at this point in the history
GetGoogleDriveRegistryProvidersAsync: remove one of its loops, rename it, split
it up, and add more logging.

So we're only accessing the registry once, set `App.AppModel.GoogleDrivePath` in
RemovableDrivesService, where we need that path first. Then
GoogleDriveCloudDetector can read from it, since it needs the path second.

Split up AddMyDriveToPathAndValidate.
  • Loading branch information
wharvex committed Aug 12, 2024
1 parent 4ce65c8 commit c802626
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 120 deletions.
28 changes: 7 additions & 21 deletions src/Files.App/Services/Storage/StorageDevicesService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,20 @@ public IStorageDeviceWatcher CreateWatcher()
public async IAsyncEnumerable<ILocatableFolder> GetDrivesAsync()
{
var list = DriveInfo.GetDrives();
var googleDrivePath = App.AppModel.GoogleDrivePath;
var pCloudDrivePath = App.AppModel.PCloudDrivePath;

var sw = Stopwatch.StartNew();
var googleDrivePath = GoogleDriveCloudDetector.GetRegistryBasePath();
sw.Stop();
#if DEBUG
Debug.WriteLine($"In RDS.GDA: googleDrivePath: {googleDrivePath}");
Debug.WriteLine($"In RemovableDrivesService: Time elapsed for registry check: {sw.Elapsed}");
#endif
App.AppModel.GoogleDrivePath = googleDrivePath ?? string.Empty;

foreach (var drive in list)
{
var shouldSkip = false;
var sw = Stopwatch.StartNew();
await foreach (var cloudProvider in GoogleDriveCloudDetector.GetGoogleDriveProvidersFromRegistryAsync(false))
{
if (cloudProvider.SyncFolder.Equals(drive.Name))
shouldSkip = true;
}
sw.Stop();

#if DEBUG
Debug.WriteLine($"In RDS.GDA after registry check: Time elapsed for filter: {sw.Elapsed}");
Debug.WriteLine($"In RDS.GDA: drive.Name: {drive.Name}");
#endif

if (shouldSkip)
// We don't want cloud drives to appear in a plain "Drives" section.
if (drive.Name.Equals(googleDrivePath) || drive.Name.Equals(pCloudDrivePath))
continue;

var res = await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(drive.Name).AsTask());
Expand All @@ -64,10 +54,6 @@ public async IAsyncEnumerable<ILocatableFolder> GetDrivesAsync()
var label = DriveHelpers.GetExtendedDriveLabel(drive);
var driveItem = await DriveItem.CreateFromPropertiesAsync(res.Result, drive.Name.TrimEnd('\\'), label, type, thumbnail);

// Don't add here because Google Drive is already displayed under cloud drives
if (drive.Name == googleDrivePath || drive.Name == pCloudDrivePath)
continue;

App.Logger.LogInformation($"Drive added: {driveItem.Path}, {driveItem.Type}");

yield return driveItem;
Expand Down
184 changes: 85 additions & 99 deletions src/Files.App/Utils/Cloud/Detector/GoogleDriveCloudDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace Files.App.Utils.Cloud
{
/// <summary>
/// Provides an utility for Google Drive Cloud detection.
/// Provides a utility for Google Drive Cloud detection.
/// </summary>
public sealed class GoogleDriveCloudDetector : AbstractCloudDetector
{
Expand Down Expand Up @@ -55,9 +55,6 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
continue;
}

if ((long)reader["is_my_drive"] == 1)
continue;

// By default, the path will be prefixed with "\\?\" (unless another app has explicitly changed it).
// \\?\ indicates to Win32 that the filename may be longer than MAX_PATH (see MSDN).
// Parts of .NET (e.g. the File class) don't handle this very well, so remove this prefix.
Expand All @@ -69,12 +66,9 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
var folder = await StorageFolder.GetFolderFromPathAsync(path);
string title = reader["title"]?.ToString() ?? folder.Name;

App.AppModel.GoogleDrivePath = path;

#if DEBUG
Debug.WriteLine($"In GDCD in roots table: App.AppModel.GoogleDrivePath being set to: {path}");
Debug.WriteLine("YIELD RETURNING from GoogleDriveCloudDetector#GetProviders (roots): ");
Debug.WriteLine($"name=Google Drive ({title}); path={path}");
Debug.WriteLine("YIELD RETURNING from `GoogleDriveCloudDetector.GetProviders()` (roots): ");
Debug.WriteLine($"Name: Google Drive ({title}); SyncFolder: {path}");
#endif

yield return new CloudProvider(CloudProviders.GoogleDrive)
Expand All @@ -84,6 +78,7 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
};
}

var iconFile = await GetGoogleDriveIconFileAsync();
// Google virtual drive
reader = cmdMedia.ExecuteReader();

Expand All @@ -94,21 +89,13 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
continue;

if (!AddMyDriveToPathAndValidate(ref path))
{
_logger.LogWarning($"Validation failed for {path} (media)");
continue;
}

var folder = await StorageFolder.GetFolderFromPathAsync(path);
string title = reader["name"]?.ToString() ?? folder.Name;

App.AppModel.GoogleDrivePath = path;

var iconFile = await GetGoogleDriveIconFileAsync();

#if DEBUG
Debug.WriteLine($"In GDCD in media table: App.AppModel.GoogleDrivePath being set to: {path}");
Debug.WriteLine("YIELD RETURNING from GoogleDriveCloudDetector#GetProviders (media): ");
Debug.WriteLine("YIELD RETURNING from `GoogleDriveCloudDetector.GetProviders` (media): ");
Debug.WriteLine($"name={title}; path={path}");
#endif

Expand All @@ -126,19 +113,18 @@ await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(Path.Combine(a
await Inspect(database, "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY 1", "root_preferences db, all tables");
#endif

await foreach (var provider in GetGoogleDriveProvidersFromRegistryAsync())
var registryPath = App.AppModel.GoogleDrivePath;
if (!AddMyDriveToPathAndValidate(ref registryPath))
yield break;
yield return new CloudProvider(CloudProviders.GoogleDrive)
{

#if DEBUG
Debug.WriteLine("YIELD RETURNING from GoogleDriveCloudDetector#GetProviders (registry): ");
Debug.WriteLine($"name={provider.Name}; path={provider.SyncFolder}");
#endif

yield return provider;
}
Name = "Google Drive",
SyncFolder = registryPath,
IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null
};
}

private async Task Inspect(SqliteConnection database, string sqlCommand, string contentsOf)
private static async Task Inspect(SqliteConnection database, string sqlCommand, string contentsOf)
{
await using var cmdTablesAll = new SqliteCommand(sqlCommand, database);
var reader = await cmdTablesAll.ExecuteReaderAsync();
Expand Down Expand Up @@ -194,12 +180,12 @@ private async Task Inspect(SqliteConnection database, string sqlCommand, string
return googleDriveRegValueJson;
}

public static async IAsyncEnumerable<ICloudProvider> GetGoogleDriveProvidersFromRegistryAsync(bool addMyDriveToPath = true)
public static string? GetRegistryBasePath()
{
var googleDriveRegValJson = GetGoogleDriveRegValJson();

if (googleDriveRegValJson is null)
yield break;
return null;

var googleDriveRegValJsonProperty = googleDriveRegValJson
.RootElement.EnumerateObject()
Expand All @@ -210,46 +196,72 @@ public static async IAsyncEnumerable<ICloudProvider> GetGoogleDriveProvidersFrom
if (googleDriveRegValJsonProperty.Value.ValueKind == JsonValueKind.Undefined)
{
_logger.LogWarning($"Root element of Google Drive registry value for value name '{_googleDriveRegValName}' was empty.");
yield break;
return null;
}

#if DEBUG
Debug.WriteLine("REGISTRY LOGGING");
Debug.WriteLine(googleDriveRegValJsonProperty.ToString());
#endif

foreach (var item in googleDriveRegValJsonProperty.Value.EnumerateArray())
var item = googleDriveRegValJsonProperty.Value.EnumerateArray().FirstOrDefault();
if (item.ValueKind == JsonValueKind.Undefined)
{
if (!item.TryGetProperty(_googleDriveRegValPropName, out var googleDriveRegValProp))
continue;
_logger.LogWarning($"Array in the root element of Google Drive registry value for value name '{_googleDriveRegValName}' was empty.");
return null;
}

if (!googleDriveRegValProp.TryGetProperty(_googleDriveRegValPropPropName, out var googleDriveRegValPropProp))
continue;
if (!item.TryGetProperty(_googleDriveRegValPropName, out var googleDriveRegValProp))
{
_logger.LogWarning($"First element in the Google Drive Registry Root Array did not have property named {_googleDriveRegValPropName}");
return null;
}

var path = googleDriveRegValPropProp.GetString();
if (path is null)
continue;
if (!googleDriveRegValProp.TryGetProperty(_googleDriveRegValPropPropName, out var googleDriveRegValPropProp))
{
_logger.LogWarning($"Value from {_googleDriveRegValPropName} did not have property named {_googleDriveRegValPropPropName}");
return null;
}

if (!AddMyDriveToPathAndValidate(ref path, addMyDriveToPath))
{
_logger.LogWarning($"Validation failed for {path} (registry)");
continue;
}
var path = googleDriveRegValPropProp.GetString();
if (path is not null)
return ConvertDriveLetterToPathAndValidate(ref path) ? path : null;

App.AppModel.GoogleDrivePath = path;
#if DEBUG
Debug.WriteLine($"In GDCD in registry: App.AppModel.GoogleDrivePath being set to: {path}");
#endif
_logger.LogWarning($"Could not get string from value from {_googleDriveRegValPropPropName}");
return null;
}

var iconFile = await GetGoogleDriveIconFileAsync();
/// <summary>
/// If Google Drive is mounted as a drive, then the path found in the registry will be
/// *just* the drive letter (e.g. just "G" as opposed to "G:\"), and therefore must be
/// reformatted as a valid path.
/// </summary>
private static bool ConvertDriveLetterToPathAndValidate(ref string path)
{
if (path.Length > 1)
return ValidatePath(path);

yield return new CloudProvider(CloudProviders.GoogleDrive)
{
Name = "Google Drive",
SyncFolder = path,
IconData = iconFile is not null ? await iconFile.ToByteArrayAsync() : null,
};
DriveInfo driveInfo;
try
{
driveInfo = new DriveInfo(path);
}
catch (ArgumentException e)
{
_logger.LogWarning(e, $"Could not resolve drive letter '{path}' to a valid drive.");
return false;
}

path = driveInfo.RootDirectory.Name;
return true;
}

private static bool ValidatePath(string path)
{
if (Directory.Exists(path))
return true;
_logger.LogWarning($"Invalid path: {path}");
return false;
}

private static async Task<StorageFile?> GetGoogleDriveIconFileAsync()
Expand All @@ -264,56 +276,30 @@ public static async IAsyncEnumerable<ICloudProvider> GetGoogleDriveProvidersFrom
return await FilesystemTasks.Wrap(() => StorageFile.GetFileFromPathAsync(iconPath).AsTask());
}

private static bool AddMyDriveToPathAndValidate(ref string path, bool addMyDrive = true)
{
// If Google Drive is mounted as a drive, then the path found in the registry will be
// *just* the drive letter (e.g. just "G" as opposed to "G:\"), and therefore must be
// reformatted as a valid path.
if (path.Length == 1)
{
DriveInfo temp;
try
{
temp = new DriveInfo(path);
}
catch (ArgumentException e)
{
_logger.LogWarning(e, $"Could not resolve drive letter '{path}' to a valid drive.");
return false;
}

path = temp.RootDirectory.Name;
}

if (addMyDrive)
{
// If `path` contains a shortcut named "My Drive", store its target in `shellFolderBaseFirst`.
// This happens when "My Drive syncing options" is set to "Mirror files".
// TODO: Avoid to use Vanara (#15000)
using var shellFolderBase = ShellFolderExtensions.GetShellItemFromPathOrPIDL(path) as ShellFolder;
var shellFolderBaseFirst = Environment.ExpandEnvironmentVariables((
shellFolderBase?.FirstOrDefault(si =>
si.Name?.Equals("My Drive") ?? false) as ShellLink)?.TargetPath
?? string.Empty);
private static bool AddMyDriveToPathAndValidate(ref string path)
{
// If `path` contains a shortcut named "My Drive", store its target in `shellFolderBaseFirst`.
// This happens when "My Drive syncing options" is set to "Mirror files".
// TODO: Avoid to use Vanara (#15000)
using var rootFolder = ShellFolderExtensions.GetShellItemFromPathOrPIDL(path) as ShellFolder;
var myDriveFolder = Environment.ExpandEnvironmentVariables((
rootFolder?.FirstOrDefault(si =>
si.Name?.Equals("My Drive") ?? false) as ShellLink)?.TargetPath
?? string.Empty);

#if DEBUG
Debug.WriteLine("INVALID PATHS LOGGER");
shellFolderBase?.ForEach(si => Debug.WriteLine(si.Name));
Debug.WriteLine("SHELL FOLDER LOGGING");
rootFolder?.ForEach(si => Debug.WriteLine(si.Name));
#endif

if (!string.IsNullOrEmpty(shellFolderBaseFirst))
{
path = shellFolderBaseFirst;
return true;
}

path = Path.Combine(path, "My Drive");
if (!string.IsNullOrEmpty(myDriveFolder))
{
path = myDriveFolder;
return true;
}

if (Directory.Exists(path))
return true;
_logger.LogWarning($"Invalid Google Drive mount point path: {path}");
return false;
path = Path.Combine(path, "My Drive");
return ValidatePath(path);
}
}
}

0 comments on commit c802626

Please sign in to comment.