77
88//! Objects related to [`SqliteStore`] live here.
99use std:: collections:: HashMap ;
10+ #[ cfg( test) ]
1011use std:: fs;
12+ #[ cfg( unix) ]
13+ use std:: fs:: OpenOptions ;
1114use std:: future:: Future ;
15+ #[ cfg( unix) ]
16+ use std:: os:: unix:: fs:: OpenOptionsExt ;
1217use std:: path:: PathBuf ;
1318use std:: sync:: atomic:: { AtomicI64 , AtomicU64 , Ordering } ;
1419use std:: sync:: { Arc , Mutex } ;
@@ -20,7 +25,7 @@ use lightning::util::persist::{
2025use lightning_types:: string:: PrintableString ;
2126use rusqlite:: { named_params, Connection } ;
2227
23- use crate :: io:: utils:: check_namespace_key_validity;
28+ use crate :: io:: utils:: { check_namespace_key_validity, create_dir_all_private } ;
2429
2530mod migrations;
2631
@@ -58,6 +63,8 @@ impl SqliteStore {
5863 /// If not already existing, a new SQLite database will be created in the given `data_dir` under the
5964 /// given `db_file_name` (or the default to [`DEFAULT_SQLITE_DB_FILE_NAME`] if set to `None`).
6065 ///
66+ /// SQLite's `:memory:` database name and `file:` URI filenames are not supported.
67+ ///
6168 /// Similarly, the given `kv_table_name` will be used or default to [`DEFAULT_KV_TABLE_NAME`].
6269 pub fn new (
6370 data_dir : PathBuf , db_file_name : Option < String > , kv_table_name : Option < String > ,
@@ -232,18 +239,37 @@ impl SqliteStoreInner {
232239 data_dir : PathBuf , db_file_name : Option < String > , kv_table_name : Option < String > ,
233240 ) -> io:: Result < Self > {
234241 let db_file_name = db_file_name. unwrap_or ( DEFAULT_SQLITE_DB_FILE_NAME . to_string ( ) ) ;
242+ let mut db_file_path = data_dir. clone ( ) ;
243+ db_file_path. push ( & db_file_name) ;
244+ if db_file_name == ":memory:"
245+ || db_file_name. starts_with ( "file:" )
246+ || db_file_path. to_string_lossy ( ) . starts_with ( "file:" )
247+ {
248+ return Err ( io:: Error :: new (
249+ io:: ErrorKind :: InvalidInput ,
250+ "SQLite :memory: and file: database names are not supported" ,
251+ ) ) ;
252+ }
235253 let kv_table_name = kv_table_name. unwrap_or ( DEFAULT_KV_TABLE_NAME . to_string ( ) ) ;
236254
237- fs :: create_dir_all ( data_dir. clone ( ) ) . map_err ( |e| {
255+ create_dir_all_private ( & data_dir) . map_err ( |e| {
238256 let msg = format ! (
239257 "Failed to create database destination directory {}: {}" ,
240258 data_dir. display( ) ,
241259 e
242260 ) ;
243261 io:: Error :: new ( io:: ErrorKind :: Other , msg)
244262 } ) ?;
245- let mut db_file_path = data_dir. clone ( ) ;
246- db_file_path. push ( db_file_name) ;
263+ #[ cfg( unix) ]
264+ match OpenOptions :: new ( ) . create_new ( true ) . write ( true ) . mode ( 0o600 ) . open ( & db_file_path) {
265+ Ok ( _) => { } ,
266+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: AlreadyExists => { } ,
267+ Err ( e) => {
268+ let msg =
269+ format ! ( "Failed to create database file {}: {}" , db_file_path. display( ) , e) ;
270+ return Err ( io:: Error :: new ( io:: ErrorKind :: Other , msg) ) ;
271+ } ,
272+ }
247273
248274 let mut connection = Connection :: open ( db_file_path. clone ( ) ) . map_err ( |e| {
249275 let msg =
@@ -700,6 +726,50 @@ mod tests {
700726 }
701727 }
702728
729+ #[ cfg( unix) ]
730+ #[ test]
731+ fn creates_private_database_storage ( ) {
732+ use std:: os:: unix:: fs:: PermissionsExt ;
733+
734+ let mut data_dir = random_storage_path ( ) ;
735+ data_dir. push ( "creates_private_database_storage" ) ;
736+ let db_file_name = "test_db" ;
737+ let db_file_path = data_dir. join ( db_file_name) ;
738+ let _store = SqliteStore :: new (
739+ data_dir. clone ( ) ,
740+ Some ( db_file_name. to_string ( ) ) ,
741+ Some ( "test_table" . to_string ( ) ) ,
742+ )
743+ . unwrap ( ) ;
744+
745+ let dir_mode = data_dir. metadata ( ) . unwrap ( ) . permissions ( ) . mode ( ) ;
746+ let file_mode = db_file_path. metadata ( ) . unwrap ( ) . permissions ( ) . mode ( ) ;
747+ assert_eq ! ( dir_mode & 0o077 , 0 ) ;
748+ assert_eq ! ( file_mode & 0o077 , 0 ) ;
749+ }
750+
751+ #[ test]
752+ fn rejects_sqlite_pseudo_filenames ( ) {
753+ for ( data_dir, db_file_name) in [
754+ ( random_storage_path ( ) , ":memory:" ) ,
755+ ( random_storage_path ( ) , "file:/tmp/node.db?mode=rwc" ) ,
756+ ( PathBuf :: from ( "file:." ) , "test_db" ) ,
757+ ] {
758+ let result = SqliteStore :: new (
759+ data_dir. clone ( ) ,
760+ Some ( db_file_name. to_string ( ) ) ,
761+ Some ( "test_table" . to_string ( ) ) ,
762+ ) ;
763+ let error = match result {
764+ Ok ( _) => panic ! ( "SQLite pseudo-filename was accepted" ) ,
765+ Err ( e) => e,
766+ } ;
767+
768+ assert_eq ! ( error. kind( ) , io:: ErrorKind :: InvalidInput ) ;
769+ assert ! ( !data_dir. exists( ) ) ;
770+ }
771+ }
772+
703773 #[ tokio:: test]
704774 async fn read_write_remove_list_persist ( ) {
705775 let mut temp_path = random_storage_path ( ) ;
0 commit comments