33
44namespace Muxarr . Data ;
55
6- /// <summary>Where the database ended up at startup, so the UI can nag. Only meaningful inside a container.</summary>
76public enum AppDataState
87{
9- /// <summary>Not running in a container, or the database is on a mounted /config.</summary>
108 Ok ,
119
12- /// <summary>The database lives in /data: an older install, or nothing mounted at /config.</summary>
13- LegacyLocation ,
14-
15- /// <summary>Nothing is mounted at /config; the database sits in the container's writable layer.</summary>
16- Unpersisted
10+ /// <summary>An older install: the database still lives in /data and is used from there.</summary>
11+ LegacyLocation
1712}
1813
1914/// <summary>
2015/// The database moved from /data to /config. An existing /data database keeps being used
21- /// until the user remounts; nothing is copied or moved. /data is only ever written to when
22- /// it already holds our database or is the empty volume the image declares .
16+ /// until the user remounts; nothing is copied, moved or deleted. Only acts on the default
17+ /// connection string, so custom or relative paths are left alone .
2318/// </summary>
2419public static class ContainerAppData
2520{
@@ -33,19 +28,13 @@ public static class ContainerAppData
3328
3429 public static string ResolveConnectionString ( string connectionString , ILogger ? logger = null )
3530 {
36- if ( ! RunningInContainer ( ) )
37- {
38- return connectionString ;
39- }
40-
4131 lock ( ResolveLock )
4232 {
43- return _resolved ??= Resolve ( connectionString , ConfigDir , DataDir , IsMountPoint , logger ) ;
33+ return _resolved ??= Resolve ( connectionString , ConfigDir , DataDir , logger ) ;
4434 }
4535 }
4636
47- internal static string Resolve ( string connectionString , string configDir , string dataDir ,
48- Func < string , bool > isMountPoint , ILogger ? logger )
37+ internal static string Resolve ( string connectionString , string configDir , string dataDir , ILogger ? logger )
4938 {
5039 var builder = new SqliteConnectionStringBuilder ( connectionString ) ;
5140 if ( string . IsNullOrWhiteSpace ( builder . DataSource ) || builder . DataSource == ":memory:" )
@@ -62,69 +51,19 @@ internal static string Resolve(string connectionString, string configDir, string
6251 }
6352
6453 var legacyDbPath = Path . Combine ( Path . GetFullPath ( dataDir ) , Path . GetFileName ( dbPath ) ) ;
65- var configMounted = isMountPoint ( configDir ) ;
66-
67- if ( ! File . Exists ( dbPath ) && ( File . Exists ( legacyDbPath ) || ! configMounted && IsEmptyMount ( dataDir , isMountPoint ) ) )
68- {
69- logger ? . LogWarning (
70- "Muxarr now stores its database in {ConfigDir} but this install runs from {DataDir}. " +
71- "Mount your appdata folder at {ConfigDir} instead; nothing is moved automatically. " +
72- "See https://muxarr.app/docs/faq.html#appdata" ,
73- configDir , dataDir , configDir ) ;
74- State = AppDataState . LegacyLocation ;
75- builder . DataSource = legacyDbPath ;
76- return builder . ToString ( ) ;
77- }
78-
79- State = configMounted ? AppDataState . Ok : AppDataState . Unpersisted ;
80- if ( ! configMounted )
81- {
82- logger ? . LogWarning (
83- "No volume is mounted at {ConfigDir}. The database will not survive a container recreation." ,
84- configDir ) ;
85- }
86-
87- return connectionString ;
88- }
89-
90- // With no mounts at all Docker gives /data an anonymous volume that survives a compose
91- // recreate; the writable layer behind /config does not. A media mount at /data is never empty.
92- private static bool IsEmptyMount ( string dir , Func < string , bool > isMountPoint )
93- {
94- return isMountPoint ( dir ) && Directory . Exists ( dir ) && ! Directory . EnumerateFileSystemEntries ( dir ) . Any ( ) ;
95- }
96-
97- private static bool RunningInContainer ( )
98- {
99- return string . Equals ( Environment . GetEnvironmentVariable ( "DOTNET_RUNNING_IN_CONTAINER" ) , "true" ,
100- StringComparison . OrdinalIgnoreCase )
101- || File . Exists ( "/.dockerenv" ) ;
102- }
103-
104- /// <summary>
105- /// Distinguishes a real mount (bind mount or named volume) from the container's
106- /// writable layer, where data is silently lost on recreation.
107- /// </summary>
108- private static bool IsMountPoint ( string path )
109- {
110- try
111- {
112- foreach ( var line in File . ReadLines ( "/proc/self/mounts" ) )
113- {
114- var fields = line . Split ( ' ' ) ;
115- if ( fields . Length > 1 && fields [ 1 ] == path )
116- {
117- return true ;
118- }
119- }
120- }
121- catch ( IOException )
122- {
123- }
124- catch ( UnauthorizedAccessException )
54+ if ( File . Exists ( dbPath ) || ! File . Exists ( legacyDbPath ) )
12555 {
56+ State = AppDataState . Ok ;
57+ return connectionString ;
12658 }
12759
128- return false ;
60+ logger ? . LogWarning (
61+ "Muxarr now stores its database in {ConfigDir} but this install runs from {DataDir}. " +
62+ "Mount your appdata folder at {ConfigDir} instead; nothing is moved automatically. " +
63+ "See https://muxarr.app/docs/faq.html#appdata" ,
64+ configDir , dataDir , configDir ) ;
65+ State = AppDataState . LegacyLocation ;
66+ builder . DataSource = legacyDbPath ;
67+ return builder . ToString ( ) ;
12968 }
13069}
0 commit comments