Skip to content

Commit 600b9f8

Browse files
committed
(chocolatey#996) Create config directory if it doesn't exist
If the directory for a LiteDatabase doesn't exist, LiteDatabase will not attempt to create it, and will throw an exception because it can't create the file without the parent directory. This ensures that the folder exists before trying to create the database.
1 parent 86e6f63 commit 600b9f8

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs

+12-3
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,22 @@ protected override void Load(ContainerBuilder builder)
148148
var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true");
149149

150150
LiteDatabase globalDatabase;
151+
var globalConfigDirectory = Path.Combine(Bootstrapper.AppDataPath, "Config");
152+
var globalConfigDatabaseFile = Path.Combine(globalConfigDirectory, "data.db");
153+
151154
if (Hacks.IsElevated)
152155
{
153-
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true");
156+
if (!Directory.Exists(globalConfigDirectory))
157+
{
158+
Directory.CreateDirectory(globalConfigDirectory);
159+
Hacks.LockDirectory(globalConfigDirectory);
160+
}
161+
162+
globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};upgrade=true");
154163
}
155164
else
156165
{
157-
if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")))
166+
if (!File.Exists(globalConfigDatabaseFile))
158167
{
159168
// Since the global configuration database file doesn't exist, we must be running in a state where an administrator user
160169
// has never run Chocolatey GUI. In this case, use null, which will mean attempts to use the global database will be ignored.
@@ -163,7 +172,7 @@ protected override void Load(ContainerBuilder builder)
163172
else
164173
{
165174
// Since this is a non-administrator user, they should only have read permissions to this database
166-
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};readonly=true");
175+
globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};readonly=true");
167176
}
168177
}
169178

Source/ChocolateyGui.Common/Hacks.cs

+31
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,43 @@
55
// </copyright>
66
// --------------------------------------------------------------------------------------------------------------------
77

8+
using System;
9+
using System.IO;
10+
using System.Security.AccessControl;
811
using System.Security.Principal;
912

1013
namespace ChocolateyGui.Common
1114
{
1215
public static class Hacks
1316
{
1417
public static bool IsElevated => (WindowsIdentity.GetCurrent().Owner?.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid)).GetValueOrDefault(false);
18+
19+
// TODO: Replace this LockDirectory with calls to DotNetFileSystem's LockDirectory when https://github.com/chocolatey/ChocolateyGUI/issues/1046 is completed.
20+
/// <summary>
21+
/// Lock the given directory path to just Administrators being able to write. This method is copied from chocolatey.infrastructure.filesystem.DotNetFileSystem, and should be replaced with that call when the minimum Chocolatey.lib is bumped to 2.2.0 or greater.
22+
/// </summary>
23+
/// <param name="directoryPath">Directory path to lock down.</param>
24+
public static void LockDirectory(string directoryPath)
25+
{
26+
var permissions = Directory.GetAccessControl(directoryPath);
27+
var rules = permissions.GetAccessRules(includeExplicit: true, includeInherited: true, targetType: typeof(NTAccount));
28+
29+
// We first need to remove all rules
30+
foreach (FileSystemAccessRule rule in rules)
31+
{
32+
permissions.RemoveAccessRuleAll(rule);
33+
}
34+
35+
var bultinAdmins = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount));
36+
var localsystem = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null).Translate(typeof(NTAccount));
37+
var builtinUsers = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null).Translate(typeof(NTAccount));
38+
var inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
39+
permissions.SetAccessRule(new FileSystemAccessRule(bultinAdmins, FileSystemRights.FullControl, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow));
40+
permissions.SetAccessRule(new FileSystemAccessRule(localsystem, FileSystemRights.FullControl, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow));
41+
permissions.SetAccessRule(new FileSystemAccessRule(builtinUsers, FileSystemRights.ReadAndExecute, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow));
42+
permissions.SetOwner(bultinAdmins);
43+
permissions.SetAccessRuleProtection(isProtected: true, preserveInheritance: false);
44+
Directory.SetAccessControl(directoryPath, permissions);
45+
}
1546
}
1647
}

Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs

+12-3
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,22 @@ protected override void Load(ContainerBuilder builder)
4848
var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true");
4949

5050
LiteDatabase globalDatabase;
51+
var globalConfigDirectory = Path.Combine(Bootstrapper.AppDataPath, "Config");
52+
var globalConfigDatabaseFile = Path.Combine(globalConfigDirectory, "data.db");
53+
5154
if (Hacks.IsElevated)
5255
{
53-
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true");
56+
if (!Directory.Exists(globalConfigDirectory))
57+
{
58+
Directory.CreateDirectory(globalConfigDirectory);
59+
Hacks.LockDirectory(globalConfigDirectory);
60+
}
61+
62+
globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};upgrade=true");
5463
}
5564
else
5665
{
57-
if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")))
66+
if (!File.Exists(globalConfigDatabaseFile))
5867
{
5968
// Since the global configuration database file doesn't exist, we must be running in a state where an administrator user
6069
// has never run Chocolatey GUI. In this case, use null, which will mean attempts to use the global database will be ignored.
@@ -63,7 +72,7 @@ protected override void Load(ContainerBuilder builder)
6372
else
6473
{
6574
// Since this is a non-administrator user, they should only have read permissions to this database
66-
globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};readonly=true");
75+
globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};readonly=true");
6776
}
6877
}
6978

0 commit comments

Comments
 (0)