From 257eca4aed229671042166b2aa91cc76968d8c8c Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Tue, 14 Mar 2023 10:43:22 +0100 Subject: [PATCH 01/14] Change isolatedstorageroot for mobile --- .../System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs index 69a2f70677908..3b4751cc6956f 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs @@ -7,7 +7,7 @@ public sealed partial class IsolatedStorageFile : IsolatedStorage, IDisposable { private string GetIsolatedStorageRoot() { - return Helper.GetRootDirectory(Scope); + return Helper.GetDataDirectory(Scope); } } } From e7f18164713bef5956dfb8691633e0147e9d15e1 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Tue, 14 Mar 2023 11:02:02 +0100 Subject: [PATCH 02/14] whitespace fix --- .../System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs index 3b4751cc6956f..45ab6927fa6cf 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs @@ -7,7 +7,7 @@ public sealed partial class IsolatedStorageFile : IsolatedStorage, IDisposable { private string GetIsolatedStorageRoot() { - return Helper.GetDataDirectory(Scope); + return Helper.GetDataDirectory(Scope); } } } From b41f38b91eb995630bf048942525e97c06ef78dc Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 15 Mar 2023 11:36:14 +0100 Subject: [PATCH 03/14] Updated getting isolated storage path for mobile --- .../IO/IsolatedStorage/Helper.AnyMobile.cs | 7 +++ .../IO/IsolatedStorage/Helper.NonMobile.cs | 32 ++++++++++++ .../IO/IsolatedStorage/Helper.Win32Unix.cs | 32 ------------ .../IsolatedStorageFile.AnyMobile.cs | 2 +- .../System.IO.IsolatedStorage.Tests.csproj | 2 + .../IsolatedStorage/HelperTests.AnyMobile.cs | 52 +++++++++++++++++++ .../IsolatedStorage/HelperTests.NonMobile.cs | 23 ++++++++ .../IsolatedStorage/HelperTests.Win32Unix.cs | 13 ----- 8 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs create mode 100644 src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.NonMobile.cs diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index 4121814515fe6..f5c7dafb039cb 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -6,5 +6,12 @@ namespace System.IO.IsolatedStorage internal static partial class Helper { public const string IsolatedStorageDirectoryName = ".isolated-storage"; + + internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) + { + string? randomDirectory = GetExistingRandomDirectory(rootDirectory); + + return randomDirectory ?? GetDataDirectory(scope); + } } } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs index cde27b6c5e27c..a3711d3a34a52 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs @@ -6,5 +6,37 @@ namespace System.IO.IsolatedStorage internal static partial class Helper { public const string IsolatedStorageDirectoryName = "IsolatedStorage"; + + internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) + { + string? randomDirectory = GetExistingRandomDirectory(rootDirectory); + if (string.IsNullOrEmpty(randomDirectory)) + { + using (Mutex m = CreateMutexNotOwned(rootDirectory)) + { + if (!m.WaitOne()) + { + throw new IsolatedStorageException(SR.IsolatedStorage_Init); + } + + try + { + randomDirectory = GetExistingRandomDirectory(rootDirectory); + if (string.IsNullOrEmpty(randomDirectory)) + { + // Someone else hasn't created the directory before we took the lock + randomDirectory = Path.Combine(rootDirectory, Path.GetRandomFileName(), Path.GetRandomFileName()); + CreateDirectory(randomDirectory, scope); + } + } + finally + { + m.ReleaseMutex(); + } + } + } + + return randomDirectory; + } } } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs index 876b9e11ce189..cc53f08ba967d 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs @@ -79,38 +79,6 @@ internal static void GetDefaultIdentityAndHash(out object identity, out string h identity = locationUri; } - internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) - { - string? randomDirectory = GetExistingRandomDirectory(rootDirectory); - if (string.IsNullOrEmpty(randomDirectory)) - { - using (Mutex m = CreateMutexNotOwned(rootDirectory)) - { - if (!m.WaitOne()) - { - throw new IsolatedStorageException(SR.IsolatedStorage_Init); - } - - try - { - randomDirectory = GetExistingRandomDirectory(rootDirectory); - if (string.IsNullOrEmpty(randomDirectory)) - { - // Someone else hasn't created the directory before we took the lock - randomDirectory = Path.Combine(rootDirectory, Path.GetRandomFileName(), Path.GetRandomFileName()); - CreateDirectory(randomDirectory, scope); - } - } - finally - { - m.ReleaseMutex(); - } - } - } - - return randomDirectory; - } - internal static string? GetExistingRandomDirectory(string rootDirectory) { // Look for an existing random directory at the given root diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs index 45ab6927fa6cf..69a2f70677908 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs @@ -7,7 +7,7 @@ public sealed partial class IsolatedStorageFile : IsolatedStorage, IDisposable { private string GetIsolatedStorageRoot() { - return Helper.GetDataDirectory(Scope); + return Helper.GetRootDirectory(Scope); } } } diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj b/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj index 8bfc91c56f7d7..da853516067ee 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj +++ b/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj @@ -56,10 +56,12 @@ + + diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs new file mode 100644 index 0000000000000..217a13ea66259 --- /dev/null +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.IsolatedStorage +{ + public partial class HelperTests + { + [Theory, InlineData(IsolatedStorageScope.User)] + public void GetRandomDirectory(IsolatedStorageScope scope) + { + using (var temp = new TempDirectory()) + { + string randomDir = Helper.GetRandomDirectory(temp.Path, scope); + Assert.True(Directory.Exists(randomDir.Replace(Helper.IsolatedStorageDirectoryName, ""))); + } + } + + [Theory, + InlineData(IsolatedStorageScope.Assembly), + InlineData(IsolatedStorageScope.Assembly | IsolatedStorageScope.Roaming), + InlineData(IsolatedStorageScope.User) + ] + public void GetRandomDirectoryWithExistingDir(IsolatedStorageScope scope) + { + using (var temp = new TempDirectory()) + { + Assert.Null(Helper.GetExistingRandomDirectory(temp.Path)); + + string randomPath = Path.Combine(temp.Path, Path.GetRandomFileName(), Path.GetRandomFileName()); + Directory.CreateDirectory(randomPath); + Assert.Equal(randomPath, Helper.GetExistingRandomDirectory(temp.Path)); + Assert.Equal(Helper.GetRandomDirectory(temp.Path, scope), Helper.GetExistingRandomDirectory(temp.Path)); + } + } + + [Theory, + InlineData(IsolatedStorageScope.Assembly), + InlineData(IsolatedStorageScope.Assembly | IsolatedStorageScope.Roaming), + InlineData(IsolatedStorageScope.User) + ] + public void GetRandomDirectoryWithNotExistingDir(IsolatedStorageScope scope) + { + using (var temp = new TempDirectory()) + { + Assert.Null(Helper.GetExistingRandomDirectory(temp.Path)); + Assert.Equal(Helper.GetRandomDirectory(temp.Path, scope), Helper.GetDataDirectory(scope)); + } + } + } +} diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.NonMobile.cs new file mode 100644 index 0000000000000..d6e58517964a0 --- /dev/null +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.NonMobile.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.IO.IsolatedStorage +{ + public partial class HelperTests + { + [Theory, + InlineData(IsolatedStorageScope.User), + InlineData(IsolatedStorageScope.Machine), + ] + public void GetRandomDirectory(IsolatedStorageScope scope) + { + using (var temp = new TempDirectory()) + { + string randomDir = Helper.GetRandomDirectory(temp.Path, scope); + Assert.True(Directory.Exists(randomDir)); + } + } + } +} diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.Win32Unix.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.Win32Unix.cs index 649de0639b5cb..78dab06717ec0 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.Win32Unix.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.Win32Unix.cs @@ -19,18 +19,5 @@ public void GetExistingRandomDirectory() Assert.Equal(randomPath, Helper.GetExistingRandomDirectory(temp.Path)); } } - - [Theory, - InlineData(IsolatedStorageScope.User), - InlineData(IsolatedStorageScope.Machine), - ] - public void GetRandomDirectory(IsolatedStorageScope scope) - { - using (var temp = new TempDirectory()) - { - string randomDir = Helper.GetRandomDirectory(temp.Path, scope); - Assert.True(Directory.Exists(randomDir)); - } - } } } From 22e3e92cc792602c60923fd59e5f26f0ea2055cf Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 15 Mar 2023 11:54:15 +0100 Subject: [PATCH 04/14] added missing namespace --- .../src/System/IO/IsolatedStorage/Helper.NonMobile.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs index a3711d3a34a52..4682836a1658e 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Threading; namespace System.IO.IsolatedStorage { From 4d6d9e5c508b3bd89704484fb66b9039490d0a6a Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 15 Mar 2023 16:42:57 +0100 Subject: [PATCH 05/14] refactored GetRandomDirectory --- .../src/System/IO/IsolatedStorage/Helper.AnyMobile.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index f5c7dafb039cb..8fa92d842e7cb 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -9,9 +9,7 @@ internal static partial class Helper internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) { - string? randomDirectory = GetExistingRandomDirectory(rootDirectory); - - return randomDirectory ?? GetDataDirectory(scope); + return GetExistingRandomDirectory(rootDirectory) ?? GetDataDirectory(scope); } } } From a420ae1feb30a140bf1fb3d5e709aafdbbc5e1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 16 Mar 2023 13:45:34 +0100 Subject: [PATCH 06/14] Add comment and fix build --- .../src/System/IO/IsolatedStorage/Helper.AnyMobile.cs | 9 ++++++++- .../src/System/IO/IsolatedStorage/Helper.cs | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index 8fa92d842e7cb..177218fe69722 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -5,11 +5,18 @@ namespace System.IO.IsolatedStorage { internal static partial class Helper { + // we're using a different directory name for compatibility with legacy Xamarin public const string IsolatedStorageDirectoryName = ".isolated-storage"; internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) { - return GetExistingRandomDirectory(rootDirectory) ?? GetDataDirectory(scope); + // In legacy Xamarin we didn't have a random directory inside of the isolated storage root for each app, + // we tried to preserve that in https://github.com/dotnet/runtime/pull/75541 but the fix wasn't complete enough + // and we still created random directories when not using the Roaming scope. + // + // Since we shipped that behavior as part of .NET 7 we can't change this now or upgraded apps wouldn't find their files anymore. + // We need to look for an existing random directory first before using the plain root directory. + return GetExistingRandomDirectory(rootDirectory) ?? rootDirectory; } } } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs index 984eeed404606..7cf558ef38b79 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs @@ -11,8 +11,8 @@ internal static partial class Helper /// /// The full root directory is the relevant special folder from Environment.GetFolderPath() plus IsolatedStorageDirectoryName - /// and a set of random directory names if not roaming. (The random directories aren't created for WinRT as - /// the FolderPath locations for WinRT are app isolated already.) + /// and a set of random directory names if not roaming. (The random directories aren't created for Android/iOS as + /// the FolderPath locations for Android/iOS are app isolated already.) /// /// Examples: /// From 9a062e8c2134e9e29ac0a3b70cd719b426774015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 16 Mar 2023 14:35:05 +0100 Subject: [PATCH 07/14] Ignore unused scope parameter --- .../src/System/IO/IsolatedStorage/Helper.AnyMobile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index 177218fe69722..c882183013d2a 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -8,7 +8,7 @@ internal static partial class Helper // we're using a different directory name for compatibility with legacy Xamarin public const string IsolatedStorageDirectoryName = ".isolated-storage"; - internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) + internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope _) { // In legacy Xamarin we didn't have a random directory inside of the isolated storage root for each app, // we tried to preserve that in https://github.com/dotnet/runtime/pull/75541 but the fix wasn't complete enough From 887571ff0899820d1cd33f3d08fe28297bfbd40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 16 Mar 2023 15:33:05 +0100 Subject: [PATCH 08/14] Simplify code and keep common methods in Helper.cs --- .../src/System.IO.IsolatedStorage.csproj | 2 - .../IO/IsolatedStorage/Helper.NonMobile.cs | 6 + .../IO/IsolatedStorage/Helper.Win32Unix.cs | 115 ------------------ .../src/System/IO/IsolatedStorage/Helper.cs | 102 ++++++++++++++++ .../IO/IsolatedStorage/IsolatedStorageFile.cs | 1 - 5 files changed, 108 insertions(+), 118 deletions(-) delete mode 100644 src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs diff --git a/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj b/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj index e9502a92ac76e..a2825d0882479 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj +++ b/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj @@ -14,7 +14,6 @@ - @@ -35,7 +34,6 @@ - diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs index 4682836a1658e..c8552b54552f8 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + using System.Threading; namespace System.IO.IsolatedStorage @@ -39,5 +40,10 @@ internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageS return randomDirectory; } + + private static Mutex CreateMutexNotOwned(string pathName) + { + return new Mutex(initiallyOwned: false, name: @"Global\" + IdentityHelper.GetStrongHashSuitableForObjectName(pathName)); + } } } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs deleted file mode 100644 index cc53f08ba967d..0000000000000 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Security; -using System.Threading; - -namespace System.IO.IsolatedStorage -{ - internal static partial class Helper - { - internal static string GetDataDirectory(IsolatedStorageScope scope) - { - // This is the relevant special folder for the given scope plus IsolatedStorageDirectoryName. - // It is meant to replicate the behavior of the VM ComIsolatedStorage::GetRootDir(). - - // (note that Silverlight used "CoreIsolatedStorage" for a directory name and did not support machine scope) - - Environment.SpecialFolder specialFolder = - IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : // e.g. C:\ProgramData - IsRoaming(scope) ? Environment.SpecialFolder.ApplicationData : // e.g. C:\Users\Joe\AppData\Roaming - Environment.SpecialFolder.LocalApplicationData; // e.g. C:\Users\Joe\AppData\Local - - string dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); - dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); - - return dataDirectory; - } - - [UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", - Justification = "Code handles single-file deployment by using the information of the .exe file")] - internal static void GetDefaultIdentityAndHash(out object identity, out string hash, char separator) - { - // In .NET Framework IsolatedStorage uses identity from System.Security.Policy.Evidence to build - // the folder structure on disk. It would use the "best" available evidence in this order: - // - // 1. Publisher (Authenticode) - // 2. StrongName - // 3. Url (CodeBase) - // 4. Site - // 5. Zone - // - // For .NET Core StrongName and Url are the only relevant types. By default evidence for the Domain comes - // from the Assembly which comes from the EntryAssembly(). We'll emulate the legacy default behavior - // by pulling directly from EntryAssembly. - // - // Note that it is possible that there won't be an EntryAssembly, which is something the .NET Framework doesn't - // have to deal with and isn't likely on .NET Core due to a single AppDomain. The exception is Android which - // doesn't set an EntryAssembly. - - Assembly? assembly = Assembly.GetEntryAssembly(); - string? location = null; - - if (assembly != null) - { - AssemblyName assemblyName = assembly.GetName(); - - hash = IdentityHelper.GetNormalizedStrongNameHash(assemblyName)!; - if (hash != null) - { - hash = string.Concat("StrongName", new ReadOnlySpan(in separator), hash); - identity = assemblyName; - return; - } - else - { - location = assembly.Location; - } - } - - // In case of SingleFile deployment, Assembly.Location is empty. On Android there is no entry assembly. - if (string.IsNullOrEmpty(location)) - location = Environment.ProcessPath; - if (string.IsNullOrEmpty(location)) - throw new IsolatedStorageException(SR.IsolatedStorage_Init); - Uri locationUri = new Uri(location); - hash = string.Concat("Url", new ReadOnlySpan(in separator), IdentityHelper.GetNormalizedUriHash(locationUri)); - identity = locationUri; - } - - internal static string? GetExistingRandomDirectory(string rootDirectory) - { - // Look for an existing random directory at the given root - // (a set of nested directories that were created via Path.GetRandomFileName()) - - // Older versions of the .NET Framework created longer (24 character) random paths and would - // migrate them if they could not find the new style directory. - - if (!Directory.Exists(rootDirectory)) - return null; - - foreach (string directory in Directory.GetDirectories(rootDirectory)) - { - if (Path.GetFileName(directory)?.Length == 12) - { - foreach (string subdirectory in Directory.GetDirectories(directory)) - { - if (Path.GetFileName(subdirectory)?.Length == 12) - { - return subdirectory; - } - } - } - } - - return null; - } - - private static Mutex CreateMutexNotOwned(string pathName) - { - return new Mutex(initiallyOwned: false, name: @"Global\" + IdentityHelper.GetStrongHashSuitableForObjectName(pathName)); - } - } -} diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs index 7cf558ef38b79..aa8082bc87297 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs @@ -1,6 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Security; +using System.Threading; + namespace System.IO.IsolatedStorage { internal static partial class Helper @@ -50,6 +55,103 @@ internal static string GetRootDirectory(IsolatedStorageScope scope) return s_userRootDirectory; } + internal static string GetDataDirectory(IsolatedStorageScope scope) + { + // This is the relevant special folder for the given scope plus IsolatedStorageDirectoryName. + // It is meant to replicate the behavior of the VM ComIsolatedStorage::GetRootDir(). + + // (note that Silverlight used "CoreIsolatedStorage" for a directory name and did not support machine scope) + + Environment.SpecialFolder specialFolder = + IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : // e.g. C:\ProgramData + IsRoaming(scope) ? Environment.SpecialFolder.ApplicationData : // e.g. C:\Users\Joe\AppData\Roaming + Environment.SpecialFolder.LocalApplicationData; // e.g. C:\Users\Joe\AppData\Local + + string dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); + dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); + + return dataDirectory; + } + + internal static string? GetExistingRandomDirectory(string rootDirectory) + { + // Look for an existing random directory at the given root + // (a set of nested directories that were created via Path.GetRandomFileName()) + + // Older versions of the .NET Framework created longer (24 character) random paths and would + // migrate them if they could not find the new style directory. + + if (!Directory.Exists(rootDirectory)) + return null; + + foreach (string directory in Directory.GetDirectories(rootDirectory)) + { + if (Path.GetFileName(directory)?.Length == 12) + { + foreach (string subdirectory in Directory.GetDirectories(directory)) + { + if (Path.GetFileName(subdirectory)?.Length == 12) + { + return subdirectory; + } + } + } + } + + return null; + } + + [UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", + Justification = "Code handles single-file deployment by using the information of the .exe file")] + internal static void GetDefaultIdentityAndHash(out object identity, out string hash, char separator) + { + // In .NET Framework IsolatedStorage uses identity from System.Security.Policy.Evidence to build + // the folder structure on disk. It would use the "best" available evidence in this order: + // + // 1. Publisher (Authenticode) + // 2. StrongName + // 3. Url (CodeBase) + // 4. Site + // 5. Zone + // + // For .NET Core StrongName and Url are the only relevant types. By default evidence for the Domain comes + // from the Assembly which comes from the EntryAssembly(). We'll emulate the legacy default behavior + // by pulling directly from EntryAssembly. + // + // Note that it is possible that there won't be an EntryAssembly, which is something the .NET Framework doesn't + // have to deal with and isn't likely on .NET Core due to a single AppDomain. The exception is Android which + // doesn't set an EntryAssembly. + + Assembly? assembly = Assembly.GetEntryAssembly(); + string? location = null; + + if (assembly != null) + { + AssemblyName assemblyName = assembly.GetName(); + + hash = IdentityHelper.GetNormalizedStrongNameHash(assemblyName)!; + if (hash != null) + { + hash = string.Concat("StrongName", new ReadOnlySpan(in separator), hash); + identity = assemblyName; + return; + } + else + { + location = assembly.Location; + } + } + + // In case of SingleFile deployment, Assembly.Location is empty. On Android there is no entry assembly. + if (string.IsNullOrEmpty(location)) + location = Environment.ProcessPath; + if (string.IsNullOrEmpty(location)) + throw new IsolatedStorageException(SR.IsolatedStorage_Init); + Uri locationUri = new Uri(location); + hash = string.Concat("Url", new ReadOnlySpan(in separator), IdentityHelper.GetNormalizedUriHash(locationUri)); + identity = locationUri; + } + internal static bool IsMachine(IsolatedStorageScope scope) => ((scope & IsolatedStorageScope.Machine) != 0); internal static bool IsAssembly(IsolatedStorageScope scope) => ((scope & IsolatedStorageScope.Assembly) != 0); internal static bool IsApplication(IsolatedStorageScope scope) => ((scope & IsolatedStorageScope.Application) != 0); diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs index 4afd6468ceaa3..7a2a31382fc44 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Text; namespace System.IO.IsolatedStorage From b5cb26029b903603fde1578fb2fc9b86b919c6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 16 Mar 2023 15:40:43 +0100 Subject: [PATCH 09/14] Fix build --- .../src/System/IO/IsolatedStorage/Helper.NonMobile.cs | 1 + .../tests/System.IO.IsolatedStorage.Tests.csproj | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs index c8552b54552f8..ef658da4a2079 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Threading; +using System.Security; namespace System.IO.IsolatedStorage { diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj b/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj index da853516067ee..b14e54de41757 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj +++ b/src/libraries/System.IO.IsolatedStorage/tests/System.IO.IsolatedStorage.Tests.csproj @@ -9,8 +9,6 @@ - From c8093bec170d390951493a59945596b87fae8230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Thu, 16 Mar 2023 16:16:11 +0100 Subject: [PATCH 10/14] Fix build --- .../src/System.IO.IsolatedStorage.csproj | 1 + .../src/System/IO/IsolatedStorage/IsolatedStorageFile.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj b/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj index a2825d0882479..3fa27c2f432f2 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj +++ b/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj @@ -34,6 +34,7 @@ + diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs index 7a2a31382fc44..4afd6468ceaa3 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Text; namespace System.IO.IsolatedStorage From 1b0c5aeaa9078a096081ebe1d0924f08bafab64b Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Fri, 24 Mar 2023 13:50:53 +0100 Subject: [PATCH 11/14] Updated getting isolated storage path for mobile --- .../IO/IsolatedStorage/Helper.AnyMobile.cs | 39 +++++++++++++ .../IO/IsolatedStorage/Helper.NonMobile.cs | 18 ++++++ .../src/System/IO/IsolatedStorage/Helper.cs | 22 +------ .../IsolatedStorageFile.AnyMobile.cs | 50 ++++++++++++++++ .../IsolatedStorageFile.NonMobile.cs | 58 +++++++++++++++++++ .../IO/IsolatedStorage/IsolatedStorageFile.cs | 55 +----------------- .../ContainsUnknownFilesTests.cs | 1 + .../IO/IsolatedStorage/FileExistsTests.cs | 2 +- .../IO/IsolatedStorage/GetStoreTests.cs | 4 ++ .../IsolatedStorage/HelperTests.AnyMobile.cs | 42 +++++++++++++- .../IsolatedStorage/TestHelper.AnyMobile.cs | 10 +++- .../IsolatedStorage/TestHelper.NonMobile.cs | 8 +++ .../System/IO/IsolatedStorage/TestHelper.cs | 7 --- 13 files changed, 230 insertions(+), 86 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index c882183013d2a..16302df748884 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -8,6 +8,45 @@ internal static partial class Helper // we're using a different directory name for compatibility with legacy Xamarin public const string IsolatedStorageDirectoryName = ".isolated-storage"; + internal static string GetDataDirectory(IsolatedStorageScope scope) + { + // This is the relevant special folder for the given scope plus IsolatedStorageDirectoryName. + // It is meant to replicate the behavior of the VM ComIsolatedStorage::GetRootDir(). + // + // In legacy Xamarin for Roaming Scope we were using Environment.SpecialFolder.LocalApplicationData + // In .Net 7 for Roaming Scope we are using Environment.SpecialFolder.ApplicationData + // e.g. .Net 7 path = /data/user/0/{packageName}/files/.isolated-storage/{hash}/{hash}/AppFiles/ + // e.g. Xamarin path = /data/user/0/{packageName}/files/.config/.isolated-storage" + // + // Since we shipped that behavior as part of .NET 7 we can't change this now or upgraded apps wouldn't find their files anymore. + // We need to look for an existing directory first before using the legacy Xamarin approach. + + Environment.SpecialFolder specialFolder = + IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : // e.g. /usr/share; + IsRoaming(scope) ? Environment.SpecialFolder.ApplicationData : // e.g. /data/user/0/{packageName}/files/.config; + Environment.SpecialFolder.LocalApplicationData; // e.g. /data/user/0/{packageName}/files; + + string dataDirectory = Environment.GetFolderPath(specialFolder); + dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); + if (Directory.Exists(dataDirectory)) + { + return dataDirectory; + } + // Otherwise return legacy xamarin path + else + { + specialFolder = + IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : + IsRoaming(scope) ? Environment.SpecialFolder.LocalApplicationData: + Environment.SpecialFolder.ApplicationData; + + dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); + dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); + } + + return dataDirectory; + } + internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope _) { // In legacy Xamarin we didn't have a random directory inside of the isolated storage root for each app, diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs index ef658da4a2079..111281734cc08 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.NonMobile.cs @@ -10,6 +10,24 @@ internal static partial class Helper { public const string IsolatedStorageDirectoryName = "IsolatedStorage"; + internal static string GetDataDirectory(IsolatedStorageScope scope) + { + // This is the relevant special folder for the given scope plus IsolatedStorageDirectoryName. + // It is meant to replicate the behavior of the VM ComIsolatedStorage::GetRootDir(). + + // (note that Silverlight used "CoreIsolatedStorage" for a directory name and did not support machine scope) + + Environment.SpecialFolder specialFolder = + IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : // e.g. C:\ProgramData + IsRoaming(scope) ? Environment.SpecialFolder.ApplicationData : // e.g. C:\Users\Joe\AppData\Roaming + Environment.SpecialFolder.LocalApplicationData; // e.g. C:\Users\Joe\AppData\Local + + string dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); + dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); + + return dataDirectory; + } + internal static string GetRandomDirectory(string rootDirectory, IsolatedStorageScope scope) { string? randomDirectory = GetExistingRandomDirectory(rootDirectory); diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs index aa8082bc87297..7e15f775bb949 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.cs @@ -24,8 +24,8 @@ internal static partial class Helper /// User: @"C:\Users\jerem\AppData\Local\IsolatedStorage\10v31ho4.bo2\eeolfu22.f2w\" /// User|Roaming: @"C:\Users\jerem\AppData\Roaming\IsolatedStorage\" /// Machine: @"C:\ProgramData\IsolatedStorage\nin03cyc.wr0\o3j0urs3.0sn\" - /// Android path: "/data/user/0/net.dot.System.IO.IsolatedStorage.Tests/files/.config/.isolated-storage/" - /// iOS path: "/var/mobile/Containers/Data/Application/A323CBB9-A2B3-4432-9449-48CC20C07A7D/Documents/.config/.isolated-storage/" + /// Android path: "/data/user/0/{packageName}/files/.config/.isolated-storage" + /// iOS path: "/var/mobile/Containers/Data/Application/A323CBB9-A2B3-4432-9449-48CC20C07A7D/Documents/.config/.isolated-storage" /// /// Identity for the current store gets tacked on after this. /// @@ -55,24 +55,6 @@ internal static string GetRootDirectory(IsolatedStorageScope scope) return s_userRootDirectory; } - internal static string GetDataDirectory(IsolatedStorageScope scope) - { - // This is the relevant special folder for the given scope plus IsolatedStorageDirectoryName. - // It is meant to replicate the behavior of the VM ComIsolatedStorage::GetRootDir(). - - // (note that Silverlight used "CoreIsolatedStorage" for a directory name and did not support machine scope) - - Environment.SpecialFolder specialFolder = - IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : // e.g. C:\ProgramData - IsRoaming(scope) ? Environment.SpecialFolder.ApplicationData : // e.g. C:\Users\Joe\AppData\Roaming - Environment.SpecialFolder.LocalApplicationData; // e.g. C:\Users\Joe\AppData\Local - - string dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); - dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); - - return dataDirectory; - } - internal static string? GetExistingRandomDirectory(string rootDirectory) { // Look for an existing random directory at the given root diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs index 69a2f70677908..ffc29dff75573 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.AnyMobile.cs @@ -1,13 +1,63 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; + namespace System.IO.IsolatedStorage { public sealed partial class IsolatedStorageFile : IsolatedStorage, IDisposable { + internal IsolatedStorageFile(IsolatedStorageScope scope) + { + // In legacy Xamarin root ends with "/.isolated-storage" + // In .Net 7 we added at the end "/AppFiles" or "/Files" or "/AssemFiles/". + // e.g. .Net 7 path = /data/user/0/{packageName}/files/.isolated-storage/{hash}/{hash}/AppFiles/ + // e.g. Xamarin path = /data/user/0/{packageName}/files/.config/.isolated-storage" + // + // Since we shipped that behavior as part of .NET 7 we can't change this now or upgraded apps wouldn't find their files anymore. + // We need to look for an existing directory first before using the legacy Xamarin approach. + + InitStore(scope, null, null); + + StringBuilder sb = new StringBuilder(GetIsolatedStorageRoot()); + + string directoryPath = sb.ToString() + SeparatorExternal; + if (Helper.IsApplication(scope) && Directory.Exists(directoryPath + s_appFiles)) + { + sb.Append(SeparatorExternal); + sb.Append(s_appFiles); + sb.Append(SeparatorExternal); + } + else if (Helper.IsDomain(scope) && Directory.Exists(directoryPath + s_files)) + { + sb.Append(SeparatorExternal); + sb.Append(s_files); + sb.Append(SeparatorExternal); + } + else if (Directory.Exists(directoryPath + s_assemFiles)) + { + sb.Append(SeparatorExternal); + sb.Append(s_assemFiles); + sb.Append(SeparatorExternal); + } + + _rootDirectory = sb.ToString(); + Helper.CreateDirectory(_rootDirectory, scope); + } + private string GetIsolatedStorageRoot() { return Helper.GetRootDirectory(Scope); } + + private bool IsMatchingScopeDirectory(string _) + { + return (Helper.IsApplication(Scope)) || (Helper.IsAssembly(Scope)) || (Helper.IsDomain(Scope)); + } + + private string? GetParentDirectory() + { + return Path.GetDirectoryName(RootDirectory); + } } } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.NonMobile.cs index 4f547d55cff2f..57da068f49bb8 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.NonMobile.cs @@ -7,6 +7,49 @@ namespace System.IO.IsolatedStorage { public sealed partial class IsolatedStorageFile : IsolatedStorage, IDisposable { + // Data file notes + // =============== + + // "identity.dat" is the serialized identity object, such as StrongName or Url. It is used to + // enumerate stores, which we currently do not support. + // + // private const string IDFile = "identity.dat"; + + // "info.dat" is used to track disk space usage (against quota). The accounting file for Silverlight + // stores is "appInfo.dat". .NET Core is always in full trust so we can safely ignore these. + // + // private const string InfoFile = "info.dat"; + // private const string AppInfoFile = "appInfo.dat"; + + internal IsolatedStorageFile(IsolatedStorageScope scope) + { + // Evidence isn't currently available: https://github.com/dotnet/runtime/issues/18208 + // public static IsolatedStorageFile GetStore(IsolatedStorageScope scope, Evidence domainEvidence, Type domainEvidenceType, Evidence assemblyEvidence, Type assemblyEvidenceType) { return default(IsolatedStorageFile); } + + // InitStore will set up the IdentityHash + InitStore(scope, null, null); + + StringBuilder sb = new StringBuilder(GetIsolatedStorageRoot()); + sb.Append(SeparatorExternal); + + if (Helper.IsApplication(scope)) + { + sb.Append(s_appFiles); + } + else if (Helper.IsDomain(scope)) + { + sb.Append(s_files); + } + else + { + sb.Append(s_assemFiles); + } + sb.Append(SeparatorExternal); + + _rootDirectory = sb.ToString(); + Helper.CreateDirectory(_rootDirectory, scope); + } + private string GetIsolatedStorageRoot() { StringBuilder root = new StringBuilder(Helper.GetRootDirectory(Scope)); @@ -15,5 +58,20 @@ private string GetIsolatedStorageRoot() return root.ToString(); } + + private bool IsMatchingScopeDirectory(string directory) + { + string directoryName = Path.GetFileName(directory); + + return + (Helper.IsApplication(Scope) && string.Equals(directoryName, s_appFiles, StringComparison.Ordinal)) + || (Helper.IsAssembly(Scope) && string.Equals(directoryName, s_assemFiles, StringComparison.Ordinal)) + || (Helper.IsDomain(Scope) && string.Equals(directoryName, s_files, StringComparison.Ordinal)); + } + + private string? GetParentDirectory() + { + return Path.GetDirectoryName(RootDirectory.TrimEnd(Path.DirectorySeparatorChar)); + } } } diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs index 4afd6468ceaa3..c787c6939b72f 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/IsolatedStorageFile.cs @@ -21,49 +21,6 @@ public sealed partial class IsolatedStorageFile : IsolatedStorage, IDisposable private readonly object _internalLock = new object(); private readonly string _rootDirectory; - // Data file notes - // =============== - - // "identity.dat" is the serialized identity object, such as StrongName or Url. It is used to - // enumerate stores, which we currently do not support. - // - // private const string IDFile = "identity.dat"; - - // "info.dat" is used to track disk space usage (against quota). The accounting file for Silverlight - // stores is "appInfo.dat". .NET Core is always in full trust so we can safely ignore these. - // - // private const string InfoFile = "info.dat"; - // private const string AppInfoFile = "appInfo.dat"; - - internal IsolatedStorageFile(IsolatedStorageScope scope) - { - // Evidence isn't currently available: https://github.com/dotnet/runtime/issues/18208 - // public static IsolatedStorageFile GetStore(IsolatedStorageScope scope, Evidence domainEvidence, Type domainEvidenceType, Evidence assemblyEvidence, Type assemblyEvidenceType) { return default(IsolatedStorageFile); } - - // InitStore will set up the IdentityHash - InitStore(scope, null, null); - - StringBuilder sb = new StringBuilder(GetIsolatedStorageRoot()); - sb.Append(SeparatorExternal); - - if (Helper.IsApplication(scope)) - { - sb.Append(s_appFiles); - } - else if (Helper.IsDomain(scope)) - { - sb.Append(s_files); - } - else - { - sb.Append(s_assemFiles); - } - sb.Append(SeparatorExternal); - - _rootDirectory = sb.ToString(); - Helper.CreateDirectory(_rootDirectory, scope); - } - // Using this property to match .NET Framework for testing private string RootDirectory { @@ -648,7 +605,7 @@ public override void Remove() Close(); - string? parentDirectory = Path.GetDirectoryName(RootDirectory.TrimEnd(Path.DirectorySeparatorChar)); + string? parentDirectory = GetParentDirectory(); Debug.Assert(parentDirectory != null); if (ContainsUnknownFiles(parentDirectory)) @@ -756,16 +713,6 @@ private bool ContainsUnknownFiles(string directory) ); } - private bool IsMatchingScopeDirectory(string directory) - { - string directoryName = Path.GetFileName(directory); - - return - (Helper.IsApplication(Scope) && string.Equals(directoryName, s_appFiles, StringComparison.Ordinal)) - || (Helper.IsAssembly(Scope) && string.Equals(directoryName, s_assemFiles, StringComparison.Ordinal)) - || (Helper.IsDomain(Scope) && string.Equals(directoryName, s_files, StringComparison.Ordinal)); - } - private static bool IsIdFile(string file) => string.Equals(Path.GetFileName(file), "identity.dat"); private static bool IsInfoFile(string file) => string.Equals(Path.GetFileName(file), "info.dat"); diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/ContainsUnknownFilesTests.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/ContainsUnknownFilesTests.cs index 97581529f6b7d..8c1980ffcf99d 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/ContainsUnknownFilesTests.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/ContainsUnknownFilesTests.cs @@ -64,6 +64,7 @@ public void ContainsUnknownFiles_NotOkFiles(PresetScopes scope) } [Theory, MemberData(nameof(ValidStores))] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "For mobile platforms root ends with /.isolated-storage/")] public void ContainsUnknownFiles_NotOkDirectory(PresetScopes scope) { TestHelper.WipeStores(); diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs index 23df212b1eb59..d257b086ad63d 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/FileExistsTests.cs @@ -63,7 +63,7 @@ public void FileExists_Existence(PresetScopes scope) using (var isf = GetPresetScope(scope)) { string root = isf.GetUserRootDirectory(); - string file = "FileExists_Existence"; + string file = scope.ToString() + "FileExists_Existence"; isf.CreateTestFile(file); Assert.True(File.Exists(Path.Combine(root, file)), "exists per file.io where expected"); diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetStoreTests.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetStoreTests.cs index a4a9582ea6b69..b841fd59c4112 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetStoreTests.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/GetStoreTests.cs @@ -48,6 +48,7 @@ public void GetUserStoreForSite_ThrowsNotSupported() } [Fact] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "For mobile platforms root ends with /.isolated-storage")] public void GetUserStoreForApplication() { var isf = IsolatedStorageFile.GetUserStoreForApplication(); @@ -62,6 +63,7 @@ private void VerifyApplicationStore(IsolatedStorageFile isf) } [Fact] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "For mobile platforms root ends with /.isolated-storage")] public void GetUserStoreForAssembly() { var isf = IsolatedStorageFile.GetUserStoreForAssembly(); @@ -71,6 +73,7 @@ public void GetUserStoreForAssembly() } [Fact] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "For mobile platforms root ends with /.isolated-storage")] public void GetUserStoreForDomain() { var isf = IsolatedStorageFile.GetUserStoreForDomain(); @@ -89,6 +92,7 @@ public void GetStore_ThrowsPlatformNotSupported() } [Fact] + [SkipOnPlatform(TestPlatforms.Android | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "For mobile platforms root ends with /.isolated-storage")] public void GetStore_NullParamsAllowed() { VerifyApplicationStore(IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Application, (Type)null)); diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs index 217a13ea66259..baca9b5294b1c 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/HelperTests.AnyMobile.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; +using System.Reflection; +using System.Text; namespace System.IO.IsolatedStorage { @@ -15,7 +17,7 @@ public void GetRandomDirectory(IsolatedStorageScope scope) string randomDir = Helper.GetRandomDirectory(temp.Path, scope); Assert.True(Directory.Exists(randomDir.Replace(Helper.IsolatedStorageDirectoryName, ""))); } - } + } [Theory, InlineData(IsolatedStorageScope.Assembly), @@ -45,7 +47,43 @@ public void GetRandomDirectoryWithNotExistingDir(IsolatedStorageScope scope) using (var temp = new TempDirectory()) { Assert.Null(Helper.GetExistingRandomDirectory(temp.Path)); - Assert.Equal(Helper.GetRandomDirectory(temp.Path, scope), Helper.GetDataDirectory(scope)); + Assert.Equal(Helper.GetRandomDirectory(Helper.GetDataDirectory(scope), scope), Helper.GetDataDirectory(scope)); + } + } + + [Fact] + public void GetUserStoreForApplicationPath() + { + TestHelper.WipeStores(); + + using (var isf = IsolatedStorageFile.GetUserStoreForApplication()) + { + string root = isf.GetUserRootDirectory(); + Assert.EndsWith("/.config/.isolated-storage", root); + } + } + + [Fact] + public void GetUserStoreForAssemblyPath() + { + TestHelper.WipeStores(); + + using (var isf = IsolatedStorageFile.GetUserStoreForAssembly()) + { + string root = isf.GetUserRootDirectory(); + Assert.EndsWith("/.config/.isolated-storage", root); + } + } + + [Fact] + public void GetUserStoreForDomainPath() + { + TestHelper.WipeStores(); + + using (var isf = IsolatedStorageFile.GetUserStoreForDomain()) + { + string root = isf.GetUserRootDirectory(); + Assert.EndsWith("/.config/.isolated-storage", root); } } } diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.AnyMobile.cs index 63709fd41ab83..d7c4aab634933 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.AnyMobile.cs @@ -15,9 +15,15 @@ private static List GetRoots() string randomUserRoot = Helper.GetRandomDirectory(userRoot, IsolatedStorageScope.User); roots.Add(randomUserRoot); - // Application scope doesn't go under a random dir - roots.Add(userRoot); return roots; } + + /// + /// The actual root of the store (housekeeping files are kept here in NetFX) + /// + public static string GetIdentityRootDirectory(this IsolatedStorageFile isf) + { + return isf.GetUserRootDirectory(); + } } } diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.NonMobile.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.NonMobile.cs index e0217dc241a5a..52914bf320e6a 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.NonMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.NonMobile.cs @@ -31,6 +31,14 @@ private static List GetRoots() return roots; } + + /// + /// The actual root of the store (housekeeping files are kept here in NetFX) + /// + public static string GetIdentityRootDirectory(this IsolatedStorageFile isf) + { + return Path.GetDirectoryName(isf.GetUserRootDirectory().TrimEnd(Path.DirectorySeparatorChar)); + } } } \ No newline at end of file diff --git a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.cs b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.cs index 06339504237dd..820174e7e7091 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/TestHelper.cs @@ -32,13 +32,6 @@ public static string GetUserRootDirectory(this IsolatedStorageFile isf) return (string)s_rootDirectoryProperty.GetValue(isf); } - /// - /// The actual root of the store (housekeeping files are kept here in NetFX) - /// - public static string GetIdentityRootDirectory(this IsolatedStorageFile isf) - { - return Path.GetDirectoryName(isf.GetUserRootDirectory().TrimEnd(Path.DirectorySeparatorChar)); - } /// /// Simple wrapper to create the given file (and close the handle) From 295d058515da3b60ff386ac3f209449876325141 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 10 May 2023 09:47:46 +0200 Subject: [PATCH 12/14] done changes requested by review --- .../System/IO/IsolatedStorage/Helper.AnyMobile.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index 16302df748884..69381fd7d112a 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -10,13 +10,12 @@ internal static partial class Helper internal static string GetDataDirectory(IsolatedStorageScope scope) { - // This is the relevant special folder for the given scope plus IsolatedStorageDirectoryName. - // It is meant to replicate the behavior of the VM ComIsolatedStorage::GetRootDir(). - // // In legacy Xamarin for Roaming Scope we were using Environment.SpecialFolder.LocalApplicationData // In .Net 7 for Roaming Scope we are using Environment.SpecialFolder.ApplicationData - // e.g. .Net 7 path = /data/user/0/{packageName}/files/.isolated-storage/{hash}/{hash}/AppFiles/ - // e.g. Xamarin path = /data/user/0/{packageName}/files/.config/.isolated-storage" + // e.g. Android .Net 7 path = /data/user/0/{packageName}/files/.isolated-storage/{hash}/{hash}/AppFiles/ + // e.g. Android Xamarin path = /data/user/0/{packageName}/files/.config/.isolated-storage/ + // e.g. iOS .Net 7 path = /Users/userName/{packageName}/Documents/.isolated-storage/{hash}/{hash}/AppFiles/ + // e.g. iOS Xamarin path = /Users/userName/{packageName}/Documents/.config/.isolated-storage/ // // Since we shipped that behavior as part of .NET 7 we can't change this now or upgraded apps wouldn't find their files anymore. // We need to look for an existing directory first before using the legacy Xamarin approach. @@ -35,9 +34,13 @@ internal static string GetDataDirectory(IsolatedStorageScope scope) // Otherwise return legacy xamarin path else { + // In .Net 7 for Android SpecialFolder.LocalApplicationData returns "/data/user/0/{packageName}/files" + // while in Xamarin it was "/data/user/0/{packageName}/files/.local/share" + // For Android we need to hardcode Xamarin path for compatibility with legacy Xamarin specialFolder = IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : - IsRoaming(scope) ? Environment.SpecialFolder.LocalApplicationData: + IsRoaming(scope) ? OperatingSystem.IsAndroid() ? OperatingSystem.SpecialFolder.UserProfile + ".local/share" : + Environment.SpecialFolder.LocalApplicationData : Environment.SpecialFolder.ApplicationData; dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); From af2ba4e01a5df5ecc6a895bfeb29261d8681a4c3 Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 10 May 2023 10:33:14 +0200 Subject: [PATCH 13/14] Minor fix --- .../src/System/IO/IsolatedStorage/Helper.AnyMobile.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index 69381fd7d112a..cd7327d1cdd67 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -39,7 +39,7 @@ internal static string GetDataDirectory(IsolatedStorageScope scope) // For Android we need to hardcode Xamarin path for compatibility with legacy Xamarin specialFolder = IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : - IsRoaming(scope) ? OperatingSystem.IsAndroid() ? OperatingSystem.SpecialFolder.UserProfile + ".local/share" : + IsRoaming(scope) ? OperatingSystem.IsAndroid() ? Environment.SpecialFolder.UserProfile + ".local/share" : Environment.SpecialFolder.LocalApplicationData : Environment.SpecialFolder.ApplicationData; From 1563f3a9f9da0c9934de05b7d719c9a275fc44cf Mon Sep 17 00:00:00 2001 From: Meri Khamoyan Date: Wed, 10 May 2023 11:36:40 +0200 Subject: [PATCH 14/14] Fix android build failure --- .../IO/IsolatedStorage/Helper.AnyMobile.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs index cd7327d1cdd67..2600c47126a75 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.AnyMobile.cs @@ -37,13 +37,21 @@ internal static string GetDataDirectory(IsolatedStorageScope scope) // In .Net 7 for Android SpecialFolder.LocalApplicationData returns "/data/user/0/{packageName}/files" // while in Xamarin it was "/data/user/0/{packageName}/files/.local/share" // For Android we need to hardcode Xamarin path for compatibility with legacy Xamarin - specialFolder = - IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : - IsRoaming(scope) ? OperatingSystem.IsAndroid() ? Environment.SpecialFolder.UserProfile + ".local/share" : - Environment.SpecialFolder.LocalApplicationData : - Environment.SpecialFolder.ApplicationData; + if (OperatingSystem.IsAndroid() && IsRoaming(scope)) + { + dataDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + ".local/share"; + Directory.CreateDirectory(dataDirectory); + } + else + { + specialFolder = + IsMachine(scope) ? Environment.SpecialFolder.CommonApplicationData : + IsRoaming(scope) ? Environment.SpecialFolder.LocalApplicationData : + Environment.SpecialFolder.ApplicationData; + + dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); + } - dataDirectory = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.Create); dataDirectory = Path.Combine(dataDirectory, IsolatedStorageDirectoryName); }