diff --git a/src/Files.App/Filesystem/FileTagsHelper.cs b/src/Files.App/Filesystem/FileTagsHelper.cs index 7ddcfc0fb801..ad2d8cca6276 100644 --- a/src/Files.App/Filesystem/FileTagsHelper.cs +++ b/src/Files.App/Filesystem/FileTagsHelper.cs @@ -1,13 +1,11 @@ using Common; using Files.App.Filesystem.StorageItems; +using Files.App.Helpers; using Files.App.Shell; using Files.Shared.Extensions; using System; -using System.IO; using System.Linq; -using System.Runtime.InteropServices.ComTypes; using System.Threading.Tasks; -using Vanara.PInvoke; using Windows.Storage; using Windows.Storage.FileProperties; using IO = System.IO; @@ -18,76 +16,45 @@ public static class FileTagsHelper { public static string FileTagsDbPath => IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, "filetags.db"); - public static FileTagsDb GetDbInstance() - { - return new FileTagsDb(FileTagsDbPath); - } + private static readonly Lazy dbInstance = new(() => new FileTagsDb(FileTagsDbPath, true)); + + public static FileTagsDb GetDbInstance() => dbInstance.Value; public static string[] ReadFileTag(string filePath) { - using var hStream = Kernel32.CreateFile($"{filePath}:files", - Kernel32.FileAccess.GENERIC_READ, 0, null, FileMode.Open, FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); - if (hStream.IsInvalid) - { - return null; - } - var bytes = new byte[4096]; - var ret = Kernel32.ReadFile(hStream, bytes, (uint)bytes.Length, out var read, IntPtr.Zero); - if (!ret) - { - return null; - } - var tagString = System.Text.Encoding.UTF8.GetString(bytes, 0, (int)read); - return tagString.Split(','); + var tagString = NativeFileOperationsHelper.ReadStringFromFile($"{filePath}:files"); + return tagString?.Split(','); } - public static bool WriteFileTag(string filePath, string[] tag) + public static void WriteFileTag(string filePath, string[] tag) { - var dateOk = GetFileDateModified(filePath, out var dateModified); // Backup date modified - bool result = false; - if (tag is null || !tag.Any()) + var isDateOk = NativeFileOperationsHelper.GetFileDateModified(filePath, out var dateModified); // Backup date modified + var isReadOnly = NativeFileOperationsHelper.HasFileAttribute(filePath, IO.FileAttributes.ReadOnly); + if (isReadOnly) // Unset read-only attribute (#7534) { - result = Kernel32.DeleteFile($"{filePath}:files"); + NativeFileOperationsHelper.UnsetFileAttribute(filePath, IO.FileAttributes.ReadOnly); } - else + if (tag is null || !tag.Any()) { - using var hStream = Kernel32.CreateFile($"{filePath}:files", - Kernel32.FileAccess.GENERIC_WRITE, 0, null, FileMode.Create, FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); - if (hStream.IsInvalid) - { - return false; - } - byte[] buff = System.Text.Encoding.UTF8.GetBytes(string.Join(',', tag)); - result = Kernel32.WriteFile(hStream, buff, (uint)buff.Length, out var written, IntPtr.Zero); + NativeFileOperationsHelper.DeleteFileFromApp($"{filePath}:files"); } - if (dateOk) + else if (ReadFileTag(filePath) is not string[] arr || !tag.SequenceEqual(arr)) { - SetFileDateModified(filePath, dateModified); // Restore date modified + NativeFileOperationsHelper.WriteStringToFile($"{filePath}:files", string.Join(',', tag)); } - return result; - } - - public static ulong? GetFileFRN(string filePath) - { - //using var si = new ShellItem(filePath); - //return (ulong?)si.Properties["System.FileFRN"]; // Leaves open file handles - using var hFile = Kernel32.CreateFile(filePath, Kernel32.FileAccess.GENERIC_READ, FileShare.ReadWrite, null, FileMode.Open, FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS); - if (hFile.IsInvalid) + if (isReadOnly) // Restore read-only attribute (#7534) { - return null; + NativeFileOperationsHelper.SetFileAttribute(filePath, IO.FileAttributes.ReadOnly); } - ulong? frn = null; - SafetyExtensions.IgnoreExceptions(() => + if (isDateOk) { - var fileID = Kernel32.GetFileInformationByHandleEx(hFile, Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileIdInfo); - frn = BitConverter.ToUInt64(fileID.FileId.Identifier, 0); - }); - return frn; + NativeFileOperationsHelper.SetFileDateModified(filePath, dateModified); // Restore date modified + } } public static void UpdateTagsDb() { - using var dbInstance = GetDbInstance(); + var dbInstance = GetDbInstance(); foreach (var file in dbInstance.GetAll()) { var pathFromFrn = Win32API.PathFromFileId(file.Frn ?? 0, file.FilePath); @@ -128,17 +95,7 @@ public static void UpdateTagsDb() } } - private static bool GetFileDateModified(string filePath, out FILETIME dateModified) - { - using var hFile = Kernel32.CreateFile(filePath, Kernel32.FileAccess.GENERIC_READ, FileShare.Read, null, FileMode.Open, FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS); - return Kernel32.GetFileTime(hFile, out _, out _, out dateModified); - } - - private static bool SetFileDateModified(string filePath, FILETIME dateModified) - { - using var hFile = Kernel32.CreateFile(filePath, Kernel32.FileAccess.FILE_WRITE_ATTRIBUTES, FileShare.None, null, FileMode.Open, FileFlagsAndAttributes.FILE_FLAG_BACKUP_SEMANTICS); - return Kernel32.SetFileTime(hFile, new(), new(), dateModified); - } + public static ulong? GetFileFRN(string filePath) => NativeFileOperationsHelper.GetFileFRN(filePath); public static Task GetFileFRN(IStorageItem item) { diff --git a/src/Files.App/Filesystem/ListedItem.cs b/src/Files.App/Filesystem/ListedItem.cs index 2a15b2fed666..aca41f3f7df8 100644 --- a/src/Files.App/Filesystem/ListedItem.cs +++ b/src/Files.App/Filesystem/ListedItem.cs @@ -125,10 +125,8 @@ public string[] FileTags { if (SetProperty(ref fileTags, value)) { - using (var dbInstance = FileTagsHelper.GetDbInstance()) - { - dbInstance.SetTags(ItemPath, FileFRN, value); - } + var dbInstance = FileTagsHelper.GetDbInstance(); + dbInstance.SetTags(ItemPath, FileFRN, value); FileTagsHelper.WriteFileTag(ItemPath, value); OnPropertyChanged(nameof(FileTagsUI)); } diff --git a/src/Files.App/Filesystem/Search/FolderSearch.cs b/src/Files.App/Filesystem/Search/FolderSearch.cs index 5bf1cef12aec..efd8bd78b1ab 100644 --- a/src/Files.App/Filesystem/Search/FolderSearch.cs +++ b/src/Files.App/Filesystem/Search/FolderSearch.cs @@ -194,12 +194,10 @@ private async Task SearchTagsAsync(string folder, IList results, Can { return; } - List? matches; - using (var dbInstance = FileTagsHelper.GetDbInstance()) - { - matches = dbInstance.GetAllUnderPath(folder) - .Where(x => tags.All(x.Tags.Contains)).ToList(); - } + + var dbInstance = FileTagsHelper.GetDbInstance(); + var matches = dbInstance.GetAllUnderPath(folder) + .Where(x => tags.All(x.Tags.Contains)); foreach (var match in matches) { diff --git a/src/Files.App/Helpers/FileOperationsHelpers.cs b/src/Files.App/Helpers/FileOperationsHelpers.cs index d655e8855294..5b6f51497bb5 100644 --- a/src/Files.App/Helpers/FileOperationsHelpers.cs +++ b/src/Files.App/Helpers/FileOperationsHelpers.cs @@ -721,7 +721,7 @@ public static bool SetCompatOptions(string filePath, string options) private static void UpdateFileTagsDb(ShellFileOperations.ShellFileOpEventArgs e, string operationType) { - using var dbInstance = FileTagsHelper.GetDbInstance(); + var dbInstance = FileTagsHelper.GetDbInstance(); if (e.Result.Succeeded) { var sourcePath = e.SourceItem.GetParsingPath(); diff --git a/src/Files.App/Helpers/LayoutPreferences/LayoutPrefsDb.cs b/src/Files.App/Helpers/LayoutPreferences/LayoutPrefsDb.cs index bb4192d5c211..8badc778274c 100644 --- a/src/Files.App/Helpers/LayoutPreferences/LayoutPrefsDb.cs +++ b/src/Files.App/Helpers/LayoutPreferences/LayoutPrefsDb.cs @@ -1,66 +1,25 @@ using Files.Shared.Extensions; using LiteDB; using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Threading; -using JsonSerializer = System.Text.Json.JsonSerializer; +using System.Linq; +using System.Text; +using IO = System.IO; namespace Files.App.Helpers.LayoutPreferences { public class LayoutPrefsDb : IDisposable { private readonly LiteDatabase db; - private readonly IEnumerator mutexCoroutine; - private static readonly Mutex dbMutex = new(false, "Files_LayoutSettingsDb"); - private static readonly ConcurrentQueue backgroundMutexOperationQueue = new(); - static LayoutPrefsDb() + public LayoutPrefsDb(string connection, bool shared = false) { - new Thread(OperationQueueWorker) { IsBackground = true }.Start(); - } - - private static void OperationQueueWorker() - { - while (!Environment.HasShutdownStarted) - { - SpinWait.SpinUntil(() => !backgroundMutexOperationQueue.IsEmpty); - while (backgroundMutexOperationQueue.TryDequeue(out var action)) - { - action(); - } - } - } - - public LayoutPrefsDb(string connection) - { - mutexCoroutine = MutexOperator().GetEnumerator(); - mutexCoroutine.MoveNext(); + SafetyExtensions.IgnoreExceptions(() => CheckDbVersion(connection)); db = new LiteDatabase(new ConnectionString(connection) { - Connection = ConnectionType.Direct, - Upgrade = true + Mode = shared ? LiteDB.FileMode.Shared : LiteDB.FileMode.Exclusive }, new BsonMapper() { IncludeFields = true }); } - private IEnumerable MutexOperator() - { - var e1 = new ManualResetEventSlim(); - var e2 = new ManualResetEventSlim(); - backgroundMutexOperationQueue.Enqueue(() => - { - dbMutex.WaitOne(); - e1.Set(); - e2.Wait(); - e2.Dispose(); - dbMutex.ReleaseMutex(); - }); - e1.Wait(); - e1.Dispose(); - yield return default; - e2.Set(); - } - public void SetPreferences(string filePath, ulong? frn, LayoutPreferences? prefs) { // Get a collection (or create, if doesn't exist) @@ -145,11 +104,11 @@ public void ResetAll(Func? predicate = null) var col = db.GetCollection("layoutprefs"); if (predicate is null) { - col.DeleteAll(); + col.Delete(Query.All()); } else { - col.DeleteMany(x => predicate(x)); + col.Delete(x => predicate(x)); } } @@ -169,20 +128,38 @@ public void ApplyToAll(Action updateAction, Func(json); - var col = db.GetCollection("layoutprefs"); - col.DeleteAll(); - col.InsertBulk(dataValues); + var dataValues = JsonSerializer.DeserializeArray(json); + var col = db.GetCollection("layoutprefs"); + col.Delete(Query.All()); + col.InsertBulk(dataValues.Select(x => x.AsDocument)); } public string Export() { - return JsonSerializer.Serialize(db.GetCollection("layoutprefs").FindAll()); + return JsonSerializer.Serialize(new BsonArray(db.GetCollection("layoutprefs").FindAll())); + } + + // https://github.com/mbdavid/LiteDB/blob/master/LiteDB/Engine/Engine/Upgrade.cs + private void CheckDbVersion(string filename) + { + var buffer = new byte[8192 * 2]; + using (var stream = new IO.FileStream(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite)) + { + // read first 16k + stream.Read(buffer, 0, buffer.Length); + + // checks if v7 (plain or encrypted) + if (Encoding.UTF8.GetString(buffer, 25, "** This is a LiteDB file **".Length) == "** This is a LiteDB file **" && + buffer[52] == 7) + { + return; // version 4.1.4 + } + } + IO.File.Delete(filename); // recreate DB with correct version } public class LayoutDbPrefs diff --git a/src/Files.App/Helpers/NativeFileOperationsHelper.cs b/src/Files.App/Helpers/NativeFileOperationsHelper.cs index c509909a2812..57d7228db62a 100644 --- a/src/Files.App/Helpers/NativeFileOperationsHelper.cs +++ b/src/Files.App/Helpers/NativeFileOperationsHelper.cs @@ -1,3 +1,4 @@ +using Files.Shared.Extensions; using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; @@ -6,6 +7,7 @@ using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Threading; +using Vanara.PInvoke; namespace Files.App.Helpers { @@ -459,6 +461,21 @@ public static bool WriteBufferToFileWithProgress(string filePath, byte[] buffer, return null; } + public static ulong? GetFileFRN(string filePath) + { + using var handle = OpenFileForRead(filePath); + if (!handle.IsInvalid) + { + try + { + var fileID = Kernel32.GetFileInformationByHandleEx(handle, Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileIdInfo); + return BitConverter.ToUInt64(fileID.FileId.Identifier, 0); + } + catch { } + } + return null; + } + // https://github.com/rad1oactive/BetterExplorer/blob/master/Windows%20API%20Code%20Pack%201.1/source/WindowsAPICodePack/Shell/ReparsePoint.cs public static string ParseSymLink(string path) { diff --git a/src/Files.App/MainWindow.xaml.cs b/src/Files.App/MainWindow.xaml.cs index 6dfecc570914..9368f4ff430d 100644 --- a/src/Files.App/MainWindow.xaml.cs +++ b/src/Files.App/MainWindow.xaml.cs @@ -261,7 +261,7 @@ async Task PerformNavigation(string payload, string selectItem = null) if (fileFRN is not null) { var tagUid = tag is not null ? new[] { tag.Uid } : null; - using var dbInstance = FileTagsHelper.GetDbInstance(); + var dbInstance = FileTagsHelper.GetDbInstance(); dbInstance.SetTags(file, fileFRN, tagUid); FileTagsHelper.WriteFileTag(file, tagUid); } diff --git a/src/Files.App/ViewModels/FolderSettingsViewModel.cs b/src/Files.App/ViewModels/FolderSettingsViewModel.cs index a631e898b7f6..628dbe3639c4 100644 --- a/src/Files.App/ViewModels/FolderSettingsViewModel.cs +++ b/src/Files.App/ViewModels/FolderSettingsViewModel.cs @@ -20,10 +20,10 @@ namespace Files.App.ViewModels public class FolderSettingsViewModel : ObservableObject { public static string LayoutSettingsDbPath => IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, "user_settings.db"); - public static LayoutPrefsDb GetDbInstance() - { - return new LayoutPrefsDb(LayoutSettingsDbPath); - } + + private static readonly Lazy dbInstance = new(() => new LayoutPrefsDb(LayoutSettingsDbPath, true)); + + public static LayoutPrefsDb GetDbInstance() => dbInstance.Value; public event EventHandler? LayoutPreferencesUpdateRequired; @@ -358,7 +358,8 @@ private static LayoutPreferences ReadLayoutPreferencesFromAds(string folderPath, { if (string.IsNullOrEmpty(folderPath)) return null; - using var dbInstance = GetDbInstance(); + + var dbInstance = GetDbInstance(); return dbInstance.GetPreferences(folderPath, frn); } @@ -382,7 +383,7 @@ private static void WriteLayoutPreferencesToDb(string folderPath, ulong? frn, La if (string.IsNullOrEmpty(folderPath)) return; - using var dbInstance = GetDbInstance(); + var dbInstance = GetDbInstance(); if (dbInstance.GetPreferences(folderPath, frn) is null) { if (LayoutPreferences.DefaultLayoutPreferences.Equals(prefs)) diff --git a/src/Files.App/ViewModels/ItemViewModel.cs b/src/Files.App/ViewModels/ItemViewModel.cs index 483157c1e0a0..221cf4e3517b 100644 --- a/src/Files.App/ViewModels/ItemViewModel.cs +++ b/src/Files.App/ViewModels/ItemViewModel.cs @@ -983,10 +983,8 @@ await dispatcherQueue.EnqueueAsync(async () => private static void SetFileTag(ListedItem item) { - using (var dbInstance = FileTagsHelper.GetDbInstance()) - { - dbInstance.SetTags(item.ItemPath, item.FileFRN, item.FileTags); - } + var dbInstance = FileTagsHelper.GetDbInstance(); + dbInstance.SetTags(item.ItemPath, item.FileFRN, item.FileTags); } // This works for recycle bin as well as GetFileFromPathAsync/GetFolderFromPathAsync work diff --git a/src/Files.App/ViewModels/SettingsViewModels/AboutViewModel.cs b/src/Files.App/ViewModels/SettingsViewModels/AboutViewModel.cs index f3da8223834c..b1ea0159ad7b 100644 --- a/src/Files.App/ViewModels/SettingsViewModels/AboutViewModel.cs +++ b/src/Files.App/ViewModels/SettingsViewModels/AboutViewModel.cs @@ -80,18 +80,12 @@ private async Task ExportSettings() // Export file tags list and DB var exportTags = UTF8Encoding.UTF8.GetBytes((string)FileTagsSettingsService.ExportSettings()); await zipFolder.CreateFileAsync(new MemoryStream(exportTags), Constants.LocalSettings.FileTagSettingsFileName, CreationCollisionOption.ReplaceExisting); - byte[] exportTagsDB; - using (var tagDbInstance = FileTagsHelper.GetDbInstance()) - { - exportTagsDB = UTF8Encoding.UTF8.GetBytes(tagDbInstance.Export()); - } + var tagDbInstance = FileTagsHelper.GetDbInstance(); + byte[] exportTagsDB = UTF8Encoding.UTF8.GetBytes(tagDbInstance.Export()); await zipFolder.CreateFileAsync(new MemoryStream(exportTagsDB), Path.GetFileName(FileTagsHelper.FileTagsDbPath), CreationCollisionOption.ReplaceExisting); // Export layout preferences DB - byte[] exportPrefsDB; - using (var layoutDbInstance = FolderSettingsViewModel.GetDbInstance()) - { - exportPrefsDB = UTF8Encoding.UTF8.GetBytes(layoutDbInstance.Export()); - } + var layoutDbInstance = FolderSettingsViewModel.GetDbInstance(); + byte[] exportPrefsDB = UTF8Encoding.UTF8.GetBytes(layoutDbInstance.Export()); await zipFolder.CreateFileAsync(new MemoryStream(exportPrefsDB), Path.GetFileName(FolderSettingsViewModel.LayoutSettingsDbPath), CreationCollisionOption.ReplaceExisting); } catch (Exception ex) @@ -143,17 +137,13 @@ private async Task ImportSettings() FileTagsSettingsService.ImportSettings(importTags); var fileTagsDB = await zipFolder.GetFileAsync(Path.GetFileName(FileTagsHelper.FileTagsDbPath)); string importTagsDB = await fileTagsDB.ReadTextAsync(); - using (var tagDbInstance = FileTagsHelper.GetDbInstance()) - { - tagDbInstance.Import(importTagsDB); - } + var tagDbInstance = FileTagsHelper.GetDbInstance(); + tagDbInstance.Import(importTagsDB); // Import layout preferences and DB var layoutPrefsDB = await zipFolder.GetFileAsync(Path.GetFileName(FolderSettingsViewModel.LayoutSettingsDbPath)); string importPrefsDB = await layoutPrefsDB.ReadTextAsync(); - using (var layoutDbInstance = FolderSettingsViewModel.GetDbInstance()) - { - layoutDbInstance.Import(importPrefsDB); - } + var layoutDbInstance = FolderSettingsViewModel.GetDbInstance(); + layoutDbInstance.Import(importPrefsDB); } catch (Exception ex) { diff --git a/src/Files.App/ViewModels/SettingsViewModels/FoldersViewModel.cs b/src/Files.App/ViewModels/SettingsViewModels/FoldersViewModel.cs index e9075442592d..e5e6f7185dad 100644 --- a/src/Files.App/ViewModels/SettingsViewModels/FoldersViewModel.cs +++ b/src/Files.App/ViewModels/SettingsViewModels/FoldersViewModel.cs @@ -244,10 +244,8 @@ public bool CalculateFolderSizes public void ResetLayoutPreferences() { // Is this proper practice? - using (var dbInstance = FolderSettingsViewModel.GetDbInstance()) - { - dbInstance.ResetAll(); - } + var dbInstance = FolderSettingsViewModel.GetDbInstance(); + dbInstance.ResetAll(); IsResetLayoutPreferencesTipOpen = false; } } diff --git a/src/Files.Shared/FileTagsDb.cs b/src/Files.Shared/FileTagsDb.cs index c895acfa1bf9..00ae64368159 100644 --- a/src/Files.Shared/FileTagsDb.cs +++ b/src/Files.Shared/FileTagsDb.cs @@ -1,69 +1,28 @@ -using LiteDB; +using Files.Shared.Extensions; +using LiteDB; using System; -using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Threading; -using JsonSerializer = System.Text.Json.JsonSerializer; +using System.Text; +using IO = System.IO; namespace Common { public class FileTagsDb : IDisposable { private readonly LiteDatabase db; - private readonly IEnumerator mutexCoroutine; - private static readonly Mutex dbMutex = new(false, "Files_FileTagDb"); - private static readonly ConcurrentQueue backgroundMutexOperationQueue = new(); private const string TaggedFiles = "taggedfiles"; - static FileTagsDb() + public FileTagsDb(string connection, bool shared = false) { - new Thread(OperationQueueWorker) { IsBackground = true }.Start(); - } - - private static void OperationQueueWorker() - { - while (!Environment.HasShutdownStarted) - { - SpinWait.SpinUntil(() => !backgroundMutexOperationQueue.IsEmpty); - while (backgroundMutexOperationQueue.TryDequeue(out var action)) - { - action(); - } - } - } - - public FileTagsDb(string connection) - { - mutexCoroutine = MutexOperator().GetEnumerator(); - mutexCoroutine.MoveNext(); + SafetyExtensions.IgnoreExceptions(() => CheckDbVersion(connection)); db = new LiteDatabase(new ConnectionString(connection) { - Connection = ConnectionType.Direct, - Upgrade = true + Mode = shared ? LiteDB.FileMode.Shared : LiteDB.FileMode.Exclusive }); UpdateDb(); } - private IEnumerable MutexOperator() - { - var e1 = new ManualResetEventSlim(); - var e2 = new ManualResetEventSlim(); - backgroundMutexOperationQueue.Enqueue(() => - { - dbMutex.WaitOne(); - e1.Set(); - e2.Wait(); - e2.Dispose(); - dbMutex.ReleaseMutex(); - }); - e1.Wait(); - e1.Dispose(); - yield return default; - e2.Set(); - } - public void SetTags(string filePath, ulong? frn, string[]? tags) { // Get a collection (or create, if doesn't exist) @@ -209,25 +168,24 @@ public IEnumerable GetAllUnderPath(string folderPath) public void Dispose() { db.Dispose(); - mutexCoroutine.MoveNext(); } public void Import(string json) { - var dataValues = JsonSerializer.Deserialize(json); - var col = db.GetCollection(TaggedFiles); - col.DeleteAll(); - col.InsertBulk(dataValues); + var dataValues = JsonSerializer.DeserializeArray(json); + var col = db.GetCollection(TaggedFiles); + col.Delete(Query.All()); + col.InsertBulk(dataValues.Select(x => x.AsDocument)); } public string Export() { - return JsonSerializer.Serialize(db.GetCollection(TaggedFiles).FindAll()); + return JsonSerializer.Serialize(new BsonArray(db.GetCollection(TaggedFiles).FindAll())); } private void UpdateDb() { - if (db.UserVersion == 0) + if (db.Engine.UserVersion == 0) { var col = db.GetCollection(TaggedFiles); foreach (var doc in col.FindAll()) @@ -236,8 +194,27 @@ private void UpdateDb() doc.Remove("Tags"); col.Update(doc); } - db.UserVersion = 1; + db.Engine.UserVersion = 1; + } + } + + // https://github.com/mbdavid/LiteDB/blob/master/LiteDB/Engine/Engine/Upgrade.cs + private void CheckDbVersion(string filename) + { + var buffer = new byte[8192 * 2]; + using (var stream = new IO.FileStream(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite)) + { + // read first 16k + stream.Read(buffer, 0, buffer.Length); + + // checks if v7 (plain or encrypted) + if (Encoding.UTF8.GetString(buffer, 25, "** This is a LiteDB file **".Length) == "** This is a LiteDB file **" && + buffer[52] == 7) + { + return; // version 4.1.4 + } } + IO.File.Delete(filename); // recreate DB with correct version } public class TaggedFile diff --git a/src/Files.Shared/Files.Shared.csproj b/src/Files.Shared/Files.Shared.csproj index 81668b3ed0af..f2447d8c6b42 100644 --- a/src/Files.Shared/Files.Shared.csproj +++ b/src/Files.Shared/Files.Shared.csproj @@ -10,7 +10,8 @@ - + +